]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nandfs/nandfs_cleaner.c
Fix regression in rpc.rquotad with certain NFS servers.
[FreeBSD/FreeBSD.git] / sys / fs / nandfs / nandfs_cleaner.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2010-2012 Semihalf.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/mount.h>
39 #include <sys/mutex.h>
40 #include <sys/buf.h>
41 #include <sys/namei.h>
42 #include <sys/vnode.h>
43 #include <sys/bio.h>
44
45 #include <fs/nandfs/nandfs_mount.h>
46 #include <fs/nandfs/nandfs.h>
47 #include <fs/nandfs/nandfs_subr.h>
48
49 #define NANDFS_CLEANER_KILL     1
50
51 static void nandfs_cleaner(struct nandfs_device *);
52 static int nandfs_cleaner_clean_segments(struct nandfs_device *,
53     struct nandfs_vinfo *, uint32_t, struct nandfs_period *, uint32_t,
54     struct nandfs_bdesc *, uint32_t, uint64_t *, uint32_t);
55
56 static int
57 nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
58     uint64_t nmembs);
59
60 static void
61 nandfs_wakeup_wait_cleaner(struct nandfs_device *fsdev, int reason)
62 {
63
64         mtx_lock(&fsdev->nd_clean_mtx);
65         if (reason == NANDFS_CLEANER_KILL)
66                 fsdev->nd_cleaner_exit = 1;
67         if (fsdev->nd_cleaning == 0) {
68                 fsdev->nd_cleaning = 1;
69                 wakeup(&fsdev->nd_cleaning);
70         }
71         cv_wait(&fsdev->nd_clean_cv, &fsdev->nd_clean_mtx);
72         mtx_unlock(&fsdev->nd_clean_mtx);
73 }
74
75 int
76 nandfs_start_cleaner(struct nandfs_device *fsdev)
77 {
78         int error;
79
80         MPASS(fsdev->nd_cleaner == NULL);
81
82         fsdev->nd_cleaner_exit = 0;
83
84         error = kthread_add((void(*)(void *))nandfs_cleaner, fsdev, NULL,
85             &fsdev->nd_cleaner, 0, 0, "nandfs_cleaner");
86         if (error)
87                 printf("nandfs: could not start cleaner: %d\n", error);
88
89         return (error);
90 }
91
92 int
93 nandfs_stop_cleaner(struct nandfs_device *fsdev)
94 {
95
96         MPASS(fsdev->nd_cleaner != NULL);
97         nandfs_wakeup_wait_cleaner(fsdev, NANDFS_CLEANER_KILL);
98         fsdev->nd_cleaner = NULL;
99
100         DPRINTF(CLEAN, ("cleaner stopped\n"));
101         return (0);
102 }
103
104 static int
105 nandfs_cleaner_finished(struct nandfs_device *fsdev)
106 {
107         int exit;
108
109         mtx_lock(&fsdev->nd_clean_mtx);
110         fsdev->nd_cleaning = 0;
111         if (!fsdev->nd_cleaner_exit) {
112                 DPRINTF(CLEAN, ("%s: sleep\n", __func__));
113                 msleep(&fsdev->nd_cleaning, &fsdev->nd_clean_mtx, PRIBIO, "-",
114                     hz * nandfs_cleaner_interval);
115         }
116         exit = fsdev->nd_cleaner_exit;
117         cv_broadcast(&fsdev->nd_clean_cv);
118         mtx_unlock(&fsdev->nd_clean_mtx);
119         if (exit) {
120                 DPRINTF(CLEAN, ("%s: no longer active\n", __func__));
121                 return (1);
122         }
123
124         return (0);
125 }
126
127 static void
128 print_suinfo(struct nandfs_suinfo *suinfo, int nsegs)
129 {
130         int i;
131
132         for (i = 0; i < nsegs; i++) {
133                 DPRINTF(CLEAN, ("%jx  %jd  %c%c%c  %10u\n",
134                     suinfo[i].nsi_num, suinfo[i].nsi_lastmod,
135                     (suinfo[i].nsi_flags &
136                     (NANDFS_SEGMENT_USAGE_ACTIVE) ? 'a' : '-'),
137                     (suinfo[i].nsi_flags &
138                     (NANDFS_SEGMENT_USAGE_DIRTY) ? 'd' : '-'),
139                     (suinfo[i].nsi_flags &
140                     (NANDFS_SEGMENT_USAGE_ERROR) ? 'e' : '-'),
141                     suinfo[i].nsi_blocks));
142         }
143 }
144
145 static int
146 nandfs_cleaner_vblock_is_alive(struct nandfs_device *fsdev,
147     struct nandfs_vinfo *vinfo, struct nandfs_cpinfo *cp, uint32_t ncps)
148 {
149         int64_t idx, min, max;
150
151         if (vinfo->nvi_end >= fsdev->nd_last_cno)
152                 return (1);
153
154         if (ncps == 0)
155                 return (0);
156
157         if (vinfo->nvi_end < cp[0].nci_cno ||
158             vinfo->nvi_start > cp[ncps - 1].nci_cno)
159                 return (0);
160
161         idx = min = 0;
162         max = ncps - 1;
163         while (min <= max) {
164                 idx = (min + max) / 2;
165                 if (vinfo->nvi_start == cp[idx].nci_cno)
166                         return (1);
167                 if (vinfo->nvi_start < cp[idx].nci_cno)
168                         max = idx - 1;
169                 else
170                         min = idx + 1;
171         }
172
173         return (vinfo->nvi_end >= cp[idx].nci_cno);
174 }
175
176 static void
177 nandfs_cleaner_vinfo_mark_alive(struct nandfs_device *fsdev,
178     struct nandfs_vinfo *vinfo, uint32_t nmembs, struct nandfs_cpinfo *cp,
179     uint32_t ncps)
180 {
181         uint32_t i;
182
183         for (i = 0; i < nmembs; i++)
184                 vinfo[i].nvi_alive =
185                     nandfs_cleaner_vblock_is_alive(fsdev, &vinfo[i], cp, ncps);
186 }
187
188 static int
189 nandfs_cleaner_bdesc_is_alive(struct nandfs_device *fsdev,
190     struct nandfs_bdesc *bdesc)
191 {
192         int alive;
193
194         alive = bdesc->bd_oblocknr == bdesc->bd_blocknr;
195         if (!alive)
196                 MPASS(abs(bdesc->bd_oblocknr - bdesc->bd_blocknr) > 2);
197
198         return (alive);
199 }
200
201 static void
202 nandfs_cleaner_bdesc_mark_alive(struct nandfs_device *fsdev,
203     struct nandfs_bdesc *bdesc, uint32_t nmembs)
204 {
205         uint32_t i;
206
207         for (i = 0; i < nmembs; i++)
208                 bdesc[i].bd_alive = nandfs_cleaner_bdesc_is_alive(fsdev,
209                     &bdesc[i]);
210 }
211
212 static void
213 nandfs_cleaner_iterate_psegment(struct nandfs_device *fsdev,
214     struct nandfs_segment_summary *segsum, union nandfs_binfo *binfo,
215     nandfs_daddr_t blk, struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp)
216 {
217         int i;
218
219         DPRINTF(CLEAN, ("%s nbinfos %x\n", __func__, segsum->ss_nbinfos));
220         for (i = 0; i < segsum->ss_nbinfos; i++) {
221                 if (binfo[i].bi_v.bi_ino == NANDFS_DAT_INO) {
222                         (*bdpp)->bd_oblocknr = blk + segsum->ss_nblocks -
223                             segsum->ss_nbinfos + i;
224                         /*
225                          * XXX Hack
226                          */
227                         if (segsum->ss_flags & NANDFS_SS_SR)
228                                 (*bdpp)->bd_oblocknr--;
229                         (*bdpp)->bd_level = binfo[i].bi_dat.bi_level;
230                         (*bdpp)->bd_offset = binfo[i].bi_dat.bi_blkoff;
231                         (*bdpp)++;
232                 } else {
233                         (*vipp)->nvi_ino = binfo[i].bi_v.bi_ino;
234                         (*vipp)->nvi_vblocknr = binfo[i].bi_v.bi_vblocknr;
235                         (*vipp)++;
236                 }
237         }
238 }
239
240 static int
241 nandfs_cleaner_iterate_segment(struct nandfs_device *fsdev, uint64_t segno,
242     struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp, int *select)
243 {
244         struct nandfs_segment_summary *segsum;
245         union nandfs_binfo *binfo;
246         struct buf *bp;
247         uint32_t nblocks;
248         nandfs_daddr_t curr, start, end;
249         int error = 0;
250
251         nandfs_get_segment_range(fsdev, segno, &start, &end);
252
253         DPRINTF(CLEAN, ("%s: segno %jx start %jx end %jx\n", __func__, segno,
254             start, end));
255
256         *select = 0;
257
258         for (curr = start; curr < end; curr += nblocks) {
259                 error = nandfs_dev_bread(fsdev, curr, NOCRED, 0, &bp);
260                 if (error) {
261                         brelse(bp);
262                         nandfs_error("%s: couldn't load segment summary of %jx: %d\n",
263                             __func__, segno, error);
264                         return (error);
265                 }
266
267                 segsum = (struct nandfs_segment_summary *)bp->b_data;
268                 binfo = (union nandfs_binfo *)(bp->b_data + segsum->ss_bytes);
269
270                 if (!nandfs_segsum_valid(segsum)) {
271                         brelse(bp);
272                         nandfs_error("nandfs: invalid summary of segment %jx\n", segno);
273                         return (error);
274                 }
275
276                 DPRINTF(CLEAN, ("%s: %jx magic %x bytes %x nblocks %x nbinfos "
277                     "%x\n", __func__, segno, segsum->ss_magic, segsum->ss_bytes,
278                     segsum->ss_nblocks, segsum->ss_nbinfos));
279
280                 nandfs_cleaner_iterate_psegment(fsdev, segsum, binfo, curr,
281                     vipp, bdpp);
282                 nblocks = segsum->ss_nblocks;
283                 brelse(bp);
284         }
285
286         if (error == 0)
287                 *select = 1;
288
289         return (error);
290 }
291
292 static int
293 nandfs_cleaner_choose_segment(struct nandfs_device *fsdev, uint64_t **segpp,
294     uint64_t nsegs, uint64_t *rseg)
295 {
296         struct nandfs_suinfo *suinfo;
297         uint64_t i, ssegs;
298         int error;
299
300         suinfo = malloc(sizeof(*suinfo) * nsegs, M_NANDFSTEMP,
301             M_ZERO | M_WAITOK);
302
303         if (*rseg >= fsdev->nd_fsdata.f_nsegments)
304                 *rseg = 0;
305
306 retry:
307         error = nandfs_get_segment_info_filter(fsdev, suinfo, nsegs, *rseg,
308             &ssegs, NANDFS_SEGMENT_USAGE_DIRTY,
309             NANDFS_SEGMENT_USAGE_ACTIVE | NANDFS_SEGMENT_USAGE_ERROR |
310             NANDFS_SEGMENT_USAGE_GC);
311         if (error) {
312                 nandfs_error("%s:%d", __FILE__, __LINE__);
313                 goto out;
314         }
315         if (ssegs == 0 && *rseg != 0) {
316                 *rseg = 0;
317                 goto retry;
318         }
319         if (ssegs > 0) {
320                 print_suinfo(suinfo, ssegs);
321
322                 for (i = 0; i < ssegs; i++) {
323                         (**segpp) = suinfo[i].nsi_num;
324                         (*segpp)++;
325                 }
326                 *rseg = suinfo[i - 1].nsi_num + 1;
327         }
328
329 out:
330         free(suinfo, M_NANDFSTEMP);
331         return (error);
332 }
333
334 static int
335 nandfs_cleaner_body(struct nandfs_device *fsdev, uint64_t *rseg)
336 {
337         struct nandfs_vinfo *vinfo, *vip, *vipi;
338         struct nandfs_bdesc *bdesc, *bdp, *bdpi;
339         struct nandfs_cpstat cpstat;
340         struct nandfs_cpinfo *cpinfo = NULL;
341         uint64_t *segnums, *segp;
342         int select, selected;
343         int error = 0;
344         int nsegs;
345         int i;
346
347         nsegs = nandfs_cleaner_segments;
348
349         vip = vinfo = malloc(sizeof(*vinfo) *
350             fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
351             M_ZERO | M_WAITOK);
352         bdp = bdesc = malloc(sizeof(*bdesc) *
353             fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
354             M_ZERO | M_WAITOK);
355         segp = segnums = malloc(sizeof(*segnums) * nsegs, M_NANDFSTEMP,
356             M_WAITOK);
357
358         error = nandfs_cleaner_choose_segment(fsdev, &segp, nsegs, rseg);
359         if (error) {
360                 nandfs_error("%s:%d", __FILE__, __LINE__);
361                 goto out;
362         }
363
364         if (segnums == segp)
365                 goto out;
366
367         selected = 0;
368         for (i = 0; i < segp - segnums; i++) {
369                 error = nandfs_cleaner_iterate_segment(fsdev, segnums[i], &vip,
370                     &bdp, &select);
371                 if (error) {
372                         /*
373                          * XXX deselect (see below)?
374                          */
375                         goto out;
376                 }
377                 if (!select)
378                         segnums[i] = NANDFS_NOSEGMENT;
379                 else {
380                         error = nandfs_markgc_segment(fsdev, segnums[i]);
381                         if (error) {
382                                 nandfs_error("%s:%d\n", __FILE__, __LINE__);
383                                 goto out;
384                         }
385                         selected++;
386                 }
387         }
388
389         if (selected == 0) {
390                 MPASS(vinfo == vip);
391                 MPASS(bdesc == bdp);
392                 goto out;
393         }
394
395         error = nandfs_get_cpstat(fsdev->nd_cp_node, &cpstat);
396         if (error) {
397                 nandfs_error("%s:%d\n", __FILE__, __LINE__);
398                 goto out;
399         }
400
401         if (cpstat.ncp_nss != 0) {
402                 cpinfo = malloc(sizeof(struct nandfs_cpinfo) * cpstat.ncp_nss,
403                     M_NANDFSTEMP, M_WAITOK);
404                 error = nandfs_get_cpinfo(fsdev->nd_cp_node, 1, NANDFS_SNAPSHOT,
405                     cpinfo, cpstat.ncp_nss, NULL);
406                 if (error) {
407                         nandfs_error("%s:%d\n", __FILE__, __LINE__);
408                         goto out_locked;
409                 }
410         }
411
412         NANDFS_WRITELOCK(fsdev);
413         DPRINTF(CLEAN, ("%s: got lock\n", __func__));
414
415         error = nandfs_get_dat_vinfo(fsdev, vinfo, vip - vinfo);
416         if (error) {
417                 nandfs_error("%s:%d\n", __FILE__, __LINE__);
418                 goto out_locked;
419         }
420
421         nandfs_cleaner_vinfo_mark_alive(fsdev, vinfo, vip - vinfo, cpinfo,
422             cpstat.ncp_nss);
423
424         error = nandfs_get_dat_bdescs(fsdev, bdesc, bdp - bdesc);
425         if (error) {
426                 nandfs_error("%s:%d\n", __FILE__, __LINE__);
427                 goto out_locked;
428         }
429
430         nandfs_cleaner_bdesc_mark_alive(fsdev, bdesc, bdp - bdesc);
431
432         DPRINTF(CLEAN, ("got:\n"));
433         for (vipi = vinfo; vipi < vip; vipi++) {
434                 DPRINTF(CLEAN, ("v ino %jx vblocknr %jx start %jx end %jx "
435                     "alive %d\n", vipi->nvi_ino, vipi->nvi_vblocknr,
436                     vipi->nvi_start, vipi->nvi_end, vipi->nvi_alive));
437         }
438         for (bdpi = bdesc; bdpi < bdp; bdpi++) {
439                 DPRINTF(CLEAN, ("b oblocknr %jx blocknr %jx offset %jx "
440                     "alive %d\n", bdpi->bd_oblocknr, bdpi->bd_blocknr,
441                     bdpi->bd_offset, bdpi->bd_alive));
442         }
443         DPRINTF(CLEAN, ("end list\n"));
444
445         error = nandfs_cleaner_clean_segments(fsdev, vinfo, vip - vinfo, NULL,
446             0, bdesc, bdp - bdesc, segnums, segp - segnums);
447         if (error)
448                 nandfs_error("%s:%d\n", __FILE__, __LINE__);
449
450 out_locked:
451         NANDFS_WRITEUNLOCK(fsdev);
452 out:
453         free(cpinfo, M_NANDFSTEMP);
454         free(segnums, M_NANDFSTEMP);
455         free(bdesc, M_NANDFSTEMP);
456         free(vinfo, M_NANDFSTEMP);
457
458         return (error);
459 }
460
461 static void
462 nandfs_cleaner(struct nandfs_device *fsdev)
463 {
464         uint64_t checked_seg = 0;
465         int error;
466
467         while (!nandfs_cleaner_finished(fsdev)) {
468                 if (!nandfs_cleaner_enable || rebooting)
469                         continue;
470
471                 DPRINTF(CLEAN, ("%s: run started\n", __func__));
472
473                 fsdev->nd_cleaning = 1;
474
475                 error = nandfs_cleaner_body(fsdev, &checked_seg);
476
477                 DPRINTF(CLEAN, ("%s: run finished error %d\n", __func__,
478                     error));
479         }
480
481         DPRINTF(CLEAN, ("%s: exiting\n", __func__));
482         kthread_exit();
483 }
484
485 static int
486 nandfs_cleaner_clean_segments(struct nandfs_device *nffsdev,
487     struct nandfs_vinfo *vinfo, uint32_t nvinfo,
488     struct nandfs_period *pd, uint32_t npd,
489     struct nandfs_bdesc *bdesc, uint32_t nbdesc,
490     uint64_t *segments, uint32_t nsegs)
491 {
492         struct nandfs_node *gc;
493         struct buf *bp;
494         uint32_t i;
495         int error = 0;
496
497         gc = nffsdev->nd_gc_node;
498
499         DPRINTF(CLEAN, ("%s: enter\n", __func__));
500
501         VOP_LOCK(NTOV(gc), LK_EXCLUSIVE);
502         for (i = 0; i < nvinfo; i++) {
503                 if (!vinfo[i].nvi_alive)
504                         continue;
505                 DPRINTF(CLEAN, ("%s: read vblknr:%#jx blk:%#jx\n",
506                     __func__, (uintmax_t)vinfo[i].nvi_vblocknr,
507                     (uintmax_t)vinfo[i].nvi_blocknr));
508                 error = nandfs_bread(nffsdev->nd_gc_node, vinfo[i].nvi_blocknr,
509                     NULL, 0, &bp);
510                 if (error) {
511                         nandfs_error("%s:%d", __FILE__, __LINE__);
512                         VOP_UNLOCK(NTOV(gc), 0);
513                         goto out;
514                 }
515                 nandfs_vblk_set(bp, vinfo[i].nvi_vblocknr);
516                 nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED);
517                 nandfs_dirty_buf(bp, 1);
518         }
519         VOP_UNLOCK(NTOV(gc), 0);
520
521         /* Delete checkpoints */
522         for (i = 0; i < npd; i++) {
523                 DPRINTF(CLEAN, ("delete checkpoint: %jx\n",
524                     (uintmax_t)pd[i].p_start));
525                 error = nandfs_delete_cp(nffsdev->nd_cp_node, pd[i].p_start,
526                     pd[i].p_end);
527                 if (error) {
528                         nandfs_error("%s:%d", __FILE__, __LINE__);
529                         goto out;
530                 }
531         }
532
533         /* Update vblocks */
534         for (i = 0; i < nvinfo; i++) {
535                 if (vinfo[i].nvi_alive)
536                         continue;
537                 DPRINTF(CLEAN, ("freeing vblknr: %jx\n", vinfo[i].nvi_vblocknr));
538                 error = nandfs_vblock_free(nffsdev, vinfo[i].nvi_vblocknr);
539                 if (error) {
540                         nandfs_error("%s:%d", __FILE__, __LINE__);
541                         goto out;
542                 }
543         }
544
545         error = nandfs_process_bdesc(nffsdev, bdesc, nbdesc);
546         if (error) {
547                 nandfs_error("%s:%d", __FILE__, __LINE__);
548                 goto out;
549         }
550
551         /* Add segments to clean */
552         if (nffsdev->nd_free_count) {
553                 nffsdev->nd_free_base = realloc(nffsdev->nd_free_base,
554                     (nffsdev->nd_free_count + nsegs) * sizeof(uint64_t),
555                     M_NANDFSTEMP, M_WAITOK | M_ZERO);
556                 memcpy(&nffsdev->nd_free_base[nffsdev->nd_free_count], segments,
557                     nsegs * sizeof(uint64_t));
558                 nffsdev->nd_free_count += nsegs;
559         } else {
560                 nffsdev->nd_free_base = malloc(nsegs * sizeof(uint64_t),
561                     M_NANDFSTEMP, M_WAITOK|M_ZERO);
562                 memcpy(nffsdev->nd_free_base, segments,
563                     nsegs * sizeof(uint64_t));
564                 nffsdev->nd_free_count = nsegs;
565         }
566
567 out:
568
569         DPRINTF(CLEAN, ("%s: exit error %d\n", __func__, error));
570
571         return (error);
572 }
573
574 static int
575 nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
576     uint64_t nmembs)
577 {
578         struct nandfs_node *dat_node;
579         struct buf *bp;
580         uint64_t i;
581         int error;
582
583         dat_node = nffsdev->nd_dat_node;
584
585         VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE);
586
587         for (i = 0; i < nmembs; i++) {
588                 if (!bd[i].bd_alive)
589                         continue;
590                 DPRINTF(CLEAN, ("%s: idx %jx offset %jx\n",
591                     __func__, i, bd[i].bd_offset));
592                 if (bd[i].bd_level) {
593                         error = nandfs_bread_meta(dat_node, bd[i].bd_offset,
594                             NULL, 0, &bp);
595                         if (error) {
596                                 nandfs_error("%s: cannot read dat node "
597                                     "level:%d\n", __func__, bd[i].bd_level);
598                                 brelse(bp);
599                                 VOP_UNLOCK(NTOV(dat_node), 0);
600                                 return (error);
601                         }
602                         nandfs_dirty_buf_meta(bp, 1);
603                         nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1);
604                 } else {
605                         error = nandfs_bread(dat_node, bd[i].bd_offset, NULL,
606                             0, &bp);
607                         if (error) {
608                                 nandfs_error("%s: cannot read dat node\n",
609                                     __func__);
610                                 brelse(bp);
611                                 VOP_UNLOCK(NTOV(dat_node), 0);
612                                 return (error);
613                         }
614                         nandfs_dirty_buf(bp, 1);
615                 }
616                 DPRINTF(CLEAN, ("%s: bp: %p\n", __func__, bp));
617         }
618
619         VOP_UNLOCK(NTOV(dat_node), 0);
620
621         return (0);
622 }