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