#include <sys/param.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include "hfs.h"
#include "hfs_dbg.h"
LIST_HEAD(vhashhead, hfsnode) *vhashtbl;
u_long vhash;
#define HFSNODEHASH(device, nodeID) (&vhashtbl[((device) + (nodeID)) & vhash])
struct slock hfs_vhash_slock;
void
hfs_vhashinit()
{
vhashtbl = hashinit(desiredvnodes, M_HFSMNT, &vhash);
simple_lock_init(&hfs_vhash_slock);
}
struct vnode *
hfs_vhashget(dev, nodeID, forkType)
dev_t dev;
UInt32 nodeID;
UInt8 forkType;
{
struct proc *p = current_proc();
struct hfsnode *hp;
struct vnode *vp;
DBG_ASSERT(forkType!=kUndefinedFork);
loop:
simple_lock(&hfs_vhash_slock);
for (hp = HFSNODEHASH(dev, nodeID)->lh_first; hp; hp = hp->h_hash.le_next) {
if (hp->h_nodeflags & IN_ALLOCATING) {
simple_unlock(&hfs_vhash_slock);
tsleep((caddr_t)hp, PINOD, "hfs_vhashlookup", 0);
goto loop;
};
DBG_ASSERT(hp->h_meta != NULL);
if ((H_FILEID(hp) == nodeID) &&
(H_DEV(hp) == dev) &&
!(hp->h_meta->h_metaflags & IN_NOEXISTS)) {
if ((H_FORKTYPE(hp) == forkType) ||
(forkType == kAnyFork) ||
((forkType == kDefault) && ((H_FORKTYPE(hp) == kDirectory)
|| (H_FORKTYPE(hp) == kDataFork)))) {
vp = HTOV(hp);
simple_lock(&vp->v_interlock);
simple_unlock(&hfs_vhash_slock);
if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p))
goto loop;
return (vp);
};
};
};
simple_unlock(&hfs_vhash_slock);
return (NULL);
}
void
hfs_vhashins_sibling(dev, nodeID, hp, fm)
dev_t dev;
UInt32 nodeID;
struct hfsnode *hp;
struct hfsfilemeta **fm;
{
struct vhashhead *ipp;
struct hfsnode *thp;
struct hfsfilemeta *tfm;
DBG_ASSERT(fm != NULL);
DBG_ASSERT(hp != NULL);
DBG_ASSERT(hp->h_meta == NULL);
DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
tfm = NULL;
lockmgr(&hp->h_lock, LK_EXCLUSIVE, (struct slock *)0, current_proc());
ipp = HFSNODEHASH(dev, nodeID);
loop:
simple_lock(&hfs_vhash_slock);
for (thp = ipp->lh_first; thp; thp = thp->h_hash.le_next) {
if (thp->h_nodeflags & IN_ALLOCATING) {
simple_unlock(&hfs_vhash_slock);
tsleep((caddr_t)thp, PINOD, "hfs_vhash_ins_meta", 0);
goto loop;
};
DBG_ASSERT(thp->h_meta != NULL);
if ((H_FILEID(thp) == nodeID) && (H_DEV(thp) == dev)) {
tfm = hp->h_meta = thp->h_meta;
break;
};
};
if (tfm && (H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork)) {
DBG_ASSERT(tfm->h_siblinghead.cqh_first != NULL && tfm->h_siblinghead.cqh_last != NULL);
simple_lock(&tfm->h_siblinglock);
CIRCLEQ_INSERT_HEAD(&tfm->h_siblinghead, hp, h_sibling);
simple_unlock(&tfm->h_siblinglock);
};
LIST_INSERT_HEAD(ipp, hp, h_hash);
simple_unlock(&hfs_vhash_slock);
*fm = tfm;
}
void
hfs_vhashins(dev, nodeID, hp)
dev_t dev;
UInt32 nodeID;
struct hfsnode *hp;
{
struct vhashhead *ipp;
DBG_ASSERT(hp != NULL);
DBG_ASSERT(nodeID != 0);
lockmgr(&hp->h_lock, LK_EXCLUSIVE, (struct slock *)0, current_proc());
simple_lock(&hfs_vhash_slock);
ipp = HFSNODEHASH(dev, nodeID);
LIST_INSERT_HEAD(ipp, hp, h_hash);
simple_unlock(&hfs_vhash_slock);
}
void
hfs_vhashrem(hp)
struct hfsnode *hp;
{
DBG_ASSERT(hp != NULL);
DBG_ASSERT(hp->h_meta != NULL);
simple_lock(&hfs_vhash_slock);
if (hp->h_meta->h_siblinghead.cqh_first != NULL) {
simple_lock(&hp->h_meta->h_siblinglock);
CIRCLEQ_REMOVE(&hp->h_meta->h_siblinghead, hp, h_sibling);
simple_unlock(&hp->h_meta->h_siblinglock);
};
LIST_REMOVE(hp, h_hash);
#if HFS_DIAGNOSTIC
hp->h_hash.le_next = NULL;
hp->h_hash.le_prev = NULL;
#endif
simple_unlock(&hfs_vhash_slock);
}
void
hfs_vhashmove(hp, oldNodeID)
struct hfsnode *hp;
UInt32 oldNodeID;
{
struct vhashhead *oldHeadIndex, *newHeadIndex;
struct hfsnode *thp, *nextNode;
UInt32 newNodeID;
DBG_ASSERT(hp != NULL);
DBG_ASSERT(hp->h_meta != NULL);
newNodeID = H_FILEID(hp);
oldHeadIndex = HFSNODEHASH(H_DEV(hp), oldNodeID);
newHeadIndex = HFSNODEHASH(H_DEV(hp), newNodeID);
if (oldHeadIndex == newHeadIndex)
return;
loop:
simple_lock(&hfs_vhash_slock);
for (nextNode = oldHeadIndex->lh_first; nextNode; ) {
if (nextNode->h_nodeflags & IN_ALLOCATING) {
simple_unlock(&hfs_vhash_slock);
tsleep((caddr_t)nextNode, PINOD, "hfs_vhashmove", 0);
goto loop;
};
DBG_ASSERT(nextNode->h_meta != NULL);
thp = nextNode;
nextNode = nextNode->h_hash.le_next;
if (newNodeID == H_FILEID(thp)) {
LIST_REMOVE(thp, h_hash);
thp->h_hash.le_next = NULL;
thp->h_hash.le_next = NULL;
LIST_INSERT_HEAD(newHeadIndex, thp, h_hash);
};
};
simple_unlock(&hfs_vhash_slock);
}
#if HFS_DIAGNOSTIC
void hfs_vhash_dbg(hp)
struct hfsnode *hp;
{
struct proc *p = current_proc();
struct vnode *vp;
struct hfsnode *thp, *tthp;
int maxsiblings = 1;
int wasFound = false;
struct vhashhead *ipp, *jpp;
dev_t dev = H_DEV(hp);
UInt32 nodeID = H_FILEID(hp);
UInt8 forkType = H_FORKTYPE(hp);
u_long forksfound = 0;
if (forkType==kDataFork || forkType==kRsrcFork)
maxsiblings++;
if (hp == NULL)
DEBUG_BREAK_MSG(("hash_dgh: Null hfsnode"));
ipp = HFSNODEHASH(dev, nodeID);
loop:
simple_lock(&hfs_vhash_slock);
for (thp = ipp->lh_first; thp; thp = thp->h_hash.le_next) {
if (thp->h_nodeflags & IN_ALLOCATING) {
simple_unlock(&hfs_vhash_slock);
tsleep((caddr_t)thp, PINOD, "hfs_vhash_ins_meta", 0);
goto loop;
};
if (thp->h_meta == NULL)
DEBUG_BREAK_MSG(("hash_dgh: Null hfs_meta"));
jpp = (HFSNODEHASH(H_DEV(thp), H_FILEID(thp)));
if (ipp != jpp)
DEBUG_BREAK_MSG(("hash_dgh: Member on wrong hash"));
if ((H_FILEID(thp) == nodeID) && (H_DEV(thp) == dev)) {
maxsiblings--;
if (maxsiblings < 0)
DEBUG_BREAK_MSG(("hash_dgh: Too many siblings"));
if ((1<<H_FORKTYPE(thp)) & forksfound)
DEBUG_BREAK_MSG(("hash_dgh: Fork already found"));
forksfound |= (1<<H_FORKTYPE(thp));
if (H_FORKTYPE(thp) == forkType) {
if (wasFound == true)
DEBUG_BREAK_MSG(("hash_dgh: Already found"));
wasFound = true;
};
};
};
simple_unlock(&hfs_vhash_slock);
if (! wasFound)
DEBUG_BREAK_MSG(("hash_dgh: Not found"));
}
#endif