diff options
| author | yz147064 <none@none> | 2008-01-23 18:09:15 -0800 |
|---|---|---|
| committer | yz147064 <none@none> | 2008-01-23 18:09:15 -0800 |
| commit | d62bc4badc1c1f1549c961cfb8b420e650e1272b (patch) | |
| tree | 9f466859e9cfb73da13b64261432aba4683f19ad /usr/src/uts/common/io/softmac | |
| parent | d38257c4392a9dd690c2f7f2383236c1fc80e509 (diff) | |
| download | illumos-gate-d62bc4badc1c1f1549c961cfb8b420e650e1272b.tar.gz | |
PSARC/2006/499 Clearview Nemo unification and vanity naming
PSARC/2007/527 Addendum for Clearview Vanity Naming and Nemo Unification
PSARC/2008/002 Clearview UV Updates
6310766 vlan statistics get reset at unplumb time
6320515 dladm commands with "-R" option should not take effect immediately
6433732 Simplify the GLDv3 control path by making its processing asynchronous
6445912 dladm show-link fails to show a specific link in the debug version
6452413 dladm show-link doesn't show VLAN links for GLDv2 drivers
6504433 libwladm's use of wladm_wlresult2status() needs an overhaul
6504507 dladm set-linkprop failure message is unclear
6534289 DR should work with aggregations
6535719 dladm_aggr_port_attr_db_t`lp_devname should be MAXNAMELEN, not MAXNAMELEN + 1
6539634 GLDv3 should DL_ERROR_ACK a DL_UDQOS_REQ with DL_OUTSTATE when the stream is DL_UNATTACHED
6540246 libdladm should not guess zoneid from DLDIOCZIDGET ioctl errno
6544195 dladm show-dev assumes GLDv3 stats.. incompatible with GLDv2
6563295 dladm show-linkprop -P does not work properly for unavailable links
6577618 integrate network vanity naming and nemo unification
6600446 links assigned to a local zone are still aggregatable by global zone
6607572 "boot net - install" can trigger assertion failure in dld_str_attach()
6613956 "svccfg import -" does not work as bfu expects
6637596 invalid assertion in ip_soft_ring_assignment()
6642350 kernel DLPI processing routines are long overdue
6643338 GLDv3 PPA hack VLAN ID checks don't always work
6647203 bfu: smf_delete_manifest() does not work for non-global zones
6649885 DL_IB GLDv3 mactype plugin must fill in its mtr_nativetype
6650395 libuuid should be lint-clean and linted nightly
--HG--
rename : usr/src/cmd/dladm/aggregation.conf => deleted_files/usr/src/cmd/dladm/aggregation.conf
rename : usr/src/cmd/dladm/linkprop.conf => deleted_files/usr/src/cmd/dladm/linkprop.conf
rename : usr/src/lib/libinetcfg/common/inetcfg_nic.c => deleted_files/usr/src/lib/libinetcfg/common/inetcfg_nic.c
rename : usr/src/lib/libinetcfg/common/inetcfg_nic.h => deleted_files/usr/src/lib/libinetcfg/common/inetcfg_nic.h
Diffstat (limited to 'usr/src/uts/common/io/softmac')
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac.conf | 27 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_capab.c | 756 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_ctl.c | 389 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_dev.c | 417 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_main.c | 1192 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_pkt.c | 320 | ||||
| -rw-r--r-- | usr/src/uts/common/io/softmac/softmac_stat.c | 270 |
7 files changed, 3371 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/softmac/softmac.conf b/usr/src/uts/common/io/softmac/softmac.conf new file mode 100644 index 0000000000..72163c27e6 --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac.conf @@ -0,0 +1,27 @@ +# +# 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 +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +name="softmac" parent="pseudo" instance=0; diff --git a/usr/src/uts/common/io/softmac/softmac_capab.c b/usr/src/uts/common/io/softmac/softmac_capab.c new file mode 100644 index 0000000000..d1178d19aa --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_capab.c @@ -0,0 +1,756 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/mac.h> +#include <sys/softmac_impl.h> + +typedef struct softmac_capab_ops { + int (*sc_hcksum_ack)(void *, t_uscalar_t); + int (*sc_zcopy_ack)(void *, t_uscalar_t); + int (*sc_mdt_ack)(void *, dl_capab_mdt_t *); +} softmac_capab_ops_t; + +static int dl_capab(ldi_handle_t, mblk_t **); +static int softmac_fill_hcksum_ack(void *, t_uscalar_t); +static int softmac_fill_zcopy_ack(void *, t_uscalar_t); +static int softmac_fill_mdt_ack(void *, dl_capab_mdt_t *); +static int softmac_adv_hcksum_ack(void *, t_uscalar_t); +static int softmac_adv_zcopy_ack(void *, t_uscalar_t); +static int softmac_adv_mdt_ack(void *, dl_capab_mdt_t *); +static int softmac_enable_hcksum_ack(void *, t_uscalar_t); +static int softmac_enable_mdt_ack(void *, dl_capab_mdt_t *); +static int softmac_capab_send(softmac_lower_t *, boolean_t); +static int i_capab_ack(mblk_t *, queue_t *, softmac_capab_ops_t *, void *); +static int i_capab_id_ack(mblk_t *, dl_capability_sub_t *, queue_t *, + softmac_capab_ops_t *, void *); +static int i_capab_sub_ack(mblk_t *, dl_capability_sub_t *, queue_t *, + softmac_capab_ops_t *, void *); +static int i_capab_hcksum_ack(dl_capab_hcksum_t *, queue_t *, + softmac_capab_ops_t *, void *); +static int i_capab_zcopy_ack(dl_capab_zerocopy_t *, queue_t *, + softmac_capab_ops_t *, void *); +static int i_capab_mdt_ack(dl_capab_mdt_t *, queue_t *, + softmac_capab_ops_t *, void *); +static int i_capab_hcksum_verify(dl_capab_hcksum_t *, queue_t *); +static int i_capab_zcopy_verify(dl_capab_zerocopy_t *, queue_t *); +static int i_capab_mdt_verify(dl_capab_mdt_t *, queue_t *); + +static softmac_capab_ops_t softmac_fill_capab_ops = +{ + softmac_fill_hcksum_ack, + softmac_fill_zcopy_ack, + softmac_fill_mdt_ack, +}; + +static softmac_capab_ops_t softmac_adv_capab_ops = +{ + softmac_adv_hcksum_ack, + softmac_adv_zcopy_ack, + softmac_adv_mdt_ack +}; + +static softmac_capab_ops_t softmac_enable_capab_ops = +{ + softmac_enable_hcksum_ack, + NULL, + softmac_enable_mdt_ack +}; + +int +softmac_fill_capab(ldi_handle_t lh, softmac_t *softmac) +{ + mblk_t *mp = NULL; + union DL_primitives *prim; + int err = 0; + + if ((err = dl_capab(lh, &mp)) != 0) + goto exit; + + prim = (union DL_primitives *)mp->b_rptr; + if (prim->dl_primitive == DL_ERROR_ACK) { + err = -1; + goto exit; + } + + err = i_capab_ack(mp, NULL, &softmac_fill_capab_ops, softmac); + +exit: + freemsg(mp); + return (err); +} + +static int +dl_capab(ldi_handle_t lh, mblk_t **mpp) +{ + dl_capability_req_t *capb; + union DL_primitives *dl_prim; + mblk_t *mp; + int err; + + if ((mp = allocb(sizeof (dl_capability_req_t), BPRI_MED)) == NULL) + return (ENOMEM); + mp->b_datap->db_type = M_PROTO; + + capb = (dl_capability_req_t *)mp->b_wptr; + mp->b_wptr += sizeof (dl_capability_req_t); + bzero(mp->b_rptr, sizeof (dl_capability_req_t)); + capb->dl_primitive = DL_CAPABILITY_REQ; + + (void) ldi_putmsg(lh, mp); + if ((err = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0) + return (err); + + dl_prim = (union DL_primitives *)mp->b_rptr; + switch (dl_prim->dl_primitive) { + case DL_CAPABILITY_ACK: + if (MBLKL(mp) < DL_CAPABILITY_ACK_SIZE) { + printf("dl_capability: DL_CAPABILITY_ACK " + "protocol err\n"); + break; + } + *mpp = mp; + return (0); + + case DL_ERROR_ACK: + if (MBLKL(mp) < DL_ERROR_ACK_SIZE) { + printf("dl_capability: DL_ERROR_ACK protocol err\n"); + break; + } + if (((dl_error_ack_t *)dl_prim)->dl_error_primitive != + DL_CAPABILITY_REQ) { + printf("dl_capability: DL_ERROR_ACK rtnd prim %u\n", + ((dl_error_ack_t *)dl_prim)->dl_error_primitive); + break; + } + + *mpp = mp; + return (0); + + default: + printf("dl_capability: bad ACK header %u\n", + dl_prim->dl_primitive); + break; + } + + freemsg(mp); + return (-1); +} + +static int +softmac_fill_hcksum_ack(void *arg, t_uscalar_t flags) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * There are two types of acks we process here: + * 1. acks in reply to a (first form) generic capability req + * (no ENABLE flag set) + * 2. acks in reply to a ENABLE capability req. + * (ENABLE flag set) + * Only the first type should be expected here. + */ + + if (flags & HCKSUM_ENABLE) { + cmn_err(CE_WARN, "softmac_fill_hcksum_ack: unexpected " + "HCKSUM_ENABLE flag in hardware checksum capability"); + } else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 | + HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) { + softmac->smac_capab_flags |= MAC_CAPAB_HCKSUM; + softmac->smac_hcksum_txflags = flags; + } + return (0); +} + +static int +softmac_fill_zcopy_ack(void *arg, t_uscalar_t flags) +{ + softmac_t *softmac = (softmac_t *)arg; + + ASSERT(flags == DL_CAPAB_VMSAFE_MEM); + softmac->smac_capab_flags &= (~MAC_CAPAB_NO_ZCOPY); + return (0); +} + +static int +softmac_fill_mdt_ack(void *arg, dl_capab_mdt_t *mdt) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * There are two types of acks we process here: + * 1. acks in reply to a (first form) generic capability req + * (ENABLE flag might be set by some drivers) + * 2. acks in reply to a ENABLE capability req. + * (ENABLE flag set) + */ + + ASSERT(mdt->mdt_version == MDT_VERSION_2); + softmac->smac_mdt = B_TRUE; + softmac->smac_mdt_capab.mdt_hdr_head = mdt->mdt_hdr_head; + softmac->smac_mdt_capab.mdt_hdr_tail = mdt->mdt_hdr_tail; + softmac->smac_mdt_capab.mdt_max_pld = mdt->mdt_max_pld; + softmac->smac_mdt_capab.mdt_span_limit = mdt->mdt_span_limit; + return (0); +} + +int +softmac_capab_enable(softmac_lower_t *slp) +{ + softmac_t *softmac = slp->sl_softmac; + int err; + + if (softmac->smac_no_capability_req) + return (0); + + /* + * Send DL_CAPABILITY_REQ to get capability advertisement. + */ + if ((err = softmac_capab_send(slp, B_FALSE)) != 0) + return (err); + + /* + * Send DL_CAPABILITY_REQ to enable specific capabilities. + */ + if ((err = softmac_capab_send(slp, B_TRUE)) != 0) + return (err); + + return (0); +} + +static int +softmac_capab_send(softmac_lower_t *slp, boolean_t enable) +{ + softmac_t *softmac; + dl_capability_req_t *capb; + dl_capability_sub_t *subcapb; + mblk_t *reqmp, *ackmp; + int err; + size_t size = 0; + + softmac = slp->sl_softmac; + + if (enable) { + /* No need to enable DL_CAPAB_ZEROCOPY */ + if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) + size += sizeof (dl_capability_sub_t) + + sizeof (dl_capab_hcksum_t); + + if (softmac->smac_mdt) { + if (!(softmac->smac_mdt_capab.mdt_flags & + DL_CAPAB_MDT_ENABLE)) { + /* + * The MDT capability was not enabled for the + * first time, enable it now. + */ + size += sizeof (dl_capability_sub_t) + + sizeof (dl_capab_mdt_t); + } + } + + if (size == 0) + return (0); + } + + /* + * Create DL_CAPABILITY_REQ message and send it down + */ + reqmp = allocb(sizeof (dl_capability_req_t) + size, BPRI_MED); + if (reqmp == NULL) + return (ENOMEM); + + bzero(reqmp->b_rptr, sizeof (dl_capability_req_t) + size); + + DB_TYPE(reqmp) = M_PROTO; + reqmp->b_wptr = reqmp->b_rptr + sizeof (dl_capability_req_t) + size; + + capb = (dl_capability_req_t *)reqmp->b_rptr; + capb->dl_primitive = DL_CAPABILITY_REQ; + + if (!enable) + goto output; + + capb->dl_sub_offset = sizeof (dl_capability_req_t); + + if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) { + dl_capab_hcksum_t *hck_subcapp; + + size = sizeof (dl_capability_sub_t) + + sizeof (dl_capab_hcksum_t); + capb->dl_sub_length += size; + + subcapb = (dl_capability_sub_t *)(capb + 1); + subcapb->dl_cap = DL_CAPAB_HCKSUM; + subcapb->dl_length = sizeof (dl_capab_hcksum_t); + hck_subcapp = (dl_capab_hcksum_t *)(subcapb + 1); + hck_subcapp->hcksum_version = HCKSUM_VERSION_1; + hck_subcapp->hcksum_txflags = + softmac->smac_hcksum_txflags | HCKSUM_ENABLE; + } + + if (softmac->smac_mdt) { + if (!(softmac->smac_mdt_capab.mdt_flags & + DL_CAPAB_MDT_ENABLE)) { + dl_capab_mdt_t *mdt_subcapp; + + size = sizeof (dl_capability_sub_t) + + sizeof (dl_capab_mdt_t); + capb->dl_sub_length += size; + + subcapb = (dl_capability_sub_t *) + ((uint8_t *)(subcapb + 1) + subcapb->dl_length); + + subcapb->dl_cap = DL_CAPAB_MDT; + subcapb->dl_length = sizeof (dl_capab_mdt_t); + mdt_subcapp = (dl_capab_mdt_t *)(subcapb + 1); + mdt_subcapp->mdt_version = MDT_VERSION_2; + mdt_subcapp->mdt_flags = + (softmac->smac_mdt_capab.mdt_flags | + DL_CAPAB_MDT_ENABLE); + mdt_subcapp->mdt_hdr_head = + softmac->smac_mdt_capab.mdt_hdr_head; + mdt_subcapp->mdt_hdr_tail = + softmac->smac_mdt_capab.mdt_hdr_tail; + mdt_subcapp->mdt_max_pld = + softmac->smac_mdt_capab.mdt_max_pld; + mdt_subcapp->mdt_span_limit = + softmac->smac_mdt_capab.mdt_span_limit; + } + } + +output: + err = softmac_proto_tx(slp, reqmp, &ackmp); + if (err == 0) { + if (enable) { + err = i_capab_ack(ackmp, NULL, + &softmac_enable_capab_ops, softmac); + } else { + err = i_capab_ack(ackmp, NULL, + &softmac_adv_capab_ops, softmac); + } + } + freemsg(ackmp); + + return (err); +} + +static int +softmac_adv_hcksum_ack(void *arg, t_uscalar_t flags) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * There are two types of acks we process here: + * 1. acks in reply to a (first form) generic capability req + * (no ENABLE flag set) + * 2. acks in reply to a ENABLE capability req. + * (ENABLE flag set) + * Only the first type should be expected here. + */ + + if (flags & HCKSUM_ENABLE) { + cmn_err(CE_WARN, "softmac_adv_hcksum_ack: unexpected " + "HCKSUM_ENABLE flag in hardware checksum capability"); + return (-1); + } else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 | + HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) { + /* + * The acknowledgement should be the same as we got when + * the softmac is created. + */ + if (!(softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)) { + ASSERT(B_FALSE); + return (-1); + } + if (softmac->smac_hcksum_txflags != flags) { + ASSERT(B_FALSE); + return (-1); + } + } + + return (0); +} + +static int +softmac_adv_zcopy_ack(void *arg, t_uscalar_t flags) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * The acknowledgement should be the same as we got when + * the softmac is created. + */ + ASSERT(flags == DL_CAPAB_VMSAFE_MEM); + if (softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY) { + ASSERT(B_FALSE); + return (-1); + } + + return (0); +} + +static int +softmac_adv_mdt_ack(void *arg, dl_capab_mdt_t *mdt) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * The acknowledgement should be the same as we got when + * the softmac is created. + */ + if (!softmac->smac_mdt) { + ASSERT(B_FALSE); + return (-1); + } + + if ((softmac->smac_mdt_capab.mdt_hdr_head != mdt->mdt_hdr_head) || + (softmac->smac_mdt_capab.mdt_hdr_tail != mdt->mdt_hdr_tail) || + (softmac->smac_mdt_capab.mdt_max_pld != mdt->mdt_max_pld) || + (softmac->smac_mdt_capab.mdt_span_limit != mdt->mdt_span_limit)) { + ASSERT(B_FALSE); + return (-1); + } + /* + * We need the mdt_flags field to know whether an additional + * DL_CAPAB_MDT_ENABLE is necessary. + */ + softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags; + return (0); +} + +static int +softmac_enable_hcksum_ack(void *arg, t_uscalar_t flags) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * There are two types of acks we process here: + * 1. acks in reply to a (first form) generic capability req + * (no ENABLE flag set) + * 2. acks in reply to a ENABLE capability req. + * (ENABLE flag set) + * Only the second type should be expected here. + */ + + if (flags & HCKSUM_ENABLE) { + if ((flags & ~HCKSUM_ENABLE) != softmac->smac_hcksum_txflags) { + cmn_err(CE_WARN, "softmac_enable_hcksum_ack: unexpected" + " hardware capability flag value 0x%x", flags); + return (-1); + } + } else { + cmn_err(CE_WARN, "softmac_enable_hcksum_ack: " + "hardware checksum flag HCKSUM_ENABLE is not set"); + return (-1); + } + + return (0); +} + +static int +softmac_enable_mdt_ack(void *arg, dl_capab_mdt_t *mdt) +{ + softmac_t *softmac = (softmac_t *)arg; + + /* + * There are two types of acks we process here: + * 1. acks in reply to a (first form) generic capability req + * (no ENABLE flag set) + * 2. acks in reply to a ENABLE capability req. + * (ENABLE flag set) + * Only the second type should be expected here. + */ + + if (mdt->mdt_flags & DL_CAPAB_MDT_ENABLE) { + if ((softmac->smac_mdt_capab.mdt_hdr_head != + mdt->mdt_hdr_head) || + (softmac->smac_mdt_capab.mdt_hdr_tail != + mdt->mdt_hdr_tail) || + (softmac->smac_mdt_capab.mdt_max_pld != + mdt->mdt_max_pld) || + (softmac->smac_mdt_capab.mdt_span_limit != + mdt->mdt_span_limit)) { + cmn_err(CE_WARN, "softmac_enable_mdt_ack: " + "unexpected MDT capability value"); + return (-1); + } + softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags; + } else { + cmn_err(CE_WARN, "softmac_enable_mdt_ack: " + "MDT flag DL_CAPAB_MDT_ENABLE is not set"); + return (-1); + } + + return (0); +} + +static int +i_capab_ack(mblk_t *mp, queue_t *q, softmac_capab_ops_t *op, void *arg) +{ + union DL_primitives *prim; + dl_capability_ack_t *cap; + dl_capability_sub_t *sub, *end; + int err = 0; + + prim = (union DL_primitives *)mp->b_rptr; + ASSERT(prim->dl_primitive == DL_CAPABILITY_ACK); + + cap = (dl_capability_ack_t *)prim; + if (cap->dl_sub_length == 0) + goto exit; + + /* Is dl_sub_length correct? */ + if ((sizeof (*cap) + cap->dl_sub_length) > MBLKL(mp)) { + err = EINVAL; + goto exit; + } + + sub = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_offset); + end = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_length + - sizeof (*sub)); + for (; (sub <= end) && (err == 0); ) { + switch (sub->dl_cap) { + case DL_CAPAB_ID_WRAPPER: + err = i_capab_id_ack(mp, sub, q, op, arg); + break; + default: + err = i_capab_sub_ack(mp, sub, q, op, arg); + break; + } + sub = (dl_capability_sub_t *)((caddr_t)sub + sizeof (*sub) + + sub->dl_length); + } + +exit: + return (err); +} + +static int +i_capab_id_ack(mblk_t *mp, dl_capability_sub_t *outers, + queue_t *q, softmac_capab_ops_t *op, void *arg) +{ + dl_capab_id_t *capab_id; + dl_capability_sub_t *inners; + caddr_t capend; + int err = EINVAL; + + ASSERT(outers->dl_cap == DL_CAPAB_ID_WRAPPER); + + capend = (caddr_t)(outers + 1) + outers->dl_length; + if (capend > (caddr_t)mp->b_wptr) { + cmn_err(CE_WARN, "i_capab_id_ack: malformed " + "sub-capability too long"); + return (err); + } + + capab_id = (dl_capab_id_t *)(outers + 1); + + if (outers->dl_length < sizeof (*capab_id) || + (inners = &capab_id->id_subcap, + inners->dl_length > (outers->dl_length - sizeof (*inners)))) { + cmn_err(CE_WARN, "i_capab_id_ack: malformed " + "encapsulated capab type %d too long", + inners->dl_cap); + return (err); + } + + if ((q != NULL) && (!dlcapabcheckqid(&capab_id->id_mid, q))) { + cmn_err(CE_WARN, "i_capab_id_ack: pass-thru module(s) " + "detected, discarding capab type %d", inners->dl_cap); + return (err); + } + + /* Process the encapsulated sub-capability */ + return (i_capab_sub_ack(mp, inners, q, op, arg)); +} + +static int +i_capab_sub_ack(mblk_t *mp, dl_capability_sub_t *sub, queue_t *q, + softmac_capab_ops_t *op, void *arg) +{ + caddr_t capend; + dl_capab_hcksum_t *hcksum; + dl_capab_zerocopy_t *zcopy; + dl_capab_mdt_t *mdt; + int err = 0; + + capend = (caddr_t)(sub + 1) + sub->dl_length; + if (capend > (caddr_t)mp->b_wptr) { + cmn_err(CE_WARN, "i_capab_sub_ack: " + "malformed sub-capability too long"); + return (EINVAL); + } + + switch (sub->dl_cap) { + case DL_CAPAB_HCKSUM: + hcksum = (dl_capab_hcksum_t *)(sub + 1); + err = i_capab_hcksum_ack(hcksum, q, op, arg); + break; + + case DL_CAPAB_ZEROCOPY: + zcopy = (dl_capab_zerocopy_t *)(sub + 1); + err = i_capab_zcopy_ack(zcopy, q, op, arg); + break; + + case DL_CAPAB_MDT: + mdt = (dl_capab_mdt_t *)(sub + 1); + err = i_capab_mdt_ack(mdt, q, op, arg); + break; + + default: + cmn_err(CE_WARN, "i_capab_sub_ack: unknown capab type %d", + sub->dl_cap); + err = EINVAL; + } + + return (err); +} + +static int +i_capab_hcksum_ack(dl_capab_hcksum_t *hcksum, queue_t *q, + softmac_capab_ops_t *op, void *arg) +{ + t_uscalar_t flags; + int err = 0; + + if ((err = i_capab_hcksum_verify(hcksum, q)) != 0) + return (err); + + flags = hcksum->hcksum_txflags; + + if (!(flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 | + HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM | HCKSUM_ENABLE))) { + cmn_err(CE_WARN, "i_capab_hcksum_ack: invalid " + "hardware checksum capability flags 0x%x", flags); + return (EINVAL); + } + + if (op->sc_hcksum_ack) + return (op->sc_hcksum_ack(arg, flags)); + else { + cmn_err(CE_WARN, "i_capab_hcksum_ack: unexpected hardware " + "checksum acknowledgement"); + return (EINVAL); + } +} + +static int +i_capab_zcopy_ack(dl_capab_zerocopy_t *zcopy, queue_t *q, + softmac_capab_ops_t *op, void *arg) +{ + t_uscalar_t flags; + int err = 0; + + if ((err = i_capab_zcopy_verify(zcopy, q)) != 0) + return (err); + + flags = zcopy->zerocopy_flags; + if (!(flags & DL_CAPAB_VMSAFE_MEM)) { + cmn_err(CE_WARN, "i_capab_zcopy_ack: invalid zcopy capability " + "flags 0x%x", flags); + return (EINVAL); + } + if (op->sc_zcopy_ack) + return (op->sc_zcopy_ack(arg, flags)); + else { + cmn_err(CE_WARN, "i_capab_zcopy_ack: unexpected zcopy " + "acknowledgement"); + return (EINVAL); + } +} + +static int +i_capab_mdt_ack(dl_capab_mdt_t *mdt, queue_t *q, + softmac_capab_ops_t *op, void *arg) +{ + int err; + + if ((err = i_capab_mdt_verify(mdt, q)) != 0) + return (err); + + if (op->sc_mdt_ack) + return (op->sc_mdt_ack(arg, mdt)); + else { + cmn_err(CE_WARN, "i_capab_mdt_ack: unexpected MDT " + "acknowledgement"); + return (EINVAL); + } +} + +static int +i_capab_hcksum_verify(dl_capab_hcksum_t *hcksum, queue_t *q) +{ + if (hcksum->hcksum_version != HCKSUM_VERSION_1) { + cmn_err(CE_WARN, "i_capab_hcksum_verify: " + "unsupported hardware checksum capability (version %d, " + "expected %d)", hcksum->hcksum_version, HCKSUM_VERSION_1); + return (-1); + } + + if ((q != NULL) && !dlcapabcheckqid(&hcksum->hcksum_mid, q)) { + cmn_err(CE_WARN, "i_capab_hcksum_verify: unexpected pass-thru " + "module detected; hardware checksum capability discarded"); + return (-1); + } + return (0); +} + +static int +i_capab_zcopy_verify(dl_capab_zerocopy_t *zcopy, queue_t *q) +{ + if (zcopy->zerocopy_version != ZEROCOPY_VERSION_1) { + cmn_err(CE_WARN, "i_capab_zcopy_verify: unsupported zcopy " + "capability (version %d, expected %d)", + zcopy->zerocopy_version, ZEROCOPY_VERSION_1); + return (-1); + } + + if ((q != NULL) && !dlcapabcheckqid(&zcopy->zerocopy_mid, q)) { + cmn_err(CE_WARN, "i_capab_zcopy_verify: unexpected pass-thru " + "module detected; zcopy checksum capability discarded"); + return (-1); + } + return (0); +} + +static int +i_capab_mdt_verify(dl_capab_mdt_t *mdt, queue_t *q) +{ + if (mdt->mdt_version != MDT_VERSION_2) { + cmn_err(CE_WARN, "i_capab_mdt_verify: unsupported MDT " + "capability (version %d, expected %d)", + mdt->mdt_version, MDT_VERSION_2); + return (-1); + } + + if ((q != NULL) && !dlcapabcheckqid(&mdt->mdt_mid, q)) { + cmn_err(CE_WARN, "i_capab_mdt_verify: unexpected pass-thru " + "module detected; MDT capability discarded"); + return (-1); + } + return (0); +} diff --git a/usr/src/uts/common/io/softmac/softmac_ctl.c b/usr/src/uts/common/io/softmac/softmac_ctl.c new file mode 100644 index 0000000000..33472bd303 --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_ctl.c @@ -0,0 +1,389 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/stropts.h> +#include <sys/softmac_impl.h> + +int +softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications) +{ + mblk_t *reqmp; + + /* + * create notify req message and send it down + */ + reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, + DL_NOTIFY_REQ); + if (reqmp == NULL) + return (ENOMEM); + + ((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications; + + return (softmac_proto_tx(slp, reqmp, NULL)); +} + +int +softmac_send_bind_req(softmac_lower_t *slp, uint_t sap) +{ + dl_bind_req_t *bind; + mblk_t *reqmp; + + /* + * create bind req message and send it down + */ + reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); + if (reqmp == NULL) + return (ENOMEM); + + bind = (dl_bind_req_t *)reqmp->b_rptr; + bind->dl_sap = sap; + bind->dl_conn_mgmt = 0; + bind->dl_max_conind = 0; + bind->dl_xidtest_flg = 0; + bind->dl_service_mode = DL_CLDLS; + + return (softmac_proto_tx(slp, reqmp, NULL)); +} + +int +softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on) +{ + mblk_t *reqmp; + size_t size; + t_uscalar_t dl_prim; + + /* + * create promisc message and send it down + */ + if (on) { + dl_prim = DL_PROMISCON_REQ; + size = DL_PROMISCON_REQ_SIZE; + } else { + dl_prim = DL_PROMISCOFF_REQ; + size = DL_PROMISCOFF_REQ_SIZE; + } + + reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); + if (reqmp == NULL) + return (ENOMEM); + + if (on) + ((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level; + else + ((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level; + + return (softmac_proto_tx(slp, reqmp, NULL)); +} + +int +softmac_m_promisc(void *arg, boolean_t on) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp = softmac->smac_lower; + + ASSERT(slp != NULL); + return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on)); +} + +int +softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp; + dl_enabmulti_req_t *enabmulti; + dl_disabmulti_req_t *disabmulti; + mblk_t *reqmp; + t_uscalar_t dl_prim; + uint32_t size, addr_length; + + /* + * create multicst message and send it down + */ + addr_length = softmac->smac_addrlen; + if (add) { + size = sizeof (dl_enabmulti_req_t) + addr_length; + dl_prim = DL_ENABMULTI_REQ; + } else { + size = sizeof (dl_disabmulti_req_t) + addr_length; + dl_prim = DL_DISABMULTI_REQ; + } + + reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); + if (reqmp == NULL) + return (ENOMEM); + + if (add) { + enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr; + enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t); + enabmulti->dl_addr_length = addr_length; + (void) memcpy(&enabmulti[1], mca, addr_length); + } else { + disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr; + disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t); + disabmulti->dl_addr_length = addr_length; + (void) memcpy(&disabmulti[1], mca, addr_length); + } + + slp = softmac->smac_lower; + ASSERT(slp != NULL); + return (softmac_proto_tx(slp, reqmp, NULL)); +} + +int +softmac_m_unicst(void *arg, const uint8_t *macaddr) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp; + dl_set_phys_addr_req_t *phyaddr; + mblk_t *reqmp; + size_t size; + + /* + * create set_phys_addr message and send it down + */ + size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen; + reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ); + if (reqmp == NULL) + return (ENOMEM); + + phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr; + phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t); + phyaddr->dl_addr_length = softmac->smac_addrlen; + (void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen); + + slp = softmac->smac_lower; + ASSERT(slp != NULL); + return (softmac_proto_tx(slp, reqmp, NULL)); +} + +void +softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) +{ + softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower; + mblk_t *ackmp; + + ASSERT(slp != NULL); + softmac_ioctl_tx(slp, mp, &ackmp); + qreply(wq, ackmp); +} + +static void +softmac_process_notify_ind(queue_t *rq, mblk_t *mp) +{ + softmac_lower_t *slp = rq->q_ptr; + dl_notify_ind_t *dlnip = (dl_notify_ind_t *)mp->b_rptr; + softmac_t *softmac = slp->sl_softmac; + uint_t addroff, addrlen; + + ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND); + + switch (dlnip->dl_notification) { + case DL_NOTE_PHYS_ADDR: + if (dlnip->dl_data != DL_CURR_PHYS_ADDR) + break; + + addroff = dlnip->dl_addr_offset; + addrlen = dlnip->dl_addr_length - softmac->smac_saplen; + if (addroff == 0 || addrlen != softmac->smac_addrlen || + !MBLKIN(mp, addroff, addrlen)) { + cmn_err(CE_NOTE, "softmac: got malformed " + "DL_NOTIFY_IND; length/offset %d/%d", + addrlen, addroff); + break; + } + + mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff); + break; + + case DL_NOTE_LINK_UP: + mac_link_update(softmac->smac_mh, LINK_STATE_UP); + break; + + case DL_NOTE_LINK_DOWN: + mac_link_update(softmac->smac_mh, LINK_STATE_DOWN); + break; + } + + freemsg(mp); +} + +static void +softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen, + t_uscalar_t reqprim) +{ + const char *ackname; + + ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive); + + if (MBLKL(mp) < minlen) { + cmn_err(CE_WARN, "softmac: got short %s", ackname); + freemsg(mp); + return; + } + + mutex_enter(&slp->sl_mutex); + if (slp->sl_pending_prim != reqprim) { + cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname); + mutex_exit(&slp->sl_mutex); + freemsg(mp); + return; + } + + slp->sl_pending_prim = DL_PRIM_INVAL; + slp->sl_ack_mp = mp; + cv_signal(&slp->sl_cv); + mutex_exit(&slp->sl_mutex); +} + +void +softmac_rput_process_proto(queue_t *rq, mblk_t *mp) +{ + softmac_lower_t *slp = rq->q_ptr; + union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; + ssize_t len = MBLKL(mp); + const char *primstr; + + if (len < sizeof (t_uscalar_t)) { + cmn_err(CE_WARN, "softmac: got runt DLPI message"); + goto exit; + } + + primstr = dl_primstr(dlp->dl_primitive); + + switch (dlp->dl_primitive) { + case DL_OK_ACK: + if (len < DL_OK_ACK_SIZE) + goto runt; + + softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE, + dlp->ok_ack.dl_correct_primitive); + return; + + case DL_ERROR_ACK: + if (len < DL_ERROR_ACK_SIZE) + goto runt; + + cmn_err(CE_NOTE, "softmac: received DL_ERROR_ACK for " + "%s errno/unix_errno 0x%x/%d", + dl_primstr(dlp->error_ack.dl_error_primitive), + dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno); + + softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE, + dlp->error_ack.dl_error_primitive); + return; + + case DL_NOTIFY_IND: + if (len < DL_NOTIFY_IND_SIZE) + goto runt; + + softmac_process_notify_ind(rq, mp); + return; + + case DL_NOTIFY_ACK: + softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE, + DL_NOTIFY_REQ); + return; + + case DL_CAPABILITY_ACK: + softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE, + DL_CAPABILITY_REQ); + return; + + case DL_BIND_ACK: + softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ); + return; + + case DL_CONTROL_ACK: + softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE, + DL_CONTROL_REQ); + return; + + case DL_UNITDATA_IND: + case DL_PHYS_ADDR_ACK: + /* + * a. Because the stream is in DLIOCRAW mode, + * DL_UNITDATA_IND messages are not expected. + * b. The lower stream should not receive DL_PHYS_ADDR_REQ, + * so DL_PHYS_ADDR_ACK messages are also unexpected. + */ + default: + cmn_err(CE_WARN, "softmac: got unexpected %s", primstr); + break; + } +exit: + freemsg(mp); + return; +runt: + cmn_err(CE_WARN, "softmac: got runt %s", primstr); + freemsg(mp); +} + +void +softmac_rput_process_notdata(queue_t *rq, mblk_t *mp) +{ + softmac_lower_t *slp = rq->q_ptr; + + switch (DB_TYPE(mp)) { + case M_PROTO: + case M_PCPROTO: + softmac_rput_process_proto(rq, mp); + break; + + case M_FLUSH: + if (*mp->b_rptr & FLUSHR) + flushq(rq, FLUSHDATA); + if (*mp->b_rptr & FLUSHW) + flushq(OTHERQ(rq), FLUSHDATA); + putnext(rq, mp); + break; + + case M_IOCACK: + case M_IOCNAK: + case M_COPYIN: + case M_COPYOUT: + mutex_enter(&slp->sl_mutex); + if (!slp->sl_pending_ioctl) { + mutex_exit(&slp->sl_mutex); + cmn_err(CE_NOTE, "softmac: got unexpected mblk " + "type 0x%x", DB_TYPE(mp)); + freemsg(mp); + break; + } + + slp->sl_pending_ioctl = B_FALSE; + slp->sl_ack_mp = mp; + cv_broadcast(&slp->sl_cv); + mutex_exit(&slp->sl_mutex); + return; + + default: + cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x", + DB_TYPE(mp)); + freemsg(mp); + break; + } +} diff --git a/usr/src/uts/common/io/softmac/softmac_dev.c b/usr/src/uts/common/io/softmac/softmac_dev.c new file mode 100644 index 0000000000..501cec84da --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_dev.c @@ -0,0 +1,417 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/dld.h> +#include <inet/common.h> +#include <sys/stropts.h> +#include <sys/modctl.h> +#include <sys/avl.h> +#include <sys/softmac_impl.h> +#include <sys/softmac.h> + +dev_info_t *softmac_dip = NULL; + +static int softmac_open(queue_t *, dev_t *, int, int, cred_t *); +static int softmac_close(queue_t *); +static void softmac_rput(queue_t *, mblk_t *); +static void softmac_rsrv(queue_t *); +static void softmac_wput(queue_t *, mblk_t *); +static void softmac_wsrv(queue_t *); +static int softmac_attach(dev_info_t *, ddi_attach_cmd_t); +static int softmac_detach(dev_info_t *, ddi_detach_cmd_t); +static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **); + +static struct module_info softmac_modinfo = { + 0, + SOFTMAC_DEV_NAME, + 0, + INFPSZ, + 65536, + 1024 +}; + +/* + * hi-water mark is 1 because of the flow control mechanism implemented in + * dld. Refer to the comments in dld_str.c for details. + */ +static struct module_info softmac_dld_modinfo = { + 0, + SOFTMAC_DEV_NAME, + 0, + INFPSZ, + 1, + 0 +}; + +static struct qinit softmac_urinit = { + (pfi_t)softmac_rput, /* qi_putp */ + (pfi_t)softmac_rsrv, /* qi_srvp */ + softmac_open, /* qi_qopen */ + softmac_close, /* qi_qclose */ + NULL, /* qi_qadmin */ + &softmac_modinfo /* qi_minfo */ +}; + +static struct qinit softmac_uwinit = { + (pfi_t)softmac_wput, /* qi_putp */ + (pfi_t)softmac_wsrv, /* qi_srvp */ + NULL, /* qi_qopen */ + NULL, /* qi_qclose */ + NULL, /* qi_qadmin */ + &softmac_modinfo /* qi_minfo */ +}; + +static struct streamtab softmac_tab = { + &softmac_urinit, /* st_rdinit */ + &softmac_uwinit /* st_wrinit */ +}; + +DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach, + softmac_detach, nodev, softmac_info, D_MP, &softmac_tab); + +static struct qinit softmac_dld_r_qinit = { + NULL, NULL, dld_open, dld_close, NULL, &softmac_dld_modinfo +}; + +static struct qinit softmac_dld_w_qinit = { + (pfi_t)dld_wput, (pfi_t)dld_wsrv, NULL, NULL, NULL, + &softmac_dld_modinfo +}; + +static struct fmodsw softmac_fmodsw = { + SOFTMAC_DEV_NAME, + &softmac_tab, + D_MP +}; + +static struct modldrv softmac_modldrv = { + &mod_driverops, + "softmac driver", + &softmac_ops +}; + +static struct modlstrmod softmac_modlstrmod = { + &mod_strmodops, + "softmac module", + &softmac_fmodsw +}; + +static struct modlinkage softmac_modlinkage = { + MODREV_1, + &softmac_modlstrmod, + &softmac_modldrv, + NULL +}; + +int +_init(void) +{ + int err; + + softmac_init(); + + if ((err = mod_install(&softmac_modlinkage)) != 0) { + softmac_fini(); + return (err); + } + + return (0); +} + +int +_fini(void) +{ + int err; + + if (softmac_busy()) + return (EBUSY); + + if ((err = mod_remove(&softmac_modlinkage)) != 0) + return (err); + + softmac_fini(); + + return (0); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&softmac_modlinkage, modinfop)); +} + +static int +softmac_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) +{ + softmac_lower_t *slp; + /* + * This is a self-cloning driver so that each queue should only + * get opened once. + */ + if (rq->q_ptr != NULL) + return (EBUSY); + + if (sflag == MODOPEN) { + /* + * This is the softmac module pushed over an underlying + * legacy device. Initialize the lower structure. + */ + if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL) + return (ENOMEM); + + slp->sl_wq = WR(rq); + cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL); + mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL); + cv_init(&slp->sl_ctl_cv, NULL, CV_DRIVER, NULL); + mutex_init(&slp->sl_ctl_mutex, NULL, MUTEX_DRIVER, NULL); + slp->sl_pending_prim = DL_PRIM_INVAL; + rq->q_ptr = WR(rq)->q_ptr = slp; + qprocson(rq); + return (0); + } + + /* + * Regular device open of a softmac DLPI node. We modify + * the queues' q_qinfo pointer such that all future STREAMS + * operations will go through dld's entry points (including + * dld_close()). + */ + rq->q_qinfo = &softmac_dld_r_qinit; + WR(rq)->q_qinfo = &softmac_dld_w_qinit; + return (dld_open(rq, devp, flag, sflag, credp)); +} + +static int +softmac_close(queue_t *rq) +{ + softmac_lower_t *slp = rq->q_ptr; + + /* + * Call the appropriate delete routine depending on whether this is + * a module or device. + */ + ASSERT(WR(rq)->q_next != NULL); + + qprocsoff(rq); + + slp->sl_softmac = NULL; + slp->sl_lh = NULL; + + /* + * slp->sl_handle could be non-NULL if it is in the aggregation. + */ + slp->sl_handle = (mac_resource_handle_t)NULL; + + ASSERT(slp->sl_ack_mp == NULL); + ASSERT(slp->sl_ctl_inprogress == B_FALSE); + ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); + ASSERT(slp->sl_pending_ioctl == B_FALSE); + + cv_destroy(&slp->sl_cv); + mutex_destroy(&slp->sl_mutex); + cv_destroy(&slp->sl_ctl_cv); + mutex_destroy(&slp->sl_ctl_mutex); + + kmem_free(slp, sizeof (*slp)); + return (0); +} + +static void +softmac_rput(queue_t *rq, mblk_t *mp) +{ + softmac_lower_t *slp = rq->q_ptr; + union DL_primitives *dlp; + + /* + * This is the softmac module. + */ + ASSERT(WR(rq)->q_next != NULL); + ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL)); + + switch (DB_TYPE(mp)) { + case M_DATA: + /* + * Some drivers start to send up packets even if not in the + * DL_IDLE state, where sl_softmac is not set yet. Drop the + * packet in this case. + */ + if (slp->sl_softmac == NULL) { + freemsg(mp); + return; + } + + /* + * This is the most common case. + */ + if (DB_REF(mp) == 1) { + ASSERT(slp->sl_softmac != NULL); + /* + * We don't need any locks to protect sl_handle + * because ip_input() can tolerate if sl_handle + * is reset to NULL when DL_CAPAB_POLL is + * disabled. + */ + mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp); + return; + } else { + softmac_rput_process_data(slp, mp); + } + break; + case M_PROTO: + case M_PCPROTO: + if (MBLKL(mp) < sizeof (dlp->dl_primitive)) { + freemsg(mp); + break; + } + dlp = (union DL_primitives *)mp->b_rptr; + if (dlp->dl_primitive == DL_UNITDATA_IND) { + cmn_err(CE_WARN, "got unexpected %s message", + dl_primstr(DL_UNITDATA_IND)); + freemsg(mp); + break; + } + /*FALLTHROUGH*/ + default: + softmac_rput_process_notdata(rq, mp); + break; + } +} + +/* ARGSUSED */ +static void +softmac_rsrv(queue_t *rq) +{ +} + +static void +softmac_wput(queue_t *wq, mblk_t *mp) +{ + /* + * This is the softmac module + */ + ASSERT(wq->q_next != NULL); + + switch (DB_TYPE(mp)) { + case M_IOCTL: { + struct iocblk *ioc = (struct iocblk *)mp->b_rptr; + + switch (ioc->ioc_cmd) { + case SMAC_IOC_START: { + softmac_lower_t *slp = wq->q_ptr; + smac_ioc_start_t *arg; + + if (ioc->ioc_count != sizeof (*arg)) { + miocnak(wq, mp, 0, EINVAL); + break; + } + + /* + * Assign the devname and perstream handle of the + * specific lower stream and return it as a part + * of the ioctl. + */ + arg = (smac_ioc_start_t *)mp->b_cont->b_rptr; + arg->si_slp = slp; + + miocack(wq, mp, sizeof (*arg), 0); + break; + } + default: + miocnak(wq, mp, 0, EINVAL); + break; + } + break; + } + default: + freemsg(mp); + break; + } +} + +static void +softmac_wsrv(queue_t *wq) +{ + softmac_lower_t *slp = wq->q_ptr; + + /* + * This is the softmac module + */ + ASSERT(wq->q_next != NULL); + + /* + * Inform that the tx resource is available; mac_tx_update() will + * inform all the upper streams sharing this lower stream. + */ + if (slp->sl_softmac != NULL) + mac_tx_update(slp->sl_softmac->smac_mh); +} + +static int +softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + ASSERT(ddi_get_instance(dip) == 0); + + if (cmd != DDI_ATTACH) + return (DDI_FAILURE); + + softmac_dip = dip; + + return (DDI_SUCCESS); +} + +/* ARGSUSED */ +static int +softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + if (cmd != DDI_DETACH) + return (DDI_FAILURE); + + softmac_dip = NULL; + return (DDI_SUCCESS); +} + +/* ARGSUSED */ +static int +softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) +{ + switch (infocmd) { + case DDI_INFO_DEVT2DEVINFO: + if (softmac_dip != NULL) { + *result = softmac_dip; + return (DDI_SUCCESS); + } + break; + + case DDI_INFO_DEVT2INSTANCE: + *result = NULL; + return (DDI_SUCCESS); + + } + + return (DDI_FAILURE); +} diff --git a/usr/src/uts/common/io/softmac/softmac_main.c b/usr/src/uts/common/io/softmac/softmac_main.c new file mode 100644 index 0000000000..8a218c53fb --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_main.c @@ -0,0 +1,1192 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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/types.h> +#include <sys/file.h> +#include <sys/cred.h> +#include <sys/dlpi.h> +#include <sys/sunndi.h> +#include <sys/modhash.h> +#include <sys/stropts.h> +#include <sys/sysmacros.h> +#include <sys/vlan.h> +#include <sys/softmac_impl.h> +#include <sys/softmac.h> +#include <sys/dls.h> + +/* + * 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; + +#define SOFTMAC_HASHSZ 64 + +static void softmac_mac_register(void *); +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 boolean_t softmac_m_getcapab(void *, mac_capab_t, void *); + +#define SOFTMAC_M_CALLBACK_FLAGS \ + (MC_RESOURCES | MC_IOCTL | MC_GETCAPAB | MC_OPEN | MC_CLOSE) + +static mac_callbacks_t softmac_m_callbacks = { + SOFTMAC_M_CALLBACK_FLAGS, + softmac_m_stat, + softmac_m_start, + softmac_m_stop, + softmac_m_promisc, + softmac_m_multicst, + softmac_m_unicst, + softmac_m_tx, + softmac_m_resources, + softmac_m_ioctl, + softmac_m_getcapab, + softmac_m_open, + softmac_m_close +}; + +void +softmac_init() +{ + softmac_hash = mod_hash_create_extended("softmac_hash", + SOFTMAC_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor, + mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP); + + rw_init(&softmac_hash_lock, NULL, RW_DEFAULT, NULL); +} + +void +softmac_fini() +{ + rw_destroy(&softmac_hash_lock); + mod_hash_destroy_hash(softmac_hash); +} + +/* ARGSUSED */ +static uint_t +softmac_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg) +{ + boolean_t *pexist = arg; + + *pexist = B_TRUE; + return (MH_WALK_TERMINATE); +} + +boolean_t +softmac_busy() +{ + boolean_t exist = B_FALSE; + + rw_enter(&softmac_hash_lock, RW_READER); + mod_hash_walk(softmac_hash, softmac_exist, &exist); + rw_exit(&softmac_hash_lock); + return (exist); +} + +/* + * This function 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()). + */ +int +softmac_create(dev_info_t *dip, dev_t dev) +{ + char devname[MAXNAMELEN]; + softmac_t *softmac; + softmac_dev_t *softmac_dev = NULL; + datalink_id_t linkid; + int index; + int ppa, err = 0; + mac_handle_t mh; + + /* + * Force the softmac driver to be attached. + */ + if (i_ddi_attach_pseudo_node(SOFTMAC_DEV_NAME) == NULL) { + cmn_err(CE_WARN, "softmac_create:softmac attach fails"); + return (ENXIO); + } + + ppa = ddi_get_instance(dip); + (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa); + + /* + * We expect legacy devices have at most two minor nodes - one style-1 + * and one style-2. + */ + if (!GLDV3_DRV(ddi_driver_major(dip)) && + i_ddi_minor_node_count(dip, DDI_NT_NET) > 2) { + cmn_err(CE_WARN, "%s has more than 2 minor nodes; unsupported", + devname); + return (ENOTSUP); + } + + /* + * Check whether the softmac for the specified device already exists + */ + rw_enter(&softmac_hash_lock, RW_WRITER); + if ((err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname, + (mod_hash_val_t *)&softmac)) != 0) { + + softmac = kmem_zalloc(sizeof (softmac_t), KM_SLEEP); + mutex_init(&softmac->smac_mutex, NULL, MUTEX_DRIVER, NULL); + cv_init(&softmac->smac_cv, NULL, CV_DRIVER, NULL); + rw_init(&softmac->smac_lock, NULL, RW_DRIVER, NULL); + (void) strlcpy(softmac->smac_devname, devname, MAXNAMELEN); + + /* + * Insert the softmac into the hash table. + */ + err = mod_hash_insert(softmac_hash, + (mod_hash_key_t)softmac->smac_devname, + (mod_hash_val_t)softmac); + ASSERT(err == 0); + } + + mutex_enter(&softmac->smac_mutex); + if (softmac->smac_attachok_cnt == 0) { + /* + * Initialize the softmac if this is the post-attach of the + * first minor node. + */ + softmac->smac_flags = 0; + softmac->smac_umajor = ddi_driver_major(dip); + softmac->smac_uppa = ppa; + + /* + * Note that for GLDv3 devices, we create devfs minor nodes + * for VLANs as well. Assume a GLDv3 driver on which only + * a VLAN is created. During the detachment of this device + * instance, the following would happen: + * a. the pre-detach callback softmac_destroy() succeeds. + * Because the physical link itself is not in use, + * softmac_destroy() succeeds and destroys softmac_t; + * b. the device detach fails in mac_unregister() because + * this MAC is still used by a VLAN. + * c. the post-attach callback is then called which leads + * us here. Note that ddi_minor_node_count() returns 3 + * (including the minior node of the VLAN). In that case, + * we must correct the minor node count to 2 as that is + * the count of minor nodes that go through post-attach. + */ + if (GLDV3_DRV(ddi_driver_major(dip))) { + softmac->smac_flags |= SOFTMAC_GLDV3; + softmac->smac_cnt = 2; + } else { + softmac->smac_cnt = + i_ddi_minor_node_count(dip, DDI_NT_NET); + } + } + + index = (getmajor(dev) == ddi_name_to_major("clone")); + if (softmac->smac_softmac[index] != NULL) { + /* + * This is possible if the post_attach() is called: + * + * a. after pre_detach() fails. + * + * b. for a new round of reattachment. Note that DACF will not + * call pre_detach() for successfully post_attached minor + * nodes even when the post-attach failed after all. + * + * Both seem to be defects in the DACF framework. To work + * around it and only clear the SOFTMAC_ATTACH_DONE flag for + * the b case, a smac_attached_left field is used to tell + * the two cases apart. + */ + ASSERT(softmac->smac_attachok_cnt != 0); + + if (softmac->smac_attached_left != 0) + /* case a */ + softmac->smac_attached_left--; + else if (softmac->smac_attachok_cnt != softmac->smac_cnt) { + /* case b */ + softmac->smac_flags &= ~SOFTMAC_ATTACH_DONE; + } + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + return (0); + } + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + + /* + * Inform dlmgmtd of this link so that softmac_hold_device() is able + * to know the existence of this link. This could fail if dlmgmtd + * is not yet started. + */ + (void) dls_mgmt_create(devname, makedevice(ddi_driver_major(dip), + ppa + 1), DATALINK_CLASS_PHYS, DL_OTHER, B_TRUE, &linkid); + + /* + * No lock is needed for access this softmac pointer, as pre-detach and + * post-attach won't happen at the same time. + */ + mutex_enter(&softmac->smac_mutex); + + softmac_dev = kmem_zalloc(sizeof (softmac_dev_t), KM_SLEEP); + softmac_dev->sd_dev = dev; + softmac->smac_softmac[index] = softmac_dev; + + /* + * Continue to register the mac and create the datalink only when all + * the minor nodes are attached. + */ + if (++softmac->smac_attachok_cnt != softmac->smac_cnt) { + mutex_exit(&softmac->smac_mutex); + return (0); + } + + if (!GLDV3_DRV(ddi_driver_major(dip))) { + + /* + * Note that this function could be called as a result of + * a open() system call, and spec_open() already locked the + * snode (SLOCKED is set). Therefore, we must start a + * taskq to finish the rest of work to sidestep the risk + * that our ldi_open_by_dev() call would again try to hold + * the same lock. + * + * If all the minor nodes have been attached, start the taskq + * to finish the rest of the work. + */ + ASSERT(softmac->smac_taskq == NULL); + softmac->smac_taskq = taskq_dispatch(system_taskq, + softmac_mac_register, softmac, TQ_SLEEP); + mutex_exit(&softmac->smac_mutex); + return (0); + } + + if ((err = mac_open(softmac->smac_devname, &mh)) != 0) + goto done; + + softmac->smac_media = (mac_info(mh))->mi_nativemedia; + softmac->smac_mh = mh; + + /* + * 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). + */ + mac_close(mh); + + /* + * Create the GLDv3 datalink for this mac. + */ + err = softmac_create_datalink(softmac); + +done: + if (err != 0) { + softmac->smac_mh = NULL; + kmem_free(softmac_dev, sizeof (softmac_dev_t)); + softmac->smac_softmac[index] = NULL; + --softmac->smac_attachok_cnt; + } + ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)); + softmac->smac_flags |= SOFTMAC_ATTACH_DONE; + softmac->smac_attacherr = err; + cv_broadcast(&softmac->smac_cv); + mutex_exit(&softmac->smac_mutex); + return (err); +} + +static boolean_t +softmac_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) +{ + softmac_t *softmac = arg; + + if (!(softmac->smac_capab_flags & cap)) + return (B_FALSE); + + switch (cap) { + case MAC_CAPAB_HCKSUM: { + uint32_t *txflags = cap_data; + + *txflags = softmac->smac_hcksum_txflags; + break; + } + case MAC_CAPAB_LEGACY: { + mac_capab_legacy_t *legacy = cap_data; + + legacy->ml_unsup_note = ~softmac->smac_notifications & + (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_SPEED); + legacy->ml_dev = makedevice(softmac->smac_umajor, + softmac->smac_uppa + 1); + 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_POLL: + case MAC_CAPAB_NO_NATIVEVLAN: + default: + break; + } + return (B_TRUE); +} + +static int +softmac_update_info(softmac_t *softmac, datalink_id_t *linkidp) +{ + datalink_id_t linkid = DATALINK_INVALID_LINKID; + uint32_t media; + int err; + + if ((err = dls_mgmt_update(softmac->smac_devname, softmac->smac_media, + softmac->smac_flags & SOFTMAC_NOSUPP, &media, &linkid)) == 0) { + *linkidp = linkid; + } + + if (err == EEXIST) { + /* + * 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. + */ + if (media != softmac->smac_media) { + cmn_err(CE_WARN, "%s device %s conflicts with " + "existing %s device %s.", + dl_mactypestr(softmac->smac_media), + softmac->smac_devname, dl_mactypestr(media), + softmac->smac_devname); + (void) dls_mgmt_destroy(linkid, B_FALSE); + } else { + cmn_err(CE_WARN, "link name %s is already in-use.", + softmac->smac_devname); + (void) dls_mgmt_destroy(linkid, B_TRUE); + } + + cmn_err(CE_WARN, "%s device might not be available " + "for use.", softmac->smac_devname); + cmn_err(CE_WARN, "See dladm(1M) for more information."); + } + + 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 +softmac_create_datalink(softmac_t *softmac) +{ + datalink_id_t linkid = DATALINK_INVALID_LINKID; + int err; + + ASSERT(MUTEX_HELD(&softmac->smac_mutex)); + + /* + * First provide the media type of the physical link to dlmgmtd. + * + * If the new <linkname, linkid> mapping operation failed with EBADF + * or ENOENT, it might because the dlmgmtd was not started in time + * (e.g., diskless boot); ignore the failure and continue. The + * mapping will be recreated once the daemon has started. + */ + if (((err = softmac_update_info(softmac, &linkid)) != 0) && + (err != EBADF) && (err != ENOENT)) { + return (err); + } + + /* + * Create the GLDv3 datalink. + */ + if ((!(softmac->smac_flags & SOFTMAC_NOSUPP)) && + ((err = dls_devnet_create(softmac->smac_mh, linkid)) != 0)) { + cmn_err(CE_WARN, "dls_devnet_create failed for %s", + softmac->smac_devname); + return (err); + } + + if (linkid == DATALINK_INVALID_LINKID) + softmac->smac_flags |= SOFTMAC_NEED_RECREATE; + + return (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 +softmac_mac_register(void *arg) +{ + softmac_t *softmac = arg; + softmac_dev_t *softmac_dev; + dev_t dev; + ldi_handle_t lh = NULL; + ldi_ident_t li = NULL; + int index; + boolean_t native_vlan = B_FALSE; + 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. + */ + ASSERT(softmac != NULL); + + if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0) { + mutex_enter(&softmac->smac_mutex); + 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). + */ + dev = makedevice(ddi_name_to_major("clone"), softmac->smac_umajor); + err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li); + if (err == 0) { + if (dl_attach(lh, softmac->smac_uppa + 1 * 1000, NULL) == 0) + native_vlan = B_TRUE; + (void) ldi_close(lh, FREAD|FWRITE, kcred); + } + + err = EINVAL; + for (index = 0; index < 2; index++) { + dl_info_ack_t dlia; + dl_error_ack_t dlea; + uint32_t notes; + struct strioctl iocb; + uint32_t margin; + int rval; + + if ((softmac_dev = softmac->smac_softmac[index]) == NULL) + continue; + + softmac->smac_dev = dev = softmac_dev->sd_dev; + if (ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, + li) != 0) { + continue; + } + + /* + * Pop all the intermediate modules in order to negotiate + * capabilities correctly. + */ + while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0) + ; + + /* DLPI style-1 or DLPI style-2? */ + if ((rval = dl_info(lh, &dlia, NULL, NULL, &dlea)) != 0) { + if (rval == ENOTSUP) { + cmn_err(CE_NOTE, "softmac: received " + "DL_ERROR_ACK to DL_INFO_ACK; " + "DLPI errno 0x%x, UNIX errno %d", + dlea.dl_errno, dlea.dl_unix_errno); + } + (void) ldi_close(lh, FREAD|FWRITE, kcred); + continue; + } + + /* + * Currently only DL_ETHER has GLDv3 mac plugin support. + * For media types that GLDv3 does not support, create a + * link id for it. + */ + if ((softmac->smac_media = dlia.dl_mac_type) != DL_ETHER) { + (void) ldi_close(lh, FREAD|FWRITE, kcred); + err = 0; + break; + } + + if ((dlia.dl_provider_style == DL_STYLE2) && + (dl_attach(lh, softmac->smac_uppa, NULL) != 0)) { + (void) ldi_close(lh, FREAD|FWRITE, kcred); + continue; + } + + if ((rval = dl_bind(lh, 0, NULL)) != 0) { + if (rval == ENOTSUP) { + cmn_err(CE_NOTE, "softmac: received " + "DL_ERROR_ACK to DL_BIND_ACK; " + "DLPI errno 0x%x, UNIX errno %d", + dlea.dl_errno, dlea.dl_unix_errno); + } + (void) ldi_close(lh, FREAD|FWRITE, kcred); + continue; + } + + /* + * Call dl_info() after dl_bind() because some drivers only + * provide correct information (e.g. MAC address) once bound. + */ + softmac->smac_addrlen = sizeof (softmac->smac_unicst_addr); + if ((rval = dl_info(lh, &dlia, softmac->smac_unicst_addr, + &softmac->smac_addrlen, &dlea)) != 0) { + if (rval == ENOTSUP) { + cmn_err(CE_NOTE, "softmac: received " + "DL_ERROR_ACK to DL_INFO_ACK; " + "DLPI errno 0x%x, UNIX errno %d", + dlea.dl_errno, dlea.dl_unix_errno); + } + (void) ldi_close(lh, FREAD|FWRITE, kcred); + continue; + } + + softmac->smac_style = dlia.dl_provider_style; + softmac->smac_saplen = ABS(dlia.dl_sap_length); + softmac->smac_min_sdu = dlia.dl_min_sdu; + softmac->smac_max_sdu = dlia.dl_max_sdu; + + if ((softmac->smac_saplen != sizeof (uint16_t)) || + (softmac->smac_addrlen != ETHERADDRL) || + (dlia.dl_brdcst_addr_length != ETHERADDRL) || + (dlia.dl_brdcst_addr_offset == 0)) { + (void) ldi_close(lh, FREAD|FWRITE, kcred); + 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. + * + * Softmac always supports POLL. + */ + softmac->smac_capab_flags = + (MAC_CAPAB_POLL | MAC_CAPAB_NO_ZCOPY | MAC_CAPAB_LEGACY); + + softmac->smac_no_capability_req = B_FALSE; + if (softmac_fill_capab(lh, softmac) != 0) + softmac->smac_no_capability_req = B_TRUE; + + /* + * Check the margin of the underlying driver. + */ + margin = 0; + iocb.ic_cmd = DLIOCMARGININFO; + iocb.ic_timout = INFTIM; + iocb.ic_len = sizeof (margin); + iocb.ic_dp = (char *)&margin; + softmac->smac_margin = 0; + + if (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred, + &rval) == 0) { + softmac->smac_margin = margin; + } + + /* + * 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) + softmac->smac_margin = VLAN_TAGSZ; + } else { + softmac->smac_capab_flags |= MAC_CAPAB_NO_NATIVEVLAN; + } + + /* + * Not all drivers support DL_NOTIFY_REQ, so ignore ENOTSUP. + */ + softmac->smac_notifications = 0; + notes = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN; + switch (dl_notify(lh, ¬es, NULL)) { + case 0: + softmac->smac_notifications = notes; + break; + case ENOTSUP: + break; + default: + (void) ldi_close(lh, FREAD|FWRITE, kcred); + continue; + } + + (void) ldi_close(lh, FREAD|FWRITE, kcred); + err = 0; + break; + } + ldi_ident_release(li); + + mutex_enter(&softmac->smac_mutex); + + if (err != 0) + goto done; + + if (softmac->smac_media != DL_ETHER) + softmac->smac_flags |= SOFTMAC_NOSUPP; + + /* + * Finally, we're ready to register ourselves with the MAC layer + * interface; if this succeeds, we're all ready to start() + */ + if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) { + mac_register_t *macp; + + if ((macp = mac_alloc(MAC_VERSION)) == NULL) { + err = ENOMEM; + goto done; + } + + macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER; + macp->m_driver = softmac; + macp->m_dip = softmac_dip; + + macp->m_margin = softmac->smac_margin; + macp->m_src_addr = softmac->smac_unicst_addr; + macp->m_min_sdu = softmac->smac_min_sdu; + macp->m_max_sdu = softmac->smac_max_sdu; + macp->m_callbacks = &softmac_m_callbacks; + macp->m_instance = (uint_t)-1; + + err = mac_register(macp, &softmac->smac_mh); + mac_free(macp); + if (err != 0) { + cmn_err(CE_WARN, "mac_register failed for %s", + softmac->smac_devname); + goto done; + } + } + + /* + * Try to create the datalink for this softmac. + */ + if ((err = softmac_create_datalink(softmac)) != 0) { + if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) { + (void) mac_unregister(softmac->smac_mh); + softmac->smac_mh = NULL; + } + } + +done: + ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)); + softmac->smac_flags |= SOFTMAC_ATTACH_DONE; + softmac->smac_attacherr = err; + softmac->smac_taskq = NULL; + cv_broadcast(&softmac->smac_cv); + mutex_exit(&softmac->smac_mutex); +} + +int +softmac_destroy(dev_info_t *dip, dev_t dev) +{ + char devname[MAXNAMELEN]; + softmac_t *softmac; + softmac_dev_t *softmac_dev; + int index; + int ppa, err; + datalink_id_t linkid; + + ppa = ddi_get_instance(dip); + (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa); + + rw_enter(&softmac_hash_lock, RW_WRITER); + err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname, + (mod_hash_val_t *)&softmac); + ASSERT(err == 0); + + mutex_enter(&softmac->smac_mutex); + + /* + * Fail the predetach routine if this softmac is in-use. + */ + if (softmac->smac_hold_cnt != 0) { + softmac->smac_attached_left = softmac->smac_attachok_cnt; + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + 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) { + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + return (EBUSY); + } + + if (softmac->smac_attachok_cnt != softmac->smac_cnt) + goto done; + + /* + * This is the detach for the first minor node. Wait until all the + * minor nodes are attached. + */ + while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)) + cv_wait(&softmac->smac_cv, &softmac->smac_mutex); + + if (softmac->smac_mh != NULL) { + if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) { + if ((err = dls_devnet_destroy(softmac->smac_mh, + &linkid)) != 0) { + goto done; + } + } + /* + * If softmac_mac_register() succeeds in registering the mac + * of the legacy device, unregister it. + */ + if (!(softmac->smac_flags & (SOFTMAC_GLDV3 | SOFTMAC_NOSUPP))) { + if ((err = mac_unregister(softmac->smac_mh)) != 0) { + (void) dls_devnet_create(softmac->smac_mh, + linkid); + goto done; + } + } + softmac->smac_mh = NULL; + } + softmac->smac_flags &= ~SOFTMAC_ATTACH_DONE; + +done: + if (err == 0) { + /* + * Free softmac_dev + */ + index = (getmajor(dev) == ddi_name_to_major("clone")); + softmac_dev = softmac->smac_softmac[index]; + ASSERT(softmac_dev != NULL); + softmac->smac_softmac[index] = NULL; + kmem_free(softmac_dev, sizeof (softmac_dev_t)); + + if (--softmac->smac_attachok_cnt == 0) { + mod_hash_val_t hashval; + + err = mod_hash_remove(softmac_hash, + (mod_hash_key_t)devname, + (mod_hash_val_t *)&hashval); + ASSERT(err == 0); + + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + + ASSERT(softmac->smac_taskq == NULL); + ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)); + mutex_destroy(&softmac->smac_mutex); + cv_destroy(&softmac->smac_cv); + rw_destroy(&softmac->smac_lock); + kmem_free(softmac, sizeof (softmac_t)); + return (0); + } + } else { + softmac->smac_attached_left = softmac->smac_attachok_cnt; + } + + mutex_exit(&softmac->smac_mutex); + rw_exit(&softmac_hash_lock); + 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 +softmac_mac_recreate(mod_hash_key_t key, mod_hash_val_t *val, void *arg) +{ + softmac_t *softmac = (softmac_t *)val; + datalink_id_t linkid; + int err; + + ASSERT(RW_READ_HELD(&softmac_hash_lock)); + + /* + * Wait for softmac_create() and softmac_mac_register() to exit. + */ + mutex_enter(&softmac->smac_mutex); + while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)) + cv_wait(&softmac->smac_cv, &softmac->smac_mutex); + + if ((softmac->smac_attacherr != 0) || + !(softmac->smac_flags & SOFTMAC_NEED_RECREATE)) { + mutex_exit(&softmac->smac_mutex); + return (MH_WALK_CONTINUE); + } + + if (dls_mgmt_create(softmac->smac_devname, + makedevice(softmac->smac_umajor, softmac->smac_uppa + 1), + DATALINK_CLASS_PHYS, softmac->smac_media, B_TRUE, &linkid) != 0) { + mutex_exit(&softmac->smac_mutex); + return (MH_WALK_CONTINUE); + } + + if ((err = softmac_update_info(softmac, &linkid)) != 0) { + cmn_err(CE_WARN, "softmac: softmac_update_info() for %s " + "failed (%d)", softmac->smac_devname, err); + mutex_exit(&softmac->smac_mutex); + return (MH_WALK_CONTINUE); + } + + /* + * Create a link for this MAC. The link name will be the same + * as the MAC name. + */ + if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) { + err = dls_devnet_recreate(softmac->smac_mh, linkid); + if (err != 0) { + cmn_err(CE_WARN, "softmac: dls_devnet_recreate() for " + "%s (linkid %d) failed (%d)", + softmac->smac_devname, linkid, err); + } + } + + softmac->smac_flags &= ~SOFTMAC_NEED_RECREATE; + mutex_exit(&softmac->smac_mutex); + + return (MH_WALK_CONTINUE); +} + +/* + * See comments above softmac_mac_recreate(). + */ +void +softmac_recreate() +{ + /* + * Walk through the softmac_hash table. Request to create the + * [link name, linkid] mapping if we failed to do so. + */ + rw_enter(&softmac_hash_lock, RW_READER); + mod_hash_walk(softmac_hash, softmac_mac_recreate, NULL); + rw_exit(&softmac_hash_lock); +} + +/* ARGSUSED */ +static int +softmac_m_start(void *arg) +{ + return (0); +} + +/* ARGSUSED */ +static void +softmac_m_stop(void *arg) +{ +} + +/* + * Set up the lower stream above the legacy device which is shared by + * GLDv3 MAC clients. Put the lower stream into 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. + */ +static int +softmac_lower_setup(softmac_t *softmac, softmac_lower_t **slpp) +{ + ldi_ident_t li; + dev_t dev; + ldi_handle_t lh = NULL; + softmac_lower_t *slp = NULL; + smac_ioc_start_t start_arg; + struct strioctl strioc; + uint32_t notifications; + int err, rval; + + if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0) + return (err); + + dev = softmac->smac_dev; + err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li); + ldi_ident_release(li); + if (err != 0) + goto done; + + /* + * Pop all the intermediate modules. The autopushed modules will + * be pushed when the softmac node is opened. + */ + while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0) + ; + + if ((softmac->smac_style == DL_STYLE2) && + ((err = dl_attach(lh, softmac->smac_uppa, NULL)) != 0)) { + goto done; + } + + /* + * Put the lower stream into DLIOCRAW mode to send/receive raw data. + */ + if ((err = ldi_ioctl(lh, DLIOCRAW, 0, FKIOCTL, kcred, &rval)) != 0) + goto done; + + /* + * Then push the softmac shim layer atop the lower stream. + */ + if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)SOFTMAC_DEV_NAME, FKIOCTL, + kcred, &rval)) != 0) { + goto done; + } + + /* + * Send the ioctl to get the slp pointer. + */ + strioc.ic_cmd = SMAC_IOC_START; + strioc.ic_timout = INFTIM; + strioc.ic_len = sizeof (start_arg); + strioc.ic_dp = (char *)&start_arg; + + if ((err = ldi_ioctl(lh, I_STR, (intptr_t)&strioc, FKIOCTL, + kcred, &rval)) != 0) { + goto done; + } + slp = start_arg.si_slp; + slp->sl_lh = lh; + slp->sl_softmac = softmac; + *slpp = slp; + + /* + * 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 (softmac->smac_media == DL_TPR) + err = softmac_send_bind_req(slp, 2); + else + err = softmac_send_bind_req(slp, 0); + if (err != 0) + goto done; + + /* + * Put the lower stream into DL_PROMISC_SAP mode to receive all + * packets of interest. + * + * Some drivers (e.g. the old legacy eri driver) incorrectly pass up + * packets to DL_PROMISC_SAP stream when the lower stream is not bound, + * so we send DL_PROMISON_REQ after DL_BIND_REQ. + */ + if ((err = softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_TRUE)) != 0) + goto done; + + /* + * Enable the capabilities the underlying driver claims to support. + * Some drivers require this to be called after the stream is bound. + */ + if ((err = softmac_capab_enable(slp)) != 0) + goto done; + + /* + * Send the DL_NOTIFY_REQ to enable certain DL_NOTIFY_IND. + * We don't have to wait for the ack. + */ + notifications = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP | + DL_NOTE_LINK_DOWN | DL_NOTE_PROMISC_ON_PHYS | + DL_NOTE_PROMISC_OFF_PHYS; + + (void) softmac_send_notify_req(slp, + (notifications & softmac->smac_notifications)); + +done: + if (err != 0) + (void) ldi_close(lh, FREAD|FWRITE, kcred); + return (err); +} + +static int +softmac_m_open(void *arg) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp; + int err; + + rw_enter(&softmac->smac_lock, RW_READER); + if (softmac->smac_state == SOFTMAC_READY) + goto done; + rw_exit(&softmac->smac_lock); + + if ((err = softmac_lower_setup(softmac, &slp)) != 0) + return (err); + + rw_enter(&softmac->smac_lock, RW_WRITER); + ASSERT(softmac->smac_state == SOFTMAC_INITIALIZED); + softmac->smac_lower = slp; + softmac->smac_state = SOFTMAC_READY; +done: + rw_exit(&softmac->smac_lock); + return (0); +} + +static void +softmac_m_close(void *arg) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp; + + rw_enter(&softmac->smac_lock, RW_WRITER); + slp = softmac->smac_lower; + ASSERT(slp != NULL); + + /* + * Note that slp is destroyed when lh is closed. + */ + (void) ldi_close(slp->sl_lh, FREAD|FWRITE, kcred); + softmac->smac_state = SOFTMAC_INITIALIZED; + softmac->smac_lower = NULL; + rw_exit(&softmac->smac_lock); +} + +int +softmac_hold_device(dev_t dev, dls_dev_handle_t *ddhp) +{ + dev_info_t *dip; + char devname[MAXNAMELEN]; + softmac_t *softmac; + int ppa, err; + + if ((ppa = getminor(dev) - 1) > 1000) + return (ENOENT); + + /* + * First try to hold this device instance to force the MAC + * to be registered. + */ + if ((dip = ddi_hold_devi_by_instance(getmajor(dev), ppa, 0)) == NULL) + return (ENOENT); + + if ((ddi_driver_major(dip) != getmajor(dev)) || + !NETWORK_DRV(getmajor(dev))) { + ddi_release_devi(dip); + return (ENOENT); + } + + /* + * This is a network device; wait for its softmac to be registered. + */ + (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa); +again: + rw_enter(&softmac_hash_lock, RW_READER); + + if (mod_hash_find(softmac_hash, (mod_hash_key_t)devname, + (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). + */ + rw_exit(&softmac_hash_lock); + goto again; + } + + /* + * Bump smac_hold_cnt to prevent device detach. + */ + mutex_enter(&softmac->smac_mutex); + softmac->smac_hold_cnt++; + mutex_exit(&softmac->smac_mutex); + + rw_exit(&softmac_hash_lock); + + /* + * Wait till the device is fully attached. + */ + mutex_enter(&softmac->smac_mutex); + while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE)) + cv_wait(&softmac->smac_cv, &softmac->smac_mutex); + + if ((err = softmac->smac_attacherr) == 0) { + /* + * If softmac is successfully attached, set smac_udip + * which is used in softmac_rele_device(). + */ + ASSERT(softmac->smac_udip == NULL || + softmac->smac_udip == dip); + softmac->smac_udip = dip; + *ddhp = (dls_dev_handle_t)softmac; + } + mutex_exit(&softmac->smac_mutex); + + if (err != 0) + softmac_rele_device((dls_dev_handle_t)softmac); + + return (err); +} + +void +softmac_rele_device(dls_dev_handle_t ddh) +{ + softmac_t *softmac; + dev_info_t *dip; + + if (ddh == NULL) + return; + + softmac = (softmac_t *)ddh; + mutex_enter(&softmac->smac_mutex); + dip = softmac->smac_udip; + if (--softmac->smac_hold_cnt == 0) + softmac->smac_udip = NULL; + mutex_exit(&softmac->smac_mutex); + + ddi_release_devi(dip); +} diff --git a/usr/src/uts/common/io/softmac/softmac_pkt.c b/usr/src/uts/common/io/softmac/softmac_pkt.c new file mode 100644 index 0000000000..8848dc755a --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_pkt.c @@ -0,0 +1,320 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/strsubr.h> +#include <inet/led.h> +#include <sys/softmac_impl.h> + +/* + * Macro to check whether the write-queue of the lower stream is full. + * + * Because softmac is pushed right above the underlying device and + * _I_INSERT/_I_REMOVE is not processed in the lower stream, it is + * safe to directly access the q_next pointer. + */ +#define CANPUTNEXT(q) \ + (!((q)->q_next->q_nfsrv->q_flag & QFULL) || canput((q)->q_next)) + +mblk_t * +softmac_m_tx(void *arg, mblk_t *mp) +{ + queue_t *wq = ((softmac_t *)arg)->smac_lower->sl_wq; + + /* + * Optimize for the most common case. + */ + if (mp->b_cont == NULL) { + if (!CANPUTNEXT(wq)) + return (mp); + + mp->b_flag |= MSGNOLOOP; + putnext(wq, mp); + return (NULL); + } + + while (mp != NULL) { + mblk_t *next = mp->b_next; + + if (!CANPUTNEXT(wq)) + break; + mp->b_next = NULL; + mp->b_flag |= MSGNOLOOP; + putnext(wq, mp); + mp = next; + } + return (mp); +} + +/*ARGSUSED*/ +static void +softmac_blank(void *arg, time_t ticks, uint_t count) +{ +} + +void +softmac_m_resources(void *arg) +{ + softmac_t *softmac = arg; + softmac_lower_t *slp = softmac->smac_lower; + mac_rx_fifo_t mrf; + + ASSERT((softmac->smac_state == SOFTMAC_READY) && (slp != NULL)); + + /* + * Register rx resources and save resource handle for future reference. + * Note that the mac_resources() function must be called when the lower + * stream is plumbed. + */ + + mutex_enter(&slp->sl_mutex); + + mrf.mrf_type = MAC_RX_FIFO; + mrf.mrf_blank = softmac_blank; + mrf.mrf_arg = slp; + mrf.mrf_normal_blank_time = SOFTMAC_BLANK_TICKS; + mrf.mrf_normal_pkt_count = SOFTMAC_BLANK_PKT_COUNT; + + slp->sl_handle = + mac_resource_add(softmac->smac_mh, (mac_resource_t *)&mrf); + + mutex_exit(&slp->sl_mutex); +} + +void +softmac_rput_process_data(softmac_lower_t *slp, mblk_t *mp) +{ + /* + * When packets arrive, the softmac might not be fully started. + */ + ASSERT((slp->sl_softmac != NULL)); + ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL)); + + if (DB_REF(mp) > 1) { + mblk_t *tmp; + + if ((tmp = copymsg(mp)) == NULL) { + cmn_err(CE_WARN, "softmac_rput_process_data: " + "copymsg failed"); + goto failed; + } + freemsg(mp); + mp = tmp; + } + + mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp); + return; + +failed: + freemsg(mp); +} + +#define ACKTIMEOUT (10 * hz) + +/* + * Serialize control message processing. + */ +static void +softmac_serialize_enter(softmac_lower_t *slp) +{ + mutex_enter(&slp->sl_ctl_mutex); + while (slp->sl_ctl_inprogress) + cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex); + + ASSERT(!slp->sl_ctl_inprogress); + ASSERT(!slp->sl_pending_ioctl); + ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); + + slp->sl_ctl_inprogress = B_TRUE; + mutex_exit(&slp->sl_ctl_mutex); +} + +static void +softmac_serialize_exit(softmac_lower_t *slp) +{ + mutex_enter(&slp->sl_ctl_mutex); + + ASSERT(slp->sl_ctl_inprogress); + ASSERT(!slp->sl_pending_ioctl); + ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); + + slp->sl_ctl_inprogress = B_FALSE; + cv_broadcast(&slp->sl_ctl_cv); + mutex_exit(&slp->sl_ctl_mutex); +} + +static int +dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno) +{ + return (error == DL_SYSERR ? unix_errno : EINVAL); +} + +static int +softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim, + t_uscalar_t ack, mblk_t **mpp) +{ + union DL_primitives *dlp; + int err = 0; + + softmac_serialize_enter(slp); + + /* + * Record the pending DLPI primitive. + */ + mutex_enter(&slp->sl_mutex); + slp->sl_pending_prim = dl_prim; + mutex_exit(&slp->sl_mutex); + + putnext(slp->sl_wq, mp); + + mutex_enter(&slp->sl_mutex); + while (slp->sl_pending_prim != DL_PRIM_INVAL) { + if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex, + lbolt + ACKTIMEOUT) == -1) + break; + } + + mp = slp->sl_ack_mp; + slp->sl_ack_mp = NULL; + + /* + * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim + * won't be set to DL_PRIM_INVAL. + */ + ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL); + + slp->sl_pending_prim = DL_PRIM_INVAL; + mutex_exit(&slp->sl_mutex); + + if (mp != NULL) { + dlp = (union DL_primitives *)mp->b_rptr; + + if (dlp->dl_primitive == DL_ERROR_ACK) { + err = dlpi_get_errno(dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + } else { + ASSERT(dlp->dl_primitive == ack); + } + } else { + err = ENOMSG; + } + + if (mpp != NULL) + *mpp = mp; + else + freemsg(mp); + + softmac_serialize_exit(slp); + return (err); +} + +void +softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) +{ + softmac_serialize_enter(slp); + + /* + * Record that ioctl processing is currently in progress. + */ + mutex_enter(&slp->sl_mutex); + slp->sl_pending_ioctl = B_TRUE; + mutex_exit(&slp->sl_mutex); + + putnext(slp->sl_wq, mp); + + mutex_enter(&slp->sl_mutex); + while (slp->sl_pending_ioctl) + cv_wait(&slp->sl_cv, &slp->sl_mutex); + mp = slp->sl_ack_mp; + slp->sl_ack_mp = NULL; + mutex_exit(&slp->sl_mutex); + + ASSERT(mpp != NULL && mp != NULL); + *mpp = mp; + + softmac_serialize_exit(slp); +} + +static int +softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive, + t_uscalar_t error, t_uscalar_t unix_errno) +{ + union DL_primitives *dlp; + + if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO, + DL_ERROR_ACK)) == NULL) + return (ENOMEM); + + dlp = (union DL_primitives *)(*mpp)->b_rptr; + dlp->error_ack.dl_error_primitive = error_primitive; + dlp->error_ack.dl_errno = error; + dlp->error_ack.dl_unix_errno = unix_errno; + + return (0); +} + +int +softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) +{ + int err = 0; + t_uscalar_t dl_prim; + + dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; + + ASSERT(slp->sl_softmac != NULL); + + switch (dl_prim) { + case DL_ENABMULTI_REQ: + case DL_DISABMULTI_REQ: + case DL_SET_PHYS_ADDR_REQ: + case DL_UNBIND_REQ: + case DL_UDQOS_REQ: + case DL_PROMISCON_REQ: + case DL_PROMISCOFF_REQ: + err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp); + break; + case DL_BIND_REQ: + err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp); + break; + case DL_NOTIFY_REQ: + err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp); + break; + case DL_CONTROL_REQ: + err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp); + break; + case DL_CAPABILITY_REQ: + err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp); + break; + default: + if (mpp != NULL) { + *mpp = mp; + err = softmac_mexchange_error_ack(mpp, dl_prim, + DL_UNSUPPORTED, 0); + } + break; + } + return (err); +} diff --git a/usr/src/uts/common/io/softmac/softmac_stat.c b/usr/src/uts/common/io/softmac/softmac_stat.c new file mode 100644 index 0000000000..78b5306c86 --- /dev/null +++ b/usr/src/uts/common/io/softmac/softmac_stat.c @@ -0,0 +1,270 @@ +/* + * 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 + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/kstat.h> +#include <sys/mac.h> +#include <sys/dls.h> +#include <sys/softmac_impl.h> + +typedef struct i_softmac_stat_info_s { + uint_t ssi_stat; + char *ssi_name; + char *ssi_alias; +} i_softmac_stat_info_t; + +/* + * Must be the same order as mac_driver_stat. + */ +static i_softmac_stat_info_t i_softmac_driver_si[] = { + { MAC_STAT_IFSPEED, "ifspeed", "link_speed" }, + { MAC_STAT_MULTIRCV, "multircv", NULL }, + { MAC_STAT_BRDCSTRCV, "brdcstrcv", NULL }, + { MAC_STAT_MULTIXMT, "multixmt", NULL }, + { MAC_STAT_BRDCSTXMT, "brdcstxmt", NULL }, + { MAC_STAT_NORCVBUF, "norcvbuf", "rx_no_buf" }, + { MAC_STAT_IERRORS, "ierrors", NULL }, + { MAC_STAT_UNKNOWNS, "unknowns", NULL }, + { MAC_STAT_NOXMTBUF, "noxmtbuf", "No Txpkt " }, + { MAC_STAT_OERRORS, "oerrors", NULL }, + { MAC_STAT_COLLISIONS, "collisions", NULL }, + { MAC_STAT_RBYTES, "rbytes64", "rbytes" }, + { MAC_STAT_IPACKETS, "ipackets64", "ipackets" }, + { MAC_STAT_OBYTES, "obytes64", "obytes" }, + { MAC_STAT_OPACKETS, "opackets64", "opackets" }, + { MAC_STAT_UNDERFLOWS, "uflo", NULL }, + { MAC_STAT_OVERFLOWS, "oflo", NULL } +}; + +/* + * Must be the same order as ether_stat. + */ +static i_softmac_stat_info_t i_softmac_ether_si[] = { + { ETHER_STAT_ALIGN_ERRORS, "align_errors", + "alignment_err" }, + { ETHER_STAT_FCS_ERRORS, "fcs_errors", "crc_err" }, + { ETHER_STAT_FIRST_COLLISIONS, "first_collisions", NULL }, + { ETHER_STAT_MULTI_COLLISIONS, "multi_collisions", NULL }, + { ETHER_STAT_SQE_ERRORS, "sqe_errors", NULL }, + { ETHER_STAT_DEFER_XMTS, "defer_xmts", NULL }, + { ETHER_STAT_TX_LATE_COLLISIONS, "tx_late_collisions", + "late_collisions" }, + { ETHER_STAT_EX_COLLISIONS, "ex_collisions", + "excessive_collisions" }, + { ETHER_STAT_MACXMT_ERRORS, "macxmt_errors", NULL }, + { ETHER_STAT_CARRIER_ERRORS, "carrier_errors", NULL }, + { ETHER_STAT_TOOLONG_ERRORS, "toolong_errors", "length_err" }, + { ETHER_STAT_MACRCV_ERRORS, "macrcv_errors", + "Rx Error Count" }, + + { ETHER_STAT_XCVR_ADDR, "xcvr_addr", NULL }, + { ETHER_STAT_XCVR_ID, "xcvr_id", NULL }, + { ETHER_STAT_XCVR_INUSE, "xcvr_inuse", NULL }, + + { ETHER_STAT_CAP_1000FDX, "cap_1000fdx", NULL }, + { ETHER_STAT_CAP_1000HDX, "cap_1000hdx", NULL }, + { ETHER_STAT_CAP_100FDX, "cap_100fdx", NULL }, + { ETHER_STAT_CAP_100HDX, "cap_100hdx", NULL }, + { ETHER_STAT_CAP_10FDX, "cap_10fdx", NULL }, + { ETHER_STAT_CAP_10HDX, "cap_10hdx", NULL }, + { ETHER_STAT_CAP_ASMPAUSE, "cap_asmpause", NULL }, + { ETHER_STAT_CAP_PAUSE, "cap_pause", NULL }, + { ETHER_STAT_CAP_AUTONEG, "cap_autoneg", NULL }, + + { ETHER_STAT_ADV_CAP_1000FDX, "adv_cap_1000fdx", NULL }, + { ETHER_STAT_ADV_CAP_1000HDX, "adv_cap_1000hdx", NULL }, + { ETHER_STAT_ADV_CAP_100FDX, "adv_cap_100fdx", NULL }, + { ETHER_STAT_ADV_CAP_100HDX, "adv_cap_100hdx", NULL }, + { ETHER_STAT_ADV_CAP_10FDX, "adv_cap_10fdx", NULL }, + { ETHER_STAT_ADV_CAP_10HDX, "adv_cap_10hdx", NULL }, + { ETHER_STAT_ADV_CAP_ASMPAUSE, "adv_cap_asmpause", NULL }, + { ETHER_STAT_ADV_CAP_PAUSE, "adv_cap_pause", NULL }, + { ETHER_STAT_ADV_CAP_AUTONEG, "adv_cap_autoneg", NULL }, + + { ETHER_STAT_LP_CAP_1000FDX, "lp_cap_1000fdx", NULL }, + { ETHER_STAT_LP_CAP_1000HDX, "lp_cap_1000hdx", NULL }, + { ETHER_STAT_LP_CAP_100FDX, "lp_cap_100fdx", NULL }, + { ETHER_STAT_LP_CAP_100HDX, "lp_cap_100hdx", NULL }, + { ETHER_STAT_LP_CAP_10FDX, "lp_cap_10fdx", NULL }, + { ETHER_STAT_LP_CAP_10HDX, "lp_cap_10hdx", NULL }, + { ETHER_STAT_LP_CAP_ASMPAUSE, "lp_cap_asmpause", NULL }, + { ETHER_STAT_LP_CAP_PAUSE, "lp_cap_pause", NULL }, + { ETHER_STAT_LP_CAP_AUTONEG, "lp_cap_autoneg", NULL }, + + { ETHER_STAT_LINK_ASMPAUSE, "link_asmpause", NULL }, + { ETHER_STAT_LINK_PAUSE, "link_pause", NULL }, + { ETHER_STAT_LINK_AUTONEG, "link_autoneg", NULL }, + { ETHER_STAT_LINK_DUPLEX, "link_duplex", "duplex" }, + + { ETHER_STAT_TOOSHORT_ERRORS, "runt_errors", NULL }, + { ETHER_STAT_CAP_REMFAULT, "cap_rem_fault", NULL }, + { ETHER_STAT_ADV_REMFAULT, "adv_rem_fault", NULL }, + { ETHER_STAT_LP_REMFAULT, "lp_rem_fault", NULL }, + + { ETHER_STAT_JABBER_ERRORS, "jabber_errors", NULL }, + { ETHER_STAT_CAP_100T4, "cap_100T4", NULL }, + { ETHER_STAT_ADV_CAP_100T4, "adv_cap_100T4", NULL }, + { ETHER_STAT_LP_CAP_100T4, "lp_cap_100T4", NULL } +}; + +static kstat_t *softmac_hold_dev_kstat(softmac_t *); +static void softmac_rele_dev_kstat(kstat_t *); +static int softmac_get_kstat(kstat_t *, char *, uint64_t *); + +static kstat_t * +softmac_hold_dev_kstat(softmac_t *softmac) +{ + char drv[MAXLINKNAMELEN]; + uint_t ppa; + kstat_t *ksp; + + if (ddi_parse(softmac->smac_devname, drv, &ppa) != DDI_SUCCESS) + return (NULL); + + /* + * Find the kstat by the module name and the instance number. + */ + ksp = kstat_hold_byname(drv, ppa, softmac->smac_devname, ALL_ZONES); + if (ksp != NULL) { + KSTAT_ENTER(ksp); + + if ((ksp->ks_data != NULL) && + (ksp->ks_type == KSTAT_TYPE_NAMED)) { + /* + * Update the kstat to get the latest statistics. + */ + if (KSTAT_UPDATE(ksp, KSTAT_READ) == 0) + return (ksp); + } + + KSTAT_EXIT(ksp); + kstat_rele(ksp); + } + return (NULL); +} + +static void +softmac_rele_dev_kstat(kstat_t *ksp) +{ + KSTAT_EXIT(ksp); + kstat_rele(ksp); +} + +/* + * The kstat needs to be held when calling this function. + */ +static int +softmac_get_kstat(kstat_t *ksp, char *name, uint64_t *valp) +{ + kstat_named_t *knp; + int i; + int ret = ENOTSUP; + + if (name == NULL) + return (ret); + + /* + * Search the kstat with the given name. + */ + for (i = 0, knp = KSTAT_NAMED_PTR(ksp); i < ksp->ks_ndata; i++, knp++) { + if (strcmp(knp->name, name) == 0) { + switch (knp->data_type) { + case KSTAT_DATA_INT32: + case KSTAT_DATA_UINT32: + *valp = (uint64_t)(knp->value.ui32); + ret = 0; + break; + case KSTAT_DATA_INT64: + case KSTAT_DATA_UINT64: + *valp = knp->value.ui64; + ret = 0; + break; +#ifdef _LP64 + case KSTAT_DATA_LONG: + case KSTAT_DATA_ULONG: + *valp = (uint64_t)knp->value.ul; + ret = 0; + break; +#endif + case KSTAT_DATA_CHAR: + if (strcmp(name, "duplex") != 0) + break; + if (strncmp(knp->value.c, "full", 4) == 0) + *valp = LINK_DUPLEX_FULL; + else if (strncmp(knp->value.c, "half", 4) == 0) + *valp = LINK_DUPLEX_HALF; + else + *valp = LINK_DUPLEX_UNKNOWN; + ret = 0; + break; + } + break; + } + } + + return (ret); +} + +int +softmac_m_stat(void *arg, uint_t stat, uint64_t *val) +{ + softmac_t *softmac = arg; + kstat_t *ksp; + uint_t index; + int ret; + + if ((ksp = softmac_hold_dev_kstat(softmac)) == NULL) + return (ENOTSUP); + + if (IS_MAC_STAT(stat)) { + index = stat - MAC_STAT_MIN; + if ((ret = softmac_get_kstat(ksp, + i_softmac_driver_si[index].ssi_name, val)) != 0) { + ret = softmac_get_kstat(ksp, + i_softmac_driver_si[index].ssi_alias, val); + } + } else { + ASSERT(IS_MACTYPE_STAT(stat)); + index = stat - MACTYPE_STAT_MIN; + + switch (softmac->smac_media) { + case DL_ETHER: + if ((ret = softmac_get_kstat(ksp, + i_softmac_ether_si[index].ssi_name, val)) != 0) { + ret = softmac_get_kstat(ksp, + i_softmac_ether_si[index].ssi_alias, val); + } + break; + default: + ret = ENOTSUP; + break; + } + } + + softmac_rele_dev_kstat(ksp); + return (ret); +} |
