hammer2 - Implement error processing and free reserve enforcement
[dragonfly.git] / sys / vfs / hammer2 / hammer2_xops.c
blobe87ce002c589f0bbe4e275b204429e01cd481a15
1 /*
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
11 * are met:
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
18 * distribution.
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
34 * SUCH DAMAGE.
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>
48 #include <sys/buf.h>
49 #include <sys/proc.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>
55 #include <sys/uio.h>
56 #include <sys/objcache.h>
57 #include <sys/event.h>
58 #include <sys/file.h>
59 #include <vfs/fifofs/fifo.h>
61 #include "hammer2.h"
64 * Determine if the specified directory is empty. Returns 0 on success.
66 static
67 int
68 checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex)
70 hammer2_chain_t *parent;
71 hammer2_chain_t *chain;
72 hammer2_key_t key_next;
73 hammer2_key_t inum;
74 int error;
76 error = 0;
77 chain = hammer2_chain_lookup_init(ochain, 0);
79 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
80 if (oparent)
81 hammer2_chain_unlock(oparent);
82 inum = chain->bref.embed.dirent.inum;
83 parent = NULL;
84 error = hammer2_chain_inode_find(chain->pmp, inum,
85 clindex, 0,
86 &parent, &chain);
87 if (parent) {
88 hammer2_chain_unlock(parent);
89 hammer2_chain_drop(parent);
91 if (oparent) {
92 hammer2_chain_lock(oparent, HAMMER2_RESOLVE_ALWAYS);
93 if (ochain->parent != oparent) {
94 if (chain) {
95 hammer2_chain_unlock(chain);
96 hammer2_chain_drop(chain);
98 return HAMMER2_ERROR_EAGAIN;
104 * Determine if the directory is empty or not by checking its
105 * visible namespace (the area which contains directory entries).
107 parent = chain;
108 chain = NULL;
109 if (parent) {
110 chain = hammer2_chain_lookup(&parent, &key_next,
111 HAMMER2_DIRHASH_VISIBLE,
112 HAMMER2_KEY_MAX,
113 &error, 0);
115 if (chain) {
116 error = HAMMER2_ERROR_ENOTEMPTY;
117 hammer2_chain_unlock(chain);
118 hammer2_chain_drop(chain);
120 hammer2_chain_lookup_done(parent);
122 return error;
126 * Backend for hammer2_vfs_root()
128 * This is called when a newly mounted PFS has not yet synchronized
129 * to the inode_tid and modify_tid.
131 void
132 hammer2_xop_ipcluster(hammer2_thread_t *thr, hammer2_xop_t *arg)
134 hammer2_xop_ipcluster_t *xop = &arg->xop_ipcluster;
135 hammer2_chain_t *chain;
136 int error;
138 chain = hammer2_inode_chain(xop->head.ip1, thr->clindex,
139 HAMMER2_RESOLVE_ALWAYS |
140 HAMMER2_RESOLVE_SHARED);
141 if (chain)
142 error = chain->error;
143 else
144 error = HAMMER2_ERROR_EIO;
146 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
147 if (chain) {
148 hammer2_chain_unlock(chain);
149 hammer2_chain_drop(chain);
154 * Backend for hammer2_vop_readdir()
156 void
157 hammer2_xop_readdir(hammer2_thread_t *thr, hammer2_xop_t *arg)
159 hammer2_xop_readdir_t *xop = &arg->xop_readdir;
160 hammer2_chain_t *parent;
161 hammer2_chain_t *chain;
162 hammer2_key_t key_next;
163 hammer2_key_t lkey;
164 int error = 0;
166 lkey = xop->lkey;
167 if (hammer2_debug & 0x0020)
168 kprintf("xop_readdir %p lkey=%016jx\n", xop, lkey);
171 * The inode's chain is the iterator. If we cannot acquire it our
172 * contribution ends here.
174 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
175 HAMMER2_RESOLVE_ALWAYS |
176 HAMMER2_RESOLVE_SHARED);
177 if (parent == NULL) {
178 kprintf("xop_readdir: NULL parent\n");
179 goto done;
183 * Directory scan [re]start and loop, the feed inherits the chain's
184 * lock so do not unlock it on the iteration.
186 chain = hammer2_chain_lookup(&parent, &key_next, lkey, lkey,
187 &error, HAMMER2_LOOKUP_SHARED);
188 if (chain == NULL) {
189 chain = hammer2_chain_lookup(&parent, &key_next,
190 lkey, HAMMER2_KEY_MAX,
191 &error, HAMMER2_LOOKUP_SHARED);
193 while (chain) {
194 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
195 if (error)
196 goto break2;
197 chain = hammer2_chain_next(&parent, chain, &key_next,
198 key_next, HAMMER2_KEY_MAX,
199 &error, HAMMER2_LOOKUP_SHARED);
201 break2:
202 if (chain) {
203 hammer2_chain_unlock(chain);
204 hammer2_chain_drop(chain);
206 hammer2_chain_unlock(parent);
207 hammer2_chain_drop(parent);
208 done:
209 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
213 * Backend for hammer2_vop_nresolve()
215 void
216 hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg)
218 hammer2_xop_nresolve_t *xop = &arg->xop_nresolve;
219 hammer2_chain_t *parent;
220 hammer2_chain_t *chain;
221 const char *name;
222 size_t name_len;
223 hammer2_key_t key_next;
224 hammer2_key_t lhc;
225 int error;
227 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
228 HAMMER2_RESOLVE_ALWAYS |
229 HAMMER2_RESOLVE_SHARED);
230 if (parent == NULL) {
231 kprintf("xop_nresolve: NULL parent\n");
232 chain = NULL;
233 error = HAMMER2_ERROR_EIO;
234 goto done;
236 name = xop->head.name1;
237 name_len = xop->head.name1_len;
240 * Lookup the directory entry
242 lhc = hammer2_dirhash(name, name_len);
243 chain = hammer2_chain_lookup(&parent, &key_next,
244 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
245 &error,
246 HAMMER2_LOOKUP_ALWAYS |
247 HAMMER2_LOOKUP_SHARED);
248 while (chain) {
249 if (hammer2_chain_dirent_test(chain, name, name_len))
250 break;
251 chain = hammer2_chain_next(&parent, chain, &key_next,
252 key_next,
253 lhc + HAMMER2_DIRHASH_LOMASK,
254 &error,
255 HAMMER2_LOOKUP_ALWAYS |
256 HAMMER2_LOOKUP_SHARED);
260 * If the entry is a hardlink pointer, resolve it.
262 if (chain && chain->error == 0) {
263 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
264 lhc = chain->bref.embed.dirent.inum;
265 error = hammer2_chain_inode_find(chain->pmp,
266 lhc,
267 thr->clindex,
268 HAMMER2_LOOKUP_SHARED,
269 &parent,
270 &chain);
272 } else if (chain && error == 0) {
273 error = chain->error;
275 done:
276 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
277 if (chain) {
278 hammer2_chain_unlock(chain);
279 hammer2_chain_drop(chain);
281 if (parent) {
282 hammer2_chain_unlock(parent);
283 hammer2_chain_drop(parent);
288 * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), and
289 * backend for pfs_delete.
291 * This function locates and removes a directory entry, and will lookup
292 * and return the underlying inode. For directory entries the underlying
293 * inode is not removed. If the directory entry is the actual inode itself,
294 * it may be conditonally removed and returned.
296 * WARNING! Any target inode's nlinks may not be synchronized to the
297 * in-memory inode. The frontend's hammer2_inode_unlink_finisher()
298 * is responsible for the final disposition of the actual inode.
300 void
301 hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg)
303 hammer2_xop_unlink_t *xop = &arg->xop_unlink;
304 hammer2_chain_t *parent;
305 hammer2_chain_t *chain;
306 const char *name;
307 size_t name_len;
308 hammer2_key_t key_next;
309 hammer2_key_t lhc;
310 int error;
312 again:
314 * Requires exclusive lock
316 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
317 HAMMER2_RESOLVE_ALWAYS);
318 chain = NULL;
319 if (parent == NULL) {
320 kprintf("xop_nresolve: NULL parent\n");
321 error = HAMMER2_ERROR_EIO;
322 goto done;
324 name = xop->head.name1;
325 name_len = xop->head.name1_len;
328 * Lookup the directory entry
330 lhc = hammer2_dirhash(name, name_len);
331 chain = hammer2_chain_lookup(&parent, &key_next,
332 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
333 &error, HAMMER2_LOOKUP_ALWAYS);
334 while (chain) {
335 if (hammer2_chain_dirent_test(chain, name, name_len))
336 break;
337 chain = hammer2_chain_next(&parent, chain, &key_next,
338 key_next,
339 lhc + HAMMER2_DIRHASH_LOMASK,
340 &error, HAMMER2_LOOKUP_ALWAYS);
344 * The directory entry will either be a BREF_TYPE_DIRENT or a
345 * BREF_TYPE_INODE. We always permanently delete DIRENTs, but
346 * must go by xop->dopermanent for BREF_TYPE_INODE.
348 * Note that the target chain's nlinks may not be synchronized with
349 * the in-memory hammer2_inode_t structure, so we don't try to do
350 * anything fancy here. The frontend deals with nlinks
351 * synchronization.
353 if (chain && chain->error == 0) {
354 int dopermanent = xop->dopermanent & 1;
355 int doforce = xop->dopermanent & 2;
356 uint8_t type;
359 * If the directory entry is the actual inode then use its
360 * type for the directory typing tests, otherwise if it is
361 * a directory entry, pull the type field from the entry.
363 * Directory entries are always permanently deleted
364 * (because they aren't the actual inode).
366 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
367 type = chain->bref.embed.dirent.type;
368 dopermanent |= HAMMER2_DELETE_PERMANENT;
369 } else {
370 type = chain->data->ipdata.meta.type;
374 * Check directory typing and delete the entry. Note that
375 * nlinks adjustments are made on the real inode by the
376 * frontend, not here.
378 * Unfortunately, checkdirempty() may have to unlock (parent).
379 * If it no longer matches chain->parent after re-locking,
380 * EAGAIN is returned.
382 if (type == HAMMER2_OBJTYPE_DIRECTORY && doforce) {
384 * If doforce then execute the operation even if
385 * the directory is not empty or errored.
387 /* ignore chain->error */
388 error = hammer2_chain_delete(parent, chain,
389 xop->head.mtid, dopermanent);
390 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
391 (error = checkdirempty(parent, chain, thr->clindex)) != 0) {
393 * error may be EAGAIN or ENOTEMPTY
395 if (error == HAMMER2_ERROR_EAGAIN) {
396 hammer2_chain_unlock(chain);
397 hammer2_chain_drop(chain);
398 hammer2_chain_unlock(parent);
399 hammer2_chain_drop(parent);
400 goto again;
402 } else if (type == HAMMER2_OBJTYPE_DIRECTORY &&
403 xop->isdir == 0) {
404 error = HAMMER2_ERROR_ENOTDIR;
405 } else if (type != HAMMER2_OBJTYPE_DIRECTORY &&
406 xop->isdir >= 1) {
407 error = HAMMER2_ERROR_EISDIR;
408 } else {
410 * Delete the directory entry. chain might also
411 * be a directly-embedded inode.
413 error = chain->error;
414 hammer2_chain_delete(parent, chain,
415 xop->head.mtid, dopermanent);
417 } else {
418 if (chain && error == 0)
419 error = chain->error;
423 * If chain is a directory entry we must resolve it. We do not try
424 * to manipulate the contents as it might not be synchronized with
425 * the frontend hammer2_inode_t, nor do we try to lookup the
426 * frontend hammer2_inode_t here (we are the backend!).
428 if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
429 int error2;
431 lhc = chain->bref.embed.dirent.inum;
433 error2 = hammer2_chain_inode_find(chain->pmp, lhc,
434 thr->clindex, 0,
435 &parent, &chain);
436 if (error2) {
437 kprintf("inode_find: %016jx %p failed\n",
438 lhc, chain);
439 error2 = 0; /* silently ignore */
441 if (error == 0)
442 error = error2;
446 * Return the inode target for further action. Typically used by
447 * hammer2_inode_unlink_finisher().
449 done:
450 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
451 if (chain) {
452 hammer2_chain_unlock(chain);
453 hammer2_chain_drop(chain);
454 chain = NULL;
456 if (parent) {
457 hammer2_chain_unlock(parent);
458 hammer2_chain_drop(parent);
459 parent = NULL;
464 * Backend for hammer2_vop_nrename()
466 * This handles the backend rename operation. Typically this renames
467 * directory entries but can also be used to rename embedded inodes.
469 * NOTE! The frontend is responsible for updating the inode meta-data in
470 * the file being renamed and for decrementing the target-replaced
471 * inode's nlinks, if present.
473 void
474 hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg)
476 hammer2_xop_nrename_t *xop = &arg->xop_nrename;
477 hammer2_pfs_t *pmp;
478 hammer2_chain_t *parent;
479 hammer2_chain_t *chain;
480 hammer2_chain_t *tmp;
481 hammer2_inode_t *ip;
482 hammer2_key_t key_next;
483 int error;
486 * We need the precise parent chain to issue the deletion.
488 * If this is a directory entry we must locate the underlying
489 * inode. If it is an embedded inode we can act directly on it.
491 ip = xop->head.ip2;
492 pmp = ip->pmp;
493 chain = NULL;
494 error = 0;
496 if (xop->ip_key & HAMMER2_DIRHASH_VISIBLE) {
498 * Find ip's direct parent chain.
500 chain = hammer2_inode_chain(ip, thr->clindex,
501 HAMMER2_RESOLVE_ALWAYS);
502 if (chain == NULL) {
503 error = HAMMER2_ERROR_EIO;
504 parent = NULL;
505 goto done;
507 parent = hammer2_chain_getparent(chain, HAMMER2_RESOLVE_ALWAYS);
508 if (parent == NULL) {
509 error = HAMMER2_ERROR_EIO;
510 goto done;
512 } else {
514 * The directory entry for the head.ip1 inode
515 * is in fdip, do a namespace search.
517 hammer2_key_t lhc;
518 const char *name;
519 size_t name_len;
521 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
522 HAMMER2_RESOLVE_ALWAYS);
523 if (parent == NULL) {
524 kprintf("xop_nrename: NULL parent\n");
525 error = HAMMER2_ERROR_EIO;
526 goto done;
528 name = xop->head.name1;
529 name_len = xop->head.name1_len;
532 * Lookup the directory entry
534 lhc = hammer2_dirhash(name, name_len);
535 chain = hammer2_chain_lookup(&parent, &key_next,
536 lhc, lhc + HAMMER2_DIRHASH_LOMASK,
537 &error, HAMMER2_LOOKUP_ALWAYS);
538 while (chain) {
539 if (hammer2_chain_dirent_test(chain, name, name_len))
540 break;
541 chain = hammer2_chain_next(&parent, chain, &key_next,
542 key_next,
543 lhc + HAMMER2_DIRHASH_LOMASK,
544 &error,
545 HAMMER2_LOOKUP_ALWAYS);
549 if (chain == NULL) {
550 /* XXX shouldn't happen, but does under fsstress */
551 kprintf("hammer2_xop_rename: \"%s\" -> \"%s\" ENOENT\n",
552 xop->head.name1,
553 xop->head.name2);
554 if (error == 0)
555 error = HAMMER2_ERROR_ENOENT;
556 goto done;
559 if (chain->error) {
560 error = chain->error;
561 goto done;
565 * Delete it, then create it in the new namespace.
567 hammer2_chain_delete(parent, chain, xop->head.mtid, 0);
568 hammer2_chain_unlock(parent);
569 hammer2_chain_drop(parent);
570 parent = NULL; /* safety */
573 * Adjust fields in the deleted chain appropriate for the rename
574 * operation.
576 * NOTE! For embedded inodes, the frontend will officially replicate
577 * the field adjustments, but we also do it here to maintain
578 * consistency in case of a crash.
580 if (chain->bref.key != xop->lhc ||
581 xop->head.name1_len != xop->head.name2_len ||
582 bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) {
583 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) {
584 hammer2_inode_data_t *wipdata;
586 error = hammer2_chain_modify(chain, xop->head.mtid,
587 0, 0);
588 if (error == 0) {
589 wipdata = &chain->data->ipdata;
591 bzero(wipdata->filename,
592 sizeof(wipdata->filename));
593 bcopy(xop->head.name2,
594 wipdata->filename,
595 xop->head.name2_len);
596 wipdata->meta.name_key = xop->lhc;
597 wipdata->meta.name_len = xop->head.name2_len;
600 if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) {
601 if (xop->head.name2_len <=
602 sizeof(chain->bref.check.buf)) {
604 * Remove any related data buffer, we can
605 * embed the filename in the bref itself.
607 error = hammer2_chain_resize(
608 chain, xop->head.mtid, 0, 0, 0);
609 if (error == 0) {
610 error = hammer2_chain_modify(
611 chain, xop->head.mtid,
612 0, 0);
614 if (error == 0) {
615 bzero(chain->bref.check.buf,
616 sizeof(chain->bref.check.buf));
617 bcopy(xop->head.name2,
618 chain->bref.check.buf,
619 xop->head.name2_len);
621 } else {
623 * Associate a data buffer with the bref.
624 * Zero it for consistency. Note that the
625 * data buffer is not 64KB so use chain->bytes
626 * instead of sizeof().
628 error = hammer2_chain_resize(
629 chain, xop->head.mtid, 0,
630 hammer2_getradix(HAMMER2_ALLOC_MIN), 0);
631 if (error == 0) {
632 error = hammer2_chain_modify(
633 chain, xop->head.mtid,
634 0, 0);
636 if (error == 0) {
637 bzero(chain->data->buf, chain->bytes);
638 bcopy(xop->head.name2,
639 chain->data->buf,
640 xop->head.name2_len);
643 if (error == 0) {
644 chain->bref.embed.dirent.namlen =
645 xop->head.name2_len;
651 * The frontend will replicate this operation and is the real final
652 * authority, but adjust the inode's iparent field too if the inode
653 * is embedded in the directory.
655 if (chain->bref.type == HAMMER2_BREF_TYPE_INODE &&
656 chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) {
657 hammer2_inode_data_t *wipdata;
659 error = hammer2_chain_modify(chain, xop->head.mtid, 0, 0);
660 if (error == 0) {
661 wipdata = &chain->data->ipdata;
662 wipdata->meta.iparent = xop->head.ip3->meta.inum;
667 * Destroy any matching target(s) before creating the new entry.
668 * This will result in some ping-ponging of the directory key
669 * iterator but that is ok.
671 parent = hammer2_inode_chain(xop->head.ip3, thr->clindex,
672 HAMMER2_RESOLVE_ALWAYS);
673 if (parent == NULL) {
674 error = HAMMER2_ERROR_EIO;
675 goto done;
678 if (error == 0) {
679 tmp = hammer2_chain_lookup(&parent, &key_next,
680 xop->lhc & ~HAMMER2_DIRHASH_LOMASK,
681 xop->lhc | HAMMER2_DIRHASH_LOMASK,
682 &error,
683 HAMMER2_LOOKUP_ALWAYS);
684 while (tmp) {
685 if (hammer2_chain_dirent_test(tmp, xop->head.name2,
686 xop->head.name2_len)) {
687 hammer2_chain_delete(parent, tmp,
688 xop->head.mtid, 0);
690 tmp = hammer2_chain_next(&parent, tmp, &key_next,
691 key_next,
692 xop->lhc |
693 HAMMER2_DIRHASH_LOMASK,
694 &error,
695 HAMMER2_LOOKUP_ALWAYS);
698 if (error == 0) {
700 * A relookup is required before the create to properly
701 * position the parent chain.
703 tmp = hammer2_chain_lookup(&parent, &key_next,
704 xop->lhc, xop->lhc,
705 &error, 0);
706 KKASSERT(tmp == NULL);
707 error = hammer2_chain_create(&parent, &chain,
708 pmp, HAMMER2_METH_DEFAULT,
709 xop->lhc, 0,
710 HAMMER2_BREF_TYPE_INODE,
711 HAMMER2_INODE_BYTES,
712 xop->head.mtid, 0, 0);
714 done:
715 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
716 if (parent) {
717 hammer2_chain_unlock(parent);
718 hammer2_chain_drop(parent);
720 if (chain) {
721 hammer2_chain_unlock(chain);
722 hammer2_chain_drop(chain);
727 * Directory collision resolver scan helper (backend, threaded).
729 * Used by the inode create code to locate an unused lhc.
731 void
732 hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *arg)
734 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
735 hammer2_chain_t *parent;
736 hammer2_chain_t *chain;
737 hammer2_key_t key_next;
738 int error = 0;
740 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
741 HAMMER2_RESOLVE_ALWAYS |
742 HAMMER2_RESOLVE_SHARED);
743 if (parent == NULL) {
744 kprintf("xop_nresolve: NULL parent\n");
745 chain = NULL;
746 error = HAMMER2_ERROR_EIO;
747 goto done;
751 * Lookup all possibly conflicting directory entries, the feed
752 * inherits the chain's lock so do not unlock it on the iteration.
754 chain = hammer2_chain_lookup(&parent, &key_next,
755 xop->lhc,
756 xop->lhc + HAMMER2_DIRHASH_LOMASK,
757 &error,
758 HAMMER2_LOOKUP_ALWAYS |
759 HAMMER2_LOOKUP_SHARED);
760 while (chain) {
761 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
762 if (error) {
763 hammer2_chain_unlock(chain);
764 hammer2_chain_drop(chain);
765 chain = NULL; /* safety */
766 goto done;
768 chain = hammer2_chain_next(&parent, chain, &key_next,
769 key_next,
770 xop->lhc + HAMMER2_DIRHASH_LOMASK,
771 &error,
772 HAMMER2_LOOKUP_ALWAYS |
773 HAMMER2_LOOKUP_SHARED);
775 done:
776 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);
777 if (parent) {
778 hammer2_chain_unlock(parent);
779 hammer2_chain_drop(parent);
784 * Generic lookup of a specific key.
786 * Used by the inode hidden directory code to find the hidden directory.
788 void
789 hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *arg)
791 hammer2_xop_scanlhc_t *xop = &arg->xop_scanlhc;
792 hammer2_chain_t *parent;
793 hammer2_chain_t *chain;
794 hammer2_key_t key_next;
795 int error = 0;
797 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
798 HAMMER2_RESOLVE_ALWAYS |
799 HAMMER2_RESOLVE_SHARED);
800 chain = NULL;
801 if (parent == NULL) {
802 error = HAMMER2_ERROR_EIO;
803 goto done;
807 * Lookup all possibly conflicting directory entries, the feed
808 * inherits the chain's lock so do not unlock it on the iteration.
810 chain = hammer2_chain_lookup(&parent, &key_next,
811 xop->lhc, xop->lhc,
812 &error,
813 HAMMER2_LOOKUP_ALWAYS |
814 HAMMER2_LOOKUP_SHARED);
815 if (error == 0) {
816 if (chain)
817 error = chain->error;
818 else
819 error = HAMMER2_ERROR_ENOENT;
821 hammer2_xop_feed(&xop->head, chain, thr->clindex, error);
823 done:
824 if (chain) {
825 hammer2_chain_unlock(chain);
826 hammer2_chain_drop(chain);
828 if (parent) {
829 hammer2_chain_unlock(parent);
830 hammer2_chain_drop(parent);
835 * Generic scan
837 * WARNING! Fed chains must be locked shared so ownership can be transfered
838 * and to prevent frontend/backend stalls that would occur with an
839 * exclusive lock. The shared lock also allows chain->data to be
840 * retained.
842 void
843 hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *arg)
845 hammer2_xop_scanall_t *xop = &arg->xop_scanall;
846 hammer2_chain_t *parent;
847 hammer2_chain_t *chain;
848 hammer2_key_t key_next;
849 int error = 0;
852 * Assert required flags.
854 KKASSERT(xop->resolve_flags & HAMMER2_RESOLVE_SHARED);
855 KKASSERT(xop->lookup_flags & HAMMER2_LOOKUP_SHARED);
858 * The inode's chain is the iterator. If we cannot acquire it our
859 * contribution ends here.
861 parent = hammer2_inode_chain(xop->head.ip1, thr->clindex,
862 xop->resolve_flags);
863 if (parent == NULL) {
864 kprintf("xop_readdir: NULL parent\n");
865 goto done;
869 * Generic scan of exact records. Note that indirect blocks are
870 * automatically recursed and will not be returned.
872 chain = hammer2_chain_lookup(&parent, &key_next,
873 xop->key_beg, xop->key_end,
874 &error, xop->lookup_flags);
875 while (chain) {
876 error = hammer2_xop_feed(&xop->head, chain, thr->clindex, 0);
877 if (error)
878 goto break2;
879 chain = hammer2_chain_next(&parent, chain, &key_next,
880 key_next, xop->key_end,
881 &error, xop->lookup_flags);
883 break2:
884 if (chain) {
885 hammer2_chain_unlock(chain);
886 hammer2_chain_drop(chain);
888 hammer2_chain_unlock(parent);
889 hammer2_chain_drop(parent);
890 done:
891 hammer2_xop_feed(&xop->head, NULL, thr->clindex, error);