int dls_vlan_destroy(const char *name) { int err; dls_vlan_t *dvp; dls_link_t *dlp; mod_hash_val_t val; /* * Find the dls_vlan_t in the global hash table. */ rw_enter(&i_dls_vlan_lock, RW_WRITER); err = mod_hash_find(i_dls_vlan_hash, (mod_hash_key_t)name, (mod_hash_val_t *)&dvp); if (err != 0) { err = ENOENT; goto done; } /* * Check to see if it is referenced by any dls_impl_t. */ if (dvp->dv_ref != 0) { err = EBUSY; goto done; } /* * Remove and destroy the hash table entry. */ err = mod_hash_remove(i_dls_vlan_hash, (mod_hash_key_t)name, (mod_hash_val_t *)&val); ASSERT(err == 0); ASSERT(dvp == (dls_vlan_t *)val); ASSERT(i_dls_vlan_count > 0); i_dls_vlan_count--; /* * Save a reference to dv_dlp before freeing the dls_vlan_t back * to the cache. */ dlp = dvp->dv_dlp; kmem_cache_free(i_dls_vlan_cachep, dvp); /* * Release the dls_link_t. This will destroy the dls_link_t and * release the MAC if there are no more dls_vlan_t. */ dls_link_rele(dlp); done: rw_exit(&i_dls_vlan_lock); return (err); }
/* * Find the given vlan id in the hash table. * Return: B_TRUE if the id is found; B_FALSE if not found. */ boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid) { int rv; mod_hash_val_t vp; rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp); if (rv != 0) return (B_FALSE); return (B_TRUE); }
static task_t * task_find(taskid_t id, zoneid_t zoneid) { task_t *tk; ASSERT(MUTEX_HELD(&task_hash_lock)); if (mod_hash_find(task_hash, (mod_hash_key_t)(uintptr_t)id, (mod_hash_val_t *)&tk) == MH_ERR_NOTFOUND || (zoneid != ALL_ZONES && zoneid != tk->tk_zone->zone_id)) return (NULL); return (tk); }
uintptr_t space_fetch(char *key) { uintptr_t ptr = 0; mod_hash_val_t val; int rval; if (key) { rval = mod_hash_find(space_hash, (mod_hash_key_t)key, &val); if (rval == 0) { ptr = (uintptr_t)val; } } return (ptr); }
/* ARGSUSED */ int vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask, vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr, uint_t mac_slot, mac_resource_props_t *mrp) { vnic_t *vnic = NULL; 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); } rw_exit(&vnic_lock); return (0); }
int vnic_info(vnic_info_t *info, cred_t *credp) { vnic_t *vnic; int err; /* Make sure that the VNIC link is visible from the caller's zone. */ if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp))) return (ENOENT); rw_enter(&vnic_lock, RW_WRITER); err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id), (mod_hash_val_t *)&vnic); if (err != 0) { rw_exit(&vnic_lock); return (ENOENT); } info->vn_link_id = vnic->vn_link_id; info->vn_mac_addr_type = vnic->vn_addr_type; info->vn_mac_len = vnic->vn_addr_len; bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN); info->vn_mac_slot = vnic->vn_slot_id; info->vn_mac_prefix_len = 0; info->vn_vid = vnic->vn_vid; info->vn_force = vnic->vn_force; info->vn_vrid = vnic->vn_vrid; info->vn_af = vnic->vn_af; bzero(&info->vn_resource_props, sizeof (mac_resource_props_t)); if (vnic->vn_mch != NULL) mac_client_get_resources(vnic->vn_mch, &info->vn_resource_props); rw_exit(&vnic_lock); return (0); }
/* 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); }
int dls_vlan_hold(const char *name, dls_vlan_t **dvpp, boolean_t create_vlan) { int err; dls_vlan_t *dvp; dls_link_t *dlp; boolean_t vlan_created = B_FALSE; again: rw_enter(&i_dls_vlan_lock, RW_WRITER); err = mod_hash_find(i_dls_vlan_hash, (mod_hash_key_t)name, (mod_hash_val_t *)&dvp); if (err != 0) { char mac[MAXNAMELEN]; uint_t index, ddi_inst, mac_ppa, len; uint16_t vid; ASSERT(err == MH_ERR_NOTFOUND); vlan_created = B_FALSE; if (!create_vlan) { err = ENOENT; goto done; } /* * Only create tagged vlans on demand. * Note that if we get here, 'name' must be a sane * value because it must have been derived from * ddi_major_to_name(). */ if (ddi_parse(name, mac, &index) != DDI_SUCCESS || (vid = DLS_PPA2VID(index)) == VLAN_ID_NONE || vid > VLAN_ID_MAX) { err = EINVAL; goto done; } mac_ppa = (uint_t)DLS_PPA2INST(index); if (strcmp(mac, "aggr") == 0) ddi_inst = 0; else ddi_inst = mac_ppa; len = strlen(mac); ASSERT(len < MAXNAMELEN); (void) snprintf(mac + len, MAXNAMELEN - len, "%d", mac_ppa); rw_exit(&i_dls_vlan_lock); if ((err = dls_vlan_create(name, mac, ddi_inst, vid)) != 0) { rw_enter(&i_dls_vlan_lock, RW_WRITER); goto done; } /* * At this point someone else could do a dls_vlan_hold and * dls_vlan_rele on this new vlan and causes it to be * destroyed. This will at worst cause us to spin a few * times. */ vlan_created = B_TRUE; goto again; } dlp = dvp->dv_dlp; if ((err = dls_mac_hold(dlp)) != 0) goto done; /* * Do not allow the creation of tagged VLAN interfaces on * non-Ethernet links. Note that we cannot do this check in * dls_vlan_create() nor in this function prior to the call to * dls_mac_hold(). The reason is that before we do a * dls_mac_hold(), we may not have opened the mac, and therefore do * not know what kind of media the mac represents. In other words, * dls_mac_hold() assigns the dl_mip of the dls_link_t we're * interested in. */ if (dvp->dv_id != VLAN_ID_NONE && dlp->dl_mip->mi_media != DL_ETHER) { dls_mac_rele(dlp); err = EINVAL; goto done; } if ((err = mac_start(dlp->dl_mh)) != 0) { dls_mac_rele(dlp); goto done; } if (dvp->dv_ref++ == 0) dls_mac_stat_create(dvp); *dvpp = dvp; done: rw_exit(&i_dls_vlan_lock); /* * We could be destroying a vlan created by another thread. This * is ok because this other thread will just loop back up and * recreate the vlan. */ if (err != 0 && vlan_created) (void) dls_vlan_destroy(name); return (err); }
/* * Forward pkts to any devices or interfaces which have registered * an interest in them (i.e. multicast groups). */ static int vsw_forward_grp(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *arg) { struct ether_header *ehp = (struct ether_header *)mp->b_rptr; mfdb_ent_t *entp = NULL; mfdb_ent_t *tpp = NULL; vsw_port_t *port; uint64_t key = 0; mblk_t *nmp = NULL; mblk_t *ret_m = NULL; boolean_t check_if = B_TRUE; /* * Convert address to hash table key */ KEY_HASH(key, &ehp->ether_dhost); D1(vswp, "%s: key 0x%llx", __func__, key); /* * If pkt came from either a vnet or down the stack (if we are * plumbed) and we are in layer 2 mode, then we send the pkt out * over the physical adapter, and then check to see if any other * vnets are interested in it. */ if ((vswp->smode & VSW_LAYER2) && ((caller == VSW_VNETPORT) || (caller == VSW_LOCALDEV))) { nmp = vsw_dupmsgchain(mp); if (nmp) { if ((ret_m = vsw_tx_msg(vswp, nmp, caller, arg)) != NULL) { DERR(vswp, "%s: dropping pkt(s) consisting of " "%ld bytes of data for physical device", __func__, MBLKL(ret_m)); freemsgchain(ret_m); } } } READ_ENTER(&vswp->mfdbrw); if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)key, (mod_hash_val_t *)&entp) != 0) { D3(vswp, "%s: no table entry found for addr 0x%llx", __func__, key); } else { /* * Send to list of devices associated with this address... */ for (tpp = entp; tpp != NULL; tpp = tpp->nextp) { /* dont send to ourselves */ if ((caller == VSW_VNETPORT) && (tpp->d_addr == (void *)arg)) { port = (vsw_port_t *)tpp->d_addr; D3(vswp, "%s: not sending to ourselves" " : port %d", __func__, port->p_instance); continue; } else if ((caller == VSW_LOCALDEV) && (tpp->d_type == VSW_LOCALDEV)) { D2(vswp, "%s: not sending back up stack", __func__); continue; } if (tpp->d_type == VSW_VNETPORT) { port = (vsw_port_t *)tpp->d_addr; D3(vswp, "%s: sending to port %ld for addr " "0x%llx", __func__, port->p_instance, key); nmp = vsw_dupmsgchain(mp); if (nmp) { /* * The vswp->mfdbrw is protecting the * portp from getting destroyed here. * So, no ref_cnt is incremented here. */ (void) vsw_portsend(port, nmp); } } else { vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_COPYMSG); D2(vswp, "%s: sending up stack" " for addr 0x%llx", __func__, key); check_if = B_FALSE; } } } RW_EXIT(&vswp->mfdbrw); /* * If the pkt came from either a vnet or from physical device, * and if we havent already sent the pkt up the stack then we * check now if we can/should (i.e. the interface is plumbed * and in promisc mode). */ if ((check_if) && ((caller == VSW_VNETPORT) || (caller == VSW_PHYSDEV))) { vsw_mac_rx(vswp, NULL, mp, VSW_MACRX_PROMISC | VSW_MACRX_COPYMSG); } freemsgchain(mp); D1(vswp, "%s: exit", __func__); return (0); }
/* * Remove a multicast entry from the hashtable. * * Search hash table based on address. If match found, scan * list of ports associated with address. If specified port * found remove it from list. */ int vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) { mfdb_ent_t *ment = NULL; mfdb_ent_t *curr_p, *prev_p; void *tgt = NULL; D1(vswp, "%s: enter", __func__); if (devtype == VSW_VNETPORT) { tgt = (vsw_port_t *)arg; D2(vswp, "%s: removing port %d from mFDB for address" " 0x%llx", __func__, ((vsw_port_t *)tgt)->p_instance, addr); } else { D2(vswp, "%s: removing entry", __func__); tgt = (void *)vswp; } WRITE_ENTER(&vswp->mfdbrw); if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, (mod_hash_val_t *)&ment) != 0) { D2(vswp, "%s: address 0x%llx not in table", __func__, addr); RW_EXIT(&vswp->mfdbrw); return (1); } prev_p = curr_p = ment; while (curr_p != NULL) { if (curr_p->d_addr == (void *)tgt) { if (devtype == VSW_VNETPORT) { D2(vswp, "%s: port %d found", __func__, ((vsw_port_t *)tgt)->p_instance); } else { D2(vswp, "%s: instance found", __func__); } if (prev_p == curr_p) { /* * head of list, if no other element is in * list then destroy this entry, otherwise * just replace it with updated value. */ ment = curr_p->nextp; if (ment == NULL) { (void) mod_hash_destroy(vswp->mfdb, (mod_hash_val_t)addr); } else { (void) mod_hash_replace(vswp->mfdb, (mod_hash_key_t)addr, (mod_hash_val_t)ment); } } else { /* * Not head of list, no need to do * replacement, just adjust list pointers. */ prev_p->nextp = curr_p->nextp; } break; } prev_p = curr_p; curr_p = curr_p->nextp; } RW_EXIT(&vswp->mfdbrw); D1(vswp, "%s: exit", __func__); if (curr_p == NULL) return (1); kmem_free(curr_p, sizeof (mfdb_ent_t)); return (0); }
/* * Add a new multicast entry. * * Search hash table based on address. If match found then * update associated val (which is chain of ports), otherwise * create new key/val (addr/port) pair and insert into table. */ int vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg) { int dup = 0; int rv = 0; mfdb_ent_t *ment = NULL; mfdb_ent_t *tmp_ent = NULL; mfdb_ent_t *new_ent = NULL; void *tgt = NULL; if (devtype == VSW_VNETPORT) { /* * Being invoked from a vnet. */ ASSERT(arg != NULL); tgt = arg; D2(NULL, "%s: port %d : address 0x%llx", __func__, ((vsw_port_t *)arg)->p_instance, addr); } else { /* * We are being invoked via the m_multicst mac entry * point. */ D2(NULL, "%s: address 0x%llx", __func__, addr); tgt = (void *)vswp; } WRITE_ENTER(&vswp->mfdbrw); if (mod_hash_find(vswp->mfdb, (mod_hash_key_t)addr, (mod_hash_val_t *)&ment) != 0) { /* address not currently in table */ ment = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); ment->d_addr = (void *)tgt; ment->d_type = devtype; ment->nextp = NULL; if (mod_hash_insert(vswp->mfdb, (mod_hash_key_t)addr, (mod_hash_val_t)ment) != 0) { DERR(vswp, "%s: hash table insertion failed", __func__); kmem_free(ment, sizeof (mfdb_ent_t)); rv = 1; } else { D2(vswp, "%s: added initial entry for 0x%llx to " "table", __func__, addr); } } else { /* * Address in table. Check to see if specified port * is already associated with the address. If not add * it now. */ tmp_ent = ment; while (tmp_ent != NULL) { if (tmp_ent->d_addr == (void *)tgt) { if (devtype == VSW_VNETPORT) { DERR(vswp, "%s: duplicate port entry " "found for portid %ld and key " "0x%llx", __func__, ((vsw_port_t *)arg)->p_instance, addr); } else { DERR(vswp, "%s: duplicate entry found" "for key 0x%llx", __func__, addr); } rv = 1; dup = 1; break; } tmp_ent = tmp_ent->nextp; } /* * Port not on list so add it to end now. */ if (0 == dup) { D2(vswp, "%s: added entry for 0x%llx to table", __func__, addr); new_ent = kmem_alloc(sizeof (mfdb_ent_t), KM_SLEEP); new_ent->d_addr = (void *)tgt; new_ent->d_type = devtype; new_ent->nextp = NULL; tmp_ent = ment; while (tmp_ent->nextp != NULL) tmp_ent = tmp_ent->nextp; tmp_ent->nextp = new_ent; } } RW_EXIT(&vswp->mfdbrw); return (rv); }
/* * For unsolicited exchanges, FCoET is only responsible for allocation of * req_payload. FCT will allocate resp_payload after the exchange is * passed on. */ static fcoet_exchange_t * fcoet_create_unsol_exchange(fcoe_frame_t *frm) { uint8_t r_ctl; int cdb_size; fcoet_exchange_t *xch, *xch_tmp; fct_cmd_t *cmd; fcoe_fcp_cmnd_t *ffc; uint32_t task_expected_len = 0; r_ctl = FRM_R_CTL(frm); switch (r_ctl) { case 0x22: /* * FCoET's unsolicited ELS */ cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_RCVD_ELS, GET_STRUCT_SIZE(fcoet_exchange_t) + frm->frm_payload_size, 0); if (cmd == NULL) { FCOET_EXT_LOG(0, "can't get cmd"); return (NULL); } break; case 0x06: /* * FCoET's unsolicited SCSI cmd */ cdb_size = 16; /* need improve later */ cmd = fct_scsi_task_alloc(FRM2SS(frm)->ss_port, FCT_HANDLE_NONE, FRM_S_ID(frm), frm->frm_payload, cdb_size, STMF_TASK_EXT_NONE); if (cmd == NULL) { FCOET_EXT_LOG(0, "can't get fcp cmd"); return (NULL); } ffc = (fcoe_fcp_cmnd_t *)frm->frm_payload; task_expected_len = FCOE_B2V_4(ffc->ffc_fcp_dl); break; default: FCOET_EXT_LOG(0, "unsupported R_CTL: %x", r_ctl); return (NULL); } /* * xch initialization */ xch = CMD2XCH(cmd); xch->xch_oxid = FRM_OXID(frm); xch->xch_flags = 0; xch->xch_ss = FRM2SS(frm); xch->xch_cmd = cmd; xch->xch_current_seq = NULL; xch->xch_left_data_size = 0; if (task_expected_len) { xch->xch_dbuf_num = (task_expected_len + FCOET_MAX_DBUF_LEN - 1) / FCOET_MAX_DBUF_LEN; xch->xch_dbufs = kmem_zalloc(xch->xch_dbuf_num * sizeof (stmf_data_buf_t *), KM_SLEEP); } xch->xch_start_time = ddi_get_lbolt(); do { xch->xch_rxid = atomic_add_16_nv( &xch->xch_ss->ss_next_unsol_rxid, 1); if (xch->xch_rxid == 0xFFFF) { xch->xch_rxid = atomic_add_16_nv( &xch->xch_ss->ss_next_unsol_rxid, 1); } } while (mod_hash_find(FRM2SS(frm)->ss_unsol_rxid_hash, (mod_hash_key_t)(intptr_t)xch->xch_rxid, (mod_hash_val_t)&xch_tmp) == 0); xch->xch_sequence_no = 0; xch->xch_ref = 0; (void) mod_hash_insert(xch->xch_ss->ss_unsol_rxid_hash, (mod_hash_key_t)(intptr_t)xch->xch_rxid, (mod_hash_val_t)xch); xch->xch_flags |= XCH_FLAG_IN_HASH_TABLE; /* * cmd initialization */ cmd->cmd_port = FRM2SS(frm)->ss_port; cmd->cmd_rp_handle = FCT_HANDLE_NONE; cmd->cmd_rportid = FRM_S_ID(frm); cmd->cmd_lportid = FRM_D_ID(frm); cmd->cmd_oxid = xch->xch_oxid; cmd->cmd_rxid = xch->xch_rxid; fcoet_init_tfm(frm, xch); return (xch); }