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
softmac_upper_constructor(void * buf,void * arg,int kmflag)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
softmac_upper_destructor(void * buf,void * arg)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)240 _info(struct modinfo *modinfop)
241 {
242 return (mod_info(&softmac_modlinkage, modinfop));
243 }
244
245 static int
softmac_cmn_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)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
softmac_mod_close(queue_t * rq,int flags __unused,cred_t * credp __unused)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
softmac_mod_rput(queue_t * rq,mblk_t * mp)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
softmac_mod_wput(queue_t * wq,mblk_t * mp)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
softmac_mod_wsrv(queue_t * wq)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
softmac_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
softmac_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
softmac_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
softmac_dedicated_rx(void * arg,mac_resource_handle_t mrh,mblk_t * mp,mac_header_info_t * mhip)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
softmac_drv_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)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
softmac_drv_close(queue_t * rq,int flags __unused,cred_t * credp __unused)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
softmac_drv_wput(queue_t * wq,mblk_t * mp)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
softmac_drv_wsrv(queue_t * wq)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