dls.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Data-Link Services Module
*/
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/sysmacros.h>
#include <sys/atomic.h>
#include <sys/ght.h>
#include <sys/dlpi.h>
#include <sys/vlan.h>
#include <sys/ethernet.h>
#include <sys/byteorder.h>
#include <sys/mac.h>
#include <sys/dls.h>
#include <sys/dls_impl.h>
static kmem_cache_t *i_dls_impl_cachep;
static uint32_t i_dls_impl_count;
/*
* Private functions.
*/
/*ARGSUSED*/
static int
i_dls_constructor(void *buf, void *arg, int kmflag)
{
dls_impl_t *dip = buf;
bzero(buf, sizeof (dls_impl_t));
rw_init(&(dip->di_lock), NULL, RW_DRIVER, NULL);
return (0);
}
/*ARGSUSED*/
static void
i_dls_destructor(void *buf, void *arg)
{
dls_impl_t *dip = buf;
ASSERT(dip->di_dvp == NULL);
ASSERT(dip->di_mnh == NULL);
ASSERT(dip->di_dmap == NULL);
ASSERT(!dip->di_bound);
ASSERT(dip->di_rx == NULL);
ASSERT(dip->di_tx == NULL);
rw_destroy(&(dip->di_lock));
}
static void
i_dls_notify(void *arg, mac_notify_type_t type)
{
dls_impl_t *dip = arg;
switch (type) {
case MAC_NOTE_UNICST:
mac_unicst_get(dip->di_mh, dip->di_unicst_addr);
break;
case MAC_NOTE_PROMISC:
/*
* Every time the MAC interface changes promiscuity we
* need to reset the transmit function:
*
* - In non-promiscuous mode we simply copy the function
* pointer from the MAC module.
*
* - In promiscuous mode we use mac_txloop(), which will
* handle the loopback case.
*/
if (mac_promisc_get(dip->di_mh, MAC_PROMISC)) {
dip->di_tx = mac_txloop;
dip->di_tx_arg = dip->di_mh;
} else {
mac_tx_get(dip->di_mh, &dip->di_tx, &dip->di_tx_arg);
}
break;
}
}
static mblk_t *
i_dls_ether_header(dls_impl_t *dip, const uint8_t *daddr, uint16_t sap,
uint_t pri)
{
struct ether_header *ehp;
struct ether_vlan_header *evhp;
const mac_info_t *mip;
uint_t addr_length;
uint16_t vid;
mblk_t *mp;
mip = dip->di_mip;
addr_length = mip->mi_addr_length;
/*
* Check whether the DLSAP value is legal for ethernet.
*/
if (!SAP_LEGAL(mip->mi_media, sap))
return (NULL);
/*
* If the interface is a VLAN interface then we need VLAN packet
* headers.
*/
if ((vid = dip->di_dvp->dv_id) != VLAN_ID_NONE)
goto vlan;
/*
* Allocate a normal ethernet packet header.
*/
if ((mp = allocb(sizeof (struct ether_header), BPRI_HI)) == NULL)
return (NULL);
/*
* Copy in the given address as the destination, our current unicast
* address as the source and the given sap as the type/length.
*/
ehp = (struct ether_header *)mp->b_rptr;
bcopy(daddr, &(ehp->ether_dhost), addr_length);
bcopy(dip->di_unicst_addr, &(ehp->ether_shost), addr_length);
ehp->ether_type = htons(sap);
mp->b_wptr += sizeof (struct ether_header);
return (mp);
vlan:
/*
* Allocate a VLAN ethernet packet header.
*/
if ((mp = allocb(sizeof (struct ether_vlan_header), BPRI_HI)) == NULL)
return (NULL);
/*
* Copy in the given address as the destination, our current unicast
* address as the source, the VLAN tpid and tci and the given sap as
* the type/length.
*/
evhp = (struct ether_vlan_header *)mp->b_rptr;
bcopy(daddr, &(evhp->ether_dhost), addr_length);
bcopy(dip->di_unicst_addr, &(evhp->ether_shost), addr_length);
evhp->ether_tpid = htons(VLAN_TPID);
evhp->ether_tci = htons(VLAN_TCI(pri, ETHER_CFI, vid));
evhp->ether_type = htons(sap);
mp->b_wptr += sizeof (struct ether_vlan_header);
return (mp);
}
/*ARGSUSED*/
static void
i_dls_ether_header_info(dls_impl_t *dip, mblk_t *mp, dls_header_info_t *dhip)
{
struct ether_header *ehp;
struct ether_vlan_header *evhp;
uint16_t type_length;
uint16_t tci;
ASSERT(MBLKL(mp) >= sizeof (struct ether_header));
ehp = (struct ether_header *)mp->b_rptr;
/*
* Determine whether to parse a normal or VLAN ethernet header.
*/
if ((type_length = ntohs(ehp->ether_type)) == VLAN_TPID)
goto vlan;
/*
* Specify the length of the header.
*/
dhip->dhi_length = sizeof (struct ether_header);
/*
* Get the destination address.
*/
dhip->dhi_daddr = (const uint8_t *)&(ehp->ether_dhost);
/*
* If the destination address was a group address then
* dl_group_address field should be non-zero.
*/
dhip->dhi_isgroup = (dhip->dhi_daddr[0] & 0x01);
/*
* Get the source address.
*/
dhip->dhi_saddr = (uint8_t *)&(ehp->ether_shost);
/*
* Get the ethertype
*/
dhip->dhi_ethertype = (type_length > ETHERMTU) ? type_length : 0;
/*
* The VLAN identifier must be VLAN_ID_NONE.
*/
dhip->dhi_vid = VLAN_ID_NONE;
return;
vlan:
ASSERT(MBLKL(mp) >= sizeof (struct ether_vlan_header));
evhp = (struct ether_vlan_header *)mp->b_rptr;
/*
* Specify the length of the header.
*/
dhip->dhi_length = sizeof (struct ether_vlan_header);
/*
* Get the destination address.
*/
dhip->dhi_daddr = (const uint8_t *)&(evhp->ether_dhost);
/*
* If the destination address was a group address then
* dl_group_address field should be non-zero.
*/
dhip->dhi_isgroup = (dhip->dhi_daddr[0] & 0x01);
/*
* Get the source address.
*/
dhip->dhi_saddr = (uint8_t *)&(evhp->ether_shost);
/*
* Get the ethertype
*/
type_length = ntohs(evhp->ether_type);
dhip->dhi_ethertype = (type_length > ETHERMTU) ? type_length : 0;
ASSERT(dhip->dhi_ethertype != VLAN_TPID);
/*
* Get the VLAN identifier.
*/
tci = ntohs(evhp->ether_tci);
dhip->dhi_vid = VLAN_ID(tci);
}
/*
* Module initialization functions.
*/
void
dls_init(void)
{
/*
* Create a kmem_cache of dls_impl_t.
*/
i_dls_impl_cachep = kmem_cache_create("dls_cache",
sizeof (dls_impl_t), 0, i_dls_constructor, i_dls_destructor, NULL,
NULL, NULL, 0);
ASSERT(i_dls_impl_cachep != NULL);
}
int
dls_fini(void)
{
/*
* If there are any dls_impl_t in use then return EBUSY.
*/
if (i_dls_impl_count != 0)
return (EBUSY);
/*
* Destroy the kmem_cache.
*/
kmem_cache_destroy(i_dls_impl_cachep);
return (0);
}
/*
* Client function.
*/
int
dls_create(const char *name, const char *dev, uint_t port, uint16_t vid)
{
return (dls_vlan_create(name, dev, port, vid));
}
int
dls_destroy(const char *name)
{
return (dls_vlan_destroy(name));
}
int
dls_open(const char *name, dls_channel_t *dcp)
{
dls_impl_t *dip;
dls_vlan_t *dvp;
dls_link_t *dlp;
int err;
/*
* Get a reference to the named dls_vlan_t.
*/
if ((err = dls_vlan_hold(name, &dvp)) != 0)
return (err);
/*
* Allocate a new dls_impl_t.
*/
dip = kmem_cache_alloc(i_dls_impl_cachep, KM_SLEEP);
dip->di_dvp = dvp;
/*
* Cache a copy of the MAC interface handle, a pointer to the
* immutable MAC info and a copy of the current MAC address.
*/
dlp = dvp->dv_dlp;
dip->di_mh = dlp->dl_mh;
dip->di_mip = dlp->dl_mip;
mac_unicst_get(dip->di_mh, dip->di_unicst_addr);
/*
* Set the MAC transmit function.
*
* NOTE: We know that the MAC is not in promiscuous mode so we
* simply copy the values from the MAC.
* (See comment in i_dls_notify() for further explanation).
*/
mac_tx_get(dip->di_mh, &dip->di_tx, &dip->di_tx_arg);
/*
* Set up packet header constructor and parser functions. (We currently
* only support ethernet).
*/
ASSERT(dip->di_mip->mi_media == DL_ETHER);
dip->di_header = i_dls_ether_header;
dip->di_header_info = i_dls_ether_header_info;
/*
* Add a notification function so that we get updates from the MAC.
*/
dip->di_mnh = mac_notify_add(dip->di_mh, i_dls_notify, (void *)dip);
/*
* Bump the kmem_cache count to make sure it is not prematurely
* destroyed.
*/
atomic_add_32(&i_dls_impl_count, 1);
/*
* Hand back a reference to the dls_impl_t.
*/
*dcp = (dls_channel_t)dip;
return (0);
}
void
dls_close(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_vlan_t *dvp;
dls_link_t *dlp;
dls_multicst_addr_t *p;
dls_multicst_addr_t *nextp;
dls_active_clear(dc);
rw_enter(&(dip->di_lock), RW_WRITER);
/*
* Remove the notify function.
*/
mac_notify_remove(dip->di_mh, dip->di_mnh);
dip->di_mnh = NULL;
/*
* If the dls_impl_t is bound then unbind it.
*/
dvp = dip->di_dvp;
dlp = dvp->dv_dlp;
if (dip->di_bound) {
rw_exit(&(dip->di_lock));
dls_link_remove(dlp, dip);
rw_enter(&(dip->di_lock), RW_WRITER);
dip->di_rx = NULL;
dip->di_rx_arg = NULL;
dip->di_bound = B_FALSE;
}
/*
* Walk the list of multicast addresses, disabling each at the MAC.
*/
for (p = dip->di_dmap; p != NULL; p = nextp) {
(void) mac_multicst_remove(dip->di_mh, p->dma_addr);
nextp = p->dma_nextp;
kmem_free(p, sizeof (dls_multicst_addr_t));
}
dip->di_dmap = NULL;
rw_exit(&(dip->di_lock));
/*
* If the MAC has been set in promiscuous mode then disable it.
*/
(void) dls_promisc(dc, 0);
/*
* Free the dls_impl_t back to the cache.
*/
dip->di_dvp = NULL;
dip->di_tx = NULL;
dip->di_tx_arg = NULL;
kmem_cache_free(i_dls_impl_cachep, dip);
/*
* Decrement the reference count to allow the cache to be destroyed
* if there are no more dls_impl_t.
*/
atomic_add_32(&i_dls_impl_count, -1);
/*
* Release our reference to the dls_vlan_t allowing that to be
* destroyed if there are no more dls_impl_t.
*/
dls_vlan_rele(dvp);
}
mac_handle_t
dls_mac(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
return (dip->di_mh);
}
uint16_t
dls_vid(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
return (dip->di_dvp->dv_id);
}
int
dls_bind(dls_channel_t dc, uint16_t sap)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_link_t *dlp;
/*
* Check to see the value is legal for the media type.
*/
if (!SAP_LEGAL(dip->di_mip->mi_media, sap))
return (EINVAL);
/*
* Set up the dls_impl_t to mark it as able to receive packets.
*/
rw_enter(&(dip->di_lock), RW_WRITER);
ASSERT(!dip->di_bound);
dip->di_sap = sap;
dip->di_bound = B_TRUE;
rw_exit(&(dip->di_lock));
/*
* Now bind the dls_impl_t by adding it into the hash table in the
* dls_link_t.
*
* NOTE: This must be done without the dls_impl_t lock being held
* otherwise deadlock may ensue.
*/
dlp = dip->di_dvp->dv_dlp;
dls_link_add(dlp,
(dip->di_promisc & DLS_PROMISC_SAP) ? DLS_SAP_PROMISC :
(uint32_t)sap, dip);
return (0);
}
void
dls_unbind(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_link_t *dlp;
/*
* Unbind the dls_impl_t by removing it from the hash table in the
* dls_link_t.
*
* NOTE: This must be done without the dls_impl_t lock being held
* otherise deadlock may enuse.
*/
dlp = dip->di_dvp->dv_dlp;
dls_link_remove(dlp, dip);
/*
* Mark the dls_impl_t as unable to receive packets This will make
* sure that 'receives in flight' will not come our way.
*/
dip->di_bound = B_FALSE;
}
int
dls_promisc(dls_channel_t dc, uint32_t flags)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_link_t *dlp;
int err = 0;
ASSERT(!(flags & ~(DLS_PROMISC_SAP | DLS_PROMISC_MULTI |
DLS_PROMISC_PHYS)));
/*
* Check if we need to turn on 'all sap' mode.
*/
rw_enter(&(dip->di_lock), RW_WRITER);
dlp = dip->di_dvp->dv_dlp;
if ((flags & DLS_PROMISC_SAP) &&
!(dip->di_promisc & DLS_PROMISC_SAP)) {
dip->di_promisc |= DLS_PROMISC_SAP;
if (!dip->di_bound)
goto multi;
rw_exit(&(dip->di_lock));
dls_link_remove(dlp, dip);
dls_link_add(dlp, DLS_SAP_PROMISC, dip);
rw_enter(&(dip->di_lock), RW_WRITER);
goto multi;
}
/*
* Check if we need to turn off 'all sap' mode.
*/
if (!(flags & DLS_PROMISC_SAP) &&
(dip->di_promisc & DLS_PROMISC_SAP)) {
dip->di_promisc &= ~DLS_PROMISC_SAP;
if (!dip->di_bound)
goto multi;
rw_exit(&(dip->di_lock));
dls_link_remove(dlp, dip);
dls_link_add(dlp, dip->di_sap, dip);
rw_enter(&(dip->di_lock), RW_WRITER);
}
multi:
/*
* It's easiest to add the txloop handler up-front; if promiscuous
* mode cannot be enabled, then we'll remove it before returning.
*/
if (dlp->dl_npromisc == 0 &&
(flags & (DLS_PROMISC_MULTI|DLS_PROMISC_PHYS))) {
ASSERT(dlp->dl_mth == NULL);
dlp->dl_mth = mac_txloop_add(dlp->dl_mh, dlp->dl_loopback, dlp);
}
/*
* Check if we need to turn on 'all multicast' mode.
*/
if ((flags & DLS_PROMISC_MULTI) &&
!(dip->di_promisc & DLS_PROMISC_MULTI)) {
err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
if (err != 0)
goto done;
dip->di_promisc |= DLS_PROMISC_MULTI;
dlp->dl_npromisc++;
goto phys;
}
/*
* Check if we need to turn off 'all multicast' mode.
*/
if (!(flags & DLS_PROMISC_MULTI) &&
(dip->di_promisc & DLS_PROMISC_MULTI)) {
err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
if (err != 0)
goto done;
dip->di_promisc &= ~DLS_PROMISC_MULTI;
dlp->dl_npromisc--;
}
phys:
/*
* Check if we need to turn on 'all physical' mode.
*/
if ((flags & DLS_PROMISC_PHYS) &&
!(dip->di_promisc & DLS_PROMISC_PHYS)) {
err = mac_promisc_set(dip->di_mh, B_TRUE, MAC_PROMISC);
if (err != 0)
goto done;
dip->di_promisc |= DLS_PROMISC_PHYS;
dlp->dl_npromisc++;
goto done;
}
/*
* Check if we need to turn off 'all physical' mode.
*/
if (!(flags & DLS_PROMISC_PHYS) &&
(dip->di_promisc & DLS_PROMISC_PHYS)) {
err = mac_promisc_set(dip->di_mh, B_FALSE, MAC_PROMISC);
if (err != 0)
goto done;
dip->di_promisc &= ~DLS_PROMISC_PHYS;
dlp->dl_npromisc--;
}
done:
if (dlp->dl_npromisc == 0 && dlp->dl_mth != NULL) {
mac_txloop_remove(dlp->dl_mh, dlp->dl_mth);
dlp->dl_mth = NULL;
}
ASSERT(dlp->dl_npromisc == 0 || dlp->dl_mth != NULL);
rw_exit(&(dip->di_lock));
return (err);
}
int
dls_multicst_add(dls_channel_t dc, const uint8_t *addr)
{
dls_impl_t *dip = (dls_impl_t *)dc;
int err;
dls_multicst_addr_t **pp;
dls_multicst_addr_t *p;
uint_t addr_length;
/*
* Check whether the address is in the list of enabled addresses for
* this dls_impl_t.
*/
rw_enter(&(dip->di_lock), RW_WRITER);
addr_length = dip->di_mip->mi_addr_length;
for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
if (bcmp(addr, p->dma_addr, addr_length) == 0) {
/*
* It is there so there's nothing to do.
*/
err = 0;
goto done;
}
}
/*
* Allocate a new list item.
*/
if ((p = kmem_zalloc(sizeof (dls_multicst_addr_t),
KM_NOSLEEP)) == NULL) {
err = ENOMEM;
goto done;
}
/*
* Enable the address at the MAC.
*/
if ((err = mac_multicst_add(dip->di_mh, addr)) != 0) {
kmem_free(p, sizeof (dls_multicst_addr_t));
goto done;
}
/*
* The address is now enabled at the MAC so add it to the list.
*/
bcopy(addr, p->dma_addr, addr_length);
*pp = p;
done:
rw_exit(&(dip->di_lock));
return (err);
}
int
dls_multicst_remove(dls_channel_t dc, const uint8_t *addr)
{
dls_impl_t *dip = (dls_impl_t *)dc;
int err;
dls_multicst_addr_t **pp;
dls_multicst_addr_t *p;
uint_t addr_length;
/*
* Find the address in the list of enabled addresses for this
* dls_impl_t.
*/
rw_enter(&(dip->di_lock), RW_WRITER);
addr_length = dip->di_mip->mi_addr_length;
for (pp = &(dip->di_dmap); (p = *pp) != NULL; pp = &(p->dma_nextp)) {
if (bcmp(addr, p->dma_addr, addr_length) == 0)
break;
}
/*
* If we walked to the end of the list then the given address is
* not currently enabled for this dls_impl_t.
*/
if (p == NULL) {
err = ENOENT;
goto done;
}
/*
* Disable the address at the MAC.
*/
if ((err = mac_multicst_remove(dip->di_mh, addr)) != 0)
goto done;
/*
* Remove the address from the list.
*/
*pp = p->dma_nextp;
kmem_free(p, sizeof (dls_multicst_addr_t));
done:
rw_exit(&(dip->di_lock));
return (err);
}
mblk_t *
dls_header(dls_channel_t dc, const uint8_t *addr, uint16_t sap, uint_t pri)
{
dls_impl_t *dip = (dls_impl_t *)dc;
return (dip->di_header(dip, addr, sap, pri));
}
void
dls_header_info(dls_channel_t dc, mblk_t *mp, dls_header_info_t *dhip)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dip->di_header_info(dip, mp, dhip);
}
void
dls_rx_set(dls_channel_t dc, dls_rx_t rx, void *arg)
{
dls_impl_t *dip = (dls_impl_t *)dc;
rw_enter(&(dip->di_lock), RW_WRITER);
dip->di_rx = rx;
dip->di_rx_arg = arg;
rw_exit(&(dip->di_lock));
}
mblk_t *
dls_tx(dls_channel_t dc, mblk_t *mp)
{
dls_impl_t *dip = (dls_impl_t *)dc;
return (dip->di_tx(dip->di_tx_arg, mp));
}
/*
* Exported functions.
*/
#define ADDR_MATCH(_addr_a, _addr_b, _length, _match) \
{ \
uint_t i; \
\
/* \
* Make sure the addresses are 16 bit aligned and that \
* the length is an even number of octets. \
*/ \
ASSERT(IS_P2ALIGNED((_addr_a), sizeof (uint16_t))); \
ASSERT(IS_P2ALIGNED((_addr_b), sizeof (uint16_t))); \
ASSERT((_length & 1) == 0); \
\
(_match) = B_TRUE; \
for (i = 0; i < (_length) >> 1; i++) { \
if (((uint16_t *)(_addr_a))[i] != \
((uint16_t *)(_addr_b))[i]) { \
(_match) = B_FALSE; \
break; \
} \
} \
}
boolean_t
dls_accept(dls_impl_t *dip, const uint8_t *daddr)
{
boolean_t match;
dls_multicst_addr_t *dmap;
uint_t addr_length = dip->di_mip->mi_addr_length;
/*
* We must not accept packets if the dls_impl_t is not marked as bound
* or is being removed.
*/
rw_enter(&(dip->di_lock), RW_READER);
if (!dip->di_bound || dip->di_removing)
goto refuse;
/*
* If the dls_impl_t is in 'all physical' mode then always accept.
*/
if (dip->di_promisc & DLS_PROMISC_PHYS)
goto accept;
/*
* Check to see if the destination address matches the dls_impl_t
* unicast address.
*/
ADDR_MATCH(daddr, dip->di_unicst_addr, addr_length, match);
if (match)
goto accept;
/*
* Check for a 'group' address. If it is not then refuse it since we
* already know it does not match the unicast address.
*/
if (!(daddr[0] & 0x01))
goto refuse;
/*
* If the address is broadcast then the dls_impl_t will always accept
* it.
*/
ADDR_MATCH(daddr, dip->di_mip->mi_brdcst_addr, addr_length,
match);
if (match)
goto accept;
/*
* If a group address is not broadcast then it must be multicast so
* check it against the list of addresses enabled for this dls_impl_t
* or accept it unconditionally if the dls_impl_t is in 'all
* multicast' mode.
*/
if (dip->di_promisc & DLS_PROMISC_MULTI)
goto accept;
for (dmap = dip->di_dmap; dmap != NULL; dmap = dmap->dma_nextp) {
ADDR_MATCH(daddr, dmap->dma_addr, addr_length, match);
if (match)
goto accept;
}
refuse:
rw_exit(&(dip->di_lock));
return (B_FALSE);
accept:
rw_exit(&(dip->di_lock));
return (B_TRUE);
}
/*ARGSUSED*/
boolean_t
dls_accept_loopback(dls_impl_t *dip, const uint8_t *daddr)
{
/*
* We must not accept packets if the dls_impl_t is not marked as bound
* or is being removed.
*/
rw_enter(&(dip->di_lock), RW_READER);
if (!dip->di_bound || dip->di_removing)
goto refuse;
/*
* A dls_impl_t should only accept loopback packets if it is in
* 'all physical' mode.
*/
if (dip->di_promisc & DLS_PROMISC_PHYS)
goto accept;
refuse:
rw_exit(&(dip->di_lock));
return (B_FALSE);
accept:
rw_exit(&(dip->di_lock));
return (B_TRUE);
}
boolean_t
dls_active_set(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_link_t *dlp = dip->di_dvp->dv_dlp;
rw_enter(&dip->di_lock, RW_WRITER);
/* If we're already active, then there's nothing more to do. */
if (dip->di_active) {
rw_exit(&dip->di_lock);
return (B_TRUE);
}
/*
* If this is the first active client on this link, notify
* the mac that we're becoming an active client.
*/
if (dlp->dl_nactive == 0 && !mac_active_set(dlp->dl_mh)) {
rw_exit(&dip->di_lock);
return (B_FALSE);
}
dip->di_active = B_TRUE;
mutex_enter(&dlp->dl_lock);
dlp->dl_nactive++;
mutex_exit(&dlp->dl_lock);
rw_exit(&dip->di_lock);
return (B_TRUE);
}
void
dls_active_clear(dls_channel_t dc)
{
dls_impl_t *dip = (dls_impl_t *)dc;
dls_link_t *dlp = dip->di_dvp->dv_dlp;
rw_enter(&dip->di_lock, RW_WRITER);
if (!dip->di_active)
goto out;
dip->di_active = B_FALSE;
mutex_enter(&dlp->dl_lock);
if (--dlp->dl_nactive == 0)
mac_active_clear(dip->di_mh);
mutex_exit(&dlp->dl_lock);
out:
rw_exit(&dip->di_lock);
}