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