softmac_main.c revision ee94b1c37a34b758315666dcd0bc7c46d1aea15c
/*
* 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.
*/
/*
* The softmac driver is used to "unify" non-GLDv3 drivers to the GLDv3
* framework. It also creates the kernel datalink structure for each
* physical network device.
*
* Specifically, a softmac will be created for each physical network device
* (dip) during the device's post-attach process. When this softmac is
* created, the following will also be done:
* - create the device's <link name, linkid> mapping;
* - register the mac if this is a non-GLDv3 device and the media type is
* supported by the GLDv3 framework;
* - create the kernel data-link structure for this physical device;
*
* This softmac will be destroyed during the device's pre-detach process,
* and all the above will be undone.
*/
#include <sys/mac_provider.h>
#include <sys/sysmacros.h>
#include <sys/softmac_impl.h>
/* Used as a parameter to the mod hash walk of softmac structures */
typedef struct {
/*
* Softmac hash table including softmacs for both style-2 and style-1 devices.
*/
static krwlock_t softmac_hash_lock;
static mod_hash_t *softmac_hash;
static kmutex_t smac_global_lock;
static kcondvar_t smac_global_cv;
static kmem_cache_t *softmac_cachep;
#define SOFTMAC_HASHSZ 64
static void softmac_create_task(void *);
static void softmac_mac_register(softmac_t *);
static int softmac_create_datalink(softmac_t *);
static int softmac_m_start(void *);
static void softmac_m_stop(void *);
static int softmac_m_open(void *);
static void softmac_m_close(void *);
static int softmac_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static int softmac_m_getprop(void *, const char *, mac_prop_id_t,
#define SOFTMAC_M_CALLBACK_FLAGS \
static mac_callbacks_t softmac_m_callbacks = {
};
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static void
{
}
void
{
sizeof (softmac_t), 0, softmac_constructor,
}
void
{
}
/* ARGSUSED */
static uint_t
{
return (MH_WALK_TERMINATE);
}
{
return (exist);
}
/*
*
* softmac_create() is called for each minor node during the post-attach of
* each DDI_NT_NET device instance. Note that it is possible that a device
* instance has two minor nodes (DLPI style-1 and style-2), so that for that
* specific device, softmac_create() could be called twice.
*
* A softmac_t is used to track each DDI_NT_NET device, and a softmac_dev_t
* is created to track each minor node.
*
* For each minor node of a legacy device, a taskq is started to finish
* softmac_mac_register(), which will finish the rest of work (see comments
* above softmac_mac_register()).
*
* softmac state machine
* --------------------------------------------------------------------------
* OLD STATE EVENT NEW STATE
* --------------------------------------------------------------------------
* UNINIT attach of 1st minor node ATTACH_INPROG
* okcnt = 0 net_postattach -> softmac_create okcnt = 1
*
* ATTACH_INPROG attach of 2nd minor node (GLDv3) ATTACH_DONE
* okcnt = 1 net_postattach -> softmac_create okcnt = 2
*
* ATTACH_INPROG attach of 2nd minor node (legacy) ATTACH_INPROG
* okcnt = 1 net_postattach -> softmac_create okcnt = 2
* schedule softmac_mac_register
*
* ATTACH_INPROG legacy device node ATTACH_DONE
* okcnt = 2 softmac_mac_register okcnt = 2
*
* ATTACH_DONE detach of 1st minor node DETACH_INPROG
* okcnt = 2 (success) okcnt = 1
*
* DETACH_INPROG detach of 2nd minor node UNINIT (or free)
* okcnt = 1 (success) okcnt = 0
*
* ATTACH_DONE detach failure state unchanged
* DETACH_INPROG left = okcnt
*
* DETACH_INPROG reattach ATTACH_INPROG
* okcnt = 0,1 net_postattach -> softmac_create
*
* ATTACH_DONE reattach ATTACH_DONE
* left != 0 net_postattach -> softmac_create left = 0
*
* Abbreviation notes:
* states have SOFTMAC_ prefix,
* okcnt - softmac_attach_okcnt,
* left - softmac_attached_left
*/
#ifdef DEBUG
void
{
/*
* There are at most 2 minor nodes, one per DLPI style
*/
/*
* The smac_attachok_cnt represents the number of attaches i.e. the
* number of times net_postattach -> softmac_create() has been called
* for a device instance.
*/
/*
* softmac_create (or softmac_mac_register) -> softmac_create_datalink
* happens only after all minor nodes have been attached
*/
if (softmac->smac_attachok_cnt == 0) {
} else {
/*
* In the stable condition the state whould be
* SOFTMAC_ATTACH_DONE. But there is a small transient window
* in softmac_destroy where we change the state to
* SOFTMAC_DETACH_INPROG and drop the lock before doing
* the link destroy
*/
}
}
#endif
#ifdef DEBUG
#else
#define SOFTMAC_STATE_VERIFY(softmac)
#endif
int
{
char devname[MAXNAMELEN];
int index;
/*
* Force the softmac driver to be attached.
*/
return (ENXIO);
}
/*
* For GLDv3, we don't care about the DLPI style 2
* compatibility node. (We know that all such devices
* have style 1 nodes.)
*/
(minor == 0)) {
return (0);
}
/*
* Likewise, we know that the minor number for DLPI style 1
* nodes is constrained to a maximum value.
*/
if (minor >= DLS_MAX_MINOR) {
return (ENOTSUP);
}
/*
* Otherwise we can decode the instance from the minor number,
* which allows for situations with multiple mac instances
* for a single dev_info_t.
*/
} else {
/*
* For legacy drivers, we just have to limit them to
* two minor nodes, one style 1 and one style 2, and
* we assume the ddi_get_instance() is the PPA.
* Drivers that need more flexibility should be ported
* to GLDv3.
*/
"unsupported", devname);
return (ENOTSUP);
}
}
/*
* Check whether the softmac for the specified device already exists
*/
(mod_hash_val_t *)&softmac)) != 0) {
}
if (softmac->smac_attachok_cnt == 0) {
/*
* Initialize the softmac if this is the post-attach of the
* first minor node.
*/
softmac->smac_flags = 0;
/*
* For GLDv3, we ignore the style 2 node (see the logic
* above on that), and we should have exactly one attach
* per MAC instance (possibly more than one per dev_info_t).
*/
} else {
}
}
/*
* This is possible if the post_attach() is called after
* pre_detach() fails. This seems to be a defect of the DACF
* framework. We work around it by using a smac_attached_left
* field that tracks this
*/
return (0);
}
/*
* Continue to register the mac and create the datalink only when all
* the minor nodes are attached.
*/
return (0);
}
/*
* All of the minor nodes have been attached; start a taskq
* to do the rest of the work. We use a taskq instead of
* doing the work here because:
*
* We could be called as a result of a open() system call
* where spec_open() already SLOCKED the snode. Using a taskq
* sidesteps the risk that our ldi_open_by_dev() call would
* deadlock trying to set SLOCKED on the snode again.
*
* The devfs design requires that the downcalls don't use any
* interruptible cv_wait which happens when we do door upcalls.
* Otherwise the downcalls which may be holding devfs resources
* may cause a deadlock if the thread is stopped. Also we need to make
* sure these downcalls into softmac_create or softmac_destroy
* don't cv_wait on any devfs related condition. Thus softmac_destroy
* returns EBUSY if the asynchronous threads started in softmac_create
* haven't finished.
*/
return (0);
}
static boolean_t
{
return (B_FALSE);
switch (cap) {
case MAC_CAPAB_HCKSUM: {
break;
}
case MAC_CAPAB_LEGACY: {
/*
* The caller is not interested in the details.
*/
break;
break;
}
/*
* For the capabilities below, there's nothing for us to fill in;
* simply return B_TRUE if we support it.
*/
case MAC_CAPAB_NO_ZCOPY:
case MAC_CAPAB_NO_NATIVEVLAN:
default:
break;
}
return (B_TRUE);
}
static int
{
int err;
}
/*
* There is a link name conflict. Either:
*
* - An existing link with the same device name with a
* different media type from of the given type.
* Mark this link back to persistent only; or
*
* - We cannot assign the "suggested" name because
* GLDv3 and therefore vanity naming is not supported
* for this link type. Delete this link's <link name,
* linkid> mapping.
*/
"existing %s device %s.",
} else {
}
}
return (err);
}
/*
* This function:
* 1. provides the link's media type to dlmgmtd.
* 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
*/
static int
{
int err;
/*
* Inform dlmgmtd of this link so that softmac_hold_device() is able
* to know the existence of this link. If this failed with EBADF,
* it might be because dlmgmtd was not started in time (e.g.,
* diskless boot); ignore the failure and continue to create
* the GLDv3 datalink if needed.
*/
return (err);
/*
* Provide the media type of the physical link to dlmgmtd.
*/
return (err);
}
/*
* Create the GLDv3 datalink.
*/
crgetzoneid(CRED()));
if (err != 0) {
return (err);
}
}
if (linkid == DATALINK_INVALID_LINKID) {
}
return (0);
}
static void
softmac_create_task(void *arg)
{
int err;
return;
}
goto done;
/*
* We can safely release the reference on the mac because
* this mac will only be unregistered and destroyed when
* the device detaches, and the softmac will be destroyed
* before then (in the pre-detach routine of the device).
*/
/*
* Create the GLDv3 datalink for this mac.
*/
done:
if (err != 0)
}
/*
* This function is only called for legacy devices. It:
* 1. registers the MAC for the legacy devices whose media type is supported
* by the GLDv3 framework.
* 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
*/
static void
{
int index;
int err;
/*
* Note that we do not need any locks to access this softmac pointer,
* as softmac_destroy() will wait until this function is called.
*/
goto done;
}
/*
* Determine whether this legacy device support VLANs by opening
* the style-2 device node (if it exists) and attaching to a VLAN
* PPA (1000 + ppa).
*/
if (err == 0) {
}
int rval;
continue;
li) != 0) {
continue;
}
/*
* Pop all the intermediate modules in order to negotiate
* capabilities correctly.
*/
;
/* DLPI style-1 or DLPI style-2? */
"DL_ERROR_ACK to DL_INFO_ACK; "
"DLPI errno 0x%x, UNIX errno %d",
}
continue;
}
/*
* Currently only DL_ETHER has GLDv3 mac plugin support.
* For media types that GLDv3 does not support, create a
* link id for it.
*/
err = 0;
break;
}
continue;
}
"DL_ERROR_ACK to DL_BIND_ACK; "
"DLPI errno 0x%x, UNIX errno %d",
}
continue;
}
/*
* Call dl_info() after dl_bind() because some drivers only
* provide correct information (e.g. MAC address) once bound.
*/
"DL_ERROR_ACK to DL_INFO_ACK; "
"DLPI errno 0x%x, UNIX errno %d",
}
continue;
}
(dlia.dl_brdcst_addr_offset == 0)) {
continue;
}
/*
* Check other DLPI capabilities. Note that this must be after
* dl_bind() because some drivers return DL_ERROR_ACK if the
* stream is not bound. It is also before mac_register(), so
* we don't need any lock protection here.
*/
/*
* Check the margin of the underlying driver.
*/
margin = 0;
softmac->smac_margin = 0;
&rval) == 0) {
}
/*
* If the legacy driver doesn't support DLIOCMARGININFO, but
* it can support native VLAN, correct its margin value to 4.
*/
if (native_vlan) {
if (softmac->smac_margin == 0)
} else {
}
/*
* Not all drivers support DL_NOTIFY_REQ, so ignore ENOTSUP.
*/
softmac->smac_notifications = 0;
case 0:
break;
case ENOTSUP:
break;
default:
continue;
}
err = 0;
break;
}
if (err != 0)
goto done;
/*
* Finally, we're ready to register ourselves with the MAC layer
* interface; if this succeeds, we're all ready to start()
*/
goto done;
}
if (err != 0) {
goto done;
}
}
/*
* Try to create the datalink for this softmac.
*/
goto done;
}
/*
* If succeed, create the thread which handles the DL_NOTIFY_IND from
* the lower stream.
*/
}
done:
}
int
{
char devname[MAXNAMELEN];
int index;
/*
* For an explanation of this logic, see the
* equivalent code in softmac_create.
*/
(minor == 0)) {
return (0);
}
if (minor >= DLS_MAX_MINOR) {
return (ENOTSUP);
}
} else {
}
/*
* We are called only from the predetach entry point. The DACF
* framework ensures there can't be a concurrent postattach call
* for the same softmac. The softmac found out from the modhash
* below can't vanish beneath us since this is the only place where
* it is deleted.
*/
(mod_hash_val_t *)&softmac);
/*
* Fail the predetach routine if this softmac is in-use.
* Make sure these downcalls into softmac_create or softmac_destroy
* don't cv_wait on any devfs related condition. Thus softmac_destroy
* returns EBUSY if the asynchronous thread started in softmac_create
* hasn't finished
*/
if ((softmac->smac_hold_cnt != 0) ||
return (EBUSY);
}
/*
* Even if the predetach of one minor node has already failed
* (smac_attached_left is not 0), the DACF framework will continue
* to call the predetach routines of the other minor nodes,
* so we fail these calls here.
*/
if (softmac->smac_attached_left != 0) {
return (EBUSY);
}
/*
* This is the first minor node that is being detached for this
* softmac.
*/
if (!(smac_flags & SOFTMAC_NOSUPP)) {
B_FALSE)) != 0) {
goto error;
}
}
/*
* If softmac_mac_register() succeeds in registering the mac
* of the legacy device, unregister it.
*/
crgetzoneid(CRED()));
goto error;
}
/*
* Ask softmac_notify_thread to quit, and wait for
* that to be done.
*/
&softmac->smac_mutex);
}
}
}
/*
* Free softmac_dev
*/
softmac->smac_attachok_cnt != 0);
if (--softmac->smac_attachok_cnt == 0) {
if (softmac->smac_hold_cnt != 0) {
/*
* Someone did a softmac_hold_device while we dropped
* the locks. Leave the softmac itself intact which
* will be reused by the reattach
*/
return (0);
}
(mod_hash_val_t *)&hashval);
return (0);
}
return (0);
return (err);
}
/*
* This function is called as the result of a newly started dlmgmtd daemon.
*
* We walk through every softmac that was created but failed to notify
* dlmgmtd about it (whose SOFTMAC_NEED_RECREATE flag is set). This occurs
* when softmacs are created before dlmgmtd is ready. For example, during
* diskless boot, a network device is used (and therefore attached) before
* the datalink-management service starts dlmgmtd.
*/
/* ARGSUSED */
static uint_t
{
int err;
/*
* The framework itself must not hold any locks across calls to the
* mac perimeter. Thus this function does not call any framework
* function that needs to grab the mac perimeter.
*/
/*
* Wait till softmac_create or softmac_mac_register finishes
* Hold the softmac to ensure it stays around. The wait itself
* is done in the caller, since we need to drop all locks
* including the mod hash's internal lock before calling
* cv_wait.
*/
softmac->smac_hold_cnt++;
return (MH_WALK_TERMINATE);
}
return (MH_WALK_CONTINUE);
}
/*
* Bumping up the smac_hold_cnt allows us to drop the lock. It also
* makes softmac_destroy() return failure on an attempted device detach.
* We don't want to hold the lock across calls to other subsystems
* like kstats, which will happen in the call to dls_devnet_recreate
*/
softmac->smac_hold_cnt++;
return (MH_WALK_CONTINUE);
}
return (MH_WALK_CONTINUE);
}
/*
* Create a link for this MAC. The link name will be the same
* as the MAC name.
*/
if (err != 0) {
"%s (linkid %d) failed (%d)",
}
}
softmac->smac_hold_cnt--;
return (MH_WALK_CONTINUE);
}
/*
* See comments above softmac_mac_recreate().
*/
void
{
/*
* Walk through the softmac_hash table. Request to create the
* [link name, linkid] mapping if we failed to do so.
*/
do {
/*
* softmac_create or softmac_mac_register hasn't yet
* finished and the softmac is not yet in the
* SOFTMAC_ATTACH_DONE state.
*/
softmac->smac_hold_cnt--;
}
}
static int
softmac_m_start(void *arg)
{
int err;
/*
* Bind to SAP 2 on token ring, 0 on other interface types.
* (SAP 0 has special significance on token ring).
* Note that the receive-side packets could come anytime after bind.
*/
if (err != 0)
return (err);
/*
* Put the lower stream to the DL_PROMISC_SAP mode in order to receive
* all packets of interest.
*
* some driver (e.g. the old legacy eri driver) incorrectly passes up
* packets to DL_PROMISC_SAP stream when the lower stream is not bound,
* so that we send DL_PROMISON_REQ after DL_BIND_REQ.
*/
if (err != 0) {
(void) softmac_send_unbind_req(slp);
return (err);
}
/*
* Enable capabilities the underlying driver claims to support.
* Some driver requires this being called after the stream is bound.
*/
(void) softmac_send_unbind_req(slp);
}
return (err);
}
/* ARGSUSED */
static void
softmac_m_stop(void *arg)
{
/*
* It is not needed to reset zerocopy, MDT or HCKSUM capabilities.
*/
(void) softmac_send_unbind_req(slp);
}
/*
* Set up the lower stream above the legacy device. There are two different
* type of lower streams:
*
* - Shared lower-stream
*
* Shared by all GLDv3 MAC clients. Put the lower stream to the DLIOCRAW
* mode to send and receive the raw data. Further, put the lower stream into
* DL_PROMISC_SAP mode to receive all packets of interest.
*
* - Dedicated lower-stream
*
* as fast-path for IP. In this case, the second argument is the pointer to
* the softmac upper-stream.
*/
int
{
return (err);
/*
* The GLDv3 framework makes sure that mac_unregister(), mac_open(),
* and mac_close() cannot be called at the same time. So we don't
* need any protection to access softmac here.
*/
if (err != 0)
goto done;
/*
* Pop all the intermediate modules. The autopushed modules will
* be pushed when the softmac node is opened.
*/
;
goto done;
}
/*
* If this is the shared-lower-stream, put the lower stream to
*/
goto done;
}
/*
* Then push the softmac shim layer atop the lower stream.
*/
goto done;
}
/*
* Send the ioctl to get the slp pointer.
*/
goto done;
}
} else {
/*
* Send DL_NOTIFY_REQ to enable certain DL_NOTIFY_IND.
* We don't have to wait for the ack.
*/
(void) softmac_send_notify_req(slp,
}
done:
if (err != 0)
return (err);
}
static int
softmac_m_open(void *arg)
{
int err;
return (err);
return (0);
}
static void
softmac_m_close(void *arg)
{
/*
* Note that slp is destroyed when lh is closed.
*/
}
/*
* Softmac supports two priviate link properteis:
*
* - "_fastpath"
*
* This is a read-only link property which points out the current data-path
* model of the given legacy link. The possible values are "disabled" and
* "enabled".
*
* - "_disable_fastpath"
*
* This is a read-write link property which can be used to disable or enable
* the fast-path of the given legacy link. The possible values are "true"
* and "false". Note that even when "_disable_fastpath" is set to be
* "false", the fast-path may still not be enabled since there may be
* other mac cleints that request the fast-path to be disabled.
*/
/* ARGSUSED */
static int
{
return (ENOTSUP);
else
return (EINVAL);
}
static int
{
char *fpstr;
if (id != MAC_PROP_PRIVATE)
return (ENOTSUP);
if ((flags & MAC_PROP_DEFAULT) != 0)
return (ENOTSUP);
"disabled" : "enabled";
*perm = MAC_PROP_PERM_RW;
} else {
return (ENOTSUP);
}
}
int
{
char devname[MAXNAMELEN];
/*
* For GLDv3 devices, look up the device instance using getinfo(9e).
* Otherwise, fall back to the old assumption that inst == ppa. The
* GLDV3_DRV() macro depends on the driver module being loaded, hence
* the call to ddi_hold_driver().
*/
return (ENXIO);
} else {
}
if (err != 0)
return (err);
/*
* First try to hold this device instance to force device to attach
* and ensure that the softmac entry gets created in net_postattach().
*/
return (ENOENT);
/*
* Exclude non-physical network device instances, for example, aggr0.
* Note: this check *must* occur after the dip is held, or else
* NETWORK_PHYSDRV might return false incorrectly. The
* DN_NETWORK_PHYSDRIVER flag used by NETWORK_PHYSDRV() gets set if
* ddi_create_minor_node() is called during the device's attach
* phase.
*/
if (!NETWORK_PHYSDRV(major)) {
return (ENOENT);
}
/* Now wait for its softmac to be created. */
ppa);
(mod_hash_val_t *)&softmac) != 0) {
/*
* This is rare but possible. It could happen when pre-detach
* routine of the device succeeds. But the softmac will then
* be recreated when device fails to detach (as this device
* is held).
*/
goto again;
}
/*
* Bump smac_hold_cnt to prevent device detach.
*/
softmac->smac_hold_cnt++;
/*
* Wait till the device is fully attached.
*/
softmac->smac_hold_cnt--;
else
return (err);
}
void
{
}
int
{
char *drv;
char mac[MAXNAMELEN];
int err;
return (EINVAL);
return (err);
softmac->smac_hold_cnt++;
return (0);
}
void
{
softmac->smac_hold_cnt--;
}