static int dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags) { dld_capab_direct_t *direct = data; ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); switch (flags) { case DLD_ENABLE: dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf, direct->di_rx_ch); direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put; direct->di_tx_dh = dsp; direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify; direct->di_tx_cb_dh = dsp->ds_mch; direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked; direct->di_tx_fctl_dh = dsp->ds_mch; dsp->ds_direct = B_TRUE; return (0); case DLD_DISABLE: dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ? dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); dsp->ds_direct = B_FALSE; return (0); } return (ENOTSUP); }
/* * Walk the MAC client's multicast address list and add/remove the addr/vid * ('arg' is 'flent') to all the addresses. */ void mac_client_bcast_refresh(mac_client_impl_t *mcip, mac_multicst_t refresh_fn, void *arg, boolean_t add) { mac_mcast_addrs_t *grp, *next; mac_impl_t *mip = mcip->mci_mip; ASSERT(refresh_fn != NULL); ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); /* * Walk the multicast address list and call the refresh function for * each address. * Broadcast addresses are not added or removed through the multicast * entry points, so don't include them as part of the refresh. */ for (grp = mcip->mci_mcast_addrs; grp != NULL; grp = next) { /* * Save the next pointer just in case the refresh * function's action causes the group entry to be * freed. * We won't be adding to this list as part of the * refresh. */ next = grp->mma_next; refresh_fn(arg, add, grp->mma_addr); } }
static int dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags) { dld_capab_poll_t *poll = data; ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); switch (flags) { case DLD_ENABLE: return (dld_capab_poll_enable(dsp, poll)); case DLD_DISABLE: return (dld_capab_poll_disable(dsp, poll)); } return (ENOTSUP); }
/* * Free the specific broadcast group. Invoked when the last reference * to the group is released. */ void mac_bcast_grp_free(void *bcast_grp) { mac_bcast_grp_t *grp = bcast_grp; mac_impl_t *mip = grp->mbg_mac_impl; ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); ASSERT(grp->mbg_addr != NULL); kmem_free(grp->mbg_addr, mip->mi_type->mt_addr_length); kmem_free(grp->mbg_clients, grp->mbg_nclients_alloc * sizeof (mac_bcast_grp_mcip_t)); mip->mi_bcast_ngrps--; kmem_cache_free(mac_bcast_grp_cache, grp); }
static int dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags) { dld_capab_lso_t *lso = data; ASSERT(MAC_PERIM_HELD(dsp->ds_mh)); switch (flags) { case DLD_ENABLE: { mac_capab_lso_t mac_lso; /* * Check if LSO is supported on this MAC & enable LSO * accordingly. */ if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) { lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max; lso->lso_flags = 0; /* translate the flag for mac clients */ if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0) lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4; dsp->ds_lso = B_TRUE; dsp->ds_lso_max = lso->lso_max; } else { dsp->ds_lso = B_FALSE; dsp->ds_lso_max = 0; return (ENOTSUP); } return (0); } case DLD_DISABLE: { dsp->ds_lso = B_FALSE; dsp->ds_lso_max = 0; return (0); } } return (ENOTSUP); }
/* * Remove the specified MAC client from the group corresponding to * the specific broadcast or multicast address. * * Note: mac_bcast_delete() calls mac_remove_flow() which * will call cv_wait for fe_refcnt to drop to 0. So this function * should not be called from interrupt or STREAMS context. */ void mac_bcast_delete(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid) { mac_impl_t *mip = mcip->mci_mip; mac_bcast_grp_t *grp = NULL, **prev; size_t addr_len = mip->mi_type->mt_addr_length; flow_entry_t *flent; uint_t i; mac_mcast_addrs_t *maddr = NULL; mac_mcast_addrs_t **mprev; ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); /* find the broadcast group. The list is protected by the perimeter */ prev = &mip->mi_bcast_grp; for (grp = mip->mi_bcast_grp; grp != NULL; prev = &grp->mbg_next, grp = grp->mbg_next) { if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && grp->mbg_vid == vid) break; } ASSERT(grp != NULL); /* * Remove the MAC client from the list of MAC clients associated * with that broadcast group. * * We mark the mbg_clients[] location corresponding to the removed MAC * client NULL and reuse that location when we add a new MAC client. */ rw_enter(&mip->mi_rw_lock, RW_WRITER); for (i = 0; i < grp->mbg_nclients_alloc; i++) { if (grp->mbg_clients[i].mgb_client == mcip) break; } ASSERT(i < grp->mbg_nclients_alloc); /* * If there are more references to this MAC client, then we let * it remain till it goes to 0. */ if (--grp->mbg_clients[i].mgb_client_ref > 0) goto update_maddr; grp->mbg_clients[i].mgb_client = NULL; grp->mbg_clients[i].mgb_client_ref = 0; /* * Since we're removing from the list of MAC clients using that group, * kick the generation count, which will allow mac_bcast_send() * to detect that condition. */ grp->mbg_clients_gen++; if (--grp->mbg_nclients == 0) { /* * The last MAC client of the group was just removed. * Unlink the current group from the list of groups * defined on top of the underlying NIC. The group * structure will stay around until the last reference * is dropped. */ *prev = grp->mbg_next; } update_maddr: rw_exit(&mip->mi_rw_lock); if (grp->mbg_addrtype == MAC_ADDRTYPE_MULTICAST) { mprev = &mcip->mci_mcast_addrs; for (maddr = mcip->mci_mcast_addrs; maddr != NULL; mprev = &maddr->mma_next, maddr = maddr->mma_next) { if (bcmp(grp->mbg_addr, maddr->mma_addr, mip->mi_type->mt_addr_length) == 0) break; } ASSERT(maddr != NULL); if (--maddr->mma_ref == 0) { *mprev = maddr->mma_next; maddr->mma_next = NULL; kmem_free(maddr, sizeof (mac_mcast_addrs_t)); } mprev = &mip->mi_mcast_addrs; for (maddr = mip->mi_mcast_addrs; maddr != NULL; mprev = &maddr->mma_next, maddr = maddr->mma_next) { if (bcmp(grp->mbg_addr, maddr->mma_addr, mip->mi_type->mt_addr_length) == 0) break; } ASSERT(maddr != NULL); if (--maddr->mma_ref == 0) { (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); *mprev = maddr->mma_next; maddr->mma_next = NULL; kmem_free(maddr, sizeof (mac_mcast_addrs_t)); } } /* * If the group itself is being removed, remove the * corresponding flow from the underlying NIC. */ flent = grp->mbg_flow_ent; if (grp->mbg_nclients == 0) { mac_flow_remove(mip->mi_flow_tab, flent, B_FALSE); mac_flow_wait(flent, FLOW_DRIVER_UPCALL); FLOW_FINAL_REFRELE(flent); } }
/* * Add the specified MAC client to the group corresponding to the specified * broadcast or multicast address. * Return 0 on success, or an errno value on failure. */ int mac_bcast_add(mac_client_impl_t *mcip, const uint8_t *addr, uint16_t vid, mac_addrtype_t addrtype) { mac_impl_t *mip = mcip->mci_mip; mac_bcast_grp_t *grp = NULL, **last_grp; size_t addr_len = mip->mi_type->mt_addr_length; int rc = 0; int i, index = -1; mac_mcast_addrs_t **prev_mi_addr = NULL; mac_mcast_addrs_t **prev_mci_addr = NULL; ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); ASSERT(addrtype == MAC_ADDRTYPE_MULTICAST || addrtype == MAC_ADDRTYPE_BROADCAST); /* * Add the MAC client to the list of MAC clients associated * with the group. */ if (addrtype == MAC_ADDRTYPE_MULTICAST) { mac_mcast_addrs_t *maddr; /* * In case of a driver (say aggr), we need this information * on a per MAC instance basis. */ prev_mi_addr = &mip->mi_mcast_addrs; for (maddr = *prev_mi_addr; maddr != NULL; prev_mi_addr = &maddr->mma_next, maddr = maddr->mma_next) { if (bcmp(maddr->mma_addr, addr, addr_len) == 0) break; } if (maddr == NULL) { /* * For multicast addresses, have the underlying MAC * join the corresponding multicast group. */ rc = mip->mi_multicst(mip->mi_driver, B_TRUE, addr); if (rc != 0) return (rc); maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), KM_SLEEP); bcopy(addr, maddr->mma_addr, addr_len); *prev_mi_addr = maddr; } else { prev_mi_addr = NULL; } maddr->mma_ref++; /* * We maintain a separate list for each MAC client. Get * the entry or add, if it is not present. */ prev_mci_addr = &mcip->mci_mcast_addrs; for (maddr = *prev_mci_addr; maddr != NULL; prev_mci_addr = &maddr->mma_next, maddr = maddr->mma_next) { if (bcmp(maddr->mma_addr, addr, addr_len) == 0) break; } if (maddr == NULL) { maddr = kmem_zalloc(sizeof (mac_mcast_addrs_t), KM_SLEEP); bcopy(addr, maddr->mma_addr, addr_len); *prev_mci_addr = maddr; } else { prev_mci_addr = NULL; } maddr->mma_ref++; } /* The list is protected by the perimeter */ last_grp = &mip->mi_bcast_grp; for (grp = *last_grp; grp != NULL; last_grp = &grp->mbg_next, grp = grp->mbg_next) { if (bcmp(grp->mbg_addr, addr, addr_len) == 0 && grp->mbg_vid == vid) break; } if (grp == NULL) { /* * The group does not yet exist, create it. */ flow_desc_t flow_desc; char flow_name[MAXFLOWNAMELEN]; grp = kmem_cache_alloc(mac_bcast_grp_cache, KM_SLEEP); bzero(grp, sizeof (mac_bcast_grp_t)); grp->mbg_next = NULL; grp->mbg_mac_impl = mip; DTRACE_PROBE1(mac__bcast__add__new__group, mac_bcast_grp_t *, grp); grp->mbg_addr = kmem_zalloc(addr_len, KM_SLEEP); bcopy(addr, grp->mbg_addr, addr_len); grp->mbg_addrtype = addrtype; grp->mbg_vid = vid; /* * Add a new flow to the underlying MAC. */ bzero(&flow_desc, sizeof (flow_desc)); bcopy(addr, &flow_desc.fd_dst_mac, addr_len); flow_desc.fd_mac_len = (uint32_t)addr_len; flow_desc.fd_mask = FLOW_LINK_DST; if (vid != 0) { flow_desc.fd_vid = vid; flow_desc.fd_mask |= FLOW_LINK_VID; } grp->mbg_id = atomic_inc_32_nv(&mac_bcast_id); (void) sprintf(flow_name, "mac/%s/mcast%d", mip->mi_name, grp->mbg_id); rc = mac_flow_create(&flow_desc, NULL, flow_name, grp, FLOW_MCAST, &grp->mbg_flow_ent); if (rc != 0) { kmem_free(grp->mbg_addr, addr_len); kmem_cache_free(mac_bcast_grp_cache, grp); goto fail; } grp->mbg_flow_ent->fe_mbg = grp; mip->mi_bcast_ngrps++; /* * Initial creation reference on the flow. This is released * in the corresponding delete action i_mac_bcast_delete() */ FLOW_REFHOLD(grp->mbg_flow_ent); /* * When the multicast and broadcast packet is received * by the underlying NIC, mac_rx_classify() will invoke * mac_bcast_send() with arg2=NULL, which will cause * mac_bcast_send() to send a copy of the packet(s) * to every MAC client opened on top of the underlying MAC. * * When the mac_bcast_send() function is invoked from * the transmit path of a MAC client, it will specify the * transmitting MAC client as the arg2 value, which will * allow mac_bcast_send() to skip that MAC client and not * send it a copy of the packet. * * We program the classifier to dispatch matching broadcast * packets to mac_bcast_send(). */ grp->mbg_flow_ent->fe_cb_fn = mac_bcast_send; grp->mbg_flow_ent->fe_cb_arg1 = grp; grp->mbg_flow_ent->fe_cb_arg2 = NULL; rc = mac_flow_add(mip->mi_flow_tab, grp->mbg_flow_ent); if (rc != 0) { FLOW_FINAL_REFRELE(grp->mbg_flow_ent); goto fail; } *last_grp = grp; } ASSERT(grp->mbg_addrtype == addrtype); /* * Add the MAC client to the list of MAC clients associated * with the group. */ rw_enter(&mip->mi_rw_lock, RW_WRITER); for (i = 0; i < grp->mbg_nclients_alloc; i++) { /* * The MAC client was already added, say when we have * different unicast addresses with the same vid. * Just increment the ref and we are done. */ if (grp->mbg_clients[i].mgb_client == mcip) { grp->mbg_clients[i].mgb_client_ref++; rw_exit(&mip->mi_rw_lock); return (0); } else if (grp->mbg_clients[i].mgb_client == NULL && index == -1) { index = i; } } if (grp->mbg_nclients_alloc == grp->mbg_nclients) { mac_bcast_grp_mcip_t *new_clients; uint_t new_size = grp->mbg_nclients+1; new_clients = kmem_zalloc(new_size * sizeof (mac_bcast_grp_mcip_t), KM_SLEEP); if (grp->mbg_nclients > 0) { ASSERT(grp->mbg_clients != NULL); bcopy(grp->mbg_clients, new_clients, grp->mbg_nclients * sizeof (mac_bcast_grp_mcip_t)); kmem_free(grp->mbg_clients, grp->mbg_nclients * sizeof (mac_bcast_grp_mcip_t)); } grp->mbg_clients = new_clients; grp->mbg_nclients_alloc = new_size; index = new_size - 1; } ASSERT(index != -1); grp->mbg_clients[index].mgb_client = mcip; grp->mbg_clients[index].mgb_client_ref = 1; grp->mbg_nclients++; /* * Since we're adding to the list of MAC clients using that group, * kick the generation count, which will allow mac_bcast_send() * to detect that condition after re-acquiring the lock. */ grp->mbg_clients_gen++; rw_exit(&mip->mi_rw_lock); return (0); fail: if (prev_mi_addr != NULL) { kmem_free(*prev_mi_addr, sizeof (mac_mcast_addrs_t)); *prev_mi_addr = NULL; (void) mip->mi_multicst(mip->mi_driver, B_FALSE, addr); } if (prev_mci_addr != NULL) { kmem_free(*prev_mci_addr, sizeof (mac_mcast_addrs_t)); *prev_mci_addr = NULL; } return (rc); }