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: releng/11.0/sys/fs/nandfs/nandfs_cleaner.c 236188 2012-05-28 16:33:58Z marcel $");
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
49static void nandfs_cleaner(struct nandfs_device *);
50static 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
54static int
55nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
56    uint64_t nmembs);
57
58static void
59nandfs_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
73int
74nandfs_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
90int
91nandfs_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
102static int
103nandfs_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
125static void
126print_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
143static int
144nandfs_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
174static void
175nandfs_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
186static int
187nandfs_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
199static void
200nandfs_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
210static void
211nandfs_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
238static int
239nandfs_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
290static int
291nandfs_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
304retry:
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
327out:
328	free(suinfo, M_NANDFSTEMP);
329	return (error);
330}
331
332static int
333nandfs_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
448out_locked:
449	NANDFS_WRITEUNLOCK(fsdev);
450out:
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
459static void
460nandfs_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
483static int
484nandfs_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
565out:
566
567	DPRINTF(CLEAN, ("%s: exit error %d\n", __func__, error));
568
569	return (error);
570}
571
572static int
573nandfs_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}
621