xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_dev.c (revision a484bfa2079bc90573ca9a036def78c0cc8aa809)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include <sys/types.h>
28 #include <inet/common.h>
29 #include <sys/stropts.h>
30 #include <sys/modctl.h>
31 #include <sys/dld.h>
32 #include <sys/softmac_impl.h>
33 
34 dev_info_t		*softmac_dip = NULL;
35 static kmem_cache_t	*softmac_upper_cachep;
36 
37 /*
38  * This function is a generic open(9E) entry point into the softmac for
39  * both the softmac module and the softmac driver.
40  */
41 static int softmac_cmn_open(queue_t *, dev_t *, int, int, cred_t *);
42 
43 /*
44  * The following softmac_mod_xxx() functions are (9E) entry point functions for
45  * the softmac module.
46  */
47 static int softmac_mod_close(queue_t *, int, cred_t *);
48 static int softmac_mod_rput(queue_t *, mblk_t *);
49 static int softmac_mod_wput(queue_t *, mblk_t *);
50 static int softmac_mod_wsrv(queue_t *);
51 
52 /*
53  * The following softmac_drv_xxx() functions are (9E) entry point functions for
54  * the softmac driver.
55  */
56 static int softmac_drv_open(queue_t *, dev_t *, int, int, cred_t *);
57 static int softmac_drv_close(queue_t *, int, cred_t *);
58 static int softmac_drv_wput(queue_t *, mblk_t *);
59 static int softmac_drv_wsrv(queue_t *);
60 
61 static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
62 static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
63 static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
64 
65 static struct module_info softmac_modinfo = {
66 	0,
67 	SOFTMAC_DEV_NAME,
68 	0,
69 	INFPSZ,
70 	65536,
71 	1024
72 };
73 
74 /*
75  * hi-water mark is 1 because of the flow control mechanism implemented in
76  * dld.  Refer to the comments in dld_str.c for details.
77  */
78 static struct module_info softmac_dld_modinfo = {
79 	0,
80 	SOFTMAC_DEV_NAME,
81 	0,
82 	INFPSZ,
83 	1,
84 	0
85 };
86 
87 static struct qinit softmac_urinit = {
88 	softmac_mod_rput,		/* qi_putp */
89 	NULL,				/* qi_srvp */
90 	softmac_cmn_open,		/* qi_qopen */
91 	softmac_mod_close,		/* qi_qclose */
92 	NULL,				/* qi_qadmin */
93 	&softmac_modinfo		/* qi_minfo */
94 };
95 
96 static struct qinit softmac_uwinit = {
97 	softmac_mod_wput,		/* qi_putp */
98 	softmac_mod_wsrv,		/* qi_srvp */
99 	NULL,				/* qi_qopen */
100 	NULL,				/* qi_qclose */
101 	NULL,				/* qi_qadmin */
102 	&softmac_modinfo		/* qi_minfo */
103 };
104 
105 static struct streamtab softmac_tab = {
106 	&softmac_urinit,	/* st_rdinit */
107 	&softmac_uwinit		/* st_wrinit */
108 };
109 
110 DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
111     softmac_detach, nodev, softmac_info, D_MP, &softmac_tab,
112     ddi_quiesce_not_supported);
113 
114 static struct qinit softmac_dld_r_qinit = {
115 	NULL, NULL, softmac_drv_open, softmac_drv_close, NULL,
116 	&softmac_dld_modinfo
117 };
118 
119 static struct qinit softmac_dld_w_qinit = {
120 	softmac_drv_wput, softmac_drv_wsrv, NULL, NULL, NULL,
121 	&softmac_dld_modinfo
122 };
123 
124 static struct fmodsw softmac_fmodsw = {
125 	SOFTMAC_DEV_NAME,
126 	&softmac_tab,
127 	D_MP
128 };
129 
130 static struct modldrv softmac_modldrv = {
131 	&mod_driverops,
132 	"softmac driver",
133 	&softmac_ops
134 };
135 
136 static struct modlstrmod softmac_modlstrmod = {
137 	&mod_strmodops,
138 	"softmac module",
139 	&softmac_fmodsw
140 };
141 
142 static struct modlinkage softmac_modlinkage = {
143 	MODREV_1,
144 	&softmac_modlstrmod,
145 	&softmac_modldrv,
146 	NULL
147 };
148 
149 static void softmac_dedicated_rx(void *, mac_resource_handle_t, mblk_t *,
150     mac_header_info_t *);
151 
152 /*ARGSUSED*/
153 static int
154 softmac_upper_constructor(void *buf, void *arg, int kmflag)
155 {
156 	softmac_upper_t	*sup = buf;
157 
158 	bzero(buf, sizeof (softmac_upper_t));
159 
160 	mutex_init(&sup->su_mutex, NULL, MUTEX_DEFAULT, NULL);
161 	cv_init(&sup->su_cv, NULL, CV_DEFAULT, NULL);
162 	mutex_init(&sup->su_disp_mutex, NULL, MUTEX_DEFAULT, NULL);
163 	cv_init(&sup->su_disp_cv, NULL, CV_DEFAULT, NULL);
164 	list_create(&sup->su_req_list, sizeof (softmac_switch_req_t),
165 	    offsetof(softmac_switch_req_t, ssq_req_list_node));
166 	return (0);
167 }
168 
169 /*ARGSUSED*/
170 static void
171 softmac_upper_destructor(void *buf, void *arg)
172 {
173 	softmac_upper_t	*sup = buf;
174 
175 	ASSERT(sup->su_slp == NULL);
176 	ASSERT(sup->su_pending_head == NULL && sup->su_pending_tail == NULL);
177 	ASSERT(!sup->su_dlpi_pending);
178 	ASSERT(!sup->su_active);
179 	ASSERT(!sup->su_closing);
180 	ASSERT(sup->su_tx_flow_mp == NULL);
181 	ASSERT(sup->su_tx_inprocess == 0);
182 	ASSERT(sup->su_mode == SOFTMAC_UNKNOWN);
183 	ASSERT(!sup->su_tx_busy);
184 	ASSERT(!sup->su_bound);
185 	ASSERT(!sup->su_taskq_scheduled);
186 	ASSERT(sup->su_tx_notify_func == NULL);
187 	ASSERT(sup->su_tx_notify_arg == NULL);
188 	ASSERT(list_is_empty(&sup->su_req_list));
189 
190 	list_destroy(&sup->su_req_list);
191 	mutex_destroy(&sup->su_mutex);
192 	cv_destroy(&sup->su_cv);
193 	mutex_destroy(&sup->su_disp_mutex);
194 	cv_destroy(&sup->su_disp_cv);
195 }
196 
197 int
198 _init(void)
199 {
200 	int	err;
201 
202 	mac_init_ops(NULL, SOFTMAC_DEV_NAME);
203 	softmac_init();
204 
205 	softmac_upper_cachep = kmem_cache_create("softmac_upper_cache",
206 	    sizeof (softmac_upper_t), 0, softmac_upper_constructor,
207 	    softmac_upper_destructor, NULL, NULL, NULL, 0);
208 	ASSERT(softmac_upper_cachep != NULL);
209 
210 	if ((err = mod_install(&softmac_modlinkage)) != 0) {
211 		softmac_fini();
212 		return (err);
213 	}
214 
215 	return (0);
216 }
217 
218 int
219 _fini(void)
220 {
221 	int err;
222 
223 	if (softmac_busy())
224 		return (EBUSY);
225 
226 	if ((err = mod_remove(&softmac_modlinkage)) != 0)
227 		return (err);
228 
229 	kmem_cache_destroy(softmac_upper_cachep);
230 	softmac_fini();
231 
232 	return (0);
233 }
234 
235 int
236 _info(struct modinfo *modinfop)
237 {
238 	return (mod_info(&softmac_modlinkage, modinfop));
239 }
240 
241 static int
242 softmac_cmn_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
243 {
244 	softmac_lower_t	*slp;
245 	/*
246 	 * This is a self-cloning driver so that each queue should only
247 	 * get opened once.
248 	 */
249 	if (rq->q_ptr != NULL)
250 		return (EBUSY);
251 
252 	if (sflag == MODOPEN) {
253 		/*
254 		 * This is the softmac module pushed over an underlying
255 		 * legacy device.  Initialize the lower structure.
256 		 */
257 		if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL)
258 			return (ENOMEM);
259 
260 		slp->sl_wq = WR(rq);
261 		cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL);
262 		mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL);
263 		slp->sl_pending_prim = DL_PRIM_INVAL;
264 		rq->q_ptr = WR(rq)->q_ptr = slp;
265 		qprocson(rq);
266 		return (0);
267 	}
268 
269 	/*
270 	 * Regular device open of a softmac DLPI node.  We modify
271 	 * the queues' q_qinfo pointer such that all future STREAMS
272 	 * operations will go through another set of entry points
273 	 */
274 	rq->q_qinfo = &softmac_dld_r_qinit;
275 	WR(rq)->q_qinfo = &softmac_dld_w_qinit;
276 	return (softmac_drv_open(rq, devp, flag, sflag, credp));
277 }
278 
279 /* ARGSUSED */
280 static int
281 softmac_mod_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
282 {
283 	softmac_lower_t	*slp = rq->q_ptr;
284 
285 	/*
286 	 * Call the appropriate delete routine depending on whether this is
287 	 * a module or device.
288 	 */
289 	ASSERT(WR(rq)->q_next != NULL);
290 
291 	qprocsoff(rq);
292 
293 	slp->sl_softmac = NULL;
294 	slp->sl_lh = NULL;
295 
296 	ASSERT(slp->sl_ack_mp == NULL);
297 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
298 	ASSERT(slp->sl_pending_ioctl == B_FALSE);
299 
300 	cv_destroy(&slp->sl_cv);
301 	mutex_destroy(&slp->sl_mutex);
302 
303 	kmem_free(slp, sizeof (*slp));
304 	return (0);
305 }
306 
307 static int
308 softmac_mod_rput(queue_t *rq, mblk_t *mp)
309 {
310 	softmac_lower_t		*slp = rq->q_ptr;
311 	softmac_lower_rxinfo_t	*rxinfo;
312 	union DL_primitives	*dlp;
313 
314 	/*
315 	 * This is the softmac module.
316 	 */
317 	ASSERT(WR(rq)->q_next != NULL);
318 	ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
319 
320 	switch (DB_TYPE(mp)) {
321 	case M_DATA: {
322 
323 		/*
324 		 * If sl_rxinfo is non-NULL. This is dedicated-lower-stream
325 		 * created for fastpath. Directly call the rx callback.
326 		 */
327 		if ((rxinfo = slp->sl_rxinfo) != NULL) {
328 			rxinfo->slr_rx(rxinfo->slr_arg, NULL, mp, NULL);
329 			break;
330 		}
331 
332 		/*
333 		 * A shared-lower-stream. Some driver starts to send up
334 		 * packets even it not in the DL_IDLE state, where
335 		 * sl_softmac is not set yet. Drop the packet in this case.
336 		 */
337 		if (slp->sl_softmac == NULL) {
338 			freemsg(mp);
339 			return (0);
340 		}
341 
342 		/*
343 		 * If this message is looped back from the legacy devices,
344 		 * drop it as the Nemo framework will be responsible for
345 		 * looping it back by the mac_txloop() function.
346 		 */
347 		if (mp->b_flag & MSGNOLOOP) {
348 			freemsg(mp);
349 			return (0);
350 		}
351 
352 		/*
353 		 * This is the most common case.
354 		 */
355 		if (DB_REF(mp) == 1) {
356 			ASSERT(slp->sl_softmac != NULL);
357 			mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
358 			return (0);
359 		} else {
360 			softmac_rput_process_data(slp, mp);
361 		}
362 		break;
363 	}
364 	case M_PROTO:
365 	case M_PCPROTO:
366 		if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
367 			freemsg(mp);
368 			break;
369 		}
370 		dlp = (union DL_primitives *)mp->b_rptr;
371 		if (dlp->dl_primitive == DL_UNITDATA_IND) {
372 
373 			if ((rxinfo = slp->sl_rxinfo) != NULL) {
374 				softmac_dedicated_rx(slp->sl_sup, NULL, mp,
375 				    NULL);
376 				break;
377 			}
378 
379 			cmn_err(CE_WARN, "got unexpected %s message",
380 			    dl_primstr(DL_UNITDATA_IND));
381 			freemsg(mp);
382 			break;
383 		}
384 		/*FALLTHROUGH*/
385 	default:
386 		softmac_rput_process_notdata(rq, slp->sl_sup, mp);
387 		break;
388 	}
389 	return (0);
390 }
391 
392 static int
393 softmac_mod_wput(queue_t *wq, mblk_t *mp)
394 {
395 	/*
396 	 * This is the softmac module
397 	 */
398 	ASSERT(wq->q_next != NULL);
399 
400 	switch (DB_TYPE(mp)) {
401 	case M_IOCTL: {
402 		struct iocblk		*ioc = (struct iocblk *)mp->b_rptr;
403 
404 		switch (ioc->ioc_cmd) {
405 		case SMAC_IOC_START: {
406 			softmac_lower_t		*slp = wq->q_ptr;
407 			smac_ioc_start_t	*arg;
408 
409 			if (ioc->ioc_count != sizeof (*arg)) {
410 				miocnak(wq, mp, 0, EINVAL);
411 				break;
412 			}
413 
414 			/*
415 			 * Assign the devname and perstream handle of the
416 			 * specific lower stream and return it as a part
417 			 * of the ioctl.
418 			 */
419 			arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
420 			arg->si_slp = slp;
421 			miocack(wq, mp, sizeof (*arg), 0);
422 			break;
423 		}
424 		default:
425 			miocnak(wq, mp, 0, EINVAL);
426 			break;
427 		}
428 		break;
429 	}
430 	default:
431 		freemsg(mp);
432 		break;
433 	}
434 	return (0);
435 }
436 
437 static int
438 softmac_mod_wsrv(queue_t *wq)
439 {
440 	softmac_lower_t *slp = wq->q_ptr;
441 
442 	/*
443 	 * This is the softmac module
444 	 */
445 	ASSERT(wq->q_next != NULL);
446 
447 	/*
448 	 * Inform that the tx resource is available; mac_tx_update() will
449 	 * inform all the upper streams sharing this lower stream.
450 	 */
451 	if (slp->sl_sup != NULL)
452 		qenable(slp->sl_sup->su_wq);
453 	else if (slp->sl_softmac != NULL)
454 		mac_tx_update(slp->sl_softmac->smac_mh);
455 	return (0);
456 }
457 
458 static int
459 softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
460 {
461 	ASSERT(ddi_get_instance(dip) == 0);
462 
463 	if (cmd != DDI_ATTACH)
464 		return (DDI_FAILURE);
465 
466 	softmac_dip = dip;
467 
468 	return (DDI_SUCCESS);
469 }
470 
471 /* ARGSUSED */
472 static int
473 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
474 {
475 	if (cmd != DDI_DETACH)
476 		return (DDI_FAILURE);
477 
478 	softmac_dip = NULL;
479 	return (DDI_SUCCESS);
480 }
481 
482 /* ARGSUSED */
483 static int
484 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
485 {
486 	switch (infocmd) {
487 	case DDI_INFO_DEVT2DEVINFO:
488 		if (softmac_dip != NULL) {
489 			*result = softmac_dip;
490 			return (DDI_SUCCESS);
491 		}
492 		break;
493 
494 	case DDI_INFO_DEVT2INSTANCE:
495 		*result = NULL;
496 		return (DDI_SUCCESS);
497 
498 	}
499 
500 	return (DDI_FAILURE);
501 }
502 
503 /*ARGSUSED*/
504 static void
505 softmac_dedicated_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
506     mac_header_info_t *mhip)
507 {
508 	queue_t *rq = ((softmac_upper_t *)arg)->su_rq;
509 
510 	if (canputnext(rq))
511 		putnext(rq, mp);
512 	else
513 		freemsg(mp);
514 }
515 
516 /*ARGSUSED*/
517 static int
518 softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
519 {
520 	softmac_upper_t	*sup = NULL;
521 	softmac_t	*softmac;
522 	int		err = 0;
523 
524 	/*
525 	 * This is a softmac device created for a legacy device, find the
526 	 * associated softmac and initialize the softmac_upper_t structure.
527 	 */
528 	if ((err = softmac_hold(*devp, &softmac)) != 0)
529 		return (err);
530 
531 	sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP);
532 	if (sup == NULL) {
533 		err = ENOMEM;
534 		goto fail;
535 	}
536 
537 	ASSERT(list_is_empty(&sup->su_req_list));
538 
539 	if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) {
540 		err = ENOMEM;
541 		goto fail;
542 	}
543 
544 	sup->su_rq = rq;
545 	sup->su_wq = WR(rq);
546 	sup->su_softmac = softmac;
547 	sup->su_mode = SOFTMAC_UNKNOWN;
548 
549 	sup->su_rxinfo.slr_arg = sup;
550 	sup->su_rxinfo.slr_rx = softmac_dedicated_rx;
551 	sup->su_direct_rxinfo.slr_arg = sup;
552 	sup->su_direct_rxinfo.slr_rx = softmac_dedicated_rx;
553 
554 	if ((err = dld_str_open(rq, devp, sup)) != 0) {
555 		freeb(sup->su_tx_flow_mp);
556 		sup->su_tx_flow_mp = NULL;
557 		goto fail;
558 	}
559 
560 	return (0);
561 
562 fail:
563 	if (sup != NULL)
564 		kmem_cache_free(softmac_upper_cachep, sup);
565 	softmac_rele(softmac);
566 	return (err);
567 }
568 
569 /* ARGSUSED */
570 static int
571 softmac_drv_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
572 {
573 	softmac_upper_t	*sup = dld_str_private(rq);
574 	softmac_t	*softmac = sup->su_softmac;
575 
576 	ASSERT(WR(rq)->q_next == NULL);
577 
578 	qprocsoff(rq);
579 
580 	ASSERT(sup->su_tx_inprocess == 0);
581 
582 	/*
583 	 * Wait until the pending request are processed by the worker thread.
584 	 */
585 	mutex_enter(&sup->su_disp_mutex);
586 	sup->su_closing = B_TRUE;
587 	while (sup->su_dlpi_pending)
588 		cv_wait(&sup->su_disp_cv, &sup->su_disp_mutex);
589 	mutex_exit(&sup->su_disp_mutex);
590 
591 	softmac_upperstream_close(sup);
592 
593 	if (sup->su_tx_flow_mp != NULL) {
594 		freeb(sup->su_tx_flow_mp);
595 		sup->su_tx_flow_mp = NULL;
596 	}
597 
598 	if (sup->su_active) {
599 		mutex_enter(&softmac->smac_active_mutex);
600 		softmac->smac_nactive--;
601 		mutex_exit(&softmac->smac_active_mutex);
602 		sup->su_active = B_FALSE;
603 	}
604 
605 	sup->su_bound = B_FALSE;
606 	sup->su_softmac = NULL;
607 	sup->su_closing = B_FALSE;
608 
609 	kmem_cache_free(softmac_upper_cachep, sup);
610 
611 	softmac_rele(softmac);
612 	return (dld_str_close(rq));
613 }
614 
615 static int
616 softmac_drv_wput(queue_t *wq, mblk_t *mp)
617 {
618 	softmac_upper_t	*sup = dld_str_private(wq);
619 	t_uscalar_t	prim;
620 
621 	ASSERT(wq->q_next == NULL);
622 
623 	switch (DB_TYPE(mp)) {
624 	case M_DATA:
625 	case M_MULTIDATA:
626 		softmac_wput_data(sup, mp);
627 		break;
628 	case M_PROTO:
629 	case M_PCPROTO:
630 
631 		if (MBLKL(mp) < sizeof (t_uscalar_t)) {
632 			freemsg(mp);
633 			return (0);
634 		}
635 
636 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
637 		if (prim == DL_UNITDATA_REQ) {
638 			softmac_wput_data(sup, mp);
639 			return (0);
640 		}
641 
642 		softmac_wput_nondata(sup, mp);
643 		break;
644 	default:
645 		softmac_wput_nondata(sup, mp);
646 		break;
647 	}
648 	return (0);
649 }
650 
651 static int
652 softmac_drv_wsrv(queue_t *wq)
653 {
654 	softmac_upper_t	*sup = dld_str_private(wq);
655 
656 	ASSERT(wq->q_next == NULL);
657 
658 	mutex_enter(&sup->su_mutex);
659 	if (sup->su_mode != SOFTMAC_FASTPATH) {
660 		/*
661 		 * Bump su_tx_inprocess so that su_mode won't change.
662 		 */
663 		sup->su_tx_inprocess++;
664 		mutex_exit(&sup->su_mutex);
665 		dld_wsrv(wq);
666 		mutex_enter(&sup->su_mutex);
667 		if (--sup->su_tx_inprocess == 0)
668 			cv_signal(&sup->su_cv);
669 	} else if (sup->su_tx_busy && SOFTMAC_CANPUTNEXT(sup->su_slp->sl_wq)) {
670 		/*
671 		 * The flow-conctol of the dedicated-lower-stream is
672 		 * relieved. If DLD_CAPAB_DIRECT is enabled, call tx_notify
673 		 * callback to relieve the flow-control of the specific client,
674 		 * otherwise relieve the flow-control of all the upper-stream
675 		 * using the traditional STREAM mechanism.
676 		 */
677 		if (sup->su_tx_notify_func != NULL) {
678 			sup->su_tx_inprocess++;
679 			mutex_exit(&sup->su_mutex);
680 			sup->su_tx_notify_func(sup->su_tx_notify_arg,
681 			    (mac_tx_cookie_t)sup);
682 			mutex_enter(&sup->su_mutex);
683 			if (--sup->su_tx_inprocess == 0)
684 				cv_signal(&sup->su_cv);
685 		}
686 		ASSERT(sup->su_tx_flow_mp == NULL);
687 		VERIFY((sup->su_tx_flow_mp = getq(wq)) != NULL);
688 		sup->su_tx_busy = B_FALSE;
689 	}
690 	mutex_exit(&sup->su_mutex);
691 	return (0);
692 }
693