/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Softmac data-path switching:
*
* - Fast-path model
*
* When the softmac fast-path is used, a dedicated lower-stream
* over the softMAC, and all DLPI messages (including control messages
* and data messages) will be exchanged between the upper-stream and
* the corresponding lower-stream directly. Therefore, the data
* demultiplexing, filtering and classification processing will be done
* no longer needed.
*
* - Slow-path model
*
* not be bypassed to assure its function correctness. For example,
* softmac fast-path must be disabled to support GLDv3 VNIC functionality.
* In this case, a shared lower-stream will be opened over the legacy
* device, which is responsible for implementing the GLDv3 callbacks
* and passing RAW data messages between the legacy devices and the GLDv3
* framework.
*
* By default, the softmac fast-path mode will be used to assure the
* performance; MAC clients will be able to request to disable the softmac
* fast-path mode to support certain features, and if that succeeds,
* the system will fallback to the slow-path softmac data-path model.
*
*
* The details of the softmac data fast-path model is stated as below
*
* 1. When a stream is opened on a softMAC, the softmac module will takes
* over the DLPI processing on this stream;
*
* used by default, unless fast-path is disabled by any MAC client
* by seeing whether there is a SIOCSLIFNAME ioctl sent from upstream,
* if there is one, this stream is either an IP or an ARP stream
* and will use fast-path potentially;
*
* 3. When the softmac fast-path is used, an dedicated lower-stream will
* upper-stream and the legacy device through this dedicated
* will be skipped, and this greatly improves the performance;
*
* 4. When the softmac data fast-path is disabled by a MAC client (e.g.,
* the fast-path to the slow-path. The dedicated lower-stream will be
* destroyed, and all the control and data-messages will go through the
* existing GLDv3 code path and (in the end) the shared lower-stream;
*
* 5. On the other hand, when the last MAC client cancels its fast-path
* the fast-path mode;
*
* Step 5 and 6 both rely on the data-path mode switching process
* described below:
*
* 1) To switch the softmac data-path mode (between fast-path and slow-path),
* softmac will first send a DL_NOTE_REPLUMB DL_NOTIFY_IND message
*
* 2) When IP receives this DL_NOTE_REPLUMB message, it will bring down
* all the IP interfaces on the corresponding ill (IP Lower level
* structure), and bring up those interfaces over again; this will in
* turn cause the ARP to "replumb" the interface.
*
* During the replumb process, both IP and ARP will send downstream the
* necessary DL_DISABMULTI_REQ and DL_UNBIND_REQ messages and cleanup
* the old state of the underlying softMAC, following with the necessary
* DL_BIND_REQ and DL_ENABMULTI_REQ messages to setup the new state.
* a DL_NOTE_REPLUMB_DONE DL_NOTIFY_CONF messages to the softMAC to
* indicate the *switching point*;
*
* 3) When softmac receives the DL_NOTE_REPLUMB_DONE message, it either
* creates or destroys the dedicated lower-stream (depending on which
* data-path mode the softMAC switches to), and change the softmac
* data-path mode. From then on, softmac will process all the succeeding
* control messages (including the DL_BIND_REQ and DL_ENABMULTI_REQ
* messages) and data messages based on new data-path mode.
*/
#include <sys/sysmacros.h>
#include <sys/softmac_impl.h>
static void softmac_taskq_dispatch();
static int softmac_fastpath_setup(softmac_upper_t *);
static void softmac_datapath_switch_done(softmac_upper_t *);
void
{
}
void
{
/*
* Request the softmac_taskq thread to quit and wait for it to be done.
*/
while (!softmac_taskq_done)
}
static boolean_t
{
claimstr(q);
releasestr(q);
return (ret);
}
/* ARGSUSED */
static int
{
switch (flags) {
case DLD_ENABLE:
break;
case DLD_DISABLE:
break;
case DLD_QUERY:
}
return (0);
}
static mac_tx_notify_handle_t
{
} else {
/*
* Wait for all tx_notify_func call to be done.
*/
while (sup->su_tx_inprocess != 0)
}
return ((mac_tx_notify_handle_t)sup);
}
static boolean_t
{
return (sup->su_tx_busy);
}
static int
{
switch (flags) {
case DLD_ENABLE:
return (0);
return (0);
case DLD_DISABLE:
return (0);
return (0);
}
return (ENOTSUP);
}
static int
{
int err;
/*
* Don't enable direct callback capabilities unless the caller is
* the IP client. When a module is inserted in a stream (_I_INSERT)
* the stack initiates capability disable, but due to races, the
* module insertion may complete before the capability disable
* completes. So we limit the check to DLD_ENABLE case.
*/
return (ENOTSUP);
}
switch (type) {
case DLD_CAPAB_DIRECT:
break;
case DLD_CAPAB_PERIM:
break;
default:
break;
}
return (err);
}
static void
{
/*
* Initially assume no capabilities.
*/
subsize = 0;
/*
* Direct capability negotiation interface between IP and softmac
*/
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_dld_t);
}
/*
* Check if checksum offload is supported on this MAC.
*/
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_hcksum_t);
}
/*
* Check if zerocopy is supported on this interface.
*/
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_zerocopy_t);
}
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_mdt_t);
}
/*
* If there are no capabilities to advertise or if we
* can't allocate a response, send a DL_ERROR_ACK.
*/
return;
}
/*
* IP polling interface.
*/
if (dld_capable) {
ptr += sizeof (dl_capability_sub_t);
ptr += sizeof (dl_capab_dld_t);
}
/*
*/
if (hcksum_capable) {
ptr += sizeof (dl_capability_sub_t);
ptr += sizeof (dl_capab_hcksum_t);
}
/*
* Zero copy
*/
if (zcopy_capable) {
ptr += sizeof (dl_capability_sub_t);
ptr += sizeof (dl_capab_zerocopy_t);
}
/*
* MDT
*/
if (mdt_capable) {
ptr += sizeof (dl_capability_sub_t);
ptr += sizeof (dl_capab_mdt_t);
}
}
static void
{
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
/*
* This request is overloaded. If there are no requested capabilities
* then we just want to acknowledge with all the capabilities we
* support. Otherwise we enable the set of capabilities requested.
*/
if (dlp->dl_sub_length == 0) {
return;
}
dl_err = DL_BADPRIM;
goto failed;
}
/*
* Walk the list of capabilities to be enabled.
*/
dl_err = DL_BADPRIM;
goto failed;
}
/*
*/
case DL_CAPAB_HCKSUM: {
/*
* Copy for alignment.
*/
break;
}
default:
break;
}
}
return;
}
static void
{
int err;
return;
}
/*
* Allocate ackmp incase the underlying driver does not ack timely.
*/
return;
}
} else {
/*
* The driver does not ack timely.
*/
}
if (err != 0)
goto failed;
/*
* Enable capabilities the underlying driver claims to support.
*/
goto failed;
/*
* Check whether this softmac is already marked as exclusively used,
* e.g., an aggregation is created over it. Fail the BIND_REQ if so.
*/
if (softmac->smac_active) {
goto failed;
}
softmac->smac_nactive++;
return;
if (err != 0) {
return;
}
}
static void
{
int err;
return;
}
return;
}
/*
* Allocate ackmp incase the underlying driver does not ack timely.
*/
return;
}
} else {
/*
* The driver does not ack timely.
*/
}
if (err != 0) {
return;
}
softmac->smac_nactive--;
}
done:
}
/*
* Process the non-data mblk.
*/
static void
{
unsigned char dbtype;
switch (dbtype) {
case M_CTL:
/* FALLTHROUGH */
case M_IOCTL: {
break;
/*
* Nak the M_IOCTL based on the STREAMS specification.
*/
else
/*
* This stream is either IP or ARP. See whether
* we need to setup a dedicated-lower-stream for it.
*/
if (expected_mode == SOFTMAC_SLOWPATH)
/*
* Setup the fast-path dedicated lower stream if fast-path
* is expected. Note that no lock is held here, and if
* smac_expected_mode is changed from SOFTMAC_FASTPATH to
* SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for
* data-path switching would already be queued and will
* be processed by softmac_wput_single_nondata() later.
*/
if (expected_mode == SOFTMAC_FASTPATH)
(void) softmac_fastpath_setup(sup);
return;
}
case M_PROTO:
case M_PCPROTO:
return;
}
switch (prim) {
case DL_NOTIFY_IND:
return;
}
/*
* This DL_NOTE_REPLUMB message is initiated
* and queued by the softmac itself, when the
* sup is trying to switching its datapath mode
* between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH.
* Send this message upstream.
*/
return;
case DL_NOTIFY_CONF:
return;
}
/*
* fastpath->slowpath switch is done.
*/
return;
}
break;
}
/*
* No need to hold lock to check su_mode, since su_mode updating only
* operation is is serialized by softmac_wput_nondata_task().
*/
return;
}
/*
* Fastpath non-data message processing. Most of non-data messages
* can be directly passed down to the dedicated-lower-stream, aside
*/
switch (dbtype) {
case M_PROTO:
case M_PCPROTO:
switch (prim) {
case DL_BIND_REQ:
break;
case DL_UNBIND_REQ:
break;
case DL_CAPABILITY_REQ:
break;
default:
break;
}
break;
default:
break;
}
}
/*
* The worker thread which processes non-data messages. Note we only process
* one message at one time in order to be able to "flush" the queued message
* and serialize the processing.
*/
static void
{
if (sup->su_closing)
break;
}
/*
* If the stream is closing, flush all queued messages and inform
* the stream to be closed.
*/
}
/*
* Kernel thread to handle taskq dispatch failures in softmac_wput_nondata().
* This thread is started when the softmac module is first loaded.
*/
static void
softmac_taskq_dispatch(void)
{
"softmac_taskq_dispatch");
while (!softmac_taskq_quit) {
}
}
thread_exit();
}
void
{
/*
* The processing of the message might block. Enqueue the
* message for later processing.
*/
if (sup->su_closing) {
return;
}
if (sup->su_dlpi_pending) {
return;
}
return;
}
if (!sup->su_taskq_scheduled) {
}
}
/*
*/
static int
{
int err;
/*
* Wait for all data messages to be processed so that we can change
* the su_mode.
*/
while (sup->su_tx_inprocess != 0)
if (err != 0) {
} else {
}
return (err);
}
/*
*/
static void
{
/*
* Wait for all data messages in the dedicated-lower-stream
* to be processed.
*/
while (sup->su_tx_inprocess != 0)
/*
* Note that this function is called either when the stream is closed,
* or the stream is unbound (fastpath-slowpath-switch). Therefore,
* No need to call the tx_notify callback.
*/
if (sup->su_tx_busy) {
}
/*
* Destroy the dedicated-lower-stream. Note that slp is destroyed
* when lh is closed.
*/
}
void
{
/*
* No lock is required to access the su_mode field since the data
* traffic is quiesce by IP when the data-path mode is in the
* process of switching.
*/
else
}
/*ARGSUSED*/
static mac_tx_cookie_t
{
/*
* This function is called from IP, only the MAC_DROP_ON_NO_DESC
* flag can be specified.
*/
/*
* Check wether the dedicated-lower-stream is able to handle more
* messages, and enable the flow-control if it is not.
*
* Note that in order not to introduce any packet reordering, we
* always send the message down to the dedicated-lower-stream:
*
* If the flow-control is already enabled, but we still get
* the messages from the upper-stream, it means that the upper
* stream does not respect STREAMS flow-control (e.g., TCP). Simply
* pass the message down to the lower-stream in that case.
*/
if (SOFTMAC_CANPUTNEXT(wq)) {
return (NULL);
}
if (sup->su_tx_busy) {
if ((flag & MAC_DROP_ON_NO_DESC) != 0)
else
return ((mac_tx_cookie_t)sup);
}
if (!sup->su_tx_busy) {
/*
* If DLD_CAPAB_DIRECT is enabled, the notify callback will be
* called when the flow control can be disabled. Otherwise,
* put the tx_flow_mp into the wq to make use of the old
* streams flow control.
*/
}
if ((flag & MAC_DROP_ON_NO_DESC) != 0)
else
return ((mac_tx_cookie_t)sup);
}
{
if (softmac->smac_nactive != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
void
{
}
/*
* MAC client or directly from administrators.
*/
int
{
int err = 0;
if (admin) {
return (0);
}
} else if (disable) {
} else {
}
if (current_mode == expected_mode) {
return (0);
}
/*
* The expected mode is different from whatever datapath mode
* this softmac is expected from last request, enqueue the data-path
* switch request.
*/
/*
* Allocate all DL_NOTIFY_IND messages and request structures that
*/
break;
continue;
}
/*
* Allocate the DL_NOTE_REPLUMB message.
*/
break;
}
} else {
}
}
/*
* Note that it is fine if the expected data-path mode is fast-path
* and some of streams fails to switch. Only return failure if we
* are expected to switch to the slow-path.
*/
goto fail;
}
/*
* will eventually succeed and there is no need to wait for it
* to finish.
*/
}
/*
* Add the switch request to the requests list of the stream.
*/
}
return (0);
fail:
if (admin) {
} else if (disable) {
} else {
}
}
return (err);
}
int
{
}
void
{
B_FALSE) == 0);
}
void
{
}
/*
* Cleanup all the switch requests queueed on this stream.
*/
}
}
/*
* stream from the fastpath mode to the slowpath mode.
*/
static void
{
return;
}
/*
* It is fine if the expected mode is fast-path and we fail
* to enable fastpath on this stream.
*/
if (expected_mode == SOFTMAC_SLOWPATH)
else
(void) softmac_fastpath_setup(sup);
}