static void i_xnbo_close_mac(xnb_t *xnbp, boolean_t locked) { xnbo_t *xnbop = xnbp->xnb_flavour_data; xmca_t *loop; ASSERT(!locked || MUTEX_HELD(&xnbp->xnb_state_lock)); if (xnbop->o_mh == NULL) return; if (xnbop->o_running) xnbop->o_running = B_FALSE; if (!locked) mutex_enter(&xnbp->xnb_state_lock); loop = xnbop->o_mca; xnbop->o_mca = NULL; if (!locked) mutex_exit(&xnbp->xnb_state_lock); while (loop != NULL) { xmca_t *next = loop->next; DTRACE_PROBE3(mcast_remove, (char *), "close", (void *), xnbp, (etheraddr_t *), loop->addr); (void) mac_multicast_remove(xnbop->o_mch, loop->addr); kmem_free(loop, sizeof (*loop)); loop = next; } if (xnbop->o_promiscuous) { if (xnbop->o_mphp != NULL) { mac_promisc_remove(xnbop->o_mphp); xnbop->o_mphp = NULL; } xnbop->o_promiscuous = B_FALSE; } else { if (xnbop->o_mch != NULL) mac_rx_clear(xnbop->o_mch); } if (xnbop->o_mah != NULL) { (void) mac_unicast_remove(xnbop->o_mch, xnbop->o_mah); xnbop->o_mah = NULL; } if (xnbop->o_mch != NULL) { mac_client_close(xnbop->o_mch, 0); xnbop->o_mch = NULL; } mac_close(xnbop->o_mh); xnbop->o_mh = NULL; }
/* * Close a MAC client for a port or an interface. */ static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type) { mac_client_handle_t *mchp; ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT)); mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch; if (*mchp != NULL) { mac_client_close(*mchp, 0); *mchp = NULL; } }
/* * 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); }
/* ARGSUSED */ static int sdpfp_close(sock_lower_handle_t handle, int flag, struct cred *cr) { struct pfpsock *ps = (struct pfpsock *)handle; if (ps->ps_phd != 0) { mac_promisc_remove(ps->ps_phd); ps->ps_phd = 0; } if (ps->ps_mch != 0) { mac_client_close(ps->ps_mch, 0); ps->ps_mch = 0; } if (ps->ps_mh != 0) { mac_close(ps->ps_mh); ps->ps_mh = 0; } kmem_free(ps, sizeof (*ps)); return (0); }
static void vnic_cleanup_secondary_macs(vnic_t *vn, int cnt) { int i; /* Remove existing secondaries (primary is at 0) */ for (i = 1; i <= cnt; i++) { mac_rx_clear(vn->vn_mc_handles[i]); /* unicast handle might not have been set yet */ if (vn->vn_mu_handles[i] != NULL) (void) mac_unicast_remove(vn->vn_mc_handles[i], vn->vn_mu_handles[i]); mac_secondary_cleanup(vn->vn_mc_handles[i]); mac_client_close(vn->vn_mc_handles[i], MAC_CLOSE_FLAGS_IS_VNIC); vn->vn_mu_handles[i] = NULL; vn->vn_mc_handles[i] = NULL; } vn->vn_nhandles = 0; }
static void pfp_close(mac_handle_t mh, mac_client_handle_t mch) { mac_client_close(mch, 0); mac_close(mh); }
/* ARGSUSED */ int vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp) { vnic_t *vnic = NULL; mod_hash_val_t val; datalink_id_t tmpid; int rc; rw_enter(&vnic_lock, RW_WRITER); if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t *)&vnic) != 0) { rw_exit(&vnic_lock); return (ENOENT); } if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) { rw_exit(&vnic_lock); return (rc); } ASSERT(vnic_id == tmpid); /* * We cannot unregister the MAC yet. Unregistering would * free up mac_impl_t which should not happen at this time. * So disable mac_impl_t by calling mac_disable(). This will prevent * any new claims on mac_impl_t. */ if ((rc = mac_disable(vnic->vn_mh)) != 0) { (void) dls_devnet_create(vnic->vn_mh, vnic_id, crgetzoneid(credp)); rw_exit(&vnic_lock); return (rc); } vnic_cleanup_secondary_macs(vnic, vnic->vn_nhandles); vnic->vn_enabled = B_FALSE; (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val); ASSERT(vnic == (vnic_t *)val); vnic_count--; rw_exit(&vnic_lock); /* * XXX-nicolas shouldn't have a void cast here, if it's * expected that the function will never fail, then we should * have an ASSERT(). */ (void) mac_unregister(vnic->vn_mh); if (vnic->vn_lower_mh != NULL) { /* * Check if MAC address for the vnic was obtained from the * factory MAC addresses. If yes, release it. */ if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) { (void) mac_addr_factory_release(vnic->vn_mch, vnic->vn_slot_id); } (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin); (void) mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu); (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); mac_close(vnic->vn_lower_mh); } kmem_cache_free(vnic_cache, vnic); return (0); }
/* ARGSUSED */ int vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr, int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid, int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag, cred_t *credp) { vnic_t *vnic; mac_register_t *mac; int err; boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0); char vnic_name[MAXNAMELEN]; const mac_info_t *minfop; uint32_t req_hwgrp_flag = B_FALSE; *diag = VNIC_IOC_DIAG_NONE; rw_enter(&vnic_lock, RW_WRITER); /* does a VNIC with the same id already exist? */ err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t *)&vnic); if (err == 0) { rw_exit(&vnic_lock); return (EEXIST); } vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP); if (vnic == NULL) { rw_exit(&vnic_lock); return (ENOMEM); } bzero(vnic, sizeof (*vnic)); vnic->vn_id = vnic_id; vnic->vn_link_id = linkid; vnic->vn_vrid = vrid; vnic->vn_af = af; if (!is_anchor) { if (linkid == DATALINK_INVALID_LINKID) { err = EINVAL; goto bail; } /* * Open the lower MAC and assign its initial bandwidth and * MAC address. We do this here during VNIC creation and * do not wait until the upper MAC client open so that we * can validate the VNIC creation parameters (bandwidth, * MAC address, etc) and reserve a factory MAC address if * one was requested. */ err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh); if (err != 0) goto bail; /* * VNIC(vlan) over VNICs(vlans) is not supported. */ if (mac_is_vnic(vnic->vn_lower_mh)) { err = EINVAL; goto bail; } /* only ethernet support for now */ minfop = mac_info(vnic->vn_lower_mh); if (minfop->mi_nativemedia != DL_ETHER) { err = ENOTSUP; goto bail; } (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL, NULL); err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch, vnic_name, MAC_OPEN_FLAGS_IS_VNIC); if (err != 0) goto bail; /* assign a MAC address to the VNIC */ err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot, mac_prefix_len, mac_len, mac_addr, flags, diag, vid, req_hwgrp_flag); if (err != 0) { vnic->vn_muh = NULL; if (diag != NULL && req_hwgrp_flag) *diag = VNIC_IOC_DIAG_NO_HWRINGS; goto bail; } /* register to receive notification from underlying MAC */ vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb, vnic); *vnic_addr_type = vnic->vn_addr_type; vnic->vn_addr_len = *mac_len; vnic->vn_vid = vid; bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len); if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) vnic->vn_slot_id = *mac_slot; /* * Set the initial VNIC capabilities. If the VNIC is created * over MACs which does not support nactive vlan, disable * VNIC's hardware checksum capability if its VID is not 0, * since the underlying MAC would get the hardware checksum * offset wrong in case of VLAN packets. */ if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_NO_NATIVEVLAN, NULL)) { if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM, &vnic->vn_hcksum_txflags)) vnic->vn_hcksum_txflags = 0; } else { vnic->vn_hcksum_txflags = 0; } } /* register with the MAC module */ if ((mac = mac_alloc(MAC_VERSION)) == NULL) goto bail; mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER; mac->m_driver = vnic; mac->m_dip = vnic_get_dip(); mac->m_instance = (uint_t)-1; mac->m_src_addr = vnic->vn_addr; mac->m_callbacks = &vnic_m_callbacks; if (!is_anchor) { /* * If this is a VNIC based VLAN, then we check for the * margin unless it has been created with the force * flag. If we are configuring a VLAN over an etherstub, * we don't check the margin even if force is not set. */ if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) { if (vid != VLAN_ID_NONE) vnic->vn_force = B_TRUE; /* * As the current margin size of the underlying mac is * used to determine the margin size of the VNIC * itself, request the underlying mac not to change * to a smaller margin size. */ err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_TRUE); ASSERT(err == 0); } else { vnic->vn_margin = VLAN_TAGSZ; err = mac_margin_add(vnic->vn_lower_mh, &vnic->vn_margin, B_FALSE); if (err != 0) { mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID; goto bail; } } mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu, &mac->m_max_sdu); err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE); if (err != 0) { VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); mac_free(mac); if (diag != NULL) *diag = VNIC_IOC_DIAG_MACMTU_INVALID; goto bail; } vnic->vn_mtu = mac->m_max_sdu; } else { vnic->vn_margin = VLAN_TAGSZ; mac->m_min_sdu = 1; mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU; vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU; } mac->m_margin = vnic->vn_margin; err = mac_register(mac, &vnic->vn_mh); mac_free(mac); if (err != 0) { if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } goto bail; } /* Set the VNIC's MAC in the client */ if (!is_anchor) { mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp); if (mrp != NULL) { if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 || (mrp->mrp_mask & MRP_TX_RINGS) != 0) { req_hwgrp_flag = B_TRUE; } err = mac_client_set_resources(vnic->vn_mch, mrp); if (err != 0) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); (void) mac_unregister(vnic->vn_mh); goto bail; } } } err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp)); if (err != 0) { VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); if (!is_anchor) { VERIFY(mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu) == 0); VERIFY(mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); } (void) mac_unregister(vnic->vn_mh); goto bail; } /* add new VNIC to hash table */ err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id), (mod_hash_val_t)vnic); ASSERT(err == 0); vnic_count++; /* * Now that we've enabled this VNIC, we should go through and update the * link state by setting it to our parents. */ vnic->vn_enabled = B_TRUE; if (is_anchor) { mac_link_update(vnic->vn_mh, LINK_STATE_UP); } else { mac_link_update(vnic->vn_mh, mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE)); } rw_exit(&vnic_lock); return (0); bail: rw_exit(&vnic_lock); if (!is_anchor) { if (vnic->vn_mnh != NULL) (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); if (vnic->vn_muh != NULL) (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); if (vnic->vn_mch != NULL) mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); if (vnic->vn_lower_mh != NULL) mac_close(vnic->vn_lower_mh); } kmem_cache_free(vnic_cache, vnic); return (err); }
static void mac_bpf_client_close(uintptr_t chandle) { mac_client_close((mac_client_handle_t)chandle, 0); }