2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
10 * Redistribution and use in source and binary forms, with or without
11 * 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 the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * from: Utah Hdr: vn.c 1.13 94/04/02
40 * from: @(#)vn.c 8.6 (Berkeley) 4/1/94
41 * $FreeBSD: src/sys/dev/vn/vn.c,v 1.105.2.4 2001/11/18 07:11:00 dillon Exp $
42 * $DragonFly: src/sys/dev/disk/vn/vn.c,v 1.38 2008/07/01 02:02:53 dillon Exp $
48 * Block/character interface to a vnode. Allows one to treat a file
49 * as a disk (e.g. build a filesystem in it, mount it, etc.).
51 * NOTE 1: There is a security issue involved with this driver.
52 * Once mounted all access to the contents of the "mapped" file via
53 * the special file is controlled by the permissions on the special
54 * file, the protection of the mapped file is ignored (effectively,
55 * by using root credentials in all transactions).
57 * NOTE 2: Doesn't interact with leases, should it?
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/kernel.h>
66 #include <sys/nlookup.h>
68 #include <sys/malloc.h>
69 #include <sys/mount.h>
70 #include <sys/vnode.h>
71 #include <sys/fcntl.h>
73 #include <sys/diskslice.h>
76 #include <sys/module.h>
77 #include <sys/vnioctl.h>
80 #include <vm/vm_object.h>
81 #include <vm/vm_page.h>
82 #include <vm/vm_pager.h>
83 #include <vm/vm_pageout.h>
84 #include <vm/swap_pager.h>
85 #include <vm/vm_extern.h>
86 #include <vm/vm_zone.h>
87 #include <sys/devfs.h>
89 static d_ioctl_t vnioctl
;
90 static d_open_t vnopen
;
91 static d_close_t vnclose
;
92 static d_psize_t vnsize
;
93 static d_strategy_t vnstrategy
;
94 static d_clone_t vnclone
;
96 DEVFS_DECLARE_CLONE_BITMAP(vn
);
99 #define VN_PREALLOCATED_UNITS 4
101 #define VN_PREALLOCATED_UNITS NVN
104 #define CDEV_MAJOR 43
106 #define VN_BSIZE_BEST 8192
110 * D_DISK we want to look like a disk
111 * D_CANFREE We support BUF_CMD_FREEBLKS
114 static struct dev_ops vn_ops
= {
115 { "vn", CDEV_MAJOR
, D_DISK
| D_CANFREE
},
119 .d_write
= physwrite
,
121 .d_strategy
= vnstrategy
,
127 int sc_flags
; /* flags */
128 u_int64_t sc_size
; /* size of vn, sc_secsize scale */
129 int sc_secsize
; /* sector size */
131 struct vnode
*sc_vp
; /* vnode if not NULL */
132 vm_object_t sc_object
; /* backing object if not NULL */
133 struct ucred
*sc_cred
; /* credentials */
134 int sc_maxactive
; /* max # of active requests */
135 struct buf sc_tab
; /* transfer queue */
136 u_long sc_options
; /* options */
137 cdev_t sc_devlist
; /* devices that refer to this unit */
138 SLIST_ENTRY(vn_softc
) sc_list
;
141 static SLIST_HEAD(, vn_softc
) vn_list
;
144 #define VNF_INITED 0x01
145 #define VNF_READONLY 0x02
147 static u_long vn_options
;
149 #define IFOPT(vn,opt) if (((vn)->sc_options|vn_options) & (opt))
150 #define TESTOPT(vn,opt) (((vn)->sc_options|vn_options) & (opt))
152 static int vnsetcred (struct vn_softc
*vn
, struct ucred
*cred
);
153 static void vnclear (struct vn_softc
*vn
);
154 static int vnget (cdev_t dev
, struct vn_softc
*vn
, struct vn_user
*vnu
);
155 static int vn_modevent (module_t
, int, void *);
156 static int vniocattach_file (struct vn_softc
*, struct vn_ioctl
*, cdev_t dev
, int flag
, struct ucred
*cred
);
157 static int vniocattach_swap (struct vn_softc
*, struct vn_ioctl
*, cdev_t dev
, int flag
, struct ucred
*cred
);
158 static cdev_t
vn_create(int unit
, struct devfs_bitmap
*bitmap
);
161 vnclone(struct dev_clone_args
*ap
)
165 unit
= devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(vn
), 0);
166 ap
->a_dev
= vn_create(unit
, &DEVFS_CLONE_BITMAP(vn
));
172 vnclose(struct dev_close_args
*ap
)
177 static struct vn_softc
*
182 vn
= kmalloc(sizeof *vn
, M_DEVBUF
, M_WAITOK
| M_ZERO
);
187 vninitvn(struct vn_softc
*vn
, cdev_t dev
)
191 KKASSERT(vn
!= NULL
);
192 KKASSERT(dev
!= NULL
);
197 vn
->sc_devlist
= dev
;
198 if (vn
->sc_devlist
->si_drv1
== NULL
) {
199 reference_dev(vn
->sc_devlist
);
200 vn
->sc_devlist
->si_drv1
= vn
;
201 vn
->sc_devlist
->si_drv2
= NULL
;
203 if (vn
->sc_devlist
!= dev
) {
205 dev
->si_drv2
= vn
->sc_devlist
;
206 vn
->sc_devlist
= dev
;
209 SLIST_INSERT_HEAD(&vn_list
, vn
, sc_list
);
213 * Called only when si_drv1 is NULL. Locate the associated vn node and
214 * attach the device to it.
216 static struct vn_softc
*
223 SLIST_FOREACH(vn
, &vn_list
, sc_list
) {
224 if (vn
->sc_unit
== unit
) {
226 dev
->si_drv2
= vn
->sc_devlist
;
227 vn
->sc_devlist
= dev
;
233 KKASSERT(vn
!= NULL
);
239 vnopen(struct dev_open_args
*ap
)
241 cdev_t dev
= ap
->a_head
.a_dev
;
245 * Locate preexisting device
248 if ((vn
= dev
->si_drv1
) == NULL
)
252 * Update si_bsize fields for device. This data will be overriden by
253 * the slice/parition code for vn accesses through partitions, and
254 * used directly if you open the 'whole disk' device.
256 * si_bsize_best must be reinitialized in case VN has been
257 * reconfigured, plus make it at least VN_BSIZE_BEST for efficiency.
259 dev
->si_bsize_phys
= vn
->sc_secsize
;
260 dev
->si_bsize_best
= vn
->sc_secsize
;
261 if (dev
->si_bsize_best
< VN_BSIZE_BEST
)
262 dev
->si_bsize_best
= VN_BSIZE_BEST
;
264 if ((ap
->a_oflags
& FWRITE
) && (vn
->sc_flags
& VNF_READONLY
))
268 kprintf("vnopen(%s, 0x%x, 0x%x)\n",
269 devtoname(dev
), ap
->a_oflags
, ap
->a_devtype
);
277 * Run strategy routine for VN device. We use VOP_READ/VOP_WRITE calls
278 * for vnode-backed vn's, and the swap_pager_strategy() call for
279 * vm_object-backed vn's.
282 vnstrategy(struct dev_strategy_args
*ap
)
284 cdev_t dev
= ap
->a_head
.a_dev
;
285 struct bio
*bio
= ap
->a_bio
;
293 if ((vn
= dev
->si_drv1
) == NULL
)
299 kprintf("vnstrategy(%p): unit %d\n", bp
, unit
);
301 if ((vn
->sc_flags
& VNF_INITED
) == 0) {
303 bp
->b_flags
|= B_ERROR
;
308 bp
->b_resid
= bp
->b_bcount
;
311 * The vnode device is using disk/slice label support.
313 * The dscheck() function is called for validating the
314 * slices that exist ON the vnode device itself, and
315 * translate the "slice-relative" block number, again.
316 * dscheck() will call biodone() and return NULL if
317 * we are at EOF or beyond the device size.
323 * Use the translated nbio from this point on
325 if (vn
->sc_vp
&& bp
->b_cmd
== BUF_CMD_FREEBLKS
) {
327 * Freeblks is not handled for vnode-backed elements yet.
330 /* operation complete */
331 } else if (vn
->sc_vp
) {
335 * If an error occurs, we set B_ERROR but we do not set
336 * B_INVAL because (for a write anyway), the buffer is
342 bzero(&auio
, sizeof(auio
));
344 aiov
.iov_base
= bp
->b_data
;
345 aiov
.iov_len
= bp
->b_bcount
;
346 auio
.uio_iov
= &aiov
;
348 auio
.uio_offset
= nbio
->bio_offset
;
349 auio
.uio_segflg
= UIO_SYSSPACE
;
350 if (bp
->b_cmd
== BUF_CMD_READ
)
351 auio
.uio_rw
= UIO_READ
;
353 auio
.uio_rw
= UIO_WRITE
;
354 auio
.uio_resid
= bp
->b_bcount
;
355 auio
.uio_td
= curthread
;
356 vn_lock(vn
->sc_vp
, LK_EXCLUSIVE
| LK_RETRY
);
357 if (bp
->b_cmd
== BUF_CMD_READ
)
358 error
= VOP_READ(vn
->sc_vp
, &auio
, IO_DIRECT
| IO_RECURSE
, vn
->sc_cred
);
360 error
= VOP_WRITE(vn
->sc_vp
, &auio
, IO_DIRECT
| IO_RECURSE
, vn
->sc_cred
);
361 vn_unlock(vn
->sc_vp
);
362 bp
->b_resid
= auio
.uio_resid
;
365 bp
->b_flags
|= B_ERROR
;
367 /* operation complete */
368 } else if (vn
->sc_object
) {
370 * OBJT_SWAP I/O (handles read, write, freebuf)
372 * We have nothing to do if freeing blocks on a reserved
373 * swap area, othrewise execute the op.
375 if (bp
->b_cmd
== BUF_CMD_FREEBLKS
&& TESTOPT(vn
, VN_RESERVE
)) {
377 /* operation complete */
379 swap_pager_strategy(vn
->sc_object
, nbio
);
384 bp
->b_resid
= bp
->b_bcount
;
385 bp
->b_flags
|= B_ERROR
| B_INVAL
;
386 bp
->b_error
= EINVAL
;
387 /* operation complete */
395 vnioctl(struct dev_ioctl_args
*ap
)
397 cdev_t dev
= ap
->a_head
.a_dev
;
399 struct vn_ioctl
*vio
;
404 IFOPT(vn
,VN_FOLLOW
) {
405 kprintf("vnioctl(%s, 0x%lx, %p, 0x%x): unit %d\n",
406 devtoname(dev
), ap
->a_cmd
, ap
->a_data
, ap
->a_fflag
,
422 if (dkslice(dev
) != WHOLE_DISK_SLICE
||
423 dkpart(dev
) != WHOLE_SLICE_PART
)
429 error
= priv_check_cred(ap
->a_cred
, PRIV_ROOT
, 0);
433 vio
= (struct vn_ioctl
*)ap
->a_data
;
434 f
= (u_long
*)ap
->a_data
;
438 if (vn
->sc_flags
& VNF_INITED
)
441 if (vio
->vn_file
== NULL
)
442 error
= vniocattach_swap(vn
, vio
, dev
, ap
->a_fflag
, ap
->a_cred
);
444 error
= vniocattach_file(vn
, vio
, dev
, ap
->a_fflag
, ap
->a_cred
);
448 if ((vn
->sc_flags
& VNF_INITED
) == 0)
451 * XXX handle i/o in progress. Return EBUSY, or wait, or
453 * XXX handle multiple opens of the device. Return EBUSY,
454 * or revoke the fd's.
455 * How are these problems handled for removable and failing
456 * hardware devices? (Hint: They are not)
458 if (count_dev(vn
->sc_devlist
) > 1)
463 kprintf("vnioctl: CLRed\n");
465 if (dkunit(dev
) >= VN_PREALLOCATED_UNITS
) {
466 devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(vn
), dkunit(dev
));
467 disk_destroy(&vn
->sc_disk
);
473 error
= vnget(dev
, vn
, (struct vn_user
*) ap
->a_data
);
487 vn
->sc_options
|= *f
;
492 vn
->sc_options
&= ~(*f
);
506 * Attach a file to a VN partition. Return the size in the vn_size
511 vniocattach_file(struct vn_softc
*vn
, struct vn_ioctl
*vio
, cdev_t dev
,
512 int flag
, struct ucred
*cred
)
515 struct nlookupdata nd
;
518 struct disk_info info
;
520 flags
= FREAD
|FWRITE
;
521 error
= nlookup_init(&nd
, vio
->vn_file
,
522 UIO_USERSPACE
, NLC_FOLLOW
|NLC_LOCKVP
);
525 if ((error
= vn_open(&nd
, NULL
, flags
, 0)) != 0) {
526 if (error
!= EACCES
&& error
!= EPERM
&& error
!= EROFS
)
530 error
= nlookup_init(&nd
, vio
->vn_file
, UIO_USERSPACE
, NLC_FOLLOW
|NLC_LOCKVP
);
533 if ((error
= vn_open(&nd
, NULL
, flags
, 0)) != 0)
537 if (vp
->v_type
!= VREG
||
538 (error
= VOP_GETATTR(vp
, &vattr
))) {
544 vn
->sc_secsize
= DEV_BSIZE
;
546 nd
.nl_open_vp
= NULL
;
549 * If the size is specified, override the file attributes. Note that
550 * the vn_size argument is in PAGE_SIZE sized blocks.
553 vn
->sc_size
= vio
->vn_size
* PAGE_SIZE
/ vn
->sc_secsize
;
555 vn
->sc_size
= vattr
.va_size
/ vn
->sc_secsize
;
556 error
= vnsetcred(vn
, cred
);
562 vn
->sc_flags
|= VNF_INITED
;
564 vn
->sc_flags
|= VNF_READONLY
;
567 * Set the disk info so that probing is triggered
569 bzero(&info
, sizeof(struct disk_info
));
570 info
.d_media_blksize
= vn
->sc_secsize
;
571 info
.d_media_blocks
= vn
->sc_size
;
573 * reserve mbr sector for backwards compatibility
574 * when no slices exist.
576 info
.d_dsflags
= DSO_COMPATMBR
;
577 info
.d_secpertrack
= 32;
578 info
.d_nheads
= 64 / (vn
->sc_secsize
/ DEV_BSIZE
);
579 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
580 info
.d_ncylinders
= vn
->sc_size
/ info
.d_secpercyl
;
581 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
583 error
= dev_dopen(dev
, flag
, S_IFCHR
, cred
);
588 kprintf("vnioctl: SET vp %p size %llx blks\n",
589 vn
->sc_vp
, (long long)vn
->sc_size
);
598 * Attach swap backing store to a VN partition of the size specified
603 vniocattach_swap(struct vn_softc
*vn
, struct vn_ioctl
*vio
, cdev_t dev
,
604 int flag
, struct ucred
*cred
)
607 struct disk_info info
;
610 * Range check. Disallow negative sizes or any size less then the
611 * size of a page. Then round to a page.
614 if (vio
->vn_size
<= 0)
618 * Allocate an OBJT_SWAP object.
620 * sc_secsize is PAGE_SIZE'd
622 * vio->vn_size is in PAGE_SIZE'd chunks.
623 * sc_size must be in PAGE_SIZE'd chunks.
624 * Note the truncation.
627 vn
->sc_secsize
= PAGE_SIZE
;
628 vn
->sc_size
= vio
->vn_size
;
629 vn
->sc_object
= swap_pager_alloc(NULL
,
630 vn
->sc_secsize
* (off_t
)vio
->vn_size
,
632 IFOPT(vn
, VN_RESERVE
) {
633 if (swap_pager_reserve(vn
->sc_object
, 0, vn
->sc_size
) < 0) {
634 vm_pager_deallocate(vn
->sc_object
);
635 vn
->sc_object
= NULL
;
639 vn
->sc_flags
|= VNF_INITED
;
641 error
= vnsetcred(vn
, cred
);
644 * Set the disk info so that probing is triggered
646 bzero(&info
, sizeof(struct disk_info
));
647 info
.d_media_blksize
= vn
->sc_secsize
;
648 info
.d_media_blocks
= vn
->sc_size
;
650 * reserve mbr sector for backwards compatibility
651 * when no slices exist.
653 info
.d_dsflags
= DSO_COMPATMBR
;
654 info
.d_secpertrack
= 32;
655 info
.d_nheads
= 64 / (vn
->sc_secsize
/ DEV_BSIZE
);
656 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
657 info
.d_ncylinders
= vn
->sc_size
/ info
.d_secpercyl
;
658 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
660 error
= dev_dopen(dev
, flag
, S_IFCHR
, cred
);
663 IFOPT(vn
, VN_FOLLOW
) {
664 kprintf("vnioctl: SET vp %p size %llx\n",
665 vn
->sc_vp
, (long long)vn
->sc_size
);
674 * Duplicate the current processes' credentials. Since we are called only
675 * as the result of a SET ioctl and only root can do that, any future access
676 * to this "disk" is essentially as root. Note that credentials may change
677 * if some other uid can write directly to the mapped file (NFS).
680 vnsetcred(struct vn_softc
*vn
, struct ucred
*cred
)
686 * Set credits in our softc
691 vn
->sc_cred
= crdup(cred
);
694 * Horrible kludge to establish credentials for NFS XXX.
701 tmpbuf
= kmalloc(vn
->sc_secsize
, M_TEMP
, M_WAITOK
);
702 bzero(&auio
, sizeof(auio
));
704 aiov
.iov_base
= tmpbuf
;
705 aiov
.iov_len
= vn
->sc_secsize
;
706 auio
.uio_iov
= &aiov
;
709 auio
.uio_rw
= UIO_READ
;
710 auio
.uio_segflg
= UIO_SYSSPACE
;
711 auio
.uio_resid
= aiov
.iov_len
;
712 vn_lock(vn
->sc_vp
, LK_EXCLUSIVE
| LK_RETRY
);
713 error
= VOP_READ(vn
->sc_vp
, &auio
, 0, vn
->sc_cred
);
714 vn_unlock(vn
->sc_vp
);
715 kfree(tmpbuf
, M_TEMP
);
721 vnclear(struct vn_softc
*vn
)
724 kprintf("vnclear(%p): vp=%p\n", vn
, vn
->sc_vp
);
725 vn
->sc_flags
&= ~VNF_INITED
;
726 if (vn
->sc_vp
!= NULL
) {
728 (vn
->sc_flags
& VNF_READONLY
) ? FREAD
: (FREAD
|FWRITE
));
731 vn
->sc_flags
&= ~VNF_READONLY
;
736 if (vn
->sc_object
!= NULL
) {
737 vm_pager_deallocate(vn
->sc_object
);
738 vn
->sc_object
= NULL
;
741 disk_unprobe(&vn
->sc_disk
);
749 * populate a struct vn_user for the VNIOCGET ioctl.
750 * interface conventions defined in sys/sys/vnioctl.h.
754 vnget(cdev_t dev
, struct vn_softc
*vn
, struct vn_user
*vnu
)
756 int error
, found
= 0;
757 char *freepath
, *fullpath
;
760 if (vnu
->vnu_unit
== -1) {
761 vnu
->vnu_unit
= dkunit(dev
);
763 else if (vnu
->vnu_unit
< 0)
766 SLIST_FOREACH(vn
, &vn_list
, sc_list
) {
768 if(vn
->sc_unit
!= vnu
->vnu_unit
)
773 if (vn
->sc_flags
& VNF_INITED
&& vn
->sc_vp
!= NULL
) {
775 /* note: u_cred checked in vnioctl above */
776 error
= VOP_GETATTR(vn
->sc_vp
, &vattr
);
778 kprintf("vnget: VOP_GETATTR for %p failed\n",
783 error
= vn_fullpath(curproc
, vn
->sc_vp
,
784 &fullpath
, &freepath
);
787 kprintf("vnget: unable to resolve vp %p\n",
792 strlcpy(vnu
->vnu_file
, fullpath
,
793 sizeof(vnu
->vnu_file
));
794 kfree(freepath
, M_TEMP
);
795 vnu
->vnu_dev
= vattr
.va_fsid
;
796 vnu
->vnu_ino
= vattr
.va_fileid
;
799 else if (vn
->sc_flags
& VNF_INITED
&& vn
->sc_object
!= NULL
){
801 strlcpy(vnu
->vnu_file
, _VN_USER_SWAP
,
802 sizeof(vnu
->vnu_file
));
803 vnu
->vnu_size
= vn
->sc_size
;
804 vnu
->vnu_secsize
= vn
->sc_secsize
;
808 bzero(vnu
->vnu_file
, sizeof(vnu
->vnu_file
));
823 vnsize(struct dev_psize_args
*ap
)
825 cdev_t dev
= ap
->a_head
.a_dev
;
831 if ((vn
->sc_flags
& VNF_INITED
) == 0)
833 ap
->a_result
= (int64_t)vn
->sc_size
;
838 vn_create(int unit
, struct devfs_bitmap
*bitmap
)
841 struct disk_info info
;
845 dev
= disk_create(unit
, &vn
->sc_disk
, &vn_ops
);
848 bzero(&info
, sizeof(struct disk_info
));
849 info
.d_media_blksize
= 512;
850 info
.d_media_blocks
= 0;
851 info
.d_dsflags
= DSO_MBRQUIET
;
852 info
.d_secpertrack
= 32;
854 info
.d_secpercyl
= info
.d_secpertrack
* info
.d_nheads
;
855 info
.d_ncylinders
= 0;
856 disk_setdiskinfo_sync(&vn
->sc_disk
, &info
);
859 devfs_clone_bitmap_set(bitmap
, unit
);
865 vn_modevent(module_t mod
, int type
, void *data
)
868 static cdev_t dev
= NULL
;
873 dev
= make_autoclone_dev(&vn_ops
, &DEVFS_CLONE_BITMAP(vn
), vnclone
, UID_ROOT
,
874 GID_OPERATOR
, 0640, "vn");
876 for (i
= 0; i
< VN_PREALLOCATED_UNITS
; i
++) {
877 vn_create(i
, &DEVFS_CLONE_BITMAP(vn
));
881 destroy_autoclone_dev(dev
, &DEVFS_CLONE_BITMAP(vn
));
884 while ((vn
= SLIST_FIRST(&vn_list
)) != NULL
) {
885 SLIST_REMOVE_HEAD(&vn_list
, sc_list
);
886 if (vn
->sc_flags
& VNF_INITED
)
888 /* Cleanup all cdev_t's that refer to this unit */
889 while ((dev
= vn
->sc_devlist
) != NULL
) {
890 vn
->sc_devlist
= dev
->si_drv2
;
891 dev
->si_drv1
= dev
->si_drv2
= NULL
;
896 dev_ops_remove_all(&vn_ops
);
904 DEV_MODULE(vn
, vn_modevent
, 0);