xref: /illumos-gate/usr/src/uts/common/io/vscan/vscan_drv.c (revision 3dbfc80346c4b24f1337e411111b9521c729cf9e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include <sys/stat.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/time.h>
32 #include <sys/varargs.h>
33 #include <sys/conf.h>
34 #include <sys/modctl.h>
35 #include <sys/vnode.h>
36 #include <fs/fs_subr.h>
37 #include <sys/types.h>
38 #include <sys/file.h>
39 #include <sys/disp.h>
40 #include <sys/vscan.h>
41 #include <sys/policy.h>
42 #include <sys/sdt.h>
43 
44 
45 /* seconds to wait for daemon to reconnect before disabling */
46 #define	VS_DAEMON_WAIT_SEC	60
47 
48 /* length of minor node name - vscan%d */
49 #define	VS_NODENAME_LEN		16
50 
51 /* global variables - tunable via /etc/system */
52 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC;
53 extern uint32_t vs_nodes_max;	/* max in-progress scan requests */
54 
55 /*
56  * vscan_drv_state
57  *
58  * Operations on instance 0 represent vscand initiated state
59  * transition events:
60  * open(0) - vscand connect
61  * close(0) - vscan disconnect
62  * enable(0) - vscand enable (ready to hand requests)
63  * disable(0) - vscand disable (shutting down)
64  *
65  *   +------------------------+
66  *   | VS_DRV_UNCONFIG        |
67  *   +------------------------+
68  *      |           ^
69  *      | attach    | detach
70  *      v           |
71  *   +------------------------+
72  *   | VS_DRV_IDLE            |<------|
73  *   +------------------------+       |
74  *      |           ^                 |
75  *      | open(0)   | close(0)        |
76  *      v           |                 |
77  *   +------------------------+       |
78  *   | VS_DRV_CONNECTED       |<-|    |
79  *   +------------------------+  |    |
80  *      |           ^            |    |
81  *      | enable(0) | disable(0) |    |
82  *      v           |            |    |
83  *   +------------------------+  |    |
84  *   | VS_DRV_ENABLED         |  |    |
85  *   +------------------------+  |    |
86  *      |                        |    |
87  *      | close(0)            open(0) |
88  *      v                        |    |
89  *   +------------------------+  |    | timeout
90  *   | VS_DRV_DELAYED_DISABLE | --    |
91  *   +------------------------+	------|
92  *
93  */
94 typedef enum {
95 	VS_DRV_UNCONFIG,
96 	VS_DRV_IDLE,
97 	VS_DRV_CONNECTED,
98 	VS_DRV_ENABLED,
99 	VS_DRV_DELAYED_DISABLE
100 } vscan_drv_state_t;
101 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG;
102 
103 
104 /*
105  * vscan_drv_inst_state
106  *
107  * Instance 0 controls the state of the driver: vscan_drv_state.
108  * vscan_drv_inst_state[0] should NOT be used.
109  *
110  * vscan_drv_inst_state[n] represents the state of driver
111  * instance n, used by vscand to access file data for the
112  * scan request with index n in vscan_svc_reqs.
113  * Minor nodes are created as required then all are destroyed
114  * during driver detach.
115  *
116  *   +------------------------+
117  *   | VS_DRV_INST_UNCONFIG   |
118  *   +------------------------+
119  *      |                 ^
120  *      | create_node(n)  | detach
121  *      v                 |
122  *   +------------------------+
123  *   | VS_DRV_INST_INIT       |<-|
124  *   +------------------------+  |
125  *      |                        |
126  *      | open(n)                |
127  *      v                        |
128  *   +------------------------+  |
129  *   | VS_DRV_INST_OPEN       |--|
130  *   +------------------------+  |
131  *      |                        |
132  *      | read(n)                |
133  *      v                        | close(n)
134  *   +------------------------+  |
135  *   | VS_DRV_INST_READING    |--|
136  *   +------------------------+
137  */
138 typedef enum {
139 	VS_DRV_INST_UNCONFIG = 0, /* minor node not created */
140 	VS_DRV_INST_INIT,
141 	VS_DRV_INST_OPEN,
142 	VS_DRV_INST_READING
143 } vscan_drv_inst_state_t;
144 
145 static vscan_drv_inst_state_t *vscan_drv_inst_state;
146 static int vscan_drv_inst_state_sz;
147 
148 static dev_info_t *vscan_drv_dip;
149 static kmutex_t vscan_drv_mutex;
150 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
151 
152 /*
153  * DDI entry points.
154  */
155 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
156 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
157 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
158 static int vscan_drv_open(dev_t *, int, int, cred_t *);
159 static int vscan_drv_close(dev_t, int, int, cred_t *);
160 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
161 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
162 
163 static boolean_t vscan_drv_in_use(void);
164 static void vscan_drv_delayed_disable(void);
165 
166 
167 /*
168  * module linkage info for the kernel
169  */
170 static struct cb_ops cbops = {
171 	vscan_drv_open,		/* cb_open */
172 	vscan_drv_close,	/* cb_close */
173 	nodev,			/* cb_strategy */
174 	nodev,			/* cb_print */
175 	nodev,			/* cb_dump */
176 	vscan_drv_read,		/* cb_read */
177 	nodev,			/* cb_write */
178 	vscan_drv_ioctl,	/* cb_ioctl */
179 	nodev,			/* cb_devmap */
180 	nodev,			/* cb_mmap */
181 	nodev,			/* cb_segmap */
182 	nochpoll,		/* cb_chpoll */
183 	ddi_prop_op,		/* cb_prop_op */
184 	NULL,			/* cb_streamtab */
185 	D_MP,			/* cb_flag */
186 	CB_REV,			/* cb_rev */
187 	nodev,			/* cb_aread */
188 	nodev,			/* cb_awrite */
189 };
190 
191 static struct dev_ops devops = {
192 	DEVO_REV,		/* devo_rev */
193 	0,			/* devo_refcnt */
194 	vscan_drv_getinfo,	/* devo_getinfo */
195 	nulldev,		/* devo_identify */
196 	nulldev,		/* devo_probe */
197 	vscan_drv_attach,	/* devo_attach */
198 	vscan_drv_detach,	/* devo_detach */
199 	nodev,			/* devo_reset */
200 	&cbops,			/* devo_cb_ops */
201 	NULL,			/* devo_bus_ops */
202 	NULL,			/* devo_power */
203 	ddi_quiesce_not_needed,		/* devo_quiesce */
204 };
205 
206 static struct modldrv modldrv = {
207 	&mod_driverops,		/* drv_modops */
208 	"virus scanning",	/* drv_linkinfo */
209 	&devops,
210 };
211 
212 static struct modlinkage modlinkage = {
213 
214 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
215 	&modldrv,	/* ptr to linkage structures			*/
216 	NULL,
217 };
218 
219 
220 /*
221  * _init
222  */
223 int
224 _init(void)
225 {
226 	int rc;
227 
228 	vscan_drv_inst_state_sz =
229 	    sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
230 
231 	if (vscan_door_init() != 0)
232 		return (DDI_FAILURE);
233 
234 	if (vscan_svc_init() != 0) {
235 		vscan_door_fini();
236 		return (DDI_FAILURE);
237 	}
238 
239 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
240 	vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
241 
242 	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
243 
244 	if ((rc  = mod_install(&modlinkage)) != 0) {
245 		vscan_door_fini();
246 		vscan_svc_fini();
247 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
248 		cv_destroy(&vscan_drv_cv);
249 		mutex_destroy(&vscan_drv_mutex);
250 	}
251 
252 	return (rc);
253 }
254 
255 
256 /*
257  * _info
258  */
259 int
260 _info(struct modinfo *modinfop)
261 {
262 	return (mod_info(&modlinkage, modinfop));
263 }
264 
265 
266 /*
267  * _fini
268  */
269 int
270 _fini(void)
271 {
272 	int rc;
273 
274 	if (vscan_drv_in_use())
275 		return (EBUSY);
276 
277 	if ((rc = mod_remove(&modlinkage)) == 0) {
278 		vscan_door_fini();
279 		vscan_svc_fini();
280 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
281 		cv_destroy(&vscan_drv_cv);
282 		mutex_destroy(&vscan_drv_mutex);
283 	}
284 
285 	return (rc);
286 }
287 
288 
289 /*
290  * DDI entry points.
291  */
292 
293 /*
294  * vscan_drv_getinfo
295  */
296 /* ARGSUSED */
297 static int
298 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
299 {
300 	ulong_t inst = getminor((dev_t)arg);
301 
302 	switch (cmd) {
303 	case DDI_INFO_DEVT2DEVINFO:
304 		*result = vscan_drv_dip;
305 		return (DDI_SUCCESS);
306 	case DDI_INFO_DEVT2INSTANCE:
307 		*result = (void *)inst;
308 		return (DDI_SUCCESS);
309 	}
310 	return (DDI_FAILURE);
311 }
312 
313 
314 /*
315  * vscan_drv_attach
316  */
317 static int
318 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
319 {
320 	if (cmd != DDI_ATTACH)
321 		return (DDI_FAILURE);
322 
323 	if (ddi_get_instance(dip) != 0)
324 		return (DDI_FAILURE);
325 
326 	vscan_drv_dip = dip;
327 
328 	/* create minor node 0 for daemon-driver synchronization */
329 	if (vscan_drv_create_node(0) == B_FALSE)
330 		return (DDI_FAILURE);
331 
332 	vscan_drv_state = VS_DRV_IDLE;
333 	return (DDI_SUCCESS);
334 }
335 
336 
337 /*
338  * vscan_drv_detach
339  */
340 static int
341 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
342 {
343 	int i;
344 
345 	if (cmd != DDI_DETACH)
346 		return (DDI_FAILURE);
347 
348 	if (ddi_get_instance(dip) != 0)
349 		return (DDI_FAILURE);
350 
351 	if (vscan_drv_in_use())
352 		return (DDI_FAILURE);
353 
354 	/* remove all minor nodes */
355 	vscan_drv_dip = NULL;
356 	ddi_remove_minor_node(dip, NULL);
357 	for (i = 0; i <= vs_nodes_max; i++)
358 		vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
359 
360 	vscan_drv_state = VS_DRV_UNCONFIG;
361 	return (DDI_SUCCESS);
362 }
363 
364 
365 /*
366  * vscan_drv_in_use
367  *
368  * If the driver state is not IDLE or UNCONFIG then the
369  * driver is in use. Otherwise, check the service interface
370  * (vscan_svc) to see if it is still in use - for example
371  * there there may be requests still in progress.
372  */
373 static boolean_t
374 vscan_drv_in_use()
375 {
376 	boolean_t in_use = B_FALSE;
377 
378 	mutex_enter(&vscan_drv_mutex);
379 	if ((vscan_drv_state != VS_DRV_IDLE) &&
380 	    (vscan_drv_state != VS_DRV_UNCONFIG)) {
381 		in_use = B_TRUE;
382 	}
383 	mutex_exit(&vscan_drv_mutex);
384 
385 	if (in_use)
386 		return (B_TRUE);
387 	else
388 		return (vscan_svc_in_use());
389 }
390 
391 
392 /*
393  * vscan_drv_open
394  *
395  * If inst == 0, this is vscand initializing.
396  * If the driver is in DELAYED_DISABLE, ie vscand previously
397  * disconnected without a clean shutdown and the driver is
398  * waiting for a period to allow vscand to reconnect, signal
399  * vscan_drv_cv to cancel the delayed disable.
400  *
401  * If inst != 0, open the file associated with inst.
402  */
403 /* ARGSUSED */
404 static int
405 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
406 {
407 	int rc;
408 	int inst = getminor(*devp);
409 
410 	if ((inst < 0) || (inst > vs_nodes_max))
411 		return (EINVAL);
412 
413 	/* check if caller has privilege for virus scanning */
414 	if ((rc = secpolicy_vscan(credp)) != 0) {
415 		DTRACE_PROBE1(vscan__priv, int, rc);
416 		return (EPERM);
417 	}
418 
419 	mutex_enter(&vscan_drv_mutex);
420 	if (inst == 0) {
421 		switch (vscan_drv_state) {
422 		case VS_DRV_IDLE:
423 			vscan_drv_state = VS_DRV_CONNECTED;
424 			break;
425 		case VS_DRV_DELAYED_DISABLE:
426 			cv_signal(&vscan_drv_cv);
427 			vscan_drv_state = VS_DRV_CONNECTED;
428 			break;
429 		default:
430 			DTRACE_PROBE1(vscan__drv__state__violation,
431 			    int, vscan_drv_state);
432 			mutex_exit(&vscan_drv_mutex);
433 			return (EINVAL);
434 		}
435 	} else {
436 		if ((vscan_drv_state != VS_DRV_ENABLED) ||
437 		    (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
438 			mutex_exit(&vscan_drv_mutex);
439 			return (EINVAL);
440 		}
441 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
442 	}
443 	mutex_exit(&vscan_drv_mutex);
444 
445 	return (0);
446 }
447 
448 
449 /*
450  * vscan_drv_close
451  *
452  * If inst == 0, this is vscand detaching.
453  * If the driver is in ENABLED state vscand has terminated without
454  * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
455  * state and initiate a delayed disable to allow vscand time to
456  * reconnect.
457  *
458  * If inst != 0, close the file associated with inst
459  */
460 /* ARGSUSED */
461 static int
462 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
463 {
464 	int i, inst = getminor(dev);
465 
466 	if ((inst < 0) || (inst > vs_nodes_max))
467 		return (EINVAL);
468 
469 	mutex_enter(&vscan_drv_mutex);
470 	if (inst != 0) {
471 		vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
472 		mutex_exit(&vscan_drv_mutex);
473 		return (0);
474 	}
475 
476 	/* instance 0 - daemon disconnect */
477 	if ((vscan_drv_state != VS_DRV_CONNECTED) &&
478 	    (vscan_drv_state != VS_DRV_ENABLED)) {
479 		DTRACE_PROBE1(vscan__drv__state__violation,
480 		    int, vscan_drv_state);
481 		mutex_exit(&vscan_drv_mutex);
482 		return (EINVAL);
483 	}
484 
485 	for (i = 1; i <= vs_nodes_max; i++) {
486 		if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
487 			vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
488 	}
489 
490 	if (vscan_drv_state == VS_DRV_CONNECTED) {
491 		vscan_drv_state = VS_DRV_IDLE;
492 	} else { /* VS_DRV_ENABLED */
493 		cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
494 		if (thread_create(NULL, 0, vscan_drv_delayed_disable,
495 		    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
496 			vscan_svc_disable();
497 			vscan_drv_state = VS_DRV_IDLE;
498 		} else {
499 			vscan_drv_state = VS_DRV_DELAYED_DISABLE;
500 		}
501 	}
502 	mutex_exit(&vscan_drv_mutex);
503 
504 	vscan_svc_scan_abort();
505 	vscan_door_close();
506 	return (0);
507 }
508 
509 
510 /*
511  * vscan_drv_delayed_disable
512  *
513  * Invoked from vscan_drv_close if the daemon disconnects
514  * without first sending disable (e.g. daemon crashed).
515  * Delays for vs_reconnect_timeout before disabling, to allow
516  * the daemon to reconnect. During this time, scan requests
517  * will be processed locally (see vscan_svc.c)
518  */
519 static void
520 vscan_drv_delayed_disable(void)
521 {
522 	clock_t timeout = lbolt + SEC_TO_TICK(vs_reconnect_timeout);
523 
524 	mutex_enter(&vscan_drv_mutex);
525 	(void) cv_timedwait(&vscan_drv_cv, &vscan_drv_mutex, timeout);
526 
527 	if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
528 		vscan_svc_disable();
529 		vscan_drv_state = VS_DRV_IDLE;
530 	} else {
531 		DTRACE_PROBE(vscan__reconnect);
532 	}
533 	mutex_exit(&vscan_drv_mutex);
534 }
535 
536 
537 /*
538  * vscan_drv_read
539  */
540 /* ARGSUSED */
541 static int
542 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
543 {
544 	int rc;
545 	int inst = getminor(dev);
546 	vnode_t *vp;
547 
548 	if ((inst <= 0) || (inst > vs_nodes_max))
549 		return (EINVAL);
550 
551 	mutex_enter(&vscan_drv_mutex);
552 	if ((vscan_drv_state != VS_DRV_ENABLED) ||
553 	    (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
554 		mutex_exit(&vscan_drv_mutex);
555 		return (EINVAL);
556 	}
557 	vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
558 	mutex_exit(&vscan_drv_mutex);
559 
560 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
561 		return (EINVAL);
562 
563 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
564 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
565 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
566 
567 	mutex_enter(&vscan_drv_mutex);
568 	if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
569 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
570 	mutex_exit(&vscan_drv_mutex);
571 
572 	return (rc);
573 }
574 
575 
576 /*
577  * vscan_drv_ioctl
578  *
579  * Process ioctls from vscand:
580  * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
581  *    enable VFS interface.
582  * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
583  * VS_IOCTL_RESULT - scan response data
584  * VS_IOCTL_CONFIG - configuration data from vscand
585  * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
586  *    to allow vscand to set appropriate resource allocation limits
587  */
588 /* ARGSUSED */
589 static int
590 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
591 	cred_t *credp, int *rvalp)
592 {
593 	int inst = getminor(dev);
594 	vs_config_t conf;
595 	vs_scan_rsp_t rsp;
596 
597 	if (inst != 0)
598 		return (EINVAL);
599 
600 	switch (cmd) {
601 	case VS_IOCTL_ENABLE:
602 		mutex_enter(&vscan_drv_mutex);
603 		if (vscan_drv_state != VS_DRV_CONNECTED) {
604 			DTRACE_PROBE1(vscan__drv__state__violation,
605 			    int, vscan_drv_state);
606 			mutex_exit(&vscan_drv_mutex);
607 			return (EINVAL);
608 		}
609 		if ((vscan_door_open((int)arg) != 0) ||
610 		    (vscan_svc_enable() != 0)) {
611 			mutex_exit(&vscan_drv_mutex);
612 			return (EINVAL);
613 		}
614 		vscan_drv_state = VS_DRV_ENABLED;
615 		mutex_exit(&vscan_drv_mutex);
616 		break;
617 
618 	case VS_IOCTL_DISABLE:
619 		mutex_enter(&vscan_drv_mutex);
620 		if (vscan_drv_state != VS_DRV_ENABLED) {
621 			DTRACE_PROBE1(vscan__drv__state__violation,
622 			    int, vscan_drv_state);
623 			mutex_exit(&vscan_drv_mutex);
624 			return (EINVAL);
625 		}
626 		vscan_svc_disable();
627 		vscan_drv_state = VS_DRV_CONNECTED;
628 		mutex_exit(&vscan_drv_mutex);
629 		break;
630 
631 	case VS_IOCTL_RESULT:
632 		if (ddi_copyin((void *)arg, &rsp,
633 		    sizeof (vs_scan_rsp_t), 0) == -1)
634 			return (EFAULT);
635 		else
636 			vscan_svc_scan_result(&rsp);
637 		break;
638 
639 	case VS_IOCTL_CONFIG:
640 		if (ddi_copyin((void *)arg, &conf,
641 		    sizeof (vs_config_t), 0) == -1)
642 			return (EFAULT);
643 		if (vscan_svc_configure(&conf) == -1)
644 			return (EINVAL);
645 		break;
646 
647 	case VS_IOCTL_MAX_REQ:
648 		if (ddi_copyout(&vs_nodes_max, (void *)arg,
649 		    sizeof (uint32_t), 0) == -1)
650 			return (EFAULT);
651 		break;
652 
653 	default:
654 		return (ENOTTY);
655 	}
656 
657 	return (0);
658 }
659 
660 
661 /*
662  * vscan_drv_create_node
663  *
664  * Create minor node with which vscan daemon will communicate
665  * to access a file. Invoked from vscan_svc before scan request
666  * sent up to daemon.
667  * Minor node 0 is reserved for daemon-driver synchronization
668  * and is created during attach.
669  * All minor nodes are removed during detach.
670  */
671 boolean_t
672 vscan_drv_create_node(int idx)
673 {
674 	char name[VS_NODENAME_LEN];
675 	boolean_t rc = B_TRUE;
676 
677 	mutex_enter(&vscan_drv_mutex);
678 
679 	if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
680 		(void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
681 		if (ddi_create_minor_node(vscan_drv_dip, name,
682 		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
683 			vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
684 		} else {
685 			rc = B_FALSE;
686 		}
687 		DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
688 	}
689 
690 	mutex_exit(&vscan_drv_mutex);
691 
692 	return (rc);
693 }
694