/* * Clients like aggr create pseudo rings (mac_ring_t) and expose them to * their clients. There is a 1-1 mapping pseudo ring and the hardware * ring. ddi interrupt handles are exported from the hardware ring to * the pseudo ring. Thus when the interrupt handle changes, clients of * aggr that are using the handle need to use the new handle and * re-target their interrupts. */ static void mac_pseudo_ring_intr_retarget(mac_impl_t *mip, mac_ring_t *ring, ddi_intr_handle_t ddh) { mac_ring_t *pring; mac_group_t *pgroup; mac_impl_t *pmip; char macname[MAXNAMELEN]; mac_perim_handle_t p_mph; uint64_t saved_gen_num; again: pring = (mac_ring_t *)ring->mr_prh; pgroup = (mac_group_t *)pring->mr_gh; pmip = (mac_impl_t *)pgroup->mrg_mh; saved_gen_num = ring->mr_gen_num; (void) strlcpy(macname, pmip->mi_name, MAXNAMELEN); /* * We need to enter aggr's perimeter. The locking hierarchy * dictates that aggr's perimeter should be entered first * and then the port's perimeter. So drop the port's * perimeter, enter aggr's and then re-enter port's * perimeter. */ i_mac_perim_exit(mip); /* * While we know pmip is the aggr's mip, there is a * possibility that aggr could have unregistered by * the time we exit port's perimeter (mip) and * enter aggr's perimeter (pmip). To avoid that * scenario, enter aggr's perimeter using its name. */ if (mac_perim_enter_by_macname(macname, &p_mph) != 0) return; i_mac_perim_enter(mip); /* * Check if the ring got assigned to another aggregation before * be could enter aggr's and the port's perimeter. When a ring * gets deleted from an aggregation, it calls mac_stop_ring() * which increments the generation number. So checking * generation number will be enough. */ if (ring->mr_gen_num != saved_gen_num && ring->mr_prh != NULL) { i_mac_perim_exit(mip); mac_perim_exit(p_mph); i_mac_perim_enter(mip); goto again; } /* Check if pseudo ring is still present */ if (ring->mr_prh != NULL) { pring->mr_info.mri_intr.mi_ddi_handle = ddh; pring->mr_info.mri_intr.mi_ddi_shared = ring->mr_info.mri_intr.mi_ddi_shared; if (ddh != NULL) mac_ring_intr_retarget(pgroup, pring); } i_mac_perim_exit(mip); mac_perim_exit(p_mph); }
/* * DL_ENABMULTI_REQ */ static void proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp) { dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { dl_err = DL_BADPRIM; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; case ENOSPC: dl_err = DL_TOOMANY; err = 0; break; default: dl_err = DL_SYSERR; break; } if (dsp->ds_dmap == NULL) dls_active_clear(dsp, B_FALSE); goto failed2; } mac_perim_exit(mph); dlokack(q, mp, DL_ENABMULTI_REQ); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); }
/* * DL_UNBIND_REQ */ static void proto_unbind_req(dld_str_t *dsp, mblk_t *mp) { queue_t *q = dsp->ds_wq; t_uscalar_t dl_err; mac_perim_handle_t mph; if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dsp->ds_dlstate != DL_IDLE) { dl_err = DL_OUTSTATE; goto failed; } mutex_enter(&dsp->ds_lock); while (dsp->ds_datathr_cnt != 0) cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock); dsp->ds_dlstate = DL_UNBIND_PENDING; mutex_exit(&dsp->ds_lock); mac_perim_enter_by_mh(dsp->ds_mh, &mph); /* * Unbind the channel to stop packets being received. */ dls_unbind(dsp); /* * Disable polling mode, if it is enabled. */ (void) dld_capab_poll_disable(dsp, NULL); /* * Clear LSO flags. */ dsp->ds_lso = B_FALSE; dsp->ds_lso_max = 0; /* * Clear the receive callback. */ dls_rx_set(dsp, NULL, NULL); dsp->ds_direct = B_FALSE; /* * Set the mode back to the default (unitdata). */ dsp->ds_mode = DLD_UNITDATA; dsp->ds_dlstate = DL_UNBOUND; dls_active_clear(dsp, B_FALSE); mac_perim_exit(mph); dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ); return; failed: dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); }
/* ARGSUSED */ static void vnic_m_propinfo(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, mac_prop_info_handle_t prh) { vnic_t *vn = m_driver; switch (pr_num) { case MAC_PROP_MTU: if (vn->vn_link_id == DATALINK_INVALID_LINKID) { mac_prop_info_set_range_uint32(prh, ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU); } else { uint32_t max; mac_perim_handle_t mph; mac_propval_range_t range; /* * The valid range for a VNIC's MTU is the minimum that * the device supports and the current value of the * device. A VNIC cannot increase the current MTU of the * device. Therefore we need to get the range from the * propinfo endpoint and current mtu from the * traditional property endpoint. */ mac_perim_enter_by_mh(vn->vn_lower_mh, &mph); if (mac_get_prop(vn->vn_lower_mh, MAC_PROP_MTU, "mtu", &max, sizeof (uint32_t)) != 0) { mac_perim_exit(mph); return; } range.mpr_count = 1; if (mac_prop_info(vn->vn_lower_mh, MAC_PROP_MTU, "mtu", NULL, 0, &range, NULL) != 0) { mac_perim_exit(mph); return; } mac_prop_info_set_default_uint32(prh, max); mac_prop_info_set_range_uint32(prh, range.mpr_range_uint32[0].mpur_min, max); mac_perim_exit(mph); } break; } }
static int mac_bpf_getzone(uintptr_t handle, zoneid_t *zip) { mac_perim_handle_t mph; int error; mac_perim_enter_by_mh((mac_handle_t)handle, &mph); error = dls_link_getzid(mac_name((mac_handle_t)handle), zip); mac_perim_exit(mph); return (error); }
/* * pfp_open_index is an internal function used to open a MAC device by * its index. Both a mac_handle_t and mac_client_handle_t are acquired * because some of the interfaces provided by the mac layer require either * only the mac_handle_t or both it and mac_handle_t. * * Whilst inside the kernel we can access data structures supporting any * zone, access to interfaces from non-global zones is restricted to those * interfaces (if any) that are exclusively assigned to a zone. */ static int pfp_open_index(int index, mac_handle_t *mhp, mac_client_handle_t *mcip, cred_t *cred) { mac_client_handle_t mch; zoneid_t ifzoneid; mac_handle_t mh; zoneid_t zoneid; int error; mh = 0; mch = 0; error = mac_open_by_linkid(index, &mh); if (error != 0) goto bad_open; error = mac_client_open(mh, &mch, NULL, MAC_OPEN_FLAGS_USE_DATALINK_NAME); if (error != 0) goto bad_open; zoneid = crgetzoneid(cred); if (zoneid != GLOBAL_ZONEID) { mac_perim_handle_t perim; mac_perim_enter_by_mh(mh, &perim); error = dls_link_getzid(mac_client_name(mch), &ifzoneid); mac_perim_exit(perim); if (error != 0) goto bad_open; if (ifzoneid != zoneid) { error = EACCES; goto bad_open; } } *mcip = mch; *mhp = mh; return (0); bad_open: if (mch != 0) mac_client_close(mch, 0); if (mh != 0) mac_close(mh); return (error); }
static int dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags) { switch (flags) { case DLD_ENABLE: mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data); return (0); case DLD_DISABLE: mac_perim_exit((mac_perim_handle_t)data); return (0); case DLD_QUERY: return (mac_perim_held(dsp->ds_mh)); } return (0); }
/* * DL_SET_PHYS_ADDR_REQ */ static void proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp) { dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { dl_err = DL_BADPRIM; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } /* * If mac-nospoof is enabled and the link is owned by a * non-global zone, changing the mac address is not allowed. */ if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID && mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) { dls_active_clear(dsp, B_FALSE); err = EACCES; goto failed2; } err = mac_unicast_primary_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; default: dl_err = DL_SYSERR; break; } dls_active_clear(dsp, B_FALSE); goto failed2; } mac_perim_exit(mph); dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); }
/* * DL_PROMISCOFF_REQ */ static void proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp) { dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr; int err = 0; t_uscalar_t dl_err; uint32_t promisc_saved; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dsp->ds_dlstate == DL_UNATTACHED || DL_ACK_PENDING(dsp->ds_dlstate)) { dl_err = DL_OUTSTATE; goto failed; } promisc_saved = dsp->ds_promisc; switch (dlp->dl_level) { case DL_PROMISC_SAP: if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_SAP; break; case DL_PROMISC_MULTI: if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_MULTI; break; case DL_PROMISC_PHYS: if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { dl_err = DL_NOTENAB; goto failed; } dsp->ds_promisc &= ~DLS_PROMISC_PHYS; break; default: dl_err = DL_NOTSUPPORTED; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); /* * Adjust channel promiscuity. */ err = dls_promisc(dsp, promisc_saved); if (err != 0) { mac_perim_exit(mph); dl_err = DL_SYSERR; goto failed; } if (dsp->ds_promisc == 0) dls_active_clear(dsp, B_FALSE); mac_perim_exit(mph); dlokack(q, mp, DL_PROMISCOFF_REQ); return; failed: dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); }
/* * DL_BIND_REQ */ static void proto_bind_req(dld_str_t *dsp, mblk_t *mp) { dl_bind_req_t *dlp = (dl_bind_req_t *)mp->b_rptr; int err = 0; uint8_t dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)]; uint_t dlsap_addr_length; t_uscalar_t dl_err; t_scalar_t sap; queue_t *q = dsp->ds_wq; mac_perim_handle_t mph; void *mdip; int32_t intr_cpu; if (MBLKL(mp) < sizeof (dl_bind_req_t)) { dl_err = DL_BADPRIM; goto failed; } if (dlp->dl_xidtest_flg != 0) { dl_err = DL_NOAUTO; goto failed; } if (dlp->dl_service_mode != DL_CLDLS) { dl_err = DL_UNSUPPORTED; goto failed; } if (dsp->ds_dlstate != DL_UNBOUND) { dl_err = DL_OUTSTATE; goto failed; } mac_perim_enter_by_mh(dsp->ds_mh, &mph); if ((err = dls_active_set(dsp)) != 0) { dl_err = DL_SYSERR; goto failed2; } dsp->ds_dlstate = DL_BIND_PENDING; /* * Set the receive callback. */ dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ? dld_str_rx_raw : dld_str_rx_unitdata, dsp); /* * Bind the channel such that it can receive packets. */ sap = dlp->dl_sap; dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") && !check_mod_above(dsp->ds_rq, "arp"); err = dls_bind(dsp, sap); if (err != 0) { switch (err) { case EINVAL: dl_err = DL_BADADDR; err = 0; break; default: dl_err = DL_SYSERR; break; } dsp->ds_dlstate = DL_UNBOUND; dls_active_clear(dsp, B_FALSE); goto failed2; } intr_cpu = mac_client_intr_cpu(dsp->ds_mch); mdip = mac_get_devinfo(dsp->ds_mh); mac_perim_exit(mph); /* * We do this after we get out of the perim to avoid deadlocks * etc. since part of mac_client_retarget_intr is to walk the * device tree in order to find and retarget the interrupts. */ if (intr_cpu != -1) mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu); /* * Copy in MAC address. */ dlsap_addr_length = dsp->ds_mip->mi_addr_length; mac_unicast_primary_get(dsp->ds_mh, dlsap_addr); /* * Copy in the SAP. */ *(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap; dlsap_addr_length += sizeof (uint16_t); dsp->ds_dlstate = DL_IDLE; dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0); return; failed2: mac_perim_exit(mph); failed: dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); }