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