#include "lf_hfs_chash.h"
#include "lf_hfs_cnode.h"
#include "lf_hfs_locks.h"
#include "lf_hfs_utils.h"
#include "lf_hfs_logger.h"
#include "lf_hfs_vfsutils.h"
#define DESIRED_VNODES (128)
#define CNODEHASH(hfsmp, inum) (&hfsmp->hfs_cnodehashtbl[(inum) & hfsmp->hfs_cnodehash])
void
hfs_chash_wait(struct hfsmount *hfsmp, struct cnode *cp)
{
SET(cp->c_hflag, H_WAITING);
pthread_cond_wait(&cp->c_cacsh_cond, &hfsmp->hfs_chash_mutex);
}
static void
hfs_chash_broadcast_and_unlock(struct hfsmount *hfsmp, struct cnode *cp)
{
if (cp)
pthread_cond_signal(&cp->c_cacsh_cond);
hfs_chash_unlock(hfsmp);
}
static void
hfs_chash_wait_and_unlock(struct hfsmount *hfsmp, struct cnode *cp)
{
SET(cp->c_hflag, H_WAITING);
pthread_cond_wait(&cp->c_cacsh_cond, &hfsmp->hfs_chash_mutex);
hfs_chash_broadcast_and_unlock(hfsmp, cp);
}
void
hfs_chash_raise_OpenLookupCounter(struct cnode *cp)
{
if (!cp || cp->uOpenLookupRefCount == UINT32_MAX)
{
LFHFS_LOG(LEVEL_ERROR,
"hfs_chash_raise_OpenLookupCounter:"
"cp[%p] is NULL or reached max Open Lookup Counter", cp);
hfs_assert(0);
}
cp->uOpenLookupRefCount++;
}
void
hfs_chash_lower_OpenLookupCounter(struct cnode *cp)
{
if (cp->uOpenLookupRefCount == 0)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_chash_lower_OpenLookupCounter: reached min Open Lookup Counter \n");
hfs_assert(0);
}
cp->uOpenLookupRefCount--;
}
void
hfs_chashinit()
{
}
void hfs_chash_lock(struct hfsmount *hfsmp)
{
lf_lck_mtx_lock(&hfsmp->hfs_chash_mutex);
}
void hfs_chash_lock_spin(struct hfsmount *hfsmp)
{
lf_lck_mtx_lock_spin(&hfsmp->hfs_chash_mutex);
}
void hfs_chash_unlock(struct hfsmount *hfsmp)
{
lf_lck_mtx_unlock(&hfsmp->hfs_chash_mutex);
}
void
hfs_chashinit_finish(struct hfsmount *hfsmp)
{
lf_lck_mtx_init(&hfsmp->hfs_chash_mutex);
hfsmp->hfs_cnodehashtbl = hashinit(DESIRED_VNODES / 4, &hfsmp->hfs_cnodehash);
}
void
hfs_delete_chash(struct hfsmount *hfsmp)
{
struct cnode *cp;
hfs_chash_lock_spin(hfsmp);
for (ino_t inum = 0; inum < (DESIRED_VNODES/4); inum++)
{
for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next) {
LFHFS_LOG(LEVEL_ERROR, "hfs_delete_chash: Cnode for file [%s], cnid: [%d] with open count [%d] left in the cache \n", cp->c_desc.cd_nameptr, cp->c_desc.cd_cnid, cp->uOpenLookupRefCount);
}
}
hfs_chash_unlock(hfsmp);
lf_lck_mtx_destroy(&hfsmp->hfs_chash_mutex);
hfs_free(hfsmp->hfs_cnodehashtbl);
}
struct cnode*
hfs_chash_getcnode(struct hfsmount *hfsmp, ino_t inum, struct vnode **vpp, int wantrsrc, int skiplock, int *out_flags, int *hflags)
{
struct cnode *cp;
struct cnode *ncp = NULL;
vnode_t vp;
loop:
hfs_chash_lock_spin(hfsmp);
loop_with_lock:
for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next)
{
if (cp->c_fileid != inum)
{
continue;
}
if (ISSET(cp->c_hflag, H_ALLOC | H_ATTACH | H_TRANSIT))
{
hfs_chash_wait(hfsmp, cp);
goto loop_with_lock;
}
vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
if (vp == NULL)
{
SET(cp->c_hflag, H_ATTACH);
*hflags |= H_ATTACH;
}
if (ncp)
{
hfs_free(ncp);
ncp = NULL;
}
if (!skiplock)
{
if (hfs_lock(cp, HFS_TRY_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS))
{
SET(cp->c_hflag, H_WAITING);
hfs_chash_broadcast_and_unlock(hfsmp, cp);
usleep(100);
goto loop;
}
}
vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
if (((cp->c_flag & (C_NOEXISTS | C_DELETED)) && !wantrsrc) ||
((vp != NULL) &&
((cp->uOpenLookupRefCount == 0) ||
(vp->uValidNodeMagic1 == VALID_NODE_BADMAGIC) ||
(vp->uValidNodeMagic2 == VALID_NODE_BADMAGIC))))
{
int renamed = 0;
if (cp->c_flag & C_RENAMED)
renamed = 1;
if (!skiplock)
{
hfs_unlock(cp);
}
if (vp != NULL)
{
vnode_rele(vp);
}
else
{
hfs_chashwakeup(hfsmp, cp, H_ATTACH);
*hflags &= ~H_ATTACH;
}
pthread_cond_signal(&cp->c_cacsh_cond);
vp = NULL;
cp = NULL;
if (renamed)
{
*out_flags = GNV_CHASH_RENAMED;
}
}
if (cp) {
hfs_chash_raise_OpenLookupCounter(cp);
}
hfs_chash_broadcast_and_unlock(hfsmp, cp);
*vpp = vp;
return (cp);
}
if (skiplock && !wantrsrc)
{
LFHFS_LOG(LEVEL_ERROR, "hfs_chash_getcnode: should never get here when skiplock is set \n");
hfs_assert(0);
}
if (ncp == NULL)
{
hfs_chash_unlock(hfsmp);
ncp = hfs_mallocz(sizeof(struct cnode));
if (ncp == NULL)
{
return ncp;
}
goto loop;
}
bzero(ncp, sizeof(*ncp));
SET(ncp->c_hflag, H_ALLOC);
*hflags |= H_ALLOC;
ncp->c_fileid = (cnid_t) inum;
TAILQ_INIT(&ncp->c_hintlist);
TAILQ_INIT(&ncp->c_originlist);
lf_lck_rw_init(&ncp->c_rwlock);
lf_cond_init(&ncp->c_cacsh_cond);
if (!skiplock)
{
(void) hfs_lock(ncp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
}
LIST_INSERT_HEAD(CNODEHASH(hfsmp, inum), ncp, c_hash);
hfs_chash_raise_OpenLookupCounter(ncp);
hfs_chash_unlock(hfsmp);
*vpp = NULL;
return (ncp);
}
void
hfs_chashwakeup(struct hfsmount *hfsmp, struct cnode *cp, int hflags)
{
hfs_chash_lock_spin(hfsmp);
CLR(cp->c_hflag, hflags);
if (ISSET(cp->c_hflag, H_WAITING)) {
CLR(cp->c_hflag, H_WAITING);
}
hfs_chash_broadcast_and_unlock(hfsmp, cp);
}
void
hfs_chash_abort(struct hfsmount *hfsmp, struct cnode *cp)
{
hfs_chash_lock_spin(hfsmp);
LIST_REMOVE(cp, c_hash);
cp->c_hash.le_next = NULL;
cp->c_hash.le_prev = NULL;
CLR(cp->c_hflag, H_ATTACH | H_ALLOC);
if (ISSET(cp->c_hflag, H_WAITING))
{
CLR(cp->c_hflag, H_WAITING);
}
hfs_chash_broadcast_and_unlock(hfsmp, cp);
}
struct vnode *
hfs_chash_getvnode(struct hfsmount *hfsmp, ino_t inum, int wantrsrc, int skiplock, int allow_deleted)
{
struct cnode *cp;
struct vnode *vp;
loop:
hfs_chash_lock_spin(hfsmp);
for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next) {
if (cp->c_fileid != inum)
continue;
if (ISSET(cp->c_hflag, H_ALLOC | H_TRANSIT | H_ATTACH)) {
SET(cp->c_hflag, H_WAITING);
hfs_chash_wait_and_unlock(hfsmp,cp);
goto loop;
}
vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
if (vp == NULL)
{
goto exit;
}
if (!skiplock)
{
if (hfs_lock(cp, HFS_TRY_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS))
{
SET(cp->c_hflag, H_WAITING);
hfs_chash_broadcast_and_unlock(hfsmp, cp);
usleep(100);
goto loop;
}
}
vp = wantrsrc ? cp->c_rsrc_vp : cp->c_vp;
if (!allow_deleted) {
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
if (!skiplock) hfs_unlock(cp);
goto exit;
}
}
hfs_chash_raise_OpenLookupCounter(cp);
hfs_chash_broadcast_and_unlock(hfsmp, cp);
return (vp);
}
exit:
hfs_chash_unlock(hfsmp);
return (NULL);
}
int
hfs_chash_snoop(struct hfsmount *hfsmp, ino_t inum, int existence_only,
int (*callout)(const cnode_t *cp, void *), void * arg)
{
struct cnode *cp;
int result = ENOENT;
hfs_chash_lock(hfsmp);
for (cp = CNODEHASH(hfsmp, inum)->lh_first; cp; cp = cp->c_hash.le_next) {
if (cp->c_fileid != inum)
continue;
if (existence_only) {
result = 0;
break;
}
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
result = EACCES;
break;
}
if (!ISSET(cp->c_hflag, H_ALLOC | H_TRANSIT | H_ATTACH)) {
result = callout(cp, arg);
}
break;
}
hfs_chash_unlock(hfsmp);
return (result);
}
static
struct cnode *
hfs_chash_search_cnid(struct hfsmount *hfsmp, cnid_t cnid)
{
struct cnode *cp;
for (cp = CNODEHASH(hfsmp, cnid)->lh_first; cp; cp = cp->c_hash.le_next) {
if (cp->c_fileid == cnid) {
break;
}
}
if (cp && ISSET(cp->c_hflag, H_ALLOC | H_TRANSIT | H_ATTACH)) {
cp = NULL;
}
return cp;
}
int
hfs_chash_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
{
int retval = -1;
struct cnode *cp;
hfs_chash_lock_spin(hfsmp);
cp = hfs_chash_search_cnid(hfsmp, cnid);
if (cp) {
if (cp->c_attr.ca_recflags & kHFSHasChildLinkMask) {
retval = 0;
} else {
cp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
retval = 1;
}
}
hfs_chash_broadcast_and_unlock(hfsmp, cp);
return retval;
}
int
hfs_chashremove(struct hfsmount *hfsmp, struct cnode *cp)
{
hfs_chash_lock_spin(hfsmp);
if (ISSET(cp->c_hflag, H_ATTACH)) {
hfs_chash_broadcast_and_unlock(hfsmp, cp);
return (EBUSY);
}
if (cp->c_hash.le_next || cp->c_hash.le_prev) {
LIST_REMOVE(cp, c_hash);
cp->c_hash.le_next = NULL;
cp->c_hash.le_prev = NULL;
}
hfs_chash_broadcast_and_unlock(hfsmp, cp);
return (0);
}
void
hfs_chash_mark_in_transit(struct hfsmount *hfsmp, struct cnode *cp)
{
hfs_chash_lock_spin(hfsmp);
SET(cp->c_hflag, H_TRANSIT);
hfs_chash_broadcast_and_unlock(hfsmp, cp);
}