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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Softmac data-path switching:
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
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
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.
56 * The details of the softmac data fast-path model is stated as below
58 * 1. When a stream is opened on a softMAC, the softmac module will takes
59 * over the DLPI processing on this stream;
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;
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;
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;
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
85 * Step 5 and 6 both rely on the data-path mode switching process
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;
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.
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*;
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.
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>
120 #include <sys/sockio.h>
121 #include <sys/softmac_impl.h>
124 static kmutex_t softmac_taskq_lock
;
125 static kcondvar_t softmac_taskq_cv
;
126 static list_t softmac_taskq_list
; /* List of softmac_upper_t */
127 boolean_t softmac_taskq_quit
;
128 boolean_t softmac_taskq_done
;
130 static void softmac_taskq_dispatch();
131 static int softmac_fastpath_setup(softmac_upper_t
*);
132 static mac_tx_cookie_t
softmac_fastpath_wput_data(softmac_upper_t
*, mblk_t
*,
133 uintptr_t, uint16_t);
134 static void softmac_datapath_switch_done(softmac_upper_t
*);
139 mutex_init(&softmac_taskq_lock
, NULL
, MUTEX_DRIVER
, NULL
);
140 cv_init(&softmac_taskq_cv
, NULL
, CV_DRIVER
, NULL
);
142 softmac_taskq_quit
= B_FALSE
;
143 softmac_taskq_done
= B_FALSE
;
144 list_create(&softmac_taskq_list
, sizeof (softmac_upper_t
),
145 offsetof(softmac_upper_t
, su_taskq_list_node
));
146 (void) thread_create(NULL
, 0, softmac_taskq_dispatch
, NULL
, 0,
147 &p0
, TS_RUN
, minclsyspri
);
154 * Request the softmac_taskq thread to quit and wait for it to be done.
156 mutex_enter(&softmac_taskq_lock
);
157 softmac_taskq_quit
= B_TRUE
;
158 cv_signal(&softmac_taskq_cv
);
159 while (!softmac_taskq_done
)
160 cv_wait(&softmac_taskq_cv
, &softmac_taskq_lock
);
161 mutex_exit(&softmac_taskq_lock
);
162 list_destroy(&softmac_taskq_list
);
164 mutex_destroy(&softmac_taskq_lock
);
165 cv_destroy(&softmac_taskq_cv
);
169 check_ip_above(queue_t
*q
)
172 boolean_t ret
= B_TRUE
;
176 if (strcmp(next_q
->q_qinfo
->qi_minfo
->mi_idname
, "ip") != 0)
184 softmac_capab_perim(softmac_upper_t
*sup
, void *data
, uint_t flags
)
188 mutex_enter(&sup
->su_mutex
);
191 mutex_exit(&sup
->su_mutex
);
194 return (MUTEX_HELD(&sup
->su_mutex
));
199 static mac_tx_notify_handle_t
200 softmac_client_tx_notify(softmac_upper_t
*sup
, mac_tx_notify_t func
, void *arg
)
202 ASSERT(MUTEX_HELD(&sup
->su_mutex
));
205 sup
->su_tx_notify_func
= func
;
206 sup
->su_tx_notify_arg
= arg
;
209 * Wait for all tx_notify_func call to be done.
211 while (sup
->su_tx_inprocess
!= 0)
212 cv_wait(&sup
->su_cv
, &sup
->su_mutex
);
214 sup
->su_tx_notify_func
= NULL
;
215 sup
->su_tx_notify_arg
= NULL
;
217 return ((mac_tx_notify_handle_t
)sup
);
221 softmac_tx_is_flow_blocked(softmac_upper_t
*sup
, mac_tx_cookie_t cookie
)
223 ASSERT(cookie
== (mac_tx_cookie_t
)sup
);
224 return (sup
->su_tx_busy
);
228 softmac_capab_direct(softmac_upper_t
*sup
, void *data
, uint_t flags
)
230 dld_capab_direct_t
*direct
= data
;
231 softmac_lower_t
*slp
= sup
->su_slp
;
233 ASSERT(MUTEX_HELD(&sup
->su_mutex
));
235 ASSERT(sup
->su_mode
== SOFTMAC_FASTPATH
);
242 sup
->su_direct_rxinfo
.slr_rx
= (softmac_rx_t
)direct
->di_rx_cf
;
243 sup
->su_direct_rxinfo
.slr_arg
= direct
->di_rx_ch
;
244 slp
->sl_rxinfo
= &sup
->su_direct_rxinfo
;
245 direct
->di_tx_df
= (uintptr_t)softmac_fastpath_wput_data
;
246 direct
->di_tx_dh
= sup
;
247 direct
->di_tx_fctl_df
= (uintptr_t)softmac_tx_is_flow_blocked
;
248 direct
->di_tx_fctl_dh
= sup
;
249 direct
->di_tx_cb_df
= (uintptr_t)softmac_client_tx_notify
;
250 direct
->di_tx_cb_dh
= sup
;
251 sup
->su_direct
= B_TRUE
;
258 slp
->sl_rxinfo
= &sup
->su_rxinfo
;
259 sup
->su_direct
= B_FALSE
;
266 softmac_dld_capab(softmac_upper_t
*sup
, uint_t type
, void *data
, uint_t flags
)
271 * Don't enable direct callback capabilities unless the caller is
272 * the IP client. When a module is inserted in a stream (_I_INSERT)
273 * the stack initiates capability disable, but due to races, the
274 * module insertion may complete before the capability disable
275 * completes. So we limit the check to DLD_ENABLE case.
277 if ((flags
== DLD_ENABLE
&& type
!= DLD_CAPAB_PERIM
) &&
278 !check_ip_above(sup
->su_rq
)) {
283 case DLD_CAPAB_DIRECT
:
284 err
= softmac_capab_direct(sup
, data
, flags
);
287 case DLD_CAPAB_PERIM
:
288 err
= softmac_capab_perim(sup
, data
, flags
);
299 softmac_capability_advertise(softmac_upper_t
*sup
, mblk_t
*mp
)
301 dl_capability_ack_t
*dlap
;
302 dl_capability_sub_t
*dlsp
;
305 queue_t
*q
= sup
->su_wq
;
307 softmac_t
*softmac
= sup
->su_softmac
;
308 boolean_t dld_capable
= B_FALSE
;
309 boolean_t hcksum_capable
= B_FALSE
;
310 boolean_t zcopy_capable
= B_FALSE
;
311 boolean_t mdt_capable
= B_FALSE
;
313 ASSERT(sup
->su_mode
== SOFTMAC_FASTPATH
);
316 * Initially assume no capabilities.
321 * Direct capability negotiation interface between IP and softmac
323 if (check_ip_above(sup
->su_rq
)) {
324 dld_capable
= B_TRUE
;
325 subsize
+= sizeof (dl_capability_sub_t
) +
326 sizeof (dl_capab_dld_t
);
330 * Check if checksum offload is supported on this MAC.
332 if (softmac
->smac_capab_flags
& MAC_CAPAB_HCKSUM
) {
333 hcksum_capable
= B_TRUE
;
334 subsize
+= sizeof (dl_capability_sub_t
) +
335 sizeof (dl_capab_hcksum_t
);
339 * Check if zerocopy is supported on this interface.
341 if (!(softmac
->smac_capab_flags
& MAC_CAPAB_NO_ZCOPY
)) {
342 zcopy_capable
= B_TRUE
;
343 subsize
+= sizeof (dl_capability_sub_t
) +
344 sizeof (dl_capab_zerocopy_t
);
347 if (softmac
->smac_mdt
) {
348 mdt_capable
= B_TRUE
;
349 subsize
+= sizeof (dl_capability_sub_t
) +
350 sizeof (dl_capab_mdt_t
);
354 * If there are no capabilities to advertise or if we
355 * can't allocate a response, send a DL_ERROR_ACK.
357 if ((subsize
== 0) || (mp1
= reallocb(mp
,
358 sizeof (dl_capability_ack_t
) + subsize
, 0)) == NULL
) {
359 dlerrorack(q
, mp
, DL_CAPABILITY_REQ
, DL_NOTSUPPORTED
, 0);
364 DB_TYPE(mp
) = M_PROTO
;
365 mp
->b_wptr
= mp
->b_rptr
+ sizeof (dl_capability_ack_t
) + subsize
;
366 bzero(mp
->b_rptr
, MBLKL(mp
));
367 dlap
= (dl_capability_ack_t
*)mp
->b_rptr
;
368 dlap
->dl_primitive
= DL_CAPABILITY_ACK
;
369 dlap
->dl_sub_offset
= sizeof (dl_capability_ack_t
);
370 dlap
->dl_sub_length
= subsize
;
371 ptr
= (uint8_t *)&dlap
[1];
374 * IP polling interface.
379 dlsp
= (dl_capability_sub_t
*)ptr
;
380 dlsp
->dl_cap
= DL_CAPAB_DLD
;
381 dlsp
->dl_length
= sizeof (dl_capab_dld_t
);
382 ptr
+= sizeof (dl_capability_sub_t
);
384 bzero(&dld
, sizeof (dl_capab_dld_t
));
385 dld
.dld_version
= DLD_CURRENT_VERSION
;
386 dld
.dld_capab
= (uintptr_t)softmac_dld_capab
;
387 dld
.dld_capab_handle
= (uintptr_t)sup
;
389 dlcapabsetqid(&(dld
.dld_mid
), sup
->su_rq
);
390 bcopy(&dld
, ptr
, sizeof (dl_capab_dld_t
));
391 ptr
+= sizeof (dl_capab_dld_t
);
395 * TCP/IP checksum offload.
397 if (hcksum_capable
) {
398 dl_capab_hcksum_t hcksum
;
400 dlsp
= (dl_capability_sub_t
*)ptr
;
402 dlsp
->dl_cap
= DL_CAPAB_HCKSUM
;
403 dlsp
->dl_length
= sizeof (dl_capab_hcksum_t
);
404 ptr
+= sizeof (dl_capability_sub_t
);
406 bzero(&hcksum
, sizeof (dl_capab_hcksum_t
));
407 hcksum
.hcksum_version
= HCKSUM_VERSION_1
;
408 hcksum
.hcksum_txflags
= softmac
->smac_hcksum_txflags
;
409 dlcapabsetqid(&(hcksum
.hcksum_mid
), sup
->su_rq
);
410 bcopy(&hcksum
, ptr
, sizeof (dl_capab_hcksum_t
));
411 ptr
+= sizeof (dl_capab_hcksum_t
);
418 dl_capab_zerocopy_t zcopy
;
420 dlsp
= (dl_capability_sub_t
*)ptr
;
422 dlsp
->dl_cap
= DL_CAPAB_ZEROCOPY
;
423 dlsp
->dl_length
= sizeof (dl_capab_zerocopy_t
);
424 ptr
+= sizeof (dl_capability_sub_t
);
426 bzero(&zcopy
, sizeof (dl_capab_zerocopy_t
));
427 zcopy
.zerocopy_version
= ZEROCOPY_VERSION_1
;
428 zcopy
.zerocopy_flags
= DL_CAPAB_VMSAFE_MEM
;
429 dlcapabsetqid(&(zcopy
.zerocopy_mid
), sup
->su_rq
);
430 bcopy(&zcopy
, ptr
, sizeof (dl_capab_zerocopy_t
));
431 ptr
+= sizeof (dl_capab_zerocopy_t
);
440 dlsp
= (dl_capability_sub_t
*)ptr
;
442 dlsp
->dl_cap
= DL_CAPAB_MDT
;
443 dlsp
->dl_length
= sizeof (dl_capab_mdt_t
);
444 ptr
+= sizeof (dl_capability_sub_t
);
446 bzero(&mdt
, sizeof (dl_capab_mdt_t
));
447 mdt
.mdt_version
= MDT_VERSION_2
;
448 mdt
.mdt_flags
= DL_CAPAB_MDT_ENABLE
;
449 mdt
.mdt_hdr_head
= softmac
->smac_mdt_capab
.mdt_hdr_head
;
450 mdt
.mdt_hdr_tail
= softmac
->smac_mdt_capab
.mdt_hdr_tail
;
451 mdt
.mdt_max_pld
= softmac
->smac_mdt_capab
.mdt_max_pld
;
452 mdt
.mdt_span_limit
= softmac
->smac_mdt_capab
.mdt_span_limit
;
453 dlcapabsetqid(&(mdt
.mdt_mid
), sup
->su_rq
);
454 bcopy(&mdt
, ptr
, sizeof (dl_capab_mdt_t
));
455 ptr
+= sizeof (dl_capab_mdt_t
);
458 ASSERT(ptr
== mp
->b_rptr
+ sizeof (dl_capability_ack_t
) + subsize
);
463 softmac_capability_req(softmac_upper_t
*sup
, mblk_t
*mp
)
465 dl_capability_req_t
*dlp
= (dl_capability_req_t
*)mp
->b_rptr
;
466 dl_capability_sub_t
*sp
;
470 queue_t
*q
= sup
->su_wq
;
472 ASSERT(sup
->su_mode
== SOFTMAC_FASTPATH
);
473 if (MBLKL(mp
) < sizeof (dl_capability_req_t
)) {
478 if (!sup
->su_bound
) {
479 dl_err
= DL_OUTSTATE
;
484 * This request is overloaded. If there are no requested capabilities
485 * then we just want to acknowledge with all the capabilities we
486 * support. Otherwise we enable the set of capabilities requested.
488 if (dlp
->dl_sub_length
== 0) {
489 softmac_capability_advertise(sup
, mp
);
493 if (!MBLKIN(mp
, dlp
->dl_sub_offset
, dlp
->dl_sub_length
)) {
498 dlp
->dl_primitive
= DL_CAPABILITY_ACK
;
500 off
= dlp
->dl_sub_offset
;
501 len
= dlp
->dl_sub_length
;
504 * Walk the list of capabilities to be enabled.
506 for (end
= off
+ len
; off
< end
; ) {
507 sp
= (dl_capability_sub_t
*)(mp
->b_rptr
+ off
);
508 size
= sizeof (dl_capability_sub_t
) + sp
->dl_length
;
510 if (off
+ size
> end
||
511 !IS_P2ALIGNED(off
, sizeof (uint32_t))) {
516 switch (sp
->dl_cap
) {
518 * TCP/IP checksum offload to hardware.
520 case DL_CAPAB_HCKSUM
: {
521 dl_capab_hcksum_t
*hcksump
;
522 dl_capab_hcksum_t hcksum
;
524 hcksump
= (dl_capab_hcksum_t
*)&sp
[1];
526 * Copy for alignment.
528 bcopy(hcksump
, &hcksum
, sizeof (dl_capab_hcksum_t
));
529 dlcapabsetqid(&(hcksum
.hcksum_mid
), sup
->su_rq
);
530 bcopy(&hcksum
, hcksump
, sizeof (dl_capab_hcksum_t
));
543 dlerrorack(q
, mp
, DL_CAPABILITY_REQ
, dl_err
, 0);
547 softmac_bind_req(softmac_upper_t
*sup
, mblk_t
*mp
)
549 softmac_lower_t
*slp
= sup
->su_slp
;
550 softmac_t
*softmac
= sup
->su_softmac
;
554 if (MBLKL(mp
) < DL_BIND_REQ_SIZE
) {
560 * Allocate ackmp incase the underlying driver does not ack timely.
562 if ((mp1
= allocb(sizeof (dl_error_ack_t
), BPRI_HI
)) == NULL
) {
563 dlerrorack(sup
->su_wq
, mp
, DL_BIND_REQ
, DL_SYSERR
, ENOMEM
);
567 err
= softmac_output(slp
, mp
, DL_BIND_REQ
, DL_BIND_ACK
, &ackmp
);
572 * The driver does not ack timely.
574 ASSERT(err
== ENOMSG
);
581 * Enable capabilities the underlying driver claims to support.
583 if ((err
= softmac_capab_enable(slp
)) != 0)
587 * Check whether this softmac is already marked as exclusively used,
588 * e.g., an aggregation is created over it. Fail the BIND_REQ if so.
590 mutex_enter(&softmac
->smac_active_mutex
);
591 if (softmac
->smac_active
) {
592 mutex_exit(&softmac
->smac_active_mutex
);
596 softmac
->smac_nactive
++;
597 sup
->su_active
= B_TRUE
;
598 mutex_exit(&softmac
->smac_active_mutex
);
599 sup
->su_bound
= B_TRUE
;
601 qreply(sup
->su_wq
, ackmp
);
605 dlerrorack(sup
->su_wq
, ackmp
, DL_BIND_REQ
, DL_SYSERR
, err
);
611 softmac_unbind_req(softmac_upper_t
*sup
, mblk_t
*mp
)
613 softmac_lower_t
*slp
= sup
->su_slp
;
614 softmac_t
*softmac
= sup
->su_softmac
;
618 if (MBLKL(mp
) < DL_UNBIND_REQ_SIZE
) {
623 if (!sup
->su_bound
) {
624 dlerrorack(sup
->su_wq
, mp
, DL_UNBIND_REQ
, DL_OUTSTATE
, 0);
629 * Allocate ackmp incase the underlying driver does not ack timely.
631 if ((mp1
= allocb(sizeof (dl_error_ack_t
), BPRI_HI
)) == NULL
) {
632 dlerrorack(sup
->su_wq
, mp
, DL_UNBIND_REQ
, DL_SYSERR
, ENOMEM
);
636 err
= softmac_output(slp
, mp
, DL_UNBIND_REQ
, DL_OK_ACK
, &ackmp
);
641 * The driver does not ack timely.
643 ASSERT(err
== ENOMSG
);
647 dlerrorack(sup
->su_wq
, ackmp
, DL_UNBIND_REQ
, DL_SYSERR
, err
);
651 sup
->su_bound
= B_FALSE
;
653 mutex_enter(&softmac
->smac_active_mutex
);
654 if (sup
->su_active
) {
655 ASSERT(!softmac
->smac_active
);
656 softmac
->smac_nactive
--;
657 sup
->su_active
= B_FALSE
;
659 mutex_exit(&softmac
->smac_active_mutex
);
662 qreply(sup
->su_wq
, ackmp
);
666 * Process the non-data mblk.
669 softmac_wput_single_nondata(softmac_upper_t
*sup
, mblk_t
*mp
)
671 softmac_t
*softmac
= sup
->su_softmac
;
672 softmac_lower_t
*slp
= sup
->su_slp
;
673 unsigned char dbtype
;
676 dbtype
= DB_TYPE(mp
);
683 uint32_t expected_mode
;
685 if (((struct iocblk
*)(mp
->b_rptr
))->ioc_cmd
!= SIOCSLIFNAME
)
689 * Nak the M_IOCTL based on the STREAMS specification.
691 if (dbtype
== M_IOCTL
)
692 miocnak(sup
->su_wq
, mp
, 0, EINVAL
);
697 * This stream is either IP or ARP. See whether
698 * we need to setup a dedicated-lower-stream for it.
700 mutex_enter(&softmac
->smac_fp_mutex
);
702 expected_mode
= DATAPATH_MODE(softmac
);
703 if (expected_mode
== SOFTMAC_SLOWPATH
)
704 sup
->su_mode
= SOFTMAC_SLOWPATH
;
705 list_insert_head(&softmac
->smac_sup_list
, sup
);
706 mutex_exit(&softmac
->smac_fp_mutex
);
709 * Setup the fast-path dedicated lower stream if fast-path
710 * is expected. Note that no lock is held here, and if
711 * smac_expected_mode is changed from SOFTMAC_FASTPATH to
712 * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for
713 * data-path switching would already be queued and will
714 * be processed by softmac_wput_single_nondata() later.
716 if (expected_mode
== SOFTMAC_FASTPATH
)
717 (void) softmac_fastpath_setup(sup
);
722 if (MBLKL(mp
) < sizeof (t_uscalar_t
)) {
726 prim
= ((union DL_primitives
*)mp
->b_rptr
)->dl_primitive
;
729 if (MBLKL(mp
) < sizeof (dl_notify_ind_t
) ||
730 ((dl_notify_ind_t
*)mp
->b_rptr
)->dl_notification
!=
736 * This DL_NOTE_REPLUMB message is initiated
737 * and queued by the softmac itself, when the
738 * sup is trying to switching its datapath mode
739 * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH.
740 * Send this message upstream.
742 qreply(sup
->su_wq
, mp
);
745 if (MBLKL(mp
) < sizeof (dl_notify_conf_t
) ||
746 ((dl_notify_conf_t
*)mp
->b_rptr
)->dl_notification
!=
747 DL_NOTE_REPLUMB_DONE
) {
752 * This is an indication from IP/ARP that the
753 * fastpath->slowpath switch is done.
756 softmac_datapath_switch_done(sup
);
763 * No need to hold lock to check su_mode, since su_mode updating only
764 * operation is is serialized by softmac_wput_nondata_task().
766 if (sup
->su_mode
!= SOFTMAC_FASTPATH
) {
767 dld_wput(sup
->su_wq
, mp
);
772 * Fastpath non-data message processing. Most of non-data messages
773 * can be directly passed down to the dedicated-lower-stream, aside
774 * from the following M_PROTO/M_PCPROTO messages.
781 softmac_bind_req(sup
, mp
);
784 softmac_unbind_req(sup
, mp
);
786 case DL_CAPABILITY_REQ
:
787 softmac_capability_req(sup
, mp
);
790 putnext(slp
->sl_wq
, mp
);
795 putnext(slp
->sl_wq
, mp
);
801 * The worker thread which processes non-data messages. Note we only process
802 * one message at one time in order to be able to "flush" the queued message
803 * and serialize the processing.
806 softmac_wput_nondata_task(void *arg
)
808 softmac_upper_t
*sup
= arg
;
811 mutex_enter(&sup
->su_disp_mutex
);
813 while (sup
->su_pending_head
!= NULL
) {
817 SOFTMAC_DQ_PENDING(sup
, &mp
);
818 mutex_exit(&sup
->su_disp_mutex
);
819 softmac_wput_single_nondata(sup
, mp
);
820 mutex_enter(&sup
->su_disp_mutex
);
824 * If the stream is closing, flush all queued messages and inform
825 * the stream to be closed.
827 freemsgchain(sup
->su_pending_head
);
828 sup
->su_pending_head
= sup
->su_pending_tail
= NULL
;
829 sup
->su_dlpi_pending
= B_FALSE
;
830 cv_signal(&sup
->su_disp_cv
);
831 mutex_exit(&sup
->su_disp_mutex
);
835 * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata().
836 * This thread is started when the softmac module is first loaded.
839 softmac_taskq_dispatch(void)
842 softmac_upper_t
*sup
;
844 CALLB_CPR_INIT(&cprinfo
, &softmac_taskq_lock
, callb_generic_cpr
,
845 "softmac_taskq_dispatch");
846 mutex_enter(&softmac_taskq_lock
);
848 while (!softmac_taskq_quit
) {
849 sup
= list_head(&softmac_taskq_list
);
850 while (sup
!= NULL
) {
851 list_remove(&softmac_taskq_list
, sup
);
852 sup
->su_taskq_scheduled
= B_FALSE
;
853 mutex_exit(&softmac_taskq_lock
);
854 VERIFY(taskq_dispatch(system_taskq
,
855 softmac_wput_nondata_task
, sup
, TQ_SLEEP
) !=
857 mutex_enter(&softmac_taskq_lock
);
858 sup
= list_head(&softmac_taskq_list
);
861 CALLB_CPR_SAFE_BEGIN(&cprinfo
);
862 cv_wait(&softmac_taskq_cv
, &softmac_taskq_lock
);
863 CALLB_CPR_SAFE_END(&cprinfo
, &softmac_taskq_lock
);
866 softmac_taskq_done
= B_TRUE
;
867 cv_signal(&softmac_taskq_cv
);
868 CALLB_CPR_EXIT(&cprinfo
);
873 softmac_wput_nondata(softmac_upper_t
*sup
, mblk_t
*mp
)
876 * The processing of the message might block. Enqueue the
877 * message for later processing.
879 mutex_enter(&sup
->su_disp_mutex
);
881 if (sup
->su_closing
) {
882 mutex_exit(&sup
->su_disp_mutex
);
887 SOFTMAC_EQ_PENDING(sup
, mp
);
889 if (sup
->su_dlpi_pending
) {
890 mutex_exit(&sup
->su_disp_mutex
);
893 sup
->su_dlpi_pending
= B_TRUE
;
894 mutex_exit(&sup
->su_disp_mutex
);
896 if (taskq_dispatch(system_taskq
, softmac_wput_nondata_task
,
897 sup
, TQ_NOSLEEP
) != (uintptr_t)NULL
) {
901 mutex_enter(&softmac_taskq_lock
);
902 if (!sup
->su_taskq_scheduled
) {
903 list_insert_tail(&softmac_taskq_list
, sup
);
904 cv_signal(&softmac_taskq_cv
);
906 sup
->su_taskq_scheduled
= B_TRUE
;
907 mutex_exit(&softmac_taskq_lock
);
911 * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
914 softmac_fastpath_setup(softmac_upper_t
*sup
)
916 softmac_t
*softmac
= sup
->su_softmac
;
917 softmac_lower_t
*slp
;
920 err
= softmac_lower_setup(softmac
, sup
, &slp
);
922 mutex_enter(&sup
->su_mutex
);
924 * Wait for all data messages to be processed so that we can change
927 while (sup
->su_tx_inprocess
!= 0)
928 cv_wait(&sup
->su_cv
, &sup
->su_mutex
);
930 ASSERT(sup
->su_mode
!= SOFTMAC_FASTPATH
);
931 ASSERT(sup
->su_slp
== NULL
);
933 sup
->su_mode
= SOFTMAC_SLOWPATH
;
936 sup
->su_mode
= SOFTMAC_FASTPATH
;
938 mutex_exit(&sup
->su_mutex
);
943 * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
946 softmac_fastpath_tear(softmac_upper_t
*sup
)
948 mutex_enter(&sup
->su_mutex
);
950 * Wait for all data messages in the dedicated-lower-stream
953 while (sup
->su_tx_inprocess
!= 0)
954 cv_wait(&sup
->su_cv
, &sup
->su_mutex
);
957 * Note that this function is called either when the stream is closed,
958 * or the stream is unbound (fastpath-slowpath-switch). Therefore,
959 * No need to call the tx_notify callback.
961 sup
->su_tx_notify_func
= NULL
;
962 sup
->su_tx_notify_arg
= NULL
;
963 if (sup
->su_tx_busy
) {
964 ASSERT(sup
->su_tx_flow_mp
== NULL
);
965 VERIFY((sup
->su_tx_flow_mp
= getq(sup
->su_wq
)) != NULL
);
966 sup
->su_tx_busy
= B_FALSE
;
969 sup
->su_mode
= SOFTMAC_SLOWPATH
;
972 * Destroy the dedicated-lower-stream. Note that slp is destroyed
975 (void) ldi_close(sup
->su_slp
->sl_lh
, FREAD
|FWRITE
, kcred
);
977 mutex_exit(&sup
->su_mutex
);
981 softmac_wput_data(softmac_upper_t
*sup
, mblk_t
*mp
)
984 * No lock is required to access the su_mode field since the data
985 * traffic is quiesce by IP when the data-path mode is in the
986 * process of switching.
988 if (sup
->su_mode
!= SOFTMAC_FASTPATH
)
989 dld_wput(sup
->su_wq
, mp
);
991 (void) softmac_fastpath_wput_data(sup
, mp
, (uintptr_t)NULL
, 0);
995 static mac_tx_cookie_t
996 softmac_fastpath_wput_data(softmac_upper_t
*sup
, mblk_t
*mp
, uintptr_t f_hint
,
999 queue_t
*wq
= sup
->su_slp
->sl_wq
;
1002 * This function is called from IP, only the MAC_DROP_ON_NO_DESC
1003 * flag can be specified.
1005 ASSERT((flag
& ~MAC_DROP_ON_NO_DESC
) == 0);
1006 ASSERT(mp
->b_next
== NULL
);
1009 * Check wether the dedicated-lower-stream is able to handle more
1010 * messages, and enable the flow-control if it is not.
1012 * Note that in order not to introduce any packet reordering, we
1013 * always send the message down to the dedicated-lower-stream:
1015 * If the flow-control is already enabled, but we still get
1016 * the messages from the upper-stream, it means that the upper
1017 * stream does not respect STREAMS flow-control (e.g., TCP). Simply
1018 * pass the message down to the lower-stream in that case.
1020 if (SOFTMAC_CANPUTNEXT(wq
)) {
1022 return ((uintptr_t)NULL
);
1025 if (sup
->su_tx_busy
) {
1026 if ((flag
& MAC_DROP_ON_NO_DESC
) != 0)
1030 return ((mac_tx_cookie_t
)sup
);
1033 mutex_enter(&sup
->su_mutex
);
1034 if (!sup
->su_tx_busy
) {
1036 * If DLD_CAPAB_DIRECT is enabled, the notify callback will be
1037 * called when the flow control can be disabled. Otherwise,
1038 * put the tx_flow_mp into the wq to make use of the old
1039 * streams flow control.
1041 ASSERT(sup
->su_tx_flow_mp
!= NULL
);
1042 (void) putq(sup
->su_wq
, sup
->su_tx_flow_mp
);
1043 sup
->su_tx_flow_mp
= NULL
;
1044 sup
->su_tx_busy
= B_TRUE
;
1047 mutex_exit(&sup
->su_mutex
);
1049 if ((flag
& MAC_DROP_ON_NO_DESC
) != 0)
1053 return ((mac_tx_cookie_t
)sup
);
1057 softmac_active_set(void *arg
)
1059 softmac_t
*softmac
= arg
;
1061 mutex_enter(&softmac
->smac_active_mutex
);
1062 if (softmac
->smac_nactive
!= 0) {
1063 mutex_exit(&softmac
->smac_active_mutex
);
1066 softmac
->smac_active
= B_TRUE
;
1067 mutex_exit(&softmac
->smac_active_mutex
);
1072 softmac_active_clear(void *arg
)
1074 softmac_t
*softmac
= arg
;
1076 mutex_enter(&softmac
->smac_active_mutex
);
1077 ASSERT(softmac
->smac_active
&& (softmac
->smac_nactive
== 0));
1078 softmac
->smac_active
= B_FALSE
;
1079 mutex_exit(&softmac
->smac_active_mutex
);
1083 * Disable/reenable fastpath on given softmac. This request could come from a
1084 * MAC client or directly from administrators.
1087 softmac_datapath_switch(softmac_t
*softmac
, boolean_t disable
, boolean_t admin
)
1089 softmac_upper_t
*sup
;
1090 mblk_t
*head
= NULL
, *tail
= NULL
, *mp
;
1092 softmac_switch_req_t
*req
;
1093 uint32_t current_mode
, expected_mode
;
1096 mutex_enter(&softmac
->smac_fp_mutex
);
1098 current_mode
= DATAPATH_MODE(softmac
);
1100 if (softmac
->smac_fastpath_admin_disabled
== disable
) {
1101 mutex_exit(&softmac
->smac_fp_mutex
);
1104 softmac
->smac_fastpath_admin_disabled
= disable
;
1105 } else if (disable
) {
1106 softmac
->smac_fp_disable_clients
++;
1108 ASSERT(softmac
->smac_fp_disable_clients
!= 0);
1109 softmac
->smac_fp_disable_clients
--;
1112 expected_mode
= DATAPATH_MODE(softmac
);
1113 if (current_mode
== expected_mode
) {
1114 mutex_exit(&softmac
->smac_fp_mutex
);
1119 * The expected mode is different from whatever datapath mode
1120 * this softmac is expected from last request, enqueue the data-path
1123 list_create(&reqlist
, sizeof (softmac_switch_req_t
),
1124 offsetof(softmac_switch_req_t
, ssq_req_list_node
));
1127 * Allocate all DL_NOTIFY_IND messages and request structures that
1128 * are required to switch each IP/ARP stream to the expected mode.
1130 for (sup
= list_head(&softmac
->smac_sup_list
); sup
!= NULL
;
1131 sup
= list_next(&softmac
->smac_sup_list
, sup
)) {
1132 dl_notify_ind_t
*dlip
;
1134 req
= kmem_alloc(sizeof (softmac_switch_req_t
), KM_NOSLEEP
);
1138 req
->ssq_expected_mode
= expected_mode
;
1139 if (sup
->su_is_arp
) {
1140 list_insert_tail(&reqlist
, req
);
1144 * Allocate the DL_NOTE_REPLUMB message.
1146 if ((mp
= allocb(sizeof (dl_notify_ind_t
), BPRI_LO
)) == NULL
) {
1147 kmem_free(req
, sizeof (softmac_switch_req_t
));
1151 list_insert_tail(&reqlist
, req
);
1153 mp
->b_wptr
= mp
->b_rptr
+ sizeof (dl_notify_ind_t
);
1154 mp
->b_datap
->db_type
= M_PROTO
;
1155 bzero(mp
->b_rptr
, sizeof (dl_notify_ind_t
));
1156 dlip
= (dl_notify_ind_t
*)mp
->b_rptr
;
1157 dlip
->dl_primitive
= DL_NOTIFY_IND
;
1158 dlip
->dl_notification
= DL_NOTE_REPLUMB
;
1168 * Note that it is fine if the expected data-path mode is fast-path
1169 * and some of streams fails to switch. Only return failure if we
1170 * are expected to switch to the slow-path.
1172 if (sup
!= NULL
&& expected_mode
== SOFTMAC_SLOWPATH
) {
1178 * Start switching for each IP/ARP stream. The switching operation
1179 * will eventually succeed and there is no need to wait for it
1182 for (sup
= list_head(&softmac
->smac_sup_list
); sup
!= NULL
;
1183 sup
= list_next(&softmac
->smac_sup_list
, sup
)) {
1184 if (!sup
->su_is_arp
) {
1186 head
->b_next
= NULL
;
1187 softmac_wput_nondata(sup
, head
);
1191 * Add the switch request to the requests list of the stream.
1193 req
= list_head(&reqlist
);
1194 ASSERT(req
!= NULL
);
1195 list_remove(&reqlist
, req
);
1196 list_insert_tail(&sup
->su_req_list
, req
);
1199 mutex_exit(&softmac
->smac_fp_mutex
);
1200 ASSERT(list_is_empty(&reqlist
));
1201 list_destroy(&reqlist
);
1205 softmac
->smac_fastpath_admin_disabled
= !disable
;
1206 } else if (disable
) {
1207 softmac
->smac_fp_disable_clients
--;
1209 softmac
->smac_fp_disable_clients
++;
1212 mutex_exit(&softmac
->smac_fp_mutex
);
1213 while ((req
= list_head(&reqlist
)) != NULL
) {
1214 list_remove(&reqlist
, req
);
1215 kmem_free(req
, sizeof (softmac_switch_req_t
));
1218 list_destroy(&reqlist
);
1223 softmac_fastpath_disable(void *arg
)
1225 return (softmac_datapath_switch((softmac_t
*)arg
, B_TRUE
, B_FALSE
));
1229 softmac_fastpath_enable(void *arg
)
1231 VERIFY(softmac_datapath_switch((softmac_t
*)arg
, B_FALSE
,
1236 softmac_upperstream_close(softmac_upper_t
*sup
)
1238 softmac_t
*softmac
= sup
->su_softmac
;
1239 softmac_switch_req_t
*req
;
1241 mutex_enter(&softmac
->smac_fp_mutex
);
1243 if (sup
->su_mode
== SOFTMAC_FASTPATH
)
1244 softmac_fastpath_tear(sup
);
1246 if (sup
->su_mode
!= SOFTMAC_UNKNOWN
) {
1247 list_remove(&softmac
->smac_sup_list
, sup
);
1248 sup
->su_mode
= SOFTMAC_UNKNOWN
;
1252 * Cleanup all the switch requests queueed on this stream.
1254 while ((req
= list_head(&sup
->su_req_list
)) != NULL
) {
1255 list_remove(&sup
->su_req_list
, req
);
1256 kmem_free(req
, sizeof (softmac_switch_req_t
));
1258 mutex_exit(&softmac
->smac_fp_mutex
);
1262 * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper
1263 * stream from the fastpath mode to the slowpath mode.
1266 softmac_datapath_switch_done(softmac_upper_t
*sup
)
1268 softmac_t
*softmac
= sup
->su_softmac
;
1269 softmac_switch_req_t
*req
;
1270 uint32_t expected_mode
;
1272 mutex_enter(&softmac
->smac_fp_mutex
);
1273 req
= list_head(&sup
->su_req_list
);
1274 list_remove(&sup
->su_req_list
, req
);
1275 expected_mode
= req
->ssq_expected_mode
;
1276 kmem_free(req
, sizeof (softmac_switch_req_t
));
1278 if (expected_mode
== sup
->su_mode
) {
1279 mutex_exit(&softmac
->smac_fp_mutex
);
1283 ASSERT(!sup
->su_bound
);
1284 mutex_exit(&softmac
->smac_fp_mutex
);
1287 * It is fine if the expected mode is fast-path and we fail
1288 * to enable fastpath on this stream.
1290 if (expected_mode
== SOFTMAC_SLOWPATH
)
1291 softmac_fastpath_tear(sup
);
1293 (void) softmac_fastpath_setup(sup
);