diff --git a/sys/vfs/ntfs/ntfs_ihash.c b/sys/vfs/ntfs/ntfs_ihash.c --- a/sys/vfs/ntfs/ntfs_ihash.c +++ b/sys/vfs/ntfs/ntfs_ihash.c @@ -44,6 +44,7 @@ #include "ntfs.h" #include "ntfs_inode.h" #include "ntfs_ihash.h" +#include "ntfs_subr.h" MALLOC_DEFINE(M_NTFSNTHASH, "NTFS nthash", "NTFS ntnode hash tables"); @@ -86,6 +87,12 @@ /* * Use the device/inum pair to find the incore inode, and return a pointer * to it. If it is in core, return it, even if it is locked. + * + * WARNING: the returned ntnode has NO reference and NO lock held. The + * hash token is released before the pointer is returned, so the ntnode + * may be concurrently freed by ntfs_ntput() on another CPU. Callers + * that intend to dereference the pointer MUST use ntfs_nthashget() + * instead, which takes a reference under the token. */ struct ntnode * ntfs_nthashlookup(cdev_t dev, ino_t inum) @@ -103,6 +110,34 @@ } /* + * Look up an ntnode and return it with i_usecount incremented and i_lock + * held exclusively. The usecount reference and lock acquisition happen + * UNDER the hash token so the ntnode cannot be torn down between the + * lookup and the caller taking the reference. Mirrors ext2_ihashget(). + * + * If ntfs_ntget() blocks in LOCKMGR(&ip->i_lock), the lwkt token is + * yielded to other CPUs, but the already-incremented i_usecount keeps + * the ntnode alive until we resume and return. Returns NULL if not + * present in the hash. + */ +struct ntnode * +ntfs_nthashget(cdev_t dev, ino_t inum) +{ + struct ntnode *ip; + + lwkt_gettoken(&ntfs_nthash_slock); + for (ip = NTNOHASH(dev, inum)->lh_first; ip; ip = ip->i_hash.le_next) { + if (inum == ip->i_number && dev == ip->i_dev) { + ntfs_ntget(ip); + break; + } + } + lwkt_reltoken(&ntfs_nthash_slock); + + return (ip); +} + +/* * Insert the ntnode into the hash table. */ void @@ -110,7 +145,20 @@ { struct nthashhead *ipp; + KKASSERT((ip->i_flag & IN_HASHED) == 0); lwkt_gettoken(&ntfs_nthash_slock); +#ifdef DIAGNOSTIC + { + struct ntnode *iq; + + LIST_FOREACH(iq, NTNOHASH(ip->i_dev, ip->i_number), i_hash) { + if (iq->i_dev == ip->i_dev && + iq->i_number == ip->i_number) + panic("ntfs_nthashins: duplicate inode %ju", + (uintmax_t)ip->i_number); + } + } +#endif ipp = NTNOHASH(ip->i_dev, ip->i_number); LIST_INSERT_HEAD(ipp, ip, i_hash); ip->i_flag |= IN_HASHED; diff --git a/sys/vfs/ntfs/ntfs_subr.c b/sys/vfs/ntfs/ntfs_subr.c --- a/sys/vfs/ntfs/ntfs_subr.c +++ b/sys/vfs/ntfs/ntfs_subr.c @@ -366,8 +366,13 @@ dprintf(("ntfs_ntlookup: looking for ntnode %ju\n", (uintmax_t)ino)); do { - if ((ip = ntfs_nthashlookup(ntmp->ntm_dev, ino)) != NULL) { - ntfs_ntget(ip); + /* + * Use ntfs_nthashget() which takes a usecount reference and + * acquires i_lock UNDER the hash token, so the ntnode cannot + * be freed between the lookup and our taking the reference. + * The returned ntnode is already locked (i_lock held excl). + */ + if ((ip = ntfs_nthashget(ntmp->ntm_dev, ino)) != NULL) { dprintf(("ntfs_ntlookup: ntnode %ju: %p, usecount: %d\n", (uintmax_t)ino, ip, ip->i_usecount)); *ipp = ip;