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