/* * Copyright (c) 2008-2016 Solarflare Communications Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are * those of the authors and should not be interpreted as representing official * policies, either expressed or implied, of the FreeBSD Project. */ /* * All efx_mac_*() must be after efx_port_init() * LOCKING STRATEGY: Aquire sm_lock and test sm_state==SFXGE_MAC_STARTED * to serialise against sfxge_restart() */ #include #include #include #include #include "sfxge.h" #include "efx.h" #define SFXGE_MAC_POLL_PERIOD_MS 1000 static void sfxge_mac_link_update_locked(sfxge_t *sp, efx_link_mode_t mode); /* MAC DMA attributes */ static ddi_device_acc_attr_t sfxge_mac_devacc = { DDI_DEVICE_ATTR_V0, /* devacc_attr_version */ DDI_NEVERSWAP_ACC, /* devacc_attr_endian_flags */ DDI_STRICTORDER_ACC /* devacc_attr_dataorder */ }; static ddi_dma_attr_t sfxge_mac_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* dma_attr_addr_lo */ 0xffffffffffffffffull, /* dma_attr_addr_hi */ 0xffffffffffffffffull, /* dma_attr_count_max */ 0x1000, /* dma_attr_align */ 0xffffffff, /* dma_attr_burstsizes */ 1, /* dma_attr_minxfer */ 0xffffffffffffffffull, /* dma_attr_maxxfer */ 0xffffffffffffffffull, /* dma_attr_seg */ 1, /* dma_attr_sgllen */ 1, /* dma_attr_granular */ 0 /* dma_attr_flags */ }; static void _sfxge_mac_stat_update(sfxge_mac_t *smp, int tries, int delay_usec) { sfxge_t *sp = smp->sm_sp; efsys_mem_t *esmp = &(smp->sm_mem); int i; ASSERT(mutex_owned(&(smp->sm_lock))); ASSERT3U(smp->sm_state, !=, SFXGE_MAC_UNINITIALIZED); /* if no stats pending then they are already freshly updated */ if (smp->sm_mac_stats_timer_reqd && !smp->sm_mac_stats_pend) return; for (i = 0; i < tries; i++) { /* Try to update the cached counters */ if (efx_mac_stats_update(sp->s_enp, esmp, smp->sm_stat, NULL) != EAGAIN) goto done; drv_usecwait(delay_usec); } DTRACE_PROBE(mac_stat_timeout); dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "MAC stats timeout"); return; done: smp->sm_mac_stats_pend = B_FALSE; smp->sm_lbolt = ddi_get_lbolt(); } static void sfxge_mac_stat_update_quick(sfxge_mac_t *smp) { /* * Update the statistics from the most recent DMA. This might race * with an inflight dma, so retry once. Otherwise get mac stat * values from the last mac_poll() or MC periodic stats. */ _sfxge_mac_stat_update(smp, 2, 50); } static void sfxge_mac_stat_update_wait(sfxge_mac_t *smp) { /* Wait a max of 20 * 500us = 10ms */ _sfxge_mac_stat_update(smp, 20, 500); } static int sfxge_mac_kstat_update(kstat_t *ksp, int rw) { sfxge_mac_t *smp = ksp->ks_private; kstat_named_t *knp; int rc; unsigned int val; sfxge_rx_coalesce_mode_t rxmode; if (rw != KSTAT_READ) { rc = EACCES; goto fail1; } ASSERT(mutex_owned(&(smp->sm_lock))); if (smp->sm_state != SFXGE_MAC_STARTED) goto done; sfxge_mac_stat_update_quick(smp); knp = smp->sm_stat; knp += EFX_MAC_NSTATS; knp->value.ui64 = (smp->sm_link_up) ? 1 : 0; knp++; knp->value.ui64 = smp->sm_link_speed; knp++; knp->value.ui64 = smp->sm_link_duplex; knp++; knp->value.ui64 = (smp->sm_fcntl & EFX_FCNTL_GENERATE) ? 1 : 0; knp++; knp->value.ui64 = (smp->sm_fcntl & EFX_FCNTL_RESPOND) ? 1 : 0; knp++; sfxge_ev_moderation_get(smp->sm_sp, &val); knp->value.ui64 = val; knp++; sfxge_rx_coalesce_mode_get(smp->sm_sp, &rxmode); knp->value.ui64 = (uint64_t)rxmode; knp++; if (sfxge_rx_scale_count_get(smp->sm_sp, &val) != 0) val = 0; knp->value.ui64 = val; knp++; done: return (0); fail1: DTRACE_PROBE1(fail1, int, rc); return (rc); } static int sfxge_mac_kstat_init(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); dev_info_t *dip = sp->s_dip; char name[MAXNAMELEN]; kstat_t *ksp; kstat_named_t *knp; unsigned int id; int rc; /* Create the set */ (void) snprintf(name, MAXNAMELEN - 1, "%s_mac", ddi_driver_name(dip)); if ((ksp = kstat_create((char *)ddi_driver_name(dip), ddi_get_instance(dip), name, "mac", KSTAT_TYPE_NAMED, EFX_MAC_NSTATS + 8, 0)) == NULL) { rc = ENOMEM; goto fail1; } smp->sm_ksp = ksp; ksp->ks_update = sfxge_mac_kstat_update; ksp->ks_private = smp; ksp->ks_lock = &(smp->sm_lock); /* Initialise the named stats */ smp->sm_stat = knp = ksp->ks_data; for (id = 0; id < EFX_MAC_NSTATS; id++) { kstat_named_init(knp, (char *)efx_mac_stat_name(sp->s_enp, id), KSTAT_DATA_UINT64); knp++; } kstat_named_init(knp++, "link_up", KSTAT_DATA_UINT64); kstat_named_init(knp++, "link_speed", KSTAT_DATA_UINT64); kstat_named_init(knp++, "link_duplex", KSTAT_DATA_UINT64); kstat_named_init(knp++, "fcntl_generate", KSTAT_DATA_UINT64); kstat_named_init(knp++, "fcntl_respond", KSTAT_DATA_UINT64); kstat_named_init(knp++, "intr_moderation", KSTAT_DATA_UINT64); kstat_named_init(knp++, "rx_coalesce_mode", KSTAT_DATA_UINT64); kstat_named_init(knp++, "rx_scale_count", KSTAT_DATA_UINT64); kstat_install(ksp); return (0); fail1: DTRACE_PROBE1(fail1, int, rc); return (rc); } static void sfxge_mac_kstat_fini(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); /* Destroy the set */ kstat_delete(smp->sm_ksp); smp->sm_ksp = NULL; smp->sm_stat = NULL; } void sfxge_mac_stat_get(sfxge_t *sp, unsigned int id, uint64_t *valp) { sfxge_mac_t *smp = &(sp->s_mac); /* Make sure the cached counter values are recent */ mutex_enter(&(smp->sm_lock)); if (smp->sm_state != SFXGE_MAC_STARTED) goto done; sfxge_mac_stat_update_quick(smp); *valp = smp->sm_stat[id].value.ui64; done: mutex_exit(&(smp->sm_lock)); } static void sfxge_mac_poll(void *arg) { sfxge_t *sp = arg; efx_nic_t *enp = sp->s_enp; sfxge_mac_t *smp = &(sp->s_mac); efsys_mem_t *esmp = &(smp->sm_mem); efx_link_mode_t mode; clock_t timeout; mutex_enter(&(smp->sm_lock)); while (smp->sm_state == SFXGE_MAC_STARTED) { /* clears smp->sm_mac_stats_pend if appropriate */ if (smp->sm_mac_stats_pend) sfxge_mac_stat_update_wait(smp); /* This may sleep waiting for MCDI completion */ mode = EFX_LINK_UNKNOWN; if (efx_port_poll(enp, &mode) == 0) sfxge_mac_link_update_locked(sp, mode); if ((smp->sm_link_poll_reqd == B_FALSE) && (smp->sm_mac_stats_timer_reqd == B_FALSE)) goto done; /* Zero the memory */ bzero(esmp->esm_base, EFX_MAC_STATS_SIZE); /* Trigger upload the MAC statistics counters */ if (smp->sm_link_up && efx_mac_stats_upload(sp->s_enp, esmp) == 0) smp->sm_mac_stats_pend = B_TRUE; /* Wait for timeout or end of polling */ timeout = ddi_get_lbolt() + drv_usectohz(1000 * SFXGE_MAC_POLL_PERIOD_MS); while (smp->sm_state == SFXGE_MAC_STARTED) { if (cv_timedwait(&(smp->sm_link_poll_kv), &(smp->sm_lock), timeout) < 0) { /* Timeout - poll if polling still enabled */ break; } } } done: mutex_exit(&(smp->sm_lock)); } static void sfxge_mac_poll_start(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); ASSERT(mutex_owned(&(smp->sm_lock))); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_STARTED); /* Schedule a poll */ (void) ddi_taskq_dispatch(smp->sm_tqp, sfxge_mac_poll, sp, DDI_SLEEP); } static void sfxge_mac_poll_stop(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); ASSERT(mutex_owned(&(smp->sm_lock))); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED); cv_broadcast(&(smp->sm_link_poll_kv)); /* Wait for link polling to cease */ mutex_exit(&(smp->sm_lock)); ddi_taskq_wait(smp->sm_tqp); mutex_enter(&(smp->sm_lock)); /* Collect the final statistics. */ sfxge_mac_stat_update_wait(smp); } int sfxge_mac_init(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); efsys_mem_t *esmp = &(smp->sm_mem); dev_info_t *dip = sp->s_dip; sfxge_dma_buffer_attr_t dma_attr; const efx_nic_cfg_t *encp; unsigned char *bytes; unsigned int n; int err, rc; SFXGE_OBJ_CHECK(smp, sfxge_mac_t); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_UNINITIALIZED); smp->sm_sp = sp; encp = efx_nic_cfg_get(sp->s_enp); smp->sm_link_poll_reqd = (~encp->enc_features & EFX_FEATURE_LINK_EVENTS); smp->sm_mac_stats_timer_reqd = (~encp->enc_features & EFX_FEATURE_PERIODIC_MAC_STATS); mutex_init(&(smp->sm_lock), NULL, MUTEX_DRIVER, DDI_INTR_PRI(sp->s_intr.si_intr_pri)); cv_init(&(smp->sm_link_poll_kv), NULL, CV_DRIVER, NULL); /* Create link poll taskq */ smp->sm_tqp = ddi_taskq_create(dip, "mac_tq", 1, TASKQ_DEFAULTPRI, 0); if (smp->sm_tqp == NULL) { rc = ENOMEM; goto fail1; } if ((rc = sfxge_phy_init(sp)) != 0) goto fail2; dma_attr.sdba_dip = dip; dma_attr.sdba_dattrp = &sfxge_mac_dma_attr; dma_attr.sdba_callback = DDI_DMA_SLEEP; dma_attr.sdba_length = EFX_MAC_STATS_SIZE; dma_attr.sdba_memflags = DDI_DMA_CONSISTENT; dma_attr.sdba_devaccp = &sfxge_mac_devacc; dma_attr.sdba_bindflags = DDI_DMA_READ | DDI_DMA_CONSISTENT; dma_attr.sdba_maxcookies = 1; dma_attr.sdba_zeroinit = B_TRUE; if ((rc = sfxge_dma_buffer_create(esmp, &dma_attr)) != 0) goto fail3; /* Set the initial flow control values */ smp->sm_fcntl = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE; /* * Determine the 'burnt-in' MAC address: * * A: if the "mac-address" property is set on our device node use that. * B: otherwise, use the value from NVRAM. */ /* A: property */ err = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "mac-address", &bytes, &n); switch (err) { case DDI_PROP_SUCCESS: if (n == ETHERADDRL) { bcopy(bytes, smp->sm_bia, ETHERADDRL); goto done; } ddi_prop_free(bytes); break; default: break; } /* B: NVRAM */ bcopy(encp->enc_mac_addr, smp->sm_bia, ETHERADDRL); done: /* Initialize the statistics */ if ((rc = sfxge_mac_kstat_init(sp)) != 0) goto fail4; if ((rc = sfxge_phy_kstat_init(sp)) != 0) goto fail5; smp->sm_state = SFXGE_MAC_INITIALIZED; return (0); fail5: DTRACE_PROBE(fail5); sfxge_mac_kstat_fini(sp); fail4: DTRACE_PROBE(fail4); /* Tear down DMA setup */ sfxge_dma_buffer_destroy(esmp); fail3: DTRACE_PROBE(fail3); sfxge_phy_fini(sp); fail2: DTRACE_PROBE(fail2); /* Destroy the link poll taskq */ ddi_taskq_destroy(smp->sm_tqp); smp->sm_tqp = NULL; fail1: DTRACE_PROBE1(fail1, int, rc); cv_destroy(&(smp->sm_link_poll_kv)); mutex_destroy(&(smp->sm_lock)); smp->sm_sp = NULL; return (rc); } static int sfxge_mac_filter_apply(sfxge_t *sp) { efx_nic_t *enp = sp->s_enp; sfxge_mac_t *smp = &(sp->s_mac); int rc; ASSERT(mutex_owned(&(smp->sm_lock))); if (smp->sm_state == SFXGE_MAC_STARTED) { boolean_t all_unicst; boolean_t mulcst; boolean_t all_mulcst; boolean_t brdcst; all_unicst = (smp->sm_promisc == SFXGE_PROMISC_ALL_PHYS); mulcst = (smp->sm_mcast_count > 0); all_mulcst = (smp->sm_promisc >= SFXGE_PROMISC_ALL_MULTI); brdcst = B_TRUE; if ((rc = efx_mac_filter_set(enp, all_unicst, mulcst, all_mulcst, brdcst)) != 0) { goto fail1; } if ((rc = efx_mac_multicast_list_set(enp, smp->sm_mcast_addr, smp->sm_mcast_count)) != 0) goto fail2; } return (0); fail2: DTRACE_PROBE(fail2); fail1: DTRACE_PROBE1(fail1, int, rc); return (rc); } int sfxge_mac_start(sfxge_t *sp, boolean_t restart) { sfxge_mac_t *smp = &(sp->s_mac); efsys_mem_t *esmp = &(smp->sm_mem); efx_nic_t *enp = sp->s_enp; size_t pdu; int rc; mutex_enter(&(smp->sm_lock)); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED); if ((rc = efx_port_init(enp)) != 0) goto fail1; /* * Set up the advertised capabilities that may have been asked for * before the call to efx_port_init(). */ if ((rc = sfxge_phy_cap_apply(sp, !restart)) != 0) goto fail2; /* Set the SDU */ pdu = EFX_MAC_PDU(sp->s_mtu); if ((rc = efx_mac_pdu_set(enp, pdu)) != 0) goto fail3; if ((rc = efx_mac_fcntl_set(enp, smp->sm_fcntl, B_TRUE)) != 0) goto fail4; /* Set the unicast address */ if ((rc = efx_mac_addr_set(enp, (smp->sm_laa_valid) ? smp->sm_laa : smp->sm_bia)) != 0) goto fail5; if ((rc = sfxge_mac_filter_apply(sp)) != 0) goto fail6; if (!smp->sm_mac_stats_timer_reqd) { if ((rc = efx_mac_stats_periodic(enp, esmp, SFXGE_MAC_POLL_PERIOD_MS, B_FALSE)) != 0) goto fail7; } if ((rc = efx_mac_drain(enp, B_FALSE)) != 0) goto fail8; smp->sm_state = SFXGE_MAC_STARTED; /* * Start link state polling. For hardware that reports link change * events we still poll once to update the initial link state. */ sfxge_mac_poll_start(sp); mutex_exit(&(smp->sm_lock)); return (0); fail8: DTRACE_PROBE(fail8); (void) efx_mac_stats_periodic(enp, esmp, 0, B_FALSE); fail7: DTRACE_PROBE(fail7); fail6: DTRACE_PROBE(fail6); fail5: DTRACE_PROBE(fail5); fail4: DTRACE_PROBE(fail4); fail3: DTRACE_PROBE(fail3); fail2: DTRACE_PROBE(fail2); efx_port_fini(enp); fail1: DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } static void sfxge_mac_link_update_locked(sfxge_t *sp, efx_link_mode_t mode) { sfxge_mac_t *smp = &(sp->s_mac); const char *change, *duplex; char info[sizeof (": now 10000Mbps FULL duplex")]; ASSERT(mutex_owned(&(smp->sm_lock))); if (smp->sm_state != SFXGE_MAC_STARTED) return; if (smp->sm_link_mode == mode) return; smp->sm_link_mode = mode; smp->sm_link_up = B_TRUE; switch (smp->sm_link_mode) { case EFX_LINK_UNKNOWN: case EFX_LINK_DOWN: smp->sm_link_speed = 0; smp->sm_link_duplex = SFXGE_LINK_DUPLEX_UNKNOWN; smp->sm_link_up = B_FALSE; break; case EFX_LINK_10HDX: case EFX_LINK_10FDX: smp->sm_link_speed = 10; smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_10HDX) ? SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL; break; case EFX_LINK_100HDX: case EFX_LINK_100FDX: smp->sm_link_speed = 100; smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_100HDX) ? SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL; break; case EFX_LINK_1000HDX: case EFX_LINK_1000FDX: smp->sm_link_speed = 1000; smp->sm_link_duplex = (smp->sm_link_mode == EFX_LINK_1000HDX) ? SFXGE_LINK_DUPLEX_HALF : SFXGE_LINK_DUPLEX_FULL; break; case EFX_LINK_10000FDX: smp->sm_link_speed = 10000; smp->sm_link_duplex = SFXGE_LINK_DUPLEX_FULL; break; case EFX_LINK_40000FDX: smp->sm_link_speed = 40000; smp->sm_link_duplex = SFXGE_LINK_DUPLEX_FULL; break; default: ASSERT(B_FALSE); break; } duplex = (smp->sm_link_duplex == SFXGE_LINK_DUPLEX_FULL) ? "full" : "half"; change = (smp->sm_link_up) ? "UP" : "DOWN"; (void) snprintf(info, sizeof (info), ": now %dMbps %s duplex", smp->sm_link_speed, duplex); dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "Link %s%s", change, smp->sm_link_up ? info : ""); /* Push link state update to the OS */ sfxge_gld_link_update(sp); } void sfxge_mac_link_update(sfxge_t *sp, efx_link_mode_t mode) { sfxge_mac_t *smp = &(sp->s_mac); mutex_enter(&(smp->sm_lock)); sfxge_mac_link_update_locked(sp, mode); mutex_exit(&(smp->sm_lock)); } void sfxge_mac_link_check(sfxge_t *sp, boolean_t *upp) { sfxge_mac_t *smp = &(sp->s_mac); mutex_enter(&(smp->sm_lock)); *upp = smp->sm_link_up; mutex_exit(&(smp->sm_lock)); } void sfxge_mac_link_speed_get(sfxge_t *sp, unsigned int *speedp) { sfxge_mac_t *smp = &(sp->s_mac); mutex_enter(&(smp->sm_lock)); *speedp = smp->sm_link_speed; mutex_exit(&(smp->sm_lock)); } void sfxge_mac_link_duplex_get(sfxge_t *sp, sfxge_link_duplex_t *duplexp) { sfxge_mac_t *smp = &(sp->s_mac); mutex_enter(&(smp->sm_lock)); *duplexp = smp->sm_link_duplex; mutex_exit(&(smp->sm_lock)); } void sfxge_mac_fcntl_get(sfxge_t *sp, unsigned int *fcntlp) { sfxge_mac_t *smp = &(sp->s_mac); mutex_enter(&(smp->sm_lock)); *fcntlp = smp->sm_fcntl; mutex_exit(&(smp->sm_lock)); } int sfxge_mac_fcntl_set(sfxge_t *sp, unsigned int fcntl) { sfxge_mac_t *smp = &(sp->s_mac); int rc; mutex_enter(&(smp->sm_lock)); if (smp->sm_fcntl == fcntl) goto done; smp->sm_fcntl = fcntl; if (smp->sm_state != SFXGE_MAC_STARTED) goto done; if ((rc = efx_mac_fcntl_set(sp->s_enp, smp->sm_fcntl, B_TRUE)) != 0) goto fail1; done: mutex_exit(&(smp->sm_lock)); return (0); fail1: DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } int sfxge_mac_unicst_get(sfxge_t *sp, sfxge_unicst_type_t type, uint8_t *addr) { sfxge_mac_t *smp = &(sp->s_mac); int rc; if (type >= SFXGE_UNICST_NTYPES) { rc = EINVAL; goto fail1; } mutex_enter(&(smp->sm_lock)); if (smp->sm_state != SFXGE_MAC_INITIALIZED && smp->sm_state != SFXGE_MAC_STARTED) { rc = EFAULT; goto fail2; } switch (type) { case SFXGE_UNICST_BIA: bcopy(smp->sm_bia, addr, ETHERADDRL); break; case SFXGE_UNICST_LAA: if (!(smp->sm_laa_valid)) { rc = ENOENT; goto fail3; } bcopy(smp->sm_laa, addr, ETHERADDRL); break; default: ASSERT(B_FALSE); break; } mutex_exit(&(smp->sm_lock)); return (0); fail3: DTRACE_PROBE(fail3); fail2: DTRACE_PROBE(fail2); mutex_exit(&(smp->sm_lock)); fail1: DTRACE_PROBE1(fail1, int, rc); return (rc); } int sfxge_mac_unicst_set(sfxge_t *sp, uint8_t *addr) { sfxge_mac_t *smp = &(sp->s_mac); efx_nic_t *enp = sp->s_enp; boolean_t old_mac_valid; uint8_t old_mac[ETHERADDRL]; int rc; mutex_enter(&(smp->sm_lock)); old_mac_valid = smp->sm_laa_valid; if (old_mac_valid) bcopy(smp->sm_laa, old_mac, ETHERADDRL); bcopy(addr, smp->sm_laa, ETHERADDRL); smp->sm_laa_valid = B_TRUE; if (smp->sm_state != SFXGE_MAC_STARTED) goto done; if (efx_nic_cfg_get(enp)->enc_allow_set_mac_with_installed_filters) { if ((rc = efx_mac_addr_set(enp, smp->sm_laa)) != 0) { dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "unable to set unicast MAC filter"); goto fail1; } } else { /* Older EF10 firmware requires a device start */ mutex_exit(&smp->sm_lock); sfxge_stop(sp); if ((rc = sfxge_start(sp, B_TRUE)) != 0) { dev_err(sp->s_dip, CE_NOTE, SFXGE_CMN_ERR "unable to restart with a new MAC"); mutex_enter(&(smp->sm_lock)); goto fail1; } mutex_enter(&smp->sm_lock); } if ((rc = efx_mac_addr_set(enp, smp->sm_laa)) != 0) goto fail1; done: mutex_exit(&(smp->sm_lock)); return (0); fail1: if (old_mac_valid) bcopy(old_mac, smp->sm_laa, ETHERADDRL); else smp->sm_laa_valid = B_FALSE; DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } int sfxge_mac_promisc_set(sfxge_t *sp, sfxge_promisc_type_t promisc) { sfxge_mac_t *smp = &(sp->s_mac); int rc; mutex_enter(&(smp->sm_lock)); if (smp->sm_promisc == promisc) goto done; smp->sm_promisc = promisc; if ((rc = sfxge_mac_filter_apply(sp)) != 0) goto fail1; done: mutex_exit(&(smp->sm_lock)); return (0); fail1: DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } int sfxge_mac_multicst_add(sfxge_t *sp, const uint8_t *addr) { sfxge_mac_t *smp = &(sp->s_mac); int i; int rc; mutex_enter(&(smp->sm_lock)); if ((addr[0] & 0x1) == 0) { rc = EINVAL; goto fail1; } /* Check if the address is already in the list */ i = 0; while (i < smp->sm_mcast_count) { if (bcmp(smp->sm_mcast_addr + (i * ETHERADDRL), addr, ETHERADDRL) == 0) goto done; else i++; } if (smp->sm_mcast_count >= EFX_MAC_MULTICAST_LIST_MAX) { rc = ENOENT; goto fail1; } /* Add to the list */ bcopy(addr, smp->sm_mcast_addr + (smp->sm_mcast_count++ * ETHERADDRL), ETHERADDRL); if ((rc = sfxge_mac_filter_apply(sp)) != 0) goto fail2; done: mutex_exit(&(smp->sm_lock)); return (0); fail2: DTRACE_PROBE(fail2); smp->sm_mcast_count--; fail1: DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } int sfxge_mac_multicst_remove(sfxge_t *sp, const uint8_t *addr) { sfxge_mac_t *smp = &(sp->s_mac); int i; int rc; mutex_enter(&(smp->sm_lock)); i = 0; while (i < smp->sm_mcast_count) { if (bcmp(smp->sm_mcast_addr + (i * ETHERADDRL), addr, ETHERADDRL) == 0) { (void) memmove(smp->sm_mcast_addr + (i * ETHERADDRL), smp->sm_mcast_addr + ((i + 1) * ETHERADDRL), (smp->sm_mcast_count - (i + 1)) * ETHERADDRL); smp->sm_mcast_count--; } else i++; } if ((rc = sfxge_mac_filter_apply(sp)) != 0) goto fail1; mutex_exit(&(smp->sm_lock)); return (0); fail1: DTRACE_PROBE1(fail1, int, rc); mutex_exit(&(smp->sm_lock)); return (rc); } void sfxge_mac_stop(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); efx_nic_t *enp = sp->s_enp; efsys_mem_t *esmp = &(smp->sm_mem); mutex_enter(&(smp->sm_lock)); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_STARTED); ASSERT3P(smp->sm_sp, ==, sp); smp->sm_state = SFXGE_MAC_INITIALIZED; /* If stopping in response to an MC reboot this may fail */ if (!smp->sm_mac_stats_timer_reqd) (void) efx_mac_stats_periodic(enp, esmp, 0, B_FALSE); sfxge_mac_poll_stop(sp); smp->sm_lbolt = 0; smp->sm_link_up = B_FALSE; smp->sm_link_speed = 0; smp->sm_link_duplex = SFXGE_LINK_DUPLEX_UNKNOWN; /* This may call MCDI */ (void) efx_mac_drain(enp, B_TRUE); smp->sm_link_mode = EFX_LINK_UNKNOWN; efx_port_fini(enp); mutex_exit(&(smp->sm_lock)); } void sfxge_mac_fini(sfxge_t *sp) { sfxge_mac_t *smp = &(sp->s_mac); efsys_mem_t *esmp = &(smp->sm_mem); ASSERT3U(smp->sm_state, ==, SFXGE_MAC_INITIALIZED); ASSERT3P(smp->sm_sp, ==, sp); /* Tear down the statistics */ sfxge_phy_kstat_fini(sp); sfxge_mac_kstat_fini(sp); smp->sm_state = SFXGE_MAC_UNINITIALIZED; smp->sm_link_mode = EFX_LINK_UNKNOWN; smp->sm_promisc = SFXGE_PROMISC_OFF; bzero(smp->sm_mcast_addr, sizeof (smp->sm_mcast_addr)); smp->sm_mcast_count = 0; bzero(smp->sm_laa, ETHERADDRL); smp->sm_laa_valid = B_FALSE; bzero(smp->sm_bia, ETHERADDRL); smp->sm_fcntl = 0; /* Finish with PHY DMA memory */ sfxge_phy_fini(sp); /* Teardown the DMA */ sfxge_dma_buffer_destroy(esmp); /* Destroy the link poll taskq */ ddi_taskq_destroy(smp->sm_tqp); smp->sm_tqp = NULL; mutex_destroy(&(smp->sm_lock)); smp->sm_sp = NULL; }