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