xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 02e56f3f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Data-Link Driver
31  */
32 
33 #include	<sys/conf.h>
34 #include	<sys/mkdev.h>
35 #include	<sys/modctl.h>
36 #include	<sys/stat.h>
37 #include	<sys/strsun.h>
38 #include	<sys/dld.h>
39 #include	<sys/dld_impl.h>
40 #include	<sys/dls_impl.h>
41 #include	<inet/common.h>
42 
43 /*
44  * dld control node state, one per open control node session.
45  */
46 typedef struct dld_ctl_str_s {
47 	minor_t cs_minor;
48 	queue_t *cs_wq;
49 } dld_ctl_str_t;
50 
51 static void	drv_init(void);
52 static int	drv_fini(void);
53 
54 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
55 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
56 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
57 
58 /*
59  * The following entry points are private to dld and are used for control
60  * operations only. The entry points exported to mac drivers are defined
61  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
62  */
63 static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
64 static int	drv_close(queue_t *);
65 
66 static void	drv_uw_put(queue_t *, mblk_t *);
67 static void	drv_uw_srv(queue_t *);
68 
69 dev_info_t	*dld_dip;		/* dev_info_t for the driver */
70 uint32_t	dld_opt = 0;		/* Global options */
71 static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
72 
73 static	struct	module_info	drv_info = {
74 	0,			/* mi_idnum */
75 	DLD_DRIVER_NAME,	/* mi_idname */
76 	0,			/* mi_minpsz */
77 	(64 * 1024),		/* mi_maxpsz */
78 	1,			/* mi_hiwat */
79 	0			/* mi_lowat */
80 };
81 
82 static	struct qinit		drv_ur_init = {
83 	NULL,			/* qi_putp */
84 	NULL,			/* qi_srvp */
85 	drv_open,		/* qi_qopen */
86 	drv_close,		/* qi_qclose */
87 	NULL,			/* qi_qadmin */
88 	&drv_info,		/* qi_minfo */
89 	NULL			/* qi_mstat */
90 };
91 
92 static	struct qinit		drv_uw_init = {
93 	(pfi_t)drv_uw_put,	/* qi_putp */
94 	(pfi_t)drv_uw_srv,	/* qi_srvp */
95 	NULL,			/* qi_qopen */
96 	NULL,			/* qi_qclose */
97 	NULL,			/* qi_qadmin */
98 	&drv_info,		/* qi_minfo */
99 	NULL			/* qi_mstat */
100 };
101 
102 static	struct streamtab	drv_stream = {
103 	&drv_ur_init,		/* st_rdinit */
104 	&drv_uw_init,		/* st_wrinit */
105 	NULL,			/* st_muxrinit */
106 	NULL			/* st_muxwinit */
107 };
108 
109 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
110     nodev, drv_getinfo, D_MP, &drv_stream);
111 
112 /*
113  * Module linkage information for the kernel.
114  */
115 
116 extern	struct mod_ops		mod_driverops;
117 
118 static	struct modldrv		drv_modldrv = {
119 	&mod_driverops,
120 	DLD_INFO,
121 	&drv_ops
122 };
123 
124 static	struct modlinkage	drv_modlinkage = {
125 	MODREV_1,
126 	&drv_modldrv,
127 	NULL
128 };
129 
130 int
131 _init(void)
132 {
133 	int	err;
134 
135 	drv_init();
136 
137 	if ((err = mod_install(&drv_modlinkage)) != 0)
138 		return (err);
139 
140 	return (0);
141 }
142 
143 int
144 _fini(void)
145 {
146 	int	err;
147 
148 	if ((err = mod_remove(&drv_modlinkage)) != 0)
149 		return (err);
150 
151 	if (drv_fini() != 0) {
152 		(void) mod_install(&drv_modlinkage);
153 		return (DDI_FAILURE);
154 	}
155 
156 	return (err);
157 }
158 
159 int
160 _info(struct modinfo *modinfop)
161 {
162 	return (mod_info(&drv_modlinkage, modinfop));
163 }
164 
165 
166 /*
167  * Initialize component modules.
168  */
169 static void
170 drv_init(void)
171 {
172 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
173 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
174 	dld_str_init();
175 }
176 
177 static int
178 drv_fini(void)
179 {
180 	int	err;
181 
182 	if ((err = dld_str_fini()) != 0)
183 		return (err);
184 
185 	vmem_destroy(dld_ctl_vmem);
186 	return (0);
187 }
188 
189 /*
190  * devo_getinfo: getinfo(9e)
191  */
192 /*ARGSUSED*/
193 static int
194 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
195 {
196 	if (dld_dip == NULL)
197 		return (DDI_FAILURE);
198 
199 	switch (cmd) {
200 	case DDI_INFO_DEVT2INSTANCE:
201 		*resp = (void *)0;
202 		break;
203 	case DDI_INFO_DEVT2DEVINFO:
204 		*resp = (void *)dld_dip;
205 		break;
206 	default:
207 		return (DDI_FAILURE);
208 	}
209 
210 	return (DDI_SUCCESS);
211 }
212 
213 /*
214  * Check properties to set options. (See dld.h for property definitions).
215  */
216 static void
217 drv_set_opt(dev_info_t *dip)
218 {
219 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
220 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
221 		dld_opt |= DLD_OPT_NO_FASTPATH;
222 	}
223 
224 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
225 	    DLD_PROP_NO_POLL, 0) != 0) {
226 		dld_opt |= DLD_OPT_NO_POLL;
227 	}
228 
229 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
230 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
231 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
232 	}
233 }
234 
235 /*
236  * devo_attach: attach(9e)
237  */
238 static int
239 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
240 {
241 	if (cmd != DDI_ATTACH)
242 		return (DDI_FAILURE);
243 
244 	ASSERT(ddi_get_instance(dip) == 0);
245 
246 	drv_set_opt(dip);
247 
248 	/*
249 	 * Create control node. DLPI provider nodes will be created on demand.
250 	 */
251 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
252 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
253 		return (DDI_FAILURE);
254 
255 	dld_dip = dip;
256 
257 	/*
258 	 * Log the fact that the driver is now attached.
259 	 */
260 	ddi_report_dev(dip);
261 	return (DDI_SUCCESS);
262 }
263 
264 /*
265  * devo_detach: detach(9e)
266  */
267 static int
268 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
269 {
270 	if (cmd != DDI_DETACH)
271 		return (DDI_FAILURE);
272 
273 	ASSERT(dld_dip == dip);
274 
275 	/*
276 	 * Remove the control node.
277 	 */
278 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
279 	dld_dip = NULL;
280 
281 	return (DDI_SUCCESS);
282 }
283 
284 /*
285  * dld control node open procedure.
286  */
287 /*ARGSUSED*/
288 static int
289 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
290 {
291 	dld_ctl_str_t	*ctls;
292 	minor_t		minor;
293 	queue_t *oq =	OTHERQ(rq);
294 
295 	if (sflag == MODOPEN)
296 		return (ENOTSUP);
297 
298 	/*
299 	 * This is a cloning driver and therefore each queue should only
300 	 * ever get opened once.
301 	 */
302 	if (rq->q_ptr != NULL)
303 		return (EBUSY);
304 
305 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
306 	if (minor == 0)
307 		return (ENOMEM);
308 
309 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
310 	if (ctls == NULL) {
311 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
312 		return (ENOMEM);
313 	}
314 
315 	ctls->cs_minor = minor;
316 	ctls->cs_wq = WR(rq);
317 
318 	rq->q_ptr = ctls;
319 	oq->q_ptr = ctls;
320 
321 	/*
322 	 * Enable the queue srv(9e) routine.
323 	 */
324 	qprocson(rq);
325 
326 	/*
327 	 * Construct a cloned dev_t to hand back.
328 	 */
329 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
330 	return (0);
331 }
332 
333 /*
334  * dld control node close procedure.
335  */
336 static int
337 drv_close(queue_t *rq)
338 {
339 	dld_ctl_str_t	*ctls;
340 
341 	ctls = rq->q_ptr;
342 	ASSERT(ctls != NULL);
343 
344 	/*
345 	 * Disable the queue srv(9e) routine.
346 	 */
347 	qprocsoff(rq);
348 
349 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
350 
351 	kmem_free(ctls, sizeof (dld_ctl_str_t));
352 
353 	return (0);
354 }
355 
356 /*
357  * DLDIOCATTR
358  */
359 static void
360 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
361 {
362 	dld_ioc_attr_t *diap;
363 	dls_vlan_t	*dvp = NULL;
364 	dls_link_t	*dlp = NULL;
365 	int		err;
366 	queue_t		*q = ctls->cs_wq;
367 
368 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
369 		goto failed;
370 
371 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
372 	diap->dia_name[IFNAMSIZ - 1] = '\0';
373 
374 	if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) {
375 		err = ENOENT;
376 		goto failed;
377 	}
378 
379 	dlp = dvp->dv_dlp;
380 	(void) strlcpy(diap->dia_dev, dlp->dl_dev, MAXNAMELEN);
381 	diap->dia_port = dlp->dl_port;
382 	diap->dia_vid = dvp->dv_id;
383 	diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max;
384 
385 	dls_vlan_rele(dvp);
386 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
387 	return;
388 
389 failed:
390 	ASSERT(err != 0);
391 	if (err == ENOENT) {
392 		char	devname[MAXNAMELEN];
393 		uint_t	instance;
394 		major_t	major;
395 
396 		/*
397 		 * Try to detect if the specified device is gldv3
398 		 * and return ENODEV if it is not.
399 		 */
400 		if (ddi_parse(diap->dia_name, devname, &instance) == 0 &&
401 		    (major = ddi_name_to_major(devname)) != (major_t)-1 &&
402 		    !GLDV3_DRV(major))
403 			err = ENODEV;
404 	}
405 	miocnak(q, mp, 0, err);
406 }
407 
408 
409 /*
410  * DLDIOCVLAN
411  */
412 typedef struct dld_ioc_vlan_state {
413 	uint_t		bytes_left;
414 	uint_t		count;
415 	dld_vlan_info_t	*vlanp;
416 } dld_ioc_vlan_state_t;
417 
418 static int
419 drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg)
420 {
421 	dld_ioc_vlan_state_t	*statep = arg;
422 
423 	if (statep->bytes_left < sizeof (dld_vlan_info_t))
424 		return (ENOSPC);
425 
426 	(void) strlcpy(statep->vlanp->dvi_name, dvp->dv_name, IFNAMSIZ);
427 	statep->vlanp->dvi_vid = dvp->dv_id;
428 
429 	statep->count++;
430 	statep->bytes_left -= sizeof (dld_vlan_info_t);
431 	statep->vlanp += 1;
432 	return (0);
433 }
434 
435 static void
436 drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
437 {
438 	dld_ioc_vlan_t		*divp;
439 	dld_ioc_vlan_state_t	state;
440 	int			err = EINVAL;
441 	queue_t			*q = ctls->cs_wq;
442 
443 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0)
444 		goto failed;
445 
446 	divp = (dld_ioc_vlan_t *)mp->b_cont->b_rptr;
447 	state.bytes_left = MBLKL(mp->b_cont) - sizeof (dld_ioc_vlan_t);
448 	state.count = 0;
449 	state.vlanp = (dld_vlan_info_t *)(divp + 1);
450 
451 	err = dls_vlan_walk(drv_ioc_vlan_info, &state);
452 	if (err != 0)
453 		goto failed;
454 
455 	divp->div_count = state.count;
456 	miocack(q, mp, sizeof (dld_ioc_vlan_t) +
457 	    state.count * sizeof (dld_vlan_info_t), 0);
458 	return;
459 
460 failed:
461 	ASSERT(err != 0);
462 	miocnak(q, mp, 0, err);
463 }
464 
465 
466 /*
467  * Process an IOCTL message received by the control node.
468  */
469 static void
470 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
471 {
472 	uint_t	cmd;
473 
474 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
475 	switch (cmd) {
476 	case DLDIOCATTR:
477 		drv_ioc_attr(ctls, mp);
478 		return;
479 	case DLDIOCVLAN:
480 		drv_ioc_vlan(ctls, mp);
481 		return;
482 	default:
483 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
484 		return;
485 	}
486 }
487 
488 /*
489  * Write side put routine of the dld control node.
490  */
491 static void
492 drv_uw_put(queue_t *q, mblk_t *mp)
493 {
494 	dld_ctl_str_t *ctls = q->q_ptr;
495 
496 	switch (mp->b_datap->db_type) {
497 	case M_IOCTL:
498 		drv_ioc(ctls, mp);
499 		break;
500 	default:
501 		freemsg(mp);
502 		break;
503 	}
504 }
505 
506 /*
507  * Write-side service procedure.
508  */
509 void
510 drv_uw_srv(queue_t *q)
511 {
512 	mblk_t *mp;
513 
514 	while (mp = getq(q))
515 		drv_uw_put(q, mp);
516 }
517