dld_proto.c revision cef310fd50f0f099f0a2bf114344d78cf47e3bdb
/*
* 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.
*/
/*
* Data-Link Driver
*/
#include <sys/sysmacros.h>
#include <sys/dld_impl.h>
#include <sys/mac_client.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_client_priv.h>
#define DL_ACK_PENDING(state) \
((state) == DL_ATTACH_PENDING || \
(state) == DL_DETACH_PENDING || \
(state) == DL_BIND_PENDING || \
(state) == DL_UNBIND_PENDING)
/*
* Process a DLPI protocol message.
* The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
* DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
* 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
* as 'passive' and forbids it from being subsequently made 'active'
* by the above primitives.
*/
void
{
return;
}
switch (prim) {
case DL_INFO_REQ:
break;
case DL_BIND_REQ:
break;
case DL_UNBIND_REQ:
break;
case DL_UNITDATA_REQ:
break;
case DL_UDQOS_REQ:
break;
case DL_ATTACH_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_ENABMULTI_REQ:
break;
case DL_DISABMULTI_REQ:
break;
case DL_PROMISCON_REQ:
break;
case DL_PROMISCOFF_REQ:
break;
case DL_PHYS_ADDR_REQ:
break;
case DL_SET_PHYS_ADDR_REQ:
break;
case DL_NOTIFY_REQ:
break;
case DL_CAPABILITY_REQ:
break;
case DL_PASSIVE_REQ:
break;
default:
break;
}
}
#define NEG(x) -(x)
typedef struct dl_info_ack_wrapper {
/*
* DL_INFO_REQ
*/
static void
{
/*
* Swap the request message for one large enough to contain the
* wrapper structure defined above.
*/
return;
/*
* Set up the sub-structure pointers.
*/
/*
* This driver supports only version 2 connectionless DLPI provider
* nodes.
*/
/*
* Set the style of the provider
*/
/*
* Set the current DLPI state.
*/
/*
* Gratuitously set the media type. This is to deal with modules
* that assume the media type is known prior to DL_ATTACH_REQ
* being completed.
*/
/*
* If the stream is not at least attached we try to retrieve the
* mac_info using mac_info_get()
*/
/*
* Cannot find mac_info. giving up.
*/
goto done;
}
} else {
/* We can only get the sdu if we're attached. */
}
/*
* Set the media type (properly this time).
*/
else
/*
* Set the DLSAP length. We only support 16 bit values and they
* appear after the MAC address portion of DLSAP addresses.
*/
sap_length = sizeof (uint16_t);
/*
* Copy in the media broadcast address.
*/
}
/* Only VLAN links and links that have a normal tag mode support QOS. */
/*
* Specify the supported range of priorities.
*/
/*
* Specify the current priority (which can be changed by
* the DL_UDQOS_REQ primitive).
*/
}
/*
* The stream is bound. Therefore we can formulate a valid
* DLSAP address.
*/
if (addr_length > 0)
}
done:
dlp->dl_qos_range_length != 0));
dlp->dl_brdcst_addr_length != 0));
}
/*
* DL_ATTACH_REQ
*/
static void
{
int err = 0;
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
if (err != 0) {
switch (err) {
case ENOENT:
err = 0;
break;
default:
break;
}
goto failed;
}
return;
}
/*
* DL_DETACH_REQ
*/
static void
{
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
dl_err = DL_BADPRIM;
goto failed;
}
return;
}
/*
* DL_BIND_REQ
*/
static void
{
int err = 0;
void *mdip;
dl_err = DL_BADPRIM;
goto failed;
}
if (dlp->dl_xidtest_flg != 0) {
goto failed;
}
goto failed;
}
goto failed;
}
goto failed2;
}
/*
* Set the receive callback.
*/
/*
* Bind the channel such that it can receive packets.
*/
if (err != 0) {
switch (err) {
case EINVAL:
dl_err = DL_BADADDR;
err = 0;
break;
default:
break;
}
goto failed2;
}
/*
* We do this after we get out of the perim to avoid deadlocks
* etc. since part of mac_client_retarget_intr is to walk the
* device tree in order to find and retarget the interrupts.
*/
/*
* Copy in MAC address.
*/
/*
* Copy in the SAP.
*/
dlsap_addr_length += sizeof (uint16_t);
return;
}
/*
* DL_UNBIND_REQ
*/
static void
{
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
while (dsp->ds_datathr_cnt != 0)
/*
* Unbind the channel to stop packets being received.
*/
if (dls_unbind(dsp) != 0) {
goto failed;
}
/*
* Disable polling mode, if it is enabled.
*/
/*
* Clear LSO flags.
*/
dsp->ds_lso_max = 0;
/*
* Clear the receive callback.
*/
/*
* Set the mode back to the default (unitdata).
*/
return;
}
/*
* DL_PROMISCON_REQ
*/
static void
{
int err = 0;
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
case DL_PROMISC_SAP:
break;
case DL_PROMISC_MULTI:
break;
case DL_PROMISC_PHYS:
break;
default:
goto failed;
}
goto failed2;
}
/*
* Adjust channel promiscuity.
*/
if (err != 0) {
goto failed2;
}
return;
}
/*
* DL_PROMISCOFF_REQ
*/
static void
{
int err = 0;
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
case DL_PROMISC_SAP:
dl_err = DL_NOTENAB;
goto failed;
}
break;
case DL_PROMISC_MULTI:
dl_err = DL_NOTENAB;
goto failed;
}
break;
case DL_PROMISC_PHYS:
dl_err = DL_NOTENAB;
goto failed;
}
break;
default:
goto failed;
}
/*
* Adjust channel promiscuity.
*/
if (err != 0) {
goto failed;
}
return;
}
/*
* DL_ENABMULTI_REQ
*/
static void
{
int err = 0;
goto failed;
}
dl_err = DL_BADPRIM;
goto failed;
}
goto failed2;
}
if (err != 0) {
switch (err) {
case EINVAL:
dl_err = DL_BADADDR;
err = 0;
break;
case ENOSPC:
dl_err = DL_TOOMANY;
err = 0;
break;
default:
break;
}
goto failed2;
}
return;
}
/*
* DL_DISABMULTI_REQ
*/
static void
{
int err = 0;
goto failed;
}
dl_err = DL_BADPRIM;
goto failed;
}
if (err != 0) {
switch (err) {
case EINVAL:
dl_err = DL_BADADDR;
err = 0;
break;
case ENOENT:
dl_err = DL_NOTENAB;
err = 0;
break;
default:
break;
}
goto failed;
}
return;
}
/*
* DL_PHYS_ADDR_REQ
*/
static void
{
char *addr;
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
goto failed;
}
if (addr_length > 0) {
else
} else {
}
return;
}
/*
* DL_SET_PHYS_ADDR_REQ
*/
static void
{
int err = 0;
goto failed;
}
dl_err = DL_BADPRIM;
goto failed;
}
goto failed2;
}
if (err != 0) {
switch (err) {
case EINVAL:
dl_err = DL_BADADDR;
err = 0;
break;
default:
break;
}
goto failed2;
}
return;
}
/*
* DL_UDQOS_REQ
*/
static void
{
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
selp->dl_priority < 0) {
goto failed;
}
return;
}
static boolean_t
{
claimstr(q);
releasestr(q);
return (ret);
}
/*
* DL_CAPABILITY_REQ
*/
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;
}
case DL_CAPAB_DLD: {
/*
* Copy for alignment.
*/
break;
}
default:
break;
}
}
return;
}
/*
* DL_NOTIFY_REQ
*/
static void
{
dl_err = DL_BADPRIM;
goto failed;
}
goto failed;
}
/*
* Cache the notifications that are being enabled.
*/
/*
* The ACK carries all notifications regardless of which set is
* being enabled.
*/
/*
* Generate DL_NOTIFY_IND messages for each enabled notification.
*/
if (dsp->ds_notifications != 0) {
}
return;
}
/*
* DL_UINTDATA_REQ
*/
void
{
return;
}
return;
}
dl_err = DL_BADPRIM;
goto failed;
}
dl_err = DL_BADADDR;
goto failed;
}
/*
* Check the length of the packet and the block types.
*/
size = 0;
goto baddata;
}
goto baddata;
/*
* Build a packet header.
*/
dl_err = DL_BADADDR;
goto failed;
}
/*
* We no longer need the M_PROTO header, so free it.
*/
/*
* Transfer the checksum offload information if it is present.
*/
&flags);
/*
* Link the payload onto the new header.
*/
/*
* No lock can be held across modules and putnext()'s,
* which can happen here with the call from DLD_TX().
*/
/* flow-controlled */
}
return;
return;
}
/*
* DL_PASSIVE_REQ
*/
static void
{
/*
* If we've already become active by issuing an active primitive,
* then it's too late to try to become passive.
*/
goto failed;
}
dl_err = DL_BADPRIM;
goto failed;
}
return;
}
/*
* Catch-all handler.
*/
static void
{
}
static int
{
switch (flags) {
case DLD_ENABLE:
return (0);
case DLD_DISABLE:
return (0);
case DLD_QUERY:
}
return (0);
}
static int
{
switch (flags) {
case DLD_ENABLE:
return (0);
case DLD_DISABLE:
return (0);
}
return (ENOTSUP);
}
/*
* dld_capab_poll_enable()
*
* This function is misnamed. All polling and fanouts are run out of the
* lower mac (in case of VNIC and the only mac in case of NICs). The
* availability of Rx ring and promiscous mode is all taken care between
* the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any
* fanout necessary is done by the soft rings that are part of the
* mac_srs (by default mac_srs sends the packets up via a TCP and
* non TCP soft ring).
*
* The mac_srs (or its associated soft rings) always store the ill_rx_ring
* (the cookie returned when they registered with IP during plumb) as their
* 2nd argument which is passed up as mac_resource_handle_t. The upcall
* function and 1st argument is what the caller registered when they
* called mac_rx_classify_flow_add() to register the flow. For VNIC,
* the function is vnic_rx and argument is vnic_t. For regular NIC
* case, it mac_rx_default and mac_handle_t. As explained above, the
* mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
* from its stored 2nd argument.
*/
static int
{
if (dsp->ds_polling)
return (EINVAL);
return (ENOTSUP);
/*
* Enable client polling if and only if DLS bypass is possible.
* Special cases like VLANs need DLS processing in the Rx data path.
* In such a case we can neither allow the client (IP) to directly
* poll the softring (since DLS processing hasn't been done) nor can
* we allow DLS bypass.
*/
return (ENOTSUP);
/*
* Register soft ring resources. This will come in handy later if
* the user decides to modify CPU bindings to use more CPUs for the
* device in which case we will switch to fanout using soft rings.
*/
poll->poll_ring_ch);
return (0);
}
/* ARGSUSED */
static int
{
if (!dsp->ds_polling)
return (EINVAL);
return (0);
}
static int
{
switch (flags) {
case DLD_ENABLE:
case DLD_DISABLE:
}
return (ENOTSUP);
}
static int
{
switch (flags) {
case DLD_ENABLE: {
/*
* Check if LSO is supported on this MAC & enable LSO
* accordingly.
*/
/* translate the flag for mac clients */
} else {
dsp->ds_lso_max = 0;
return (ENOTSUP);
}
return (0);
}
case DLD_DISABLE: {
dsp->ds_lso_max = 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_POLL:
break;
case DLD_CAPAB_PERIM:
break;
case DLD_CAPAB_LSO:
break;
default:
break;
}
return (err);
}
/*
*/
static void
{
/*
* Initially assume no capabilities.
*/
subsize = 0;
/*
* Check if checksum offload is supported on this MAC. Don't
* advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable,
* since it might not be able to do the hardware checksum offload
* with the correct offset.
*/
&hcksum.hcksum_txflags)) {
if (hcksum.hcksum_txflags != 0) {
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_hcksum_t);
}
}
/*
* Check if zerocopy is supported on this interface.
* If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
* then reserve space for that capability.
*/
!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_zerocopy_t);
}
/*
* Direct capability negotiation interface between IP and DLD
*/
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_dld_t);
}
/*
* If there are no capabilities to advertise or if we
* can't allocate a response, send a DL_ERROR_ACK.
*/
return;
}
/*
*/
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);
}
/*
* Direct capability negotiation interface between IP and DLD.
* Refer to dld.h for details.
*/
if (dld_capable) {
ptr += sizeof (dl_capability_sub_t);
ptr += sizeof (dl_capab_dld_t);
}
}
/*
* Disable any enabled capabilities.
*/
void
{
if (dsp->ds_polling)
}