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