2 * Copyright (c) 2011-2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7 * by Daniel Flores (GSOC 2013 - mentored by Matthew Dillon, compression)
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * Per-node backend for kernel filesystem interface.
39 * This executes a VOP concurrently on multiple nodes, each node via its own
40 * thread, and competes to advance the original request. The original
41 * request is retired the moment all requirements are met, even if the
42 * operation is still in-progress on some nodes.
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/fcntl.h>
50 #include <sys/namei.h>
51 #include <sys/mount.h>
52 #include <sys/vnode.h>
53 #include <sys/mountctl.h>
54 #include <sys/dirent.h>
56 #include <sys/objcache.h>
57 #include <sys/event.h>
59 #include <vfs/fifofs/fifo.h>
64 * Determine if the specified directory is empty. Returns 0 on success.
66 * May return 0, ENOTDIR, or EAGAIN.
70 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex)
72 hammer2_chain_t *parent;
73 hammer2_chain_t *chain;
74 hammer2_key_t key_next;
80 chain = hammer2_chain_lookup_init(ochain, 0);
82 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
84 hammer2_chain_unlock(oparent);
85 inum = chain->bref.embed.dirent.inum;
87 error = hammer2_chain_inode_find(chain->pmp, inum,
91 hammer2_chain_unlock(parent);
92 hammer2_chain_drop(parent);
95 hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS);
96 if (ochain->parent != oparent) {
98 hammer2_chain_unlock(chain);
99 hammer2_chain_drop(chain);
107 * Determine if the directory is empty or not by checking its
108 * visible namespace (the area which contains directory entries).
113 chain = hammer2_chain_lookup(&parent, &key_next,
114 HAMMER2_DIRHASH_VISIBLE,
120 hammer2_chain_unlock(chain);
121 hammer2_chain_drop(chain);
125 hammer2_chain_lookup_done(parent);
131 * Backend for hammer2_vfs_root()
133 * This is called when a newly mounted PFS has not yet synchronized
134 * to the inode_tid and modify_tid.
137 hammer2_xop_ipcluster(hammer2_thread_t *thr, hammer2_xop_t *arg)
139 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
140 hammer2_chain_t *chain;
143 chain = hammer2_inode_chain(xop->head.ip1, thr->clindex,
144 HAMMER2_RESOLVE_ALWAYS |
145 HAMMER2_RESOLVE_SHARED);
147 error = chain->error;
151 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
153 hammer2_chain_unlock(chain);
154 hammer2_chain_drop(chain);
159 * Backend for hammer2_vop_readdir()
162 hammer2_xop_readdir(hammer2_thread_t *thr, hammer2_xop_t *arg)
164 hammer2_xop_readdir_t *xop = &arg->xop_readdir;
165 hammer2_chain_t *parent;
166 hammer2_chain_t *chain;
167 hammer2_key_t key_next;
169 int cache_index = -1;
173 if (hammer2_debug & 0x0020)
174 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
177 * The inode's chain is the iterator. If we cannot acquire it our
178 * contribution ends here.
180 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
181 HAMMER2_RESOLVE_ALWAYS |
182 HAMMER2_RESOLVE_SHARED);
183 if (parent == NULL) {
184 kprintf("xop_readdir: NULL parent\n");
189 * Directory scan [re]start and loop, the feed inherits the chain's
190 * lock so do not unlock it on the iteration.
192 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
193 &cache_index, HAMMER2_LOOKUP_SHARED);
195 chain = hammer2_chain_lookup(&parent, &key_next,
196 lkey, HAMMER2_KEY_MAX,
198 HAMMER2_LOOKUP_SHARED);
201 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
204 chain = hammer2_chain_next(&parent, chain, &key_next,
205 key_next, HAMMER2_KEY_MAX,
207 HAMMER2_LOOKUP_SHARED);
210 hammer2_chain_unlock(chain);
211 hammer2_chain_drop(chain);
213 hammer2_chain_unlock(parent);
214 hammer2_chain_drop(parent);
216 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
220 * Backend for hammer2_vop_nresolve()
223 hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg)
225 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
226 hammer2_chain_t *parent;
227 hammer2_chain_t *chain;
230 hammer2_key_t key_next;
232 int cache_index = -1; /* XXX */
235 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
236 HAMMER2_RESOLVE_ALWAYS |
237 HAMMER2_RESOLVE_SHARED);
238 if (parent == NULL) {
239 kprintf("xop_nresolve: NULL parent\n");
244 name = xop->head.name1;
245 name_len = xop->head.name1_len;
248 * Lookup the directory entry
250 lhc = hammer2_dirhash(name, name_len);
251 chain = hammer2_chain_lookup(&parent, &key_next,
252 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
254 HAMMER2_LOOKUP_ALWAYS |
255 HAMMER2_LOOKUP_SHARED);
257 if (hammer2_chain_dirent_test(chain, name, name_len))
259 chain = hammer2_chain_next(&parent, chain, &key_next,
261 lhc + HAMMER2_DIRHASH_LOMASK,
263 HAMMER2_LOOKUP_ALWAYS |
264 HAMMER2_LOOKUP_SHARED);
268 * If the entry is a hardlink pointer, resolve it.
272 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
273 lhc = chain->bref.embed.dirent.inum;
274 error = hammer2_chain_inode_find(chain->pmp,
277 HAMMER2_LOOKUP_SHARED,
283 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
285 hammer2_chain_unlock(chain);
286 hammer2_chain_drop(chain);
289 hammer2_chain_unlock(parent);
290 hammer2_chain_drop(parent);
295 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and
296 * backend for pfs_delete.
298 * This function locates and removes a directory entry, and will lookup
299 * and return the underlying inode. For directory entries the underlying
300 * inode is not removed. If the directory entry is the actual inode itself,
301 * it may be conditonally removed and returned.
303 * WARNING! Any target inode's nlinks may not be synchronized to the
304 * in-memory inode. The frontend's hammer2_inode_unlink_finisher()
305 * is responsible for the final disposition of the actual inode.
308 hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg)
310 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
311 hammer2_chain_t *parent;
312 hammer2_chain_t *chain;
315 hammer2_key_t key_next;
317 int cache_index = -1; /* XXX */
322 * Requires exclusive lock
324 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
325 HAMMER2_RESOLVE_ALWAYS);
327 if (parent == NULL) {
328 kprintf("xop_nresolve: NULL parent\n");
332 name = xop->head.name1;
333 name_len = xop->head.name1_len;
336 * Lookup the directory entry
338 lhc = hammer2_dirhash(name, name_len);
339 chain = hammer2_chain_lookup(&parent, &key_next,
340 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
342 HAMMER2_LOOKUP_ALWAYS);
344 if (hammer2_chain_dirent_test(chain, name, name_len))
346 chain = hammer2_chain_next(&parent, chain, &key_next,
348 lhc + HAMMER2_DIRHASH_LOMASK,
350 HAMMER2_LOOKUP_ALWAYS);
354 * The directory entry will either be a BREF_TYPE_DIRENT or a
355 * BREF_TYPE_INODE. We always permanently delete DIRENTs, but
356 * must go by xop->dopermanent for BREF_TYPE_INODE.
358 * Note that the target chain's nlinks may not be synchronized with
359 * the in-memory hammer2_inode_t structure, so we don't try to do
360 * anything fancy here. The frontend deals with nlinks
365 int dopermanent = xop->dopermanent & 1;
366 int doforce = xop->dopermanent & 2;
370 * If the directory entry is the actual inode then use its
371 * type for the directory typing tests, otherwise if it is
372 * a directory entry, pull the type field from the entry.
374 * Directory entries are always permanently deleted
375 * (because they aren't the actual inode).
377 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
378 type = chain->bref.embed.dirent.type;
379 dopermanent |= HAMMER2_DELETE_PERMANENT;
381 type = chain->data->ipdata.meta.type;
385 * Check directory typing and delete the entry. Note that
386 * nlinks adjustments are made on the real inode by the
387 * frontend, not here.
389 * Unfortunately, checkdirempty() may have to unlock (parent).
390 * If it no longer matches chain->parent after re-locking,
391 * EAGAIN is returned.
393 if (type == HAMMER2_OBJTYPE_DIRECTORY && doforce) {
395 * If doforce then execute the operation even if
396 * the directory is not empty.
398 error = chain->error;
399 hammer2_chain_delete(parent, chain,
400 xop->head.mtid, dopermanent);
401 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
402 (error = checkdirempty(parent, chain, thr->clindex)) != 0) {
404 * error may be EAGAIN or ENOTEMPTY
406 if (error == EAGAIN) {
407 hammer2_chain_unlock(chain);
408 hammer2_chain_drop(chain);
409 hammer2_chain_unlock(parent);
410 hammer2_chain_drop(parent);
413 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
416 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
421 * Delete the directory entry. chain might also
422 * be a directly-embedded inode.
424 error = chain->error;
425 hammer2_chain_delete(parent, chain,
426 xop->head.mtid, dopermanent);
431 * If chain is a directory entry we must resolve it. We do not try
432 * to manipulate the contents as it might not be synchronized with
433 * the frontend hammer2_inode_t, nor do we try to lookup the
434 * frontend hammer2_inode_t here (we are the backend!).
436 if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
439 lhc = chain->bref.embed.dirent.inum;
441 error2 = hammer2_chain_inode_find(chain->pmp, lhc,
445 kprintf("inode_find: %016jx %p failed\n",
447 error2 = 0; /* silently ignore */
454 * Return the inode target for further action. Typically used by
455 * hammer2_inode_unlink_finisher().
458 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
460 hammer2_chain_unlock(chain);
461 hammer2_chain_drop(chain);
465 hammer2_chain_unlock(parent);
466 hammer2_chain_drop(parent);
472 * Backend for hammer2_vop_nrename()
474 * This handles the backend rename operation. Typically this renames
475 * directory entries but can also be used to rename embedded inodes.
477 * NOTE! The frontend is responsible for updating the inode meta-data in
478 * the file being renamed and for decrementing the target-replaced
479 * inode's nlinks, if present.
482 hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg)
484 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
486 hammer2_chain_t *parent;
487 hammer2_chain_t *chain;
488 hammer2_chain_t *tmp;
490 hammer2_key_t key_next;
491 int cache_index = -1;
495 * We need the precise parent chain to issue the deletion.
497 * If this is a directory entry we must locate the underlying
498 * inode. If it is an embedded inode we can act directly on it.
504 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
506 * Find ip's direct parent chain.
508 chain = hammer2_inode_chain(ip, thr->clindex,
509 HAMMER2_RESOLVE_ALWAYS);
515 parent = hammer2_chain_getparent(chain, HAMMER2_RESOLVE_ALWAYS);
516 if (parent == NULL) {
522 * The directory entry for the head.ip1 inode
523 * is in fdip, do a namespace search.
529 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
530 HAMMER2_RESOLVE_ALWAYS);
531 if (parent == NULL) {
532 kprintf("xop_nrename: NULL parent\n");
536 name = xop->head.name1;
537 name_len = xop->head.name1_len;
540 * Lookup the directory entry
542 lhc = hammer2_dirhash(name, name_len);
543 chain = hammer2_chain_lookup(&parent, &key_next,
544 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
546 HAMMER2_LOOKUP_ALWAYS);
548 if (hammer2_chain_dirent_test(chain, name, name_len))
550 chain = hammer2_chain_next(&parent, chain, &key_next,
552 lhc + HAMMER2_DIRHASH_LOMASK,
554 HAMMER2_LOOKUP_ALWAYS);
559 /* XXX shouldn't happen, but does under fsstress */
560 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
568 * Delete it, then create it in the new namespace.
570 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
571 hammer2_chain_unlock(parent);
572 hammer2_chain_drop(parent);
573 parent = NULL; /* safety */
576 * Adjust fields in the deleted chain appropriate for the rename
579 * NOTE! For embedded inodes, the frontend will officially replicate
580 * the field adjustments, but we also do it here to maintain
581 * consistency in case of a crash.
583 if (chain->bref.key != xop->lhc ||
584 xop->head.name1_len != xop->head.name2_len ||
585 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
586 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
587 hammer2_inode_data_t *wipdata;
589 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
590 wipdata = &chain->data->ipdata;
592 bzero(wipdata->filename, sizeof(wipdata->filename));
593 bcopy(xop->head.name2, wipdata->filename,
594 xop->head.name2_len);
595 wipdata->meta.name_key = xop->lhc;
596 wipdata->meta.name_len = xop->head.name2_len;
598 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
599 if (xop->head.name2_len <= sizeof(chain->bref.check.buf)) {
601 * Remove any related data buffer, we can
602 * embed the filename in the bref itself.
604 hammer2_chain_resize(chain, xop->head.mtid, 0,
606 hammer2_chain_modify(chain, xop->head.mtid,
608 bzero(chain->bref.check.buf,
609 sizeof(chain->bref.check.buf));
610 bcopy(xop->head.name2, chain->bref.check.buf,
611 xop->head.name2_len);
614 * Associate a data buffer with the bref.
615 * Zero it for consistency. Note that the
616 * data buffer is not 64KB so use chain->bytes
617 * instead of sizeof().
619 hammer2_chain_resize(chain, xop->head.mtid, 0,
620 hammer2_getradix(HAMMER2_ALLOC_MIN), 0);
621 hammer2_chain_modify(chain, xop->head.mtid,
623 bzero(chain->data->buf, chain->bytes);
624 bcopy(xop->head.name2,
626 xop->head.name2_len);
628 chain->bref.embed.dirent.namlen = xop->head.name2_len;
633 * The frontend will replicate this operation and is the real final
634 * authority, but adjust the inode's iparent field too if the inode
635 * is embedded in the directory.
637 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
638 chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
639 hammer2_inode_data_t *wipdata;
641 hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
642 wipdata = &chain->data->ipdata;
644 wipdata->meta.iparent = xop->head.ip3->meta.inum;
648 * Destroy any matching target(s) before creating the new entry.
649 * This will result in some ping-ponging of the directory key
650 * iterator but that is ok.
652 parent = hammer2_inode_chain(xop->head.ip3, thr->clindex,
653 HAMMER2_RESOLVE_ALWAYS);
654 if (parent == NULL) {
659 tmp = hammer2_chain_lookup(&parent, &key_next,
660 xop->lhc & ~HAMMER2_DIRHASH_LOMASK,
661 xop->lhc | HAMMER2_DIRHASH_LOMASK,
663 HAMMER2_LOOKUP_ALWAYS);
665 if (hammer2_chain_dirent_test(tmp, xop->head.name2,
666 xop->head.name2_len)) {
667 hammer2_chain_delete(parent, tmp, xop->head.mtid, 0);
669 tmp = hammer2_chain_next(&parent, tmp, &key_next,
671 xop->lhc | HAMMER2_DIRHASH_LOMASK,
673 HAMMER2_LOOKUP_ALWAYS);
677 * A relookup is required before the create to properly position
680 tmp = hammer2_chain_lookup(&parent, &key_next,
683 KKASSERT(tmp == NULL);
684 error = hammer2_chain_create(&parent, &chain,
685 pmp, HAMMER2_METH_DEFAULT,
687 HAMMER2_BREF_TYPE_INODE,
689 xop->head.mtid, 0, 0);
691 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
693 hammer2_chain_unlock(parent);
694 hammer2_chain_drop(parent);
697 hammer2_chain_unlock(chain);
698 hammer2_chain_drop(chain);
703 * Directory collision resolver scan helper (backend, threaded).
705 * Used by the inode create code to locate an unused lhc.
708 hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *arg)
710 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
711 hammer2_chain_t *parent;
712 hammer2_chain_t *chain;
713 hammer2_key_t key_next;
714 int cache_index = -1; /* XXX */
717 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
718 HAMMER2_RESOLVE_ALWAYS |
719 HAMMER2_RESOLVE_SHARED);
720 if (parent == NULL) {
721 kprintf("xop_nresolve: NULL parent\n");
728 * Lookup all possibly conflicting directory entries, the feed
729 * inherits the chain's lock so do not unlock it on the iteration.
731 chain = hammer2_chain_lookup(&parent, &key_next,
733 xop->lhc + HAMMER2_DIRHASH_LOMASK,
735 HAMMER2_LOOKUP_ALWAYS |
736 HAMMER2_LOOKUP_SHARED);
738 error = hammer2_xop_feed(&xop->head, chain, thr->clindex,
741 hammer2_chain_unlock(chain);
742 hammer2_chain_drop(chain);
743 chain = NULL; /* safety */
746 chain = hammer2_chain_next(&parent, chain, &key_next,
748 xop->lhc + HAMMER2_DIRHASH_LOMASK,
750 HAMMER2_LOOKUP_ALWAYS |
751 HAMMER2_LOOKUP_SHARED);
754 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
756 hammer2_chain_unlock(parent);
757 hammer2_chain_drop(parent);
762 * Generic lookup of a specific key.
764 * Used by the inode hidden directory code to find the hidden directory.
767 hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *arg)
769 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
770 hammer2_chain_t *parent;
771 hammer2_chain_t *chain;
772 hammer2_key_t key_next;
773 int cache_index = -1; /* XXX */
776 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
777 HAMMER2_RESOLVE_ALWAYS |
778 HAMMER2_RESOLVE_SHARED);
780 if (parent == NULL) {
786 * Lookup all possibly conflicting directory entries, the feed
787 * inherits the chain's lock so do not unlock it on the iteration.
789 chain = hammer2_chain_lookup(&parent, &key_next,
792 HAMMER2_LOOKUP_ALWAYS |
793 HAMMER2_LOOKUP_SHARED);
795 hammer2_xop_feed(&xop->head, chain, thr->clindex, chain->error);
797 hammer2_xop_feed(&xop->head, NULL, thr->clindex, ENOENT);
801 hammer2_chain_unlock(chain);
802 hammer2_chain_drop(chain);
805 hammer2_chain_unlock(parent);
806 hammer2_chain_drop(parent);
813 * WARNING! Fed chains must be locked shared so ownership can be transfered
814 * and to prevent frontend/backend stalls that would occur with an
815 * exclusive lock. The shared lock also allows chain->data to be
819 hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *arg)
821 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
822 hammer2_chain_t *parent;
823 hammer2_chain_t *chain;
824 hammer2_key_t key_next;
825 int cache_index = -1;
829 * Assert required flags.
831 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
832 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
835 * The inode's chain is the iterator. If we cannot acquire it our
836 * contribution ends here.
838 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
840 if (parent == NULL) {
841 kprintf("xop_readdir: NULL parent\n");
846 * Generic scan of exact records. Note that indirect blocks are
847 * automatically recursed and will not be returned.
849 chain = hammer2_chain_lookup(&parent, &key_next,
850 xop->key_beg, xop->key_end,
851 &cache_index, xop->lookup_flags);
853 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
856 chain = hammer2_chain_next(&parent, chain, &key_next,
857 key_next, xop->key_end,
858 &cache_index, xop->lookup_flags);
861 hammer2_chain_unlock(chain);
862 hammer2_chain_drop(chain);
864 hammer2_chain_unlock(parent);
865 hammer2_chain_drop(parent);
867 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);