xref: /titanic_52/usr/src/uts/common/io/vscan/vscan_drv.c (revision d3d50737e566cade9a08d73d2af95105ac7cd960)
1911106dfSjm199354 /*
2911106dfSjm199354  * CDDL HEADER START
3911106dfSjm199354  *
4911106dfSjm199354  * The contents of this file are subject to the terms of the
5911106dfSjm199354  * Common Development and Distribution License (the "License").
6911106dfSjm199354  * You may not use this file except in compliance with the License.
7911106dfSjm199354  *
8911106dfSjm199354  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9911106dfSjm199354  * or http://www.opensolaris.org/os/licensing.
10911106dfSjm199354  * See the License for the specific language governing permissions
11911106dfSjm199354  * and limitations under the License.
12911106dfSjm199354  *
13911106dfSjm199354  * When distributing Covered Code, include this CDDL HEADER in each
14911106dfSjm199354  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15911106dfSjm199354  * If applicable, add the following below this CDDL HEADER, with the
16911106dfSjm199354  * fields enclosed by brackets "[]" replaced with your own identifying
17911106dfSjm199354  * information: Portions Copyright [yyyy] [name of copyright owner]
18911106dfSjm199354  *
19911106dfSjm199354  * CDDL HEADER END
20911106dfSjm199354  */
21911106dfSjm199354 
22911106dfSjm199354 /*
23*d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24911106dfSjm199354  * Use is subject to license terms.
25911106dfSjm199354  */
26911106dfSjm199354 
27911106dfSjm199354 #include <sys/stat.h>
28911106dfSjm199354 #include <sys/ddi.h>
29911106dfSjm199354 #include <sys/sunddi.h>
30911106dfSjm199354 #include <sys/time.h>
31911106dfSjm199354 #include <sys/varargs.h>
32911106dfSjm199354 #include <sys/conf.h>
33911106dfSjm199354 #include <sys/modctl.h>
34911106dfSjm199354 #include <sys/vnode.h>
35911106dfSjm199354 #include <fs/fs_subr.h>
36911106dfSjm199354 #include <sys/types.h>
37911106dfSjm199354 #include <sys/file.h>
38911106dfSjm199354 #include <sys/disp.h>
39911106dfSjm199354 #include <sys/vscan.h>
40911106dfSjm199354 #include <sys/policy.h>
41911106dfSjm199354 #include <sys/sdt.h>
42911106dfSjm199354 
43bfc848c6Sjm199354 
44bfc848c6Sjm199354 /* seconds to wait for daemon to reconnect before disabling */
45bfc848c6Sjm199354 #define	VS_DAEMON_WAIT_SEC	60
46bfc848c6Sjm199354 
47bfc848c6Sjm199354 /* length of minor node name - vscan%d */
48bfc848c6Sjm199354 #define	VS_NODENAME_LEN		16
49bfc848c6Sjm199354 
50bfc848c6Sjm199354 /* global variables - tunable via /etc/system */
51bfc848c6Sjm199354 uint32_t vs_reconnect_timeout = VS_DAEMON_WAIT_SEC;
52bfc848c6Sjm199354 extern uint32_t vs_nodes_max;	/* max in-progress scan requests */
53bfc848c6Sjm199354 
54bfc848c6Sjm199354 /*
55bfc848c6Sjm199354  * vscan_drv_state
56bfc848c6Sjm199354  *
57bfc848c6Sjm199354  * Operations on instance 0 represent vscand initiated state
58bfc848c6Sjm199354  * transition events:
59bfc848c6Sjm199354  * open(0) - vscand connect
60bfc848c6Sjm199354  * close(0) - vscan disconnect
61bfc848c6Sjm199354  * enable(0) - vscand enable (ready to hand requests)
62bfc848c6Sjm199354  * disable(0) - vscand disable (shutting down)
63bfc848c6Sjm199354  *
64bfc848c6Sjm199354  *   +------------------------+
65bfc848c6Sjm199354  *   | VS_DRV_UNCONFIG        |
66bfc848c6Sjm199354  *   +------------------------+
67bfc848c6Sjm199354  *      |           ^
68bfc848c6Sjm199354  *      | attach    | detach
69bfc848c6Sjm199354  *      v           |
70bfc848c6Sjm199354  *   +------------------------+
71bfc848c6Sjm199354  *   | VS_DRV_IDLE            |<------|
72bfc848c6Sjm199354  *   +------------------------+       |
73bfc848c6Sjm199354  *      |           ^                 |
74bfc848c6Sjm199354  *      | open(0)   | close(0)        |
75bfc848c6Sjm199354  *      v           |                 |
76bfc848c6Sjm199354  *   +------------------------+       |
77bfc848c6Sjm199354  *   | VS_DRV_CONNECTED       |<-|    |
78bfc848c6Sjm199354  *   +------------------------+  |    |
79bfc848c6Sjm199354  *      |           ^            |    |
80bfc848c6Sjm199354  *      | enable(0) | disable(0) |    |
81bfc848c6Sjm199354  *      v           |            |    |
82bfc848c6Sjm199354  *   +------------------------+  |    |
83bfc848c6Sjm199354  *   | VS_DRV_ENABLED         |  |    |
84bfc848c6Sjm199354  *   +------------------------+  |    |
85bfc848c6Sjm199354  *      |                        |    |
86bfc848c6Sjm199354  *      | close(0)            open(0) |
87bfc848c6Sjm199354  *      v                        |    |
88bfc848c6Sjm199354  *   +------------------------+  |    | timeout
89bfc848c6Sjm199354  *   | VS_DRV_DELAYED_DISABLE | --    |
90bfc848c6Sjm199354  *   +------------------------+	------|
91bfc848c6Sjm199354  *
92bfc848c6Sjm199354  */
93bfc848c6Sjm199354 typedef enum {
94bfc848c6Sjm199354 	VS_DRV_UNCONFIG,
95bfc848c6Sjm199354 	VS_DRV_IDLE,
96bfc848c6Sjm199354 	VS_DRV_CONNECTED,
97bfc848c6Sjm199354 	VS_DRV_ENABLED,
98bfc848c6Sjm199354 	VS_DRV_DELAYED_DISABLE
99bfc848c6Sjm199354 } vscan_drv_state_t;
100bfc848c6Sjm199354 static vscan_drv_state_t vscan_drv_state = VS_DRV_UNCONFIG;
101911106dfSjm199354 
102911106dfSjm199354 
103911106dfSjm199354 /*
104bfc848c6Sjm199354  * vscan_drv_inst_state
105911106dfSjm199354  *
106bfc848c6Sjm199354  * Instance 0 controls the state of the driver: vscan_drv_state.
107bfc848c6Sjm199354  * vscan_drv_inst_state[0] should NOT be used.
108bfc848c6Sjm199354  *
109bfc848c6Sjm199354  * vscan_drv_inst_state[n] represents the state of driver
110bfc848c6Sjm199354  * instance n, used by vscand to access file data for the
111bfc848c6Sjm199354  * scan request with index n in vscan_svc_reqs.
112bfc848c6Sjm199354  * Minor nodes are created as required then all are destroyed
113bfc848c6Sjm199354  * during driver detach.
114bfc848c6Sjm199354  *
115bfc848c6Sjm199354  *   +------------------------+
116bfc848c6Sjm199354  *   | VS_DRV_INST_UNCONFIG   |
117bfc848c6Sjm199354  *   +------------------------+
118bfc848c6Sjm199354  *      |                 ^
119bfc848c6Sjm199354  *      | create_node(n)  | detach
120bfc848c6Sjm199354  *      v                 |
121bfc848c6Sjm199354  *   +------------------------+
122bfc848c6Sjm199354  *   | VS_DRV_INST_INIT       |<-|
123bfc848c6Sjm199354  *   +------------------------+  |
124bfc848c6Sjm199354  *      |                        |
125bfc848c6Sjm199354  *      | open(n)                |
126bfc848c6Sjm199354  *      v                        |
127bfc848c6Sjm199354  *   +------------------------+  |
128bfc848c6Sjm199354  *   | VS_DRV_INST_OPEN       |--|
129bfc848c6Sjm199354  *   +------------------------+  |
130bfc848c6Sjm199354  *      |                        |
131bfc848c6Sjm199354  *      | read(n)                |
132bfc848c6Sjm199354  *      v                        | close(n)
133bfc848c6Sjm199354  *   +------------------------+  |
134bfc848c6Sjm199354  *   | VS_DRV_INST_READING    |--|
135bfc848c6Sjm199354  *   +------------------------+
136911106dfSjm199354  */
137911106dfSjm199354 typedef enum {
138bfc848c6Sjm199354 	VS_DRV_INST_UNCONFIG = 0, /* minor node not created */
139bfc848c6Sjm199354 	VS_DRV_INST_INIT,
140bfc848c6Sjm199354 	VS_DRV_INST_OPEN,
141bfc848c6Sjm199354 	VS_DRV_INST_READING
142bfc848c6Sjm199354 } vscan_drv_inst_state_t;
143911106dfSjm199354 
144bfc848c6Sjm199354 static vscan_drv_inst_state_t *vscan_drv_inst_state;
145bfc848c6Sjm199354 static int vscan_drv_inst_state_sz;
146911106dfSjm199354 
147911106dfSjm199354 static dev_info_t *vscan_drv_dip;
148911106dfSjm199354 static kmutex_t vscan_drv_mutex;
14953c11029Sjm199354 static kcondvar_t vscan_drv_cv; /* wait for daemon reconnect */
150911106dfSjm199354 
151911106dfSjm199354 /*
152911106dfSjm199354  * DDI entry points.
153911106dfSjm199354  */
154911106dfSjm199354 static int vscan_drv_attach(dev_info_t *, ddi_attach_cmd_t);
155911106dfSjm199354 static int vscan_drv_detach(dev_info_t *, ddi_detach_cmd_t);
156911106dfSjm199354 static int vscan_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
157911106dfSjm199354 static int vscan_drv_open(dev_t *, int, int, cred_t *);
158911106dfSjm199354 static int vscan_drv_close(dev_t, int, int, cred_t *);
159911106dfSjm199354 static int vscan_drv_read(dev_t, struct uio *, cred_t *);
160911106dfSjm199354 static int vscan_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
161911106dfSjm199354 
16253c11029Sjm199354 static boolean_t vscan_drv_in_use(void);
16353c11029Sjm199354 static void vscan_drv_delayed_disable(void);
164911106dfSjm199354 
165911106dfSjm199354 
166911106dfSjm199354 /*
167911106dfSjm199354  * module linkage info for the kernel
168911106dfSjm199354  */
169911106dfSjm199354 static struct cb_ops cbops = {
170911106dfSjm199354 	vscan_drv_open,		/* cb_open */
171911106dfSjm199354 	vscan_drv_close,	/* cb_close */
172911106dfSjm199354 	nodev,			/* cb_strategy */
173911106dfSjm199354 	nodev,			/* cb_print */
174911106dfSjm199354 	nodev,			/* cb_dump */
175911106dfSjm199354 	vscan_drv_read,		/* cb_read */
176911106dfSjm199354 	nodev,			/* cb_write */
177911106dfSjm199354 	vscan_drv_ioctl,	/* cb_ioctl */
178911106dfSjm199354 	nodev,			/* cb_devmap */
179911106dfSjm199354 	nodev,			/* cb_mmap */
180911106dfSjm199354 	nodev,			/* cb_segmap */
181911106dfSjm199354 	nochpoll,		/* cb_chpoll */
182911106dfSjm199354 	ddi_prop_op,		/* cb_prop_op */
183911106dfSjm199354 	NULL,			/* cb_streamtab */
184911106dfSjm199354 	D_MP,			/* cb_flag */
185911106dfSjm199354 	CB_REV,			/* cb_rev */
186911106dfSjm199354 	nodev,			/* cb_aread */
187911106dfSjm199354 	nodev,			/* cb_awrite */
188911106dfSjm199354 };
189911106dfSjm199354 
190911106dfSjm199354 static struct dev_ops devops = {
191911106dfSjm199354 	DEVO_REV,		/* devo_rev */
192911106dfSjm199354 	0,			/* devo_refcnt */
193911106dfSjm199354 	vscan_drv_getinfo,	/* devo_getinfo */
194911106dfSjm199354 	nulldev,		/* devo_identify */
195911106dfSjm199354 	nulldev,		/* devo_probe */
196911106dfSjm199354 	vscan_drv_attach,	/* devo_attach */
197911106dfSjm199354 	vscan_drv_detach,	/* devo_detach */
198911106dfSjm199354 	nodev,			/* devo_reset */
199911106dfSjm199354 	&cbops,			/* devo_cb_ops */
200911106dfSjm199354 	NULL,			/* devo_bus_ops */
201911106dfSjm199354 	NULL,			/* devo_power */
20219397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
203911106dfSjm199354 };
204911106dfSjm199354 
205911106dfSjm199354 static struct modldrv modldrv = {
206911106dfSjm199354 	&mod_driverops,		/* drv_modops */
207911106dfSjm199354 	"virus scanning",	/* drv_linkinfo */
208911106dfSjm199354 	&devops,
209911106dfSjm199354 };
210911106dfSjm199354 
211911106dfSjm199354 static struct modlinkage modlinkage = {
212911106dfSjm199354 
213911106dfSjm199354 	MODREV_1,	/* revision of the module, must be: MODREV_1	*/
214911106dfSjm199354 	&modldrv,	/* ptr to linkage structures			*/
215911106dfSjm199354 	NULL,
216911106dfSjm199354 };
217911106dfSjm199354 
218911106dfSjm199354 
219911106dfSjm199354 /*
220911106dfSjm199354  * _init
221911106dfSjm199354  */
222911106dfSjm199354 int
223911106dfSjm199354 _init(void)
224911106dfSjm199354 {
225911106dfSjm199354 	int rc;
226911106dfSjm199354 
227bfc848c6Sjm199354 	vscan_drv_inst_state_sz =
228bfc848c6Sjm199354 	    sizeof (vscan_drv_inst_state_t) * (vs_nodes_max + 1);
229911106dfSjm199354 
230bfc848c6Sjm199354 	if (vscan_door_init() != 0)
231911106dfSjm199354 		return (DDI_FAILURE);
232911106dfSjm199354 
233911106dfSjm199354 	if (vscan_svc_init() != 0) {
234911106dfSjm199354 		vscan_door_fini();
235911106dfSjm199354 		return (DDI_FAILURE);
236911106dfSjm199354 	}
237911106dfSjm199354 
238bfc848c6Sjm199354 	mutex_init(&vscan_drv_mutex, NULL, MUTEX_DRIVER, NULL);
239bfc848c6Sjm199354 	vscan_drv_inst_state = kmem_zalloc(vscan_drv_inst_state_sz, KM_SLEEP);
240bfc848c6Sjm199354 
241bfc848c6Sjm199354 	cv_init(&vscan_drv_cv, NULL, CV_DEFAULT, NULL);
242911106dfSjm199354 
243911106dfSjm199354 	if ((rc  = mod_install(&modlinkage)) != 0) {
244911106dfSjm199354 		vscan_door_fini();
245911106dfSjm199354 		vscan_svc_fini();
246bfc848c6Sjm199354 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
247bfc848c6Sjm199354 		cv_destroy(&vscan_drv_cv);
248911106dfSjm199354 		mutex_destroy(&vscan_drv_mutex);
249911106dfSjm199354 	}
250911106dfSjm199354 
251911106dfSjm199354 	return (rc);
252911106dfSjm199354 }
253911106dfSjm199354 
254911106dfSjm199354 
255911106dfSjm199354 /*
256911106dfSjm199354  * _info
257911106dfSjm199354  */
258911106dfSjm199354 int
259911106dfSjm199354 _info(struct modinfo *modinfop)
260911106dfSjm199354 {
261911106dfSjm199354 	return (mod_info(&modlinkage, modinfop));
262911106dfSjm199354 }
263911106dfSjm199354 
264911106dfSjm199354 
265911106dfSjm199354 /*
266911106dfSjm199354  * _fini
267911106dfSjm199354  */
268911106dfSjm199354 int
269911106dfSjm199354 _fini(void)
270911106dfSjm199354 {
271911106dfSjm199354 	int rc;
272911106dfSjm199354 
273911106dfSjm199354 	if (vscan_drv_in_use())
274911106dfSjm199354 		return (EBUSY);
275911106dfSjm199354 
276911106dfSjm199354 	if ((rc = mod_remove(&modlinkage)) == 0) {
277911106dfSjm199354 		vscan_door_fini();
278911106dfSjm199354 		vscan_svc_fini();
279bfc848c6Sjm199354 		kmem_free(vscan_drv_inst_state, vscan_drv_inst_state_sz);
28053c11029Sjm199354 		cv_destroy(&vscan_drv_cv);
281911106dfSjm199354 		mutex_destroy(&vscan_drv_mutex);
282911106dfSjm199354 	}
283911106dfSjm199354 
284911106dfSjm199354 	return (rc);
285911106dfSjm199354 }
286911106dfSjm199354 
287911106dfSjm199354 
288911106dfSjm199354 /*
289911106dfSjm199354  * DDI entry points.
290911106dfSjm199354  */
291911106dfSjm199354 
292911106dfSjm199354 /*
293911106dfSjm199354  * vscan_drv_getinfo
294911106dfSjm199354  */
295911106dfSjm199354 /* ARGSUSED */
296911106dfSjm199354 static int
297911106dfSjm199354 vscan_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
298911106dfSjm199354 {
299911106dfSjm199354 	ulong_t inst = getminor((dev_t)arg);
300911106dfSjm199354 
301911106dfSjm199354 	switch (cmd) {
302911106dfSjm199354 	case DDI_INFO_DEVT2DEVINFO:
303911106dfSjm199354 		*result = vscan_drv_dip;
304911106dfSjm199354 		return (DDI_SUCCESS);
305911106dfSjm199354 	case DDI_INFO_DEVT2INSTANCE:
306911106dfSjm199354 		*result = (void *)inst;
307911106dfSjm199354 		return (DDI_SUCCESS);
308911106dfSjm199354 	}
309911106dfSjm199354 	return (DDI_FAILURE);
310911106dfSjm199354 }
311911106dfSjm199354 
312911106dfSjm199354 
313911106dfSjm199354 /*
314911106dfSjm199354  * vscan_drv_attach
315911106dfSjm199354  */
316911106dfSjm199354 static int
317911106dfSjm199354 vscan_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
318911106dfSjm199354 {
319911106dfSjm199354 	if (cmd != DDI_ATTACH)
320911106dfSjm199354 		return (DDI_FAILURE);
321911106dfSjm199354 
322911106dfSjm199354 	if (ddi_get_instance(dip) != 0)
323911106dfSjm199354 		return (DDI_FAILURE);
324911106dfSjm199354 
325911106dfSjm199354 	vscan_drv_dip = dip;
326911106dfSjm199354 
32753c11029Sjm199354 	/* create minor node 0 for daemon-driver synchronization */
32853c11029Sjm199354 	if (vscan_drv_create_node(0) == B_FALSE)
329911106dfSjm199354 		return (DDI_FAILURE);
330911106dfSjm199354 
331bfc848c6Sjm199354 	vscan_drv_state = VS_DRV_IDLE;
332911106dfSjm199354 	return (DDI_SUCCESS);
333911106dfSjm199354 }
334911106dfSjm199354 
335911106dfSjm199354 
336911106dfSjm199354 /*
337911106dfSjm199354  * vscan_drv_detach
338911106dfSjm199354  */
339911106dfSjm199354 static int
340911106dfSjm199354 vscan_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
341911106dfSjm199354 {
342bfc848c6Sjm199354 	int i;
343bfc848c6Sjm199354 
344911106dfSjm199354 	if (cmd != DDI_DETACH)
345911106dfSjm199354 		return (DDI_FAILURE);
346911106dfSjm199354 
347911106dfSjm199354 	if (ddi_get_instance(dip) != 0)
348911106dfSjm199354 		return (DDI_FAILURE);
349911106dfSjm199354 
350911106dfSjm199354 	if (vscan_drv_in_use())
351911106dfSjm199354 		return (DDI_FAILURE);
352911106dfSjm199354 
35353c11029Sjm199354 	/* remove all minor nodes */
354911106dfSjm199354 	vscan_drv_dip = NULL;
355911106dfSjm199354 	ddi_remove_minor_node(dip, NULL);
356bfc848c6Sjm199354 	for (i = 0; i <= vs_nodes_max; i++)
357bfc848c6Sjm199354 		vscan_drv_inst_state[i] = VS_DRV_INST_UNCONFIG;
358911106dfSjm199354 
359bfc848c6Sjm199354 	vscan_drv_state = VS_DRV_UNCONFIG;
360911106dfSjm199354 	return (DDI_SUCCESS);
361911106dfSjm199354 }
362911106dfSjm199354 
363911106dfSjm199354 
364911106dfSjm199354 /*
365911106dfSjm199354  * vscan_drv_in_use
36653c11029Sjm199354  *
367bfc848c6Sjm199354  * If the driver state is not IDLE or UNCONFIG then the
368bfc848c6Sjm199354  * driver is in use. Otherwise, check the service interface
369bfc848c6Sjm199354  * (vscan_svc) to see if it is still in use - for example
370bfc848c6Sjm199354  * there there may be requests still in progress.
371911106dfSjm199354  */
372911106dfSjm199354 static boolean_t
373911106dfSjm199354 vscan_drv_in_use()
374911106dfSjm199354 {
375bfc848c6Sjm199354 	boolean_t in_use = B_FALSE;
37653c11029Sjm199354 
37753c11029Sjm199354 	mutex_enter(&vscan_drv_mutex);
378bfc848c6Sjm199354 	if ((vscan_drv_state != VS_DRV_IDLE) &&
379bfc848c6Sjm199354 	    (vscan_drv_state != VS_DRV_UNCONFIG)) {
380bfc848c6Sjm199354 		in_use = B_TRUE;
381bfc848c6Sjm199354 	}
38253c11029Sjm199354 	mutex_exit(&vscan_drv_mutex);
38353c11029Sjm199354 
384bfc848c6Sjm199354 	if (in_use)
385bfc848c6Sjm199354 		return (B_TRUE);
386bfc848c6Sjm199354 	else
387bfc848c6Sjm199354 		return (vscan_svc_in_use());
388911106dfSjm199354 }
389911106dfSjm199354 
390911106dfSjm199354 
391911106dfSjm199354 /*
392911106dfSjm199354  * vscan_drv_open
393bfc848c6Sjm199354  *
394bfc848c6Sjm199354  * If inst == 0, this is vscand initializing.
395bfc848c6Sjm199354  * If the driver is in DELAYED_DISABLE, ie vscand previously
396bfc848c6Sjm199354  * disconnected without a clean shutdown and the driver is
397bfc848c6Sjm199354  * waiting for a period to allow vscand to reconnect, signal
398bfc848c6Sjm199354  * vscan_drv_cv to cancel the delayed disable.
399bfc848c6Sjm199354  *
400bfc848c6Sjm199354  * If inst != 0, open the file associated with inst.
401911106dfSjm199354  */
402911106dfSjm199354 /* ARGSUSED */
403911106dfSjm199354 static int
404911106dfSjm199354 vscan_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
405911106dfSjm199354 {
406911106dfSjm199354 	int rc;
407911106dfSjm199354 	int inst = getminor(*devp);
408911106dfSjm199354 
409bfc848c6Sjm199354 	if ((inst < 0) || (inst > vs_nodes_max))
410911106dfSjm199354 		return (EINVAL);
411911106dfSjm199354 
412911106dfSjm199354 	/* check if caller has privilege for virus scanning */
413911106dfSjm199354 	if ((rc = secpolicy_vscan(credp)) != 0) {
414911106dfSjm199354 		DTRACE_PROBE1(vscan__priv, int, rc);
415911106dfSjm199354 		return (EPERM);
416911106dfSjm199354 	}
417911106dfSjm199354 
418911106dfSjm199354 	mutex_enter(&vscan_drv_mutex);
419911106dfSjm199354 	if (inst == 0) {
420bfc848c6Sjm199354 		switch (vscan_drv_state) {
421bfc848c6Sjm199354 		case VS_DRV_IDLE:
422bfc848c6Sjm199354 			vscan_drv_state = VS_DRV_CONNECTED;
423bfc848c6Sjm199354 			break;
424bfc848c6Sjm199354 		case VS_DRV_DELAYED_DISABLE:
42553c11029Sjm199354 			cv_signal(&vscan_drv_cv);
426bfc848c6Sjm199354 			vscan_drv_state = VS_DRV_CONNECTED;
427bfc848c6Sjm199354 			break;
428bfc848c6Sjm199354 		default:
429bfc848c6Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
430bfc848c6Sjm199354 			    int, vscan_drv_state);
431911106dfSjm199354 			mutex_exit(&vscan_drv_mutex);
432911106dfSjm199354 			return (EINVAL);
433911106dfSjm199354 		}
434bfc848c6Sjm199354 	} else {
435bfc848c6Sjm199354 		if ((vscan_drv_state != VS_DRV_ENABLED) ||
436bfc848c6Sjm199354 		    (vscan_drv_inst_state[inst] != VS_DRV_INST_INIT)) {
437bfc848c6Sjm199354 			mutex_exit(&vscan_drv_mutex);
438bfc848c6Sjm199354 			return (EINVAL);
439bfc848c6Sjm199354 		}
440bfc848c6Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
441911106dfSjm199354 	}
442911106dfSjm199354 	mutex_exit(&vscan_drv_mutex);
443911106dfSjm199354 
444911106dfSjm199354 	return (0);
445911106dfSjm199354 }
446911106dfSjm199354 
447911106dfSjm199354 
448911106dfSjm199354 /*
449911106dfSjm199354  * vscan_drv_close
450bfc848c6Sjm199354  *
451bfc848c6Sjm199354  * If inst == 0, this is vscand detaching.
452bfc848c6Sjm199354  * If the driver is in ENABLED state vscand has terminated without
453bfc848c6Sjm199354  * a clean shutdown (nod DISABLE received). Enter DELAYED_DISABLE
454bfc848c6Sjm199354  * state and initiate a delayed disable to allow vscand time to
455bfc848c6Sjm199354  * reconnect.
456bfc848c6Sjm199354  *
457bfc848c6Sjm199354  * If inst != 0, close the file associated with inst
458911106dfSjm199354  */
459911106dfSjm199354 /* ARGSUSED */
460911106dfSjm199354 static int
461911106dfSjm199354 vscan_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
462911106dfSjm199354 {
463911106dfSjm199354 	int i, inst = getminor(dev);
464911106dfSjm199354 
465bfc848c6Sjm199354 	if ((inst < 0) || (inst > vs_nodes_max))
466911106dfSjm199354 		return (EINVAL);
467911106dfSjm199354 
468911106dfSjm199354 	mutex_enter(&vscan_drv_mutex);
469bfc848c6Sjm199354 	if (inst != 0) {
470bfc848c6Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_INIT;
471bfc848c6Sjm199354 		mutex_exit(&vscan_drv_mutex);
472bfc848c6Sjm199354 		return (0);
473bfc848c6Sjm199354 	}
474911106dfSjm199354 
475bfc848c6Sjm199354 	/* instance 0 - daemon disconnect */
476bfc848c6Sjm199354 	if ((vscan_drv_state != VS_DRV_CONNECTED) &&
477bfc848c6Sjm199354 	    (vscan_drv_state != VS_DRV_ENABLED)) {
478bfc848c6Sjm199354 		DTRACE_PROBE1(vscan__drv__state__violation,
479bfc848c6Sjm199354 		    int, vscan_drv_state);
480bfc848c6Sjm199354 		mutex_exit(&vscan_drv_mutex);
481bfc848c6Sjm199354 		return (EINVAL);
482bfc848c6Sjm199354 	}
483bfc848c6Sjm199354 
484bfc848c6Sjm199354 	for (i = 1; i <= vs_nodes_max; i++) {
485bfc848c6Sjm199354 		if (vscan_drv_inst_state[i] != VS_DRV_INST_UNCONFIG)
486bfc848c6Sjm199354 			vscan_drv_inst_state[i] = VS_DRV_INST_INIT;
487bfc848c6Sjm199354 	}
488bfc848c6Sjm199354 
489bfc848c6Sjm199354 	if (vscan_drv_state == VS_DRV_CONNECTED) {
490bfc848c6Sjm199354 		vscan_drv_state = VS_DRV_IDLE;
491bfc848c6Sjm199354 	} else { /* VS_DRV_ENABLED */
492bfc848c6Sjm199354 		cmn_err(CE_WARN, "Detected vscand exit without clean shutdown");
49353c11029Sjm199354 		if (thread_create(NULL, 0, vscan_drv_delayed_disable,
49453c11029Sjm199354 		    0, 0, &p0, TS_RUN, minclsyspri) == NULL) {
495bfc848c6Sjm199354 			vscan_svc_disable();
496bfc848c6Sjm199354 			vscan_drv_state = VS_DRV_IDLE;
497911106dfSjm199354 		} else {
498bfc848c6Sjm199354 			vscan_drv_state = VS_DRV_DELAYED_DISABLE;
499bfc848c6Sjm199354 		}
500911106dfSjm199354 	}
501911106dfSjm199354 	mutex_exit(&vscan_drv_mutex);
502911106dfSjm199354 
503bfc848c6Sjm199354 	vscan_svc_scan_abort();
504bfc848c6Sjm199354 	vscan_door_close();
505911106dfSjm199354 	return (0);
506911106dfSjm199354 }
507911106dfSjm199354 
508911106dfSjm199354 
509911106dfSjm199354 /*
51053c11029Sjm199354  * vscan_drv_delayed_disable
51153c11029Sjm199354  *
51253c11029Sjm199354  * Invoked from vscan_drv_close if the daemon disconnects
51353c11029Sjm199354  * without first sending disable (e.g. daemon crashed).
514bfc848c6Sjm199354  * Delays for vs_reconnect_timeout before disabling, to allow
51553c11029Sjm199354  * the daemon to reconnect. During this time, scan requests
51653c11029Sjm199354  * will be processed locally (see vscan_svc.c)
51753c11029Sjm199354  */
51853c11029Sjm199354 static void
51953c11029Sjm199354 vscan_drv_delayed_disable(void)
52053c11029Sjm199354 {
52153c11029Sjm199354 	mutex_enter(&vscan_drv_mutex);
522*d3d50737SRafael Vanoni 	(void) cv_reltimedwait(&vscan_drv_cv, &vscan_drv_mutex,
523*d3d50737SRafael Vanoni 	    SEC_TO_TICK(vs_reconnect_timeout), TR_CLOCK_TICK);
52453c11029Sjm199354 
525bfc848c6Sjm199354 	if (vscan_drv_state == VS_DRV_DELAYED_DISABLE) {
52653c11029Sjm199354 		vscan_svc_disable();
527bfc848c6Sjm199354 		vscan_drv_state = VS_DRV_IDLE;
528bfc848c6Sjm199354 	} else {
529bfc848c6Sjm199354 		DTRACE_PROBE(vscan__reconnect);
53053c11029Sjm199354 	}
53153c11029Sjm199354 	mutex_exit(&vscan_drv_mutex);
53253c11029Sjm199354 }
53353c11029Sjm199354 
53453c11029Sjm199354 
53553c11029Sjm199354 /*
536911106dfSjm199354  * vscan_drv_read
537911106dfSjm199354  */
538911106dfSjm199354 /* ARGSUSED */
539911106dfSjm199354 static int
540911106dfSjm199354 vscan_drv_read(dev_t dev, struct uio *uiop, cred_t *credp)
541911106dfSjm199354 {
542911106dfSjm199354 	int rc;
543911106dfSjm199354 	int inst = getminor(dev);
544911106dfSjm199354 	vnode_t *vp;
545911106dfSjm199354 
546bfc848c6Sjm199354 	if ((inst <= 0) || (inst > vs_nodes_max))
547911106dfSjm199354 		return (EINVAL);
548911106dfSjm199354 
549911106dfSjm199354 	mutex_enter(&vscan_drv_mutex);
550bfc848c6Sjm199354 	if ((vscan_drv_state != VS_DRV_ENABLED) ||
551bfc848c6Sjm199354 	    (vscan_drv_inst_state[inst] != VS_DRV_INST_OPEN)) {
552911106dfSjm199354 		mutex_exit(&vscan_drv_mutex);
553911106dfSjm199354 		return (EINVAL);
554911106dfSjm199354 	}
555bfc848c6Sjm199354 	vscan_drv_inst_state[inst] = VS_DRV_INST_READING;
556911106dfSjm199354 	mutex_exit(&vscan_drv_mutex);
557911106dfSjm199354 
558911106dfSjm199354 	if ((vp = vscan_svc_get_vnode(inst)) == NULL)
559911106dfSjm199354 		return (EINVAL);
560911106dfSjm199354 
561911106dfSjm199354 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
562911106dfSjm199354 	rc = VOP_READ(vp, uiop, 0, kcred, NULL);
563911106dfSjm199354 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
564911106dfSjm199354 
565911106dfSjm199354 	mutex_enter(&vscan_drv_mutex);
566bfc848c6Sjm199354 	if (vscan_drv_inst_state[inst] == VS_DRV_INST_READING)
567bfc848c6Sjm199354 		vscan_drv_inst_state[inst] = VS_DRV_INST_OPEN;
568911106dfSjm199354 	mutex_exit(&vscan_drv_mutex);
569911106dfSjm199354 
570911106dfSjm199354 	return (rc);
571911106dfSjm199354 }
572911106dfSjm199354 
573911106dfSjm199354 
574911106dfSjm199354 /*
575911106dfSjm199354  * vscan_drv_ioctl
576bfc848c6Sjm199354  *
577bfc848c6Sjm199354  * Process ioctls from vscand:
578bfc848c6Sjm199354  * VS_IOCTL_ENABLE - vscand is ready to handle scan requests,
579bfc848c6Sjm199354  *    enable VFS interface.
580bfc848c6Sjm199354  * VS_IOCTL_DISABLE - vscand is shutting down, disable VFS interface
581bfc848c6Sjm199354  * VS_IOCTL_RESULT - scan response data
582bfc848c6Sjm199354  * VS_IOCTL_CONFIG - configuration data from vscand
583bfc848c6Sjm199354  * VS_IOCTL_MAX_REQ - provide the max request idx to vscand,
584bfc848c6Sjm199354  *    to allow vscand to set appropriate resource allocation limits
585911106dfSjm199354  */
586911106dfSjm199354 /* ARGSUSED */
587911106dfSjm199354 static int
588911106dfSjm199354 vscan_drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
589911106dfSjm199354 	cred_t *credp, int *rvalp)
590911106dfSjm199354 {
591911106dfSjm199354 	int inst = getminor(dev);
592911106dfSjm199354 	vs_config_t conf;
593bfc848c6Sjm199354 	vs_scan_rsp_t rsp;
594911106dfSjm199354 
595911106dfSjm199354 	if (inst != 0)
596911106dfSjm199354 		return (EINVAL);
597911106dfSjm199354 
598911106dfSjm199354 	switch (cmd) {
599bfc848c6Sjm199354 	case VS_IOCTL_ENABLE:
600911106dfSjm199354 		mutex_enter(&vscan_drv_mutex);
601bfc848c6Sjm199354 		if (vscan_drv_state != VS_DRV_CONNECTED) {
602bfc848c6Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
603bfc848c6Sjm199354 			    int, vscan_drv_state);
604911106dfSjm199354 			mutex_exit(&vscan_drv_mutex);
605911106dfSjm199354 			return (EINVAL);
606911106dfSjm199354 		}
607bfc848c6Sjm199354 		if ((vscan_door_open((int)arg) != 0) ||
608bfc848c6Sjm199354 		    (vscan_svc_enable() != 0)) {
609bfc848c6Sjm199354 			mutex_exit(&vscan_drv_mutex);
610bfc848c6Sjm199354 			return (EINVAL);
611bfc848c6Sjm199354 		}
612bfc848c6Sjm199354 		vscan_drv_state = VS_DRV_ENABLED;
613911106dfSjm199354 		mutex_exit(&vscan_drv_mutex);
614911106dfSjm199354 		break;
615bfc848c6Sjm199354 
616bfc848c6Sjm199354 	case VS_IOCTL_DISABLE:
617bfc848c6Sjm199354 		mutex_enter(&vscan_drv_mutex);
618bfc848c6Sjm199354 		if (vscan_drv_state != VS_DRV_ENABLED) {
619bfc848c6Sjm199354 			DTRACE_PROBE1(vscan__drv__state__violation,
620bfc848c6Sjm199354 			    int, vscan_drv_state);
621bfc848c6Sjm199354 			mutex_exit(&vscan_drv_mutex);
622bfc848c6Sjm199354 			return (EINVAL);
623bfc848c6Sjm199354 		}
62453c11029Sjm199354 		vscan_svc_disable();
625bfc848c6Sjm199354 		vscan_drv_state = VS_DRV_CONNECTED;
626bfc848c6Sjm199354 		mutex_exit(&vscan_drv_mutex);
627911106dfSjm199354 		break;
628bfc848c6Sjm199354 
629bfc848c6Sjm199354 	case VS_IOCTL_RESULT:
630bfc848c6Sjm199354 		if (ddi_copyin((void *)arg, &rsp,
631bfc848c6Sjm199354 		    sizeof (vs_scan_rsp_t), 0) == -1)
632bfc848c6Sjm199354 			return (EFAULT);
633bfc848c6Sjm199354 		else
634bfc848c6Sjm199354 			vscan_svc_scan_result(&rsp);
635bfc848c6Sjm199354 		break;
636bfc848c6Sjm199354 
637bfc848c6Sjm199354 	case VS_IOCTL_CONFIG:
638911106dfSjm199354 		if (ddi_copyin((void *)arg, &conf,
639911106dfSjm199354 		    sizeof (vs_config_t), 0) == -1)
640911106dfSjm199354 			return (EFAULT);
641911106dfSjm199354 		if (vscan_svc_configure(&conf) == -1)
642911106dfSjm199354 			return (EINVAL);
643911106dfSjm199354 		break;
644bfc848c6Sjm199354 
645bfc848c6Sjm199354 	case VS_IOCTL_MAX_REQ:
646bfc848c6Sjm199354 		if (ddi_copyout(&vs_nodes_max, (void *)arg,
647bfc848c6Sjm199354 		    sizeof (uint32_t), 0) == -1)
648bfc848c6Sjm199354 			return (EFAULT);
649bfc848c6Sjm199354 		break;
650bfc848c6Sjm199354 
651911106dfSjm199354 	default:
652911106dfSjm199354 		return (ENOTTY);
653911106dfSjm199354 	}
654911106dfSjm199354 
655911106dfSjm199354 	return (0);
656911106dfSjm199354 }
65753c11029Sjm199354 
65853c11029Sjm199354 
65953c11029Sjm199354 /*
66053c11029Sjm199354  * vscan_drv_create_node
66153c11029Sjm199354  *
66253c11029Sjm199354  * Create minor node with which vscan daemon will communicate
66353c11029Sjm199354  * to access a file. Invoked from vscan_svc before scan request
66453c11029Sjm199354  * sent up to daemon.
66553c11029Sjm199354  * Minor node 0 is reserved for daemon-driver synchronization
66653c11029Sjm199354  * and is created during attach.
66753c11029Sjm199354  * All minor nodes are removed during detach.
66853c11029Sjm199354  */
66953c11029Sjm199354 boolean_t
67053c11029Sjm199354 vscan_drv_create_node(int idx)
67153c11029Sjm199354 {
672bfc848c6Sjm199354 	char name[VS_NODENAME_LEN];
673bfc848c6Sjm199354 	boolean_t rc = B_TRUE;
67453c11029Sjm199354 
67553c11029Sjm199354 	mutex_enter(&vscan_drv_mutex);
67653c11029Sjm199354 
677bfc848c6Sjm199354 	if (vscan_drv_inst_state[idx] == VS_DRV_INST_UNCONFIG) {
678bfc848c6Sjm199354 		(void) snprintf(name, VS_NODENAME_LEN, "vscan%d", idx);
67953c11029Sjm199354 		if (ddi_create_minor_node(vscan_drv_dip, name,
68053c11029Sjm199354 		    S_IFCHR, idx, DDI_PSEUDO, 0) == DDI_SUCCESS) {
681bfc848c6Sjm199354 			vscan_drv_inst_state[idx] = VS_DRV_INST_INIT;
682bfc848c6Sjm199354 		} else {
683bfc848c6Sjm199354 			rc = B_FALSE;
68453c11029Sjm199354 		}
685bfc848c6Sjm199354 		DTRACE_PROBE2(vscan__minor__node, int, idx, int, rc);
68653c11029Sjm199354 	}
68753c11029Sjm199354 
68853c11029Sjm199354 	mutex_exit(&vscan_drv_mutex);
68953c11029Sjm199354 
69053c11029Sjm199354 	return (rc);
69153c11029Sjm199354 }
692