xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_fp.c (revision 5d460eafffba936e81c4dd5ebe0f59b238f09121)
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  * Softmac data-path switching:
28  *
29  * - Fast-path model
30  *
31  * When the softmac fast-path is used, a dedicated lower-stream
32  * will be opened over the legacy device for each IP/ARP (upper-)stream
33  * over the softMAC, and all DLPI messages (including control messages
34  * and data messages) will be exchanged between the upper-stream and
35  * the corresponding lower-stream directly. Therefore, the data
36  * demultiplexing, filtering and classification processing will be done
37  * by the lower-stream, and the GLDv3 DLS/MAC layer processing will be
38  * no longer needed.
39  *
40  * - Slow-path model
41  *
42  * Some GLDv3 features requires the GLDv3 DLS/MAC layer processing to
43  * not be bypassed to assure its function correctness. For example,
44  * softmac fast-path must be disabled to support GLDv3 VNIC functionality.
45  * In this case, a shared lower-stream will be opened over the legacy
46  * device, which is responsible for implementing the GLDv3 callbacks
47  * and passing RAW data messages between the legacy devices and the GLDv3
48  * framework.
49  *
50  * By default, the softmac fast-path mode will be used to assure the
51  * performance; MAC clients will be able to request to disable the softmac
52  * fast-path mode to support certain features, and if that succeeds,
53  * the system will fallback to the slow-path softmac data-path model.
54  *
55  *
56  * The details of the softmac data fast-path model is stated as below
57  *
58  * 1. When a stream is opened on a softMAC, the softmac module will takes
59  *    over the DLPI processing on this stream;
60  *
61  * 2. For IP/ARP streams over a softMAC, softmac data fast-path will be
62  *    used by default, unless fast-path is disabled by any MAC client
63  *    explicitly. The softmac module first identifies an IP/ARP stream
64  *    by seeing whether there is a SIOCSLIFNAME ioctl sent from upstream,
65  *    if there is one, this stream is either an IP or an ARP stream
66  *    and will use fast-path potentially;
67  *
68  * 3. When the softmac fast-path is used, an dedicated lower-stream will
69  *    be setup for each IP/ARP stream (1-1 mapping). From that point on,
70  *    all control and data messages will be exchanged between the IP/ARP
71  *    upper-stream and the legacy device through this dedicated
72  *    lower-stream. As a result, the DLS/MAC layer processing in GLDv3
73  *    will be skipped, and this greatly improves the performance;
74  *
75  * 4. When the softmac data fast-path is disabled by a MAC client (e.g.,
76  *    by a VNIC), all the IP/ARP upper streams will try to switch from
77  *    the fast-path to the slow-path. The dedicated lower-stream will be
78  *    destroyed, and all the control and data-messages will go through the
79  *    existing GLDv3 code path and (in the end) the shared lower-stream;
80  *
81  * 5. On the other hand, when the last MAC client cancels its fast-path
82  *    disable request, all the IP/ARP streams will try to switch back to
83  *    the fast-path mode;
84  *
85  * Step 5 and 6 both rely on the data-path mode switching process
86  * described below:
87  *
88  * 1) To switch the softmac data-path mode (between fast-path and slow-path),
89  *    softmac will first send a DL_NOTE_REPLUMB DL_NOTIFY_IND message
90  *    upstream over each IP/ARP streams that needs data-path mode switching;
91  *
92  * 2) When IP receives this DL_NOTE_REPLUMB message, it will bring down
93  *    all the IP interfaces on the corresponding ill (IP Lower level
94  *    structure), and bring up those interfaces over again; this will in
95  *    turn cause the ARP to "replumb" the interface.
96  *
97  *    During the replumb process, both IP and ARP will send downstream the
98  *    necessary DL_DISABMULTI_REQ and DL_UNBIND_REQ messages and cleanup
99  *    the old state of the underlying softMAC, following with the necessary
100  *    DL_BIND_REQ and DL_ENABMULTI_REQ messages to setup the new state.
101  *    Between the cleanup and re-setup process, IP/ARP will also send down
102  *    a DL_NOTE_REPLUMB_DONE DL_NOTIFY_CONF messages to the softMAC to
103  *    indicate the *switching point*;
104  *
105  * 3) When softmac receives the DL_NOTE_REPLUMB_DONE message, it either
106  *    creates or destroys the dedicated lower-stream (depending on which
107  *    data-path mode the softMAC switches to), and change the softmac
108  *    data-path mode. From then on, softmac will process all the succeeding
109  *    control messages (including the DL_BIND_REQ and DL_ENABMULTI_REQ
110  *    messages) and data messages based on new data-path mode.
111  */
112 
113 #include <sys/types.h>
114 #include <sys/disp.h>
115 #include <sys/callb.h>
116 #include <sys/sysmacros.h>
117 #include <sys/file.h>
118 #include <sys/vlan.h>
119 #include <sys/dld.h>
120 #include <sys/sockio.h>
121 #include <sys/softmac_impl.h>
122 
123 static kmutex_t		softmac_taskq_lock;
124 static kcondvar_t	softmac_taskq_cv;
125 static list_t		softmac_taskq_list;	/* List of softmac_upper_t */
126 boolean_t		softmac_taskq_quit;
127 boolean_t		softmac_taskq_done;
128 
129 static void		softmac_taskq_dispatch();
130 static int		softmac_fastpath_setup(softmac_upper_t *);
131 static mac_tx_cookie_t	softmac_fastpath_wput_data(softmac_upper_t *, mblk_t *,
132 			    uintptr_t, uint16_t);
133 static void		softmac_datapath_switch_done(softmac_upper_t *);
134 
135 void
136 softmac_fp_init()
137 {
138 	mutex_init(&softmac_taskq_lock, NULL, MUTEX_DRIVER, NULL);
139 	cv_init(&softmac_taskq_cv, NULL, CV_DRIVER, NULL);
140 
141 	softmac_taskq_quit = B_FALSE;
142 	softmac_taskq_done = B_FALSE;
143 	list_create(&softmac_taskq_list, sizeof (softmac_upper_t),
144 	    offsetof(softmac_upper_t, su_taskq_list_node));
145 	(void) thread_create(NULL, 0, softmac_taskq_dispatch, NULL, 0,
146 	    &p0, TS_RUN, minclsyspri);
147 }
148 
149 void
150 softmac_fp_fini()
151 {
152 	/*
153 	 * Request the softmac_taskq thread to quit and wait for it to be done.
154 	 */
155 	mutex_enter(&softmac_taskq_lock);
156 	softmac_taskq_quit = B_TRUE;
157 	cv_signal(&softmac_taskq_cv);
158 	while (!softmac_taskq_done)
159 		cv_wait(&softmac_taskq_cv, &softmac_taskq_lock);
160 	mutex_exit(&softmac_taskq_lock);
161 	list_destroy(&softmac_taskq_list);
162 
163 	mutex_destroy(&softmac_taskq_lock);
164 	cv_destroy(&softmac_taskq_cv);
165 }
166 
167 static boolean_t
168 check_ip_above(queue_t *q)
169 {
170 	queue_t		*next_q;
171 	boolean_t	ret = B_TRUE;
172 
173 	claimstr(q);
174 	next_q = q->q_next;
175 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
176 		ret = B_FALSE;
177 	releasestr(q);
178 	return (ret);
179 }
180 
181 /* ARGSUSED */
182 static int
183 softmac_capab_perim(softmac_upper_t *sup, void *data, uint_t flags)
184 {
185 	switch (flags) {
186 	case DLD_ENABLE:
187 		mutex_enter(&sup->su_mutex);
188 		break;
189 	case DLD_DISABLE:
190 		mutex_exit(&sup->su_mutex);
191 		break;
192 	case DLD_QUERY:
193 		return (MUTEX_HELD(&sup->su_mutex));
194 	}
195 	return (0);
196 }
197 
198 /* ARGSUSED */
199 static mac_tx_notify_handle_t
200 softmac_client_tx_notify(void *txcb, mac_tx_notify_t func, void *arg)
201 {
202 	return (NULL);
203 }
204 
205 static int
206 softmac_capab_direct(softmac_upper_t *sup, void *data, uint_t flags)
207 {
208 	dld_capab_direct_t	*direct = data;
209 	softmac_lower_t		*slp = sup->su_slp;
210 
211 	ASSERT(MUTEX_HELD(&sup->su_mutex));
212 
213 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
214 
215 	switch (flags) {
216 	case DLD_ENABLE:
217 		if (sup->su_direct)
218 			return (0);
219 
220 		sup->su_direct_rxinfo.slr_rx = (softmac_rx_t)direct->di_rx_cf;
221 		sup->su_direct_rxinfo.slr_arg = direct->di_rx_ch;
222 		slp->sl_rxinfo = &sup->su_direct_rxinfo;
223 		direct->di_tx_df = (uintptr_t)softmac_fastpath_wput_data;
224 		direct->di_tx_dh = sup;
225 
226 		/*
227 		 * We relying on the STREAM flow-control to backenable
228 		 * the IP stream. Therefore, no notify callback needs to
229 		 * be registered. But IP requires this to be a valid function
230 		 * pointer.
231 		 */
232 		direct->di_tx_cb_df = (uintptr_t)softmac_client_tx_notify;
233 		direct->di_tx_cb_dh = NULL;
234 		sup->su_direct = B_TRUE;
235 		return (0);
236 
237 	case DLD_DISABLE:
238 		if (!sup->su_direct)
239 			return (0);
240 
241 		slp->sl_rxinfo = &sup->su_rxinfo;
242 		sup->su_direct = B_FALSE;
243 		return (0);
244 	}
245 	return (ENOTSUP);
246 }
247 
248 static int
249 softmac_dld_capab(softmac_upper_t *sup, uint_t type, void *data, uint_t flags)
250 {
251 	int	err;
252 
253 	/*
254 	 * Don't enable direct callback capabilities unless the caller is
255 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
256 	 * the stack initiates capability disable, but due to races, the
257 	 * module insertion may complete before the capability disable
258 	 * completes. So we limit the check to DLD_ENABLE case.
259 	 */
260 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
261 	    !check_ip_above(sup->su_rq)) {
262 		return (ENOTSUP);
263 	}
264 
265 	switch (type) {
266 	case DLD_CAPAB_DIRECT:
267 		err = softmac_capab_direct(sup, data, flags);
268 		break;
269 
270 	case DLD_CAPAB_PERIM:
271 		err = softmac_capab_perim(sup, data, flags);
272 		break;
273 
274 	default:
275 		err = ENOTSUP;
276 		break;
277 	}
278 	return (err);
279 }
280 
281 static void
282 softmac_capability_advertise(softmac_upper_t *sup, mblk_t *mp)
283 {
284 	dl_capability_ack_t	*dlap;
285 	dl_capability_sub_t	*dlsp;
286 	t_uscalar_t		subsize;
287 	uint8_t			*ptr;
288 	queue_t			*q = sup->su_wq;
289 	mblk_t			*mp1;
290 	softmac_t		*softmac = sup->su_softmac;
291 	boolean_t		dld_capable = B_FALSE;
292 	boolean_t		hcksum_capable = B_FALSE;
293 	boolean_t		zcopy_capable = B_FALSE;
294 	boolean_t		mdt_capable = B_FALSE;
295 
296 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
297 
298 	/*
299 	 * Initially assume no capabilities.
300 	 */
301 	subsize = 0;
302 
303 	/*
304 	 * Direct capability negotiation interface between IP and softmac
305 	 */
306 	if (check_ip_above(sup->su_rq)) {
307 		dld_capable = B_TRUE;
308 		subsize += sizeof (dl_capability_sub_t) +
309 		    sizeof (dl_capab_dld_t);
310 	}
311 
312 	/*
313 	 * Check if checksum offload is supported on this MAC.
314 	 */
315 	if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
316 		hcksum_capable = B_TRUE;
317 		subsize += sizeof (dl_capability_sub_t) +
318 		    sizeof (dl_capab_hcksum_t);
319 	}
320 
321 	/*
322 	 * Check if zerocopy is supported on this interface.
323 	 */
324 	if (!(softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY)) {
325 		zcopy_capable = B_TRUE;
326 		subsize += sizeof (dl_capability_sub_t) +
327 		    sizeof (dl_capab_zerocopy_t);
328 	}
329 
330 	if (softmac->smac_mdt) {
331 		mdt_capable = B_TRUE;
332 		subsize += sizeof (dl_capability_sub_t) +
333 		    sizeof (dl_capab_mdt_t);
334 	}
335 
336 	/*
337 	 * If there are no capabilities to advertise or if we
338 	 * can't allocate a response, send a DL_ERROR_ACK.
339 	 */
340 	if ((subsize == 0) || (mp1 = reallocb(mp,
341 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
342 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
343 		return;
344 	}
345 
346 	mp = mp1;
347 	DB_TYPE(mp) = M_PROTO;
348 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
349 	bzero(mp->b_rptr, MBLKL(mp));
350 	dlap = (dl_capability_ack_t *)mp->b_rptr;
351 	dlap->dl_primitive = DL_CAPABILITY_ACK;
352 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
353 	dlap->dl_sub_length = subsize;
354 	ptr = (uint8_t *)&dlap[1];
355 
356 	/*
357 	 * IP polling interface.
358 	 */
359 	if (dld_capable) {
360 		dl_capab_dld_t		dld;
361 
362 		dlsp = (dl_capability_sub_t *)ptr;
363 		dlsp->dl_cap = DL_CAPAB_DLD;
364 		dlsp->dl_length = sizeof (dl_capab_dld_t);
365 		ptr += sizeof (dl_capability_sub_t);
366 
367 		bzero(&dld, sizeof (dl_capab_dld_t));
368 		dld.dld_version = DLD_CURRENT_VERSION;
369 		dld.dld_capab = (uintptr_t)softmac_dld_capab;
370 		dld.dld_capab_handle = (uintptr_t)sup;
371 
372 		dlcapabsetqid(&(dld.dld_mid), sup->su_rq);
373 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
374 		ptr += sizeof (dl_capab_dld_t);
375 	}
376 
377 	/*
378 	 * TCP/IP checksum offload.
379 	 */
380 	if (hcksum_capable) {
381 		dl_capab_hcksum_t	hcksum;
382 
383 		dlsp = (dl_capability_sub_t *)ptr;
384 
385 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
386 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
387 		ptr += sizeof (dl_capability_sub_t);
388 
389 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
390 		hcksum.hcksum_version = HCKSUM_VERSION_1;
391 		hcksum.hcksum_txflags = softmac->smac_hcksum_txflags;
392 		dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
393 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
394 		ptr += sizeof (dl_capab_hcksum_t);
395 	}
396 
397 	/*
398 	 * Zero copy
399 	 */
400 	if (zcopy_capable) {
401 		dl_capab_zerocopy_t	zcopy;
402 
403 		dlsp = (dl_capability_sub_t *)ptr;
404 
405 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
406 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
407 		ptr += sizeof (dl_capability_sub_t);
408 
409 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
410 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
411 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
412 		dlcapabsetqid(&(zcopy.zerocopy_mid), sup->su_rq);
413 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
414 		ptr += sizeof (dl_capab_zerocopy_t);
415 	}
416 
417 	/*
418 	 * MDT
419 	 */
420 	if (mdt_capable) {
421 		dl_capab_mdt_t mdt;
422 
423 		dlsp = (dl_capability_sub_t *)ptr;
424 
425 		dlsp->dl_cap = DL_CAPAB_MDT;
426 		dlsp->dl_length = sizeof (dl_capab_mdt_t);
427 		ptr += sizeof (dl_capability_sub_t);
428 
429 		bzero(&mdt, sizeof (dl_capab_mdt_t));
430 		mdt.mdt_version = MDT_VERSION_2;
431 		mdt.mdt_flags = DL_CAPAB_MDT_ENABLE;
432 		mdt.mdt_hdr_head = softmac->smac_mdt_capab.mdt_hdr_head;
433 		mdt.mdt_hdr_tail = softmac->smac_mdt_capab.mdt_hdr_tail;
434 		mdt.mdt_max_pld = softmac->smac_mdt_capab.mdt_max_pld;
435 		mdt.mdt_span_limit = softmac->smac_mdt_capab.mdt_span_limit;
436 		dlcapabsetqid(&(mdt.mdt_mid), sup->su_rq);
437 		bcopy(&mdt, ptr, sizeof (dl_capab_mdt_t));
438 		ptr += sizeof (dl_capab_mdt_t);
439 	}
440 
441 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
442 	qreply(q, mp);
443 }
444 
445 static void
446 softmac_capability_req(softmac_upper_t *sup, mblk_t *mp)
447 {
448 	dl_capability_req_t	*dlp = (dl_capability_req_t *)mp->b_rptr;
449 	dl_capability_sub_t	*sp;
450 	size_t			size, len;
451 	offset_t		off, end;
452 	t_uscalar_t		dl_err;
453 	queue_t			*q = sup->su_wq;
454 
455 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
456 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
457 		dl_err = DL_BADPRIM;
458 		goto failed;
459 	}
460 
461 	if (!sup->su_bound) {
462 		dl_err = DL_OUTSTATE;
463 		goto failed;
464 	}
465 
466 	/*
467 	 * This request is overloaded. If there are no requested capabilities
468 	 * then we just want to acknowledge with all the capabilities we
469 	 * support. Otherwise we enable the set of capabilities requested.
470 	 */
471 	if (dlp->dl_sub_length == 0) {
472 		softmac_capability_advertise(sup, mp);
473 		return;
474 	}
475 
476 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
477 		dl_err = DL_BADPRIM;
478 		goto failed;
479 	}
480 
481 	dlp->dl_primitive = DL_CAPABILITY_ACK;
482 
483 	off = dlp->dl_sub_offset;
484 	len = dlp->dl_sub_length;
485 
486 	/*
487 	 * Walk the list of capabilities to be enabled.
488 	 */
489 	for (end = off + len; off < end; ) {
490 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
491 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
492 
493 		if (off + size > end ||
494 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
495 			dl_err = DL_BADPRIM;
496 			goto failed;
497 		}
498 
499 		switch (sp->dl_cap) {
500 		/*
501 		 * TCP/IP checksum offload to hardware.
502 		 */
503 		case DL_CAPAB_HCKSUM: {
504 			dl_capab_hcksum_t *hcksump;
505 			dl_capab_hcksum_t hcksum;
506 
507 			hcksump = (dl_capab_hcksum_t *)&sp[1];
508 			/*
509 			 * Copy for alignment.
510 			 */
511 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
512 			dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
513 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
514 			break;
515 		}
516 
517 		default:
518 			break;
519 		}
520 
521 		off += size;
522 	}
523 	qreply(q, mp);
524 	return;
525 failed:
526 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
527 }
528 
529 static void
530 softmac_bind_req(softmac_upper_t *sup, mblk_t *mp)
531 {
532 	softmac_lower_t	*slp = sup->su_slp;
533 	softmac_t	*softmac = sup->su_softmac;
534 	mblk_t		*ackmp, *mp1;
535 	int		err;
536 
537 	if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
538 		freemsg(mp);
539 		return;
540 	}
541 
542 	/*
543 	 * Allocate ackmp incase the underlying driver does not ack timely.
544 	 */
545 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
546 		dlerrorack(sup->su_wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM);
547 		return;
548 	}
549 
550 	err = softmac_output(slp, mp, DL_BIND_REQ, DL_BIND_ACK, &ackmp);
551 	if (ackmp != NULL) {
552 		freemsg(mp1);
553 	} else {
554 		/*
555 		 * The driver does not ack timely.
556 		 */
557 		ASSERT(err == ENOMSG);
558 		ackmp = mp1;
559 	}
560 	if (err != 0)
561 		goto failed;
562 
563 	/*
564 	 * Enable capabilities the underlying driver claims to support.
565 	 */
566 	if ((err = softmac_capab_enable(slp)) != 0)
567 		goto failed;
568 
569 	/*
570 	 * Check whether this softmac is already marked as exclusively used,
571 	 * e.g., an aggregation is created over it. Fail the BIND_REQ if so.
572 	 */
573 	mutex_enter(&softmac->smac_active_mutex);
574 	if (softmac->smac_active) {
575 		mutex_exit(&softmac->smac_active_mutex);
576 		err = EBUSY;
577 		goto failed;
578 	}
579 	softmac->smac_nactive++;
580 	sup->su_active = B_TRUE;
581 	mutex_exit(&softmac->smac_active_mutex);
582 	sup->su_bound = B_TRUE;
583 
584 	qreply(sup->su_wq, ackmp);
585 	return;
586 failed:
587 	if (err != 0) {
588 		dlerrorack(sup->su_wq, ackmp, DL_BIND_REQ, DL_SYSERR, err);
589 		return;
590 	}
591 }
592 
593 static void
594 softmac_unbind_req(softmac_upper_t *sup, mblk_t *mp)
595 {
596 	softmac_lower_t	*slp = sup->su_slp;
597 	softmac_t	*softmac = sup->su_softmac;
598 	mblk_t		*ackmp, *mp1;
599 	int		err;
600 
601 	if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
602 		freemsg(mp);
603 		return;
604 	}
605 
606 	if (!sup->su_bound) {
607 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
608 		return;
609 	}
610 
611 	/*
612 	 * Allocate ackmp incase the underlying driver does not ack timely.
613 	 */
614 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
615 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM);
616 		return;
617 	}
618 
619 	err = softmac_output(slp, mp, DL_UNBIND_REQ, DL_OK_ACK, &ackmp);
620 	if (ackmp != NULL) {
621 		freemsg(mp1);
622 	} else {
623 		/*
624 		 * The driver does not ack timely.
625 		 */
626 		ASSERT(err == ENOMSG);
627 		ackmp = mp1;
628 	}
629 	if (err != 0) {
630 		dlerrorack(sup->su_wq, ackmp, DL_UNBIND_REQ, DL_SYSERR, err);
631 		return;
632 	}
633 
634 	sup->su_bound = B_FALSE;
635 
636 	mutex_enter(&softmac->smac_active_mutex);
637 	if (sup->su_active) {
638 		ASSERT(!softmac->smac_active);
639 		softmac->smac_nactive--;
640 		sup->su_active = B_FALSE;
641 	}
642 	mutex_exit(&softmac->smac_active_mutex);
643 
644 done:
645 	qreply(sup->su_wq, ackmp);
646 }
647 
648 /*
649  * Process the non-data mblk.
650  */
651 static void
652 softmac_wput_single_nondata(softmac_upper_t *sup, mblk_t *mp)
653 {
654 	softmac_t *softmac = sup->su_softmac;
655 	softmac_lower_t	*slp = sup->su_slp;
656 	unsigned char	dbtype;
657 	t_uscalar_t	prim;
658 
659 	dbtype = DB_TYPE(mp);
660 	switch (dbtype) {
661 	case M_IOCTL:
662 	case M_CTL: {
663 		uint32_t	expected_mode;
664 
665 		if (((struct iocblk *)(mp->b_rptr))->ioc_cmd != SIOCSLIFNAME)
666 			break;
667 
668 		/*
669 		 * Nak the M_IOCTL based on the STREAMS specification.
670 		 */
671 		if (dbtype == M_IOCTL)
672 			miocnak(sup->su_wq, mp, 0, EINVAL);
673 
674 		/*
675 		 * This stream is either IP or ARP. See whether
676 		 * we need to setup a dedicated-lower-stream for it.
677 		 */
678 		mutex_enter(&softmac->smac_fp_mutex);
679 
680 		expected_mode = DATAPATH_MODE(softmac);
681 		if (expected_mode == SOFTMAC_SLOWPATH)
682 			sup->su_mode = SOFTMAC_SLOWPATH;
683 		list_insert_head(&softmac->smac_sup_list, sup);
684 		mutex_exit(&softmac->smac_fp_mutex);
685 
686 		/*
687 		 * Setup the fast-path dedicated lower stream if fast-path
688 		 * is expected. Note that no lock is held here, and if
689 		 * smac_expected_mode is changed from SOFTMAC_FASTPATH to
690 		 * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for
691 		 * data-path switching would already be queued and will
692 		 * be processed by softmac_wput_single_nondata() later.
693 		 */
694 		if (expected_mode == SOFTMAC_FASTPATH)
695 			(void) softmac_fastpath_setup(sup);
696 		return;
697 	}
698 	case M_PROTO:
699 	case M_PCPROTO:
700 		if (MBLKL(mp) < sizeof (t_uscalar_t)) {
701 			freemsg(mp);
702 			return;
703 		}
704 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
705 		switch (prim) {
706 		case DL_NOTIFY_IND:
707 			if (MBLKL(mp) < sizeof (dl_notify_ind_t) ||
708 			    ((dl_notify_ind_t *)mp->b_rptr)->dl_notification !=
709 			    DL_NOTE_REPLUMB) {
710 				freemsg(mp);
711 				return;
712 			}
713 			/*
714 			 * This DL_NOTE_REPLUMB message is initiated
715 			 * and queued by the softmac itself, when the
716 			 * sup is trying to switching its datapath mode
717 			 * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH.
718 			 * Send this message upstream.
719 			 */
720 			qreply(sup->su_wq, mp);
721 			return;
722 		case DL_NOTIFY_CONF:
723 			if (MBLKL(mp) < sizeof (dl_notify_conf_t) ||
724 			    ((dl_notify_conf_t *)mp->b_rptr)->dl_notification !=
725 			    DL_NOTE_REPLUMB_DONE) {
726 				freemsg(mp);
727 				return;
728 			}
729 			/*
730 			 * This is an indication from IP/ARP that the
731 			 * fastpath->slowpath switch is done.
732 			 */
733 			freemsg(mp);
734 			softmac_datapath_switch_done(sup);
735 			return;
736 		}
737 		break;
738 	}
739 
740 	/*
741 	 * No need to hold lock to check su_mode, since su_mode updating only
742 	 * operation is is serialized by softmac_wput_nondata_task().
743 	 */
744 	if (sup->su_mode != SOFTMAC_FASTPATH) {
745 		dld_wput(sup->su_wq, mp);
746 		return;
747 	}
748 
749 	/*
750 	 * Fastpath non-data message processing. Most of non-data messages
751 	 * can be directly passed down to the dedicated-lower-stream, aside
752 	 * from the following M_PROTO/M_PCPROTO messages.
753 	 */
754 	switch (dbtype) {
755 	case M_PROTO:
756 	case M_PCPROTO:
757 		switch (prim) {
758 		case DL_BIND_REQ:
759 			softmac_bind_req(sup, mp);
760 			break;
761 		case DL_UNBIND_REQ:
762 			softmac_unbind_req(sup, mp);
763 			break;
764 		case DL_CAPABILITY_REQ:
765 			softmac_capability_req(sup, mp);
766 			break;
767 		default:
768 			putnext(slp->sl_wq, mp);
769 			break;
770 		}
771 		break;
772 	default:
773 		putnext(slp->sl_wq, mp);
774 		break;
775 	}
776 }
777 
778 /*
779  * The worker thread which processes non-data messages. Note we only process
780  * one message at one time in order to be able to "flush" the queued message
781  * and serialize the processing.
782  */
783 static void
784 softmac_wput_nondata_task(void *arg)
785 {
786 	softmac_upper_t	*sup = arg;
787 	mblk_t		*mp;
788 
789 	mutex_enter(&sup->su_disp_mutex);
790 
791 	while (sup->su_pending_head != NULL) {
792 		if (sup->su_closing)
793 			break;
794 
795 		SOFTMAC_DQ_PENDING(sup, &mp);
796 		mutex_exit(&sup->su_disp_mutex);
797 		softmac_wput_single_nondata(sup, mp);
798 		mutex_enter(&sup->su_disp_mutex);
799 	}
800 
801 	/*
802 	 * If the stream is closing, flush all queued messages and inform
803 	 * the stream to be closed.
804 	 */
805 	freemsgchain(sup->su_pending_head);
806 	sup->su_pending_head = sup->su_pending_tail = NULL;
807 	sup->su_dlpi_pending = B_FALSE;
808 	cv_signal(&sup->su_disp_cv);
809 	mutex_exit(&sup->su_disp_mutex);
810 }
811 
812 /*
813  * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata().
814  * This thread is started when the softmac module is first loaded.
815  */
816 static void
817 softmac_taskq_dispatch(void)
818 {
819 	callb_cpr_t	cprinfo;
820 	softmac_upper_t	*sup;
821 
822 	CALLB_CPR_INIT(&cprinfo, &softmac_taskq_lock, callb_generic_cpr,
823 	    "softmac_taskq_dispatch");
824 	mutex_enter(&softmac_taskq_lock);
825 
826 	while (!softmac_taskq_quit) {
827 		sup = list_head(&softmac_taskq_list);
828 		while (sup != NULL) {
829 			list_remove(&softmac_taskq_list, sup);
830 			sup->su_taskq_scheduled = B_FALSE;
831 			mutex_exit(&softmac_taskq_lock);
832 			VERIFY(taskq_dispatch(system_taskq,
833 			    softmac_wput_nondata_task, sup, TQ_SLEEP) != NULL);
834 			mutex_enter(&softmac_taskq_lock);
835 			sup = list_head(&softmac_taskq_list);
836 		}
837 
838 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
839 		cv_wait(&softmac_taskq_cv, &softmac_taskq_lock);
840 		CALLB_CPR_SAFE_END(&cprinfo, &softmac_taskq_lock);
841 	}
842 
843 	softmac_taskq_done = B_TRUE;
844 	cv_signal(&softmac_taskq_cv);
845 	CALLB_CPR_EXIT(&cprinfo);
846 	thread_exit();
847 }
848 
849 void
850 softmac_wput_nondata(softmac_upper_t *sup, mblk_t *mp)
851 {
852 	/*
853 	 * The processing of the message might block. Enqueue the
854 	 * message for later processing.
855 	 */
856 	mutex_enter(&sup->su_disp_mutex);
857 
858 	if (sup->su_closing) {
859 		mutex_exit(&sup->su_disp_mutex);
860 		freemsg(mp);
861 		return;
862 	}
863 
864 	SOFTMAC_EQ_PENDING(sup, mp);
865 
866 	if (sup->su_dlpi_pending) {
867 		mutex_exit(&sup->su_disp_mutex);
868 		return;
869 	}
870 	sup->su_dlpi_pending = B_TRUE;
871 	mutex_exit(&sup->su_disp_mutex);
872 
873 	if (taskq_dispatch(system_taskq, softmac_wput_nondata_task,
874 	    sup, TQ_NOSLEEP) != NULL) {
875 		return;
876 	}
877 
878 	mutex_enter(&softmac_taskq_lock);
879 	if (!sup->su_taskq_scheduled) {
880 		list_insert_tail(&softmac_taskq_list, sup);
881 		cv_signal(&softmac_taskq_cv);
882 	}
883 	sup->su_taskq_scheduled = B_TRUE;
884 	mutex_exit(&softmac_taskq_lock);
885 }
886 
887 /*
888  * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
889  */
890 static int
891 softmac_fastpath_setup(softmac_upper_t *sup)
892 {
893 	softmac_t	*softmac = sup->su_softmac;
894 	softmac_lower_t	*slp;
895 	int		err;
896 
897 	err = softmac_lower_setup(softmac, sup, &slp);
898 
899 	mutex_enter(&sup->su_mutex);
900 	/*
901 	 * Wait for all data messages to be processed so that we can change
902 	 * the su_mode.
903 	 */
904 	while (sup->su_tx_inprocess != 0)
905 		cv_wait(&sup->su_cv, &sup->su_mutex);
906 
907 	ASSERT(sup->su_mode != SOFTMAC_FASTPATH);
908 	ASSERT(sup->su_slp == NULL);
909 	if (err != 0) {
910 		sup->su_mode = SOFTMAC_SLOWPATH;
911 	} else {
912 		sup->su_slp = slp;
913 		sup->su_mode = SOFTMAC_FASTPATH;
914 	}
915 	mutex_exit(&sup->su_mutex);
916 	return (err);
917 }
918 
919 /*
920  * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
921  */
922 static void
923 softmac_fastpath_tear(softmac_upper_t *sup)
924 {
925 	mutex_enter(&sup->su_mutex);
926 	/*
927 	 * Wait for all data messages in the dedicated-lower-stream
928 	 * to be processed.
929 	 */
930 	while (sup->su_tx_inprocess != 0)
931 		cv_wait(&sup->su_cv, &sup->su_mutex);
932 
933 	if (sup->su_tx_busy) {
934 		ASSERT(sup->su_tx_flow_mp == NULL);
935 		sup->su_tx_flow_mp = getq(sup->su_wq);
936 		sup->su_tx_busy = B_FALSE;
937 	}
938 
939 	sup->su_mode = SOFTMAC_SLOWPATH;
940 
941 	/*
942 	 * Destroy the dedicated-lower-stream. Note that slp is destroyed
943 	 * when lh is closed.
944 	 */
945 	(void) ldi_close(sup->su_slp->sl_lh, FREAD|FWRITE, kcred);
946 	sup->su_slp = NULL;
947 	mutex_exit(&sup->su_mutex);
948 }
949 
950 void
951 softmac_wput_data(softmac_upper_t *sup, mblk_t *mp)
952 {
953 	/*
954 	 * No lock is required to access the su_mode field since the data
955 	 * traffic is quiesce by IP when the data-path mode is in the
956 	 * process of switching.
957 	 */
958 	if (sup->su_mode != SOFTMAC_FASTPATH)
959 		dld_wput(sup->su_wq, mp);
960 	else
961 		(void) softmac_fastpath_wput_data(sup, mp, NULL, 0);
962 }
963 
964 /*ARGSUSED*/
965 static mac_tx_cookie_t
966 softmac_fastpath_wput_data(softmac_upper_t *sup, mblk_t *mp, uintptr_t f_hint,
967     uint16_t flag)
968 {
969 	queue_t		*wq = sup->su_slp->sl_wq;
970 
971 	/*
972 	 * This function is called from IP, only the MAC_DROP_ON_NO_DESC
973 	 * flag can be specified.
974 	 */
975 	ASSERT((flag & ~MAC_DROP_ON_NO_DESC) == 0);
976 	ASSERT(mp->b_next == NULL);
977 
978 	/*
979 	 * Check wether the dedicated-lower-stream is able to handle more
980 	 * messages, and enable the flow-control if it is not.
981 	 *
982 	 * Note that in order not to introduce any packet reordering, we
983 	 * always send the message down to the dedicated-lower-stream:
984 	 *
985 	 * If the flow-control is already enabled, but we still get
986 	 * the messages from the upper-stream, it means that the upper
987 	 * stream does not respect STREAMS flow-control (e.g., TCP). Simply
988 	 * pass the message down to the lower-stream in that case.
989 	 */
990 	if (SOFTMAC_CANPUTNEXT(wq)) {
991 		putnext(wq, mp);
992 		return (NULL);
993 	}
994 
995 	if ((flag & MAC_DROP_ON_NO_DESC) != 0) {
996 		freemsg(mp);
997 		return ((mac_tx_cookie_t)wq);
998 	}
999 
1000 	if (sup->su_tx_busy) {
1001 		putnext(wq, mp);
1002 		return ((mac_tx_cookie_t)wq);
1003 	}
1004 
1005 	mutex_enter(&sup->su_mutex);
1006 	if (!sup->su_tx_busy) {
1007 		ASSERT(sup->su_tx_flow_mp != NULL);
1008 		(void) putq(sup->su_wq, sup->su_tx_flow_mp);
1009 		sup->su_tx_flow_mp = NULL;
1010 		sup->su_tx_busy = B_TRUE;
1011 		qenable(wq);
1012 	}
1013 	mutex_exit(&sup->su_mutex);
1014 	putnext(wq, mp);
1015 	return ((mac_tx_cookie_t)wq);
1016 }
1017 
1018 boolean_t
1019 softmac_active_set(void *arg)
1020 {
1021 	softmac_t	*softmac = arg;
1022 
1023 	mutex_enter(&softmac->smac_active_mutex);
1024 	if (softmac->smac_nactive != 0) {
1025 		mutex_exit(&softmac->smac_active_mutex);
1026 		return (B_FALSE);
1027 	}
1028 	softmac->smac_active = B_TRUE;
1029 	mutex_exit(&softmac->smac_active_mutex);
1030 	return (B_TRUE);
1031 }
1032 
1033 void
1034 softmac_active_clear(void *arg)
1035 {
1036 	softmac_t	*softmac = arg;
1037 
1038 	mutex_enter(&softmac->smac_active_mutex);
1039 	ASSERT(softmac->smac_active && (softmac->smac_nactive == 0));
1040 	softmac->smac_active = B_FALSE;
1041 	mutex_exit(&softmac->smac_active_mutex);
1042 }
1043 
1044 /*
1045  * Disable/reenable fastpath on given softmac. This request could come from a
1046  * MAC client or directly from administrators.
1047  */
1048 int
1049 softmac_datapath_switch(softmac_t *softmac, boolean_t disable, boolean_t admin)
1050 {
1051 	softmac_upper_t		*sup;
1052 	mblk_t			*head = NULL, *tail = NULL, *mp;
1053 	list_t			reqlist;
1054 	softmac_switch_req_t	*req;
1055 	uint32_t		current_mode, expected_mode;
1056 	int			err = 0;
1057 
1058 	mutex_enter(&softmac->smac_fp_mutex);
1059 
1060 	current_mode = DATAPATH_MODE(softmac);
1061 	if (admin) {
1062 		if (softmac->smac_fastpath_admin_disabled == disable) {
1063 			mutex_exit(&softmac->smac_fp_mutex);
1064 			return (0);
1065 		}
1066 		softmac->smac_fastpath_admin_disabled = disable;
1067 	} else if (disable) {
1068 		softmac->smac_fp_disable_clients++;
1069 	} else {
1070 		ASSERT(softmac->smac_fp_disable_clients != 0);
1071 		softmac->smac_fp_disable_clients--;
1072 	}
1073 
1074 	expected_mode = DATAPATH_MODE(softmac);
1075 	if (current_mode == expected_mode) {
1076 		mutex_exit(&softmac->smac_fp_mutex);
1077 		return (0);
1078 	}
1079 
1080 	/*
1081 	 * The expected mode is different from whatever datapath mode
1082 	 * this softmac is expected from last request, enqueue the data-path
1083 	 * switch request.
1084 	 */
1085 	list_create(&reqlist, sizeof (softmac_switch_req_t),
1086 	    offsetof(softmac_switch_req_t, ssq_req_list_node));
1087 
1088 	/*
1089 	 * Allocate all DL_NOTIFY_IND messages and request structures that
1090 	 * are required to switch each IP/ARP stream to the expected mode.
1091 	 */
1092 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
1093 	    sup = list_next(&softmac->smac_sup_list, sup)) {
1094 		dl_notify_ind_t	*dlip;
1095 
1096 		req = kmem_alloc(sizeof (softmac_switch_req_t), KM_NOSLEEP);
1097 		if (req == NULL)
1098 			break;
1099 
1100 		req->ssq_expected_mode = expected_mode;
1101 
1102 		/*
1103 		 * Allocate the DL_NOTE_REPLUMB message.
1104 		 */
1105 		if ((mp = allocb(sizeof (dl_notify_ind_t), BPRI_LO)) == NULL) {
1106 			kmem_free(req, sizeof (softmac_switch_req_t));
1107 			break;
1108 		}
1109 
1110 		list_insert_tail(&reqlist, req);
1111 
1112 		mp->b_wptr = mp->b_rptr + sizeof (dl_notify_ind_t);
1113 		mp->b_datap->db_type = M_PROTO;
1114 		bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
1115 		dlip = (dl_notify_ind_t *)mp->b_rptr;
1116 		dlip->dl_primitive = DL_NOTIFY_IND;
1117 		dlip->dl_notification = DL_NOTE_REPLUMB;
1118 		if (head == NULL) {
1119 			head = tail = mp;
1120 		} else {
1121 			tail->b_next = mp;
1122 			tail = mp;
1123 		}
1124 	}
1125 
1126 	/*
1127 	 * Note that it is fine if the expected data-path mode is fast-path
1128 	 * and some of streams fails to switch. Only return failure if we
1129 	 * are expected to switch to the slow-path.
1130 	 */
1131 	if (sup != NULL && expected_mode == SOFTMAC_SLOWPATH) {
1132 		err = ENOMEM;
1133 		goto fail;
1134 	}
1135 
1136 	/*
1137 	 * Start switching for each IP/ARP stream. The switching operation
1138 	 * will eventually succeed and there is no need to wait for it
1139 	 * to finish.
1140 	 */
1141 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
1142 	    sup = list_next(&softmac->smac_sup_list, sup)) {
1143 		mp = head->b_next;
1144 		head->b_next = NULL;
1145 
1146 		/*
1147 		 * Add the swtich request to the requests list of the stream.
1148 		 */
1149 		req = list_head(&reqlist);
1150 		ASSERT(req != NULL);
1151 		list_remove(&reqlist, req);
1152 		list_insert_tail(&sup->su_req_list, req);
1153 		softmac_wput_nondata(sup, head);
1154 		head = mp;
1155 	}
1156 
1157 	mutex_exit(&softmac->smac_fp_mutex);
1158 	ASSERT(list_is_empty(&reqlist));
1159 	list_destroy(&reqlist);
1160 	return (0);
1161 fail:
1162 	if (admin) {
1163 		softmac->smac_fastpath_admin_disabled = !disable;
1164 	} else if (disable) {
1165 		softmac->smac_fp_disable_clients--;
1166 	} else {
1167 		softmac->smac_fp_disable_clients++;
1168 	}
1169 
1170 	mutex_exit(&softmac->smac_fp_mutex);
1171 	while ((req = list_head(&reqlist)) != NULL) {
1172 		list_remove(&reqlist, req);
1173 		kmem_free(req, sizeof (softmac_switch_req_t));
1174 	}
1175 	freemsgchain(head);
1176 	list_destroy(&reqlist);
1177 	return (err);
1178 }
1179 
1180 int
1181 softmac_fastpath_disable(void *arg)
1182 {
1183 	return (softmac_datapath_switch((softmac_t *)arg, B_TRUE, B_FALSE));
1184 }
1185 
1186 void
1187 softmac_fastpath_enable(void *arg)
1188 {
1189 	VERIFY(softmac_datapath_switch((softmac_t *)arg, B_FALSE,
1190 	    B_FALSE) == 0);
1191 }
1192 
1193 void
1194 softmac_upperstream_close(softmac_upper_t *sup)
1195 {
1196 	softmac_t		*softmac = sup->su_softmac;
1197 	softmac_switch_req_t	*req;
1198 
1199 	mutex_enter(&softmac->smac_fp_mutex);
1200 
1201 	if (sup->su_mode == SOFTMAC_FASTPATH)
1202 		softmac_fastpath_tear(sup);
1203 
1204 	if (sup->su_mode != SOFTMAC_UNKNOWN) {
1205 		list_remove(&softmac->smac_sup_list, sup);
1206 		sup->su_mode = SOFTMAC_UNKNOWN;
1207 	}
1208 
1209 	/*
1210 	 * Cleanup all the switch requests queueed on this stream.
1211 	 */
1212 	while ((req = list_head(&sup->su_req_list)) != NULL) {
1213 		list_remove(&sup->su_req_list, req);
1214 		kmem_free(req, sizeof (softmac_switch_req_t));
1215 	}
1216 	mutex_exit(&softmac->smac_fp_mutex);
1217 }
1218 
1219 /*
1220  * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper
1221  * stream from the fastpath mode to the slowpath mode.
1222  */
1223 static void
1224 softmac_datapath_switch_done(softmac_upper_t *sup)
1225 {
1226 	softmac_t		*softmac = sup->su_softmac;
1227 	softmac_switch_req_t	*req;
1228 	uint32_t		expected_mode;
1229 
1230 	mutex_enter(&softmac->smac_fp_mutex);
1231 	req = list_head(&sup->su_req_list);
1232 	list_remove(&sup->su_req_list, req);
1233 	expected_mode = req->ssq_expected_mode;
1234 	kmem_free(req, sizeof (softmac_switch_req_t));
1235 
1236 	if (expected_mode == sup->su_mode) {
1237 		mutex_exit(&softmac->smac_fp_mutex);
1238 		return;
1239 	}
1240 
1241 	ASSERT(!sup->su_bound);
1242 	mutex_exit(&softmac->smac_fp_mutex);
1243 
1244 	/*
1245 	 * It is fine if the expected mode is fast-path and we fail
1246 	 * to enable fastpath on this stream.
1247 	 */
1248 	if (expected_mode == SOFTMAC_SLOWPATH)
1249 		softmac_fastpath_tear(sup);
1250 	else
1251 		(void) softmac_fastpath_setup(sup);
1252 }
1253