#include "if_em.h"
#include <sys/sbuf.h>
#define IGBV_82576_QUEUES 2
#define IGBV_I350_QUEUES 1
#define IGBV_MAX_MAC_FILTERS 3
#define IGBV_QUEUE_DISABLE_BUSY_RETRIES 10
#define IGBV_QUEUE_DISABLE_DELAY_US 10
#define IGBV_QUEUE_DISABLE_PAUSE (100 * SBT_1US)
#define IGBV_QUEUE_DISABLE_RETRIES 20
#define IGBV_QUEUE_SANITIZE_ATTEMPTS 3
#define IGBV_VLAN_RETRY_BATCH 4
#define IGBV_VLAN_RETRY_WINDOW (8 * SBT_1S)
static const struct timeval igbv_queue_log_interval = { 2, 0 };
static const sbintime_t igbv_queue_retry_delay[] = {
100 * SBT_1MS,
500 * SBT_1MS,
};
_Static_assert(nitems(igbv_queue_retry_delay) + 1 ==
IGBV_QUEUE_SANITIZE_ATTEMPTS, "missing queue retry delay");
struct igb_vf_uc_addr_list {
struct e1000_softc *sc;
u8 addrs[IGBV_MAX_MAC_FILTERS][ETHER_ADDR_LEN];
};
static bool igbv_tx_pending(struct e1000_softc *);
static bool igbv_vlan_retry_pending(const struct e1000_softc *);
static void igbv_vlan_retry_tick(struct e1000_softc *);
static void
igbv_queue_retry_callout(void *arg)
{
struct e1000_softc *sc;
if_t ifp;
sc = arg;
if (atomic_readandclear_32(&sc->vf_queue_retry_pending) == 0)
return;
ifp = iflib_get_ifp(sc->ctx);
if ((if_getflags(ifp) & IFF_UP) == 0) {
atomic_set_32(&sc->vf_queue_retry_new_epoch, 1);
return;
}
iflib_request_reset(sc->ctx);
iflib_admin_intr_deferred(sc->ctx);
}
void
igbv_queue_retry_detach(struct e1000_softc *sc)
{
if (!sc->vf_queue_retry_initialized)
return;
atomic_readandclear_32(&sc->vf_queue_retry_pending);
callout_drain(&sc->vf_queue_retry);
sc->vf_queue_retry_initialized = false;
}
void
igbv_queue_retry_stop(struct e1000_softc *sc)
{
if (!sc->vf_queue_retry_initialized)
return;
if (atomic_readandclear_32(&sc->vf_queue_retry_pending) != 0)
atomic_set_32(&sc->vf_queue_retry_new_epoch, 1);
callout_stop(&sc->vf_queue_retry);
}
void
igbv_queue_retry_prepare(struct e1000_softc *sc)
{
bool new_epoch;
new_epoch =
atomic_readandclear_32(&sc->vf_queue_retry_new_epoch) != 0;
if (!sc->vf_queue_gave_up && !new_epoch)
return;
sc->vf_queue_failures = 0;
sc->vf_queue_gave_up = false;
}
static void
igbv_queue_retry_succeeded(struct e1000_softc *sc)
{
atomic_readandclear_32(&sc->vf_queue_retry_pending);
atomic_readandclear_32(&sc->vf_queue_retry_new_epoch);
if (sc->vf_queue_retry_initialized)
callout_stop(&sc->vf_queue_retry);
sc->vf_queue_failures = 0;
sc->vf_queue_gave_up = false;
}
void
igbv_queue_retry_failed(if_ctx_t ctx)
{
struct e1000_softc *sc;
sbintime_t delay;
sc = iflib_get_softc(ctx);
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
if (sc->vf_queue_failures < IGBV_QUEUE_SANITIZE_ATTEMPTS)
sc->vf_queue_failures++;
if (sc->vf_queue_failures < IGBV_QUEUE_SANITIZE_ATTEMPTS) {
delay = igbv_queue_retry_delay[sc->vf_queue_failures - 1];
atomic_set_32(&sc->vf_queue_retry_pending, 1);
callout_reset_sbt(&sc->vf_queue_retry, delay, 0,
igbv_queue_retry_callout, sc, C_PREL(1));
} else if (!sc->vf_queue_gave_up) {
atomic_readandclear_32(&sc->vf_queue_retry_pending);
callout_stop(&sc->vf_queue_retry);
sc->vf_queue_gave_up = true;
device_printf(sc->dev,
"retained VF queues remained active after %u attempts; "
"interface left down; toggle it down/up to retry\n",
sc->vf_queue_failures);
}
iflib_link_state_change(ctx, LINK_STATE_DOWN, 0);
iflib_admin_intr_deferred(ctx);
}
void
igbv_vlan_retry_add(struct e1000_softc *sc, u16 vid)
{
bool pending;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
pending = igbv_vlan_retry_pending(sc);
sc->vf_vfta_retry[vid >> 5] |= 1U << (vid & 0x1f);
if (!pending)
sc->vf_vlan_retry_deadline =
getsbinuptime() + IGBV_VLAN_RETRY_WINDOW;
}
void
igbv_vlan_retry_clear(struct e1000_softc *sc, u16 vid)
{
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
sc->vf_vfta_retry[vid >> 5] &= ~(1U << (vid & 0x1f));
}
static bool
igbv_vlan_retry_pending(const struct e1000_softc *sc)
{
int i;
for (i = 0; i < EM_VFTA_SIZE; i++)
if (sc->vf_vfta_retry[i] != 0)
return (true);
return (false);
}
static void
igbv_vlan_retry_tick(struct e1000_softc *sc)
{
u32 bit;
u16 vid;
int attempts, i, remaining;
if (!igbv_vlan_retry_pending(sc)) {
sc->vf_vlan_retry_deadline = 0;
return;
}
if (getsbinuptime() >= sc->vf_vlan_retry_deadline) {
remaining = 0;
for (i = 0; i < EM_VFTA_SIZE; i++)
remaining += bitcount32(sc->vf_vfta_retry[i]);
memset(sc->vf_vfta_retry, 0, sizeof(sc->vf_vfta_retry));
sc->vf_vlan_retry_deadline = 0;
device_printf(sc->dev,
"VF VLAN restore retries exhausted for %d VIDs\n",
remaining);
return;
}
for (attempts = 0, i = 0;
attempts < IGBV_VLAN_RETRY_BATCH && i < 4096; i++) {
vid = sc->vf_vlan_retry_cursor;
sc->vf_vlan_retry_cursor = (vid + 1) & 0xfff;
bit = 1U << (vid & 0x1f);
if ((sc->vf_vfta_retry[vid >> 5] & bit) == 0)
continue;
attempts++;
if ((sc->shadow_vfta[vid >> 5] & bit) == 0 ||
e1000_vfta_set_vf(&sc->hw, vid, true) ==
E1000_SUCCESS)
sc->vf_vfta_retry[vid >> 5] &= ~bit;
}
if (!igbv_vlan_retry_pending(sc))
sc->vf_vlan_retry_deadline = 0;
}
int
igbv_if_attach_pre(if_ctx_t ctx)
{
struct e1000_softc *sc;
device_t dev;
int error;
dev = iflib_get_dev(ctx);
if (pci_msix_count(dev) < 2) {
device_printf(dev, "VF operation requires two MSI-X vectors\n");
return (ENXIO);
}
error = em_if_attach_pre(ctx);
if (error != 0)
return (error);
sc = iflib_get_softc(ctx);
callout_init(&sc->vf_queue_retry, 1);
sc->vf_queue_retry_initialized = true;
KASSERT(sc->vf_ifp &&
(iflib_get_sctx(ctx)->isc_flags & IFLIB_IS_VF) != 0,
("%s: igbv attached without VF policy", __func__));
return (0);
}
int
igbv_if_attach_post(if_ctx_t ctx)
{
struct e1000_softc *sc;
int error;
sc = iflib_get_softc(ctx);
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
if (sc->intr_type != IFLIB_INTR_MSIX) {
device_printf(sc->dev, "VF operation requires MSI-X\n");
return (ENXIO);
}
error = em_if_attach_post(ctx);
if (error != 0)
return (error);
em_add_device_sysctls(sc);
return (0);
}
int
igbv_if_media_change(if_ctx_t ctx __unused)
{
return (EOPNOTSUPP);
}
void
igbv_if_update_admin_status(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
device_t dev;
bool link_check, timer_tick;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
dev = iflib_get_dev(ctx);
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
if (!sc->vf_queues_sanitized) {
igbv_if_intr_disable(ctx);
if_setdrvflagbits(iflib_get_ifp(ctx), IFF_DRV_OACTIVE,
IFF_DRV_RUNNING);
return;
}
if (!sc->vf_reset_pending &&
atomic_readandclear_32(&sc->promisc_pending) != 0)
(void)em_if_set_promisc_impl(ctx,
if_getflags(iflib_get_ifp(ctx)));
if (e1000_check_for_link(hw) != E1000_SUCCESS &&
!sc->vf_reset_pending) {
sc->vf_reset_pending = true;
iflib_request_reset(ctx);
iflib_admin_intr_deferred(ctx);
}
link_check = !hw->mac.get_link_status;
if (link_check &&
(sc->link_state == EM_LINK_STATE_DOWN ||
sc->link_state == EM_LINK_STATE_DOWN_RESET_PENDING)) {
e1000_get_speed_and_duplex(hw, &sc->link_speed,
&sc->link_duplex);
if (bootverbose)
device_printf(dev, "Link is up %d Mbps %s\n",
sc->link_speed,
sc->link_duplex == FULL_DUPLEX ?
"Full Duplex" : "Half Duplex");
sc->link_state = EM_LINK_STATE_UP;
iflib_link_state_change(ctx, LINK_STATE_UP,
IF_Mbps(sc->link_speed));
} else if (!link_check &&
(sc->link_state == EM_LINK_STATE_UP ||
sc->link_state == EM_LINK_STATE_UP_RESET_PENDING)) {
sc->link_speed = 0;
sc->link_duplex = 0;
sc->link_state = EM_LINK_STATE_DOWN;
iflib_link_state_change(ctx, LINK_STATE_DOWN, 0);
}
if (!link_check && !sc->vf_reset_pending && igbv_tx_pending(sc)) {
sc->vf_reset_pending = true;
iflib_request_reset(ctx);
iflib_admin_intr_deferred(ctx);
}
timer_tick = !sc->vf_reset_pending &&
atomic_readandclear_32(&sc->stats_pending) != 0;
if (timer_tick) {
em_update_stats_counters(sc);
igbv_vlan_retry_tick(sc);
}
}
static bool
igbv_tx_pending(struct e1000_softc *sc)
{
struct tx_ring *txr;
u32 head, tail;
for (int i = 0; i < sc->tx_num_queues; i++) {
txr = &sc->tx_queues[i].txr;
head = E1000_READ_REG(&sc->hw, E1000_TDH(txr->me));
tail = E1000_READ_REG(&sc->hw, E1000_TDT(txr->me));
if (head != tail)
return (true);
}
return (false);
}
static bool
igbv_sanitize_queues(struct e1000_softc *sc)
{
struct e1000_hw *hw;
u32 rxdctl, txdctl;
int i, nqueues, retry;
hw = &sc->hw;
switch (hw->mac.type) {
case e1000_vfadapt:
nqueues = IGBV_82576_QUEUES;
break;
case e1000_vfadapt_i350:
nqueues = IGBV_I350_QUEUES;
break;
default:
return (true);
}
for (i = 0; i < nqueues; i++) {
E1000_WRITE_REG(hw, E1000_RXDCTL(i), 0);
E1000_WRITE_REG(hw, E1000_TXDCTL(i), 0);
}
E1000_WRITE_FLUSH(hw);
for (retry = 0; retry < IGBV_QUEUE_DISABLE_RETRIES; retry++) {
for (i = 0; i < nqueues; i++) {
rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(i));
txdctl = E1000_READ_REG(hw, E1000_TXDCTL(i));
if ((rxdctl & E1000_RXDCTL_QUEUE_ENABLE) != 0 ||
(txdctl & E1000_TXDCTL_QUEUE_ENABLE) != 0)
break;
}
if (i == nqueues)
break;
if (retry + 1 < IGBV_QUEUE_DISABLE_RETRIES) {
if (retry < IGBV_QUEUE_DISABLE_BUSY_RETRIES)
DELAY(IGBV_QUEUE_DISABLE_DELAY_US);
else
pause_sbt("igbvqds",
IGBV_QUEUE_DISABLE_PAUSE, 0,
C_PREL(1));
}
}
if (retry == IGBV_QUEUE_DISABLE_RETRIES) {
if (ratecheck(&sc->vf_last_queue_log,
&igbv_queue_log_interval))
device_printf(sc->dev,
"could not disable retained VF queues; "
"reset deferred\n");
return (false);
}
for (i = 0; i < nqueues; i++) {
E1000_WRITE_REG(hw, E1000_SRRCTL(i), 0);
E1000_WRITE_REG(hw, E1000_DCA_RXCTRL(i), 0);
E1000_WRITE_REG(hw, E1000_TDWBAL(i), 0);
E1000_WRITE_REG(hw, E1000_TDWBAH(i), 0);
E1000_WRITE_REG(hw, E1000_DCA_TXCTRL(i), 0);
}
E1000_WRITE_REG(hw, E1000_VFPSRTYPE, 0);
E1000_WRITE_FLUSH(hw);
return (true);
}
bool
igbv_reset(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
s32 error;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
sc->pba = 0;
hw->fc = (struct e1000_fc_info){
.current_mode = e1000_fc_none,
.requested_mode = e1000_fc_none,
};
error = e1000_reset_hw(hw);
sc->vf_queues_sanitized = igbv_sanitize_queues(sc);
if (!sc->vf_queues_sanitized) {
e1000_check_for_link(hw);
return (false);
}
igbv_queue_retry_succeeded(sc);
if (error != E1000_SUCCESS) {
e1000_check_for_link(hw);
return (false);
}
memset(sc->vf_vfta_stale, 0, sizeof(sc->vf_vfta_stale));
memset(sc->vf_vfta_retry, 0, sizeof(sc->vf_vfta_retry));
sc->vf_vlan_retry_deadline = 0;
sc->vf_vlan_retry_cursor = 0;
if (e1000_init_hw(hw) < 0) {
device_printf(sc->dev, "Hardware Initialization Failed\n");
return (false);
}
e1000_check_for_link(hw);
return (true);
}
void
igbv_initialize_transmit_unit(if_ctx_t ctx)
{
KASSERT(((struct e1000_softc *)iflib_get_softc(ctx))->vf_ifp,
("%s called for a PF", __func__));
em_initialize_transmit_rings(ctx);
}
void
igbv_initialize_receive_unit(if_ctx_t ctx)
{
KASSERT(((struct e1000_softc *)iflib_get_softc(ctx))->vf_ifp,
("%s called for a PF", __func__));
igb_initialize_receive_rings(ctx, true);
}
void
igbv_if_intr_enable(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
u32 mask;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
if (!sc->vf_queues_sanitized)
return;
mask = sc->que_mask | sc->link_mask;
E1000_WRITE_REG(hw, E1000_EIAC, mask);
E1000_WRITE_REG(hw, E1000_EIAM, mask);
E1000_WRITE_REG(hw, E1000_EIMS, mask);
E1000_WRITE_FLUSH(hw);
}
void
igbv_if_intr_disable(if_ctx_t ctx)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
sc = iflib_get_softc(ctx);
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
E1000_WRITE_REG(hw, E1000_EIMC, 0xffffffff);
E1000_WRITE_REG(hw, E1000_EIAC, 0);
E1000_WRITE_FLUSH(hw);
}
int
igbv_get_regs(SYSCTL_HANDLER_ARGS)
{
struct e1000_softc *sc;
struct e1000_hw *hw;
struct sbuf *sb;
int error;
sc = (struct e1000_softc *)arg1;
hw = &sc->hw;
KASSERT(sc->vf_ifp, ("%s called for a PF", __func__));
sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
if (sb == NULL)
return (ENOMEM);
sbuf_printf(sb, "VF Registers\n");
sbuf_printf(sb, "\tVTCTRL\t %08x\n",
E1000_READ_REG(hw, E1000_CTRL));
sbuf_printf(sb, "\tSTATUS\t %08x\n",
E1000_READ_REG(hw, E1000_STATUS));
sbuf_printf(sb, "\tRDLEN\t %08x\n",
E1000_READ_REG(hw, E1000_RDLEN(0)));
sbuf_printf(sb, "\tRDH\t %08x\n",
E1000_READ_REG(hw, E1000_RDH(0)));
sbuf_printf(sb, "\tRDT\t %08x\n",
E1000_READ_REG(hw, E1000_RDT(0)));
sbuf_printf(sb, "\tTDLEN\t %08x\n",
E1000_READ_REG(hw, E1000_TDLEN(0)));
sbuf_printf(sb, "\tTDH\t %08x\n",
E1000_READ_REG(hw, E1000_TDH(0)));
sbuf_printf(sb, "\tTDT\t %08x\n",
E1000_READ_REG(hw, E1000_TDT(0)));
error = sbuf_finish(sb);
sbuf_delete(sb);
return (error);
}
static u_int
igbv_copy_uc_addr(void *arg, struct sockaddr_dl *sdl, u_int idx)
{
struct igb_vf_uc_addr_list *list;
const u8 *addr;
list = arg;
addr = (const u8 *)LLADDR(sdl);
if (memcmp(addr, list->sc->hw.mac.addr, ETHER_ADDR_LEN) == 0)
return (0);
if (idx < IGBV_MAX_MAC_FILTERS)
memcpy(list->addrs[idx], addr, ETHER_ADDR_LEN);
return (1);
}
void
igbv_update_uc_addr_list(struct e1000_softc *sc, if_t ifp)
{
struct igb_vf_uc_addr_list list = {
.sc = sc,
};
u_int count;
count = if_foreach_lladdr(ifp, igbv_copy_uc_addr, &list);
if (count > IGBV_MAX_MAC_FILTERS) {
device_printf(sc->dev,
"too many secondary unicast addresses; maximum is %u\n",
IGBV_MAX_MAC_FILTERS);
}
if (count == 0 && !sc->vf_uc_filters_set)
return;
if (e1000_set_uc_addr_vf(&sc->hw, E1000_VF_MAC_FILTER_CLR,
sc->hw.mac.addr) != E1000_SUCCESS) {
device_printf(sc->dev,
"VF secondary unicast filter clear request failed\n");
return;
}
sc->vf_uc_filters_set = false;
if (count > IGBV_MAX_MAC_FILTERS)
return;
for (u_int i = 0; i < count; i++) {
if (e1000_set_uc_addr_vf(&sc->hw, E1000_VF_MAC_FILTER_ADD,
list.addrs[i]) != E1000_SUCCESS) {
device_printf(sc->dev,
"VF secondary unicast filter add request failed "
"for %6D\n", list.addrs[i], ":");
} else
sc->vf_uc_filters_set = true;
usec_delay(200);
}
}
void
igbv_reconcile_mac(struct e1000_softc *sc, if_t ifp)
{
u8 *lladdr;
if (!em_is_valid_ether_addr(sc->hw.mac.addr))
return;
lladdr = (u8 *)if_getlladdr(ifp);
if (memcmp(lladdr, sc->hw.mac.addr, ETHER_ADDR_LEN) == 0)
return;
device_printf(sc->dev,
"PF rejected or replaced the requested MAC; using %6D\n",
sc->hw.mac.addr, ":");
memcpy(lladdr, sc->hw.mac.addr, ETHER_ADDR_LEN);
CURVNET_SET_QUIET(if_getvnet(ifp));
EVENTHANDLER_INVOKE(iflladdr_event, ifp);
CURVNET_RESTORE();
}