/core/10.4/fusefs/fuse_nodehash.c

http://macfuse.googlecode.com/ · C · 1050 lines · 558 code · 217 blank · 275 comment · 178 complexity · d15dd9a04ec610a4d91a98dd2738606b MD5 · raw file

  1. /*
  2. * Copyright (C) 2006-2008 Google. All Rights Reserved.
  3. * Amit Singh <singh@>
  4. */
  5. /*
  6. * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
  7. *
  8. * This file contains Original Code and/or Modifications of Original Code as
  9. * defined in and that are subject to the Apple Public Source License Version
  10. * 2.0 (the 'License'). You may not use this file except in compliance with
  11. * the License. Please obtain a copy of the License at
  12. * http://www.opensource.apple.com/apsl/ and read it before using this file.
  13. *
  14. * The Original Code and all software distributed under the License are
  15. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  16. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  17. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
  18. * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
  19. * the License for the specific language governing rights and limitations
  20. * under the License.
  21. */
  22. #include "fuse.h"
  23. #include "fuse_nodehash.h"
  24. #include <fuse_param.h>
  25. #include <sys/vnode.h>
  26. #include <kern/assert.h>
  27. #if M_MACFUSE_ENABLE_UNSUPPORTED
  28. #define LCK_MTX_ASSERT lck_mtx_assert
  29. #else
  30. #define LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED) do { } while (0)
  31. #endif
  32. /*
  33. * The HNode structure represents an entry in the VFS plug-ins hash table.
  34. * See the comments in the header file for a detailed description of the
  35. * relationship between this data structure and the vnodes that are maintained
  36. * by VFS itself.
  37. *
  38. * The HNode and the FSNode are intimately tied together. In fact, they're
  39. * allocated from the same memory block. When we allocate an HNode, we allocate
  40. * extra space (gFSNodeSize bytes) for the FSNode.
  41. *
  42. * This data structure is effectively reference counted by the forkVNodesCount
  43. * field. When the last vnode that references this HNode is reclaimed, the
  44. * HNode itself is reclaimed (along with the associated FSNode).
  45. */
  46. struct HNode {
  47. /* [1] -> gMagic, that is, client supplied magic number */
  48. uint32_t magic;
  49. /* [2] next pointer for hash chain */
  50. LIST_ENTRY(HNode) hashLink;
  51. /* [1] device number on which file system object (fsobj) resides */
  52. fuse_device_t dev;
  53. /* [1] inode number of fsobj resides */
  54. uint64_t ino;
  55. /* [2] [3] */
  56. boolean_t attachOutstanding;
  57. /* [2] true if someone is waiting for attachOutstanding to go false */
  58. boolean_t waiting;
  59. /* [2] size of forkVNodes array, must be non-zero */
  60. size_t forkVNodesSize;
  61. /* [2] # of non-NULL vnodes in array (+1 if attachOutstanding is true) */
  62. size_t forkVNodesCount;
  63. /* [2] array of vnodes, indexed by forkIndex */
  64. vnode_t *forkVNodes;
  65. union {
  66. /* [2] if forkVNodesSize == 1, the vnode is stored internally */
  67. vnode_t internal;
  68. /*
  69. * [2] if forkVNodesSize > 1, the vnodes are stored in a separately
  70. * allocated array
  71. */
  72. vnode_t *external;
  73. } forkVNodesStorage;
  74. };
  75. typedef struct HNode HNode;
  76. /*
  77. * [HNode Notes]
  78. *
  79. * [1] This field is immutable. That is, it's set up as part of the process of
  80. * creating an HNode, and is not modified after that. Thus, it doesn't need
  81. * to be protected from concurrent access.
  82. *
  83. * [2] The gHashMutex lock protects this field in /all/ HNodes.
  84. *
  85. * [3] This is true if HNodeLookupCreatingIfNecessary has return success but
  86. * with a NULL vnode. In this case, we're expecting the client to call
  87. * either HNodeAttachVNodeSucceeded or HNodeAttachVNodeFailed at some time
  88. * in the future. While this is true, forkVNodesCount is incremented to
  89. * prevent the HNode from going away.
  90. */
  91. /*
  92. * The following client globals are set by the client when it calls HNodeInit.
  93. * See the header comments for HNodeInit for more details.
  94. */
  95. static uint32_t gMagic;
  96. static lck_grp_t *gLockGroup;
  97. static size_t gFSNodeSize;
  98. static OSMallocTag gOSMallocTag;
  99. /*
  100. *gHashMutex is a single mutex that protects all fields (except the immutable
  101. * ones) of all HNodes, the hash table itself (all elements of the gHashTable
  102. * array), and gHashNodeCount.
  103. */
  104. static lck_mtx_t *gHashMutex;
  105. /*
  106. * gHashNodeCount is a count of the number of HNodes in the hash table.
  107. * This is used solely for debugging (if it's non-zero when HNodeTerm is
  108. * called, the debug version of the code will panic).
  109. */
  110. static size_t gHashNodeCount;
  111. /*
  112. * gHashTable is a pointer to an array of HNodeHashHead structures that
  113. * represent the heads of all the hash chains. This structure, and the
  114. * associated gHashTableMask, are all a consequence of my use of hashinit.
  115. */
  116. static LIST_HEAD(HNodeHashHead, HNode) *gHashTable;
  117. typedef struct HNodeHashHead HNodeHashHead;
  118. static u_long gHashTableMask;
  119. /*
  120. * Given a device number and an inode number, return a pointer to the
  121. * hash chain head.
  122. */
  123. static HNodeHashHead *
  124. HNodeGetFirstFromHashTable(fuse_device_t dev, uint64_t ino)
  125. {
  126. return (HNodeHashHead *)&gHashTable[((uint64_t)(u_long)dev + ino) & gHashTableMask];
  127. }
  128. extern errno_t
  129. HNodeInit(lck_grp_t *lockGroup,
  130. lck_attr_t *lockAttr,
  131. OSMallocTag mallocTag,
  132. uint32_t magic,
  133. size_t fsNodeSize)
  134. {
  135. errno_t err;
  136. assert(lockGroup != NULL);
  137. // lockAttr may be NULL
  138. assert(mallocTag != NULL);
  139. assert(fsNodeSize != 0);
  140. gMagic = magic;
  141. gFSNodeSize = fsNodeSize;
  142. gOSMallocTag = mallocTag;
  143. gLockGroup = lockGroup;
  144. gHashMutex = lck_mtx_alloc_init(lockGroup, lockAttr);
  145. gHashTable = hashinit(desiredvnodes, M_TEMP, &gHashTableMask);
  146. err = 0;
  147. if ((gHashMutex == NULL) || (gHashTable == NULL)) {
  148. HNodeTerm(); /* Clean up any partial allocations */
  149. err = ENOMEM;
  150. }
  151. return err;
  152. }
  153. extern void
  154. HNodeTerm(void)
  155. {
  156. /*
  157. * Free the hash table. Also, if there are any hash nodes left, we
  158. * shouldn't be terminating.
  159. */
  160. if (gHashTable != NULL) {
  161. assert(gHashNodeCount == 0);
  162. #if MACH_ASSERT
  163. {
  164. u_long i;
  165. for (i = 0; i < (gHashTableMask + 1); i++) {
  166. assert(gHashTable[i].lh_first == NULL);
  167. }
  168. }
  169. #endif
  170. FREE(gHashTable, M_TEMP);
  171. gHashTable = NULL;
  172. }
  173. if (gHashMutex != NULL) {
  174. assert(gLockGroup != NULL);
  175. lck_mtx_free(gHashMutex, gLockGroup);
  176. gHashMutex = NULL;
  177. }
  178. gLockGroup = NULL;
  179. gOSMallocTag = NULL;
  180. gFSNodeSize = 0;
  181. gMagic = 0;
  182. }
  183. extern void *
  184. FSNodeGenericFromHNode(HNodeRef hnode)
  185. {
  186. assert(hnode != NULL);
  187. assert(hnode->magic == gMagic);
  188. return (void *) &hnode[1];
  189. }
  190. extern HNodeRef
  191. HNodeFromFSNodeGeneric(void *fsNode)
  192. {
  193. assert(fsNode != NULL);
  194. return &((HNodeRef) fsNode)[-1];
  195. }
  196. extern HNodeRef
  197. HNodeFromVNode(vnode_t vn)
  198. {
  199. HNodeRef hnode;
  200. assert(vn != NULL);
  201. hnode = vnode_fsnode(vn);
  202. assert(hnode != NULL);
  203. assert(hnode->magic = gMagic);
  204. return hnode;
  205. }
  206. extern void *
  207. FSNodeGenericFromVNode(vnode_t vn)
  208. {
  209. assert(vn != NULL);
  210. return FSNodeGenericFromHNode(HNodeFromVNode(vn));
  211. }
  212. extern fuse_device_t
  213. HNodeGetDevice(HNodeRef hnode)
  214. {
  215. assert(hnode != NULL);
  216. assert(hnode->magic == gMagic);
  217. return hnode->dev;
  218. }
  219. extern uint64_t
  220. HNodeGetInodeNumber(HNodeRef hnode)
  221. {
  222. assert(hnode != NULL);
  223. assert(hnode->magic == gMagic);
  224. return hnode->ino;
  225. }
  226. extern vnode_t
  227. HNodeGetVNodeForForkAtIndex(HNodeRef hnode, __unused size_t forkIndex)
  228. {
  229. vnode_t vn;
  230. assert(hnode != NULL);
  231. assert(hnode->magic == gMagic);
  232. assert(forkIndex < hnode->forkVNodesSize);
  233. /*
  234. * Locking and unlocking gHashMutex /is/ needed, because another thread
  235. * might be swapping in an expanded forkVNodes array. Because of the
  236. * multi-threaded nature of the kernel, no amount of clever ordering of
  237. * this swap can prevent the possibility of us seeing inconsistent data.
  238. */
  239. lck_mtx_lock(gHashMutex);
  240. vn = hnode->forkVNodes[forkIndex];
  241. lck_mtx_unlock(gHashMutex);
  242. return vn;
  243. }
  244. /*
  245. * The fact that the caller must hold some sort of reference on the vnode
  246. * prevents the vnode from being reclaimed, which means that we're
  247. * guaranteed to find the vnode in the fork array.
  248. */
  249. extern size_t
  250. HNodeGetForkIndexForVNode(vnode_t vn)
  251. {
  252. HNodeRef hnode;
  253. size_t forkCount;
  254. size_t forkIndex;
  255. assert(vn != NULL);
  256. /* HNodeFromVNode asserts the validity of its result */
  257. hnode = HNodeFromVNode(vn);
  258. /*
  259. * Locking and unlocking gHashMutex is needed, because another thread might
  260. * be switching in an expanded forkVNodes array.
  261. */
  262. lck_mtx_lock(gHashMutex);
  263. forkCount = hnode->forkVNodesSize;
  264. for (forkIndex = 0; forkIndex < forkCount; forkIndex++) {
  265. if (vn == hnode->forkVNodes[forkIndex]) {
  266. break;
  267. }
  268. }
  269. /* That is, that vn is in forkVNodes */
  270. assert(forkIndex != forkCount);
  271. lck_mtx_unlock(gHashMutex);
  272. return forkIndex;
  273. }
  274. extern void
  275. HNodeExchangeFromFSNode(void *fsnode1, void *fsnode2)
  276. {
  277. struct HNode tmpHNode;
  278. lck_mtx_lock(gHashMutex);
  279. HNodeRef hnode1 = HNodeFromFSNodeGeneric(fsnode1);
  280. HNodeRef hnode2 = HNodeFromFSNodeGeneric(fsnode2);
  281. memcpy(&tmpHNode, hnode1, sizeof(struct HNode));
  282. memcpy(hnode1, hnode2, sizeof(struct HNode));
  283. memcpy(hnode2, &tmpHNode, sizeof(struct HNode));
  284. LIST_REMOVE(hnode1, hashLink);
  285. LIST_REMOVE(hnode2, hashLink);
  286. LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(hnode1->dev, hnode1->ino),
  287. hnode1, hashLink);
  288. LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(hnode2->dev, hnode2->ino),
  289. hnode2, hashLink);
  290. lck_mtx_unlock(gHashMutex);
  291. }
  292. extern errno_t
  293. HNodeLookupCreatingIfNecessary(fuse_device_t dev,
  294. uint64_t ino,
  295. size_t forkIndex,
  296. HNodeRef *hnodePtr,
  297. vnode_t *vnPtr)
  298. {
  299. errno_t err;
  300. HNodeRef thisNode;
  301. HNodeRef newNode;
  302. vnode_t *newForkBuffer;
  303. boolean_t needsUnlock;
  304. vnode_t resultVN;
  305. uint32_t vid;
  306. assert( hnodePtr != NULL);
  307. assert(*hnodePtr == NULL);
  308. assert( vnPtr != NULL);
  309. assert(*vnPtr == NULL);
  310. /*
  311. * If you forget to call HNodeInit, it's likely that the first call
  312. * you'll make is HNodeLookupCreatingIfNecessary (to set up your root
  313. * vnode), and this assert will fire (rather than you dying inside wit
  314. * a memory access exception inside lck_mtx_lock).
  315. */
  316. assert(gHashMutex != NULL);
  317. newNode = NULL;
  318. newForkBuffer = NULL;
  319. needsUnlock = TRUE;
  320. resultVN = NULL;
  321. lck_mtx_lock(gHashMutex);
  322. do {
  323. LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
  324. err = EAGAIN;
  325. /* First look it up in the hash table. */
  326. thisNode = LIST_FIRST(HNodeGetFirstFromHashTable(dev, ino));
  327. while (thisNode != NULL) {
  328. assert(thisNode->magic == gMagic);
  329. if ((thisNode->dev == dev) && (thisNode->ino == ino)) {
  330. break;
  331. }
  332. thisNode = LIST_NEXT(thisNode, hashLink);
  333. }
  334. /*
  335. * If we didn't find it, we're creating a new HNode. If we haven't
  336. * already allocated newNode, we must do so. This drops the mutex,
  337. * so the hash table might have been changed by someone else, so we
  338. * have to loop.
  339. */
  340. /* If we do have a newNode at hand, use that. */
  341. if (thisNode == NULL) {
  342. if (newNode == NULL) {
  343. lck_mtx_unlock(gHashMutex);
  344. /* Allocate a new node. */
  345. newNode = FUSE_OSMalloc(sizeof(*newNode) + gFSNodeSize,
  346. gOSMallocTag);
  347. if (newNode == NULL) {
  348. err = ENOMEM;
  349. } else {
  350. /* Fill it in. */
  351. memset(newNode, 0, sizeof(*newNode) + gFSNodeSize);
  352. newNode->magic = gMagic;
  353. newNode->dev = dev;
  354. newNode->ino = ino;
  355. /*
  356. * If we're dealing with the first fork, use the internal
  357. * buffer. Otherwise allocate an external buffer.
  358. */
  359. if (forkIndex == 0) {
  360. newNode->forkVNodesSize = 1;
  361. newNode->forkVNodes = &newNode->forkVNodesStorage.internal;
  362. newNode->forkVNodesStorage.internal = NULL;
  363. } else {
  364. newNode->forkVNodesSize = forkIndex + 1;
  365. newNode->forkVNodesStorage.external = FUSE_OSMalloc(sizeof(*newNode->forkVNodesStorage.external) * (forkIndex + 1), gOSMallocTag);
  366. newNode->forkVNodes = newNode->forkVNodesStorage.external;
  367. if (newNode->forkVNodesStorage.external == NULL) {
  368. /*
  369. * If this allocation fails, we don't have to clean
  370. * up newNode, because we'll fall out of the loop
  371. * and newNode will get cleaned up at the end.
  372. */
  373. err = ENOMEM;
  374. } else {
  375. memset(newNode->forkVNodesStorage.external, 0, sizeof(*newNode->forkVNodesStorage.external) * (forkIndex + 1));
  376. }
  377. }
  378. }
  379. lck_mtx_lock(gHashMutex);
  380. } else {
  381. LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(dev, ino),
  382. newNode, hashLink);
  383. gHashNodeCount += 1;
  384. /*
  385. * Set thisNode to the node that we inserted, and clear
  386. * newNode so it doesn't get freed.
  387. */
  388. thisNode = newNode;
  389. newNode = NULL;
  390. /*
  391. * IMPORTANT:
  392. * There's a /really/ subtle point here. Once we've inserted
  393. * the new node into the hash table, it can be discovered by
  394. * other threads. This would be bad, because it's only
  395. * partially constructed at this point. We prevent this
  396. * problem by not dropping gHashMutex from this point to
  397. * the point that we're done. This only works because we
  398. * allocate the new node with a fork buffer that's adequate
  399. * to meet our needs.
  400. */
  401. }
  402. }
  403. /*
  404. * If we found a hash node (including the case where we've used one
  405. * that we previously allocated), check its status.
  406. */
  407. if (thisNode != NULL) {
  408. if (thisNode->attachOutstanding) {
  409. /*
  410. * If there are outstanding attaches, wait for them to
  411. * complete. This means that there can be only one outstanding
  412. * attach at a time, which is important because we don't want
  413. * two threads trying to fill in the same fork's vnode entry.
  414. *
  415. * In theory we might keep an array of outstanding attach
  416. * flags, one for each fork, but that's difficult and probably
  417. * overkill.
  418. */
  419. thisNode->waiting = TRUE;
  420. (void)fuse_msleep(thisNode, gHashMutex, PINOD,
  421. "HNodeLookupCreatingIfNecessary", NULL);
  422. /*
  423. * msleep drops and reacquires the mutex; the hash table may
  424. * have changed, so we loop.
  425. */
  426. } else if (forkIndex >= thisNode->forkVNodesSize) {
  427. /*
  428. * If the fork vnode array (a buffer described by
  429. * thisNode->forkVNodes and thisNode->forkVNodesSize) is too
  430. * small, install a new buffer, big enough to hold the vnode
  431. * fork forkIndex'th fork.
  432. */
  433. if (newForkBuffer == NULL) {
  434. /*
  435. * If we don't already have a new fork buffer, allocate
  436. * one. Because this drops the mutex, we have to loop and
  437. * start again from scratch.
  438. */
  439. lck_mtx_unlock(gHashMutex);
  440. newForkBuffer = FUSE_OSMalloc(sizeof(*newForkBuffer) * (forkIndex + 1), gOSMallocTag);
  441. if (newForkBuffer == NULL) {
  442. err = ENOMEM;
  443. } else {
  444. memset(newForkBuffer, 0, sizeof(*newForkBuffer) * (forkIndex + 1));
  445. }
  446. lck_mtx_lock(gHashMutex);
  447. } else {
  448. /*
  449. * Insert the newForkBuffer into theNode. This only works
  450. * because readers of the thisNode->forkVNodes array
  451. * (notably this routine and HNodeGetVNodeForForkAtIndex)
  452. * always take gHashMutex. If that wasn't the case, you
  453. * could get into some subtle race conditions as thread A
  454. * brings a copy of thisNode->forkVNodes into a register
  455. * and then gets blocked, then thread B runs and expands
  456. * the array and frees the buffer that is being pointed
  457. * to be thread A's register.
  458. */
  459. vnode_t *oldForkBuffer;
  460. size_t oldForkBufferSize;
  461. oldForkBufferSize = 0; /* Quieten a false warning */
  462. /*
  463. * We only free the old fork buffer if it was external,
  464. * rather than the single vnode buffer embedded in the HNode
  465. */
  466. oldForkBuffer = NULL;
  467. if (thisNode->forkVNodesSize > 1) {
  468. oldForkBuffer = thisNode->forkVNodesStorage.external;
  469. oldForkBufferSize = thisNode->forkVNodesSize;
  470. }
  471. memcpy(newForkBuffer, thisNode->forkVNodes,
  472. sizeof(*thisNode->forkVNodes) * thisNode->forkVNodesSize);
  473. thisNode->forkVNodes = newForkBuffer;
  474. thisNode->forkVNodesSize = (forkIndex + 1);
  475. thisNode->forkVNodesStorage.external = newForkBuffer;
  476. /* So we don't free it at the end */
  477. newForkBuffer = NULL;
  478. /*
  479. * Drop the mutex around the free, and then start
  480. * again from scratch.
  481. */
  482. lck_mtx_unlock(gHashMutex);
  483. if (oldForkBuffer != NULL) {
  484. FUSE_OSFree(oldForkBuffer,
  485. sizeof(*oldForkBuffer) * oldForkBufferSize,
  486. gOSMallocTag);
  487. }
  488. lck_mtx_lock(gHashMutex);
  489. }
  490. } else if (thisNode->forkVNodes[forkIndex] == NULL) {
  491. /*
  492. * If there's no existing vnode associated with this fork of
  493. * the HNode, we're done. The caller is responsible for
  494. * attaching a vnode for this fork. Setting attachOutstanding
  495. * will block any other threads from using the HNode until the
  496. * caller is done attaching. Also, we artificially increment
  497. * the reference count to prevent the HNode from being freed
  498. * until the caller has finished with it.
  499. */
  500. thisNode->attachOutstanding = TRUE;
  501. thisNode->forkVNodesCount += 1;
  502. /* Results for the caller. */
  503. assert(thisNode != NULL);
  504. assert(resultVN == NULL);
  505. err = 0;
  506. } else {
  507. vnode_t candidateVN;
  508. /*
  509. * If there is an existing vnode, get a reference on it and
  510. * return that to the caller. This vnode reference prevents
  511. * the vnode from being reclaimed, which prevents the HNode
  512. * from being freed.
  513. */
  514. candidateVN = thisNode->forkVNodes[forkIndex];
  515. assert(candidateVN != NULL);
  516. /*
  517. * Check that our vnode hasn't been recycled. If this succeeds,
  518. * it acquires a reference on the vnode, which is the one we
  519. * return to our caller. We do this with gHashMutex unlocked
  520. * to avoid any deadlock concerns.
  521. */
  522. vid = vnode_vid(candidateVN);
  523. lck_mtx_unlock(gHashMutex);
  524. err = vnode_getwithvid(candidateVN, vid);
  525. if (err == 0) {
  526. /* All ok; return the HNode/vnode to the caller. */
  527. assert(thisNode != NULL);
  528. assert(resultVN == NULL);
  529. resultVN = candidateVN;
  530. needsUnlock = FALSE;
  531. } else {
  532. /* We're going to loop and retry, so relock the mutex. */
  533. lck_mtx_lock(gHashMutex);
  534. err = EAGAIN;
  535. }
  536. }
  537. }
  538. } while (err == EAGAIN);
  539. /* On success, pass results back to the caller. */
  540. if (err == 0) {
  541. *hnodePtr = thisNode;
  542. *vnPtr = resultVN;
  543. }
  544. /* Clean up. */
  545. if (needsUnlock) {
  546. lck_mtx_unlock(gHashMutex);
  547. }
  548. /* Free newForkBuffer if we allocated it but didn't use it. */
  549. if (newForkBuffer != NULL) {
  550. FUSE_OSFree(newForkBuffer, sizeof(*newForkBuffer) * (forkIndex + 1),
  551. gOSMallocTag);
  552. }
  553. /* Free newNode if we allocated it but didn't put it into the table. */
  554. if (newNode != NULL) {
  555. FUSE_OSFree(newNode, sizeof(*newNode) + gFSNodeSize, gOSMallocTag);
  556. }
  557. assert( (err == 0) == (*hnodePtr != NULL) );
  558. return err;
  559. }
  560. /*
  561. * An attach operate has completed. If there is someone waiting for
  562. * the HNode, wake them up.
  563. */
  564. static void
  565. HNodeAttachComplete(HNodeRef hnode)
  566. {
  567. assert(hnode != NULL);
  568. assert(hnode->magic == gMagic);
  569. LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
  570. assert(hnode->attachOutstanding);
  571. hnode->attachOutstanding = FALSE;
  572. if (hnode->waiting) {
  573. fuse_wakeup(hnode);
  574. hnode->waiting = FALSE;
  575. }
  576. }
  577. /*
  578. * Decrement the number of fork vnodes for this HNode. If it hits zero,
  579. * the HNode is gone and we remove it from the hash table and return
  580. * true indicating to our caller that they need to clean it up.
  581. */
  582. static boolean_t
  583. HNodeForkVNodeDecrement(HNodeRef hnode)
  584. {
  585. boolean_t scrubIt;
  586. assert(hnode != NULL);
  587. assert(hnode->magic == gMagic);
  588. LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
  589. scrubIt = FALSE;
  590. hnode->forkVNodesCount -= 1;
  591. assert(hnode->forkVNodesCount >= 0);
  592. if (hnode->forkVNodesCount == 0) {
  593. LIST_REMOVE(hnode, hashLink);
  594. /* We test for this case before decrementing it because it's unsigned */
  595. assert(gHashNodeCount > 0);
  596. gHashNodeCount -= 1;
  597. scrubIt = TRUE;
  598. }
  599. return scrubIt;
  600. }
  601. extern void
  602. HNodeAttachVNodeSucceeded(HNodeRef hnode, size_t forkIndex, vnode_t vn)
  603. {
  604. errno_t junk;
  605. lck_mtx_lock(gHashMutex);
  606. assert(hnode != NULL);
  607. assert(hnode->magic == gMagic);
  608. assert(forkIndex < hnode->forkVNodesSize);
  609. assert(vn != NULL);
  610. assert(vnode_fsnode(vn) == hnode);
  611. /*
  612. * If someone is waiting for the HNode, wake them up. They won't actually
  613. * start running until we drop gHashMutex.
  614. */
  615. HNodeAttachComplete(hnode);
  616. /* Record the vnode's association with this HNode. */
  617. assert(hnode->forkVNodes[forkIndex] == NULL);
  618. hnode->forkVNodes[forkIndex] = vn;
  619. junk = vnode_addfsref(vn);
  620. assert(junk == 0);
  621. lck_mtx_unlock(gHashMutex);
  622. }
  623. extern boolean_t
  624. HNodeAttachVNodeFailed(HNodeRef hnode, __unused size_t forkIndex)
  625. {
  626. boolean_t scrubIt;
  627. lck_mtx_lock(gHashMutex);
  628. assert(hnode != NULL);
  629. assert(hnode->magic == gMagic);
  630. assert(forkIndex < hnode->forkVNodesSize);
  631. /*
  632. * If someone is waiting for the HNode, wake them up. They won't actually
  633. * start running until we drop gHashMutex.
  634. */
  635. HNodeAttachComplete(hnode);
  636. /*
  637. * Decrement the number of fork vnodes referencing the HNode, freeing
  638. * the HNode if it hits zero.
  639. */
  640. scrubIt = HNodeForkVNodeDecrement(hnode);
  641. lck_mtx_unlock(gHashMutex);
  642. return scrubIt;
  643. }
  644. extern boolean_t
  645. HNodeDetachVNode(HNodeRef hnode, vnode_t vn)
  646. {
  647. errno_t junk;
  648. size_t forkIndex;
  649. boolean_t scrubIt;
  650. lck_mtx_lock(gHashMutex);
  651. assert(hnode != NULL);
  652. assert(hnode->magic == gMagic);
  653. assert(vn != NULL);
  654. /* Find the fork index for vn. */
  655. for (forkIndex = 0; forkIndex < hnode->forkVNodesSize; forkIndex++) {
  656. if (hnode->forkVNodes[forkIndex] == vn) {
  657. break;
  658. }
  659. }
  660. /* If this trips, vn isn't in the forkVNodes array */
  661. assert(forkIndex < hnode->forkVNodesSize);
  662. /* Disassociate the vnode with this fork of the HNode. */
  663. hnode->forkVNodes[forkIndex] = NULL;
  664. junk = vnode_removefsref(vn);
  665. assert(junk == 0);
  666. vnode_clearfsnode(vn);
  667. /*
  668. * Decrement the number of fork vnodes referencing the HNode,
  669. * freeing the HNode if it hits zero.
  670. */
  671. scrubIt = HNodeForkVNodeDecrement(hnode);
  672. lck_mtx_unlock(gHashMutex);
  673. return scrubIt;
  674. }
  675. extern void
  676. HNodeScrubDone(HNodeRef hnode)
  677. {
  678. assert(hnode != NULL);
  679. assert(hnode->magic == gMagic);
  680. if (hnode->forkVNodesSize > 1) {
  681. FUSE_OSFree(hnode->forkVNodesStorage.external,
  682. sizeof(*hnode->forkVNodesStorage.external) * hnode->forkVNodesSize,
  683. gOSMallocTag);
  684. }
  685. /*
  686. * If anyone is waiting on this HNode, that would be bad.
  687. * It would be easy to fix this (we could wake them up at this
  688. * point) but, as I don't think it can actually happen, I'd rather
  689. * have this assert tell me whether the code is necessary than
  690. * just add it blindly.
  691. */
  692. assert(!hnode->waiting );
  693. FUSE_OSFree(hnode, sizeof(*hnode) + gFSNodeSize, gOSMallocTag);
  694. }
  695. /*
  696. * There's a significant thread safety bug here. Specifically, the forkVNodes
  697. * array is not included as part of the snapshot. If we switch the HNode's
  698. * forkVNodes array between the point where we take the snapshot at the point
  699. * where we print it, bad things will happen.
  700. */
  701. extern void
  702. HNodePrintState(void)
  703. {
  704. int err;
  705. size_t nodeCount;
  706. size_t nodeIndex;
  707. HNode *nodes;
  708. u_long hashBucketIndex;
  709. /* Take a snapshot. */
  710. do {
  711. err = 0;
  712. nodeCount = gHashNodeCount;
  713. nodes = FUSE_OSMalloc(sizeof(*nodes) * nodeCount, gOSMallocTag);
  714. if (nodes == NULL) {
  715. err = ENOMEM;
  716. }
  717. if (err == 0) {
  718. lck_mtx_lock(gHashMutex);
  719. if (gHashNodeCount > nodeCount) {
  720. /* Whoops, it changed size, let's try again. */
  721. FUSE_OSFree(nodes, sizeof(*nodes) * nodeCount, gOSMallocTag);
  722. err = EAGAIN;
  723. } else {
  724. nodeIndex = 0;
  725. for (hashBucketIndex = 0; hashBucketIndex < gHashTableMask;
  726. hashBucketIndex++) {
  727. HNode *thisNode;
  728. LIST_FOREACH(thisNode, &gHashTable[hashBucketIndex],
  729. hashLink) {
  730. assert(nodeIndex < nodeCount);
  731. nodes[nodeIndex] = *thisNode;
  732. nodeIndex += 1;
  733. }
  734. }
  735. assert(nodeIndex == nodeCount);
  736. }
  737. lck_mtx_unlock(gHashMutex);
  738. }
  739. } while (err == EAGAIN);
  740. assert( (err == 0) == (nodes != NULL) );
  741. /* Print the snapshot. */
  742. if (err == 0) {
  743. printf("HNodePrintState\n");
  744. for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
  745. HNode *thisNode;
  746. size_t forkIndex;
  747. thisNode = &nodes[nodeIndex];
  748. printf("{%p.%lld %c%c ", thisNode->dev, thisNode->ino,
  749. " A"[thisNode->attachOutstanding], " W"[thisNode->waiting]);
  750. for (forkIndex = 0; forkIndex < thisNode->forkVNodesSize;
  751. forkIndex++) {
  752. if (forkIndex > 0) {
  753. printf(" ");
  754. }
  755. printf("%p", thisNode->forkVNodes[forkIndex]);
  756. }
  757. printf("}");
  758. if (nodes[nodeIndex].hashLink.le_next == NULL) {
  759. printf("\n");
  760. }
  761. }
  762. }
  763. if (nodes != NULL) {
  764. FUSE_OSFree(nodes, sizeof(*nodes) * nodeCount, gOSMallocTag);
  765. }
  766. }
  767. /*
  768. * Device Kill
  769. */
  770. extern errno_t
  771. HNodeLookupRealQuickIfExists(fuse_device_t dev,
  772. uint64_t ino,
  773. size_t forkIndex,
  774. HNodeRef *hnodePtr,
  775. vnode_t *vnPtr)
  776. {
  777. errno_t err = EAGAIN;
  778. HNodeRef thisNode;
  779. boolean_t needsUnlock;
  780. vnode_t resultVN;
  781. uint32_t vid;
  782. assert(hnodePtr != NULL);
  783. assert(*hnodePtr == NULL);
  784. assert(vnPtr != NULL);
  785. assert(*vnPtr == NULL);
  786. assert(gHashMutex != NULL);
  787. needsUnlock = TRUE;
  788. resultVN = NULL;
  789. lck_mtx_lock(gHashMutex);
  790. LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
  791. thisNode = LIST_FIRST(HNodeGetFirstFromHashTable(dev, ino));
  792. while (thisNode != NULL) {
  793. assert(thisNode->magic == gMagic);
  794. if ((thisNode->dev == dev) && (thisNode->ino == ino)) {
  795. break;
  796. }
  797. thisNode = LIST_NEXT(thisNode, hashLink);
  798. }
  799. if (thisNode == NULL) {
  800. err = ENOENT;
  801. } else {
  802. if (thisNode->attachOutstanding) {
  803. err = EAGAIN;
  804. } else if (forkIndex >= thisNode->forkVNodesSize) {
  805. err = ENOENT;
  806. } else if (thisNode->forkVNodes[forkIndex] == NULL) {
  807. err = ENOENT;
  808. } else {
  809. vnode_t candidateVN = thisNode->forkVNodes[forkIndex];
  810. assert(candidateVN != NULL);
  811. vid = vnode_vid(candidateVN);
  812. lck_mtx_unlock(gHashMutex);
  813. err = vnode_getwithvid(candidateVN, vid);
  814. needsUnlock = FALSE;
  815. if (err == 0) {
  816. assert(thisNode != NULL);
  817. assert(resultVN == NULL);
  818. resultVN = candidateVN;
  819. } else {
  820. err = EAGAIN;
  821. }
  822. }
  823. }
  824. if (err == 0) {
  825. *hnodePtr = thisNode;
  826. *vnPtr = resultVN;
  827. }
  828. if (needsUnlock) {
  829. lck_mtx_unlock(gHashMutex);
  830. }
  831. assert((err == 0) == (*hnodePtr != NULL));
  832. return err;
  833. }