mac_provider.c revision 9b41bdc4d1e09925acb52ea879d632a2611393df
/*
* 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
*/
/*
*/
#include <sys/id_space.h>
#include <sys/mac_provider.h>
#include <sys/mac_impl.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_client_priv.h>
#include <sys/mac_soft_ring.h>
#include <sys/mac_stat.h>
#include <sys/mac_flow.h>
#include <sys/ddi_intr_impl.h>
/*
* MAC Provider Interface.
*
* Interface for GLDv3 compatible NIC drivers.
*/
static void i_mac_notify_thread(void *);
typedef void (*mac_notify_default_cb_fn_t)(mac_impl_t *);
mac_fanout_recompute, /* MAC_NOTE_LINK */
NULL, /* MAC_NOTE_UNICST */
NULL, /* MAC_NOTE_TX */
NULL, /* MAC_NOTE_DEVPROMISC */
NULL, /* MAC_NOTE_FASTPATH_FLUSH */
NULL, /* MAC_NOTE_SDU_SIZE */
NULL, /* MAC_NOTE_MARGIN */
NULL, /* MAC_NOTE_CAPAB_CHG */
NULL /* MAC_NOTE_LOWLINK */
};
/*
* Driver support functions.
*/
/* REGISTRATION */
{
/*
* Make sure there isn't a version mismatch between the driver and
* the framework. In the future, if multiple versions are
* supported, this check could become more sophisticated.
*/
if (mac_version != MAC_VERSION)
return (NULL);
return (mregp);
}
void
{
}
/*
* mac_register() is how drivers register new MACs with the GLDv3
* framework. The mregp argument is allocated by drivers using the
* mac_alloc() function, and can be freed using mac_free() immediately upon
* return from mac_register(). Upon success (0 return value), the mhp
* opaque pointer becomes the driver's handle to its MAC interface, and is
* the argument to all other mac module entry points.
*/
/* ARGSUSED */
int
{
char *driver;
/* A successful call to mac_init_ops() sets the DN_GLDV3_DRIVER flag. */
return (EINVAL);
/* Find the required MAC-Type plugin. */
return (EINVAL);
/* Create a mac_impl_t to represent this MAC. */
/*
* The mac is not ready for open yet.
*/
/*
* When a mac is registered, the m_instance field can be set to:
*
* 0: Get the mac's instance number from m_dip.
* This is usually used for physical device dips.
*
* [1 .. MAC_MAX_MINOR-1]: Use the value as the mac's instance number.
* For example, when an aggregation is created with the key option,
* "key" will be used as the instance number.
*
* -1: Assign an instance number from [MAC_MAX_MINOR .. MAXMIN-1].
* This is often used when a MAC of a virtual link is registered
* (e.g., aggregation when "key" is not specified, or vnic).
*
* Note that the instance number is used to derive the mi_minor field
* of mac_impl_t, which will then be used to derive the name of kstats
* and the devfs nodes. The first 2 cases are needed to preserve
* backward compatibility.
*/
switch (mregp->m_instance) {
case 0:
break;
case ((uint_t)-1):
if (minor == 0) {
goto fail;
}
break;
default:
if (instance >= MAC_MAX_MINOR) {
goto fail;
}
break;
}
mip->mi_nclients = 0;
/* Set the default IEEE Port VLAN Identifier */
/* Default bridge link learning protection values */
/* Construct the MAC name as <drvname><instance> */
goto fail;
if (mregp->m_multicast_sdu == 0)
goto fail;
/*
* If the media supports a broadcast address, cache a pointer to it
* in the mac_info_t so that upper layers can use it.
*/
/*
* Copy the unicast source address into the mac_info_t, but only if
* the MAC-Type defines a non-zero address length. We need to
* handle MAC-Types that have an address length of 0
* (point-to-point protocol MACs for example).
*/
goto fail;
/*
* Copy the fixed 'factory' MAC address from the immutable
* info. This is taken to be the MAC address currently in
* use.
*/
/*
* At this point, we should set up the classification
* rules etc but we delay it till mac_open() so that
* the resource discovery has taken place and we
* know someone wants to use the device. Otherwise
* memory gets allocated for Rx ring structures even
* during probe.
*/
/* Copy the destination address if one is provided. */
}
goto fail;
}
/*
* The format of the m_pdata is specific to the plugin. It is
* passed in as an argument to all of the plugin callbacks. The
* driver can update this information by calling
* mac_pdata_update().
*/
/*
* Verify if the supplied plugin data is valid. Note that
* even if the caller passed in a NULL pointer as plugin data,
* we still need to verify if that's valid as the plugin may
* require plugin data to function.
*/
mregp->m_pdata_size)) {
goto fail;
}
}
/*
* The caller supplied non-NULL plugin data, but the plugin
* does not recognize plugin data.
*/
goto fail;
}
/*
* Register the private properties.
*/
/*
* Stash the driver callbacks into the mac_impl_t, but first sanity
* check to make sure all mandatory callbacks are set.
*/
goto fail;
}
&mip->mi_capab_legacy)) {
} else {
}
/*
* Allocate a notification thread. thread_create blocks for memory
* if needed, it never fails.
*/
/*
* Initialize the capabilities
*/
/*
* Enforce the virtrualization level registered.
*/
goto fail;
/*
* The driver needs to register at least rx rings for this
* virtualization level.
*/
goto fail;
}
/*
* The driver must set mc_unicst entry point to NULL when it advertises
* CAP_RINGS for rx groups.
*/
goto fail;
} else {
goto fail;
}
/*
* Initialize MAC addresses. Must be called after mac_init_rings().
*/
&mip->mi_share_capab);
}
/*
* Initialize the kstats for this device.
*/
/* Zero out any properties. */
/* Create a style-2 DLPI device */
goto fail;
/* Create a style-1 DLPI device */
goto fail;
}
goto fail;
}
(mac_impl_t *), mip);
/*
* Mark the MAC to be ready for open.
*/
return (0);
fail:
if (style1_created)
if (style2_created)
/* Clean up registered MAC addresses */
/* Clean up registered rings */
/* Clean up notification thread */
}
}
mip->mi_pdata_size = 0;
}
if (minor != 0) {
}
mip->mi_state_flags = 0;
/*
* Clear the state before destroying the mac_impl_t
*/
mip->mi_state_flags = 0;
return (err);
}
/*
* Unregister from the GLDv3 framework
*/
int
{
int err;
/* Fail the unregister if there are any open references to this mac. */
return (err);
/*
* Clean up notification thread and wait for it to exit.
*/
/*
* There is still resource properties configured over this mac.
*/
}
(void) mod_hash_remove(i_mac_impl_hash,
ASSERT(i_mac_impl_count > 0);
mip->mi_pdata_size = 0;
/*
* Free the list of margin request.
*/
}
/*
* Free the primary MAC address.
*/
/*
* free all rings
*/
/* and the flows */
/*
* Reset the perim related fields to default values before
* kmem_cache_free
*/
mip->mi_state_flags = 0;
return (0);
}
/* DATA RECEPTION */
/*
* This function is invoked for packets received by the MAC driver in
* interrupt context. The ring generation number provided by the driver
* is matched with the ring generation number held in MAC. If they do not
* match, received packets are considered stale packets coming from an older
* assignment of the ring. Drop them.
*/
void
{
return;
}
}
/*
* This function is invoked for each packet received by the underlying driver.
*/
void
{
/*
* Check if the link is part of a bridge. If not, then we don't need
* to take the lock to remain consistent. Make this common case
* lock-free and tail-call optimized.
*/
} else {
/*
* Once we take a reference on the bridge link, the bridge
* module itself can't unload, so the callback pointers are
* stable.
*/
} else {
}
}
}
/*
* Special case function: this allows snooping of packets transmitted and
* received by TRILL. By design, they go directly into the TRILL module.
*/
void
{
}
/*
* This is the upward reentry point for packets arriving from the bridging
* module and from mac_rx for links not part of a bridge.
*/
void
{
/*
* If there are any promiscuous mode callbacks defined for
* this MAC, pass them a copy if appropriate.
*/
/*
* If the SRS teardown has started, just return. The 'mr'
* continues to be valid until the driver unregisters the mac.
* Hardware classified packets will not make their way up
* beyond this point once the teardown has started. The driver
* is never passed a pointer to a flow entry or SRS or any
* structure that can be freed much before mac_unregister.
*/
return;
}
}
/*
* We check if an SRS is controlling this ring.
* If so, we can directly call the srs_lower_proc
* routine otherwise we need to go through mac_rx_classify
* to reach the right place.
*/
if (hw_classified) {
/*
* This is supposed to be the fast path.
* All packets received though here were steered by
* the hardware classifier, and share the same
* MAC header info.
*/
MR_REFRELE(mr);
return;
}
/* We'll fall through to software classification */
} else {
int err;
if (err == 0) {
return;
}
} else {
}
}
return;
}
}
/* DATA TRANSMISSION */
/*
* A driver's notification to resume transmission, in case of a provider
* without TX rings.
*/
void
{
}
/*
* A driver's notification to resume transmission on the specified TX ring.
*/
void
{
}
/* LINK STATE */
/*
* Notify the MAC layer about a link state change
*/
void
{
/*
* Save the link state.
*/
/*
* Send a MAC_NOTE_LOWLINK notification. This tells the notification
* thread to deliver both lower and upper notifications.
*/
}
/*
* Notify the MAC layer about a link state change due to bridging.
*/
void
{
/*
* Save the link state.
*/
/*
* Send a MAC_NOTE_LINK notification. Only upper notifications are
* made.
*/
}
/* MINOR NODE HANDLING */
/*
* Given a dev_t, return the instance number (PPA) associated with it.
* Drivers can use this in their getinfo(9e) implementation to lookup
* the instance number (i.e. PPA) of the device, to use as an index to
* their own array of soft state structures.
*
* Returns -1 on error.
*/
int
{
return (dld_devt_to_instance(devt));
}
/*
* This function returns the first minor number that is available for
* driver private use. All minor numbers smaller than this are
* reserved for GLDv3 use.
*/
mac_private_minor(void)
{
return (MAC_PRIVATE_MINOR);
}
/* OTHER CONTROL INFORMATION */
/*
* A driver notified us that its primary MAC address has changed.
*/
void
{
return;
/*
* If address changes, freshen the MAC address value and update
* all MAC clients that share this MAC address.
*/
}
/*
* Send a MAC_NOTE_UNICST notification.
*/
}
void
{
return;
}
/*
* MAC plugin information changed.
*/
int
{
/*
* Verify that the plugin supports MAC plugin data and that the
* supplied data is valid.
*/
return (EINVAL);
return (EINVAL);
/*
* Since the MAC plugin data is used to construct MAC headers that
* were cached in fast-path headers, we need to flush fast-path
* information for links associated with this mac.
*/
return (0);
}
/*
* Invoked by driver as well as the framework to notify its capability change.
*/
void
{
/* Send MAC_NOTE_CAPAB_CHG notification */
}
/*
* Used by normal drivers to update the max sdu size.
* We need to handle the case of a smaller mi_sdu_multicast
* since this is called by mac_set_mtu() even for drivers that
* have differing unicast and multicast mtu and we don't want to
* increase the multicast mtu by accident in that case.
*/
int
{
return (EINVAL);
/* Send a MAC_NOTE_SDU_SIZE notification. */
return (0);
}
/*
* Version of the above function that is used by drivers that have a different
*/
int
{
return (EINVAL);
if (sdu_multicast == 0)
return (EINVAL);
/* Send a MAC_NOTE_SDU_SIZE notification. */
return (0);
}
static void
{
int i;
/* interrupt can be re-targeted */
for (i = 0; i < flent->fe_rx_srs_cnt; i++) {
continue;
break;
}
} else {
}
}
}
}
/*
* Clients like aggr create pseudo rings (mac_ring_t) and expose them to
* their clients. There is a 1-1 mapping pseudo ring and the hardware
* ring. ddi interrupt handles are exported from the hardware ring to
* the pseudo ring. Thus when the interrupt handle changes, clients of
* aggr that are using the handle need to use the new handle and
* re-target their interrupts.
*/
static void
{
char macname[MAXNAMELEN];
/*
* We need to enter aggr's perimeter. The locking hierarchy
* dictates that aggr's perimeter should be entered first
* and then the port's perimeter. So drop the port's
* perimeter, enter aggr's and then re-enter port's
* perimeter.
*/
/*
* While we know pmip is the aggr's mip, there is a
* possibility that aggr could have unregistered by
* the time we exit port's perimeter (mip) and
* enter aggr's perimeter (pmip). To avoid that
* scenario, enter aggr's perimeter using its name.
*/
return;
/*
* Check if the ring got assigned to another aggregation before
* be could enter aggr's and the port's perimeter. When a ring
* gets deleted from an aggregation, it calls mac_stop_ring()
* which increments the generation number. So checking
* generation number will be enough.
*/
goto again;
}
/* Check if pseudo ring is still present */
}
}
/*
* This usually happens when IRM (Interrupt Resource Manangement)
* framework either gives the driver more MSI-x interrupts or takes
* away MSI-x interrupts from the driver.
*/
void
{
/* Interrupts being reset */
return;
}
} else {
/* New interrupt handle */
}
return;
} else {
}
}
}
/* PRIVATE FUNCTIONS, FOR INTERNAL USE ONLY */
/*
* Updates the mac_impl structure with the current state of the link
*/
static void
{
/*
* If no change, then it is not interesting.
*/
return;
switch (mip->mi_lowlinkstate) {
case LINK_STATE_UP:
char det[200];
} else {
}
break;
case LINK_STATE_DOWN:
/*
* Only transitions from UP to DOWN are interesting
*/
break;
case LINK_STATE_UNKNOWN:
/*
* This case is normally not interesting.
*/
break;
}
}
/*
* Main routine for the callbacks notifications thread
*/
static void
i_mac_notify_thread(void *arg)
{
"i_mac_notify_thread");
for (;;) {
if (bits == 0) {
continue;
}
mip->mi_notify_bits = 0;
/* request to quit */
break;
}
/*
* Log link changes on the actual link, but then do reports on
* synthetic state (if part of a bridge).
*/
newstate);
}
}
}
}
/*
* Do notification callbacks for each notification type.
*/
continue;
}
/*
* Walk the list of notifications.
*/
}
&mip->mi_notify_cb_list);
}
}
/* CALLB_CPR_EXIT drops the lock */
thread_exit();
}
/*
* Signal the i_mac_notify_thread asking it to quit.
* Then wait till it is done.
*/
void
{
}
/* Necessary clean up before doing kmem_cache_free */
mip->mi_notify_bits = 0;
}
/*
* Entry point invoked by drivers to dynamically add a ring to an
* existing group.
*/
int
{
int ret;
return (ret);
}
/*
* Entry point invoked by drivers to dynamically remove a ring
* from an existing group. The specified ring handle must no longer
* be used by the driver after a call to this function.
*/
void
{
}
/*
* mac_prop_info_*() callbacks called from the driver's prefix_propinfo()
* entry points.
*/
void
{
/* nothing to do if the caller doesn't want the default value */
return;
}
void
{
/* nothing to do if the caller doesn't want the default value */
return;
}
void
{
/* nothing to do if the caller doesn't want the default value */
return;
}
void
{
/* nothing to do if the caller doesn't want the default value */
return;
else
}
void
{
/* nothing to do if the caller doesn't want the default value */
return;
}
void
{
/* nothing to do if the caller doesn't want the range info */
return;
if (pr->pr_range_cur_count++ == 0) {
/* first range */
} else {
/* all ranges of a property should be of the same type */
return;
}
}
}
void
{
}
{
if ((flags & HCK_PARTIALCKSUM) != 0) {
}
}
}
{
}
void
{
}
}