diff options
Diffstat (limited to 'drivers/net')
364 files changed, 19197 insertions, 5789 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index befd67df08e1..0c5415b05ea9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -192,6 +192,23 @@ config GENEVE To compile this driver as a module, choose M here: the module will be called geneve. +config GTP + tristate "GPRS Tunneling Protocol datapath (GTP-U)" + depends on INET && NET_UDP_TUNNEL + select NET_IP_TUNNEL + ---help--- + This allows one to create gtp virtual interfaces that provide + the GPRS Tunneling Protocol datapath (GTP-U). This tunneling protocol + is used to prevent subscribers from accessing mobile carrier core + network infrastructure. This driver requires a userspace software that + implements the signaling protocol (GTP-C) to update its PDP context + base, such as OpenGGSN <http://git.osmocom.org/openggsn/). This + tunneling protocol is implemented according to the GSM TS 09.60 and + 3GPP TS 29.060 standards. + + To compile this drivers as a module, choose M here: the module + wil be called gtp. + config MACSEC tristate "IEEE 802.1AE MAC-level encryption (MACsec)" select CRYPTO diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1aa7cb845663..7336cbd3ef5d 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VXLAN) += vxlan.o obj-$(CONFIG_GENEVE) += geneve.o +obj-$(CONFIG_GTP) += gtp.o obj-$(CONFIG_NLMON) += nlmon.o obj-$(CONFIG_NET_VRF) += vrf.o diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 67977f15af25..11fe71278f40 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -35,8 +35,8 @@ #include <net/Space.h> /* A unified ethernet device probe. This is the easiest way to have every - ethernet adaptor have the name "eth[0123...]". - */ + * ethernet adaptor have the name "eth[0123...]". + */ struct devprobe2 { struct net_device *(*probe)(int unit); @@ -46,6 +46,7 @@ struct devprobe2 { static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) { struct net_device *dev; + for (; p->probe; p++) { if (autoprobe && p->status) continue; @@ -58,8 +59,7 @@ static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) return -ENODEV; } -/* - * ISA probes that touch addresses < 0x400 (including those that also +/* ISA probes that touch addresses < 0x400 (including those that also * look for EISA/PCI cards in addition to ISA cards). */ static struct devprobe2 isa_probes[] __initdata = { @@ -86,11 +86,11 @@ static struct devprobe2 isa_probes[] __initdata = { #endif #ifdef CONFIG_CS89x0 #ifndef CONFIG_CS89x0_PLATFORM - {cs89x0_probe, 0}, + {cs89x0_probe, 0}, #endif #endif -#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */ - {i82596_probe, 0}, +#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel */ + {i82596_probe, 0}, /* I82596 */ #endif #ifdef CONFIG_NI65 {ni65_probe, 0}, @@ -118,13 +118,12 @@ static struct devprobe2 m68k_probes[] __initdata = { {mac8390_probe, 0}, #endif #ifdef CONFIG_MAC89x0 - {mac89x0_probe, 0}, + {mac89x0_probe, 0}, #endif {NULL, 0}, }; -/* - * Unified ethernet device probe, segmented per architecture and +/* Unified ethernet device probe, segmented per architecture and * per bus interface. This drives the legacy devices only for now. */ @@ -135,7 +134,7 @@ static void __init ethif_probe2(int unit) if (base_addr == 1) return; - (void)( probe_list2(unit, m68k_probes, base_addr == 0) && + (void)(probe_list2(unit, m68k_probes, base_addr == 0) && probe_list2(unit, isa_probes, base_addr == 0)); } diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index 7f2a032c354c..1b2e9217ec78 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -861,7 +861,7 @@ static void cops_timeout(struct net_device *dev) } printk(KERN_WARNING "%s: Transmit timed out.\n", dev->name); cops_jumpstart(dev); /* Restart the card. */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 141c2a42d7ed..910c12e2638e 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -696,11 +696,17 @@ int can_change_mtu(struct net_device *dev, int new_mtu) /* allow change of MTU according to the CANFD ability of the device */ switch (new_mtu) { case CAN_MTU: + /* 'CANFD-only' controllers can not switch to CAN_MTU */ + if (priv->ctrlmode_static & CAN_CTRLMODE_FD) + return -EINVAL; + priv->ctrlmode &= ~CAN_CTRLMODE_FD; break; case CANFD_MTU: - if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD)) + /* check for potential CANFD ability */ + if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && + !(priv->ctrlmode_static & CAN_CTRLMODE_FD)) return -EINVAL; priv->ctrlmode |= CAN_CTRLMODE_FD; @@ -782,6 +788,35 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { = { .len = sizeof(struct can_bittiming_const) }, }; +static int can_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + bool is_can_fd = false; + + /* Make sure that valid CAN FD configurations always consist of + * - nominal/arbitration bittiming + * - data bittiming + * - control mode with CAN_CTRLMODE_FD set + */ + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); + + is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; + } + + if (is_can_fd) { + if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) + return -EOPNOTSUPP; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + if (!is_can_fd || !data[IFLA_CAN_BITTIMING]) + return -EOPNOTSUPP; + } + + return 0; +} + static int can_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -813,19 +848,31 @@ static int can_changelink(struct net_device *dev, if (data[IFLA_CAN_CTRLMODE]) { struct can_ctrlmode *cm; + u32 ctrlstatic; + u32 maskedflags; /* Do not allow changing controller mode while running */ if (dev->flags & IFF_UP) return -EBUSY; cm = nla_data(data[IFLA_CAN_CTRLMODE]); + ctrlstatic = priv->ctrlmode_static; + maskedflags = cm->flags & cm->mask; + + /* check whether provided bits are allowed to be passed */ + if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) + return -EOPNOTSUPP; + + /* do not check for static fd-non-iso if 'fd' is disabled */ + if (!(maskedflags & CAN_CTRLMODE_FD)) + ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; - /* check whether changed bits are allowed to be modified */ - if (cm->mask & ~priv->ctrlmode_supported) + /* make sure static options are provided by configuration */ + if ((maskedflags & ctrlstatic) != ctrlstatic) return -EOPNOTSUPP; /* clear bits to be modified and copy the flag values */ priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= (cm->flags & cm->mask); + priv->ctrlmode |= maskedflags; /* CAN_CTRLMODE_FD can only be set when driver supports FD */ if (priv->ctrlmode & CAN_CTRLMODE_FD) @@ -966,6 +1013,7 @@ static struct rtnl_link_ops can_link_ops __read_mostly = { .maxtype = IFLA_CAN_MAX, .policy = can_policy, .setup = can_setup, + .validate = can_validate, .newlink = can_newlink, .changelink = can_changelink, .get_size = can_get_size, diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c index a1bd54ffd31e..2d1d22eec750 100644 --- a/drivers/net/can/ifi_canfd/ifi_canfd.c +++ b/drivers/net/can/ifi_canfd/ifi_canfd.c @@ -34,6 +34,7 @@ #define IFI_CANFD_STCMD_LOOPBACK BIT(18) #define IFI_CANFD_STCMD_DISABLE_CANFD BIT(24) #define IFI_CANFD_STCMD_ENABLE_ISO BIT(25) +#define IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING BIT(26) #define IFI_CANFD_STCMD_NORMAL_MODE ((u32)BIT(31)) #define IFI_CANFD_RXSTCMD 0x4 @@ -51,7 +52,8 @@ #define IFI_CANFD_TXSTCMD_OVERFLOW BIT(13) #define IFI_CANFD_INTERRUPT 0xc -#define IFI_CANFD_INTERRUPT_ERROR_WARNING ((u32)BIT(1)) +#define IFI_CANFD_INTERRUPT_ERROR_WARNING BIT(1) +#define IFI_CANFD_INTERRUPT_ERROR_COUNTER BIT(10) #define IFI_CANFD_INTERRUPT_TXFIFO_EMPTY BIT(16) #define IFI_CANFD_INTERRUPT_TXFIFO_REMOVE BIT(22) #define IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY BIT(24) @@ -71,12 +73,12 @@ #define IFI_CANFD_TIME_TIMEB_OFF 0 #define IFI_CANFD_TIME_TIMEA_OFF 8 #define IFI_CANFD_TIME_PRESCALE_OFF 16 -#define IFI_CANFD_TIME_SJW_OFF_ISO 25 -#define IFI_CANFD_TIME_SJW_OFF_BOSCH 28 -#define IFI_CANFD_TIME_SET_SJW_BOSCH BIT(6) -#define IFI_CANFD_TIME_SET_TIMEB_BOSCH BIT(7) -#define IFI_CANFD_TIME_SET_PRESC_BOSCH BIT(14) -#define IFI_CANFD_TIME_SET_TIMEA_BOSCH BIT(15) +#define IFI_CANFD_TIME_SJW_OFF_7_9_8_8 25 +#define IFI_CANFD_TIME_SJW_OFF_4_12_6_6 28 +#define IFI_CANFD_TIME_SET_SJW_4_12_6_6 BIT(6) +#define IFI_CANFD_TIME_SET_TIMEB_4_12_6_6 BIT(7) +#define IFI_CANFD_TIME_SET_PRESC_4_12_6_6 BIT(14) +#define IFI_CANFD_TIME_SET_TIMEA_4_12_6_6 BIT(15) #define IFI_CANFD_TDELAY 0x1c @@ -102,7 +104,26 @@ #define IFI_CANFD_RES1 0x40 -#define IFI_CANFD_RES2 0x44 +#define IFI_CANFD_ERROR_CTR 0x44 +#define IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC 0x21302899 +#define IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST BIT(0) +#define IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST BIT(1) +#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST BIT(2) +#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST BIT(3) +#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST BIT(4) +#define IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST BIT(5) +#define IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST BIT(6) +#define IFI_CANFD_ERROR_CTR_OVERLOAD_ALL BIT(8) +#define IFI_CANFD_ERROR_CTR_ACK_ERROR_ALL BIT(9) +#define IFI_CANFD_ERROR_CTR_BIT0_ERROR_ALL BIT(10) +#define IFI_CANFD_ERROR_CTR_BIT1_ERROR_ALL BIT(11) +#define IFI_CANFD_ERROR_CTR_STUFF_ERROR_ALL BIT(12) +#define IFI_CANFD_ERROR_CTR_CRC_ERROR_ALL BIT(13) +#define IFI_CANFD_ERROR_CTR_FORM_ERROR_ALL BIT(14) +#define IFI_CANFD_ERROR_CTR_BITPOSITION_OFFSET 16 +#define IFI_CANFD_ERROR_CTR_BITPOSITION_MASK 0xff +#define IFI_CANFD_ERROR_CTR_ER_RESET BIT(30) +#define IFI_CANFD_ERROR_CTR_ER_ENABLE ((u32)BIT(31)) #define IFI_CANFD_PAR 0x48 @@ -196,6 +217,8 @@ static void ifi_canfd_irq_enable(struct net_device *ndev, bool enable) if (enable) { enirq = IFI_CANFD_IRQMASK_TXFIFO_EMPTY | IFI_CANFD_IRQMASK_RXFIFO_NEMPTY; + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + enirq |= IFI_CANFD_INTERRUPT_ERROR_COUNTER; } writel(IFI_CANFD_IRQMASK_SET_ERR | @@ -334,6 +357,68 @@ static int ifi_canfd_handle_lost_msg(struct net_device *ndev) return 1; } +static int ifi_canfd_handle_lec_err(struct net_device *ndev, const u32 errctr) +{ + struct ifi_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + const u32 errmask = IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST | + IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST | + IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST; + + if (!(errctr & errmask)) /* No error happened. */ + return 0; + + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + /* Propagate the error condition to the CAN stack. */ + skb = alloc_can_err_skb(ndev, &cf); + if (unlikely(!skb)) + return 0; + + /* Read the error counter register and check for new errors. */ + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (errctr & IFI_CANFD_ERROR_CTR_OVERLOAD_FIRST) + cf->data[2] |= CAN_ERR_PROT_OVERLOAD; + + if (errctr & IFI_CANFD_ERROR_CTR_ACK_ERROR_FIRST) + cf->data[3] = CAN_ERR_PROT_LOC_ACK; + + if (errctr & IFI_CANFD_ERROR_CTR_BIT0_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_BIT0; + + if (errctr & IFI_CANFD_ERROR_CTR_BIT1_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_BIT1; + + if (errctr & IFI_CANFD_ERROR_CTR_STUFF_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_STUFF; + + if (errctr & IFI_CANFD_ERROR_CTR_CRC_ERROR_FIRST) + cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ; + + if (errctr & IFI_CANFD_ERROR_CTR_FORM_ERROR_FIRST) + cf->data[2] |= CAN_ERR_PROT_FORM; + + /* Reset the error counter, ack the IRQ and re-enable the counter. */ + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_INTERRUPT_ERROR_COUNTER, + priv->base + IFI_CANFD_INTERRUPT); + writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + + return 1; +} + static int ifi_canfd_get_berr_counter(const struct net_device *ndev, struct can_berr_counter *bec) { @@ -469,6 +554,7 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) u32 stcmd = readl(priv->base + IFI_CANFD_STCMD); u32 rxstcmd = readl(priv->base + IFI_CANFD_STCMD); + u32 errctr = readl(priv->base + IFI_CANFD_ERROR_CTR); /* Handle bus state changes */ if ((stcmd & stcmd_state_mask) || @@ -479,6 +565,10 @@ static int ifi_canfd_poll(struct napi_struct *napi, int quota) if (rxstcmd & IFI_CANFD_RXSTCMD_OVERFLOW) work_done += ifi_canfd_handle_lost_msg(ndev); + /* Handle lec errors on the bus */ + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + work_done += ifi_canfd_handle_lec_err(ndev, errctr); + /* Handle normal messages on RX */ if (!(rxstcmd & IFI_CANFD_RXSTCMD_EMPTY)) work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); @@ -497,11 +587,13 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) struct ifi_canfd_priv *priv = netdev_priv(ndev); struct net_device_stats *stats = &ndev->stats; const u32 rx_irq_mask = IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY | - IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER; + IFI_CANFD_INTERRUPT_RXFIFO_NEMPTY_PER | + IFI_CANFD_INTERRUPT_ERROR_WARNING | + IFI_CANFD_INTERRUPT_ERROR_COUNTER; const u32 tx_irq_mask = IFI_CANFD_INTERRUPT_TXFIFO_EMPTY | IFI_CANFD_INTERRUPT_TXFIFO_REMOVE; - const u32 clr_irq_mask = ~(IFI_CANFD_INTERRUPT_SET_IRQ | - IFI_CANFD_INTERRUPT_ERROR_WARNING); + const u32 clr_irq_mask = ~((u32)(IFI_CANFD_INTERRUPT_SET_IRQ | + IFI_CANFD_INTERRUPT_ERROR_WARNING)); u32 isr; isr = readl(priv->base + IFI_CANFD_INTERRUPT); @@ -513,44 +605,34 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id) /* Clear all pending interrupts but ErrWarn */ writel(clr_irq_mask, priv->base + IFI_CANFD_INTERRUPT); - /* RX IRQ, start NAPI */ + /* RX IRQ or bus warning, start NAPI */ if (isr & rx_irq_mask) { ifi_canfd_irq_enable(ndev, 0); napi_schedule(&priv->napi); } /* TX IRQ */ - if (isr & tx_irq_mask) { + if (isr & IFI_CANFD_INTERRUPT_TXFIFO_REMOVE) { stats->tx_bytes += can_get_echo_skb(ndev, 0); stats->tx_packets++; can_led_event(ndev, CAN_LED_EVENT_TX); - netif_wake_queue(ndev); } + if (isr & tx_irq_mask) + netif_wake_queue(ndev); + return IRQ_HANDLED; } static const struct can_bittiming_const ifi_canfd_bittiming_const = { .name = KBUILD_MODNAME, .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ - .tseg1_max = 64, - .tseg2_min = 2, /* Time segment 2 = phase_seg2 */ - .tseg2_max = 64, - .sjw_max = 16, - .brp_min = 2, - .brp_max = 256, - .brp_inc = 1, -}; - -static const struct can_bittiming_const ifi_canfd_data_bittiming_const = { - .name = KBUILD_MODNAME, - .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ - .tseg1_max = 64, + .tseg1_max = 256, .tseg2_min = 2, /* Time segment 2 = phase_seg2 */ - .tseg2_max = 64, - .sjw_max = 16, + .tseg2_max = 256, + .sjw_max = 128, .brp_min = 2, - .brp_max = 256, + .brp_max = 512, .brp_inc = 1, }; @@ -560,19 +642,6 @@ static void ifi_canfd_set_bittiming(struct net_device *ndev) const struct can_bittiming *bt = &priv->can.bittiming; const struct can_bittiming *dbt = &priv->can.data_bittiming; u16 brp, sjw, tseg1, tseg2; - u32 noniso_arg = 0; - u32 time_off; - - if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && - !(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) { - time_off = IFI_CANFD_TIME_SJW_OFF_ISO; - } else { - noniso_arg = IFI_CANFD_TIME_SET_TIMEB_BOSCH | - IFI_CANFD_TIME_SET_TIMEA_BOSCH | - IFI_CANFD_TIME_SET_PRESC_BOSCH | - IFI_CANFD_TIME_SET_SJW_BOSCH; - time_off = IFI_CANFD_TIME_SJW_OFF_BOSCH; - } /* Configure bit timing */ brp = bt->brp - 2; @@ -582,8 +651,7 @@ static void ifi_canfd_set_bittiming(struct net_device *ndev) writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | (brp << IFI_CANFD_TIME_PRESCALE_OFF) | - (sjw << time_off) | - noniso_arg, + (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), priv->base + IFI_CANFD_TIME); /* Configure data bit timing */ @@ -594,8 +662,7 @@ static void ifi_canfd_set_bittiming(struct net_device *ndev) writel((tseg2 << IFI_CANFD_TIME_TIMEB_OFF) | (tseg1 << IFI_CANFD_TIME_TIMEA_OFF) | (brp << IFI_CANFD_TIME_PRESCALE_OFF) | - (sjw << time_off) | - noniso_arg, + (sjw << IFI_CANFD_TIME_SJW_OFF_7_9_8_8), priv->base + IFI_CANFD_FTIME); } @@ -640,7 +707,8 @@ static void ifi_canfd_start(struct net_device *ndev) /* Reset the IP */ writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); - writel(0, priv->base + IFI_CANFD_STCMD); + writel(IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING, + priv->base + IFI_CANFD_STCMD); ifi_canfd_set_bittiming(ndev); ifi_canfd_set_filters(ndev); @@ -659,7 +727,8 @@ static void ifi_canfd_start(struct net_device *ndev) writel((u32)(~IFI_CANFD_INTERRUPT_SET_IRQ), priv->base + IFI_CANFD_INTERRUPT); - stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE; + stcmd = IFI_CANFD_STCMD_ENABLE | IFI_CANFD_STCMD_NORMAL_MODE | + IFI_CANFD_STCMD_ENABLE_7_9_8_8_TIMING; if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) stcmd |= IFI_CANFD_STCMD_BUSMONITOR; @@ -667,16 +736,23 @@ static void ifi_canfd_start(struct net_device *ndev) if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) stcmd |= IFI_CANFD_STCMD_LOOPBACK; - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + if ((priv->can.ctrlmode & CAN_CTRLMODE_FD) && + !(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) stcmd |= IFI_CANFD_STCMD_ENABLE_ISO; - if (!(priv->can.ctrlmode & (CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO))) + if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD)) stcmd |= IFI_CANFD_STCMD_DISABLE_CANFD; priv->can.state = CAN_STATE_ERROR_ACTIVE; ifi_canfd_irq_enable(ndev, 1); + /* Unlock, reset and enable the error counter. */ + writel(IFI_CANFD_ERROR_CTR_UNLOCK_MAGIC, + priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(IFI_CANFD_ERROR_CTR_ER_ENABLE, priv->base + IFI_CANFD_ERROR_CTR); + /* Enable controller */ writel(stcmd, priv->base + IFI_CANFD_STCMD); } @@ -685,6 +761,10 @@ static void ifi_canfd_stop(struct net_device *ndev) { struct ifi_canfd_priv *priv = netdev_priv(ndev); + /* Reset and disable the error counter. */ + writel(IFI_CANFD_ERROR_CTR_ER_RESET, priv->base + IFI_CANFD_ERROR_CTR); + writel(0, priv->base + IFI_CANFD_ERROR_CTR); + /* Reset the IP */ writel(IFI_CANFD_STCMD_HARDRESET, priv->base + IFI_CANFD_STCMD); @@ -877,7 +957,7 @@ static int ifi_canfd_plat_probe(struct platform_device *pdev) priv->can.clock.freq = readl(addr + IFI_CANFD_CANCLOCK); priv->can.bittiming_const = &ifi_canfd_bittiming_const; - priv->can.data_bittiming_const = &ifi_canfd_data_bittiming_const; + priv->can.data_bittiming_const = &ifi_canfd_bittiming_const; priv->can.do_set_mode = ifi_canfd_set_mode; priv->can.do_get_berr_counter = ifi_canfd_get_berr_counter; @@ -888,7 +968,8 @@ static int ifi_canfd_plat_probe(struct platform_device *pdev) priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_FD | - CAN_CTRLMODE_FD_NON_ISO; + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_BERR_REPORTING; platform_set_drvdata(pdev, ndev); SET_NETDEV_DEV(ndev, dev); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 5d04f5464faf..f13bb8d9bb84 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -84,6 +84,7 @@ #define MSG_COFFREQ 0x42 #define MSG_CONREQ 0x43 #define MSG_CCONFREQ 0x47 +#define MSG_NMTS 0xb0 #define MSG_LMTS 0xb4 /* @@ -130,6 +131,22 @@ #define ICAN3_CAN_DLC_MASK 0x0f +/* Janz ICAN3 NMTS subtypes */ +#define NMTS_CREATE_NODE_REQ 0x0 +#define NMTS_SLAVE_STATE_IND 0x8 +#define NMTS_SLAVE_EVENT_IND 0x9 + +/* Janz ICAN3 LMTS subtypes */ +#define LMTS_BUSON_REQ 0x0 +#define LMTS_BUSOFF_REQ 0x1 +#define LMTS_CAN_CONF_REQ 0x2 + +/* Janz ICAN3 NMTS Event indications */ +#define NE_LOCAL_OCCURRED 0x3 +#define NE_LOCAL_RESOLVED 0x2 +#define NE_REMOTE_OCCURRED 0xc +#define NE_REMOTE_RESOLVED 0x8 + /* * SJA1000 Status and Error Register Definitions * @@ -800,21 +817,41 @@ static int ican3_set_bus_state(struct ican3_dev *mod, bool on) return ican3_send_msg(mod, &msg); } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { + /* bittiming + can-on/off request */ memset(&msg, 0, sizeof(msg)); msg.spec = MSG_LMTS; if (on) { msg.len = cpu_to_le16(4); - msg.data[0] = 0; + msg.data[0] = LMTS_BUSON_REQ; msg.data[1] = 0; msg.data[2] = btr0; msg.data[3] = btr1; } else { msg.len = cpu_to_le16(2); - msg.data[0] = 1; + msg.data[0] = LMTS_BUSOFF_REQ; msg.data[1] = 0; } + res = ican3_send_msg(mod, &msg); + if (res) + return res; - return ican3_send_msg(mod, &msg); + if (on) { + /* create NMT Slave Node for error processing + * class 2 (with error capability, see CiA/DS203-1) + * id 1 + * name locnod1 (must be exactly 7 bytes) + */ + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_NMTS; + msg.len = cpu_to_le16(11); + msg.data[0] = NMTS_CREATE_NODE_REQ; + msg.data[1] = 0; + msg.data[2] = 2; /* node class */ + msg.data[3] = 1; /* node id */ + strcpy(msg.data + 4, "locnod1"); /* node name */ + return ican3_send_msg(mod, &msg); + } + return 0; } return -ENOTSUPP; } @@ -849,12 +886,23 @@ static int ican3_set_buserror(struct ican3_dev *mod, u8 quota) { struct ican3_msg msg; - memset(&msg, 0, sizeof(msg)); - msg.spec = MSG_CCONFREQ; - msg.len = cpu_to_le16(2); - msg.data[0] = 0x00; - msg.data[1] = quota; - + if (mod->fwtype == ICAN3_FWTYPE_ICANOS) { + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_CCONFREQ; + msg.len = cpu_to_le16(2); + msg.data[0] = 0x00; + msg.data[1] = quota; + } else if (mod->fwtype == ICAN3_FWTYPE_CAL_CANOPEN) { + memset(&msg, 0, sizeof(msg)); + msg.spec = MSG_LMTS; + msg.len = cpu_to_le16(4); + msg.data[0] = LMTS_CAN_CONF_REQ; + msg.data[1] = 0x00; + msg.data[2] = 0x00; + msg.data[3] = quota; + } else { + return -ENOTSUPP; + } return ican3_send_msg(mod, &msg); } @@ -1150,6 +1198,41 @@ static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg) } } +/* Handle NMTS Slave Event Indication Messages from the firmware */ +static void ican3_handle_nmtsind(struct ican3_dev *mod, struct ican3_msg *msg) +{ + u16 subspec; + + subspec = msg->data[0] + msg->data[1] * 0x100; + if (subspec == NMTS_SLAVE_EVENT_IND) { + switch (msg->data[2]) { + case NE_LOCAL_OCCURRED: + case NE_LOCAL_RESOLVED: + /* now follows the same message as Raw ICANOS CEVTIND + * shift the data at the same place and call this method + */ + le16_add_cpu(&msg->len, -3); + memmove(msg->data, msg->data + 3, le16_to_cpu(msg->len)); + ican3_handle_cevtind(mod, msg); + break; + case NE_REMOTE_OCCURRED: + case NE_REMOTE_RESOLVED: + /* should not occurre, ignore */ + break; + default: + netdev_warn(mod->ndev, "unknown NMTS event indication %x\n", + msg->data[2]); + break; + } + } else if (subspec == NMTS_SLAVE_STATE_IND) { + /* ignore state indications */ + } else { + netdev_warn(mod->ndev, "unhandled NMTS indication %x\n", + subspec); + return; + } +} + static void ican3_handle_unknown_message(struct ican3_dev *mod, struct ican3_msg *msg) { @@ -1179,6 +1262,9 @@ static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg) case MSG_INQUIRY: ican3_handle_inquiry(mod, msg); break; + case MSG_NMTS: + ican3_handle_nmtsind(mod, msg); + break; default: ican3_handle_unknown_message(mod, msg); break; diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 39cf911f7a1e..195f15edb32e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -955,7 +955,7 @@ static struct net_device *alloc_m_can_dev(void) priv->can.do_get_berr_counter = m_can_get_berr_counter; /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ - priv->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); /* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index e36b7400d5cc..acb708fc1463 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -276,7 +276,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev) out_8(®s->cantflg, 1 << buf_id); if (!test_bit(F_TX_PROGRESS, &priv->flags)) - dev->trans_start = jiffies; + netif_trans_update(dev); list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head); @@ -469,7 +469,7 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) clear_bit(F_TX_PROGRESS, &priv->flags); priv->cur_pri = 0; } else { - dev->trans_start = jiffies; + netif_trans_update(dev); } if (!test_bit(F_TX_WAIT_ALL, &priv->flags)) diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c index 8836a7485c81..3eb7430dffbf 100644 --- a/drivers/net/can/sja1000/plx_pci.c +++ b/drivers/net/can/sja1000/plx_pci.c @@ -39,6 +39,7 @@ MODULE_DESCRIPTION("Socket-CAN driver for PLX90xx PCI-bridge cards with " MODULE_SUPPORTED_DEVICE("Adlink PCI-7841/cPCI-7841, " "Adlink PCI-7841/cPCI-7841 SE, " "Marathon CAN-bus-PCI, " + "Marathon CAN-bus-PCIe, " "TEWS TECHNOLOGIES TPMC810, " "esd CAN-PCI/CPCI/PCI104/200, " "esd CAN-PCI/PMC/266, " @@ -133,6 +134,7 @@ struct plx_pci_card { #define IXXAT_PCI_SUB_SYS_ID 0x2540 #define MARATHON_PCI_DEVICE_ID 0x2715 +#define MARATHON_PCIE_DEVICE_ID 0x3432 #define TEWS_PCI_VENDOR_ID 0x1498 #define TEWS_PCI_DEVICE_ID_TMPC810 0x032A @@ -141,8 +143,9 @@ struct plx_pci_card { #define CTI_PCI_DEVICE_ID_CRG001 0x0900 static void plx_pci_reset_common(struct pci_dev *pdev); -static void plx_pci_reset_marathon(struct pci_dev *pdev); static void plx9056_pci_reset_common(struct pci_dev *pdev); +static void plx_pci_reset_marathon_pci(struct pci_dev *pdev); +static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev); struct plx_pci_channel_map { u32 bar; @@ -215,14 +218,22 @@ static struct plx_pci_card_info plx_pci_card_info_ixxat = { /* based on PLX9050 */ }; -static struct plx_pci_card_info plx_pci_card_info_marathon = { +static struct plx_pci_card_info plx_pci_card_info_marathon_pci = { "Marathon CAN-bus-PCI", 2, PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {4, 0x00, 0x00} }, - &plx_pci_reset_marathon + &plx_pci_reset_marathon_pci /* based on PLX9052 */ }; +static struct plx_pci_card_info plx_pci_card_info_marathon_pcie = { + "Marathon CAN-bus-PCIe", 2, + PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, + {0, 0x00, 0x00}, { {2, 0x00, 0x00}, {3, 0x80, 0x00} }, + &plx_pci_reset_marathon_pcie + /* based on PEX8311 */ +}; + static struct plx_pci_card_info plx_pci_card_info_tews = { "TEWS TECHNOLOGIES TPMC810", 2, PLX_PCI_CAN_CLOCK, PLX_PCI_OCR, PLX_PCI_CDR, @@ -316,7 +327,14 @@ static const struct pci_device_id plx_pci_tbl[] = { PCI_VENDOR_ID_PLX, MARATHON_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - (kernel_ulong_t)&plx_pci_card_info_marathon + (kernel_ulong_t)&plx_pci_card_info_marathon_pci + }, + { + /* Marathon CAN-bus-PCIe card */ + PCI_VENDOR_ID_PLX, MARATHON_PCIE_DEVICE_ID, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&plx_pci_card_info_marathon_pcie }, { /* TEWS TECHNOLOGIES TPMC810 card */ @@ -437,8 +455,8 @@ static void plx9056_pci_reset_common(struct pci_dev *pdev) iowrite32(cntrl, card->conf_addr + PLX9056_CNTRL); }; -/* Special reset function for Marathon card */ -static void plx_pci_reset_marathon(struct pci_dev *pdev) +/* Special reset function for Marathon CAN-bus-PCI card */ +static void plx_pci_reset_marathon_pci(struct pci_dev *pdev) { void __iomem *reset_addr; int i; @@ -460,6 +478,34 @@ static void plx_pci_reset_marathon(struct pci_dev *pdev) } } +/* Special reset function for Marathon CAN-bus-PCIe card */ +static void plx_pci_reset_marathon_pcie(struct pci_dev *pdev) +{ + void __iomem *addr; + void __iomem *reset_addr; + int i; + + plx9056_pci_reset_common(pdev); + + for (i = 0; i < 2; i++) { + struct plx_pci_channel_map *chan_map = + &plx_pci_card_info_marathon_pcie.chan_map_tbl[i]; + addr = pci_iomap(pdev, chan_map->bar, chan_map->size); + if (!addr) { + dev_err(&pdev->dev, "Failed to remap reset " + "space %d (BAR%d)\n", i, chan_map->bar); + } else { + /* reset the SJA1000 chip */ + #define MARATHON_PCIE_RESET_OFFSET 32 + reset_addr = addr + chan_map->offset + + MARATHON_PCIE_RESET_OFFSET; + iowrite8(0x1, reset_addr); + udelay(100); + pci_iounmap(pdev, addr); + } + } +} + static void plx_pci_del_card(struct pci_dev *pdev) { struct plx_pci_card *card = pci_get_drvdata(pdev); @@ -486,7 +532,8 @@ static void plx_pci_del_card(struct pci_dev *pdev) * Disable interrupts from PCI-card and disable local * interrupts */ - if (pdev->device != PCI_DEVICE_ID_PLX_9056) + if (pdev->device != PCI_DEVICE_ID_PLX_9056 && + pdev->device != MARATHON_PCIE_DEVICE_ID) iowrite32(0x0, card->conf_addr + PLX_INTCSR); else iowrite32(0x0, card->conf_addr + PLX9056_INTCSR); @@ -619,7 +666,8 @@ static int plx_pci_add_card(struct pci_dev *pdev, * Enable interrupts from PCI-card (PLX90xx) and enable Local_1, * Local_2 interrupts from the SJA1000 chips */ - if (pdev->device != PCI_DEVICE_ID_PLX_9056) { + if (pdev->device != PCI_DEVICE_ID_PLX_9056 && + pdev->device != MARATHON_PCIE_DEVICE_ID) { val = ioread32(card->conf_addr + PLX_INTCSR); if (pdev->subsystem_vendor == PCI_VENDOR_ID_ESDGMBH) val |= PLX_LINT1_EN | PLX_PCI_INT_EN; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 8dda3b703d39..9f107798f904 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -438,6 +438,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + /* set error type */ switch (ecc & ECC_MASK) { case ECC_BIT: cf->data[2] |= CAN_ERR_PROT_BIT; @@ -449,9 +450,12 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) cf->data[2] |= CAN_ERR_PROT_STUFF; break; default: - cf->data[3] = ecc & ECC_SEG; break; } + + /* set error location */ + cf->data[3] = ecc & ECC_SEG; + /* Error occurred during transmission? */ if ((ecc & ECC_DIR) == 0) cf->data[2] |= CAN_ERR_PROT_TX; diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index 74a7dfecee27..cf36d26ef002 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -961,7 +961,8 @@ static int mcp251x_open(struct net_device *net) goto open_unlock; } - priv->wq = create_freezable_workqueue("mcp251x_wq"); + priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 3400fd1cada7..71f0e791355b 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -521,7 +521,7 @@ static void ems_usb_write_bulk_callback(struct urb *urb) if (urb->status) netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* transmission complete interrupt */ netdev->stats.tx_packets++; @@ -835,7 +835,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne stats->tx_dropped++; } } else { - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* Slow down tx path */ if (atomic_read(&dev->active_tx_urbs) >= MAX_TX_URBS || diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 113e64fcd73b..784a9002fbb9 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -480,7 +480,7 @@ static void esd_usb2_write_bulk_callback(struct urb *urb) if (urb->status) netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); - netdev->trans_start = jiffies; + netif_trans_update(netdev); } static ssize_t show_firmware(struct device *d, @@ -820,7 +820,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb, goto releasebuf; } - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* * Release our reference to this URB, the USB core will eventually free diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index cbc99d5649af..1556d4286235 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -950,7 +950,8 @@ static void gs_usb_disconnect(struct usb_interface *intf) } static const struct usb_device_id gs_usb_table[] = { - {USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)}, + { USB_DEVICE_INTERFACE_NUMBER(USB_GSUSB_1_VENDOR_ID, + USB_GSUSB_1_PRODUCT_ID, 0) }, {} /* Terminating entry */ }; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 5a2e341a6d1e..bfb91d8fa460 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -274,7 +274,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb) netdev->stats.tx_bytes += context->data_len; /* prevent tx timeout */ - netdev->trans_start = jiffies; + netif_trans_update(netdev); break; default: @@ -373,7 +373,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, stats->tx_dropped++; } } else { - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* slow down tx path */ if (atomic_read(&dev->active_tx_urbs) >= PCAN_USB_MAX_TX_URBS) diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 64c016a99af8..221f5f011ff9 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1106,7 +1106,7 @@ e100_send_packet(struct sk_buff *skb, struct net_device *dev) myNextTxDesc->skb = skb; - dev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */ + netif_trans_update(dev); /* NETIF_F_LLTX driver :( */ e100_hardware_send_packet(np, buf, skb->len); diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 90ba003d8fdf..200663c43ce9 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -1,10 +1,6 @@ menu "Distributed Switch Architecture drivers" depends on HAVE_NET_DSA -config NET_DSA_MV88E6XXX - tristate - default n - config NET_DSA_MV88E6060 tristate "Marvell 88E6060 ethernet switch chip support" depends on NET_DSA @@ -13,46 +9,13 @@ config NET_DSA_MV88E6060 This enables support for the Marvell 88E6060 ethernet switch chip. -config NET_DSA_MV88E6XXX_NEED_PPU - bool - default n - -config NET_DSA_MV88E6131 - tristate "Marvell 88E6085/6095/6095F/6131 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_MV88E6XXX_NEED_PPU - select NET_DSA_TAG_DSA - ---help--- - This enables support for the Marvell 88E6085/6095/6095F/6131 - ethernet switch chips. - -config NET_DSA_MV88E6123 - tristate "Marvell 88E6123/6161/6165 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_TAG_EDSA - ---help--- - This enables support for the Marvell 88E6123/6161/6165 - ethernet switch chips. - -config NET_DSA_MV88E6171 - tristate "Marvell 88E6171/6175/6350/6351 ethernet switch chip support" - depends on NET_DSA - select NET_DSA_MV88E6XXX - select NET_DSA_TAG_EDSA - ---help--- - This enables support for the Marvell 88E6171/6175/6350/6351 - ethernet switches chips. - -config NET_DSA_MV88E6352 - tristate "Marvell 88E6172/6176/6320/6321/6352 ethernet switch chip support" +config NET_DSA_MV88E6XXX + tristate "Marvell 88E6xxx Ethernet switch chip support" depends on NET_DSA - select NET_DSA_MV88E6XXX select NET_DSA_TAG_EDSA ---help--- - This enables support for the Marvell 88E6172, 88E6176, 88E6320, - 88E6321 and 88E6352 ethernet switch chips. + This enables support for most of the Marvell 88E6xxx models of + Ethernet switch chips, except 88E6060. config NET_DSA_BCM_SF2 tristate "Broadcom Starfighter 2 Ethernet switch support" diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index a6e09939be65..76b751dd9efd 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -1,16 +1,3 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o -obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx_drv.o -mv88e6xxx_drv-y += mv88e6xxx.o -ifdef CONFIG_NET_DSA_MV88E6123 -mv88e6xxx_drv-y += mv88e6123.o -endif -ifdef CONFIG_NET_DSA_MV88E6131 -mv88e6xxx_drv-y += mv88e6131.o -endif -ifdef CONFIG_NET_DSA_MV88E6352 -mv88e6xxx_drv-y += mv88e6352.o -endif -ifdef CONFIG_NET_DSA_MV88E6171 -mv88e6xxx_drv-y += mv88e6171.o -endif +obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm_sf2.o diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 448deb59b9a4..10ddd5a5dfb6 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -949,8 +949,8 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) /* All the interesting properties are at the parent device_node * level */ - dn = ds->pd->of_node->parent; - bcm_sf2_identify_ports(priv, ds->pd->of_node); + dn = ds->cd->of_node->parent; + bcm_sf2_identify_ports(priv, ds->cd->of_node); priv->irq0 = irq_of_parse_and_map(dn, 0); priv->irq1 = irq_of_parse_and_map(dn, 1); diff --git a/drivers/net/dsa/mv88e6123.c b/drivers/net/dsa/mv88e6123.c deleted file mode 100644 index 5535a42a6113..000000000000 --- a/drivers/net/dsa/mv88e6123.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 switch chip support - * Copyright (c) 2008-2009 Marvell Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/delay.h> -#include <linux/jiffies.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/phy.h> -#include <net/dsa.h> -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_info mv88e6123_table[] = { - { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, - .family = MV88E6XXX_FAMILY_6165, - .name = "Marvell 88E6123", - .num_databases = 4096, - .num_ports = 3, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, - .family = MV88E6XXX_FAMILY_6165, - .name = "Marvell 88E6161", - .num_databases = 4096, - .num_ports = 6, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6165, - .family = MV88E6XXX_FAMILY_6165, - .name = "Marvell 88E6165", - .num_databases = 4096, - .num_ports = 6, - } -}; - -static const char *mv88e6123_drv_probe(struct device *dsa_dev, - struct device *host_dev, int sw_addr, - void **priv) -{ - return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv, - mv88e6123_table, - ARRAY_SIZE(mv88e6123_table)); -} - -static int mv88e6123_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Disable the PHY polling unit (since there won't be any - * external PHYs to poll), don't discard packets with - * excessive collisions, and mask all interrupt sources. - */ - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, 0x0000); - if (ret) - return ret; - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - if (ret) - return ret; - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, - ds->index & 0x1f); -} - -static int mv88e6123_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ps->ds = ds; - - ret = mv88e6xxx_setup_common(ps); - if (ret < 0) - return ret; - - ret = mv88e6xxx_switch_reset(ps, false); - if (ret < 0) - return ret; - - ret = mv88e6123_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -struct dsa_switch_driver mv88e6123_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_EDSA, - .probe = mv88e6123_drv_probe, - .setup = mv88e6123_setup, - .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6xxx_phy_read, - .phy_write = mv88e6xxx_phy_write, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, -#ifdef CONFIG_NET_DSA_HWMON - .get_temp = mv88e6xxx_get_temp, -#endif - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, -}; - -MODULE_ALIAS("platform:mv88e6123"); -MODULE_ALIAS("platform:mv88e6161"); -MODULE_ALIAS("platform:mv88e6165"); diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c deleted file mode 100644 index 357ab794d720..000000000000 --- a/drivers/net/dsa/mv88e6131.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * net/dsa/mv88e6131.c - Marvell 88e6095/6095f/6131 switch chip support - * Copyright (c) 2008-2009 Marvell Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/delay.h> -#include <linux/jiffies.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/phy.h> -#include <net/dsa.h> -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_info mv88e6131_table[] = { - { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6095, - .family = MV88E6XXX_FAMILY_6095, - .name = "Marvell 88E6095/88E6095F", - .num_databases = 256, - .num_ports = 11, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, - .family = MV88E6XXX_FAMILY_6097, - .name = "Marvell 88E6085", - .num_databases = 4096, - .num_ports = 10, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6131, - .family = MV88E6XXX_FAMILY_6185, - .name = "Marvell 88E6131", - .num_databases = 256, - .num_ports = 8, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6185, - .family = MV88E6XXX_FAMILY_6185, - .name = "Marvell 88E6185", - .num_databases = 256, - .num_ports = 10, - } -}; - -static const char *mv88e6131_drv_probe(struct device *dsa_dev, - struct device *host_dev, int sw_addr, - void **priv) -{ - return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv, - mv88e6131_table, - ARRAY_SIZE(mv88e6131_table)); -} - -static int mv88e6131_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Enable the PHY polling unit, don't discard packets with - * excessive collisions, use a weighted fair queueing scheme - * to arbitrate between packet queues, set the maximum frame - * size to 1632, and mask all interrupt sources. - */ - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | - GLOBAL_CONTROL_MAX_FRAME_1632); - if (ret) - return ret; - - /* Set the VLAN ethertype to 0x8100. */ - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CORE_TAG_TYPE, 0x8100); - if (ret) - return ret; - - /* Disable ARP mirroring, and configure the upstream port as - * the port to which ingress and egress monitor frames are to - * be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - GLOBAL_MONITOR_CONTROL_ARP_DISABLED; - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - if (ret) - return ret; - - /* Disable cascade port functionality unless this device - * is used in a cascade configuration, and set the switch's - * DSA device number. - */ - if (ds->dst->pd->nr_chips > 1) - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, - GLOBAL_CONTROL_2_MULTIPLE_CASCADE | - (ds->index & 0x1f)); - else - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, - GLOBAL_CONTROL_2_NO_CASCADE | - (ds->index & 0x1f)); - if (ret) - return ret; - - /* Force the priority of IGMP/MLD snoop frames and ARP frames - * to the highest setting. - */ - return mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE, - GLOBAL2_PRIO_OVERRIDE_FORCE_SNOOP | - 7 << GLOBAL2_PRIO_OVERRIDE_SNOOP_SHIFT | - GLOBAL2_PRIO_OVERRIDE_FORCE_ARP | - 7 << GLOBAL2_PRIO_OVERRIDE_ARP_SHIFT); -} - -static int mv88e6131_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ps->ds = ds; - - ret = mv88e6xxx_setup_common(ps); - if (ret < 0) - return ret; - - mv88e6xxx_ppu_state_init(ps); - - ret = mv88e6xxx_switch_reset(ps, false); - if (ret < 0) - return ret; - - ret = mv88e6131_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -static int mv88e6131_port_to_phy_addr(struct dsa_switch *ds, int port) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - - if (port >= 0 && port < ps->info->num_ports) - return port; - - return -EINVAL; -} - -static int -mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum) -{ - int addr = mv88e6131_port_to_phy_addr(ds, port); - - if (addr < 0) - return addr; - - return mv88e6xxx_phy_read_ppu(ds, addr, regnum); -} - -static int -mv88e6131_phy_write(struct dsa_switch *ds, - int port, int regnum, u16 val) -{ - int addr = mv88e6131_port_to_phy_addr(ds, port); - - if (addr < 0) - return addr; - - return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val); -} - -struct dsa_switch_driver mv88e6131_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_DSA, - .probe = mv88e6131_drv_probe, - .setup = mv88e6131_setup, - .set_addr = mv88e6xxx_set_addr_direct, - .phy_read = mv88e6131_phy_read, - .phy_write = mv88e6131_phy_write, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, - .port_bridge_join = mv88e6xxx_port_bridge_join, - .port_bridge_leave = mv88e6xxx_port_bridge_leave, - .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, - .port_vlan_add = mv88e6xxx_port_vlan_add, - .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, -}; - -MODULE_ALIAS("platform:mv88e6085"); -MODULE_ALIAS("platform:mv88e6095"); -MODULE_ALIAS("platform:mv88e6095f"); -MODULE_ALIAS("platform:mv88e6131"); diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c deleted file mode 100644 index f75164dc3bd6..000000000000 --- a/drivers/net/dsa/mv88e6171.c +++ /dev/null @@ -1,151 +0,0 @@ -/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support - * Copyright (c) 2008-2009 Marvell Semiconductor - * Copyright (c) 2014 Claudio Leite <leitec@staticky.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/delay.h> -#include <linux/jiffies.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/phy.h> -#include <net/dsa.h> -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_info mv88e6171_table[] = { - { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6171, - .family = MV88E6XXX_FAMILY_6351, - .name = "Marvell 88E6171", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6175, - .family = MV88E6XXX_FAMILY_6351, - .name = "Marvell 88E6175", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6350, - .family = MV88E6XXX_FAMILY_6351, - .name = "Marvell 88E6350", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6351, - .family = MV88E6XXX_FAMILY_6351, - .name = "Marvell 88E6351", - .num_databases = 4096, - .num_ports = 7, - } -}; - -static const char *mv88e6171_drv_probe(struct device *dsa_dev, - struct device *host_dev, int sw_addr, - void **priv) -{ - return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv, - mv88e6171_table, - ARRAY_SIZE(mv88e6171_table)); -} - -static int mv88e6171_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Discard packets with excessive collisions, mask all - * interrupt sources, enable PPU. - */ - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | - GLOBAL_CONTROL_DISCARD_EXCESS); - if (ret) - return ret; - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_MIRROR_SHIFT; - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - if (ret) - return ret; - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, - ds->index & 0x1f); -} - -static int mv88e6171_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ps->ds = ds; - - ret = mv88e6xxx_setup_common(ps); - if (ret < 0) - return ret; - - ret = mv88e6xxx_switch_reset(ps, true); - if (ret < 0) - return ret; - - ret = mv88e6171_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -struct dsa_switch_driver mv88e6171_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_EDSA, - .probe = mv88e6171_drv_probe, - .setup = mv88e6171_setup, - .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6xxx_phy_read_indirect, - .phy_write = mv88e6xxx_phy_write_indirect, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, -#ifdef CONFIG_NET_DSA_HWMON - .get_temp = mv88e6xxx_get_temp, -#endif - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, - .port_bridge_join = mv88e6xxx_port_bridge_join, - .port_bridge_leave = mv88e6xxx_port_bridge_leave, - .port_stp_state_set = mv88e6xxx_port_stp_state_set, - .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, - .port_vlan_add = mv88e6xxx_port_vlan_add, - .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, -}; - -MODULE_ALIAS("platform:mv88e6171"); -MODULE_ALIAS("platform:mv88e6175"); -MODULE_ALIAS("platform:mv88e6350"); -MODULE_ALIAS("platform:mv88e6351"); diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c deleted file mode 100644 index c622a1d58480..000000000000 --- a/drivers/net/dsa/mv88e6352.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support - * - * Copyright (c) 2014 Guenter Roeck - * - * Derived from mv88e6123_61_65.c - * Copyright (c) 2008-2009 Marvell Semiconductor - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <linux/delay.h> -#include <linux/jiffies.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/platform_device.h> -#include <linux/phy.h> -#include <net/dsa.h> -#include "mv88e6xxx.h" - -static const struct mv88e6xxx_info mv88e6352_table[] = { - { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, - .family = MV88E6XXX_FAMILY_6320, - .name = "Marvell 88E6320", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6321, - .family = MV88E6XXX_FAMILY_6320, - .name = "Marvell 88E6321", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6172, - .family = MV88E6XXX_FAMILY_6352, - .name = "Marvell 88E6172", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6176, - .family = MV88E6XXX_FAMILY_6352, - .name = "Marvell 88E6176", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, - .family = MV88E6XXX_FAMILY_6352, - .name = "Marvell 88E6240", - .num_databases = 4096, - .num_ports = 7, - }, { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6352, - .family = MV88E6XXX_FAMILY_6352, - .name = "Marvell 88E6352", - .num_databases = 4096, - .num_ports = 7, - } -}; - -static const char *mv88e6352_drv_probe(struct device *dsa_dev, - struct device *host_dev, int sw_addr, - void **priv) -{ - return mv88e6xxx_drv_probe(dsa_dev, host_dev, sw_addr, priv, - mv88e6352_table, - ARRAY_SIZE(mv88e6352_table)); -} - -static int mv88e6352_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u32 upstream_port = dsa_upstream_port(ds); - int ret; - u32 reg; - - ret = mv88e6xxx_setup_global(ds); - if (ret) - return ret; - - /* Discard packets with excessive collisions, - * mask all interrupt sources, enable PPU (bit 14, undocumented). - */ - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, - GLOBAL_CONTROL_PPU_ENABLE | - GLOBAL_CONTROL_DISCARD_EXCESS); - if (ret) - return ret; - - /* Configure the upstream port, and configure the upstream - * port as the port to which ingress and egress monitor frames - * are to be sent. - */ - reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | - upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); - if (ret) - return ret; - - /* Disable remote management for now, and set the switch's - * DSA device number. - */ - return mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x1c, ds->index & 0x1f); -} - -static int mv88e6352_setup(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ps->ds = ds; - - ret = mv88e6xxx_setup_common(ps); - if (ret < 0) - return ret; - - mutex_init(&ps->eeprom_mutex); - - ret = mv88e6xxx_switch_reset(ps, true); - if (ret < 0) - return ret; - - ret = mv88e6352_setup_global(ds); - if (ret < 0) - return ret; - - return mv88e6xxx_setup_ports(ds); -} - -static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->eeprom_mutex); - - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, - GLOBAL2_EEPROM_OP_READ | - (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); - if (ret < 0) - goto error; - - ret = mv88e6xxx_eeprom_busy_wait(ds); - if (ret < 0) - goto error; - - ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA); -error: - mutex_unlock(&ps->eeprom_mutex); - return ret; -} - -static int mv88e6352_get_eeprom(struct dsa_switch *ds, - struct ethtool_eeprom *eeprom, u8 *data) -{ - int offset; - int len; - int ret; - - offset = eeprom->offset; - len = eeprom->len; - eeprom->len = 0; - - eeprom->magic = 0xc3ec4951; - - ret = mv88e6xxx_eeprom_load_wait(ds); - if (ret < 0) - return ret; - - if (offset & 1) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = (word >> 8) & 0xff; - - offset++; - len--; - eeprom->len++; - } - - while (len >= 2) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = word & 0xff; - *data++ = (word >> 8) & 0xff; - - offset += 2; - len -= 2; - eeprom->len += 2; - } - - if (len) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - *data++ = word & 0xff; - - offset++; - len--; - eeprom->len++; - } - - return 0; -} - -static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP); - if (ret < 0) - return ret; - - if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN)) - return -EROFS; - - return 0; -} - -static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr, - u16 data) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - - mutex_lock(&ps->eeprom_mutex); - - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data); - if (ret < 0) - goto error; - - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, - GLOBAL2_EEPROM_OP_WRITE | - (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); - if (ret < 0) - goto error; - - ret = mv88e6xxx_eeprom_busy_wait(ds); -error: - mutex_unlock(&ps->eeprom_mutex); - return ret; -} - -static int mv88e6352_set_eeprom(struct dsa_switch *ds, - struct ethtool_eeprom *eeprom, u8 *data) -{ - int offset; - int ret; - int len; - - if (eeprom->magic != 0xc3ec4951) - return -EINVAL; - - ret = mv88e6352_eeprom_is_readonly(ds); - if (ret) - return ret; - - offset = eeprom->offset; - len = eeprom->len; - eeprom->len = 0; - - ret = mv88e6xxx_eeprom_load_wait(ds); - if (ret < 0) - return ret; - - if (offset & 1) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - word = (*data++ << 8) | (word & 0xff); - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset++; - len--; - eeprom->len++; - } - - while (len >= 2) { - int word; - - word = *data++; - word |= *data++ << 8; - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset += 2; - len -= 2; - eeprom->len += 2; - } - - if (len) { - int word; - - word = mv88e6352_read_eeprom_word(ds, offset >> 1); - if (word < 0) - return word; - - word = (word & 0xff00) | *data++; - - ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word); - if (ret < 0) - return ret; - - offset++; - len--; - eeprom->len++; - } - - return 0; -} - -struct dsa_switch_driver mv88e6352_switch_driver = { - .tag_protocol = DSA_TAG_PROTO_EDSA, - .probe = mv88e6352_drv_probe, - .setup = mv88e6352_setup, - .set_addr = mv88e6xxx_set_addr_indirect, - .phy_read = mv88e6xxx_phy_read_indirect, - .phy_write = mv88e6xxx_phy_write_indirect, - .get_strings = mv88e6xxx_get_strings, - .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, - .get_sset_count = mv88e6xxx_get_sset_count, - .adjust_link = mv88e6xxx_adjust_link, - .set_eee = mv88e6xxx_set_eee, - .get_eee = mv88e6xxx_get_eee, -#ifdef CONFIG_NET_DSA_HWMON - .get_temp = mv88e6xxx_get_temp, - .get_temp_limit = mv88e6xxx_get_temp_limit, - .set_temp_limit = mv88e6xxx_set_temp_limit, - .get_temp_alarm = mv88e6xxx_get_temp_alarm, -#endif - .get_eeprom = mv88e6352_get_eeprom, - .set_eeprom = mv88e6352_set_eeprom, - .get_regs_len = mv88e6xxx_get_regs_len, - .get_regs = mv88e6xxx_get_regs, - .port_bridge_join = mv88e6xxx_port_bridge_join, - .port_bridge_leave = mv88e6xxx_port_bridge_leave, - .port_stp_state_set = mv88e6xxx_port_stp_state_set, - .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, - .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, - .port_vlan_add = mv88e6xxx_port_vlan_add, - .port_vlan_del = mv88e6xxx_port_vlan_del, - .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, - .port_fdb_add = mv88e6xxx_port_fdb_add, - .port_fdb_del = mv88e6xxx_port_fdb_del, - .port_fdb_dump = mv88e6xxx_port_fdb_dump, -}; - -MODULE_ALIAS("platform:mv88e6172"); -MODULE_ALIAS("platform:mv88e6176"); -MODULE_ALIAS("platform:mv88e6320"); -MODULE_ALIAS("platform:mv88e6321"); -MODULE_ALIAS("platform:mv88e6352"); diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 61150af37bc7..ba9dfc9421ef 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -5,6 +5,8 @@ * Copyright (c) 2015 CMC Electronics, Inc. * Added support for VLAN Table Unit operations * + * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,6 +19,7 @@ #include <linux/if_bridge.h> #include <linux/jiffies.h> #include <linux/list.h> +#include <linux/mdio.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/gpio/consumer.h> @@ -173,7 +176,7 @@ int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr, return ret; } -int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) +static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int err; @@ -192,7 +195,7 @@ int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr) (addr[4] << 8) | addr[5]); } -int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) +static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; @@ -225,6 +228,16 @@ int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr) return 0; } +int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC)) + return mv88e6xxx_set_addr_indirect(ds, addr); + else + return mv88e6xxx_set_addr_direct(ds, addr); +} + static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr, int regnum) { @@ -241,24 +254,23 @@ static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr, return 0; } -#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps) { int ret; unsigned long timeout; - ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL); if (ret < 0) return ret; - ret = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, - ret & ~GLOBAL_CONTROL_PPU_ENABLE); + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, + ret & ~GLOBAL_CONTROL_PPU_ENABLE); if (ret) return ret; timeout = jiffies + 1 * HZ; while (time_before(jiffies, timeout)) { - ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS); if (ret < 0) return ret; @@ -361,35 +373,33 @@ void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps) ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer; } -int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum) +static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr, + int regnum) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; ret = mv88e6xxx_ppu_access_get(ps); if (ret >= 0) { - ret = mv88e6xxx_reg_read(ps, addr, regnum); + ret = _mv88e6xxx_reg_read(ps, addr, regnum); mv88e6xxx_ppu_access_put(ps); } return ret; } -int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, - int regnum, u16 val) +static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr, + int regnum, u16 val) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; ret = mv88e6xxx_ppu_access_get(ps); if (ret >= 0) { - ret = mv88e6xxx_reg_write(ps, addr, regnum, val); + ret = _mv88e6xxx_reg_write(ps, addr, regnum, val); mv88e6xxx_ppu_access_put(ps); } return ret; } -#endif static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps) { @@ -446,22 +456,12 @@ static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps) return false; } -static bool mv88e6xxx_has_stu(struct mv88e6xxx_priv_state *ps) -{ - /* Does the device have STU and dedicated SID registers for VTU ops? */ - if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) || - mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) - return true; - - return false; -} - /* We expect the switch to perform auto negotiation if there is a real * phy. However, in the case of a fixed link phy, we force the port * settings from the fixed link settings. */ -void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev) +static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u32 reg; @@ -707,7 +707,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps, return value; } -void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, + uint8_t *data) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; @@ -723,7 +724,7 @@ void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data) } } -int mv88e6xxx_get_sset_count(struct dsa_switch *ds) +static int mv88e6xxx_get_sset_count(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; @@ -737,9 +738,8 @@ int mv88e6xxx_get_sset_count(struct dsa_switch *ds) return j; } -void -mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, - int port, uint64_t *data) +static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_hw_stat *stat; @@ -764,13 +764,13 @@ mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, mutex_unlock(&ps->smi_mutex); } -int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) +static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port) { return 32 * sizeof(u16); } -void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, - struct ethtool_regs *regs, void *_p) +static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, + struct ethtool_regs *regs, void *_p) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 *p = _p; @@ -780,13 +780,17 @@ void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, memset(p, 0xff, 32 * sizeof(u16)); + mutex_lock(&ps->smi_mutex); + for (i = 0; i < 32; i++) { int ret; - ret = mv88e6xxx_reg_read(ps, REG_PORT(port), i); + ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i); if (ret >= 0) p[i] = ret; } + + mutex_unlock(&ps->smi_mutex); } static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset, @@ -826,7 +830,7 @@ static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps) GLOBAL2_SMI_OP_BUSY); } -int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) +static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -834,7 +838,7 @@ int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds) GLOBAL2_EEPROM_OP_LOAD); } -int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) +static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -842,6 +846,225 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) GLOBAL2_EEPROM_OP_BUSY); } +static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->eeprom_mutex); + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_READ | + (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); + if (ret < 0) + goto error; + + ret = mv88e6xxx_eeprom_busy_wait(ds); + if (ret < 0) + goto error; + + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA); +error: + mutex_unlock(&ps->eeprom_mutex); + return ret; +} + +static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return ps->eeprom_len; + + return 0; +} + +static int mv88e6xxx_get_eeprom(struct dsa_switch *ds, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int offset; + int len; + int ret; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return -EOPNOTSUPP; + + offset = eeprom->offset; + len = eeprom->len; + eeprom->len = 0; + + eeprom->magic = 0xc3ec4951; + + ret = mv88e6xxx_eeprom_load_wait(ds); + if (ret < 0) + return ret; + + if (offset & 1) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = (word >> 8) & 0xff; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = word & 0xff; + *data++ = (word >> 8) & 0xff; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + *data++ = word & 0xff; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + +static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP); + if (ret < 0) + return ret; + + if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN)) + return -EROFS; + + return 0; +} + +static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr, + u16 data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int ret; + + mutex_lock(&ps->eeprom_mutex); + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data); + if (ret < 0) + goto error; + + ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP, + GLOBAL2_EEPROM_OP_WRITE | + (addr & GLOBAL2_EEPROM_OP_ADDR_MASK)); + if (ret < 0) + goto error; + + ret = mv88e6xxx_eeprom_busy_wait(ds); +error: + mutex_unlock(&ps->eeprom_mutex); + return ret; +} + +static int mv88e6xxx_set_eeprom(struct dsa_switch *ds, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int offset; + int ret; + int len; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + return -EOPNOTSUPP; + + if (eeprom->magic != 0xc3ec4951) + return -EINVAL; + + ret = mv88e6xxx_eeprom_is_readonly(ds); + if (ret) + return ret; + + offset = eeprom->offset; + len = eeprom->len; + eeprom->len = 0; + + ret = mv88e6xxx_eeprom_load_wait(ds); + if (ret < 0) + return ret; + + if (offset & 1) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + word = (*data++ << 8) | (word & 0xff); + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset++; + len--; + eeprom->len++; + } + + while (len >= 2) { + int word; + + word = *data++; + word |= *data++ << 8; + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset += 2; + len -= 2; + eeprom->len += 2; + } + + if (len) { + int word; + + word = mv88e6xxx_read_eeprom_word(ds, offset >> 1); + if (word < 0) + return word; + + word = (word & 0xff00) | *data++; + + ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word); + if (ret < 0) + return ret; + + offset++; + len--; + eeprom->len++; + } + + return 0; +} + static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps) { return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP, @@ -884,11 +1107,15 @@ static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps, return _mv88e6xxx_phy_wait(ps); } -int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) +static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int reg; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); reg = _mv88e6xxx_phy_read_indirect(ps, port, 16); @@ -910,13 +1137,16 @@ out: return reg; } -int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) +static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, + struct phy_device *phydev, struct ethtool_eee *e) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int reg; int ret; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); ret = _mv88e6xxx_phy_read_indirect(ps, port, 16); @@ -1138,10 +1368,15 @@ static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps, return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg); } -void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int stp_state; + int err; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE)) + return; switch (state) { case BR_STATE_DISABLED: @@ -1160,12 +1395,13 @@ void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) break; } - /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled, - * so we can not update the port state directly but need to schedule it. - */ - ps->ports[port].state = stp_state; - set_bit(port, ps->port_state_update_mask); - schedule_work(&ps->bridge_work); + mutex_lock(&ps->smi_mutex); + err = _mv88e6xxx_port_state(ps, port, stp_state); + mutex_unlock(&ps->smi_mutex); + + if (err) + netdev_err(ds->ports[port], "failed to update state to %s\n", + mv88e6xxx_port_state_names[stp_state]); } static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port, @@ -1267,6 +1503,18 @@ static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps, return 0; } +static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0); +} + +static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2); +} + static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps, struct mv88e6xxx_vtu_stu_entry *entry, unsigned int nibble_offset) @@ -1292,6 +1540,18 @@ static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps, return 0; } +static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0); +} + +static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps, + struct mv88e6xxx_vtu_stu_entry *entry) +{ + return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2); +} + static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid) { return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, @@ -1320,7 +1580,7 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps, next.valid = !!(ret & GLOBAL_VTU_VID_VALID); if (next.valid) { - ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 0); + ret = mv88e6xxx_vtu_data_read(ps, &next); if (ret < 0) return ret; @@ -1344,7 +1604,7 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps, next.fid |= ret & 0xf; } - if (mv88e6xxx_has_stu(ps)) { + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) { ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID); if (ret < 0) @@ -1358,15 +1618,18 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps, return 0; } -int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - int (*cb)(struct switchdev_obj *obj)) +static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_vlan *vlan, + int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_vtu_stu_entry next; u16 pvid; int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); @@ -1424,11 +1687,11 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps, goto loadpurge; /* Write port member tags */ - ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 0); + ret = mv88e6xxx_vtu_data_write(ps, entry); if (ret < 0) return ret; - if (mv88e6xxx_has_stu(ps)) { + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) { reg = entry->sid & GLOBAL_VTU_SID_MASK; ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg); if (ret < 0) @@ -1490,7 +1753,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid, next.valid = !!(ret & GLOBAL_VTU_VID_VALID); if (next.valid) { - ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 2); + ret = mv88e6xxx_stu_data_read(ps, &next); if (ret < 0) return ret; } @@ -1513,7 +1776,7 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps, goto loadpurge; /* Write port states */ - ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 2); + ret = mv88e6xxx_stu_data_write(ps, entry); if (ret < 0) return ret; @@ -1782,14 +2045,17 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = { [PORT_CONTROL_2_8021Q_SECURE] = "Secure", }; -int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering) +static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : PORT_CONTROL_2_8021Q_DISABLED; int ret; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2); @@ -1819,12 +2085,16 @@ unlock: return ret; } -int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + /* If the requested port doesn't belong to the same bridge as the VLAN * members, do not support it (yet) and fallback to software VLAN. */ @@ -1856,15 +2126,18 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port, return _mv88e6xxx_vtu_loadpurge(ps, &vlan); } -void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; u16 vid; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return; + mutex_lock(&ps->smi_mutex); for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) @@ -1915,13 +2188,16 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps, return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false); } -int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); u16 pvid, vid; int err = 0; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); err = _mv88e6xxx_port_pvid_get(ps, port, &pvid); @@ -2026,37 +2302,48 @@ static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port, return _mv88e6xxx_atu_load(ps, &entry); } -int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. */ return 0; } -void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) +static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) { int state = is_multicast_ether_addr(fdb->addr) ? GLOBAL_ATU_DATA_STATE_MC_STATIC : GLOBAL_ATU_DATA_STATE_UC_STATIC; struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return; + mutex_lock(&ps->smi_mutex); if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state)) netdev_err(ds->ports[port], "failed to load MAC address\n"); mutex_unlock(&ps->smi_mutex); } -int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) +static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int ret; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, GLOBAL_ATU_DATA_STATE_UNUSED); @@ -2151,9 +2438,9 @@ static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps, return err; } -int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - int (*cb)(struct switchdev_obj *obj)) +static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_fdb *fdb, + int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct mv88e6xxx_vtu_stu_entry vlan = { @@ -2162,6 +2449,9 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, u16 fid; int err; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU)) + return -EOPNOTSUPP; + mutex_lock(&ps->smi_mutex); /* Dump port's default Filtering Information Database (VLAN ID 0) */ @@ -2198,11 +2488,14 @@ unlock: return err; } -int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *bridge) +static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int i, err; + int i, err = 0; + + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE)) + return -EOPNOTSUPP; mutex_lock(&ps->smi_mutex); @@ -2222,12 +2515,15 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, return err; } -void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) +static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct net_device *bridge = ps->ports[port].bridge_dev; int i; + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE)) + return; + mutex_lock(&ps->smi_mutex); /* Unassign the bridge and remap each port's VLANTable */ @@ -2241,27 +2537,6 @@ void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) mutex_unlock(&ps->smi_mutex); } -static void mv88e6xxx_bridge_work(struct work_struct *work) -{ - struct mv88e6xxx_priv_state *ps; - struct dsa_switch *ds; - int port; - - ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work); - ds = ps->ds; - - mutex_lock(&ps->smi_mutex); - - for (port = 0; port < ps->info->num_ports; ++port) - if (test_and_clear_bit(port, ps->port_state_update_mask) && - _mv88e6xxx_port_state(ps, port, ps->ports[port].state)) - netdev_warn(ds->ports[port], - "failed to update state to %s\n", - mv88e6xxx_port_state_names[ps->ports[port].state]); - - mutex_unlock(&ps->smi_mutex); -} - static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps, int port, int page, int reg, int val) { @@ -2294,6 +2569,68 @@ restore_page_0: return ret; } +static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps) +{ + bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE); + u16 is_reset = (ppu_active ? 0x8800 : 0xc800); + struct gpio_desc *gpiod = ps->reset; + unsigned long timeout; + int ret; + int i; + + /* Set all ports to the disabled state. */ + for (i = 0; i < ps->info->num_ports; i++) { + ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL); + if (ret < 0) + return ret; + + ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL, + ret & 0xfffc); + if (ret) + return ret; + } + + /* Wait for transmit queues to drain. */ + usleep_range(2000, 4000); + + /* If there is a gpio connected to the reset pin, toggle it */ + if (gpiod) { + gpiod_set_value_cansleep(gpiod, 1); + usleep_range(10000, 20000); + gpiod_set_value_cansleep(gpiod, 0); + usleep_range(10000, 20000); + } + + /* Reset the switch. Keep the PPU active if requested. The PPU + * needs to be active to support indirect phy register access + * through global registers 0x18 and 0x19. + */ + if (ppu_active) + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000); + else + ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400); + if (ret) + return ret; + + /* Wait up to one second for reset to complete. */ + timeout = jiffies + 1 * HZ; + while (time_before(jiffies, timeout)) { + ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00); + if (ret < 0) + return ret; + + if ((ret & is_reset) == is_reset) + break; + usleep_range(1000, 2000); + } + if (time_after(jiffies, timeout)) + ret = -ETIMEDOUT; + else + ret = 0; + + return ret; +} + static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) { int ret; @@ -2313,14 +2650,12 @@ static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps) return ret; } -static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) +static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port) { - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + struct dsa_switch *ds = ps->ds; int ret; u16 reg; - mutex_lock(&ps->smi_mutex); - if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) || @@ -2349,7 +2684,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg); if (ret) - goto abort; + return ret; } /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, @@ -2413,7 +2748,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL, reg); if (ret) - goto abort; + return ret; } /* If this port is connected to a SerDes, make sure the SerDes is not @@ -2422,14 +2757,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) if (mv88e6xxx_6352_family(ps)) { ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS); if (ret < 0) - goto abort; + return ret; ret &= PORT_STATUS_CMODE_MASK; if ((ret == PORT_STATUS_CMODE_100BASE_X) || (ret == PORT_STATUS_CMODE_1000BASE_X) || (ret == PORT_STATUS_CMODE_SGMII)) { ret = mv88e6xxx_power_on_serdes(ps); if (ret < 0) - goto abort; + return ret; } } @@ -2466,7 +2801,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2, reg); if (ret) - goto abort; + return ret; } /* Port Association Vector: when learning source addresses @@ -2481,13 +2816,13 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg); if (ret) - goto abort; + return ret; /* Egress rate control 2: disable egress rate control. */ ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2, 0x0000); if (ret) - goto abort; + return ret; if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) || @@ -2499,7 +2834,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PAUSE_CTRL, 0x0000); if (ret) - goto abort; + return ret; /* Port ATU control: disable limiting the number of * address database entries that this port is allowed @@ -2513,7 +2848,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PRI_OVERRIDE, 0x0000); if (ret) - goto abort; + return ret; /* Port Ethertype: use the Ethertype DSA Ethertype * value. @@ -2521,14 +2856,14 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ETH_TYPE, ETH_P_EDSA); if (ret) - goto abort; + return ret; /* Tag Remap: use an identity 802.1p prio -> switch * prio mapping. */ ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_TAG_REGMAP_0123, 0x3210); if (ret) - goto abort; + return ret; /* Tag Remap 2: use an identity 802.1p prio -> switch * prio mapping. @@ -2536,7 +2871,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_TAG_REGMAP_4567, 0x7654); if (ret) - goto abort; + return ret; } if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || @@ -2547,7 +2882,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL, 0x0001); if (ret) - goto abort; + return ret; } /* Port Control 1: disable trunking, disable sending @@ -2555,7 +2890,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) */ ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000); if (ret) - goto abort; + return ret; /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the @@ -2563,52 +2898,60 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) */ ret = _mv88e6xxx_port_fid_set(ps, port, 0); if (ret) - goto abort; + return ret; ret = _mv88e6xxx_port_based_vlan_map(ps, port); if (ret) - goto abort; + return ret; /* Default VLAN ID and priority: don't set a default VLAN * ID, and set the default packet priority to zero. */ ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN, 0x0000); -abort: - mutex_unlock(&ps->smi_mutex); - return ret; -} - -int mv88e6xxx_setup_ports(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int ret; - int i; + if (ret) + return ret; - for (i = 0; i < ps->info->num_ports; i++) { - ret = mv88e6xxx_setup_port(ds, i); - if (ret < 0) - return ret; - } return 0; } -int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps) +static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps) { - mutex_init(&ps->smi_mutex); + struct dsa_switch *ds = ps->ds; + u32 upstream_port = dsa_upstream_port(ds); + u16 reg; + int err; + int i; - INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work); + /* Enable the PHY Polling Unit if present, don't discard any packets, + * and mask all interrupt sources. + */ + reg = 0; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) || + mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE)) + reg |= GLOBAL_CONTROL_PPU_ENABLE; - return 0; -} + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg); + if (err) + return err; -int mv88e6xxx_setup_global(struct dsa_switch *ds) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int err; - int i; + /* Configure the upstream port, and configure it as the port to which + * ingress and egress and ARP monitor frames are to be sent. + */ + reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT | + upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT | + upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT; + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg); + if (err) + return err; + + /* Disable remote management, and set the switch's DSA device number. */ + err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2, + GLOBAL_CONTROL_2_MULTIPLE_CASCADE | + (ds->index & 0x1f)); + if (err) + return err; - mutex_lock(&ps->smi_mutex); /* Set the default address aging time to 5 minutes, and * enable address learn messages to be sent to all message * ports. @@ -2616,45 +2959,45 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL, 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL); if (err) - goto unlock; + return err; /* Configure the IP ToS mapping registers. */ err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff); if (err) - goto unlock; + return err; err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff); if (err) - goto unlock; + return err; /* Configure the IEEE 802.1p priority mapping register. */ err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41); if (err) - goto unlock; + return err; /* Send all frames with destination addresses matching * 01:80:c2:00:00:0x to the CPU port. */ err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff); if (err) - goto unlock; + return err; /* Ignore removed tag data on doubly tagged packets, disable * flow control messages, force flow control priority to the @@ -2665,15 +3008,15 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 | GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI); if (err) - goto unlock; + return err; /* Program the DSA routing table. */ for (i = 0; i < 32; i++) { int nexthop = 0x1f; - if (ds->pd->rtable && - i != ds->index && i < ds->dst->pd->nr_chips) - nexthop = ds->pd->rtable[i] & 0x1f; + if (ps->ds->cd->rtable && + i != ps->ds->index && i < ps->ds->dst->pd->nr_chips) + nexthop = ps->ds->cd->rtable[i] & 0x1f; err = _mv88e6xxx_reg_write( ps, REG_GLOBAL2, @@ -2681,7 +3024,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) GLOBAL2_DEVICE_MAPPING_UPDATE | (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop); if (err) - goto unlock; + return err; } /* Clear all trunk masks. */ @@ -2691,7 +3034,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) | ((1 << ps->info->num_ports) - 1)); if (err) - goto unlock; + return err; } /* Clear all trunk mappings. */ @@ -2702,7 +3045,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) GLOBAL2_TRUNK_MAPPING_UPDATE | (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT)); if (err) - goto unlock; + return err; } if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) || @@ -2714,7 +3057,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_2X, 0xffff); if (err) - goto unlock; + return err; /* Initialise cross-chip port VLAN table to reset * defaults. @@ -2722,7 +3065,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_PVT_ADDR, 0x9000); if (err) - goto unlock; + return err; /* Clear the priority override table. */ for (i = 0; i < 16; i++) { @@ -2730,7 +3073,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) GLOBAL2_PRIO_OVERRIDE, 0x8000 | (i << 8)); if (err) - goto unlock; + return err; } } @@ -2747,7 +3090,7 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) GLOBAL2_INGRESS_OP, 0x9000 | (i << 8)); if (err) - goto unlock; + return err; } } @@ -2755,89 +3098,60 @@ int mv88e6xxx_setup_global(struct dsa_switch *ds) err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP, GLOBAL_STATS_OP_FLUSH_ALL); if (err) - goto unlock; + return err; /* Wait for the flush to complete. */ err = _mv88e6xxx_stats_wait(ps); - if (err < 0) - goto unlock; + if (err) + return err; /* Clear all ATU entries */ err = _mv88e6xxx_atu_flush(ps, 0, true); - if (err < 0) - goto unlock; + if (err) + return err; /* Clear all the VTU and STU entries */ err = _mv88e6xxx_vtu_stu_flush(ps); -unlock: - mutex_unlock(&ps->smi_mutex); + if (err < 0) + return err; return err; } -int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active) +static int mv88e6xxx_setup(struct dsa_switch *ds) { - u16 is_reset = (ppu_active ? 0x8800 : 0xc800); - struct gpio_desc *gpiod = ps->ds->pd->reset; - unsigned long timeout; - int ret; + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + int err; int i; - mutex_lock(&ps->smi_mutex); + ps->ds = ds; - /* Set all ports to the disabled state. */ - for (i = 0; i < ps->info->num_ports; i++) { - ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL); - if (ret < 0) - goto unlock; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM)) + mutex_init(&ps->eeprom_mutex); - ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL, - ret & 0xfffc); - if (ret) - goto unlock; - } + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + mv88e6xxx_ppu_state_init(ps); - /* Wait for transmit queues to drain. */ - usleep_range(2000, 4000); + mutex_lock(&ps->smi_mutex); - /* If there is a gpio connected to the reset pin, toggle it */ - if (gpiod) { - gpiod_set_value_cansleep(gpiod, 1); - usleep_range(10000, 20000); - gpiod_set_value_cansleep(gpiod, 0); - usleep_range(10000, 20000); - } + err = mv88e6xxx_switch_reset(ps); + if (err) + goto unlock; - /* Reset the switch. Keep the PPU active if requested. The PPU - * needs to be active to support indirect phy register access - * through global registers 0x18 and 0x19. - */ - if (ppu_active) - ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000); - else - ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400); - if (ret) + err = mv88e6xxx_setup_global(ps); + if (err) goto unlock; - /* Wait up to one second for reset to complete. */ - timeout = jiffies + 1 * HZ; - while (time_before(jiffies, timeout)) { - ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00); - if (ret < 0) + for (i = 0; i < ps->info->num_ports; i++) { + err = mv88e6xxx_setup_port(ps, i); + if (err) goto unlock; - - if ((ret & is_reset) == is_reset) - break; - usleep_range(1000, 2000); } - if (time_after(jiffies, timeout)) - ret = -ETIMEDOUT; - else - ret = 0; + unlock: mutex_unlock(&ps->smi_mutex); - return ret; + return err; } int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) @@ -2873,8 +3187,7 @@ static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps, return -EINVAL; } -int -mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) +static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int addr = mv88e6xxx_port_to_phy_addr(ps, port); @@ -2884,29 +3197,20 @@ mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum) return 0xffff; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_read(ps, addr, regnum); - mutex_unlock(&ps->smi_mutex); - return ret; -} -int -mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6xxx_port_to_phy_addr(ps, port); - int ret; - - if (addr < 0) - return 0xffff; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum); + else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) + ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum); + else + ret = _mv88e6xxx_phy_read(ps, addr, regnum); - mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write(ps, addr, regnum, val); mutex_unlock(&ps->smi_mutex); return ret; } -int -mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) +static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, + u16 val) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int addr = mv88e6xxx_port_to_phy_addr(ps, port); @@ -2916,24 +3220,14 @@ mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum) return 0xffff; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum); - mutex_unlock(&ps->smi_mutex); - return ret; -} - -int -mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, - u16 val) -{ - struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - int addr = mv88e6xxx_port_to_phy_addr(ps, port); - int ret; - if (addr < 0) - return addr; + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU)) + ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val); + else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY)) + ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val); + else + ret = _mv88e6xxx_phy_write(ps, addr, regnum, val); - mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val); mutex_unlock(&ps->smi_mutex); return ret; } @@ -3002,23 +3296,26 @@ static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp) return 0; } -int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) +static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP)) + return -EOPNOTSUPP; + if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps)) return mv88e63xx_get_temp(ds, temp); return mv88e61xx_get_temp(ds, temp); } -int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) +static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; *temp = 0; @@ -3032,13 +3329,13 @@ int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp) return 0; } -int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) +static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26); @@ -3049,13 +3346,13 @@ int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp) (ret & 0xe0ff) | (temp << 8)); } -int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) +static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); int phy = mv88e6xxx_6320_family(ps) ? 3 : 0; int ret; - if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps)) + if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT)) return -EOPNOTSUPP; *alarm = false; @@ -3070,6 +3367,161 @@ int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm) } #endif /* CONFIG_NET_DSA_HWMON */ +static const struct mv88e6xxx_info mv88e6xxx_table[] = { + [MV88E6085] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, + .family = MV88E6XXX_FAMILY_6097, + .name = "Marvell 88E6085", + .num_databases = 4096, + .num_ports = 10, + .flags = MV88E6XXX_FLAGS_FAMILY_6097, + }, + + [MV88E6095] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6095, + .family = MV88E6XXX_FAMILY_6095, + .name = "Marvell 88E6095/88E6095F", + .num_databases = 256, + .num_ports = 11, + .flags = MV88E6XXX_FLAGS_FAMILY_6095, + }, + + [MV88E6123] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6123, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6123", + .num_databases = 4096, + .num_ports = 3, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6131] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6131, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6131", + .num_databases = 256, + .num_ports = 8, + .flags = MV88E6XXX_FLAGS_FAMILY_6185, + }, + + [MV88E6161] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6161", + .num_databases = 4096, + .num_ports = 6, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6165] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6165, + .family = MV88E6XXX_FAMILY_6165, + .name = "Marvell 88E6165", + .num_databases = 4096, + .num_ports = 6, + .flags = MV88E6XXX_FLAGS_FAMILY_6165, + }, + + [MV88E6171] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6171, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6171", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6172] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6172, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6172", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6175] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6175, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6175", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6176] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6176, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6176", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6185] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6185, + .family = MV88E6XXX_FAMILY_6185, + .name = "Marvell 88E6185", + .num_databases = 256, + .num_ports = 10, + .flags = MV88E6XXX_FLAGS_FAMILY_6185, + }, + + [MV88E6240] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6240, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6240", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, + + [MV88E6320] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6320, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6320", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6320, + }, + + [MV88E6321] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6321, + .family = MV88E6XXX_FAMILY_6320, + .name = "Marvell 88E6321", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6320, + }, + + [MV88E6350] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6350, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6350", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6351] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6351, + .family = MV88E6XXX_FAMILY_6351, + .name = "Marvell 88E6351", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6351, + }, + + [MV88E6352] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6352, + .family = MV88E6XXX_FAMILY_6352, + .name = "Marvell 88E6352", + .num_databases = 4096, + .num_ports = 7, + .flags = MV88E6XXX_FLAGS_FAMILY_6352, + }, +}; + static const struct mv88e6xxx_info * mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table, unsigned int num) @@ -3083,10 +3535,9 @@ mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table, return NULL; } -const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, - int sw_addr, void **priv, - const struct mv88e6xxx_info *table, - unsigned int num) +static const char *mv88e6xxx_drv_probe(struct device *dsa_dev, + struct device *host_dev, int sw_addr, + void **priv) { const struct mv88e6xxx_info *info; struct mv88e6xxx_priv_state *ps; @@ -3105,7 +3556,8 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, prod_num = (id & 0xfff0) >> 4; rev = id & 0x000f; - info = mv88e6xxx_lookup_info(prod_num, table, num); + info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table, + ARRAY_SIZE(mv88e6xxx_table)); if (!info) return NULL; @@ -3118,6 +3570,7 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, ps->bus = bus; ps->sw_addr = sw_addr; ps->info = info; + mutex_init(&ps->smi_mutex); *priv = ps; @@ -3127,38 +3580,141 @@ const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, return name; } -static int __init mv88e6xxx_init(void) -{ -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) - register_switch_driver(&mv88e6131_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123) - register_switch_driver(&mv88e6123_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352) - register_switch_driver(&mv88e6352_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) - register_switch_driver(&mv88e6171_switch_driver); +struct dsa_switch_driver mv88e6xxx_switch_driver = { + .tag_protocol = DSA_TAG_PROTO_EDSA, + .probe = mv88e6xxx_drv_probe, + .setup = mv88e6xxx_setup, + .set_addr = mv88e6xxx_set_addr, + .phy_read = mv88e6xxx_phy_read, + .phy_write = mv88e6xxx_phy_write, + .adjust_link = mv88e6xxx_adjust_link, + .get_strings = mv88e6xxx_get_strings, + .get_ethtool_stats = mv88e6xxx_get_ethtool_stats, + .get_sset_count = mv88e6xxx_get_sset_count, + .set_eee = mv88e6xxx_set_eee, + .get_eee = mv88e6xxx_get_eee, +#ifdef CONFIG_NET_DSA_HWMON + .get_temp = mv88e6xxx_get_temp, + .get_temp_limit = mv88e6xxx_get_temp_limit, + .set_temp_limit = mv88e6xxx_set_temp_limit, + .get_temp_alarm = mv88e6xxx_get_temp_alarm, #endif + .get_eeprom_len = mv88e6xxx_get_eeprom_len, + .get_eeprom = mv88e6xxx_get_eeprom, + .set_eeprom = mv88e6xxx_set_eeprom, + .get_regs_len = mv88e6xxx_get_regs_len, + .get_regs = mv88e6xxx_get_regs, + .port_bridge_join = mv88e6xxx_port_bridge_join, + .port_bridge_leave = mv88e6xxx_port_bridge_leave, + .port_stp_state_set = mv88e6xxx_port_stp_state_set, + .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, + .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, + .port_vlan_add = mv88e6xxx_port_vlan_add, + .port_vlan_del = mv88e6xxx_port_vlan_del, + .port_vlan_dump = mv88e6xxx_port_vlan_dump, + .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, + .port_fdb_add = mv88e6xxx_port_fdb_add, + .port_fdb_del = mv88e6xxx_port_fdb_del, + .port_fdb_dump = mv88e6xxx_port_fdb_dump, +}; + +int mv88e6xxx_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct device_node *np = dev->of_node; + struct mv88e6xxx_priv_state *ps; + int id, prod_num, rev; + struct dsa_switch *ds; + u32 eeprom_len; + int err; + + ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL); + if (!ds) + return -ENOMEM; + + ps = (struct mv88e6xxx_priv_state *)(ds + 1); + ds->priv = ps; + ds->dev = dev; + ps->dev = dev; + ps->ds = ds; + ps->bus = mdiodev->bus; + ps->sw_addr = mdiodev->addr; + mutex_init(&ps->smi_mutex); + + get_device(&ps->bus->dev); + + ds->drv = &mv88e6xxx_switch_driver; + + id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID); + if (id < 0) + return id; + + prod_num = (id & 0xfff0) >> 4; + rev = id & 0x000f; + + ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table, + ARRAY_SIZE(mv88e6xxx_table)); + if (!ps->info) + return -ENODEV; + + ps->reset = devm_gpiod_get(&mdiodev->dev, "reset", GPIOD_ASIS); + if (IS_ERR(ps->reset)) { + err = PTR_ERR(ps->reset); + if (err == -ENOENT) { + /* Optional, so not an error */ + ps->reset = NULL; + } else { + return err; + } + } + + if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) && + !of_property_read_u32(np, "eeprom-length", &eeprom_len)) + ps->eeprom_len = eeprom_len; + + dev_set_drvdata(dev, ds); + + dev_info(dev, "switch 0x%x probed: %s, revision %u\n", + prod_num, ps->info->name, rev); + return 0; } + +static void mv88e6xxx_remove(struct mdio_device *mdiodev) +{ + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); + + put_device(&ps->bus->dev); +} + +static const struct of_device_id mv88e6xxx_of_match[] = { + { .compatible = "marvell,mv88e6085" }, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); + +static struct mdio_driver mv88e6xxx_driver = { + .probe = mv88e6xxx_probe, + .remove = mv88e6xxx_remove, + .mdiodrv.driver = { + .name = "mv88e6085", + .of_match_table = mv88e6xxx_of_match, + }, +}; + +static int __init mv88e6xxx_init(void) +{ + register_switch_driver(&mv88e6xxx_switch_driver); + return mdio_driver_register(&mv88e6xxx_driver); +} module_init(mv88e6xxx_init); static void __exit mv88e6xxx_cleanup(void) { -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) - unregister_switch_driver(&mv88e6171_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352) - unregister_switch_driver(&mv88e6352_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123) - unregister_switch_driver(&mv88e6123_switch_driver); -#endif -#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) - unregister_switch_driver(&mv88e6131_switch_driver); -#endif + mdio_driver_unregister(&mv88e6xxx_driver); + unregister_switch_driver(&mv88e6xxx_switch_driver); } module_exit(mv88e6xxx_cleanup); diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 4f455d219859..36d0e1504de1 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -12,6 +12,7 @@ #define __MV88E6XXX_H #include <linux/if_vlan.h> +#include <linux/gpio/consumer.h> #ifndef UINT64_MAX #define UINT64_MAX (u64)(~((u64)0)) @@ -338,6 +339,27 @@ #define MV88E6XXX_N_FID 4096 +/* List of supported models */ +enum mv88e6xxx_model { + MV88E6085, + MV88E6095, + MV88E6123, + MV88E6131, + MV88E6161, + MV88E6165, + MV88E6171, + MV88E6172, + MV88E6175, + MV88E6176, + MV88E6185, + MV88E6240, + MV88E6320, + MV88E6321, + MV88E6350, + MV88E6351, + MV88E6352, +}; + enum mv88e6xxx_family { MV88E6XXX_FAMILY_NONE, MV88E6XXX_FAMILY_6065, /* 6031 6035 6061 6065 */ @@ -350,12 +372,154 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ }; +enum mv88e6xxx_cap { + /* Address Translation Unit. + * The ATU is used to lookup and learn MAC addresses. See GLOBAL_ATU_OP. + */ + MV88E6XXX_CAP_ATU, + + /* Energy Efficient Ethernet. + */ + MV88E6XXX_CAP_EEE, + + /* EEPROM Command and Data registers. + * See GLOBAL2_EEPROM_OP and GLOBAL2_EEPROM_DATA. + */ + MV88E6XXX_CAP_EEPROM, + + /* Port State Filtering for 802.1D Spanning Tree. + * See PORT_CONTROL_STATE_* values in the PORT_CONTROL register. + */ + MV88E6XXX_CAP_PORTSTATE, + + /* PHY Polling Unit. + * See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING. + */ + MV88E6XXX_CAP_PPU, + MV88E6XXX_CAP_PPU_ACTIVE, + + /* SMI PHY Command and Data registers. + * This requires an indirect access to PHY registers through + * GLOBAL2_SMI_OP, otherwise direct access to PHY registers is done. + */ + MV88E6XXX_CAP_SMI_PHY, + + /* Per VLAN Spanning Tree Unit (STU). + * The Port State database, if present, is accessed through VTU + * operations and dedicated SID registers. See GLOBAL_VTU_SID. + */ + MV88E6XXX_CAP_STU, + + /* Switch MAC/WoL/WoF register. + * This requires an indirect access to set the switch MAC address + * through GLOBAL2_SWITCH_MAC, otherwise GLOBAL_MAC_01, GLOBAL_MAC_23, + * and GLOBAL_MAC_45 are used with a direct access. + */ + MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF, + + /* Internal temperature sensor. + * Available from any enabled port's PHY register 26, page 6. + */ + MV88E6XXX_CAP_TEMP, + MV88E6XXX_CAP_TEMP_LIMIT, + + /* In-chip Port Based VLANs. + * Each port VLANTable register (see PORT_BASE_VLAN) is used to restrict + * the output (or egress) ports to which it is allowed to send frames. + */ + MV88E6XXX_CAP_VLANTABLE, + + /* VLAN Table Unit. + * The VTU is used to program 802.1Q VLANs. See GLOBAL_VTU_OP. + */ + MV88E6XXX_CAP_VTU, +}; + +/* Bitmask of capabilities */ +#define MV88E6XXX_FLAG_ATU BIT(MV88E6XXX_CAP_ATU) +#define MV88E6XXX_FLAG_EEE BIT(MV88E6XXX_CAP_EEE) +#define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM) +#define MV88E6XXX_FLAG_PORTSTATE BIT(MV88E6XXX_CAP_PORTSTATE) +#define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU) +#define MV88E6XXX_FLAG_PPU_ACTIVE BIT(MV88E6XXX_CAP_PPU_ACTIVE) +#define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY) +#define MV88E6XXX_FLAG_STU BIT(MV88E6XXX_CAP_STU) +#define MV88E6XXX_FLAG_SWITCH_MAC BIT(MV88E6XXX_CAP_SWITCH_MAC_WOL_WOF) +#define MV88E6XXX_FLAG_TEMP BIT(MV88E6XXX_CAP_TEMP) +#define MV88E6XXX_FLAG_TEMP_LIMIT BIT(MV88E6XXX_CAP_TEMP_LIMIT) +#define MV88E6XXX_FLAG_VLANTABLE BIT(MV88E6XXX_CAP_VLANTABLE) +#define MV88E6XXX_FLAG_VTU BIT(MV88E6XXX_CAP_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6095 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6097 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6165 \ + (MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6185 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PPU | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6320 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_EEE | \ + MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_TEMP_LIMIT | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6351 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + +#define MV88E6XXX_FLAGS_FAMILY_6352 \ + (MV88E6XXX_FLAG_ATU | \ + MV88E6XXX_FLAG_EEE | \ + MV88E6XXX_FLAG_EEPROM | \ + MV88E6XXX_FLAG_PORTSTATE | \ + MV88E6XXX_FLAG_PPU_ACTIVE | \ + MV88E6XXX_FLAG_SMI_PHY | \ + MV88E6XXX_FLAG_STU | \ + MV88E6XXX_FLAG_SWITCH_MAC | \ + MV88E6XXX_FLAG_TEMP | \ + MV88E6XXX_FLAG_TEMP_LIMIT | \ + MV88E6XXX_FLAG_VLANTABLE | \ + MV88E6XXX_FLAG_VTU) + struct mv88e6xxx_info { enum mv88e6xxx_family family; u16 prod_num; const char *name; unsigned int num_databases; unsigned int num_ports; + unsigned long flags; }; struct mv88e6xxx_atu_entry { @@ -379,7 +543,6 @@ struct mv88e6xxx_vtu_stu_entry { struct mv88e6xxx_priv_port { struct net_device *bridge_dev; - u8 state; }; struct mv88e6xxx_priv_state { @@ -403,7 +566,6 @@ struct mv88e6xxx_priv_state { struct mii_bus *bus; int sw_addr; -#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU /* Handles automatic disabling and re-enabling of the PHY * polling unit. */ @@ -411,7 +573,6 @@ struct mv88e6xxx_priv_state { int ppu_disabled; struct work_struct ppu_work; struct timer_list ppu_timer; -#endif /* This mutex serialises access to the statistics unit. * Hold this mutex over snapshot + dump sequences. @@ -431,9 +592,14 @@ struct mv88e6xxx_priv_state { struct mv88e6xxx_priv_port ports[DSA_MAX_PORTS]; - DECLARE_BITMAP(port_state_update_mask, DSA_MAX_PORTS); + /* A switch may have a GPIO line tied to its reset pin. Parse + * this from the device tree, and use it before performing + * switch soft reset. + */ + struct gpio_desc *reset; - struct work_struct bridge_work; + /* set to size of eeprom if supported by the switch */ + int eeprom_len; }; enum stat_type { @@ -449,86 +615,10 @@ struct mv88e6xxx_hw_stat { enum stat_type type; }; -int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active); -const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev, - int sw_addr, void **priv, - const struct mv88e6xxx_info *table, - unsigned int num); - -int mv88e6xxx_setup_ports(struct dsa_switch *ds); -int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps); -int mv88e6xxx_setup_global(struct dsa_switch *ds); -int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg); -int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr, - int reg, u16 val); -int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr); -int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr); -int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum); -int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val); -int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum); -int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum, - u16 val); -void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps); -int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum); -int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr, - int regnum, u16 val); -void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data); -void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port, - uint64_t *data); -int mv88e6xxx_get_sset_count(struct dsa_switch *ds); -int mv88e6xxx_get_sset_count_basic(struct dsa_switch *ds); -void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, - struct phy_device *phydev); -int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port); -void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, - struct ethtool_regs *regs, void *_p); -int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp); -int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp); -int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp); -int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm); -int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds); -int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds); -int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum); -int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, - u16 val); -int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); -int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e); -int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *bridge); -void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port); -void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); -int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering); -int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); -void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans); -int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan); -int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - int (*cb)(struct switchdev_obj *obj)); -int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); -void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); -int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); -int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - int (*cb)(struct switchdev_obj *obj)); -int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg); -int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, - int reg, int val); - -extern struct dsa_switch_driver mv88e6131_switch_driver; -extern struct dsa_switch_driver mv88e6123_switch_driver; -extern struct dsa_switch_driver mv88e6352_switch_driver; -extern struct dsa_switch_driver mv88e6171_switch_driver; +static inline bool mv88e6xxx_has(struct mv88e6xxx_priv_state *ps, + unsigned long flags) +{ + return (ps->info->flags & flags) == flags; +} #endif diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index 7677c745fb30..91ada52f776b 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -699,7 +699,7 @@ el3_tx_timeout (struct net_device *dev) dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), inw(ioaddr + TX_FREE)); dev->stats.tx_errors++; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ /* Issue TX_RESET and TX_START commands. */ outw(TxReset, ioaddr + EL3_CMD); outw(TxEnable, ioaddr + EL3_CMD); diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c index 942fb0d5aace..b26e038b4a0e 100644 --- a/drivers/net/ethernet/3com/3c515.c +++ b/drivers/net/ethernet/3com/3c515.c @@ -992,7 +992,7 @@ static void corkscrew_timeout(struct net_device *dev) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(TxEnable, ioaddr + EL3_CMD); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; dev->stats.tx_dropped++; netif_wake_queue(dev); diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c index b9948f00c5e9..b88afd759307 100644 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ b/drivers/net/ethernet/3com/3c574_cs.c @@ -700,7 +700,7 @@ static void el3_tx_timeout(struct net_device *dev) netdev_notice(dev, "Transmit timed out!\n"); dump_status(dev); dev->stats.tx_errors++; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ /* Issue TX_RESET and TX_START commands. */ tc574_wait_for_completion(dev, TxReset); outw(TxEnable, ioaddr + EL3_CMD); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c index c5a320507556..71396e4b87e3 100644 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ b/drivers/net/ethernet/3com/3c589_cs.c @@ -534,7 +534,7 @@ static void el3_tx_timeout(struct net_device *dev) netdev_warn(dev, "Transmit timed out!\n"); dump_status(dev); dev->stats.tx_errors++; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ /* Issue TX_RESET and TX_START commands. */ tc589_wait_for_completion(dev, TxReset); outw(TxEnable, ioaddr + EL3_CMD); diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index d81fceddbe0e..25c55ab05c7d 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1944,7 +1944,7 @@ static void vortex_tx_timeout(struct net_device *dev) } /* Issue Tx Enable */ iowrite16(TxEnable, ioaddr + EL3_CMD); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } /* diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c index ec6eac1f8c95..4ea717d68c95 100644 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ b/drivers/net/ethernet/8390/axnet_cs.c @@ -1041,7 +1041,7 @@ static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, { ei_local->txing = 1; NS8390_trigger_send(dev, send_length, output_page); - dev->trans_start = jiffies; + netif_trans_update(dev); if (output_page == ei_local->tx_start_page) { ei_local->tx1 = -1; @@ -1270,7 +1270,7 @@ static void ei_tx_intr(struct net_device *dev) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); - dev->trans_start = jiffies; + netif_trans_update(dev); ei_local->tx2 = -1, ei_local->lasttx = 2; } @@ -1287,7 +1287,7 @@ static void ei_tx_intr(struct net_device *dev) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); - dev->trans_start = jiffies; + netif_trans_update(dev); ei_local->tx1 = -1; ei_local->lasttx = 1; } diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c index b96e8852b2d1..60f8e2c8e726 100644 --- a/drivers/net/ethernet/8390/lib8390.c +++ b/drivers/net/ethernet/8390/lib8390.c @@ -596,7 +596,7 @@ static void ei_tx_intr(struct net_device *dev) if (ei_local->tx2 > 0) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); - dev->trans_start = jiffies; + netif_trans_update(dev); ei_local->tx2 = -1, ei_local->lasttx = 2; } else @@ -609,7 +609,7 @@ static void ei_tx_intr(struct net_device *dev) if (ei_local->tx1 > 0) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); - dev->trans_start = jiffies; + netif_trans_update(dev); ei_local->tx1 = -1; ei_local->lasttx = 1; } else diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index ac7288240d55..1d1069641d81 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -1129,7 +1129,7 @@ static void tx_timeout(struct net_device *dev) /* Trigger an immediate transmit demand. */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index 74139cb7f849..3d2245fdc283 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1430,7 +1430,7 @@ static void bfin_mac_timeout(struct net_device *dev) bfin_mac_enable(lp->phydev); /* We can accept TX packets again */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } static void bfin_mac_multicast_hash(struct net_device *dev) diff --git a/drivers/net/ethernet/agere/et131x.c b/drivers/net/ethernet/agere/et131x.c index 0907ab6ff309..30defe6c81f2 100644 --- a/drivers/net/ethernet/agere/et131x.c +++ b/drivers/net/ethernet/agere/et131x.c @@ -3349,7 +3349,7 @@ static void et131x_down(struct net_device *netdev) struct et131x_adapter *adapter = netdev_priv(netdev); /* Save the timestamp for the TX watchdog, prevent a timeout */ - netdev->trans_start = jiffies; + netif_trans_update(netdev); phy_stop(adapter->phydev); et131x_disable_txrx(netdev); @@ -3816,7 +3816,7 @@ static netdev_tx_t et131x_tx(struct sk_buff *skb, struct net_device *netdev) netif_stop_queue(netdev); /* Save the timestamp for the TX timeout watchdog */ - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* TCB is not available */ if (tx_ring->used >= NUM_TCB) diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 8d50314ac3eb..de2c4bf5fac4 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -428,7 +428,7 @@ static void emac_timeout(struct net_device *dev) emac_reset(db); emac_init_device(dev); /* We can accept TX packets again */ - dev->trans_start = jiffies; + netif_trans_update(dev); netif_wake_queue(dev); /* Restore previous register address */ @@ -468,7 +468,7 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) db->membase + EMAC_TX_CTL0_REG); /* save the time stamp */ - dev->trans_start = jiffies; + netif_trans_update(dev); } else if (channel == 1) { /* set TX len */ writel(skb->len, db->membase + EMAC_TX_PL1_REG); @@ -477,7 +477,7 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) db->membase + EMAC_TX_CTL1_REG); /* save the time stamp */ - dev->trans_start = jiffies; + netif_trans_update(dev); } if ((db->tx_fifo_stat & 3) == 3) { diff --git a/drivers/net/ethernet/amd/7990.c b/drivers/net/ethernet/amd/7990.c index 8e7575571531..dcf2a1f3643d 100644 --- a/drivers/net/ethernet/amd/7990.c +++ b/drivers/net/ethernet/amd/7990.c @@ -260,7 +260,7 @@ static int lance_reset(struct net_device *dev) load_csrs(lp); lance_init_ring(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ status = init_restart_lance(lp); #ifdef DEBUG_DRIVER printk("Lance restart=%d\n", status); @@ -530,7 +530,7 @@ void lance_tx_timeout(struct net_device *dev) { printk("lance_tx_timeout\n"); lance_reset(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } EXPORT_SYMBOL_GPL(lance_tx_timeout); diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index 2a18d34d2610..a83cd1c4ce1d 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -512,7 +512,7 @@ static inline int lance_reset(struct net_device *dev) load_csrs(lp); lance_init_ring(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_start_queue(dev); status = init_restart_lance(lp); diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index b10964e8cb54..d2bc8e5dcd23 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -764,7 +764,7 @@ static void lance_tx_timeout (struct net_device *dev) /* lance_restart, essentially */ lance_init_ring(dev); REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index d3977d032b48..e0fb0f1122db 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1074,7 +1074,7 @@ static void au1000_tx_timeout(struct net_device *dev) netdev_err(dev, "au1000_tx_timeout: dev=%p\n", dev); au1000_reset_mac(dev); au1000_init(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } @@ -1269,7 +1269,7 @@ static int au1000_probe(struct platform_device *pdev) aup->phy_irq = pd->phy_irq; } - if (aup->phy_busid && aup->phy_busid > 0) { + if (aup->phy_busid > 0) { dev_err(&pdev->dev, "MAC0-associated PHY attached 2nd MACs MII bus not supported yet\n"); err = -ENODEV; goto err_mdiobus_alloc; diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c index b584b78237df..b799c7ac899b 100644 --- a/drivers/net/ethernet/amd/declance.c +++ b/drivers/net/ethernet/amd/declance.c @@ -877,7 +877,7 @@ static inline int lance_reset(struct net_device *dev) lance_init_ring(dev); load_csrs(lp); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ status = init_restart_lance(lp); return status; } diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c index 3a7ebfdda57d..abb1ba228b26 100644 --- a/drivers/net/ethernet/amd/lance.c +++ b/drivers/net/ethernet/amd/lance.c @@ -943,7 +943,7 @@ static void lance_tx_timeout (struct net_device *dev) #endif lance_restart (dev, 0x0043, 1); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue (dev); } diff --git a/drivers/net/ethernet/amd/ni65.c b/drivers/net/ethernet/amd/ni65.c index 1cf33addd15e..cda53db75f17 100644 --- a/drivers/net/ethernet/amd/ni65.c +++ b/drivers/net/ethernet/amd/ni65.c @@ -782,7 +782,7 @@ static void ni65_stop_start(struct net_device *dev,struct priv *p) if(!p->lock) if (p->tmdnum || !p->xmit_queued) netif_wake_queue(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } else writedatareg(CSR0_STRT | csr0); @@ -1148,7 +1148,7 @@ static void ni65_timeout(struct net_device *dev) printk("%02x ",p->tmdhead[i].u.s.status); printk("\n"); ni65_lance_reinit(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 27245efe9f50..2807e181647b 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -851,7 +851,7 @@ static void mace_tx_timeout(struct net_device *dev) #else /* #if RESET_ON_TIMEOUT */ pr_cont("NOT resetting card\n"); #endif /* #if RESET_ON_TIMEOUT */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 7ccebae9cb48..c22bf52d3320 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -448,7 +448,7 @@ static void pcnet32_netif_stop(struct net_device *dev) { struct pcnet32_private *lp = netdev_priv(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ napi_disable(&lp->napi); netif_tx_disable(dev); } @@ -2426,7 +2426,7 @@ static void pcnet32_tx_timeout(struct net_device *dev) } pcnet32_restart(dev, CSR0_NORMAL); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); spin_unlock_irqrestore(&lp->lock, flags); diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index 7847638bdd22..9b56b40259dc 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -997,7 +997,7 @@ static int lance_reset(struct net_device *dev) } lp->init_ring(dev); load_csrs(lp); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ status = init_restart_lance(lp); return status; } diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c index b212488606da..472c0fb3f4c4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.c @@ -43,6 +43,7 @@ static void xgene_cle_idt_to_hw(u32 dstqid, u32 fpsel, static void xgene_cle_dbptr_to_hw(struct xgene_enet_pdata *pdata, struct xgene_cle_dbptr *dbptr, u32 *buf) { + buf[0] = SET_VAL(CLE_DROP, dbptr->drop); buf[4] = SET_VAL(CLE_FPSEL, dbptr->fpsel) | SET_VAL(CLE_DSTQIDL, dbptr->dstqid); @@ -412,7 +413,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) .branch = { { /* IPV4 */ - .valid = 0, + .valid = 1, .next_packet_pointer = 22, .jump_bw = JMP_FW, .jump_rel = JMP_ABS, @@ -420,7 +421,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) .next_node = PKT_PROT_NODE, .next_branch = 0, .data = 0x8, - .mask = 0xffff + .mask = 0x0 }, { .valid = 0, @@ -456,7 +457,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) .next_node = RSS_IPV4_TCP_NODE, .next_branch = 0, .data = 0x0600, - .mask = 0xffff + .mask = 0x00ff }, { /* UDP */ @@ -468,7 +469,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) .next_node = RSS_IPV4_UDP_NODE, .next_branch = 0, .data = 0x1100, - .mask = 0xffff + .mask = 0x00ff }, { .valid = 0, @@ -642,7 +643,7 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) { /* TCP DST Port */ .valid = 0, - .next_packet_pointer = 256, + .next_packet_pointer = 258, .jump_bw = JMP_FW, .jump_rel = JMP_ABS, .operation = EQT, @@ -729,6 +730,6 @@ static int xgene_enet_cle_init(struct xgene_enet_pdata *pdata) return xgene_cle_setup_ptree(pdata, enet_cle); } -struct xgene_cle_ops xgene_cle3in_ops = { +const struct xgene_cle_ops xgene_cle3in_ops = { .cle_init = xgene_enet_cle_init, }; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h index 29a17abdd828..33c5f6b25824 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_cle.h @@ -83,6 +83,8 @@ #define CLE_TYPE_POS 0 #define CLE_TYPE_LEN 2 +#define CLE_DROP_POS 28 +#define CLE_DROP_LEN 1 #define CLE_DSTQIDL_POS 25 #define CLE_DSTQIDL_LEN 7 #define CLE_DSTQIDH_POS 0 @@ -290,6 +292,6 @@ struct xgene_enet_cle { u32 jump_bytes; }; -extern struct xgene_cle_ops xgene_cle3in_ops; +extern const struct xgene_cle_ops xgene_cle3in_ops; #endif /* __XGENE_ENET_CLE_H__ */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 457f74500242..2f5638f7f864 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -219,27 +219,30 @@ void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring, struct xgene_enet_pdata *pdata, enum xgene_enet_err_code status) { - struct rtnl_link_stats64 *stats = &pdata->stats; - switch (status) { case INGRESS_CRC: - stats->rx_crc_errors++; + ring->rx_crc_errors++; + ring->rx_dropped++; break; case INGRESS_CHECKSUM: case INGRESS_CHECKSUM_COMPUTE: - stats->rx_errors++; + ring->rx_errors++; + ring->rx_dropped++; break; case INGRESS_TRUNC_FRAME: - stats->rx_frame_errors++; + ring->rx_frame_errors++; + ring->rx_dropped++; break; case INGRESS_PKT_LEN: - stats->rx_length_errors++; + ring->rx_length_errors++; + ring->rx_dropped++; break; case INGRESS_PKT_UNDER: - stats->rx_frame_errors++; + ring->rx_frame_errors++; + ring->rx_dropped++; break; case INGRESS_FIFO_OVERRUN: - stats->rx_fifo_errors++; + ring->rx_fifo_errors++; break; default: break; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index ba7da98af2ef..45220be3122f 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -86,7 +86,7 @@ enum xgene_enet_rm { #define RINGADDRL_POS 5 #define RINGADDRL_LEN 27 #define RINGADDRH_POS 0 -#define RINGADDRH_LEN 6 +#define RINGADDRH_LEN 7 #define RINGSIZE_POS 23 #define RINGSIZE_LEN 3 #define RINGTYPE_POS 19 @@ -94,9 +94,9 @@ enum xgene_enet_rm { #define RINGMODE_POS 20 #define RINGMODE_LEN 3 #define RECOMTIMEOUTL_POS 28 -#define RECOMTIMEOUTL_LEN 3 +#define RECOMTIMEOUTL_LEN 4 #define RECOMTIMEOUTH_POS 0 -#define RECOMTIMEOUTH_LEN 2 +#define RECOMTIMEOUTH_LEN 3 #define NUMMSGSINQ_POS 1 #define NUMMSGSINQ_LEN 16 #define ACCEPTLERR BIT(19) @@ -201,6 +201,8 @@ enum xgene_enet_rm { #define USERINFO_LEN 32 #define FPQNUM_POS 32 #define FPQNUM_LEN 12 +#define ELERR_POS 46 +#define ELERR_LEN 2 #define NV_POS 50 #define NV_LEN 1 #define LL_POS 51 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 409152b21191..d208b172f4d7 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -443,8 +443,8 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); - pdata->stats.tx_packets++; - pdata->stats.tx_bytes += skb->len; + tx_ring->tx_packets++; + tx_ring->tx_bytes += skb->len; pdata->ring_ops->wr_cmd(tx_ring, count); return NETDEV_TX_OK; @@ -483,12 +483,12 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skb = buf_pool->rx_skb[skb_index]; /* checking for error */ - status = GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); + status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) || + GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); if (unlikely(status > 2)) { dev_kfree_skb_any(skb); xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev), status); - pdata->stats.rx_dropped++; ret = -EIO; goto out; } @@ -506,8 +506,8 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, xgene_enet_skip_csum(skb); } - pdata->stats.rx_packets++; - pdata->stats.rx_bytes += datalen; + rx_ring->rx_packets++; + rx_ring->rx_bytes += datalen; napi_gro_receive(&rx_ring->napi, skb); out: if (--rx_ring->nbufpool == 0) { @@ -630,7 +630,7 @@ static int xgene_enet_register_irq(struct net_device *ndev) ring = pdata->rx_ring[i]; irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY); ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq, - IRQF_SHARED, ring->irq_name, ring); + 0, ring->irq_name, ring); if (ret) { netdev_err(ndev, "Failed to request irq %s\n", ring->irq_name); @@ -641,7 +641,7 @@ static int xgene_enet_register_irq(struct net_device *ndev) ring = pdata->tx_ring[i]->cp_ring; irq_set_status_flags(ring->irq, IRQ_DISABLE_UNLAZY); ret = devm_request_irq(dev, ring->irq, xgene_enet_rx_irq, - IRQF_SHARED, ring->irq_name, ring); + 0, ring->irq_name, ring); if (ret) { netdev_err(ndev, "Failed to request irq %s\n", ring->irq_name); @@ -1127,12 +1127,31 @@ static struct rtnl_link_stats64 *xgene_enet_get_stats64( { struct xgene_enet_pdata *pdata = netdev_priv(ndev); struct rtnl_link_stats64 *stats = &pdata->stats; + struct xgene_enet_desc_ring *ring; + int i; - stats->rx_errors += stats->rx_length_errors + - stats->rx_crc_errors + - stats->rx_frame_errors + - stats->rx_fifo_errors; - memcpy(storage, &pdata->stats, sizeof(struct rtnl_link_stats64)); + memset(stats, 0, sizeof(struct rtnl_link_stats64)); + for (i = 0; i < pdata->txq_cnt; i++) { + ring = pdata->tx_ring[i]; + if (ring) { + stats->tx_packets += ring->tx_packets; + stats->tx_bytes += ring->tx_bytes; + } + } + + for (i = 0; i < pdata->rxq_cnt; i++) { + ring = pdata->rx_ring[i]; + if (ring) { + stats->rx_packets += ring->rx_packets; + stats->rx_bytes += ring->rx_bytes; + stats->rx_errors += ring->rx_length_errors + + ring->rx_crc_errors + + ring->rx_frame_errors + + ring->rx_fifo_errors; + stats->rx_dropped += ring->rx_dropped; + } + } + memcpy(storage, stats, sizeof(struct rtnl_link_stats64)); return storage; } @@ -1247,6 +1266,13 @@ static int xgene_enet_get_irqs(struct xgene_enet_pdata *pdata) for (i = 0; i < max_irqs; i++) { ret = platform_get_irq(pdev, i); if (ret <= 0) { + if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { + max_irqs = i; + pdata->rxq_cnt = max_irqs / 2; + pdata->txq_cnt = max_irqs / 2; + pdata->cq_cnt = max_irqs / 2; + break; + } dev_err(dev, "Unable to get ENET IRQ\n"); ret = ret ? : -ENXIO; return ret; @@ -1450,19 +1476,28 @@ static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata) pdata->port_ops = &xgene_xgport_ops; pdata->cle_ops = &xgene_cle3in_ops; pdata->rm = RM0; - pdata->rxq_cnt = XGENE_NUM_RX_RING; - pdata->txq_cnt = XGENE_NUM_TX_RING; - pdata->cq_cnt = XGENE_NUM_TXC_RING; + if (!pdata->rxq_cnt) { + pdata->rxq_cnt = XGENE_NUM_RX_RING; + pdata->txq_cnt = XGENE_NUM_TX_RING; + pdata->cq_cnt = XGENE_NUM_TXC_RING; + } break; } if (pdata->enet_id == XGENE_ENET1) { switch (pdata->port_id) { case 0: - pdata->cpu_bufnum = START_CPU_BUFNUM_0; - pdata->eth_bufnum = START_ETH_BUFNUM_0; - pdata->bp_bufnum = START_BP_BUFNUM_0; - pdata->ring_num = START_RING_NUM_0; + if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { + pdata->cpu_bufnum = X2_START_CPU_BUFNUM_0; + pdata->eth_bufnum = X2_START_ETH_BUFNUM_0; + pdata->bp_bufnum = X2_START_BP_BUFNUM_0; + pdata->ring_num = START_RING_NUM_0; + } else { + pdata->cpu_bufnum = START_CPU_BUFNUM_0; + pdata->eth_bufnum = START_ETH_BUFNUM_0; + pdata->bp_bufnum = START_BP_BUFNUM_0; + pdata->ring_num = START_RING_NUM_0; + } break; case 1: if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { @@ -1608,21 +1643,22 @@ static int xgene_enet_probe(struct platform_device *pdev) ret = xgene_enet_init_hw(pdata); if (ret) - goto err; + goto err_netdev; mac_ops = pdata->mac_ops; if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) { ret = xgene_enet_mdio_config(pdata); if (ret) - goto err; + goto err_netdev; } else { INIT_DELAYED_WORK(&pdata->link_work, mac_ops->link_state); } xgene_enet_napi_add(pdata); return 0; -err: +err_netdev: unregister_netdev(ndev); +err: free_netdev(ndev); return ret; } diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 175d18890c7a..092fbeccaa20 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -49,10 +49,10 @@ #define XGENE_ENET_MSS 1448 #define XGENE_MIN_ENET_FRAME_SIZE 60 -#define XGENE_MAX_ENET_IRQ 8 -#define XGENE_NUM_RX_RING 4 -#define XGENE_NUM_TX_RING 4 -#define XGENE_NUM_TXC_RING 4 +#define XGENE_MAX_ENET_IRQ 16 +#define XGENE_NUM_RX_RING 8 +#define XGENE_NUM_TX_RING 8 +#define XGENE_NUM_TXC_RING 8 #define START_CPU_BUFNUM_0 0 #define START_ETH_BUFNUM_0 2 @@ -121,6 +121,16 @@ struct xgene_enet_desc_ring { struct xgene_enet_raw_desc16 *raw_desc16; }; __le64 *exp_bufs; + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; + u64 rx_dropped; + u64 rx_errors; + u64 rx_length_errors; + u64 rx_crc_errors; + u64 rx_frame_errors; + u64 rx_fifo_errors; }; struct xgene_mac_ops { @@ -191,7 +201,7 @@ struct xgene_enet_pdata { const struct xgene_mac_ops *mac_ops; const struct xgene_port_ops *port_ops; struct xgene_ring_ops *ring_ops; - struct xgene_cle_ops *cle_ops; + const struct xgene_cle_ops *cle_ops; struct delayed_work link_work; u32 port_id; u8 cpu_bufnum; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h index 29a71b4dcc44..002df5a6756e 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.h @@ -33,7 +33,7 @@ #define LINK_STATUS BIT(2) #define LINK_UP BIT(15) #define MPA_IDLE_WITH_QMI_EMPTY BIT(12) -#define SG_RX_DV_GATE_REG_0_ADDR 0x0dfc +#define SG_RX_DV_GATE_REG_0_ADDR 0x05fc extern const struct xgene_mac_ops xgene_sgmac_ops; extern const struct xgene_port_ops xgene_sgport_ops; diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 55b118e876fd..9fe8b5e310d1 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -745,7 +745,7 @@ static netdev_features_t alx_fix_features(struct net_device *netdev, static void alx_netif_stop(struct alx_priv *alx) { - alx->dev->trans_start = jiffies; + netif_trans_update(alx->dev); if (netif_carrier_ok(alx->dev)) { netif_carrier_off(alx->dev); netif_tx_disable(alx->dev); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 30b0c2895a56..543bf38105c9 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -1117,7 +1117,7 @@ static void bcm_sysport_tx_timeout(struct net_device *dev) { netdev_warn(dev, "transmit timeout!\n"); - dev->trans_start = jiffies; + netif_trans_update(dev); dev->stats.tx_errors++; netif_tx_wake_all_queues(dev); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 38db2e4d7d54..ee5f431ab32a 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1515,7 +1515,7 @@ static int bgmac_mii_register(struct bgmac *bgmac) phy_dev = phy_connect(bgmac->net_dev, bus_id, &bgmac_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phy_dev)) { - bgmac_err(bgmac, "PHY connecton failed\n"); + bgmac_err(bgmac, "PHY connection failed\n"); err = PTR_ERR(phy_dev); goto err_unregister_bus; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index d465bd721146..0a5b770cefaa 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -13259,12 +13259,11 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX; if (!chip_is_e1x) { dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT; + NETIF_F_GSO_IPXIP4; dev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | - NETIF_F_GSO_IPIP | - NETIF_F_GSO_SIT | + NETIF_F_GSO_IPXIP4 | NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4645c44e7c15..72a2efff8e49 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -78,6 +78,7 @@ enum board_idx { BCM57402, BCM57404, BCM57406, + BCM57314, BCM57304_VF, BCM57404_VF, }; @@ -92,6 +93,7 @@ static const struct { { "Broadcom BCM57402 NetXtreme-E Dual-port 10Gb Ethernet" }, { "Broadcom BCM57404 NetXtreme-E Dual-port 10Gb/25Gb Ethernet" }, { "Broadcom BCM57406 NetXtreme-E Dual-port 10GBase-T Ethernet" }, + { "Broadcom BCM57314 NetXtreme-C Dual-port 10Gb/25Gb/40Gb/50Gb Ethernet" }, { "Broadcom BCM57304 NetXtreme-C Ethernet Virtual Function" }, { "Broadcom BCM57404 NetXtreme-E Ethernet Virtual Function" }, }; @@ -103,6 +105,7 @@ static const struct pci_device_id bnxt_pci_tbl[] = { { PCI_VDEVICE(BROADCOM, 0x16d0), .driver_data = BCM57402 }, { PCI_VDEVICE(BROADCOM, 0x16d1), .driver_data = BCM57404 }, { PCI_VDEVICE(BROADCOM, 0x16d2), .driver_data = BCM57406 }, + { PCI_VDEVICE(BROADCOM, 0x16df), .driver_data = BCM57314 }, #ifdef CONFIG_BNXT_SRIOV { PCI_VDEVICE(BROADCOM, 0x16cb), .driver_data = BCM57304_VF }, { PCI_VDEVICE(BROADCOM, 0x16d3), .driver_data = BCM57404_VF }, @@ -588,12 +591,30 @@ static inline int bnxt_alloc_rx_page(struct bnxt *bp, struct page *page; dma_addr_t mapping; u16 sw_prod = rxr->rx_sw_agg_prod; + unsigned int offset = 0; - page = alloc_page(gfp); - if (!page) - return -ENOMEM; + if (PAGE_SIZE > BNXT_RX_PAGE_SIZE) { + page = rxr->rx_page; + if (!page) { + page = alloc_page(gfp); + if (!page) + return -ENOMEM; + rxr->rx_page = page; + rxr->rx_page_offset = 0; + } + offset = rxr->rx_page_offset; + rxr->rx_page_offset += BNXT_RX_PAGE_SIZE; + if (rxr->rx_page_offset == PAGE_SIZE) + rxr->rx_page = NULL; + else + get_page(page); + } else { + page = alloc_page(gfp); + if (!page) + return -ENOMEM; + } - mapping = dma_map_page(&pdev->dev, page, 0, PAGE_SIZE, + mapping = dma_map_page(&pdev->dev, page, offset, BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); if (dma_mapping_error(&pdev->dev, mapping)) { __free_page(page); @@ -608,6 +629,7 @@ static inline int bnxt_alloc_rx_page(struct bnxt *bp, rxr->rx_sw_agg_prod = NEXT_RX_AGG(sw_prod); rx_agg_buf->page = page; + rx_agg_buf->offset = offset; rx_agg_buf->mapping = mapping; rxbd->rx_bd_haddr = cpu_to_le64(mapping); rxbd->rx_bd_opaque = sw_prod; @@ -649,6 +671,7 @@ static void bnxt_reuse_rx_agg_bufs(struct bnxt_napi *bnapi, u16 cp_cons, page = cons_rx_buf->page; cons_rx_buf->page = NULL; prod_rx_buf->page = page; + prod_rx_buf->offset = cons_rx_buf->offset; prod_rx_buf->mapping = cons_rx_buf->mapping; @@ -716,7 +739,8 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi, RX_AGG_CMP_LEN) >> RX_AGG_CMP_LEN_SHIFT; cons_rx_buf = &rxr->rx_agg_ring[cons]; - skb_fill_page_desc(skb, i, cons_rx_buf->page, 0, frag_len); + skb_fill_page_desc(skb, i, cons_rx_buf->page, + cons_rx_buf->offset, frag_len); __clear_bit(cons, rxr->rx_agg_bmap); /* It is possible for bnxt_alloc_rx_page() to allocate @@ -747,7 +771,7 @@ static struct sk_buff *bnxt_rx_pages(struct bnxt *bp, struct bnxt_napi *bnapi, return NULL; } - dma_unmap_page(&pdev->dev, mapping, PAGE_SIZE, + dma_unmap_page(&pdev->dev, mapping, BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); skb->data_len += frag_len; @@ -799,6 +823,46 @@ static inline struct sk_buff *bnxt_copy_skb(struct bnxt_napi *bnapi, u8 *data, return skb; } +static int bnxt_discard_rx(struct bnxt *bp, struct bnxt_napi *bnapi, + u32 *raw_cons, void *cmp) +{ + struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; + struct rx_cmp *rxcmp = cmp; + u32 tmp_raw_cons = *raw_cons; + u8 cmp_type, agg_bufs = 0; + + cmp_type = RX_CMP_TYPE(rxcmp); + + if (cmp_type == CMP_TYPE_RX_L2_CMP) { + agg_bufs = (le32_to_cpu(rxcmp->rx_cmp_misc_v1) & + RX_CMP_AGG_BUFS) >> + RX_CMP_AGG_BUFS_SHIFT; + } else if (cmp_type == CMP_TYPE_RX_L2_TPA_END_CMP) { + struct rx_tpa_end_cmp *tpa_end = cmp; + + agg_bufs = (le32_to_cpu(tpa_end->rx_tpa_end_cmp_misc_v1) & + RX_TPA_END_CMP_AGG_BUFS) >> + RX_TPA_END_CMP_AGG_BUFS_SHIFT; + } + + if (agg_bufs) { + if (!bnxt_agg_bufs_valid(bp, cpr, agg_bufs, &tmp_raw_cons)) + return -EBUSY; + } + *raw_cons = tmp_raw_cons; + return 0; +} + +static void bnxt_sched_reset(struct bnxt *bp, struct bnxt_rx_ring_info *rxr) +{ + if (!rxr->bnapi->in_reset) { + rxr->bnapi->in_reset = true; + set_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event); + schedule_work(&bp->sp_task); + } + rxr->rx_next_cons = 0xffff; +} + static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, struct rx_tpa_start_cmp *tpa_start, struct rx_tpa_start_cmp_ext *tpa_start1) @@ -816,6 +880,11 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, prod_rx_buf = &rxr->rx_buf_ring[prod]; tpa_info = &rxr->rx_tpa[agg_id]; + if (unlikely(cons != rxr->rx_next_cons)) { + bnxt_sched_reset(bp, rxr); + return; + } + prod_rx_buf->data = tpa_info->data; mapping = tpa_info->mapping; @@ -853,6 +922,7 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, rxr->rx_prod = NEXT_RX(prod); cons = NEXT_RX(cons); + rxr->rx_next_cons = NEXT_RX(cons); cons_rx_buf = &rxr->rx_buf_ring[cons]; bnxt_reuse_rx_data(rxr, cons, cons_rx_buf->data); @@ -966,6 +1036,14 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, dma_addr_t mapping; struct sk_buff *skb; + if (unlikely(bnapi->in_reset)) { + int rc = bnxt_discard_rx(bp, bnapi, raw_cons, tpa_end); + + if (rc < 0) + return ERR_PTR(-EBUSY); + return NULL; + } + tpa_info = &rxr->rx_tpa[agg_id]; data = tpa_info->data; prefetch(data); @@ -1132,6 +1210,12 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, cons = rxcmp->rx_cmp_opaque; rx_buf = &rxr->rx_buf_ring[cons]; data = rx_buf->data; + if (unlikely(cons != rxr->rx_next_cons)) { + int rc1 = bnxt_discard_rx(bp, bnapi, raw_cons, rxcmp); + + bnxt_sched_reset(bp, rxr); + return rc1; + } prefetch(data); agg_bufs = (le32_to_cpu(rxcmp->rx_cmp_misc_v1) & RX_CMP_AGG_BUFS) >> @@ -1231,6 +1315,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, next_rx: rxr->rx_prod = NEXT_RX(prod); + rxr->rx_next_cons = NEXT_RX(cons); next_rx_no_prod: *raw_cons = tmp_raw_cons; @@ -1242,15 +1327,6 @@ next_rx_no_prod: ((data) & \ HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_PORT_ID_MASK) -#define BNXT_EVENT_POLICY_MASK \ - HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_MASK - -#define BNXT_EVENT_POLICY_SFT \ - HWRM_ASYNC_EVENT_CMPL_PORT_CONN_NOT_ALLOWED_EVENT_DATA1_ENFORCEMENT_POLICY_SFT - -#define BNXT_GET_EVENT_POLICY(data) \ - (((data) & BNXT_EVENT_POLICY_MASK) >> BNXT_EVENT_POLICY_SFT) - static int bnxt_async_event_process(struct bnxt *bp, struct hwrm_async_event_cmpl *cmpl) { @@ -1289,9 +1365,6 @@ static int bnxt_async_event_process(struct bnxt *bp, if (bp->pf.port_id != port_id) break; - bp->link_info.last_port_module_event = - BNXT_GET_EVENT_POLICY(data1); - set_bit(BNXT_HWRM_PORT_MODULE_SP_EVENT, &bp->sp_event); break; } @@ -1418,6 +1491,10 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) if (!TX_CMP_VALID(txcmp, raw_cons)) break; + /* The valid test of the entry must be done first before + * reading any further. + */ + dma_rmb(); if (TX_CMP_TYPE(txcmp) == CMP_TYPE_TX_L2_CMP) { tx_pkts++; /* return full budget so NAPI will complete. */ @@ -1635,13 +1712,17 @@ static void bnxt_free_rx_skbs(struct bnxt *bp) dma_unmap_page(&pdev->dev, dma_unmap_addr(rx_agg_buf, mapping), - PAGE_SIZE, PCI_DMA_FROMDEVICE); + BNXT_RX_PAGE_SIZE, PCI_DMA_FROMDEVICE); rx_agg_buf->page = NULL; __clear_bit(j, rxr->rx_agg_bmap); __free_page(page); } + if (rxr->rx_page) { + __free_page(rxr->rx_page); + rxr->rx_page = NULL; + } } } @@ -2024,7 +2105,7 @@ static int bnxt_init_one_rx_ring(struct bnxt *bp, int ring_nr) if (!(bp->flags & BNXT_FLAG_AGG_RINGS)) return 0; - type = ((u32)PAGE_SIZE << RX_BD_LEN_SHIFT) | + type = ((u32)BNXT_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP; bnxt_init_rxbd_pages(ring, type); @@ -2215,7 +2296,7 @@ void bnxt_set_ring_params(struct bnxt *bp) bp->rx_agg_nr_pages = 0; if (bp->flags & BNXT_FLAG_TPA) - agg_factor = 4; + agg_factor = min_t(u32, 4, 65536 / BNXT_RX_PAGE_SIZE); bp->flags &= ~BNXT_FLAG_JUMBO; if (rx_space > PAGE_SIZE) { @@ -2508,6 +2589,7 @@ static void bnxt_clear_ring_indices(struct bnxt *bp) rxr->rx_prod = 0; rxr->rx_agg_prod = 0; rxr->rx_sw_agg_prod = 0; + rxr->rx_next_cons = 0; } } } @@ -2689,7 +2771,7 @@ void bnxt_hwrm_cmd_hdr_init(struct bnxt *bp, void *request, u16 req_type, static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, int timeout, bool silent) { - int i, intr_process, rc; + int i, intr_process, rc, tmo_count; struct input *req = msg; u32 *data = msg; __le32 *resp_len, *valid; @@ -2718,11 +2800,12 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, timeout = DFLT_HWRM_CMD_TIMEOUT; i = 0; + tmo_count = timeout * 40; if (intr_process) { /* Wait until hwrm response cmpl interrupt is processed */ while (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID && - i++ < timeout) { - usleep_range(600, 800); + i++ < tmo_count) { + usleep_range(25, 40); } if (bp->hwrm_intr_seq_id != HWRM_SEQ_ID_INVALID) { @@ -2733,30 +2816,30 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, } else { /* Check if response len is updated */ resp_len = bp->hwrm_cmd_resp_addr + HWRM_RESP_LEN_OFFSET; - for (i = 0; i < timeout; i++) { + for (i = 0; i < tmo_count; i++) { len = (le32_to_cpu(*resp_len) & HWRM_RESP_LEN_MASK) >> HWRM_RESP_LEN_SFT; if (len) break; - usleep_range(600, 800); + usleep_range(25, 40); } - if (i >= timeout) { + if (i >= tmo_count) { netdev_err(bp->dev, "Error (timeout: %d) msg {0x%x 0x%x} len:%d\n", timeout, le16_to_cpu(req->req_type), - le16_to_cpu(req->seq_id), *resp_len); + le16_to_cpu(req->seq_id), len); return -1; } /* Last word of resp contains valid bit */ valid = bp->hwrm_cmd_resp_addr + len - 4; - for (i = 0; i < timeout; i++) { + for (i = 0; i < 5; i++) { if (le32_to_cpu(*valid) & HWRM_RESP_VALID_MASK) break; - usleep_range(600, 800); + udelay(1); } - if (i >= timeout) { + if (i >= 5) { netdev_err(bp->dev, "Error (timeout: %d) msg {0x%x 0x%x} len:%d v:%d\n", timeout, le16_to_cpu(req->req_type), le16_to_cpu(req->seq_id), len, *valid); @@ -3076,12 +3159,12 @@ static int bnxt_hwrm_vnic_set_tpa(struct bnxt *bp, u16 vnic_id, u32 tpa_flags) /* Number of segs are log2 units, and first packet is not * included as part of this units. */ - if (mss <= PAGE_SIZE) { - n = PAGE_SIZE / mss; + if (mss <= BNXT_RX_PAGE_SIZE) { + n = BNXT_RX_PAGE_SIZE / mss; nsegs = (MAX_SKB_FRAGS - 1) * n; } else { - n = mss / PAGE_SIZE; - if (mss & (PAGE_SIZE - 1)) + n = mss / BNXT_RX_PAGE_SIZE; + if (mss & (BNXT_RX_PAGE_SIZE - 1)) n++; nsegs = (MAX_SKB_FRAGS - n) / n; } @@ -4071,9 +4154,11 @@ static int bnxt_alloc_rfs_vnics(struct bnxt *bp) } static int bnxt_cfg_rx_mode(struct bnxt *); +static bool bnxt_mc_list_updated(struct bnxt *, u32 *); static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) { + struct bnxt_vnic_info *vnic = &bp->vnic_info[0]; int rc = 0; if (irq_re_init) { @@ -4129,13 +4214,22 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) netdev_err(bp->dev, "HWRM vnic filter failure rc: %x\n", rc); goto err_out; } - bp->vnic_info[0].uc_filter_count = 1; + vnic->uc_filter_count = 1; - bp->vnic_info[0].rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; + vnic->rx_mask = CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; if ((bp->dev->flags & IFF_PROMISC) && BNXT_PF(bp)) - bp->vnic_info[0].rx_mask |= - CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; + + if (bp->dev->flags & IFF_ALLMULTI) { + vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; + vnic->mc_list_count = 0; + } else { + u32 mask = 0; + + bnxt_mc_list_updated(bp, &mask); + vnic->rx_mask |= mask; + } rc = bnxt_cfg_rx_mode(bp); if (rc) @@ -4367,7 +4461,7 @@ static int bnxt_setup_int_mode(struct bnxt *bp) if (bp->flags & BNXT_FLAG_MSIX_CAP) rc = bnxt_setup_msix(bp); - if (!(bp->flags & BNXT_FLAG_USING_MSIX)) { + if (!(bp->flags & BNXT_FLAG_USING_MSIX) && BNXT_PF(bp)) { /* fallback to INTA */ rc = bnxt_setup_inta(bp); } @@ -4480,6 +4574,7 @@ static void bnxt_enable_napi(struct bnxt *bp) int i; for (i = 0; i < bp->cp_nr_rings; i++) { + bp->bnapi[i]->in_reset = false; bnxt_enable_poll(bp->bnapi[i]); napi_enable(&bp->bnapi[i]->napi); } @@ -4631,6 +4726,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) link_info->transceiver = resp->xcvr_pkg_type; link_info->phy_addr = resp->eee_config_phy_addr & PORT_PHY_QCFG_RESP_PHY_ADDR_MASK; + link_info->module_status = resp->module_status; if (bp->flags & BNXT_FLAG_EEE_CAP) { struct ethtool_eee *eee = &bp->eee; @@ -4683,6 +4779,33 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) return 0; } +static void bnxt_get_port_module_status(struct bnxt *bp) +{ + struct bnxt_link_info *link_info = &bp->link_info; + struct hwrm_port_phy_qcfg_output *resp = &link_info->phy_qcfg_resp; + u8 module_status; + + if (bnxt_update_link(bp, true)) + return; + + module_status = link_info->module_status; + switch (module_status) { + case PORT_PHY_QCFG_RESP_MODULE_STATUS_DISABLETX: + case PORT_PHY_QCFG_RESP_MODULE_STATUS_PWRDOWN: + case PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG: + netdev_warn(bp->dev, "Unqualified SFP+ module detected on port %d\n", + bp->pf.port_id); + if (bp->hwrm_spec_code >= 0x10201) { + netdev_warn(bp->dev, "Module part number %s\n", + resp->phy_vendor_partnumber); + } + if (module_status == PORT_PHY_QCFG_RESP_MODULE_STATUS_DISABLETX) + netdev_warn(bp->dev, "TX is disabled\n"); + if (module_status == PORT_PHY_QCFG_RESP_MODULE_STATUS_PWRDOWN) + netdev_warn(bp->dev, "SFP+ module is shutdown\n"); + } +} + static void bnxt_hwrm_set_pause_common(struct bnxt *bp, struct hwrm_port_phy_cfg_input *req) { @@ -4975,7 +5098,8 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) /* Enable TX queues */ bnxt_tx_enable(bp); mod_timer(&bp->timer, jiffies + bp->current_interval); - bnxt_update_link(bp, true); + /* Poll link status and check for SFP+ module status */ + bnxt_get_port_module_status(bp); return 0; @@ -5510,28 +5634,6 @@ bnxt_restart_timer: mod_timer(&bp->timer, jiffies + bp->current_interval); } -static void bnxt_port_module_event(struct bnxt *bp) -{ - struct bnxt_link_info *link_info = &bp->link_info; - struct hwrm_port_phy_qcfg_output *resp = &link_info->phy_qcfg_resp; - - if (bnxt_update_link(bp, true)) - return; - - if (link_info->last_port_module_event != 0) { - netdev_warn(bp->dev, "Unqualified SFP+ module detected on port %d\n", - bp->pf.port_id); - if (bp->hwrm_spec_code >= 0x10201) { - netdev_warn(bp->dev, "Module part number %s\n", - resp->phy_vendor_partnumber); - } - } - if (link_info->last_port_module_event == 1) - netdev_warn(bp->dev, "TX is disabled\n"); - if (link_info->last_port_module_event == 3) - netdev_warn(bp->dev, "Shutdown SFP+ module\n"); -} - static void bnxt_cfg_ntp_filters(struct bnxt *); static void bnxt_sp_task(struct work_struct *work) @@ -5580,7 +5682,7 @@ static void bnxt_sp_task(struct work_struct *work) } if (test_and_clear_bit(BNXT_HWRM_PORT_MODULE_SP_EVENT, &bp->sp_event)) - bnxt_port_module_event(bp); + bnxt_get_port_module_status(bp); if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) bnxt_hwrm_port_qstats(bp); @@ -6157,6 +6259,22 @@ static int bnxt_set_dflt_rings(struct bnxt *bp) return rc; } +static void bnxt_parse_log_pcie_link(struct bnxt *bp) +{ + enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN; + enum pci_bus_speed speed = PCI_SPEED_UNKNOWN; + + if (pcie_get_minimum_link(bp->pdev, &speed, &width) || + speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) + netdev_info(bp->dev, "Failed to determine PCIe Link Info\n"); + else + netdev_info(bp->dev, "PCIe: Speed %s Width x%d\n", + speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : + speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : + speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : + "Unknown", width); +} + static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int version_printed; @@ -6193,15 +6311,19 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT | - NETIF_F_RXHASH | + NETIF_F_GSO_IPXIP4 | + NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM | + NETIF_F_GSO_PARTIAL | NETIF_F_RXHASH | NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO; dev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | - NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT; + NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM | + NETIF_F_GSO_IPXIP4 | NETIF_F_GSO_PARTIAL; + dev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_GRE_CSUM; dev->vlan_features = dev->hw_features | NETIF_F_HIGHDMA; dev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX; @@ -6272,6 +6394,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) board_info[ent->driver_data].name, (long)pci_resource_start(pdev, 0), dev->dev_addr); + bnxt_parse_log_pcie_link(bp); + return 0; init_err: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 26dac2f3c63c..2824d65b2e35 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -407,6 +407,15 @@ struct rx_tpa_end_cmp_ext { #define BNXT_PAGE_SIZE (1 << BNXT_PAGE_SHIFT) +/* The RXBD length is 16-bit so we can only support page sizes < 64K */ +#if (PAGE_SHIFT > 15) +#define BNXT_RX_PAGE_SHIFT 15 +#else +#define BNXT_RX_PAGE_SHIFT PAGE_SHIFT +#endif + +#define BNXT_RX_PAGE_SIZE (1 << BNXT_RX_PAGE_SHIFT) + #define BNXT_MIN_PKT_SIZE 45 #define BNXT_NUM_TESTS(bp) 0 @@ -416,10 +425,17 @@ struct rx_tpa_end_cmp_ext { #define MAX_TPA 64 +#if (BNXT_PAGE_SHIFT == 16) +#define MAX_RX_PAGES 1 +#define MAX_RX_AGG_PAGES 4 +#define MAX_TX_PAGES 1 +#define MAX_CP_PAGES 8 +#else #define MAX_RX_PAGES 8 #define MAX_RX_AGG_PAGES 32 #define MAX_TX_PAGES 8 #define MAX_CP_PAGES 64 +#endif #define RX_DESC_CNT (BNXT_PAGE_SIZE / sizeof(struct rx_bd)) #define TX_DESC_CNT (BNXT_PAGE_SIZE / sizeof(struct tx_bd)) @@ -506,6 +522,7 @@ struct bnxt_sw_rx_bd { struct bnxt_sw_rx_agg_bd { struct page *page; + unsigned int offset; dma_addr_t mapping; }; @@ -574,6 +591,7 @@ struct bnxt_rx_ring_info { u16 rx_prod; u16 rx_agg_prod; u16 rx_sw_agg_prod; + u16 rx_next_cons; void __iomem *rx_doorbell; void __iomem *rx_agg_doorbell; @@ -586,6 +604,9 @@ struct bnxt_rx_ring_info { unsigned long *rx_agg_bmap; u16 rx_agg_bmap_size; + struct page *rx_page; + unsigned int rx_page_offset; + dma_addr_t rx_desc_mapping[MAX_RX_PAGES]; dma_addr_t rx_agg_desc_mapping[MAX_RX_AGG_PAGES]; @@ -623,6 +644,7 @@ struct bnxt_napi { #ifdef CONFIG_NET_RX_BUSY_POLL atomic_t poll_state; #endif + bool in_reset; }; #ifdef CONFIG_NET_RX_BUSY_POLL @@ -816,6 +838,7 @@ struct bnxt_link_info { u16 lp_auto_link_speeds; u16 force_link_speed; u32 preemphasis; + u8 module_status; /* copy of requested setting from ethtool cmd */ u8 autoneg; @@ -827,7 +850,6 @@ struct bnxt_link_info { u32 advertising; bool force_link_chng; - u8 last_port_module_event; /* a copy of phy_qcfg output used to report link * info to VF */ @@ -1108,6 +1130,16 @@ static inline void bnxt_disable_poll(struct bnxt_napi *bnapi) #endif +#define I2C_DEV_ADDR_A0 0xa0 +#define I2C_DEV_ADDR_A2 0xa2 +#define SFP_EEPROM_SFF_8472_COMP_ADDR 0x5e +#define SFP_EEPROM_SFF_8472_COMP_SIZE 1 +#define SFF_MODULE_ID_SFP 0x3 +#define SFF_MODULE_ID_QSFP 0xc +#define SFF_MODULE_ID_QSFP_PLUS 0xd +#define SFF_MODULE_ID_QSFP28 0x11 +#define BNXT_MAX_PHY_I2C_RESP_SIZE 64 + void bnxt_set_ring_params(struct bnxt *); void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16); int _hwrm_send_message(struct bnxt *, void *, u32, int); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index d6e41f237f2c..a38cb047b540 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -327,7 +327,11 @@ static void bnxt_get_channels(struct net_device *dev, bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, true); channel->max_combined = max_rx_rings; - bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false); + if (bnxt_get_max_rings(bp, &max_rx_rings, &max_tx_rings, false)) { + max_rx_rings = 0; + max_tx_rings = 0; + } + tcs = netdev_get_num_tc(dev); if (tcs > 1) max_tx_rings /= tcs; @@ -1494,6 +1498,125 @@ static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata) return 0; } +static int bnxt_read_sfp_module_eeprom_info(struct bnxt *bp, u16 i2c_addr, + u16 page_number, u16 start_addr, + u16 data_length, u8 *buf) +{ + struct hwrm_port_phy_i2c_read_input req = {0}; + struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr; + int rc, byte_offset = 0; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1); + req.i2c_slave_addr = i2c_addr; + req.page_number = cpu_to_le16(page_number); + req.port_id = cpu_to_le16(bp->pf.port_id); + do { + u16 xfer_size; + + xfer_size = min_t(u16, data_length, BNXT_MAX_PHY_I2C_RESP_SIZE); + data_length -= xfer_size; + req.page_offset = cpu_to_le16(start_addr + byte_offset); + req.data_length = xfer_size; + req.enables = cpu_to_le32(start_addr + byte_offset ? + PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET : 0); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), + HWRM_CMD_TIMEOUT); + if (!rc) + memcpy(buf + byte_offset, output->data, xfer_size); + mutex_unlock(&bp->hwrm_cmd_lock); + byte_offset += xfer_size; + } while (!rc && data_length > 0); + + return rc; +} + +static int bnxt_get_module_info(struct net_device *dev, + struct ethtool_modinfo *modinfo) +{ + struct bnxt *bp = netdev_priv(dev); + struct hwrm_port_phy_i2c_read_input req = {0}; + struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr; + int rc; + + /* No point in going further if phy status indicates + * module is not inserted or if it is powered down or + * if it is of type 10GBase-T + */ + if (bp->link_info.module_status > + PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG) + return -EOPNOTSUPP; + + /* This feature is not supported in older firmware versions */ + if (bp->hwrm_spec_code < 0x10202) + return -EOPNOTSUPP; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1); + req.i2c_slave_addr = I2C_DEV_ADDR_A0; + req.page_number = 0; + req.page_offset = cpu_to_le16(SFP_EEPROM_SFF_8472_COMP_ADDR); + req.data_length = SFP_EEPROM_SFF_8472_COMP_SIZE; + req.port_id = cpu_to_le16(bp->pf.port_id); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + u32 module_id = le32_to_cpu(output->data[0]); + + switch (module_id) { + case SFF_MODULE_ID_SFP: + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + break; + case SFF_MODULE_ID_QSFP: + case SFF_MODULE_ID_QSFP_PLUS: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + case SFF_MODULE_ID_QSFP28: + modinfo->type = ETH_MODULE_SFF_8636; + modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; + break; + default: + rc = -EOPNOTSUPP; + break; + } + } + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +static int bnxt_get_module_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, + u8 *data) +{ + struct bnxt *bp = netdev_priv(dev); + u16 start = eeprom->offset, length = eeprom->len; + int rc; + + memset(data, 0, eeprom->len); + + /* Read A0 portion of the EEPROM */ + if (start < ETH_MODULE_SFF_8436_LEN) { + if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN) + length = ETH_MODULE_SFF_8436_LEN - start; + rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, + start, length, data); + if (rc) + return rc; + start += length; + data += length; + length = eeprom->len - length; + } + + /* Read A2 portion of the EEPROM */ + if (length) { + start -= ETH_MODULE_SFF_8436_LEN; + bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 1, start, + length, data); + } + return rc; +} + const struct ethtool_ops bnxt_ethtool_ops = { .get_settings = bnxt_get_settings, .set_settings = bnxt_set_settings, @@ -1524,4 +1647,6 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_link = bnxt_get_link, .get_eee = bnxt_get_eee, .set_eee = bnxt_set_eee, + .get_module_info = bnxt_get_module_info, + .get_module_eeprom = bnxt_get_module_eeprom, }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 80f95560086d..05e3c49a7677 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -2093,6 +2093,40 @@ struct hwrm_port_phy_qcaps_output { #define PORT_PHY_QCAPS_RESP_VALID_SFT 24 }; +/* hwrm_port_phy_i2c_read */ +/* Input (40 bytes) */ +struct hwrm_port_phy_i2c_read_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + __le32 enables; + #define PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET 0x1UL + __le16 port_id; + u8 i2c_slave_addr; + u8 unused_0; + __le16 page_number; + __le16 page_offset; + u8 data_length; + u8 unused_1[7]; +}; + +/* Output (80 bytes) */ +struct hwrm_port_phy_i2c_read_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 data[16]; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* Input (24 bytes) */ struct hwrm_queue_qportcfg_input { __le16 req_type; diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index b69dc58faeab..b1d2ac818710 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -5350,7 +5350,10 @@ static int cnic_start_hw(struct cnic_dev *dev) return 0; err1: - cp->free_resc(dev); + if (ethdev->drv_state & CNIC_DRV_STATE_HANDLES_IRQ) + cp->stop_hw(dev); + else + cp->free_resc(dev); pci_dev_put(dev->pcidev); return err; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index fbff226369ac..541456398dfb 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -3059,7 +3059,7 @@ static void bcmgenet_timeout(struct net_device *dev) bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); - dev->trans_start = jiffies; + netif_trans_update(dev); dev->stats.tx_errors++; diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index eacc559679bf..f1b81187a201 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2462,7 +2462,7 @@ static void sbmac_tx_timeout (struct net_device *dev) spin_lock_irqsave(&sc->sbm_lock, flags); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; spin_unlock_irqrestore(&sc->sbm_lock, flags); diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 3010080cfeee..ff300f7cf529 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -7383,7 +7383,7 @@ static void tg3_napi_fini(struct tg3 *tp) static inline void tg3_netif_stop(struct tg3 *tp) { - tp->dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(tp->dev); /* prevent tx timeout */ tg3_napi_disable(tp); netif_carrier_off(tp->dev); netif_tx_disable(tp->dev); diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index eec3200ade4a..cb07d95e3dd9 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -440,7 +440,7 @@ static int macb_mii_init(struct macb *bp) snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", bp->pdev->name, bp->pdev->id); bp->mii_bus->priv = bp; - bp->mii_bus->parent = &bp->dev->dev; + bp->mii_bus->parent = &bp->pdev->dev; pdata = dev_get_platdata(&bp->pdev->dev); dev_set_drvdata(&bp->dev->dev, bp->mii_bus); @@ -458,7 +458,8 @@ static int macb_mii_init(struct macb *bp) struct phy_device *phydev; phydev = mdiobus_scan(bp->mii_bus, i); - if (IS_ERR(phydev)) { + if (IS_ERR(phydev) && + PTR_ERR(phydev) != -ENODEV) { err = PTR_ERR(phydev); break; } @@ -3005,29 +3006,36 @@ static int macb_probe(struct platform_device *pdev) if (err) goto err_out_free_netdev; + err = macb_mii_init(bp); + if (err) + goto err_out_free_netdev; + + phydev = bp->phy_dev; + + netif_carrier_off(dev); + err = register_netdev(dev); if (err) { dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); - goto err_out_unregister_netdev; + goto err_out_unregister_mdio; } - err = macb_mii_init(bp); - if (err) - goto err_out_unregister_netdev; - - netif_carrier_off(dev); + phy_attached_info(phydev); netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n", macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID), dev->base_addr, dev->irq, dev->dev_addr); - phydev = bp->phy_dev; - phy_attached_info(phydev); - return 0; -err_out_unregister_netdev: - unregister_netdev(dev); +err_out_unregister_mdio: + phy_disconnect(bp->phy_dev); + mdiobus_unregister(bp->mii_bus); + mdiobus_free(bp->mii_bus); + + /* Shutdown the PHY if there is a GPIO reset */ + if (bp->reset_gpio) + gpiod_set_value(bp->reset_gpio, 0); err_out_free_netdev: free_netdev(dev); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 34d269cd5579..8de79ae63231 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2899,7 +2899,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev) if (status == IQ_SEND_STOP) stop_q(lio->netdev, q_idx); - netdev->trans_start = jiffies; + netif_trans_update(netdev); stats->tx_done++; stats->tx_tot_bytes += skb->len; @@ -2928,7 +2928,7 @@ static void liquidio_tx_timeout(struct net_device *netdev) netif_info(lio, tx_err, lio->netdev, "Transmit timeout tx_dropped:%ld, waking up queues now!!\n", netdev->stats.tx_dropped); - netdev->trans_start = jiffies; + netif_trans_update(netdev); txqs_wake(netdev); } diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c index c177c7cec13b..388cd799d9ed 100644 --- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c @@ -1320,7 +1320,7 @@ static int octeon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) /* Ring the bell. */ cvmx_write_csr(p->mix + MIX_ORING2, 1); - netdev->trans_start = jiffies; + netif_trans_update(netdev); rv = NETDEV_TX_OK; out: octeon_mgmt_update_tx_stats(netdev); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index bfee298fc02a..a19e73f11d73 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1442,7 +1442,7 @@ static void nicvf_reset_task(struct work_struct *work) nicvf_stop(nic->netdev); nicvf_open(nic->netdev); - nic->netdev->trans_start = jiffies; + netif_trans_update(nic->netdev); } static int nicvf_config_loopback(struct nicvf *nic, diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index fa05e347262f..06b819db51b1 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -533,6 +533,7 @@ static void nicvf_rcv_queue_config(struct nicvf *nic, struct queue_set *qs, nicvf_config_vlan_stripping(nic, nic->netdev->features); /* Enable Receive queue */ + memset(&rq_cfg, 0, sizeof(struct rq_cfg)); rq_cfg.ena = 1; rq_cfg.tcp_ena = 0; nicvf_queue_reg_write(nic, NIC_QSET_RQ_0_7_CFG, qidx, *(u64 *)&rq_cfg); @@ -565,6 +566,7 @@ void nicvf_cmp_queue_config(struct nicvf *nic, struct queue_set *qs, qidx, (u64)(cq->dmem.phys_base)); /* Enable Completion queue */ + memset(&cq_cfg, 0, sizeof(struct cq_cfg)); cq_cfg.ena = 1; cq_cfg.reset = 0; cq_cfg.caching = 0; @@ -613,6 +615,7 @@ static void nicvf_snd_queue_config(struct nicvf *nic, struct queue_set *qs, qidx, (u64)(sq->dmem.phys_base)); /* Enable send queue & set queue size */ + memset(&sq_cfg, 0, sizeof(struct sq_cfg)); sq_cfg.ena = 1; sq_cfg.reset = 0; sq_cfg.ldwb = 0; @@ -649,6 +652,7 @@ static void nicvf_rbdr_config(struct nicvf *nic, struct queue_set *qs, /* Enable RBDR & set queue size */ /* Buffer size should be in multiples of 128 bytes */ + memset(&rbdr_cfg, 0, sizeof(struct rbdr_cfg)); rbdr_cfg.ena = 1; rbdr_cfg.reset = 0; rbdr_cfg.ldwb = 0; diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index d20539a6d162..3ed21988626b 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -274,12 +274,14 @@ static void bgx_sgmii_change_link_state(struct lmac *lmac) static void bgx_lmac_handler(struct net_device *netdev) { struct lmac *lmac = container_of(netdev, struct lmac, netdev); - struct phy_device *phydev = lmac->phydev; + struct phy_device *phydev; int link_changed = 0; if (!lmac) return; + phydev = lmac->phydev; + if (!phydev->link && lmac->last_link) link_changed = -1; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 60908eab3b3a..43da891fab97 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -576,7 +576,7 @@ static void setup_rss(struct adapter *adap) unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; u8 cpus[SGE_QSETS + 1]; - u16 rspq_map[RSS_TABLE_SIZE]; + u16 rspq_map[RSS_TABLE_SIZE + 1]; for (i = 0; i < SGE_QSETS; ++i) cpus[i] = i; @@ -586,6 +586,7 @@ static void setup_rss(struct adapter *adap) rspq_map[i] = i % nq0; rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; } + rspq_map[RSS_TABLE_SIZE] = 0xffff; /* terminator */ t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index d7f40436f319..477db477b133 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -304,6 +304,22 @@ static void dcb_tx_queue_prio_enable(struct net_device *dev, int enable) } #endif /* CONFIG_CHELSIO_T4_DCB */ +int cxgb4_dcb_enabled(const struct net_device *dev) +{ +#ifdef CONFIG_CHELSIO_T4_DCB + struct port_info *pi = netdev_priv(dev); + + if (!pi->dcb.enabled) + return 0; + + return ((pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED) || + (pi->dcb.state == CXGB4_DCB_STATE_HOST)); +#else + return 0; +#endif +} +EXPORT_SYMBOL(cxgb4_dcb_enabled); + void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat) { struct net_device *dev = adapter->port[port_id]; @@ -314,8 +330,10 @@ void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat) netif_carrier_on(dev); else { #ifdef CONFIG_CHELSIO_T4_DCB - cxgb4_dcb_state_init(dev); - dcb_tx_queue_prio_enable(dev, false); + if (cxgb4_dcb_enabled(dev)) { + cxgb4_dcb_state_init(dev); + dcb_tx_queue_prio_enable(dev, false); + } #endif /* CONFIG_CHELSIO_T4_DCB */ netif_carrier_off(dev); } @@ -494,22 +512,6 @@ static int link_start(struct net_device *dev) return ret; } -int cxgb4_dcb_enabled(const struct net_device *dev) -{ -#ifdef CONFIG_CHELSIO_T4_DCB - struct port_info *pi = netdev_priv(dev); - - if (!pi->dcb.enabled) - return 0; - - return ((pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED) || - (pi->dcb.state == CXGB4_DCB_STATE_HOST)); -#else - return 0; -#endif -} -EXPORT_SYMBOL(cxgb4_dcb_enabled); - #ifdef CONFIG_CHELSIO_T4_DCB /* Handle a Data Center Bridging update message from the firmware. */ static void dcb_rpl(struct adapter *adap, const struct fw_port_cmd *pcmd) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 49bcbf16c9ca..a63addb4e72c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -293,6 +293,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A); __be64 cmd_rpl[MBOX_LEN / 8]; + u32 pcie_fw; if ((size & 15) || size > MBOX_LEN) return -EINVAL; @@ -304,6 +305,12 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, if (adap->pdev->error_state != pci_channel_io_normal) return -EIO; + /* If we have a negative timeout, that implies that we can't sleep. */ + if (timeout < 0) { + sleep_ok = false; + timeout = -timeout; + } + v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); @@ -325,7 +332,10 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, delay_idx = 0; ms = delay[0]; - for (i = 0; i < timeout; i += ms) { + for (i = 0; + !((pcie_fw = t4_read_reg(adap, PCIE_FW_A)) & PCIE_FW_ERR_F) && + i < timeout; + i += ms) { if (sleep_ok) { ms = delay[delay_idx]; /* last element may repeat */ if (delay_idx < ARRAY_SIZE(delay) - 1) @@ -360,7 +370,7 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, } } - ret = -ETIMEDOUT; + ret = (pcie_fw & PCIE_FW_ERR_F) ? -ENXIO : -ETIMEDOUT; t4_record_mbox(adap, cmd, MBOX_LEN, access, ret); dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n", *(const u8 *)cmd, mbox); diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 1ccd282949a5..1bb57d3fbbe8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -1448,7 +1448,7 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev) * the new TX descriptors and return success. */ txq_advance(&txq->q, ndesc); - dev->trans_start = jiffies; + netif_trans_update(dev); ring_tx_db(adapter, &txq->q, ndesc); return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 48d91941408d..1471e16ba719 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -966,7 +966,7 @@ dm9000_init_dm9000(struct net_device *dev) /* Init Driver variable */ db->tx_pkt_cnt = 0; db->queue_pkt_len = 0; - dev->trans_start = jiffies; + netif_trans_update(dev); } /* Our watchdog timed out. Called by the networking layer */ @@ -985,7 +985,7 @@ static void dm9000_timeout(struct net_device *dev) dm9000_init_dm9000(dev); dm9000_unmask_interrupts(db); /* We can accept TX packets again */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); /* Restore previous register address */ @@ -1432,6 +1432,7 @@ dm9000_probe(struct platform_device *pdev) int reset_gpios; enum of_gpio_flags flags; struct regulator *power; + bool inv_mac_addr = false; power = devm_regulator_get(dev, "vcc"); if (IS_ERR(power)) { @@ -1686,9 +1687,7 @@ dm9000_probe(struct platform_device *pdev) } if (!is_valid_ether_addr(ndev->dev_addr)) { - dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", ndev->name); - + inv_mac_addr = true; eth_hw_addr_random(ndev); mac_src = "random"; } @@ -1697,11 +1696,15 @@ dm9000_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ndev); ret = register_netdev(ndev); - if (ret == 0) + if (ret == 0) { + if (inv_mac_addr) + dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please set using ip\n", + ndev->name); printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n", ndev->name, dm9000_type_to_char(db->type), db->io_addr, db->io_data, ndev->irq, ndev->dev_addr, mac_src); + } return 0; out: diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index d88fbab378aa..cbe84972ff7a 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -1336,7 +1336,7 @@ de4x5_open(struct net_device *dev) } lp->interrupt = UNMASK_INTERRUPTS; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ START_DE4X5; @@ -1935,7 +1935,7 @@ set_multicast_list(struct net_device *dev) lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } } } diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index afd8e78e024e..8ed0fd8b1dda 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -192,9 +192,6 @@ (__CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, \ (pci_dev)->revision)) -/* Sten Check */ -#define DEVICE net_device - /* Structure/enum declaration ------------------------------- */ struct tx_desc { __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ @@ -313,10 +310,10 @@ static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control /* function declaration ------------------------------------- */ -static int dmfe_open(struct DEVICE *); -static netdev_tx_t dmfe_start_xmit(struct sk_buff *, struct DEVICE *); -static int dmfe_stop(struct DEVICE *); -static void dmfe_set_filter_mode(struct DEVICE *); +static int dmfe_open(struct net_device *); +static netdev_tx_t dmfe_start_xmit(struct sk_buff *, struct net_device *); +static int dmfe_stop(struct net_device *); +static void dmfe_set_filter_mode(struct net_device *); static const struct ethtool_ops netdev_ethtool_ops; static u16 read_srom_word(void __iomem *, int); static irqreturn_t dmfe_interrupt(int , void *); @@ -326,8 +323,8 @@ static void poll_dmfe (struct net_device *dev); static void dmfe_descriptor_init(struct net_device *); static void allocate_rx_buffer(struct net_device *); static void update_cr6(u32, void __iomem *); -static void send_filter_frame(struct DEVICE *); -static void dm9132_id_table(struct DEVICE *); +static void send_filter_frame(struct net_device *); +static void dm9132_id_table(struct net_device *); static u16 dmfe_phy_read(void __iomem *, u8, u8, u32); static void dmfe_phy_write(void __iomem *, u8, u8, u16, u32); static void dmfe_phy_write_1bit(void __iomem *, u32); @@ -336,12 +333,12 @@ static u8 dmfe_sense_speed(struct dmfe_board_info *); static void dmfe_process_mode(struct dmfe_board_info *); static void dmfe_timer(unsigned long); static inline u32 cal_CRC(unsigned char *, unsigned int, u8); -static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); -static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); +static void dmfe_rx_packet(struct net_device *, struct dmfe_board_info *); +static void dmfe_free_tx_pkt(struct net_device *, struct dmfe_board_info *); static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); -static void dmfe_dynamic_reset(struct DEVICE *); +static void dmfe_dynamic_reset(struct net_device *); static void dmfe_free_rxbuffer(struct dmfe_board_info *); -static void dmfe_init_dm910x(struct DEVICE *); +static void dmfe_init_dm910x(struct net_device *); static void dmfe_parse_srom(struct dmfe_board_info *); static void dmfe_program_DM9801(struct dmfe_board_info *, int); static void dmfe_program_DM9802(struct dmfe_board_info *); @@ -558,7 +555,7 @@ static void dmfe_remove_one(struct pci_dev *pdev) * The interface is opened whenever "ifconfig" actives it. */ -static int dmfe_open(struct DEVICE *dev) +static int dmfe_open(struct net_device *dev) { struct dmfe_board_info *db = netdev_priv(dev); const int irq = db->pdev->irq; @@ -617,7 +614,7 @@ static int dmfe_open(struct DEVICE *dev) * Enable Tx/Rx machine */ -static void dmfe_init_dm910x(struct DEVICE *dev) +static void dmfe_init_dm910x(struct net_device *dev) { struct dmfe_board_info *db = netdev_priv(dev); void __iomem *ioaddr = db->ioaddr; @@ -684,7 +681,7 @@ static void dmfe_init_dm910x(struct DEVICE *dev) */ static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb, - struct DEVICE *dev) + struct net_device *dev) { struct dmfe_board_info *db = netdev_priv(dev); void __iomem *ioaddr = db->ioaddr; @@ -728,7 +725,7 @@ static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb, txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ db->tx_packet_cnt++; /* Ready to send */ dw32(DCR1, 0x1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ + netif_trans_update(dev); /* saved time stamp */ } else { db->tx_queue_cnt++; /* queue TX packet */ dw32(DCR1, 0x1); /* Issue Tx polling */ @@ -754,7 +751,7 @@ static netdev_tx_t dmfe_start_xmit(struct sk_buff *skb, * The interface is stopped when it is brought. */ -static int dmfe_stop(struct DEVICE *dev) +static int dmfe_stop(struct net_device *dev) { struct dmfe_board_info *db = netdev_priv(dev); void __iomem *ioaddr = db->ioaddr; @@ -798,7 +795,7 @@ static int dmfe_stop(struct DEVICE *dev) static irqreturn_t dmfe_interrupt(int irq, void *dev_id) { - struct DEVICE *dev = dev_id; + struct net_device *dev = dev_id; struct dmfe_board_info *db = netdev_priv(dev); void __iomem *ioaddr = db->ioaddr; unsigned long flags; @@ -879,7 +876,7 @@ static void poll_dmfe (struct net_device *dev) * Free TX resource after TX complete */ -static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) +static void dmfe_free_tx_pkt(struct net_device *dev, struct dmfe_board_info *db) { struct tx_desc *txptr; void __iomem *ioaddr = db->ioaddr; @@ -934,7 +931,7 @@ static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db) db->tx_packet_cnt++; /* Ready to send */ db->tx_queue_cnt--; dw32(DCR1, 0x1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ + netif_trans_update(dev); /* saved time stamp */ } /* Resource available check */ @@ -961,7 +958,7 @@ static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) * Receive the come packet and pass to upper layer */ -static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) +static void dmfe_rx_packet(struct net_device *dev, struct dmfe_board_info *db) { struct rx_desc *rxptr; struct sk_buff *skb, *newskb; @@ -1052,7 +1049,7 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) * Set DM910X multicast address */ -static void dmfe_set_filter_mode(struct DEVICE * dev) +static void dmfe_set_filter_mode(struct net_device *dev) { struct dmfe_board_info *db = netdev_priv(dev); unsigned long flags; @@ -1545,7 +1542,7 @@ static void send_filter_frame(struct net_device *dev) update_cr6(db->cr6_data | 0x2000, ioaddr); dw32(DCR1, 0x1); /* Issue Tx polling */ update_cr6(db->cr6_data, ioaddr); - dev->trans_start = jiffies; + netif_trans_update(dev); } else db->tx_queue_cnt++; /* Put in TX queue */ } diff --git a/drivers/net/ethernet/dec/tulip/pnic.c b/drivers/net/ethernet/dec/tulip/pnic.c index 5364563c4378..7bcccf5cac7a 100644 --- a/drivers/net/ethernet/dec/tulip/pnic.c +++ b/drivers/net/ethernet/dec/tulip/pnic.c @@ -44,7 +44,7 @@ void pnic_do_nway(struct net_device *dev) tp->csr6 = new_csr6; /* Restart Tx */ tulip_restart_rxtx(tp); - dev->trans_start = jiffies; + netif_trans_update(dev); } } } @@ -70,7 +70,7 @@ void pnic_lnk_change(struct net_device *dev, int csr5) iowrite32(tp->csr6, ioaddr + CSR6); iowrite32(0x30, ioaddr + CSR12); iowrite32(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ - dev->trans_start = jiffies; + netif_trans_update(dev); } } else if (ioread32(ioaddr + CSR5) & TPLnkPass) { if (tulip_media_cap[dev->if_port] & MediaIsMII) { @@ -147,7 +147,7 @@ void pnic_timer(unsigned long data) tp->csr6 = new_csr6; /* Restart Tx */ tulip_restart_rxtx(tp); - dev->trans_start = jiffies; + netif_trans_update(dev); if (tulip_debug > 1) dev_info(&dev->dev, "Changing PNIC configuration to %s %s-duplex, CSR6 %08x\n", diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 94d0eebef129..bbde90bc74fe 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -605,7 +605,7 @@ static void tulip_tx_timeout(struct net_device *dev) out_unlock: spin_unlock_irqrestore (&tp->lock, flags); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue (dev); } diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 447d09272ab7..e750b5ddc0fb 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -636,7 +636,7 @@ static netdev_tx_t uli526x_start_xmit(struct sk_buff *skb, txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */ db->tx_packet_cnt++; /* Ready to send */ uw32(DCR1, 0x1); /* Issue Tx polling */ - dev->trans_start = jiffies; /* saved time stamp */ + netif_trans_update(dev); /* saved time stamp */ } /* Tx resource check */ @@ -1431,7 +1431,7 @@ static void send_filter_frame(struct net_device *dev, int mc_cnt) update_cr6(db->cr6_data | 0x2000, ioaddr); uw32(DCR1, 0x1); /* Issue Tx polling */ update_cr6(db->cr6_data, ioaddr); - dev->trans_start = jiffies; + netif_trans_update(dev); } else netdev_err(dev, "No Tx resource - Send_filter_frame!\n"); } diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 3c0e4d5c5fef..1f62b9423851 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -966,7 +966,7 @@ static void tx_timeout(struct net_device *dev) enable_irq(irq); netif_wake_queue(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ np->stats.tx_errors++; } diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index f92b6d948398..78f144696d6b 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -706,7 +706,7 @@ rio_tx_timeout (struct net_device *dev) dev->name, dr32(TxStatus)); rio_free_tx(dev, 0); dev->if_port = 0; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } static netdev_tx_t diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index a28a2e583f0f..58c6338a839e 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -1011,7 +1011,7 @@ static void tx_timeout(struct net_device *dev) dev->if_port = 0; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { netif_wake_queue(dev); diff --git a/drivers/net/ethernet/ezchip/nps_enet.c b/drivers/net/ethernet/ezchip/nps_enet.c index 1f23845a0694..085f9125cf42 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.c +++ b/drivers/net/ethernet/ezchip/nps_enet.c @@ -145,7 +145,7 @@ static void nps_enet_tx_handler(struct net_device *ndev) u32 tx_ctrl_nt = (tx_ctrl_value & TX_CTL_NT_MASK) >> TX_CTL_NT_SHIFT; /* Check if we got TX */ - if (!priv->tx_packet_sent || tx_ctrl_ct) + if (!priv->tx_skb || tx_ctrl_ct) return; /* Ack Tx ctrl register */ @@ -160,7 +160,7 @@ static void nps_enet_tx_handler(struct net_device *ndev) } dev_kfree_skb(priv->tx_skb); - priv->tx_packet_sent = false; + priv->tx_skb = NULL; if (netif_queue_stopped(ndev)) netif_wake_queue(ndev); @@ -183,6 +183,9 @@ static int nps_enet_poll(struct napi_struct *napi, int budget) work_done = nps_enet_rx_handler(ndev); if (work_done < budget) { u32 buf_int_enable_value = 0; + u32 tx_ctrl_value = nps_enet_reg_get(priv, NPS_ENET_REG_TX_CTL); + u32 tx_ctrl_ct = + (tx_ctrl_value & TX_CTL_CT_MASK) >> TX_CTL_CT_SHIFT; napi_complete(napi); @@ -192,6 +195,18 @@ static int nps_enet_poll(struct napi_struct *napi, int budget) nps_enet_reg_set(priv, NPS_ENET_REG_BUF_INT_ENABLE, buf_int_enable_value); + + /* in case we will get a tx interrupt while interrupts + * are masked, we will lose it since the tx is edge interrupt. + * specifically, while executing the code section above, + * between nps_enet_tx_handler and the interrupts enable, all + * tx requests will be stuck until we will get an rx interrupt. + * the two code lines below will solve this situation by + * re-adding ourselves to the poll list. + */ + + if (priv->tx_skb && !tx_ctrl_ct) + napi_reschedule(napi); } return work_done; @@ -217,7 +232,7 @@ static irqreturn_t nps_enet_irq_handler(s32 irq, void *dev_instance) u32 tx_ctrl_ct = (tx_ctrl_value & TX_CTL_CT_MASK) >> TX_CTL_CT_SHIFT; u32 rx_ctrl_cr = (rx_ctrl_value & RX_CTL_CR_MASK) >> RX_CTL_CR_SHIFT; - if ((!tx_ctrl_ct && priv->tx_packet_sent) || rx_ctrl_cr) + if ((!tx_ctrl_ct && priv->tx_skb) || rx_ctrl_cr) if (likely(napi_schedule_prep(&priv->napi))) { nps_enet_reg_set(priv, NPS_ENET_REG_BUF_INT_ENABLE, 0); __napi_schedule(&priv->napi); @@ -387,8 +402,6 @@ static void nps_enet_send_frame(struct net_device *ndev, /* Write the length of the Frame */ tx_ctrl_value |= length << TX_CTL_NT_SHIFT; - /* Indicate SW is done */ - priv->tx_packet_sent = true; tx_ctrl_value |= NPS_ENET_ENABLE << TX_CTL_CT_SHIFT; /* Send Frame */ nps_enet_reg_set(priv, NPS_ENET_REG_TX_CTL, tx_ctrl_value); @@ -465,7 +478,7 @@ static s32 nps_enet_open(struct net_device *ndev) s32 err; /* Reset private variables */ - priv->tx_packet_sent = false; + priv->tx_skb = NULL; priv->ge_mac_cfg_2_value = 0; priv->ge_mac_cfg_3_value = 0; @@ -534,6 +547,11 @@ static netdev_tx_t nps_enet_start_xmit(struct sk_buff *skb, priv->tx_skb = skb; + /* make sure tx_skb is actually written to the memory + * before the HW is informed and the IRQ is fired. + */ + wmb(); + nps_enet_send_frame(ndev, skb); return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/ezchip/nps_enet.h b/drivers/net/ethernet/ezchip/nps_enet.h index d0cab600bce8..3939ca20cc9f 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.h +++ b/drivers/net/ethernet/ezchip/nps_enet.h @@ -165,14 +165,12 @@ * struct nps_enet_priv - Storage of ENET's private information. * @regs_base: Base address of ENET memory-mapped control registers. * @irq: For RX/TX IRQ number. - * @tx_packet_sent: SW indication if frame is being sent. * @tx_skb: socket buffer of sent frame. * @napi: Structure for NAPI. */ struct nps_enet_priv { void __iomem *regs_base; s32 irq; - bool tx_packet_sent; struct sk_buff *tx_skb; struct napi_struct napi; u32 ge_mac_cfg_2_value; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 84384e1585a5..e7cf313e359b 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -71,7 +71,6 @@ struct ftgmac100 { struct napi_struct napi; struct mii_bus *mii_bus; - struct phy_device *phydev; int old_speed; }; @@ -807,7 +806,7 @@ err: static void ftgmac100_adjust_link(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); - struct phy_device *phydev = priv->phydev; + struct phy_device *phydev = netdev->phydev; int ier; if (phydev->speed == priv->old_speed) @@ -850,7 +849,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) return PTR_ERR(phydev); } - priv->phydev = phydev; return 0; } @@ -939,27 +937,11 @@ static void ftgmac100_get_drvinfo(struct net_device *netdev, strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } -static int ftgmac100_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_ethtool_gset(priv->phydev, cmd); -} - -static int ftgmac100_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_ethtool_sset(priv->phydev, cmd); -} - static const struct ethtool_ops ftgmac100_ethtool_ops = { - .set_settings = ftgmac100_set_settings, - .get_settings = ftgmac100_get_settings, .get_drvinfo = ftgmac100_get_drvinfo, .get_link = ethtool_op_get_link, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, }; /****************************************************************************** @@ -1085,7 +1067,7 @@ static int ftgmac100_open(struct net_device *netdev) ftgmac100_init_hw(priv); ftgmac100_start_hw(priv, 10); - phy_start(priv->phydev); + phy_start(netdev->phydev); napi_enable(&priv->napi); netif_start_queue(netdev); @@ -1111,7 +1093,7 @@ static int ftgmac100_stop(struct net_device *netdev) netif_stop_queue(netdev); napi_disable(&priv->napi); - phy_stop(priv->phydev); + phy_stop(netdev->phydev); ftgmac100_stop_hw(priv); free_irq(priv->irq, netdev); @@ -1152,9 +1134,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* optional */ static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_mii_ioctl(priv->phydev, ifr, cmd); + return phy_mii_ioctl(netdev->phydev, ifr, cmd); } static const struct net_device_ops ftgmac100_netdev_ops = { @@ -1275,7 +1255,7 @@ static int ftgmac100_probe(struct platform_device *pdev) return 0; err_register_netdev: - phy_disconnect(priv->phydev); + phy_disconnect(netdev->phydev); err_mii_probe: mdiobus_unregister(priv->mii_bus); err_register_mdiobus: @@ -1301,7 +1281,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev) unregister_netdev(netdev); - phy_disconnect(priv->phydev); + phy_disconnect(netdev->phydev); mdiobus_unregister(priv->mii_bus); mdiobus_free(priv->mii_bus); diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index b1b9ebafb354..c08bd763172a 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -1227,7 +1227,7 @@ static void fealnx_tx_timeout(struct net_device *dev) spin_unlock_irqrestore(&np->lock, flags); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; netif_wake_queue(dev); /* or .._start_.. ?? */ } diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 195122e11f10..f58f9ea51639 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -517,7 +517,6 @@ struct fec_enet_private { /* Phylib and MDIO interface */ struct mii_bus *mii_bus; - struct phy_device *phy_dev; int mii_timeout; uint phy_speed; phy_interface_t phy_interface; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index bfa10c3da35f..ca2cccc594fd 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -967,10 +967,10 @@ fec_restart(struct net_device *ndev) rcntl &= ~(1 << 8); /* 1G, 100M or 10M */ - if (fep->phy_dev) { - if (fep->phy_dev->speed == SPEED_1000) + if (ndev->phydev) { + if (ndev->phydev->speed == SPEED_1000) ecntl |= (1 << 5); - else if (fep->phy_dev->speed == SPEED_100) + else if (ndev->phydev->speed == SPEED_100) rcntl &= ~(1 << 9); else rcntl |= (1 << 9); @@ -991,7 +991,7 @@ fec_restart(struct net_device *ndev) */ cfgr = (fep->phy_interface == PHY_INTERFACE_MODE_RMII) ? BM_MIIGSK_CFGR_RMII : BM_MIIGSK_CFGR_MII; - if (fep->phy_dev && fep->phy_dev->speed == SPEED_10) + if (ndev->phydev && ndev->phydev->speed == SPEED_10) cfgr |= BM_MIIGSK_CFGR_FRCONT_10M; writel(cfgr, fep->hwp + FEC_MIIGSK_CFGR); @@ -1005,7 +1005,7 @@ fec_restart(struct net_device *ndev) /* enable pause frame*/ if ((fep->pause_flag & FEC_PAUSE_FLAG_ENABLE) || ((fep->pause_flag & FEC_PAUSE_FLAG_AUTONEG) && - fep->phy_dev && fep->phy_dev->pause)) { + ndev->phydev && ndev->phydev->pause)) { rcntl |= FEC_ENET_FCE; /* set FIFO threshold parameter to reduce overrun */ @@ -1521,9 +1521,15 @@ fec_enet_rx(struct net_device *ndev, int budget) struct fec_enet_private *fep = netdev_priv(ndev); for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) { - clear_bit(queue_id, &fep->work_rx); - pkt_received += fec_enet_rx_queue(ndev, + int ret; + + ret = fec_enet_rx_queue(ndev, budget - pkt_received, queue_id); + + if (ret < budget - pkt_received) + clear_bit(queue_id, &fep->work_rx); + + pkt_received += ret; } return pkt_received; } @@ -1679,7 +1685,7 @@ static void fec_get_mac(struct net_device *ndev) static void fec_enet_adjust_link(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phy_dev = fep->phy_dev; + struct phy_device *phy_dev = ndev->phydev; int status_change = 0; /* Prevent a state halted on mii error */ @@ -1879,8 +1885,6 @@ static int fec_enet_mii_probe(struct net_device *ndev) int phy_id; int dev_id = fep->dev_id; - fep->phy_dev = NULL; - if (fep->phy_node) { phy_dev = of_phy_connect(ndev, fep->phy_node, &fec_enet_adjust_link, 0, @@ -1928,7 +1932,6 @@ static int fec_enet_mii_probe(struct net_device *ndev) phy_dev->advertising = phy_dev->supported; - fep->phy_dev = phy_dev; fep->link = 0; fep->full_duplex = 0; @@ -2058,30 +2061,6 @@ static void fec_enet_mii_remove(struct fec_enet_private *fep) } } -static int fec_enet_get_link_ksettings(struct net_device *ndev, - struct ethtool_link_ksettings *cmd) -{ - struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phydev = fep->phy_dev; - - if (!phydev) - return -ENODEV; - - return phy_ethtool_ksettings_get(phydev, cmd); -} - -static int fec_enet_set_link_ksettings(struct net_device *ndev, - const struct ethtool_link_ksettings *cmd) -{ - struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phydev = fep->phy_dev; - - if (!phydev) - return -ENODEV; - - return phy_ethtool_ksettings_set(phydev, cmd); -} - static void fec_enet_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { @@ -2214,7 +2193,7 @@ static int fec_enet_set_pauseparam(struct net_device *ndev, { struct fec_enet_private *fep = netdev_priv(ndev); - if (!fep->phy_dev) + if (!ndev->phydev) return -ENODEV; if (pause->tx_pause != pause->rx_pause) { @@ -2230,17 +2209,17 @@ static int fec_enet_set_pauseparam(struct net_device *ndev, fep->pause_flag |= pause->autoneg ? FEC_PAUSE_FLAG_AUTONEG : 0; if (pause->rx_pause || pause->autoneg) { - fep->phy_dev->supported |= ADVERTISED_Pause; - fep->phy_dev->advertising |= ADVERTISED_Pause; + ndev->phydev->supported |= ADVERTISED_Pause; + ndev->phydev->advertising |= ADVERTISED_Pause; } else { - fep->phy_dev->supported &= ~ADVERTISED_Pause; - fep->phy_dev->advertising &= ~ADVERTISED_Pause; + ndev->phydev->supported &= ~ADVERTISED_Pause; + ndev->phydev->advertising &= ~ADVERTISED_Pause; } if (pause->autoneg) { if (netif_running(ndev)) fec_stop(ndev); - phy_start_aneg(fep->phy_dev); + phy_start_aneg(ndev->phydev); } if (netif_running(ndev)) { napi_disable(&fep->napi); @@ -2356,8 +2335,7 @@ static int fec_enet_get_sset_count(struct net_device *dev, int sset) static int fec_enet_nway_reset(struct net_device *dev) { - struct fec_enet_private *fep = netdev_priv(dev); - struct phy_device *phydev = fep->phy_dev; + struct phy_device *phydev = dev->phydev; if (!phydev) return -ENODEV; @@ -2581,14 +2559,14 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .set_tunable = fec_enet_set_tunable, .get_wol = fec_enet_get_wol, .set_wol = fec_enet_set_wol, - .get_link_ksettings = fec_enet_get_link_ksettings, - .set_link_ksettings = fec_enet_set_link_ksettings, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, }; static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) { struct fec_enet_private *fep = netdev_priv(ndev); - struct phy_device *phydev = fep->phy_dev; + struct phy_device *phydev = ndev->phydev; if (!netif_running(ndev)) return -EINVAL; @@ -2843,7 +2821,7 @@ fec_enet_open(struct net_device *ndev) goto err_enet_mii_probe; napi_enable(&fep->napi); - phy_start(fep->phy_dev); + phy_start(ndev->phydev); netif_tx_start_all_queues(ndev); device_set_wakeup_enable(&ndev->dev, fep->wol_flag & @@ -2867,7 +2845,7 @@ fec_enet_close(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); - phy_stop(fep->phy_dev); + phy_stop(ndev->phydev); if (netif_device_present(ndev)) { napi_disable(&fep->napi); @@ -2875,8 +2853,7 @@ fec_enet_close(struct net_device *ndev) fec_stop(ndev); } - phy_disconnect(fep->phy_dev); - fep->phy_dev = NULL; + phy_disconnect(ndev->phydev); fec_enet_clk_enable(ndev, false); pinctrl_pm_select_sleep_state(&fep->pdev->dev); @@ -3504,7 +3481,7 @@ static int __maybe_unused fec_suspend(struct device *dev) if (netif_running(ndev)) { if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; - phy_stop(fep->phy_dev); + phy_stop(ndev->phydev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); netif_device_detach(ndev); @@ -3564,7 +3541,7 @@ static int __maybe_unused fec_resume(struct device *dev) netif_device_attach(ndev); netif_tx_unlock_bh(ndev); napi_enable(&fep->napi); - phy_start(fep->phy_dev); + phy_start(ndev->phydev); } rtnl_unlock(); diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 25553ee857b4..446ae9d60c71 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -66,7 +66,6 @@ struct mpc52xx_fec_priv { /* MDIO link details */ unsigned int mdio_speed; struct device_node *phy_node; - struct phy_device *phydev; enum phy_state link; int seven_wire_mode; }; @@ -165,7 +164,7 @@ static int mpc52xx_fec_alloc_rx_buffers(struct net_device *dev, struct bcom_task static void mpc52xx_fec_adjust_link(struct net_device *dev) { struct mpc52xx_fec_priv *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; + struct phy_device *phydev = dev->phydev; int new_state = 0; if (phydev->link != PHY_DOWN) { @@ -215,16 +214,17 @@ static void mpc52xx_fec_adjust_link(struct net_device *dev) static int mpc52xx_fec_open(struct net_device *dev) { struct mpc52xx_fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = NULL; int err = -EBUSY; if (priv->phy_node) { - priv->phydev = of_phy_connect(priv->ndev, priv->phy_node, - mpc52xx_fec_adjust_link, 0, 0); - if (!priv->phydev) { + phydev = of_phy_connect(priv->ndev, priv->phy_node, + mpc52xx_fec_adjust_link, 0, 0); + if (!phydev) { dev_err(&dev->dev, "of_phy_connect failed\n"); return -ENODEV; } - phy_start(priv->phydev); + phy_start(phydev); } if (request_irq(dev->irq, mpc52xx_fec_interrupt, IRQF_SHARED, @@ -268,10 +268,9 @@ static int mpc52xx_fec_open(struct net_device *dev) free_ctrl_irq: free_irq(dev->irq, dev); free_phy: - if (priv->phydev) { - phy_stop(priv->phydev); - phy_disconnect(priv->phydev); - priv->phydev = NULL; + if (phydev) { + phy_stop(phydev); + phy_disconnect(phydev); } return err; @@ -280,6 +279,7 @@ static int mpc52xx_fec_open(struct net_device *dev) static int mpc52xx_fec_close(struct net_device *dev) { struct mpc52xx_fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; netif_stop_queue(dev); @@ -291,11 +291,10 @@ static int mpc52xx_fec_close(struct net_device *dev) free_irq(priv->r_irq, dev); free_irq(priv->t_irq, dev); - if (priv->phydev) { + if (phydev) { /* power down phy */ - phy_stop(priv->phydev); - phy_disconnect(priv->phydev); - priv->phydev = NULL; + phy_stop(phydev); + phy_disconnect(phydev); } return 0; @@ -763,26 +762,6 @@ static void mpc52xx_fec_reset(struct net_device *dev) /* ethtool interface */ -static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct mpc52xx_fec_priv *priv = netdev_priv(dev); - - if (!priv->phydev) - return -ENODEV; - - return phy_ethtool_gset(priv->phydev, cmd); -} - -static int mpc52xx_fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct mpc52xx_fec_priv *priv = netdev_priv(dev); - - if (!priv->phydev) - return -ENODEV; - - return phy_ethtool_sset(priv->phydev, cmd); -} - static u32 mpc52xx_fec_get_msglevel(struct net_device *dev) { struct mpc52xx_fec_priv *priv = netdev_priv(dev); @@ -796,23 +775,23 @@ static void mpc52xx_fec_set_msglevel(struct net_device *dev, u32 level) } static const struct ethtool_ops mpc52xx_fec_ethtool_ops = { - .get_settings = mpc52xx_fec_get_settings, - .set_settings = mpc52xx_fec_set_settings, .get_link = ethtool_op_get_link, .get_msglevel = mpc52xx_fec_get_msglevel, .set_msglevel = mpc52xx_fec_set_msglevel, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, }; static int mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct mpc52xx_fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; - if (!priv->phydev) + if (!phydev) return -ENOTSUPP; - return phy_mii_ioctl(priv->phydev, rq, cmd); + return phy_mii_ioctl(phydev, rq, cmd); } static const struct net_device_ops mpc52xx_fec_netdev_ops = { diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index ea83712a6d62..bcb9dccada4d 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -2772,7 +2772,7 @@ static struct fman *read_dts_node(struct platform_device *of_dev) /* Get the FM address */ res = platform_get_resource(of_dev, IORESOURCE_MEM, 0); if (!res) { - dev_err(&of_dev->dev, "%s: Can't get FMan memory resouce\n", + dev_err(&of_dev->dev, "%s: Can't get FMan memory resource\n", __func__); goto fman_node_put; } diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 48a9c176e0d1..61fd486c50bb 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -652,13 +652,13 @@ static void fs_timeout(struct net_device *dev) spin_lock_irqsave(&fep->lock, flags); if (dev->flags & IFF_UP) { - phy_stop(fep->phydev); + phy_stop(dev->phydev); (*fep->ops->stop)(dev); (*fep->ops->restart)(dev); - phy_start(fep->phydev); + phy_start(dev->phydev); } - phy_start(fep->phydev); + phy_start(dev->phydev); wake = fep->tx_free && !(CBDR_SC(fep->cur_tx) & BD_ENET_TX_READY); spin_unlock_irqrestore(&fep->lock, flags); @@ -672,7 +672,7 @@ static void fs_timeout(struct net_device *dev) static void generic_adjust_link(struct net_device *dev) { struct fs_enet_private *fep = netdev_priv(dev); - struct phy_device *phydev = fep->phydev; + struct phy_device *phydev = dev->phydev; int new_state = 0; if (phydev->link) { @@ -741,8 +741,6 @@ static int fs_init_phy(struct net_device *dev) return -ENODEV; } - fep->phydev = phydev; - return 0; } @@ -776,7 +774,7 @@ static int fs_enet_open(struct net_device *dev) napi_disable(&fep->napi_tx); return err; } - phy_start(fep->phydev); + phy_start(dev->phydev); netif_start_queue(dev); @@ -792,7 +790,7 @@ static int fs_enet_close(struct net_device *dev) netif_carrier_off(dev); napi_disable(&fep->napi); napi_disable(&fep->napi_tx); - phy_stop(fep->phydev); + phy_stop(dev->phydev); spin_lock_irqsave(&fep->lock, flags); spin_lock(&fep->tx_lock); @@ -801,8 +799,7 @@ static int fs_enet_close(struct net_device *dev) spin_unlock_irqrestore(&fep->lock, flags); /* release any irqs */ - phy_disconnect(fep->phydev); - fep->phydev = NULL; + phy_disconnect(dev->phydev); free_irq(fep->interrupt, dev); return 0; @@ -847,26 +844,6 @@ static void fs_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs->version = 0; } -static int fs_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct fs_enet_private *fep = netdev_priv(dev); - - if (!fep->phydev) - return -ENODEV; - - return phy_ethtool_gset(fep->phydev, cmd); -} - -static int fs_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct fs_enet_private *fep = netdev_priv(dev); - - if (!fep->phydev) - return -ENODEV; - - return phy_ethtool_sset(fep->phydev, cmd); -} - static int fs_nway_reset(struct net_device *dev) { return 0; @@ -887,24 +864,22 @@ static void fs_set_msglevel(struct net_device *dev, u32 value) static const struct ethtool_ops fs_ethtool_ops = { .get_drvinfo = fs_get_drvinfo, .get_regs_len = fs_get_regs_len, - .get_settings = fs_get_settings, - .set_settings = fs_set_settings, .nway_reset = fs_nway_reset, .get_link = ethtool_op_get_link, .get_msglevel = fs_get_msglevel, .set_msglevel = fs_set_msglevel, .get_regs = fs_get_regs, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, }; static int fs_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct fs_enet_private *fep = netdev_priv(dev); - if (!netif_running(dev)) return -EINVAL; - return phy_mii_ioctl(fep->phydev, rq, cmd); + return phy_mii_ioctl(dev->phydev, rq, cmd); } extern int fs_mii_connect(struct net_device *dev); diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h index f184d8f952e2..e29f54a35210 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h @@ -149,7 +149,6 @@ struct fs_enet_private { unsigned int last_mii_status; int interrupt; - struct phy_device *phydev; int oldduplex, oldspeed, oldlink; /* current settings */ /* event masks */ diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c index 1ba359f17ec6..d71761a34022 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fcc.c @@ -370,7 +370,7 @@ static void restart(struct net_device *dev) /* adjust to speed (for RMII mode) */ if (fpi->use_rmii) { - if (fep->phydev->speed == 100) + if (dev->phydev->speed == 100) C8(fcccp, fcc_gfemr, 0x20); else S8(fcccp, fcc_gfemr, 0x20); @@ -396,7 +396,7 @@ static void restart(struct net_device *dev) S32(fccp, fcc_fpsmr, FCC_PSMR_RMII); /* adjust to duplex mode */ - if (fep->phydev->duplex) + if (dev->phydev->duplex) S32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); else C32(fccp, fcc_fpsmr, FCC_PSMR_FDE | FCC_PSMR_LPB); diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index bade2f8f9b5c..35a318ed3a62 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -254,7 +254,7 @@ static void restart(struct net_device *dev) int r; u32 addrhi, addrlo; - struct mii_bus *mii = fep->phydev->mdio.bus; + struct mii_bus *mii = dev->phydev->mdio.bus; struct fec_info* fec_inf = mii->priv; r = whack_reset(fep->fec.fecp); @@ -333,7 +333,7 @@ static void restart(struct net_device *dev) /* * adjust to duplex mode */ - if (fep->phydev->duplex) { + if (dev->phydev->duplex) { FC(fecp, r_cntrl, FEC_RCNTRL_DRT); FS(fecp, x_cntrl, FEC_TCNTRL_FDEN); /* FD enable */ } else { @@ -363,7 +363,7 @@ static void stop(struct net_device *dev) const struct fs_platform_info *fpi = fep->fpi; struct fec __iomem *fecp = fep->fec.fecp; - struct fec_info *feci = fep->phydev->mdio.bus->priv; + struct fec_info *feci = dev->phydev->mdio.bus->priv; int i; diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index 7a184e8816a4..e8b9c33d35b4 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c @@ -352,7 +352,7 @@ static void restart(struct net_device *dev) W16(sccp, scc_psmr, SCC_PSMR_ENCRC | SCC_PSMR_NIB22); /* Set full duplex mode if needed */ - if (fep->phydev->duplex) + if (dev->phydev->duplex) S16(sccp, scc_psmr, SCC_PSMR_LPB | SCC_PSMR_FDE); /* Restore multicast and promiscuous settings */ diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index d2f917af539f..7615e0668acb 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -999,7 +999,7 @@ static int gfar_hwtstamp_get(struct net_device *netdev, struct ifreq *ifr) static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct gfar_private *priv = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; if (!netif_running(dev)) return -EINVAL; @@ -1009,10 +1009,10 @@ static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (cmd == SIOCGHWTSTAMP) return gfar_hwtstamp_get(dev, rq); - if (!priv->phydev) + if (!phydev) return -ENODEV; - return phy_mii_ioctl(priv->phydev, rq, cmd); + return phy_mii_ioctl(phydev, rq, cmd); } static u32 cluster_entry_per_class(struct gfar_private *priv, u32 rqfar, @@ -1635,7 +1635,7 @@ static int gfar_suspend(struct device *dev) gfar_start_wol_filer(priv); } else { - phy_stop(priv->phydev); + phy_stop(ndev->phydev); } return 0; @@ -1664,7 +1664,7 @@ static int gfar_resume(struct device *dev) gfar_filer_restore_table(priv); } else { - phy_start(priv->phydev); + phy_start(ndev->phydev); } gfar_start(priv); @@ -1698,8 +1698,8 @@ static int gfar_restore(struct device *dev) priv->oldspeed = 0; priv->oldduplex = -1; - if (priv->phydev) - phy_start(priv->phydev); + if (ndev->phydev) + phy_start(ndev->phydev); netif_device_attach(ndev); enable_napi(priv); @@ -1778,6 +1778,7 @@ static int init_phy(struct net_device *dev) priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ? GFAR_SUPPORTED_GBIT : 0; phy_interface_t interface; + struct phy_device *phydev; priv->oldlink = 0; priv->oldspeed = 0; @@ -1785,9 +1786,9 @@ static int init_phy(struct net_device *dev) interface = gfar_get_interface(dev); - priv->phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0, - interface); - if (!priv->phydev) { + phydev = of_phy_connect(dev, priv->phy_node, &adjust_link, 0, + interface); + if (!phydev) { dev_err(&dev->dev, "could not attach to PHY\n"); return -ENODEV; } @@ -1796,11 +1797,11 @@ static int init_phy(struct net_device *dev) gfar_configure_serdes(dev); /* Remove any features not supported by the controller */ - priv->phydev->supported &= (GFAR_SUPPORTED | gigabit_support); - priv->phydev->advertising = priv->phydev->supported; + phydev->supported &= (GFAR_SUPPORTED | gigabit_support); + phydev->advertising = phydev->supported; /* Add support for flow control, but don't advertise it by default */ - priv->phydev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); + phydev->supported |= (SUPPORTED_Pause | SUPPORTED_Asym_Pause); return 0; } @@ -1944,7 +1945,7 @@ void stop_gfar(struct net_device *dev) /* disable ints and gracefully shut down Rx/Tx DMA */ gfar_halt(priv); - phy_stop(priv->phydev); + phy_stop(dev->phydev); free_skb_resources(priv); } @@ -2076,7 +2077,7 @@ void gfar_start(struct gfar_private *priv) gfar_ints_enable(priv); - priv->ndev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(priv->ndev); /* prevent tx timeout */ } static void free_grp_irqs(struct gfar_priv_grp *grp) @@ -2204,7 +2205,7 @@ int startup_gfar(struct net_device *ndev) priv->oldspeed = 0; priv->oldduplex = -1; - phy_start(priv->phydev); + phy_start(ndev->phydev); enable_napi(priv); @@ -2572,8 +2573,7 @@ static int gfar_close(struct net_device *dev) stop_gfar(dev); /* Disconnect from the PHY */ - phy_disconnect(priv->phydev); - priv->phydev = NULL; + phy_disconnect(dev->phydev); gfar_free_irq(priv); @@ -3379,7 +3379,7 @@ static irqreturn_t gfar_interrupt(int irq, void *grp_id) static void adjust_link(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; + struct phy_device *phydev = dev->phydev; if (unlikely(phydev->link != priv->oldlink || (phydev->link && (phydev->duplex != priv->oldduplex || @@ -3620,7 +3620,8 @@ static irqreturn_t gfar_error(int irq, void *grp_id) static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) { - struct phy_device *phydev = priv->phydev; + struct net_device *ndev = priv->ndev; + struct phy_device *phydev = ndev->phydev; u32 val = 0; if (!phydev->duplex) @@ -3660,7 +3661,8 @@ static u32 gfar_get_flowctrl_cfg(struct gfar_private *priv) static noinline void gfar_update_link_state(struct gfar_private *priv) { struct gfar __iomem *regs = priv->gfargrp[0].regs; - struct phy_device *phydev = priv->phydev; + struct net_device *ndev = priv->ndev; + struct phy_device *phydev = ndev->phydev; struct gfar_priv_rx_q *rx_queue = NULL; int i; diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index cb77667971a7..373fd094f2f3 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -1153,7 +1153,6 @@ struct gfar_private { phy_interface_t interface; struct device_node *phy_node; struct device_node *tbi_node; - struct phy_device *phydev; struct mii_bus *mii_bus; int oldspeed; int oldduplex; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 4b0ee855edd7..56588f2e1d91 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -184,40 +184,6 @@ static void gfar_gdrvinfo(struct net_device *dev, strlcpy(drvinfo->bus_info, "N/A", sizeof(drvinfo->bus_info)); } - -static int gfar_ssettings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct gfar_private *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; - - if (NULL == phydev) - return -ENODEV; - - return phy_ethtool_sset(phydev, cmd); -} - - -/* Return the current settings in the ethtool_cmd structure */ -static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct gfar_private *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; - struct gfar_priv_rx_q *rx_queue = NULL; - struct gfar_priv_tx_q *tx_queue = NULL; - - if (NULL == phydev) - return -ENODEV; - tx_queue = priv->tx_queue[0]; - rx_queue = priv->rx_queue[0]; - - /* etsec-1.7 and older versions have only one txic - * and rxic regs although they support multiple queues */ - cmd->maxtxpkt = get_icft_value(tx_queue->txic); - cmd->maxrxpkt = get_icft_value(rx_queue->rxic); - - return phy_ethtool_gset(phydev, cmd); -} - /* Return the length of the register structure */ static int gfar_reglen(struct net_device *dev) { @@ -242,10 +208,12 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs) { + struct net_device *ndev = priv->ndev; + struct phy_device *phydev = ndev->phydev; unsigned int count; /* The timer is different, depending on the interface speed */ - switch (priv->phydev->speed) { + switch (phydev->speed) { case SPEED_1000: count = GFAR_GBIT_TIME; break; @@ -267,10 +235,12 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks) { + struct net_device *ndev = priv->ndev; + struct phy_device *phydev = ndev->phydev; unsigned int count; /* The timer is different, depending on the interface speed */ - switch (priv->phydev->speed) { + switch (phydev->speed) { case SPEED_1000: count = GFAR_GBIT_TIME; break; @@ -304,7 +274,7 @@ static int gfar_gcoalesce(struct net_device *dev, if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; - if (NULL == priv->phydev) + if (!dev->phydev) return -ENODEV; rx_queue = priv->rx_queue[0]; @@ -365,7 +335,7 @@ static int gfar_scoalesce(struct net_device *dev, if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) return -EOPNOTSUPP; - if (NULL == priv->phydev) + if (!dev->phydev) return -ENODEV; /* Check the bounds of the values */ @@ -529,7 +499,7 @@ static int gfar_spauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { struct gfar_private *priv = netdev_priv(dev); - struct phy_device *phydev = priv->phydev; + struct phy_device *phydev = dev->phydev; struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 oldadv, newadv; @@ -1565,8 +1535,6 @@ static int gfar_get_ts_info(struct net_device *dev, } const struct ethtool_ops gfar_ethtool_ops = { - .get_settings = gfar_gsettings, - .set_settings = gfar_ssettings, .get_drvinfo = gfar_gdrvinfo, .get_regs_len = gfar_reglen, .get_regs = gfar_get_regs, @@ -1589,4 +1557,6 @@ const struct ethtool_ops gfar_ethtool_ops = { .set_rxnfc = gfar_set_nfc, .get_rxnfc = gfar_get_nfc, .get_ts_info = gfar_get_ts_info, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, }; diff --git a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c index 89714f5e0dfc..812a968a78e9 100644 --- a/drivers/net/ethernet/freescale/ucc_geth_ethtool.c +++ b/drivers/net/ethernet/freescale/ucc_geth_ethtool.c @@ -105,23 +105,20 @@ static const char rx_fw_stat_gstrings[][ETH_GSTRING_LEN] = { #define UEC_RX_FW_STATS_LEN ARRAY_SIZE(rx_fw_stat_gstrings) static int -uec_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +uec_get_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct ucc_geth_private *ugeth = netdev_priv(netdev); struct phy_device *phydev = ugeth->phydev; - struct ucc_geth_info *ug_info = ugeth->ug_info; if (!phydev) return -ENODEV; - ecmd->maxtxpkt = 1; - ecmd->maxrxpkt = ug_info->interruptcoalescingmaxvalue[0]; - - return phy_ethtool_gset(phydev, ecmd); + return phy_ethtool_ksettings_get(phydev, cmd); } static int -uec_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +uec_set_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct ucc_geth_private *ugeth = netdev_priv(netdev); struct phy_device *phydev = ugeth->phydev; @@ -129,7 +126,7 @@ uec_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) if (!phydev) return -ENODEV; - return phy_ethtool_sset(phydev, ecmd); + return phy_ethtool_ksettings_set(phydev, cmd); } static void @@ -392,8 +389,6 @@ static int uec_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) #endif /* CONFIG_PM */ static const struct ethtool_ops uec_ethtool_ops = { - .get_settings = uec_get_settings, - .set_settings = uec_set_settings, .get_drvinfo = uec_get_drvinfo, .get_regs_len = uec_get_regs_len, .get_regs = uec_get_regs, @@ -411,6 +406,8 @@ static const struct ethtool_ops uec_ethtool_ops = { .get_wol = uec_get_wol, .set_wol = uec_set_wol, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = uec_get_ksettings, + .set_link_ksettings = uec_set_ksettings, }; void uec_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c index 678f5018d0be..399cfd217288 100644 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c @@ -746,7 +746,7 @@ static irqreturn_t fjn_interrupt(int dummy, void *dev_id) lp->sent = lp->tx_queue ; lp->tx_queue = 0; lp->tx_queue_len = 0; - dev->trans_start = jiffies; + netif_trans_update(dev); } else { lp->tx_started = 0; } diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index e51892d518ff..b9f2ea59308a 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -636,7 +636,7 @@ static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev) pos = dma_ring_incr(pos, TX_DESC_NUM); writel_relaxed(dma_byte(pos), priv->base + TX_BQ_WR_ADDR); - dev->trans_start = jiffies; + netif_trans_update(dev); dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; netdev_sent_queue(dev, skb->len); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index e47aff250b15..e621636e69b9 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1275,7 +1275,7 @@ void hns_nic_net_reinit(struct net_device *netdev) { struct hns_nic_priv *priv = netdev_priv(netdev); - priv->netdev->trans_start = jiffies; + netif_trans_update(priv->netdev); while (test_and_set_bit(NIC_STATE_REINITING, &priv->state)) usleep_range(1000, 2000); @@ -1376,7 +1376,7 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, ret = hns_nic_net_xmit_hw(ndev, skb, &tx_ring_data(priv, skb->queue_mapping)); if (ret == NETDEV_TX_OK) { - ndev->trans_start = jiffies; + netif_trans_update(ndev); ndev->stats.tx_bytes += skb->len; ndev->stats.tx_packets++; } @@ -1648,7 +1648,7 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv) rtnl_lock(); /* put off any impending NetWatchDogTimeout */ - priv->netdev->trans_start = jiffies; + netif_trans_update(priv->netdev); if (type == HNAE_PORT_DEBUG) { hns_nic_net_reinit(priv->netdev); diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index 3daf2d4a7ca0..631dbc7b4dbb 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -1102,7 +1102,7 @@ static int hp100_open(struct net_device *dev) return -EAGAIN; } - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_start_queue(dev); lp->lan_type = hp100_sense_lan(dev); diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c index 7ce6379fd1a3..befb4ac3e2b0 100644 --- a/drivers/net/ethernet/i825xx/82596.c +++ b/drivers/net/ethernet/i825xx/82596.c @@ -1042,7 +1042,7 @@ static void i596_tx_timeout (struct net_device *dev) lp->last_restart = dev->stats.tx_packets; } - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue (dev); } diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c index c984998b34a0..3dbc53c21baa 100644 --- a/drivers/net/ethernet/i825xx/lib82596.c +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -960,7 +960,7 @@ static void i596_tx_timeout (struct net_device *dev) lp->last_restart = dev->stats.tx_packets; } - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue (dev); } diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c index 353f57f675d0..21c84cc9c871 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.c +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -983,7 +983,7 @@ static void sun3_82586_timeout(struct net_device *dev) p->scb->cmd_cuc = CUC_START; sun3_attn586(); WAIT_4_SCB_CMD(); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ return 0; } #endif @@ -996,7 +996,7 @@ static void sun3_82586_timeout(struct net_device *dev) sun3_82586_close(dev); sun3_82586_open(dev); } - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } /****************************************************** diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 2a0dc127df3f..54efa9a5167b 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -1169,16 +1169,15 @@ static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe) ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe); portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe); port = ehea_get_port(adapter, portnum); + if (!port) { + netdev_err(NULL, "unknown portnum %x\n", portnum); + return; + } dev = port->netdev; switch (ec) { case EHEA_EC_PORTSTATE_CHG: /* port state change */ - if (!port) { - netdev_err(dev, "unknown portnum %x\n", portnum); - break; - } - if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) { if (!netif_carrier_ok(dev)) { ret = ehea_sense_port_attr(port); diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 5d7db6c01c46..4c9771d57d6e 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -301,7 +301,7 @@ static inline void emac_netif_stop(struct emac_instance *dev) dev->no_mcast = 1; netif_addr_unlock(dev->ndev); netif_tx_unlock_bh(dev->ndev); - dev->ndev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev->ndev); /* prevent tx timeout */ mal_poll_disable(dev->mal, &dev->commac); netif_tx_disable(dev->ndev); } @@ -1377,7 +1377,7 @@ static inline int emac_xmit_finish(struct emac_instance *dev, int len) DBG2(dev, "stopped TX queue" NL); } - ndev->trans_start = jiffies; + netif_trans_update(ndev); ++dev->stats.tx_packets; dev->stats.tx_bytes += len; diff --git a/drivers/net/ethernet/ibm/emac/phy.c b/drivers/net/ethernet/ibm/emac/phy.c index d3b9d103353e..5b88cc690c22 100644 --- a/drivers/net/ethernet/ibm/emac/phy.c +++ b/drivers/net/ethernet/ibm/emac/phy.c @@ -470,12 +470,38 @@ static struct mii_phy_def m88e1112_phy_def = { .ops = &m88e1112_phy_ops, }; +static int ar8035_init(struct mii_phy *phy) +{ + phy_write(phy, 0x1d, 0x5); /* Address debug register 5 */ + phy_write(phy, 0x1e, 0x2d47); /* Value copied from u-boot */ + phy_write(phy, 0x1d, 0xb); /* Address hib ctrl */ + phy_write(phy, 0x1e, 0xbc20); /* Value copied from u-boot */ + + return 0; +} + +static struct mii_phy_ops ar8035_phy_ops = { + .init = ar8035_init, + .setup_aneg = genmii_setup_aneg, + .setup_forced = genmii_setup_forced, + .poll_link = genmii_poll_link, + .read_link = genmii_read_link, +}; + +static struct mii_phy_def ar8035_phy_def = { + .phy_id = 0x004dd070, + .phy_id_mask = 0xfffffff0, + .name = "Atheros 8035 Gigabit Ethernet", + .ops = &ar8035_phy_ops, +}; + static struct mii_phy_def *mii_phy_table[] = { &et1011c_phy_def, &cis8201_phy_def, &bcm5248_phy_def, &m88e1111_phy_def, &m88e1112_phy_def, + &ar8035_phy_def, &genmii_phy_def, NULL }; diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c index 2af603f3e418..cd391376036c 100644 --- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c +++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c @@ -121,7 +121,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = BIT(size); return 0; } @@ -845,27 +845,27 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) /* Transmit Descriptor Control 0 */ reg = er32(TXDCTL(0)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(0), reg); /* Transmit Descriptor Control 1 */ reg = er32(TXDCTL(1)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(1), reg); /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); reg &= ~(0xF << 27); /* 30:27 */ if (hw->phy.media_type != e1000_media_type_copper) - reg &= ~(1 << 20); + reg &= ~BIT(20); ew32(TARC(0), reg); /* Transmit Arbitration Control 1 */ reg = er32(TARC(1)); if (er32(TCTL) & E1000_TCTL_MULR) - reg &= ~(1 << 28); + reg &= ~BIT(28); else - reg |= (1 << 28); + reg |= BIT(28); ew32(TARC(1), reg); /* Disable IPv6 extension header parsing because some malformed diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index 5f7016442ec4..7fd4d54599e4 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -185,7 +185,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = BIT(size); break; } @@ -1163,12 +1163,12 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) /* Transmit Descriptor Control 0 */ reg = er32(TXDCTL(0)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(0), reg); /* Transmit Descriptor Control 1 */ reg = er32(TXDCTL(1)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(1), reg); /* Transmit Arbitration Control 0 */ @@ -1177,11 +1177,11 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82571: case e1000_82572: - reg |= (1 << 23) | (1 << 24) | (1 << 25) | (1 << 26); + reg |= BIT(23) | BIT(24) | BIT(25) | BIT(26); break; case e1000_82574: case e1000_82583: - reg |= (1 << 26); + reg |= BIT(26); break; default: break; @@ -1193,12 +1193,12 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82571: case e1000_82572: - reg &= ~((1 << 29) | (1 << 30)); - reg |= (1 << 22) | (1 << 24) | (1 << 25) | (1 << 26); + reg &= ~(BIT(29) | BIT(30)); + reg |= BIT(22) | BIT(24) | BIT(25) | BIT(26); if (er32(TCTL) & E1000_TCTL_MULR) - reg &= ~(1 << 28); + reg &= ~BIT(28); else - reg |= (1 << 28); + reg |= BIT(28); ew32(TARC(1), reg); break; default: @@ -1211,7 +1211,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) case e1000_82574: case e1000_82583: reg = er32(CTRL); - reg &= ~(1 << 29); + reg &= ~BIT(29); ew32(CTRL, reg); break; default: @@ -1224,8 +1224,8 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) case e1000_82574: case e1000_82583: reg = er32(CTRL_EXT); - reg &= ~(1 << 23); - reg |= (1 << 22); + reg &= ~BIT(23); + reg |= BIT(22); ew32(CTRL_EXT, reg); break; default: @@ -1261,7 +1261,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) case e1000_82574: case e1000_82583: reg = er32(GCR); - reg |= (1 << 22); + reg |= BIT(22); ew32(GCR, reg); /* Workaround for hardware errata. @@ -1308,8 +1308,8 @@ static void e1000_clear_vfta_82571(struct e1000_hw *hw) E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK; vfta_bit_in_reg = - 1 << (hw->mng_cookie.vlan_id & - E1000_VFTA_ENTRY_BIT_SHIFT_MASK); + BIT(hw->mng_cookie.vlan_id & + E1000_VFTA_ENTRY_BIT_SHIFT_MASK); } break; default: diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 52eb641fc9dc..ef96cd11d6d2 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -109,18 +109,18 @@ struct e1000_info; #define E1000_TXDCTL_DMA_BURST_ENABLE \ (E1000_TXDCTL_GRAN | /* set descriptor granularity */ \ E1000_TXDCTL_COUNT_DESC | \ - (1 << 16) | /* wthresh must be +1 more than desired */\ - (1 << 8) | /* hthresh */ \ - 0x1f) /* pthresh */ + (1u << 16) | /* wthresh must be +1 more than desired */\ + (1u << 8) | /* hthresh */ \ + 0x1f) /* pthresh */ #define E1000_RXDCTL_DMA_BURST_ENABLE \ (0x01000000 | /* set descriptor granularity */ \ - (4 << 16) | /* set writeback threshold */ \ - (4 << 8) | /* set prefetch threshold */ \ + (4u << 16) | /* set writeback threshold */ \ + (4u << 8) | /* set prefetch threshold */ \ 0x20) /* set hthresh */ -#define E1000_TIDV_FPD (1 << 31) -#define E1000_RDTR_FPD (1 << 31) +#define E1000_TIDV_FPD BIT(31) +#define E1000_RDTR_FPD BIT(31) enum e1000_boards { board_82571, @@ -347,6 +347,7 @@ struct e1000_adapter { struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; struct pm_qos_request pm_qos_req; + s32 ptp_delta; u16 eee_advert; }; @@ -404,53 +405,53 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca); #define E1000_82574_SYSTIM_EPSILON (1ULL << 35ULL) /* hardware capability, feature, and workaround flags */ -#define FLAG_HAS_AMT (1 << 0) -#define FLAG_HAS_FLASH (1 << 1) -#define FLAG_HAS_HW_VLAN_FILTER (1 << 2) -#define FLAG_HAS_WOL (1 << 3) -/* reserved bit4 */ -#define FLAG_HAS_CTRLEXT_ON_LOAD (1 << 5) -#define FLAG_HAS_SWSM_ON_LOAD (1 << 6) -#define FLAG_HAS_JUMBO_FRAMES (1 << 7) -#define FLAG_READ_ONLY_NVM (1 << 8) -#define FLAG_IS_ICH (1 << 9) -#define FLAG_HAS_MSIX (1 << 10) -#define FLAG_HAS_SMART_POWER_DOWN (1 << 11) -#define FLAG_IS_QUAD_PORT_A (1 << 12) -#define FLAG_IS_QUAD_PORT (1 << 13) -#define FLAG_HAS_HW_TIMESTAMP (1 << 14) -#define FLAG_APME_IN_WUC (1 << 15) -#define FLAG_APME_IN_CTRL3 (1 << 16) -#define FLAG_APME_CHECK_PORT_B (1 << 17) -#define FLAG_DISABLE_FC_PAUSE_TIME (1 << 18) -#define FLAG_NO_WAKE_UCAST (1 << 19) -#define FLAG_MNG_PT_ENABLED (1 << 20) -#define FLAG_RESET_OVERWRITES_LAA (1 << 21) -#define FLAG_TARC_SPEED_MODE_BIT (1 << 22) -#define FLAG_TARC_SET_BIT_ZERO (1 << 23) -#define FLAG_RX_NEEDS_RESTART (1 << 24) -#define FLAG_LSC_GIG_SPEED_DROP (1 << 25) -#define FLAG_SMART_POWER_DOWN (1 << 26) -#define FLAG_MSI_ENABLED (1 << 27) -/* reserved (1 << 28) */ -#define FLAG_TSO_FORCE (1 << 29) -#define FLAG_RESTART_NOW (1 << 30) -#define FLAG_MSI_TEST_FAILED (1 << 31) - -#define FLAG2_CRC_STRIPPING (1 << 0) -#define FLAG2_HAS_PHY_WAKEUP (1 << 1) -#define FLAG2_IS_DISCARDING (1 << 2) -#define FLAG2_DISABLE_ASPM_L1 (1 << 3) -#define FLAG2_HAS_PHY_STATS (1 << 4) -#define FLAG2_HAS_EEE (1 << 5) -#define FLAG2_DMA_BURST (1 << 6) -#define FLAG2_DISABLE_ASPM_L0S (1 << 7) -#define FLAG2_DISABLE_AIM (1 << 8) -#define FLAG2_CHECK_PHY_HANG (1 << 9) -#define FLAG2_NO_DISABLE_RX (1 << 10) -#define FLAG2_PCIM2PCI_ARBITER_WA (1 << 11) -#define FLAG2_DFLT_CRC_STRIPPING (1 << 12) -#define FLAG2_CHECK_RX_HWTSTAMP (1 << 13) +#define FLAG_HAS_AMT BIT(0) +#define FLAG_HAS_FLASH BIT(1) +#define FLAG_HAS_HW_VLAN_FILTER BIT(2) +#define FLAG_HAS_WOL BIT(3) +/* reserved BIT(4) */ +#define FLAG_HAS_CTRLEXT_ON_LOAD BIT(5) +#define FLAG_HAS_SWSM_ON_LOAD BIT(6) +#define FLAG_HAS_JUMBO_FRAMES BIT(7) +#define FLAG_READ_ONLY_NVM BIT(8) +#define FLAG_IS_ICH BIT(9) +#define FLAG_HAS_MSIX BIT(10) +#define FLAG_HAS_SMART_POWER_DOWN BIT(11) +#define FLAG_IS_QUAD_PORT_A BIT(12) +#define FLAG_IS_QUAD_PORT BIT(13) +#define FLAG_HAS_HW_TIMESTAMP BIT(14) +#define FLAG_APME_IN_WUC BIT(15) +#define FLAG_APME_IN_CTRL3 BIT(16) +#define FLAG_APME_CHECK_PORT_B BIT(17) +#define FLAG_DISABLE_FC_PAUSE_TIME BIT(18) +#define FLAG_NO_WAKE_UCAST BIT(19) +#define FLAG_MNG_PT_ENABLED BIT(20) +#define FLAG_RESET_OVERWRITES_LAA BIT(21) +#define FLAG_TARC_SPEED_MODE_BIT BIT(22) +#define FLAG_TARC_SET_BIT_ZERO BIT(23) +#define FLAG_RX_NEEDS_RESTART BIT(24) +#define FLAG_LSC_GIG_SPEED_DROP BIT(25) +#define FLAG_SMART_POWER_DOWN BIT(26) +#define FLAG_MSI_ENABLED BIT(27) +/* reserved BIT(28) */ +#define FLAG_TSO_FORCE BIT(29) +#define FLAG_RESTART_NOW BIT(30) +#define FLAG_MSI_TEST_FAILED BIT(31) + +#define FLAG2_CRC_STRIPPING BIT(0) +#define FLAG2_HAS_PHY_WAKEUP BIT(1) +#define FLAG2_IS_DISCARDING BIT(2) +#define FLAG2_DISABLE_ASPM_L1 BIT(3) +#define FLAG2_HAS_PHY_STATS BIT(4) +#define FLAG2_HAS_EEE BIT(5) +#define FLAG2_DMA_BURST BIT(6) +#define FLAG2_DISABLE_ASPM_L0S BIT(7) +#define FLAG2_DISABLE_AIM BIT(8) +#define FLAG2_CHECK_PHY_HANG BIT(9) +#define FLAG2_NO_DISABLE_RX BIT(10) +#define FLAG2_PCIM2PCI_ARBITER_WA BIT(11) +#define FLAG2_DFLT_CRC_STRIPPING BIT(12) +#define FLAG2_CHECK_RX_HWTSTAMP BIT(13) #define E1000_RX_DESC_PS(R, i) \ (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 1e3973aa707c..7aff68a4a4df 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -201,6 +201,9 @@ static int e1000_get_settings(struct net_device *netdev, else ecmd->eth_tp_mdix_ctrl = hw->phy.mdix; + if (hw->phy.media_type != e1000_media_type_copper) + ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + return 0; } @@ -236,8 +239,13 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) mac->forced_speed_duplex = ADVERTISE_100_FULL; break; case SPEED_1000 + DUPLEX_FULL: - mac->autoneg = 1; - adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; + if (adapter->hw.phy.media_type == e1000_media_type_copper) { + mac->autoneg = 1; + adapter->hw.phy.autoneg_advertised = + ADVERTISE_1000_FULL; + } else { + mac->forced_speed_duplex = ADVERTISE_1000_FULL; + } break; case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: @@ -439,8 +447,9 @@ static void e1000_get_regs(struct net_device *netdev, memset(p, 0, E1000_REGS_LEN * sizeof(u32)); - regs->version = (1 << 24) | (adapter->pdev->revision << 16) | - adapter->pdev->device; + regs->version = (1u << 24) | + (adapter->pdev->revision << 16) | + adapter->pdev->device; regs_buff[0] = er32(CTRL); regs_buff[1] = er32(STATUS); @@ -895,7 +904,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: - mask |= (1 << 18); + mask |= BIT(18); break; default: break; @@ -914,9 +923,9 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) /* SHRAH[9] different than the others */ if (i == 10) - mask |= (1 << 30); + mask |= BIT(30); else - mask &= ~(1 << 30); + mask &= ~BIT(30); } if (mac->type == e1000_pch2lan) { /* SHRAH[0,1,2] different than previous */ @@ -924,7 +933,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) mask &= 0xFFF4FFFF; /* SHRAH[3] different than SHRAH[0,1,2] */ if (i == 4) - mask |= (1 << 30); + mask |= BIT(30); /* RAR[1-6] owned by management engine - skipping */ if (i > 0) i += 6; @@ -1019,7 +1028,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) /* Test each interrupt */ for (i = 0; i < 10; i++) { /* Interrupt to test */ - mask = 1 << i; + mask = BIT(i); if (adapter->flags & FLAG_IS_ICH) { switch (mask) { @@ -1387,7 +1396,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) case e1000_phy_82579: /* Disable PHY energy detect power down */ e1e_rphy(hw, PHY_REG(0, 21), &phy_reg); - e1e_wphy(hw, PHY_REG(0, 21), phy_reg & ~(1 << 3)); + e1e_wphy(hw, PHY_REG(0, 21), phy_reg & ~BIT(3)); /* Disable full chip energy detect */ e1e_rphy(hw, PHY_REG(776, 18), &phy_reg); e1e_wphy(hw, PHY_REG(776, 18), phy_reg | 1); @@ -1453,7 +1462,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter) /* disable autoneg */ ctrl = er32(TXCW); - ctrl &= ~(1 << 31); + ctrl &= ~BIT(31); ew32(TXCW, ctrl); link = (er32(STATUS) & E1000_STATUS_LU); @@ -2283,19 +2292,19 @@ static int e1000e_get_ts_info(struct net_device *netdev, SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE); - info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); - - info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) | - (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | - (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_ALL)); + info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON); + + info->rx_filters = (BIT(HWTSTAMP_FILTER_NONE) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | + BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_ALL)); if (adapter->ptp_clock) info->phc_index = ptp_clock_index(adapter->ptp_clock); diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index c0f4887ea44d..3e11322d8d58 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -1048,7 +1048,7 @@ static s32 e1000_platform_pm_pch_lpt(struct e1000_hw *hw, bool link) while (value > PCI_LTR_VALUE_MASK) { scale++; - value = DIV_ROUND_UP(value, (1 << 5)); + value = DIV_ROUND_UP(value, BIT(5)); } if (scale > E1000_LTRV_SCALE_MAX) { e_dbg("Invalid LTR latency scale %d\n", scale); @@ -1573,7 +1573,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) phy_reg &= ~HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK; if ((er32(STATUS) & E1000_STATUS_FD) != E1000_STATUS_FD) - phy_reg |= (1 << HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT); + phy_reg |= BIT(HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT); e1e_wphy(hw, HV_KMRN_FIFO_CTRLSTA, phy_reg); break; @@ -2044,9 +2044,9 @@ static s32 e1000_write_smbus_addr(struct e1000_hw *hw) /* Restore SMBus frequency */ if (freq--) { phy_data &= ~HV_SMB_ADDR_FREQ_MASK; - phy_data |= (freq & (1 << 0)) << + phy_data |= (freq & BIT(0)) << HV_SMB_ADDR_FREQ_LOW_SHIFT; - phy_data |= (freq & (1 << 1)) << + phy_data |= (freq & BIT(1)) << (HV_SMB_ADDR_FREQ_HIGH_SHIFT - 1); } else { e_dbg("Unsupported SMB frequency in PHY\n"); @@ -2530,7 +2530,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) /* disable Rx path while enabling/disabling workaround */ e1e_rphy(hw, PHY_REG(769, 20), &phy_reg); - ret_val = e1e_wphy(hw, PHY_REG(769, 20), phy_reg | (1 << 14)); + ret_val = e1e_wphy(hw, PHY_REG(769, 20), phy_reg | BIT(14)); if (ret_val) return ret_val; @@ -2561,7 +2561,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) /* Enable jumbo frame workaround in the MAC */ mac_reg = er32(FFLT_DBG); - mac_reg &= ~(1 << 14); + mac_reg &= ~BIT(14); mac_reg |= (7 << 15); ew32(FFLT_DBG, mac_reg); @@ -2576,7 +2576,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) return ret_val; ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_CTRL_OFFSET, - data | (1 << 0)); + data | BIT(0)); if (ret_val) return ret_val; ret_val = e1000e_read_kmrn_reg(hw, @@ -2600,7 +2600,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) if (ret_val) return ret_val; e1e_rphy(hw, PHY_REG(769, 16), &data); - data &= ~(1 << 13); + data &= ~BIT(13); ret_val = e1e_wphy(hw, PHY_REG(769, 16), data); if (ret_val) return ret_val; @@ -2614,7 +2614,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) if (ret_val) return ret_val; e1e_rphy(hw, HV_PM_CTRL, &data); - ret_val = e1e_wphy(hw, HV_PM_CTRL, data | (1 << 10)); + ret_val = e1e_wphy(hw, HV_PM_CTRL, data | BIT(10)); if (ret_val) return ret_val; } else { @@ -2634,7 +2634,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) return ret_val; ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_CTRL_OFFSET, - data & ~(1 << 0)); + data & ~BIT(0)); if (ret_val) return ret_val; ret_val = e1000e_read_kmrn_reg(hw, @@ -2657,7 +2657,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) if (ret_val) return ret_val; e1e_rphy(hw, PHY_REG(769, 16), &data); - data |= (1 << 13); + data |= BIT(13); ret_val = e1e_wphy(hw, PHY_REG(769, 16), data); if (ret_val) return ret_val; @@ -2671,13 +2671,13 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) if (ret_val) return ret_val; e1e_rphy(hw, HV_PM_CTRL, &data); - ret_val = e1e_wphy(hw, HV_PM_CTRL, data & ~(1 << 10)); + ret_val = e1e_wphy(hw, HV_PM_CTRL, data & ~BIT(10)); if (ret_val) return ret_val; } /* re-enable Rx path after enabling/disabling workaround */ - return e1e_wphy(hw, PHY_REG(769, 20), phy_reg & ~(1 << 14)); + return e1e_wphy(hw, PHY_REG(769, 20), phy_reg & ~BIT(14)); } /** @@ -4841,7 +4841,7 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) /* Extended Device Control */ reg = er32(CTRL_EXT); - reg |= (1 << 22); + reg |= BIT(22); /* Enable PHY low-power state when MAC is at D3 w/o WoL */ if (hw->mac.type >= e1000_pchlan) reg |= E1000_CTRL_EXT_PHYPDEN; @@ -4849,34 +4849,34 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) /* Transmit Descriptor Control 0 */ reg = er32(TXDCTL(0)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(0), reg); /* Transmit Descriptor Control 1 */ reg = er32(TXDCTL(1)); - reg |= (1 << 22); + reg |= BIT(22); ew32(TXDCTL(1), reg); /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); if (hw->mac.type == e1000_ich8lan) - reg |= (1 << 28) | (1 << 29); - reg |= (1 << 23) | (1 << 24) | (1 << 26) | (1 << 27); + reg |= BIT(28) | BIT(29); + reg |= BIT(23) | BIT(24) | BIT(26) | BIT(27); ew32(TARC(0), reg); /* Transmit Arbitration Control 1 */ reg = er32(TARC(1)); if (er32(TCTL) & E1000_TCTL_MULR) - reg &= ~(1 << 28); + reg &= ~BIT(28); else - reg |= (1 << 28); - reg |= (1 << 24) | (1 << 26) | (1 << 30); + reg |= BIT(28); + reg |= BIT(24) | BIT(26) | BIT(30); ew32(TARC(1), reg); /* Device Status */ if (hw->mac.type == e1000_ich8lan) { reg = er32(STATUS); - reg &= ~(1 << 31); + reg &= ~BIT(31); ew32(STATUS, reg); } diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.h b/drivers/net/ethernet/intel/e1000e/ich8lan.h index 2311f6003f58..67163ca898ba 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.h +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.h @@ -73,10 +73,10 @@ (ID_LED_OFF1_ON2 << 4) | \ (ID_LED_DEF1_DEF2)) -#define E1000_ICH_NVM_SIG_WORD 0x13 -#define E1000_ICH_NVM_SIG_MASK 0xC000 -#define E1000_ICH_NVM_VALID_SIG_MASK 0xC0 -#define E1000_ICH_NVM_SIG_VALUE 0x80 +#define E1000_ICH_NVM_SIG_WORD 0x13u +#define E1000_ICH_NVM_SIG_MASK 0xC000u +#define E1000_ICH_NVM_VALID_SIG_MASK 0xC0u +#define E1000_ICH_NVM_SIG_VALUE 0x80u #define E1000_ICH8_LAN_INIT_TIMEOUT 1500 diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index e59d7c283cd4..b322011ec282 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -346,7 +346,7 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw, hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); hash_bit = hash_value & 0x1F; - hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + hw->mac.mta_shadow[hash_reg] |= BIT(hash_bit); mc_addr_list += (ETH_ALEN); } diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index a7f16c35ebcd..75e60897b7e7 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -242,7 +242,7 @@ static void e1000e_dump(struct e1000_adapter *adapter) dev_info(&adapter->pdev->dev, "Net device Info\n"); pr_info("Device Name state trans_start last_rx\n"); pr_info("%-15s %016lX %016lX %016lX\n", netdev->name, - netdev->state, netdev->trans_start, netdev->last_rx); + netdev->state, dev_trans_start(netdev), netdev->last_rx); } /* Print Registers */ @@ -317,8 +317,8 @@ static void e1000e_dump(struct e1000_adapter *adapter) else next_desc = ""; pr_info("T%c[0x%03X] %016llX %016llX %016llX %04X %3X %016llX %p%s\n", - (!(le64_to_cpu(u0->b) & (1 << 29)) ? 'l' : - ((le64_to_cpu(u0->b) & (1 << 20)) ? 'd' : 'c')), + (!(le64_to_cpu(u0->b) & BIT(29)) ? 'l' : + ((le64_to_cpu(u0->b) & BIT(20)) ? 'd' : 'c')), i, (unsigned long long)le64_to_cpu(u0->a), (unsigned long long)le64_to_cpu(u0->b), @@ -2018,7 +2018,7 @@ static void e1000_configure_msix(struct e1000_adapter *adapter) adapter->eiac_mask |= E1000_IMS_OTHER; /* Cause Tx interrupts on every write back */ - ivar |= (1 << 31); + ivar |= BIT(31); ew32(IVAR, ivar); @@ -2709,7 +2709,7 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev, if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) { index = (vid >> 5) & 0x7F; vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); - vfta |= (1 << (vid & 0x1F)); + vfta |= BIT((vid & 0x1F)); hw->mac.ops.write_vfta(hw, index, vfta); } @@ -2737,7 +2737,7 @@ static int e1000_vlan_rx_kill_vid(struct net_device *netdev, if (adapter->flags & FLAG_HAS_HW_VLAN_FILTER) { index = (vid >> 5) & 0x7F; vfta = E1000_READ_REG_ARRAY(hw, E1000_VFTA, index); - vfta &= ~(1 << (vid & 0x1F)); + vfta &= ~BIT((vid & 0x1F)); hw->mac.ops.write_vfta(hw, index, vfta); } @@ -2878,7 +2878,7 @@ static void e1000_init_manageability_pt(struct e1000_adapter *adapter) /* Enable this decision filter in MANC2H */ if (mdef) - manc2h |= (1 << i); + manc2h |= BIT(i); j |= mdef; } @@ -2891,7 +2891,7 @@ static void e1000_init_manageability_pt(struct e1000_adapter *adapter) if (er32(MDEF(i)) == 0) { ew32(MDEF(i), (E1000_MDEF_PORT_623 | E1000_MDEF_PORT_664)); - manc2h |= (1 << 1); + manc2h |= BIT(1); j++; break; } @@ -2971,7 +2971,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) /* set the speed mode bit, we'll clear it if we're not at * gigabit link later */ -#define SPEED_MODE_BIT (1 << 21) +#define SPEED_MODE_BIT BIT(21) tarc |= SPEED_MODE_BIT; ew32(TARC(0), tarc); } @@ -3071,12 +3071,12 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) e1e_rphy(hw, PHY_REG(770, 26), &phy_data); phy_data &= 0xfff8; - phy_data |= (1 << 2); + phy_data |= BIT(2); e1e_wphy(hw, PHY_REG(770, 26), phy_data); e1e_rphy(hw, 22, &phy_data); phy_data &= 0x0fff; - phy_data |= (1 << 14); + phy_data |= BIT(14); e1e_wphy(hw, 0x10, 0x2823); e1e_wphy(hw, 0x11, 0x0003); e1e_wphy(hw, 22, phy_data); @@ -3368,12 +3368,12 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev) * combining */ netdev_for_each_uc_addr(ha, netdev) { - int rval; + int ret_val; if (!rar_entries) break; - rval = hw->mac.ops.rar_set(hw, ha->addr, rar_entries--); - if (rval < 0) + ret_val = hw->mac.ops.rar_set(hw, ha->addr, rar_entries--); + if (ret_val < 0) return -ENOMEM; count++; } @@ -3503,8 +3503,8 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) !(er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_ENABLED)) { u32 fextnvm7 = er32(FEXTNVM7); - if (!(fextnvm7 & (1 << 0))) { - ew32(FEXTNVM7, fextnvm7 | (1 << 0)); + if (!(fextnvm7 & BIT(0))) { + ew32(FEXTNVM7, fextnvm7 | BIT(0)); e1e_flush(); } } @@ -3580,7 +3580,6 @@ static int e1000e_config_hwtstamp(struct e1000_adapter *adapter, bool is_l4 = false; bool is_l2 = false; u32 regval; - s32 ret_val; if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP)) return -EINVAL; @@ -3719,16 +3718,6 @@ static int e1000e_config_hwtstamp(struct e1000_adapter *adapter, er32(RXSTMPH); er32(TXSTMPH); - /* Get and set the System Time Register SYSTIM base frequency */ - ret_val = e1000e_get_base_timinca(adapter, ®val); - if (ret_val) - return ret_val; - ew32(TIMINCA, regval); - - /* reset the ns time counter */ - timecounter_init(&adapter->tc, &adapter->cc, - ktime_to_ns(ktime_get_real())); - return 0; } @@ -3839,7 +3828,7 @@ static void e1000_flush_rx_ring(struct e1000_adapter *adapter) /* update thresholds: prefetch threshold to 31, host threshold to 1 * and make sure the granularity is "descriptors" and not "cache lines" */ - rxdctl |= (0x1F | (1 << 8) | E1000_RXDCTL_THRESH_UNIT_DESC); + rxdctl |= (0x1F | BIT(8) | E1000_RXDCTL_THRESH_UNIT_DESC); ew32(RXDCTL(0), rxdctl); /* momentarily enable the RX ring for the changes to take effect */ @@ -3885,6 +3874,53 @@ static void e1000_flush_desc_rings(struct e1000_adapter *adapter) } /** + * e1000e_systim_reset - reset the timesync registers after a hardware reset + * @adapter: board private structure + * + * When the MAC is reset, all hardware bits for timesync will be reset to the + * default values. This function will restore the settings last in place. + * Since the clock SYSTIME registers are reset, we will simply restore the + * cyclecounter to the kernel real clock time. + **/ +static void e1000e_systim_reset(struct e1000_adapter *adapter) +{ + struct ptp_clock_info *info = &adapter->ptp_clock_info; + struct e1000_hw *hw = &adapter->hw; + unsigned long flags; + u32 timinca; + s32 ret_val; + + if (!(adapter->flags & FLAG_HAS_HW_TIMESTAMP)) + return; + + if (info->adjfreq) { + /* restore the previous ptp frequency delta */ + ret_val = info->adjfreq(info, adapter->ptp_delta); + } else { + /* set the default base frequency if no adjustment possible */ + ret_val = e1000e_get_base_timinca(adapter, &timinca); + if (!ret_val) + ew32(TIMINCA, timinca); + } + + if (ret_val) { + dev_warn(&adapter->pdev->dev, + "Failed to restore TIMINCA clock rate delta: %d\n", + ret_val); + return; + } + + /* reset the systim ns time counter */ + spin_lock_irqsave(&adapter->systim_lock, flags); + timecounter_init(&adapter->tc, &adapter->cc, + ktime_to_ns(ktime_get_real())); + spin_unlock_irqrestore(&adapter->systim_lock, flags); + + /* restore the previous hwtstamp configuration settings */ + e1000e_config_hwtstamp(adapter, &adapter->hwtstamp_config); +} + +/** * e1000e_reset - bring the hardware into a known good state * * This function boots the hardware and enables some settings that @@ -4063,8 +4099,8 @@ void e1000e_reset(struct e1000_adapter *adapter) e1000e_reset_adaptive(hw); - /* initialize systim and reset the ns time counter */ - e1000e_config_hwtstamp(adapter, &adapter->hwtstamp_config); + /* restore systim and hwtstamp settings */ + e1000e_systim_reset(adapter); /* Set EEE advertisement as appropriate */ if (adapter->flags2 & FLAG2_HAS_EEE) { @@ -4275,7 +4311,7 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc) struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter, cc); struct e1000_hw *hw = &adapter->hw; - u32 systimel_1, systimel_2, systimeh; + u32 systimel, systimeh; cycle_t systim, systim_next; /* SYSTIMH latching upon SYSTIML read does not work well. * This means that if SYSTIML overflows after we read it but before @@ -4283,24 +4319,25 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc) * will experience a huge non linear increment in the systime value * to fix that we test for overflow and if true, we re-read systime. */ - systimel_1 = er32(SYSTIML); + systimel = er32(SYSTIML); systimeh = er32(SYSTIMH); - systimel_2 = er32(SYSTIML); - /* Check for overflow. If there was no overflow, use the values */ - if (systimel_1 < systimel_2) { - systim = (cycle_t)systimel_1; - systim |= (cycle_t)systimeh << 32; - } else { - /* There was an overflow, read again SYSTIMH, and use - * systimel_2 - */ - systimeh = er32(SYSTIMH); - systim = (cycle_t)systimel_2; - systim |= (cycle_t)systimeh << 32; + /* Is systimel is so large that overflow is possible? */ + if (systimel >= (u32)0xffffffff - E1000_TIMINCA_INCVALUE_MASK) { + u32 systimel_2 = er32(SYSTIML); + if (systimel > systimel_2) { + /* There was an overflow, read again SYSTIMH, and use + * systimel_2 + */ + systimeh = er32(SYSTIMH); + systimel = systimel_2; + } } + systim = (cycle_t)systimel; + systim |= (cycle_t)systimeh << 32; if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) { - u64 incvalue, time_delta, rem, temp; + u64 time_delta, rem, temp; + u32 incvalue; int i; /* errata for 82574/82583 possible bad bits read from SYSTIMH/L @@ -6861,7 +6898,7 @@ static void e1000_eeprom_checks(struct e1000_adapter *adapter) ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &buf); le16_to_cpus(&buf); - if (!ret_val && (!(buf & (1 << 0)))) { + if (!ret_val && (!(buf & BIT(0)))) { /* Deep Smart Power Down (DSPD) */ dev_warn(&adapter->pdev->dev, "Warning: detected DSPD enabled in EEPROM\n"); @@ -6965,7 +7002,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int bars, i, err, pci_using_dac; u16 eeprom_data = 0; u16 eeprom_apme_mask = E1000_EEPROM_APME; - s32 rval = 0; + s32 ret_val = 0; if (ei->flags2 & FLAG2_DISABLE_ASPM_L0S) aspm_disable_flag = PCIE_LINK_STATE_L0S; @@ -7200,18 +7237,18 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } else if (adapter->flags & FLAG_APME_IN_CTRL3) { if (adapter->flags & FLAG_APME_CHECK_PORT_B && (adapter->hw.bus.func == 1)) - rval = e1000_read_nvm(&adapter->hw, + ret_val = e1000_read_nvm(&adapter->hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); else - rval = e1000_read_nvm(&adapter->hw, + ret_val = e1000_read_nvm(&adapter->hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); } /* fetch WoL from EEPROM */ - if (rval) - e_dbg("NVM read error getting WoL initial values: %d\n", rval); + if (ret_val) + e_dbg("NVM read error getting WoL initial values: %d\n", ret_val); else if (eeprom_data & eeprom_apme_mask) adapter->eeprom_wol |= E1000_WUFC_MAG; @@ -7231,13 +7268,16 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) device_wakeup_enable(&pdev->dev); /* save off EEPROM version number */ - rval = e1000_read_nvm(&adapter->hw, 5, 1, &adapter->eeprom_vers); + ret_val = e1000_read_nvm(&adapter->hw, 5, 1, &adapter->eeprom_vers); - if (rval) { - e_dbg("NVM read error getting EEPROM version: %d\n", rval); + if (ret_val) { + e_dbg("NVM read error getting EEPROM version: %d\n", ret_val); adapter->eeprom_vers = 0; } + /* init PTP hardware clock */ + e1000e_ptp_init(adapter); + /* reset the hardware with the new settings */ e1000e_reset(adapter); @@ -7256,9 +7296,6 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* carrier off reporting is important to ethtool even BEFORE open */ netif_carrier_off(netdev); - /* init PTP hardware clock */ - e1000e_ptp_init(adapter); - e1000_print_device_info(adapter); if (pci_dev_run_wake(pdev)) diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c index 49f205c023bf..2efd80dfd88e 100644 --- a/drivers/net/ethernet/intel/e1000e/nvm.c +++ b/drivers/net/ethernet/intel/e1000e/nvm.c @@ -67,7 +67,7 @@ static void e1000_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) u32 eecd = er32(EECD); u32 mask; - mask = 0x01 << (count - 1); + mask = BIT(count - 1); if (nvm->type == e1000_nvm_eeprom_spi) eecd |= E1000_EECD_DO; diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index de13aeacae97..d78d47b41a71 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -2894,11 +2894,11 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data, if ((hw->phy.type == e1000_phy_82578) && (hw->phy.revision >= 1) && (hw->phy.addr == 2) && - !(MAX_PHY_REG_ADDRESS & reg) && (data & (1 << 11))) { + !(MAX_PHY_REG_ADDRESS & reg) && (data & BIT(11))) { u16 data2 = 0x7EFF; ret_val = e1000_access_phy_debug_regs_hv(hw, - (1 << 6) | 0x3, + BIT(6) | 0x3, &data2, false); if (ret_val) goto out; diff --git a/drivers/net/ethernet/intel/e1000e/phy.h b/drivers/net/ethernet/intel/e1000e/phy.h index 55bfe473514d..3027f63ee793 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.h +++ b/drivers/net/ethernet/intel/e1000e/phy.h @@ -104,9 +104,9 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw); #define BM_WUC_DATA_OPCODE 0x12 #define BM_WUC_ENABLE_PAGE BM_PORT_CTRL_PAGE #define BM_WUC_ENABLE_REG 17 -#define BM_WUC_ENABLE_BIT (1 << 2) -#define BM_WUC_HOST_WU_BIT (1 << 4) -#define BM_WUC_ME_WU_BIT (1 << 5) +#define BM_WUC_ENABLE_BIT BIT(2) +#define BM_WUC_HOST_WU_BIT BIT(4) +#define BM_WUC_ME_WU_BIT BIT(5) #define PHY_UPPER_SHIFT 21 #define BM_PHY_REG(page, reg) \ @@ -124,8 +124,8 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw); #define I82578_ADDR_REG 29 #define I82577_ADDR_REG 16 #define I82577_CFG_REG 22 -#define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15) -#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift */ +#define I82577_CFG_ASSERT_CRS_ON_TX BIT(15) +#define I82577_CFG_ENABLE_DOWNSHIFT (3u << 10) /* auto downshift */ #define I82577_CTRL_REG 23 /* 82577 specific PHY registers */ diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index e2ff3ef75d5d..2e1b17ad52a3 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -79,6 +79,8 @@ static int e1000e_phc_adjfreq(struct ptp_clock_info *ptp, s32 delta) ew32(TIMINCA, timinca); + adapter->ptp_delta = delta; + spin_unlock_irqrestore(&adapter->systim_lock, flags); return 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 206a466999ed..e05aca9bef0e 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -145,7 +145,7 @@ static void fm10k_reinit(struct fm10k_intfc *interface) WARN_ON(in_interrupt()); /* put off any impending NetWatchDogTimeout */ - netdev->trans_start = jiffies; + netif_trans_update(netdev); while (test_and_set_bit(__FM10K_RESETTING, &interface->state)) usleep_range(1000, 2000); diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 00c473874f01..9c44739da5e2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -97,12 +97,12 @@ #define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16) /* Ethtool Private Flags */ -#define I40E_PRIV_FLAGS_NPAR_FLAG BIT(0) -#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) -#define I40E_PRIV_FLAGS_FD_ATR BIT(2) -#define I40E_PRIV_FLAGS_VEB_STATS BIT(3) -#define I40E_PRIV_FLAGS_PS BIT(4) -#define I40E_PRIV_FLAGS_HW_ATR_EVICT BIT(5) +#define I40E_PRIV_FLAGS_MFP_FLAG BIT(0) +#define I40E_PRIV_FLAGS_LINKPOLL_FLAG BIT(1) +#define I40E_PRIV_FLAGS_FD_ATR BIT(2) +#define I40E_PRIV_FLAGS_VEB_STATS BIT(3) +#define I40E_PRIV_FLAGS_HW_ATR_EVICT BIT(4) +#define I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT BIT(5) #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) @@ -112,7 +112,9 @@ #define I40E_OEM_VER_PATCH_MASK 0xff #define I40E_OEM_VER_BUILD_SHIFT 8 #define I40E_OEM_VER_SHIFT 24 -#define I40E_PHY_DEBUG_PORT BIT(4) +#define I40E_PHY_DEBUG_ALL \ + (I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW | \ + I40E_AQ_PHY_DEBUG_DISABLE_ALL_LINK_FW) /* The values in here are decimal coded as hex as is the case in the NVM map*/ #define I40E_CURRENT_NVM_VERSION_HI 0x2 @@ -123,10 +125,7 @@ #define XSTRINGIFY(bar) STRINGIFY(bar) #define I40E_RX_DESC(R, i) \ - ((ring_is_16byte_desc_enabled(R)) \ - ? (union i40e_32byte_rx_desc *) \ - (&(((union i40e_16byte_rx_desc *)((R)->desc))[i])) \ - : (&(((union i40e_32byte_rx_desc *)((R)->desc))[i]))) + (&(((union i40e_32byte_rx_desc *)((R)->desc))[i])) #define I40E_TX_DESC(R, i) \ (&(((struct i40e_tx_desc *)((R)->desc))[i])) #define I40E_TX_CTXTDESC(R, i) \ @@ -320,8 +319,6 @@ struct i40e_pf { #define I40E_FLAG_RX_CSUM_ENABLED BIT_ULL(1) #define I40E_FLAG_MSI_ENABLED BIT_ULL(2) #define I40E_FLAG_MSIX_ENABLED BIT_ULL(3) -#define I40E_FLAG_RX_1BUF_ENABLED BIT_ULL(4) -#define I40E_FLAG_RX_PS_ENABLED BIT_ULL(5) #define I40E_FLAG_RSS_ENABLED BIT_ULL(6) #define I40E_FLAG_VMDQ_ENABLED BIT_ULL(7) #define I40E_FLAG_FDIR_REQUIRES_REINIT BIT_ULL(8) @@ -330,7 +327,6 @@ struct i40e_pf { #ifdef I40E_FCOE #define I40E_FLAG_FCOE_ENABLED BIT_ULL(11) #endif /* I40E_FCOE */ -#define I40E_FLAG_16BYTE_RX_DESC_ENABLED BIT_ULL(13) #define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) #define I40E_FLAG_FILTER_SYNC BIT_ULL(15) #define I40E_FLAG_SERVICE_CLIENT_REQUESTED BIT_ULL(16) @@ -363,6 +359,7 @@ struct i40e_pf { #define I40E_FLAG_STOP_FW_LLDP BIT_ULL(47) #define I40E_FLAG_HAVE_10GBASET_PHY BIT_ULL(48) #define I40E_FLAG_PF_MAC BIT_ULL(50) +#define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; @@ -534,9 +531,7 @@ struct i40e_vsi { u8 *rss_lut_user; /* User configured lookup table entries */ u16 max_frame; - u16 rx_hdr_len; u16 rx_buf_len; - u8 dtype; /* List of q_vectors allocated to this VSI */ struct i40e_q_vector **q_vectors; diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index eacbe7430b48..11cf1a5ebccf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -1833,7 +1833,10 @@ struct i40e_aqc_set_phy_debug { #define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_NONE 0x00 #define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_HARD 0x01 #define I40E_AQ_PHY_DEBUG_RESET_EXTERNAL_SOFT 0x02 +/* Disable link manageability on a single port */ #define I40E_AQ_PHY_DEBUG_DISABLE_LINK_FW 0x10 +/* Disable link manageability on all ports */ +#define I40E_AQ_PHY_DEBUG_DISABLE_ALL_LINK_FW 0x20 u8 reserved[15]; }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.h b/drivers/net/ethernet/intel/i40e/i40e_client.h index bf6b453d93a1..a4601d97fb24 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.h +++ b/drivers/net/ethernet/intel/i40e/i40e_client.h @@ -217,7 +217,7 @@ struct i40e_client { #define I40E_CLIENT_FLAGS_LAUNCH_ON_PROBE BIT(0) #define I40E_TX_FLAGS_NOTIFY_OTHER_EVENTS BIT(2) enum i40e_client_type type; - struct i40e_client_ops *ops; /* client ops provided by the client */ + const struct i40e_client_ops *ops; /* client ops provided by the client */ }; static inline bool i40e_client_is_registered(struct i40e_client *client) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 4a934e14574d..422b41d61c9a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -696,7 +696,7 @@ struct i40e_rx_ptype_decoded i40e_ptype_lookup[] = { /* Non Tunneled IPv6 */ I40E_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3), I40E_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3), - I40E_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP, PAY3), + I40E_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP, PAY4), I40E_PTT_UNUSED_ENTRY(91), I40E_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP, PAY4), I40E_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4), @@ -1972,10 +1972,12 @@ aq_add_vsi_exit: * @seid: vsi number * @set: set unicast promiscuous enable/disable * @cmd_details: pointer to command details structure or NULL + * @rx_only_promisc: flag to decide if egress traffic gets mirrored in promisc **/ i40e_status i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw, u16 seid, bool set, - struct i40e_asq_cmd_details *cmd_details) + struct i40e_asq_cmd_details *cmd_details, + bool rx_only_promisc) { struct i40e_aq_desc desc; struct i40e_aqc_set_vsi_promiscuous_modes *cmd = @@ -1988,8 +1990,9 @@ i40e_status i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw, if (set) { flags |= I40E_AQC_SET_VSI_PROMISC_UNICAST; - if (((hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver >= 5)) || - (hw->aq.api_maj_ver > 1)) + if (rx_only_promisc && + (((hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver >= 5)) || + (hw->aq.api_maj_ver > 1))) flags |= I40E_AQC_SET_VSI_PROMISC_TX; } @@ -2283,6 +2286,35 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw, } /** + * i40e_aq_set_switch_config + * @hw: pointer to the hardware structure + * @flags: bit flag values to set + * @valid_flags: which bit flags to set + * @cmd_details: pointer to command details structure or NULL + * + * Set switch configuration bits + **/ +enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw, + u16 flags, + u16 valid_flags, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_set_switch_config *scfg = + (struct i40e_aqc_set_switch_config *)&desc.params.raw; + enum i40e_status_code status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_set_switch_config); + scfg->flags = cpu_to_le16(flags); + scfg->valid_flags = cpu_to_le16(valid_flags); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + return status; +} + +/** * i40e_aq_get_firmware_version * @hw: pointer to the hw struct * @fw_major_version: firmware major version diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 83dccf1792e7..e6af8c8d7019 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -268,13 +268,11 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) rx_ring->queue_index, rx_ring->reg_idx); dev_info(&pf->pdev->dev, - " rx_rings[%i]: rx_hdr_len = %d, rx_buf_len = %d, dtype = %d\n", - i, rx_ring->rx_hdr_len, - rx_ring->rx_buf_len, - rx_ring->dtype); + " rx_rings[%i]: rx_buf_len = %d\n", + i, rx_ring->rx_buf_len); dev_info(&pf->pdev->dev, - " rx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n", - i, ring_is_ps_enabled(rx_ring), + " rx_rings[%i]: next_to_use = %d, next_to_clean = %d, ring_active = %i\n", + i, rx_ring->next_to_use, rx_ring->next_to_clean, rx_ring->ring_active); @@ -326,9 +324,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) tx_ring->queue_index, tx_ring->reg_idx); dev_info(&pf->pdev->dev, - " tx_rings[%i]: dtype = %d\n", - i, tx_ring->dtype); - dev_info(&pf->pdev->dev, " tx_rings[%i]: next_to_use = %d, next_to_clean = %d, ring_active = %i\n", i, tx_ring->next_to_use, @@ -365,8 +360,8 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) " work_limit = %d\n", vsi->work_limit); dev_info(&pf->pdev->dev, - " max_frame = %d, rx_hdr_len = %d, rx_buf_len = %d dtype = %d\n", - vsi->max_frame, vsi->rx_hdr_len, vsi->rx_buf_len, vsi->dtype); + " max_frame = %d, rx_buf_len = %d dtype = %d\n", + vsi->max_frame, vsi->rx_buf_len, 0); dev_info(&pf->pdev->dev, " num_q_vectors = %i, base_vector = %i\n", vsi->num_q_vectors, vsi->base_vector); @@ -591,13 +586,6 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, " d[%03x] = 0x%016llx 0x%016llx\n", i, txd->buffer_addr, txd->cmd_type_offset_bsz); - } else if (sizeof(union i40e_rx_desc) == - sizeof(union i40e_16byte_rx_desc)) { - rxd = I40E_RX_DESC(ring, i); - dev_info(&pf->pdev->dev, - " d[%03x] = 0x%016llx 0x%016llx\n", - i, rxd->read.pkt_addr, - rxd->read.hdr_addr); } else { rxd = I40E_RX_DESC(ring, i); dev_info(&pf->pdev->dev, @@ -619,13 +607,6 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, "vsi = %02i tx ring = %02i d[%03x] = 0x%016llx 0x%016llx\n", vsi_seid, ring_id, desc_n, txd->buffer_addr, txd->cmd_type_offset_bsz); - } else if (sizeof(union i40e_rx_desc) == - sizeof(union i40e_16byte_rx_desc)) { - rxd = I40E_RX_DESC(ring, desc_n); - dev_info(&pf->pdev->dev, - "vsi = %02i rx ring = %02i d[%03x] = 0x%016llx 0x%016llx\n", - vsi_seid, ring_id, desc_n, - rxd->read.pkt_addr, rxd->read.hdr_addr); } else { rxd = I40E_RX_DESC(ring, desc_n); dev_info(&pf->pdev->dev, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 8e56c43c4104..5e8d84ff7d5f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -230,12 +230,22 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { #define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN) +static const char i40e_priv_flags_strings_gl[][ETH_GSTRING_LEN] = { + "MFP", + "LinkPolling", + "flow-director-atr", + "veb-stats", + "hw-atr-eviction", + "vf-true-promisc-support", +}; + +#define I40E_PRIV_FLAGS_GL_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings_gl) + static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { "NPAR", "LinkPolling", "flow-director-atr", "veb-stats", - "packet-split", "hw-atr-eviction", }; @@ -252,6 +262,110 @@ static void i40e_partition_setting_complaint(struct i40e_pf *pf) } /** + * i40e_phy_type_to_ethtool - convert the phy_types to ethtool link modes + * @phy_types: PHY types to convert + * @supported: pointer to the ethtool supported variable to fill in + * @advertising: pointer to the ethtool advertising variable to fill in + * + **/ +static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, + u32 *advertising) +{ + enum i40e_aq_capabilities_phy_type phy_types = pf->hw.phy.phy_types; + + *supported = 0x0; + *advertising = 0x0; + + if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { + *supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + *advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { + *supported |= SUPPORTED_100baseT_Full; + *advertising |= ADVERTISED_100baseT_Full; + } + } + if (phy_types & I40E_CAP_PHY_TYPE_XAUI || + phy_types & I40E_CAP_PHY_TYPE_XFI || + phy_types & I40E_CAP_PHY_TYPE_SFI || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) + *supported |= SUPPORTED_10000baseT_Full; + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || + phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { + *supported |= SUPPORTED_Autoneg | + SUPPORTED_10000baseT_Full; + *advertising |= ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_XLAUI || + phy_types & I40E_CAP_PHY_TYPE_XLPPI || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC) + *supported |= SUPPORTED_40000baseCR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || + phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) { + *supported |= SUPPORTED_Autoneg | + SUPPORTED_40000baseCR4_Full; + *advertising |= ADVERTISED_Autoneg | + ADVERTISED_40000baseCR4_Full; + } + if ((phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) && + !(phy_types & I40E_CAP_PHY_TYPE_1000BASE_T)) { + *supported |= SUPPORTED_Autoneg | + SUPPORTED_100baseT_Full; + *advertising |= ADVERTISED_Autoneg | + ADVERTISED_100baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || + phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { + *supported |= SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + *advertising |= ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; + } + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) + *supported |= SUPPORTED_40000baseSR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) + *supported |= SUPPORTED_40000baseLR4_Full; + if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) { + *supported |= SUPPORTED_40000baseKR4_Full | + SUPPORTED_Autoneg; + *advertising |= ADVERTISED_40000baseKR4_Full | + ADVERTISED_Autoneg; + } + if (phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) { + *supported |= SUPPORTED_20000baseKR2_Full | + SUPPORTED_Autoneg; + *advertising |= ADVERTISED_20000baseKR2_Full | + ADVERTISED_Autoneg; + } + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { + *supported |= SUPPORTED_10000baseKR_Full | + SUPPORTED_Autoneg; + *advertising |= ADVERTISED_10000baseKR_Full | + ADVERTISED_Autoneg; + } + if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { + *supported |= SUPPORTED_10000baseKX4_Full | + SUPPORTED_Autoneg; + *advertising |= ADVERTISED_10000baseKX4_Full | + ADVERTISED_Autoneg; + } + if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { + *supported |= SUPPORTED_1000baseKX_Full | + SUPPORTED_Autoneg; + *advertising |= ADVERTISED_1000baseKX_Full | + ADVERTISED_Autoneg; + } +} + +/** * i40e_get_settings_link_up - Get the Link settings for when link is up * @hw: hw structure * @ecmd: ethtool command to fill in @@ -265,6 +379,8 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, { struct i40e_link_status *hw_link_info = &hw->phy.link_info; u32 link_speed = hw_link_info->link_speed; + u32 e_advertising = 0x0; + u32 e_supported = 0x0; /* Initialize supported and advertised settings based on phy settings */ switch (hw_link_info->phy_type) { @@ -305,21 +421,18 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, break; case I40E_PHY_TYPE_10GBASE_T: case I40E_PHY_TYPE_1000BASE_T: + case I40E_PHY_TYPE_100BASE_TX: ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; + SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full; ecmd->advertising = ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) ecmd->advertising |= ADVERTISED_10000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) ecmd->advertising |= ADVERTISED_1000baseT_Full; - /* adding 100baseT support for 10GBASET_PHY */ - if (pf->flags & I40E_FLAG_HAVE_10GBASET_PHY) { - ecmd->supported |= SUPPORTED_100baseT_Full; - ecmd->advertising |= ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full; - } + if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) + ecmd->advertising |= ADVERTISED_100baseT_Full; break; case I40E_PHY_TYPE_1000BASE_T_OPTICAL: ecmd->supported = SUPPORTED_Autoneg | @@ -327,21 +440,6 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, ecmd->advertising = ADVERTISED_Autoneg | ADVERTISED_1000baseT_Full; break; - case I40E_PHY_TYPE_100BASE_TX: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_100baseT_Full; - if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; - /* firmware detects 10G phy as 100M phy at 100M speed */ - if (pf->flags & I40E_FLAG_HAVE_10GBASET_PHY) { - ecmd->supported |= SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full; - } - break; case I40E_PHY_TYPE_10GBASE_CR1_CU: case I40E_PHY_TYPE_10GBASE_CR1: ecmd->supported = SUPPORTED_Autoneg | @@ -368,14 +466,23 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, ecmd->advertising |= ADVERTISED_100baseT_Full; } break; - /* Backplane is set based on supported phy types in get_settings - * so don't set anything here but don't warn either - */ case I40E_PHY_TYPE_40GBASE_KR4: case I40E_PHY_TYPE_20GBASE_KR2: case I40E_PHY_TYPE_10GBASE_KR: case I40E_PHY_TYPE_10GBASE_KX4: case I40E_PHY_TYPE_1000BASE_KX: + ecmd->supported |= SUPPORTED_40000baseKR4_Full | + SUPPORTED_20000baseKR2_Full | + SUPPORTED_10000baseKR_Full | + SUPPORTED_10000baseKX4_Full | + SUPPORTED_1000baseKX_Full | + SUPPORTED_Autoneg; + ecmd->advertising |= ADVERTISED_40000baseKR4_Full | + ADVERTISED_20000baseKR2_Full | + ADVERTISED_10000baseKR_Full | + ADVERTISED_10000baseKX4_Full | + ADVERTISED_1000baseKX_Full | + ADVERTISED_Autoneg; break; default: /* if we got here and link is up something bad is afoot */ @@ -383,6 +490,16 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, hw_link_info->phy_type); } + /* Now that we've worked out everything that could be supported by the + * current PHY type, get what is supported by the NVM and them to + * get what is truly supported + */ + i40e_phy_type_to_ethtool(pf, &e_supported, + &e_advertising); + + ecmd->supported = ecmd->supported & e_supported; + ecmd->advertising = ecmd->advertising & e_advertising; + /* Set speed and duplex */ switch (link_speed) { case I40E_LINK_SPEED_40GB: @@ -417,74 +534,11 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, struct ethtool_cmd *ecmd, struct i40e_pf *pf) { - enum i40e_aq_capabilities_phy_type phy_types = hw->phy.phy_types; - /* link is down and the driver needs to fall back on * supported phy types to figure out what info to display */ - ecmd->supported = 0x0; - ecmd->advertising = 0x0; - if (phy_types & I40E_CAP_PHY_TYPE_SGMII) { - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_1000baseT_Full; - if (pf->hw.mac.type == I40E_MAC_X722) { - ecmd->supported |= SUPPORTED_100baseT_Full; - ecmd->advertising |= ADVERTISED_100baseT_Full; - if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { - ecmd->supported |= SUPPORTED_100baseT_Full; - ecmd->advertising |= ADVERTISED_100baseT_Full; - } - } - } - if (phy_types & I40E_CAP_PHY_TYPE_XAUI || - phy_types & I40E_CAP_PHY_TYPE_XFI || - phy_types & I40E_CAP_PHY_TYPE_SFI || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_SFPP_CU || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_AOC) - ecmd->supported |= SUPPORTED_10000baseT_Full; - if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1_CU || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_CR1 || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_T || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_SR || - phy_types & I40E_CAP_PHY_TYPE_10GBASE_LR) { - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_10000baseT_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_10000baseT_Full; - } - if (phy_types & I40E_CAP_PHY_TYPE_XLAUI || - phy_types & I40E_CAP_PHY_TYPE_XLPPI || - phy_types & I40E_CAP_PHY_TYPE_40GBASE_AOC) - ecmd->supported |= SUPPORTED_40000baseCR4_Full; - if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4_CU || - phy_types & I40E_CAP_PHY_TYPE_40GBASE_CR4) { - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_40000baseCR4_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_40000baseCR4_Full; - } - if ((phy_types & I40E_CAP_PHY_TYPE_100BASE_TX) && - !(phy_types & I40E_CAP_PHY_TYPE_1000BASE_T)) { - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_100baseT_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_100baseT_Full; - } - if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_T || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_SX || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_LX || - phy_types & I40E_CAP_PHY_TYPE_1000BASE_T_OPTICAL) { - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_1000baseT_Full; - } - if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_SR4) - ecmd->supported |= SUPPORTED_40000baseSR4_Full; - if (phy_types & I40E_CAP_PHY_TYPE_40GBASE_LR4) - ecmd->supported |= SUPPORTED_40000baseLR4_Full; + i40e_phy_type_to_ethtool(pf, &ecmd->supported, + &ecmd->advertising); /* With no link speed and duplex are unknown */ ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); @@ -513,38 +567,6 @@ static int i40e_get_settings(struct net_device *netdev, i40e_get_settings_link_down(hw, ecmd, pf); /* Now set the settings that don't rely on link being up/down */ - - /* For backplane, supported and advertised are only reliant on the - * phy types the NVM specifies are supported. - */ - if (hw->device_id == I40E_DEV_ID_KX_B || - hw->device_id == I40E_DEV_ID_KX_C || - hw->device_id == I40E_DEV_ID_20G_KR2 || - hw->device_id == I40E_DEV_ID_20G_KR2_A) { - ecmd->supported = SUPPORTED_Autoneg; - ecmd->advertising = ADVERTISED_Autoneg; - if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_40GBASE_KR4) { - ecmd->supported |= SUPPORTED_40000baseKR4_Full; - ecmd->advertising |= ADVERTISED_40000baseKR4_Full; - } - if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_20GBASE_KR2) { - ecmd->supported |= SUPPORTED_20000baseKR2_Full; - ecmd->advertising |= ADVERTISED_20000baseKR2_Full; - } - if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { - ecmd->supported |= SUPPORTED_10000baseKR_Full; - ecmd->advertising |= ADVERTISED_10000baseKR_Full; - } - if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { - ecmd->supported |= SUPPORTED_10000baseKX4_Full; - ecmd->advertising |= ADVERTISED_10000baseKX4_Full; - } - if (hw->phy.phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { - ecmd->supported |= SUPPORTED_1000baseKX_Full; - ecmd->advertising |= ADVERTISED_1000baseKX_Full; - } - } - /* Set autoneg settings */ ecmd->autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? AUTONEG_ENABLE : AUTONEG_DISABLE); @@ -1159,6 +1181,10 @@ static void i40e_get_drvinfo(struct net_device *netdev, sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(pf->pdev), sizeof(drvinfo->bus_info)); + if (pf->hw.pf_id == 0) + drvinfo->n_priv_flags = I40E_PRIV_FLAGS_GL_STR_LEN; + else + drvinfo->n_priv_flags = I40E_PRIV_FLAGS_STR_LEN; } static void i40e_get_ringparam(struct net_device *netdev, @@ -1275,6 +1301,13 @@ static int i40e_set_ringparam(struct net_device *netdev, } for (i = 0; i < vsi->num_queue_pairs; i++) { + /* this is to allow wr32 to have something to write to + * during early allocation of Rx buffers + */ + u32 __iomem faketail = 0; + struct i40e_ring *ring; + u16 unused; + /* clone ring and setup updated count */ rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_count; @@ -1283,12 +1316,22 @@ static int i40e_set_ringparam(struct net_device *netdev, */ rx_rings[i].desc = NULL; rx_rings[i].rx_bi = NULL; + rx_rings[i].tail = (u8 __iomem *)&faketail; err = i40e_setup_rx_descriptors(&rx_rings[i]); + if (err) + goto rx_unwind; + + /* now allocate the Rx buffers to make sure the OS + * has enough memory, any failure here means abort + */ + ring = &rx_rings[i]; + unused = I40E_DESC_UNUSED(ring); + err = i40e_alloc_rx_buffers(ring, unused); +rx_unwind: if (err) { - while (i) { - i--; + do { i40e_free_rx_resources(&rx_rings[i]); - } + } while (i--); kfree(rx_rings); rx_rings = NULL; @@ -1314,6 +1357,17 @@ static int i40e_set_ringparam(struct net_device *netdev, if (rx_rings) { for (i = 0; i < vsi->num_queue_pairs; i++) { i40e_free_rx_resources(vsi->rx_rings[i]); + /* get the real tail offset */ + rx_rings[i].tail = vsi->rx_rings[i]->tail; + /* this is to fake out the allocation routine + * into thinking it has to realloc everything + * but the recycling logic will let us re-use + * the buffers allocated above + */ + rx_rings[i].next_to_use = 0; + rx_rings[i].next_to_clean = 0; + rx_rings[i].next_to_alloc = 0; + /* do a struct copy */ *vsi->rx_rings[i] = rx_rings[i]; } kfree(rx_rings); @@ -1358,7 +1412,10 @@ static int i40e_get_sset_count(struct net_device *netdev, int sset) return I40E_VSI_STATS_LEN(netdev); } case ETH_SS_PRIV_FLAGS: - return I40E_PRIV_FLAGS_STR_LEN; + if (pf->hw.pf_id == 0) + return I40E_PRIV_FLAGS_GL_STR_LEN; + else + return I40E_PRIV_FLAGS_STR_LEN; default: return -EOPNOTSUPP; } @@ -1556,10 +1613,18 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */ break; case ETH_SS_PRIV_FLAGS: - for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { - memcpy(data, i40e_priv_flags_strings[i], - ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; + if (pf->hw.pf_id == 0) { + for (i = 0; i < I40E_PRIV_FLAGS_GL_STR_LEN; i++) { + memcpy(data, i40e_priv_flags_strings_gl[i], + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + } else { + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + memcpy(data, i40e_priv_flags_strings[i], + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } } break; default: @@ -1853,7 +1918,7 @@ static int i40e_set_phys_id(struct net_device *netdev, if (!(pf->flags & I40E_FLAG_HAVE_10GBASET_PHY)) { pf->led_status = i40e_led_get(hw); } else { - i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_PORT, NULL); + i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, NULL); ret = i40e_led_get_phy(hw, &temp_status, &pf->phy_led_val); pf->led_status = temp_status; @@ -2821,18 +2886,18 @@ static u32 i40e_get_priv_flags(struct net_device *dev) struct i40e_pf *pf = vsi->back; u32 ret_flags = 0; - ret_flags |= pf->hw.func_caps.npar_enable ? - I40E_PRIV_FLAGS_NPAR_FLAG : 0; ret_flags |= pf->flags & I40E_FLAG_LINK_POLLING_ENABLED ? I40E_PRIV_FLAGS_LINKPOLL_FLAG : 0; ret_flags |= pf->flags & I40E_FLAG_FD_ATR_ENABLED ? I40E_PRIV_FLAGS_FD_ATR : 0; ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? I40E_PRIV_FLAGS_VEB_STATS : 0; - ret_flags |= pf->flags & I40E_FLAG_RX_PS_ENABLED ? - I40E_PRIV_FLAGS_PS : 0; ret_flags |= pf->auto_disable_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE ? 0 : I40E_PRIV_FLAGS_HW_ATR_EVICT; + if (pf->hw.pf_id == 0) { + ret_flags |= pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT ? + I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT : 0; + } return ret_flags; } @@ -2847,27 +2912,13 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + u16 sw_flags = 0, valid_flags = 0; bool reset_required = false; + bool promisc_change = false; + int ret; /* NOTE: MFP is not settable */ - /* allow the user to control the method of receive - * buffer DMA, whether the packet is split at header - * boundaries into two separate buffers. In some cases - * one routine or the other will perform better. - */ - if ((flags & I40E_PRIV_FLAGS_PS) && - !(pf->flags & I40E_FLAG_RX_PS_ENABLED)) { - pf->flags |= I40E_FLAG_RX_PS_ENABLED; - pf->flags &= ~I40E_FLAG_RX_1BUF_ENABLED; - reset_required = true; - } else if (!(flags & I40E_PRIV_FLAGS_PS) && - (pf->flags & I40E_FLAG_RX_PS_ENABLED)) { - pf->flags &= ~I40E_FLAG_RX_PS_ENABLED; - pf->flags |= I40E_FLAG_RX_1BUF_ENABLED; - reset_required = true; - } - if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; else @@ -2894,6 +2945,33 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) reset_required = true; } + if (pf->hw.pf_id == 0) { + if ((flags & I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT) && + !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) { + pf->flags |= I40E_FLAG_TRUE_PROMISC_SUPPORT; + promisc_change = true; + } else if (!(flags & I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT) && + (pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) { + pf->flags &= ~I40E_FLAG_TRUE_PROMISC_SUPPORT; + promisc_change = true; + } + } + if (promisc_change) { + if (!(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) + sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; + valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; + ret = i40e_aq_set_switch_config(&pf->hw, sw_flags, valid_flags, + NULL); + if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) { + dev_info(&pf->pdev->dev, + "couldn't set switch config bits, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + /* not a fatal problem, just keep going */ + } + } + if ((flags & I40E_PRIV_FLAGS_HW_ATR_EVICT) && (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE)) pf->auto_disable_flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; diff --git a/drivers/net/ethernet/intel/i40e/i40e_hmc.c b/drivers/net/ethernet/intel/i40e/i40e_hmc.c index 5ebe12d56ebf..a7c7b1d9b7c8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_hmc.c +++ b/drivers/net/ethernet/intel/i40e/i40e_hmc.c @@ -49,7 +49,7 @@ i40e_status i40e_add_sd_table_entry(struct i40e_hw *hw, struct i40e_hmc_sd_entry *sd_entry; bool dma_mem_alloc_done = false; struct i40e_dma_mem mem; - i40e_status ret_code; + i40e_status ret_code = I40E_SUCCESS; u64 alloc_len; if (NULL == hmc_info->sd_table.sd_entry) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8e6c0f2487d7..5ea22008d721 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -46,7 +46,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 1 #define DRV_VERSION_MINOR 5 -#define DRV_VERSION_BUILD 10 +#define DRV_VERSION_BUILD 16 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -328,7 +328,7 @@ static void i40e_tx_timeout(struct net_device *netdev) unsigned long trans_start; q = netdev_get_tx_queue(netdev, i); - trans_start = q->trans_start ? : netdev->trans_start; + trans_start = q->trans_start; if (netif_xmit_stopped(q) && time_after(jiffies, (trans_start + netdev->watchdog_timeo))) { @@ -2128,7 +2128,8 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) aq_ret = i40e_aq_set_vsi_unicast_promiscuous( &vsi->back->hw, vsi->seid, - cur_promisc, NULL); + cur_promisc, NULL, + true); if (aq_ret) { retval = i40e_aq_rc_to_posix(aq_ret, @@ -2855,34 +2856,21 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) memset(&rx_ctx, 0, sizeof(rx_ctx)); ring->rx_buf_len = vsi->rx_buf_len; - ring->rx_hdr_len = vsi->rx_hdr_len; rx_ctx.dbuff = ring->rx_buf_len >> I40E_RXQ_CTX_DBUFF_SHIFT; - rx_ctx.hbuff = ring->rx_hdr_len >> I40E_RXQ_CTX_HBUFF_SHIFT; rx_ctx.base = (ring->dma / 128); rx_ctx.qlen = ring->count; - if (vsi->back->flags & I40E_FLAG_16BYTE_RX_DESC_ENABLED) { - set_ring_16byte_desc_enabled(ring); - rx_ctx.dsize = 0; - } else { - rx_ctx.dsize = 1; - } + /* use 32 byte descriptors */ + rx_ctx.dsize = 1; - rx_ctx.dtype = vsi->dtype; - if (vsi->dtype) { - set_ring_ps_enabled(ring); - rx_ctx.hsplit_0 = I40E_RX_SPLIT_L2 | - I40E_RX_SPLIT_IP | - I40E_RX_SPLIT_TCP_UDP | - I40E_RX_SPLIT_SCTP; - } else { - rx_ctx.hsplit_0 = 0; - } + /* descriptor type is always zero + * rx_ctx.dtype = 0; + */ + rx_ctx.hsplit_0 = 0; - rx_ctx.rxmax = min_t(u16, vsi->max_frame, - (chain_len * ring->rx_buf_len)); + rx_ctx.rxmax = min_t(u16, vsi->max_frame, chain_len * ring->rx_buf_len); if (hw->revision_id == 0) rx_ctx.lrxqthresh = 0; else @@ -2919,12 +2907,7 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); - if (ring_is_ps_enabled(ring)) { - i40e_alloc_rx_headers(ring); - i40e_alloc_rx_buffers_ps(ring, I40E_DESC_UNUSED(ring)); - } else { - i40e_alloc_rx_buffers_1buf(ring, I40E_DESC_UNUSED(ring)); - } + i40e_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); return 0; } @@ -2963,40 +2946,18 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) else vsi->max_frame = I40E_RXBUFFER_2048; - /* figure out correct receive buffer length */ - switch (vsi->back->flags & (I40E_FLAG_RX_1BUF_ENABLED | - I40E_FLAG_RX_PS_ENABLED)) { - case I40E_FLAG_RX_1BUF_ENABLED: - vsi->rx_hdr_len = 0; - vsi->rx_buf_len = vsi->max_frame; - vsi->dtype = I40E_RX_DTYPE_NO_SPLIT; - break; - case I40E_FLAG_RX_PS_ENABLED: - vsi->rx_hdr_len = I40E_RX_HDR_SIZE; - vsi->rx_buf_len = I40E_RXBUFFER_2048; - vsi->dtype = I40E_RX_DTYPE_HEADER_SPLIT; - break; - default: - vsi->rx_hdr_len = I40E_RX_HDR_SIZE; - vsi->rx_buf_len = I40E_RXBUFFER_2048; - vsi->dtype = I40E_RX_DTYPE_SPLIT_ALWAYS; - break; - } + vsi->rx_buf_len = I40E_RXBUFFER_2048; #ifdef I40E_FCOE /* setup rx buffer for FCoE */ if ((vsi->type == I40E_VSI_FCOE) && (vsi->back->flags & I40E_FLAG_FCOE_ENABLED)) { - vsi->rx_hdr_len = 0; vsi->rx_buf_len = I40E_RXBUFFER_3072; vsi->max_frame = I40E_RXBUFFER_3072; - vsi->dtype = I40E_RX_DTYPE_NO_SPLIT; } #endif /* I40E_FCOE */ /* round up for the chip's needs */ - vsi->rx_hdr_len = ALIGN(vsi->rx_hdr_len, - BIT_ULL(I40E_RXQ_CTX_HBUFF_SHIFT)); vsi->rx_buf_len = ALIGN(vsi->rx_buf_len, BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); @@ -7512,10 +7473,6 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) rx_ring->count = vsi->num_desc; rx_ring->size = 0; rx_ring->dcb_tc = 0; - if (pf->flags & I40E_FLAG_16BYTE_RX_DESC_ENABLED) - set_ring_16byte_desc_enabled(rx_ring); - else - clear_ring_16byte_desc_enabled(rx_ring); rx_ring->rx_itr_setting = pf->rx_itr_default; vsi->rx_rings[i] = rx_ring; } @@ -8460,11 +8417,6 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_FLAG_MSI_ENABLED | I40E_FLAG_MSIX_ENABLED; - if (iommu_present(&pci_bus_type)) - pf->flags |= I40E_FLAG_RX_PS_ENABLED; - else - pf->flags |= I40E_FLAG_RX_1BUF_ENABLED; - /* Set default ITR */ pf->rx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_RX_DEF; pf->tx_itr_default = I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF; @@ -9131,8 +9083,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) NETIF_F_TSO6 | NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | - NETIF_F_GSO_IPIP | - NETIF_F_GSO_SIT | + NETIF_F_GSO_IPXIP4 | + NETIF_F_GSO_IPXIP6 | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_PARTIAL | @@ -9410,7 +9362,8 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_QUEUE_OPT_VALID); ctxt.info.queueing_opt_flags |= - I40E_AQ_VSI_QUE_OPT_TCP_ENA; + (I40E_AQ_VSI_QUE_OPT_TCP_ENA | + I40E_AQ_VSI_QUE_OPT_RSS_LUT_VSI); } ctxt.info.valid_sections |= cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID); @@ -10456,6 +10409,7 @@ int i40e_fetch_switch_configuration(struct i40e_pf *pf, bool printconfig) **/ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) { + u16 flags = 0; int ret; /* find out what's out there already */ @@ -10469,6 +10423,32 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) } i40e_pf_reset_stats(pf); + /* set the switch config bit for the whole device to + * support limited promisc or true promisc + * when user requests promisc. The default is limited + * promisc. + */ + + if ((pf->hw.pf_id == 0) && + !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) + flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; + + if (pf->hw.pf_id == 0) { + u16 valid_flags; + + valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; + ret = i40e_aq_set_switch_config(&pf->hw, flags, valid_flags, + NULL); + if (ret && pf->hw.aq.asq_last_status != I40E_AQ_RC_ESRCH) { + dev_info(&pf->pdev->dev, + "couldn't set switch config bits, err %s aq_err %s\n", + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); + /* not a fatal problem, just keep going */ + } + } + /* first time setup */ if (pf->lan_vsi == I40E_NO_VSI || reinit) { struct i40e_vsi *vsi = NULL; @@ -10696,11 +10676,9 @@ static void i40e_print_features(struct i40e_pf *pf) #ifdef CONFIG_PCI_IOV i += snprintf(&buf[i], REMAIN(i), " VFs: %d", pf->num_req_vfs); #endif - i += snprintf(&buf[i], REMAIN(i), " VSIs: %d QP: %d RX: %s", + i += snprintf(&buf[i], REMAIN(i), " VSIs: %d QP: %d", pf->hw.func_caps.num_vsis, - pf->vsi[pf->lan_vsi]->num_queue_pairs, - pf->flags & I40E_FLAG_RX_PS_ENABLED ? "PS" : "1BUF"); - + pf->vsi[pf->lan_vsi]->num_queue_pairs); if (pf->flags & I40E_FLAG_RSS_ENABLED) i += snprintf(&buf[i], REMAIN(i), " RSS"); if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 4c8977c805df..80403c6ee7f0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -130,7 +130,8 @@ i40e_status i40e_aq_set_vsi_broadcast(struct i40e_hw *hw, u16 vsi_id, bool set_filter, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_vsi_unicast_promiscuous(struct i40e_hw *hw, - u16 vsi_id, bool set, struct i40e_asq_cmd_details *cmd_details); + u16 vsi_id, bool set, struct i40e_asq_cmd_details *cmd_details, + bool rx_only_promisc); i40e_status i40e_aq_set_vsi_multicast_promiscuous(struct i40e_hw *hw, u16 vsi_id, bool set, struct i40e_asq_cmd_details *cmd_details); enum i40e_status_code i40e_aq_set_vsi_mc_promisc_on_vlan(struct i40e_hw *hw, @@ -182,6 +183,10 @@ i40e_status i40e_aq_get_switch_config(struct i40e_hw *hw, struct i40e_aqc_get_switch_config_resp *buf, u16 buf_size, u16 *start_seid, struct i40e_asq_cmd_details *cmd_details); +enum i40e_status_code i40e_aq_set_switch_config(struct i40e_hw *hw, + u16 flags, + u16 valid_flags, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_request_resource(struct i40e_hw *hw, enum i40e_aq_resources_ids resource, enum i40e_aq_resource_access_type access, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index a1b878abd5b0..ed39cbad24bd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -289,9 +289,7 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) rd32(hw, I40E_PRTTSYN_RXTIME_H(3)); pf->last_rx_ptp_check = jiffies; pf->rx_hwtstamp_cleared++; - dev_warn(&vsi->back->pdev->dev, - "%s: clearing Rx timestamp hang\n", - __func__); + WARN_ONCE(1, "Detected Rx timestamp register hang\n"); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 2765d7efdd9c..55f151fca1dc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1024,7 +1024,6 @@ err: void i40e_clean_rx_ring(struct i40e_ring *rx_ring) { struct device *dev = rx_ring->dev; - struct i40e_rx_buffer *rx_bi; unsigned long bi_size; u16 i; @@ -1032,48 +1031,22 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) if (!rx_ring->rx_bi) return; - if (ring_is_ps_enabled(rx_ring)) { - int bufsz = ALIGN(rx_ring->rx_hdr_len, 256) * rx_ring->count; - - rx_bi = &rx_ring->rx_bi[0]; - if (rx_bi->hdr_buf) { - dma_free_coherent(dev, - bufsz, - rx_bi->hdr_buf, - rx_bi->dma); - for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - rx_bi->dma = 0; - rx_bi->hdr_buf = NULL; - } - } - } /* Free all the Rx ring sk_buffs */ for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - if (rx_bi->dma) { - dma_unmap_single(dev, - rx_bi->dma, - rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - rx_bi->dma = 0; - } + struct i40e_rx_buffer *rx_bi = &rx_ring->rx_bi[i]; + if (rx_bi->skb) { dev_kfree_skb(rx_bi->skb); rx_bi->skb = NULL; } - if (rx_bi->page) { - if (rx_bi->page_dma) { - dma_unmap_page(dev, - rx_bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - rx_bi->page_dma = 0; - } - __free_page(rx_bi->page); - rx_bi->page = NULL; - rx_bi->page_offset = 0; - } + if (!rx_bi->page) + continue; + + dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE); + __free_pages(rx_bi->page, 0); + + rx_bi->page = NULL; + rx_bi->page_offset = 0; } bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; @@ -1082,6 +1055,7 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) /* Zero out the descriptor ring */ memset(rx_ring->desc, 0, rx_ring->size); + rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; } @@ -1106,37 +1080,6 @@ void i40e_free_rx_resources(struct i40e_ring *rx_ring) } /** - * i40e_alloc_rx_headers - allocate rx header buffers - * @rx_ring: ring to alloc buffers - * - * Allocate rx header buffers for the entire ring. As these are static, - * this is only called when setting up a new ring. - **/ -void i40e_alloc_rx_headers(struct i40e_ring *rx_ring) -{ - struct device *dev = rx_ring->dev; - struct i40e_rx_buffer *rx_bi; - dma_addr_t dma; - void *buffer; - int buf_size; - int i; - - if (rx_ring->rx_bi[0].hdr_buf) - return; - /* Make sure the buffers don't cross cache line boundaries. */ - buf_size = ALIGN(rx_ring->rx_hdr_len, 256); - buffer = dma_alloc_coherent(dev, buf_size * rx_ring->count, - &dma, GFP_KERNEL); - if (!buffer) - return; - for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - rx_bi->dma = dma + (i * buf_size); - rx_bi->hdr_buf = buffer + (i * buf_size); - } -} - -/** * i40e_setup_rx_descriptors - Allocate Rx descriptors * @rx_ring: Rx descriptor ring (for a specific queue) to setup * @@ -1157,9 +1100,7 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) u64_stats_init(&rx_ring->syncp); /* Round up to nearest 4K */ - rx_ring->size = ring_is_16byte_desc_enabled(rx_ring) - ? rx_ring->count * sizeof(union i40e_16byte_rx_desc) - : rx_ring->count * sizeof(union i40e_32byte_rx_desc); + rx_ring->size = rx_ring->count * sizeof(union i40e_32byte_rx_desc); rx_ring->size = ALIGN(rx_ring->size, 4096); rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); @@ -1170,6 +1111,7 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) goto err; } + rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; @@ -1188,6 +1130,10 @@ err: static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) { rx_ring->next_to_use = val; + + /* update next to alloc since we have filled the ring */ + rx_ring->next_to_alloc = val; + /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, @@ -1198,160 +1144,122 @@ static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) } /** - * i40e_alloc_rx_buffers_ps - Replace used receive buffers; packet split - * @rx_ring: ring to place buffers on - * @cleaned_count: number of buffers to replace + * i40e_alloc_mapped_page - recycle or make a new page + * @rx_ring: ring to use + * @bi: rx_buffer struct to modify * - * Returns true if any errors on allocation + * Returns true if the page was successfully allocated or + * reused. **/ -bool i40e_alloc_rx_buffers_ps(struct i40e_ring *rx_ring, u16 cleaned_count) +static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *bi) { - u16 i = rx_ring->next_to_use; - union i40e_rx_desc *rx_desc; - struct i40e_rx_buffer *bi; - const int current_node = numa_node_id(); + struct page *page = bi->page; + dma_addr_t dma; - /* do nothing if no valid netdev defined */ - if (!rx_ring->netdev || !cleaned_count) - return false; + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) { + rx_ring->rx_stats.page_reuse_count++; + return true; + } - while (cleaned_count--) { - rx_desc = I40E_RX_DESC(rx_ring, i); - bi = &rx_ring->rx_bi[i]; + /* alloc new page for storage */ + page = dev_alloc_page(); + if (unlikely(!page)) { + rx_ring->rx_stats.alloc_page_failed++; + return false; + } - if (bi->skb) /* desc is in use */ - goto no_buffers; + /* map page for use */ + dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); - /* If we've been moved to a different NUMA node, release the - * page so we can get a new one on the current node. + /* if mapping failed free memory back to system since + * there isn't much point in holding memory we can't use */ - if (bi->page && page_to_nid(bi->page) != current_node) { - dma_unmap_page(rx_ring->dev, - bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - __free_page(bi->page); - bi->page = NULL; - bi->page_dma = 0; - rx_ring->rx_stats.realloc_count++; - } else if (bi->page) { - rx_ring->rx_stats.page_reuse_count++; - } - - if (!bi->page) { - bi->page = alloc_page(GFP_ATOMIC); - if (!bi->page) { - rx_ring->rx_stats.alloc_page_failed++; - goto no_buffers; - } - bi->page_dma = dma_map_page(rx_ring->dev, - bi->page, - 0, - PAGE_SIZE, - DMA_FROM_DEVICE); - if (dma_mapping_error(rx_ring->dev, bi->page_dma)) { - rx_ring->rx_stats.alloc_page_failed++; - __free_page(bi->page); - bi->page = NULL; - bi->page_dma = 0; - bi->page_offset = 0; - goto no_buffers; - } - bi->page_offset = 0; - } - - /* Refresh the desc even if buffer_addrs didn't change - * because each write-back erases this info. - */ - rx_desc->read.pkt_addr = - cpu_to_le64(bi->page_dma + bi->page_offset); - rx_desc->read.hdr_addr = cpu_to_le64(bi->dma); - i++; - if (i == rx_ring->count) - i = 0; + if (dma_mapping_error(rx_ring->dev, dma)) { + __free_pages(page, 0); + rx_ring->rx_stats.alloc_page_failed++; + return false; } - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + bi->dma = dma; + bi->page = page; + bi->page_offset = 0; - return false; + return true; +} -no_buffers: - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); +/** + * i40e_receive_skb - Send a completed packet up the stack + * @rx_ring: rx ring in play + * @skb: packet to send up + * @vlan_tag: vlan tag for packet + **/ +static void i40e_receive_skb(struct i40e_ring *rx_ring, + struct sk_buff *skb, u16 vlan_tag) +{ + struct i40e_q_vector *q_vector = rx_ring->q_vector; - /* make sure to come back via polling to try again after - * allocation failure - */ - return true; + if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + (vlan_tag & VLAN_VID_MASK)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); + + napi_gro_receive(&q_vector->napi, skb); } /** - * i40e_alloc_rx_buffers_1buf - Replace used receive buffers; single buffer + * i40e_alloc_rx_buffers - Replace used receive buffers * @rx_ring: ring to place buffers on * @cleaned_count: number of buffers to replace * - * Returns true if any errors on allocation + * Returns false if all allocations were successful, true if any fail **/ -bool i40e_alloc_rx_buffers_1buf(struct i40e_ring *rx_ring, u16 cleaned_count) +bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) { - u16 i = rx_ring->next_to_use; + u16 ntu = rx_ring->next_to_use; union i40e_rx_desc *rx_desc; struct i40e_rx_buffer *bi; - struct sk_buff *skb; /* do nothing if no valid netdev defined */ if (!rx_ring->netdev || !cleaned_count) return false; - while (cleaned_count--) { - rx_desc = I40E_RX_DESC(rx_ring, i); - bi = &rx_ring->rx_bi[i]; - skb = bi->skb; - - if (!skb) { - skb = __netdev_alloc_skb_ip_align(rx_ring->netdev, - rx_ring->rx_buf_len, - GFP_ATOMIC | - __GFP_NOWARN); - if (!skb) { - rx_ring->rx_stats.alloc_buff_failed++; - goto no_buffers; - } - /* initialize queue mapping */ - skb_record_rx_queue(skb, rx_ring->queue_index); - bi->skb = skb; - } + rx_desc = I40E_RX_DESC(rx_ring, ntu); + bi = &rx_ring->rx_bi[ntu]; - if (!bi->dma) { - bi->dma = dma_map_single(rx_ring->dev, - skb->data, - rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - if (dma_mapping_error(rx_ring->dev, bi->dma)) { - rx_ring->rx_stats.alloc_buff_failed++; - bi->dma = 0; - dev_kfree_skb(bi->skb); - bi->skb = NULL; - goto no_buffers; - } - } + do { + if (!i40e_alloc_mapped_page(rx_ring, bi)) + goto no_buffers; - rx_desc->read.pkt_addr = cpu_to_le64(bi->dma); + /* Refresh the desc even if buffer_addrs didn't change + * because each write-back erases this info. + */ + rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); rx_desc->read.hdr_addr = 0; - i++; - if (i == rx_ring->count) - i = 0; - } - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + rx_desc++; + bi++; + ntu++; + if (unlikely(ntu == rx_ring->count)) { + rx_desc = I40E_RX_DESC(rx_ring, 0); + bi = rx_ring->rx_bi; + ntu = 0; + } + + /* clear the status bits for the next_to_use descriptor */ + rx_desc->wb.qword1.status_error_len = 0; + + cleaned_count--; + } while (cleaned_count); + + if (rx_ring->next_to_use != ntu) + i40e_release_rx_desc(rx_ring, ntu); return false; no_buffers: - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + if (rx_ring->next_to_use != ntu) + i40e_release_rx_desc(rx_ring, ntu); /* make sure to come back via polling to try again after * allocation failure @@ -1360,42 +1268,35 @@ no_buffers: } /** - * i40e_receive_skb - Send a completed packet up the stack - * @rx_ring: rx ring in play - * @skb: packet to send up - * @vlan_tag: vlan tag for packet - **/ -static void i40e_receive_skb(struct i40e_ring *rx_ring, - struct sk_buff *skb, u16 vlan_tag) -{ - struct i40e_q_vector *q_vector = rx_ring->q_vector; - - if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - (vlan_tag & VLAN_VID_MASK)) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - - napi_gro_receive(&q_vector->napi, skb); -} - -/** * i40e_rx_checksum - Indicate in skb if hw indicated a good cksum * @vsi: the VSI we care about * @skb: skb currently being received and modified - * @rx_status: status value of last descriptor in packet - * @rx_error: error value of last descriptor in packet - * @rx_ptype: ptype value of last descriptor in packet + * @rx_desc: the receive descriptor + * + * skb->protocol must be set before this function is called **/ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, struct sk_buff *skb, - u32 rx_status, - u32 rx_error, - u16 rx_ptype) + union i40e_rx_desc *rx_desc) { - struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype); - bool ipv4, ipv6, ipv4_tunnel, ipv6_tunnel; + struct i40e_rx_ptype_decoded decoded; + bool ipv4, ipv6, tunnel = false; + u32 rx_error, rx_status; + u8 ptype; + u64 qword; + + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> I40E_RXD_QW1_PTYPE_SHIFT; + rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> + I40E_RXD_QW1_ERROR_SHIFT; + rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> + I40E_RXD_QW1_STATUS_SHIFT; + decoded = decode_rx_desc_ptype(ptype); skb->ip_summed = CHECKSUM_NONE; + skb_checksum_none_assert(skb); + /* Rx csum enabled and ip headers found? */ if (!(vsi->netdev->features & NETIF_F_RXCSUM)) return; @@ -1441,14 +1342,13 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, * doesn't make it a hard requirement so if we have validated the * inner checksum report CHECKSUM_UNNECESSARY. */ - - ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) && - (rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4); - ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) && - (rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4); + if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP | + I40E_RX_PTYPE_INNER_PROT_UDP | + I40E_RX_PTYPE_INNER_PROT_SCTP)) + tunnel = true; skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->csum_level = ipv4_tunnel || ipv6_tunnel; + skb->csum_level = tunnel ? 1 : 0; return; @@ -1462,7 +1362,7 @@ checksum_fail: * * Returns a hash type to be used by skb_set_hash **/ -static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype) +static inline int i40e_ptype_to_htype(u8 ptype) { struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype); @@ -1490,11 +1390,11 @@ static inline void i40e_rx_hash(struct i40e_ring *ring, u8 rx_ptype) { u32 hash; - const __le64 rss_mask = + const __le64 rss_mask = cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); - if (ring->netdev->features & NETIF_F_RXHASH) + if (!(ring->netdev->features & NETIF_F_RXHASH)) return; if ((rx_desc->wb.qword1.status_error_len & rss_mask) == rss_mask) { @@ -1504,338 +1404,419 @@ static inline void i40e_rx_hash(struct i40e_ring *ring, } /** - * i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split - * @rx_ring: rx ring to clean - * @budget: how many cleans we're allowed + * i40e_process_skb_fields - Populate skb header fields from Rx descriptor + * @rx_ring: rx descriptor ring packet is being transacted on + * @rx_desc: pointer to the EOP Rx descriptor + * @skb: pointer to current skb being populated + * @rx_ptype: the packet type decoded by hardware * - * Returns true if there's any budget left (e.g. the clean is finished) + * This function checks the ring, descriptor, and packet information in + * order to populate the hash, checksum, VLAN, protocol, and + * other fields within the skb. **/ -static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, const int budget) +static inline +void i40e_process_skb_fields(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc, struct sk_buff *skb, + u8 rx_ptype) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0; - u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; - u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - struct i40e_vsi *vsi = rx_ring->vsi; - u16 i = rx_ring->next_to_clean; - union i40e_rx_desc *rx_desc; - u32 rx_error, rx_status; - bool failure = false; - u8 rx_ptype; - u64 qword; - u32 copysize; + u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + u32 rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> + I40E_RXD_QW1_STATUS_SHIFT; + u32 rsyn = (rx_status & I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> + I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT; - if (budget <= 0) - return 0; + if (unlikely(rsyn)) { + i40e_ptp_rx_hwtstamp(rx_ring->vsi->back, skb, rsyn); + rx_ring->last_rx_timestamp = jiffies; + } - do { - struct i40e_rx_buffer *rx_bi; - struct sk_buff *skb; - u16 vlan_tag; - /* return some buffers to hardware, one at a time is too slow */ - if (cleaned_count >= I40E_RX_BUFFER_WRITE) { - failure = failure || - i40e_alloc_rx_buffers_ps(rx_ring, - cleaned_count); - cleaned_count = 0; - } + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); - i = rx_ring->next_to_clean; - rx_desc = I40E_RX_DESC(rx_ring, i); - qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; + /* modifies the skb - consumes the enet header */ + skb->protocol = eth_type_trans(skb, rx_ring->netdev); - if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) - break; + i40e_rx_checksum(rx_ring->vsi, skb, rx_desc); - /* This memory barrier is needed to keep us from reading - * any other fields out of the rx_desc until we know the - * DD bit is set. - */ - dma_rmb(); - /* sync header buffer for reading */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_ring->rx_bi[0].dma, - i * rx_ring->rx_hdr_len, - rx_ring->rx_hdr_len, - DMA_FROM_DEVICE); - if (i40e_rx_is_programming_status(qword)) { - i40e_clean_programming_status(rx_ring, rx_desc); - I40E_RX_INCREMENT(rx_ring, i); - continue; - } - rx_bi = &rx_ring->rx_bi[i]; - skb = rx_bi->skb; - if (likely(!skb)) { - skb = __netdev_alloc_skb_ip_align(rx_ring->netdev, - rx_ring->rx_hdr_len, - GFP_ATOMIC | - __GFP_NOWARN); - if (!skb) { - rx_ring->rx_stats.alloc_buff_failed++; - failure = true; - break; - } + skb_record_rx_queue(skb, rx_ring->queue_index); +} - /* initialize queue mapping */ - skb_record_rx_queue(skb, rx_ring->queue_index); - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_ring->rx_bi[0].dma, - i * rx_ring->rx_hdr_len, - rx_ring->rx_hdr_len, - DMA_FROM_DEVICE); - } - rx_packet_len = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - rx_header_len = (qword & I40E_RXD_QW1_LENGTH_HBUF_MASK) >> - I40E_RXD_QW1_LENGTH_HBUF_SHIFT; - rx_sph = (qword & I40E_RXD_QW1_LENGTH_SPH_MASK) >> - I40E_RXD_QW1_LENGTH_SPH_SHIFT; - - rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> - I40E_RXD_QW1_ERROR_SHIFT; - rx_hbo = rx_error & BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); - rx_error &= ~BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); +/** + * i40e_pull_tail - i40e specific version of skb_pull_tail + * @rx_ring: rx descriptor ring packet is being transacted on + * @skb: pointer to current skb being adjusted + * + * This function is an i40e specific version of __pskb_pull_tail. The + * main difference between this version and the original function is that + * this function can make several assumptions about the state of things + * that allow for significant optimizations versus the standard function. + * As a result we can do things like drop a frag and maintain an accurate + * truesize for the skb. + */ +static void i40e_pull_tail(struct i40e_ring *rx_ring, struct sk_buff *skb) +{ + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0]; + unsigned char *va; + unsigned int pull_len; - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - /* sync half-page for reading */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_bi->page_dma, - rx_bi->page_offset, - PAGE_SIZE / 2, - DMA_FROM_DEVICE); - prefetch(page_address(rx_bi->page) + rx_bi->page_offset); - rx_bi->skb = NULL; - cleaned_count++; - copysize = 0; - if (rx_hbo || rx_sph) { - int len; + /* it is valid to use page_address instead of kmap since we are + * working with pages allocated out of the lomem pool per + * alloc_page(GFP_ATOMIC) + */ + va = skb_frag_address(frag); - if (rx_hbo) - len = I40E_RX_HDR_SIZE; - else - len = rx_header_len; - memcpy(__skb_put(skb, len), rx_bi->hdr_buf, len); - } else if (skb->len == 0) { - int len; - unsigned char *va = page_address(rx_bi->page) + - rx_bi->page_offset; - - len = min(rx_packet_len, rx_ring->rx_hdr_len); - memcpy(__skb_put(skb, len), va, len); - copysize = len; - rx_packet_len -= len; - } - /* Get the rest of the data if this was a header split */ - if (rx_packet_len) { - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - rx_bi->page, - rx_bi->page_offset + copysize, - rx_packet_len, I40E_RXBUFFER_2048); - - /* If the page count is more than 2, then both halves - * of the page are used and we need to free it. Do it - * here instead of in the alloc code. Otherwise one - * of the half-pages might be released between now and - * then, and we wouldn't know which one to use. - * Don't call get_page and free_page since those are - * both expensive atomic operations that just change - * the refcount in opposite directions. Just give the - * page to the stack; he can have our refcount. - */ - if (page_count(rx_bi->page) > 2) { - dma_unmap_page(rx_ring->dev, - rx_bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - rx_bi->page = NULL; - rx_bi->page_dma = 0; - rx_ring->rx_stats.realloc_count++; - } else { - get_page(rx_bi->page); - /* switch to the other half-page here; the - * allocation code programs the right addr - * into HW. If we haven't used this half-page, - * the address won't be changed, and HW can - * just use it next time through. - */ - rx_bi->page_offset ^= PAGE_SIZE / 2; - } + /* we need the header to contain the greater of either ETH_HLEN or + * 60 bytes if the skb->len is less than 60 for skb_pad. + */ + pull_len = eth_get_headlen(va, I40E_RX_HDR_SIZE); - } - I40E_RX_INCREMENT(rx_ring, i); + /* align pull length to size of long to optimize memcpy performance */ + skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long))); - if (unlikely( - !(rx_status & BIT(I40E_RX_DESC_STATUS_EOF_SHIFT)))) { - struct i40e_rx_buffer *next_buffer; + /* update all of the pointers */ + skb_frag_size_sub(frag, pull_len); + frag->page_offset += pull_len; + skb->data_len -= pull_len; + skb->tail += pull_len; +} - next_buffer = &rx_ring->rx_bi[i]; - next_buffer->skb = skb; - rx_ring->rx_stats.non_eop_descs++; - continue; - } +/** + * i40e_cleanup_headers - Correct empty headers + * @rx_ring: rx descriptor ring packet is being transacted on + * @skb: pointer to current skb being fixed + * + * Also address the case where we are pulling data in on pages only + * and as such no data is present in the skb header. + * + * In addition if skb is not at least 60 bytes we need to pad it so that + * it is large enough to qualify as a valid Ethernet frame. + * + * Returns true if an error was encountered and skb was freed. + **/ +static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb) +{ + /* place header in linear portion of buffer */ + if (skb_is_nonlinear(skb)) + i40e_pull_tail(rx_ring, skb); - /* ERR_MASK will only have valid bits if EOP set */ - if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { - dev_kfree_skb_any(skb); - continue; - } + /* if eth_skb_pad returns an error the skb was freed */ + if (eth_skb_pad(skb)) + return true; - i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); + return false; +} - if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) { - i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status & - I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> - I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT); - rx_ring->last_rx_timestamp = jiffies; - } +/** + * i40e_reuse_rx_page - page flip buffer and store it back on the ring + * @rx_ring: rx descriptor ring to store buffers on + * @old_buff: donor buffer to have page reused + * + * Synchronizes page for reuse by the adapter + **/ +static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *old_buff) +{ + struct i40e_rx_buffer *new_buff; + u16 nta = rx_ring->next_to_alloc; - /* probably a little skewed due to removing CRC */ - total_rx_bytes += skb->len; - total_rx_packets++; + new_buff = &rx_ring->rx_bi[nta]; - skb->protocol = eth_type_trans(skb, rx_ring->netdev); + /* update, and store next to alloc */ + nta++; + rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; - i40e_rx_checksum(vsi, skb, rx_status, rx_error, rx_ptype); + /* transfer page from old buffer to new buffer */ + *new_buff = *old_buff; +} - vlan_tag = rx_status & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT) - ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) - : 0; -#ifdef I40E_FCOE - if (unlikely( - i40e_rx_is_fcoe(rx_ptype) && - !i40e_fcoe_handle_offload(rx_ring, rx_desc, skb))) { - dev_kfree_skb_any(skb); - continue; - } +/** + * i40e_page_is_reserved - check if reuse is possible + * @page: page struct to check + */ +static inline bool i40e_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); +} + +/** + * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: buffer containing page to add + * @rx_desc: descriptor containing length of buffer written by hardware + * @skb: sk_buff to place the data into + * + * This function will add the data contained in rx_buffer->page to the skb. + * This is done either through a direct copy if the data in the buffer is + * less than the skb header size, otherwise it will just attach the page as + * a frag to the skb. + * + * The function will then update the page offset if necessary and return + * true if the buffer can be reused by the adapter. + **/ +static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb) +{ + struct page *page = rx_buffer->page; + u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + unsigned int size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; +#if (PAGE_SIZE < 8192) + unsigned int truesize = I40E_RXBUFFER_2048; +#else + unsigned int truesize = ALIGN(size, L1_CACHE_BYTES); + unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif - i40e_receive_skb(rx_ring, skb, vlan_tag); - rx_desc->wb.qword1.status_error_len = 0; + /* will the data fit in the skb we allocated? if so, just + * copy it as it is pretty small anyway + */ + if ((size <= I40E_RX_HDR_SIZE) && !skb_is_nonlinear(skb)) { + unsigned char *va = page_address(page) + rx_buffer->page_offset; - } while (likely(total_rx_packets < budget)); + memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - u64_stats_update_begin(&rx_ring->syncp); - rx_ring->stats.packets += total_rx_packets; - rx_ring->stats.bytes += total_rx_bytes; - u64_stats_update_end(&rx_ring->syncp); - rx_ring->q_vector->rx.total_packets += total_rx_packets; - rx_ring->q_vector->rx.total_bytes += total_rx_bytes; + /* page is not reserved, we can reuse buffer as-is */ + if (likely(!i40e_page_is_reserved(page))) + return true; - return failure ? budget : total_rx_packets; + /* this page cannot be reused so discard it */ + __free_pages(page, 0); + return false; + } + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + rx_buffer->page_offset, size, truesize); + + /* avoid re-using remote pages */ + if (unlikely(i40e_page_is_reserved(page))) + return false; + +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ + if (unlikely(page_count(page) != 1)) + return false; + + /* flip page offset to other buffer */ + rx_buffer->page_offset ^= truesize; +#else + /* move offset up to the next cache line */ + rx_buffer->page_offset += truesize; + + if (rx_buffer->page_offset > last_offset) + return false; +#endif + + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ + get_page(rx_buffer->page); + + return true; } /** - * i40e_clean_rx_irq_1buf - Reclaim resources after receive; single buffer - * @rx_ring: rx ring to clean - * @budget: how many cleans we're allowed + * i40e_fetch_rx_buffer - Allocate skb and populate it + * @rx_ring: rx descriptor ring to transact packets on + * @rx_desc: descriptor containing info written by hardware * - * Returns number of packets cleaned + * This function allocates an skb on the fly, and populates it with the page + * data from the current receive descriptor, taking care to set up the skb + * correctly, as well as handling calling the page recycle function if + * necessary. + */ +static inline +struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc) +{ + struct i40e_rx_buffer *rx_buffer; + struct sk_buff *skb; + struct page *page; + + rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; + page = rx_buffer->page; + prefetchw(page); + + skb = rx_buffer->skb; + + if (likely(!skb)) { + void *page_addr = page_address(page) + rx_buffer->page_offset; + + /* prefetch first cache line of first page */ + prefetch(page_addr); +#if L1_CACHE_BYTES < 128 + prefetch(page_addr + L1_CACHE_BYTES); +#endif + + /* allocate a skb to store the frags */ + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + I40E_RX_HDR_SIZE, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) { + rx_ring->rx_stats.alloc_buff_failed++; + return NULL; + } + + /* we will be copying header into skb->data in + * pskb_may_pull so it is in our interest to prefetch + * it now to avoid a possible cache miss + */ + prefetchw(skb->data); + } else { + rx_buffer->skb = NULL; + } + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_buffer->dma, + rx_buffer->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + + /* pull page into skb */ + if (i40e_add_rx_frag(rx_ring, rx_buffer, rx_desc, skb)) { + /* hand second half of page back to the ring */ + i40e_reuse_rx_page(rx_ring, rx_buffer); + rx_ring->rx_stats.page_reuse_count++; + } else { + /* we are not reusing the buffer so unmap it */ + dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + DMA_FROM_DEVICE); + } + + /* clear contents of buffer_info */ + rx_buffer->page = NULL; + + return skb; +} + +/** + * i40e_is_non_eop - process handling of non-EOP buffers + * @rx_ring: Rx ring being processed + * @rx_desc: Rx descriptor for current buffer + * @skb: Current socket buffer containing buffer in progress + * + * This function updates next to clean. If the buffer is an EOP buffer + * this function exits returning false, otherwise it will place the + * sk_buff in the next buffer to be chained and return true indicating + * that this is in fact a non-EOP buffer. + **/ +static bool i40e_is_non_eop(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb) +{ + u32 ntc = rx_ring->next_to_clean + 1; + + /* fetch, update, and store next to clean */ + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + + prefetch(I40E_RX_DESC(rx_ring, ntc)); + +#define staterrlen rx_desc->wb.qword1.status_error_len + if (unlikely(i40e_rx_is_programming_status(le64_to_cpu(staterrlen)))) { + i40e_clean_programming_status(rx_ring, rx_desc); + rx_ring->rx_bi[ntc].skb = skb; + return true; + } + /* if we are the last buffer then there is nothing else to do */ +#define I40E_RXD_EOF BIT(I40E_RX_DESC_STATUS_EOF_SHIFT) + if (likely(i40e_test_staterr(rx_desc, I40E_RXD_EOF))) + return false; + + /* place skb in next buffer to be received */ + rx_ring->rx_bi[ntc].skb = skb; + rx_ring->rx_stats.non_eop_descs++; + + return true; +} + +/** + * i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf + * @rx_ring: rx descriptor ring to transact packets on + * @budget: Total limit on number of packets to process + * + * This function provides a "bounce buffer" approach to Rx interrupt + * processing. The advantage to this is that on systems that have + * expensive overhead for IOMMU access this provides a means of avoiding + * it by maintaining the mapping of the page to the system. + * + * Returns amount of work completed **/ -static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) +static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) { unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - struct i40e_vsi *vsi = rx_ring->vsi; - union i40e_rx_desc *rx_desc; - u32 rx_error, rx_status; - u16 rx_packet_len; bool failure = false; - u8 rx_ptype; - u64 qword; - u16 i; - do { - struct i40e_rx_buffer *rx_bi; + while (likely(total_rx_packets < budget)) { + union i40e_rx_desc *rx_desc; struct sk_buff *skb; + u32 rx_status; u16 vlan_tag; + u8 rx_ptype; + u64 qword; + /* return some buffers to hardware, one at a time is too slow */ if (cleaned_count >= I40E_RX_BUFFER_WRITE) { failure = failure || - i40e_alloc_rx_buffers_1buf(rx_ring, - cleaned_count); + i40e_alloc_rx_buffers(rx_ring, cleaned_count); cleaned_count = 0; } - i = rx_ring->next_to_clean; - rx_desc = I40E_RX_DESC(rx_ring, i); + rx_desc = I40E_RX_DESC(rx_ring, rx_ring->next_to_clean); + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> + I40E_RXD_QW1_PTYPE_SHIFT; rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; + I40E_RXD_QW1_STATUS_SHIFT; if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) break; + /* status_error_len will always be zero for unused descriptors + * because it's cleared in cleanup, and overlaps with hdr_addr + * which is always zero because packet split isn't used, if the + * hardware wrote DD then it will be non-zero + */ + if (!rx_desc->wb.qword1.status_error_len) + break; + /* This memory barrier is needed to keep us from reading * any other fields out of the rx_desc until we know the * DD bit is set. */ dma_rmb(); - if (i40e_rx_is_programming_status(qword)) { - i40e_clean_programming_status(rx_ring, rx_desc); - I40E_RX_INCREMENT(rx_ring, i); - continue; - } - rx_bi = &rx_ring->rx_bi[i]; - skb = rx_bi->skb; - prefetch(skb->data); - - rx_packet_len = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - - rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> - I40E_RXD_QW1_ERROR_SHIFT; - rx_error &= ~BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); + skb = i40e_fetch_rx_buffer(rx_ring, rx_desc); + if (!skb) + break; - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - rx_bi->skb = NULL; cleaned_count++; - /* Get the header and possibly the whole packet - * If this is an skb from previous receive dma will be 0 - */ - skb_put(skb, rx_packet_len); - dma_unmap_single(rx_ring->dev, rx_bi->dma, rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - rx_bi->dma = 0; - - I40E_RX_INCREMENT(rx_ring, i); - - if (unlikely( - !(rx_status & BIT(I40E_RX_DESC_STATUS_EOF_SHIFT)))) { - rx_ring->rx_stats.non_eop_descs++; + if (i40e_is_non_eop(rx_ring, rx_desc, skb)) continue; - } - /* ERR_MASK will only have valid bits if EOP set */ - if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { + /* ERR_MASK will only have valid bits if EOP set, and + * what we are doing here is actually checking + * I40E_RX_DESC_ERROR_RXE_SHIFT, since it is the zeroth bit in + * the error field + */ + if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) { dev_kfree_skb_any(skb); continue; } - i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); - if (unlikely(rx_status & I40E_RXD_QW1_STATUS_TSYNVALID_MASK)) { - i40e_ptp_rx_hwtstamp(vsi->back, skb, (rx_status & - I40E_RXD_QW1_STATUS_TSYNINDX_MASK) >> - I40E_RXD_QW1_STATUS_TSYNINDX_SHIFT); - rx_ring->last_rx_timestamp = jiffies; - } + if (i40e_cleanup_headers(rx_ring, skb)) + continue; /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; - total_rx_packets++; - skb->protocol = eth_type_trans(skb, rx_ring->netdev); + /* populate checksum, VLAN, and protocol */ + i40e_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype); - i40e_rx_checksum(vsi, skb, rx_status, rx_error, rx_ptype); - - vlan_tag = rx_status & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT) - ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) - : 0; #ifdef I40E_FCOE if (unlikely( i40e_rx_is_fcoe(rx_ptype) && @@ -1844,10 +1825,15 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) continue; } #endif + + vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ? + le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0; + i40e_receive_skb(rx_ring, skb, vlan_tag); - rx_desc->wb.qword1.status_error_len = 0; - } while (likely(total_rx_packets < budget)); + /* update budget accounting */ + total_rx_packets++; + } u64_stats_update_begin(&rx_ring->syncp); rx_ring->stats.packets += total_rx_packets; @@ -1856,6 +1842,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_packets += total_rx_packets; rx_ring->q_vector->rx.total_bytes += total_rx_bytes; + /* guarantee a trip back through this routine if there was a failure */ return failure ? budget : total_rx_packets; } @@ -2000,12 +1987,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { - int cleaned; - - if (ring_is_ps_enabled(ring)) - cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); - else - cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + int cleaned = i40e_clean_rx_irq(ring, budget_per_ring); work_done += cleaned; /* if we clean as many as budgeted, we must not be done */ @@ -2302,8 +2284,8 @@ static int i40e_tso(struct sk_buff *skb, u8 *hdr_len, u64 *cd_type_cmd_tso_mss) if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | SKB_GSO_GRE_CSUM | - SKB_GSO_IPIP | - SKB_GSO_SIT | + SKB_GSO_IPXIP4 | + SKB_GSO_IPXIP6 | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) { if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) && diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 6b2b1913527d..b78c810d1835 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -102,8 +102,8 @@ enum i40e_dyn_idx_t { (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) -/* Supported Rx Buffer Sizes */ -#define I40E_RXBUFFER_512 512 /* Used for packet split */ +/* Supported Rx Buffer Sizes (a multiple of 128) */ +#define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_2048 2048 #define I40E_RXBUFFER_3072 3072 /* For FCoE MTU of 2158 */ #define I40E_RXBUFFER_4096 4096 @@ -114,9 +114,28 @@ enum i40e_dyn_idx_t { * reserve 2 more, and skb_shared_info adds an additional 384 bytes more, * this adds up to 512 bytes of extra data meaning the smallest allocation * we could have is 1K. - * i.e. RXBUFFER_512 --> size-1024 slab + * i.e. RXBUFFER_256 --> 960 byte skb (size-1024 slab) + * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ -#define I40E_RX_HDR_SIZE I40E_RXBUFFER_512 +#define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define i40e_rx_desc i40e_32byte_rx_desc + +/** + * i40e_test_staterr - tests bits in Rx descriptor status and error fields + * @rx_desc: pointer to receive descriptor (in le64 format) + * @stat_err_bits: value to mask + * + * This function does some fast chicanery in order to return the + * value of the mask which is really only used for boolean tests. + * The status_error_len doesn't need to be shifted because it begins + * at offset zero. + */ +static inline bool i40e_test_staterr(union i40e_rx_desc *rx_desc, + const u64 stat_err_bits) +{ + return !!(rx_desc->wb.qword1.status_error_len & + cpu_to_le64(stat_err_bits)); +} /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define I40E_RX_BUFFER_WRITE 16 /* Must be power of 2 */ @@ -142,8 +161,6 @@ enum i40e_dyn_idx_t { prefetch((n)); \ } while (0) -#define i40e_rx_desc i40e_32byte_rx_desc - #define I40E_MAX_BUFFER_TXD 8 #define I40E_MIN_TX_LEN 17 @@ -213,10 +230,8 @@ struct i40e_tx_buffer { struct i40e_rx_buffer { struct sk_buff *skb; - void *hdr_buf; dma_addr_t dma; struct page *page; - dma_addr_t page_dma; unsigned int page_offset; }; @@ -245,22 +260,18 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_RX_PS_ENABLED, - __I40E_RX_16BYTE_DESC_ENABLED, }; -#define ring_is_ps_enabled(ring) \ - test_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define set_ring_ps_enabled(ring) \ - set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define clear_ring_ps_enabled(ring) \ - clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define ring_is_16byte_desc_enabled(ring) \ - test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) -#define set_ring_16byte_desc_enabled(ring) \ - set_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) -#define clear_ring_16byte_desc_enabled(ring) \ - clear_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) +/* some useful defines for virtchannel interface, which + * is the only remaining user of header split + */ +#define I40E_RX_DTYPE_NO_SPLIT 0 +#define I40E_RX_DTYPE_HEADER_SPLIT 1 +#define I40E_RX_DTYPE_SPLIT_ALWAYS 2 +#define I40E_RX_SPLIT_L2 0x1 +#define I40E_RX_SPLIT_IP 0x2 +#define I40E_RX_SPLIT_TCP_UDP 0x4 +#define I40E_RX_SPLIT_SCTP 0x8 /* struct that defines a descriptor ring, associated with a VSI */ struct i40e_ring { @@ -287,16 +298,7 @@ struct i40e_ring { u16 count; /* Number of descriptors */ u16 reg_idx; /* HW register index of the ring */ - u16 rx_hdr_len; u16 rx_buf_len; - u8 dtype; -#define I40E_RX_DTYPE_NO_SPLIT 0 -#define I40E_RX_DTYPE_HEADER_SPLIT 1 -#define I40E_RX_DTYPE_SPLIT_ALWAYS 2 -#define I40E_RX_SPLIT_L2 0x1 -#define I40E_RX_SPLIT_IP 0x2 -#define I40E_RX_SPLIT_TCP_UDP 0x4 -#define I40E_RX_SPLIT_SCTP 0x8 /* used in interrupt processing */ u16 next_to_use; @@ -330,6 +332,7 @@ struct i40e_ring { struct i40e_q_vector *q_vector; /* Backreference to associated vector */ struct rcu_head rcu; /* to avoid race on free */ + u16 next_to_alloc; } ____cacheline_internodealigned_in_smp; enum i40e_latency_range { @@ -353,9 +356,7 @@ struct i40e_ring_container { #define i40e_for_each_ring(pos, head) \ for (pos = (head).ring; pos != NULL; pos = pos->next) -bool i40e_alloc_rx_buffers_ps(struct i40e_ring *rxr, u16 cleaned_count); -bool i40e_alloc_rx_buffers_1buf(struct i40e_ring *rxr, u16 cleaned_count); -void i40e_alloc_rx_headers(struct i40e_ring *rxr); +bool i40e_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void i40e_clean_tx_ring(struct i40e_ring *tx_ring); void i40e_clean_rx_ring(struct i40e_ring *rx_ring); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 6b9db7983693..1fcafcfa8f14 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -590,7 +590,7 @@ static int i40e_config_vsi_rx_queue(struct i40e_vf *vf, u16 vsi_id, } rx_ctx.hbuff = info->hdr_size >> I40E_RXQ_CTX_HBUFF_SHIFT; - /* set splitalways mode 10b */ + /* set split mode 10b */ rx_ctx.dtype = I40E_RX_DTYPE_HEADER_SPLIT; } @@ -665,8 +665,6 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) goto error_alloc_vsi_res; } if (type == I40E_VSI_SRIOV) { - u8 brdcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - vf->lan_vsi_idx = vsi->idx; vf->lan_vsi_id = vsi->id; /* If the port VLAN has been configured and then the @@ -688,12 +686,6 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) "Could not add MAC filter %pM for VF %d\n", vf->default_lan_addr.addr, vf->vf_id); } - f = i40e_add_filter(vsi, brdcast, - vf->port_vlan_id ? vf->port_vlan_id : -1, - true, false); - if (!f) - dev_info(&pf->pdev->dev, - "Could not allocate VF broadcast filter\n"); spin_unlock_bh(&vsi->mac_filter_list_lock); } @@ -1474,12 +1466,16 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, vsi = i40e_find_vsi_from_id(pf, info->vsi_id); if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || - !test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps) || !i40e_vc_isvalid_vsi_id(vf, info->vsi_id)) { + aq_ret = I40E_ERR_PARAM; + goto error_param; + } + if (!test_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps)) { dev_err(&pf->pdev->dev, - "VF %d doesn't meet requirements to enter promiscuous mode\n", + "Unprivileged VF %d is attempting to configure promiscuous mode\n", vf->vf_id); - aq_ret = I40E_ERR_PARAM; + /* Lie to the VF on purpose. */ + aq_ret = 0; goto error_param; } /* Multicast promiscuous handling*/ @@ -1544,7 +1540,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { list_for_each_entry(f, &vsi->mac_filter_list, list) { aq_ret = 0; - if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) + if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) { aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, vsi->seid, @@ -1552,6 +1548,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, f->vlan, NULL); aq_err = pf->hw.aq.asq_last_status; + } if (aq_ret) dev_err(&pf->pdev->dev, "Could not add VLAN %d to Unicast promiscuous domain err %s aq_err %s\n", @@ -1561,7 +1558,8 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, } } else { aq_ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid, - allmulti, NULL); + allmulti, NULL, + true); aq_err = pf->hw.aq.asq_last_status; if (aq_ret) dev_err(&pf->pdev->dev, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index ede8dfc189bc..be99189da925 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -496,7 +496,6 @@ err: void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) { struct device *dev = rx_ring->dev; - struct i40e_rx_buffer *rx_bi; unsigned long bi_size; u16 i; @@ -504,48 +503,22 @@ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) if (!rx_ring->rx_bi) return; - if (ring_is_ps_enabled(rx_ring)) { - int bufsz = ALIGN(rx_ring->rx_hdr_len, 256) * rx_ring->count; - - rx_bi = &rx_ring->rx_bi[0]; - if (rx_bi->hdr_buf) { - dma_free_coherent(dev, - bufsz, - rx_bi->hdr_buf, - rx_bi->dma); - for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - rx_bi->dma = 0; - rx_bi->hdr_buf = NULL; - } - } - } /* Free all the Rx ring sk_buffs */ for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - if (rx_bi->dma) { - dma_unmap_single(dev, - rx_bi->dma, - rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - rx_bi->dma = 0; - } + struct i40e_rx_buffer *rx_bi = &rx_ring->rx_bi[i]; + if (rx_bi->skb) { dev_kfree_skb(rx_bi->skb); rx_bi->skb = NULL; } - if (rx_bi->page) { - if (rx_bi->page_dma) { - dma_unmap_page(dev, - rx_bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - rx_bi->page_dma = 0; - } - __free_page(rx_bi->page); - rx_bi->page = NULL; - rx_bi->page_offset = 0; - } + if (!rx_bi->page) + continue; + + dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE); + __free_pages(rx_bi->page, 0); + + rx_bi->page = NULL; + rx_bi->page_offset = 0; } bi_size = sizeof(struct i40e_rx_buffer) * rx_ring->count; @@ -554,6 +527,7 @@ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) /* Zero out the descriptor ring */ memset(rx_ring->desc, 0, rx_ring->size); + rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; } @@ -578,37 +552,6 @@ void i40evf_free_rx_resources(struct i40e_ring *rx_ring) } /** - * i40evf_alloc_rx_headers - allocate rx header buffers - * @rx_ring: ring to alloc buffers - * - * Allocate rx header buffers for the entire ring. As these are static, - * this is only called when setting up a new ring. - **/ -void i40evf_alloc_rx_headers(struct i40e_ring *rx_ring) -{ - struct device *dev = rx_ring->dev; - struct i40e_rx_buffer *rx_bi; - dma_addr_t dma; - void *buffer; - int buf_size; - int i; - - if (rx_ring->rx_bi[0].hdr_buf) - return; - /* Make sure the buffers don't cross cache line boundaries. */ - buf_size = ALIGN(rx_ring->rx_hdr_len, 256); - buffer = dma_alloc_coherent(dev, buf_size * rx_ring->count, - &dma, GFP_KERNEL); - if (!buffer) - return; - for (i = 0; i < rx_ring->count; i++) { - rx_bi = &rx_ring->rx_bi[i]; - rx_bi->dma = dma + (i * buf_size); - rx_bi->hdr_buf = buffer + (i * buf_size); - } -} - -/** * i40evf_setup_rx_descriptors - Allocate Rx descriptors * @rx_ring: Rx descriptor ring (for a specific queue) to setup * @@ -629,9 +572,7 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring) u64_stats_init(&rx_ring->syncp); /* Round up to nearest 4K */ - rx_ring->size = ring_is_16byte_desc_enabled(rx_ring) - ? rx_ring->count * sizeof(union i40e_16byte_rx_desc) - : rx_ring->count * sizeof(union i40e_32byte_rx_desc); + rx_ring->size = rx_ring->count * sizeof(union i40e_32byte_rx_desc); rx_ring->size = ALIGN(rx_ring->size, 4096); rx_ring->desc = dma_alloc_coherent(dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); @@ -642,6 +583,7 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring) goto err; } + rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; @@ -660,6 +602,10 @@ err: static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) { rx_ring->next_to_use = val; + + /* update next to alloc since we have filled the ring */ + rx_ring->next_to_alloc = val; + /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, @@ -670,160 +616,122 @@ static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) } /** - * i40evf_alloc_rx_buffers_ps - Replace used receive buffers; packet split - * @rx_ring: ring to place buffers on - * @cleaned_count: number of buffers to replace + * i40e_alloc_mapped_page - recycle or make a new page + * @rx_ring: ring to use + * @bi: rx_buffer struct to modify * - * Returns true if any errors on allocation + * Returns true if the page was successfully allocated or + * reused. **/ -bool i40evf_alloc_rx_buffers_ps(struct i40e_ring *rx_ring, u16 cleaned_count) +static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *bi) { - u16 i = rx_ring->next_to_use; - union i40e_rx_desc *rx_desc; - struct i40e_rx_buffer *bi; - const int current_node = numa_node_id(); + struct page *page = bi->page; + dma_addr_t dma; - /* do nothing if no valid netdev defined */ - if (!rx_ring->netdev || !cleaned_count) - return false; + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) { + rx_ring->rx_stats.page_reuse_count++; + return true; + } - while (cleaned_count--) { - rx_desc = I40E_RX_DESC(rx_ring, i); - bi = &rx_ring->rx_bi[i]; + /* alloc new page for storage */ + page = dev_alloc_page(); + if (unlikely(!page)) { + rx_ring->rx_stats.alloc_page_failed++; + return false; + } - if (bi->skb) /* desc is in use */ - goto no_buffers; + /* map page for use */ + dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); - /* If we've been moved to a different NUMA node, release the - * page so we can get a new one on the current node. + /* if mapping failed free memory back to system since + * there isn't much point in holding memory we can't use */ - if (bi->page && page_to_nid(bi->page) != current_node) { - dma_unmap_page(rx_ring->dev, - bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - __free_page(bi->page); - bi->page = NULL; - bi->page_dma = 0; - rx_ring->rx_stats.realloc_count++; - } else if (bi->page) { - rx_ring->rx_stats.page_reuse_count++; - } - - if (!bi->page) { - bi->page = alloc_page(GFP_ATOMIC); - if (!bi->page) { - rx_ring->rx_stats.alloc_page_failed++; - goto no_buffers; - } - bi->page_dma = dma_map_page(rx_ring->dev, - bi->page, - 0, - PAGE_SIZE, - DMA_FROM_DEVICE); - if (dma_mapping_error(rx_ring->dev, bi->page_dma)) { - rx_ring->rx_stats.alloc_page_failed++; - __free_page(bi->page); - bi->page = NULL; - bi->page_dma = 0; - bi->page_offset = 0; - goto no_buffers; - } - bi->page_offset = 0; - } - - /* Refresh the desc even if buffer_addrs didn't change - * because each write-back erases this info. - */ - rx_desc->read.pkt_addr = - cpu_to_le64(bi->page_dma + bi->page_offset); - rx_desc->read.hdr_addr = cpu_to_le64(bi->dma); - i++; - if (i == rx_ring->count) - i = 0; + if (dma_mapping_error(rx_ring->dev, dma)) { + __free_pages(page, 0); + rx_ring->rx_stats.alloc_page_failed++; + return false; } - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + bi->dma = dma; + bi->page = page; + bi->page_offset = 0; - return false; + return true; +} -no_buffers: - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); +/** + * i40e_receive_skb - Send a completed packet up the stack + * @rx_ring: rx ring in play + * @skb: packet to send up + * @vlan_tag: vlan tag for packet + **/ +static void i40e_receive_skb(struct i40e_ring *rx_ring, + struct sk_buff *skb, u16 vlan_tag) +{ + struct i40e_q_vector *q_vector = rx_ring->q_vector; - /* make sure to come back via polling to try again after - * allocation failure - */ - return true; + if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + (vlan_tag & VLAN_VID_MASK)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); + + napi_gro_receive(&q_vector->napi, skb); } /** - * i40evf_alloc_rx_buffers_1buf - Replace used receive buffers; single buffer + * i40evf_alloc_rx_buffers - Replace used receive buffers * @rx_ring: ring to place buffers on * @cleaned_count: number of buffers to replace * - * Returns true if any errors on allocation + * Returns false if all allocations were successful, true if any fail **/ -bool i40evf_alloc_rx_buffers_1buf(struct i40e_ring *rx_ring, u16 cleaned_count) +bool i40evf_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) { - u16 i = rx_ring->next_to_use; + u16 ntu = rx_ring->next_to_use; union i40e_rx_desc *rx_desc; struct i40e_rx_buffer *bi; - struct sk_buff *skb; /* do nothing if no valid netdev defined */ if (!rx_ring->netdev || !cleaned_count) return false; - while (cleaned_count--) { - rx_desc = I40E_RX_DESC(rx_ring, i); - bi = &rx_ring->rx_bi[i]; - skb = bi->skb; - - if (!skb) { - skb = __netdev_alloc_skb_ip_align(rx_ring->netdev, - rx_ring->rx_buf_len, - GFP_ATOMIC | - __GFP_NOWARN); - if (!skb) { - rx_ring->rx_stats.alloc_buff_failed++; - goto no_buffers; - } - /* initialize queue mapping */ - skb_record_rx_queue(skb, rx_ring->queue_index); - bi->skb = skb; - } + rx_desc = I40E_RX_DESC(rx_ring, ntu); + bi = &rx_ring->rx_bi[ntu]; - if (!bi->dma) { - bi->dma = dma_map_single(rx_ring->dev, - skb->data, - rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - if (dma_mapping_error(rx_ring->dev, bi->dma)) { - rx_ring->rx_stats.alloc_buff_failed++; - bi->dma = 0; - dev_kfree_skb(bi->skb); - bi->skb = NULL; - goto no_buffers; - } - } + do { + if (!i40e_alloc_mapped_page(rx_ring, bi)) + goto no_buffers; - rx_desc->read.pkt_addr = cpu_to_le64(bi->dma); + /* Refresh the desc even if buffer_addrs didn't change + * because each write-back erases this info. + */ + rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset); rx_desc->read.hdr_addr = 0; - i++; - if (i == rx_ring->count) - i = 0; - } - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + rx_desc++; + bi++; + ntu++; + if (unlikely(ntu == rx_ring->count)) { + rx_desc = I40E_RX_DESC(rx_ring, 0); + bi = rx_ring->rx_bi; + ntu = 0; + } + + /* clear the status bits for the next_to_use descriptor */ + rx_desc->wb.qword1.status_error_len = 0; + + cleaned_count--; + } while (cleaned_count); + + if (rx_ring->next_to_use != ntu) + i40e_release_rx_desc(rx_ring, ntu); return false; no_buffers: - if (rx_ring->next_to_use != i) - i40e_release_rx_desc(rx_ring, i); + if (rx_ring->next_to_use != ntu) + i40e_release_rx_desc(rx_ring, ntu); /* make sure to come back via polling to try again after * allocation failure @@ -832,42 +740,35 @@ no_buffers: } /** - * i40e_receive_skb - Send a completed packet up the stack - * @rx_ring: rx ring in play - * @skb: packet to send up - * @vlan_tag: vlan tag for packet - **/ -static void i40e_receive_skb(struct i40e_ring *rx_ring, - struct sk_buff *skb, u16 vlan_tag) -{ - struct i40e_q_vector *q_vector = rx_ring->q_vector; - - if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - (vlan_tag & VLAN_VID_MASK)) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); - - napi_gro_receive(&q_vector->napi, skb); -} - -/** * i40e_rx_checksum - Indicate in skb if hw indicated a good cksum * @vsi: the VSI we care about * @skb: skb currently being received and modified - * @rx_status: status value of last descriptor in packet - * @rx_error: error value of last descriptor in packet - * @rx_ptype: ptype value of last descriptor in packet + * @rx_desc: the receive descriptor + * + * skb->protocol must be set before this function is called **/ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, struct sk_buff *skb, - u32 rx_status, - u32 rx_error, - u16 rx_ptype) + union i40e_rx_desc *rx_desc) { - struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype); - bool ipv4, ipv6, ipv4_tunnel, ipv6_tunnel; + struct i40e_rx_ptype_decoded decoded; + bool ipv4, ipv6, tunnel = false; + u32 rx_error, rx_status; + u8 ptype; + u64 qword; + + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> I40E_RXD_QW1_PTYPE_SHIFT; + rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> + I40E_RXD_QW1_ERROR_SHIFT; + rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> + I40E_RXD_QW1_STATUS_SHIFT; + decoded = decode_rx_desc_ptype(ptype); skb->ip_summed = CHECKSUM_NONE; + skb_checksum_none_assert(skb); + /* Rx csum enabled and ip headers found? */ if (!(vsi->netdev->features & NETIF_F_RXCSUM)) return; @@ -913,14 +814,13 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, * doesn't make it a hard requirement so if we have validated the * inner checksum report CHECKSUM_UNNECESSARY. */ - - ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) && - (rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4); - ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) && - (rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4); + if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP | + I40E_RX_PTYPE_INNER_PROT_UDP | + I40E_RX_PTYPE_INNER_PROT_SCTP)) + tunnel = true; skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->csum_level = ipv4_tunnel || ipv6_tunnel; + skb->csum_level = tunnel ? 1 : 0; return; @@ -934,7 +834,7 @@ checksum_fail: * * Returns a hash type to be used by skb_set_hash **/ -static inline enum pkt_hash_types i40e_ptype_to_htype(u8 ptype) +static inline int i40e_ptype_to_htype(u8 ptype) { struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(ptype); @@ -962,7 +862,7 @@ static inline void i40e_rx_hash(struct i40e_ring *ring, u8 rx_ptype) { u32 hash; - const __le64 rss_mask = + const __le64 rss_mask = cpu_to_le64((u64)I40E_RX_DESC_FLTSTAT_RSS_HASH << I40E_RX_DESC_STATUS_FLTSTAT_SHIFT); @@ -976,315 +876,411 @@ static inline void i40e_rx_hash(struct i40e_ring *ring, } /** - * i40e_clean_rx_irq_ps - Reclaim resources after receive; packet split - * @rx_ring: rx ring to clean - * @budget: how many cleans we're allowed + * i40evf_process_skb_fields - Populate skb header fields from Rx descriptor + * @rx_ring: rx descriptor ring packet is being transacted on + * @rx_desc: pointer to the EOP Rx descriptor + * @skb: pointer to current skb being populated + * @rx_ptype: the packet type decoded by hardware * - * Returns true if there's any budget left (e.g. the clean is finished) + * This function checks the ring, descriptor, and packet information in + * order to populate the hash, checksum, VLAN, protocol, and + * other fields within the skb. **/ -static int i40e_clean_rx_irq_ps(struct i40e_ring *rx_ring, const int budget) +static inline +void i40evf_process_skb_fields(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc, struct sk_buff *skb, + u8 rx_ptype) { - unsigned int total_rx_bytes = 0, total_rx_packets = 0; - u16 rx_packet_len, rx_header_len, rx_sph, rx_hbo; - u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - struct i40e_vsi *vsi = rx_ring->vsi; - u16 i = rx_ring->next_to_clean; - union i40e_rx_desc *rx_desc; - u32 rx_error, rx_status; - bool failure = false; - u8 rx_ptype; - u64 qword; - u32 copysize; + i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); - do { - struct i40e_rx_buffer *rx_bi; - struct sk_buff *skb; - u16 vlan_tag; - /* return some buffers to hardware, one at a time is too slow */ - if (cleaned_count >= I40E_RX_BUFFER_WRITE) { - failure = failure || - i40evf_alloc_rx_buffers_ps(rx_ring, - cleaned_count); - cleaned_count = 0; - } + /* modifies the skb - consumes the enet header */ + skb->protocol = eth_type_trans(skb, rx_ring->netdev); - i = rx_ring->next_to_clean; - rx_desc = I40E_RX_DESC(rx_ring, i); - qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; + i40e_rx_checksum(rx_ring->vsi, skb, rx_desc); - if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) - break; + skb_record_rx_queue(skb, rx_ring->queue_index); +} - /* This memory barrier is needed to keep us from reading - * any other fields out of the rx_desc until we know the - * DD bit is set. - */ - dma_rmb(); - /* sync header buffer for reading */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_ring->rx_bi[0].dma, - i * rx_ring->rx_hdr_len, - rx_ring->rx_hdr_len, - DMA_FROM_DEVICE); - rx_bi = &rx_ring->rx_bi[i]; - skb = rx_bi->skb; - if (likely(!skb)) { - skb = __netdev_alloc_skb_ip_align(rx_ring->netdev, - rx_ring->rx_hdr_len, - GFP_ATOMIC | - __GFP_NOWARN); - if (!skb) { - rx_ring->rx_stats.alloc_buff_failed++; - failure = true; - break; - } +/** + * i40e_pull_tail - i40e specific version of skb_pull_tail + * @rx_ring: rx descriptor ring packet is being transacted on + * @skb: pointer to current skb being adjusted + * + * This function is an i40e specific version of __pskb_pull_tail. The + * main difference between this version and the original function is that + * this function can make several assumptions about the state of things + * that allow for significant optimizations versus the standard function. + * As a result we can do things like drop a frag and maintain an accurate + * truesize for the skb. + */ +static void i40e_pull_tail(struct i40e_ring *rx_ring, struct sk_buff *skb) +{ + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0]; + unsigned char *va; + unsigned int pull_len; - /* initialize queue mapping */ - skb_record_rx_queue(skb, rx_ring->queue_index); - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_ring->rx_bi[0].dma, - i * rx_ring->rx_hdr_len, - rx_ring->rx_hdr_len, - DMA_FROM_DEVICE); - } - rx_packet_len = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - rx_header_len = (qword & I40E_RXD_QW1_LENGTH_HBUF_MASK) >> - I40E_RXD_QW1_LENGTH_HBUF_SHIFT; - rx_sph = (qword & I40E_RXD_QW1_LENGTH_SPH_MASK) >> - I40E_RXD_QW1_LENGTH_SPH_SHIFT; - - rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> - I40E_RXD_QW1_ERROR_SHIFT; - rx_hbo = rx_error & BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); - rx_error &= ~BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); + /* it is valid to use page_address instead of kmap since we are + * working with pages allocated out of the lomem pool per + * alloc_page(GFP_ATOMIC) + */ + va = skb_frag_address(frag); - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - /* sync half-page for reading */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_bi->page_dma, - rx_bi->page_offset, - PAGE_SIZE / 2, - DMA_FROM_DEVICE); - prefetch(page_address(rx_bi->page) + rx_bi->page_offset); - rx_bi->skb = NULL; - cleaned_count++; - copysize = 0; - if (rx_hbo || rx_sph) { - int len; - - if (rx_hbo) - len = I40E_RX_HDR_SIZE; - else - len = rx_header_len; - memcpy(__skb_put(skb, len), rx_bi->hdr_buf, len); - } else if (skb->len == 0) { - int len; - unsigned char *va = page_address(rx_bi->page) + - rx_bi->page_offset; - - len = min(rx_packet_len, rx_ring->rx_hdr_len); - memcpy(__skb_put(skb, len), va, len); - copysize = len; - rx_packet_len -= len; - } - /* Get the rest of the data if this was a header split */ - if (rx_packet_len) { - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - rx_bi->page, - rx_bi->page_offset + copysize, - rx_packet_len, I40E_RXBUFFER_2048); - - /* If the page count is more than 2, then both halves - * of the page are used and we need to free it. Do it - * here instead of in the alloc code. Otherwise one - * of the half-pages might be released between now and - * then, and we wouldn't know which one to use. - * Don't call get_page and free_page since those are - * both expensive atomic operations that just change - * the refcount in opposite directions. Just give the - * page to the stack; he can have our refcount. - */ - if (page_count(rx_bi->page) > 2) { - dma_unmap_page(rx_ring->dev, - rx_bi->page_dma, - PAGE_SIZE, - DMA_FROM_DEVICE); - rx_bi->page = NULL; - rx_bi->page_dma = 0; - rx_ring->rx_stats.realloc_count++; - } else { - get_page(rx_bi->page); - /* switch to the other half-page here; the - * allocation code programs the right addr - * into HW. If we haven't used this half-page, - * the address won't be changed, and HW can - * just use it next time through. - */ - rx_bi->page_offset ^= PAGE_SIZE / 2; - } + /* we need the header to contain the greater of either ETH_HLEN or + * 60 bytes if the skb->len is less than 60 for skb_pad. + */ + pull_len = eth_get_headlen(va, I40E_RX_HDR_SIZE); - } - I40E_RX_INCREMENT(rx_ring, i); + /* align pull length to size of long to optimize memcpy performance */ + skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long))); - if (unlikely( - !(rx_status & BIT(I40E_RX_DESC_STATUS_EOF_SHIFT)))) { - struct i40e_rx_buffer *next_buffer; + /* update all of the pointers */ + skb_frag_size_sub(frag, pull_len); + frag->page_offset += pull_len; + skb->data_len -= pull_len; + skb->tail += pull_len; +} - next_buffer = &rx_ring->rx_bi[i]; - next_buffer->skb = skb; - rx_ring->rx_stats.non_eop_descs++; - continue; - } +/** + * i40e_cleanup_headers - Correct empty headers + * @rx_ring: rx descriptor ring packet is being transacted on + * @skb: pointer to current skb being fixed + * + * Also address the case where we are pulling data in on pages only + * and as such no data is present in the skb header. + * + * In addition if skb is not at least 60 bytes we need to pad it so that + * it is large enough to qualify as a valid Ethernet frame. + * + * Returns true if an error was encountered and skb was freed. + **/ +static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb) +{ + /* place header in linear portion of buffer */ + if (skb_is_nonlinear(skb)) + i40e_pull_tail(rx_ring, skb); - /* ERR_MASK will only have valid bits if EOP set */ - if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { - dev_kfree_skb_any(skb); - continue; - } + /* if eth_skb_pad returns an error the skb was freed */ + if (eth_skb_pad(skb)) + return true; - i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); + return false; +} - /* probably a little skewed due to removing CRC */ - total_rx_bytes += skb->len; - total_rx_packets++; +/** + * i40e_reuse_rx_page - page flip buffer and store it back on the ring + * @rx_ring: rx descriptor ring to store buffers on + * @old_buff: donor buffer to have page reused + * + * Synchronizes page for reuse by the adapter + **/ +static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *old_buff) +{ + struct i40e_rx_buffer *new_buff; + u16 nta = rx_ring->next_to_alloc; - skb->protocol = eth_type_trans(skb, rx_ring->netdev); + new_buff = &rx_ring->rx_bi[nta]; - i40e_rx_checksum(vsi, skb, rx_status, rx_error, rx_ptype); + /* update, and store next to alloc */ + nta++; + rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; - vlan_tag = rx_status & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT) - ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) - : 0; -#ifdef I40E_FCOE - if (unlikely( - i40e_rx_is_fcoe(rx_ptype) && - !i40e_fcoe_handle_offload(rx_ring, rx_desc, skb))) { - dev_kfree_skb_any(skb); - continue; - } + /* transfer page from old buffer to new buffer */ + *new_buff = *old_buff; +} + +/** + * i40e_page_is_reserved - check if reuse is possible + * @page: page struct to check + */ +static inline bool i40e_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); +} + +/** + * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: buffer containing page to add + * @rx_desc: descriptor containing length of buffer written by hardware + * @skb: sk_buff to place the data into + * + * This function will add the data contained in rx_buffer->page to the skb. + * This is done either through a direct copy if the data in the buffer is + * less than the skb header size, otherwise it will just attach the page as + * a frag to the skb. + * + * The function will then update the page offset if necessary and return + * true if the buffer can be reused by the adapter. + **/ +static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb) +{ + struct page *page = rx_buffer->page; + u64 qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + unsigned int size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; +#if (PAGE_SIZE < 8192) + unsigned int truesize = I40E_RXBUFFER_2048; +#else + unsigned int truesize = ALIGN(size, L1_CACHE_BYTES); + unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif - i40e_receive_skb(rx_ring, skb, vlan_tag); - rx_desc->wb.qword1.status_error_len = 0; + /* will the data fit in the skb we allocated? if so, just + * copy it as it is pretty small anyway + */ + if ((size <= I40E_RX_HDR_SIZE) && !skb_is_nonlinear(skb)) { + unsigned char *va = page_address(page) + rx_buffer->page_offset; - } while (likely(total_rx_packets < budget)); + memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - u64_stats_update_begin(&rx_ring->syncp); - rx_ring->stats.packets += total_rx_packets; - rx_ring->stats.bytes += total_rx_bytes; - u64_stats_update_end(&rx_ring->syncp); - rx_ring->q_vector->rx.total_packets += total_rx_packets; - rx_ring->q_vector->rx.total_bytes += total_rx_bytes; + /* page is not reserved, we can reuse buffer as-is */ + if (likely(!i40e_page_is_reserved(page))) + return true; - return failure ? budget : total_rx_packets; + /* this page cannot be reused so discard it */ + __free_pages(page, 0); + return false; + } + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + rx_buffer->page_offset, size, truesize); + + /* avoid re-using remote pages */ + if (unlikely(i40e_page_is_reserved(page))) + return false; + +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ + if (unlikely(page_count(page) != 1)) + return false; + + /* flip page offset to other buffer */ + rx_buffer->page_offset ^= truesize; +#else + /* move offset up to the next cache line */ + rx_buffer->page_offset += truesize; + + if (rx_buffer->page_offset > last_offset) + return false; +#endif + + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ + get_page(rx_buffer->page); + + return true; +} + +/** + * i40evf_fetch_rx_buffer - Allocate skb and populate it + * @rx_ring: rx descriptor ring to transact packets on + * @rx_desc: descriptor containing info written by hardware + * + * This function allocates an skb on the fly, and populates it with the page + * data from the current receive descriptor, taking care to set up the skb + * correctly, as well as handling calling the page recycle function if + * necessary. + */ +static inline +struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc) +{ + struct i40e_rx_buffer *rx_buffer; + struct sk_buff *skb; + struct page *page; + + rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; + page = rx_buffer->page; + prefetchw(page); + + skb = rx_buffer->skb; + + if (likely(!skb)) { + void *page_addr = page_address(page) + rx_buffer->page_offset; + + /* prefetch first cache line of first page */ + prefetch(page_addr); +#if L1_CACHE_BYTES < 128 + prefetch(page_addr + L1_CACHE_BYTES); +#endif + + /* allocate a skb to store the frags */ + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + I40E_RX_HDR_SIZE, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) { + rx_ring->rx_stats.alloc_buff_failed++; + return NULL; + } + + /* we will be copying header into skb->data in + * pskb_may_pull so it is in our interest to prefetch + * it now to avoid a possible cache miss + */ + prefetchw(skb->data); + } else { + rx_buffer->skb = NULL; + } + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_buffer->dma, + rx_buffer->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + + /* pull page into skb */ + if (i40e_add_rx_frag(rx_ring, rx_buffer, rx_desc, skb)) { + /* hand second half of page back to the ring */ + i40e_reuse_rx_page(rx_ring, rx_buffer); + rx_ring->rx_stats.page_reuse_count++; + } else { + /* we are not reusing the buffer so unmap it */ + dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + DMA_FROM_DEVICE); + } + + /* clear contents of buffer_info */ + rx_buffer->page = NULL; + + return skb; +} + +/** + * i40e_is_non_eop - process handling of non-EOP buffers + * @rx_ring: Rx ring being processed + * @rx_desc: Rx descriptor for current buffer + * @skb: Current socket buffer containing buffer in progress + * + * This function updates next to clean. If the buffer is an EOP buffer + * this function exits returning false, otherwise it will place the + * sk_buff in the next buffer to be chained and return true indicating + * that this is in fact a non-EOP buffer. + **/ +static bool i40e_is_non_eop(struct i40e_ring *rx_ring, + union i40e_rx_desc *rx_desc, + struct sk_buff *skb) +{ + u32 ntc = rx_ring->next_to_clean + 1; + + /* fetch, update, and store next to clean */ + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + + prefetch(I40E_RX_DESC(rx_ring, ntc)); + + /* if we are the last buffer then there is nothing else to do */ +#define I40E_RXD_EOF BIT(I40E_RX_DESC_STATUS_EOF_SHIFT) + if (likely(i40e_test_staterr(rx_desc, I40E_RXD_EOF))) + return false; + + /* place skb in next buffer to be received */ + rx_ring->rx_bi[ntc].skb = skb; + rx_ring->rx_stats.non_eop_descs++; + + return true; } /** - * i40e_clean_rx_irq_1buf - Reclaim resources after receive; single buffer - * @rx_ring: rx ring to clean - * @budget: how many cleans we're allowed + * i40e_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf + * @rx_ring: rx descriptor ring to transact packets on + * @budget: Total limit on number of packets to process * - * Returns number of packets cleaned + * This function provides a "bounce buffer" approach to Rx interrupt + * processing. The advantage to this is that on systems that have + * expensive overhead for IOMMU access this provides a means of avoiding + * it by maintaining the mapping of the page to the system. + * + * Returns amount of work completed **/ -static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) +static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) { unsigned int total_rx_bytes = 0, total_rx_packets = 0; u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); - struct i40e_vsi *vsi = rx_ring->vsi; - union i40e_rx_desc *rx_desc; - u32 rx_error, rx_status; - u16 rx_packet_len; bool failure = false; - u8 rx_ptype; - u64 qword; - u16 i; - do { - struct i40e_rx_buffer *rx_bi; + while (likely(total_rx_packets < budget)) { + union i40e_rx_desc *rx_desc; struct sk_buff *skb; + u32 rx_status; u16 vlan_tag; + u8 rx_ptype; + u64 qword; + /* return some buffers to hardware, one at a time is too slow */ if (cleaned_count >= I40E_RX_BUFFER_WRITE) { failure = failure || - i40evf_alloc_rx_buffers_1buf(rx_ring, - cleaned_count); + i40evf_alloc_rx_buffers(rx_ring, cleaned_count); cleaned_count = 0; } - i = rx_ring->next_to_clean; - rx_desc = I40E_RX_DESC(rx_ring, i); + rx_desc = I40E_RX_DESC(rx_ring, rx_ring->next_to_clean); + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> + I40E_RXD_QW1_PTYPE_SHIFT; rx_status = (qword & I40E_RXD_QW1_STATUS_MASK) >> - I40E_RXD_QW1_STATUS_SHIFT; + I40E_RXD_QW1_STATUS_SHIFT; if (!(rx_status & BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) break; + /* status_error_len will always be zero for unused descriptors + * because it's cleared in cleanup, and overlaps with hdr_addr + * which is always zero because packet split isn't used, if the + * hardware wrote DD then it will be non-zero + */ + if (!rx_desc->wb.qword1.status_error_len) + break; + /* This memory barrier is needed to keep us from reading * any other fields out of the rx_desc until we know the * DD bit is set. */ dma_rmb(); - rx_bi = &rx_ring->rx_bi[i]; - skb = rx_bi->skb; - prefetch(skb->data); - - rx_packet_len = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - - rx_error = (qword & I40E_RXD_QW1_ERROR_MASK) >> - I40E_RXD_QW1_ERROR_SHIFT; - rx_error &= ~BIT(I40E_RX_DESC_ERROR_HBO_SHIFT); + skb = i40evf_fetch_rx_buffer(rx_ring, rx_desc); + if (!skb) + break; - rx_ptype = (qword & I40E_RXD_QW1_PTYPE_MASK) >> - I40E_RXD_QW1_PTYPE_SHIFT; - rx_bi->skb = NULL; cleaned_count++; - /* Get the header and possibly the whole packet - * If this is an skb from previous receive dma will be 0 - */ - skb_put(skb, rx_packet_len); - dma_unmap_single(rx_ring->dev, rx_bi->dma, rx_ring->rx_buf_len, - DMA_FROM_DEVICE); - rx_bi->dma = 0; - - I40E_RX_INCREMENT(rx_ring, i); - - if (unlikely( - !(rx_status & BIT(I40E_RX_DESC_STATUS_EOF_SHIFT)))) { - rx_ring->rx_stats.non_eop_descs++; + if (i40e_is_non_eop(rx_ring, rx_desc, skb)) continue; - } - /* ERR_MASK will only have valid bits if EOP set */ - if (unlikely(rx_error & BIT(I40E_RX_DESC_ERROR_RXE_SHIFT))) { + /* ERR_MASK will only have valid bits if EOP set, and + * what we are doing here is actually checking + * I40E_RX_DESC_ERROR_RXE_SHIFT, since it is the zeroth bit in + * the error field + */ + if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) { dev_kfree_skb_any(skb); continue; } - i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); + if (i40e_cleanup_headers(rx_ring, skb)) + continue; + /* probably a little skewed due to removing CRC */ total_rx_bytes += skb->len; - total_rx_packets++; - skb->protocol = eth_type_trans(skb, rx_ring->netdev); + /* populate checksum, VLAN, and protocol */ + i40evf_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype); - i40e_rx_checksum(vsi, skb, rx_status, rx_error, rx_ptype); - vlan_tag = rx_status & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT) - ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) - : 0; + vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ? + le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0; + i40e_receive_skb(rx_ring, skb, vlan_tag); - rx_desc->wb.qword1.status_error_len = 0; - } while (likely(total_rx_packets < budget)); + /* update budget accounting */ + total_rx_packets++; + } u64_stats_update_begin(&rx_ring->syncp); rx_ring->stats.packets += total_rx_packets; @@ -1293,6 +1289,7 @@ static int i40e_clean_rx_irq_1buf(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_packets += total_rx_packets; rx_ring->q_vector->rx.total_bytes += total_rx_bytes; + /* guarantee a trip back through this routine if there was a failure */ return failure ? budget : total_rx_packets; } @@ -1434,12 +1431,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) budget_per_ring = max(budget/q_vector->num_ringpairs, 1); i40e_for_each_ring(ring, q_vector->rx) { - int cleaned; - - if (ring_is_ps_enabled(ring)) - cleaned = i40e_clean_rx_irq_ps(ring, budget_per_ring); - else - cleaned = i40e_clean_rx_irq_1buf(ring, budget_per_ring); + int cleaned = i40e_clean_rx_irq(ring, budget_per_ring); work_done += cleaned; /* if we clean as many as budgeted, we must not be done */ @@ -1567,8 +1559,8 @@ static int i40e_tso(struct sk_buff *skb, u8 *hdr_len, u64 *cd_type_cmd_tso_mss) if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | SKB_GSO_GRE_CSUM | - SKB_GSO_IPIP | - SKB_GSO_SIT | + SKB_GSO_IPXIP4 | + SKB_GSO_IPXIP6 | SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM)) { if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) && diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 54b52e8f7097..0112277e5882 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -102,8 +102,8 @@ enum i40e_dyn_idx_t { (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) -/* Supported Rx Buffer Sizes */ -#define I40E_RXBUFFER_512 512 /* Used for packet split */ +/* Supported Rx Buffer Sizes (a multiple of 128) */ +#define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_2048 2048 #define I40E_RXBUFFER_3072 3072 /* For FCoE MTU of 2158 */ #define I40E_RXBUFFER_4096 4096 @@ -114,9 +114,28 @@ enum i40e_dyn_idx_t { * reserve 2 more, and skb_shared_info adds an additional 384 bytes more, * this adds up to 512 bytes of extra data meaning the smallest allocation * we could have is 1K. - * i.e. RXBUFFER_512 --> size-1024 slab + * i.e. RXBUFFER_256 --> 960 byte skb (size-1024 slab) + * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ -#define I40E_RX_HDR_SIZE I40E_RXBUFFER_512 +#define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define i40e_rx_desc i40e_32byte_rx_desc + +/** + * i40e_test_staterr - tests bits in Rx descriptor status and error fields + * @rx_desc: pointer to receive descriptor (in le64 format) + * @stat_err_bits: value to mask + * + * This function does some fast chicanery in order to return the + * value of the mask which is really only used for boolean tests. + * The status_error_len doesn't need to be shifted because it begins + * at offset zero. + */ +static inline bool i40e_test_staterr(union i40e_rx_desc *rx_desc, + const u64 stat_err_bits) +{ + return !!(rx_desc->wb.qword1.status_error_len & + cpu_to_le64(stat_err_bits)); +} /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define I40E_RX_BUFFER_WRITE 16 /* Must be power of 2 */ @@ -142,8 +161,6 @@ enum i40e_dyn_idx_t { prefetch((n)); \ } while (0) -#define i40e_rx_desc i40e_32byte_rx_desc - #define I40E_MAX_BUFFER_TXD 8 #define I40E_MIN_TX_LEN 17 @@ -212,10 +229,8 @@ struct i40e_tx_buffer { struct i40e_rx_buffer { struct sk_buff *skb; - void *hdr_buf; dma_addr_t dma; struct page *page; - dma_addr_t page_dma; unsigned int page_offset; }; @@ -244,22 +259,18 @@ struct i40e_rx_queue_stats { enum i40e_ring_state_t { __I40E_TX_FDIR_INIT_DONE, __I40E_TX_XPS_INIT_DONE, - __I40E_RX_PS_ENABLED, - __I40E_RX_16BYTE_DESC_ENABLED, }; -#define ring_is_ps_enabled(ring) \ - test_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define set_ring_ps_enabled(ring) \ - set_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define clear_ring_ps_enabled(ring) \ - clear_bit(__I40E_RX_PS_ENABLED, &(ring)->state) -#define ring_is_16byte_desc_enabled(ring) \ - test_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) -#define set_ring_16byte_desc_enabled(ring) \ - set_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) -#define clear_ring_16byte_desc_enabled(ring) \ - clear_bit(__I40E_RX_16BYTE_DESC_ENABLED, &(ring)->state) +/* some useful defines for virtchannel interface, which + * is the only remaining user of header split + */ +#define I40E_RX_DTYPE_NO_SPLIT 0 +#define I40E_RX_DTYPE_HEADER_SPLIT 1 +#define I40E_RX_DTYPE_SPLIT_ALWAYS 2 +#define I40E_RX_SPLIT_L2 0x1 +#define I40E_RX_SPLIT_IP 0x2 +#define I40E_RX_SPLIT_TCP_UDP 0x4 +#define I40E_RX_SPLIT_SCTP 0x8 /* struct that defines a descriptor ring, associated with a VSI */ struct i40e_ring { @@ -278,16 +289,7 @@ struct i40e_ring { u16 count; /* Number of descriptors */ u16 reg_idx; /* HW register index of the ring */ - u16 rx_hdr_len; u16 rx_buf_len; - u8 dtype; -#define I40E_RX_DTYPE_NO_SPLIT 0 -#define I40E_RX_DTYPE_HEADER_SPLIT 1 -#define I40E_RX_DTYPE_SPLIT_ALWAYS 2 -#define I40E_RX_SPLIT_L2 0x1 -#define I40E_RX_SPLIT_IP 0x2 -#define I40E_RX_SPLIT_TCP_UDP 0x4 -#define I40E_RX_SPLIT_SCTP 0x8 /* used in interrupt processing */ u16 next_to_use; @@ -319,6 +321,7 @@ struct i40e_ring { struct i40e_q_vector *q_vector; /* Backreference to associated vector */ struct rcu_head rcu; /* to avoid race on free */ + u16 next_to_alloc; } ____cacheline_internodealigned_in_smp; enum i40e_latency_range { @@ -342,9 +345,7 @@ struct i40e_ring_container { #define i40e_for_each_ring(pos, head) \ for (pos = (head).ring; pos != NULL; pos = pos->next) -bool i40evf_alloc_rx_buffers_ps(struct i40e_ring *rxr, u16 cleaned_count); -bool i40evf_alloc_rx_buffers_1buf(struct i40e_ring *rxr, u16 cleaned_count); -void i40evf_alloc_rx_headers(struct i40e_ring *rxr); +bool i40evf_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40evf_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void i40evf_clean_tx_ring(struct i40e_ring *tx_ring); void i40evf_clean_rx_ring(struct i40e_ring *rx_ring); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 25afabf999d0..76ed97db28e2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -80,9 +80,6 @@ struct i40e_vsi { #define I40EVF_REQ_DESCRIPTOR_MULTIPLE 32 /* Supported Rx Buffer Sizes */ -#define I40EVF_RXBUFFER_64 64 /* Used for packet split */ -#define I40EVF_RXBUFFER_128 128 /* Used for packet split */ -#define I40EVF_RXBUFFER_256 256 /* Used for packet split */ #define I40EVF_RXBUFFER_2048 2048 #define I40EVF_MAX_RXBUFFER 16384 /* largest size for single descriptor */ #define I40EVF_MAX_AQ_BUF_SIZE 4096 @@ -208,9 +205,6 @@ struct i40evf_adapter { u32 flags; #define I40EVF_FLAG_RX_CSUM_ENABLED BIT(0) -#define I40EVF_FLAG_RX_1BUF_CAPABLE BIT(1) -#define I40EVF_FLAG_RX_PS_CAPABLE BIT(2) -#define I40EVF_FLAG_RX_PS_ENABLED BIT(3) #define I40EVF_FLAG_IMIR_ENABLED BIT(5) #define I40EVF_FLAG_MQ_CAPABLE BIT(6) #define I40EVF_FLAG_NEED_LINK_UPDATE BIT(7) @@ -221,6 +215,7 @@ struct i40evf_adapter { #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE BIT(12) #define I40EVF_FLAG_ADDR_SET_BY_PF BIT(13) #define I40EVF_FLAG_PROMISC_ON BIT(15) +#define I40EVF_FLAG_ALLMULTI_ON BIT(16) /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 @@ -247,6 +242,8 @@ struct i40evf_adapter { #define I40EVF_FLAG_AQ_SET_RSS_LUT BIT(14) #define I40EVF_FLAG_AQ_REQUEST_PROMISC BIT(15) #define I40EVF_FLAG_AQ_RELEASE_PROMISC BIT(16) +#define I40EVF_FLAG_AQ_REQUEST_ALLMULTI BIT(17) +#define I40EVF_FLAG_AQ_RELEASE_ALLMULTI BIT(18) /* OS defined structs */ struct net_device *netdev; @@ -295,7 +292,6 @@ struct i40evf_adapter { /* Ethtool Private Flags */ -#define I40EVF_PRIV_FLAGS_PS BIT(0) /* needed by i40evf_ethtool.c */ extern char i40evf_driver_name[]; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 5a48ee07688f..c9c202f6c521 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -63,12 +63,6 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = { #define I40EVF_STATS_LEN(_dev) \ (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev)) -static const char i40evf_priv_flags_strings[][ETH_GSTRING_LEN] = { - "packet-split", -}; - -#define I40EVF_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40evf_priv_flags_strings) - /** * i40evf_get_settings - Get Link Speed and Duplex settings * @netdev: network interface device structure @@ -103,8 +97,6 @@ static int i40evf_get_sset_count(struct net_device *netdev, int sset) { if (sset == ETH_SS_STATS) return I40EVF_STATS_LEN(netdev); - else if (sset == ETH_SS_PRIV_FLAGS) - return I40EVF_PRIV_FLAGS_STR_LEN; else return -EINVAL; } @@ -170,12 +162,6 @@ static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) snprintf(p, ETH_GSTRING_LEN, "rx-%u.bytes", i); p += ETH_GSTRING_LEN; } - } else if (sset == ETH_SS_PRIV_FLAGS) { - for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { - memcpy(data, i40evf_priv_flags_strings[i], - ETH_GSTRING_LEN); - data += ETH_GSTRING_LEN; - } } } @@ -225,7 +211,6 @@ static void i40evf_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, i40evf_driver_version, 32); strlcpy(drvinfo->fw_version, "N/A", 4); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); - drvinfo->n_priv_flags = I40EVF_PRIV_FLAGS_STR_LEN; } /** @@ -515,54 +500,6 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, return i40evf_config_rss(adapter); } -/** - * i40evf_get_priv_flags - report device private flags - * @dev: network interface device structure - * - * The get string set count and the string set should be matched for each - * flag returned. Add new strings for each flag to the i40e_priv_flags_strings - * array. - * - * Returns a u32 bitmap of flags. - **/ -static u32 i40evf_get_priv_flags(struct net_device *dev) -{ - struct i40evf_adapter *adapter = netdev_priv(dev); - u32 ret_flags = 0; - - ret_flags |= adapter->flags & I40EVF_FLAG_RX_PS_ENABLED ? - I40EVF_PRIV_FLAGS_PS : 0; - - return ret_flags; -} - -/** - * i40evf_set_priv_flags - set private flags - * @dev: network interface device structure - * @flags: bit flags to be set - **/ -static int i40evf_set_priv_flags(struct net_device *dev, u32 flags) -{ - struct i40evf_adapter *adapter = netdev_priv(dev); - bool reset_required = false; - - if ((flags & I40EVF_PRIV_FLAGS_PS) && - !(adapter->flags & I40EVF_FLAG_RX_PS_ENABLED)) { - adapter->flags |= I40EVF_FLAG_RX_PS_ENABLED; - reset_required = true; - } else if (!(flags & I40EVF_PRIV_FLAGS_PS) && - (adapter->flags & I40EVF_FLAG_RX_PS_ENABLED)) { - adapter->flags &= ~I40EVF_FLAG_RX_PS_ENABLED; - reset_required = true; - } - - /* if needed, issue reset to cause things to take effect */ - if (reset_required) - i40evf_schedule_reset(adapter); - - return 0; -} - static const struct ethtool_ops i40evf_ethtool_ops = { .get_settings = i40evf_get_settings, .get_drvinfo = i40evf_get_drvinfo, @@ -572,8 +509,6 @@ static const struct ethtool_ops i40evf_ethtool_ops = { .get_strings = i40evf_get_strings, .get_ethtool_stats = i40evf_get_ethtool_stats, .get_sset_count = i40evf_get_sset_count, - .get_priv_flags = i40evf_get_priv_flags, - .set_priv_flags = i40evf_set_priv_flags, .get_msglevel = i40evf_get_msglevel, .set_msglevel = i40evf_set_msglevel, .get_coalesce = i40evf_get_coalesce, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 9f0bd7acc22a..16c552952860 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -641,28 +641,11 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter) static void i40evf_configure_rx(struct i40evf_adapter *adapter) { struct i40e_hw *hw = &adapter->hw; - struct net_device *netdev = adapter->netdev; - int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; int i; - int rx_buf_len; - - - /* Set the RX buffer length according to the mode */ - if (adapter->flags & I40EVF_FLAG_RX_PS_ENABLED || - netdev->mtu <= ETH_DATA_LEN) - rx_buf_len = I40EVF_RXBUFFER_2048; - else - rx_buf_len = ALIGN(max_frame, 1024); for (i = 0; i < adapter->num_active_queues; i++) { adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i); - adapter->rx_rings[i].rx_buf_len = rx_buf_len; - if (adapter->flags & I40EVF_FLAG_RX_PS_ENABLED) { - set_ring_ps_enabled(&adapter->rx_rings[i]); - adapter->rx_rings[i].rx_hdr_len = I40E_RX_HDR_SIZE; - } else { - clear_ring_ps_enabled(&adapter->rx_rings[i]); - } + adapter->rx_rings[i].rx_buf_len = I40EVF_RXBUFFER_2048; } } @@ -951,6 +934,13 @@ bottom_of_search_loop: adapter->flags & I40EVF_FLAG_PROMISC_ON) adapter->aq_required |= I40EVF_FLAG_AQ_RELEASE_PROMISC; + if (netdev->flags & IFF_ALLMULTI && + !(adapter->flags & I40EVF_FLAG_ALLMULTI_ON)) + adapter->aq_required |= I40EVF_FLAG_AQ_REQUEST_ALLMULTI; + else if (!(netdev->flags & IFF_ALLMULTI) && + adapter->flags & I40EVF_FLAG_ALLMULTI_ON) + adapter->aq_required |= I40EVF_FLAG_AQ_RELEASE_ALLMULTI; + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } @@ -1007,14 +997,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter) for (i = 0; i < adapter->num_active_queues; i++) { struct i40e_ring *ring = &adapter->rx_rings[i]; - if (adapter->flags & I40EVF_FLAG_RX_PS_ENABLED) { - i40evf_alloc_rx_headers(ring); - i40evf_alloc_rx_buffers_ps(ring, ring->count); - } else { - i40evf_alloc_rx_buffers_1buf(ring, ring->count); - } - ring->next_to_use = ring->count - 1; - writel(ring->next_to_use, ring->tail); + i40evf_alloc_rx_buffers(ring, I40E_DESC_UNUSED(ring)); } } @@ -1636,7 +1619,13 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; } - if (adapter->aq_required & I40EVF_FLAG_AQ_RELEASE_PROMISC) { + if (adapter->aq_required & I40EVF_FLAG_AQ_REQUEST_ALLMULTI) { + i40evf_set_promiscuous(adapter, I40E_FLAG_VF_MULTICAST_PROMISC); + goto watchdog_done; + } + + if ((adapter->aq_required & I40EVF_FLAG_AQ_RELEASE_PROMISC) && + (adapter->aq_required & I40EVF_FLAG_AQ_RELEASE_ALLMULTI)) { i40evf_set_promiscuous(adapter, 0); goto watchdog_done; } @@ -2241,8 +2230,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter) NETIF_F_TSO6 | NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | - NETIF_F_GSO_IPIP | - NETIF_F_GSO_SIT | + NETIF_F_GSO_IPXIP4 | + NETIF_F_GSO_IPXIP6 | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_PARTIAL | @@ -2423,11 +2412,6 @@ static void i40evf_init_task(struct work_struct *work) adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN; adapter->flags |= I40EVF_FLAG_RX_CSUM_ENABLED; - adapter->flags |= I40EVF_FLAG_RX_1BUF_CAPABLE; - adapter->flags |= I40EVF_FLAG_RX_PS_CAPABLE; - - /* Default to single buffer rx, can be changed through ethtool. */ - adapter->flags &= ~I40EVF_FLAG_RX_PS_ENABLED; netdev->netdev_ops = &i40evf_netdev_ops; i40evf_set_ethtool_ops(netdev); @@ -2795,7 +2779,6 @@ static void i40evf_remove(struct pci_dev *pdev) iounmap(hw->hw_addr); pci_release_regions(pdev); - i40evf_free_all_tx_resources(adapter); i40evf_free_all_rx_resources(adapter); i40evf_free_queues(adapter); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index ba7fbc0608a6..f13445691507 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -270,10 +270,6 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) vqpi->rxq.max_pkt_size = adapter->netdev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; vqpi->rxq.databuffer_size = adapter->rx_rings[i].rx_buf_len; - if (adapter->flags & I40EVF_FLAG_RX_PS_ENABLED) { - vqpi->rxq.splithdr_enabled = true; - vqpi->rxq.hdr_size = I40E_RX_HDR_SIZE; - } vqpi++; } @@ -645,6 +641,7 @@ void i40evf_del_vlans(struct i40evf_adapter *adapter) void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags) { struct i40e_virtchnl_promisc_info vpi; + int promisc_all; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -653,11 +650,21 @@ void i40evf_set_promiscuous(struct i40evf_adapter *adapter, int flags) return; } - if (flags) { + promisc_all = I40E_FLAG_VF_UNICAST_PROMISC | + I40E_FLAG_VF_MULTICAST_PROMISC; + if ((flags & promisc_all) == promisc_all) { adapter->flags |= I40EVF_FLAG_PROMISC_ON; adapter->aq_required &= ~I40EVF_FLAG_AQ_REQUEST_PROMISC; dev_info(&adapter->pdev->dev, "Entering promiscuous mode\n"); - } else { + } + + if (flags & I40E_FLAG_VF_MULTICAST_PROMISC) { + adapter->flags |= I40EVF_FLAG_ALLMULTI_ON; + adapter->aq_required &= ~I40EVF_FLAG_AQ_REQUEST_ALLMULTI; + dev_info(&adapter->pdev->dev, "Entering multicast promiscuous mode\n"); + } + + if (!flags) { adapter->flags &= ~I40EVF_FLAG_PROMISC_ON; adapter->aq_required &= ~I40EVF_FLAG_AQ_RELEASE_PROMISC; dev_info(&adapter->pdev->dev, "Leaving promiscuous mode\n"); diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index a23aa6704394..a61447fd778e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -361,7 +361,7 @@ static s32 igb_init_nvm_params_82575(struct e1000_hw *hw) if (size > 15) size = 15; - nvm->word_size = 1 << size; + nvm->word_size = BIT(size); nvm->opcode_bits = 8; nvm->delay_usec = 1; @@ -380,7 +380,7 @@ static s32 igb_init_nvm_params_82575(struct e1000_hw *hw) 16 : 8; break; } - if (nvm->word_size == (1 << 15)) + if (nvm->word_size == BIT(15)) nvm->page_size = 128; nvm->type = e1000_nvm_eeprom_spi; @@ -391,7 +391,7 @@ static s32 igb_init_nvm_params_82575(struct e1000_hw *hw) nvm->ops.write = igb_write_nvm_spi; nvm->ops.validate = igb_validate_nvm_checksum; nvm->ops.update = igb_update_nvm_checksum; - if (nvm->word_size < (1 << 15)) + if (nvm->word_size < BIT(15)) nvm->ops.read = igb_read_nvm_eerd; else nvm->ops.read = igb_read_nvm_spi; @@ -2107,7 +2107,7 @@ void igb_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf) /* The PF can spoof - it has to in order to * support emulation mode NICs */ - reg_val ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); + reg_val ^= (BIT(pf) | BIT(pf + MAX_NUM_VFS)); } else { reg_val &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | E1000_DTXSWC_VLAN_SPOOF_MASK); diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h index de8805a2a2fe..199ff98209cf 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.h +++ b/drivers/net/ethernet/intel/igb/e1000_82575.h @@ -168,16 +168,16 @@ struct e1000_adv_tx_context_desc { #define E1000_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */ #define E1000_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */ -#define E1000_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */ -#define E1000_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header enable */ -#define E1000_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload enable */ -#define E1000_DCA_RXCTRL_DESC_RRO_EN (1 << 9) /* DCA Rx rd Desc Relax Order */ +#define E1000_DCA_RXCTRL_DESC_DCA_EN BIT(5) /* DCA Rx Desc enable */ +#define E1000_DCA_RXCTRL_HEAD_DCA_EN BIT(6) /* DCA Rx Desc header enable */ +#define E1000_DCA_RXCTRL_DATA_DCA_EN BIT(7) /* DCA Rx Desc payload enable */ +#define E1000_DCA_RXCTRL_DESC_RRO_EN BIT(9) /* DCA Rx rd Desc Relax Order */ #define E1000_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ -#define E1000_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ -#define E1000_DCA_TXCTRL_DESC_RRO_EN (1 << 9) /* Tx rd Desc Relax Order */ -#define E1000_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */ -#define E1000_DCA_TXCTRL_DATA_RRO_EN (1 << 13) /* Tx rd data Relax Order */ +#define E1000_DCA_TXCTRL_DESC_DCA_EN BIT(5) /* DCA Tx Desc enable */ +#define E1000_DCA_TXCTRL_DESC_RRO_EN BIT(9) /* Tx rd Desc Relax Order */ +#define E1000_DCA_TXCTRL_TX_WB_RO_EN BIT(11) /* Tx Desc writeback RO bit */ +#define E1000_DCA_TXCTRL_DATA_RRO_EN BIT(13) /* Tx rd data Relax Order */ /* Additional DCA related definitions, note change in position of CPUID */ #define E1000_DCA_TXCTRL_CPUID_MASK_82576 0xFF000000 /* Tx CPUID Mask */ @@ -186,8 +186,8 @@ struct e1000_adv_tx_context_desc { #define E1000_DCA_RXCTRL_CPUID_SHIFT 24 /* Rx CPUID now in the last byte */ /* ETQF register bit definitions */ -#define E1000_ETQF_FILTER_ENABLE (1 << 26) -#define E1000_ETQF_1588 (1 << 30) +#define E1000_ETQF_FILTER_ENABLE BIT(26) +#define E1000_ETQF_1588 BIT(30) /* FTQF register bit definitions */ #define E1000_FTQF_VF_BP 0x00008000 @@ -203,16 +203,16 @@ struct e1000_adv_tx_context_desc { #define E1000_DTXSWC_VLAN_SPOOF_MASK 0x0000FF00 /* Per VF VLAN spoof control */ #define E1000_DTXSWC_LLE_MASK 0x00FF0000 /* Per VF Local LB enables */ #define E1000_DTXSWC_VLAN_SPOOF_SHIFT 8 -#define E1000_DTXSWC_VMDQ_LOOPBACK_EN (1 << 31) /* global VF LB enable */ +#define E1000_DTXSWC_VMDQ_LOOPBACK_EN BIT(31) /* global VF LB enable */ /* Easy defines for setting default pool, would normally be left a zero */ #define E1000_VT_CTL_DEFAULT_POOL_SHIFT 7 #define E1000_VT_CTL_DEFAULT_POOL_MASK (0x7 << E1000_VT_CTL_DEFAULT_POOL_SHIFT) /* Other useful VMD_CTL register defines */ -#define E1000_VT_CTL_IGNORE_MAC (1 << 28) -#define E1000_VT_CTL_DISABLE_DEF_POOL (1 << 29) -#define E1000_VT_CTL_VM_REPL_EN (1 << 30) +#define E1000_VT_CTL_IGNORE_MAC BIT(28) +#define E1000_VT_CTL_DISABLE_DEF_POOL BIT(29) +#define E1000_VT_CTL_VM_REPL_EN BIT(30) /* Per VM Offload register setup */ #define E1000_VMOLR_RLPML_MASK 0x00003FFF /* Long Packet Maximum Length mask */ @@ -252,7 +252,7 @@ struct e1000_adv_tx_context_desc { #define E1000_DTXCTL_MDP_EN 0x0020 #define E1000_DTXCTL_SPOOF_INT 0x0040 -#define E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT (1 << 14) +#define E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT BIT(14) #define ALL_QUEUES 0xFFFF diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index e9f23ee8f15e..2997c443c5dc 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -530,65 +530,65 @@ /* Time Sync Interrupt Cause/Mask Register Bits */ -#define TSINTR_SYS_WRAP (1 << 0) /* SYSTIM Wrap around. */ -#define TSINTR_TXTS (1 << 1) /* Transmit Timestamp. */ -#define TSINTR_RXTS (1 << 2) /* Receive Timestamp. */ -#define TSINTR_TT0 (1 << 3) /* Target Time 0 Trigger. */ -#define TSINTR_TT1 (1 << 4) /* Target Time 1 Trigger. */ -#define TSINTR_AUTT0 (1 << 5) /* Auxiliary Timestamp 0 Taken. */ -#define TSINTR_AUTT1 (1 << 6) /* Auxiliary Timestamp 1 Taken. */ -#define TSINTR_TADJ (1 << 7) /* Time Adjust Done. */ +#define TSINTR_SYS_WRAP BIT(0) /* SYSTIM Wrap around. */ +#define TSINTR_TXTS BIT(1) /* Transmit Timestamp. */ +#define TSINTR_RXTS BIT(2) /* Receive Timestamp. */ +#define TSINTR_TT0 BIT(3) /* Target Time 0 Trigger. */ +#define TSINTR_TT1 BIT(4) /* Target Time 1 Trigger. */ +#define TSINTR_AUTT0 BIT(5) /* Auxiliary Timestamp 0 Taken. */ +#define TSINTR_AUTT1 BIT(6) /* Auxiliary Timestamp 1 Taken. */ +#define TSINTR_TADJ BIT(7) /* Time Adjust Done. */ #define TSYNC_INTERRUPTS TSINTR_TXTS #define E1000_TSICR_TXTS TSINTR_TXTS /* TSAUXC Configuration Bits */ -#define TSAUXC_EN_TT0 (1 << 0) /* Enable target time 0. */ -#define TSAUXC_EN_TT1 (1 << 1) /* Enable target time 1. */ -#define TSAUXC_EN_CLK0 (1 << 2) /* Enable Configurable Frequency Clock 0. */ -#define TSAUXC_SAMP_AUT0 (1 << 3) /* Latch SYSTIML/H into AUXSTMPL/0. */ -#define TSAUXC_ST0 (1 << 4) /* Start Clock 0 Toggle on Target Time 0. */ -#define TSAUXC_EN_CLK1 (1 << 5) /* Enable Configurable Frequency Clock 1. */ -#define TSAUXC_SAMP_AUT1 (1 << 6) /* Latch SYSTIML/H into AUXSTMPL/1. */ -#define TSAUXC_ST1 (1 << 7) /* Start Clock 1 Toggle on Target Time 1. */ -#define TSAUXC_EN_TS0 (1 << 8) /* Enable hardware timestamp 0. */ -#define TSAUXC_AUTT0 (1 << 9) /* Auxiliary Timestamp Taken. */ -#define TSAUXC_EN_TS1 (1 << 10) /* Enable hardware timestamp 0. */ -#define TSAUXC_AUTT1 (1 << 11) /* Auxiliary Timestamp Taken. */ -#define TSAUXC_PLSG (1 << 17) /* Generate a pulse. */ -#define TSAUXC_DISABLE (1 << 31) /* Disable SYSTIM Count Operation. */ +#define TSAUXC_EN_TT0 BIT(0) /* Enable target time 0. */ +#define TSAUXC_EN_TT1 BIT(1) /* Enable target time 1. */ +#define TSAUXC_EN_CLK0 BIT(2) /* Enable Configurable Frequency Clock 0. */ +#define TSAUXC_SAMP_AUT0 BIT(3) /* Latch SYSTIML/H into AUXSTMPL/0. */ +#define TSAUXC_ST0 BIT(4) /* Start Clock 0 Toggle on Target Time 0. */ +#define TSAUXC_EN_CLK1 BIT(5) /* Enable Configurable Frequency Clock 1. */ +#define TSAUXC_SAMP_AUT1 BIT(6) /* Latch SYSTIML/H into AUXSTMPL/1. */ +#define TSAUXC_ST1 BIT(7) /* Start Clock 1 Toggle on Target Time 1. */ +#define TSAUXC_EN_TS0 BIT(8) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT0 BIT(9) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_EN_TS1 BIT(10) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT1 BIT(11) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_PLSG BIT(17) /* Generate a pulse. */ +#define TSAUXC_DISABLE BIT(31) /* Disable SYSTIM Count Operation. */ /* SDP Configuration Bits */ -#define AUX0_SEL_SDP0 (0 << 0) /* Assign SDP0 to auxiliary time stamp 0. */ -#define AUX0_SEL_SDP1 (1 << 0) /* Assign SDP1 to auxiliary time stamp 0. */ -#define AUX0_SEL_SDP2 (2 << 0) /* Assign SDP2 to auxiliary time stamp 0. */ -#define AUX0_SEL_SDP3 (3 << 0) /* Assign SDP3 to auxiliary time stamp 0. */ -#define AUX0_TS_SDP_EN (1 << 2) /* Enable auxiliary time stamp trigger 0. */ -#define AUX1_SEL_SDP0 (0 << 3) /* Assign SDP0 to auxiliary time stamp 1. */ -#define AUX1_SEL_SDP1 (1 << 3) /* Assign SDP1 to auxiliary time stamp 1. */ -#define AUX1_SEL_SDP2 (2 << 3) /* Assign SDP2 to auxiliary time stamp 1. */ -#define AUX1_SEL_SDP3 (3 << 3) /* Assign SDP3 to auxiliary time stamp 1. */ -#define AUX1_TS_SDP_EN (1 << 5) /* Enable auxiliary time stamp trigger 1. */ -#define TS_SDP0_SEL_TT0 (0 << 6) /* Target time 0 is output on SDP0. */ -#define TS_SDP0_SEL_TT1 (1 << 6) /* Target time 1 is output on SDP0. */ -#define TS_SDP0_SEL_FC0 (2 << 6) /* Freq clock 0 is output on SDP0. */ -#define TS_SDP0_SEL_FC1 (3 << 6) /* Freq clock 1 is output on SDP0. */ -#define TS_SDP0_EN (1 << 8) /* SDP0 is assigned to Tsync. */ -#define TS_SDP1_SEL_TT0 (0 << 9) /* Target time 0 is output on SDP1. */ -#define TS_SDP1_SEL_TT1 (1 << 9) /* Target time 1 is output on SDP1. */ -#define TS_SDP1_SEL_FC0 (2 << 9) /* Freq clock 0 is output on SDP1. */ -#define TS_SDP1_SEL_FC1 (3 << 9) /* Freq clock 1 is output on SDP1. */ -#define TS_SDP1_EN (1 << 11) /* SDP1 is assigned to Tsync. */ -#define TS_SDP2_SEL_TT0 (0 << 12) /* Target time 0 is output on SDP2. */ -#define TS_SDP2_SEL_TT1 (1 << 12) /* Target time 1 is output on SDP2. */ -#define TS_SDP2_SEL_FC0 (2 << 12) /* Freq clock 0 is output on SDP2. */ -#define TS_SDP2_SEL_FC1 (3 << 12) /* Freq clock 1 is output on SDP2. */ -#define TS_SDP2_EN (1 << 14) /* SDP2 is assigned to Tsync. */ -#define TS_SDP3_SEL_TT0 (0 << 15) /* Target time 0 is output on SDP3. */ -#define TS_SDP3_SEL_TT1 (1 << 15) /* Target time 1 is output on SDP3. */ -#define TS_SDP3_SEL_FC0 (2 << 15) /* Freq clock 0 is output on SDP3. */ -#define TS_SDP3_SEL_FC1 (3 << 15) /* Freq clock 1 is output on SDP3. */ -#define TS_SDP3_EN (1 << 17) /* SDP3 is assigned to Tsync. */ +#define AUX0_SEL_SDP0 (0u << 0) /* Assign SDP0 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP1 (1u << 0) /* Assign SDP1 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP2 (2u << 0) /* Assign SDP2 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP3 (3u << 0) /* Assign SDP3 to auxiliary time stamp 0. */ +#define AUX0_TS_SDP_EN (1u << 2) /* Enable auxiliary time stamp trigger 0. */ +#define AUX1_SEL_SDP0 (0u << 3) /* Assign SDP0 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP1 (1u << 3) /* Assign SDP1 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP2 (2u << 3) /* Assign SDP2 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP3 (3u << 3) /* Assign SDP3 to auxiliary time stamp 1. */ +#define AUX1_TS_SDP_EN (1u << 5) /* Enable auxiliary time stamp trigger 1. */ +#define TS_SDP0_SEL_TT0 (0u << 6) /* Target time 0 is output on SDP0. */ +#define TS_SDP0_SEL_TT1 (1u << 6) /* Target time 1 is output on SDP0. */ +#define TS_SDP0_SEL_FC0 (2u << 6) /* Freq clock 0 is output on SDP0. */ +#define TS_SDP0_SEL_FC1 (3u << 6) /* Freq clock 1 is output on SDP0. */ +#define TS_SDP0_EN (1u << 8) /* SDP0 is assigned to Tsync. */ +#define TS_SDP1_SEL_TT0 (0u << 9) /* Target time 0 is output on SDP1. */ +#define TS_SDP1_SEL_TT1 (1u << 9) /* Target time 1 is output on SDP1. */ +#define TS_SDP1_SEL_FC0 (2u << 9) /* Freq clock 0 is output on SDP1. */ +#define TS_SDP1_SEL_FC1 (3u << 9) /* Freq clock 1 is output on SDP1. */ +#define TS_SDP1_EN (1u << 11) /* SDP1 is assigned to Tsync. */ +#define TS_SDP2_SEL_TT0 (0u << 12) /* Target time 0 is output on SDP2. */ +#define TS_SDP2_SEL_TT1 (1u << 12) /* Target time 1 is output on SDP2. */ +#define TS_SDP2_SEL_FC0 (2u << 12) /* Freq clock 0 is output on SDP2. */ +#define TS_SDP2_SEL_FC1 (3u << 12) /* Freq clock 1 is output on SDP2. */ +#define TS_SDP2_EN (1u << 14) /* SDP2 is assigned to Tsync. */ +#define TS_SDP3_SEL_TT0 (0u << 15) /* Target time 0 is output on SDP3. */ +#define TS_SDP3_SEL_TT1 (1u << 15) /* Target time 1 is output on SDP3. */ +#define TS_SDP3_SEL_FC0 (2u << 15) /* Freq clock 0 is output on SDP3. */ +#define TS_SDP3_SEL_FC1 (3u << 15) /* Freq clock 1 is output on SDP3. */ +#define TS_SDP3_EN (1u << 17) /* SDP3 is assigned to Tsync. */ #define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */ #define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */ @@ -997,8 +997,8 @@ #define E1000_M88E1543_FIBER_CTRL 0x0 #define E1000_EEE_ADV_DEV_I354 7 #define E1000_EEE_ADV_ADDR_I354 60 -#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */ -#define E1000_EEE_ADV_1000_SUPPORTED (1 << 2) /* 1000BaseT EEE Supported */ +#define E1000_EEE_ADV_100_SUPPORTED BIT(1) /* 100BaseTx EEE Supported */ +#define E1000_EEE_ADV_1000_SUPPORTED BIT(2) /* 1000BaseT EEE Supported */ #define E1000_PCS_STATUS_DEV_I354 3 #define E1000_PCS_STATUS_ADDR_I354 1 #define E1000_PCS_STATUS_TX_LPI_IND 0x0200 /* Tx in LPI state */ diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 07cf4fe58338..5010e2232c50 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -212,7 +212,7 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vlan, u32 vind, * bits[4-0]: which bit in the register */ regidx = vlan / 32; - vfta_delta = 1 << (vlan % 32); + vfta_delta = BIT(vlan % 32); vfta = adapter->shadow_vfta[regidx]; /* vfta_delta represents the difference between the current value @@ -243,12 +243,12 @@ s32 igb_vfta_set(struct e1000_hw *hw, u32 vlan, u32 vind, bits = rd32(E1000_VLVF(vlvf_index)); /* set the pool bit */ - bits |= 1 << (E1000_VLVF_POOLSEL_SHIFT + vind); + bits |= BIT(E1000_VLVF_POOLSEL_SHIFT + vind); if (vlan_on) goto vlvf_update; /* clear the pool bit */ - bits ^= 1 << (E1000_VLVF_POOLSEL_SHIFT + vind); + bits ^= BIT(E1000_VLVF_POOLSEL_SHIFT + vind); if (!(bits & E1000_VLVF_POOLSEL_MASK)) { /* Clear VFTA first, then disable VLVF. Otherwise @@ -427,7 +427,7 @@ void igb_mta_set(struct e1000_hw *hw, u32 hash_value) mta = array_rd32(E1000_MTA, hash_reg); - mta |= (1 << hash_bit); + mta |= BIT(hash_bit); array_wr32(E1000_MTA, hash_reg, mta); wrfl(); @@ -527,7 +527,7 @@ void igb_update_mc_addr_list(struct e1000_hw *hw, hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); hash_bit = hash_value & 0x1F; - hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + hw->mac.mta_shadow[hash_reg] |= BIT(hash_bit); mc_addr_list += (ETH_ALEN); } diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 10f5c9e016a9..00e263f0c030 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -302,9 +302,9 @@ static s32 igb_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number) u32 vflre = rd32(E1000_VFLRE); s32 ret_val = -E1000_ERR_MBX; - if (vflre & (1 << vf_number)) { + if (vflre & BIT(vf_number)) { ret_val = 0; - wr32(E1000_VFLRE, (1 << vf_number)); + wr32(E1000_VFLRE, BIT(vf_number)); hw->mbx.stats.rsts++; } diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index e8280d0d7f02..3582c5cf8843 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -72,7 +72,7 @@ static void igb_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) u32 eecd = rd32(E1000_EECD); u32 mask; - mask = 0x01 << (count - 1); + mask = 1u << (count - 1); if (nvm->type == e1000_nvm_eeprom_spi) eecd |= E1000_EECD_DO; diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 969a6ddafa3b..9b622b33bb5a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -91,10 +91,10 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw); #define I82580_ADDR_REG 16 #define I82580_CFG_REG 22 -#define I82580_CFG_ASSERT_CRS_ON_TX (1 << 15) -#define I82580_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift 100/10 */ +#define I82580_CFG_ASSERT_CRS_ON_TX BIT(15) +#define I82580_CFG_ENABLE_DOWNSHIFT (3u << 10) /* auto downshift 100/10 */ #define I82580_CTRL_REG 23 -#define I82580_CTRL_DOWNSHIFT_MASK (7 << 10) +#define I82580_CTRL_DOWNSHIFT_MASK (7u << 10) /* 82580 specific PHY registers */ #define I82580_PHY_CTRL_2 18 diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 9413fa61392f..b9609afa5ca3 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -91,6 +91,14 @@ struct igb_adapter; #define NVM_COMB_VER_OFF 0x0083 #define NVM_COMB_VER_PTR 0x003d +/* Transmit and receive latency (for PTP timestamps) */ +#define IGB_I210_TX_LATENCY_10 9542 +#define IGB_I210_TX_LATENCY_100 1024 +#define IGB_I210_TX_LATENCY_1000 178 +#define IGB_I210_RX_LATENCY_10 20662 +#define IGB_I210_RX_LATENCY_100 2213 +#define IGB_I210_RX_LATENCY_1000 448 + struct vf_data_storage { unsigned char vf_mac_addresses[ETH_ALEN]; u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES]; @@ -169,7 +177,7 @@ enum igb_tx_flags { * maintain a power of two alignment we have to limit ourselves to 32K. */ #define IGB_MAX_TXD_PWR 15 -#define IGB_MAX_DATA_PER_TXD (1 << IGB_MAX_TXD_PWR) +#define IGB_MAX_DATA_PER_TXD (1u << IGB_MAX_TXD_PWR) /* Tx Descriptors needed, worst case */ #define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IGB_MAX_DATA_PER_TXD) @@ -466,21 +474,21 @@ struct igb_adapter { u16 eee_advert; }; -#define IGB_FLAG_HAS_MSI (1 << 0) -#define IGB_FLAG_DCA_ENABLED (1 << 1) -#define IGB_FLAG_QUAD_PORT_A (1 << 2) -#define IGB_FLAG_QUEUE_PAIRS (1 << 3) -#define IGB_FLAG_DMAC (1 << 4) -#define IGB_FLAG_PTP (1 << 5) -#define IGB_FLAG_RSS_FIELD_IPV4_UDP (1 << 6) -#define IGB_FLAG_RSS_FIELD_IPV6_UDP (1 << 7) -#define IGB_FLAG_WOL_SUPPORTED (1 << 8) -#define IGB_FLAG_NEED_LINK_UPDATE (1 << 9) -#define IGB_FLAG_MEDIA_RESET (1 << 10) -#define IGB_FLAG_MAS_CAPABLE (1 << 11) -#define IGB_FLAG_MAS_ENABLE (1 << 12) -#define IGB_FLAG_HAS_MSIX (1 << 13) -#define IGB_FLAG_EEE (1 << 14) +#define IGB_FLAG_HAS_MSI BIT(0) +#define IGB_FLAG_DCA_ENABLED BIT(1) +#define IGB_FLAG_QUAD_PORT_A BIT(2) +#define IGB_FLAG_QUEUE_PAIRS BIT(3) +#define IGB_FLAG_DMAC BIT(4) +#define IGB_FLAG_PTP BIT(5) +#define IGB_FLAG_RSS_FIELD_IPV4_UDP BIT(6) +#define IGB_FLAG_RSS_FIELD_IPV6_UDP BIT(7) +#define IGB_FLAG_WOL_SUPPORTED BIT(8) +#define IGB_FLAG_NEED_LINK_UPDATE BIT(9) +#define IGB_FLAG_MEDIA_RESET BIT(10) +#define IGB_FLAG_MAS_CAPABLE BIT(11) +#define IGB_FLAG_MAS_ENABLE BIT(12) +#define IGB_FLAG_HAS_MSIX BIT(13) +#define IGB_FLAG_EEE BIT(14) #define IGB_FLAG_VLAN_PROMISC BIT(15) /* Media Auto Sense */ diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index bb4d6cdcd0b8..64e91c575a39 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -466,7 +466,7 @@ static void igb_get_regs(struct net_device *netdev, memset(p, 0, IGB_REGS_LEN * sizeof(u32)); - regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; + regs->version = (1u << 24) | (hw->revision_id << 16) | hw->device_id; /* General Registers */ regs_buff[0] = rd32(E1000_CTRL); @@ -1448,7 +1448,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data) /* Test each interrupt */ for (; i < 31; i++) { /* Interrupt to test */ - mask = 1 << i; + mask = BIT(i); if (!(mask & ics_mask)) continue; @@ -2411,19 +2411,19 @@ static int igb_get_ts_info(struct net_device *dev, SOF_TIMESTAMPING_RAW_HARDWARE; info->tx_types = - (1 << HWTSTAMP_TX_OFF) | - (1 << HWTSTAMP_TX_ON); + BIT(HWTSTAMP_TX_OFF) | + BIT(HWTSTAMP_TX_ON); - info->rx_filters = 1 << HWTSTAMP_FILTER_NONE; + info->rx_filters = BIT(HWTSTAMP_FILTER_NONE); /* 82576 does not support timestamping all packets. */ if (adapter->hw.mac.type >= e1000_82580) - info->rx_filters |= 1 << HWTSTAMP_FILTER_ALL; + info->rx_filters |= BIT(HWTSTAMP_FILTER_ALL); else info->rx_filters |= - (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + BIT(HWTSTAMP_FILTER_PTP_V2_EVENT); return 0; default: diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 8e96c35307fb..ef3d642f5ff2 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -383,7 +383,7 @@ static void igb_dump(struct igb_adapter *adapter) dev_info(&adapter->pdev->dev, "Net device Info\n"); pr_info("Device Name state trans_start last_rx\n"); pr_info("%-15s %016lX %016lX %016lX\n", netdev->name, - netdev->state, netdev->trans_start, netdev->last_rx); + netdev->state, dev_trans_start(netdev), netdev->last_rx); } /* Print Registers */ @@ -836,7 +836,7 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector) igb_write_ivar(hw, msix_vector, tx_queue & 0x7, ((tx_queue & 0x8) << 1) + 8); - q_vector->eims_value = 1 << msix_vector; + q_vector->eims_value = BIT(msix_vector); break; case e1000_82580: case e1000_i350: @@ -857,7 +857,7 @@ static void igb_assign_vector(struct igb_q_vector *q_vector, int msix_vector) igb_write_ivar(hw, msix_vector, tx_queue >> 1, ((tx_queue & 0x1) << 4) + 8); - q_vector->eims_value = 1 << msix_vector; + q_vector->eims_value = BIT(msix_vector); break; default: BUG(); @@ -919,7 +919,7 @@ static void igb_configure_msix(struct igb_adapter *adapter) E1000_GPIE_NSICR); /* enable msix_other interrupt */ - adapter->eims_other = 1 << vector; + adapter->eims_other = BIT(vector); tmp = (vector++ | E1000_IVAR_VALID) << 8; wr32(E1000_IVAR_MISC, tmp); @@ -2087,6 +2087,40 @@ static int igb_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return ndo_dflt_fdb_add(ndm, tb, dev, addr, vid, flags); } +#define IGB_MAX_MAC_HDR_LEN 127 +#define IGB_MAX_NETWORK_HDR_LEN 511 + +static netdev_features_t +igb_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + unsigned int network_hdr_len, mac_hdr_len; + + /* Make certain the headers can be described by a context descriptor */ + mac_hdr_len = skb_network_header(skb) - skb->data; + if (unlikely(mac_hdr_len > IGB_MAX_MAC_HDR_LEN)) + return features & ~(NETIF_F_HW_CSUM | + NETIF_F_SCTP_CRC | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_TSO | + NETIF_F_TSO6); + + network_hdr_len = skb_checksum_start(skb) - skb_network_header(skb); + if (unlikely(network_hdr_len > IGB_MAX_NETWORK_HDR_LEN)) + return features & ~(NETIF_F_HW_CSUM | + NETIF_F_SCTP_CRC | + NETIF_F_TSO | + NETIF_F_TSO6); + + /* We can only support IPV4 TSO in tunnels if we can mangle the + * inner IP ID field, so strip TSO if MANGLEID is not supported. + */ + if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) + features &= ~NETIF_F_TSO; + + return features; +} + static const struct net_device_ops igb_netdev_ops = { .ndo_open = igb_open, .ndo_stop = igb_close, @@ -2111,7 +2145,7 @@ static const struct net_device_ops igb_netdev_ops = { .ndo_fix_features = igb_fix_features, .ndo_set_features = igb_set_features, .ndo_fdb_add = igb_ndo_fdb_add, - .ndo_features_check = passthru_features_check, + .ndo_features_check = igb_features_check, }; /** @@ -2377,38 +2411,43 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_TSO6 | NETIF_F_RXHASH | NETIF_F_RXCSUM | - NETIF_F_HW_CSUM | - NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_VLAN_CTAG_TX; + NETIF_F_HW_CSUM; if (hw->mac.type >= e1000_82576) netdev->features |= NETIF_F_SCTP_CRC; +#define IGB_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \ + NETIF_F_GSO_GRE_CSUM | \ + NETIF_F_GSO_IPXIP4 | \ + NETIF_F_GSO_IPXIP6 | \ + NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) + + netdev->gso_partial_features = IGB_GSO_PARTIAL_FEATURES; + netdev->features |= NETIF_F_GSO_PARTIAL | IGB_GSO_PARTIAL_FEATURES; + /* copy netdev features into list of user selectable features */ - netdev->hw_features |= netdev->features; - netdev->hw_features |= NETIF_F_RXALL; + netdev->hw_features |= netdev->features | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_RXALL; if (hw->mac.type >= e1000_i350) netdev->hw_features |= NETIF_F_NTUPLE; - /* set this bit last since it cannot be part of hw_features */ - netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; - - netdev->vlan_features |= NETIF_F_SG | - NETIF_F_TSO | - NETIF_F_TSO6 | - NETIF_F_HW_CSUM | - NETIF_F_SCTP_CRC; + if (pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_HW_CSUM; - netdev->hw_enc_features |= NETIF_F_HW_CSUM; + netdev->hw_enc_features |= netdev->vlan_features; - netdev->priv_flags |= IFF_SUPP_NOFCS; + /* set this bit last since it cannot be part of vlan_features */ + netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX; - if (pci_using_dac) { - netdev->features |= NETIF_F_HIGHDMA; - netdev->vlan_features |= NETIF_F_HIGHDMA; - } + netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->priv_flags |= IFF_UNICAST_FLT; @@ -4064,7 +4103,7 @@ static int igb_vlan_promisc_enable(struct igb_adapter *adapter) for (i = E1000_VLVF_ARRAY_SIZE; --i;) { u32 vlvf = rd32(E1000_VLVF(i)); - vlvf |= 1 << pf_id; + vlvf |= BIT(pf_id); wr32(E1000_VLVF(i), vlvf); } @@ -4091,7 +4130,7 @@ static void igb_scrub_vfta(struct igb_adapter *adapter, u32 vfta_offset) /* guarantee that we don't scrub out management VLAN */ vid = adapter->mng_vlan_id; if (vid >= vid_start && vid < vid_end) - vfta[(vid - vid_start) / 32] |= 1 << (vid % 32); + vfta[(vid - vid_start) / 32] |= BIT(vid % 32); if (!adapter->vfs_allocated_count) goto set_vfta; @@ -4110,7 +4149,7 @@ static void igb_scrub_vfta(struct igb_adapter *adapter, u32 vfta_offset) if (vlvf & E1000_VLVF_VLANID_ENABLE) { /* record VLAN ID in VFTA */ - vfta[(vid - vid_start) / 32] |= 1 << (vid % 32); + vfta[(vid - vid_start) / 32] |= BIT(vid % 32); /* if PF is part of this then continue */ if (test_bit(vid, adapter->active_vlans)) @@ -4118,7 +4157,7 @@ static void igb_scrub_vfta(struct igb_adapter *adapter, u32 vfta_offset) } /* remove PF from the pool */ - bits = ~(1 << pf_id); + bits = ~BIT(pf_id); bits &= rd32(E1000_VLVF(i)); wr32(E1000_VLVF(i), bits); } @@ -4276,13 +4315,13 @@ static void igb_spoof_check(struct igb_adapter *adapter) return; for (j = 0; j < adapter->vfs_allocated_count; j++) { - if (adapter->wvbr & (1 << j) || - adapter->wvbr & (1 << (j + IGB_STAGGERED_QUEUE_OFFSET))) { + if (adapter->wvbr & BIT(j) || + adapter->wvbr & BIT(j + IGB_STAGGERED_QUEUE_OFFSET)) { dev_warn(&adapter->pdev->dev, "Spoof event(s) detected on VF %d\n", j); adapter->wvbr &= - ~((1 << j) | - (1 << (j + IGB_STAGGERED_QUEUE_OFFSET))); + ~(BIT(j) | + BIT(j + IGB_STAGGERED_QUEUE_OFFSET)); } } } @@ -4842,9 +4881,18 @@ static int igb_tso(struct igb_ring *tx_ring, struct igb_tx_buffer *first, u8 *hdr_len) { + u32 vlan_macip_lens, type_tucmd, mss_l4len_idx; struct sk_buff *skb = first->skb; - u32 vlan_macip_lens, type_tucmd; - u32 mss_l4len_idx, l4len; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + union { + struct tcphdr *tcp; + unsigned char *hdr; + } l4; + u32 paylen, l4_offset; int err; if (skb->ip_summed != CHECKSUM_PARTIAL) @@ -4857,45 +4905,52 @@ static int igb_tso(struct igb_ring *tx_ring, if (err < 0) return err; + ip.hdr = skb_network_header(skb); + l4.hdr = skb_checksum_start(skb); + /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ type_tucmd = E1000_ADVTXD_TUCMD_L4T_TCP; - if (first->protocol == htons(ETH_P_IP)) { - struct iphdr *iph = ip_hdr(skb); - iph->tot_len = 0; - iph->check = 0; - tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, - iph->daddr, 0, - IPPROTO_TCP, - 0); + /* initialize outer IP header fields */ + if (ip.v4->version == 4) { + /* IP header will have to cancel out any data that + * is not a part of the outer IP header + */ + ip.v4->check = csum_fold(csum_add(lco_csum(skb), + csum_unfold(l4.tcp->check))); type_tucmd |= E1000_ADVTXD_TUCMD_IPV4; + + ip.v4->tot_len = 0; first->tx_flags |= IGB_TX_FLAGS_TSO | IGB_TX_FLAGS_CSUM | IGB_TX_FLAGS_IPV4; - } else if (skb_is_gso_v6(skb)) { - ipv6_hdr(skb)->payload_len = 0; - tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - 0, IPPROTO_TCP, 0); + } else { + ip.v6->payload_len = 0; first->tx_flags |= IGB_TX_FLAGS_TSO | IGB_TX_FLAGS_CSUM; } - /* compute header lengths */ - l4len = tcp_hdrlen(skb); - *hdr_len = skb_transport_offset(skb) + l4len; + /* determine offset of inner transport header */ + l4_offset = l4.hdr - skb->data; + + /* compute length of segmentation header */ + *hdr_len = (l4.tcp->doff * 4) + l4_offset; + + /* remove payload length from inner checksum */ + paylen = skb->len - l4_offset; + csum_replace_by_diff(&l4.tcp->check, htonl(paylen)); /* update gso size and bytecount with header size */ first->gso_segs = skb_shinfo(skb)->gso_segs; first->bytecount += (first->gso_segs - 1) * *hdr_len; /* MSS L4LEN IDX */ - mss_l4len_idx = l4len << E1000_ADVTXD_L4LEN_SHIFT; + mss_l4len_idx = (*hdr_len - l4_offset) << E1000_ADVTXD_L4LEN_SHIFT; mss_l4len_idx |= skb_shinfo(skb)->gso_size << E1000_ADVTXD_MSS_SHIFT; /* VLAN MACLEN IPLEN */ - vlan_macip_lens = skb_network_header_len(skb); - vlan_macip_lens |= skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT; + vlan_macip_lens = l4.hdr - ip.hdr; + vlan_macip_lens |= (ip.hdr - skb->data) << E1000_ADVTXD_MACLEN_SHIFT; vlan_macip_lens |= first->tx_flags & IGB_TX_FLAGS_VLAN_MASK; igb_tx_ctxtdesc(tx_ring, vlan_macip_lens, type_tucmd, mss_l4len_idx); @@ -5963,11 +6018,11 @@ static void igb_clear_vf_vfta(struct igb_adapter *adapter, u32 vf) /* create mask for VF and other pools */ pool_mask = E1000_VLVF_POOLSEL_MASK; - vlvf_mask = 1 << (E1000_VLVF_POOLSEL_SHIFT + vf); + vlvf_mask = BIT(E1000_VLVF_POOLSEL_SHIFT + vf); /* drop PF from pool bits */ - pool_mask &= ~(1 << (E1000_VLVF_POOLSEL_SHIFT + - adapter->vfs_allocated_count)); + pool_mask &= ~BIT(E1000_VLVF_POOLSEL_SHIFT + + adapter->vfs_allocated_count); /* Find the vlan filter for this id */ for (i = E1000_VLVF_ARRAY_SIZE; i--;) { @@ -5990,7 +6045,7 @@ static void igb_clear_vf_vfta(struct igb_adapter *adapter, u32 vf) goto update_vlvf; vid = vlvf & E1000_VLVF_VLANID_MASK; - vfta_mask = 1 << (vid % 32); + vfta_mask = BIT(vid % 32); /* clear bit from VFTA */ vfta = adapter->shadow_vfta[vid / 32]; @@ -6027,7 +6082,7 @@ static int igb_find_vlvf_entry(struct e1000_hw *hw, u32 vlan) return idx; } -void igb_update_pf_vlvf(struct igb_adapter *adapter, u32 vid) +static void igb_update_pf_vlvf(struct igb_adapter *adapter, u32 vid) { struct e1000_hw *hw = &adapter->hw; u32 bits, pf_id; @@ -6041,13 +6096,13 @@ void igb_update_pf_vlvf(struct igb_adapter *adapter, u32 vid) * entry other than the PF. */ pf_id = adapter->vfs_allocated_count + E1000_VLVF_POOLSEL_SHIFT; - bits = ~(1 << pf_id) & E1000_VLVF_POOLSEL_MASK; + bits = ~BIT(pf_id) & E1000_VLVF_POOLSEL_MASK; bits &= rd32(E1000_VLVF(idx)); /* Disable the filter so this falls into the default pool. */ if (!bits) { if (adapter->flags & IGB_FLAG_VLAN_PROMISC) - wr32(E1000_VLVF(idx), 1 << pf_id); + wr32(E1000_VLVF(idx), BIT(pf_id)); else wr32(E1000_VLVF(idx), 0); } @@ -6231,9 +6286,9 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) /* enable transmit and receive for vf */ reg = rd32(E1000_VFTE); - wr32(E1000_VFTE, reg | (1 << vf)); + wr32(E1000_VFTE, reg | BIT(vf)); reg = rd32(E1000_VFRE); - wr32(E1000_VFRE, reg | (1 << vf)); + wr32(E1000_VFRE, reg | BIT(vf)); adapter->vf_data[vf].flags |= IGB_VF_FLAG_CTS; @@ -7927,7 +7982,7 @@ static void igb_set_vf_rate_limit(struct e1000_hw *hw, int vf, int tx_rate, /* Calculate the rate factor values to set */ rf_int = link_speed / tx_rate; rf_dec = (link_speed - (rf_int * tx_rate)); - rf_dec = (rf_dec * (1 << E1000_RTTBCNRC_RF_INT_SHIFT)) / + rf_dec = (rf_dec * BIT(E1000_RTTBCNRC_RF_INT_SHIFT)) / tx_rate; bcnrc_val = E1000_RTTBCNRC_RS_ENA; @@ -8017,11 +8072,11 @@ static int igb_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, reg_offset = (hw->mac.type == e1000_82576) ? E1000_DTXSWC : E1000_TXSWC; reg_val = rd32(reg_offset); if (setting) - reg_val |= ((1 << vf) | - (1 << (vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT))); + reg_val |= (BIT(vf) | + BIT(vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT)); else - reg_val &= ~((1 << vf) | - (1 << (vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT))); + reg_val &= ~(BIT(vf) | + BIT(vf + E1000_DTXSWC_VLAN_SPOOF_SHIFT)); wr32(reg_offset, reg_val); adapter->vf_data[vf].spoofchk_enabled = setting; diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 22a8a29895b4..f097c5a8ab93 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -69,9 +69,9 @@ #define IGB_SYSTIM_OVERFLOW_PERIOD (HZ * 60 * 9) #define IGB_PTP_TX_TIMEOUT (HZ * 15) -#define INCPERIOD_82576 (1 << E1000_TIMINCA_16NS_SHIFT) -#define INCVALUE_82576_MASK ((1 << E1000_TIMINCA_16NS_SHIFT) - 1) -#define INCVALUE_82576 (16 << IGB_82576_TSYNC_SHIFT) +#define INCPERIOD_82576 BIT(E1000_TIMINCA_16NS_SHIFT) +#define INCVALUE_82576_MASK GENMASK(E1000_TIMINCA_16NS_SHIFT - 1, 0) +#define INCVALUE_82576 (16u << IGB_82576_TSYNC_SHIFT) #define IGB_NBITS_82580 40 static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); @@ -722,11 +722,29 @@ static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; struct skb_shared_hwtstamps shhwtstamps; u64 regval; + int adjust = 0; regval = rd32(E1000_TXSTMPL); regval |= (u64)rd32(E1000_TXSTMPH) << 32; igb_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval); + /* adjust timestamp for the TX latency based on link speed */ + if (adapter->hw.mac.type == e1000_i210) { + switch (adapter->link_speed) { + case SPEED_10: + adjust = IGB_I210_TX_LATENCY_10; + break; + case SPEED_100: + adjust = IGB_I210_TX_LATENCY_100; + break; + case SPEED_1000: + adjust = IGB_I210_TX_LATENCY_1000; + break; + } + } + + shhwtstamps.hwtstamp = ktime_sub_ns(shhwtstamps.hwtstamp, adjust); + skb_tstamp_tx(adapter->ptp_tx_skb, &shhwtstamps); dev_kfree_skb_any(adapter->ptp_tx_skb); adapter->ptp_tx_skb = NULL; @@ -771,6 +789,7 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct igb_adapter *adapter = q_vector->adapter; struct e1000_hw *hw = &adapter->hw; u64 regval; + int adjust = 0; /* If this bit is set, then the RX registers contain the time stamp. No * other packet will be time stamped until we read these registers, so @@ -790,6 +809,23 @@ void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, igb_ptp_systim_to_hwtstamp(adapter, skb_hwtstamps(skb), regval); + /* adjust timestamp for the RX latency based on link speed */ + if (adapter->hw.mac.type == e1000_i210) { + switch (adapter->link_speed) { + case SPEED_10: + adjust = IGB_I210_RX_LATENCY_10; + break; + case SPEED_100: + adjust = IGB_I210_RX_LATENCY_100; + break; + case SPEED_1000: + adjust = IGB_I210_RX_LATENCY_1000; + break; + } + } + skb_hwtstamps(skb)->hwtstamp = + ktime_add_ns(skb_hwtstamps(skb)->hwtstamp, adjust); + /* Update the last_rx_timestamp timer in order to enable watchdog check * for error case of latched timestamp on a dropped packet. */ diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h index ae3f28332fa0..ee1ef08d7fc4 100644 --- a/drivers/net/ethernet/intel/igbvf/defines.h +++ b/drivers/net/ethernet/intel/igbvf/defines.h @@ -113,7 +113,7 @@ #define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Enable specific Rx Que */ /* Direct Cache Access (DCA) definitions */ -#define E1000_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */ +#define E1000_DCA_TXCTRL_TX_WB_RO_EN BIT(11) /* Tx Desc writeback RO bit */ #define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */ diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index b74ce53d7b52..8dea1b1367ef 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -154,7 +154,8 @@ static void igbvf_get_regs(struct net_device *netdev, memset(p, 0, IGBVF_REGS_LEN * sizeof(u32)); - regs->version = (1 << 24) | (adapter->pdev->revision << 16) | + regs->version = (1u << 24) | + (adapter->pdev->revision << 16) | adapter->pdev->device; regs_buff[0] = er32(CTRL); diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index f166baab8d7e..6f4290d6dc9f 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -287,8 +287,8 @@ struct igbvf_info { }; /* hardware capability, feature, and workaround flags */ -#define IGBVF_FLAG_RX_CSUM_DISABLED (1 << 0) -#define IGBVF_FLAG_RX_LB_VLAN_BSWAP (1 << 1) +#define IGBVF_FLAG_RX_CSUM_DISABLED BIT(0) +#define IGBVF_FLAG_RX_LB_VLAN_BSWAP BIT(1) #define IGBVF_RX_DESC_ADV(R, i) \ (&((((R).desc))[i].rx_desc)) #define IGBVF_TX_DESC_ADV(R, i) \ diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index c12442252adb..b0778ba65083 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -964,7 +964,7 @@ static void igbvf_assign_vector(struct igbvf_adapter *adapter, int rx_queue, ivar = ivar & 0xFFFFFF00; ivar |= msix_vector | E1000_IVAR_VALID; } - adapter->rx_ring[rx_queue].eims_value = 1 << msix_vector; + adapter->rx_ring[rx_queue].eims_value = BIT(msix_vector); array_ew32(IVAR0, index, ivar); } if (tx_queue > IGBVF_NO_QUEUE) { @@ -979,7 +979,7 @@ static void igbvf_assign_vector(struct igbvf_adapter *adapter, int rx_queue, ivar = ivar & 0xFFFF00FF; ivar |= (msix_vector | E1000_IVAR_VALID) << 8; } - adapter->tx_ring[tx_queue].eims_value = 1 << msix_vector; + adapter->tx_ring[tx_queue].eims_value = BIT(msix_vector); array_ew32(IVAR0, index, ivar); } } @@ -1014,8 +1014,8 @@ static void igbvf_configure_msix(struct igbvf_adapter *adapter) ew32(IVAR_MISC, tmp); - adapter->eims_enable_mask = (1 << (vector)) - 1; - adapter->eims_other = 1 << (vector - 1); + adapter->eims_enable_mask = GENMASK(vector - 1, 0); + adapter->eims_other = BIT(vector - 1); e1e_flush(); } @@ -1367,7 +1367,7 @@ static void igbvf_configure_rx(struct igbvf_adapter *adapter) struct e1000_hw *hw = &adapter->hw; struct igbvf_ring *rx_ring = adapter->rx_ring; u64 rdba; - u32 rdlen, rxdctl; + u32 rxdctl; /* disable receives */ rxdctl = er32(RXDCTL(0)); @@ -1375,8 +1375,6 @@ static void igbvf_configure_rx(struct igbvf_adapter *adapter) e1e_flush(); msleep(10); - rdlen = rx_ring->count * sizeof(union e1000_adv_rx_desc); - /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ @@ -1933,83 +1931,74 @@ static void igbvf_tx_ctxtdesc(struct igbvf_ring *tx_ring, u32 vlan_macip_lens, buffer_info->dma = 0; } -static int igbvf_tso(struct igbvf_adapter *adapter, - struct igbvf_ring *tx_ring, - struct sk_buff *skb, u32 tx_flags, u8 *hdr_len, - __be16 protocol) -{ - struct e1000_adv_tx_context_desc *context_desc; - struct igbvf_buffer *buffer_info; - u32 info = 0, tu_cmd = 0; - u32 mss_l4len_idx, l4len; - unsigned int i; +static int igbvf_tso(struct igbvf_ring *tx_ring, + struct sk_buff *skb, u32 tx_flags, u8 *hdr_len) +{ + u32 vlan_macip_lens, type_tucmd, mss_l4len_idx; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + union { + struct tcphdr *tcp; + unsigned char *hdr; + } l4; + u32 paylen, l4_offset; int err; - *hdr_len = 0; + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (!skb_is_gso(skb)) + return 0; err = skb_cow_head(skb, 0); - if (err < 0) { - dev_err(&adapter->pdev->dev, "igbvf_tso returning an error\n"); + if (err < 0) return err; - } - l4len = tcp_hdrlen(skb); - *hdr_len += l4len; - - if (protocol == htons(ETH_P_IP)) { - struct iphdr *iph = ip_hdr(skb); - - iph->tot_len = 0; - iph->check = 0; - tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, - iph->daddr, 0, - IPPROTO_TCP, - 0); - } else if (skb_is_gso_v6(skb)) { - ipv6_hdr(skb)->payload_len = 0; - tcp_hdr(skb)->check = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, - &ipv6_hdr(skb)->daddr, - 0, IPPROTO_TCP, 0); - } + ip.hdr = skb_network_header(skb); + l4.hdr = skb_checksum_start(skb); - i = tx_ring->next_to_use; + /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ + type_tucmd = E1000_ADVTXD_TUCMD_L4T_TCP; - buffer_info = &tx_ring->buffer_info[i]; - context_desc = IGBVF_TX_CTXTDESC_ADV(*tx_ring, i); - /* VLAN MACLEN IPLEN */ - if (tx_flags & IGBVF_TX_FLAGS_VLAN) - info |= (tx_flags & IGBVF_TX_FLAGS_VLAN_MASK); - info |= (skb_network_offset(skb) << E1000_ADVTXD_MACLEN_SHIFT); - *hdr_len += skb_network_offset(skb); - info |= (skb_transport_header(skb) - skb_network_header(skb)); - *hdr_len += (skb_transport_header(skb) - skb_network_header(skb)); - context_desc->vlan_macip_lens = cpu_to_le32(info); + /* initialize outer IP header fields */ + if (ip.v4->version == 4) { + /* IP header will have to cancel out any data that + * is not a part of the outer IP header + */ + ip.v4->check = csum_fold(csum_add(lco_csum(skb), + csum_unfold(l4.tcp->check))); + type_tucmd |= E1000_ADVTXD_TUCMD_IPV4; - /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ - tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT); + ip.v4->tot_len = 0; + } else { + ip.v6->payload_len = 0; + } - if (protocol == htons(ETH_P_IP)) - tu_cmd |= E1000_ADVTXD_TUCMD_IPV4; - tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP; + /* determine offset of inner transport header */ + l4_offset = l4.hdr - skb->data; - context_desc->type_tucmd_mlhl = cpu_to_le32(tu_cmd); + /* compute length of segmentation header */ + *hdr_len = (l4.tcp->doff * 4) + l4_offset; - /* MSS L4LEN IDX */ - mss_l4len_idx = (skb_shinfo(skb)->gso_size << E1000_ADVTXD_MSS_SHIFT); - mss_l4len_idx |= (l4len << E1000_ADVTXD_L4LEN_SHIFT); + /* remove payload length from inner checksum */ + paylen = skb->len - l4_offset; + csum_replace_by_diff(&l4.tcp->check, htonl(paylen)); - context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx); - context_desc->seqnum_seed = 0; + /* MSS L4LEN IDX */ + mss_l4len_idx = (*hdr_len - l4_offset) << E1000_ADVTXD_L4LEN_SHIFT; + mss_l4len_idx |= skb_shinfo(skb)->gso_size << E1000_ADVTXD_MSS_SHIFT; - buffer_info->time_stamp = jiffies; - buffer_info->dma = 0; - i++; - if (i == tx_ring->count) - i = 0; + /* VLAN MACLEN IPLEN */ + vlan_macip_lens = l4.hdr - ip.hdr; + vlan_macip_lens |= (ip.hdr - skb->data) << E1000_ADVTXD_MACLEN_SHIFT; + vlan_macip_lens |= tx_flags & IGBVF_TX_FLAGS_VLAN_MASK; - tx_ring->next_to_use = i; + igbvf_tx_ctxtdesc(tx_ring, vlan_macip_lens, type_tucmd, mss_l4len_idx); - return true; + return 1; } static inline bool igbvf_ipv6_csum_is_sctp(struct sk_buff *skb) @@ -2091,7 +2080,7 @@ static int igbvf_maybe_stop_tx(struct net_device *netdev, int size) } #define IGBVF_MAX_TXD_PWR 16 -#define IGBVF_MAX_DATA_PER_TXD (1 << IGBVF_MAX_TXD_PWR) +#define IGBVF_MAX_DATA_PER_TXD (1u << IGBVF_MAX_TXD_PWR) static inline int igbvf_tx_map_adv(struct igbvf_adapter *adapter, struct igbvf_ring *tx_ring, @@ -2271,8 +2260,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb, first = tx_ring->next_to_use; - tso = skb_is_gso(skb) ? - igbvf_tso(adapter, tx_ring, skb, tx_flags, &hdr_len, protocol) : 0; + tso = igbvf_tso(tx_ring, skb, tx_flags, &hdr_len); if (unlikely(tso < 0)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -2615,6 +2603,40 @@ static int igbvf_set_features(struct net_device *netdev, return 0; } +#define IGBVF_MAX_MAC_HDR_LEN 127 +#define IGBVF_MAX_NETWORK_HDR_LEN 511 + +static netdev_features_t +igbvf_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + unsigned int network_hdr_len, mac_hdr_len; + + /* Make certain the headers can be described by a context descriptor */ + mac_hdr_len = skb_network_header(skb) - skb->data; + if (unlikely(mac_hdr_len > IGBVF_MAX_MAC_HDR_LEN)) + return features & ~(NETIF_F_HW_CSUM | + NETIF_F_SCTP_CRC | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_TSO | + NETIF_F_TSO6); + + network_hdr_len = skb_checksum_start(skb) - skb_network_header(skb); + if (unlikely(network_hdr_len > IGBVF_MAX_NETWORK_HDR_LEN)) + return features & ~(NETIF_F_HW_CSUM | + NETIF_F_SCTP_CRC | + NETIF_F_TSO | + NETIF_F_TSO6); + + /* We can only support IPV4 TSO in tunnels if we can mangle the + * inner IP ID field, so strip TSO if MANGLEID is not supported. + */ + if (skb->encapsulation && !(features & NETIF_F_TSO_MANGLEID)) + features &= ~NETIF_F_TSO; + + return features; +} + static const struct net_device_ops igbvf_netdev_ops = { .ndo_open = igbvf_open, .ndo_stop = igbvf_close, @@ -2631,7 +2653,7 @@ static const struct net_device_ops igbvf_netdev_ops = { .ndo_poll_controller = igbvf_netpoll, #endif .ndo_set_features = igbvf_set_features, - .ndo_features_check = passthru_features_check, + .ndo_features_check = igbvf_features_check, }; /** @@ -2739,22 +2761,30 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_HW_CSUM | NETIF_F_SCTP_CRC; - netdev->features = netdev->hw_features | - NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_VLAN_CTAG_FILTER; +#define IGBVF_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \ + NETIF_F_GSO_GRE_CSUM | \ + NETIF_F_GSO_IPXIP4 | \ + NETIF_F_GSO_IPXIP6 | \ + NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_UDP_TUNNEL_CSUM) + + netdev->gso_partial_features = IGBVF_GSO_PARTIAL_FEATURES; + netdev->hw_features |= NETIF_F_GSO_PARTIAL | + IGBVF_GSO_PARTIAL_FEATURES; + + netdev->features = netdev->hw_features; if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - netdev->vlan_features |= NETIF_F_SG | - NETIF_F_TSO | - NETIF_F_TSO6 | - NETIF_F_HW_CSUM | - NETIF_F_SCTP_CRC; - + netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; netdev->mpls_features |= NETIF_F_HW_CSUM; - netdev->hw_enc_features |= NETIF_F_HW_CSUM; + netdev->hw_enc_features |= netdev->vlan_features; + + /* set this bit last since it cannot be part of vlan_features */ + netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX; /*reset the controller to put the device in a known good state */ err = hw->mac.ops.reset_hw(hw); diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index a13baa90ae20..335ba6642145 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -266,7 +266,7 @@ static s32 e1000_set_vfta_vf(struct e1000_hw *hw, u16 vid, bool set) msgbuf[1] = vid; /* Setting the 8 bit field MSG INFO to true indicates "add" */ if (set) - msgbuf[0] |= 1 << E1000_VT_MSGINFO_SHIFT; + msgbuf[0] |= BIT(E1000_VT_MSGINFO_SHIFT); mbx->ops.write_posted(hw, msgbuf, 2); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 781c8787ab66..9f2db1855412 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -644,6 +644,7 @@ struct ixgbe_adapter { #define IXGBE_FLAG_VXLAN_OFFLOAD_CAPABLE BIT(24) #define IXGBE_FLAG_RX_HWTSTAMP_ENABLED BIT(25) #define IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER BIT(26) +#define IXGBE_FLAG_DCB_CAPABLE BIT(27) u32 flags2; #define IXGBE_FLAG2_RSC_CAPABLE BIT(0) @@ -792,7 +793,7 @@ struct ixgbe_adapter { unsigned long fwd_bitmask; /* Bitmask indicating in use pools */ #define IXGBE_MAX_LINK_HANDLE 10 - struct ixgbe_mat_field *jump_tables[IXGBE_MAX_LINK_HANDLE]; + struct ixgbe_jump_table *jump_tables[IXGBE_MAX_LINK_HANDLE]; unsigned long tables; /* maximum number of RETA entries among all devices supported by ixgbe @@ -895,8 +896,8 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *, struct ixgbe_ring *); void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter, struct ixgbe_ring *); void ixgbe_update_stats(struct ixgbe_adapter *adapter); int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter); -int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, - u16 subdevice_id); +bool ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, + u16 subdevice_id); #ifdef CONFIG_PCI_IOV void ixgbe_full_sync_mac_table(struct ixgbe_adapter *adapter); #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index d3efcb4fecce..59b771b9b354 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -533,10 +533,8 @@ static void ixgbe_get_regs(struct net_device *netdev, /* Flow Control */ regs_buff[30] = IXGBE_READ_REG(hw, IXGBE_PFCTOP); - regs_buff[31] = IXGBE_READ_REG(hw, IXGBE_FCTTV(0)); - regs_buff[32] = IXGBE_READ_REG(hw, IXGBE_FCTTV(1)); - regs_buff[33] = IXGBE_READ_REG(hw, IXGBE_FCTTV(2)); - regs_buff[34] = IXGBE_READ_REG(hw, IXGBE_FCTTV(3)); + for (i = 0; i < 4; i++) + regs_buff[31 + i] = IXGBE_READ_REG(hw, IXGBE_FCTTV(i)); for (i = 0; i < 8; i++) { switch (hw->mac.type) { case ixgbe_mac_82598EB: @@ -720,8 +718,10 @@ static void ixgbe_get_regs(struct net_device *netdev, regs_buff[939] = IXGBE_GET_STAT(adapter, bprc); regs_buff[940] = IXGBE_GET_STAT(adapter, mprc); regs_buff[941] = IXGBE_GET_STAT(adapter, gptc); - regs_buff[942] = IXGBE_GET_STAT(adapter, gorc); - regs_buff[944] = IXGBE_GET_STAT(adapter, gotc); + regs_buff[942] = (u32)IXGBE_GET_STAT(adapter, gorc); + regs_buff[943] = (u32)(IXGBE_GET_STAT(adapter, gorc) >> 32); + regs_buff[944] = (u32)IXGBE_GET_STAT(adapter, gotc); + regs_buff[945] = (u32)(IXGBE_GET_STAT(adapter, gotc) >> 32); for (i = 0; i < 8; i++) regs_buff[946 + i] = IXGBE_GET_STAT(adapter, rnbc[i]); regs_buff[954] = IXGBE_GET_STAT(adapter, ruc); @@ -731,7 +731,8 @@ static void ixgbe_get_regs(struct net_device *netdev, regs_buff[958] = IXGBE_GET_STAT(adapter, mngprc); regs_buff[959] = IXGBE_GET_STAT(adapter, mngpdc); regs_buff[960] = IXGBE_GET_STAT(adapter, mngptc); - regs_buff[961] = IXGBE_GET_STAT(adapter, tor); + regs_buff[961] = (u32)IXGBE_GET_STAT(adapter, tor); + regs_buff[962] = (u32)(IXGBE_GET_STAT(adapter, tor) >> 32); regs_buff[963] = IXGBE_GET_STAT(adapter, tpr); regs_buff[964] = IXGBE_GET_STAT(adapter, tpt); regs_buff[965] = IXGBE_GET_STAT(adapter, ptc64); @@ -803,15 +804,11 @@ static void ixgbe_get_regs(struct net_device *netdev, regs_buff[1096 + i] = IXGBE_READ_REG(hw, IXGBE_TIC_DW(i)); regs_buff[1100] = IXGBE_READ_REG(hw, IXGBE_TDPROBE); regs_buff[1101] = IXGBE_READ_REG(hw, IXGBE_TXBUFCTRL); - regs_buff[1102] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA0); - regs_buff[1103] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA1); - regs_buff[1104] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA2); - regs_buff[1105] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA3); + for (i = 0; i < 4; i++) + regs_buff[1102 + i] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA(i)); regs_buff[1106] = IXGBE_READ_REG(hw, IXGBE_RXBUFCTRL); - regs_buff[1107] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA0); - regs_buff[1108] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA1); - regs_buff[1109] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA2); - regs_buff[1110] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA3); + for (i = 0; i < 4; i++) + regs_buff[1107 + i] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA(i)); for (i = 0; i < 8; i++) regs_buff[1111 + i] = IXGBE_READ_REG(hw, IXGBE_PCIE_DIAG(i)); regs_buff[1119] = IXGBE_READ_REG(hw, IXGBE_RFVAL); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0ef4a15bb23e..088c47cf27d9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -53,6 +53,7 @@ #include <net/vxlan.h> #include <net/pkt_cls.h> #include <net/tc_act/tc_gact.h> +#include <net/tc_act/tc_mirred.h> #include "ixgbe.h" #include "ixgbe_common.h" @@ -608,7 +609,7 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) pr_info("%-15s %016lX %016lX %016lX\n", netdev->name, netdev->state, - netdev->trans_start, + dev_trans_start(netdev), netdev->last_rx); } @@ -3766,9 +3767,9 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) reg_offset = (VMDQ_P(0) >= 32) ? 1 : 0; /* Enable only the PF's pool for Tx/Rx */ - IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), GENMASK(vf_shift, 31)); + IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset), GENMASK(31, vf_shift)); IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset ^ 1), reg_offset - 1); - IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), GENMASK(vf_shift, 31)); + IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), GENMASK(31, vf_shift)); IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset ^ 1), reg_offset - 1); if (adapter->bridge_mode == BRIDGE_MODE_VEB) IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); @@ -5287,7 +5288,7 @@ void ixgbe_reinit_locked(struct ixgbe_adapter *adapter) { WARN_ON(in_interrupt()); /* put off any impending NetWatchDogTimeout */ - adapter->netdev->trans_start = jiffies; + netif_trans_update(adapter->netdev); while (test_and_set_bit(__IXGBE_RESETTING, &adapter->state)) usleep_range(1000, 2000); @@ -5558,6 +5559,58 @@ static void ixgbe_tx_timeout(struct net_device *netdev) ixgbe_tx_timeout_reset(adapter); } +#ifdef CONFIG_IXGBE_DCB +static void ixgbe_init_dcb(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + struct tc_configuration *tc; + int j; + + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + case ixgbe_mac_82599EB: + adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS; + adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS; + break; + case ixgbe_mac_X540: + case ixgbe_mac_X550: + adapter->dcb_cfg.num_tcs.pg_tcs = X540_TRAFFIC_CLASS; + adapter->dcb_cfg.num_tcs.pfc_tcs = X540_TRAFFIC_CLASS; + break; + case ixgbe_mac_X550EM_x: + case ixgbe_mac_x550em_a: + default: + adapter->dcb_cfg.num_tcs.pg_tcs = DEF_TRAFFIC_CLASS; + adapter->dcb_cfg.num_tcs.pfc_tcs = DEF_TRAFFIC_CLASS; + break; + } + + /* Configure DCB traffic classes */ + for (j = 0; j < MAX_TRAFFIC_CLASS; j++) { + tc = &adapter->dcb_cfg.tc_config[j]; + tc->path[DCB_TX_CONFIG].bwg_id = 0; + tc->path[DCB_TX_CONFIG].bwg_percent = 12 + (j & 1); + tc->path[DCB_RX_CONFIG].bwg_id = 0; + tc->path[DCB_RX_CONFIG].bwg_percent = 12 + (j & 1); + tc->dcb_pfc = pfc_disabled; + } + + /* Initialize default user to priority mapping, UPx->TC0 */ + tc = &adapter->dcb_cfg.tc_config[0]; + tc->path[DCB_TX_CONFIG].up_to_tc_bitmap = 0xFF; + tc->path[DCB_RX_CONFIG].up_to_tc_bitmap = 0xFF; + + adapter->dcb_cfg.bw_percentage[DCB_TX_CONFIG][0] = 100; + adapter->dcb_cfg.bw_percentage[DCB_RX_CONFIG][0] = 100; + adapter->dcb_cfg.pfc_mode_enable = false; + adapter->dcb_set_bitmap = 0x00; + if (adapter->flags & IXGBE_FLAG_DCB_CAPABLE) + adapter->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE; + memcpy(&adapter->temp_dcb_cfg, &adapter->dcb_cfg, + sizeof(adapter->temp_dcb_cfg)); +} +#endif + /** * ixgbe_sw_init - Initialize general software structures (struct ixgbe_adapter) * @adapter: board private structure to initialize @@ -5573,10 +5626,7 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) unsigned int rss, fdir; u32 fwsm; u16 device_caps; -#ifdef CONFIG_IXGBE_DCB - int j; - struct tc_configuration *tc; -#endif + int i; /* PCI config space info */ @@ -5598,6 +5648,10 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) #ifdef CONFIG_IXGBE_DCA adapter->flags |= IXGBE_FLAG_DCA_CAPABLE; #endif +#ifdef CONFIG_IXGBE_DCB + adapter->flags |= IXGBE_FLAG_DCB_CAPABLE; + adapter->flags &= ~IXGBE_FLAG_DCB_ENABLED; +#endif #ifdef IXGBE_FCOE adapter->flags |= IXGBE_FLAG_FCOE_CAPABLE; adapter->flags &= ~IXGBE_FLAG_FCOE_ENABLED; @@ -5608,7 +5662,14 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) #endif /* IXGBE_FCOE */ /* initialize static ixgbe jump table entries */ - adapter->jump_tables[0] = ixgbe_ipv4_fields; + adapter->jump_tables[0] = kzalloc(sizeof(*adapter->jump_tables[0]), + GFP_KERNEL); + if (!adapter->jump_tables[0]) + return -ENOMEM; + adapter->jump_tables[0]->mat = ixgbe_ipv4_fields; + + for (i = 1; i < IXGBE_MAX_LINK_HANDLE; i++) + adapter->jump_tables[i] = NULL; adapter->mac_table = kzalloc(sizeof(struct ixgbe_mac_addr) * hw->mac.num_rar_entries, @@ -5647,6 +5708,16 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) break; case ixgbe_mac_X550EM_x: case ixgbe_mac_x550em_a: +#ifdef CONFIG_IXGBE_DCB + adapter->flags &= ~IXGBE_FLAG_DCB_CAPABLE; +#endif +#ifdef IXGBE_FCOE + adapter->flags &= ~IXGBE_FLAG_FCOE_CAPABLE; +#ifdef CONFIG_IXGBE_DCB + adapter->fcoe.up = 0; +#endif /* IXGBE_DCB */ +#endif /* IXGBE_FCOE */ + /* Fall Through */ case ixgbe_mac_X550: #ifdef CONFIG_IXGBE_DCA adapter->flags &= ~IXGBE_FLAG_DCA_CAPABLE; @@ -5668,43 +5739,7 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) spin_lock_init(&adapter->fdir_perfect_lock); #ifdef CONFIG_IXGBE_DCB - switch (hw->mac.type) { - case ixgbe_mac_X540: - case ixgbe_mac_X550: - case ixgbe_mac_X550EM_x: - case ixgbe_mac_x550em_a: - adapter->dcb_cfg.num_tcs.pg_tcs = X540_TRAFFIC_CLASS; - adapter->dcb_cfg.num_tcs.pfc_tcs = X540_TRAFFIC_CLASS; - break; - default: - adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS; - adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS; - break; - } - - /* Configure DCB traffic classes */ - for (j = 0; j < MAX_TRAFFIC_CLASS; j++) { - tc = &adapter->dcb_cfg.tc_config[j]; - tc->path[DCB_TX_CONFIG].bwg_id = 0; - tc->path[DCB_TX_CONFIG].bwg_percent = 12 + (j & 1); - tc->path[DCB_RX_CONFIG].bwg_id = 0; - tc->path[DCB_RX_CONFIG].bwg_percent = 12 + (j & 1); - tc->dcb_pfc = pfc_disabled; - } - - /* Initialize default user to priority mapping, UPx->TC0 */ - tc = &adapter->dcb_cfg.tc_config[0]; - tc->path[DCB_TX_CONFIG].up_to_tc_bitmap = 0xFF; - tc->path[DCB_RX_CONFIG].up_to_tc_bitmap = 0xFF; - - adapter->dcb_cfg.bw_percentage[DCB_TX_CONFIG][0] = 100; - adapter->dcb_cfg.bw_percentage[DCB_RX_CONFIG][0] = 100; - adapter->dcb_cfg.pfc_mode_enable = false; - adapter->dcb_set_bitmap = 0x00; - adapter->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_CEE; - memcpy(&adapter->temp_dcb_cfg, &adapter->dcb_cfg, - sizeof(adapter->temp_dcb_cfg)); - + ixgbe_init_dcb(adapter); #endif /* default flow control settings */ @@ -8319,6 +8354,134 @@ static int ixgbe_configure_clsu32_del_hnode(struct ixgbe_adapter *adapter, return 0; } +#ifdef CONFIG_NET_CLS_ACT +static int handle_redirect_action(struct ixgbe_adapter *adapter, int ifindex, + u8 *queue, u64 *action) +{ + unsigned int num_vfs = adapter->num_vfs, vf; + struct net_device *upper; + struct list_head *iter; + + /* redirect to a SRIOV VF */ + for (vf = 0; vf < num_vfs; ++vf) { + upper = pci_get_drvdata(adapter->vfinfo[vf].vfdev); + if (upper->ifindex == ifindex) { + if (adapter->num_rx_pools > 1) + *queue = vf * 2; + else + *queue = vf * adapter->num_rx_queues_per_pool; + + *action = vf + 1; + *action <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + return 0; + } + } + + /* redirect to a offloaded macvlan netdev */ + netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { + if (netif_is_macvlan(upper)) { + struct macvlan_dev *dfwd = netdev_priv(upper); + struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; + + if (vadapter && vadapter->netdev->ifindex == ifindex) { + *queue = adapter->rx_ring[vadapter->rx_base_queue]->reg_idx; + *action = *queue; + return 0; + } + } + } + + return -EINVAL; +} + +static int parse_tc_actions(struct ixgbe_adapter *adapter, + struct tcf_exts *exts, u64 *action, u8 *queue) +{ + const struct tc_action *a; + int err; + + if (tc_no_actions(exts)) + return -EINVAL; + + tc_for_each_action(a, exts) { + + /* Drop action */ + if (is_tcf_gact_shot(a)) { + *action = IXGBE_FDIR_DROP_QUEUE; + *queue = IXGBE_FDIR_DROP_QUEUE; + return 0; + } + + /* Redirect to a VF or a offloaded macvlan */ + if (is_tcf_mirred_redirect(a)) { + int ifindex = tcf_mirred_ifindex(a); + + err = handle_redirect_action(adapter, ifindex, queue, + action); + if (err == 0) + return err; + } + } + + return -EINVAL; +} +#else +static int parse_tc_actions(struct ixgbe_adapter *adapter, + struct tcf_exts *exts, u64 *action, u8 *queue) +{ + return -EINVAL; +} +#endif /* CONFIG_NET_CLS_ACT */ + +static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input, + union ixgbe_atr_input *mask, + struct tc_cls_u32_offload *cls, + struct ixgbe_mat_field *field_ptr, + struct ixgbe_nexthdr *nexthdr) +{ + int i, j, off; + __be32 val, m; + bool found_entry = false, found_jump_field = false; + + for (i = 0; i < cls->knode.sel->nkeys; i++) { + off = cls->knode.sel->keys[i].off; + val = cls->knode.sel->keys[i].val; + m = cls->knode.sel->keys[i].mask; + + for (j = 0; field_ptr[j].val; j++) { + if (field_ptr[j].off == off) { + field_ptr[j].val(input, mask, val, m); + input->filter.formatted.flow_type |= + field_ptr[j].type; + found_entry = true; + break; + } + } + if (nexthdr) { + if (nexthdr->off == cls->knode.sel->keys[i].off && + nexthdr->val == cls->knode.sel->keys[i].val && + nexthdr->mask == cls->knode.sel->keys[i].mask) + found_jump_field = true; + else + continue; + } + } + + if (nexthdr && !found_jump_field) + return -EINVAL; + + if (!found_entry) + return 0; + + mask->formatted.flow_type = IXGBE_ATR_L4TYPE_IPV6_MASK | + IXGBE_ATR_L4TYPE_MASK; + + if (input->filter.formatted.flow_type == IXGBE_ATR_FLOW_TYPE_IPV4) + mask->formatted.flow_type &= IXGBE_ATR_L4TYPE_IPV6_MASK; + + return 0; +} + static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, __be16 protocol, struct tc_cls_u32_offload *cls) @@ -8326,16 +8489,13 @@ static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, u32 loc = cls->knode.handle & 0xfffff; struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_mat_field *field_ptr; - struct ixgbe_fdir_filter *input; - union ixgbe_atr_input mask; -#ifdef CONFIG_NET_CLS_ACT - const struct tc_action *a; -#endif - int i, err = 0; + struct ixgbe_fdir_filter *input = NULL; + union ixgbe_atr_input *mask = NULL; + struct ixgbe_jump_table *jump = NULL; + int i, err = -EINVAL; u8 queue; u32 uhtid, link_uhtid; - memset(&mask, 0, sizeof(union ixgbe_atr_input)); uhtid = TC_U32_USERHTID(cls->knode.handle); link_uhtid = TC_U32_USERHTID(cls->knode.link_handle); @@ -8347,39 +8507,11 @@ static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, * headers when needed. */ if (protocol != htons(ETH_P_IP)) - return -EINVAL; - - if (link_uhtid) { - struct ixgbe_nexthdr *nexthdr = ixgbe_ipv4_jumps; - - if (link_uhtid >= IXGBE_MAX_LINK_HANDLE) - return -EINVAL; - - if (!test_bit(link_uhtid - 1, &adapter->tables)) - return -EINVAL; - - for (i = 0; nexthdr[i].jump; i++) { - if (nexthdr[i].o != cls->knode.sel->offoff || - nexthdr[i].s != cls->knode.sel->offshift || - nexthdr[i].m != cls->knode.sel->offmask || - /* do not support multiple key jumps its just mad */ - cls->knode.sel->nkeys > 1) - return -EINVAL; - - if (nexthdr[i].off == cls->knode.sel->keys[0].off && - nexthdr[i].val == cls->knode.sel->keys[0].val && - nexthdr[i].mask == cls->knode.sel->keys[0].mask) { - adapter->jump_tables[link_uhtid] = - nexthdr[i].jump; - break; - } - } - return 0; - } + return err; if (loc >= ((1024 << adapter->fdir_pballoc) - 2)) { e_err(drv, "Location out of range\n"); - return -EINVAL; + return err; } /* cls u32 is a graph starting at root node 0x800. The driver tracks @@ -8390,87 +8522,123 @@ static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, * this function _should_ be generic try not to hardcode values here. */ if (uhtid == 0x800) { - field_ptr = adapter->jump_tables[0]; + field_ptr = (adapter->jump_tables[0])->mat; } else { if (uhtid >= IXGBE_MAX_LINK_HANDLE) - return -EINVAL; - - field_ptr = adapter->jump_tables[uhtid]; + return err; + if (!adapter->jump_tables[uhtid]) + return err; + field_ptr = (adapter->jump_tables[uhtid])->mat; } if (!field_ptr) - return -EINVAL; + return err; - input = kzalloc(sizeof(*input), GFP_KERNEL); - if (!input) - return -ENOMEM; + /* At this point we know the field_ptr is valid and need to either + * build cls_u32 link or attach filter. Because adding a link to + * a handle that does not exist is invalid and the same for adding + * rules to handles that don't exist. + */ - for (i = 0; i < cls->knode.sel->nkeys; i++) { - int off = cls->knode.sel->keys[i].off; - __be32 val = cls->knode.sel->keys[i].val; - __be32 m = cls->knode.sel->keys[i].mask; - bool found_entry = false; - int j; + if (link_uhtid) { + struct ixgbe_nexthdr *nexthdr = ixgbe_ipv4_jumps; - for (j = 0; field_ptr[j].val; j++) { - if (field_ptr[j].off == off) { - field_ptr[j].val(input, &mask, val, m); - input->filter.formatted.flow_type |= - field_ptr[j].type; - found_entry = true; + if (link_uhtid >= IXGBE_MAX_LINK_HANDLE) + return err; + + if (!test_bit(link_uhtid - 1, &adapter->tables)) + return err; + + for (i = 0; nexthdr[i].jump; i++) { + if (nexthdr[i].o != cls->knode.sel->offoff || + nexthdr[i].s != cls->knode.sel->offshift || + nexthdr[i].m != cls->knode.sel->offmask) + return err; + + jump = kzalloc(sizeof(*jump), GFP_KERNEL); + if (!jump) + return -ENOMEM; + input = kzalloc(sizeof(*input), GFP_KERNEL); + if (!input) { + err = -ENOMEM; + goto free_jump; + } + mask = kzalloc(sizeof(*mask), GFP_KERNEL); + if (!mask) { + err = -ENOMEM; + goto free_input; + } + jump->input = input; + jump->mask = mask; + err = ixgbe_clsu32_build_input(input, mask, cls, + field_ptr, &nexthdr[i]); + if (!err) { + jump->mat = nexthdr[i].jump; + adapter->jump_tables[link_uhtid] = jump; break; } } - - if (!found_entry) - goto err_out; + return 0; } - mask.formatted.flow_type = IXGBE_ATR_L4TYPE_IPV6_MASK | - IXGBE_ATR_L4TYPE_MASK; - - if (input->filter.formatted.flow_type == IXGBE_ATR_FLOW_TYPE_IPV4) - mask.formatted.flow_type &= IXGBE_ATR_L4TYPE_IPV6_MASK; + input = kzalloc(sizeof(*input), GFP_KERNEL); + if (!input) + return -ENOMEM; + mask = kzalloc(sizeof(*mask), GFP_KERNEL); + if (!mask) { + err = -ENOMEM; + goto free_input; + } -#ifdef CONFIG_NET_CLS_ACT - if (list_empty(&cls->knode.exts->actions)) + if ((uhtid != 0x800) && (adapter->jump_tables[uhtid])) { + if ((adapter->jump_tables[uhtid])->input) + memcpy(input, (adapter->jump_tables[uhtid])->input, + sizeof(*input)); + if ((adapter->jump_tables[uhtid])->mask) + memcpy(mask, (adapter->jump_tables[uhtid])->mask, + sizeof(*mask)); + } + err = ixgbe_clsu32_build_input(input, mask, cls, field_ptr, NULL); + if (err) goto err_out; - list_for_each_entry(a, &cls->knode.exts->actions, list) { - if (!is_tcf_gact_shot(a)) - goto err_out; - } -#endif + err = parse_tc_actions(adapter, cls->knode.exts, &input->action, + &queue); + if (err < 0) + goto err_out; - input->action = IXGBE_FDIR_DROP_QUEUE; - queue = IXGBE_FDIR_DROP_QUEUE; input->sw_idx = loc; spin_lock(&adapter->fdir_perfect_lock); if (hlist_empty(&adapter->fdir_filter_list)) { - memcpy(&adapter->fdir_mask, &mask, sizeof(mask)); - err = ixgbe_fdir_set_input_mask_82599(hw, &mask); + memcpy(&adapter->fdir_mask, mask, sizeof(*mask)); + err = ixgbe_fdir_set_input_mask_82599(hw, mask); if (err) goto err_out_w_lock; - } else if (memcmp(&adapter->fdir_mask, &mask, sizeof(mask))) { + } else if (memcmp(&adapter->fdir_mask, mask, sizeof(*mask))) { err = -EINVAL; goto err_out_w_lock; } - ixgbe_atr_compute_perfect_hash_82599(&input->filter, &mask); + ixgbe_atr_compute_perfect_hash_82599(&input->filter, mask); err = ixgbe_fdir_write_perfect_filter_82599(hw, &input->filter, input->sw_idx, queue); if (!err) ixgbe_update_ethtool_fdir_entry(adapter, input, input->sw_idx); spin_unlock(&adapter->fdir_perfect_lock); + kfree(mask); return err; err_out_w_lock: spin_unlock(&adapter->fdir_perfect_lock); err_out: + kfree(mask); +free_input: kfree(input); - return -EINVAL; +free_jump: + kfree(jump); + return err; } static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, __be16 proto, @@ -9043,7 +9211,7 @@ static inline int ixgbe_enumerate_functions(struct ixgbe_adapter *adapter) /** * ixgbe_wol_supported - Check whether device supports WoL - * @hw: hw specific details + * @adapter: the adapter private structure * @device_id: the device ID * @subdev_id: the subsystem device ID * @@ -9051,19 +9219,33 @@ static inline int ixgbe_enumerate_functions(struct ixgbe_adapter *adapter) * which devices have WoL support * **/ -int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, - u16 subdevice_id) +bool ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, + u16 subdevice_id) { struct ixgbe_hw *hw = &adapter->hw; u16 wol_cap = adapter->eeprom_cap & IXGBE_DEVICE_CAPS_WOL_MASK; - int is_wol_supported = 0; + /* WOL not supported on 82598 */ + if (hw->mac.type == ixgbe_mac_82598EB) + return false; + + /* check eeprom to see if WOL is enabled for X540 and newer */ + if (hw->mac.type >= ixgbe_mac_X540) { + if ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0_1) || + ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0) && + (hw->bus.func == 0))) + return true; + } + + /* WOL is determined based on device IDs for 82599 MACs */ switch (device_id) { case IXGBE_DEV_ID_82599_SFP: /* Only these subdevices could supports WOL */ switch (subdevice_id) { - case IXGBE_SUBDEV_ID_82599_SFP_WOL0: case IXGBE_SUBDEV_ID_82599_560FLR: + case IXGBE_SUBDEV_ID_82599_LOM_SNAP6: + case IXGBE_SUBDEV_ID_82599_SFP_WOL0: + case IXGBE_SUBDEV_ID_82599_SFP_2OCP: /* only support first port */ if (hw->bus.func != 0) break; @@ -9071,44 +9253,31 @@ int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, case IXGBE_SUBDEV_ID_82599_SFP: case IXGBE_SUBDEV_ID_82599_RNDC: case IXGBE_SUBDEV_ID_82599_ECNA_DP: - case IXGBE_SUBDEV_ID_82599_LOM_SFP: - is_wol_supported = 1; - break; + case IXGBE_SUBDEV_ID_82599_SFP_1OCP: + case IXGBE_SUBDEV_ID_82599_SFP_LOM_OEM1: + case IXGBE_SUBDEV_ID_82599_SFP_LOM_OEM2: + return true; } break; case IXGBE_DEV_ID_82599EN_SFP: - /* Only this subdevice supports WOL */ + /* Only these subdevices support WOL */ switch (subdevice_id) { case IXGBE_SUBDEV_ID_82599EN_SFP_OCP1: - is_wol_supported = 1; - break; + return true; } break; case IXGBE_DEV_ID_82599_COMBO_BACKPLANE: /* All except this subdevice support WOL */ if (subdevice_id != IXGBE_SUBDEV_ID_82599_KX4_KR_MEZZ) - is_wol_supported = 1; + return true; break; case IXGBE_DEV_ID_82599_KX4: - is_wol_supported = 1; - break; - case IXGBE_DEV_ID_X540T: - case IXGBE_DEV_ID_X540T1: - case IXGBE_DEV_ID_X550T: - case IXGBE_DEV_ID_X550T1: - case IXGBE_DEV_ID_X550EM_X_KX4: - case IXGBE_DEV_ID_X550EM_X_KR: - case IXGBE_DEV_ID_X550EM_X_10G_T: - /* check eeprom to see if enabled wol */ - if ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0_1) || - ((wol_cap == IXGBE_DEVICE_CAPS_WOL_PORT0) && - (hw->bus.func == 0))) { - is_wol_supported = 1; - } + return true; + default: break; } - return is_wol_supported; + return false; } /** @@ -9313,8 +9482,8 @@ skip_sriov: #define IXGBE_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \ NETIF_F_GSO_GRE_CSUM | \ - NETIF_F_GSO_IPIP | \ - NETIF_F_GSO_SIT | \ + NETIF_F_GSO_IPXIP4 | \ + NETIF_F_GSO_IPXIP6 | \ NETIF_F_GSO_UDP_TUNNEL | \ NETIF_F_GSO_UDP_TUNNEL_CSUM) @@ -9339,20 +9508,21 @@ skip_sriov: if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; + netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; + netdev->hw_enc_features |= netdev->vlan_features; + netdev->mpls_features |= NETIF_F_HW_CSUM; + /* set this bit last since it cannot be part of vlan_features */ netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; - netdev->vlan_features |= netdev->features | NETIF_F_TSO_MANGLEID; - netdev->hw_enc_features |= netdev->vlan_features; - netdev->mpls_features |= NETIF_F_HW_CSUM; - netdev->priv_flags |= IFF_UNICAST_FLT; netdev->priv_flags |= IFF_SUPP_NOFCS; #ifdef CONFIG_IXGBE_DCB - netdev->dcbnl_ops = &dcbnl_ops; + if (adapter->flags & IXGBE_FLAG_DCB_CAPABLE) + netdev->dcbnl_ops = &dcbnl_ops; #endif #ifdef IXGBE_FCOE @@ -9542,6 +9712,7 @@ err_sw_init: ixgbe_disable_sriov(adapter); adapter->flags2 &= ~IXGBE_FLAG2_SEARCH_FOR_SFP; iounmap(adapter->io_addr); + kfree(adapter->jump_tables[0]); kfree(adapter->mac_table); err_ioremap: disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); @@ -9570,6 +9741,7 @@ static void ixgbe_remove(struct pci_dev *pdev) struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev; bool disable_dev; + int i; /* if !adapter then we already cleaned up in probe */ if (!adapter) @@ -9619,6 +9791,14 @@ static void ixgbe_remove(struct pci_dev *pdev) e_dev_info("complete\n"); + for (i = 0; i < IXGBE_MAX_LINK_HANDLE; i++) { + if (adapter->jump_tables[i]) { + kfree(adapter->jump_tables[i]->input); + kfree(adapter->jump_tables[i]->mask); + } + kfree(adapter->jump_tables[i]); + } + kfree(adapter->mac_table); disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h index 60adde55a8c3..a8bed3d887f7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_model.h @@ -38,6 +38,12 @@ struct ixgbe_mat_field { unsigned int type; }; +struct ixgbe_jump_table { + struct ixgbe_mat_field *mat; + struct ixgbe_fdir_filter *input; + union ixgbe_atr_input *mask; +}; + static inline int ixgbe_mat_prgm_sip(struct ixgbe_fdir_filter *input, union ixgbe_atr_input *mask, u32 val, u32 m) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 7af451460374..da3d8358fee0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -59,8 +59,12 @@ #define IXGBE_SUBDEV_ID_82599_RNDC 0x1F72 #define IXGBE_SUBDEV_ID_82599_560FLR 0x17D0 #define IXGBE_SUBDEV_ID_82599_SP_560FLR 0x211B +#define IXGBE_SUBDEV_ID_82599_LOM_SNAP6 0x2159 +#define IXGBE_SUBDEV_ID_82599_SFP_1OCP 0x000D +#define IXGBE_SUBDEV_ID_82599_SFP_2OCP 0x0008 +#define IXGBE_SUBDEV_ID_82599_SFP_LOM_OEM1 0x8976 +#define IXGBE_SUBDEV_ID_82599_SFP_LOM_OEM2 0x06EE #define IXGBE_SUBDEV_ID_82599_ECNA_DP 0x0470 -#define IXGBE_SUBDEV_ID_82599_LOM_SFP 0x8976 #define IXGBE_DEV_ID_82599_SFP_EM 0x1507 #define IXGBE_DEV_ID_82599_SFP_SF2 0x154D #define IXGBE_DEV_ID_82599EN_SFP 0x1557 @@ -89,12 +93,8 @@ #define IXGBE_DEV_ID_X550EM_A_SFP 0x15CE /* VF Device IDs */ -#define IXGBE_DEV_ID_X550_VF_HV 0x1564 -#define IXGBE_DEV_ID_X550_VF 0x1565 -#define IXGBE_DEV_ID_X550EM_X_VF 0x15A8 -#define IXGBE_DEV_ID_X550EM_X_VF_HV 0x15A9 -#define IXGBE_DEV_ID_82599_VF 0x10ED -#define IXGBE_DEV_ID_X540_VF 0x1515 +#define IXGBE_DEV_ID_82599_VF 0x10ED +#define IXGBE_DEV_ID_X540_VF 0x1515 #define IXGBE_DEV_ID_X550_VF 0x1565 #define IXGBE_DEV_ID_X550EM_X_VF 0x15A8 #define IXGBE_DEV_ID_X550EM_A_VF 0x15C5 @@ -548,6 +548,7 @@ struct ixgbe_thermal_sensor_data { /* DCB registers */ #define MAX_TRAFFIC_CLASS 8 #define X540_TRAFFIC_CLASS 4 +#define DEF_TRAFFIC_CLASS 1 #define IXGBE_RMCS 0x03D00 #define IXGBE_DPMCS 0x07F40 #define IXGBE_PDPMCS 0x0CD00 @@ -1060,15 +1061,9 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_TIC_DW2(_i) (0x082B0 + ((_i) * 4)) #define IXGBE_TDPROBE 0x07F20 #define IXGBE_TXBUFCTRL 0x0C600 -#define IXGBE_TXBUFDATA0 0x0C610 -#define IXGBE_TXBUFDATA1 0x0C614 -#define IXGBE_TXBUFDATA2 0x0C618 -#define IXGBE_TXBUFDATA3 0x0C61C +#define IXGBE_TXBUFDATA(_i) (0x0C610 + ((_i) * 4)) /* 4 of these (0-3) */ #define IXGBE_RXBUFCTRL 0x03600 -#define IXGBE_RXBUFDATA0 0x03610 -#define IXGBE_RXBUFDATA1 0x03614 -#define IXGBE_RXBUFDATA2 0x03618 -#define IXGBE_RXBUFDATA3 0x0361C +#define IXGBE_RXBUFDATA(_i) (0x03610 + ((_i) * 4)) /* 4 of these (0-3) */ #define IXGBE_PCIE_DIAG(_i) (0x11090 + ((_i) * 4)) /* 8 of these */ #define IXGBE_RFVAL 0x050A4 #define IXGBE_MDFTC1 0x042B8 diff --git a/drivers/net/ethernet/intel/ixgbevf/defines.h b/drivers/net/ethernet/intel/ixgbevf/defines.h index 74901f7ef391..ae09d60e7b67 100644 --- a/drivers/net/ethernet/intel/ixgbevf/defines.h +++ b/drivers/net/ethernet/intel/ixgbevf/defines.h @@ -33,6 +33,11 @@ #define IXGBE_DEV_ID_X550_VF 0x1565 #define IXGBE_DEV_ID_X550EM_X_VF 0x15A8 +#define IXGBE_DEV_ID_82599_VF_HV 0x152E +#define IXGBE_DEV_ID_X540_VF_HV 0x1530 +#define IXGBE_DEV_ID_X550_VF_HV 0x1564 +#define IXGBE_DEV_ID_X550EM_X_VF_HV 0x15A9 + #define IXGBE_VF_IRQ_CLEAR_MASK 7 #define IXGBE_VF_MAX_TX_QUEUES 8 #define IXGBE_VF_MAX_RX_QUEUES 8 diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index aa28c4fb1a43..d5944c391cbb 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -450,9 +450,13 @@ enum ixbgevf_state_t { enum ixgbevf_boards { board_82599_vf, + board_82599_vf_hv, board_X540_vf, + board_X540_vf_hv, board_X550_vf, + board_X550_vf_hv, board_X550EM_x_vf, + board_X550EM_x_vf_hv, }; enum ixgbevf_xcast_modes { @@ -467,6 +471,12 @@ extern const struct ixgbevf_info ixgbevf_X550_vf_info; extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_info; extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops; +extern const struct ixgbevf_info ixgbevf_82599_vf_hv_info; +extern const struct ixgbevf_info ixgbevf_X540_vf_hv_info; +extern const struct ixgbevf_info ixgbevf_X550_vf_hv_info; +extern const struct ixgbevf_info ixgbevf_X550EM_x_vf_hv_info; +extern const struct ixgbe_mbx_operations ixgbevf_hv_mbx_ops; + /* needed by ethtool.c */ extern const char ixgbevf_driver_name[]; extern const char ixgbevf_driver_version[]; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 319e25f29883..acc24010cfe0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -62,10 +62,14 @@ static char ixgbevf_copyright[] = "Copyright (c) 2009 - 2015 Intel Corporation."; static const struct ixgbevf_info *ixgbevf_info_tbl[] = { - [board_82599_vf] = &ixgbevf_82599_vf_info, - [board_X540_vf] = &ixgbevf_X540_vf_info, - [board_X550_vf] = &ixgbevf_X550_vf_info, - [board_X550EM_x_vf] = &ixgbevf_X550EM_x_vf_info, + [board_82599_vf] = &ixgbevf_82599_vf_info, + [board_82599_vf_hv] = &ixgbevf_82599_vf_hv_info, + [board_X540_vf] = &ixgbevf_X540_vf_info, + [board_X540_vf_hv] = &ixgbevf_X540_vf_hv_info, + [board_X550_vf] = &ixgbevf_X550_vf_info, + [board_X550_vf_hv] = &ixgbevf_X550_vf_hv_info, + [board_X550EM_x_vf] = &ixgbevf_X550EM_x_vf_info, + [board_X550EM_x_vf_hv] = &ixgbevf_X550EM_x_vf_hv_info, }; /* ixgbevf_pci_tbl - PCI Device ID Table @@ -78,9 +82,13 @@ static const struct ixgbevf_info *ixgbevf_info_tbl[] = { */ static const struct pci_device_id ixgbevf_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF), board_82599_vf }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_VF_HV), board_82599_vf_hv }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF), board_X540_vf }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X540_VF_HV), board_X540_vf_hv }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF), board_X550_vf }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550_VF_HV), board_X550_vf_hv }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF), board_X550EM_x_vf }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_VF_HV), board_X550EM_x_vf_hv}, /* required last entry */ {0, } }; @@ -1752,9 +1760,15 @@ static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_VFRDLEN(reg_idx), ring->count * sizeof(union ixgbe_adv_rx_desc)); +#ifndef CONFIG_SPARC /* enable relaxed ordering */ IXGBE_WRITE_REG(hw, IXGBE_VFDCA_RXCTRL(reg_idx), IXGBE_DCA_RXCTRL_DESC_RRO_EN); +#else + IXGBE_WRITE_REG(hw, IXGBE_VFDCA_RXCTRL(reg_idx), + IXGBE_DCA_RXCTRL_DESC_RRO_EN | + IXGBE_DCA_RXCTRL_DATA_WRO_EN); +#endif /* reset head and tail pointers */ IXGBE_WRITE_REG(hw, IXGBE_VFRDH(reg_idx), 0); @@ -1795,7 +1809,7 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter) ixgbevf_setup_vfmrqc(adapter); /* notify the PF of our intent to use this size of frame */ - ixgbevf_rlpml_set_vf(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN); + hw->mac.ops.set_rlpml(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring @@ -1908,7 +1922,7 @@ static void ixgbevf_set_rx_mode(struct net_device *netdev) spin_lock_bh(&adapter->mbx_lock); - hw->mac.ops.update_xcast_mode(hw, netdev, xcast_mode); + hw->mac.ops.update_xcast_mode(hw, xcast_mode); /* reprogram multicast list */ hw->mac.ops.update_mc_addr_list(hw, netdev); @@ -3740,7 +3754,7 @@ static int ixgbevf_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; /* notify the PF of our intent to use this size of frame */ - ixgbevf_rlpml_set_vf(hw, max_frame); + hw->mac.ops.set_rlpml(hw, max_frame); return 0; } @@ -4048,8 +4062,8 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #define IXGBEVF_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \ NETIF_F_GSO_GRE_CSUM | \ - NETIF_F_GSO_IPIP | \ - NETIF_F_GSO_SIT | \ + NETIF_F_GSO_IPXIP4 | \ + NETIF_F_GSO_IPXIP6 | \ NETIF_F_GSO_UDP_TUNNEL | \ NETIF_F_GSO_UDP_TUNNEL_CSUM) diff --git a/drivers/net/ethernet/intel/ixgbevf/mbx.c b/drivers/net/ethernet/intel/ixgbevf/mbx.c index dc68fea4894b..61a80da8b6f0 100644 --- a/drivers/net/ethernet/intel/ixgbevf/mbx.c +++ b/drivers/net/ethernet/intel/ixgbevf/mbx.c @@ -346,3 +346,14 @@ const struct ixgbe_mbx_operations ixgbevf_mbx_ops = { .check_for_rst = ixgbevf_check_for_rst_vf, }; +/* Mailbox operations when running on Hyper-V. + * On Hyper-V, PF/VF communication is not through the + * hardware mailbox; this communication is through + * a software mediated path. + * Most mail box operations are noop while running on + * Hyper-V. + */ +const struct ixgbe_mbx_operations ixgbevf_hv_mbx_ops = { + .init_params = ixgbevf_init_mbx_params_vf, + .check_for_rst = ixgbevf_check_for_rst_vf, +}; diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 987ad69d4918..e670d3b19c3c 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -27,6 +27,12 @@ #include "vf.h" #include "ixgbevf.h" +/* On Hyper-V, to reset, we need to read from this offset + * from the PCI config space. This is the mechanism used on + * Hyper-V to support PF/VF communication. + */ +#define IXGBE_HV_RESET_OFFSET 0x201 + /** * ixgbevf_start_hw_vf - Prepare hardware for Tx/Rx * @hw: pointer to hardware structure @@ -126,6 +132,27 @@ static s32 ixgbevf_reset_hw_vf(struct ixgbe_hw *hw) } /** + * Hyper-V variant; the VF/PF communication is through the PCI + * config space. + */ +static s32 ixgbevf_hv_reset_hw_vf(struct ixgbe_hw *hw) +{ +#if IS_ENABLED(CONFIG_PCI_MMCONFIG) + struct ixgbevf_adapter *adapter = hw->back; + int i; + + for (i = 0; i < 6; i++) + pci_read_config_byte(adapter->pdev, + (i + IXGBE_HV_RESET_OFFSET), + &hw->mac.perm_addr[i]); + return 0; +#else + pr_err("PCI_MMCONFIG needs to be enabled for Hyper-V\n"); + return -EOPNOTSUPP; +#endif +} + +/** * ixgbevf_stop_hw_vf - Generic stop Tx/Rx units * @hw: pointer to hardware structure * @@ -258,6 +285,11 @@ static s32 ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr) return ret_val; } +static s32 ixgbevf_hv_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr) +{ + return -EOPNOTSUPP; +} + /** * ixgbevf_get_reta_locked - get the RSS redirection table (RETA) contents. * @adapter: pointer to the port handle @@ -416,6 +448,26 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, return ret_val; } +/** + * ixgbevf_hv_set_rar_vf - set device MAC address Hyper-V variant + * @hw: pointer to hardware structure + * @index: Receive address register to write + * @addr: Address to put into receive address register + * @vmdq: Unused in this implementation + * + * We don't really allow setting the device MAC address. However, + * if the address being set is the permanent MAC address we will + * permit that. + **/ +static s32 ixgbevf_hv_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, + u32 vmdq) +{ + if (ether_addr_equal(addr, hw->mac.perm_addr)) + return 0; + + return -EOPNOTSUPP; +} + static void ixgbevf_write_msg_read_ack(struct ixgbe_hw *hw, u32 *msg, u16 size) { @@ -473,15 +525,22 @@ static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw, } /** + * Hyper-V variant - just a stub. + */ +static s32 ixgbevf_hv_update_mc_addr_list_vf(struct ixgbe_hw *hw, + struct net_device *netdev) +{ + return -EOPNOTSUPP; +} + +/** * ixgbevf_update_xcast_mode - Update Multicast mode * @hw: pointer to the HW structure - * @netdev: pointer to net device structure * @xcast_mode: new multicast mode * * Updates the Multicast Mode of VF. **/ -static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, - struct net_device *netdev, int xcast_mode) +static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode) { struct ixgbe_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; @@ -513,6 +572,14 @@ static s32 ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, } /** + * Hyper-V variant - just a stub. + */ +static s32 ixgbevf_hv_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode) +{ + return -EOPNOTSUPP; +} + +/** * ixgbevf_set_vfta_vf - Set/Unset VLAN filter table address * @hw: pointer to the HW structure * @vlan: 12 bit VLAN ID @@ -551,6 +618,15 @@ mbx_err: } /** + * Hyper-V variant - just a stub. + */ +static s32 ixgbevf_hv_set_vfta_vf(struct ixgbe_hw *hw, u32 vlan, u32 vind, + bool vlan_on) +{ + return -EOPNOTSUPP; +} + +/** * ixgbevf_setup_mac_link_vf - Setup MAC link settings * @hw: pointer to hardware structure * @speed: Unused in this implementation @@ -656,11 +732,72 @@ out: } /** - * ixgbevf_rlpml_set_vf - Set the maximum receive packet length + * Hyper-V variant; there is no mailbox communication. + */ +static s32 ixgbevf_hv_check_mac_link_vf(struct ixgbe_hw *hw, + ixgbe_link_speed *speed, + bool *link_up, + bool autoneg_wait_to_complete) +{ + struct ixgbe_mbx_info *mbx = &hw->mbx; + struct ixgbe_mac_info *mac = &hw->mac; + u32 links_reg; + + /* If we were hit with a reset drop the link */ + if (!mbx->ops.check_for_rst(hw) || !mbx->timeout) + mac->get_link_status = true; + + if (!mac->get_link_status) + goto out; + + /* if link status is down no point in checking to see if pf is up */ + links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS); + if (!(links_reg & IXGBE_LINKS_UP)) + goto out; + + /* for SFP+ modules and DA cables on 82599 it can take up to 500usecs + * before the link status is correct + */ + if (mac->type == ixgbe_mac_82599_vf) { + int i; + + for (i = 0; i < 5; i++) { + udelay(100); + links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS); + + if (!(links_reg & IXGBE_LINKS_UP)) + goto out; + } + } + + switch (links_reg & IXGBE_LINKS_SPEED_82599) { + case IXGBE_LINKS_SPEED_10G_82599: + *speed = IXGBE_LINK_SPEED_10GB_FULL; + break; + case IXGBE_LINKS_SPEED_1G_82599: + *speed = IXGBE_LINK_SPEED_1GB_FULL; + break; + case IXGBE_LINKS_SPEED_100_82599: + *speed = IXGBE_LINK_SPEED_100_FULL; + break; + } + + /* if we passed all the tests above then the link is up and we no + * longer need to check for link + */ + mac->get_link_status = false; + +out: + *link_up = !mac->get_link_status; + return 0; +} + +/** + * ixgbevf_set_rlpml_vf - Set the maximum receive packet length * @hw: pointer to the HW structure * @max_size: value to assign to max frame size **/ -void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size) +static void ixgbevf_set_rlpml_vf(struct ixgbe_hw *hw, u16 max_size) { u32 msgbuf[2]; @@ -670,6 +807,25 @@ void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size) } /** + * ixgbevf_hv_set_rlpml_vf - Set the maximum receive packet length + * @hw: pointer to the HW structure + * @max_size: value to assign to max frame size + * Hyper-V variant. + **/ +static void ixgbevf_hv_set_rlpml_vf(struct ixgbe_hw *hw, u16 max_size) +{ + u32 reg; + + /* If we are on Hyper-V, we implement this functionality + * differently. + */ + reg = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(0)); + /* CRC == 4 */ + reg |= ((max_size + 4) | IXGBE_RXDCTL_RLPML_EN); + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(0), reg); +} + +/** * ixgbevf_negotiate_api_version_vf - Negotiate supported API version * @hw: pointer to the HW structure * @api: integer containing requested API version @@ -703,6 +859,21 @@ static int ixgbevf_negotiate_api_version_vf(struct ixgbe_hw *hw, int api) return err; } +/** + * ixgbevf_hv_negotiate_api_version_vf - Negotiate supported API version + * @hw: pointer to the HW structure + * @api: integer containing requested API version + * Hyper-V version - only ixgbe_mbox_api_10 supported. + **/ +static int ixgbevf_hv_negotiate_api_version_vf(struct ixgbe_hw *hw, int api) +{ + /* Hyper-V only supports api version ixgbe_mbox_api_10 */ + if (api != ixgbe_mbox_api_10) + return IXGBE_ERR_INVALID_ARGUMENT; + + return 0; +} + int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs, unsigned int *default_tc) { @@ -775,6 +946,24 @@ static const struct ixgbe_mac_operations ixgbevf_mac_ops = { .update_xcast_mode = ixgbevf_update_xcast_mode, .set_uc_addr = ixgbevf_set_uc_addr_vf, .set_vfta = ixgbevf_set_vfta_vf, + .set_rlpml = ixgbevf_set_rlpml_vf, +}; + +static const struct ixgbe_mac_operations ixgbevf_hv_mac_ops = { + .init_hw = ixgbevf_init_hw_vf, + .reset_hw = ixgbevf_hv_reset_hw_vf, + .start_hw = ixgbevf_start_hw_vf, + .get_mac_addr = ixgbevf_get_mac_addr_vf, + .stop_adapter = ixgbevf_stop_hw_vf, + .setup_link = ixgbevf_setup_mac_link_vf, + .check_link = ixgbevf_hv_check_mac_link_vf, + .negotiate_api_version = ixgbevf_hv_negotiate_api_version_vf, + .set_rar = ixgbevf_hv_set_rar_vf, + .update_mc_addr_list = ixgbevf_hv_update_mc_addr_list_vf, + .update_xcast_mode = ixgbevf_hv_update_xcast_mode, + .set_uc_addr = ixgbevf_hv_set_uc_addr_vf, + .set_vfta = ixgbevf_hv_set_vfta_vf, + .set_rlpml = ixgbevf_hv_set_rlpml_vf, }; const struct ixgbevf_info ixgbevf_82599_vf_info = { @@ -782,17 +971,37 @@ const struct ixgbevf_info ixgbevf_82599_vf_info = { .mac_ops = &ixgbevf_mac_ops, }; +const struct ixgbevf_info ixgbevf_82599_vf_hv_info = { + .mac = ixgbe_mac_82599_vf, + .mac_ops = &ixgbevf_hv_mac_ops, +}; + const struct ixgbevf_info ixgbevf_X540_vf_info = { .mac = ixgbe_mac_X540_vf, .mac_ops = &ixgbevf_mac_ops, }; +const struct ixgbevf_info ixgbevf_X540_vf_hv_info = { + .mac = ixgbe_mac_X540_vf, + .mac_ops = &ixgbevf_hv_mac_ops, +}; + const struct ixgbevf_info ixgbevf_X550_vf_info = { .mac = ixgbe_mac_X550_vf, .mac_ops = &ixgbevf_mac_ops, }; +const struct ixgbevf_info ixgbevf_X550_vf_hv_info = { + .mac = ixgbe_mac_X550_vf, + .mac_ops = &ixgbevf_hv_mac_ops, +}; + const struct ixgbevf_info ixgbevf_X550EM_x_vf_info = { .mac = ixgbe_mac_X550EM_x_vf, .mac_ops = &ixgbevf_mac_ops, }; + +const struct ixgbevf_info ixgbevf_X550EM_x_vf_hv_info = { + .mac = ixgbe_mac_X550EM_x_vf, + .mac_ops = &ixgbevf_hv_mac_ops, +}; diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.h b/drivers/net/ethernet/intel/ixgbevf/vf.h index 8e623f9327ae..2cac610f32ba 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.h +++ b/drivers/net/ethernet/intel/ixgbevf/vf.h @@ -64,11 +64,12 @@ struct ixgbe_mac_operations { s32 (*set_uc_addr)(struct ixgbe_hw *, u32, u8 *); s32 (*init_rx_addrs)(struct ixgbe_hw *); s32 (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *); - s32 (*update_xcast_mode)(struct ixgbe_hw *, struct net_device *, int); + s32 (*update_xcast_mode)(struct ixgbe_hw *, int); s32 (*enable_mc)(struct ixgbe_hw *); s32 (*disable_mc)(struct ixgbe_hw *); s32 (*clear_vfta)(struct ixgbe_hw *); s32 (*set_vfta)(struct ixgbe_hw *, u32, u32, bool); + void (*set_rlpml)(struct ixgbe_hw *, u16); }; enum ixgbe_mac_type { @@ -208,7 +209,6 @@ static inline u32 ixgbe_read_reg_array(struct ixgbe_hw *hw, u32 reg, #define IXGBE_READ_REG_ARRAY(h, r, o) ixgbe_read_reg_array(h, r, o) -void ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, u16 max_size); int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs, unsigned int *default_tc); int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues); diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index d74f5f4e5782..1799fe1415df 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -152,7 +152,7 @@ static inline void korina_abort_dma(struct net_device *dev, writel(0x10, &ch->dmac); while (!(readl(&ch->dmas) & DMA_STAT_HALT)) - dev->trans_start = jiffies; + netif_trans_update(dev); writel(0, &ch->dmas); } @@ -283,7 +283,7 @@ static int korina_send_packet(struct sk_buff *skb, struct net_device *dev) } dma_cache_wback((u32) td, sizeof(*td)); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&lp->lock, flags); return NETDEV_TX_OK; @@ -622,7 +622,7 @@ korina_tx_dma_interrupt(int irq, void *dev_id) &(lp->tx_dma_regs->dmandptr)); lp->tx_chain_status = desc_empty; lp->tx_chain_head = lp->tx_chain_tail; - dev->trans_start = jiffies; + netif_trans_update(dev); } if (dmas & DMA_STAT_ERR) printk(KERN_ERR "%s: DMA error\n", dev->name); @@ -811,7 +811,7 @@ static int korina_init(struct net_device *dev) /* reset ethernet logic */ writel(0, &lp->eth_regs->ethintfc); while ((readl(&lp->eth_regs->ethintfc) & ETH_INT_FC_RIP)) - dev->trans_start = jiffies; + netif_trans_update(dev); /* Enable Ethernet Interface */ writel(ETH_INT_FC_EN, &lp->eth_regs->ethintfc); diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c index b630ef1e9646..dc82b1b19574 100644 --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -519,7 +519,7 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) byte_offset = CPHYSADDR(skb->data) % 16; ch->skb[ch->dma.desc] = skb; - dev->trans_start = jiffies; + netif_trans_update(dev); spin_lock_irqsave(&priv->lock, flags); desc->addr = ((unsigned int) dma_map_single(NULL, skb->data, len, @@ -657,7 +657,7 @@ ltq_etop_tx_timeout(struct net_device *dev) err = ltq_etop_hw_init(dev); if (err) goto err_hw; - dev->trans_start = jiffies; + netif_trans_update(dev); netif_wake_queue(dev); return; diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index b5c6d42daa12..2664827ddecd 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -68,7 +68,7 @@ config MVNETA config MVNETA_BM tristate - default y if MVNETA=y && MVNETA_BM_ENABLE + default y if MVNETA=y && MVNETA_BM_ENABLE!=n default MVNETA_BM_ENABLE select HWBM help diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7fc490225da5..a6d26d351dfc 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3354,8 +3354,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, /* Enable per-CPU interrupts on the CPU that is * brought up. */ - smp_call_function_single(cpu, mvneta_percpu_enable, - pp, true); + mvneta_percpu_enable(pp); /* Enable per-CPU interrupt on the one CPU we care * about. @@ -3387,8 +3386,7 @@ static int mvneta_percpu_notifier(struct notifier_block *nfb, /* Disable per-CPU interrupts on the CPU that is * brought down. */ - smp_call_function_single(cpu, mvneta_percpu_disable, - pp, true); + mvneta_percpu_disable(pp); break; case CPU_DEAD: diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 7ace07dad6a3..54d5154ac0f8 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -286,12 +286,12 @@ static int pxa168_eth_stop(struct net_device *dev); static inline u32 rdl(struct pxa168_eth_private *pep, int offset) { - return readl(pep->base + offset); + return readl_relaxed(pep->base + offset); } static inline void wrl(struct pxa168_eth_private *pep, int offset, u32 data) { - writel(data, pep->base + offset); + writel_relaxed(data, pep->base + offset); } static void abort_dma(struct pxa168_eth_private *pep) @@ -342,9 +342,9 @@ static void rxq_refill(struct net_device *dev) pep->rx_skb[used_rx_desc] = skb; /* Return the descriptor to DMA ownership */ - wmb(); + dma_wmb(); p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT; - wmb(); + dma_wmb(); /* Move the used descriptor pointer to the next descriptor */ pep->rx_used_desc_q = (used_rx_desc + 1) % pep->rx_ring_size; @@ -794,7 +794,7 @@ static int rxq_process(struct net_device *dev, int budget) rx_used_desc = pep->rx_used_desc_q; rx_desc = &pep->p_rx_desc_area[rx_curr_desc]; cmd_sts = rx_desc->cmd_sts; - rmb(); + dma_rmb(); if (cmd_sts & (BUF_OWNED_BY_DMA)) break; skb = pep->rx_skb[rx_curr_desc]; @@ -979,8 +979,8 @@ static int pxa168_init_phy(struct net_device *dev) return 0; pep->phy = mdiobus_scan(pep->smi_bus, pep->phy_addr); - if (!pep->phy) - return -ENODEV; + if (IS_ERR(pep->phy)) + return PTR_ERR(pep->phy); err = phy_connect_direct(dev, pep->phy, pxa168_eth_adjust_link, pep->phy_intf); @@ -1287,7 +1287,7 @@ static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_tx_timestamp(skb); - wmb(); + dma_wmb(); desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC | TX_ZERO_PADDING | TX_LAST_DESC | TX_EN_INT; wmb(); @@ -1295,7 +1295,7 @@ static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_bytes += length; stats->tx_packets++; - dev->trans_start = jiffies; + netif_trans_update(dev); if (pep->tx_ring_size - pep->tx_desc_count <= 1) { /* We handled the current skb, but now we are out of space.*/ netif_stop_queue(dev); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index ec0a22119e09..467138b423d3 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -2418,7 +2418,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) sky2_write32(hw, B0_IMSK, 0); sky2_read32(hw, B0_IMSK); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ napi_disable(&hw->napi); netif_tx_disable(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index 0c51c69f802f..249a4584401a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -576,41 +576,48 @@ out: return res; } -/* - * Handling for queue buffers -- we allocate a bunch of memory and - * register it in a memory region at HCA virtual address 0. If the - * requested size is > max_direct, we split the allocation into - * multiple pages, so we don't require too much contiguous memory. - */ -int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, - struct mlx4_buf *buf, gfp_t gfp) +static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size, + struct mlx4_buf *buf, gfp_t gfp) { dma_addr_t t; - if (size <= max_direct) { - buf->nbufs = 1; - buf->npages = 1; - buf->page_shift = get_order(size) + PAGE_SHIFT; - buf->direct.buf = dma_alloc_coherent(&dev->persist->pdev->dev, - size, &t, gfp); - if (!buf->direct.buf) - return -ENOMEM; + buf->nbufs = 1; + buf->npages = 1; + buf->page_shift = get_order(size) + PAGE_SHIFT; + buf->direct.buf = + dma_zalloc_coherent(&dev->persist->pdev->dev, + size, &t, gfp); + if (!buf->direct.buf) + return -ENOMEM; - buf->direct.map = t; + buf->direct.map = t; - while (t & ((1 << buf->page_shift) - 1)) { - --buf->page_shift; - buf->npages *= 2; - } + while (t & ((1 << buf->page_shift) - 1)) { + --buf->page_shift; + buf->npages *= 2; + } - memset(buf->direct.buf, 0, size); + return 0; +} + +/* Handling for queue buffers -- we allocate a bunch of memory and + * register it in a memory region at HCA virtual address 0. If the + * requested size is > max_direct, we split the allocation into + * multiple pages, so we don't require too much contiguous memory. + */ +int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, + struct mlx4_buf *buf, gfp_t gfp) +{ + if (size <= max_direct) { + return mlx4_buf_direct_alloc(dev, size, buf, gfp); } else { + dma_addr_t t; int i; - buf->direct.buf = NULL; - buf->nbufs = (size + PAGE_SIZE - 1) / PAGE_SIZE; - buf->npages = buf->nbufs; + buf->direct.buf = NULL; + buf->nbufs = (size + PAGE_SIZE - 1) / PAGE_SIZE; + buf->npages = buf->nbufs; buf->page_shift = PAGE_SHIFT; buf->page_list = kcalloc(buf->nbufs, sizeof(*buf->page_list), gfp); @@ -619,28 +626,12 @@ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, for (i = 0; i < buf->nbufs; ++i) { buf->page_list[i].buf = - dma_alloc_coherent(&dev->persist->pdev->dev, - PAGE_SIZE, - &t, gfp); + dma_zalloc_coherent(&dev->persist->pdev->dev, + PAGE_SIZE, &t, gfp); if (!buf->page_list[i].buf) goto err_free; buf->page_list[i].map = t; - - memset(buf->page_list[i].buf, 0, PAGE_SIZE); - } - - if (BITS_PER_LONG == 64) { - struct page **pages; - pages = kmalloc(sizeof *pages * buf->nbufs, gfp); - if (!pages) - goto err_free; - for (i = 0; i < buf->nbufs; ++i) - pages[i] = virt_to_page(buf->page_list[i].buf); - buf->direct.buf = vmap(pages, buf->nbufs, VM_MAP, PAGE_KERNEL); - kfree(pages); - if (!buf->direct.buf) - goto err_free; } } @@ -655,15 +646,11 @@ EXPORT_SYMBOL_GPL(mlx4_buf_alloc); void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf) { - int i; - - if (buf->nbufs == 1) + if (buf->nbufs == 1) { dma_free_coherent(&dev->persist->pdev->dev, size, - buf->direct.buf, - buf->direct.map); - else { - if (BITS_PER_LONG == 64) - vunmap(buf->direct.buf); + buf->direct.buf, buf->direct.map); + } else { + int i; for (i = 0; i < buf->nbufs; ++i) if (buf->page_list[i].buf) @@ -789,7 +776,7 @@ void mlx4_db_free(struct mlx4_dev *dev, struct mlx4_db *db) EXPORT_SYMBOL_GPL(mlx4_db_free); int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres, - int size, int max_direct) + int size) { int err; @@ -799,7 +786,7 @@ int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres, *wqres->db.db = 0; - err = mlx4_buf_alloc(dev, size, max_direct, &wqres->buf, GFP_KERNEL); + err = mlx4_buf_direct_alloc(dev, size, &wqres->buf, GFP_KERNEL); if (err) goto err_db; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index af975a2b74c6..132cea655920 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -73,22 +73,16 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, */ set_dev_node(&mdev->dev->persist->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres, - cq->buf_size, 2 * PAGE_SIZE); + cq->buf_size); set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) goto err_cq; - err = mlx4_en_map_buffer(&cq->wqres.buf); - if (err) - goto err_res; - cq->buf = (struct mlx4_cqe *)cq->wqres.buf.direct.buf; *pcq = cq; return 0; -err_res: - mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); err_cq: kfree(cq); *pcq = NULL; @@ -177,7 +171,6 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq) struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_en_cq *cq = *pcq; - mlx4_en_unmap_buffer(&cq->wqres.buf); mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); if (mlx4_is_eq_vector_valid(mdev->dev, priv->port, cq->vector) && cq->is_tx == RX) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 8bd143dda95d..92e0624f4cf0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2357,8 +2357,12 @@ out: } /* set offloads */ - priv->dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM | - NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL; + priv->dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL; } static void mlx4_en_del_vxlan_offloads(struct work_struct *work) @@ -2367,8 +2371,12 @@ static void mlx4_en_del_vxlan_offloads(struct work_struct *work) struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv, vxlan_del_task); /* unset offloads */ - priv->dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM | - NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL); + priv->dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL); ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port, VXLAN_STEER_BY_OUTER_MAC, 0); @@ -2427,7 +2435,18 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb, netdev_features_t features) { features = vlan_features_check(skb, features); - return vxlan_features_check(skb, features); + features = vxlan_features_check(skb, features); + + /* The ConnectX-3 doesn't support outer IPv6 checksums but it does + * support inner IPv6 checksums and segmentation so we need to + * strip that feature if this is an IPv6 encapsulated frame. + */ + if (skb->encapsulation && + (skb->ip_summed == CHECKSUM_PARTIAL) && + (ip_hdr(skb)->version != 4)) + features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); + + return features; } #endif @@ -2909,7 +2928,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, /* Allocate page for receive rings */ err = mlx4_alloc_hwq_res(mdev->dev, &priv->res, - MLX4_EN_PAGE_SIZE, MLX4_EN_PAGE_SIZE); + MLX4_EN_PAGE_SIZE); if (err) { en_err(priv, "Failed to allocate page for rx qps\n"); goto out; @@ -2992,8 +3011,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, } if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) { - dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; - dev->features |= NETIF_F_GSO_UDP_TUNNEL; + dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL; + dev->features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL; + dev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM; } mdev->pndev[port] = dev; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c index 02e925d6f734..a6b0db0e0383 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c @@ -107,37 +107,6 @@ int mlx4_en_change_mcast_lb(struct mlx4_en_priv *priv, struct mlx4_qp *qp, return ret; } -int mlx4_en_map_buffer(struct mlx4_buf *buf) -{ - struct page **pages; - int i; - - if (BITS_PER_LONG == 64 || buf->nbufs == 1) - return 0; - - pages = kmalloc(sizeof *pages * buf->nbufs, GFP_KERNEL); - if (!pages) - return -ENOMEM; - - for (i = 0; i < buf->nbufs; ++i) - pages[i] = virt_to_page(buf->page_list[i].buf); - - buf->direct.buf = vmap(pages, buf->nbufs, VM_MAP, PAGE_KERNEL); - kfree(pages); - if (!buf->direct.buf) - return -ENOMEM; - - return 0; -} - -void mlx4_en_unmap_buffer(struct mlx4_buf *buf) -{ - if (BITS_PER_LONG == 64 || buf->nbufs == 1) - return; - - vunmap(buf->direct.buf); -} - void mlx4_en_sqp_event(struct mlx4_qp *qp, enum mlx4_event event) { return; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index b723e3bcab39..c1b3a9c8cf3b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -394,17 +394,11 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, /* Allocate HW buffers on provided NUMA node */ set_dev_node(&mdev->dev->persist->pdev->dev, node); - err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, - ring->buf_size, 2 * PAGE_SIZE); + err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) goto err_info; - err = mlx4_en_map_buffer(&ring->wqres.buf); - if (err) { - en_err(priv, "Failed to map RX buffer\n"); - goto err_hwq; - } ring->buf = ring->wqres.buf.direct.buf; ring->hwtstamp_rx_filter = priv->hwtstamp_config.rx_filter; @@ -412,8 +406,6 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, *pring = ring; return 0; -err_hwq: - mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); err_info: vfree(ring->rx_info); ring->rx_info = NULL; @@ -517,7 +509,6 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_en_rx_ring *ring = *pring; - mlx4_en_unmap_buffer(&ring->wqres.buf); mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE); vfree(ring->rx_info); ring->rx_info = NULL; @@ -707,7 +698,7 @@ static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb, if (ipv6h->nexthdr == IPPROTO_FRAGMENT || ipv6h->nexthdr == IPPROTO_HOPOPTS) return -1; - hw_checksum = csum_add(hw_checksum, (__force __wsum)(ipv6h->nexthdr << 8)); + hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(ipv6h->nexthdr)); csum_pseudo_hdr = csum_partial(&ipv6h->saddr, sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index a386f047c1af..f6e61570cb2c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -41,6 +41,7 @@ #include <linux/vmalloc.h> #include <linux/tcp.h> #include <linux/ip.h> +#include <linux/ipv6.h> #include <linux/moduleparam.h> #include "mlx4_en.h" @@ -93,20 +94,13 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, /* Allocate HW buffers on provided NUMA node */ set_dev_node(&mdev->dev->persist->pdev->dev, node); - err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size, - 2 * PAGE_SIZE); + err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) { en_err(priv, "Failed allocating hwq resources\n"); goto err_bounce; } - err = mlx4_en_map_buffer(&ring->wqres.buf); - if (err) { - en_err(priv, "Failed to map TX buffer\n"); - goto err_hwq_res; - } - ring->buf = ring->wqres.buf.direct.buf; en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d buf_size:%d dma:%llx\n", @@ -117,7 +111,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, MLX4_RESERVE_ETH_BF_QP); if (err) { en_err(priv, "failed reserving qp for TX ring\n"); - goto err_map; + goto err_hwq_res; } err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->qp, GFP_KERNEL); @@ -154,8 +148,6 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, err_reserve: mlx4_qp_release_range(mdev->dev, ring->qpn, 1); -err_map: - mlx4_en_unmap_buffer(&ring->wqres.buf); err_hwq_res: mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); err_bounce: @@ -182,7 +174,6 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, mlx4_qp_remove(mdev->dev, &ring->qp); mlx4_qp_free(mdev->dev, &ring->qp); mlx4_qp_release_range(priv->mdev->dev, ring->qpn, 1); - mlx4_en_unmap_buffer(&ring->wqres.buf); mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); kfree(ring->bounce_buf); ring->bounce_buf = NULL; @@ -920,8 +911,18 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) tx_ind, fragptr); if (skb->encapsulation) { - struct iphdr *ipv4 = (struct iphdr *)skb_inner_network_header(skb); - if (ipv4->protocol == IPPROTO_TCP || ipv4->protocol == IPPROTO_UDP) + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + u8 proto; + + ip.hdr = skb_inner_network_header(skb); + proto = (ip.v4->version == 4) ? ip.v4->protocol : + ip.v6->nexthdr; + + if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP | MLX4_WQE_CTRL_ILP); else op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 6aa73972d478..f2d0920018a5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1102,7 +1102,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], struct mlx4_cmd_mailbox *mailbox; struct mlx4_mgm *mgm; u32 members_count; - int index, prev; + int index = -1, prev; int link = 0; int i; int err; @@ -1181,7 +1181,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], goto out; out: - if (prot == MLX4_PROT_ETH) { + if (prot == MLX4_PROT_ETH && index != -1) { /* manage the steering entry for promisc mode */ if (new_entry) err = new_steering_entry(dev, port, steer, diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 63b1aeae2c03..cc84e09f324a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -672,8 +672,6 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, int is_tx, int rss, int qpn, int cqn, int user_prio, struct mlx4_qp_context *context); void mlx4_en_sqp_event(struct mlx4_qp *qp, enum mlx4_event event); -int mlx4_en_map_buffer(struct mlx4_buf *buf); -void mlx4_en_unmap_buffer(struct mlx4_buf *buf); int mlx4_en_change_mcast_lb(struct mlx4_en_priv *priv, struct mlx4_qp *qp, int loopback); void mlx4_en_calc_rx_buf(struct net_device *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 679e18ffb3a6..9ea7b583096a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -2,11 +2,10 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ - mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o + mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o fs_counters.o mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o \ en_main.o en_fs.o en_ethtool.o en_tx.o en_rx.o \ - en_txrx.o en_clock.o vxlan.o en_tc.o + en_txrx.o en_clock.o vxlan.o en_tc.o en_arfs.o mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o -mlx5_core-$(CONFIG_RFS_ACCEL) += en_arfs.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index eb926e1ee71c..dcd2df6518de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -294,6 +294,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_DESTROY_FLOW_TABLE: case MLX5_CMD_OP_DESTROY_FLOW_GROUP: case MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY: + case MLX5_CMD_OP_DEALLOC_FLOW_COUNTER: return MLX5_CMD_STAT_OK; case MLX5_CMD_OP_QUERY_HCA_CAP: @@ -395,6 +396,8 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_QUERY_FLOW_GROUP: case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY: case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY: + case MLX5_CMD_OP_ALLOC_FLOW_COUNTER: + case MLX5_CMD_OP_QUERY_FLOW_COUNTER: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; return -EIO; @@ -406,178 +409,142 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, const char *mlx5_command_str(int command) { - switch (command) { - case MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT: - return "QUERY_HCA_VPORT_CONTEXT"; - - case MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT: - return "MODIFY_HCA_VPORT_CONTEXT"; - - case MLX5_CMD_OP_QUERY_HCA_CAP: - return "QUERY_HCA_CAP"; - - case MLX5_CMD_OP_SET_HCA_CAP: - return "SET_HCA_CAP"; - - case MLX5_CMD_OP_QUERY_ADAPTER: - return "QUERY_ADAPTER"; - - case MLX5_CMD_OP_INIT_HCA: - return "INIT_HCA"; - - case MLX5_CMD_OP_TEARDOWN_HCA: - return "TEARDOWN_HCA"; - - case MLX5_CMD_OP_ENABLE_HCA: - return "MLX5_CMD_OP_ENABLE_HCA"; - - case MLX5_CMD_OP_DISABLE_HCA: - return "MLX5_CMD_OP_DISABLE_HCA"; - - case MLX5_CMD_OP_QUERY_PAGES: - return "QUERY_PAGES"; - - case MLX5_CMD_OP_MANAGE_PAGES: - return "MANAGE_PAGES"; - - case MLX5_CMD_OP_CREATE_MKEY: - return "CREATE_MKEY"; - - case MLX5_CMD_OP_QUERY_MKEY: - return "QUERY_MKEY"; - - case MLX5_CMD_OP_DESTROY_MKEY: - return "DESTROY_MKEY"; - - case MLX5_CMD_OP_QUERY_SPECIAL_CONTEXTS: - return "QUERY_SPECIAL_CONTEXTS"; - - case MLX5_CMD_OP_CREATE_EQ: - return "CREATE_EQ"; - - case MLX5_CMD_OP_DESTROY_EQ: - return "DESTROY_EQ"; - - case MLX5_CMD_OP_QUERY_EQ: - return "QUERY_EQ"; - - case MLX5_CMD_OP_CREATE_CQ: - return "CREATE_CQ"; - - case MLX5_CMD_OP_DESTROY_CQ: - return "DESTROY_CQ"; - - case MLX5_CMD_OP_QUERY_CQ: - return "QUERY_CQ"; - - case MLX5_CMD_OP_MODIFY_CQ: - return "MODIFY_CQ"; - - case MLX5_CMD_OP_CREATE_QP: - return "CREATE_QP"; - - case MLX5_CMD_OP_DESTROY_QP: - return "DESTROY_QP"; - - case MLX5_CMD_OP_RST2INIT_QP: - return "RST2INIT_QP"; - - case MLX5_CMD_OP_INIT2RTR_QP: - return "INIT2RTR_QP"; - - case MLX5_CMD_OP_RTR2RTS_QP: - return "RTR2RTS_QP"; - - case MLX5_CMD_OP_RTS2RTS_QP: - return "RTS2RTS_QP"; - - case MLX5_CMD_OP_SQERR2RTS_QP: - return "SQERR2RTS_QP"; - - case MLX5_CMD_OP_2ERR_QP: - return "2ERR_QP"; - - case MLX5_CMD_OP_2RST_QP: - return "2RST_QP"; - - case MLX5_CMD_OP_QUERY_QP: - return "QUERY_QP"; - - case MLX5_CMD_OP_MAD_IFC: - return "MAD_IFC"; - - case MLX5_CMD_OP_INIT2INIT_QP: - return "INIT2INIT_QP"; - - case MLX5_CMD_OP_CREATE_PSV: - return "CREATE_PSV"; - - case MLX5_CMD_OP_DESTROY_PSV: - return "DESTROY_PSV"; - - case MLX5_CMD_OP_CREATE_SRQ: - return "CREATE_SRQ"; - - case MLX5_CMD_OP_DESTROY_SRQ: - return "DESTROY_SRQ"; - - case MLX5_CMD_OP_QUERY_SRQ: - return "QUERY_SRQ"; - - case MLX5_CMD_OP_ARM_RQ: - return "ARM_RQ"; - - case MLX5_CMD_OP_CREATE_XRC_SRQ: - return "CREATE_XRC_SRQ"; - - case MLX5_CMD_OP_DESTROY_XRC_SRQ: - return "DESTROY_XRC_SRQ"; - - case MLX5_CMD_OP_QUERY_XRC_SRQ: - return "QUERY_XRC_SRQ"; - - case MLX5_CMD_OP_ARM_XRC_SRQ: - return "ARM_XRC_SRQ"; - - case MLX5_CMD_OP_ALLOC_PD: - return "ALLOC_PD"; - - case MLX5_CMD_OP_DEALLOC_PD: - return "DEALLOC_PD"; - - case MLX5_CMD_OP_ALLOC_UAR: - return "ALLOC_UAR"; - - case MLX5_CMD_OP_DEALLOC_UAR: - return "DEALLOC_UAR"; - - case MLX5_CMD_OP_ATTACH_TO_MCG: - return "ATTACH_TO_MCG"; - - case MLX5_CMD_OP_DETTACH_FROM_MCG: - return "DETTACH_FROM_MCG"; - - case MLX5_CMD_OP_ALLOC_XRCD: - return "ALLOC_XRCD"; - - case MLX5_CMD_OP_DEALLOC_XRCD: - return "DEALLOC_XRCD"; - - case MLX5_CMD_OP_ACCESS_REG: - return "MLX5_CMD_OP_ACCESS_REG"; - - case MLX5_CMD_OP_SET_WOL_ROL: - return "SET_WOL_ROL"; - - case MLX5_CMD_OP_QUERY_WOL_ROL: - return "QUERY_WOL_ROL"; - - case MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT: - return "ADD_VXLAN_UDP_DPORT"; - - case MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT: - return "DELETE_VXLAN_UDP_DPORT"; +#define MLX5_COMMAND_STR_CASE(__cmd) case MLX5_CMD_OP_ ## __cmd: return #__cmd + switch (command) { + MLX5_COMMAND_STR_CASE(QUERY_HCA_CAP); + MLX5_COMMAND_STR_CASE(QUERY_ADAPTER); + MLX5_COMMAND_STR_CASE(INIT_HCA); + MLX5_COMMAND_STR_CASE(TEARDOWN_HCA); + MLX5_COMMAND_STR_CASE(ENABLE_HCA); + MLX5_COMMAND_STR_CASE(DISABLE_HCA); + MLX5_COMMAND_STR_CASE(QUERY_PAGES); + MLX5_COMMAND_STR_CASE(MANAGE_PAGES); + MLX5_COMMAND_STR_CASE(SET_HCA_CAP); + MLX5_COMMAND_STR_CASE(QUERY_ISSI); + MLX5_COMMAND_STR_CASE(SET_ISSI); + MLX5_COMMAND_STR_CASE(CREATE_MKEY); + MLX5_COMMAND_STR_CASE(QUERY_MKEY); + MLX5_COMMAND_STR_CASE(DESTROY_MKEY); + MLX5_COMMAND_STR_CASE(QUERY_SPECIAL_CONTEXTS); + MLX5_COMMAND_STR_CASE(PAGE_FAULT_RESUME); + MLX5_COMMAND_STR_CASE(CREATE_EQ); + MLX5_COMMAND_STR_CASE(DESTROY_EQ); + MLX5_COMMAND_STR_CASE(QUERY_EQ); + MLX5_COMMAND_STR_CASE(GEN_EQE); + MLX5_COMMAND_STR_CASE(CREATE_CQ); + MLX5_COMMAND_STR_CASE(DESTROY_CQ); + MLX5_COMMAND_STR_CASE(QUERY_CQ); + MLX5_COMMAND_STR_CASE(MODIFY_CQ); + MLX5_COMMAND_STR_CASE(CREATE_QP); + MLX5_COMMAND_STR_CASE(DESTROY_QP); + MLX5_COMMAND_STR_CASE(RST2INIT_QP); + MLX5_COMMAND_STR_CASE(INIT2RTR_QP); + MLX5_COMMAND_STR_CASE(RTR2RTS_QP); + MLX5_COMMAND_STR_CASE(RTS2RTS_QP); + MLX5_COMMAND_STR_CASE(SQERR2RTS_QP); + MLX5_COMMAND_STR_CASE(2ERR_QP); + MLX5_COMMAND_STR_CASE(2RST_QP); + MLX5_COMMAND_STR_CASE(QUERY_QP); + MLX5_COMMAND_STR_CASE(SQD_RTS_QP); + MLX5_COMMAND_STR_CASE(INIT2INIT_QP); + MLX5_COMMAND_STR_CASE(CREATE_PSV); + MLX5_COMMAND_STR_CASE(DESTROY_PSV); + MLX5_COMMAND_STR_CASE(CREATE_SRQ); + MLX5_COMMAND_STR_CASE(DESTROY_SRQ); + MLX5_COMMAND_STR_CASE(QUERY_SRQ); + MLX5_COMMAND_STR_CASE(ARM_RQ); + MLX5_COMMAND_STR_CASE(CREATE_XRC_SRQ); + MLX5_COMMAND_STR_CASE(DESTROY_XRC_SRQ); + MLX5_COMMAND_STR_CASE(QUERY_XRC_SRQ); + MLX5_COMMAND_STR_CASE(ARM_XRC_SRQ); + MLX5_COMMAND_STR_CASE(CREATE_DCT); + MLX5_COMMAND_STR_CASE(DESTROY_DCT); + MLX5_COMMAND_STR_CASE(DRAIN_DCT); + MLX5_COMMAND_STR_CASE(QUERY_DCT); + MLX5_COMMAND_STR_CASE(ARM_DCT_FOR_KEY_VIOLATION); + MLX5_COMMAND_STR_CASE(QUERY_VPORT_STATE); + MLX5_COMMAND_STR_CASE(MODIFY_VPORT_STATE); + MLX5_COMMAND_STR_CASE(QUERY_ESW_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(MODIFY_ESW_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(QUERY_NIC_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(MODIFY_NIC_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(QUERY_ROCE_ADDRESS); + MLX5_COMMAND_STR_CASE(SET_ROCE_ADDRESS); + MLX5_COMMAND_STR_CASE(QUERY_HCA_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(MODIFY_HCA_VPORT_CONTEXT); + MLX5_COMMAND_STR_CASE(QUERY_HCA_VPORT_GID); + MLX5_COMMAND_STR_CASE(QUERY_HCA_VPORT_PKEY); + MLX5_COMMAND_STR_CASE(QUERY_VPORT_COUNTER); + MLX5_COMMAND_STR_CASE(ALLOC_Q_COUNTER); + MLX5_COMMAND_STR_CASE(DEALLOC_Q_COUNTER); + MLX5_COMMAND_STR_CASE(QUERY_Q_COUNTER); + MLX5_COMMAND_STR_CASE(ALLOC_PD); + MLX5_COMMAND_STR_CASE(DEALLOC_PD); + MLX5_COMMAND_STR_CASE(ALLOC_UAR); + MLX5_COMMAND_STR_CASE(DEALLOC_UAR); + MLX5_COMMAND_STR_CASE(CONFIG_INT_MODERATION); + MLX5_COMMAND_STR_CASE(ACCESS_REG); + MLX5_COMMAND_STR_CASE(ATTACH_TO_MCG); + MLX5_COMMAND_STR_CASE(DETTACH_FROM_MCG); + MLX5_COMMAND_STR_CASE(GET_DROPPED_PACKET_LOG); + MLX5_COMMAND_STR_CASE(MAD_IFC); + MLX5_COMMAND_STR_CASE(QUERY_MAD_DEMUX); + MLX5_COMMAND_STR_CASE(SET_MAD_DEMUX); + MLX5_COMMAND_STR_CASE(NOP); + MLX5_COMMAND_STR_CASE(ALLOC_XRCD); + MLX5_COMMAND_STR_CASE(DEALLOC_XRCD); + MLX5_COMMAND_STR_CASE(ALLOC_TRANSPORT_DOMAIN); + MLX5_COMMAND_STR_CASE(DEALLOC_TRANSPORT_DOMAIN); + MLX5_COMMAND_STR_CASE(QUERY_CONG_STATUS); + MLX5_COMMAND_STR_CASE(MODIFY_CONG_STATUS); + MLX5_COMMAND_STR_CASE(QUERY_CONG_PARAMS); + MLX5_COMMAND_STR_CASE(MODIFY_CONG_PARAMS); + MLX5_COMMAND_STR_CASE(QUERY_CONG_STATISTICS); + MLX5_COMMAND_STR_CASE(ADD_VXLAN_UDP_DPORT); + MLX5_COMMAND_STR_CASE(DELETE_VXLAN_UDP_DPORT); + MLX5_COMMAND_STR_CASE(SET_L2_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(QUERY_L2_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(DELETE_L2_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(SET_WOL_ROL); + MLX5_COMMAND_STR_CASE(QUERY_WOL_ROL); + MLX5_COMMAND_STR_CASE(CREATE_TIR); + MLX5_COMMAND_STR_CASE(MODIFY_TIR); + MLX5_COMMAND_STR_CASE(DESTROY_TIR); + MLX5_COMMAND_STR_CASE(QUERY_TIR); + MLX5_COMMAND_STR_CASE(CREATE_SQ); + MLX5_COMMAND_STR_CASE(MODIFY_SQ); + MLX5_COMMAND_STR_CASE(DESTROY_SQ); + MLX5_COMMAND_STR_CASE(QUERY_SQ); + MLX5_COMMAND_STR_CASE(CREATE_RQ); + MLX5_COMMAND_STR_CASE(MODIFY_RQ); + MLX5_COMMAND_STR_CASE(DESTROY_RQ); + MLX5_COMMAND_STR_CASE(QUERY_RQ); + MLX5_COMMAND_STR_CASE(CREATE_RMP); + MLX5_COMMAND_STR_CASE(MODIFY_RMP); + MLX5_COMMAND_STR_CASE(DESTROY_RMP); + MLX5_COMMAND_STR_CASE(QUERY_RMP); + MLX5_COMMAND_STR_CASE(CREATE_TIS); + MLX5_COMMAND_STR_CASE(MODIFY_TIS); + MLX5_COMMAND_STR_CASE(DESTROY_TIS); + MLX5_COMMAND_STR_CASE(QUERY_TIS); + MLX5_COMMAND_STR_CASE(CREATE_RQT); + MLX5_COMMAND_STR_CASE(MODIFY_RQT); + MLX5_COMMAND_STR_CASE(DESTROY_RQT); + MLX5_COMMAND_STR_CASE(QUERY_RQT); + MLX5_COMMAND_STR_CASE(SET_FLOW_TABLE_ROOT); + MLX5_COMMAND_STR_CASE(CREATE_FLOW_TABLE); + MLX5_COMMAND_STR_CASE(DESTROY_FLOW_TABLE); + MLX5_COMMAND_STR_CASE(QUERY_FLOW_TABLE); + MLX5_COMMAND_STR_CASE(CREATE_FLOW_GROUP); + MLX5_COMMAND_STR_CASE(DESTROY_FLOW_GROUP); + MLX5_COMMAND_STR_CASE(QUERY_FLOW_GROUP); + MLX5_COMMAND_STR_CASE(SET_FLOW_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(QUERY_FLOW_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(DELETE_FLOW_TABLE_ENTRY); + MLX5_COMMAND_STR_CASE(ALLOC_FLOW_COUNTER); + MLX5_COMMAND_STR_CASE(DEALLOC_FLOW_COUNTER); + MLX5_COMMAND_STR_CASE(QUERY_FLOW_COUNTER); default: return "unknown command opcode"; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 34523c48444e..e8a6c3325b39 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -64,12 +64,9 @@ #define MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW 0x4 #define MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE_MPW 0x6 -#define MLX5_MPWRQ_LOG_NUM_STRIDES 11 /* >= 9, HW restriction */ #define MLX5_MPWRQ_LOG_STRIDE_SIZE 6 /* >= 6, HW restriction */ -#define MLX5_MPWRQ_NUM_STRIDES BIT(MLX5_MPWRQ_LOG_NUM_STRIDES) -#define MLX5_MPWRQ_STRIDE_SIZE BIT(MLX5_MPWRQ_LOG_STRIDE_SIZE) -#define MLX5_MPWRQ_LOG_WQE_SZ (MLX5_MPWRQ_LOG_NUM_STRIDES +\ - MLX5_MPWRQ_LOG_STRIDE_SIZE) +#define MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS 8 /* >= 6, HW restriction */ +#define MLX5_MPWRQ_LOG_WQE_SZ 17 #define MLX5_MPWRQ_WQE_PAGE_ORDER (MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT > 0 ? \ MLX5_MPWRQ_LOG_WQE_SZ - PAGE_SHIFT : 0) #define MLX5_MPWRQ_PAGES_PER_WQE BIT(MLX5_MPWRQ_WQE_PAGE_ORDER) @@ -154,9 +151,13 @@ struct mlx5e_umr_wqe { struct mlx5e_params { u8 log_sq_size; u8 rq_wq_type; + u8 mpwqe_log_stride_sz; + u8 mpwqe_log_num_strides; u8 log_rq_size; u16 num_channels; u8 num_tc; + bool rx_cqe_compress_admin; + bool rx_cqe_compress; u16 rx_cq_moderation_usec; u16 rx_cq_moderation_pkts; u16 tx_cq_moderation_usec; @@ -202,6 +203,13 @@ struct mlx5e_cq { struct mlx5e_channel *channel; struct mlx5e_priv *priv; + /* cqe decompression */ + struct mlx5_cqe64 title; + struct mlx5_mini_cqe8 mini_arr[MLX5_MINI_CQE_ARRAY_SIZE]; + u8 mini_arr_idx; + u16 decmprs_left; + u16 decmprs_wqe_counter; + /* control */ struct mlx5_wq_ctrl wq_ctrl; } ____cacheline_aligned_in_smp; @@ -240,6 +248,8 @@ struct mlx5e_rq { /* control */ struct mlx5_wq_ctrl wq_ctrl; u8 wq_type; + u32 mpwqe_stride_sz; + u32 mpwqe_num_strides; u32 rqn; struct mlx5e_channel *channel; struct mlx5e_priv *priv; @@ -263,7 +273,7 @@ struct mlx5e_mpw_info { void (*dma_pre_sync)(struct device *pdev, struct mlx5e_mpw_info *wi, u32 wqe_offset, u32 len); - void (*add_skb_frag)(struct device *pdev, + void (*add_skb_frag)(struct mlx5e_rq *rq, struct sk_buff *skb, struct mlx5e_mpw_info *wi, u32 page_idx, u32 frag_offset, u32 len); @@ -525,6 +535,7 @@ struct mlx5e_priv { struct mlx5e_vxlan_db vxlan; struct mlx5e_params params; + struct workqueue_struct *wq; struct work_struct update_carrier_work; struct work_struct set_rx_mode_work; struct delayed_work update_stats_work; @@ -615,6 +626,7 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv); void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv); int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr); int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr); +void mlx5e_modify_rx_cqe_compression(struct mlx5e_priv *priv, bool val); int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, u16 vid); @@ -633,6 +645,7 @@ int mlx5e_close_locked(struct net_device *netdev); void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, u32 *indirection_rqt, int len, int num_channels); +int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); static inline void mlx5e_tx_notify_hw(struct mlx5e_sq *sq, struct mlx5_wqe_ctrl_seg *ctrl, int bf_sz) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index b4ae0fe15878..3515e78ba68f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -30,6 +30,8 @@ * SOFTWARE. */ +#ifdef CONFIG_RFS_ACCEL + #include <linux/hash.h> #include <linux/mlx5/fs.h> #include <linux/ip.h> @@ -747,3 +749,4 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, spin_unlock_bh(&arfs->arfs_lock); return arfs_rule->filter_id; } +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 2018eebe1531..847a8f3ac2b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -93,6 +93,8 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) /* RX HW timestamp */ switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: + /* Reset CQE compression to Admin default */ + mlx5e_modify_rx_cqe_compression(priv, priv->params.rx_cqe_compress_admin); break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_SOME: @@ -108,6 +110,8 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + /* Disable CQE compression */ + mlx5e_modify_rx_cqe_compression(priv, false); config.rx_filter = HWTSTAMP_FILTER_ALL; break; default: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 534d99e2f9c8..fc7dcc03b1de 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -613,6 +613,25 @@ static u32 ptys2ethtool_supported_port(u32 eth_proto_cap) return 0; } +int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) +{ + u32 max_speed = 0; + u32 proto_cap; + int err; + int i; + + err = mlx5_query_port_proto_cap(mdev, &proto_cap, MLX5_PTYS_EN); + if (err) + return err; + + for (i = 0; i < MLX5E_LINK_MODES_NUMBER; ++i) + if (proto_cap & MLX5E_PROT_MASK(i)) + max_speed = max(max_speed, ptys2ethtool_table[i].speed); + + *speed = max_speed; + return 0; +} + static void get_speed_duplex(struct net_device *netdev, u32 eth_proto_oper, struct ethtool_cmd *cmd) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 4ccfc1ac62c5..fd4392999eee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -114,6 +114,8 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) s->rx_mpwqe_filler += rq_stats->mpwqe_filler; s->rx_mpwqe_frag += rq_stats->mpwqe_frag; s->rx_buff_alloc_err += rq_stats->buff_alloc_err; + s->rx_cqe_compress_blks += rq_stats->cqe_compress_blks; + s->rx_cqe_compress_pkts += rq_stats->cqe_compress_pkts; for (j = 0; j < priv->params.num_tc; j++) { sq_stats = &priv->channel[i]->sq[j].stats; @@ -231,9 +233,8 @@ static void mlx5e_update_stats_work(struct work_struct *work) mutex_lock(&priv->state_lock); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { mlx5e_update_stats(priv); - schedule_delayed_work(dwork, - msecs_to_jiffies( - MLX5E_UPDATE_STATS_INTERVAL)); + queue_delayed_work(priv->wq, dwork, + msecs_to_jiffies(MLX5E_UPDATE_STATS_INTERVAL)); } mutex_unlock(&priv->state_lock); } @@ -249,7 +250,7 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv, switch (event) { case MLX5_DEV_EVENT_PORT_UP: case MLX5_DEV_EVENT_PORT_DOWN: - schedule_work(&priv->update_carrier_work); + queue_work(priv->wq, &priv->update_carrier_work); break; default: @@ -306,7 +307,9 @@ static int mlx5e_create_rq(struct mlx5e_channel *c, rq->handle_rx_cqe = mlx5e_handle_rx_cqe_mpwrq; rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; - rq->wqe_sz = MLX5_MPWRQ_NUM_STRIDES * MLX5_MPWRQ_STRIDE_SIZE; + rq->mpwqe_stride_sz = BIT(priv->params.mpwqe_log_stride_sz); + rq->mpwqe_num_strides = BIT(priv->params.mpwqe_log_num_strides); + rq->wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; byte_count = rq->wqe_sz; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ @@ -1129,9 +1132,9 @@ static void mlx5e_build_rq_param(struct mlx5e_priv *priv, switch (priv->params.rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: MLX5_SET(wq, wq, log_wqe_num_of_strides, - MLX5_MPWRQ_LOG_NUM_STRIDES - 9); + priv->params.mpwqe_log_num_strides - 9); MLX5_SET(wq, wq, log_wqe_stride_size, - MLX5_MPWRQ_LOG_STRIDE_SIZE - 6); + priv->params.mpwqe_log_stride_sz - 6); MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ); break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ @@ -1198,13 +1201,17 @@ static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, switch (priv->params.rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: log_cq_size = priv->params.log_rq_size + - MLX5_MPWRQ_LOG_NUM_STRIDES; + priv->params.mpwqe_log_num_strides; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ log_cq_size = priv->params.log_rq_size; } MLX5_SET(cqc, cqc, log_cq_size, log_cq_size); + if (priv->params.rx_cqe_compress) { + MLX5_SET(cqc, cqc, mini_cqe_res_format, MLX5_CQE_FORMAT_CSUM); + MLX5_SET(cqc, cqc, cqe_comp_en, 1); + } mlx5e_build_common_cq_param(priv, param); } @@ -1695,7 +1702,7 @@ int mlx5e_open_locked(struct net_device *netdev) priv->netdev->rx_cpu_rmap = priv->mdev->rmap; #endif - schedule_delayed_work(&priv->update_stats_work, 0); + queue_delayed_work(priv->wq, &priv->update_stats_work, 0); return 0; @@ -2147,6 +2154,8 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, return mlx5e_configure_flower(priv, proto, tc->cls_flower); case TC_CLSFLOWER_DESTROY: return mlx5e_delete_flower(priv, tc->cls_flower); + case TC_CLSFLOWER_STATS: + return mlx5e_stats_flower(priv, tc->cls_flower); } default: return -EOPNOTSUPP; @@ -2202,7 +2211,7 @@ static void mlx5e_set_rx_mode(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); } static int mlx5e_set_mac(struct net_device *netdev, void *addr) @@ -2217,7 +2226,7 @@ static int mlx5e_set_mac(struct net_device *netdev, void *addr) ether_addr_copy(netdev->dev_addr, saddr->sa_data); netif_addr_unlock_bh(netdev); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); return 0; } @@ -2439,6 +2448,21 @@ static int mlx5e_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos) vlan, qos); } +static int mlx5e_set_vf_spoofchk(struct net_device *dev, int vf, bool setting) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_set_vport_spoofchk(mdev->priv.eswitch, vf + 1, setting); +} + +static int mlx5e_set_vf_trust(struct net_device *dev, int vf, bool setting) +{ + struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + + return mlx5_eswitch_set_vport_trust(mdev->priv.eswitch, vf + 1, setting); +} static int mlx5_vport_link2ifla(u8 esw_link) { switch (esw_link) { @@ -2503,7 +2527,7 @@ static void mlx5e_add_vxlan_port(struct net_device *netdev, if (!mlx5e_vxlan_allowed(priv->mdev)) return; - mlx5e_vxlan_add_port(priv, be16_to_cpu(port)); + mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 1); } static void mlx5e_del_vxlan_port(struct net_device *netdev, @@ -2514,7 +2538,7 @@ static void mlx5e_del_vxlan_port(struct net_device *netdev, if (!mlx5e_vxlan_allowed(priv->mdev)) return; - mlx5e_vxlan_del_port(priv, be16_to_cpu(port)); + mlx5e_vxlan_queue_work(priv, sa_family, be16_to_cpu(port), 0); } static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, @@ -2608,6 +2632,8 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { #endif .ndo_set_vf_mac = mlx5e_set_vf_mac, .ndo_set_vf_vlan = mlx5e_set_vf_vlan, + .ndo_set_vf_spoofchk = mlx5e_set_vf_spoofchk, + .ndo_set_vf_trust = mlx5e_set_vf_trust, .ndo_get_vf_config = mlx5e_get_vf_config, .ndo_set_vf_link_state = mlx5e_set_vf_link_state, .ndo_get_vf_stats = mlx5e_get_vf_stats, @@ -2692,11 +2718,49 @@ static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) MLX5_CAP_ETH(mdev, reg_umr_sq); } +static int mlx5e_get_pci_bw(struct mlx5_core_dev *mdev, u32 *pci_bw) +{ + enum pcie_link_width width; + enum pci_bus_speed speed; + int err = 0; + + err = pcie_get_minimum_link(mdev->pdev, &speed, &width); + if (err) + return err; + + if (speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) + return -EINVAL; + + switch (speed) { + case PCIE_SPEED_2_5GT: + *pci_bw = 2500 * width; + break; + case PCIE_SPEED_5_0GT: + *pci_bw = 5000 * width; + break; + case PCIE_SPEED_8_0GT: + *pci_bw = 8000 * width; + break; + default: + return -EINVAL; + } + + return 0; +} + +static bool cqe_compress_heuristic(u32 link_speed, u32 pci_bw) +{ + return (link_speed && pci_bw && + (pci_bw < 40000) && (pci_bw < link_speed)); +} + static void mlx5e_build_netdev_priv(struct mlx5_core_dev *mdev, struct net_device *netdev, int num_channels) { struct mlx5e_priv *priv = netdev_priv(netdev); + u32 link_speed = 0; + u32 pci_bw = 0; priv->params.log_sq_size = MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE; @@ -2704,15 +2768,42 @@ static void mlx5e_build_netdev_priv(struct mlx5_core_dev *mdev, MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ : MLX5_WQ_TYPE_LINKED_LIST; + /* set CQE compression */ + priv->params.rx_cqe_compress_admin = false; + if (MLX5_CAP_GEN(mdev, cqe_compression) && + MLX5_CAP_GEN(mdev, vport_group_manager)) { + mlx5e_get_max_linkspeed(mdev, &link_speed); + mlx5e_get_pci_bw(mdev, &pci_bw); + mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", + link_speed, pci_bw); + priv->params.rx_cqe_compress_admin = + cqe_compress_heuristic(link_speed, pci_bw); + } + + priv->params.rx_cqe_compress = priv->params.rx_cqe_compress_admin; + switch (priv->params.rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW; + priv->params.mpwqe_log_stride_sz = + priv->params.rx_cqe_compress ? + MLX5_MPWRQ_LOG_STRIDE_SIZE_CQE_COMPRESS : + MLX5_MPWRQ_LOG_STRIDE_SIZE; + priv->params.mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - + priv->params.mpwqe_log_stride_sz; priv->params.lro_en = true; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ priv->params.log_rq_size = MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE; } + mlx5_core_info(mdev, + "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n", + priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, + BIT(priv->params.log_rq_size), + BIT(priv->params.mpwqe_log_stride_sz), + priv->params.rx_cqe_compress_admin); + priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type, BIT(priv->params.log_rq_size)); priv->params.rx_cq_moderation_usec = @@ -2803,13 +2894,17 @@ static void mlx5e_build_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; if (mlx5e_vxlan_allowed(mdev)) { - netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL; netdev->hw_enc_features |= NETIF_F_IP_CSUM; - netdev->hw_enc_features |= NETIF_F_RXCSUM; + netdev->hw_enc_features |= NETIF_F_IPV6_CSUM; netdev->hw_enc_features |= NETIF_F_TSO; netdev->hw_enc_features |= NETIF_F_TSO6; - netdev->hw_enc_features |= NETIF_F_RXHASH; netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL; + netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_GSO_PARTIAL; + netdev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM; } mlx5_query_port_fcs(mdev, &fcs_supported, &fcs_enabled); @@ -2947,10 +3042,14 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev) priv = netdev_priv(netdev); + priv->wq = create_singlethread_workqueue("mlx5e"); + if (!priv->wq) + goto err_free_netdev; + err = mlx5_alloc_map_uar(mdev, &priv->cq_uar, false); if (err) { mlx5_core_err(mdev, "alloc_map uar failed, %d\n", err); - goto err_free_netdev; + goto err_destroy_wq; } err = mlx5_core_alloc_pd(mdev, &priv->pdn); @@ -3034,7 +3133,7 @@ static void *mlx5e_create_netdev(struct mlx5_core_dev *mdev) } mlx5e_enable_async_events(priv); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); return priv; @@ -3072,6 +3171,9 @@ err_dealloc_pd: err_unmap_free_uar: mlx5_unmap_free_uar(mdev, &priv->cq_uar); +err_destroy_wq: + destroy_workqueue(priv->wq); + err_free_netdev: free_netdev(netdev); @@ -3085,9 +3187,9 @@ static void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, void *vpriv) set_bit(MLX5E_STATE_DESTROYING, &priv->state); - schedule_work(&priv->set_rx_mode_work); + queue_work(priv->wq, &priv->set_rx_mode_work); mlx5e_disable_async_events(priv); - flush_scheduled_work(); + flush_workqueue(priv->wq); if (test_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &mdev->intf_state)) { netif_device_detach(netdev); mutex_lock(&priv->state_lock); @@ -3111,6 +3213,8 @@ static void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, void *vpriv) mlx5_core_dealloc_transport_domain(priv->mdev, priv->tdn); mlx5_core_dealloc_pd(priv->mdev, priv->pdn); mlx5_unmap_free_uar(priv->mdev, &priv->cq_uar); + cancel_delayed_work_sync(&priv->update_stats_work); + destroy_workqueue(priv->wq); if (!test_bit(MLX5_INTERFACE_STATE_SHUTDOWN, &mdev->intf_state)) free_netdev(netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 23adfe2fcba9..f3456798c596 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -42,6 +42,143 @@ static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp) return tstamp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL; } +static inline void mlx5e_read_cqe_slot(struct mlx5e_cq *cq, u32 cqcc, + void *data) +{ + u32 ci = cqcc & cq->wq.sz_m1; + + memcpy(data, mlx5_cqwq_get_wqe(&cq->wq, ci), sizeof(struct mlx5_cqe64)); +} + +static inline void mlx5e_read_title_slot(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, u32 cqcc) +{ + mlx5e_read_cqe_slot(cq, cqcc, &cq->title); + cq->decmprs_left = be32_to_cpu(cq->title.byte_cnt); + cq->decmprs_wqe_counter = be16_to_cpu(cq->title.wqe_counter); + rq->stats.cqe_compress_blks++; +} + +static inline void mlx5e_read_mini_arr_slot(struct mlx5e_cq *cq, u32 cqcc) +{ + mlx5e_read_cqe_slot(cq, cqcc, cq->mini_arr); + cq->mini_arr_idx = 0; +} + +static inline void mlx5e_cqes_update_owner(struct mlx5e_cq *cq, u32 cqcc, int n) +{ + u8 op_own = (cqcc >> cq->wq.log_sz) & 1; + u32 wq_sz = 1 << cq->wq.log_sz; + u32 ci = cqcc & cq->wq.sz_m1; + u32 ci_top = min_t(u32, wq_sz, ci + n); + + for (; ci < ci_top; ci++, n--) { + struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(&cq->wq, ci); + + cqe->op_own = op_own; + } + + if (unlikely(ci == wq_sz)) { + op_own = !op_own; + for (ci = 0; ci < n; ci++) { + struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(&cq->wq, ci); + + cqe->op_own = op_own; + } + } +} + +static inline void mlx5e_decompress_cqe(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, u32 cqcc) +{ + u16 wqe_cnt_step; + + cq->title.byte_cnt = cq->mini_arr[cq->mini_arr_idx].byte_cnt; + cq->title.check_sum = cq->mini_arr[cq->mini_arr_idx].checksum; + cq->title.op_own &= 0xf0; + cq->title.op_own |= 0x01 & (cqcc >> cq->wq.log_sz); + cq->title.wqe_counter = cpu_to_be16(cq->decmprs_wqe_counter); + + wqe_cnt_step = + rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ ? + mpwrq_get_cqe_consumed_strides(&cq->title) : 1; + cq->decmprs_wqe_counter = + (cq->decmprs_wqe_counter + wqe_cnt_step) & rq->wq.sz_m1; +} + +static inline void mlx5e_decompress_cqe_no_hash(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, u32 cqcc) +{ + mlx5e_decompress_cqe(rq, cq, cqcc); + cq->title.rss_hash_type = 0; + cq->title.rss_hash_result = 0; +} + +static inline u32 mlx5e_decompress_cqes_cont(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, + int update_owner_only, + int budget_rem) +{ + u32 cqcc = cq->wq.cc + update_owner_only; + u32 cqe_count; + u32 i; + + cqe_count = min_t(u32, cq->decmprs_left, budget_rem); + + for (i = update_owner_only; i < cqe_count; + i++, cq->mini_arr_idx++, cqcc++) { + if (cq->mini_arr_idx == MLX5_MINI_CQE_ARRAY_SIZE) + mlx5e_read_mini_arr_slot(cq, cqcc); + + mlx5e_decompress_cqe_no_hash(rq, cq, cqcc); + rq->handle_rx_cqe(rq, &cq->title); + } + mlx5e_cqes_update_owner(cq, cq->wq.cc, cqcc - cq->wq.cc); + cq->wq.cc = cqcc; + cq->decmprs_left -= cqe_count; + rq->stats.cqe_compress_pkts += cqe_count; + + return cqe_count; +} + +static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, + struct mlx5e_cq *cq, + int budget_rem) +{ + mlx5e_read_title_slot(rq, cq, cq->wq.cc); + mlx5e_read_mini_arr_slot(cq, cq->wq.cc + 1); + mlx5e_decompress_cqe(rq, cq, cq->wq.cc); + rq->handle_rx_cqe(rq, &cq->title); + cq->mini_arr_idx++; + + return mlx5e_decompress_cqes_cont(rq, cq, 1, budget_rem) - 1; +} + +void mlx5e_modify_rx_cqe_compression(struct mlx5e_priv *priv, bool val) +{ + bool was_opened; + + if (!MLX5_CAP_GEN(priv->mdev, cqe_compression)) + return; + + mutex_lock(&priv->state_lock); + + if (priv->params.rx_cqe_compress == val) + goto unlock; + + was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); + if (was_opened) + mlx5e_close_locked(priv->netdev); + + priv->params.rx_cqe_compress = val; + + if (was_opened) + mlx5e_open_locked(priv->netdev); + +unlock: + mutex_unlock(&priv->state_lock); +} + int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { struct sk_buff *skb; @@ -75,6 +212,11 @@ err_free_skb: return -ENOMEM; } +static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq) +{ + return rq->mpwqe_num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER; +} + static inline void mlx5e_dma_pre_sync_linear_mpwqe(struct device *pdev, struct mlx5e_mpw_info *wi, @@ -93,13 +235,13 @@ mlx5e_dma_pre_sync_fragmented_mpwqe(struct device *pdev, } static inline void -mlx5e_add_skb_frag_linear_mpwqe(struct device *pdev, +mlx5e_add_skb_frag_linear_mpwqe(struct mlx5e_rq *rq, struct sk_buff *skb, struct mlx5e_mpw_info *wi, u32 page_idx, u32 frag_offset, u32 len) { - unsigned int truesize = ALIGN(len, MLX5_MPWRQ_STRIDE_SIZE); + unsigned int truesize = ALIGN(len, rq->mpwqe_stride_sz); wi->skbs_frags[page_idx]++; skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, @@ -108,15 +250,15 @@ mlx5e_add_skb_frag_linear_mpwqe(struct device *pdev, } static inline void -mlx5e_add_skb_frag_fragmented_mpwqe(struct device *pdev, +mlx5e_add_skb_frag_fragmented_mpwqe(struct mlx5e_rq *rq, struct sk_buff *skb, struct mlx5e_mpw_info *wi, u32 page_idx, u32 frag_offset, u32 len) { - unsigned int truesize = ALIGN(len, MLX5_MPWRQ_STRIDE_SIZE); + unsigned int truesize = ALIGN(len, rq->mpwqe_stride_sz); - dma_sync_single_for_cpu(pdev, + dma_sync_single_for_cpu(rq->pdev, wi->umr.dma_info[page_idx].addr + frag_offset, len, DMA_FROM_DEVICE); wi->skbs_frags[page_idx]++; @@ -156,7 +298,6 @@ mlx5e_copy_skb_header_fragmented_mpwqe(struct device *pdev, skb_copy_to_linear_data_offset(skb, 0, page_address(dma_info->page) + offset, len); -#if (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD >= MLX5_MPWRQ_STRIDE_SIZE) if (unlikely(offset + headlen > PAGE_SIZE)) { dma_info++; headlen_pg = len; @@ -167,7 +308,6 @@ mlx5e_copy_skb_header_fragmented_mpwqe(struct device *pdev, page_address(dma_info->page), len); } -#endif } static u16 mlx5e_get_wqe_mtt_offset(u16 rq_ix, u16 wqe_ix) @@ -293,7 +433,7 @@ static int mlx5e_alloc_rx_fragmented_mpwqe(struct mlx5e_rq *rq, for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { if (unlikely(mlx5e_alloc_and_map_page(rq, wi, i))) goto err_unmap; - atomic_add(MLX5_MPWRQ_STRIDES_PER_PAGE, + atomic_add(mlx5e_mpwqe_strides_per_page(rq), &wi->umr.dma_info[i].page->_count); wi->skbs_frags[i] = 0; } @@ -312,7 +452,7 @@ err_unmap: while (--i >= 0) { dma_unmap_page(rq->pdev, wi->umr.dma_info[i].addr, PAGE_SIZE, PCI_DMA_FROMDEVICE); - atomic_sub(MLX5_MPWRQ_STRIDES_PER_PAGE, + atomic_sub(mlx5e_mpwqe_strides_per_page(rq), &wi->umr.dma_info[i].page->_count); put_page(wi->umr.dma_info[i].page); } @@ -337,7 +477,7 @@ void mlx5e_free_rx_fragmented_mpwqe(struct mlx5e_rq *rq, for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { dma_unmap_page(rq->pdev, wi->umr.dma_info[i].addr, PAGE_SIZE, PCI_DMA_FROMDEVICE); - atomic_sub(MLX5_MPWRQ_STRIDES_PER_PAGE - wi->skbs_frags[i], + atomic_sub(mlx5e_mpwqe_strides_per_page(rq) - wi->skbs_frags[i], &wi->umr.dma_info[i].page->_count); put_page(wi->umr.dma_info[i].page); } @@ -387,7 +527,7 @@ static int mlx5e_alloc_rx_linear_mpwqe(struct mlx5e_rq *rq, */ split_page(wi->dma_info.page, MLX5_MPWRQ_WQE_PAGE_ORDER); for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - atomic_add(MLX5_MPWRQ_STRIDES_PER_PAGE, + atomic_add(mlx5e_mpwqe_strides_per_page(rq), &wi->dma_info.page[i]._count); wi->skbs_frags[i] = 0; } @@ -411,7 +551,7 @@ void mlx5e_free_rx_linear_mpwqe(struct mlx5e_rq *rq, dma_unmap_page(rq->pdev, wi->dma_info.addr, rq->wqe_sz, PCI_DMA_FROMDEVICE); for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - atomic_sub(MLX5_MPWRQ_STRIDES_PER_PAGE - wi->skbs_frags[i], + atomic_sub(mlx5e_mpwqe_strides_per_page(rq) - wi->skbs_frags[i], &wi->dma_info.page[i]._count); put_page(&wi->dma_info.page[i]); } @@ -656,9 +796,9 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, u32 cqe_bcnt, struct sk_buff *skb) { - u32 consumed_bytes = ALIGN(cqe_bcnt, MLX5_MPWRQ_STRIDE_SIZE); + u32 consumed_bytes = ALIGN(cqe_bcnt, rq->mpwqe_stride_sz); u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); - u32 wqe_offset = stride_ix * MLX5_MPWRQ_STRIDE_SIZE; + u32 wqe_offset = stride_ix * rq->mpwqe_stride_sz; u32 head_offset = wqe_offset & (PAGE_SIZE - 1); u32 page_idx = wqe_offset >> PAGE_SHIFT; u32 head_page_idx = page_idx; @@ -666,19 +806,17 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, u32 frag_offset = head_offset + headlen; u16 byte_cnt = cqe_bcnt - headlen; -#if (MLX5_MPWRQ_SMALL_PACKET_THRESHOLD >= MLX5_MPWRQ_STRIDE_SIZE) if (unlikely(frag_offset >= PAGE_SIZE)) { page_idx++; frag_offset -= PAGE_SIZE; } -#endif wi->dma_pre_sync(rq->pdev, wi, wqe_offset, consumed_bytes); while (byte_cnt) { u32 pg_consumed_bytes = min_t(u32, PAGE_SIZE - frag_offset, byte_cnt); - wi->add_skb_frag(rq->pdev, skb, wi, page_idx, frag_offset, + wi->add_skb_frag(rq, skb, wi, page_idx, frag_offset, pg_consumed_bytes); byte_cnt -= pg_consumed_bytes; frag_offset = 0; @@ -728,7 +866,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb); mpwrq_cqe_out: - if (likely(wi->consumed_strides < MLX5_MPWRQ_NUM_STRIDES)) + if (likely(wi->consumed_strides < rq->mpwqe_num_strides)) return; wi->free_wqe(rq, wi); @@ -738,14 +876,24 @@ mpwrq_cqe_out: int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - int work_done; + int work_done = 0; - for (work_done = 0; work_done < budget; work_done++) { + if (cq->decmprs_left) + work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget); + + for (; work_done < budget; work_done++) { struct mlx5_cqe64 *cqe = mlx5e_get_cqe(cq); if (!cqe) break; + if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) { + work_done += + mlx5e_decompress_cqes_start(rq, cq, + budget - work_done); + continue; + } + mlx5_cqwq_pop(&cq->wq); rq->handle_rx_cqe(rq, cqe); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 115752b53d85..83bc32b25849 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -72,6 +72,8 @@ struct mlx5e_sw_stats { u64 rx_mpwqe_filler; u64 rx_mpwqe_frag; u64 rx_buff_alloc_err; + u64 rx_cqe_compress_blks; + u64 rx_cqe_compress_pkts; /* Special handling counters */ u64 link_down_events; @@ -101,6 +103,8 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_frag) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_buff_alloc_err) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_blks) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_pkts) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, link_down_events) }, }; @@ -283,6 +287,8 @@ struct mlx5e_rq_stats { u64 mpwqe_filler; u64 mpwqe_frag; u64 buff_alloc_err; + u64 cqe_compress_blks; + u64 cqe_compress_pkts; }; static const struct counter_desc rq_stats_desc[] = { @@ -297,6 +303,8 @@ static const struct counter_desc rq_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, mpwqe_filler) }, { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, mpwqe_frag) }, { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, buff_alloc_err) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, cqe_compress_blks) }, + { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, cqe_compress_pkts) }, }; struct mlx5e_sq_stats { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index ef017c0decdc..704c3d30493e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -53,13 +53,24 @@ static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv, u32 *match_c, u32 *match_v, u32 action, u32 flow_tag) { - struct mlx5_flow_destination dest = { - .type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE, - {.ft = priv->fs.vlan.ft.t}, - }; + struct mlx5_core_dev *dev = priv->mdev; + struct mlx5_flow_destination dest = { 0 }; + struct mlx5_fc *counter = NULL; struct mlx5_flow_rule *rule; bool table_created = false; + if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fs.vlan.ft.t; + } else { + counter = mlx5_fc_create(dev, true); + if (IS_ERR(counter)) + return ERR_CAST(counter); + + dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; + dest.counter = counter; + } + if (IS_ERR_OR_NULL(priv->fs.tc.t)) { priv->fs.tc.t = mlx5_create_auto_grouped_flow_table(priv->fs.ns, @@ -70,7 +81,8 @@ static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv, if (IS_ERR(priv->fs.tc.t)) { netdev_err(priv->netdev, "Failed to create tc offload table\n"); - return ERR_CAST(priv->fs.tc.t); + rule = ERR_CAST(priv->fs.tc.t); + goto err_create_ft; } table_created = true; @@ -79,12 +91,20 @@ static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv, rule = mlx5_add_flow_rule(priv->fs.tc.t, MLX5_MATCH_OUTER_HEADERS, match_c, match_v, action, flow_tag, - action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ? &dest : NULL); + &dest); + + if (IS_ERR(rule)) + goto err_add_rule; + + return rule; - if (IS_ERR(rule) && table_created) { +err_add_rule: + if (table_created) { mlx5_destroy_flow_table(priv->fs.tc.t); priv->fs.tc.t = NULL; } +err_create_ft: + mlx5_fc_destroy(dev, counter); return rule; } @@ -92,8 +112,14 @@ static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv, static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, struct mlx5_flow_rule *rule) { + struct mlx5_fc *counter = NULL; + + counter = mlx5_flow_rule_counter(rule); + mlx5_del_flow_rule(rule); + mlx5_fc_destroy(priv->mdev, counter); + if (!mlx5e_tc_num_filters(priv)) { mlx5_destroy_flow_table(priv->fs.tc.t); priv->fs.tc.t = NULL; @@ -286,6 +312,9 @@ static int parse_tc_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, if (is_tcf_gact_shot(a)) { *action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + if (MLX5_CAP_FLOWTABLE(priv->mdev, + flow_table_properties_nic_receive.flow_counter)) + *action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; continue; } @@ -394,6 +423,34 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, return 0; } +int mlx5e_stats_flower(struct mlx5e_priv *priv, + struct tc_cls_flower_offload *f) +{ + struct mlx5e_tc_table *tc = &priv->fs.tc; + struct mlx5e_tc_flow *flow; + struct tc_action *a; + struct mlx5_fc *counter; + u64 bytes; + u64 packets; + u64 lastuse; + + flow = rhashtable_lookup_fast(&tc->ht, &f->cookie, + tc->ht_params); + if (!flow) + return -EINVAL; + + counter = mlx5_flow_rule_counter(flow->rule); + if (!counter) + return 0; + + mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse); + + tc_for_each_action(a, f->exts) + tcf_action_stats_update(a, bytes, packets, lastuse); + + return 0; +} + static const struct rhashtable_params mlx5e_tc_flow_ht_params = { .head_offset = offsetof(struct mlx5e_tc_flow, node), .key_offset = offsetof(struct mlx5e_tc_flow, cookie), diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index a4f17b974d62..34bf903fc886 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -43,6 +43,9 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); +int mlx5e_stats_flower(struct mlx5e_priv *priv, + struct tc_cls_flower_offload *f); + static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return atomic_read(&priv->fs.tc.ht.nelems); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index ff91bb5e1c43..b84a6918a700 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -77,16 +77,20 @@ struct vport_addr { u8 action; u32 vport; struct mlx5_flow_rule *flow_rule; /* SRIOV only */ + /* A flag indicating that mac was added due to mc promiscuous vport */ + bool mc_promisc; }; enum { UC_ADDR_CHANGE = BIT(0), MC_ADDR_CHANGE = BIT(1), + PROMISC_CHANGE = BIT(3), }; /* Vport context events */ #define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \ - MC_ADDR_CHANGE) + MC_ADDR_CHANGE | \ + PROMISC_CHANGE) static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport, u32 events_mask) @@ -116,6 +120,9 @@ static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport, if (events_mask & MC_ADDR_CHANGE) MLX5_SET(nic_vport_context, nic_vport_ctx, event_on_mc_address_change, 1); + if (events_mask & PROMISC_CHANGE) + MLX5_SET(nic_vport_context, nic_vport_ctx, + event_on_promisc_change, 1); err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); if (err) @@ -323,30 +330,45 @@ static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) /* E-Switch FDB */ static struct mlx5_flow_rule * -esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) +__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, + u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN]) { - int match_header = MLX5_MATCH_OUTER_HEADERS; - struct mlx5_flow_destination dest; + int match_header = (is_zero_ether_addr(mac_c) ? 0 : + MLX5_MATCH_OUTER_HEADERS); struct mlx5_flow_rule *flow_rule = NULL; + struct mlx5_flow_destination dest; + void *mv_misc = NULL; + void *mc_misc = NULL; + u8 *dmac_v = NULL; + u8 *dmac_c = NULL; u32 *match_v; u32 *match_c; - u8 *dmac_v; - u8 *dmac_c; + if (rx_rule) + match_header |= MLX5_MATCH_MISC_PARAMETERS; match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); if (!match_v || !match_c) { pr_warn("FDB: Failed to alloc match parameters\n"); goto out; } + dmac_v = MLX5_ADDR_OF(fte_match_param, match_v, outer_headers.dmac_47_16); dmac_c = MLX5_ADDR_OF(fte_match_param, match_c, outer_headers.dmac_47_16); - ether_addr_copy(dmac_v, mac); - /* Match criteria mask */ - memset(dmac_c, 0xff, 6); + if (match_header & MLX5_MATCH_OUTER_HEADERS) { + ether_addr_copy(dmac_v, mac_v); + ether_addr_copy(dmac_c, mac_c); + } + + if (match_header & MLX5_MATCH_MISC_PARAMETERS) { + mv_misc = MLX5_ADDR_OF(fte_match_param, match_v, misc_parameters); + mc_misc = MLX5_ADDR_OF(fte_match_param, match_c, misc_parameters); + MLX5_SET(fte_match_set_misc, mv_misc, source_port, UPLINK_VPORT); + MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port); + } dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT; dest.vport_num = vport; @@ -373,6 +395,39 @@ out: return flow_rule; } +static struct mlx5_flow_rule * +esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport) +{ + u8 mac_c[ETH_ALEN]; + + eth_broadcast_addr(mac_c); + return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac); +} + +static struct mlx5_flow_rule * +esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u32 vport) +{ + u8 mac_c[ETH_ALEN]; + u8 mac_v[ETH_ALEN]; + + eth_zero_addr(mac_c); + eth_zero_addr(mac_v); + mac_c[0] = 0x01; + mac_v[0] = 0x01; + return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac_v); +} + +static struct mlx5_flow_rule * +esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u32 vport) +{ + u8 mac_c[ETH_ALEN]; + u8 mac_v[ETH_ALEN]; + + eth_zero_addr(mac_c); + eth_zero_addr(mac_v); + return __esw_fdb_set_vport_rule(esw, vport, true, mac_c, mac_v); +} + static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); @@ -407,28 +462,74 @@ static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports) esw_warn(dev, "Failed to create FDB Table err %d\n", err); goto out; } + esw->fdb_table.fdb = fdb; + /* Addresses group : Full match unicast/multicast addresses */ MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16); MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); - MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1); + /* Preserve 2 entries for allmulti and promisc rules*/ + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 3); eth_broadcast_addr(dmac); - g = mlx5_create_flow_group(fdb, flow_group_in); if (IS_ERR_OR_NULL(g)) { err = PTR_ERR(g); esw_warn(dev, "Failed to create flow group err(%d)\n", err); goto out; } - esw->fdb_table.addr_grp = g; - esw->fdb_table.fdb = fdb; + + /* Allmulti group : One rule that forwards any mcast traffic */ + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_OUTER_HEADERS); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 2); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 2); + eth_zero_addr(dmac); + dmac[0] = 0x01; + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err); + goto out; + } + esw->fdb_table.allmulti_grp = g; + + /* Promiscuous group : + * One rule that forward all unmatched traffic from previous groups + */ + eth_zero_addr(dmac); + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_MISC_PARAMETERS); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1); + g = mlx5_create_flow_group(fdb, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err); + goto out; + } + esw->fdb_table.promisc_grp = g; + out: + if (err) { + if (!IS_ERR_OR_NULL(esw->fdb_table.allmulti_grp)) { + mlx5_destroy_flow_group(esw->fdb_table.allmulti_grp); + esw->fdb_table.allmulti_grp = NULL; + } + if (!IS_ERR_OR_NULL(esw->fdb_table.addr_grp)) { + mlx5_destroy_flow_group(esw->fdb_table.addr_grp); + esw->fdb_table.addr_grp = NULL; + } + if (!IS_ERR_OR_NULL(esw->fdb_table.fdb)) { + mlx5_destroy_flow_table(esw->fdb_table.fdb); + esw->fdb_table.fdb = NULL; + } + } + kfree(flow_group_in); - if (err && !IS_ERR_OR_NULL(fdb)) - mlx5_destroy_flow_table(fdb); return err; } @@ -438,10 +539,14 @@ static void esw_destroy_fdb_table(struct mlx5_eswitch *esw) return; esw_debug(esw->dev, "Destroy FDB Table\n"); + mlx5_destroy_flow_group(esw->fdb_table.promisc_grp); + mlx5_destroy_flow_group(esw->fdb_table.allmulti_grp); mlx5_destroy_flow_group(esw->fdb_table.addr_grp); mlx5_destroy_flow_table(esw->fdb_table.fdb); esw->fdb_table.fdb = NULL; esw->fdb_table.addr_grp = NULL; + esw->fdb_table.allmulti_grp = NULL; + esw->fdb_table.promisc_grp = NULL; } /* E-Switch vport UC/MC lists management */ @@ -511,6 +616,52 @@ static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) return 0; } +static void update_allmulti_vports(struct mlx5_eswitch *esw, + struct vport_addr *vaddr, + struct esw_mc_addr *esw_mc) +{ + u8 *mac = vaddr->node.addr; + u32 vport_idx = 0; + + for (vport_idx = 0; vport_idx < esw->total_vports; vport_idx++) { + struct mlx5_vport *vport = &esw->vports[vport_idx]; + struct hlist_head *vport_hash = vport->mc_list; + struct vport_addr *iter_vaddr = + l2addr_hash_find(vport_hash, + mac, + struct vport_addr); + if (IS_ERR_OR_NULL(vport->allmulti_rule) || + vaddr->vport == vport_idx) + continue; + switch (vaddr->action) { + case MLX5_ACTION_ADD: + if (iter_vaddr) + continue; + iter_vaddr = l2addr_hash_add(vport_hash, mac, + struct vport_addr, + GFP_KERNEL); + if (!iter_vaddr) { + esw_warn(esw->dev, + "ALL-MULTI: Failed to add MAC(%pM) to vport[%d] DB\n", + mac, vport_idx); + continue; + } + iter_vaddr->vport = vport_idx; + iter_vaddr->flow_rule = + esw_fdb_set_vport_rule(esw, + mac, + vport_idx); + break; + case MLX5_ACTION_DEL: + if (!iter_vaddr) + continue; + mlx5_del_flow_rule(iter_vaddr->flow_rule); + l2addr_hash_del(iter_vaddr); + break; + } + } +} + static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { struct hlist_head *hash = esw->mc_table; @@ -531,8 +682,17 @@ static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) esw_mc->uplink_rule = /* Forward MC MAC to Uplink */ esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT); + + /* Add this multicast mac to all the mc promiscuous vports */ + update_allmulti_vports(esw, vaddr, esw_mc); + add: - esw_mc->refcnt++; + /* If the multicast mac is added as a result of mc promiscuous vport, + * don't increment the multicast ref count + */ + if (!vaddr->mc_promisc) + esw_mc->refcnt++; + /* Forward MC MAC to vport */ vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); esw_debug(esw->dev, @@ -568,9 +728,15 @@ static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) mlx5_del_flow_rule(vaddr->flow_rule); vaddr->flow_rule = NULL; - if (--esw_mc->refcnt) + /* If the multicast mac is added as a result of mc promiscuous vport, + * don't decrement the multicast ref count. + */ + if (vaddr->mc_promisc || (--esw_mc->refcnt > 0)) return 0; + /* Remove this multicast mac from all the mc promiscuous vports */ + update_allmulti_vports(esw, vaddr, esw_mc); + if (esw_mc->uplink_rule) mlx5_del_flow_rule(esw_mc->uplink_rule); @@ -643,10 +809,13 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, addr->action = MLX5_ACTION_DEL; } + if (!vport->enabled) + goto out; + err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type, mac_list, &size); if (err) - return; + goto out; esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n", vport_num, is_uc ? "UC" : "MC", size); @@ -660,6 +829,24 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr); if (addr) { addr->action = MLX5_ACTION_NONE; + /* If this mac was previously added because of allmulti + * promiscuous rx mode, its now converted to be original + * vport mac. + */ + if (addr->mc_promisc) { + struct esw_mc_addr *esw_mc = + l2addr_hash_find(esw->mc_table, + mac_list[i], + struct esw_mc_addr); + if (!esw_mc) { + esw_warn(esw->dev, + "Failed to MAC(%pM) in mcast DB\n", + mac_list[i]); + continue; + } + esw_mc->refcnt++; + addr->mc_promisc = false; + } continue; } @@ -674,13 +861,121 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw, addr->vport = vport_num; addr->action = MLX5_ACTION_ADD; } +out: kfree(mac_list); } -static void esw_vport_change_handler(struct work_struct *work) +/* Sync vport UC/MC list from vport context + * Must be called after esw_update_vport_addr_list + */ +static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u32 vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + struct l2addr_node *node; + struct vport_addr *addr; + struct hlist_head *hash; + struct hlist_node *tmp; + int hi; + + hash = vport->mc_list; + + for_each_l2hash_node(node, tmp, esw->mc_table, hi) { + u8 *mac = node->addr; + + addr = l2addr_hash_find(hash, mac, struct vport_addr); + if (addr) { + if (addr->action == MLX5_ACTION_DEL) + addr->action = MLX5_ACTION_NONE; + continue; + } + addr = l2addr_hash_add(hash, mac, struct vport_addr, + GFP_KERNEL); + if (!addr) { + esw_warn(esw->dev, + "Failed to add allmulti MAC(%pM) to vport[%d] DB\n", + mac, vport_num); + continue; + } + addr->vport = vport_num; + addr->action = MLX5_ACTION_ADD; + addr->mc_promisc = true; + } +} + +/* Apply vport rx mode to HW FDB table */ +static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num, + bool promisc, bool mc_promisc) +{ + struct esw_mc_addr *allmulti_addr = esw->mc_promisc; + struct mlx5_vport *vport = &esw->vports[vport_num]; + + if (IS_ERR_OR_NULL(vport->allmulti_rule) != mc_promisc) + goto promisc; + + if (mc_promisc) { + vport->allmulti_rule = + esw_fdb_set_vport_allmulti_rule(esw, vport_num); + if (!allmulti_addr->uplink_rule) + allmulti_addr->uplink_rule = + esw_fdb_set_vport_allmulti_rule(esw, + UPLINK_VPORT); + allmulti_addr->refcnt++; + } else if (vport->allmulti_rule) { + mlx5_del_flow_rule(vport->allmulti_rule); + vport->allmulti_rule = NULL; + + if (--allmulti_addr->refcnt > 0) + goto promisc; + + if (allmulti_addr->uplink_rule) + mlx5_del_flow_rule(allmulti_addr->uplink_rule); + allmulti_addr->uplink_rule = NULL; + } + +promisc: + if (IS_ERR_OR_NULL(vport->promisc_rule) != promisc) + return; + + if (promisc) { + vport->promisc_rule = esw_fdb_set_vport_promisc_rule(esw, + vport_num); + } else if (vport->promisc_rule) { + mlx5_del_flow_rule(vport->promisc_rule); + vport->promisc_rule = NULL; + } +} + +/* Sync vport rx mode from vport context */ +static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num) +{ + struct mlx5_vport *vport = &esw->vports[vport_num]; + int promisc_all = 0; + int promisc_uc = 0; + int promisc_mc = 0; + int err; + + err = mlx5_query_nic_vport_promisc(esw->dev, + vport_num, + &promisc_uc, + &promisc_mc, + &promisc_all); + if (err) + return; + esw_debug(esw->dev, "vport[%d] context update rx mode promisc_all=%d, all_multi=%d\n", + vport_num, promisc_all, promisc_mc); + + if (!vport->trusted || !vport->enabled) { + promisc_uc = 0; + promisc_mc = 0; + promisc_all = 0; + } + + esw_apply_vport_rx_mode(esw, vport_num, promisc_all, + (promisc_all || promisc_mc)); +} + +static void esw_vport_change_handle_locked(struct mlx5_vport *vport) { - struct mlx5_vport *vport = - container_of(work, struct mlx5_vport, vport_change_handler); struct mlx5_core_dev *dev = vport->dev; struct mlx5_eswitch *esw = dev->priv.eswitch; u8 mac[ETH_ALEN]; @@ -699,6 +994,15 @@ static void esw_vport_change_handler(struct work_struct *work) if (vport->enabled_events & MC_ADDR_CHANGE) { esw_update_vport_addr_list(esw, vport->vport, MLX5_NVPRT_LIST_TYPE_MC); + } + + if (vport->enabled_events & PROMISC_CHANGE) { + esw_update_vport_rx_mode(esw, vport->vport); + if (!IS_ERR_OR_NULL(vport->allmulti_rule)) + esw_update_vport_mc_promisc(esw, vport->vport); + } + + if (vport->enabled_events & (PROMISC_CHANGE | MC_ADDR_CHANGE)) { esw_apply_vport_addr_list(esw, vport->vport, MLX5_NVPRT_LIST_TYPE_MC); } @@ -709,15 +1013,477 @@ static void esw_vport_change_handler(struct work_struct *work) vport->enabled_events); } +static void esw_vport_change_handler(struct work_struct *work) +{ + struct mlx5_vport *vport = + container_of(work, struct mlx5_vport, vport_change_handler); + struct mlx5_eswitch *esw = vport->dev->priv.eswitch; + + mutex_lock(&esw->state_lock); + esw_vport_change_handle_locked(vport); + mutex_unlock(&esw->state_lock); +} + +static void esw_vport_enable_egress_acl(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_group *vlan_grp = NULL; + struct mlx5_flow_group *drop_grp = NULL; + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_namespace *root_ns; + struct mlx5_flow_table *acl; + void *match_criteria; + u32 *flow_group_in; + /* The egress acl table contains 2 rules: + * 1)Allow traffic with vlan_tag=vst_vlan_id + * 2)Drop all other traffic. + */ + int table_size = 2; + int err = 0; + + if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support) || + !IS_ERR_OR_NULL(vport->egress.acl)) + return; + + esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n", + vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size)); + + root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS); + if (!root_ns) { + esw_warn(dev, "Failed to get E-Switch egress flow namespace\n"); + return; + } + + flow_group_in = mlx5_vzalloc(inlen); + if (!flow_group_in) + return; + + acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport); + if (IS_ERR_OR_NULL(acl)) { + err = PTR_ERR(acl); + esw_warn(dev, "Failed to create E-Switch vport[%d] egress flow Table, err(%d)\n", + vport->vport, err); + goto out; + } + + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.first_vid); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0); + + vlan_grp = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(vlan_grp)) { + err = PTR_ERR(vlan_grp); + esw_warn(dev, "Failed to create E-Switch vport[%d] egress allowed vlans flow group, err(%d)\n", + vport->vport, err); + goto out; + } + + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1); + drop_grp = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(drop_grp)) { + err = PTR_ERR(drop_grp); + esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop flow group, err(%d)\n", + vport->vport, err); + goto out; + } + + vport->egress.acl = acl; + vport->egress.drop_grp = drop_grp; + vport->egress.allowed_vlans_grp = vlan_grp; +out: + kfree(flow_group_in); + if (err && !IS_ERR_OR_NULL(vlan_grp)) + mlx5_destroy_flow_group(vlan_grp); + if (err && !IS_ERR_OR_NULL(acl)) + mlx5_destroy_flow_table(acl); +} + +static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan)) + mlx5_del_flow_rule(vport->egress.allowed_vlan); + + if (!IS_ERR_OR_NULL(vport->egress.drop_rule)) + mlx5_del_flow_rule(vport->egress.drop_rule); + + vport->egress.allowed_vlan = NULL; + vport->egress.drop_rule = NULL; +} + +static void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + if (IS_ERR_OR_NULL(vport->egress.acl)) + return; + + esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport); + + esw_vport_cleanup_egress_rules(esw, vport); + mlx5_destroy_flow_group(vport->egress.allowed_vlans_grp); + mlx5_destroy_flow_group(vport->egress.drop_grp); + mlx5_destroy_flow_table(vport->egress.acl); + vport->egress.allowed_vlans_grp = NULL; + vport->egress.drop_grp = NULL; + vport->egress.acl = NULL; +} + +static void esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_namespace *root_ns; + struct mlx5_flow_table *acl; + struct mlx5_flow_group *g; + void *match_criteria; + u32 *flow_group_in; + /* The ingress acl table contains 4 groups + * (2 active rules at the same time - + * 1 allow rule from one of the first 3 groups. + * 1 drop rule from the last group): + * 1)Allow untagged traffic with smac=original mac. + * 2)Allow untagged traffic. + * 3)Allow traffic with smac=original mac. + * 4)Drop all other traffic. + */ + int table_size = 4; + int err = 0; + + if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support) || + !IS_ERR_OR_NULL(vport->ingress.acl)) + return; + + esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n", + vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size)); + + root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS); + if (!root_ns) { + esw_warn(dev, "Failed to get E-Switch ingress flow namespace\n"); + return; + } + + flow_group_in = mlx5_vzalloc(inlen); + if (!flow_group_in) + return; + + acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport); + if (IS_ERR_OR_NULL(acl)) { + err = PTR_ERR(acl); + esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow Table, err(%d)\n", + vport->vport, err); + goto out; + } + vport->ingress.acl = acl; + + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria); + + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0); + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged spoofchk flow group, err(%d)\n", + vport->vport, err); + goto out; + } + vport->ingress.allow_untagged_spoofchk_grp = g; + + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.vlan_tag); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1); + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged flow group, err(%d)\n", + vport->vport, err); + goto out; + } + vport->ingress.allow_untagged_only_grp = g; + + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16); + MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 2); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 2); + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create E-Switch vport[%d] ingress spoofchk flow group, err(%d)\n", + vport->vport, err); + goto out; + } + vport->ingress.allow_spoofchk_only_grp = g; + + memset(flow_group_in, 0, inlen); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 3); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 3); + + g = mlx5_create_flow_group(acl, flow_group_in); + if (IS_ERR_OR_NULL(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create E-Switch vport[%d] ingress drop flow group, err(%d)\n", + vport->vport, err); + goto out; + } + vport->ingress.drop_grp = g; + +out: + if (err) { + if (!IS_ERR_OR_NULL(vport->ingress.allow_spoofchk_only_grp)) + mlx5_destroy_flow_group( + vport->ingress.allow_spoofchk_only_grp); + if (!IS_ERR_OR_NULL(vport->ingress.allow_untagged_only_grp)) + mlx5_destroy_flow_group( + vport->ingress.allow_untagged_only_grp); + if (!IS_ERR_OR_NULL(vport->ingress.allow_untagged_spoofchk_grp)) + mlx5_destroy_flow_group( + vport->ingress.allow_untagged_spoofchk_grp); + if (!IS_ERR_OR_NULL(vport->ingress.acl)) + mlx5_destroy_flow_table(vport->ingress.acl); + } + + kfree(flow_group_in); +} + +static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + if (!IS_ERR_OR_NULL(vport->ingress.drop_rule)) + mlx5_del_flow_rule(vport->ingress.drop_rule); + + if (!IS_ERR_OR_NULL(vport->ingress.allow_rule)) + mlx5_del_flow_rule(vport->ingress.allow_rule); + + vport->ingress.drop_rule = NULL; + vport->ingress.allow_rule = NULL; +} + +static void esw_vport_disable_ingress_acl(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + if (IS_ERR_OR_NULL(vport->ingress.acl)) + return; + + esw_debug(esw->dev, "Destroy vport[%d] E-Switch ingress ACL\n", vport->vport); + + esw_vport_cleanup_ingress_rules(esw, vport); + mlx5_destroy_flow_group(vport->ingress.allow_spoofchk_only_grp); + mlx5_destroy_flow_group(vport->ingress.allow_untagged_only_grp); + mlx5_destroy_flow_group(vport->ingress.allow_untagged_spoofchk_grp); + mlx5_destroy_flow_group(vport->ingress.drop_grp); + mlx5_destroy_flow_table(vport->ingress.acl); + vport->ingress.acl = NULL; + vport->ingress.drop_grp = NULL; + vport->ingress.allow_spoofchk_only_grp = NULL; + vport->ingress.allow_untagged_only_grp = NULL; + vport->ingress.allow_untagged_spoofchk_grp = NULL; +} + +static int esw_vport_ingress_config(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + u8 smac[ETH_ALEN]; + u32 *match_v; + u32 *match_c; + int err = 0; + u8 *smac_v; + + if (vport->spoofchk) { + err = mlx5_query_nic_vport_mac_address(esw->dev, vport->vport, smac); + if (err) { + esw_warn(esw->dev, + "vport[%d] configure ingress rules failed, query smac failed, err(%d)\n", + vport->vport, err); + return err; + } + + if (!is_valid_ether_addr(smac)) { + mlx5_core_warn(esw->dev, + "vport[%d] configure ingress rules failed, illegal mac with spoofchk\n", + vport->vport); + return -EPERM; + } + } + + esw_vport_cleanup_ingress_rules(esw, vport); + + if (!vport->vlan && !vport->qos && !vport->spoofchk) { + esw_vport_disable_ingress_acl(esw, vport); + return 0; + } + + esw_vport_enable_ingress_acl(esw, vport); + + esw_debug(esw->dev, + "vport[%d] configure ingress rules, vlan(%d) qos(%d)\n", + vport->vport, vport->vlan, vport->qos); + + match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + if (!match_v || !match_c) { + err = -ENOMEM; + esw_warn(esw->dev, "vport[%d] configure ingress rules failed, err(%d)\n", + vport->vport, err); + goto out; + } + + if (vport->vlan || vport->qos) + MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.vlan_tag); + + if (vport->spoofchk) { + MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.smac_47_16); + MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.smac_15_0); + smac_v = MLX5_ADDR_OF(fte_match_param, + match_v, + outer_headers.smac_47_16); + ether_addr_copy(smac_v, smac); + } + + vport->ingress.allow_rule = + mlx5_add_flow_rule(vport->ingress.acl, + MLX5_MATCH_OUTER_HEADERS, + match_c, + match_v, + MLX5_FLOW_CONTEXT_ACTION_ALLOW, + 0, NULL); + if (IS_ERR_OR_NULL(vport->ingress.allow_rule)) { + err = PTR_ERR(vport->ingress.allow_rule); + pr_warn("vport[%d] configure ingress allow rule, err(%d)\n", + vport->vport, err); + vport->ingress.allow_rule = NULL; + goto out; + } + + memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + vport->ingress.drop_rule = + mlx5_add_flow_rule(vport->ingress.acl, + 0, + match_c, + match_v, + MLX5_FLOW_CONTEXT_ACTION_DROP, + 0, NULL); + if (IS_ERR_OR_NULL(vport->ingress.drop_rule)) { + err = PTR_ERR(vport->ingress.drop_rule); + pr_warn("vport[%d] configure ingress drop rule, err(%d)\n", + vport->vport, err); + vport->ingress.drop_rule = NULL; + goto out; + } + +out: + if (err) + esw_vport_cleanup_ingress_rules(esw, vport); + + kfree(match_v); + kfree(match_c); + return err; +} + +static int esw_vport_egress_config(struct mlx5_eswitch *esw, + struct mlx5_vport *vport) +{ + u32 *match_v; + u32 *match_c; + int err = 0; + + esw_vport_cleanup_egress_rules(esw, vport); + + if (!vport->vlan && !vport->qos) { + esw_vport_disable_egress_acl(esw, vport); + return 0; + } + + esw_vport_enable_egress_acl(esw, vport); + + esw_debug(esw->dev, + "vport[%d] configure egress rules, vlan(%d) qos(%d)\n", + vport->vport, vport->vlan, vport->qos); + + match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL); + if (!match_v || !match_c) { + err = -ENOMEM; + esw_warn(esw->dev, "vport[%d] configure egress rules failed, err(%d)\n", + vport->vport, err); + goto out; + } + + /* Allowed vlan rule */ + MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.vlan_tag); + MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.first_vid); + MLX5_SET(fte_match_param, match_v, outer_headers.first_vid, vport->vlan); + + vport->egress.allowed_vlan = + mlx5_add_flow_rule(vport->egress.acl, + MLX5_MATCH_OUTER_HEADERS, + match_c, + match_v, + MLX5_FLOW_CONTEXT_ACTION_ALLOW, + 0, NULL); + if (IS_ERR_OR_NULL(vport->egress.allowed_vlan)) { + err = PTR_ERR(vport->egress.allowed_vlan); + pr_warn("vport[%d] configure egress allowed vlan rule failed, err(%d)\n", + vport->vport, err); + vport->egress.allowed_vlan = NULL; + goto out; + } + + /* Drop others rule (star rule) */ + memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param)); + vport->egress.drop_rule = + mlx5_add_flow_rule(vport->egress.acl, + 0, + match_c, + match_v, + MLX5_FLOW_CONTEXT_ACTION_DROP, + 0, NULL); + if (IS_ERR_OR_NULL(vport->egress.drop_rule)) { + err = PTR_ERR(vport->egress.drop_rule); + pr_warn("vport[%d] configure egress drop rule failed, err(%d)\n", + vport->vport, err); + vport->egress.drop_rule = NULL; + } +out: + kfree(match_v); + kfree(match_c); + return err; +} + static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num, int enable_events) { struct mlx5_vport *vport = &esw->vports[vport_num]; - unsigned long flags; + mutex_lock(&esw->state_lock); WARN_ON(vport->enabled); esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num); + + if (vport_num) { /* Only VFs need ACLs for VST and spoofchk filtering */ + esw_vport_ingress_config(esw, vport); + esw_vport_egress_config(esw, vport); + } + mlx5_modify_vport_admin_state(esw->dev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, vport_num, @@ -725,53 +1491,32 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num, /* Sync with current vport context */ vport->enabled_events = enable_events; - esw_vport_change_handler(&vport->vport_change_handler); + esw_vport_change_handle_locked(vport); - spin_lock_irqsave(&vport->lock, flags); vport->enabled = true; - spin_unlock_irqrestore(&vport->lock, flags); + + /* only PF is trusted by default */ + vport->trusted = (vport_num) ? false : true; arm_vport_context_events_cmd(esw->dev, vport_num, enable_events); esw->enabled_vports++; esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num); -} - -static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num) -{ - struct mlx5_vport *vport = &esw->vports[vport_num]; - struct l2addr_node *node; - struct vport_addr *addr; - struct hlist_node *tmp; - int hi; - - for_each_l2hash_node(node, tmp, vport->uc_list, hi) { - addr = container_of(node, struct vport_addr, node); - addr->action = MLX5_ACTION_DEL; - } - esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_UC); - - for_each_l2hash_node(node, tmp, vport->mc_list, hi) { - addr = container_of(node, struct vport_addr, node); - addr->action = MLX5_ACTION_DEL; - } - esw_apply_vport_addr_list(esw, vport_num, MLX5_NVPRT_LIST_TYPE_MC); + mutex_unlock(&esw->state_lock); } static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) { struct mlx5_vport *vport = &esw->vports[vport_num]; - unsigned long flags; if (!vport->enabled) return; esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num); /* Mark this vport as disabled to discard new events */ - spin_lock_irqsave(&vport->lock, flags); vport->enabled = false; - vport->enabled_events = 0; - spin_unlock_irqrestore(&vport->lock, flags); + + synchronize_irq(mlx5_get_msix_vec(esw->dev, MLX5_EQ_VEC_ASYNC)); mlx5_modify_vport_admin_state(esw->dev, MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT, @@ -781,9 +1526,19 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) flush_workqueue(esw->work_queue); /* Disable events from this vport */ arm_vport_context_events_cmd(esw->dev, vport->vport, 0); - /* We don't assume VFs will cleanup after themselves */ - esw_cleanup_vport(esw, vport_num); + mutex_lock(&esw->state_lock); + /* We don't assume VFs will cleanup after themselves. + * Calling vport change handler while vport is disabled will cleanup + * the vport resources. + */ + esw_vport_change_handle_locked(vport); + vport->enabled_events = 0; + if (vport_num) { + esw_vport_disable_egress_acl(esw, vport); + esw_vport_disable_ingress_acl(esw, vport); + } esw->enabled_vports--; + mutex_unlock(&esw->state_lock); } /* Public E-Switch API */ @@ -802,6 +1557,12 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs) return -ENOTSUPP; } + if (!MLX5_CAP_ESW_INGRESS_ACL(esw->dev, ft_support)) + esw_warn(esw->dev, "E-Switch ingress ACL is not supported by FW\n"); + + if (!MLX5_CAP_ESW_EGRESS_ACL(esw->dev, ft_support)) + esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n"); + esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs); esw_disable_vport(esw, 0); @@ -824,6 +1585,7 @@ abort: void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) { + struct esw_mc_addr *mc_promisc; int i; if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || @@ -833,9 +1595,14 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_info(esw->dev, "disable SRIOV: active vports(%d)\n", esw->enabled_vports); + mc_promisc = esw->mc_promisc; + for (i = 0; i < esw->total_vports; i++) esw_disable_vport(esw, i); + if (mc_promisc && mc_promisc->uplink_rule) + mlx5_del_flow_rule(mc_promisc->uplink_rule); + esw_destroy_fdb_table(esw); /* VPORT 0 (PF) must be enabled back with non-sriov configuration */ @@ -845,7 +1612,8 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) int mlx5_eswitch_init(struct mlx5_core_dev *dev) { int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); - int total_vports = 1 + pci_sriov_get_totalvfs(dev->pdev); + int total_vports = MLX5_TOTAL_VPORTS(dev); + struct esw_mc_addr *mc_promisc; struct mlx5_eswitch *esw; int vport_num; int err; @@ -874,6 +1642,13 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) } esw->l2_table.size = l2_table_size; + mc_promisc = kzalloc(sizeof(*mc_promisc), GFP_KERNEL); + if (!mc_promisc) { + err = -ENOMEM; + goto abort; + } + esw->mc_promisc = mc_promisc; + esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); if (!esw->work_queue) { err = -ENOMEM; @@ -887,6 +1662,8 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) goto abort; } + mutex_init(&esw->state_lock); + for (vport_num = 0; vport_num < total_vports; vport_num++) { struct mlx5_vport *vport = &esw->vports[vport_num]; @@ -894,7 +1671,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) vport->dev = dev; INIT_WORK(&vport->vport_change_handler, esw_vport_change_handler); - spin_lock_init(&vport->lock); } esw->total_vports = total_vports; @@ -925,6 +1701,7 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw->dev->priv.eswitch = NULL; destroy_workqueue(esw->work_queue); kfree(esw->l2_table.bitmap); + kfree(esw->mc_promisc); kfree(esw->vports); kfree(esw); } @@ -942,10 +1719,8 @@ void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) } vport = &esw->vports[vport_num]; - spin_lock(&vport->lock); if (vport->enabled) queue_work(esw->work_queue, &vport->vport_change_handler); - spin_unlock(&vport->lock); } /* Vport Administration */ @@ -957,12 +1732,22 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, int vport, u8 mac[ETH_ALEN]) { int err = 0; + struct mlx5_vport *evport; if (!ESW_ALLOWED(esw)) return -EPERM; if (!LEGAL_VPORT(esw, vport)) return -EINVAL; + evport = &esw->vports[vport]; + + if (evport->spoofchk && !is_valid_ether_addr(mac)) { + mlx5_core_warn(esw->dev, + "MAC invalidation is not allowed when spoofchk is on, vport(%d)\n", + vport); + return -EPERM; + } + err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac); if (err) { mlx5_core_warn(esw->dev, @@ -971,6 +1756,11 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, return err; } + mutex_lock(&esw->state_lock); + if (evport->enabled) + err = esw_vport_ingress_config(esw, evport); + mutex_unlock(&esw->state_lock); + return err; } @@ -990,6 +1780,7 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi) { + struct mlx5_vport *evport; u16 vlan; u8 qos; @@ -998,6 +1789,8 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, if (!LEGAL_VPORT(esw, vport)) return -EINVAL; + evport = &esw->vports[vport]; + memset(ivi, 0, sizeof(*ivi)); ivi->vf = vport - 1; @@ -1008,7 +1801,7 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos); ivi->vlan = vlan; ivi->qos = qos; - ivi->spoofchk = 0; + ivi->spoofchk = evport->spoofchk; return 0; } @@ -1016,6 +1809,8 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, int vport, u16 vlan, u8 qos) { + struct mlx5_vport *evport; + int err = 0; int set = 0; if (!ESW_ALLOWED(esw)) @@ -1026,7 +1821,72 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, if (vlan || qos) set = 1; - return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set); + evport = &esw->vports[vport]; + + err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set); + if (err) + return err; + + mutex_lock(&esw->state_lock); + evport->vlan = vlan; + evport->qos = qos; + if (evport->enabled) { + err = esw_vport_ingress_config(esw, evport); + if (err) + goto out; + err = esw_vport_egress_config(esw, evport); + } + +out: + mutex_unlock(&esw->state_lock); + return err; +} + +int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw, + int vport, bool spoofchk) +{ + struct mlx5_vport *evport; + bool pschk; + int err = 0; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + evport = &esw->vports[vport]; + + mutex_lock(&esw->state_lock); + pschk = evport->spoofchk; + evport->spoofchk = spoofchk; + if (evport->enabled) + err = esw_vport_ingress_config(esw, evport); + if (err) + evport->spoofchk = pschk; + mutex_unlock(&esw->state_lock); + + return err; +} + +int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw, + int vport, bool setting) +{ + struct mlx5_vport *evport; + + if (!ESW_ALLOWED(esw)) + return -EPERM; + if (!LEGAL_VPORT(esw, vport)) + return -EINVAL; + + evport = &esw->vports[vport]; + + mutex_lock(&esw->state_lock); + evport->trusted = setting; + if (evport->enabled) + esw_vport_change_handle_locked(evport); + mutex_unlock(&esw->state_lock); + + return 0; } int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 3416a428f70f..fd6800256d4a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -88,18 +88,40 @@ struct l2addr_node { kfree(ptr); \ }) +struct vport_ingress { + struct mlx5_flow_table *acl; + struct mlx5_flow_group *allow_untagged_spoofchk_grp; + struct mlx5_flow_group *allow_spoofchk_only_grp; + struct mlx5_flow_group *allow_untagged_only_grp; + struct mlx5_flow_group *drop_grp; + struct mlx5_flow_rule *allow_rule; + struct mlx5_flow_rule *drop_rule; +}; + +struct vport_egress { + struct mlx5_flow_table *acl; + struct mlx5_flow_group *allowed_vlans_grp; + struct mlx5_flow_group *drop_grp; + struct mlx5_flow_rule *allowed_vlan; + struct mlx5_flow_rule *drop_rule; +}; + struct mlx5_vport { struct mlx5_core_dev *dev; int vport; struct hlist_head uc_list[MLX5_L2_ADDR_HASH_SIZE]; struct hlist_head mc_list[MLX5_L2_ADDR_HASH_SIZE]; + struct mlx5_flow_rule *promisc_rule; + struct mlx5_flow_rule *allmulti_rule; struct work_struct vport_change_handler; - /* This spinlock protects access to vport data, between - * "esw_vport_disable" and ongoing interrupt "mlx5_eswitch_vport_event" - * once vport marked as disabled new interrupts are discarded. - */ - spinlock_t lock; /* vport events sync */ + struct vport_ingress ingress; + struct vport_egress egress; + + u16 vlan; + u8 qos; + bool spoofchk; + bool trusted; bool enabled; u16 enabled_events; }; @@ -113,6 +135,8 @@ struct mlx5_l2_table { struct mlx5_eswitch_fdb { void *fdb; struct mlx5_flow_group *addr_grp; + struct mlx5_flow_group *allmulti_grp; + struct mlx5_flow_group *promisc_grp; }; struct mlx5_eswitch { @@ -124,6 +148,11 @@ struct mlx5_eswitch { struct mlx5_vport *vports; int total_vports; int enabled_vports; + /* Synchronize between vport change events + * and async SRIOV admin state changes + */ + struct mutex state_lock; + struct esw_mc_addr *mc_promisc; }; /* E-Switch API */ @@ -138,6 +167,10 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw, int vport, int link_state); int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, int vport, u16 vlan, u8 qos); +int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw, + int vport, bool spoofchk); +int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw, + int vport_num, bool setting); int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw, int vport, struct ifla_vf_info *ivi); int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index f46f1db0fc00..a5bb6b695242 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -50,6 +50,10 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, MLX5_CMD_OP_SET_FLOW_TABLE_ROOT); MLX5_SET(set_flow_table_root_in, in, table_type, ft->type); MLX5_SET(set_flow_table_root_in, in, table_id, ft->id); + if (ft->vport) { + MLX5_SET(set_flow_table_root_in, in, vport_number, ft->vport); + MLX5_SET(set_flow_table_root_in, in, other_vport, 1); + } memset(out, 0, sizeof(out)); return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, @@ -57,6 +61,7 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, } int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, + u16 vport, enum fs_flow_table_type type, unsigned int level, unsigned int log_size, struct mlx5_flow_table *next_ft, unsigned int *table_id) @@ -77,6 +82,10 @@ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, MLX5_SET(create_flow_table_in, in, table_type, type); MLX5_SET(create_flow_table_in, in, level, level); MLX5_SET(create_flow_table_in, in, log_size, log_size); + if (vport) { + MLX5_SET(create_flow_table_in, in, vport_number, vport); + MLX5_SET(create_flow_table_in, in, other_vport, 1); + } memset(out, 0, sizeof(out)); err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, @@ -101,6 +110,10 @@ int mlx5_cmd_destroy_flow_table(struct mlx5_core_dev *dev, MLX5_CMD_OP_DESTROY_FLOW_TABLE); MLX5_SET(destroy_flow_table_in, in, table_type, ft->type); MLX5_SET(destroy_flow_table_in, in, table_id, ft->id); + if (ft->vport) { + MLX5_SET(destroy_flow_table_in, in, vport_number, ft->vport); + MLX5_SET(destroy_flow_table_in, in, other_vport, 1); + } return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out)); @@ -120,6 +133,10 @@ int mlx5_cmd_modify_flow_table(struct mlx5_core_dev *dev, MLX5_CMD_OP_MODIFY_FLOW_TABLE); MLX5_SET(modify_flow_table_in, in, table_type, ft->type); MLX5_SET(modify_flow_table_in, in, table_id, ft->id); + if (ft->vport) { + MLX5_SET(modify_flow_table_in, in, vport_number, ft->vport); + MLX5_SET(modify_flow_table_in, in, other_vport, 1); + } MLX5_SET(modify_flow_table_in, in, modify_field_select, MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID); if (next_ft) { @@ -148,6 +165,10 @@ int mlx5_cmd_create_flow_group(struct mlx5_core_dev *dev, MLX5_CMD_OP_CREATE_FLOW_GROUP); MLX5_SET(create_flow_group_in, in, table_type, ft->type); MLX5_SET(create_flow_group_in, in, table_id, ft->id); + if (ft->vport) { + MLX5_SET(create_flow_group_in, in, vport_number, ft->vport); + MLX5_SET(create_flow_group_in, in, other_vport, 1); + } err = mlx5_cmd_exec_check_status(dev, in, inlen, out, @@ -174,6 +195,10 @@ int mlx5_cmd_destroy_flow_group(struct mlx5_core_dev *dev, MLX5_SET(destroy_flow_group_in, in, table_type, ft->type); MLX5_SET(destroy_flow_group_in, in, table_id, ft->id); MLX5_SET(destroy_flow_group_in, in, group_id, group_id); + if (ft->vport) { + MLX5_SET(destroy_flow_group_in, in, vport_number, ft->vport); + MLX5_SET(destroy_flow_group_in, in, other_vport, 1); + } return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out)); @@ -207,22 +232,29 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(set_fte_in, in, table_type, ft->type); MLX5_SET(set_fte_in, in, table_id, ft->id); MLX5_SET(set_fte_in, in, flow_index, fte->index); + if (ft->vport) { + MLX5_SET(set_fte_in, in, vport_number, ft->vport); + MLX5_SET(set_fte_in, in, other_vport, 1); + } in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context); MLX5_SET(flow_context, in_flow_context, group_id, group_id); MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag); MLX5_SET(flow_context, in_flow_context, action, fte->action); - MLX5_SET(flow_context, in_flow_context, destination_list_size, - fte->dests_size); in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, match_value); memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); + in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); if (fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { - in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); + int list_size = 0; + list_for_each_entry(dst, &fte->node.children, node.list) { unsigned int id; + if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) + continue; + MLX5_SET(dest_format_struct, in_dests, destination_type, dst->dest_attr.type); if (dst->dest_attr.type == @@ -233,8 +265,31 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, } MLX5_SET(dest_format_struct, in_dests, destination_id, id); in_dests += MLX5_ST_SZ_BYTES(dest_format_struct); + list_size++; } + + MLX5_SET(flow_context, in_flow_context, destination_list_size, + list_size); } + + if (fte->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + int list_size = 0; + + list_for_each_entry(dst, &fte->node.children, node.list) { + if (dst->dest_attr.type != + MLX5_FLOW_DESTINATION_TYPE_COUNTER) + continue; + + MLX5_SET(flow_counter_list, in_dests, flow_counter_id, + dst->dest_attr.counter->id); + in_dests += MLX5_ST_SZ_BYTES(dest_format_struct); + list_size++; + } + + MLX5_SET(flow_context, in_flow_context, flow_counter_list_size, + list_size); + } + memset(out, 0, sizeof(out)); err = mlx5_cmd_exec_check_status(dev, in, inlen, out, sizeof(out)); @@ -254,18 +309,16 @@ int mlx5_cmd_create_fte(struct mlx5_core_dev *dev, int mlx5_cmd_update_fte(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, unsigned group_id, + int modify_mask, struct fs_fte *fte) { int opmod; - int modify_mask; int atomic_mod_cap = MLX5_CAP_FLOWTABLE(dev, flow_table_properties_nic_receive. flow_modify_en); if (!atomic_mod_cap) return -ENOTSUPP; opmod = 1; - modify_mask = 1 << - MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST; return mlx5_cmd_set_fte(dev, opmod, modify_mask, ft, group_id, fte); } @@ -285,8 +338,78 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, MLX5_SET(delete_fte_in, in, table_type, ft->type); MLX5_SET(delete_fte_in, in, table_id, ft->id); MLX5_SET(delete_fte_in, in, flow_index, index); + if (ft->vport) { + MLX5_SET(delete_fte_in, in, vport_number, ft->vport); + MLX5_SET(delete_fte_in, in, other_vport, 1); + } err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out)); return err; } + +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id) +{ + u32 in[MLX5_ST_SZ_DW(alloc_flow_counter_in)]; + u32 out[MLX5_ST_SZ_DW(alloc_flow_counter_out)]; + int err; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(alloc_flow_counter_in, in, opcode, + MLX5_CMD_OP_ALLOC_FLOW_COUNTER); + + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); + if (err) + return err; + + *id = MLX5_GET(alloc_flow_counter_out, out, flow_counter_id); + + return 0; +} + +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id) +{ + u32 in[MLX5_ST_SZ_DW(dealloc_flow_counter_in)]; + u32 out[MLX5_ST_SZ_DW(dealloc_flow_counter_out)]; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(dealloc_flow_counter_in, in, opcode, + MLX5_CMD_OP_DEALLOC_FLOW_COUNTER); + MLX5_SET(dealloc_flow_counter_in, in, flow_counter_id, id); + + return mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, + sizeof(out)); +} + +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, + u64 *packets, u64 *bytes) +{ + u32 out[MLX5_ST_SZ_BYTES(query_flow_counter_out) + + MLX5_ST_SZ_BYTES(traffic_counter)]; + u32 in[MLX5_ST_SZ_DW(query_flow_counter_in)]; + void *stats; + int err = 0; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + MLX5_SET(query_flow_counter_in, in, opcode, + MLX5_CMD_OP_QUERY_FLOW_COUNTER); + MLX5_SET(query_flow_counter_in, in, op_mod, 0); + MLX5_SET(query_flow_counter_in, in, flow_counter_id, id); + + err = mlx5_cmd_exec_check_status(dev, in, sizeof(in), out, sizeof(out)); + if (err) + return err; + + stats = MLX5_ADDR_OF(query_flow_counter_out, out, flow_statistics); + *packets = MLX5_GET64(traffic_counter, stats, packets); + *bytes = MLX5_GET64(traffic_counter, stats, octets); + + return 0; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 9814d4784803..fc4f7b83fe0a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -34,6 +34,7 @@ #define _MLX5_FS_CMD_ int mlx5_cmd_create_flow_table(struct mlx5_core_dev *dev, + u16 vport, enum fs_flow_table_type type, unsigned int level, unsigned int log_size, struct mlx5_flow_table *next_ft, unsigned int *table_id); @@ -61,6 +62,7 @@ int mlx5_cmd_create_fte(struct mlx5_core_dev *dev, int mlx5_cmd_update_fte(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, unsigned group_id, + int modify_mask, struct fs_fte *fte); int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, @@ -69,4 +71,9 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft); + +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id); +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id); +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, + u64 *packets, u64 *bytes); #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 4d78d5a48af3..8b5f0b2c0d5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -344,6 +344,7 @@ static void del_rule(struct fs_node *node) struct mlx5_flow_group *fg; struct fs_fte *fte; u32 *match_value; + int modify_mask; struct mlx5_core_dev *dev = get_dev(node); int match_len = MLX5_ST_SZ_BYTES(fte_match_param); int err; @@ -367,8 +368,11 @@ static void del_rule(struct fs_node *node) } if ((fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) && --fte->dests_size) { + modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST), err = mlx5_cmd_update_fte(dev, ft, - fg->id, fte); + fg->id, + modify_mask, + fte); if (err) pr_warn("%s can't del rule fg id=%d fte_index=%d\n", __func__, fg->id, fte->index); @@ -457,7 +461,7 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in) return fg; } -static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte, +static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_fte, enum fs_flow_table_type table_type) { struct mlx5_flow_table *ft; @@ -469,6 +473,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, int max_fte, ft->level = level; ft->node.type = FS_TYPE_FLOW_TABLE; ft->type = table_type; + ft->vport = vport; ft->max_fte = max_fte; INIT_LIST_HEAD(&ft->fwd_rules); mutex_init(&ft->lock); @@ -614,6 +619,7 @@ int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; struct fs_fte *fte; + int modify_mask = BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); int err = 0; fs_get_obj(fte, rule->node.parent); @@ -625,7 +631,9 @@ int mlx5_modify_rule_destination(struct mlx5_flow_rule *rule, memcpy(&rule->dest_attr, dest, sizeof(*dest)); err = mlx5_cmd_update_fte(get_dev(&ft->node), - ft, fg->id, fte); + ft, fg->id, + modify_mask, + fte); unlock_ref_node(&fte->node); return err; @@ -700,9 +708,9 @@ static void list_add_flow_table(struct mlx5_flow_table *ft, list_add(&ft->node.list, prev); } -struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, - int prio, int max_fte, - u32 level) +static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + u16 vport, int prio, + int max_fte, u32 level) { struct mlx5_flow_table *next_ft = NULL; struct mlx5_flow_table *ft; @@ -732,6 +740,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, */ level += fs_prio->start_level; ft = alloc_flow_table(level, + vport, roundup_pow_of_two(max_fte), root->table_type); if (!ft) { @@ -742,7 +751,7 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, tree_init_node(&ft->node, 1, del_flow_table); log_table_sz = ilog2(ft->max_fte); next_ft = find_next_chained_ft(fs_prio); - err = mlx5_cmd_create_flow_table(root->dev, ft->type, ft->level, + err = mlx5_cmd_create_flow_table(root->dev, ft->vport, ft->type, ft->level, log_table_sz, next_ft, &ft->id); if (err) goto free_ft; @@ -766,6 +775,20 @@ unlock_root: return ERR_PTR(err); } +struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + int prio, int max_fte, + u32 level) +{ + return __mlx5_create_flow_table(ns, 0, prio, max_fte, level); +} + +struct mlx5_flow_table *mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, + int prio, int max_fte, + u32 level, u16 vport) +{ + return __mlx5_create_flow_table(ns, vport, prio, max_fte, level); +} + struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, int prio, int num_flow_table_entries, @@ -861,6 +884,7 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, { struct mlx5_flow_table *ft; struct mlx5_flow_rule *rule; + int modify_mask = 0; int err; rule = alloc_rule(dest); @@ -876,14 +900,20 @@ static struct mlx5_flow_rule *add_rule_fte(struct fs_fte *fte, list_add(&rule->node.list, &fte->node.children); else list_add_tail(&rule->node.list, &fte->node.children); - if (dest) + if (dest) { fte->dests_size++; + + modify_mask |= dest->type == MLX5_FLOW_DESTINATION_TYPE_COUNTER ? + BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS) : + BIT(MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST); + } + if (fte->dests_size == 1 || !dest) err = mlx5_cmd_create_fte(get_dev(&ft->node), ft, fg->id, fte); else err = mlx5_cmd_update_fte(get_dev(&ft->node), - ft, fg->id, fte); + ft, fg->id, modify_mask, fte); if (err) goto free_rule; @@ -1076,10 +1106,40 @@ unlock_fg: return rule; } +struct mlx5_fc *mlx5_flow_rule_counter(struct mlx5_flow_rule *rule) +{ + struct mlx5_flow_rule *dst; + struct fs_fte *fte; + + fs_get_obj(fte, rule->node.parent); + + fs_for_each_dst(dst, fte) { + if (dst->dest_attr.type == MLX5_FLOW_DESTINATION_TYPE_COUNTER) + return dst->dest_attr.counter; + } + + return NULL; +} + +static bool counter_is_valid(struct mlx5_fc *counter, u32 action) +{ + if (!(action & MLX5_FLOW_CONTEXT_ACTION_COUNT)) + return !counter; + + if (!counter) + return false; + + /* Hardware support counter for a drop action only */ + return action == (MLX5_FLOW_CONTEXT_ACTION_DROP | MLX5_FLOW_CONTEXT_ACTION_COUNT); +} + static bool dest_is_valid(struct mlx5_flow_destination *dest, u32 action, struct mlx5_flow_table *ft) { + if (dest && (dest->type == MLX5_FLOW_DESTINATION_TYPE_COUNTER)) + return counter_is_valid(dest->counter, action); + if (!(action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST)) return true; @@ -1319,6 +1379,16 @@ struct mlx5_flow_namespace *mlx5_get_flow_namespace(struct mlx5_core_dev *dev, return &dev->priv.fdb_root_ns->ns; else return NULL; + case MLX5_FLOW_NAMESPACE_ESW_EGRESS: + if (dev->priv.esw_egress_root_ns) + return &dev->priv.esw_egress_root_ns->ns; + else + return NULL; + case MLX5_FLOW_NAMESPACE_ESW_INGRESS: + if (dev->priv.esw_ingress_root_ns) + return &dev->priv.esw_ingress_root_ns->ns; + else + return NULL; default: return NULL; } @@ -1699,6 +1769,9 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev) { cleanup_root_ns(dev); cleanup_single_prio_root_ns(dev, dev->priv.fdb_root_ns); + cleanup_single_prio_root_ns(dev, dev->priv.esw_egress_root_ns); + cleanup_single_prio_root_ns(dev, dev->priv.esw_ingress_root_ns); + mlx5_cleanup_fc_stats(dev); } static int init_fdb_root_ns(struct mlx5_core_dev *dev) @@ -1719,20 +1792,69 @@ static int init_fdb_root_ns(struct mlx5_core_dev *dev) } } +static int init_egress_acl_root_ns(struct mlx5_core_dev *dev) +{ + struct fs_prio *prio; + + dev->priv.esw_egress_root_ns = create_root_ns(dev, FS_FT_ESW_EGRESS_ACL); + if (!dev->priv.esw_egress_root_ns) + return -ENOMEM; + + /* create 1 prio*/ + prio = fs_create_prio(&dev->priv.esw_egress_root_ns->ns, 0, MLX5_TOTAL_VPORTS(dev)); + if (IS_ERR(prio)) + return PTR_ERR(prio); + else + return 0; +} + +static int init_ingress_acl_root_ns(struct mlx5_core_dev *dev) +{ + struct fs_prio *prio; + + dev->priv.esw_ingress_root_ns = create_root_ns(dev, FS_FT_ESW_INGRESS_ACL); + if (!dev->priv.esw_ingress_root_ns) + return -ENOMEM; + + /* create 1 prio*/ + prio = fs_create_prio(&dev->priv.esw_ingress_root_ns->ns, 0, MLX5_TOTAL_VPORTS(dev)); + if (IS_ERR(prio)) + return PTR_ERR(prio); + else + return 0; +} + int mlx5_init_fs(struct mlx5_core_dev *dev) { int err = 0; + err = mlx5_init_fc_stats(dev); + if (err) + return err; + if (MLX5_CAP_GEN(dev, nic_flow_table)) { err = init_root_ns(dev); if (err) - return err; + goto err; } if (MLX5_CAP_GEN(dev, eswitch_flow_table)) { err = init_fdb_root_ns(dev); if (err) - cleanup_root_ns(dev); + goto err; + } + if (MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support)) { + err = init_egress_acl_root_ns(dev); + if (err) + goto err; + } + if (MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support)) { + err = init_ingress_acl_root_ns(dev); + if (err) + goto err; } + return 0; +err: + mlx5_cleanup_fs(dev); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index d607e564f454..aa41a7314691 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -45,8 +45,10 @@ enum fs_node_type { }; enum fs_flow_table_type { - FS_FT_NIC_RX = 0x0, - FS_FT_FDB = 0X4, + FS_FT_NIC_RX = 0x0, + FS_FT_ESW_EGRESS_ACL = 0x2, + FS_FT_ESW_INGRESS_ACL = 0x3, + FS_FT_FDB = 0X4, }; enum fs_fte_status { @@ -79,6 +81,7 @@ struct mlx5_flow_rule { struct mlx5_flow_table { struct fs_node node; u32 id; + u16 vport; unsigned int max_fte; unsigned int level; enum fs_flow_table_type type; @@ -93,6 +96,28 @@ struct mlx5_flow_table { struct list_head fwd_rules; }; +struct mlx5_fc_cache { + u64 packets; + u64 bytes; + u64 lastuse; +}; + +struct mlx5_fc { + struct list_head list; + + /* last{packets,bytes} members are used when calculating the delta since + * last reading + */ + u64 lastpackets; + u64 lastbytes; + + u16 id; + bool deleted; + bool aging; + + struct mlx5_fc_cache cache ____cacheline_aligned_in_smp; +}; + /* Type of children is mlx5_flow_rule */ struct fs_fte { struct fs_node node; @@ -102,6 +127,7 @@ struct fs_fte { u32 index; u32 action; enum fs_fte_status status; + struct mlx5_fc *counter; }; /* Type of children is mlx5_flow_table/namespace */ @@ -143,6 +169,9 @@ struct mlx5_flow_root_namespace { struct mutex chain_lock; }; +int mlx5_init_fc_stats(struct mlx5_core_dev *dev); +void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev); + int mlx5_init_fs(struct mlx5_core_dev *dev); void mlx5_cleanup_fs(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c new file mode 100644 index 000000000000..164dc37fda72 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2016, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/mlx5/driver.h> +#include <linux/mlx5/fs.h> +#include "mlx5_core.h" +#include "fs_core.h" +#include "fs_cmd.h" + +#define MLX5_FC_STATS_PERIOD msecs_to_jiffies(1000) + +/* locking scheme: + * + * It is the responsibility of the user to prevent concurrent calls or bad + * ordering to mlx5_fc_create(), mlx5_fc_destroy() and accessing a reference + * to struct mlx5_fc. + * e.g en_tc.c is protected by RTNL lock of its caller, and will never call a + * dump (access to struct mlx5_fc) after a counter is destroyed. + * + * access to counter list: + * - create (user context) + * - mlx5_fc_create() only adds to an addlist to be used by + * mlx5_fc_stats_query_work(). addlist is protected by a spinlock. + * - spawn thread to do the actual destroy + * + * - destroy (user context) + * - mark a counter as deleted + * - spawn thread to do the actual del + * + * - dump (user context) + * user should not call dump after destroy + * + * - query (single thread workqueue context) + * destroy/dump - no conflict (see destroy) + * query/dump - packets and bytes might be inconsistent (since update is not + * atomic) + * query/create - no conflict (see create) + * since every create/destroy spawn the work, only after necessary time has + * elapsed, the thread will actually query the hardware. + */ + +static void mlx5_fc_stats_work(struct work_struct *work) +{ + struct mlx5_core_dev *dev = container_of(work, struct mlx5_core_dev, + priv.fc_stats.work.work); + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + unsigned long now = jiffies; + struct mlx5_fc *counter; + struct mlx5_fc *tmp; + int err = 0; + + spin_lock(&fc_stats->addlist_lock); + + list_splice_tail_init(&fc_stats->addlist, &fc_stats->list); + + if (!list_empty(&fc_stats->list)) + queue_delayed_work(fc_stats->wq, &fc_stats->work, MLX5_FC_STATS_PERIOD); + + spin_unlock(&fc_stats->addlist_lock); + + list_for_each_entry_safe(counter, tmp, &fc_stats->list, list) { + struct mlx5_fc_cache *c = &counter->cache; + u64 packets; + u64 bytes; + + if (counter->deleted) { + list_del(&counter->list); + + mlx5_cmd_fc_free(dev, counter->id); + + kfree(counter); + continue; + } + + if (time_before(now, fc_stats->next_query)) + continue; + + err = mlx5_cmd_fc_query(dev, counter->id, &packets, &bytes); + if (err) { + pr_err("Error querying stats for counter id %d\n", + counter->id); + continue; + } + + if (packets == c->packets) + continue; + + c->lastuse = jiffies; + c->packets = packets; + c->bytes = bytes; + } + + if (time_after_eq(now, fc_stats->next_query)) + fc_stats->next_query = now + MLX5_FC_STATS_PERIOD; +} + +struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + struct mlx5_fc *counter; + int err; + + counter = kzalloc(sizeof(*counter), GFP_KERNEL); + if (!counter) + return ERR_PTR(-ENOMEM); + + err = mlx5_cmd_fc_alloc(dev, &counter->id); + if (err) + goto err_out; + + if (aging) { + counter->aging = true; + + spin_lock(&fc_stats->addlist_lock); + list_add(&counter->list, &fc_stats->addlist); + spin_unlock(&fc_stats->addlist_lock); + + mod_delayed_work(fc_stats->wq, &fc_stats->work, 0); + } + + return counter; + +err_out: + kfree(counter); + + return ERR_PTR(err); +} + +void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + + if (!counter) + return; + + if (counter->aging) { + counter->deleted = true; + mod_delayed_work(fc_stats->wq, &fc_stats->work, 0); + return; + } + + mlx5_cmd_fc_free(dev, counter->id); + kfree(counter); +} + +int mlx5_init_fc_stats(struct mlx5_core_dev *dev) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + + INIT_LIST_HEAD(&fc_stats->list); + INIT_LIST_HEAD(&fc_stats->addlist); + spin_lock_init(&fc_stats->addlist_lock); + + fc_stats->wq = create_singlethread_workqueue("mlx5_fc"); + if (!fc_stats->wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&fc_stats->work, mlx5_fc_stats_work); + + return 0; +} + +void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + struct mlx5_fc *counter; + struct mlx5_fc *tmp; + + cancel_delayed_work_sync(&dev->priv.fc_stats.work); + destroy_workqueue(dev->priv.fc_stats.wq); + dev->priv.fc_stats.wq = NULL; + + list_splice_tail_init(&fc_stats->addlist, &fc_stats->list); + + list_for_each_entry_safe(counter, tmp, &fc_stats->list, list) { + list_del(&counter->list); + + mlx5_cmd_fc_free(dev, counter->id); + + kfree(counter); + } +} + +void mlx5_fc_query_cached(struct mlx5_fc *counter, + u64 *bytes, u64 *packets, u64 *lastuse) +{ + struct mlx5_fc_cache c; + + c = counter->cache; + + *bytes = c.bytes - counter->lastbytes; + *packets = c.packets - counter->lastpackets; + *lastuse = c.lastuse; + + counter->lastbytes = c.bytes; + counter->lastpackets = c.packets; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c index f5deb642d0d6..42d16b9458e4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/health.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c @@ -176,11 +176,11 @@ static const char *hsynd_str(u8 synd) case MLX5_HEALTH_SYNDR_EQ_ERR: return "EQ error"; case MLX5_HEALTH_SYNDR_EQ_INV: - return "Invalid EQ refrenced"; + return "Invalid EQ referenced"; case MLX5_HEALTH_SYNDR_FFSER_ERR: return "FFSER error"; case MLX5_HEALTH_SYNDR_HIGH_TEMP: - return "High temprature"; + return "High temperature"; default: return "unrecognized error"; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 0b0b226c789e..482604bd051c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -42,6 +42,8 @@ #define DRIVER_VERSION "3.0-1" #define DRIVER_RELDATE "January 2015" +#define MLX5_TOTAL_VPORTS(mdev) (1 + pci_sriov_get_totalvfs(mdev->pdev)) + extern int mlx5_core_debug_mask; #define mlx5_core_dbg(__dev, format, ...) \ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 7b24386794f9..d6a3f412ba9f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -140,7 +140,7 @@ int mlx5_core_sriov_configure(struct pci_dev *pdev, int num_vfs) struct mlx5_core_sriov *sriov = &dev->priv.sriov; int err; - mlx5_core_dbg(dev, "requsted num_vfs %d\n", num_vfs); + mlx5_core_dbg(dev, "requested num_vfs %d\n", num_vfs); if (!mlx5_core_is_pf(dev)) return -EPERM; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/uar.c b/drivers/net/ethernet/mellanox/mlx5/core/uar.c index 8ba080e441a1..5ff8af472bf5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/uar.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/uar.c @@ -269,8 +269,10 @@ EXPORT_SYMBOL(mlx5_alloc_map_uar); void mlx5_unmap_free_uar(struct mlx5_core_dev *mdev, struct mlx5_uar *uar) { - iounmap(uar->map); - iounmap(uar->bf_map); + if (uar->map) + iounmap(uar->map); + else + iounmap(uar->bf_map); mlx5_cmd_free_uar(mdev, uar->index); } EXPORT_SYMBOL(mlx5_unmap_free_uar); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c index 9f10df25f3cd..f2fd1ef16da7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c @@ -95,21 +95,22 @@ struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port) return vxlan; } -int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port) +static void mlx5e_vxlan_add_port(struct work_struct *work) { + struct mlx5e_vxlan_work *vxlan_work = + container_of(work, struct mlx5e_vxlan_work, work); + struct mlx5e_priv *priv = vxlan_work->priv; struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan; + u16 port = vxlan_work->port; struct mlx5e_vxlan *vxlan; int err; - err = mlx5e_vxlan_core_add_port_cmd(priv->mdev, port); - if (err) - return err; + if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port)) + goto free_work; vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL); - if (!vxlan) { - err = -ENOMEM; + if (!vxlan) goto err_delete_port; - } vxlan->udp_port = port; @@ -119,13 +120,14 @@ int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port) if (err) goto err_free; - return 0; + goto free_work; err_free: kfree(vxlan); err_delete_port: mlx5e_vxlan_core_del_port_cmd(priv->mdev, port); - return err; +free_work: + kfree(vxlan_work); } static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) @@ -145,12 +147,36 @@ static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port) kfree(vxlan); } -void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port) +static void mlx5e_vxlan_del_port(struct work_struct *work) { - if (!mlx5e_vxlan_lookup_port(priv, port)) - return; + struct mlx5e_vxlan_work *vxlan_work = + container_of(work, struct mlx5e_vxlan_work, work); + struct mlx5e_priv *priv = vxlan_work->priv; + u16 port = vxlan_work->port; __mlx5e_vxlan_core_del_port(priv, port); + + kfree(vxlan_work); +} + +void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family, + u16 port, int add) +{ + struct mlx5e_vxlan_work *vxlan_work; + + vxlan_work = kmalloc(sizeof(*vxlan_work), GFP_ATOMIC); + if (!vxlan_work) + return; + + if (add) + INIT_WORK(&vxlan_work->work, mlx5e_vxlan_add_port); + else + INIT_WORK(&vxlan_work->work, mlx5e_vxlan_del_port); + + vxlan_work->priv = priv; + vxlan_work->port = port; + vxlan_work->sa_family = sa_family; + queue_work(priv->wq, &vxlan_work->work); } void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h index a01685056ab1..5def12c048e3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h @@ -39,6 +39,13 @@ struct mlx5e_vxlan { u16 udp_port; }; +struct mlx5e_vxlan_work { + struct work_struct work; + struct mlx5e_priv *priv; + sa_family_t sa_family; + u16 port; +}; + static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev) { return (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) && @@ -46,9 +53,10 @@ static inline bool mlx5e_vxlan_allowed(struct mlx5_core_dev *mdev) } void mlx5e_vxlan_init(struct mlx5e_priv *priv); -int mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port); -void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port); -struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port); void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv); +void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family, + u16 port, int add); +struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port); + #endif /* __MLX5_VXLAN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 681afe1a3802..4a7273771028 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2449,8 +2449,8 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); - mlxsw_sp_buffers_fini(mlxsw_sp); mlxsw_sp_switchdev_fini(mlxsw_sp); + mlxsw_sp_buffers_fini(mlxsw_sp); mlxsw_sp_traps_fini(mlxsw_sp); mlxsw_sp_event_unregister(mlxsw_sp, MLXSW_TRAP_ID_PUDE); mlxsw_sp_ports_remove(mlxsw_sp); @@ -2843,11 +2843,11 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, lag->ref_count++; return 0; +err_col_port_enable: + mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); err_col_port_add: if (!lag->ref_count) mlxsw_sp_lag_destroy(mlxsw_sp, lag_id); -err_col_port_enable: - mlxsw_sp_lag_col_port_remove(mlxsw_sp_port, lag_id); return err; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index fb9efb84f13b..3710f19ed6bb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -214,7 +214,15 @@ static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, idx_begin, table_type, range, local_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + if (err) + goto err_flood_bm_set; + else + goto buffer_out; +err_flood_bm_set: + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, idx_begin, + table_type, range, local_port, !set); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); buffer_out: kfree(sftr_pl); return err; diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c index a8522d8af95d..20cb85bc0c5f 100644 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ b/drivers/net/ethernet/micrel/ks8695net.c @@ -1354,6 +1354,7 @@ ks8695_probe(struct platform_device *pdev) struct resource *rxirq_res, *txirq_res, *linkirq_res; int ret = 0; int buff_n; + bool inv_mac_addr = false; u32 machigh, maclow; /* Initialise a net_device */ @@ -1456,8 +1457,7 @@ ks8695_probe(struct platform_device *pdev) ndev->dev_addr[5] = maclow & 0xFF; if (!is_valid_ether_addr(ndev->dev_addr)) - dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", ndev->name); + inv_mac_addr = true; /* In order to be efficient memory-wise, we allocate both * rings in one go. @@ -1520,6 +1520,9 @@ ks8695_probe(struct platform_device *pdev) ret = register_netdev(ndev); if (ret == 0) { + if (inv_mac_addr) + dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please set using ip\n", + ndev->name); dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n", ks8695_port_type(ksp), ndev->dev_addr); } else { diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 75dc46c5fca2..280e761d3a97 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4790,7 +4790,7 @@ static void transmit_cleanup(struct dev_info *hw_priv, int normal) /* Notify the network subsystem that the packet has been sent. */ if (dev) - dev->trans_start = jiffies; + netif_trans_update(dev); } /** @@ -4965,7 +4965,7 @@ static void netdev_tx_timeout(struct net_device *dev) hw_ena_intr(hw); } - dev->trans_start = jiffies; + netif_trans_update(dev); netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/microchip/encx24j600.c b/drivers/net/ethernet/microchip/encx24j600.c index 7df318346b05..42e34076d2de 100644 --- a/drivers/net/ethernet/microchip/encx24j600.c +++ b/drivers/net/ethernet/microchip/encx24j600.c @@ -874,7 +874,7 @@ static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); /* save the timestamp */ - dev->trans_start = jiffies; + netif_trans_update(dev); /* Remember the skb for deferred processing */ priv->tx_skb = skb; @@ -890,7 +890,7 @@ static void encx24j600_tx_timeout(struct net_device *dev) struct encx24j600_priv *priv = netdev_priv(dev); netif_err(priv, tx_err, dev, "TX timeout at %ld, latency %ld\n", - jiffies, jiffies - dev->trans_start); + jiffies, jiffies - dev_trans_start(dev)); dev->stats.tx_errors++; netif_wake_queue(dev); diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 3e67f451f2ab..4367dd6879a2 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -376,7 +376,7 @@ static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) priv->tx_head = TX_NEXT(tx_head); - ndev->trans_start = jiffies; + netif_trans_update(ndev); ret = NETDEV_TX_OK; out_unlock: spin_unlock_irq(&priv->txlock); diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 270c9eeb7ab6..6d1a956e3f77 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -2668,9 +2668,9 @@ static int myri10ge_close(struct net_device *dev) del_timer_sync(&mgp->watchdog_timer); mgp->running = MYRI10GE_ETH_STOPPING; - local_bh_disable(); /* myri10ge_ss_lock_napi needs bh disabled */ for (i = 0; i < mgp->num_slices; i++) { napi_disable(&mgp->ss[i].napi); + local_bh_disable(); /* myri10ge_ss_lock_napi needs this */ /* Lock the slice to prevent the busy_poll handler from * accessing it. Later when we bring the NIC up, myri10ge_open * resets the slice including this lock. @@ -2679,8 +2679,8 @@ static int myri10ge_close(struct net_device *dev) pr_info("Slice %d locked\n", i); mdelay(1); } + local_bh_enable(); } - local_bh_enable(); netif_carrier_off(dev); netif_tx_stop_all_queues(dev); diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index 122c2ee3dfe2..ed89029ff75b 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -1904,7 +1904,7 @@ static void ns_tx_timeout(struct net_device *dev) spin_unlock_irq(&np->lock); enable_irq(irq); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c index 1bd419dbda6d..612c7a44b26c 100644 --- a/drivers/net/ethernet/natsemi/sonic.c +++ b/drivers/net/ethernet/natsemi/sonic.c @@ -174,7 +174,7 @@ static void sonic_tx_timeout(struct net_device *dev) /* Try to restart the adaptor. */ sonic_init(dev); lp->stats.tx_errors++; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/netx-eth.c b/drivers/net/ethernet/netx-eth.c index 9fbc30264237..adbc47f2d132 100644 --- a/drivers/net/ethernet/netx-eth.c +++ b/drivers/net/ethernet/netx-eth.c @@ -313,7 +313,8 @@ static int netx_eth_enable(struct net_device *ndev) { struct netx_eth_priv *priv = netdev_priv(ndev); unsigned int mac4321, mac65; - int running, i; + int running, i, ret; + bool inv_mac_addr = false; ndev->netdev_ops = &netx_eth_netdev_ops; ndev->watchdog_timeo = msecs_to_jiffies(5000); @@ -358,15 +359,18 @@ static int netx_eth_enable(struct net_device *ndev) xc_start(priv->xc); if (!is_valid_ether_addr(ndev->dev_addr)) - printk("%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", ndev->name); + inv_mac_addr = true; for (i=2; i<=18; i++) pfifo_push(EMPTY_PTR_FIFO(priv->id), FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(priv->id)); - return register_netdev(ndev); + ret = register_netdev(ndev); + if (inv_mac_addr) + printk("%s: Invalid ethernet MAC address. Please set using ip\n", + ndev->name); + return ret; } static int netx_eth_drv_probe(struct platform_device *pdev) diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index 52d9a94aebb9..87b7b814778b 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -476,7 +476,7 @@ static void w90p910_reset_mac(struct net_device *dev) w90p910_init_desc(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ ether->cur_tx = 0x0; ether->finish_tx = 0x0; ether->cur_rx = 0x0; @@ -490,7 +490,7 @@ static void w90p910_reset_mac(struct net_device *dev) w90p910_trigger_tx(dev); w90p910_trigger_rx(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ if (netif_queue_stopped(dev)) netif_wake_queue(dev); diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index 13d88a6025c8..91be2f02ef1c 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -1144,7 +1144,7 @@ static void hamachi_tx_timeout(struct net_device *dev) hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); /* Trigger an immediate transmit demand. */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; /* Restart the chip's Tx/Rx processes . */ diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index fa2db41e02f8..fb1d1031b091 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -714,7 +714,7 @@ static void yellowfin_tx_timeout(struct net_device *dev) if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) netif_wake_queue (dev); /* Typical path */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; } diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig index c0a11b5158e7..680d8c736d2b 100644 --- a/drivers/net/ethernet/qlogic/Kconfig +++ b/drivers/net/ethernet/qlogic/Kconfig @@ -98,6 +98,16 @@ config QED ---help--- This enables the support for ... +config QED_SRIOV + bool "QLogic QED 25/40/100Gb SR-IOV support" + depends on QED && PCI_IOV + default y + ---help--- + This configuration parameter enables Single Root Input Output + Virtualization support for QED devices. + This allows for virtual function acceleration in virtualized + environments. + config QEDE tristate "QLogic QED 25/40/100Gb Ethernet NIC" depends on QED diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index db80eb1c6d4f..2b10f1bcd151 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -1015,20 +1015,24 @@ static int netxen_get_flash_block(struct netxen_adapter *adapter, int base, { int i, v, addr; __le32 *ptr32; + int ret; addr = base; ptr32 = buf; for (i = 0; i < size / sizeof(u32); i++) { - if (netxen_rom_fast_read(adapter, addr, &v) == -1) - return -1; + ret = netxen_rom_fast_read(adapter, addr, &v); + if (ret) + return ret; + *ptr32 = cpu_to_le32(v); ptr32++; addr += sizeof(u32); } if ((char *)buf + size > (char *)ptr32) { __le32 local; - if (netxen_rom_fast_read(adapter, addr, &v) == -1) - return -1; + ret = netxen_rom_fast_read(adapter, addr, &v); + if (ret) + return ret; local = cpu_to_le32(v); memcpy(ptr32, &local, (char *)buf + size - (char *)ptr32); } @@ -1940,7 +1944,7 @@ void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) if (adapter->phy_read && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, - &autoneg) != 0) + &autoneg) == 0) adapter->link_autoneg = autoneg; } else goto link_down; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index fd362b6923f4..7a0281a36c28 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -852,7 +852,8 @@ netxen_check_options(struct netxen_adapter *adapter) ptr32 = (__le32 *)&serial_num; offset = NX_FW_SERIAL_NUM_OFFSET; for (i = 0; i < 8; i++) { - if (netxen_rom_fast_read(adapter, offset, &val) == -1) { + err = netxen_rom_fast_read(adapter, offset, &val); + if (err) { dev_err(&pdev->dev, "error reading board info\n"); adapter->driver_mismatch = 1; return; @@ -2285,7 +2286,7 @@ static void netxen_tx_timeout_task(struct work_struct *work) goto request_reset; } } - adapter->netdev->trans_start = jiffies; + netif_trans_update(adapter->netdev); rtnl_unlock(); return; diff --git a/drivers/net/ethernet/qlogic/qed/Makefile b/drivers/net/ethernet/qlogic/qed/Makefile index aafa6692e62f..d1f157e439cf 100644 --- a/drivers/net/ethernet/qlogic/qed/Makefile +++ b/drivers/net/ethernet/qlogic/qed/Makefile @@ -2,4 +2,5 @@ obj-$(CONFIG_QED) := qed.o qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \ qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o \ - qed_selftest.o + qed_selftest.o qed_dcbx.o +qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index cceac3272cce..1042f2af854a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -152,6 +152,7 @@ enum QED_RESOURCES { enum QED_FEATURE { QED_PF_L2_QUE, + QED_VF, QED_MAX_FEATURES, }; @@ -310,6 +311,8 @@ struct qed_hwfn { bool first_on_engine; bool hw_init_done; + u8 num_funcs_on_engine; + /* BAR access */ void __iomem *regview; void __iomem *doorbells; @@ -360,8 +363,12 @@ struct qed_hwfn { /* True if the driver requests for the link */ bool b_drv_link_init; + struct qed_vf_iov *vf_iov_info; + struct qed_pf_iov *pf_iov_info; struct qed_mcp_info *mcp_info; + struct qed_dcbx_info *p_dcbx_info; + struct qed_hw_cid_data *p_tx_cids; struct qed_hw_cid_data *p_rx_cids; @@ -376,6 +383,12 @@ struct qed_hwfn { struct qed_simd_fp_handler simd_proto_handler[64]; +#ifdef CONFIG_QED_SRIOV + struct workqueue_struct *iov_wq; + struct delayed_work iov_task; + unsigned long iov_task_flags; +#endif + struct z_stream_s *stream; }; @@ -484,7 +497,13 @@ struct qed_dev { u8 num_hwfns; struct qed_hwfn hwfns[MAX_HWFNS_PER_DEVICE]; + /* SRIOV */ + struct qed_hw_sriov_info *p_iov_info; +#define IS_QED_SRIOV(cdev) (!!(cdev)->p_iov_info) + unsigned long tunn_mode; + + bool b_is_vf; u32 drv_type; struct qed_eth_stats *reset_stats; @@ -514,6 +533,8 @@ struct qed_dev { const struct firmware *firmware; }; +#define NUM_OF_VFS(dev) MAX_NUM_VFS_BB +#define NUM_OF_L2_QUEUES(dev) MAX_NUM_L2_QUEUES_BB #define NUM_OF_SBS(dev) MAX_SB_PER_PATH_BB #define NUM_OF_ENG_PFS(dev) MAX_NUM_PFS_BB @@ -535,8 +556,10 @@ static inline u8 qed_concrete_to_sw_fid(struct qed_dev *cdev, #define PURE_LB_TC 8 +int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate); void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate); +void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) /* Other Linux specific common definitions */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index fc767c07a264..ac284c58d8c2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -24,11 +24,13 @@ #include "qed_hw.h" #include "qed_init_ops.h" #include "qed_reg_addr.h" +#include "qed_sriov.h" /* Max number of connection types in HW (DQ/CDU etc.) */ #define MAX_CONN_TYPES PROTOCOLID_COMMON #define NUM_TASK_TYPES 2 #define NUM_TASK_PF_SEGMENTS 4 +#define NUM_TASK_VF_SEGMENTS 1 /* QM constants */ #define QM_PQ_ELEMENT_SIZE 4 /* in bytes */ @@ -63,10 +65,12 @@ union conn_context { struct qed_conn_type_cfg { u32 cid_count; u32 cid_start; + u32 cids_per_vf; }; /* ILT Client configuration, Per connection type (protocol) resources. */ #define ILT_CLI_PF_BLOCKS (1 + NUM_TASK_PF_SEGMENTS * 2) +#define ILT_CLI_VF_BLOCKS (1 + NUM_TASK_VF_SEGMENTS * 2) #define CDUC_BLK (0) enum ilt_clients { @@ -97,6 +101,10 @@ struct qed_ilt_client_cfg { /* ILT client blocks for PF */ struct qed_ilt_cli_blk pf_blks[ILT_CLI_PF_BLOCKS]; u32 pf_total_lines; + + /* ILT client blocks for VFs */ + struct qed_ilt_cli_blk vf_blks[ILT_CLI_VF_BLOCKS]; + u32 vf_total_lines; }; /* Per Path - @@ -123,6 +131,11 @@ struct qed_cxt_mngr { /* computed ILT structure */ struct qed_ilt_client_cfg clients[ILT_CLI_MAX]; + /* total number of VFs for this hwfn - + * ALL VFs are symmetric in terms of HW resources + */ + u32 vf_count; + /* Acquired CIDs */ struct qed_cid_acquired_map acquired[MAX_CONN_TYPES]; @@ -131,37 +144,60 @@ struct qed_cxt_mngr { u32 pf_start_line; }; -static u32 qed_cxt_cdu_iids(struct qed_cxt_mngr *p_mngr) -{ - u32 type, pf_cids = 0; +/* counts the iids for the CDU/CDUC ILT client configuration */ +struct qed_cdu_iids { + u32 pf_cids; + u32 per_vf_cids; +}; - for (type = 0; type < MAX_CONN_TYPES; type++) - pf_cids += p_mngr->conn_cfg[type].cid_count; +static void qed_cxt_cdu_iids(struct qed_cxt_mngr *p_mngr, + struct qed_cdu_iids *iids) +{ + u32 type; - return pf_cids; + for (type = 0; type < MAX_CONN_TYPES; type++) { + iids->pf_cids += p_mngr->conn_cfg[type].cid_count; + iids->per_vf_cids += p_mngr->conn_cfg[type].cids_per_vf; + } } static void qed_cxt_qm_iids(struct qed_hwfn *p_hwfn, struct qed_qm_iids *iids) { struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr; - int type; + u32 vf_cids = 0, type; - for (type = 0; type < MAX_CONN_TYPES; type++) + for (type = 0; type < MAX_CONN_TYPES; type++) { iids->cids += p_mngr->conn_cfg[type].cid_count; + vf_cids += p_mngr->conn_cfg[type].cids_per_vf; + } - DP_VERBOSE(p_hwfn, QED_MSG_ILT, "iids: CIDS %08x\n", iids->cids); + iids->vf_cids += vf_cids * p_mngr->vf_count; + DP_VERBOSE(p_hwfn, QED_MSG_ILT, + "iids: CIDS %08x vf_cids %08x\n", + iids->cids, iids->vf_cids); } /* set the iids count per protocol */ static void qed_cxt_set_proto_cid_count(struct qed_hwfn *p_hwfn, enum protocol_type type, - u32 cid_count) + u32 cid_count, u32 vf_cid_cnt) { struct qed_cxt_mngr *p_mgr = p_hwfn->p_cxt_mngr; struct qed_conn_type_cfg *p_conn = &p_mgr->conn_cfg[type]; p_conn->cid_count = roundup(cid_count, DQ_RANGE_ALIGN); + p_conn->cids_per_vf = roundup(vf_cid_cnt, DQ_RANGE_ALIGN); +} + +u32 qed_cxt_get_proto_cid_count(struct qed_hwfn *p_hwfn, + enum protocol_type type, + u32 *vf_cid) +{ + if (vf_cid) + *vf_cid = p_hwfn->p_cxt_mngr->conn_cfg[type].cids_per_vf; + + return p_hwfn->p_cxt_mngr->conn_cfg[type].cid_count; } static void qed_ilt_cli_blk_fill(struct qed_ilt_client_cfg *p_cli, @@ -210,10 +246,12 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr; struct qed_ilt_client_cfg *p_cli; struct qed_ilt_cli_blk *p_blk; - u32 curr_line, total, pf_cids; + struct qed_cdu_iids cdu_iids; struct qed_qm_iids qm_iids; + u32 curr_line, total, i; memset(&qm_iids, 0, sizeof(qm_iids)); + memset(&cdu_iids, 0, sizeof(cdu_iids)); p_mngr->pf_start_line = RESC_START(p_hwfn, QED_ILT); @@ -224,14 +262,16 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* CDUC */ p_cli = &p_mngr->clients[ILT_CLI_CDUC]; curr_line = p_mngr->pf_start_line; + + /* CDUC PF */ p_cli->pf_total_lines = 0; /* get the counters for the CDUC and QM clients */ - pf_cids = qed_cxt_cdu_iids(p_mngr); + qed_cxt_cdu_iids(p_mngr, &cdu_iids); p_blk = &p_cli->pf_blks[CDUC_BLK]; - total = pf_cids * CONN_CXT_SIZE(p_hwfn); + total = cdu_iids.pf_cids * CONN_CXT_SIZE(p_hwfn); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total, CONN_CXT_SIZE(p_hwfn)); @@ -239,17 +279,36 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_CDUC); p_cli->pf_total_lines = curr_line - p_blk->start_line; + /* CDUC VF */ + p_blk = &p_cli->vf_blks[CDUC_BLK]; + total = cdu_iids.per_vf_cids * CONN_CXT_SIZE(p_hwfn); + + qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, + total, CONN_CXT_SIZE(p_hwfn)); + + qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_CDUC); + p_cli->vf_total_lines = curr_line - p_blk->start_line; + + for (i = 1; i < p_mngr->vf_count; i++) + qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, + ILT_CLI_CDUC); + /* QM */ p_cli = &p_mngr->clients[ILT_CLI_QM]; p_blk = &p_cli->pf_blks[0]; qed_cxt_qm_iids(p_hwfn, &qm_iids); - total = qed_qm_pf_mem_size(p_hwfn->rel_pf_id, qm_iids.cids, 0, 0, - p_hwfn->qm_info.num_pqs, 0); - - DP_VERBOSE(p_hwfn, QED_MSG_ILT, - "QM ILT Info, (cids=%d, num_pqs=%d, memory_size=%d)\n", - qm_iids.cids, p_hwfn->qm_info.num_pqs, total); + total = qed_qm_pf_mem_size(p_hwfn->rel_pf_id, qm_iids.cids, + qm_iids.vf_cids, 0, + p_hwfn->qm_info.num_pqs, + p_hwfn->qm_info.num_vf_pqs); + + DP_VERBOSE(p_hwfn, + QED_MSG_ILT, + "QM ILT Info, (cids=%d, vf_cids=%d, num_pqs=%d, num_vf_pqs=%d, memory_size=%d)\n", + qm_iids.cids, + qm_iids.vf_cids, + p_hwfn->qm_info.num_pqs, p_hwfn->qm_info.num_vf_pqs, total); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total * 0x1000, @@ -358,7 +417,7 @@ static int qed_ilt_shadow_alloc(struct qed_hwfn *p_hwfn) struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr; struct qed_ilt_client_cfg *clients = p_mngr->clients; struct qed_ilt_cli_blk *p_blk; - u32 size, i, j; + u32 size, i, j, k; int rc; size = qed_cxt_ilt_shadow_size(clients); @@ -383,6 +442,16 @@ static int qed_ilt_shadow_alloc(struct qed_hwfn *p_hwfn) if (rc != 0) goto ilt_shadow_fail; } + for (k = 0; k < p_mngr->vf_count; k++) { + for (j = 0; j < ILT_CLI_VF_BLOCKS; j++) { + u32 lines = clients[i].vf_total_lines * k; + + p_blk = &clients[i].vf_blks[j]; + rc = qed_ilt_blk_alloc(p_hwfn, p_blk, i, lines); + if (rc != 0) + goto ilt_shadow_fail; + } + } } return 0; @@ -467,6 +536,9 @@ int qed_cxt_mngr_alloc(struct qed_hwfn *p_hwfn) for (i = 0; i < ILT_CLI_MAX; i++) p_mngr->clients[i].p_size.val = ILT_DEFAULT_HW_P_SIZE; + if (p_hwfn->cdev->p_iov_info) + p_mngr->vf_count = p_hwfn->cdev->p_iov_info->total_vfs; + /* Set the cxt mangr pointer priori to further allocations */ p_hwfn->p_cxt_mngr = p_mngr; @@ -579,8 +651,10 @@ void qed_qm_init_pf(struct qed_hwfn *p_hwfn) params.max_phys_tcs_per_port = qm_info->max_phys_tcs_per_port; params.is_first_pf = p_hwfn->first_on_engine; params.num_pf_cids = iids.cids; + params.num_vf_cids = iids.vf_cids; params.start_pq = qm_info->start_pq; - params.num_pf_pqs = qm_info->num_pqs; + params.num_pf_pqs = qm_info->num_pqs - qm_info->num_vf_pqs; + params.num_vf_pqs = qm_info->num_vf_pqs; params.start_vport = qm_info->start_vport; params.num_vports = qm_info->num_vports; params.pf_wfq = qm_info->pf_wfq; @@ -610,26 +684,55 @@ static int qed_cm_init_pf(struct qed_hwfn *p_hwfn) static void qed_dq_init_pf(struct qed_hwfn *p_hwfn) { struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr; - u32 dq_pf_max_cid = 0; + u32 dq_pf_max_cid = 0, dq_vf_max_cid = 0; dq_pf_max_cid += (p_mngr->conn_cfg[0].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_0_RT_OFFSET, dq_pf_max_cid); + dq_vf_max_cid += (p_mngr->conn_cfg[0].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_0_RT_OFFSET, dq_vf_max_cid); + dq_pf_max_cid += (p_mngr->conn_cfg[1].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_1_RT_OFFSET, dq_pf_max_cid); + dq_vf_max_cid += (p_mngr->conn_cfg[1].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_1_RT_OFFSET, dq_vf_max_cid); + dq_pf_max_cid += (p_mngr->conn_cfg[2].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_2_RT_OFFSET, dq_pf_max_cid); + dq_vf_max_cid += (p_mngr->conn_cfg[2].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_2_RT_OFFSET, dq_vf_max_cid); + dq_pf_max_cid += (p_mngr->conn_cfg[3].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_3_RT_OFFSET, dq_pf_max_cid); + dq_vf_max_cid += (p_mngr->conn_cfg[3].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_3_RT_OFFSET, dq_vf_max_cid); + dq_pf_max_cid += (p_mngr->conn_cfg[4].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_4_RT_OFFSET, dq_pf_max_cid); - /* 5 - PF */ + dq_vf_max_cid += (p_mngr->conn_cfg[4].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_4_RT_OFFSET, dq_vf_max_cid); + dq_pf_max_cid += (p_mngr->conn_cfg[5].cid_count >> DQ_RANGE_SHIFT); STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_5_RT_OFFSET, dq_pf_max_cid); + + dq_vf_max_cid += (p_mngr->conn_cfg[5].cids_per_vf >> DQ_RANGE_SHIFT); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_5_RT_OFFSET, dq_vf_max_cid); + + /* Connection types 6 & 7 are not in use, yet they must be configured + * as the highest possible connection. Not configuring them means the + * defaults will be used, and with a large number of cids a bug may + * occur, if the defaults will be smaller than dq_pf_max_cid / + * dq_vf_max_cid. + */ + STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_6_RT_OFFSET, dq_pf_max_cid); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_6_RT_OFFSET, dq_vf_max_cid); + + STORE_RT_REG(p_hwfn, DORQ_REG_PF_MAX_ICID_7_RT_OFFSET, dq_pf_max_cid); + STORE_RT_REG(p_hwfn, DORQ_REG_VF_MAX_ICID_7_RT_OFFSET, dq_vf_max_cid); } static void qed_ilt_bounds_init(struct qed_hwfn *p_hwfn) @@ -653,6 +756,38 @@ static void qed_ilt_bounds_init(struct qed_hwfn *p_hwfn) } } +static void qed_ilt_vf_bounds_init(struct qed_hwfn *p_hwfn) +{ + struct qed_ilt_client_cfg *p_cli; + u32 blk_factor; + + /* For simplicty we set the 'block' to be an ILT page */ + if (p_hwfn->cdev->p_iov_info) { + struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info; + + STORE_RT_REG(p_hwfn, + PSWRQ2_REG_VF_BASE_RT_OFFSET, + p_iov->first_vf_in_pf); + STORE_RT_REG(p_hwfn, + PSWRQ2_REG_VF_LAST_ILT_RT_OFFSET, + p_iov->first_vf_in_pf + p_iov->total_vfs); + } + + p_cli = &p_hwfn->p_cxt_mngr->clients[ILT_CLI_CDUC]; + blk_factor = ilog2(ILT_PAGE_IN_BYTES(p_cli->p_size.val) >> 10); + if (p_cli->active) { + STORE_RT_REG(p_hwfn, + PSWRQ2_REG_CDUC_BLOCKS_FACTOR_RT_OFFSET, + blk_factor); + STORE_RT_REG(p_hwfn, + PSWRQ2_REG_CDUC_NUMBER_OF_PF_BLOCKS_RT_OFFSET, + p_cli->pf_total_lines); + STORE_RT_REG(p_hwfn, + PSWRQ2_REG_CDUC_VF_BLOCKS_RT_OFFSET, + p_cli->vf_total_lines); + } +} + /* ILT (PSWRQ2) PF */ static void qed_ilt_init_pf(struct qed_hwfn *p_hwfn) { @@ -662,6 +797,7 @@ static void qed_ilt_init_pf(struct qed_hwfn *p_hwfn) u32 line, rt_offst, i; qed_ilt_bounds_init(p_hwfn); + qed_ilt_vf_bounds_init(p_hwfn); p_mngr = p_hwfn->p_cxt_mngr; p_shdw = p_mngr->ilt_shadow; @@ -839,10 +975,10 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn) /* Set the number of required CORE connections */ u32 core_cids = 1; /* SPQ */ - qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_CORE, core_cids); + qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_CORE, core_cids, 0); qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_ETH, - p_params->num_cons); + p_params->num_cons, 1); return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.h b/drivers/net/ethernet/qlogic/qed/qed_cxt.h index c8e1f5e5c42b..234c0fa8db2a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.h +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.h @@ -51,6 +51,9 @@ enum qed_cxt_elem_type { QED_ELEM_TASK }; +u32 qed_cxt_get_proto_cid_count(struct qed_hwfn *p_hwfn, + enum protocol_type type, u32 *vf_cid); + /** * @brief qed_cxt_set_pf_params - Set the PF params for cxt init * @@ -128,6 +131,16 @@ void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn); void qed_qm_init_pf(struct qed_hwfn *p_hwfn); /** + * @brief Reconfigures QM pf on the fly + * + * @param p_hwfn + * @param p_ptt + * + * @return int + */ +int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); + +/** * @brief qed_cxt_release - Release a cid * * @param p_hwfn diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c new file mode 100644 index 000000000000..cbf58e1f9333 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -0,0 +1,562 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include <linux/types.h> +#include <asm/byteorder.h> +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/string.h> +#include "qed.h" +#include "qed_cxt.h" +#include "qed_dcbx.h" +#include "qed_hsi.h" +#include "qed_sp.h" + +#define QED_DCBX_MAX_MIB_READ_TRY (100) +#define QED_ETH_TYPE_DEFAULT (0) +#define QED_ETH_TYPE_ROCE (0x8915) +#define QED_UDP_PORT_TYPE_ROCE_V2 (0x12B7) +#define QED_ETH_TYPE_FCOE (0x8906) +#define QED_TCP_PORT_ISCSI (0xCBC) + +#define QED_DCBX_INVALID_PRIORITY 0xFF + +/* Get Traffic Class from priority traffic class table, 4 bits represent + * the traffic class corresponding to the priority. + */ +#define QED_DCBX_PRIO2TC(prio_tc_tbl, prio) \ + ((u32)(prio_tc_tbl >> ((7 - prio) * 4)) & 0x7) + +static const struct qed_dcbx_app_metadata qed_dcbx_app_update[] = { + {DCBX_PROTOCOL_ISCSI, "ISCSI", QED_PCI_DEFAULT}, + {DCBX_PROTOCOL_FCOE, "FCOE", QED_PCI_DEFAULT}, + {DCBX_PROTOCOL_ROCE, "ROCE", QED_PCI_DEFAULT}, + {DCBX_PROTOCOL_ROCE_V2, "ROCE_V2", QED_PCI_DEFAULT}, + {DCBX_PROTOCOL_ETH, "ETH", QED_PCI_ETH} +}; + +static bool qed_dcbx_app_ethtype(u32 app_info_bitmap) +{ + return !!(QED_MFW_GET_FIELD(app_info_bitmap, DCBX_APP_SF) == + DCBX_APP_SF_ETHTYPE); +} + +static bool qed_dcbx_app_port(u32 app_info_bitmap) +{ + return !!(QED_MFW_GET_FIELD(app_info_bitmap, DCBX_APP_SF) == + DCBX_APP_SF_PORT); +} + +static bool qed_dcbx_default_tlv(u32 app_info_bitmap, u16 proto_id) +{ + return !!(qed_dcbx_app_ethtype(app_info_bitmap) && + proto_id == QED_ETH_TYPE_DEFAULT); +} + +static bool qed_dcbx_iscsi_tlv(u32 app_info_bitmap, u16 proto_id) +{ + return !!(qed_dcbx_app_port(app_info_bitmap) && + proto_id == QED_TCP_PORT_ISCSI); +} + +static bool qed_dcbx_fcoe_tlv(u32 app_info_bitmap, u16 proto_id) +{ + return !!(qed_dcbx_app_ethtype(app_info_bitmap) && + proto_id == QED_ETH_TYPE_FCOE); +} + +static bool qed_dcbx_roce_tlv(u32 app_info_bitmap, u16 proto_id) +{ + return !!(qed_dcbx_app_ethtype(app_info_bitmap) && + proto_id == QED_ETH_TYPE_ROCE); +} + +static bool qed_dcbx_roce_v2_tlv(u32 app_info_bitmap, u16 proto_id) +{ + return !!(qed_dcbx_app_port(app_info_bitmap) && + proto_id == QED_UDP_PORT_TYPE_ROCE_V2); +} + +static void +qed_dcbx_dp_protocol(struct qed_hwfn *p_hwfn, struct qed_dcbx_results *p_data) +{ + enum dcbx_protocol_type id; + int i; + + DP_VERBOSE(p_hwfn, QED_MSG_DCB, "DCBX negotiated: %d\n", + p_data->dcbx_enabled); + + for (i = 0; i < ARRAY_SIZE(qed_dcbx_app_update); i++) { + id = qed_dcbx_app_update[i].id; + + DP_VERBOSE(p_hwfn, QED_MSG_DCB, + "%s info: update %d, enable %d, prio %d, tc %d, num_tc %d\n", + qed_dcbx_app_update[i].name, p_data->arr[id].update, + p_data->arr[id].enable, p_data->arr[id].priority, + p_data->arr[id].tc, p_hwfn->hw_info.num_tc); + } +} + +static void +qed_dcbx_set_params(struct qed_dcbx_results *p_data, + struct qed_hw_info *p_info, + bool enable, + bool update, + u8 prio, + u8 tc, + enum dcbx_protocol_type type, + enum qed_pci_personality personality) +{ + /* PF update ramrod data */ + p_data->arr[type].update = update; + p_data->arr[type].enable = enable; + p_data->arr[type].priority = prio; + p_data->arr[type].tc = tc; + + /* QM reconf data */ + if (p_info->personality == personality) { + if (personality == QED_PCI_ETH) + p_info->non_offload_tc = tc; + else + p_info->offload_tc = tc; + } +} + +/* Update app protocol data and hw_info fields with the TLV info */ +static void +qed_dcbx_update_app_info(struct qed_dcbx_results *p_data, + struct qed_hwfn *p_hwfn, + bool enable, + bool update, + u8 prio, u8 tc, enum dcbx_protocol_type type) +{ + struct qed_hw_info *p_info = &p_hwfn->hw_info; + enum qed_pci_personality personality; + enum dcbx_protocol_type id; + char *name; + int i; + + for (i = 0; i < ARRAY_SIZE(qed_dcbx_app_update); i++) { + id = qed_dcbx_app_update[i].id; + + if (type != id) + continue; + + personality = qed_dcbx_app_update[i].personality; + name = qed_dcbx_app_update[i].name; + + qed_dcbx_set_params(p_data, p_info, enable, update, + prio, tc, type, personality); + } +} + +static bool +qed_dcbx_get_app_protocol_type(struct qed_hwfn *p_hwfn, + u32 app_prio_bitmap, + u16 id, enum dcbx_protocol_type *type) +{ + if (qed_dcbx_fcoe_tlv(app_prio_bitmap, id)) { + *type = DCBX_PROTOCOL_FCOE; + } else if (qed_dcbx_roce_tlv(app_prio_bitmap, id)) { + *type = DCBX_PROTOCOL_ROCE; + } else if (qed_dcbx_iscsi_tlv(app_prio_bitmap, id)) { + *type = DCBX_PROTOCOL_ISCSI; + } else if (qed_dcbx_default_tlv(app_prio_bitmap, id)) { + *type = DCBX_PROTOCOL_ETH; + } else if (qed_dcbx_roce_v2_tlv(app_prio_bitmap, id)) { + *type = DCBX_PROTOCOL_ROCE_V2; + } else { + *type = DCBX_MAX_PROTOCOL_TYPE; + DP_ERR(p_hwfn, + "No action required, App TLV id = 0x%x app_prio_bitmap = 0x%x\n", + id, app_prio_bitmap); + return false; + } + + return true; +} + +/* Parse app TLV's to update TC information in hw_info structure for + * reconfiguring QM. Get protocol specific data for PF update ramrod command. + */ +static int +qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, + struct qed_dcbx_results *p_data, + struct dcbx_app_priority_entry *p_tbl, + u32 pri_tc_tbl, int count, bool dcbx_enabled) +{ + u8 tc, priority, priority_map; + enum dcbx_protocol_type type; + u16 protocol_id; + bool enable; + int i; + + DP_VERBOSE(p_hwfn, QED_MSG_DCB, "Num APP entries = %d\n", count); + + /* Parse APP TLV */ + for (i = 0; i < count; i++) { + protocol_id = QED_MFW_GET_FIELD(p_tbl[i].entry, + DCBX_APP_PROTOCOL_ID); + priority_map = QED_MFW_GET_FIELD(p_tbl[i].entry, + DCBX_APP_PRI_MAP); + priority = ffs(priority_map) - 1; + if (priority < 0) { + DP_ERR(p_hwfn, "Invalid priority\n"); + return -EINVAL; + } + + tc = QED_DCBX_PRIO2TC(pri_tc_tbl, priority); + if (qed_dcbx_get_app_protocol_type(p_hwfn, p_tbl[i].entry, + protocol_id, &type)) { + /* ETH always have the enable bit reset, as it gets + * vlan information per packet. For other protocols, + * should be set according to the dcbx_enabled + * indication, but we only got here if there was an + * app tlv for the protocol, so dcbx must be enabled. + */ + enable = !!(type == DCBX_PROTOCOL_ETH); + + qed_dcbx_update_app_info(p_data, p_hwfn, enable, true, + priority, tc, type); + } + } + + /* If RoCE-V2 TLV is not detected, driver need to use RoCE app + * data for RoCE-v2 not the default app data. + */ + if (!p_data->arr[DCBX_PROTOCOL_ROCE_V2].update && + p_data->arr[DCBX_PROTOCOL_ROCE].update) { + tc = p_data->arr[DCBX_PROTOCOL_ROCE].tc; + priority = p_data->arr[DCBX_PROTOCOL_ROCE].priority; + qed_dcbx_update_app_info(p_data, p_hwfn, true, true, + priority, tc, DCBX_PROTOCOL_ROCE_V2); + } + + /* Update ramrod protocol data and hw_info fields + * with default info when corresponding APP TLV's are not detected. + * The enabled field has a different logic for ethernet as only for + * ethernet dcb should disabled by default, as the information arrives + * from the OS (unless an explicit app tlv was present). + */ + tc = p_data->arr[DCBX_PROTOCOL_ETH].tc; + priority = p_data->arr[DCBX_PROTOCOL_ETH].priority; + for (type = 0; type < DCBX_MAX_PROTOCOL_TYPE; type++) { + if (p_data->arr[type].update) + continue; + + enable = (type == DCBX_PROTOCOL_ETH) ? false : dcbx_enabled; + qed_dcbx_update_app_info(p_data, p_hwfn, enable, true, + priority, tc, type); + } + + return 0; +} + +/* Parse app TLV's to update TC information in hw_info structure for + * reconfiguring QM. Get protocol specific data for PF update ramrod command. + */ +static int qed_dcbx_process_mib_info(struct qed_hwfn *p_hwfn) +{ + struct dcbx_app_priority_feature *p_app; + struct dcbx_app_priority_entry *p_tbl; + struct qed_dcbx_results data = { 0 }; + struct dcbx_ets_feature *p_ets; + struct qed_hw_info *p_info; + u32 pri_tc_tbl, flags; + bool dcbx_enabled; + int num_entries; + int rc = 0; + + /* If DCBx version is non zero, then negotiation was + * successfuly performed + */ + flags = p_hwfn->p_dcbx_info->operational.flags; + dcbx_enabled = !!QED_MFW_GET_FIELD(flags, DCBX_CONFIG_VERSION); + + p_app = &p_hwfn->p_dcbx_info->operational.features.app; + p_tbl = p_app->app_pri_tbl; + + p_ets = &p_hwfn->p_dcbx_info->operational.features.ets; + pri_tc_tbl = p_ets->pri_tc_tbl[0]; + + p_info = &p_hwfn->hw_info; + num_entries = QED_MFW_GET_FIELD(p_app->flags, DCBX_APP_NUM_ENTRIES); + + rc = qed_dcbx_process_tlv(p_hwfn, &data, p_tbl, pri_tc_tbl, + num_entries, dcbx_enabled); + if (rc) + return rc; + + p_info->num_tc = QED_MFW_GET_FIELD(p_ets->flags, DCBX_ETS_MAX_TCS); + data.pf_id = p_hwfn->rel_pf_id; + data.dcbx_enabled = dcbx_enabled; + + qed_dcbx_dp_protocol(p_hwfn, &data); + + memcpy(&p_hwfn->p_dcbx_info->results, &data, + sizeof(struct qed_dcbx_results)); + + return 0; +} + +static int +qed_dcbx_copy_mib(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_dcbx_mib_meta_data *p_data, + enum qed_mib_read_type type) +{ + u32 prefix_seq_num, suffix_seq_num; + int read_count = 0; + int rc = 0; + + /* The data is considered to be valid only if both sequence numbers are + * the same. + */ + do { + if (type == QED_DCBX_REMOTE_LLDP_MIB) { + qed_memcpy_from(p_hwfn, p_ptt, p_data->lldp_remote, + p_data->addr, p_data->size); + prefix_seq_num = p_data->lldp_remote->prefix_seq_num; + suffix_seq_num = p_data->lldp_remote->suffix_seq_num; + } else { + qed_memcpy_from(p_hwfn, p_ptt, p_data->mib, + p_data->addr, p_data->size); + prefix_seq_num = p_data->mib->prefix_seq_num; + suffix_seq_num = p_data->mib->suffix_seq_num; + } + read_count++; + + DP_VERBOSE(p_hwfn, + QED_MSG_DCB, + "mib type = %d, try count = %d prefix seq num = %d suffix seq num = %d\n", + type, read_count, prefix_seq_num, suffix_seq_num); + } while ((prefix_seq_num != suffix_seq_num) && + (read_count < QED_DCBX_MAX_MIB_READ_TRY)); + + if (read_count >= QED_DCBX_MAX_MIB_READ_TRY) { + DP_ERR(p_hwfn, + "MIB read err, mib type = %d, try count = %d prefix seq num = %d suffix seq num = %d\n", + type, read_count, prefix_seq_num, suffix_seq_num); + rc = -EIO; + } + + return rc; +} + +static int +qed_dcbx_read_local_lldp_mib(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_dcbx_mib_meta_data data; + int rc = 0; + + memset(&data, 0, sizeof(data)); + data.addr = p_hwfn->mcp_info->port_addr + offsetof(struct public_port, + lldp_config_params); + data.lldp_local = p_hwfn->p_dcbx_info->lldp_local; + data.size = sizeof(struct lldp_config_params_s); + qed_memcpy_from(p_hwfn, p_ptt, data.lldp_local, data.addr, data.size); + + return rc; +} + +static int +qed_dcbx_read_remote_lldp_mib(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_mib_read_type type) +{ + struct qed_dcbx_mib_meta_data data; + int rc = 0; + + memset(&data, 0, sizeof(data)); + data.addr = p_hwfn->mcp_info->port_addr + offsetof(struct public_port, + lldp_status_params); + data.lldp_remote = p_hwfn->p_dcbx_info->lldp_remote; + data.size = sizeof(struct lldp_status_params_s); + rc = qed_dcbx_copy_mib(p_hwfn, p_ptt, &data, type); + + return rc; +} + +static int +qed_dcbx_read_operational_mib(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_mib_read_type type) +{ + struct qed_dcbx_mib_meta_data data; + int rc = 0; + + memset(&data, 0, sizeof(data)); + data.addr = p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, operational_dcbx_mib); + data.mib = &p_hwfn->p_dcbx_info->operational; + data.size = sizeof(struct dcbx_mib); + rc = qed_dcbx_copy_mib(p_hwfn, p_ptt, &data, type); + + return rc; +} + +static int +qed_dcbx_read_remote_mib(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum qed_mib_read_type type) +{ + struct qed_dcbx_mib_meta_data data; + int rc = 0; + + memset(&data, 0, sizeof(data)); + data.addr = p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, remote_dcbx_mib); + data.mib = &p_hwfn->p_dcbx_info->remote; + data.size = sizeof(struct dcbx_mib); + rc = qed_dcbx_copy_mib(p_hwfn, p_ptt, &data, type); + + return rc; +} + +static int +qed_dcbx_read_local_mib(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_dcbx_mib_meta_data data; + int rc = 0; + + memset(&data, 0, sizeof(data)); + data.addr = p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, local_admin_dcbx_mib); + data.local_admin = &p_hwfn->p_dcbx_info->local_admin; + data.size = sizeof(struct dcbx_local_params); + qed_memcpy_from(p_hwfn, p_ptt, data.local_admin, data.addr, data.size); + + return rc; +} + +static int qed_dcbx_read_mib(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum qed_mib_read_type type) +{ + int rc = -EINVAL; + + switch (type) { + case QED_DCBX_OPERATIONAL_MIB: + rc = qed_dcbx_read_operational_mib(p_hwfn, p_ptt, type); + break; + case QED_DCBX_REMOTE_MIB: + rc = qed_dcbx_read_remote_mib(p_hwfn, p_ptt, type); + break; + case QED_DCBX_LOCAL_MIB: + rc = qed_dcbx_read_local_mib(p_hwfn, p_ptt); + break; + case QED_DCBX_REMOTE_LLDP_MIB: + rc = qed_dcbx_read_remote_lldp_mib(p_hwfn, p_ptt, type); + break; + case QED_DCBX_LOCAL_LLDP_MIB: + rc = qed_dcbx_read_local_lldp_mib(p_hwfn, p_ptt); + break; + default: + DP_ERR(p_hwfn, "MIB read err, unknown mib type %d\n", type); + } + + return rc; +} + +/* Read updated MIB. + * Reconfigure QM and invoke PF update ramrod command if operational MIB + * change is detected. + */ +int +qed_dcbx_mib_update_event(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum qed_mib_read_type type) +{ + int rc = 0; + + rc = qed_dcbx_read_mib(p_hwfn, p_ptt, type); + if (rc) + return rc; + + if (type == QED_DCBX_OPERATIONAL_MIB) { + rc = qed_dcbx_process_mib_info(p_hwfn); + if (!rc) { + /* reconfigure tcs of QM queues according + * to negotiation results + */ + qed_qm_reconf(p_hwfn, p_ptt); + + /* update storm FW with negotiation results */ + qed_sp_pf_update(p_hwfn); + } + } + + return rc; +} + +int qed_dcbx_info_alloc(struct qed_hwfn *p_hwfn) +{ + int rc = 0; + + p_hwfn->p_dcbx_info = kzalloc(sizeof(*p_hwfn->p_dcbx_info), GFP_KERNEL); + if (!p_hwfn->p_dcbx_info) { + DP_NOTICE(p_hwfn, + "Failed to allocate 'struct qed_dcbx_info'\n"); + rc = -ENOMEM; + } + + return rc; +} + +void qed_dcbx_info_free(struct qed_hwfn *p_hwfn, + struct qed_dcbx_info *p_dcbx_info) +{ + kfree(p_hwfn->p_dcbx_info); +} + +static void qed_dcbx_update_protocol_data(struct protocol_dcb_data *p_data, + struct qed_dcbx_results *p_src, + enum dcbx_protocol_type type) +{ + p_data->dcb_enable_flag = p_src->arr[type].enable; + p_data->dcb_priority = p_src->arr[type].priority; + p_data->dcb_tc = p_src->arr[type].tc; +} + +/* Set pf update ramrod command params */ +void qed_dcbx_set_pf_update_params(struct qed_dcbx_results *p_src, + struct pf_update_ramrod_data *p_dest) +{ + struct protocol_dcb_data *p_dcb_data; + bool update_flag = false; + + p_dest->pf_id = p_src->pf_id; + + update_flag = p_src->arr[DCBX_PROTOCOL_FCOE].update; + p_dest->update_fcoe_dcb_data_flag = update_flag; + + update_flag = p_src->arr[DCBX_PROTOCOL_ROCE].update; + p_dest->update_roce_dcb_data_flag = update_flag; + update_flag = p_src->arr[DCBX_PROTOCOL_ROCE_V2].update; + p_dest->update_roce_dcb_data_flag = update_flag; + + update_flag = p_src->arr[DCBX_PROTOCOL_ISCSI].update; + p_dest->update_iscsi_dcb_data_flag = update_flag; + update_flag = p_src->arr[DCBX_PROTOCOL_ETH].update; + p_dest->update_eth_dcb_data_flag = update_flag; + + p_dcb_data = &p_dest->fcoe_dcb_data; + qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_FCOE); + p_dcb_data = &p_dest->roce_dcb_data; + + if (p_src->arr[DCBX_PROTOCOL_ROCE].update) + qed_dcbx_update_protocol_data(p_dcb_data, p_src, + DCBX_PROTOCOL_ROCE); + if (p_src->arr[DCBX_PROTOCOL_ROCE_V2].update) + qed_dcbx_update_protocol_data(p_dcb_data, p_src, + DCBX_PROTOCOL_ROCE_V2); + + p_dcb_data = &p_dest->iscsi_dcb_data; + qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_ISCSI); + p_dcb_data = &p_dest->eth_dcb_data; + qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_ETH); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.h b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h new file mode 100644 index 000000000000..e7f834dbda2d --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h @@ -0,0 +1,80 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_DCBX_H +#define _QED_DCBX_H +#include <linux/types.h> +#include <linux/slab.h> +#include "qed.h" +#include "qed_hsi.h" +#include "qed_hw.h" +#include "qed_mcp.h" +#include "qed_reg_addr.h" + +#define DCBX_CONFIG_MAX_APP_PROTOCOL 4 + +enum qed_mib_read_type { + QED_DCBX_OPERATIONAL_MIB, + QED_DCBX_REMOTE_MIB, + QED_DCBX_LOCAL_MIB, + QED_DCBX_REMOTE_LLDP_MIB, + QED_DCBX_LOCAL_LLDP_MIB +}; + +struct qed_dcbx_app_data { + bool enable; /* DCB enabled */ + bool update; /* Update indication */ + u8 priority; /* Priority */ + u8 tc; /* Traffic Class */ +}; + +struct qed_dcbx_results { + bool dcbx_enabled; + u8 pf_id; + struct qed_dcbx_app_data arr[DCBX_MAX_PROTOCOL_TYPE]; +}; + +struct qed_dcbx_app_metadata { + enum dcbx_protocol_type id; + char *name; + enum qed_pci_personality personality; +}; + +#define QED_MFW_GET_FIELD(name, field) \ + (((name) & (field ## _MASK)) >> (field ## _SHIFT)) + +struct qed_dcbx_info { + struct lldp_status_params_s lldp_remote[LLDP_MAX_LLDP_AGENTS]; + struct lldp_config_params_s lldp_local[LLDP_MAX_LLDP_AGENTS]; + struct dcbx_local_params local_admin; + struct qed_dcbx_results results; + struct dcbx_mib operational; + struct dcbx_mib remote; + u8 dcbx_cap; +}; + +struct qed_dcbx_mib_meta_data { + struct lldp_config_params_s *lldp_local; + struct lldp_status_params_s *lldp_remote; + struct dcbx_local_params *local_admin; + struct dcbx_mib *mib; + size_t size; + u32 addr; +}; + +/* QED local interface routines */ +int +qed_dcbx_mib_update_event(struct qed_hwfn *, + struct qed_ptt *, enum qed_mib_read_type); + +int qed_dcbx_info_alloc(struct qed_hwfn *p_hwfn); +void qed_dcbx_info_free(struct qed_hwfn *, struct qed_dcbx_info *); +void qed_dcbx_set_pf_update_params(struct qed_dcbx_results *p_src, + struct pf_update_ramrod_data *p_dest); + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index b500c86d7d06..089016f46f26 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -22,6 +22,7 @@ #include <linux/qed/qed_if.h> #include "qed.h" #include "qed_cxt.h" +#include "qed_dcbx.h" #include "qed_dev_api.h" #include "qed_hsi.h" #include "qed_hw.h" @@ -30,6 +31,11 @@ #include "qed_mcp.h" #include "qed_reg_addr.h" #include "qed_sp.h" +#include "qed_sriov.h" +#include "qed_vf.h" + +static spinlock_t qm_lock; +static bool qm_lock_init = false; /* API common to all protocols */ enum BAR_ID { @@ -40,10 +46,14 @@ enum BAR_ID { static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn, enum BAR_ID bar_id) { - u32 bar_reg = (bar_id == BAR_ID_0 ? - PGLUE_B_REG_PF_BAR0_SIZE : PGLUE_B_REG_PF_BAR1_SIZE); - u32 val = qed_rd(p_hwfn, p_hwfn->p_main_ptt, bar_reg); + u32 bar_reg = (bar_id == BAR_ID_0 ? + PGLUE_B_REG_PF_BAR0_SIZE : PGLUE_B_REG_PF_BAR1_SIZE); + u32 val; + + if (IS_VF(p_hwfn->cdev)) + return 1 << 17; + val = qed_rd(p_hwfn, p_hwfn->p_main_ptt, bar_reg); if (val) return 1 << (val + 15); @@ -113,6 +123,9 @@ void qed_resc_free(struct qed_dev *cdev) { int i; + if (IS_VF(cdev)) + return; + kfree(cdev->fw_data); cdev->fw_data = NULL; @@ -136,20 +149,27 @@ void qed_resc_free(struct qed_dev *cdev) qed_eq_free(p_hwfn, p_hwfn->p_eq); qed_consq_free(p_hwfn, p_hwfn->p_consq); qed_int_free(p_hwfn); + qed_iov_free(p_hwfn); qed_dmae_info_free(p_hwfn); + qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info); } } static int qed_init_qm_info(struct qed_hwfn *p_hwfn) { + u8 num_vports, vf_offset = 0, i, vport_id, num_ports, curr_queue = 0; struct qed_qm_info *qm_info = &p_hwfn->qm_info; struct init_qm_port_params *p_qm_port; - u8 num_vports, i, vport_id, num_ports; u16 num_pqs, multi_cos_tcs = 1; + u16 num_vfs = 0; +#ifdef CONFIG_QED_SRIOV + if (p_hwfn->cdev->p_iov_info) + num_vfs = p_hwfn->cdev->p_iov_info->total_vfs; +#endif memset(qm_info, 0, sizeof(*qm_info)); - num_pqs = multi_cos_tcs + 1; /* The '1' is for pure-LB */ + num_pqs = multi_cos_tcs + num_vfs + 1; /* The '1' is for pure-LB */ num_vports = (u8)RESC_NUM(p_hwfn, QED_VPORT); /* Sanity checking that setup requires legal number of resources */ @@ -186,21 +206,41 @@ static int qed_init_qm_info(struct qed_hwfn *p_hwfn) /* First init per-TC PQs */ for (i = 0; i < multi_cos_tcs; i++) { - struct init_qm_pq_params *params = &qm_info->qm_pq_params[i]; - - params->vport_id = vport_id; - params->tc_id = p_hwfn->hw_info.non_offload_tc; - params->wrr_group = 1; + struct init_qm_pq_params *params = + &qm_info->qm_pq_params[curr_queue++]; + + if (p_hwfn->hw_info.personality == QED_PCI_ETH) { + params->vport_id = vport_id; + params->tc_id = p_hwfn->hw_info.non_offload_tc; + params->wrr_group = 1; + } else { + params->vport_id = vport_id; + params->tc_id = p_hwfn->hw_info.offload_tc; + params->wrr_group = 1; + } } /* Then init pure-LB PQ */ - qm_info->pure_lb_pq = i; - qm_info->qm_pq_params[i].vport_id = (u8)RESC_START(p_hwfn, QED_VPORT); - qm_info->qm_pq_params[i].tc_id = PURE_LB_TC; - qm_info->qm_pq_params[i].wrr_group = 1; - i++; + qm_info->pure_lb_pq = curr_queue; + qm_info->qm_pq_params[curr_queue].vport_id = + (u8) RESC_START(p_hwfn, QED_VPORT); + qm_info->qm_pq_params[curr_queue].tc_id = PURE_LB_TC; + qm_info->qm_pq_params[curr_queue].wrr_group = 1; + curr_queue++; qm_info->offload_pq = 0; + /* Then init per-VF PQs */ + vf_offset = curr_queue; + for (i = 0; i < num_vfs; i++) { + /* First vport is used by the PF */ + qm_info->qm_pq_params[curr_queue].vport_id = vport_id + i + 1; + qm_info->qm_pq_params[curr_queue].tc_id = + p_hwfn->hw_info.non_offload_tc; + qm_info->qm_pq_params[curr_queue].wrr_group = 1; + curr_queue++; + } + + qm_info->vf_queues_offset = vf_offset; qm_info->num_pqs = num_pqs; qm_info->num_vports = num_vports; @@ -218,7 +258,8 @@ static int qed_init_qm_info(struct qed_hwfn *p_hwfn) qm_info->start_pq = (u16)RESC_START(p_hwfn, QED_PQ); - qm_info->start_vport = (u8)RESC_START(p_hwfn, QED_VPORT); + qm_info->num_vf_pqs = num_vfs; + qm_info->start_vport = (u8) RESC_START(p_hwfn, QED_VPORT); for (i = 0; i < qm_info->num_vports; i++) qm_info->qm_vport_params[i].vport_wfq = 1; @@ -236,12 +277,72 @@ alloc_err: return -ENOMEM; } +/* This function reconfigures the QM pf on the fly. + * For this purpose we: + * 1. reconfigure the QM database + * 2. set new values to runtime arrat + * 3. send an sdm_qm_cmd through the rbc interface to stop the QM + * 4. activate init tool in QM_PF stage + * 5. send an sdm_qm_cmd through rbc interface to release the QM + */ +int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + bool b_rc; + int rc; + + /* qm_info is allocated in qed_init_qm_info() which is already called + * from qed_resc_alloc() or previous call of qed_qm_reconf(). + * The allocated size may change each init, so we free it before next + * allocation. + */ + qed_qm_info_free(p_hwfn); + + /* initialize qed's qm data structure */ + rc = qed_init_qm_info(p_hwfn); + if (rc) + return rc; + + /* stop PF's qm queues */ + spin_lock_bh(&qm_lock); + b_rc = qed_send_qm_stop_cmd(p_hwfn, p_ptt, false, true, + qm_info->start_pq, qm_info->num_pqs); + spin_unlock_bh(&qm_lock); + if (!b_rc) + return -EINVAL; + + /* clear the QM_PF runtime phase leftovers from previous init */ + qed_init_clear_rt_data(p_hwfn); + + /* prepare QM portion of runtime array */ + qed_qm_init_pf(p_hwfn); + + /* activate init tool on runtime array */ + rc = qed_init_run(p_hwfn, p_ptt, PHASE_QM_PF, p_hwfn->rel_pf_id, + p_hwfn->hw_info.hw_mode); + if (rc) + return rc; + + /* start PF's qm queues */ + spin_lock_bh(&qm_lock); + b_rc = qed_send_qm_stop_cmd(p_hwfn, p_ptt, true, true, + qm_info->start_pq, qm_info->num_pqs); + spin_unlock_bh(&qm_lock); + if (!b_rc) + return -EINVAL; + + return 0; +} + int qed_resc_alloc(struct qed_dev *cdev) { struct qed_consq *p_consq; struct qed_eq *p_eq; int i, rc = 0; + if (IS_VF(cdev)) + return rc; + cdev->fw_data = kzalloc(sizeof(*cdev->fw_data), GFP_KERNEL); if (!cdev->fw_data) return -ENOMEM; @@ -316,6 +417,10 @@ int qed_resc_alloc(struct qed_dev *cdev) if (rc) goto alloc_err; + rc = qed_iov_alloc(p_hwfn); + if (rc) + goto alloc_err; + /* EQ */ p_eq = qed_eq_alloc(p_hwfn, 256); if (!p_eq) { @@ -338,6 +443,14 @@ int qed_resc_alloc(struct qed_dev *cdev) "Failed to allocate memory for dmae_info structure\n"); goto alloc_err; } + + /* DCBX initialization */ + rc = qed_dcbx_info_alloc(p_hwfn); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed to allocate memory for dcbx structure\n"); + goto alloc_err; + } } cdev->reset_stats = kzalloc(sizeof(*cdev->reset_stats), GFP_KERNEL); @@ -358,6 +471,9 @@ void qed_resc_setup(struct qed_dev *cdev) { int i; + if (IS_VF(cdev)) + return; + for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; @@ -373,14 +489,15 @@ void qed_resc_setup(struct qed_dev *cdev) p_hwfn->mcp_info->mfw_mb_length); qed_int_setup(p_hwfn, p_hwfn->p_main_ptt); + + qed_iov_setup(p_hwfn, p_hwfn->p_main_ptt); } } #define FINAL_CLEANUP_POLL_CNT (100) #define FINAL_CLEANUP_POLL_TIME (10) int qed_final_cleanup(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u16 id) + struct qed_ptt *p_ptt, u16 id, bool is_vf) { u32 command = 0, addr, count = FINAL_CLEANUP_POLL_CNT; int rc = -EBUSY; @@ -388,6 +505,9 @@ int qed_final_cleanup(struct qed_hwfn *p_hwfn, addr = GTT_BAR0_MAP_REG_USDM_RAM + USTORM_FLR_FINAL_ACK_OFFSET(p_hwfn->rel_pf_id); + if (is_vf) + id += 0x10; + command |= X_FINAL_CLEANUP_AGG_INT << SDM_AGG_INT_COMP_PARAMS_AGG_INT_INDEX_SHIFT; command |= 1 << SDM_AGG_INT_COMP_PARAMS_AGG_VECTOR_ENABLE_SHIFT; @@ -500,7 +620,9 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, struct qed_qm_info *qm_info = &p_hwfn->qm_info; struct qed_qm_common_rt_init_params params; struct qed_dev *cdev = p_hwfn->cdev; + u32 concrete_fid; int rc = 0; + u8 vf_id; qed_init_cau_rt_data(cdev); @@ -550,6 +672,14 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, qed_wr(p_hwfn, p_ptt, 0x20b4, qed_rd(p_hwfn, p_ptt, 0x20b4) & ~0x10); + for (vf_id = 0; vf_id < MAX_NUM_VFS_BB; vf_id++) { + concrete_fid = qed_vfid_to_concrete(p_hwfn, vf_id); + qed_fid_pretend(p_hwfn, p_ptt, (u16) concrete_fid); + qed_wr(p_hwfn, p_ptt, CCFC_REG_STRONG_ENABLE_VF, 0x1); + } + /* pretend to original PF */ + qed_fid_pretend(p_hwfn, p_ptt, p_hwfn->rel_pf_id); + return rc; } @@ -612,7 +742,7 @@ static int qed_hw_init_pf(struct qed_hwfn *p_hwfn, STORE_RT_REG(p_hwfn, PRS_REG_SEARCH_ROCE_RT_OFFSET, 0); /* Cleanup chip from previous driver if such remains exist */ - rc = qed_final_cleanup(p_hwfn, p_ptt, rel_pf_id); + rc = qed_final_cleanup(p_hwfn, p_ptt, rel_pf_id, false); if (rc != 0) return rc; @@ -634,7 +764,8 @@ static int qed_hw_init_pf(struct qed_hwfn *p_hwfn, qed_int_igu_enable(p_hwfn, p_ptt, int_mode); /* send function start command */ - rc = qed_sp_pf_start(p_hwfn, p_tunn, p_hwfn->cdev->mf_mode); + rc = qed_sp_pf_start(p_hwfn, p_tunn, p_hwfn->cdev->mf_mode, + allow_npar_tx_switch); if (rc) DP_NOTICE(p_hwfn, "Function start ramrod failed\n"); } @@ -690,13 +821,20 @@ int qed_hw_init(struct qed_dev *cdev, u32 load_code, param; int rc, mfw_rc, i; - rc = qed_init_fw_data(cdev, bin_fw_data); - if (rc != 0) - return rc; + if (IS_PF(cdev)) { + rc = qed_init_fw_data(cdev, bin_fw_data); + if (rc != 0) + return rc; + } for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + if (IS_VF(cdev)) { + p_hwfn->b_int_enabled = 1; + continue; + } + /* Enable DMAE in PXP */ rc = qed_change_pci_hwfn(p_hwfn, p_hwfn->p_main_ptt, true); @@ -718,6 +856,11 @@ int qed_hw_init(struct qed_dev *cdev, p_hwfn->first_on_engine = (load_code == FW_MSG_CODE_DRV_LOAD_ENGINE); + if (!qm_lock_init) { + spin_lock_init(&qm_lock); + qm_lock_init = true; + } + switch (load_code) { case FW_MSG_CODE_DRV_LOAD_ENGINE: rc = qed_hw_init_common(p_hwfn, p_hwfn->p_main_ptt, @@ -759,6 +902,20 @@ int qed_hw_init(struct qed_dev *cdev, return mfw_rc; } + /* send DCBX attention request command */ + DP_VERBOSE(p_hwfn, + QED_MSG_DCB, + "sending phony dcbx set command to trigger DCBx attention handling\n"); + mfw_rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, + DRV_MSG_CODE_SET_DCBX, + 1 << DRV_MB_PARAM_DCBX_NOTIFY_SHIFT, + &load_code, ¶m); + if (mfw_rc) { + DP_NOTICE(p_hwfn, + "Failed to send DCBX attention request\n"); + return mfw_rc; + } + p_hwfn->hw_init_done = true; } @@ -821,6 +978,11 @@ int qed_hw_stop(struct qed_dev *cdev) DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Stopping hw/fw\n"); + if (IS_VF(cdev)) { + qed_vf_pf_int_cleanup(p_hwfn); + continue; + } + /* mark the hw as uninitialized... */ p_hwfn->hw_init_done = false; @@ -852,15 +1014,16 @@ int qed_hw_stop(struct qed_dev *cdev) usleep_range(1000, 2000); } - /* Disable DMAE in PXP - in CMT, this should only be done for - * first hw-function, and only after all transactions have - * stopped for all active hw-functions. - */ - t_rc = qed_change_pci_hwfn(&cdev->hwfns[0], - cdev->hwfns[0].p_main_ptt, - false); - if (t_rc != 0) - rc = t_rc; + if (IS_PF(cdev)) { + /* Disable DMAE in PXP - in CMT, this should only be done for + * first hw-function, and only after all transactions have + * stopped for all active hw-functions. + */ + t_rc = qed_change_pci_hwfn(&cdev->hwfns[0], + cdev->hwfns[0].p_main_ptt, false); + if (t_rc != 0) + rc = t_rc; + } return rc; } @@ -871,7 +1034,12 @@ void qed_hw_stop_fastpath(struct qed_dev *cdev) for_each_hwfn(cdev, j) { struct qed_hwfn *p_hwfn = &cdev->hwfns[j]; - struct qed_ptt *p_ptt = p_hwfn->p_main_ptt; + struct qed_ptt *p_ptt = p_hwfn->p_main_ptt; + + if (IS_VF(cdev)) { + qed_vf_pf_int_cleanup(p_hwfn); + continue; + } DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, @@ -895,6 +1063,9 @@ void qed_hw_stop_fastpath(struct qed_dev *cdev) void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn) { + if (IS_VF(p_hwfn->cdev)) + return; + /* Re-open incoming traffic */ qed_wr(p_hwfn, p_hwfn->p_main_ptt, NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x0); @@ -924,6 +1095,13 @@ int qed_hw_reset(struct qed_dev *cdev) for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + if (IS_VF(cdev)) { + rc = qed_vf_pf_reset(p_hwfn); + if (rc) + return rc; + continue; + } + DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Resetting hw/fw\n"); /* Check for incorrect states */ @@ -1019,13 +1197,19 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) static void qed_hw_get_resc(struct qed_hwfn *p_hwfn) { u32 *resc_start = p_hwfn->hw_info.resc_start; + u8 num_funcs = p_hwfn->num_funcs_on_engine; u32 *resc_num = p_hwfn->hw_info.resc_num; struct qed_sb_cnt_info sb_cnt_info; - int num_funcs, i; - - num_funcs = MAX_NUM_PFS_BB; + int i, max_vf_vlan_filters; memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + +#ifdef CONFIG_QED_SRIOV + max_vf_vlan_filters = QED_ETH_MAX_VF_NUM_VLAN_FILTERS; +#else + max_vf_vlan_filters = 0; +#endif + qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); resc_num[QED_SB] = min_t(u32, @@ -1230,6 +1414,51 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, return qed_mcp_fill_shmem_func_info(p_hwfn, p_ptt); } +static void qed_get_num_funcs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 reg_function_hide, tmp, eng_mask; + u8 num_funcs; + + num_funcs = MAX_NUM_PFS_BB; + + /* Bit 0 of MISCS_REG_FUNCTION_HIDE indicates whether the bypass values + * in the other bits are selected. + * Bits 1-15 are for functions 1-15, respectively, and their value is + * '0' only for enabled functions (function 0 always exists and + * enabled). + * In case of CMT, only the "even" functions are enabled, and thus the + * number of functions for both hwfns is learnt from the same bits. + */ + reg_function_hide = qed_rd(p_hwfn, p_ptt, MISCS_REG_FUNCTION_HIDE); + + if (reg_function_hide & 0x1) { + if (QED_PATH_ID(p_hwfn) && p_hwfn->cdev->num_hwfns == 1) { + num_funcs = 0; + eng_mask = 0xaaaa; + } else { + num_funcs = 1; + eng_mask = 0x5554; + } + + /* Get the number of the enabled functions on the engine */ + tmp = (reg_function_hide ^ 0xffffffff) & eng_mask; + while (tmp) { + if (tmp & 0x1) + num_funcs++; + tmp >>= 0x1; + } + } + + p_hwfn->num_funcs_on_engine = num_funcs; + + DP_VERBOSE(p_hwfn, + NETIF_MSG_PROBE, + "PF [rel_id %d, abs_id %d] within the %d enabled functions on the engine\n", + p_hwfn->rel_pf_id, + p_hwfn->abs_pf_id, + p_hwfn->num_funcs_on_engine); +} + static int qed_get_hw_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, @@ -1238,6 +1467,13 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, u32 port_mode; int rc; + /* Since all information is common, only first hwfns should do this */ + if (IS_LEAD_HWFN(p_hwfn)) { + rc = qed_iov_hw_info(p_hwfn); + if (rc) + return rc; + } + /* Read the port mode */ port_mode = qed_rd(p_hwfn, p_ptt, CNIG_REG_NW_PORT_MODE_BB_B0); @@ -1281,6 +1517,8 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, p_hwfn->hw_info.personality = protocol; } + qed_get_num_funcs(p_hwfn, p_ptt); + qed_hw_get_resc(p_hwfn); return rc; @@ -1346,6 +1584,9 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, p_hwfn->regview = p_regview; p_hwfn->doorbells = p_doorbells; + if (IS_VF(p_hwfn->cdev)) + return qed_vf_hw_prepare(p_hwfn); + /* Validate that chip access is feasible */ if (REG_RD(p_hwfn, PXP_PF_ME_OPAQUE_ADDR) == 0xffffffff) { DP_ERR(p_hwfn, @@ -1397,6 +1638,8 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, return rc; err2: + if (IS_LEAD_HWFN(p_hwfn)) + qed_iov_free_hw_info(p_hwfn->cdev); qed_mcp_free(p_hwfn); err1: qed_hw_hwfn_free(p_hwfn); @@ -1411,7 +1654,8 @@ int qed_hw_prepare(struct qed_dev *cdev, int rc; /* Store the precompiled init data ptrs */ - qed_init_iro_array(cdev); + if (IS_PF(cdev)) + qed_init_iro_array(cdev); /* Initialize the first hwfn - will learn number of hwfns */ rc = qed_hw_prepare_single(p_hwfn, @@ -1443,9 +1687,11 @@ int qed_hw_prepare(struct qed_dev *cdev, * initiliazed hwfn 0. */ if (rc) { - qed_init_free(p_hwfn); - qed_mcp_free(p_hwfn); - qed_hw_hwfn_free(p_hwfn); + if (IS_PF(cdev)) { + qed_init_free(p_hwfn); + qed_mcp_free(p_hwfn); + qed_hw_hwfn_free(p_hwfn); + } } } @@ -1459,10 +1705,17 @@ void qed_hw_remove(struct qed_dev *cdev) for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + if (IS_VF(cdev)) { + qed_vf_pf_release(p_hwfn); + continue; + } + qed_init_free(p_hwfn); qed_hw_hwfn_free(p_hwfn); qed_mcp_free(p_hwfn); } + + qed_iov_free_hw_info(cdev); } int qed_chain_alloc(struct qed_dev *cdev, @@ -1732,6 +1985,32 @@ static int qed_init_wfq_param(struct qed_hwfn *p_hwfn, return 0; } +static int __qed_configure_vport_wfq(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 vp_id, u32 rate) +{ + struct qed_mcp_link_state *p_link; + int rc = 0; + + p_link = &p_hwfn->cdev->hwfns[0].mcp_info->link_output; + + if (!p_link->min_pf_rate) { + p_hwfn->qm_info.wfq_data[vp_id].min_speed = rate; + p_hwfn->qm_info.wfq_data[vp_id].configured = true; + return rc; + } + + rc = qed_init_wfq_param(p_hwfn, vp_id, rate, p_link->min_pf_rate); + + if (rc == 0) + qed_configure_wfq_for_all_vports(p_hwfn, p_ptt, + p_link->min_pf_rate); + else + DP_NOTICE(p_hwfn, + "Validation failed while configuring min rate\n"); + + return rc; +} + static int __qed_configure_vp_wfq_on_link_change(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 min_pf_rate) @@ -1766,6 +2045,42 @@ static int __qed_configure_vp_wfq_on_link_change(struct qed_hwfn *p_hwfn, return rc; } +/* Main API for qed clients to configure vport min rate. + * vp_id - vport id in PF Range[0 - (total_num_vports_per_pf - 1)] + * rate - Speed in Mbps needs to be assigned to a given vport. + */ +int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate) +{ + int i, rc = -EINVAL; + + /* Currently not supported; Might change in future */ + if (cdev->num_hwfns > 1) { + DP_NOTICE(cdev, + "WFQ configuration is not supported for this device\n"); + return rc; + } + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + struct qed_ptt *p_ptt; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EBUSY; + + rc = __qed_configure_vport_wfq(p_hwfn, p_ptt, vp_id, rate); + + if (!rc) { + qed_ptt_release(p_hwfn, p_ptt); + return rc; + } + + qed_ptt_release(p_hwfn, p_ptt); + } + + return rc; +} + /* API to configure WFQ from mcp link change */ void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate) { @@ -1912,3 +2227,17 @@ int qed_configure_pf_min_bandwidth(struct qed_dev *cdev, u8 min_bw) return rc; } + +void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_mcp_link_state *p_link; + + p_link = &p_hwfn->mcp_info->link_output; + + if (p_link->min_pf_rate) + qed_disable_wfq_for_all_vports(p_hwfn, p_ptt, + p_link->min_pf_rate); + + memset(p_hwfn->qm_info.wfq_data, 0, + sizeof(*p_hwfn->qm_info.wfq_data) * p_hwfn->qm_info.num_vports); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 6aac3f855aa1..dde364d6f502 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -182,11 +182,15 @@ enum qed_dmae_address_type_t { * used mostly to write a zeroed buffer to destination address * using DMA */ -#define QED_DMAE_FLAG_RW_REPL_SRC 0x00000001 -#define QED_DMAE_FLAG_COMPLETION_DST 0x00000008 +#define QED_DMAE_FLAG_RW_REPL_SRC 0x00000001 +#define QED_DMAE_FLAG_VF_SRC 0x00000002 +#define QED_DMAE_FLAG_VF_DST 0x00000004 +#define QED_DMAE_FLAG_COMPLETION_DST 0x00000008 struct qed_dmae_params { - u32 flags; /* consists of QED_DMAE_FLAG_* values */ + u32 flags; /* consists of QED_DMAE_FLAG_* values */ + u8 src_vfid; + u8 dst_vfid; }; /** @@ -209,6 +213,23 @@ qed_dmae_host2grc(struct qed_hwfn *p_hwfn, u32 flags); /** + * @brief qed_dmae_host2host - copy data from to source address + * to a destination adress (for SRIOV) using the given ptt + * + * @param p_hwfn + * @param p_ptt + * @param source_addr + * @param dest_addr + * @param size_in_dwords + * @param params + */ +int qed_dmae_host2host(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + dma_addr_t source_addr, + dma_addr_t dest_addr, + u32 size_in_dwords, struct qed_dmae_params *p_params); + +/** * @brief qed_chain_alloc - Allocate and initialize a chain * * @param p_hwfn @@ -282,11 +303,11 @@ int qed_fw_rss_eng(struct qed_hwfn *p_hwfn, * @param p_hwfn * @param p_ptt * @param id - For PF, engine-relative. For VF, PF-relative. + * @param is_vf - true iff cleanup is made for a VF. * * @return int */ int qed_final_cleanup(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u16 id); + struct qed_ptt *p_ptt, u16 id, bool is_vf); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index c4fae71bed11..9afc15fdbb02 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -29,9 +29,9 @@ struct qed_ptt; enum common_event_opcode { COMMON_EVENT_PF_START, COMMON_EVENT_PF_STOP, - COMMON_EVENT_RESERVED, - COMMON_EVENT_RESERVED2, - COMMON_EVENT_RESERVED3, + COMMON_EVENT_VF_START, + COMMON_EVENT_VF_STOP, + COMMON_EVENT_VF_PF_CHANNEL, COMMON_EVENT_RESERVED4, COMMON_EVENT_RESERVED5, COMMON_EVENT_RESERVED6, @@ -44,8 +44,8 @@ enum common_ramrod_cmd_id { COMMON_RAMROD_UNUSED, COMMON_RAMROD_PF_START /* PF Function Start Ramrod */, COMMON_RAMROD_PF_STOP /* PF Function Stop Ramrod */, - COMMON_RAMROD_RESERVED, - COMMON_RAMROD_RESERVED2, + COMMON_RAMROD_VF_START, + COMMON_RAMROD_VF_STOP, COMMON_RAMROD_PF_UPDATE, COMMON_RAMROD_EMPTY, MAX_COMMON_RAMROD_CMD_ID @@ -573,6 +573,14 @@ union event_ring_element { struct event_ring_next_addr next_addr; }; +struct mstorm_non_trigger_vf_zone { + struct eth_mstorm_per_queue_stat eth_queue_stat; +}; + +struct mstorm_vf_zone { + struct mstorm_non_trigger_vf_zone non_trigger; +}; + enum personality_type { BAD_PERSONALITY_TYP, PERSONALITY_RESERVED, @@ -626,6 +634,14 @@ struct pf_start_ramrod_data { u8 reserved0[4]; }; +/* Data for port update ramrod */ +struct protocol_dcb_data { + u8 dcb_enable_flag; + u8 dcb_priority; + u8 dcb_tc; + u8 reserved; +}; + /* tunnel configuration */ struct pf_update_tunnel_config { u8 update_rx_pf_clss; @@ -648,8 +664,17 @@ struct pf_update_tunnel_config { }; struct pf_update_ramrod_data { - u32 reserved[2]; - u32 reserved_1[6]; + u8 pf_id; + u8 update_eth_dcb_data_flag; + u8 update_fcoe_dcb_data_flag; + u8 update_iscsi_dcb_data_flag; + u8 update_roce_dcb_data_flag; + u8 update_mf_vlan_flag; + __le16 mf_vlan; + struct protocol_dcb_data eth_dcb_data; + struct protocol_dcb_data fcoe_dcb_data; + struct protocol_dcb_data iscsi_dcb_data; + struct protocol_dcb_data roce_dcb_data; struct pf_update_tunnel_config tunnel_config; }; @@ -671,6 +696,16 @@ enum ports_mode { MAX_PORTS_MODE }; +struct pstorm_non_trigger_vf_zone { + struct eth_pstorm_per_queue_stat eth_queue_stat; + struct regpair reserved[2]; +}; + +struct pstorm_vf_zone { + struct pstorm_non_trigger_vf_zone non_trigger; + struct regpair reserved[7]; +}; + /* Ramrod Header of SPQE */ struct ramrod_header { __le32 cid /* Slowpath Connection CID */; @@ -700,6 +735,36 @@ struct tstorm_per_port_stat { struct regpair preroce_irregular_pkt; }; +struct ustorm_non_trigger_vf_zone { + struct eth_ustorm_per_queue_stat eth_queue_stat; + struct regpair vf_pf_msg_addr; +}; + +struct ustorm_trigger_vf_zone { + u8 vf_pf_msg_valid; + u8 reserved[7]; +}; + +struct ustorm_vf_zone { + struct ustorm_non_trigger_vf_zone non_trigger; + struct ustorm_trigger_vf_zone trigger; +}; + +struct vf_start_ramrod_data { + u8 vf_id; + u8 enable_flr_ack; + __le16 opaque_fid; + u8 personality; + u8 reserved[3]; +}; + +struct vf_stop_ramrod_data { + u8 vf_id; + u8 reserved0; + __le16 reserved1; + __le32 reserved2; +}; + struct atten_status_block { __le32 atten_bits; __le32 atten_ack; @@ -1026,7 +1091,7 @@ enum init_phases { PHASE_ENGINE, PHASE_PORT, PHASE_PF, - PHASE_RESERVED, + PHASE_VF, PHASE_QM_PF, MAX_INIT_PHASES }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.c b/drivers/net/ethernet/qlogic/qed/qed_hw.c index a95a3e4b3101..0ada7fdb91bc 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.c +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.c @@ -23,6 +23,7 @@ #include "qed_hsi.h" #include "qed_hw.h" #include "qed_reg_addr.h" +#include "qed_sriov.h" #define QED_BAR_ACQUIRE_TIMEOUT 1000 @@ -236,8 +237,12 @@ static void qed_memcpy_hw(struct qed_hwfn *p_hwfn, quota = min_t(size_t, n - done, PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE); - qed_ptt_set_win(p_hwfn, p_ptt, hw_addr + done); - hw_offset = qed_ptt_get_bar_addr(p_ptt); + if (IS_PF(p_hwfn->cdev)) { + qed_ptt_set_win(p_hwfn, p_ptt, hw_addr + done); + hw_offset = qed_ptt_get_bar_addr(p_ptt); + } else { + hw_offset = hw_addr + done; + } dw_count = quota / 4; host_addr = (u32 *)((u8 *)addr + done); @@ -338,14 +343,25 @@ void qed_port_unpretend(struct qed_hwfn *p_hwfn, *(u32 *)&p_ptt->pxp.pretend); } +u32 qed_vfid_to_concrete(struct qed_hwfn *p_hwfn, u8 vfid) +{ + u32 concrete_fid = 0; + + SET_FIELD(concrete_fid, PXP_CONCRETE_FID_PFID, p_hwfn->rel_pf_id); + SET_FIELD(concrete_fid, PXP_CONCRETE_FID_VFID, vfid); + SET_FIELD(concrete_fid, PXP_CONCRETE_FID_VFVALID, 1); + + return concrete_fid; +} + /* DMAE */ static void qed_dmae_opcode(struct qed_hwfn *p_hwfn, const u8 is_src_type_grc, const u8 is_dst_type_grc, struct qed_dmae_params *p_params) { + u16 opcode_b = 0; u32 opcode = 0; - u16 opcodeB = 0; /* Whether the source is the PCIe or the GRC. * 0- The source is the PCIe @@ -387,14 +403,24 @@ static void qed_dmae_opcode(struct qed_hwfn *p_hwfn, opcode |= (DMAE_CMD_DST_ADDR_RESET_MASK << DMAE_CMD_DST_ADDR_RESET_SHIFT); - opcodeB |= (DMAE_CMD_SRC_VF_ID_MASK << - DMAE_CMD_SRC_VF_ID_SHIFT); + /* SRC/DST VFID: all 1's - pf, otherwise VF id */ + if (p_params->flags & QED_DMAE_FLAG_VF_SRC) { + opcode |= 1 << DMAE_CMD_SRC_VF_ID_VALID_SHIFT; + opcode_b |= p_params->src_vfid << DMAE_CMD_SRC_VF_ID_SHIFT; + } else { + opcode_b |= DMAE_CMD_SRC_VF_ID_MASK << + DMAE_CMD_SRC_VF_ID_SHIFT; + } - opcodeB |= (DMAE_CMD_DST_VF_ID_MASK << - DMAE_CMD_DST_VF_ID_SHIFT); + if (p_params->flags & QED_DMAE_FLAG_VF_DST) { + opcode |= 1 << DMAE_CMD_DST_VF_ID_VALID_SHIFT; + opcode_b |= p_params->dst_vfid << DMAE_CMD_DST_VF_ID_SHIFT; + } else { + opcode_b |= DMAE_CMD_DST_VF_ID_MASK << DMAE_CMD_DST_VF_ID_SHIFT; + } p_hwfn->dmae_info.p_dmae_cmd->opcode = cpu_to_le32(opcode); - p_hwfn->dmae_info.p_dmae_cmd->opcode_b = cpu_to_le16(opcodeB); + p_hwfn->dmae_info.p_dmae_cmd->opcode_b = cpu_to_le16(opcode_b); } u32 qed_dmae_idx_to_go_cmd(u8 idx) @@ -742,6 +768,28 @@ int qed_dmae_host2grc(struct qed_hwfn *p_hwfn, return rc; } +int +qed_dmae_host2host(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + dma_addr_t source_addr, + dma_addr_t dest_addr, + u32 size_in_dwords, struct qed_dmae_params *p_params) +{ + int rc; + + mutex_lock(&(p_hwfn->dmae_info.mutex)); + + rc = qed_dmae_execute_command(p_hwfn, p_ptt, source_addr, + dest_addr, + QED_DMAE_ADDRESS_HOST_PHYS, + QED_DMAE_ADDRESS_HOST_PHYS, + size_in_dwords, p_params); + + mutex_unlock(&(p_hwfn->dmae_info.mutex)); + + return rc; +} + u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn, enum protocol_type proto, union qed_qm_pq_params *p_params) @@ -765,6 +813,9 @@ u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn, break; case PROTOCOLID_ETH: pq_id = p_params->eth.tc; + if (p_params->eth.is_vf) + pq_id += p_hwfn->qm_info.vf_queues_offset + + p_params->eth.vf_id; break; default: pq_id = 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.h b/drivers/net/ethernet/qlogic/qed/qed_hw.h index e56d433793be..4367363ade40 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.h @@ -221,6 +221,16 @@ void qed_port_unpretend(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); /** + * @brief qed_vfid_to_concrete - build a concrete FID for a + * given VF ID + * + * @param p_hwfn + * @param p_ptt + * @param vfid + */ +u32 qed_vfid_to_concrete(struct qed_hwfn *p_hwfn, u8 vfid); + +/** * @brief qed_dmae_idx_to_go_cmd - map the idx to dmae cmd * this is declared here since other files will require it. * @param idx diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c index 3269b3610e03..d358c3bb1308 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c @@ -18,6 +18,7 @@ #include "qed_hw.h" #include "qed_init_ops.h" #include "qed_reg_addr.h" +#include "qed_sriov.h" #define QED_INIT_MAX_POLL_COUNT 100 #define QED_INIT_POLL_PERIOD_US 500 @@ -128,6 +129,9 @@ int qed_init_alloc(struct qed_hwfn *p_hwfn) { struct qed_rt_data *rt_data = &p_hwfn->rt_data; + if (IS_VF(p_hwfn->cdev)) + return 0; + rt_data->b_valid = kzalloc(sizeof(bool) * RUNTIME_ARRAY_SIZE, GFP_KERNEL); if (!rt_data->b_valid) diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index 2017b0121f5f..09a6ad3d22dd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -26,6 +26,8 @@ #include "qed_mcp.h" #include "qed_reg_addr.h" #include "qed_sp.h" +#include "qed_sriov.h" +#include "qed_vf.h" struct qed_pi_info { qed_int_comp_cb_t comp_cb; @@ -2513,6 +2515,9 @@ void qed_int_cau_conf_pi(struct qed_hwfn *p_hwfn, u32 sb_offset; u32 pi_offset; + if (IS_VF(p_hwfn->cdev)) + return; + sb_offset = igu_sb_id * PIS_PER_SB; memset(&pi_entry, 0, sizeof(struct cau_pi_entry)); @@ -2542,8 +2547,9 @@ void qed_int_sb_setup(struct qed_hwfn *p_hwfn, sb_info->sb_ack = 0; memset(sb_info->sb_virt, 0, sizeof(*sb_info->sb_virt)); - qed_int_cau_conf_sb(p_hwfn, p_ptt, sb_info->sb_phys, - sb_info->igu_sb_id, 0, 0); + if (IS_PF(p_hwfn->cdev)) + qed_int_cau_conf_sb(p_hwfn, p_ptt, sb_info->sb_phys, + sb_info->igu_sb_id, 0, 0); } /** @@ -2563,8 +2569,10 @@ static u16 qed_get_igu_sb_id(struct qed_hwfn *p_hwfn, /* Assuming continuous set of IGU SBs dedicated for given PF */ if (sb_id == QED_SP_SB_ID) igu_sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id; - else + else if (IS_PF(p_hwfn->cdev)) igu_sb_id = sb_id + p_hwfn->hw_info.p_igu_info->igu_base_sb; + else + igu_sb_id = qed_vf_get_igu_sb_id(p_hwfn, sb_id); DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, "SB [%s] index is 0x%04x\n", (sb_id == QED_SP_SB_ID) ? "DSB" : "non-DSB", igu_sb_id); @@ -2594,9 +2602,16 @@ int qed_int_sb_init(struct qed_hwfn *p_hwfn, /* The igu address will hold the absolute address that needs to be * written to for a specific status block */ - sb_info->igu_addr = (u8 __iomem *)p_hwfn->regview + - GTT_BAR0_MAP_REG_IGU_CMD + - (sb_info->igu_sb_id << 3); + if (IS_PF(p_hwfn->cdev)) { + sb_info->igu_addr = (u8 __iomem *)p_hwfn->regview + + GTT_BAR0_MAP_REG_IGU_CMD + + (sb_info->igu_sb_id << 3); + } else { + sb_info->igu_addr = (u8 __iomem *)p_hwfn->regview + + PXP_VF_BAR0_START_IGU + + ((IGU_CMD_INT_ACK_BASE + + sb_info->igu_sb_id) << 3); + } sb_info->flags |= QED_SB_INFO_INIT; @@ -2783,24 +2798,20 @@ void qed_int_igu_disable_int(struct qed_hwfn *p_hwfn, { p_hwfn->b_int_enabled = 0; + if (IS_VF(p_hwfn->cdev)) + return; + qed_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, 0); } #define IGU_CLEANUP_SLEEP_LENGTH (1000) -void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 sb_id, - bool cleanup_set, - u16 opaque_fid - ) +static void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 sb_id, bool cleanup_set, u16 opaque_fid) { + u32 cmd_ctrl = 0, val = 0, sb_bit = 0, sb_bit_addr = 0, data = 0; u32 pxp_addr = IGU_CMD_INT_ACK_BASE + sb_id; u32 sleep_cnt = IGU_CLEANUP_SLEEP_LENGTH; - u32 data = 0; - u32 cmd_ctrl = 0; - u32 val = 0; - u32 sb_bit = 0; - u32 sb_bit_addr = 0; /* Set the data field */ SET_FIELD(data, IGU_CLEANUP_CLEANUP_SET, cleanup_set ? 1 : 0); @@ -2845,11 +2856,9 @@ void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn, void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u32 sb_id, - u16 opaque, - bool b_set) + u32 sb_id, u16 opaque, bool b_set) { - int pi; + int pi, i; /* Set */ if (b_set) @@ -2858,6 +2867,22 @@ void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn, /* Clear */ qed_int_igu_cleanup_sb(p_hwfn, p_ptt, sb_id, 0, opaque); + /* Wait for the IGU SB to cleanup */ + for (i = 0; i < IGU_CLEANUP_SLEEP_LENGTH; i++) { + u32 val; + + val = qed_rd(p_hwfn, p_ptt, + IGU_REG_WRITE_DONE_PENDING + ((sb_id / 32) * 4)); + if (val & (1 << (sb_id % 32))) + usleep_range(10, 20); + else + break; + } + if (i == IGU_CLEANUP_SLEEP_LENGTH) + DP_NOTICE(p_hwfn, + "Failed SB[0x%08x] still appearing in WRITE_DONE_PENDING\n", + sb_id); + /* Clear the CAU for the SB */ for (pi = 0; pi < 12; pi++) qed_wr(p_hwfn, p_ptt, @@ -2866,13 +2891,11 @@ void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn, void qed_int_igu_init_pure_rt(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - bool b_set, - bool b_slowpath) + bool b_set, bool b_slowpath) { u32 igu_base_sb = p_hwfn->hw_info.p_igu_info->igu_base_sb; u32 igu_sb_cnt = p_hwfn->hw_info.p_igu_info->igu_sb_cnt; - u32 sb_id = 0; - u32 val = 0; + u32 sb_id = 0, val = 0; val = qed_rd(p_hwfn, p_ptt, IGU_REG_BLOCK_CONFIGURATION); val |= IGU_REG_BLOCK_CONFIGURATION_VF_CLEANUP_EN; @@ -2888,14 +2911,14 @@ void qed_int_igu_init_pure_rt(struct qed_hwfn *p_hwfn, p_hwfn->hw_info.opaque_fid, b_set); - if (b_slowpath) { - sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id; - DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, - "IGU cleaning slowpath SB [%d]\n", sb_id); - qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id, - p_hwfn->hw_info.opaque_fid, - b_set); - } + if (!b_slowpath) + return; + + sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id; + DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, + "IGU cleaning slowpath SB [%d]\n", sb_id); + qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id, + p_hwfn->hw_info.opaque_fid, b_set); } static u32 qed_int_igu_read_cam_block(struct qed_hwfn *p_hwfn, @@ -2935,9 +2958,9 @@ int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { struct qed_igu_info *p_igu_info; + u32 val, min_vf = 0, max_vf = 0; + u16 sb_id, last_iov_sb_id = 0; struct qed_igu_block *blk; - u32 val; - u16 sb_id; u16 prev_sb_id = 0xFF; p_hwfn->hw_info.p_igu_info = kzalloc(sizeof(*p_igu_info), GFP_KERNEL); @@ -2947,12 +2970,19 @@ int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn, p_igu_info = p_hwfn->hw_info.p_igu_info; - /* Initialize base sb / sb cnt for PFs */ + /* Initialize base sb / sb cnt for PFs and VFs */ p_igu_info->igu_base_sb = 0xffff; p_igu_info->igu_sb_cnt = 0; p_igu_info->igu_dsb_id = 0xffff; p_igu_info->igu_base_sb_iov = 0xffff; + if (p_hwfn->cdev->p_iov_info) { + struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info; + + min_vf = p_iov->first_vf_in_pf; + max_vf = p_iov->first_vf_in_pf + p_iov->total_vfs; + } + for (sb_id = 0; sb_id < QED_MAPPING_MEMORY_SIZE(p_hwfn->cdev); sb_id++) { blk = &p_igu_info->igu_map.igu_blocks[sb_id]; @@ -2986,14 +3016,43 @@ int qed_int_igu_read_cam(struct qed_hwfn *p_hwfn, (p_igu_info->igu_sb_cnt)++; } } + } else { + if ((blk->function_id >= min_vf) && + (blk->function_id < max_vf)) { + /* Available for VFs of this PF */ + if (p_igu_info->igu_base_sb_iov == 0xffff) { + p_igu_info->igu_base_sb_iov = sb_id; + } else if (last_iov_sb_id != sb_id - 1) { + if (!val) { + DP_VERBOSE(p_hwfn->cdev, + NETIF_MSG_INTR, + "First uninitialized IGU CAM entry at index 0x%04x\n", + sb_id); + } else { + DP_NOTICE(p_hwfn->cdev, + "Consecutive igu vectors for HWFN %x vfs is broken [jumps from %04x to %04x]\n", + p_hwfn->rel_pf_id, + last_iov_sb_id, + sb_id); } + break; + } + blk->status |= QED_IGU_STATUS_FREE; + p_hwfn->hw_info.p_igu_info->free_blks++; + last_iov_sb_id = sb_id; + } } } - - DP_VERBOSE(p_hwfn, NETIF_MSG_INTR, - "IGU igu_base_sb=0x%x igu_sb_cnt=%d igu_dsb_id=0x%x\n", - p_igu_info->igu_base_sb, - p_igu_info->igu_sb_cnt, - p_igu_info->igu_dsb_id); + p_igu_info->igu_sb_cnt_iov = p_igu_info->free_blks; + + DP_VERBOSE( + p_hwfn, + NETIF_MSG_INTR, + "IGU igu_base_sb=0x%x [IOV 0x%x] igu_sb_cnt=%d [IOV 0x%x] igu_dsb_id=0x%x\n", + p_igu_info->igu_base_sb, + p_igu_info->igu_base_sb_iov, + p_igu_info->igu_sb_cnt, + p_igu_info->igu_sb_cnt_iov, + p_igu_info->igu_dsb_id); if (p_igu_info->igu_base_sb == 0xffff || p_igu_info->igu_dsb_id == 0xffff || @@ -3116,6 +3175,23 @@ void qed_int_get_num_sbs(struct qed_hwfn *p_hwfn, p_sb_cnt_info->sb_free_blk = info->free_blks; } +u16 qed_int_queue_id_from_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) +{ + struct qed_igu_info *p_info = p_hwfn->hw_info.p_igu_info; + + /* Determine origin of SB id */ + if ((sb_id >= p_info->igu_base_sb) && + (sb_id < p_info->igu_base_sb + p_info->igu_sb_cnt)) { + return sb_id - p_info->igu_base_sb; + } else if ((sb_id >= p_info->igu_base_sb_iov) && + (sb_id < p_info->igu_base_sb_iov + p_info->igu_sb_cnt_iov)) { + return sb_id - p_info->igu_base_sb_iov + p_info->igu_sb_cnt; + } else { + DP_NOTICE(p_hwfn, "SB %d not in range for function\n", sb_id); + return 0; + } +} + void qed_int_disable_post_isr_release(struct qed_dev *cdev) { int i; diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.h b/drivers/net/ethernet/qlogic/qed/qed_int.h index c57f2e680770..20b468637504 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.h +++ b/drivers/net/ethernet/qlogic/qed/qed_int.h @@ -20,6 +20,12 @@ #define IGU_PF_CONF_ATTN_BIT_EN (0x1 << 3) /* attention enable */ #define IGU_PF_CONF_SINGLE_ISR_EN (0x1 << 4) /* single ISR mode enable */ #define IGU_PF_CONF_SIMD_MODE (0x1 << 5) /* simd all ones mode */ +/* Fields of IGU VF CONFIGRATION REGISTER */ +#define IGU_VF_CONF_FUNC_EN (0x1 << 0) /* function enable */ +#define IGU_VF_CONF_MSI_MSIX_EN (0x1 << 1) /* MSI/MSIX enable */ +#define IGU_VF_CONF_SINGLE_ISR_EN (0x1 << 4) /* single ISR mode enable */ +#define IGU_VF_CONF_PARENT_MASK (0xF) /* Parent PF */ +#define IGU_VF_CONF_PARENT_SHIFT 5 /* Parent PF */ /* Igu control commands */ @@ -292,26 +298,8 @@ u16 qed_int_get_sp_sb_id(struct qed_hwfn *p_hwfn); * @param p_hwfn * @param p_ptt * @param sb_id - igu status block id - * @param cleanup_set - set(1) / clear(0) - * @param opaque_fid - the function for which to perform - * cleanup, for example a PF on behalf of - * its VFs. - */ -void qed_int_igu_cleanup_sb(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 sb_id, - bool cleanup_set, - u16 opaque_fid); - -/** - * @brief Status block cleanup. Should be called for each status - * block that will be used -> both PF / VF - * - * @param p_hwfn - * @param p_ptt - * @param sb_id - igu status block id * @param opaque - opaque fid of the sb owner. - * @param cleanup_set - set(1) / clear(0) + * @param b_set - set(1) / clear(0) */ void qed_int_igu_init_pure_rt_single(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, @@ -365,6 +353,16 @@ void qed_int_setup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); /** + * @brief - Returns an Rx queue index appropriate for usage with given SB. + * + * @param p_hwfn + * @param sb_id - absolute index of SB + * + * @return index of Rx queue + */ +u16 qed_int_queue_id_from_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id); + +/** * @brief - Enable Interrupt & Attention for hw function * * @param p_hwfn diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 31e1d510a991..8fba87dd48af 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -31,124 +31,25 @@ #include "qed_hsi.h" #include "qed_hw.h" #include "qed_int.h" +#include "qed_l2.h" #include "qed_mcp.h" #include "qed_reg_addr.h" #include "qed_sp.h" +#include "qed_sriov.h" -struct qed_rss_params { - u8 update_rss_config; - u8 rss_enable; - u8 rss_eng_id; - u8 update_rss_capabilities; - u8 update_rss_ind_table; - u8 update_rss_key; - u8 rss_caps; - u8 rss_table_size_log; - u16 rss_ind_table[QED_RSS_IND_TABLE_SIZE]; - u32 rss_key[QED_RSS_KEY_SIZE]; -}; - -enum qed_filter_opcode { - QED_FILTER_ADD, - QED_FILTER_REMOVE, - QED_FILTER_MOVE, - QED_FILTER_REPLACE, /* Delete all MACs and add new one instead */ - QED_FILTER_FLUSH, /* Removes all filters */ -}; - -enum qed_filter_ucast_type { - QED_FILTER_MAC, - QED_FILTER_VLAN, - QED_FILTER_MAC_VLAN, - QED_FILTER_INNER_MAC, - QED_FILTER_INNER_VLAN, - QED_FILTER_INNER_PAIR, - QED_FILTER_INNER_MAC_VNI_PAIR, - QED_FILTER_MAC_VNI_PAIR, - QED_FILTER_VNI, -}; - -struct qed_filter_ucast { - enum qed_filter_opcode opcode; - enum qed_filter_ucast_type type; - u8 is_rx_filter; - u8 is_tx_filter; - u8 vport_to_add_to; - u8 vport_to_remove_from; - unsigned char mac[ETH_ALEN]; - u8 assert_on_error; - u16 vlan; - u32 vni; -}; - -struct qed_filter_mcast { - /* MOVE is not supported for multicast */ - enum qed_filter_opcode opcode; - u8 vport_to_add_to; - u8 vport_to_remove_from; - u8 num_mc_addrs; -#define QED_MAX_MC_ADDRS 64 - unsigned char mac[QED_MAX_MC_ADDRS][ETH_ALEN]; -}; - -struct qed_filter_accept_flags { - u8 update_rx_mode_config; - u8 update_tx_mode_config; - u8 rx_accept_filter; - u8 tx_accept_filter; -#define QED_ACCEPT_NONE 0x01 -#define QED_ACCEPT_UCAST_MATCHED 0x02 -#define QED_ACCEPT_UCAST_UNMATCHED 0x04 -#define QED_ACCEPT_MCAST_MATCHED 0x08 -#define QED_ACCEPT_MCAST_UNMATCHED 0x10 -#define QED_ACCEPT_BCAST 0x20 -}; - -struct qed_sp_vport_update_params { - u16 opaque_fid; - u8 vport_id; - u8 update_vport_active_rx_flg; - u8 vport_active_rx_flg; - u8 update_vport_active_tx_flg; - u8 vport_active_tx_flg; - u8 update_approx_mcast_flg; - u8 update_accept_any_vlan_flg; - u8 accept_any_vlan; - unsigned long bins[8]; - struct qed_rss_params *rss_params; - struct qed_filter_accept_flags accept_flags; -}; - -enum qed_tpa_mode { - QED_TPA_MODE_NONE, - QED_TPA_MODE_UNUSED, - QED_TPA_MODE_GRO, - QED_TPA_MODE_MAX -}; - -struct qed_sp_vport_start_params { - enum qed_tpa_mode tpa_mode; - bool remove_inner_vlan; - bool drop_ttl0; - u8 max_buffers_per_cqe; - u32 concrete_fid; - u16 opaque_fid; - u8 vport_id; - u16 mtu; -}; #define QED_MAX_SGES_NUM 16 #define CRC32_POLY 0x1edc6f41 -static int qed_sp_vport_start(struct qed_hwfn *p_hwfn, - struct qed_sp_vport_start_params *p_params) +int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_start_params *p_params) { struct vport_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; + u8 abs_vport_id = 0; int rc = -EINVAL; u16 rx_mode = 0; - u8 abs_vport_id = 0; rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id); if (rc != 0) @@ -198,6 +99,8 @@ static int qed_sp_vport_start(struct qed_hwfn *p_hwfn, break; } + p_ramrod->tx_switching_en = p_params->tx_switching; + /* Software Function ID in hwfn (PFs are 0 - 15, VFs are 16 - 135) */ p_ramrod->sw_fid = qed_concrete_to_sw_fid(p_hwfn->cdev, p_params->concrete_fid); @@ -205,6 +108,21 @@ static int qed_sp_vport_start(struct qed_hwfn *p_hwfn, return qed_spq_post(p_hwfn, p_ent, NULL); } +int qed_sp_vport_start(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_start_params *p_params) +{ + if (IS_VF(p_hwfn->cdev)) { + return qed_vf_pf_vport_start(p_hwfn, p_params->vport_id, + p_params->mtu, + p_params->remove_inner_vlan, + p_params->tpa_mode, + p_params->max_buffers_per_cqe, + p_params->only_untagged); + } + + return qed_sp_eth_vport_start(p_hwfn, p_params); +} + static int qed_sp_vport_update_rss(struct qed_hwfn *p_hwfn, struct vport_update_ramrod_data *p_ramrod, @@ -350,6 +268,38 @@ qed_sp_update_accept_mode(struct qed_hwfn *p_hwfn, } static void +qed_sp_vport_update_sge_tpa(struct qed_hwfn *p_hwfn, + struct vport_update_ramrod_data *p_ramrod, + struct qed_sge_tpa_params *p_params) +{ + struct eth_vport_tpa_param *p_tpa; + + if (!p_params) { + p_ramrod->common.update_tpa_param_flg = 0; + p_ramrod->common.update_tpa_en_flg = 0; + p_ramrod->common.update_tpa_param_flg = 0; + return; + } + + p_ramrod->common.update_tpa_en_flg = p_params->update_tpa_en_flg; + p_tpa = &p_ramrod->tpa_param; + p_tpa->tpa_ipv4_en_flg = p_params->tpa_ipv4_en_flg; + p_tpa->tpa_ipv6_en_flg = p_params->tpa_ipv6_en_flg; + p_tpa->tpa_ipv4_tunn_en_flg = p_params->tpa_ipv4_tunn_en_flg; + p_tpa->tpa_ipv6_tunn_en_flg = p_params->tpa_ipv6_tunn_en_flg; + + p_ramrod->common.update_tpa_param_flg = p_params->update_tpa_param_flg; + p_tpa->max_buff_num = p_params->max_buffers_per_cqe; + p_tpa->tpa_pkt_split_flg = p_params->tpa_pkt_split_flg; + p_tpa->tpa_hdr_data_split_flg = p_params->tpa_hdr_data_split_flg; + p_tpa->tpa_gro_consistent_flg = p_params->tpa_gro_consistent_flg; + p_tpa->tpa_max_aggs_num = p_params->tpa_max_aggs_num; + p_tpa->tpa_max_size = p_params->tpa_max_size; + p_tpa->tpa_min_size_to_start = p_params->tpa_min_size_to_start; + p_tpa->tpa_min_size_to_cont = p_params->tpa_min_size_to_cont; +} + +static void qed_sp_update_mcast_bin(struct qed_hwfn *p_hwfn, struct vport_update_ramrod_data *p_ramrod, struct qed_sp_vport_update_params *p_params) @@ -370,20 +320,24 @@ qed_sp_update_mcast_bin(struct qed_hwfn *p_hwfn, } } -static int -qed_sp_vport_update(struct qed_hwfn *p_hwfn, - struct qed_sp_vport_update_params *p_params, - enum spq_mode comp_mode, - struct qed_spq_comp_cb *p_comp_data) +int qed_sp_vport_update(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_params, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data) { struct qed_rss_params *p_rss_params = p_params->rss_params; struct vport_update_ramrod_data_cmn *p_cmn; struct qed_sp_init_data init_data; struct vport_update_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; - u8 abs_vport_id = 0; + u8 abs_vport_id = 0, val; int rc = -EINVAL; + if (IS_VF(p_hwfn->cdev)) { + rc = qed_vf_pf_vport_update(p_hwfn, p_params); + return rc; + } + rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id); if (rc != 0) return rc; @@ -412,6 +366,27 @@ qed_sp_vport_update(struct qed_hwfn *p_hwfn, p_cmn->accept_any_vlan = p_params->accept_any_vlan; p_cmn->update_accept_any_vlan_flg = p_params->update_accept_any_vlan_flg; + + p_cmn->inner_vlan_removal_en = p_params->inner_vlan_removal_flg; + val = p_params->update_inner_vlan_removal_flg; + p_cmn->update_inner_vlan_removal_en_flg = val; + + p_cmn->default_vlan_en = p_params->default_vlan_enable_flg; + val = p_params->update_default_vlan_enable_flg; + p_cmn->update_default_vlan_en_flg = val; + + p_cmn->default_vlan = cpu_to_le16(p_params->default_vlan); + p_cmn->update_default_vlan_flg = p_params->update_default_vlan_flg; + + p_cmn->silent_vlan_removal_en = p_params->silent_vlan_removal_flg; + + p_ramrod->common.tx_switching_en = p_params->tx_switching_flg; + p_cmn->update_tx_switching_en_flg = p_params->update_tx_switching_flg; + + p_cmn->anti_spoofing_en = p_params->anti_spoofing_en; + val = p_params->update_anti_spoofing_en_flg; + p_ramrod->common.update_anti_spoofing_en_flg = val; + rc = qed_sp_vport_update_rss(p_hwfn, p_ramrod, p_rss_params); if (rc) { /* Return spq entry which is taken in qed_sp_init_request()*/ @@ -423,12 +398,11 @@ qed_sp_vport_update(struct qed_hwfn *p_hwfn, qed_sp_update_mcast_bin(p_hwfn, p_ramrod, p_params); qed_sp_update_accept_mode(p_hwfn, p_ramrod, p_params->accept_flags); + qed_sp_vport_update_sge_tpa(p_hwfn, p_ramrod, p_params->sge_tpa_params); return qed_spq_post(p_hwfn, p_ent, NULL); } -static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u8 vport_id) +int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, u16 opaque_fid, u8 vport_id) { struct vport_stop_ramrod_data *p_ramrod; struct qed_sp_init_data init_data; @@ -436,6 +410,9 @@ static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, u8 abs_vport_id = 0; int rc; + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_vport_stop(p_hwfn); + rc = qed_fw_vport(p_hwfn, vport_id, &abs_vport_id); if (rc != 0) return rc; @@ -457,13 +434,26 @@ static int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, return qed_spq_post(p_hwfn, p_ent, NULL); } +static int +qed_vf_pf_accept_flags(struct qed_hwfn *p_hwfn, + struct qed_filter_accept_flags *p_accept_flags) +{ + struct qed_sp_vport_update_params s_params; + + memset(&s_params, 0, sizeof(s_params)); + memcpy(&s_params.accept_flags, p_accept_flags, + sizeof(struct qed_filter_accept_flags)); + + return qed_vf_pf_vport_update(p_hwfn, &s_params); +} + static int qed_filter_accept_cmd(struct qed_dev *cdev, u8 vport, struct qed_filter_accept_flags accept_flags, u8 update_accept_any_vlan, u8 accept_any_vlan, - enum spq_mode comp_mode, - struct qed_spq_comp_cb *p_comp_data) + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data) { struct qed_sp_vport_update_params vport_update_params; int i, rc; @@ -480,6 +470,13 @@ static int qed_filter_accept_cmd(struct qed_dev *cdev, vport_update_params.opaque_fid = p_hwfn->hw_info.opaque_fid; + if (IS_VF(cdev)) { + rc = qed_vf_pf_accept_flags(p_hwfn, &accept_flags); + if (rc) + return rc; + continue; + } + rc = qed_sp_vport_update(p_hwfn, &vport_update_params, comp_mode, p_comp_data); if (rc != 0) { @@ -514,16 +511,14 @@ static int qed_sp_release_queue_cid( return 0; } -static int -qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *params, - u8 stats_id, - u16 bd_max_bytes, - dma_addr_t bd_chain_phys_addr, - dma_addr_t cqe_pbl_addr, - u16 cqe_pbl_size) +int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + struct qed_queue_start_common_params *params, + u8 stats_id, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size) { struct rx_queue_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; @@ -592,8 +587,7 @@ qed_sp_eth_rx_queue_start(struct qed_hwfn *p_hwfn, u16 bd_max_bytes, dma_addr_t bd_chain_phys_addr, dma_addr_t cqe_pbl_addr, - u16 cqe_pbl_size, - void __iomem **pp_prod) + u16 cqe_pbl_size, void __iomem **pp_prod) { struct qed_hw_cid_data *p_rx_cid; u64 init_prod_val = 0; @@ -601,6 +595,16 @@ qed_sp_eth_rx_queue_start(struct qed_hwfn *p_hwfn, u8 abs_stats_id = 0; int rc; + if (IS_VF(p_hwfn->cdev)) { + return qed_vf_pf_rxq_start(p_hwfn, + params->queue_id, + params->sb, + params->sb_idx, + bd_max_bytes, + bd_chain_phys_addr, + cqe_pbl_addr, cqe_pbl_size, pp_prod); + } + rc = qed_fw_l2_queue(p_hwfn, params->queue_id, &abs_l2_queue); if (rc != 0) return rc; @@ -643,10 +647,59 @@ qed_sp_eth_rx_queue_start(struct qed_hwfn *p_hwfn, return rc; } -static int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, - u16 rx_queue_id, - bool eq_completion_only, - bool cqe_completion) +int qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, + u16 rx_queue_id, + u8 num_rxqs, + u8 complete_cqe_flg, + u8 complete_event_flg, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data) +{ + struct rx_queue_update_ramrod_data *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + struct qed_hw_cid_data *p_rx_cid; + u16 qid, abs_rx_q_id = 0; + int rc = -EINVAL; + u8 i; + + memset(&init_data, 0, sizeof(init_data)); + init_data.comp_mode = comp_mode; + init_data.p_comp_data = p_comp_data; + + for (i = 0; i < num_rxqs; i++) { + qid = rx_queue_id + i; + p_rx_cid = &p_hwfn->p_rx_cids[qid]; + + /* Get SPQ entry */ + init_data.cid = p_rx_cid->cid; + init_data.opaque_fid = p_rx_cid->opaque_fid; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ETH_RAMROD_RX_QUEUE_UPDATE, + PROTOCOLID_ETH, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.rx_queue_update; + + qed_fw_vport(p_hwfn, p_rx_cid->vport_id, &p_ramrod->vport_id); + qed_fw_l2_queue(p_hwfn, qid, &abs_rx_q_id); + p_ramrod->rx_queue_id = cpu_to_le16(abs_rx_q_id); + p_ramrod->complete_cqe_flg = complete_cqe_flg; + p_ramrod->complete_event_flg = complete_event_flg; + + rc = qed_spq_post(p_hwfn, p_ent, NULL); + if (rc) + return rc; + } + + return rc; +} + +int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, + u16 rx_queue_id, + bool eq_completion_only, bool cqe_completion) { struct qed_hw_cid_data *p_rx_cid = &p_hwfn->p_rx_cids[rx_queue_id]; struct rx_queue_stop_ramrod_data *p_ramrod = NULL; @@ -655,6 +708,9 @@ static int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, u16 abs_rx_q_id = 0; int rc = -EINVAL; + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_rxq_stop(p_hwfn, rx_queue_id, cqe_completion); + /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); init_data.cid = p_rx_cid->cid; @@ -690,15 +746,14 @@ static int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, return qed_sp_release_queue_cid(p_hwfn, p_rx_cid); } -static int -qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - u32 cid, - struct qed_queue_start_common_params *p_params, - u8 stats_id, - dma_addr_t pbl_addr, - u16 pbl_size, - union qed_qm_pq_params *p_pq_params) +int qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + struct qed_queue_start_common_params *p_params, + u8 stats_id, + dma_addr_t pbl_addr, + u16 pbl_size, + union qed_qm_pq_params *p_pq_params) { struct tx_queue_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; @@ -752,14 +807,21 @@ qed_sp_eth_tx_queue_start(struct qed_hwfn *p_hwfn, u16 opaque_fid, struct qed_queue_start_common_params *p_params, dma_addr_t pbl_addr, - u16 pbl_size, - void __iomem **pp_doorbell) + u16 pbl_size, void __iomem **pp_doorbell) { struct qed_hw_cid_data *p_tx_cid; union qed_qm_pq_params pq_params; u8 abs_stats_id = 0; int rc; + if (IS_VF(p_hwfn->cdev)) { + return qed_vf_pf_txq_start(p_hwfn, + p_params->queue_id, + p_params->sb, + p_params->sb_idx, + pbl_addr, pbl_size, pp_doorbell); + } + rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_stats_id); if (rc) return rc; @@ -800,14 +862,16 @@ qed_sp_eth_tx_queue_start(struct qed_hwfn *p_hwfn, return rc; } -static int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, - u16 tx_queue_id) +int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, u16 tx_queue_id) { struct qed_hw_cid_data *p_tx_cid = &p_hwfn->p_tx_cids[tx_queue_id]; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; int rc = -EINVAL; + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_txq_stop(p_hwfn, tx_queue_id); + /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); init_data.cid = p_tx_cid->cid; @@ -1003,11 +1067,11 @@ qed_filter_ucast_common(struct qed_hwfn *p_hwfn, return 0; } -static int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn, - u16 opaque_fid, - struct qed_filter_ucast *p_filter_cmd, - enum spq_mode comp_mode, - struct qed_spq_comp_cb *p_comp_data) +int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + struct qed_filter_ucast *p_filter_cmd, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data) { struct vport_filter_update_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; @@ -1105,7 +1169,7 @@ static inline u32 qed_crc32c_le(u32 seed, return qed_calc_crc32c((u8 *)packet_buf, 8, seed, 0); } -static u8 qed_mcast_bin_from_mac(u8 *mac) +u8 qed_mcast_bin_from_mac(u8 *mac) { u32 crc = qed_crc32c_le(ETH_MULTICAST_BIN_FROM_MAC_SEED, mac, ETH_ALEN); @@ -1188,11 +1252,10 @@ qed_sp_eth_filter_mcast(struct qed_hwfn *p_hwfn, return qed_spq_post(p_hwfn, p_ent, NULL); } -static int -qed_filter_mcast_cmd(struct qed_dev *cdev, - struct qed_filter_mcast *p_filter_cmd, - enum spq_mode comp_mode, - struct qed_spq_comp_cb *p_comp_data) +static int qed_filter_mcast_cmd(struct qed_dev *cdev, + struct qed_filter_mcast *p_filter_cmd, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data) { int rc = 0; int i; @@ -1208,8 +1271,10 @@ qed_filter_mcast_cmd(struct qed_dev *cdev, u16 opaque_fid; - if (rc != 0) - break; + if (IS_VF(cdev)) { + qed_vf_pf_filter_mcast(p_hwfn, p_filter_cmd); + continue; + } opaque_fid = p_hwfn->hw_info.opaque_fid; @@ -1234,8 +1299,10 @@ static int qed_filter_ucast_cmd(struct qed_dev *cdev, struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; u16 opaque_fid; - if (rc != 0) - break; + if (IS_VF(cdev)) { + rc = qed_vf_pf_filter_ucast(p_hwfn, p_filter_cmd); + continue; + } opaque_fid = p_hwfn->hw_info.opaque_fid; @@ -1244,6 +1311,8 @@ static int qed_filter_ucast_cmd(struct qed_dev *cdev, p_filter_cmd, comp_mode, p_comp_data); + if (rc != 0) + break; } return rc; @@ -1252,12 +1321,19 @@ static int qed_filter_ucast_cmd(struct qed_dev *cdev, /* Statistics related code */ static void __qed_get_vport_pstats_addrlen(struct qed_hwfn *p_hwfn, u32 *p_addr, - u32 *p_len, - u16 statistics_bin) + u32 *p_len, u16 statistics_bin) { - *p_addr = BAR0_MAP_REG_PSDM_RAM + - PSTORM_QUEUE_STAT_OFFSET(statistics_bin); - *p_len = sizeof(struct eth_pstorm_per_queue_stat); + if (IS_PF(p_hwfn->cdev)) { + *p_addr = BAR0_MAP_REG_PSDM_RAM + + PSTORM_QUEUE_STAT_OFFSET(statistics_bin); + *p_len = sizeof(struct eth_pstorm_per_queue_stat); + } else { + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_acquire_resp_tlv *p_resp = &p_iov->acquire_resp; + + *p_addr = p_resp->pfdev_info.stats_info.pstats.address; + *p_len = p_resp->pfdev_info.stats_info.pstats.len; + } } static void __qed_get_vport_pstats(struct qed_hwfn *p_hwfn, @@ -1272,32 +1348,15 @@ static void __qed_get_vport_pstats(struct qed_hwfn *p_hwfn, statistics_bin); memset(&pstats, 0, sizeof(pstats)); - qed_memcpy_from(p_hwfn, p_ptt, &pstats, - pstats_addr, pstats_len); - - p_stats->tx_ucast_bytes += - HILO_64_REGPAIR(pstats.sent_ucast_bytes); - p_stats->tx_mcast_bytes += - HILO_64_REGPAIR(pstats.sent_mcast_bytes); - p_stats->tx_bcast_bytes += - HILO_64_REGPAIR(pstats.sent_bcast_bytes); - p_stats->tx_ucast_pkts += - HILO_64_REGPAIR(pstats.sent_ucast_pkts); - p_stats->tx_mcast_pkts += - HILO_64_REGPAIR(pstats.sent_mcast_pkts); - p_stats->tx_bcast_pkts += - HILO_64_REGPAIR(pstats.sent_bcast_pkts); - p_stats->tx_err_drop_pkts += - HILO_64_REGPAIR(pstats.error_drop_pkts); -} - -static void __qed_get_vport_tstats_addrlen(struct qed_hwfn *p_hwfn, - u32 *p_addr, - u32 *p_len) -{ - *p_addr = BAR0_MAP_REG_TSDM_RAM + - TSTORM_PORT_STAT_OFFSET(MFW_PORT(p_hwfn)); - *p_len = sizeof(struct tstorm_per_port_stat); + qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, pstats_len); + + p_stats->tx_ucast_bytes += HILO_64_REGPAIR(pstats.sent_ucast_bytes); + p_stats->tx_mcast_bytes += HILO_64_REGPAIR(pstats.sent_mcast_bytes); + p_stats->tx_bcast_bytes += HILO_64_REGPAIR(pstats.sent_bcast_bytes); + p_stats->tx_ucast_pkts += HILO_64_REGPAIR(pstats.sent_ucast_pkts); + p_stats->tx_mcast_pkts += HILO_64_REGPAIR(pstats.sent_mcast_pkts); + p_stats->tx_bcast_pkts += HILO_64_REGPAIR(pstats.sent_bcast_pkts); + p_stats->tx_err_drop_pkts += HILO_64_REGPAIR(pstats.error_drop_pkts); } static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, @@ -1305,14 +1364,23 @@ static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, struct qed_eth_stats *p_stats, u16 statistics_bin) { - u32 tstats_addr = 0, tstats_len = 0; struct tstorm_per_port_stat tstats; + u32 tstats_addr, tstats_len; + + if (IS_PF(p_hwfn->cdev)) { + tstats_addr = BAR0_MAP_REG_TSDM_RAM + + TSTORM_PORT_STAT_OFFSET(MFW_PORT(p_hwfn)); + tstats_len = sizeof(struct tstorm_per_port_stat); + } else { + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_acquire_resp_tlv *p_resp = &p_iov->acquire_resp; - __qed_get_vport_tstats_addrlen(p_hwfn, &tstats_addr, &tstats_len); + tstats_addr = p_resp->pfdev_info.stats_info.tstats.address; + tstats_len = p_resp->pfdev_info.stats_info.tstats.len; + } memset(&tstats, 0, sizeof(tstats)); - qed_memcpy_from(p_hwfn, p_ptt, &tstats, - tstats_addr, tstats_len); + qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, tstats_len); p_stats->mftag_filter_discards += HILO_64_REGPAIR(tstats.mftag_filter_discard); @@ -1322,12 +1390,19 @@ static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, static void __qed_get_vport_ustats_addrlen(struct qed_hwfn *p_hwfn, u32 *p_addr, - u32 *p_len, - u16 statistics_bin) + u32 *p_len, u16 statistics_bin) { - *p_addr = BAR0_MAP_REG_USDM_RAM + - USTORM_QUEUE_STAT_OFFSET(statistics_bin); - *p_len = sizeof(struct eth_ustorm_per_queue_stat); + if (IS_PF(p_hwfn->cdev)) { + *p_addr = BAR0_MAP_REG_USDM_RAM + + USTORM_QUEUE_STAT_OFFSET(statistics_bin); + *p_len = sizeof(struct eth_ustorm_per_queue_stat); + } else { + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_acquire_resp_tlv *p_resp = &p_iov->acquire_resp; + + *p_addr = p_resp->pfdev_info.stats_info.ustats.address; + *p_len = p_resp->pfdev_info.stats_info.ustats.len; + } } static void __qed_get_vport_ustats(struct qed_hwfn *p_hwfn, @@ -1342,31 +1417,31 @@ static void __qed_get_vport_ustats(struct qed_hwfn *p_hwfn, statistics_bin); memset(&ustats, 0, sizeof(ustats)); - qed_memcpy_from(p_hwfn, p_ptt, &ustats, - ustats_addr, ustats_len); - - p_stats->rx_ucast_bytes += - HILO_64_REGPAIR(ustats.rcv_ucast_bytes); - p_stats->rx_mcast_bytes += - HILO_64_REGPAIR(ustats.rcv_mcast_bytes); - p_stats->rx_bcast_bytes += - HILO_64_REGPAIR(ustats.rcv_bcast_bytes); - p_stats->rx_ucast_pkts += - HILO_64_REGPAIR(ustats.rcv_ucast_pkts); - p_stats->rx_mcast_pkts += - HILO_64_REGPAIR(ustats.rcv_mcast_pkts); - p_stats->rx_bcast_pkts += - HILO_64_REGPAIR(ustats.rcv_bcast_pkts); + qed_memcpy_from(p_hwfn, p_ptt, &ustats, ustats_addr, ustats_len); + + p_stats->rx_ucast_bytes += HILO_64_REGPAIR(ustats.rcv_ucast_bytes); + p_stats->rx_mcast_bytes += HILO_64_REGPAIR(ustats.rcv_mcast_bytes); + p_stats->rx_bcast_bytes += HILO_64_REGPAIR(ustats.rcv_bcast_bytes); + p_stats->rx_ucast_pkts += HILO_64_REGPAIR(ustats.rcv_ucast_pkts); + p_stats->rx_mcast_pkts += HILO_64_REGPAIR(ustats.rcv_mcast_pkts); + p_stats->rx_bcast_pkts += HILO_64_REGPAIR(ustats.rcv_bcast_pkts); } static void __qed_get_vport_mstats_addrlen(struct qed_hwfn *p_hwfn, u32 *p_addr, - u32 *p_len, - u16 statistics_bin) + u32 *p_len, u16 statistics_bin) { - *p_addr = BAR0_MAP_REG_MSDM_RAM + - MSTORM_QUEUE_STAT_OFFSET(statistics_bin); - *p_len = sizeof(struct eth_mstorm_per_queue_stat); + if (IS_PF(p_hwfn->cdev)) { + *p_addr = BAR0_MAP_REG_MSDM_RAM + + MSTORM_QUEUE_STAT_OFFSET(statistics_bin); + *p_len = sizeof(struct eth_mstorm_per_queue_stat); + } else { + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_acquire_resp_tlv *p_resp = &p_iov->acquire_resp; + + *p_addr = p_resp->pfdev_info.stats_info.mstats.address; + *p_len = p_resp->pfdev_info.stats_info.mstats.len; + } } static void __qed_get_vport_mstats(struct qed_hwfn *p_hwfn, @@ -1381,21 +1456,17 @@ static void __qed_get_vport_mstats(struct qed_hwfn *p_hwfn, statistics_bin); memset(&mstats, 0, sizeof(mstats)); - qed_memcpy_from(p_hwfn, p_ptt, &mstats, - mstats_addr, mstats_len); + qed_memcpy_from(p_hwfn, p_ptt, &mstats, mstats_addr, mstats_len); - p_stats->no_buff_discards += - HILO_64_REGPAIR(mstats.no_buff_discard); + p_stats->no_buff_discards += HILO_64_REGPAIR(mstats.no_buff_discard); p_stats->packet_too_big_discard += HILO_64_REGPAIR(mstats.packet_too_big_discard); - p_stats->ttl0_discard += - HILO_64_REGPAIR(mstats.ttl0_discard); + p_stats->ttl0_discard += HILO_64_REGPAIR(mstats.ttl0_discard); p_stats->tpa_coalesced_pkts += HILO_64_REGPAIR(mstats.tpa_coalesced_pkts); p_stats->tpa_coalesced_events += HILO_64_REGPAIR(mstats.tpa_coalesced_events); - p_stats->tpa_aborts_num += - HILO_64_REGPAIR(mstats.tpa_aborts_num); + p_stats->tpa_aborts_num += HILO_64_REGPAIR(mstats.tpa_aborts_num); p_stats->tpa_coalesced_bytes += HILO_64_REGPAIR(mstats.tpa_coalesced_bytes); } @@ -1468,44 +1539,49 @@ static void __qed_get_vport_port_stats(struct qed_hwfn *p_hwfn, static void __qed_get_vport_stats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_eth_stats *stats, - u16 statistics_bin) + u16 statistics_bin, bool b_get_port_stats) { __qed_get_vport_mstats(p_hwfn, p_ptt, stats, statistics_bin); __qed_get_vport_ustats(p_hwfn, p_ptt, stats, statistics_bin); __qed_get_vport_tstats(p_hwfn, p_ptt, stats, statistics_bin); __qed_get_vport_pstats(p_hwfn, p_ptt, stats, statistics_bin); - if (p_hwfn->mcp_info) + if (b_get_port_stats && p_hwfn->mcp_info) __qed_get_vport_port_stats(p_hwfn, p_ptt, stats); } static void _qed_get_vport_stats(struct qed_dev *cdev, struct qed_eth_stats *stats) { - u8 fw_vport = 0; - int i; + u8 fw_vport = 0; + int i; memset(stats, 0, sizeof(*stats)); for_each_hwfn(cdev, i) { struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - struct qed_ptt *p_ptt; - - /* The main vport index is relative first */ - if (qed_fw_vport(p_hwfn, 0, &fw_vport)) { - DP_ERR(p_hwfn, "No vport available!\n"); - continue; + struct qed_ptt *p_ptt = IS_PF(cdev) ? qed_ptt_acquire(p_hwfn) + : NULL; + + if (IS_PF(cdev)) { + /* The main vport index is relative first */ + if (qed_fw_vport(p_hwfn, 0, &fw_vport)) { + DP_ERR(p_hwfn, "No vport available!\n"); + goto out; + } } - p_ptt = qed_ptt_acquire(p_hwfn); - if (!p_ptt) { + if (IS_PF(cdev) && !p_ptt) { DP_ERR(p_hwfn, "Failed to acquire ptt\n"); continue; } - __qed_get_vport_stats(p_hwfn, p_ptt, stats, fw_vport); + __qed_get_vport_stats(p_hwfn, p_ptt, stats, fw_vport, + IS_PF(cdev) ? true : false); - qed_ptt_release(p_hwfn, p_ptt); +out: + if (IS_PF(cdev) && p_ptt) + qed_ptt_release(p_hwfn, p_ptt); } } @@ -1539,10 +1615,11 @@ void qed_reset_vport_stats(struct qed_dev *cdev) struct eth_mstorm_per_queue_stat mstats; struct eth_ustorm_per_queue_stat ustats; struct eth_pstorm_per_queue_stat pstats; - struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn); + struct qed_ptt *p_ptt = IS_PF(cdev) ? qed_ptt_acquire(p_hwfn) + : NULL; u32 addr = 0, len = 0; - if (!p_ptt) { + if (IS_PF(cdev) && !p_ptt) { DP_ERR(p_hwfn, "Failed to acquire ptt\n"); continue; } @@ -1559,7 +1636,8 @@ void qed_reset_vport_stats(struct qed_dev *cdev) __qed_get_vport_pstats_addrlen(p_hwfn, &addr, &len, 0); qed_memcpy_to(p_hwfn, p_ptt, addr, &pstats, len); - qed_ptt_release(p_hwfn, p_ptt); + if (IS_PF(cdev)) + qed_ptt_release(p_hwfn, p_ptt); } /* PORT statistics are not necessarily reset, so we need to @@ -1580,32 +1658,61 @@ static int qed_fill_eth_dev_info(struct qed_dev *cdev, info->num_tc = 1; - if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) { - for_each_hwfn(cdev, i) - info->num_queues += FEAT_NUM(&cdev->hwfns[i], - QED_PF_L2_QUE); - if (cdev->int_params.fp_msix_cnt) - info->num_queues = min_t(u8, info->num_queues, - cdev->int_params.fp_msix_cnt); + if (IS_PF(cdev)) { + if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) { + for_each_hwfn(cdev, i) + info->num_queues += + FEAT_NUM(&cdev->hwfns[i], QED_PF_L2_QUE); + if (cdev->int_params.fp_msix_cnt) + info->num_queues = + min_t(u8, info->num_queues, + cdev->int_params.fp_msix_cnt); + } else { + info->num_queues = cdev->num_hwfns; + } + + info->num_vlan_filters = RESC_NUM(&cdev->hwfns[0], QED_VLAN); + ether_addr_copy(info->port_mac, + cdev->hwfns[0].hw_info.hw_mac_addr); } else { - info->num_queues = cdev->num_hwfns; - } + qed_vf_get_num_rxqs(QED_LEADING_HWFN(cdev), &info->num_queues); + if (cdev->num_hwfns > 1) { + u8 queues = 0; + + qed_vf_get_num_rxqs(&cdev->hwfns[1], &queues); + info->num_queues += queues; + } - info->num_vlan_filters = RESC_NUM(&cdev->hwfns[0], QED_VLAN); - ether_addr_copy(info->port_mac, - cdev->hwfns[0].hw_info.hw_mac_addr); + qed_vf_get_num_vlan_filters(&cdev->hwfns[0], + &info->num_vlan_filters); + qed_vf_get_port_mac(&cdev->hwfns[0], info->port_mac); + } qed_fill_dev_info(cdev, &info->common); + if (IS_VF(cdev)) + memset(info->common.hw_mac, 0, ETH_ALEN); + return 0; } static void qed_register_eth_ops(struct qed_dev *cdev, - struct qed_eth_cb_ops *ops, - void *cookie) + struct qed_eth_cb_ops *ops, void *cookie) { - cdev->protocol_ops.eth = ops; - cdev->ops_cookie = cookie; + cdev->protocol_ops.eth = ops; + cdev->ops_cookie = cookie; + + /* For VF, we start bulletin reading */ + if (IS_VF(cdev)) + qed_vf_start_iov_wq(cdev); +} + +static bool qed_check_mac(struct qed_dev *cdev, u8 *mac) +{ + if (IS_PF(cdev)) + return true; + + return qed_vf_check_mac(&cdev->hwfns[0], mac); } static int qed_start_vport(struct qed_dev *cdev, @@ -1620,6 +1727,7 @@ static int qed_start_vport(struct qed_dev *cdev, start.tpa_mode = params->gro_enable ? QED_TPA_MODE_GRO : QED_TPA_MODE_NONE; start.remove_inner_vlan = params->remove_inner_vlan; + start.only_untagged = true; /* untagged only */ start.drop_ttl0 = params->drop_ttl0; start.opaque_fid = p_hwfn->hw_info.opaque_fid; start.concrete_fid = p_hwfn->hw_info.concrete_fid; @@ -1686,6 +1794,8 @@ static int qed_update_vport(struct qed_dev *cdev, params->update_vport_active_flg; sp_params.vport_active_rx_flg = params->vport_active_flg; sp_params.vport_active_tx_flg = params->vport_active_flg; + sp_params.update_tx_switching_flg = params->update_tx_switching_flg; + sp_params.tx_switching_flg = params->tx_switching_flg; sp_params.accept_any_vlan = params->accept_any_vlan; sp_params.update_accept_any_vlan_flg = params->update_accept_any_vlan_flg; @@ -1890,6 +2000,9 @@ static int qed_tunn_configure(struct qed_dev *cdev, struct qed_tunn_update_params tunn_info; int i, rc; + if (IS_VF(cdev)) + return 0; + memset(&tunn_info, 0, sizeof(tunn_info)); if (tunn_params->update_vxlan_port == 1) { tunn_info.update_vxlan_udp_port = 1; @@ -2041,10 +2154,18 @@ static int qed_fp_cqe_completion(struct qed_dev *dev, cqe); } +#ifdef CONFIG_QED_SRIOV +extern const struct qed_iov_hv_ops qed_iov_ops_pass; +#endif + static const struct qed_eth_ops qed_eth_ops_pass = { .common = &qed_common_ops_pass, +#ifdef CONFIG_QED_SRIOV + .iov = &qed_iov_ops_pass, +#endif .fill_dev_info = &qed_fill_eth_dev_info, .register_ops = &qed_register_eth_ops, + .check_mac = &qed_check_mac, .vport_start = &qed_start_vport, .vport_stop = &qed_stop_vport, .vport_update = &qed_update_vport, diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h new file mode 100644 index 000000000000..002114543451 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -0,0 +1,239 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef _QED_L2_H +#define _QED_L2_H +#include <linux/types.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/qed/qed_eth_if.h> +#include "qed.h" +#include "qed_hw.h" +#include "qed_sp.h" + +struct qed_sge_tpa_params { + u8 max_buffers_per_cqe; + + u8 update_tpa_en_flg; + u8 tpa_ipv4_en_flg; + u8 tpa_ipv6_en_flg; + u8 tpa_ipv4_tunn_en_flg; + u8 tpa_ipv6_tunn_en_flg; + + u8 update_tpa_param_flg; + u8 tpa_pkt_split_flg; + u8 tpa_hdr_data_split_flg; + u8 tpa_gro_consistent_flg; + u8 tpa_max_aggs_num; + u16 tpa_max_size; + u16 tpa_min_size_to_start; + u16 tpa_min_size_to_cont; +}; + +enum qed_filter_opcode { + QED_FILTER_ADD, + QED_FILTER_REMOVE, + QED_FILTER_MOVE, + QED_FILTER_REPLACE, /* Delete all MACs and add new one instead */ + QED_FILTER_FLUSH, /* Removes all filters */ +}; + +enum qed_filter_ucast_type { + QED_FILTER_MAC, + QED_FILTER_VLAN, + QED_FILTER_MAC_VLAN, + QED_FILTER_INNER_MAC, + QED_FILTER_INNER_VLAN, + QED_FILTER_INNER_PAIR, + QED_FILTER_INNER_MAC_VNI_PAIR, + QED_FILTER_MAC_VNI_PAIR, + QED_FILTER_VNI, +}; + +struct qed_filter_ucast { + enum qed_filter_opcode opcode; + enum qed_filter_ucast_type type; + u8 is_rx_filter; + u8 is_tx_filter; + u8 vport_to_add_to; + u8 vport_to_remove_from; + unsigned char mac[ETH_ALEN]; + u8 assert_on_error; + u16 vlan; + u32 vni; +}; + +struct qed_filter_mcast { + /* MOVE is not supported for multicast */ + enum qed_filter_opcode opcode; + u8 vport_to_add_to; + u8 vport_to_remove_from; + u8 num_mc_addrs; +#define QED_MAX_MC_ADDRS 64 + unsigned char mac[QED_MAX_MC_ADDRS][ETH_ALEN]; +}; + +int qed_sp_eth_rx_queue_stop(struct qed_hwfn *p_hwfn, + u16 rx_queue_id, + bool eq_completion_only, bool cqe_completion); + +int qed_sp_eth_tx_queue_stop(struct qed_hwfn *p_hwfn, u16 tx_queue_id); + +enum qed_tpa_mode { + QED_TPA_MODE_NONE, + QED_TPA_MODE_UNUSED, + QED_TPA_MODE_GRO, + QED_TPA_MODE_MAX +}; + +struct qed_sp_vport_start_params { + enum qed_tpa_mode tpa_mode; + bool remove_inner_vlan; + bool tx_switching; + bool only_untagged; + bool drop_ttl0; + u8 max_buffers_per_cqe; + u32 concrete_fid; + u16 opaque_fid; + u8 vport_id; + u16 mtu; +}; + +int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_start_params *p_params); + +struct qed_rss_params { + u8 update_rss_config; + u8 rss_enable; + u8 rss_eng_id; + u8 update_rss_capabilities; + u8 update_rss_ind_table; + u8 update_rss_key; + u8 rss_caps; + u8 rss_table_size_log; + u16 rss_ind_table[QED_RSS_IND_TABLE_SIZE]; + u32 rss_key[QED_RSS_KEY_SIZE]; +}; + +struct qed_filter_accept_flags { + u8 update_rx_mode_config; + u8 update_tx_mode_config; + u8 rx_accept_filter; + u8 tx_accept_filter; +#define QED_ACCEPT_NONE 0x01 +#define QED_ACCEPT_UCAST_MATCHED 0x02 +#define QED_ACCEPT_UCAST_UNMATCHED 0x04 +#define QED_ACCEPT_MCAST_MATCHED 0x08 +#define QED_ACCEPT_MCAST_UNMATCHED 0x10 +#define QED_ACCEPT_BCAST 0x20 +}; + +struct qed_sp_vport_update_params { + u16 opaque_fid; + u8 vport_id; + u8 update_vport_active_rx_flg; + u8 vport_active_rx_flg; + u8 update_vport_active_tx_flg; + u8 vport_active_tx_flg; + u8 update_inner_vlan_removal_flg; + u8 inner_vlan_removal_flg; + u8 silent_vlan_removal_flg; + u8 update_default_vlan_enable_flg; + u8 default_vlan_enable_flg; + u8 update_default_vlan_flg; + u16 default_vlan; + u8 update_tx_switching_flg; + u8 tx_switching_flg; + u8 update_approx_mcast_flg; + u8 update_anti_spoofing_en_flg; + u8 anti_spoofing_en; + u8 update_accept_any_vlan_flg; + u8 accept_any_vlan; + unsigned long bins[8]; + struct qed_rss_params *rss_params; + struct qed_filter_accept_flags accept_flags; + struct qed_sge_tpa_params *sge_tpa_params; +}; + +int qed_sp_vport_update(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_params, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data); + +/** + * @brief qed_sp_vport_stop - + * + * This ramrod closes a VPort after all its RX and TX queues are terminated. + * An Assert is generated if any queues are left open. + * + * @param p_hwfn + * @param opaque_fid + * @param vport_id VPort ID + * + * @return int + */ +int qed_sp_vport_stop(struct qed_hwfn *p_hwfn, u16 opaque_fid, u8 vport_id); + +int qed_sp_eth_filter_ucast(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + struct qed_filter_ucast *p_filter_cmd, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data); + +/** + * @brief qed_sp_rx_eth_queues_update - + * + * This ramrod updates an RX queue. It is used for setting the active state + * of the queue and updating the TPA and SGE parameters. + * + * @note At the moment - only used by non-linux VFs. + * + * @param p_hwfn + * @param rx_queue_id RX Queue ID + * @param num_rxqs Allow to update multiple rx + * queues, from rx_queue_id to + * (rx_queue_id + num_rxqs) + * @param complete_cqe_flg Post completion to the CQE Ring if set + * @param complete_event_flg Post completion to the Event Ring if set + * + * @return int + */ + +int +qed_sp_eth_rx_queues_update(struct qed_hwfn *p_hwfn, + u16 rx_queue_id, + u8 num_rxqs, + u8 complete_cqe_flg, + u8 complete_event_flg, + enum spq_mode comp_mode, + struct qed_spq_comp_cb *p_comp_data); + +int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_start_params *p_params); + +int qed_sp_eth_rxq_start_ramrod(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + struct qed_queue_start_common_params *params, + u8 stats_id, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, u16 cqe_pbl_size); + +int qed_sp_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, + u16 opaque_fid, + u32 cid, + struct qed_queue_start_common_params *p_params, + u8 stats_id, + dma_addr_t pbl_addr, + u16 pbl_size, + union qed_qm_pq_params *p_pq_params); + +u8 qed_mcast_bin_from_mac(u8 *mac); + +#endif /* _QED_L2_H */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 1b758bdec587..8b22f87033ce 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -24,6 +24,7 @@ #include <linux/qed/qed_if.h> #include "qed.h" +#include "qed_sriov.h" #include "qed_sp.h" #include "qed_dev_api.h" #include "qed_mcp.h" @@ -125,7 +126,7 @@ static int qed_init_pci(struct qed_dev *cdev, goto err1; } - if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { + if (IS_PF(cdev) && !(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { DP_NOTICE(cdev, "No memory region found in bar #2\n"); rc = -EIO; goto err1; @@ -157,7 +158,7 @@ static int qed_init_pci(struct qed_dev *cdev, } cdev->pci_params.pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (cdev->pci_params.pm_cap == 0) + if (IS_PF(cdev) && !cdev->pci_params.pm_cap) DP_NOTICE(cdev, "Cannot find power management capability\n"); rc = qed_set_coherency_mask(cdev); @@ -175,12 +176,14 @@ static int qed_init_pci(struct qed_dev *cdev, goto err2; } - cdev->db_phys_addr = pci_resource_start(cdev->pdev, 2); - cdev->db_size = pci_resource_len(cdev->pdev, 2); - cdev->doorbells = ioremap_wc(cdev->db_phys_addr, cdev->db_size); - if (!cdev->doorbells) { - DP_NOTICE(cdev, "Cannot map doorbell space\n"); - return -ENOMEM; + if (IS_PF(cdev)) { + cdev->db_phys_addr = pci_resource_start(cdev->pdev, 2); + cdev->db_size = pci_resource_len(cdev->pdev, 2); + cdev->doorbells = ioremap_wc(cdev->db_phys_addr, cdev->db_size); + if (!cdev->doorbells) { + DP_NOTICE(cdev, "Cannot map doorbell space\n"); + return -ENOMEM; + } } return 0; @@ -207,20 +210,33 @@ int qed_fill_dev_info(struct qed_dev *cdev, dev_info->is_mf_default = IS_MF_DEFAULT(&cdev->hwfns[0]); ether_addr_copy(dev_info->hw_mac, cdev->hwfns[0].hw_info.hw_mac_addr); - dev_info->fw_major = FW_MAJOR_VERSION; - dev_info->fw_minor = FW_MINOR_VERSION; - dev_info->fw_rev = FW_REVISION_VERSION; - dev_info->fw_eng = FW_ENGINEERING_VERSION; - dev_info->mf_mode = cdev->mf_mode; + if (IS_PF(cdev)) { + dev_info->fw_major = FW_MAJOR_VERSION; + dev_info->fw_minor = FW_MINOR_VERSION; + dev_info->fw_rev = FW_REVISION_VERSION; + dev_info->fw_eng = FW_ENGINEERING_VERSION; + dev_info->mf_mode = cdev->mf_mode; + dev_info->tx_switching = true; + } else { + qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major, + &dev_info->fw_minor, &dev_info->fw_rev, + &dev_info->fw_eng); + } - qed_mcp_get_mfw_ver(cdev, &dev_info->mfw_rev); + if (IS_PF(cdev)) { + ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); + if (ptt) { + qed_mcp_get_mfw_ver(QED_LEADING_HWFN(cdev), ptt, + &dev_info->mfw_rev, NULL); - ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); - if (ptt) { - qed_mcp_get_flash_size(QED_LEADING_HWFN(cdev), ptt, - &dev_info->flash_size); + qed_mcp_get_flash_size(QED_LEADING_HWFN(cdev), ptt, + &dev_info->flash_size); - qed_ptt_release(QED_LEADING_HWFN(cdev), ptt); + qed_ptt_release(QED_LEADING_HWFN(cdev), ptt); + } + } else { + qed_mcp_get_mfw_ver(QED_LEADING_HWFN(cdev), NULL, + &dev_info->mfw_rev, NULL); } return 0; @@ -257,9 +273,7 @@ static int qed_set_power_state(struct qed_dev *cdev, /* probing */ static struct qed_dev *qed_probe(struct pci_dev *pdev, - enum qed_protocol protocol, - u32 dp_module, - u8 dp_level) + struct qed_probe_params *params) { struct qed_dev *cdev; int rc; @@ -268,9 +282,12 @@ static struct qed_dev *qed_probe(struct pci_dev *pdev, if (!cdev) goto err0; - cdev->protocol = protocol; + cdev->protocol = params->protocol; + + if (params->is_vf) + cdev->b_is_vf = true; - qed_init_dp(cdev, dp_module, dp_level); + qed_init_dp(cdev, params->dp_module, params->dp_level); rc = qed_init_pci(cdev, pdev); if (rc) { @@ -664,6 +681,35 @@ static int qed_slowpath_setup_int(struct qed_dev *cdev, return 0; } +static int qed_slowpath_vf_setup_int(struct qed_dev *cdev) +{ + int rc; + + memset(&cdev->int_params, 0, sizeof(struct qed_int_params)); + cdev->int_params.in.int_mode = QED_INT_MODE_MSIX; + + qed_vf_get_num_rxqs(QED_LEADING_HWFN(cdev), + &cdev->int_params.in.num_vectors); + if (cdev->num_hwfns > 1) { + u8 vectors = 0; + + qed_vf_get_num_rxqs(&cdev->hwfns[1], &vectors); + cdev->int_params.in.num_vectors += vectors; + } + + /* We want a minimum of one fastpath vector per vf hwfn */ + cdev->int_params.in.min_msix_cnt = cdev->num_hwfns; + + rc = qed_set_int_mode(cdev, true); + if (rc) + return rc; + + cdev->int_params.fp_msix_base = 0; + cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors; + + return 0; +} + u32 qed_unzip_data(struct qed_hwfn *p_hwfn, u32 input_len, u8 *input_buf, u32 max_size, u8 *unzip_buf) { @@ -749,34 +795,43 @@ static int qed_slowpath_start(struct qed_dev *cdev, struct qed_mcp_drv_version drv_version; const u8 *data = NULL; struct qed_hwfn *hwfn; - int rc; + int rc = -EINVAL; - rc = request_firmware(&cdev->firmware, QED_FW_FILE_NAME, - &cdev->pdev->dev); - if (rc) { - DP_NOTICE(cdev, - "Failed to find fw file - /lib/firmware/%s\n", - QED_FW_FILE_NAME); + if (qed_iov_wq_start(cdev)) goto err; + + if (IS_PF(cdev)) { + rc = request_firmware(&cdev->firmware, QED_FW_FILE_NAME, + &cdev->pdev->dev); + if (rc) { + DP_NOTICE(cdev, + "Failed to find fw file - /lib/firmware/%s\n", + QED_FW_FILE_NAME); + goto err; + } } rc = qed_nic_setup(cdev); if (rc) goto err; - rc = qed_slowpath_setup_int(cdev, params->int_mode); + if (IS_PF(cdev)) + rc = qed_slowpath_setup_int(cdev, params->int_mode); + else + rc = qed_slowpath_vf_setup_int(cdev); if (rc) goto err1; - /* Allocate stream for unzipping */ - rc = qed_alloc_stream_mem(cdev); - if (rc) { - DP_NOTICE(cdev, "Failed to allocate stream memory\n"); - goto err2; - } + if (IS_PF(cdev)) { + /* Allocate stream for unzipping */ + rc = qed_alloc_stream_mem(cdev); + if (rc) { + DP_NOTICE(cdev, "Failed to allocate stream memory\n"); + goto err2; + } - /* Start the slowpath */ - data = cdev->firmware->data; + data = cdev->firmware->data; + } memset(&tunn_info, 0, sizeof(tunn_info)); tunn_info.tunn_mode |= 1 << QED_MODE_VXLAN_TUNN | @@ -789,6 +844,7 @@ static int qed_slowpath_start(struct qed_dev *cdev, tunn_info.tunn_clss_l2gre = QED_TUNN_CLSS_MAC_VLAN; tunn_info.tunn_clss_ipgre = QED_TUNN_CLSS_MAC_VLAN; + /* Start the slowpath */ rc = qed_hw_init(cdev, &tunn_info, true, cdev->int_params.out.int_mode, true, data); @@ -798,18 +854,20 @@ static int qed_slowpath_start(struct qed_dev *cdev, DP_INFO(cdev, "HW initialization and function start completed successfully\n"); - hwfn = QED_LEADING_HWFN(cdev); - drv_version.version = (params->drv_major << 24) | - (params->drv_minor << 16) | - (params->drv_rev << 8) | - (params->drv_eng); - strlcpy(drv_version.name, params->name, - MCP_DRV_VER_STR_SIZE - 4); - rc = qed_mcp_send_drv_version(hwfn, hwfn->p_main_ptt, - &drv_version); - if (rc) { - DP_NOTICE(cdev, "Failed sending drv version command\n"); - return rc; + if (IS_PF(cdev)) { + hwfn = QED_LEADING_HWFN(cdev); + drv_version.version = (params->drv_major << 24) | + (params->drv_minor << 16) | + (params->drv_rev << 8) | + (params->drv_eng); + strlcpy(drv_version.name, params->name, + MCP_DRV_VER_STR_SIZE - 4); + rc = qed_mcp_send_drv_version(hwfn, hwfn->p_main_ptt, + &drv_version); + if (rc) { + DP_NOTICE(cdev, "Failed sending drv version command\n"); + return rc; + } } qed_reset_vport_stats(cdev); @@ -818,13 +876,17 @@ static int qed_slowpath_start(struct qed_dev *cdev, err2: qed_hw_timers_stop_all(cdev); - qed_slowpath_irq_free(cdev); + if (IS_PF(cdev)) + qed_slowpath_irq_free(cdev); qed_free_stream_mem(cdev); qed_disable_msix(cdev); err1: qed_resc_free(cdev); err: - release_firmware(cdev->firmware); + if (IS_PF(cdev)) + release_firmware(cdev->firmware); + + qed_iov_wq_stop(cdev, false); return rc; } @@ -834,15 +896,21 @@ static int qed_slowpath_stop(struct qed_dev *cdev) if (!cdev) return -ENODEV; - qed_free_stream_mem(cdev); + if (IS_PF(cdev)) { + qed_free_stream_mem(cdev); + qed_sriov_disable(cdev, true); - qed_nic_stop(cdev); - qed_slowpath_irq_free(cdev); + qed_nic_stop(cdev); + qed_slowpath_irq_free(cdev); + } qed_disable_msix(cdev); qed_nic_reset(cdev); - release_firmware(cdev->firmware); + qed_iov_wq_stop(cdev, true); + + if (IS_PF(cdev)) + release_firmware(cdev->firmware); return 0; } @@ -932,6 +1000,9 @@ static int qed_set_link(struct qed_dev *cdev, if (!cdev) return -ENODEV; + if (IS_VF(cdev)) + return 0; + /* The link should be set only once per PF */ hwfn = &cdev->hwfns[0]; @@ -1043,10 +1114,16 @@ static void qed_fill_link(struct qed_hwfn *hwfn, memset(if_link, 0, sizeof(*if_link)); /* Prepare source inputs */ - memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params)); - memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link)); - memcpy(&link_caps, qed_mcp_get_link_capabilities(hwfn), - sizeof(link_caps)); + if (IS_PF(hwfn->cdev)) { + memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params)); + memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link)); + memcpy(&link_caps, qed_mcp_get_link_capabilities(hwfn), + sizeof(link_caps)); + } else { + qed_vf_get_link_params(hwfn, ¶ms); + qed_vf_get_link_state(hwfn, &link); + qed_vf_get_link_caps(hwfn, &link_caps); + } /* Set the link parameters to pass to protocol driver */ if (link.link_up) @@ -1148,7 +1225,12 @@ static void qed_fill_link(struct qed_hwfn *hwfn, static void qed_get_current_link(struct qed_dev *cdev, struct qed_link_output *if_link) { + int i; + qed_fill_link(&cdev->hwfns[0], if_link); + + for_each_hwfn(cdev, i) + qed_inform_vf_link_state(&cdev->hwfns[i]); } void qed_link_update(struct qed_hwfn *hwfn) @@ -1158,6 +1240,7 @@ void qed_link_update(struct qed_hwfn *hwfn) struct qed_link_output if_link; qed_fill_link(hwfn, &if_link); + qed_inform_vf_link_state(hwfn); if (IS_LEAD_HWFN(hwfn) && cookie) op->link_update(cookie, &if_link); @@ -1169,6 +1252,9 @@ static int qed_drain(struct qed_dev *cdev) struct qed_ptt *ptt; int i, rc; + if (IS_VF(cdev)) + return 0; + for_each_hwfn(cdev, i) { hwfn = &cdev->hwfns[i]; ptt = qed_ptt_acquire(hwfn); diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 2f8309d772c8..1182361798b5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -15,10 +15,13 @@ #include <linux/spinlock.h> #include <linux/string.h> #include "qed.h" +#include "qed_dcbx.h" #include "qed_hsi.h" #include "qed_hw.h" #include "qed_mcp.h" #include "qed_reg_addr.h" +#include "qed_sriov.h" + #define CHIP_MCP_RESP_ITER_US 10 #define QED_DRV_MB_MAX_RETRIES (500 * 1000) /* Account for 5 sec */ @@ -440,6 +443,75 @@ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, return 0; } +static void qed_mcp_handle_vf_flr(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) +{ + u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base, + PUBLIC_PATH); + u32 mfw_path_offsize = qed_rd(p_hwfn, p_ptt, addr); + u32 path_addr = SECTION_ADDR(mfw_path_offsize, + QED_PATH_ID(p_hwfn)); + u32 disabled_vfs[VF_MAX_STATIC / 32]; + int i; + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Reading Disabled VF information from [offset %08x], path_addr %08x\n", + mfw_path_offsize, path_addr); + + for (i = 0; i < (VF_MAX_STATIC / 32); i++) { + disabled_vfs[i] = qed_rd(p_hwfn, p_ptt, + path_addr + + offsetof(struct public_path, + mcp_vf_disabled) + + sizeof(u32) * i); + DP_VERBOSE(p_hwfn, (QED_MSG_SP | QED_MSG_IOV), + "FLR-ed VFs [%08x,...,%08x] - %08x\n", + i * 32, (i + 1) * 32 - 1, disabled_vfs[i]); + } + + if (qed_iov_mark_vf_flr(p_hwfn, disabled_vfs)) + qed_schedule_iov(p_hwfn, QED_IOV_WQ_FLR_FLAG); +} + +int qed_mcp_ack_vf_flr(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u32 *vfs_to_ack) +{ + u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base, + PUBLIC_FUNC); + u32 mfw_func_offsize = qed_rd(p_hwfn, p_ptt, addr); + u32 func_addr = SECTION_ADDR(mfw_func_offsize, + MCP_PF_ID(p_hwfn)); + struct qed_mcp_mb_params mb_params; + union drv_union_data union_data; + int rc; + int i; + + for (i = 0; i < (VF_MAX_STATIC / 32); i++) + DP_VERBOSE(p_hwfn, (QED_MSG_SP | QED_MSG_IOV), + "Acking VFs [%08x,...,%08x] - %08x\n", + i * 32, (i + 1) * 32 - 1, vfs_to_ack[i]); + + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd = DRV_MSG_CODE_VF_DISABLED_DONE; + memcpy(&union_data.ack_vf_disabled, vfs_to_ack, VF_MAX_STATIC / 8); + mb_params.p_data_src = &union_data; + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + if (rc) { + DP_NOTICE(p_hwfn, "Failed to pass ACK for VF flr to MFW\n"); + return -EBUSY; + } + + /* Clear the ACK bits */ + for (i = 0; i < (VF_MAX_STATIC / 32); i++) + qed_wr(p_hwfn, p_ptt, + func_addr + + offsetof(struct public_func, drv_ack_vf_disabled) + + i * sizeof(u32), 0); + + return rc; +} + static void qed_mcp_handle_transceiver_change(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { @@ -751,6 +823,21 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, case MFW_DRV_MSG_LINK_CHANGE: qed_mcp_handle_link_change(p_hwfn, p_ptt, false); break; + case MFW_DRV_MSG_VF_DISABLED: + qed_mcp_handle_vf_flr(p_hwfn, p_ptt); + break; + case MFW_DRV_MSG_LLDP_DATA_UPDATED: + qed_dcbx_mib_update_event(p_hwfn, p_ptt, + QED_DCBX_REMOTE_LLDP_MIB); + break; + case MFW_DRV_MSG_DCBX_REMOTE_MIB_UPDATED: + qed_dcbx_mib_update_event(p_hwfn, p_ptt, + QED_DCBX_REMOTE_MIB); + break; + case MFW_DRV_MSG_DCBX_OPERATIONAL_MIB_UPDATED: + qed_dcbx_mib_update_event(p_hwfn, p_ptt, + QED_DCBX_OPERATIONAL_MIB); + break; case MFW_DRV_MSG_TRANSCEIVER_STATE_CHANGE: qed_mcp_handle_transceiver_change(p_hwfn, p_ptt); break; @@ -787,26 +874,42 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, return rc; } -int qed_mcp_get_mfw_ver(struct qed_dev *cdev, - u32 *p_mfw_ver) +int qed_mcp_get_mfw_ver(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *p_mfw_ver, u32 *p_running_bundle_id) { - struct qed_hwfn *p_hwfn = &cdev->hwfns[0]; - struct qed_ptt *p_ptt; u32 global_offsize; - p_ptt = qed_ptt_acquire(p_hwfn); - if (!p_ptt) - return -EBUSY; + if (IS_VF(p_hwfn->cdev)) { + if (p_hwfn->vf_iov_info) { + struct pfvf_acquire_resp_tlv *p_resp; + + p_resp = &p_hwfn->vf_iov_info->acquire_resp; + *p_mfw_ver = p_resp->pfdev_info.mfw_ver; + return 0; + } else { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF requested MFW version prior to ACQUIRE\n"); + return -EINVAL; + } + } global_offsize = qed_rd(p_hwfn, p_ptt, - SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info-> - public_base, + SECTION_OFFSIZE_ADDR(p_hwfn-> + mcp_info->public_base, PUBLIC_GLOBAL)); - *p_mfw_ver = qed_rd(p_hwfn, p_ptt, - SECTION_ADDR(global_offsize, 0) + - offsetof(struct public_global, mfw_ver)); - - qed_ptt_release(p_hwfn, p_ptt); + *p_mfw_ver = + qed_rd(p_hwfn, p_ptt, + SECTION_ADDR(global_offsize, + 0) + offsetof(struct public_global, mfw_ver)); + + if (p_running_bundle_id != NULL) { + *p_running_bundle_id = qed_rd(p_hwfn, p_ptt, + SECTION_ADDR(global_offsize, 0) + + offsetof(struct public_global, + running_bundle_id)); + } return 0; } @@ -817,6 +920,9 @@ int qed_mcp_get_media_type(struct qed_dev *cdev, struct qed_hwfn *p_hwfn = &cdev->hwfns[0]; struct qed_ptt *p_ptt; + if (IS_VF(cdev)) + return -EINVAL; + if (!qed_mcp_is_init(p_hwfn)) { DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); return -EBUSY; @@ -951,6 +1057,9 @@ int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn, { u32 flash_size; + if (IS_VF(p_hwfn->cdev)) + return -EINVAL; + flash_size = qed_rd(p_hwfn, p_ptt, MCP_REG_NVM_CFG4); flash_size = (flash_size & MCP_REG_NVM_CFG4_FLASH_SIZE) >> MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT; @@ -961,6 +1070,37 @@ int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn, return 0; } +int qed_mcp_config_vf_msix(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u8 vf_id, u8 num) +{ + u32 resp = 0, param = 0, rc_param = 0; + int rc; + + /* Only Leader can configure MSIX, and need to take CMT into account */ + if (!IS_LEAD_HWFN(p_hwfn)) + return 0; + num *= p_hwfn->cdev->num_hwfns; + + param |= (vf_id << DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_SHIFT) & + DRV_MB_PARAM_CFG_VF_MSIX_VF_ID_MASK; + param |= (num << DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_SHIFT) & + DRV_MB_PARAM_CFG_VF_MSIX_SB_NUM_MASK; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_CFG_VF_MSIX, param, + &resp, &rc_param); + + if (resp != FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE) { + DP_NOTICE(p_hwfn, "VF[%d]: MFW failed to set MSI-X\n", vf_id); + rc = -EINVAL; + } else { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Requested 0x%02x MSI-x interrupts from VF 0x%02x\n", + num, vf_id); + } + + return rc; +} + int qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 5f218eed0541..6dd59eb7f4c6 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -149,13 +149,16 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, /** * @brief Get the management firmware version value * - * @param cdev - qed dev pointer - * @param mfw_ver - mfw version value + * @param p_hwfn + * @param p_ptt + * @param p_mfw_ver - mfw version value + * @param p_running_bundle_id - image id in nvram; Optional. * - * @return int - 0 - operation was successul. + * @return int - 0 - operation was successful. */ -int qed_mcp_get_mfw_ver(struct qed_dev *cdev, - u32 *mfw_ver); +int qed_mcp_get_mfw_ver(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *p_mfw_ver, u32 *p_running_bundle_id); /** * @brief Get media type value of the port. @@ -390,6 +393,18 @@ void qed_mcp_read_mb(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); /** + * @brief Ack to mfw that driver finished FLR process for VFs + * + * @param p_hwfn + * @param p_ptt + * @param vfs_to_ack - bit mask of all engine VFs for which the PF acks. + * + * @param return int - 0 upon success. + */ +int qed_mcp_ack_vf_flr(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u32 *vfs_to_ack); + +/** * @brief - calls during init to read shmem of all function-related info. * * @param p_hwfn @@ -418,6 +433,20 @@ int qed_mcp_reset(struct qed_hwfn *p_hwfn, * @return true iff MFW is running and mcp_info is initialized */ bool qed_mcp_is_init(struct qed_hwfn *p_hwfn); + +/** + * @brief request MFW to configure MSI-X for a VF + * + * @param p_hwfn + * @param p_ptt + * @param vf_id - absolute inside engine + * @param num_sbs - number of entries to request + * + * @return int + */ +int qed_mcp_config_vf_msix(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u8 vf_id, u8 num); + int qed_configure_pf_min_bandwidth(struct qed_dev *cdev, u8 min_bw); int qed_configure_pf_max_bandwidth(struct qed_dev *cdev, u8 max_bw); int __qed_configure_pf_max_bandwidth(struct qed_hwfn *p_hwfn, diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index bf4d7ccd56bb..3a6c506f0d71 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -39,6 +39,10 @@ 0x2aae04UL #define PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER \ 0x2aa16cUL +#define PGLUE_B_REG_WAS_ERROR_VF_31_0_CLR \ + 0x2aa118UL +#define PSWHST_REG_ZONE_PERMISSION_TABLE \ + 0x2a0800UL #define BAR0_MAP_REG_MSDM_RAM \ 0x1d00000UL #define BAR0_MAP_REG_USDM_RAM \ @@ -77,6 +81,8 @@ 0x2f2eb0UL #define DORQ_REG_PF_DB_ENABLE \ 0x100508UL +#define DORQ_REG_VF_USAGE_CNT \ + 0x1009c4UL #define QM_REG_PF_EN \ 0x2f2ea4UL #define TCFC_REG_STRONG_ENABLE_PF \ @@ -111,6 +117,8 @@ 0x009778UL #define MISCS_REG_CHIP_METAL \ 0x009774UL +#define MISCS_REG_FUNCTION_HIDE \ + 0x0096f0UL #define BRB_REG_HEADER_SIZE \ 0x340804UL #define BTB_REG_HEADER_SIZE \ @@ -119,6 +127,8 @@ 0x1c0708UL #define CCFC_REG_ACTIVITY_COUNTER \ 0x2e8800UL +#define CCFC_REG_STRONG_ENABLE_VF \ + 0x2e070cUL #define CDU_REG_CID_ADDR_PARAMS \ 0x580900UL #define DBG_REG_CLIENT_ENABLE \ @@ -161,6 +171,10 @@ 0x040200UL #define PBF_REG_INIT \ 0xd80000UL +#define PBF_REG_NUM_BLOCKS_ALLOCATED_PROD_VOQ0 \ + 0xd806c8UL +#define PBF_REG_NUM_BLOCKS_ALLOCATED_CONS_VOQ0 \ + 0xd806ccUL #define PTU_REG_ATC_INIT_ARRAY \ 0x560000UL #define PCM_REG_INIT \ @@ -385,6 +399,8 @@ 0x1d0000UL #define IGU_REG_PF_CONFIGURATION \ 0x180800UL +#define IGU_REG_VF_CONFIGURATION \ + 0x180804UL #define MISC_REG_AEU_ENABLE1_IGU_OUT_0 \ 0x00849cUL #define MISC_REG_AEU_AFTER_INVERT_1_IGU \ @@ -411,6 +427,10 @@ 0x1 << 0) #define IGU_REG_MAPPING_MEMORY \ 0x184000UL +#define IGU_REG_STATISTIC_NUM_VF_MSG_SENT \ + 0x180408UL +#define IGU_REG_WRITE_DONE_PENDING \ + 0x180900UL #define MISCS_REG_GENERIC_POR_0 \ 0x0096d4UL #define MCP_REG_NVM_CFG4 \ diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h index eec137f40895..ea4e9ce53e0a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sp.h @@ -62,6 +62,9 @@ union ramrod_data { struct vport_stop_ramrod_data vport_stop; struct vport_update_ramrod_data vport_update; struct vport_filter_update_ramrod_data vport_filter_update; + + struct vf_start_ramrod_data vf_start; + struct vf_stop_ramrod_data vf_stop; }; #define EQ_MAX_CREDIT 0xffffffff @@ -341,13 +344,27 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn, * @param p_hwfn * @param p_tunn * @param mode + * @param allow_npar_tx_switch * * @return int */ int qed_sp_pf_start(struct qed_hwfn *p_hwfn, struct qed_tunn_start_params *p_tunn, - enum qed_mf_mode mode); + enum qed_mf_mode mode, bool allow_npar_tx_switch); + +/** + * @brief qed_sp_pf_update - PF Function Update Ramrod + * + * This ramrod updates function-related parameters. Every parameter can be + * updated independently, according to configuration flags. + * + * @param p_hwfn + * + * @return int + */ + +int qed_sp_pf_update(struct qed_hwfn *p_hwfn); /** * @brief qed_sp_pf_stop - PF Function Stop Ramrod diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c index 9f9bc10d0f6c..67f6ce3c84c8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c @@ -15,11 +15,13 @@ #include "qed.h" #include <linux/qed/qed_chain.h> #include "qed_cxt.h" +#include "qed_dcbx.h" #include "qed_hsi.h" #include "qed_hw.h" #include "qed_int.h" #include "qed_reg_addr.h" #include "qed_sp.h" +#include "qed_sriov.h" int qed_sp_init_request(struct qed_hwfn *p_hwfn, struct qed_spq_entry **pp_ent, @@ -298,7 +300,7 @@ qed_tunn_set_pf_start_params(struct qed_hwfn *p_hwfn, int qed_sp_pf_start(struct qed_hwfn *p_hwfn, struct qed_tunn_start_params *p_tunn, - enum qed_mf_mode mode) + enum qed_mf_mode mode, bool allow_npar_tx_switch) { struct pf_start_ramrod_data *p_ramrod = NULL; u16 sb = qed_int_get_sp_sb_id(p_hwfn); @@ -357,11 +359,53 @@ int qed_sp_pf_start(struct qed_hwfn *p_hwfn, &p_ramrod->tunnel_config); p_hwfn->hw_info.personality = PERSONALITY_ETH; + if (IS_MF_SI(p_hwfn)) + p_ramrod->allow_npar_tx_switching = allow_npar_tx_switch; + + if (p_hwfn->cdev->p_iov_info) { + struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info; + + p_ramrod->base_vf_id = (u8) p_iov->first_vf_in_pf; + p_ramrod->num_vfs = (u8) p_iov->total_vfs; + } + DP_VERBOSE(p_hwfn, QED_MSG_SPQ, "Setting event_ring_sb [id %04x index %02x], outer_tag [%d]\n", sb, sb_index, p_ramrod->outer_tag); + rc = qed_spq_post(p_hwfn, p_ent, NULL); + + if (p_tunn) { + qed_set_hw_tunn_mode(p_hwfn, p_hwfn->p_main_ptt, + p_tunn->tunn_mode); + p_hwfn->cdev->tunn_mode = p_tunn->tunn_mode; + } + + return rc; +} + +int qed_sp_pf_update(struct qed_hwfn *p_hwfn) +{ + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + init_data.comp_mode = QED_SPQ_MODE_CB; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + COMMON_RAMROD_PF_UPDATE, PROTOCOLID_COMMON, + &init_data); + if (rc) + return rc; + + qed_dcbx_set_pf_update_params(&p_hwfn->p_dcbx_info->results, + &p_ent->ramrod.pf_update); + return qed_spq_post(p_hwfn, p_ent, NULL); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 89469d5aae25..acac6626a1b2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -27,6 +27,7 @@ #include "qed_mcp.h" #include "qed_reg_addr.h" #include "qed_sp.h" +#include "qed_sriov.h" /*************************************************************************** * Structures & Definitions @@ -242,10 +243,17 @@ static int qed_async_event_completion(struct qed_hwfn *p_hwfn, struct event_ring_entry *p_eqe) { - DP_NOTICE(p_hwfn, - "Unknown Async completion for protocol: %d\n", - p_eqe->protocol_id); - return -EINVAL; + switch (p_eqe->protocol_id) { + case PROTOCOLID_COMMON: + return qed_sriov_eqe_event(p_hwfn, + p_eqe->opcode, + p_eqe->echo, &p_eqe->data); + default: + DP_NOTICE(p_hwfn, + "Unknown Async completion for protocol: %d\n", + p_eqe->protocol_id); + return -EINVAL; + } } /*************************************************************************** @@ -379,6 +387,9 @@ static int qed_cqe_completion( struct eth_slow_path_rx_cqe *cqe, enum protocol_type protocol) { + if (IS_VF(p_hwfn->cdev)) + return 0; + /* @@@tmp - it's possible we'll eventually want to handle some * actual commands that can arrive here, but for now this is only * used to complete the ramrod using the echo value on the cqe diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c new file mode 100644 index 000000000000..c325ee857ecd --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -0,0 +1,3613 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include <linux/etherdevice.h> +#include <linux/crc32.h> +#include <linux/qed/qed_iov_if.h> +#include "qed_cxt.h" +#include "qed_hsi.h" +#include "qed_hw.h" +#include "qed_init_ops.h" +#include "qed_int.h" +#include "qed_mcp.h" +#include "qed_reg_addr.h" +#include "qed_sp.h" +#include "qed_sriov.h" +#include "qed_vf.h" + +/* IOV ramrods */ +static int qed_sp_vf_start(struct qed_hwfn *p_hwfn, + u32 concrete_vfid, u16 opaque_vfid) +{ + struct vf_start_ramrod_data *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + init_data.opaque_fid = opaque_vfid; + init_data.comp_mode = QED_SPQ_MODE_EBLOCK; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + COMMON_RAMROD_VF_START, + PROTOCOLID_COMMON, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.vf_start; + + p_ramrod->vf_id = GET_FIELD(concrete_vfid, PXP_CONCRETE_FID_VFID); + p_ramrod->opaque_fid = cpu_to_le16(opaque_vfid); + + p_ramrod->personality = PERSONALITY_ETH; + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +static int qed_sp_vf_stop(struct qed_hwfn *p_hwfn, + u32 concrete_vfid, u16 opaque_vfid) +{ + struct vf_stop_ramrod_data *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + int rc = -EINVAL; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + init_data.opaque_fid = opaque_vfid; + init_data.comp_mode = QED_SPQ_MODE_EBLOCK; + + rc = qed_sp_init_request(p_hwfn, &p_ent, + COMMON_RAMROD_VF_STOP, + PROTOCOLID_COMMON, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.vf_stop; + + p_ramrod->vf_id = GET_FIELD(concrete_vfid, PXP_CONCRETE_FID_VFID); + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + +bool qed_iov_is_valid_vfid(struct qed_hwfn *p_hwfn, + int rel_vf_id, bool b_enabled_only) +{ + if (!p_hwfn->pf_iov_info) { + DP_NOTICE(p_hwfn->cdev, "No iov info\n"); + return false; + } + + if ((rel_vf_id >= p_hwfn->cdev->p_iov_info->total_vfs) || + (rel_vf_id < 0)) + return false; + + if ((!p_hwfn->pf_iov_info->vfs_array[rel_vf_id].b_init) && + b_enabled_only) + return false; + + return true; +} + +static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn, + u16 relative_vf_id, + bool b_enabled_only) +{ + struct qed_vf_info *vf = NULL; + + if (!p_hwfn->pf_iov_info) { + DP_NOTICE(p_hwfn->cdev, "No iov info\n"); + return NULL; + } + + if (qed_iov_is_valid_vfid(p_hwfn, relative_vf_id, b_enabled_only)) + vf = &p_hwfn->pf_iov_info->vfs_array[relative_vf_id]; + else + DP_ERR(p_hwfn, "qed_iov_get_vf_info: VF[%d] is not enabled\n", + relative_vf_id); + + return vf; +} + +int qed_iov_post_vf_bulletin(struct qed_hwfn *p_hwfn, + int vfid, struct qed_ptt *p_ptt) +{ + struct qed_bulletin_content *p_bulletin; + int crc_size = sizeof(p_bulletin->crc); + struct qed_dmae_params params; + struct qed_vf_info *p_vf; + + p_vf = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!p_vf) + return -EINVAL; + + if (!p_vf->vf_bulletin) + return -EINVAL; + + p_bulletin = p_vf->bulletin.p_virt; + + /* Increment bulletin board version and compute crc */ + p_bulletin->version++; + p_bulletin->crc = crc32(0, (u8 *)p_bulletin + crc_size, + p_vf->bulletin.size - crc_size); + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Posting Bulletin 0x%08x to VF[%d] (CRC 0x%08x)\n", + p_bulletin->version, p_vf->relative_vf_id, p_bulletin->crc); + + /* propagate bulletin board via dmae to vm memory */ + memset(¶ms, 0, sizeof(params)); + params.flags = QED_DMAE_FLAG_VF_DST; + params.dst_vfid = p_vf->abs_vf_id; + return qed_dmae_host2host(p_hwfn, p_ptt, p_vf->bulletin.phys, + p_vf->vf_bulletin, p_vf->bulletin.size / 4, + ¶ms); +} + +static int qed_iov_pci_cfg_info(struct qed_dev *cdev) +{ + struct qed_hw_sriov_info *iov = cdev->p_iov_info; + int pos = iov->pos; + + DP_VERBOSE(cdev, QED_MSG_IOV, "sriov ext pos %d\n", pos); + pci_read_config_word(cdev->pdev, pos + PCI_SRIOV_CTRL, &iov->ctrl); + + pci_read_config_word(cdev->pdev, + pos + PCI_SRIOV_TOTAL_VF, &iov->total_vfs); + pci_read_config_word(cdev->pdev, + pos + PCI_SRIOV_INITIAL_VF, &iov->initial_vfs); + + pci_read_config_word(cdev->pdev, pos + PCI_SRIOV_NUM_VF, &iov->num_vfs); + if (iov->num_vfs) { + DP_VERBOSE(cdev, + QED_MSG_IOV, + "Number of VFs are already set to non-zero value. Ignoring PCI configuration value\n"); + iov->num_vfs = 0; + } + + pci_read_config_word(cdev->pdev, + pos + PCI_SRIOV_VF_OFFSET, &iov->offset); + + pci_read_config_word(cdev->pdev, + pos + PCI_SRIOV_VF_STRIDE, &iov->stride); + + pci_read_config_word(cdev->pdev, + pos + PCI_SRIOV_VF_DID, &iov->vf_device_id); + + pci_read_config_dword(cdev->pdev, + pos + PCI_SRIOV_SUP_PGSIZE, &iov->pgsz); + + pci_read_config_dword(cdev->pdev, pos + PCI_SRIOV_CAP, &iov->cap); + + pci_read_config_byte(cdev->pdev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); + + DP_VERBOSE(cdev, + QED_MSG_IOV, + "IOV info: nres %d, cap 0x%x, ctrl 0x%x, total %d, initial %d, num vfs %d, offset %d, stride %d, page size 0x%x\n", + iov->nres, + iov->cap, + iov->ctrl, + iov->total_vfs, + iov->initial_vfs, + iov->nr_virtfn, iov->offset, iov->stride, iov->pgsz); + + /* Some sanity checks */ + if (iov->num_vfs > NUM_OF_VFS(cdev) || + iov->total_vfs > NUM_OF_VFS(cdev)) { + /* This can happen only due to a bug. In this case we set + * num_vfs to zero to avoid memory corruption in the code that + * assumes max number of vfs + */ + DP_NOTICE(cdev, + "IOV: Unexpected number of vfs set: %d setting num_vf to zero\n", + iov->num_vfs); + + iov->num_vfs = 0; + iov->total_vfs = 0; + } + + return 0; +} + +static void qed_iov_clear_vf_igu_blocks(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) +{ + struct qed_igu_block *p_sb; + u16 sb_id; + u32 val; + + if (!p_hwfn->hw_info.p_igu_info) { + DP_ERR(p_hwfn, + "qed_iov_clear_vf_igu_blocks IGU Info not initialized\n"); + return; + } + + for (sb_id = 0; sb_id < QED_MAPPING_MEMORY_SIZE(p_hwfn->cdev); + sb_id++) { + p_sb = &p_hwfn->hw_info.p_igu_info->igu_map.igu_blocks[sb_id]; + if ((p_sb->status & QED_IGU_STATUS_FREE) && + !(p_sb->status & QED_IGU_STATUS_PF)) { + val = qed_rd(p_hwfn, p_ptt, + IGU_REG_MAPPING_MEMORY + sb_id * 4); + SET_FIELD(val, IGU_MAPPING_LINE_VALID, 0); + qed_wr(p_hwfn, p_ptt, + IGU_REG_MAPPING_MEMORY + 4 * sb_id, val); + } + } +} + +static void qed_iov_setup_vfdb(struct qed_hwfn *p_hwfn) +{ + struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info; + struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info; + struct qed_bulletin_content *p_bulletin_virt; + dma_addr_t req_p, rply_p, bulletin_p; + union pfvf_tlvs *p_reply_virt_addr; + union vfpf_tlvs *p_req_virt_addr; + u8 idx = 0; + + memset(p_iov_info->vfs_array, 0, sizeof(p_iov_info->vfs_array)); + + p_req_virt_addr = p_iov_info->mbx_msg_virt_addr; + req_p = p_iov_info->mbx_msg_phys_addr; + p_reply_virt_addr = p_iov_info->mbx_reply_virt_addr; + rply_p = p_iov_info->mbx_reply_phys_addr; + p_bulletin_virt = p_iov_info->p_bulletins; + bulletin_p = p_iov_info->bulletins_phys; + if (!p_req_virt_addr || !p_reply_virt_addr || !p_bulletin_virt) { + DP_ERR(p_hwfn, + "qed_iov_setup_vfdb called without allocating mem first\n"); + return; + } + + for (idx = 0; idx < p_iov->total_vfs; idx++) { + struct qed_vf_info *vf = &p_iov_info->vfs_array[idx]; + u32 concrete; + + vf->vf_mbx.req_virt = p_req_virt_addr + idx; + vf->vf_mbx.req_phys = req_p + idx * sizeof(union vfpf_tlvs); + vf->vf_mbx.reply_virt = p_reply_virt_addr + idx; + vf->vf_mbx.reply_phys = rply_p + idx * sizeof(union pfvf_tlvs); + + vf->state = VF_STOPPED; + vf->b_init = false; + + vf->bulletin.phys = idx * + sizeof(struct qed_bulletin_content) + + bulletin_p; + vf->bulletin.p_virt = p_bulletin_virt + idx; + vf->bulletin.size = sizeof(struct qed_bulletin_content); + + vf->relative_vf_id = idx; + vf->abs_vf_id = idx + p_iov->first_vf_in_pf; + concrete = qed_vfid_to_concrete(p_hwfn, vf->abs_vf_id); + vf->concrete_fid = concrete; + vf->opaque_fid = (p_hwfn->hw_info.opaque_fid & 0xff) | + (vf->abs_vf_id << 8); + vf->vport_id = idx + 1; + } +} + +static int qed_iov_allocate_vfdb(struct qed_hwfn *p_hwfn) +{ + struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info; + void **p_v_addr; + u16 num_vfs = 0; + + num_vfs = p_hwfn->cdev->p_iov_info->total_vfs; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "qed_iov_allocate_vfdb for %d VFs\n", num_vfs); + + /* Allocate PF Mailbox buffer (per-VF) */ + p_iov_info->mbx_msg_size = sizeof(union vfpf_tlvs) * num_vfs; + p_v_addr = &p_iov_info->mbx_msg_virt_addr; + *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->mbx_msg_size, + &p_iov_info->mbx_msg_phys_addr, + GFP_KERNEL); + if (!*p_v_addr) + return -ENOMEM; + + /* Allocate PF Mailbox Reply buffer (per-VF) */ + p_iov_info->mbx_reply_size = sizeof(union pfvf_tlvs) * num_vfs; + p_v_addr = &p_iov_info->mbx_reply_virt_addr; + *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->mbx_reply_size, + &p_iov_info->mbx_reply_phys_addr, + GFP_KERNEL); + if (!*p_v_addr) + return -ENOMEM; + + p_iov_info->bulletins_size = sizeof(struct qed_bulletin_content) * + num_vfs; + p_v_addr = &p_iov_info->p_bulletins; + *p_v_addr = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->bulletins_size, + &p_iov_info->bulletins_phys, + GFP_KERNEL); + if (!*p_v_addr) + return -ENOMEM; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "PF's Requests mailbox [%p virt 0x%llx phys], Response mailbox [%p virt 0x%llx phys] Bulletins [%p virt 0x%llx phys]\n", + p_iov_info->mbx_msg_virt_addr, + (u64) p_iov_info->mbx_msg_phys_addr, + p_iov_info->mbx_reply_virt_addr, + (u64) p_iov_info->mbx_reply_phys_addr, + p_iov_info->p_bulletins, (u64) p_iov_info->bulletins_phys); + + return 0; +} + +static void qed_iov_free_vfdb(struct qed_hwfn *p_hwfn) +{ + struct qed_pf_iov *p_iov_info = p_hwfn->pf_iov_info; + + if (p_hwfn->pf_iov_info->mbx_msg_virt_addr) + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->mbx_msg_size, + p_iov_info->mbx_msg_virt_addr, + p_iov_info->mbx_msg_phys_addr); + + if (p_hwfn->pf_iov_info->mbx_reply_virt_addr) + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->mbx_reply_size, + p_iov_info->mbx_reply_virt_addr, + p_iov_info->mbx_reply_phys_addr); + + if (p_iov_info->p_bulletins) + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + p_iov_info->bulletins_size, + p_iov_info->p_bulletins, + p_iov_info->bulletins_phys); +} + +int qed_iov_alloc(struct qed_hwfn *p_hwfn) +{ + struct qed_pf_iov *p_sriov; + + if (!IS_PF_SRIOV(p_hwfn)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "No SR-IOV - no need for IOV db\n"); + return 0; + } + + p_sriov = kzalloc(sizeof(*p_sriov), GFP_KERNEL); + if (!p_sriov) { + DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_sriov'\n"); + return -ENOMEM; + } + + p_hwfn->pf_iov_info = p_sriov; + + return qed_iov_allocate_vfdb(p_hwfn); +} + +void qed_iov_setup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + if (!IS_PF_SRIOV(p_hwfn) || !IS_PF_SRIOV_ALLOC(p_hwfn)) + return; + + qed_iov_setup_vfdb(p_hwfn); + qed_iov_clear_vf_igu_blocks(p_hwfn, p_ptt); +} + +void qed_iov_free(struct qed_hwfn *p_hwfn) +{ + if (IS_PF_SRIOV_ALLOC(p_hwfn)) { + qed_iov_free_vfdb(p_hwfn); + kfree(p_hwfn->pf_iov_info); + } +} + +void qed_iov_free_hw_info(struct qed_dev *cdev) +{ + kfree(cdev->p_iov_info); + cdev->p_iov_info = NULL; +} + +int qed_iov_hw_info(struct qed_hwfn *p_hwfn) +{ + struct qed_dev *cdev = p_hwfn->cdev; + int pos; + int rc; + + if (IS_VF(p_hwfn->cdev)) + return 0; + + /* Learn the PCI configuration */ + pos = pci_find_ext_capability(p_hwfn->cdev->pdev, + PCI_EXT_CAP_ID_SRIOV); + if (!pos) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, "No PCIe IOV support\n"); + return 0; + } + + /* Allocate a new struct for IOV information */ + cdev->p_iov_info = kzalloc(sizeof(*cdev->p_iov_info), GFP_KERNEL); + if (!cdev->p_iov_info) { + DP_NOTICE(p_hwfn, "Can't support IOV due to lack of memory\n"); + return -ENOMEM; + } + cdev->p_iov_info->pos = pos; + + rc = qed_iov_pci_cfg_info(cdev); + if (rc) + return rc; + + /* We want PF IOV to be synonemous with the existance of p_iov_info; + * In case the capability is published but there are no VFs, simply + * de-allocate the struct. + */ + if (!cdev->p_iov_info->total_vfs) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "IOV capabilities, but no VFs are published\n"); + kfree(cdev->p_iov_info); + cdev->p_iov_info = NULL; + return 0; + } + + /* Calculate the first VF index - this is a bit tricky; Basically, + * VFs start at offset 16 relative to PF0, and 2nd engine VFs begin + * after the first engine's VFs. + */ + cdev->p_iov_info->first_vf_in_pf = p_hwfn->cdev->p_iov_info->offset + + p_hwfn->abs_pf_id - 16; + if (QED_PATH_ID(p_hwfn)) + cdev->p_iov_info->first_vf_in_pf -= MAX_NUM_VFS_BB; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "First VF in hwfn 0x%08x\n", + cdev->p_iov_info->first_vf_in_pf); + + return 0; +} + +static bool qed_iov_pf_sanity_check(struct qed_hwfn *p_hwfn, int vfid) +{ + /* Check PF supports sriov */ + if (IS_VF(p_hwfn->cdev) || !IS_QED_SRIOV(p_hwfn->cdev) || + !IS_PF_SRIOV_ALLOC(p_hwfn)) + return false; + + /* Check VF validity */ + if (!qed_iov_is_valid_vfid(p_hwfn, vfid, true)) + return false; + + return true; +} + +static void qed_iov_set_vf_to_disable(struct qed_dev *cdev, + u16 rel_vf_id, u8 to_disable) +{ + struct qed_vf_info *vf; + int i; + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + + vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, false); + if (!vf) + continue; + + vf->to_disable = to_disable; + } +} + +void qed_iov_set_vfs_to_disable(struct qed_dev *cdev, u8 to_disable) +{ + u16 i; + + if (!IS_QED_SRIOV(cdev)) + return; + + for (i = 0; i < cdev->p_iov_info->total_vfs; i++) + qed_iov_set_vf_to_disable(cdev, i, to_disable); +} + +static void qed_iov_vf_pglue_clear_err(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u8 abs_vfid) +{ + qed_wr(p_hwfn, p_ptt, + PGLUE_B_REG_WAS_ERROR_VF_31_0_CLR + (abs_vfid >> 5) * 4, + 1 << (abs_vfid & 0x1f)); +} + +static void qed_iov_vf_igu_reset(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, struct qed_vf_info *vf) +{ + int i; + + /* Set VF masks and configuration - pretend */ + qed_fid_pretend(p_hwfn, p_ptt, (u16) vf->concrete_fid); + + qed_wr(p_hwfn, p_ptt, IGU_REG_STATISTIC_NUM_VF_MSG_SENT, 0); + + /* unpretend */ + qed_fid_pretend(p_hwfn, p_ptt, (u16) p_hwfn->hw_info.concrete_fid); + + /* iterate over all queues, clear sb consumer */ + for (i = 0; i < vf->num_sbs; i++) + qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, + vf->igu_sbs[i], + vf->opaque_fid, true); +} + +static void qed_iov_vf_igu_set_int(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf, bool enable) +{ + u32 igu_vf_conf; + + qed_fid_pretend(p_hwfn, p_ptt, (u16) vf->concrete_fid); + + igu_vf_conf = qed_rd(p_hwfn, p_ptt, IGU_REG_VF_CONFIGURATION); + + if (enable) + igu_vf_conf |= IGU_VF_CONF_MSI_MSIX_EN; + else + igu_vf_conf &= ~IGU_VF_CONF_MSI_MSIX_EN; + + qed_wr(p_hwfn, p_ptt, IGU_REG_VF_CONFIGURATION, igu_vf_conf); + + /* unpretend */ + qed_fid_pretend(p_hwfn, p_ptt, (u16) p_hwfn->hw_info.concrete_fid); +} + +static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u32 igu_vf_conf = IGU_VF_CONF_FUNC_EN; + int rc; + + if (vf->to_disable) + return 0; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "Enable internal access for vf %x [abs %x]\n", + vf->abs_vf_id, QED_VF_ABS_ID(p_hwfn, vf)); + + qed_iov_vf_pglue_clear_err(p_hwfn, p_ptt, QED_VF_ABS_ID(p_hwfn, vf)); + + qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf); + + rc = qed_mcp_config_vf_msix(p_hwfn, p_ptt, vf->abs_vf_id, vf->num_sbs); + if (rc) + return rc; + + qed_fid_pretend(p_hwfn, p_ptt, (u16) vf->concrete_fid); + + SET_FIELD(igu_vf_conf, IGU_VF_CONF_PARENT, p_hwfn->rel_pf_id); + STORE_RT_REG(p_hwfn, IGU_REG_VF_CONFIGURATION_RT_OFFSET, igu_vf_conf); + + qed_init_run(p_hwfn, p_ptt, PHASE_VF, vf->abs_vf_id, + p_hwfn->hw_info.hw_mode); + + /* unpretend */ + qed_fid_pretend(p_hwfn, p_ptt, (u16) p_hwfn->hw_info.concrete_fid); + + if (vf->state != VF_STOPPED) { + DP_NOTICE(p_hwfn, "VF[%02x] is already started\n", + vf->abs_vf_id); + return -EINVAL; + } + + /* Start VF */ + rc = qed_sp_vf_start(p_hwfn, vf->concrete_fid, vf->opaque_fid); + if (rc) + DP_NOTICE(p_hwfn, "Failed to start VF[%02x]\n", vf->abs_vf_id); + + vf->state = VF_FREE; + + return rc; +} + +/** + * @brief qed_iov_config_perm_table - configure the permission + * zone table. + * In E4, queue zone permission table size is 320x9. There + * are 320 VF queues for single engine device (256 for dual + * engine device), and each entry has the following format: + * {Valid, VF[7:0]} + * @param p_hwfn + * @param p_ptt + * @param vf + * @param enable + */ +static void qed_iov_config_perm_table(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf, u8 enable) +{ + u32 reg_addr, val; + u16 qzone_id = 0; + int qid; + + for (qid = 0; qid < vf->num_rxqs; qid++) { + qed_fw_l2_queue(p_hwfn, vf->vf_queues[qid].fw_rx_qid, + &qzone_id); + + reg_addr = PSWHST_REG_ZONE_PERMISSION_TABLE + qzone_id * 4; + val = enable ? (vf->abs_vf_id | (1 << 8)) : 0; + qed_wr(p_hwfn, p_ptt, reg_addr, val); + } +} + +static void qed_iov_enable_vf_traffic(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + /* Reset vf in IGU - interrupts are still disabled */ + qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf); + + qed_iov_vf_igu_set_int(p_hwfn, p_ptt, vf, 1); + + /* Permission Table */ + qed_iov_config_perm_table(p_hwfn, p_ptt, vf, true); +} + +static u8 qed_iov_alloc_vf_igu_sbs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf, u16 num_rx_queues) +{ + struct qed_igu_block *igu_blocks; + int qid = 0, igu_id = 0; + u32 val = 0; + + igu_blocks = p_hwfn->hw_info.p_igu_info->igu_map.igu_blocks; + + if (num_rx_queues > p_hwfn->hw_info.p_igu_info->free_blks) + num_rx_queues = p_hwfn->hw_info.p_igu_info->free_blks; + p_hwfn->hw_info.p_igu_info->free_blks -= num_rx_queues; + + SET_FIELD(val, IGU_MAPPING_LINE_FUNCTION_NUMBER, vf->abs_vf_id); + SET_FIELD(val, IGU_MAPPING_LINE_VALID, 1); + SET_FIELD(val, IGU_MAPPING_LINE_PF_VALID, 0); + + while ((qid < num_rx_queues) && + (igu_id < QED_MAPPING_MEMORY_SIZE(p_hwfn->cdev))) { + if (igu_blocks[igu_id].status & QED_IGU_STATUS_FREE) { + struct cau_sb_entry sb_entry; + + vf->igu_sbs[qid] = (u16)igu_id; + igu_blocks[igu_id].status &= ~QED_IGU_STATUS_FREE; + + SET_FIELD(val, IGU_MAPPING_LINE_VECTOR_NUMBER, qid); + + qed_wr(p_hwfn, p_ptt, + IGU_REG_MAPPING_MEMORY + sizeof(u32) * igu_id, + val); + + /* Configure igu sb in CAU which were marked valid */ + qed_init_cau_sb_entry(p_hwfn, &sb_entry, + p_hwfn->rel_pf_id, + vf->abs_vf_id, 1); + qed_dmae_host2grc(p_hwfn, p_ptt, + (u64)(uintptr_t)&sb_entry, + CAU_REG_SB_VAR_MEMORY + + igu_id * sizeof(u64), 2, 0); + qid++; + } + igu_id++; + } + + vf->num_sbs = (u8) num_rx_queues; + + return vf->num_sbs; +} + +static void qed_iov_free_vf_igu_sbs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_igu_info *p_info = p_hwfn->hw_info.p_igu_info; + int idx, igu_id; + u32 addr, val; + + /* Invalidate igu CAM lines and mark them as free */ + for (idx = 0; idx < vf->num_sbs; idx++) { + igu_id = vf->igu_sbs[idx]; + addr = IGU_REG_MAPPING_MEMORY + sizeof(u32) * igu_id; + + val = qed_rd(p_hwfn, p_ptt, addr); + SET_FIELD(val, IGU_MAPPING_LINE_VALID, 0); + qed_wr(p_hwfn, p_ptt, addr, val); + + p_info->igu_map.igu_blocks[igu_id].status |= + QED_IGU_STATUS_FREE; + + p_hwfn->hw_info.p_igu_info->free_blks++; + } + + vf->num_sbs = 0; +} + +static int qed_iov_init_hw_for_vf(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 rel_vf_id, u16 num_rx_queues) +{ + u8 num_of_vf_avaiable_chains = 0; + struct qed_vf_info *vf = NULL; + int rc = 0; + u32 cids; + u8 i; + + vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, false); + if (!vf) { + DP_ERR(p_hwfn, "qed_iov_init_hw_for_vf : vf is NULL\n"); + return -EINVAL; + } + + if (vf->b_init) { + DP_NOTICE(p_hwfn, "VF[%d] is already active.\n", rel_vf_id); + return -EINVAL; + } + + /* Limit number of queues according to number of CIDs */ + qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_ETH, &cids); + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d] - requesting to initialize for 0x%04x queues [0x%04x CIDs available]\n", + vf->relative_vf_id, num_rx_queues, (u16) cids); + num_rx_queues = min_t(u16, num_rx_queues, ((u16) cids)); + + num_of_vf_avaiable_chains = qed_iov_alloc_vf_igu_sbs(p_hwfn, + p_ptt, + vf, + num_rx_queues); + if (!num_of_vf_avaiable_chains) { + DP_ERR(p_hwfn, "no available igu sbs\n"); + return -ENOMEM; + } + + /* Choose queue number and index ranges */ + vf->num_rxqs = num_of_vf_avaiable_chains; + vf->num_txqs = num_of_vf_avaiable_chains; + + for (i = 0; i < vf->num_rxqs; i++) { + u16 queue_id = qed_int_queue_id_from_sb_id(p_hwfn, + vf->igu_sbs[i]); + + if (queue_id > RESC_NUM(p_hwfn, QED_L2_QUEUE)) { + DP_NOTICE(p_hwfn, + "VF[%d] will require utilizing of out-of-bounds queues - %04x\n", + vf->relative_vf_id, queue_id); + return -EINVAL; + } + + /* CIDs are per-VF, so no problem having them 0-based. */ + vf->vf_queues[i].fw_rx_qid = queue_id; + vf->vf_queues[i].fw_tx_qid = queue_id; + vf->vf_queues[i].fw_cid = i; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d] - [%d] SB %04x, Tx/Rx queue %04x CID %04x\n", + vf->relative_vf_id, i, vf->igu_sbs[i], queue_id, i); + } + rc = qed_iov_enable_vf_access(p_hwfn, p_ptt, vf); + if (!rc) { + vf->b_init = true; + + if (IS_LEAD_HWFN(p_hwfn)) + p_hwfn->cdev->p_iov_info->num_vfs++; + } + + return rc; +} + +static void qed_iov_set_link(struct qed_hwfn *p_hwfn, + u16 vfid, + struct qed_mcp_link_params *params, + struct qed_mcp_link_state *link, + struct qed_mcp_link_capabilities *p_caps) +{ + struct qed_vf_info *p_vf = qed_iov_get_vf_info(p_hwfn, + vfid, + false); + struct qed_bulletin_content *p_bulletin; + + if (!p_vf) + return; + + p_bulletin = p_vf->bulletin.p_virt; + p_bulletin->req_autoneg = params->speed.autoneg; + p_bulletin->req_adv_speed = params->speed.advertised_speeds; + p_bulletin->req_forced_speed = params->speed.forced_speed; + p_bulletin->req_autoneg_pause = params->pause.autoneg; + p_bulletin->req_forced_rx = params->pause.forced_rx; + p_bulletin->req_forced_tx = params->pause.forced_tx; + p_bulletin->req_loopback = params->loopback_mode; + + p_bulletin->link_up = link->link_up; + p_bulletin->speed = link->speed; + p_bulletin->full_duplex = link->full_duplex; + p_bulletin->autoneg = link->an; + p_bulletin->autoneg_complete = link->an_complete; + p_bulletin->parallel_detection = link->parallel_detection; + p_bulletin->pfc_enabled = link->pfc_enabled; + p_bulletin->partner_adv_speed = link->partner_adv_speed; + p_bulletin->partner_tx_flow_ctrl_en = link->partner_tx_flow_ctrl_en; + p_bulletin->partner_rx_flow_ctrl_en = link->partner_rx_flow_ctrl_en; + p_bulletin->partner_adv_pause = link->partner_adv_pause; + p_bulletin->sfp_tx_fault = link->sfp_tx_fault; + + p_bulletin->capability_speed = p_caps->speed_capabilities; +} + +static int qed_iov_release_hw_for_vf(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 rel_vf_id) +{ + struct qed_mcp_link_capabilities caps; + struct qed_mcp_link_params params; + struct qed_mcp_link_state link; + struct qed_vf_info *vf = NULL; + int rc = 0; + + vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, true); + if (!vf) { + DP_ERR(p_hwfn, "qed_iov_release_hw_for_vf : vf is NULL\n"); + return -EINVAL; + } + + if (vf->bulletin.p_virt) + memset(vf->bulletin.p_virt, 0, sizeof(*vf->bulletin.p_virt)); + + memset(&vf->p_vf_info, 0, sizeof(vf->p_vf_info)); + + /* Get the link configuration back in bulletin so + * that when VFs are re-enabled they get the actual + * link configuration. + */ + memcpy(¶ms, qed_mcp_get_link_params(p_hwfn), sizeof(params)); + memcpy(&link, qed_mcp_get_link_state(p_hwfn), sizeof(link)); + memcpy(&caps, qed_mcp_get_link_capabilities(p_hwfn), sizeof(caps)); + qed_iov_set_link(p_hwfn, rel_vf_id, ¶ms, &link, &caps); + + if (vf->state != VF_STOPPED) { + /* Stopping the VF */ + rc = qed_sp_vf_stop(p_hwfn, vf->concrete_fid, vf->opaque_fid); + + if (rc != 0) { + DP_ERR(p_hwfn, "qed_sp_vf_stop returned error %d\n", + rc); + return rc; + } + + vf->state = VF_STOPPED; + } + + /* disablng interrupts and resetting permission table was done during + * vf-close, however, we could get here without going through vf_close + */ + /* Disable Interrupts for VF */ + qed_iov_vf_igu_set_int(p_hwfn, p_ptt, vf, 0); + + /* Reset Permission table */ + qed_iov_config_perm_table(p_hwfn, p_ptt, vf, 0); + + vf->num_rxqs = 0; + vf->num_txqs = 0; + qed_iov_free_vf_igu_sbs(p_hwfn, p_ptt, vf); + + if (vf->b_init) { + vf->b_init = false; + + if (IS_LEAD_HWFN(p_hwfn)) + p_hwfn->cdev->p_iov_info->num_vfs--; + } + + return 0; +} + +static bool qed_iov_tlv_supported(u16 tlvtype) +{ + return CHANNEL_TLV_NONE < tlvtype && tlvtype < CHANNEL_TLV_MAX; +} + +/* place a given tlv on the tlv buffer, continuing current tlv list */ +void *qed_add_tlv(struct qed_hwfn *p_hwfn, u8 **offset, u16 type, u16 length) +{ + struct channel_tlv *tl = (struct channel_tlv *)*offset; + + tl->type = type; + tl->length = length; + + /* Offset should keep pointing to next TLV (the end of the last) */ + *offset += length; + + /* Return a pointer to the start of the added tlv */ + return *offset - length; +} + +/* list the types and lengths of the tlvs on the buffer */ +void qed_dp_tlv_list(struct qed_hwfn *p_hwfn, void *tlvs_list) +{ + u16 i = 1, total_length = 0; + struct channel_tlv *tlv; + + do { + tlv = (struct channel_tlv *)((u8 *)tlvs_list + total_length); + + /* output tlv */ + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "TLV number %d: type %d, length %d\n", + i, tlv->type, tlv->length); + + if (tlv->type == CHANNEL_TLV_LIST_END) + return; + + /* Validate entry - protect against malicious VFs */ + if (!tlv->length) { + DP_NOTICE(p_hwfn, "TLV of length 0 found\n"); + return; + } + + total_length += tlv->length; + + if (total_length >= sizeof(struct tlv_buffer_size)) { + DP_NOTICE(p_hwfn, "TLV ==> Buffer overflow\n"); + return; + } + + i++; + } while (1); +} + +static void qed_iov_send_response(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *p_vf, + u16 length, u8 status) +{ + struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx; + struct qed_dmae_params params; + u8 eng_vf_id; + + mbx->reply_virt->default_resp.hdr.status = status; + + qed_dp_tlv_list(p_hwfn, mbx->reply_virt); + + eng_vf_id = p_vf->abs_vf_id; + + memset(¶ms, 0, sizeof(struct qed_dmae_params)); + params.flags = QED_DMAE_FLAG_VF_DST; + params.dst_vfid = eng_vf_id; + + qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys + sizeof(u64), + mbx->req_virt->first_tlv.reply_address + + sizeof(u64), + (sizeof(union pfvf_tlvs) - sizeof(u64)) / 4, + ¶ms); + + qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys, + mbx->req_virt->first_tlv.reply_address, + sizeof(u64) / 4, ¶ms); + + REG_WR(p_hwfn, + GTT_BAR0_MAP_REG_USDM_RAM + + USTORM_VF_PF_CHANNEL_READY_OFFSET(eng_vf_id), 1); +} + +static u16 qed_iov_vport_to_tlv(struct qed_hwfn *p_hwfn, + enum qed_iov_vport_update_flag flag) +{ + switch (flag) { + case QED_IOV_VP_UPDATE_ACTIVATE: + return CHANNEL_TLV_VPORT_UPDATE_ACTIVATE; + case QED_IOV_VP_UPDATE_VLAN_STRIP: + return CHANNEL_TLV_VPORT_UPDATE_VLAN_STRIP; + case QED_IOV_VP_UPDATE_TX_SWITCH: + return CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH; + case QED_IOV_VP_UPDATE_MCAST: + return CHANNEL_TLV_VPORT_UPDATE_MCAST; + case QED_IOV_VP_UPDATE_ACCEPT_PARAM: + return CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM; + case QED_IOV_VP_UPDATE_RSS: + return CHANNEL_TLV_VPORT_UPDATE_RSS; + case QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN: + return CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN; + case QED_IOV_VP_UPDATE_SGE_TPA: + return CHANNEL_TLV_VPORT_UPDATE_SGE_TPA; + default: + return 0; + } +} + +static u16 qed_iov_prep_vp_update_resp_tlvs(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, + struct qed_iov_vf_mbx *p_mbx, + u8 status, + u16 tlvs_mask, u16 tlvs_accepted) +{ + struct pfvf_def_resp_tlv *resp; + u16 size, total_len, i; + + memset(p_mbx->reply_virt, 0, sizeof(union pfvf_tlvs)); + p_mbx->offset = (u8 *)p_mbx->reply_virt; + size = sizeof(struct pfvf_def_resp_tlv); + total_len = size; + + qed_add_tlv(p_hwfn, &p_mbx->offset, CHANNEL_TLV_VPORT_UPDATE, size); + + /* Prepare response for all extended tlvs if they are found by PF */ + for (i = 0; i < QED_IOV_VP_UPDATE_MAX; i++) { + if (!(tlvs_mask & (1 << i))) + continue; + + resp = qed_add_tlv(p_hwfn, &p_mbx->offset, + qed_iov_vport_to_tlv(p_hwfn, i), size); + + if (tlvs_accepted & (1 << i)) + resp->hdr.status = status; + else + resp->hdr.status = PFVF_STATUS_NOT_SUPPORTED; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d] - vport_update response: TLV %d, status %02x\n", + p_vf->relative_vf_id, + qed_iov_vport_to_tlv(p_hwfn, i), resp->hdr.status); + + total_len += size; + } + + qed_add_tlv(p_hwfn, &p_mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + return total_len; +} + +static void qed_iov_prepare_resp(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf_info, + u16 type, u16 length, u8 status) +{ + struct qed_iov_vf_mbx *mbx = &vf_info->vf_mbx; + + mbx->offset = (u8 *)mbx->reply_virt; + + qed_add_tlv(p_hwfn, &mbx->offset, type, length); + qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + qed_iov_send_response(p_hwfn, p_ptt, vf_info, length, status); +} + +struct qed_public_vf_info *qed_iov_get_public_vf_info(struct qed_hwfn *p_hwfn, + u16 relative_vf_id, + bool b_enabled_only) +{ + struct qed_vf_info *vf = NULL; + + vf = qed_iov_get_vf_info(p_hwfn, relative_vf_id, b_enabled_only); + if (!vf) + return NULL; + + return &vf->p_vf_info; +} + +void qed_iov_clean_vf(struct qed_hwfn *p_hwfn, u8 vfid) +{ + struct qed_public_vf_info *vf_info; + + vf_info = qed_iov_get_public_vf_info(p_hwfn, vfid, false); + + if (!vf_info) + return; + + /* Clear the VF mac */ + memset(vf_info->mac, 0, ETH_ALEN); +} + +static void qed_iov_vf_cleanup(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf) +{ + u32 i; + + p_vf->vf_bulletin = 0; + p_vf->vport_instance = 0; + p_vf->num_mac_filters = 0; + p_vf->num_vlan_filters = 0; + p_vf->configured_features = 0; + + /* If VF previously requested less resources, go back to default */ + p_vf->num_rxqs = p_vf->num_sbs; + p_vf->num_txqs = p_vf->num_sbs; + + p_vf->num_active_rxqs = 0; + + for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) + p_vf->vf_queues[i].rxq_active = 0; + + memset(&p_vf->shadow_config, 0, sizeof(p_vf->shadow_config)); + qed_iov_clean_vf(p_hwfn, p_vf->relative_vf_id); +} + +static void qed_iov_vf_mbx_acquire(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct pfvf_acquire_resp_tlv *resp = &mbx->reply_virt->acquire_resp; + struct pf_vf_pfdev_info *pfdev_info = &resp->pfdev_info; + struct vfpf_acquire_tlv *req = &mbx->req_virt->acquire; + u8 i, vfpf_status = PFVF_STATUS_SUCCESS; + struct pf_vf_resc *resc = &resp->resc; + + /* Validate FW compatibility */ + if (req->vfdev_info.fw_major != FW_MAJOR_VERSION || + req->vfdev_info.fw_minor != FW_MINOR_VERSION || + req->vfdev_info.fw_revision != FW_REVISION_VERSION || + req->vfdev_info.fw_engineering != FW_ENGINEERING_VERSION) { + DP_INFO(p_hwfn, + "VF[%d] is running an incompatible driver [VF needs FW %02x:%02x:%02x:%02x but Hypervisor is using %02x:%02x:%02x:%02x]\n", + vf->abs_vf_id, + req->vfdev_info.fw_major, + req->vfdev_info.fw_minor, + req->vfdev_info.fw_revision, + req->vfdev_info.fw_engineering, + FW_MAJOR_VERSION, + FW_MINOR_VERSION, + FW_REVISION_VERSION, FW_ENGINEERING_VERSION); + vfpf_status = PFVF_STATUS_NOT_SUPPORTED; + goto out; + } + + /* On 100g PFs, prevent old VFs from loading */ + if ((p_hwfn->cdev->num_hwfns > 1) && + !(req->vfdev_info.capabilities & VFPF_ACQUIRE_CAP_100G)) { + DP_INFO(p_hwfn, + "VF[%d] is running an old driver that doesn't support 100g\n", + vf->abs_vf_id); + vfpf_status = PFVF_STATUS_NOT_SUPPORTED; + goto out; + } + + memset(resp, 0, sizeof(*resp)); + + /* Fill in vf info stuff */ + vf->opaque_fid = req->vfdev_info.opaque_fid; + vf->num_mac_filters = 1; + vf->num_vlan_filters = QED_ETH_VF_NUM_VLAN_FILTERS; + + vf->vf_bulletin = req->bulletin_addr; + vf->bulletin.size = (vf->bulletin.size < req->bulletin_size) ? + vf->bulletin.size : req->bulletin_size; + + /* fill in pfdev info */ + pfdev_info->chip_num = p_hwfn->cdev->chip_num; + pfdev_info->db_size = 0; + pfdev_info->indices_per_sb = PIS_PER_SB; + + pfdev_info->capabilities = PFVF_ACQUIRE_CAP_DEFAULT_UNTAGGED | + PFVF_ACQUIRE_CAP_POST_FW_OVERRIDE; + if (p_hwfn->cdev->num_hwfns > 1) + pfdev_info->capabilities |= PFVF_ACQUIRE_CAP_100G; + + pfdev_info->stats_info.mstats.address = + PXP_VF_BAR0_START_MSDM_ZONE_B + + offsetof(struct mstorm_vf_zone, non_trigger.eth_queue_stat); + pfdev_info->stats_info.mstats.len = + sizeof(struct eth_mstorm_per_queue_stat); + + pfdev_info->stats_info.ustats.address = + PXP_VF_BAR0_START_USDM_ZONE_B + + offsetof(struct ustorm_vf_zone, non_trigger.eth_queue_stat); + pfdev_info->stats_info.ustats.len = + sizeof(struct eth_ustorm_per_queue_stat); + + pfdev_info->stats_info.pstats.address = + PXP_VF_BAR0_START_PSDM_ZONE_B + + offsetof(struct pstorm_vf_zone, non_trigger.eth_queue_stat); + pfdev_info->stats_info.pstats.len = + sizeof(struct eth_pstorm_per_queue_stat); + + pfdev_info->stats_info.tstats.address = 0; + pfdev_info->stats_info.tstats.len = 0; + + memcpy(pfdev_info->port_mac, p_hwfn->hw_info.hw_mac_addr, ETH_ALEN); + + pfdev_info->fw_major = FW_MAJOR_VERSION; + pfdev_info->fw_minor = FW_MINOR_VERSION; + pfdev_info->fw_rev = FW_REVISION_VERSION; + pfdev_info->fw_eng = FW_ENGINEERING_VERSION; + pfdev_info->os_type = VFPF_ACQUIRE_OS_LINUX; + qed_mcp_get_mfw_ver(p_hwfn, p_ptt, &pfdev_info->mfw_ver, NULL); + + pfdev_info->dev_type = p_hwfn->cdev->type; + pfdev_info->chip_rev = p_hwfn->cdev->chip_rev; + + resc->num_rxqs = vf->num_rxqs; + resc->num_txqs = vf->num_txqs; + resc->num_sbs = vf->num_sbs; + for (i = 0; i < resc->num_sbs; i++) { + resc->hw_sbs[i].hw_sb_id = vf->igu_sbs[i]; + resc->hw_sbs[i].sb_qid = 0; + } + + for (i = 0; i < resc->num_rxqs; i++) { + qed_fw_l2_queue(p_hwfn, vf->vf_queues[i].fw_rx_qid, + (u16 *)&resc->hw_qid[i]); + resc->cid[i] = vf->vf_queues[i].fw_cid; + } + + resc->num_mac_filters = min_t(u8, vf->num_mac_filters, + req->resc_request.num_mac_filters); + resc->num_vlan_filters = min_t(u8, vf->num_vlan_filters, + req->resc_request.num_vlan_filters); + + /* This isn't really required as VF isn't limited, but some VFs might + * actually test this value, so need to provide it. + */ + resc->num_mc_filters = req->resc_request.num_mc_filters; + + /* Fill agreed size of bulletin board in response */ + resp->bulletin_size = vf->bulletin.size; + qed_iov_post_vf_bulletin(p_hwfn, vf->relative_vf_id, p_ptt); + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d] ACQUIRE_RESPONSE: pfdev_info- chip_num=0x%x, db_size=%d, idx_per_sb=%d, pf_cap=0x%llx\n" + "resources- n_rxq-%d, n_txq-%d, n_sbs-%d, n_macs-%d, n_vlans-%d\n", + vf->abs_vf_id, + resp->pfdev_info.chip_num, + resp->pfdev_info.db_size, + resp->pfdev_info.indices_per_sb, + resp->pfdev_info.capabilities, + resc->num_rxqs, + resc->num_txqs, + resc->num_sbs, + resc->num_mac_filters, + resc->num_vlan_filters); + vf->state = VF_ACQUIRED; + + /* Prepare Response */ +out: + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_ACQUIRE, + sizeof(struct pfvf_acquire_resp_tlv), vfpf_status); +} + +static int __qed_iov_spoofchk_set(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, bool val) +{ + struct qed_sp_vport_update_params params; + int rc; + + if (val == p_vf->spoof_chk) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Spoofchk value[%d] is already configured\n", val); + return 0; + } + + memset(¶ms, 0, sizeof(struct qed_sp_vport_update_params)); + params.opaque_fid = p_vf->opaque_fid; + params.vport_id = p_vf->vport_id; + params.update_anti_spoofing_en_flg = 1; + params.anti_spoofing_en = val; + + rc = qed_sp_vport_update(p_hwfn, ¶ms, QED_SPQ_MODE_EBLOCK, NULL); + if (rc) { + p_vf->spoof_chk = val; + p_vf->req_spoofchk_val = p_vf->spoof_chk; + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Spoofchk val[%d] configured\n", val); + } else { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Spoofchk configuration[val:%d] failed for VF[%d]\n", + val, p_vf->relative_vf_id); + } + + return rc; +} + +static int qed_iov_reconfigure_unicast_vlan(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf) +{ + struct qed_filter_ucast filter; + int rc = 0; + int i; + + memset(&filter, 0, sizeof(filter)); + filter.is_rx_filter = 1; + filter.is_tx_filter = 1; + filter.vport_to_add_to = p_vf->vport_id; + filter.opcode = QED_FILTER_ADD; + + /* Reconfigure vlans */ + for (i = 0; i < QED_ETH_VF_NUM_VLAN_FILTERS + 1; i++) { + if (!p_vf->shadow_config.vlans[i].used) + continue; + + filter.type = QED_FILTER_VLAN; + filter.vlan = p_vf->shadow_config.vlans[i].vid; + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "Reconfiguring VLAN [0x%04x] for VF [%04x]\n", + filter.vlan, p_vf->relative_vf_id); + rc = qed_sp_eth_filter_ucast(p_hwfn, + p_vf->opaque_fid, + &filter, + QED_SPQ_MODE_CB, NULL); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed to configure VLAN [%04x] to VF [%04x]\n", + filter.vlan, p_vf->relative_vf_id); + break; + } + } + + return rc; +} + +static int +qed_iov_reconfigure_unicast_shadow(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, u64 events) +{ + int rc = 0; + + if ((events & (1 << VLAN_ADDR_FORCED)) && + !(p_vf->configured_features & (1 << VLAN_ADDR_FORCED))) + rc = qed_iov_reconfigure_unicast_vlan(p_hwfn, p_vf); + + return rc; +} + +static int qed_iov_configure_vport_forced(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, u64 events) +{ + int rc = 0; + struct qed_filter_ucast filter; + + if (!p_vf->vport_instance) + return -EINVAL; + + if (events & (1 << MAC_ADDR_FORCED)) { + /* Since there's no way [currently] of removing the MAC, + * we can always assume this means we need to force it. + */ + memset(&filter, 0, sizeof(filter)); + filter.type = QED_FILTER_MAC; + filter.opcode = QED_FILTER_REPLACE; + filter.is_rx_filter = 1; + filter.is_tx_filter = 1; + filter.vport_to_add_to = p_vf->vport_id; + ether_addr_copy(filter.mac, p_vf->bulletin.p_virt->mac); + + rc = qed_sp_eth_filter_ucast(p_hwfn, p_vf->opaque_fid, + &filter, QED_SPQ_MODE_CB, NULL); + if (rc) { + DP_NOTICE(p_hwfn, + "PF failed to configure MAC for VF\n"); + return rc; + } + + p_vf->configured_features |= 1 << MAC_ADDR_FORCED; + } + + if (events & (1 << VLAN_ADDR_FORCED)) { + struct qed_sp_vport_update_params vport_update; + u8 removal; + int i; + + memset(&filter, 0, sizeof(filter)); + filter.type = QED_FILTER_VLAN; + filter.is_rx_filter = 1; + filter.is_tx_filter = 1; + filter.vport_to_add_to = p_vf->vport_id; + filter.vlan = p_vf->bulletin.p_virt->pvid; + filter.opcode = filter.vlan ? QED_FILTER_REPLACE : + QED_FILTER_FLUSH; + + /* Send the ramrod */ + rc = qed_sp_eth_filter_ucast(p_hwfn, p_vf->opaque_fid, + &filter, QED_SPQ_MODE_CB, NULL); + if (rc) { + DP_NOTICE(p_hwfn, + "PF failed to configure VLAN for VF\n"); + return rc; + } + + /* Update the default-vlan & silent vlan stripping */ + memset(&vport_update, 0, sizeof(vport_update)); + vport_update.opaque_fid = p_vf->opaque_fid; + vport_update.vport_id = p_vf->vport_id; + vport_update.update_default_vlan_enable_flg = 1; + vport_update.default_vlan_enable_flg = filter.vlan ? 1 : 0; + vport_update.update_default_vlan_flg = 1; + vport_update.default_vlan = filter.vlan; + + vport_update.update_inner_vlan_removal_flg = 1; + removal = filter.vlan ? 1 + : p_vf->shadow_config.inner_vlan_removal; + vport_update.inner_vlan_removal_flg = removal; + vport_update.silent_vlan_removal_flg = filter.vlan ? 1 : 0; + rc = qed_sp_vport_update(p_hwfn, + &vport_update, + QED_SPQ_MODE_EBLOCK, NULL); + if (rc) { + DP_NOTICE(p_hwfn, + "PF failed to configure VF vport for vlan\n"); + return rc; + } + + /* Update all the Rx queues */ + for (i = 0; i < QED_MAX_VF_CHAINS_PER_PF; i++) { + u16 qid; + + if (!p_vf->vf_queues[i].rxq_active) + continue; + + qid = p_vf->vf_queues[i].fw_rx_qid; + + rc = qed_sp_eth_rx_queues_update(p_hwfn, qid, + 1, 0, 1, + QED_SPQ_MODE_EBLOCK, + NULL); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed to send Rx update fo queue[0x%04x]\n", + qid); + return rc; + } + } + + if (filter.vlan) + p_vf->configured_features |= 1 << VLAN_ADDR_FORCED; + else + p_vf->configured_features &= ~(1 << VLAN_ADDR_FORCED); + } + + /* If forced features are terminated, we need to configure the shadow + * configuration back again. + */ + if (events) + qed_iov_reconfigure_unicast_shadow(p_hwfn, p_vf, events); + + return rc; +} + +static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_sp_vport_start_params params = { 0 }; + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct vfpf_vport_start_tlv *start; + u8 status = PFVF_STATUS_SUCCESS; + struct qed_vf_info *vf_info; + u64 *p_bitmap; + int sb_id; + int rc; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vf->relative_vf_id, true); + if (!vf_info) { + DP_NOTICE(p_hwfn->cdev, + "Failed to get VF info, invalid vfid [%d]\n", + vf->relative_vf_id); + return; + } + + vf->state = VF_ENABLED; + start = &mbx->req_virt->start_vport; + + /* Initialize Status block in CAU */ + for (sb_id = 0; sb_id < vf->num_sbs; sb_id++) { + if (!start->sb_addr[sb_id]) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d] did not fill the address of SB %d\n", + vf->relative_vf_id, sb_id); + break; + } + + qed_int_cau_conf_sb(p_hwfn, p_ptt, + start->sb_addr[sb_id], + vf->igu_sbs[sb_id], + vf->abs_vf_id, 1); + } + qed_iov_enable_vf_traffic(p_hwfn, p_ptt, vf); + + vf->mtu = start->mtu; + vf->shadow_config.inner_vlan_removal = start->inner_vlan_removal; + + /* Take into consideration configuration forced by hypervisor; + * If none is configured, use the supplied VF values [for old + * vfs that would still be fine, since they passed '0' as padding]. + */ + p_bitmap = &vf_info->bulletin.p_virt->valid_bitmap; + if (!(*p_bitmap & (1 << VFPF_BULLETIN_UNTAGGED_DEFAULT_FORCED))) { + u8 vf_req = start->only_untagged; + + vf_info->bulletin.p_virt->default_only_untagged = vf_req; + *p_bitmap |= 1 << VFPF_BULLETIN_UNTAGGED_DEFAULT; + } + + params.tpa_mode = start->tpa_mode; + params.remove_inner_vlan = start->inner_vlan_removal; + params.tx_switching = true; + + params.only_untagged = vf_info->bulletin.p_virt->default_only_untagged; + params.drop_ttl0 = false; + params.concrete_fid = vf->concrete_fid; + params.opaque_fid = vf->opaque_fid; + params.vport_id = vf->vport_id; + params.max_buffers_per_cqe = start->max_buffers_per_cqe; + params.mtu = vf->mtu; + + rc = qed_sp_eth_vport_start(p_hwfn, ¶ms); + if (rc != 0) { + DP_ERR(p_hwfn, + "qed_iov_vf_mbx_start_vport returned error %d\n", rc); + status = PFVF_STATUS_FAILURE; + } else { + vf->vport_instance++; + + /* Force configuration if needed on the newly opened vport */ + qed_iov_configure_vport_forced(p_hwfn, vf, *p_bitmap); + + __qed_iov_spoofchk_set(p_hwfn, vf, vf->req_spoofchk_val); + } + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_VPORT_START, + sizeof(struct pfvf_def_resp_tlv), status); +} + +static void qed_iov_vf_mbx_stop_vport(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u8 status = PFVF_STATUS_SUCCESS; + int rc; + + vf->vport_instance--; + vf->spoof_chk = false; + + rc = qed_sp_vport_stop(p_hwfn, vf->opaque_fid, vf->vport_id); + if (rc != 0) { + DP_ERR(p_hwfn, "qed_iov_vf_mbx_stop_vport returned error %d\n", + rc); + status = PFVF_STATUS_FAILURE; + } + + /* Forget the configuration on the vport */ + vf->configured_features = 0; + memset(&vf->shadow_config, 0, sizeof(vf->shadow_config)); + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_VPORT_TEARDOWN, + sizeof(struct pfvf_def_resp_tlv), status); +} + +#define TSTORM_QZONE_START PXP_VF_BAR0_START_SDM_ZONE_A +#define MSTORM_QZONE_START(dev) (TSTORM_QZONE_START + \ + (TSTORM_QZONE_SIZE * NUM_OF_L2_QUEUES(dev))) + +static void qed_iov_vf_mbx_start_rxq_resp(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf, u8 status) +{ + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct pfvf_start_queue_resp_tlv *p_tlv; + struct vfpf_start_rxq_tlv *req; + + mbx->offset = (u8 *)mbx->reply_virt; + + p_tlv = qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_START_RXQ, + sizeof(*p_tlv)); + qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + /* Update the TLV with the response */ + if (status == PFVF_STATUS_SUCCESS) { + u16 hw_qid = 0; + + req = &mbx->req_virt->start_rxq; + qed_fw_l2_queue(p_hwfn, vf->vf_queues[req->rx_qid].fw_rx_qid, + &hw_qid); + + p_tlv->offset = MSTORM_QZONE_START(p_hwfn->cdev) + + hw_qid * MSTORM_QZONE_SIZE + + offsetof(struct mstorm_eth_queue_zone, + rx_producers); + } + + qed_iov_send_response(p_hwfn, p_ptt, vf, sizeof(*p_tlv), status); +} + +static void qed_iov_vf_mbx_start_rxq(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_queue_start_common_params params; + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + u8 status = PFVF_STATUS_SUCCESS; + struct vfpf_start_rxq_tlv *req; + int rc; + + memset(¶ms, 0, sizeof(params)); + req = &mbx->req_virt->start_rxq; + params.queue_id = vf->vf_queues[req->rx_qid].fw_rx_qid; + params.vport_id = vf->vport_id; + params.sb = req->hw_sb; + params.sb_idx = req->sb_index; + + rc = qed_sp_eth_rxq_start_ramrod(p_hwfn, vf->opaque_fid, + vf->vf_queues[req->rx_qid].fw_cid, + ¶ms, + vf->abs_vf_id + 0x10, + req->bd_max_bytes, + req->rxq_addr, + req->cqe_pbl_addr, req->cqe_pbl_size); + + if (rc) { + status = PFVF_STATUS_FAILURE; + } else { + vf->vf_queues[req->rx_qid].rxq_active = true; + vf->num_active_rxqs++; + } + + qed_iov_vf_mbx_start_rxq_resp(p_hwfn, p_ptt, vf, status); +} + +static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + struct qed_queue_start_common_params params; + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + union qed_qm_pq_params pq_params; + u8 status = PFVF_STATUS_SUCCESS; + struct vfpf_start_txq_tlv *req; + int rc; + + /* Prepare the parameters which would choose the right PQ */ + memset(&pq_params, 0, sizeof(pq_params)); + pq_params.eth.is_vf = 1; + pq_params.eth.vf_id = vf->relative_vf_id; + + memset(¶ms, 0, sizeof(params)); + req = &mbx->req_virt->start_txq; + params.queue_id = vf->vf_queues[req->tx_qid].fw_tx_qid; + params.vport_id = vf->vport_id; + params.sb = req->hw_sb; + params.sb_idx = req->sb_index; + + rc = qed_sp_eth_txq_start_ramrod(p_hwfn, + vf->opaque_fid, + vf->vf_queues[req->tx_qid].fw_cid, + ¶ms, + vf->abs_vf_id + 0x10, + req->pbl_addr, + req->pbl_size, &pq_params); + + if (rc) + status = PFVF_STATUS_FAILURE; + else + vf->vf_queues[req->tx_qid].txq_active = true; + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_START_TXQ, + length, status); +} + +static int qed_iov_vf_stop_rxqs(struct qed_hwfn *p_hwfn, + struct qed_vf_info *vf, + u16 rxq_id, u8 num_rxqs, bool cqe_completion) +{ + int rc = 0; + int qid; + + if (rxq_id + num_rxqs > ARRAY_SIZE(vf->vf_queues)) + return -EINVAL; + + for (qid = rxq_id; qid < rxq_id + num_rxqs; qid++) { + if (vf->vf_queues[qid].rxq_active) { + rc = qed_sp_eth_rx_queue_stop(p_hwfn, + vf->vf_queues[qid]. + fw_rx_qid, false, + cqe_completion); + + if (rc) + return rc; + } + vf->vf_queues[qid].rxq_active = false; + vf->num_active_rxqs--; + } + + return rc; +} + +static int qed_iov_vf_stop_txqs(struct qed_hwfn *p_hwfn, + struct qed_vf_info *vf, u16 txq_id, u8 num_txqs) +{ + int rc = 0; + int qid; + + if (txq_id + num_txqs > ARRAY_SIZE(vf->vf_queues)) + return -EINVAL; + + for (qid = txq_id; qid < txq_id + num_txqs; qid++) { + if (vf->vf_queues[qid].txq_active) { + rc = qed_sp_eth_tx_queue_stop(p_hwfn, + vf->vf_queues[qid]. + fw_tx_qid); + + if (rc) + return rc; + } + vf->vf_queues[qid].txq_active = false; + } + return rc; +} + +static void qed_iov_vf_mbx_stop_rxqs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + u8 status = PFVF_STATUS_SUCCESS; + struct vfpf_stop_rxqs_tlv *req; + int rc; + + /* We give the option of starting from qid != 0, in this case we + * need to make sure that qid + num_qs doesn't exceed the actual + * amount of queues that exist. + */ + req = &mbx->req_virt->stop_rxqs; + rc = qed_iov_vf_stop_rxqs(p_hwfn, vf, req->rx_qid, + req->num_rxqs, req->cqe_completion); + if (rc) + status = PFVF_STATUS_FAILURE; + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_STOP_RXQS, + length, status); +} + +static void qed_iov_vf_mbx_stop_txqs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + u8 status = PFVF_STATUS_SUCCESS; + struct vfpf_stop_txqs_tlv *req; + int rc; + + /* We give the option of starting from qid != 0, in this case we + * need to make sure that qid + num_qs doesn't exceed the actual + * amount of queues that exist. + */ + req = &mbx->req_virt->stop_txqs; + rc = qed_iov_vf_stop_txqs(p_hwfn, vf, req->tx_qid, req->num_txqs); + if (rc) + status = PFVF_STATUS_FAILURE; + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_STOP_TXQS, + length, status); +} + +static void qed_iov_vf_mbx_update_rxqs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct vfpf_update_rxq_tlv *req; + u8 status = PFVF_STATUS_SUCCESS; + u8 complete_event_flg; + u8 complete_cqe_flg; + u16 qid; + int rc; + u8 i; + + req = &mbx->req_virt->update_rxq; + complete_cqe_flg = !!(req->flags & VFPF_RXQ_UPD_COMPLETE_CQE_FLAG); + complete_event_flg = !!(req->flags & VFPF_RXQ_UPD_COMPLETE_EVENT_FLAG); + + for (i = 0; i < req->num_rxqs; i++) { + qid = req->rx_qid + i; + + if (!vf->vf_queues[qid].rxq_active) { + DP_NOTICE(p_hwfn, "VF rx_qid = %d isn`t active!\n", + qid); + status = PFVF_STATUS_FAILURE; + break; + } + + rc = qed_sp_eth_rx_queues_update(p_hwfn, + vf->vf_queues[qid].fw_rx_qid, + 1, + complete_cqe_flg, + complete_event_flg, + QED_SPQ_MODE_EBLOCK, NULL); + + if (rc) { + status = PFVF_STATUS_FAILURE; + break; + } + } + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_UPDATE_RXQ, + length, status); +} + +void *qed_iov_search_list_tlvs(struct qed_hwfn *p_hwfn, + void *p_tlvs_list, u16 req_type) +{ + struct channel_tlv *p_tlv = (struct channel_tlv *)p_tlvs_list; + int len = 0; + + do { + if (!p_tlv->length) { + DP_NOTICE(p_hwfn, "Zero length TLV found\n"); + return NULL; + } + + if (p_tlv->type == req_type) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Extended tlv type %d, length %d found\n", + p_tlv->type, p_tlv->length); + return p_tlv; + } + + len += p_tlv->length; + p_tlv = (struct channel_tlv *)((u8 *)p_tlv + p_tlv->length); + + if ((len + p_tlv->length) > TLV_BUFFER_SIZE) { + DP_NOTICE(p_hwfn, "TLVs has overrun the buffer size\n"); + return NULL; + } + } while (p_tlv->type != CHANNEL_TLV_LIST_END); + + return NULL; +} + +static void +qed_iov_vp_update_act_param(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_activate_tlv *p_act_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_ACTIVATE; + + p_act_tlv = (struct vfpf_vport_update_activate_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + if (!p_act_tlv) + return; + + p_data->update_vport_active_rx_flg = p_act_tlv->update_rx; + p_data->vport_active_rx_flg = p_act_tlv->active_rx; + p_data->update_vport_active_tx_flg = p_act_tlv->update_tx; + p_data->vport_active_tx_flg = p_act_tlv->active_tx; + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_ACTIVATE; +} + +static void +qed_iov_vp_update_vlan_param(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_vf_info *p_vf, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_vlan_strip_tlv *p_vlan_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_VLAN_STRIP; + + p_vlan_tlv = (struct vfpf_vport_update_vlan_strip_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + if (!p_vlan_tlv) + return; + + p_vf->shadow_config.inner_vlan_removal = p_vlan_tlv->remove_vlan; + + /* Ignore the VF request if we're forcing a vlan */ + if (!(p_vf->configured_features & (1 << VLAN_ADDR_FORCED))) { + p_data->update_inner_vlan_removal_flg = 1; + p_data->inner_vlan_removal_flg = p_vlan_tlv->remove_vlan; + } + + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_VLAN_STRIP; +} + +static void +qed_iov_vp_update_tx_switch(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_tx_switch_tlv *p_tx_switch_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH; + + p_tx_switch_tlv = (struct vfpf_vport_update_tx_switch_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, + tlv); + if (!p_tx_switch_tlv) + return; + + p_data->update_tx_switching_flg = 1; + p_data->tx_switching_flg = p_tx_switch_tlv->tx_switching; + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_TX_SWITCH; +} + +static void +qed_iov_vp_update_mcast_bin_param(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_mcast_bin_tlv *p_mcast_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_MCAST; + + p_mcast_tlv = (struct vfpf_vport_update_mcast_bin_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + if (!p_mcast_tlv) + return; + + p_data->update_approx_mcast_flg = 1; + memcpy(p_data->bins, p_mcast_tlv->bins, + sizeof(unsigned long) * ETH_MULTICAST_MAC_BINS_IN_REGS); + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_MCAST; +} + +static void +qed_iov_vp_update_accept_flag(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct qed_filter_accept_flags *p_flags = &p_data->accept_flags; + struct vfpf_vport_update_accept_param_tlv *p_accept_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM; + + p_accept_tlv = (struct vfpf_vport_update_accept_param_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + if (!p_accept_tlv) + return; + + p_flags->update_rx_mode_config = p_accept_tlv->update_rx_mode; + p_flags->rx_accept_filter = p_accept_tlv->rx_accept_filter; + p_flags->update_tx_mode_config = p_accept_tlv->update_tx_mode; + p_flags->tx_accept_filter = p_accept_tlv->tx_accept_filter; + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_ACCEPT_PARAM; +} + +static void +qed_iov_vp_update_accept_any_vlan(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_accept_any_vlan_tlv *p_accept_any_vlan; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN; + + p_accept_any_vlan = (struct vfpf_vport_update_accept_any_vlan_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, + tlv); + if (!p_accept_any_vlan) + return; + + p_data->accept_any_vlan = p_accept_any_vlan->accept_any_vlan; + p_data->update_accept_any_vlan_flg = + p_accept_any_vlan->update_accept_any_vlan_flg; + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN; +} + +static void +qed_iov_vp_update_rss_param(struct qed_hwfn *p_hwfn, + struct qed_vf_info *vf, + struct qed_sp_vport_update_params *p_data, + struct qed_rss_params *p_rss, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_rss_tlv *p_rss_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_RSS; + u16 i, q_idx, max_q_idx; + u16 table_size; + + p_rss_tlv = (struct vfpf_vport_update_rss_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + if (!p_rss_tlv) { + p_data->rss_params = NULL; + return; + } + + memset(p_rss, 0, sizeof(struct qed_rss_params)); + + p_rss->update_rss_config = !!(p_rss_tlv->update_rss_flags & + VFPF_UPDATE_RSS_CONFIG_FLAG); + p_rss->update_rss_capabilities = !!(p_rss_tlv->update_rss_flags & + VFPF_UPDATE_RSS_CAPS_FLAG); + p_rss->update_rss_ind_table = !!(p_rss_tlv->update_rss_flags & + VFPF_UPDATE_RSS_IND_TABLE_FLAG); + p_rss->update_rss_key = !!(p_rss_tlv->update_rss_flags & + VFPF_UPDATE_RSS_KEY_FLAG); + + p_rss->rss_enable = p_rss_tlv->rss_enable; + p_rss->rss_eng_id = vf->relative_vf_id + 1; + p_rss->rss_caps = p_rss_tlv->rss_caps; + p_rss->rss_table_size_log = p_rss_tlv->rss_table_size_log; + memcpy(p_rss->rss_ind_table, p_rss_tlv->rss_ind_table, + sizeof(p_rss->rss_ind_table)); + memcpy(p_rss->rss_key, p_rss_tlv->rss_key, sizeof(p_rss->rss_key)); + + table_size = min_t(u16, ARRAY_SIZE(p_rss->rss_ind_table), + (1 << p_rss_tlv->rss_table_size_log)); + + max_q_idx = ARRAY_SIZE(vf->vf_queues); + + for (i = 0; i < table_size; i++) { + u16 index = vf->vf_queues[0].fw_rx_qid; + + q_idx = p_rss->rss_ind_table[i]; + if (q_idx >= max_q_idx) + DP_NOTICE(p_hwfn, + "rss_ind_table[%d] = %d, rxq is out of range\n", + i, q_idx); + else if (!vf->vf_queues[q_idx].rxq_active) + DP_NOTICE(p_hwfn, + "rss_ind_table[%d] = %d, rxq is not active\n", + i, q_idx); + else + index = vf->vf_queues[q_idx].fw_rx_qid; + p_rss->rss_ind_table[i] = index; + } + + p_data->rss_params = p_rss; + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_RSS; +} + +static void +qed_iov_vp_update_sge_tpa_param(struct qed_hwfn *p_hwfn, + struct qed_vf_info *vf, + struct qed_sp_vport_update_params *p_data, + struct qed_sge_tpa_params *p_sge_tpa, + struct qed_iov_vf_mbx *p_mbx, u16 *tlvs_mask) +{ + struct vfpf_vport_update_sge_tpa_tlv *p_sge_tpa_tlv; + u16 tlv = CHANNEL_TLV_VPORT_UPDATE_SGE_TPA; + + p_sge_tpa_tlv = (struct vfpf_vport_update_sge_tpa_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_mbx->req_virt, tlv); + + if (!p_sge_tpa_tlv) { + p_data->sge_tpa_params = NULL; + return; + } + + memset(p_sge_tpa, 0, sizeof(struct qed_sge_tpa_params)); + + p_sge_tpa->update_tpa_en_flg = + !!(p_sge_tpa_tlv->update_sge_tpa_flags & VFPF_UPDATE_TPA_EN_FLAG); + p_sge_tpa->update_tpa_param_flg = + !!(p_sge_tpa_tlv->update_sge_tpa_flags & + VFPF_UPDATE_TPA_PARAM_FLAG); + + p_sge_tpa->tpa_ipv4_en_flg = + !!(p_sge_tpa_tlv->sge_tpa_flags & VFPF_TPA_IPV4_EN_FLAG); + p_sge_tpa->tpa_ipv6_en_flg = + !!(p_sge_tpa_tlv->sge_tpa_flags & VFPF_TPA_IPV6_EN_FLAG); + p_sge_tpa->tpa_pkt_split_flg = + !!(p_sge_tpa_tlv->sge_tpa_flags & VFPF_TPA_PKT_SPLIT_FLAG); + p_sge_tpa->tpa_hdr_data_split_flg = + !!(p_sge_tpa_tlv->sge_tpa_flags & VFPF_TPA_HDR_DATA_SPLIT_FLAG); + p_sge_tpa->tpa_gro_consistent_flg = + !!(p_sge_tpa_tlv->sge_tpa_flags & VFPF_TPA_GRO_CONSIST_FLAG); + + p_sge_tpa->tpa_max_aggs_num = p_sge_tpa_tlv->tpa_max_aggs_num; + p_sge_tpa->tpa_max_size = p_sge_tpa_tlv->tpa_max_size; + p_sge_tpa->tpa_min_size_to_start = p_sge_tpa_tlv->tpa_min_size_to_start; + p_sge_tpa->tpa_min_size_to_cont = p_sge_tpa_tlv->tpa_min_size_to_cont; + p_sge_tpa->max_buffers_per_cqe = p_sge_tpa_tlv->max_buffers_per_cqe; + + p_data->sge_tpa_params = p_sge_tpa; + + *tlvs_mask |= 1 << QED_IOV_VP_UPDATE_SGE_TPA; +} + +static void qed_iov_vf_mbx_vport_update(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_sp_vport_update_params params; + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct qed_sge_tpa_params sge_tpa_params; + struct qed_rss_params rss_params; + u8 status = PFVF_STATUS_SUCCESS; + u16 tlvs_mask = 0; + u16 length; + int rc; + + memset(¶ms, 0, sizeof(params)); + params.opaque_fid = vf->opaque_fid; + params.vport_id = vf->vport_id; + params.rss_params = NULL; + + /* Search for extended tlvs list and update values + * from VF in struct qed_sp_vport_update_params. + */ + qed_iov_vp_update_act_param(p_hwfn, ¶ms, mbx, &tlvs_mask); + qed_iov_vp_update_vlan_param(p_hwfn, ¶ms, vf, mbx, &tlvs_mask); + qed_iov_vp_update_tx_switch(p_hwfn, ¶ms, mbx, &tlvs_mask); + qed_iov_vp_update_mcast_bin_param(p_hwfn, ¶ms, mbx, &tlvs_mask); + qed_iov_vp_update_accept_flag(p_hwfn, ¶ms, mbx, &tlvs_mask); + qed_iov_vp_update_rss_param(p_hwfn, vf, ¶ms, &rss_params, + mbx, &tlvs_mask); + qed_iov_vp_update_accept_any_vlan(p_hwfn, ¶ms, mbx, &tlvs_mask); + qed_iov_vp_update_sge_tpa_param(p_hwfn, vf, ¶ms, + &sge_tpa_params, mbx, &tlvs_mask); + + /* Just log a message if there is no single extended tlv in buffer. + * When all features of vport update ramrod would be requested by VF + * as extended TLVs in buffer then an error can be returned in response + * if there is no extended TLV present in buffer. + */ + if (!tlvs_mask) { + DP_NOTICE(p_hwfn, + "No feature tlvs found for vport update\n"); + status = PFVF_STATUS_NOT_SUPPORTED; + goto out; + } + + rc = qed_sp_vport_update(p_hwfn, ¶ms, QED_SPQ_MODE_EBLOCK, NULL); + + if (rc) + status = PFVF_STATUS_FAILURE; + +out: + length = qed_iov_prep_vp_update_resp_tlvs(p_hwfn, vf, mbx, status, + tlvs_mask, tlvs_mask); + qed_iov_send_response(p_hwfn, p_ptt, vf, length, status); +} + +static int qed_iov_vf_update_unicast_shadow(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, + struct qed_filter_ucast *p_params) +{ + int i; + + if (p_params->type == QED_FILTER_MAC) + return 0; + + /* First remove entries and then add new ones */ + if (p_params->opcode == QED_FILTER_REMOVE) { + for (i = 0; i < QED_ETH_VF_NUM_VLAN_FILTERS + 1; i++) + if (p_vf->shadow_config.vlans[i].used && + p_vf->shadow_config.vlans[i].vid == + p_params->vlan) { + p_vf->shadow_config.vlans[i].used = false; + break; + } + if (i == QED_ETH_VF_NUM_VLAN_FILTERS + 1) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF [%d] - Tries to remove a non-existing vlan\n", + p_vf->relative_vf_id); + return -EINVAL; + } + } else if (p_params->opcode == QED_FILTER_REPLACE || + p_params->opcode == QED_FILTER_FLUSH) { + for (i = 0; i < QED_ETH_VF_NUM_VLAN_FILTERS + 1; i++) + p_vf->shadow_config.vlans[i].used = false; + } + + /* In forced mode, we're willing to remove entries - but we don't add + * new ones. + */ + if (p_vf->bulletin.p_virt->valid_bitmap & (1 << VLAN_ADDR_FORCED)) + return 0; + + if (p_params->opcode == QED_FILTER_ADD || + p_params->opcode == QED_FILTER_REPLACE) { + for (i = 0; i < QED_ETH_VF_NUM_VLAN_FILTERS + 1; i++) { + if (p_vf->shadow_config.vlans[i].used) + continue; + + p_vf->shadow_config.vlans[i].used = true; + p_vf->shadow_config.vlans[i].vid = p_params->vlan; + break; + } + + if (i == QED_ETH_VF_NUM_VLAN_FILTERS + 1) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF [%d] - Tries to configure more than %d vlan filters\n", + p_vf->relative_vf_id, + QED_ETH_VF_NUM_VLAN_FILTERS + 1); + return -EINVAL; + } + } + + return 0; +} + +int qed_iov_chk_ucast(struct qed_hwfn *hwfn, + int vfid, struct qed_filter_ucast *params) +{ + struct qed_public_vf_info *vf; + + vf = qed_iov_get_public_vf_info(hwfn, vfid, true); + if (!vf) + return -EINVAL; + + /* No real decision to make; Store the configured MAC */ + if (params->type == QED_FILTER_MAC || + params->type == QED_FILTER_MAC_VLAN) + ether_addr_copy(vf->mac, params->mac); + + return 0; +} + +static void qed_iov_vf_mbx_ucast_filter(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_bulletin_content *p_bulletin = vf->bulletin.p_virt; + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct vfpf_ucast_filter_tlv *req; + u8 status = PFVF_STATUS_SUCCESS; + struct qed_filter_ucast params; + int rc; + + /* Prepare the unicast filter params */ + memset(¶ms, 0, sizeof(struct qed_filter_ucast)); + req = &mbx->req_virt->ucast_filter; + params.opcode = (enum qed_filter_opcode)req->opcode; + params.type = (enum qed_filter_ucast_type)req->type; + + params.is_rx_filter = 1; + params.is_tx_filter = 1; + params.vport_to_remove_from = vf->vport_id; + params.vport_to_add_to = vf->vport_id; + memcpy(params.mac, req->mac, ETH_ALEN); + params.vlan = req->vlan; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: opcode 0x%02x type 0x%02x [%s %s] [vport 0x%02x] MAC %02x:%02x:%02x:%02x:%02x:%02x, vlan 0x%04x\n", + vf->abs_vf_id, params.opcode, params.type, + params.is_rx_filter ? "RX" : "", + params.is_tx_filter ? "TX" : "", + params.vport_to_add_to, + params.mac[0], params.mac[1], + params.mac[2], params.mac[3], + params.mac[4], params.mac[5], params.vlan); + + if (!vf->vport_instance) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "No VPORT instance available for VF[%d], failing ucast MAC configuration\n", + vf->abs_vf_id); + status = PFVF_STATUS_FAILURE; + goto out; + } + + /* Update shadow copy of the VF configuration */ + if (qed_iov_vf_update_unicast_shadow(p_hwfn, vf, ¶ms)) { + status = PFVF_STATUS_FAILURE; + goto out; + } + + /* Determine if the unicast filtering is acceptible by PF */ + if ((p_bulletin->valid_bitmap & (1 << VLAN_ADDR_FORCED)) && + (params.type == QED_FILTER_VLAN || + params.type == QED_FILTER_MAC_VLAN)) { + /* Once VLAN is forced or PVID is set, do not allow + * to add/replace any further VLANs. + */ + if (params.opcode == QED_FILTER_ADD || + params.opcode == QED_FILTER_REPLACE) + status = PFVF_STATUS_FORCED; + goto out; + } + + if ((p_bulletin->valid_bitmap & (1 << MAC_ADDR_FORCED)) && + (params.type == QED_FILTER_MAC || + params.type == QED_FILTER_MAC_VLAN)) { + if (!ether_addr_equal(p_bulletin->mac, params.mac) || + (params.opcode != QED_FILTER_ADD && + params.opcode != QED_FILTER_REPLACE)) + status = PFVF_STATUS_FORCED; + goto out; + } + + rc = qed_iov_chk_ucast(p_hwfn, vf->relative_vf_id, ¶ms); + if (rc) { + status = PFVF_STATUS_FAILURE; + goto out; + } + + rc = qed_sp_eth_filter_ucast(p_hwfn, vf->opaque_fid, ¶ms, + QED_SPQ_MODE_CB, NULL); + if (rc) + status = PFVF_STATUS_FAILURE; + +out: + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_UCAST_FILTER, + sizeof(struct pfvf_def_resp_tlv), status); +} + +static void qed_iov_vf_mbx_int_cleanup(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + int i; + + /* Reset the SBs */ + for (i = 0; i < vf->num_sbs; i++) + qed_int_igu_init_pure_rt_single(p_hwfn, p_ptt, + vf->igu_sbs[i], + vf->opaque_fid, false); + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_INT_CLEANUP, + sizeof(struct pfvf_def_resp_tlv), + PFVF_STATUS_SUCCESS); +} + +static void qed_iov_vf_mbx_close(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, struct qed_vf_info *vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + u8 status = PFVF_STATUS_SUCCESS; + + /* Disable Interrupts for VF */ + qed_iov_vf_igu_set_int(p_hwfn, p_ptt, vf, 0); + + /* Reset Permission table */ + qed_iov_config_perm_table(p_hwfn, p_ptt, vf, 0); + + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_CLOSE, + length, status); +} + +static void qed_iov_vf_mbx_release(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *p_vf) +{ + u16 length = sizeof(struct pfvf_def_resp_tlv); + + qed_iov_vf_cleanup(p_hwfn, p_vf); + + qed_iov_prepare_resp(p_hwfn, p_ptt, p_vf, CHANNEL_TLV_RELEASE, + length, PFVF_STATUS_SUCCESS); +} + +static int +qed_iov_vf_flr_poll_dorq(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, struct qed_ptt *p_ptt) +{ + int cnt; + u32 val; + + qed_fid_pretend(p_hwfn, p_ptt, (u16) p_vf->concrete_fid); + + for (cnt = 0; cnt < 50; cnt++) { + val = qed_rd(p_hwfn, p_ptt, DORQ_REG_VF_USAGE_CNT); + if (!val) + break; + msleep(20); + } + qed_fid_pretend(p_hwfn, p_ptt, (u16) p_hwfn->hw_info.concrete_fid); + + if (cnt == 50) { + DP_ERR(p_hwfn, + "VF[%d] - dorq failed to cleanup [usage 0x%08x]\n", + p_vf->abs_vf_id, val); + return -EBUSY; + } + + return 0; +} + +static int +qed_iov_vf_flr_poll_pbf(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, struct qed_ptt *p_ptt) +{ + u32 cons[MAX_NUM_VOQS], distance[MAX_NUM_VOQS]; + int i, cnt; + + /* Read initial consumers & producers */ + for (i = 0; i < MAX_NUM_VOQS; i++) { + u32 prod; + + cons[i] = qed_rd(p_hwfn, p_ptt, + PBF_REG_NUM_BLOCKS_ALLOCATED_CONS_VOQ0 + + i * 0x40); + prod = qed_rd(p_hwfn, p_ptt, + PBF_REG_NUM_BLOCKS_ALLOCATED_PROD_VOQ0 + + i * 0x40); + distance[i] = prod - cons[i]; + } + + /* Wait for consumers to pass the producers */ + i = 0; + for (cnt = 0; cnt < 50; cnt++) { + for (; i < MAX_NUM_VOQS; i++) { + u32 tmp; + + tmp = qed_rd(p_hwfn, p_ptt, + PBF_REG_NUM_BLOCKS_ALLOCATED_CONS_VOQ0 + + i * 0x40); + if (distance[i] > tmp - cons[i]) + break; + } + + if (i == MAX_NUM_VOQS) + break; + + msleep(20); + } + + if (cnt == 50) { + DP_ERR(p_hwfn, "VF[%d] - pbf polling failed on VOQ %d\n", + p_vf->abs_vf_id, i); + return -EBUSY; + } + + return 0; +} + +static int qed_iov_vf_flr_poll(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, struct qed_ptt *p_ptt) +{ + int rc; + + rc = qed_iov_vf_flr_poll_dorq(p_hwfn, p_vf, p_ptt); + if (rc) + return rc; + + rc = qed_iov_vf_flr_poll_pbf(p_hwfn, p_vf, p_ptt); + if (rc) + return rc; + + return 0; +} + +static int +qed_iov_execute_vf_flr_cleanup(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 rel_vf_id, u32 *ack_vfs) +{ + struct qed_vf_info *p_vf; + int rc = 0; + + p_vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, false); + if (!p_vf) + return 0; + + if (p_hwfn->pf_iov_info->pending_flr[rel_vf_id / 64] & + (1ULL << (rel_vf_id % 64))) { + u16 vfid = p_vf->abs_vf_id; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d] - Handling FLR\n", vfid); + + qed_iov_vf_cleanup(p_hwfn, p_vf); + + /* If VF isn't active, no need for anything but SW */ + if (!p_vf->b_init) + goto cleanup; + + rc = qed_iov_vf_flr_poll(p_hwfn, p_vf, p_ptt); + if (rc) + goto cleanup; + + rc = qed_final_cleanup(p_hwfn, p_ptt, vfid, true); + if (rc) { + DP_ERR(p_hwfn, "Failed handle FLR of VF[%d]\n", vfid); + return rc; + } + + /* VF_STOPPED has to be set only after final cleanup + * but prior to re-enabling the VF. + */ + p_vf->state = VF_STOPPED; + + rc = qed_iov_enable_vf_access(p_hwfn, p_ptt, p_vf); + if (rc) { + DP_ERR(p_hwfn, "Failed to re-enable VF[%d] acces\n", + vfid); + return rc; + } +cleanup: + /* Mark VF for ack and clean pending state */ + if (p_vf->state == VF_RESET) + p_vf->state = VF_STOPPED; + ack_vfs[vfid / 32] |= (1 << (vfid % 32)); + p_hwfn->pf_iov_info->pending_flr[rel_vf_id / 64] &= + ~(1ULL << (rel_vf_id % 64)); + p_hwfn->pf_iov_info->pending_events[rel_vf_id / 64] &= + ~(1ULL << (rel_vf_id % 64)); + } + + return rc; +} + +int qed_iov_vf_flr_cleanup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 ack_vfs[VF_MAX_STATIC / 32]; + int rc = 0; + u16 i; + + memset(ack_vfs, 0, sizeof(u32) * (VF_MAX_STATIC / 32)); + + /* Since BRB <-> PRS interface can't be tested as part of the flr + * polling due to HW limitations, simply sleep a bit. And since + * there's no need to wait per-vf, do it before looping. + */ + msleep(100); + + for (i = 0; i < p_hwfn->cdev->p_iov_info->total_vfs; i++) + qed_iov_execute_vf_flr_cleanup(p_hwfn, p_ptt, i, ack_vfs); + + rc = qed_mcp_ack_vf_flr(p_hwfn, p_ptt, ack_vfs); + return rc; +} + +int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *p_disabled_vfs) +{ + u16 i, found = 0; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, "Marking FLR-ed VFs\n"); + for (i = 0; i < (VF_MAX_STATIC / 32); i++) + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "[%08x,...,%08x]: %08x\n", + i * 32, (i + 1) * 32 - 1, p_disabled_vfs[i]); + + if (!p_hwfn->cdev->p_iov_info) { + DP_NOTICE(p_hwfn, "VF flr but no IOV\n"); + return 0; + } + + /* Mark VFs */ + for (i = 0; i < p_hwfn->cdev->p_iov_info->total_vfs; i++) { + struct qed_vf_info *p_vf; + u8 vfid; + + p_vf = qed_iov_get_vf_info(p_hwfn, i, false); + if (!p_vf) + continue; + + vfid = p_vf->abs_vf_id; + if ((1 << (vfid % 32)) & p_disabled_vfs[vfid / 32]) { + u64 *p_flr = p_hwfn->pf_iov_info->pending_flr; + u16 rel_vf_id = p_vf->relative_vf_id; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d] [rel %d] got FLR-ed\n", + vfid, rel_vf_id); + + p_vf->state = VF_RESET; + + /* No need to lock here, since pending_flr should + * only change here and before ACKing MFw. Since + * MFW will not trigger an additional attention for + * VF flr until ACKs, we're safe. + */ + p_flr[rel_vf_id / 64] |= 1ULL << (rel_vf_id % 64); + found = 1; + } + } + + return found; +} + +static void qed_iov_get_link(struct qed_hwfn *p_hwfn, + u16 vfid, + struct qed_mcp_link_params *p_params, + struct qed_mcp_link_state *p_link, + struct qed_mcp_link_capabilities *p_caps) +{ + struct qed_vf_info *p_vf = qed_iov_get_vf_info(p_hwfn, + vfid, + false); + struct qed_bulletin_content *p_bulletin; + + if (!p_vf) + return; + + p_bulletin = p_vf->bulletin.p_virt; + + if (p_params) + __qed_vf_get_link_params(p_hwfn, p_params, p_bulletin); + if (p_link) + __qed_vf_get_link_state(p_hwfn, p_link, p_bulletin); + if (p_caps) + __qed_vf_get_link_caps(p_hwfn, p_caps, p_bulletin); +} + +static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, int vfid) +{ + struct qed_iov_vf_mbx *mbx; + struct qed_vf_info *p_vf; + int i; + + p_vf = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!p_vf) + return; + + mbx = &p_vf->vf_mbx; + + /* qed_iov_process_mbx_request */ + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "qed_iov_process_mbx_req vfid %d\n", p_vf->abs_vf_id); + + mbx->first_tlv = mbx->req_virt->first_tlv; + + /* check if tlv type is known */ + if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { + switch (mbx->first_tlv.tl.type) { + case CHANNEL_TLV_ACQUIRE: + qed_iov_vf_mbx_acquire(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_VPORT_START: + qed_iov_vf_mbx_start_vport(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_VPORT_TEARDOWN: + qed_iov_vf_mbx_stop_vport(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_START_RXQ: + qed_iov_vf_mbx_start_rxq(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_START_TXQ: + qed_iov_vf_mbx_start_txq(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_STOP_RXQS: + qed_iov_vf_mbx_stop_rxqs(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_STOP_TXQS: + qed_iov_vf_mbx_stop_txqs(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_UPDATE_RXQ: + qed_iov_vf_mbx_update_rxqs(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_VPORT_UPDATE: + qed_iov_vf_mbx_vport_update(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_UCAST_FILTER: + qed_iov_vf_mbx_ucast_filter(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_CLOSE: + qed_iov_vf_mbx_close(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_INT_CLEANUP: + qed_iov_vf_mbx_int_cleanup(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_RELEASE: + qed_iov_vf_mbx_release(p_hwfn, p_ptt, p_vf); + break; + } + } else { + /* unknown TLV - this may belong to a VF driver from the future + * - a version written after this PF driver was written, which + * supports features unknown as of yet. Too bad since we don't + * support them. Or this may be because someone wrote a crappy + * VF driver and is sending garbage over the channel. + */ + DP_ERR(p_hwfn, + "unknown TLV. type %d length %d. first 20 bytes of mailbox buffer:\n", + mbx->first_tlv.tl.type, mbx->first_tlv.tl.length); + + for (i = 0; i < 20; i++) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "%x ", + mbx->req_virt->tlv_buf_size.tlv_buffer[i]); + } + } +} + +void qed_iov_pf_add_pending_events(struct qed_hwfn *p_hwfn, u8 vfid) +{ + u64 add_bit = 1ULL << (vfid % 64); + + p_hwfn->pf_iov_info->pending_events[vfid / 64] |= add_bit; +} + +static void qed_iov_pf_get_and_clear_pending_events(struct qed_hwfn *p_hwfn, + u64 *events) +{ + u64 *p_pending_events = p_hwfn->pf_iov_info->pending_events; + + memcpy(events, p_pending_events, sizeof(u64) * QED_VF_ARRAY_LENGTH); + memset(p_pending_events, 0, sizeof(u64) * QED_VF_ARRAY_LENGTH); +} + +static int qed_sriov_vfpf_msg(struct qed_hwfn *p_hwfn, + u16 abs_vfid, struct regpair *vf_msg) +{ + u8 min = (u8)p_hwfn->cdev->p_iov_info->first_vf_in_pf; + struct qed_vf_info *p_vf; + + if (!qed_iov_pf_sanity_check(p_hwfn, (int)abs_vfid - min)) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "Got a message from VF [abs 0x%08x] that cannot be handled by PF\n", + abs_vfid); + return 0; + } + p_vf = &p_hwfn->pf_iov_info->vfs_array[(u8)abs_vfid - min]; + + /* List the physical address of the request so that handler + * could later on copy the message from it. + */ + p_vf->vf_mbx.pending_req = (((u64)vf_msg->hi) << 32) | vf_msg->lo; + + /* Mark the event and schedule the workqueue */ + qed_iov_pf_add_pending_events(p_hwfn, p_vf->relative_vf_id); + qed_schedule_iov(p_hwfn, QED_IOV_WQ_MSG_FLAG); + + return 0; +} + +int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, + u8 opcode, __le16 echo, union event_ring_data *data) +{ + switch (opcode) { + case COMMON_EVENT_VF_PF_CHANNEL: + return qed_sriov_vfpf_msg(p_hwfn, le16_to_cpu(echo), + &data->vf_pf_channel.msg_addr); + default: + DP_INFO(p_hwfn->cdev, "Unknown sriov eqe event 0x%02x\n", + opcode); + return -EINVAL; + } +} + +u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id) +{ + struct qed_hw_sriov_info *p_iov = p_hwfn->cdev->p_iov_info; + u16 i; + + if (!p_iov) + goto out; + + for (i = rel_vf_id; i < p_iov->total_vfs; i++) + if (qed_iov_is_valid_vfid(p_hwfn, rel_vf_id, true)) + return i; + +out: + return MAX_NUM_VFS; +} + +static int qed_iov_copy_vf_msg(struct qed_hwfn *p_hwfn, struct qed_ptt *ptt, + int vfid) +{ + struct qed_dmae_params params; + struct qed_vf_info *vf_info; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!vf_info) + return -EINVAL; + + memset(¶ms, 0, sizeof(struct qed_dmae_params)); + params.flags = QED_DMAE_FLAG_VF_SRC | QED_DMAE_FLAG_COMPLETION_DST; + params.src_vfid = vf_info->abs_vf_id; + + if (qed_dmae_host2host(p_hwfn, ptt, + vf_info->vf_mbx.pending_req, + vf_info->vf_mbx.req_phys, + sizeof(union vfpf_tlvs) / 4, ¶ms)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Failed to copy message from VF 0x%02x\n", vfid); + + return -EIO; + } + + return 0; +} + +static void qed_iov_bulletin_set_forced_mac(struct qed_hwfn *p_hwfn, + u8 *mac, int vfid) +{ + struct qed_vf_info *vf_info; + u64 feature; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16)vfid, true); + if (!vf_info) { + DP_NOTICE(p_hwfn->cdev, + "Can not set forced MAC, invalid vfid [%d]\n", vfid); + return; + } + + feature = 1 << MAC_ADDR_FORCED; + memcpy(vf_info->bulletin.p_virt->mac, mac, ETH_ALEN); + + vf_info->bulletin.p_virt->valid_bitmap |= feature; + /* Forced MAC will disable MAC_ADDR */ + vf_info->bulletin.p_virt->valid_bitmap &= + ~(1 << VFPF_BULLETIN_MAC_ADDR); + + qed_iov_configure_vport_forced(p_hwfn, vf_info, feature); +} + +void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn, + u16 pvid, int vfid) +{ + struct qed_vf_info *vf_info; + u64 feature; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!vf_info) { + DP_NOTICE(p_hwfn->cdev, + "Can not set forced MAC, invalid vfid [%d]\n", vfid); + return; + } + + feature = 1 << VLAN_ADDR_FORCED; + vf_info->bulletin.p_virt->pvid = pvid; + if (pvid) + vf_info->bulletin.p_virt->valid_bitmap |= feature; + else + vf_info->bulletin.p_virt->valid_bitmap &= ~feature; + + qed_iov_configure_vport_forced(p_hwfn, vf_info, feature); +} + +static bool qed_iov_vf_has_vport_instance(struct qed_hwfn *p_hwfn, int vfid) +{ + struct qed_vf_info *p_vf_info; + + p_vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!p_vf_info) + return false; + + return !!p_vf_info->vport_instance; +} + +bool qed_iov_is_vf_stopped(struct qed_hwfn *p_hwfn, int vfid) +{ + struct qed_vf_info *p_vf_info; + + p_vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!p_vf_info) + return true; + + return p_vf_info->state == VF_STOPPED; +} + +static bool qed_iov_spoofchk_get(struct qed_hwfn *p_hwfn, int vfid) +{ + struct qed_vf_info *vf_info; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!vf_info) + return false; + + return vf_info->spoof_chk; +} + +int qed_iov_spoofchk_set(struct qed_hwfn *p_hwfn, int vfid, bool val) +{ + struct qed_vf_info *vf; + int rc = -EINVAL; + + if (!qed_iov_pf_sanity_check(p_hwfn, vfid)) { + DP_NOTICE(p_hwfn, + "SR-IOV sanity check failed, can't set spoofchk\n"); + goto out; + } + + vf = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!vf) + goto out; + + if (!qed_iov_vf_has_vport_instance(p_hwfn, vfid)) { + /* After VF VPORT start PF will configure spoof check */ + vf->req_spoofchk_val = val; + rc = 0; + goto out; + } + + rc = __qed_iov_spoofchk_set(p_hwfn, vf, val); + +out: + return rc; +} + +static u8 *qed_iov_bulletin_get_forced_mac(struct qed_hwfn *p_hwfn, + u16 rel_vf_id) +{ + struct qed_vf_info *p_vf; + + p_vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, true); + if (!p_vf || !p_vf->bulletin.p_virt) + return NULL; + + if (!(p_vf->bulletin.p_virt->valid_bitmap & (1 << MAC_ADDR_FORCED))) + return NULL; + + return p_vf->bulletin.p_virt->mac; +} + +u16 qed_iov_bulletin_get_forced_vlan(struct qed_hwfn *p_hwfn, u16 rel_vf_id) +{ + struct qed_vf_info *p_vf; + + p_vf = qed_iov_get_vf_info(p_hwfn, rel_vf_id, true); + if (!p_vf || !p_vf->bulletin.p_virt) + return 0; + + if (!(p_vf->bulletin.p_virt->valid_bitmap & (1 << VLAN_ADDR_FORCED))) + return 0; + + return p_vf->bulletin.p_virt->pvid; +} + +static int qed_iov_configure_tx_rate(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, int vfid, int val) +{ + struct qed_vf_info *vf; + u8 abs_vp_id = 0; + int rc; + + vf = qed_iov_get_vf_info(p_hwfn, (u16)vfid, true); + if (!vf) + return -EINVAL; + + rc = qed_fw_vport(p_hwfn, vf->vport_id, &abs_vp_id); + if (rc) + return rc; + + return qed_init_vport_rl(p_hwfn, p_ptt, abs_vp_id, (u32)val); +} + +int qed_iov_configure_min_tx_rate(struct qed_dev *cdev, int vfid, u32 rate) +{ + struct qed_vf_info *vf; + u8 vport_id; + int i; + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + + if (!qed_iov_pf_sanity_check(p_hwfn, vfid)) { + DP_NOTICE(p_hwfn, + "SR-IOV sanity check failed, can't set min rate\n"); + return -EINVAL; + } + } + + vf = qed_iov_get_vf_info(QED_LEADING_HWFN(cdev), (u16)vfid, true); + vport_id = vf->vport_id; + + return qed_configure_vport_wfq(cdev, vport_id, rate); +} + +static int qed_iov_get_vf_min_rate(struct qed_hwfn *p_hwfn, int vfid) +{ + struct qed_wfq_data *vf_vp_wfq; + struct qed_vf_info *vf_info; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16) vfid, true); + if (!vf_info) + return 0; + + vf_vp_wfq = &p_hwfn->qm_info.wfq_data[vf_info->vport_id]; + + if (vf_vp_wfq->configured) + return vf_vp_wfq->min_speed; + else + return 0; +} + +/** + * qed_schedule_iov - schedules IOV task for VF and PF + * @hwfn: hardware function pointer + * @flag: IOV flag for VF/PF + */ +void qed_schedule_iov(struct qed_hwfn *hwfn, enum qed_iov_wq_flag flag) +{ + smp_mb__before_atomic(); + set_bit(flag, &hwfn->iov_task_flags); + smp_mb__after_atomic(); + DP_VERBOSE(hwfn, QED_MSG_IOV, "Scheduling iov task [Flag: %d]\n", flag); + queue_delayed_work(hwfn->iov_wq, &hwfn->iov_task, 0); +} + +void qed_vf_start_iov_wq(struct qed_dev *cdev) +{ + int i; + + for_each_hwfn(cdev, i) + queue_delayed_work(cdev->hwfns[i].iov_wq, + &cdev->hwfns[i].iov_task, 0); +} + +int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) +{ + int i, j; + + for_each_hwfn(cdev, i) + if (cdev->hwfns[i].iov_wq) + flush_workqueue(cdev->hwfns[i].iov_wq); + + /* Mark VFs for disablement */ + qed_iov_set_vfs_to_disable(cdev, true); + + if (cdev->p_iov_info && cdev->p_iov_info->num_vfs && pci_enabled) + pci_disable_sriov(cdev->pdev); + + for_each_hwfn(cdev, i) { + struct qed_hwfn *hwfn = &cdev->hwfns[i]; + struct qed_ptt *ptt = qed_ptt_acquire(hwfn); + + /* Failure to acquire the ptt in 100g creates an odd error + * where the first engine has already relased IOV. + */ + if (!ptt) { + DP_ERR(hwfn, "Failed to acquire ptt\n"); + return -EBUSY; + } + + /* Clean WFQ db and configure equal weight for all vports */ + qed_clean_wfq_db(hwfn, ptt); + + qed_for_each_vf(hwfn, j) { + int k; + + if (!qed_iov_is_valid_vfid(hwfn, j, true)) + continue; + + /* Wait until VF is disabled before releasing */ + for (k = 0; k < 100; k++) { + if (!qed_iov_is_vf_stopped(hwfn, j)) + msleep(20); + else + break; + } + + if (k < 100) + qed_iov_release_hw_for_vf(&cdev->hwfns[i], + ptt, j); + else + DP_ERR(hwfn, + "Timeout waiting for VF's FLR to end\n"); + } + + qed_ptt_release(hwfn, ptt); + } + + qed_iov_set_vfs_to_disable(cdev, false); + + return 0; +} + +static int qed_sriov_enable(struct qed_dev *cdev, int num) +{ + struct qed_sb_cnt_info sb_cnt_info; + int i, j, rc; + + if (num >= RESC_NUM(&cdev->hwfns[0], QED_VPORT)) { + DP_NOTICE(cdev, "Can start at most %d VFs\n", + RESC_NUM(&cdev->hwfns[0], QED_VPORT) - 1); + return -EINVAL; + } + + /* Initialize HW for VF access */ + for_each_hwfn(cdev, j) { + struct qed_hwfn *hwfn = &cdev->hwfns[j]; + struct qed_ptt *ptt = qed_ptt_acquire(hwfn); + int num_sbs = 0, limit = 16; + + if (!ptt) { + DP_ERR(hwfn, "Failed to acquire ptt\n"); + rc = -EBUSY; + goto err; + } + + if (IS_MF_DEFAULT(hwfn)) + limit = MAX_NUM_VFS_BB / hwfn->num_funcs_on_engine; + + memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + qed_int_get_num_sbs(hwfn, &sb_cnt_info); + num_sbs = min_t(int, sb_cnt_info.sb_free_blk, limit); + + for (i = 0; i < num; i++) { + if (!qed_iov_is_valid_vfid(hwfn, i, false)) + continue; + + rc = qed_iov_init_hw_for_vf(hwfn, + ptt, i, num_sbs / num); + if (rc) { + DP_ERR(cdev, "Failed to enable VF[%d]\n", i); + qed_ptt_release(hwfn, ptt); + goto err; + } + } + + qed_ptt_release(hwfn, ptt); + } + + /* Enable SRIOV PCIe functions */ + rc = pci_enable_sriov(cdev->pdev, num); + if (rc) { + DP_ERR(cdev, "Failed to enable sriov [%d]\n", rc); + goto err; + } + + return num; + +err: + qed_sriov_disable(cdev, false); + return rc; +} + +static int qed_sriov_configure(struct qed_dev *cdev, int num_vfs_param) +{ + if (!IS_QED_SRIOV(cdev)) { + DP_VERBOSE(cdev, QED_MSG_IOV, "SR-IOV is not supported\n"); + return -EOPNOTSUPP; + } + + if (num_vfs_param) + return qed_sriov_enable(cdev, num_vfs_param); + else + return qed_sriov_disable(cdev, true); +} + +static int qed_sriov_pf_set_mac(struct qed_dev *cdev, u8 *mac, int vfid) +{ + int i; + + if (!IS_QED_SRIOV(cdev) || !IS_PF_SRIOV_ALLOC(&cdev->hwfns[0])) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "Cannot set a VF MAC; Sriov is not enabled\n"); + return -EINVAL; + } + + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "Cannot set VF[%d] MAC (VF is not active)\n", vfid); + return -EINVAL; + } + + for_each_hwfn(cdev, i) { + struct qed_hwfn *hwfn = &cdev->hwfns[i]; + struct qed_public_vf_info *vf_info; + + vf_info = qed_iov_get_public_vf_info(hwfn, vfid, true); + if (!vf_info) + continue; + + /* Set the forced MAC, and schedule the IOV task */ + ether_addr_copy(vf_info->forced_mac, mac); + qed_schedule_iov(hwfn, QED_IOV_WQ_SET_UNICAST_FILTER_FLAG); + } + + return 0; +} + +static int qed_sriov_pf_set_vlan(struct qed_dev *cdev, u16 vid, int vfid) +{ + int i; + + if (!IS_QED_SRIOV(cdev) || !IS_PF_SRIOV_ALLOC(&cdev->hwfns[0])) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "Cannot set a VF MAC; Sriov is not enabled\n"); + return -EINVAL; + } + + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vfid, true)) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "Cannot set VF[%d] MAC (VF is not active)\n", vfid); + return -EINVAL; + } + + for_each_hwfn(cdev, i) { + struct qed_hwfn *hwfn = &cdev->hwfns[i]; + struct qed_public_vf_info *vf_info; + + vf_info = qed_iov_get_public_vf_info(hwfn, vfid, true); + if (!vf_info) + continue; + + /* Set the forced vlan, and schedule the IOV task */ + vf_info->forced_vlan = vid; + qed_schedule_iov(hwfn, QED_IOV_WQ_SET_UNICAST_FILTER_FLAG); + } + + return 0; +} + +static int qed_get_vf_config(struct qed_dev *cdev, + int vf_id, struct ifla_vf_info *ivi) +{ + struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); + struct qed_public_vf_info *vf_info; + struct qed_mcp_link_state link; + u32 tx_rate; + + /* Sanitize request */ + if (IS_VF(cdev)) + return -EINVAL; + + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "VF index [%d] isn't active\n", vf_id); + return -EINVAL; + } + + vf_info = qed_iov_get_public_vf_info(hwfn, vf_id, true); + + qed_iov_get_link(hwfn, vf_id, NULL, &link, NULL); + + /* Fill information about VF */ + ivi->vf = vf_id; + + if (is_valid_ether_addr(vf_info->forced_mac)) + ether_addr_copy(ivi->mac, vf_info->forced_mac); + else + ether_addr_copy(ivi->mac, vf_info->mac); + + ivi->vlan = vf_info->forced_vlan; + ivi->spoofchk = qed_iov_spoofchk_get(hwfn, vf_id); + ivi->linkstate = vf_info->link_state; + tx_rate = vf_info->tx_rate; + ivi->max_tx_rate = tx_rate ? tx_rate : link.speed; + ivi->min_tx_rate = qed_iov_get_vf_min_rate(hwfn, vf_id); + + return 0; +} + +void qed_inform_vf_link_state(struct qed_hwfn *hwfn) +{ + struct qed_mcp_link_capabilities caps; + struct qed_mcp_link_params params; + struct qed_mcp_link_state link; + int i; + + if (!hwfn->pf_iov_info) + return; + + /* Update bulletin of all future possible VFs with link configuration */ + for (i = 0; i < hwfn->cdev->p_iov_info->total_vfs; i++) { + struct qed_public_vf_info *vf_info; + + vf_info = qed_iov_get_public_vf_info(hwfn, i, false); + if (!vf_info) + continue; + + memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params)); + memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link)); + memcpy(&caps, qed_mcp_get_link_capabilities(hwfn), + sizeof(caps)); + + /* Modify link according to the VF's configured link state */ + switch (vf_info->link_state) { + case IFLA_VF_LINK_STATE_DISABLE: + link.link_up = false; + break; + case IFLA_VF_LINK_STATE_ENABLE: + link.link_up = true; + /* Set speed according to maximum supported by HW. + * that is 40G for regular devices and 100G for CMT + * mode devices. + */ + link.speed = (hwfn->cdev->num_hwfns > 1) ? + 100000 : 40000; + default: + /* In auto mode pass PF link image to VF */ + break; + } + + if (link.link_up && vf_info->tx_rate) { + struct qed_ptt *ptt; + int rate; + + rate = min_t(int, vf_info->tx_rate, link.speed); + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) { + DP_NOTICE(hwfn, "Failed to acquire PTT\n"); + return; + } + + if (!qed_iov_configure_tx_rate(hwfn, ptt, i, rate)) { + vf_info->tx_rate = rate; + link.speed = rate; + } + + qed_ptt_release(hwfn, ptt); + } + + qed_iov_set_link(hwfn, i, ¶ms, &link, &caps); + } + + qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG); +} + +static int qed_set_vf_link_state(struct qed_dev *cdev, + int vf_id, int link_state) +{ + int i; + + /* Sanitize request */ + if (IS_VF(cdev)) + return -EINVAL; + + if (!qed_iov_is_valid_vfid(&cdev->hwfns[0], vf_id, true)) { + DP_VERBOSE(cdev, QED_MSG_IOV, + "VF index [%d] isn't active\n", vf_id); + return -EINVAL; + } + + /* Handle configuration of link state */ + for_each_hwfn(cdev, i) { + struct qed_hwfn *hwfn = &cdev->hwfns[i]; + struct qed_public_vf_info *vf; + + vf = qed_iov_get_public_vf_info(hwfn, vf_id, true); + if (!vf) + continue; + + if (vf->link_state == link_state) + continue; + + vf->link_state = link_state; + qed_inform_vf_link_state(&cdev->hwfns[i]); + } + + return 0; +} + +static int qed_spoof_configure(struct qed_dev *cdev, int vfid, bool val) +{ + int i, rc = -EINVAL; + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + + rc = qed_iov_spoofchk_set(p_hwfn, vfid, val); + if (rc) + break; + } + + return rc; +} + +static int qed_configure_max_vf_rate(struct qed_dev *cdev, int vfid, int rate) +{ + int i; + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + struct qed_public_vf_info *vf; + + if (!qed_iov_pf_sanity_check(p_hwfn, vfid)) { + DP_NOTICE(p_hwfn, + "SR-IOV sanity check failed, can't set tx rate\n"); + return -EINVAL; + } + + vf = qed_iov_get_public_vf_info(p_hwfn, vfid, true); + + vf->tx_rate = rate; + + qed_inform_vf_link_state(p_hwfn); + } + + return 0; +} + +static int qed_set_vf_rate(struct qed_dev *cdev, + int vfid, u32 min_rate, u32 max_rate) +{ + int rc_min = 0, rc_max = 0; + + if (max_rate) + rc_max = qed_configure_max_vf_rate(cdev, vfid, max_rate); + + if (min_rate) + rc_min = qed_iov_configure_min_tx_rate(cdev, vfid, min_rate); + + if (rc_max | rc_min) + return -EINVAL; + + return 0; +} + +static void qed_handle_vf_msg(struct qed_hwfn *hwfn) +{ + u64 events[QED_VF_ARRAY_LENGTH]; + struct qed_ptt *ptt; + int i; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) { + DP_VERBOSE(hwfn, QED_MSG_IOV, + "Can't acquire PTT; re-scheduling\n"); + qed_schedule_iov(hwfn, QED_IOV_WQ_MSG_FLAG); + return; + } + + qed_iov_pf_get_and_clear_pending_events(hwfn, events); + + DP_VERBOSE(hwfn, QED_MSG_IOV, + "Event mask of VF events: 0x%llx 0x%llx 0x%llx\n", + events[0], events[1], events[2]); + + qed_for_each_vf(hwfn, i) { + /* Skip VFs with no pending messages */ + if (!(events[i / 64] & (1ULL << (i % 64)))) + continue; + + DP_VERBOSE(hwfn, QED_MSG_IOV, + "Handling VF message from VF 0x%02x [Abs 0x%02x]\n", + i, hwfn->cdev->p_iov_info->first_vf_in_pf + i); + + /* Copy VF's message to PF's request buffer for that VF */ + if (qed_iov_copy_vf_msg(hwfn, ptt, i)) + continue; + + qed_iov_process_mbx_req(hwfn, ptt, i); + } + + qed_ptt_release(hwfn, ptt); +} + +static void qed_handle_pf_set_vf_unicast(struct qed_hwfn *hwfn) +{ + int i; + + qed_for_each_vf(hwfn, i) { + struct qed_public_vf_info *info; + bool update = false; + u8 *mac; + + info = qed_iov_get_public_vf_info(hwfn, i, true); + if (!info) + continue; + + /* Update data on bulletin board */ + mac = qed_iov_bulletin_get_forced_mac(hwfn, i); + if (is_valid_ether_addr(info->forced_mac) && + (!mac || !ether_addr_equal(mac, info->forced_mac))) { + DP_VERBOSE(hwfn, + QED_MSG_IOV, + "Handling PF setting of VF MAC to VF 0x%02x [Abs 0x%02x]\n", + i, + hwfn->cdev->p_iov_info->first_vf_in_pf + i); + + /* Update bulletin board with forced MAC */ + qed_iov_bulletin_set_forced_mac(hwfn, + info->forced_mac, i); + update = true; + } + + if (qed_iov_bulletin_get_forced_vlan(hwfn, i) ^ + info->forced_vlan) { + DP_VERBOSE(hwfn, + QED_MSG_IOV, + "Handling PF setting of pvid [0x%04x] to VF 0x%02x [Abs 0x%02x]\n", + info->forced_vlan, + i, + hwfn->cdev->p_iov_info->first_vf_in_pf + i); + qed_iov_bulletin_set_forced_vlan(hwfn, + info->forced_vlan, i); + update = true; + } + + if (update) + qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG); + } +} + +static void qed_handle_bulletin_post(struct qed_hwfn *hwfn) +{ + struct qed_ptt *ptt; + int i; + + ptt = qed_ptt_acquire(hwfn); + if (!ptt) { + DP_NOTICE(hwfn, "Failed allocating a ptt entry\n"); + qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG); + return; + } + + qed_for_each_vf(hwfn, i) + qed_iov_post_vf_bulletin(hwfn, i, ptt); + + qed_ptt_release(hwfn, ptt); +} + +void qed_iov_pf_task(struct work_struct *work) +{ + struct qed_hwfn *hwfn = container_of(work, struct qed_hwfn, + iov_task.work); + int rc; + + if (test_and_clear_bit(QED_IOV_WQ_STOP_WQ_FLAG, &hwfn->iov_task_flags)) + return; + + if (test_and_clear_bit(QED_IOV_WQ_FLR_FLAG, &hwfn->iov_task_flags)) { + struct qed_ptt *ptt = qed_ptt_acquire(hwfn); + + if (!ptt) { + qed_schedule_iov(hwfn, QED_IOV_WQ_FLR_FLAG); + return; + } + + rc = qed_iov_vf_flr_cleanup(hwfn, ptt); + if (rc) + qed_schedule_iov(hwfn, QED_IOV_WQ_FLR_FLAG); + + qed_ptt_release(hwfn, ptt); + } + + if (test_and_clear_bit(QED_IOV_WQ_MSG_FLAG, &hwfn->iov_task_flags)) + qed_handle_vf_msg(hwfn); + + if (test_and_clear_bit(QED_IOV_WQ_SET_UNICAST_FILTER_FLAG, + &hwfn->iov_task_flags)) + qed_handle_pf_set_vf_unicast(hwfn); + + if (test_and_clear_bit(QED_IOV_WQ_BULLETIN_UPDATE_FLAG, + &hwfn->iov_task_flags)) + qed_handle_bulletin_post(hwfn); +} + +void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first) +{ + int i; + + for_each_hwfn(cdev, i) { + if (!cdev->hwfns[i].iov_wq) + continue; + + if (schedule_first) { + qed_schedule_iov(&cdev->hwfns[i], + QED_IOV_WQ_STOP_WQ_FLAG); + cancel_delayed_work_sync(&cdev->hwfns[i].iov_task); + } + + flush_workqueue(cdev->hwfns[i].iov_wq); + destroy_workqueue(cdev->hwfns[i].iov_wq); + } +} + +int qed_iov_wq_start(struct qed_dev *cdev) +{ + char name[NAME_SIZE]; + int i; + + for_each_hwfn(cdev, i) { + struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; + + /* PFs needs a dedicated workqueue only if they support IOV. + * VFs always require one. + */ + if (IS_PF(p_hwfn->cdev) && !IS_PF_SRIOV(p_hwfn)) + continue; + + snprintf(name, NAME_SIZE, "iov-%02x:%02x.%02x", + cdev->pdev->bus->number, + PCI_SLOT(cdev->pdev->devfn), p_hwfn->abs_pf_id); + + p_hwfn->iov_wq = create_singlethread_workqueue(name); + if (!p_hwfn->iov_wq) { + DP_NOTICE(p_hwfn, "Cannot create iov workqueue\n"); + return -ENOMEM; + } + + if (IS_PF(cdev)) + INIT_DELAYED_WORK(&p_hwfn->iov_task, qed_iov_pf_task); + else + INIT_DELAYED_WORK(&p_hwfn->iov_task, qed_iov_vf_task); + } + + return 0; +} + +const struct qed_iov_hv_ops qed_iov_ops_pass = { + .configure = &qed_sriov_configure, + .set_mac = &qed_sriov_pf_set_mac, + .set_vlan = &qed_sriov_pf_set_vlan, + .get_config = &qed_get_vf_config, + .set_link_state = &qed_set_vf_link_state, + .set_spoof = &qed_spoof_configure, + .set_rate = &qed_set_vf_rate, +}; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h new file mode 100644 index 000000000000..c8667c65e685 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -0,0 +1,386 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_SRIOV_H +#define _QED_SRIOV_H +#include <linux/types.h> +#include "qed_vf.h" +#define QED_VF_ARRAY_LENGTH (3) + +#define IS_VF(cdev) ((cdev)->b_is_vf) +#define IS_PF(cdev) (!((cdev)->b_is_vf)) +#ifdef CONFIG_QED_SRIOV +#define IS_PF_SRIOV(p_hwfn) (!!((p_hwfn)->cdev->p_iov_info)) +#else +#define IS_PF_SRIOV(p_hwfn) (0) +#endif +#define IS_PF_SRIOV_ALLOC(p_hwfn) (!!((p_hwfn)->pf_iov_info)) + +#define QED_MAX_VF_CHAINS_PER_PF 16 +#define QED_ETH_VF_NUM_VLAN_FILTERS 2 + +#define QED_ETH_MAX_VF_NUM_VLAN_FILTERS \ + (MAX_NUM_VFS * QED_ETH_VF_NUM_VLAN_FILTERS) + +enum qed_iov_vport_update_flag { + QED_IOV_VP_UPDATE_ACTIVATE, + QED_IOV_VP_UPDATE_VLAN_STRIP, + QED_IOV_VP_UPDATE_TX_SWITCH, + QED_IOV_VP_UPDATE_MCAST, + QED_IOV_VP_UPDATE_ACCEPT_PARAM, + QED_IOV_VP_UPDATE_RSS, + QED_IOV_VP_UPDATE_ACCEPT_ANY_VLAN, + QED_IOV_VP_UPDATE_SGE_TPA, + QED_IOV_VP_UPDATE_MAX, +}; + +struct qed_public_vf_info { + /* These copies will later be reflected in the bulletin board, + * but this copy should be newer. + */ + u8 forced_mac[ETH_ALEN]; + u16 forced_vlan; + u8 mac[ETH_ALEN]; + + /* IFLA_VF_LINK_STATE_<X> */ + int link_state; + + /* Currently configured Tx rate in MB/sec. 0 if unconfigured */ + int tx_rate; +}; + +/* This struct is part of qed_dev and contains data relevant to all hwfns; + * Initialized only if SR-IOV cpabability is exposed in PCIe config space. + */ +struct qed_hw_sriov_info { + int pos; /* capability position */ + int nres; /* number of resources */ + u32 cap; /* SR-IOV Capabilities */ + u16 ctrl; /* SR-IOV Control */ + u16 total_vfs; /* total VFs associated with the PF */ + u16 num_vfs; /* number of vfs that have been started */ + u16 initial_vfs; /* initial VFs associated with the PF */ + u16 nr_virtfn; /* number of VFs available */ + u16 offset; /* first VF Routing ID offset */ + u16 stride; /* following VF stride */ + u16 vf_device_id; /* VF device id */ + u32 pgsz; /* page size for BAR alignment */ + u8 link; /* Function Dependency Link */ + + u32 first_vf_in_pf; +}; + +/* This mailbox is maintained per VF in its PF contains all information + * required for sending / receiving a message. + */ +struct qed_iov_vf_mbx { + union vfpf_tlvs *req_virt; + dma_addr_t req_phys; + union pfvf_tlvs *reply_virt; + dma_addr_t reply_phys; + + /* Address in VF where a pending message is located */ + dma_addr_t pending_req; + + u8 *offset; + + /* saved VF request header */ + struct vfpf_first_tlv first_tlv; +}; + +struct qed_vf_q_info { + u16 fw_rx_qid; + u16 fw_tx_qid; + u8 fw_cid; + u8 rxq_active; + u8 txq_active; +}; + +enum vf_state { + VF_FREE = 0, /* VF ready to be acquired holds no resc */ + VF_ACQUIRED, /* VF, acquired, but not initalized */ + VF_ENABLED, /* VF, Enabled */ + VF_RESET, /* VF, FLR'd, pending cleanup */ + VF_STOPPED /* VF, Stopped */ +}; + +struct qed_vf_vlan_shadow { + bool used; + u16 vid; +}; + +struct qed_vf_shadow_config { + /* Shadow copy of all guest vlans */ + struct qed_vf_vlan_shadow vlans[QED_ETH_VF_NUM_VLAN_FILTERS + 1]; + + u8 inner_vlan_removal; +}; + +/* PFs maintain an array of this structure, per VF */ +struct qed_vf_info { + struct qed_iov_vf_mbx vf_mbx; + enum vf_state state; + bool b_init; + u8 to_disable; + + struct qed_bulletin bulletin; + dma_addr_t vf_bulletin; + + u32 concrete_fid; + u16 opaque_fid; + u16 mtu; + + u8 vport_id; + u8 relative_vf_id; + u8 abs_vf_id; +#define QED_VF_ABS_ID(p_hwfn, p_vf) (QED_PATH_ID(p_hwfn) ? \ + (p_vf)->abs_vf_id + MAX_NUM_VFS_BB : \ + (p_vf)->abs_vf_id) + + u8 vport_instance; + u8 num_rxqs; + u8 num_txqs; + + u8 num_sbs; + + u8 num_mac_filters; + u8 num_vlan_filters; + struct qed_vf_q_info vf_queues[QED_MAX_VF_CHAINS_PER_PF]; + u16 igu_sbs[QED_MAX_VF_CHAINS_PER_PF]; + u8 num_active_rxqs; + struct qed_public_vf_info p_vf_info; + bool spoof_chk; + bool req_spoofchk_val; + + /* Stores the configuration requested by VF */ + struct qed_vf_shadow_config shadow_config; + + /* A bitfield using bulletin's valid-map bits, used to indicate + * which of the bulletin board features have been configured. + */ + u64 configured_features; +#define QED_IOV_CONFIGURED_FEATURES_MASK ((1 << MAC_ADDR_FORCED) | \ + (1 << VLAN_ADDR_FORCED)) +}; + +/* This structure is part of qed_hwfn and used only for PFs that have sriov + * capability enabled. + */ +struct qed_pf_iov { + struct qed_vf_info vfs_array[MAX_NUM_VFS]; + u64 pending_events[QED_VF_ARRAY_LENGTH]; + u64 pending_flr[QED_VF_ARRAY_LENGTH]; + + /* Allocate message address continuosuly and split to each VF */ + void *mbx_msg_virt_addr; + dma_addr_t mbx_msg_phys_addr; + u32 mbx_msg_size; + void *mbx_reply_virt_addr; + dma_addr_t mbx_reply_phys_addr; + u32 mbx_reply_size; + void *p_bulletins; + dma_addr_t bulletins_phys; + u32 bulletins_size; +}; + +enum qed_iov_wq_flag { + QED_IOV_WQ_MSG_FLAG, + QED_IOV_WQ_SET_UNICAST_FILTER_FLAG, + QED_IOV_WQ_BULLETIN_UPDATE_FLAG, + QED_IOV_WQ_STOP_WQ_FLAG, + QED_IOV_WQ_FLR_FLAG, +}; + +#ifdef CONFIG_QED_SRIOV +/** + * @brief - Given a VF index, return index of next [including that] active VF. + * + * @param p_hwfn + * @param rel_vf_id + * + * @return MAX_NUM_VFS in case no further active VFs, otherwise index. + */ +u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id); + +/** + * @brief Read sriov related information and allocated resources + * reads from configuraiton space, shmem, etc. + * + * @param p_hwfn + * + * @return int + */ +int qed_iov_hw_info(struct qed_hwfn *p_hwfn); + +/** + * @brief qed_add_tlv - place a given tlv on the tlv buffer at next offset + * + * @param p_hwfn + * @param p_iov + * @param type + * @param length + * + * @return pointer to the newly placed tlv + */ +void *qed_add_tlv(struct qed_hwfn *p_hwfn, u8 **offset, u16 type, u16 length); + +/** + * @brief list the types and lengths of the tlvs on the buffer + * + * @param p_hwfn + * @param tlvs_list + */ +void qed_dp_tlv_list(struct qed_hwfn *p_hwfn, void *tlvs_list); + +/** + * @brief qed_iov_alloc - allocate sriov related resources + * + * @param p_hwfn + * + * @return int + */ +int qed_iov_alloc(struct qed_hwfn *p_hwfn); + +/** + * @brief qed_iov_setup - setup sriov related resources + * + * @param p_hwfn + * @param p_ptt + */ +void qed_iov_setup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); + +/** + * @brief qed_iov_free - free sriov related resources + * + * @param p_hwfn + */ +void qed_iov_free(struct qed_hwfn *p_hwfn); + +/** + * @brief free sriov related memory that was allocated during hw_prepare + * + * @param cdev + */ +void qed_iov_free_hw_info(struct qed_dev *cdev); + +/** + * @brief qed_sriov_eqe_event - handle async sriov event arrived on eqe. + * + * @param p_hwfn + * @param opcode + * @param echo + * @param data + */ +int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, + u8 opcode, __le16 echo, union event_ring_data *data); + +/** + * @brief Mark structs of vfs that have been FLR-ed. + * + * @param p_hwfn + * @param disabled_vfs - bitmask of all VFs on path that were FLRed + * + * @return 1 iff one of the PF's vfs got FLRed. 0 otherwise. + */ +int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *disabled_vfs); + +/** + * @brief Search extended TLVs in request/reply buffer. + * + * @param p_hwfn + * @param p_tlvs_list - Pointer to tlvs list + * @param req_type - Type of TLV + * + * @return pointer to tlv type if found, otherwise returns NULL. + */ +void *qed_iov_search_list_tlvs(struct qed_hwfn *p_hwfn, + void *p_tlvs_list, u16 req_type); + +void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first); +int qed_iov_wq_start(struct qed_dev *cdev); + +void qed_schedule_iov(struct qed_hwfn *hwfn, enum qed_iov_wq_flag flag); +void qed_vf_start_iov_wq(struct qed_dev *cdev); +int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled); +void qed_inform_vf_link_state(struct qed_hwfn *hwfn); +#else +static inline u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, + u16 rel_vf_id) +{ + return MAX_NUM_VFS; +} + +static inline int qed_iov_hw_info(struct qed_hwfn *p_hwfn) +{ + return 0; +} + +static inline int qed_iov_alloc(struct qed_hwfn *p_hwfn) +{ + return 0; +} + +static inline void qed_iov_setup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ +} + +static inline void qed_iov_free(struct qed_hwfn *p_hwfn) +{ +} + +static inline void qed_iov_free_hw_info(struct qed_dev *cdev) +{ +} + +static inline int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, + u8 opcode, + __le16 echo, union event_ring_data *data) +{ + return -EINVAL; +} + +static inline int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, + u32 *disabled_vfs) +{ + return 0; +} + +static inline void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first) +{ +} + +static inline int qed_iov_wq_start(struct qed_dev *cdev) +{ + return 0; +} + +static inline void qed_schedule_iov(struct qed_hwfn *hwfn, + enum qed_iov_wq_flag flag) +{ +} + +static inline void qed_vf_start_iov_wq(struct qed_dev *cdev) +{ +} + +static inline int qed_sriov_disable(struct qed_dev *cdev, bool pci_enabled) +{ + return 0; +} + +static inline void qed_inform_vf_link_state(struct qed_hwfn *hwfn) +{ +} +#endif + +#define qed_for_each_vf(_p_hwfn, _i) \ + for (_i = qed_iov_get_next_active_vf(_p_hwfn, 0); \ + _i < MAX_NUM_VFS; \ + _i = qed_iov_get_next_active_vf(_p_hwfn, _i + 1)) + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c new file mode 100644 index 000000000000..72e69c0ec10d --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -0,0 +1,1102 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include <linux/crc32.h> +#include <linux/etherdevice.h> +#include "qed.h" +#include "qed_sriov.h" +#include "qed_vf.h" + +static void *qed_vf_pf_prep(struct qed_hwfn *p_hwfn, u16 type, u16 length) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + void *p_tlv; + + /* This lock is released when we receive PF's response + * in qed_send_msg2pf(). + * So, qed_vf_pf_prep() and qed_send_msg2pf() + * must come in sequence. + */ + mutex_lock(&(p_iov->mutex)); + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "preparing to send 0x%04x tlv over vf pf channel\n", + type); + + /* Reset Requst offset */ + p_iov->offset = (u8 *)p_iov->vf2pf_request; + + /* Clear mailbox - both request and reply */ + memset(p_iov->vf2pf_request, 0, sizeof(union vfpf_tlvs)); + memset(p_iov->pf2vf_reply, 0, sizeof(union pfvf_tlvs)); + + /* Init type and length */ + p_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, type, length); + + /* Init first tlv header */ + ((struct vfpf_first_tlv *)p_tlv)->reply_address = + (u64)p_iov->pf2vf_reply_phys; + + return p_tlv; +} + +static int qed_send_msg2pf(struct qed_hwfn *p_hwfn, u8 *done, u32 resp_size) +{ + union vfpf_tlvs *p_req = p_hwfn->vf_iov_info->vf2pf_request; + struct ustorm_trigger_vf_zone trigger; + struct ustorm_vf_zone *zone_data; + int rc = 0, time = 100; + + zone_data = (struct ustorm_vf_zone *)PXP_VF_BAR0_START_USDM_ZONE_B; + + /* output tlvs list */ + qed_dp_tlv_list(p_hwfn, p_req); + + /* need to add the END TLV to the message size */ + resp_size += sizeof(struct channel_list_end_tlv); + + /* Send TLVs over HW channel */ + memset(&trigger, 0, sizeof(struct ustorm_trigger_vf_zone)); + trigger.vf_pf_msg_valid = 1; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF -> PF [%02x] message: [%08x, %08x] --> %p, %08x --> %p\n", + GET_FIELD(p_hwfn->hw_info.concrete_fid, + PXP_CONCRETE_FID_PFID), + upper_32_bits(p_hwfn->vf_iov_info->vf2pf_request_phys), + lower_32_bits(p_hwfn->vf_iov_info->vf2pf_request_phys), + &zone_data->non_trigger.vf_pf_msg_addr, + *((u32 *)&trigger), &zone_data->trigger); + + REG_WR(p_hwfn, + (uintptr_t)&zone_data->non_trigger.vf_pf_msg_addr.lo, + lower_32_bits(p_hwfn->vf_iov_info->vf2pf_request_phys)); + + REG_WR(p_hwfn, + (uintptr_t)&zone_data->non_trigger.vf_pf_msg_addr.hi, + upper_32_bits(p_hwfn->vf_iov_info->vf2pf_request_phys)); + + /* The message data must be written first, to prevent trigger before + * data is written. + */ + wmb(); + + REG_WR(p_hwfn, (uintptr_t)&zone_data->trigger, *((u32 *)&trigger)); + + /* When PF would be done with the response, it would write back to the + * `done' address. Poll until then. + */ + while ((!*done) && time) { + msleep(25); + time--; + } + + if (!*done) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF <-- PF Timeout [Type %d]\n", + p_req->first_tlv.tl.type); + rc = -EBUSY; + goto exit; + } else { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "PF response: %d [Type %d]\n", + *done, p_req->first_tlv.tl.type); + } + +exit: + mutex_unlock(&(p_hwfn->vf_iov_info->mutex)); + + return rc; +} + +#define VF_ACQUIRE_THRESH 3 +#define VF_ACQUIRE_MAC_FILTERS 1 + +static int qed_vf_pf_acquire(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_acquire_resp_tlv *resp = &p_iov->pf2vf_reply->acquire_resp; + struct pf_vf_pfdev_info *pfdev_info = &resp->pfdev_info; + u8 rx_count = 1, tx_count = 1, num_sbs = 1; + u8 num_mac = VF_ACQUIRE_MAC_FILTERS; + bool resources_acquired = false; + struct vfpf_acquire_tlv *req; + int rc = 0, attempts = 0; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_ACQUIRE, sizeof(*req)); + + /* starting filling the request */ + req->vfdev_info.opaque_fid = p_hwfn->hw_info.opaque_fid; + + req->resc_request.num_rxqs = rx_count; + req->resc_request.num_txqs = tx_count; + req->resc_request.num_sbs = num_sbs; + req->resc_request.num_mac_filters = num_mac; + req->resc_request.num_vlan_filters = QED_ETH_VF_NUM_VLAN_FILTERS; + + req->vfdev_info.os_type = VFPF_ACQUIRE_OS_LINUX; + req->vfdev_info.fw_major = FW_MAJOR_VERSION; + req->vfdev_info.fw_minor = FW_MINOR_VERSION; + req->vfdev_info.fw_revision = FW_REVISION_VERSION; + req->vfdev_info.fw_engineering = FW_ENGINEERING_VERSION; + + /* Fill capability field with any non-deprecated config we support */ + req->vfdev_info.capabilities |= VFPF_ACQUIRE_CAP_100G; + + /* pf 2 vf bulletin board address */ + req->bulletin_addr = p_iov->bulletin.phys; + req->bulletin_size = p_iov->bulletin.size; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + while (!resources_acquired) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, "attempting to acquire resources\n"); + + /* send acquire request */ + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + /* copy acquire response from buffer to p_hwfn */ + memcpy(&p_iov->acquire_resp, resp, sizeof(p_iov->acquire_resp)); + + attempts++; + + if (resp->hdr.status == PFVF_STATUS_SUCCESS) { + /* PF agrees to allocate our resources */ + if (!(resp->pfdev_info.capabilities & + PFVF_ACQUIRE_CAP_POST_FW_OVERRIDE)) { + DP_INFO(p_hwfn, + "PF is using old incompatible driver; Either downgrade driver or request provider to update hypervisor version\n"); + return -EINVAL; + } + DP_VERBOSE(p_hwfn, QED_MSG_IOV, "resources acquired\n"); + resources_acquired = true; + } else if (resp->hdr.status == PFVF_STATUS_NO_RESOURCE && + attempts < VF_ACQUIRE_THRESH) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "PF unwilling to fullfill resource request. Try PF recommended amount\n"); + + /* humble our request */ + req->resc_request.num_txqs = resp->resc.num_txqs; + req->resc_request.num_rxqs = resp->resc.num_rxqs; + req->resc_request.num_sbs = resp->resc.num_sbs; + req->resc_request.num_mac_filters = + resp->resc.num_mac_filters; + req->resc_request.num_vlan_filters = + resp->resc.num_vlan_filters; + + /* Clear response buffer */ + memset(p_iov->pf2vf_reply, 0, sizeof(union pfvf_tlvs)); + } else { + DP_ERR(p_hwfn, + "PF returned error %d to VF acquisition request\n", + resp->hdr.status); + return -EAGAIN; + } + } + + /* Update bulletin board size with response from PF */ + p_iov->bulletin.size = resp->bulletin_size; + + /* get HW info */ + p_hwfn->cdev->type = resp->pfdev_info.dev_type; + p_hwfn->cdev->chip_rev = resp->pfdev_info.chip_rev; + + p_hwfn->cdev->chip_num = pfdev_info->chip_num & 0xffff; + + /* Learn of the possibility of CMT */ + if (IS_LEAD_HWFN(p_hwfn)) { + if (resp->pfdev_info.capabilities & PFVF_ACQUIRE_CAP_100G) { + DP_NOTICE(p_hwfn, "100g VF\n"); + p_hwfn->cdev->num_hwfns = 2; + } + } + + return 0; +} + +int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov; + u32 reg; + + /* Set number of hwfns - might be overriden once leading hwfn learns + * actual configuration from PF. + */ + if (IS_LEAD_HWFN(p_hwfn)) + p_hwfn->cdev->num_hwfns = 1; + + /* Set the doorbell bar. Assumption: regview is set */ + p_hwfn->doorbells = (u8 __iomem *)p_hwfn->regview + + PXP_VF_BAR0_START_DQ; + + reg = PXP_VF_BAR0_ME_OPAQUE_ADDRESS; + p_hwfn->hw_info.opaque_fid = (u16)REG_RD(p_hwfn, reg); + + reg = PXP_VF_BAR0_ME_CONCRETE_ADDRESS; + p_hwfn->hw_info.concrete_fid = REG_RD(p_hwfn, reg); + + /* Allocate vf sriov info */ + p_iov = kzalloc(sizeof(*p_iov), GFP_KERNEL); + if (!p_iov) { + DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_sriov'\n"); + return -ENOMEM; + } + + /* Allocate vf2pf msg */ + p_iov->vf2pf_request = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(union vfpf_tlvs), + &p_iov->vf2pf_request_phys, + GFP_KERNEL); + if (!p_iov->vf2pf_request) { + DP_NOTICE(p_hwfn, + "Failed to allocate `vf2pf_request' DMA memory\n"); + goto free_p_iov; + } + + p_iov->pf2vf_reply = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(union pfvf_tlvs), + &p_iov->pf2vf_reply_phys, + GFP_KERNEL); + if (!p_iov->pf2vf_reply) { + DP_NOTICE(p_hwfn, + "Failed to allocate `pf2vf_reply' DMA memory\n"); + goto free_vf2pf_request; + } + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF's Request mailbox [%p virt 0x%llx phys], Response mailbox [%p virt 0x%llx phys]\n", + p_iov->vf2pf_request, + (u64) p_iov->vf2pf_request_phys, + p_iov->pf2vf_reply, (u64)p_iov->pf2vf_reply_phys); + + /* Allocate Bulletin board */ + p_iov->bulletin.size = sizeof(struct qed_bulletin_content); + p_iov->bulletin.p_virt = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, + p_iov->bulletin.size, + &p_iov->bulletin.phys, + GFP_KERNEL); + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF's bulletin Board [%p virt 0x%llx phys 0x%08x bytes]\n", + p_iov->bulletin.p_virt, + (u64)p_iov->bulletin.phys, p_iov->bulletin.size); + + mutex_init(&p_iov->mutex); + + p_hwfn->vf_iov_info = p_iov; + + p_hwfn->hw_info.personality = QED_PCI_ETH; + + return qed_vf_pf_acquire(p_hwfn); + +free_vf2pf_request: + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(union vfpf_tlvs), + p_iov->vf2pf_request, p_iov->vf2pf_request_phys); +free_p_iov: + kfree(p_iov); + + return -ENOMEM; +} + +int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, + u8 rx_qid, + u16 sb, + u8 sb_index, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, + u16 cqe_pbl_size, void __iomem **pp_prod) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_start_queue_resp_tlv *resp; + struct vfpf_start_rxq_tlv *req; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_START_RXQ, sizeof(*req)); + + req->rx_qid = rx_qid; + req->cqe_pbl_addr = cqe_pbl_addr; + req->cqe_pbl_size = cqe_pbl_size; + req->rxq_addr = bd_chain_phys_addr; + req->hw_sb = sb; + req->sb_index = sb_index; + req->bd_max_bytes = bd_max_bytes; + req->stat_id = -1; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->queue_start; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + /* Learn the address of the producer from the response */ + if (pp_prod) { + u64 init_prod_val = 0; + + *pp_prod = (u8 __iomem *)p_hwfn->regview + resp->offset; + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Rxq[0x%02x]: producer at %p [offset 0x%08x]\n", + rx_qid, *pp_prod, resp->offset); + + /* Init the rcq, rx bd and rx sge (if valid) producers to 0 */ + __internal_ram_wr(p_hwfn, *pp_prod, sizeof(u64), + (u32 *)&init_prod_val); + } + + return rc; +} + +int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, u16 rx_qid, bool cqe_completion) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_stop_rxqs_tlv *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_STOP_RXQS, sizeof(*req)); + + req->rx_qid = rx_qid; + req->num_rxqs = 1; + req->cqe_completion = cqe_completion; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + return rc; +} + +int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, + u16 tx_queue_id, + u16 sb, + u8 sb_index, + dma_addr_t pbl_addr, + u16 pbl_size, void __iomem **pp_doorbell) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_start_txq_tlv *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_START_TXQ, sizeof(*req)); + + req->tx_qid = tx_queue_id; + + /* Tx */ + req->pbl_addr = pbl_addr; + req->pbl_size = pbl_size; + req->hw_sb = sb; + req->sb_index = sb_index; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + if (pp_doorbell) { + u8 cid = p_iov->acquire_resp.resc.cid[tx_queue_id]; + + *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells + + qed_db_addr(cid, DQ_DEMS_LEGACY); + } + + return rc; +} + +int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_stop_txqs_tlv *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_STOP_TXQS, sizeof(*req)); + + req->tx_qid = tx_qid; + req->num_txqs = 1; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + return rc; +} + +int qed_vf_pf_vport_start(struct qed_hwfn *p_hwfn, + u8 vport_id, + u16 mtu, + u8 inner_vlan_removal, + enum qed_tpa_mode tpa_mode, + u8 max_buffers_per_cqe, u8 only_untagged) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_vport_start_tlv *req; + struct pfvf_def_resp_tlv *resp; + int rc, i; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_VPORT_START, sizeof(*req)); + + req->mtu = mtu; + req->vport_id = vport_id; + req->inner_vlan_removal = inner_vlan_removal; + req->tpa_mode = tpa_mode; + req->max_buffers_per_cqe = max_buffers_per_cqe; + req->only_untagged = only_untagged; + + /* status blocks */ + for (i = 0; i < p_hwfn->vf_iov_info->acquire_resp.resc.num_sbs; i++) + if (p_hwfn->sbs_info[i]) + req->sb_addr[i] = p_hwfn->sbs_info[i]->sb_phys; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + return rc; +} + +int qed_vf_pf_vport_stop(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_def_resp_tlv *resp = &p_iov->pf2vf_reply->default_resp; + int rc; + + /* clear mailbox and prep first tlv */ + qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_VPORT_TEARDOWN, + sizeof(struct vfpf_first_tlv)); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + return rc; +} + +static bool +qed_vf_handle_vp_update_is_needed(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data, + u16 tlv) +{ + switch (tlv) { + case CHANNEL_TLV_VPORT_UPDATE_ACTIVATE: + return !!(p_data->update_vport_active_rx_flg || + p_data->update_vport_active_tx_flg); + case CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH: + return !!p_data->update_tx_switching_flg; + case CHANNEL_TLV_VPORT_UPDATE_VLAN_STRIP: + return !!p_data->update_inner_vlan_removal_flg; + case CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN: + return !!p_data->update_accept_any_vlan_flg; + case CHANNEL_TLV_VPORT_UPDATE_MCAST: + return !!p_data->update_approx_mcast_flg; + case CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM: + return !!(p_data->accept_flags.update_rx_mode_config || + p_data->accept_flags.update_tx_mode_config); + case CHANNEL_TLV_VPORT_UPDATE_RSS: + return !!p_data->rss_params; + case CHANNEL_TLV_VPORT_UPDATE_SGE_TPA: + return !!p_data->sge_tpa_params; + default: + DP_INFO(p_hwfn, "Unexpected vport-update TLV[%d]\n", + tlv); + return false; + } +} + +static void +qed_vf_handle_vp_update_tlvs_resp(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_data) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_def_resp_tlv *p_resp; + u16 tlv; + + for (tlv = CHANNEL_TLV_VPORT_UPDATE_ACTIVATE; + tlv < CHANNEL_TLV_VPORT_UPDATE_MAX; tlv++) { + if (!qed_vf_handle_vp_update_is_needed(p_hwfn, p_data, tlv)) + continue; + + p_resp = (struct pfvf_def_resp_tlv *) + qed_iov_search_list_tlvs(p_hwfn, p_iov->pf2vf_reply, + tlv); + if (p_resp && p_resp->hdr.status) + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "TLV[%d] Configuration %s\n", + tlv, + (p_resp && p_resp->hdr.status) ? "succeeded" + : "failed"); + } +} + +int qed_vf_pf_vport_update(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_params) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_vport_update_tlv *req; + struct pfvf_def_resp_tlv *resp; + u8 update_rx, update_tx; + u32 resp_size = 0; + u16 size, tlv; + int rc; + + resp = &p_iov->pf2vf_reply->default_resp; + resp_size = sizeof(*resp); + + update_rx = p_params->update_vport_active_rx_flg; + update_tx = p_params->update_vport_active_tx_flg; + + /* clear mailbox and prep header tlv */ + qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_VPORT_UPDATE, sizeof(*req)); + + /* Prepare extended tlvs */ + if (update_rx || update_tx) { + struct vfpf_vport_update_activate_tlv *p_act_tlv; + + size = sizeof(struct vfpf_vport_update_activate_tlv); + p_act_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_VPORT_UPDATE_ACTIVATE, + size); + resp_size += sizeof(struct pfvf_def_resp_tlv); + + if (update_rx) { + p_act_tlv->update_rx = update_rx; + p_act_tlv->active_rx = p_params->vport_active_rx_flg; + } + + if (update_tx) { + p_act_tlv->update_tx = update_tx; + p_act_tlv->active_tx = p_params->vport_active_tx_flg; + } + } + + if (p_params->update_tx_switching_flg) { + struct vfpf_vport_update_tx_switch_tlv *p_tx_switch_tlv; + + size = sizeof(struct vfpf_vport_update_tx_switch_tlv); + tlv = CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH; + p_tx_switch_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, + tlv, size); + resp_size += sizeof(struct pfvf_def_resp_tlv); + + p_tx_switch_tlv->tx_switching = p_params->tx_switching_flg; + } + + if (p_params->update_approx_mcast_flg) { + struct vfpf_vport_update_mcast_bin_tlv *p_mcast_tlv; + + size = sizeof(struct vfpf_vport_update_mcast_bin_tlv); + p_mcast_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_VPORT_UPDATE_MCAST, size); + resp_size += sizeof(struct pfvf_def_resp_tlv); + + memcpy(p_mcast_tlv->bins, p_params->bins, + sizeof(unsigned long) * ETH_MULTICAST_MAC_BINS_IN_REGS); + } + + update_rx = p_params->accept_flags.update_rx_mode_config; + update_tx = p_params->accept_flags.update_tx_mode_config; + + if (update_rx || update_tx) { + struct vfpf_vport_update_accept_param_tlv *p_accept_tlv; + + tlv = CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM; + size = sizeof(struct vfpf_vport_update_accept_param_tlv); + p_accept_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, tlv, size); + resp_size += sizeof(struct pfvf_def_resp_tlv); + + if (update_rx) { + p_accept_tlv->update_rx_mode = update_rx; + p_accept_tlv->rx_accept_filter = + p_params->accept_flags.rx_accept_filter; + } + + if (update_tx) { + p_accept_tlv->update_tx_mode = update_tx; + p_accept_tlv->tx_accept_filter = + p_params->accept_flags.tx_accept_filter; + } + } + + if (p_params->rss_params) { + struct qed_rss_params *rss_params = p_params->rss_params; + struct vfpf_vport_update_rss_tlv *p_rss_tlv; + + size = sizeof(struct vfpf_vport_update_rss_tlv); + p_rss_tlv = qed_add_tlv(p_hwfn, + &p_iov->offset, + CHANNEL_TLV_VPORT_UPDATE_RSS, size); + resp_size += sizeof(struct pfvf_def_resp_tlv); + + if (rss_params->update_rss_config) + p_rss_tlv->update_rss_flags |= + VFPF_UPDATE_RSS_CONFIG_FLAG; + if (rss_params->update_rss_capabilities) + p_rss_tlv->update_rss_flags |= + VFPF_UPDATE_RSS_CAPS_FLAG; + if (rss_params->update_rss_ind_table) + p_rss_tlv->update_rss_flags |= + VFPF_UPDATE_RSS_IND_TABLE_FLAG; + if (rss_params->update_rss_key) + p_rss_tlv->update_rss_flags |= VFPF_UPDATE_RSS_KEY_FLAG; + + p_rss_tlv->rss_enable = rss_params->rss_enable; + p_rss_tlv->rss_caps = rss_params->rss_caps; + p_rss_tlv->rss_table_size_log = rss_params->rss_table_size_log; + memcpy(p_rss_tlv->rss_ind_table, rss_params->rss_ind_table, + sizeof(rss_params->rss_ind_table)); + memcpy(p_rss_tlv->rss_key, rss_params->rss_key, + sizeof(rss_params->rss_key)); + } + + if (p_params->update_accept_any_vlan_flg) { + struct vfpf_vport_update_accept_any_vlan_tlv *p_any_vlan_tlv; + + size = sizeof(struct vfpf_vport_update_accept_any_vlan_tlv); + tlv = CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN; + p_any_vlan_tlv = qed_add_tlv(p_hwfn, &p_iov->offset, tlv, size); + + resp_size += sizeof(struct pfvf_def_resp_tlv); + p_any_vlan_tlv->accept_any_vlan = p_params->accept_any_vlan; + p_any_vlan_tlv->update_accept_any_vlan_flg = + p_params->update_accept_any_vlan_flg; + } + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, resp_size); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + qed_vf_handle_vp_update_tlvs_resp(p_hwfn, p_params); + + return rc; +} + +int qed_vf_pf_reset(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_def_resp_tlv *resp; + struct vfpf_first_tlv *req; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_CLOSE, sizeof(*req)); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EAGAIN; + + p_hwfn->b_int_enabled = 0; + + return 0; +} + +int qed_vf_pf_release(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_def_resp_tlv *resp; + struct vfpf_first_tlv *req; + u32 size; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_RELEASE, sizeof(*req)); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + + if (!rc && resp->hdr.status != PFVF_STATUS_SUCCESS) + rc = -EAGAIN; + + p_hwfn->b_int_enabled = 0; + + if (p_iov->vf2pf_request) + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(union vfpf_tlvs), + p_iov->vf2pf_request, + p_iov->vf2pf_request_phys); + if (p_iov->pf2vf_reply) + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + sizeof(union pfvf_tlvs), + p_iov->pf2vf_reply, p_iov->pf2vf_reply_phys); + + if (p_iov->bulletin.p_virt) { + size = sizeof(struct qed_bulletin_content); + dma_free_coherent(&p_hwfn->cdev->pdev->dev, + size, + p_iov->bulletin.p_virt, p_iov->bulletin.phys); + } + + kfree(p_hwfn->vf_iov_info); + p_hwfn->vf_iov_info = NULL; + + return rc; +} + +void qed_vf_pf_filter_mcast(struct qed_hwfn *p_hwfn, + struct qed_filter_mcast *p_filter_cmd) +{ + struct qed_sp_vport_update_params sp_params; + int i; + + memset(&sp_params, 0, sizeof(sp_params)); + sp_params.update_approx_mcast_flg = 1; + + if (p_filter_cmd->opcode == QED_FILTER_ADD) { + for (i = 0; i < p_filter_cmd->num_mc_addrs; i++) { + u32 bit; + + bit = qed_mcast_bin_from_mac(p_filter_cmd->mac[i]); + __set_bit(bit, sp_params.bins); + } + } + + qed_vf_pf_vport_update(p_hwfn, &sp_params); +} + +int qed_vf_pf_filter_ucast(struct qed_hwfn *p_hwfn, + struct qed_filter_ucast *p_ucast) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_ucast_filter_tlv *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep first tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_UCAST_FILTER, sizeof(*req)); + req->opcode = (u8) p_ucast->opcode; + req->type = (u8) p_ucast->type; + memcpy(req->mac, p_ucast->mac, ETH_ALEN); + req->vlan = p_ucast->vlan; + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EAGAIN; + + return 0; +} + +int qed_vf_pf_int_cleanup(struct qed_hwfn *p_hwfn) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_def_resp_tlv *resp = &p_iov->pf2vf_reply->default_resp; + int rc; + + /* clear mailbox and prep first tlv */ + qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_INT_CLEANUP, + sizeof(struct vfpf_first_tlv)); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); + + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + return rc; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + return -EINVAL; + + return 0; +} + +u16 qed_vf_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + + if (!p_iov) { + DP_NOTICE(p_hwfn, "vf_sriov_info isn't initialized\n"); + return 0; + } + + return p_iov->acquire_resp.resc.hw_sbs[sb_id].hw_sb_id; +} + +int qed_vf_read_bulletin(struct qed_hwfn *p_hwfn, u8 *p_change) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct qed_bulletin_content shadow; + u32 crc, crc_size; + + crc_size = sizeof(p_iov->bulletin.p_virt->crc); + *p_change = 0; + + /* Need to guarantee PF is not in the middle of writing it */ + memcpy(&shadow, p_iov->bulletin.p_virt, p_iov->bulletin.size); + + /* If version did not update, no need to do anything */ + if (shadow.version == p_iov->bulletin_shadow.version) + return 0; + + /* Verify the bulletin we see is valid */ + crc = crc32(0, (u8 *)&shadow + crc_size, + p_iov->bulletin.size - crc_size); + if (crc != shadow.crc) + return -EAGAIN; + + /* Set the shadow bulletin and process it */ + memcpy(&p_iov->bulletin_shadow, &shadow, p_iov->bulletin.size); + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Read a bulletin update %08x\n", shadow.version); + + *p_change = 1; + + return 0; +} + +void __qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params *p_params, + struct qed_bulletin_content *p_bulletin) +{ + memset(p_params, 0, sizeof(*p_params)); + + p_params->speed.autoneg = p_bulletin->req_autoneg; + p_params->speed.advertised_speeds = p_bulletin->req_adv_speed; + p_params->speed.forced_speed = p_bulletin->req_forced_speed; + p_params->pause.autoneg = p_bulletin->req_autoneg_pause; + p_params->pause.forced_rx = p_bulletin->req_forced_rx; + p_params->pause.forced_tx = p_bulletin->req_forced_tx; + p_params->loopback_mode = p_bulletin->req_loopback; +} + +void qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params *params) +{ + __qed_vf_get_link_params(p_hwfn, params, + &(p_hwfn->vf_iov_info->bulletin_shadow)); +} + +void __qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *p_link, + struct qed_bulletin_content *p_bulletin) +{ + memset(p_link, 0, sizeof(*p_link)); + + p_link->link_up = p_bulletin->link_up; + p_link->speed = p_bulletin->speed; + p_link->full_duplex = p_bulletin->full_duplex; + p_link->an = p_bulletin->autoneg; + p_link->an_complete = p_bulletin->autoneg_complete; + p_link->parallel_detection = p_bulletin->parallel_detection; + p_link->pfc_enabled = p_bulletin->pfc_enabled; + p_link->partner_adv_speed = p_bulletin->partner_adv_speed; + p_link->partner_tx_flow_ctrl_en = p_bulletin->partner_tx_flow_ctrl_en; + p_link->partner_rx_flow_ctrl_en = p_bulletin->partner_rx_flow_ctrl_en; + p_link->partner_adv_pause = p_bulletin->partner_adv_pause; + p_link->sfp_tx_fault = p_bulletin->sfp_tx_fault; +} + +void qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *link) +{ + __qed_vf_get_link_state(p_hwfn, link, + &(p_hwfn->vf_iov_info->bulletin_shadow)); +} + +void __qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps, + struct qed_bulletin_content *p_bulletin) +{ + memset(p_link_caps, 0, sizeof(*p_link_caps)); + p_link_caps->speed_capabilities = p_bulletin->capability_speed; +} + +void qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps) +{ + __qed_vf_get_link_caps(p_hwfn, p_link_caps, + &(p_hwfn->vf_iov_info->bulletin_shadow)); +} + +void qed_vf_get_num_rxqs(struct qed_hwfn *p_hwfn, u8 *num_rxqs) +{ + *num_rxqs = p_hwfn->vf_iov_info->acquire_resp.resc.num_rxqs; +} + +void qed_vf_get_port_mac(struct qed_hwfn *p_hwfn, u8 *port_mac) +{ + memcpy(port_mac, + p_hwfn->vf_iov_info->acquire_resp.pfdev_info.port_mac, ETH_ALEN); +} + +void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, u8 *num_vlan_filters) +{ + struct qed_vf_iov *p_vf; + + p_vf = p_hwfn->vf_iov_info; + *num_vlan_filters = p_vf->acquire_resp.resc.num_vlan_filters; +} + +bool qed_vf_check_mac(struct qed_hwfn *p_hwfn, u8 *mac) +{ + struct qed_bulletin_content *bulletin; + + bulletin = &p_hwfn->vf_iov_info->bulletin_shadow; + if (!(bulletin->valid_bitmap & (1 << MAC_ADDR_FORCED))) + return true; + + /* Forbid VF from changing a MAC enforced by PF */ + if (ether_addr_equal(bulletin->mac, mac)) + return false; + + return false; +} + +bool qed_vf_bulletin_get_forced_mac(struct qed_hwfn *hwfn, + u8 *dst_mac, u8 *p_is_forced) +{ + struct qed_bulletin_content *bulletin; + + bulletin = &hwfn->vf_iov_info->bulletin_shadow; + + if (bulletin->valid_bitmap & (1 << MAC_ADDR_FORCED)) { + if (p_is_forced) + *p_is_forced = 1; + } else if (bulletin->valid_bitmap & (1 << VFPF_BULLETIN_MAC_ADDR)) { + if (p_is_forced) + *p_is_forced = 0; + } else { + return false; + } + + ether_addr_copy(dst_mac, bulletin->mac); + + return true; +} + +void qed_vf_get_fw_version(struct qed_hwfn *p_hwfn, + u16 *fw_major, u16 *fw_minor, + u16 *fw_rev, u16 *fw_eng) +{ + struct pf_vf_pfdev_info *info; + + info = &p_hwfn->vf_iov_info->acquire_resp.pfdev_info; + + *fw_major = info->fw_major; + *fw_minor = info->fw_minor; + *fw_rev = info->fw_rev; + *fw_eng = info->fw_eng; +} + +static void qed_handle_bulletin_change(struct qed_hwfn *hwfn) +{ + struct qed_eth_cb_ops *ops = hwfn->cdev->protocol_ops.eth; + u8 mac[ETH_ALEN], is_mac_exist, is_mac_forced; + void *cookie = hwfn->cdev->ops_cookie; + + is_mac_exist = qed_vf_bulletin_get_forced_mac(hwfn, mac, + &is_mac_forced); + if (is_mac_exist && is_mac_forced && cookie) + ops->force_mac(cookie, mac); + + /* Always update link configuration according to bulletin */ + qed_link_update(hwfn); +} + +void qed_iov_vf_task(struct work_struct *work) +{ + struct qed_hwfn *hwfn = container_of(work, struct qed_hwfn, + iov_task.work); + u8 change = 0; + + if (test_and_clear_bit(QED_IOV_WQ_STOP_WQ_FLAG, &hwfn->iov_task_flags)) + return; + + /* Handle bulletin board changes */ + qed_vf_read_bulletin(hwfn, &change); + if (change) + qed_handle_bulletin_change(hwfn); + + /* As VF is polling bulletin board, need to constantly re-schedule */ + queue_delayed_work(hwfn->iov_wq, &hwfn->iov_task, HZ); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h new file mode 100644 index 000000000000..b82fda964bbd --- /dev/null +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -0,0 +1,990 @@ +/* QLogic qed NIC Driver + * Copyright (c) 2015 QLogic Corporation + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QED_VF_H +#define _QED_VF_H + +#include "qed_l2.h" +#include "qed_mcp.h" + +#define T_ETH_INDIRECTION_TABLE_SIZE 128 +#define T_ETH_RSS_KEY_SIZE 10 + +struct vf_pf_resc_request { + u8 num_rxqs; + u8 num_txqs; + u8 num_sbs; + u8 num_mac_filters; + u8 num_vlan_filters; + u8 num_mc_filters; + u16 padding; +}; + +struct hw_sb_info { + u16 hw_sb_id; + u8 sb_qid; + u8 padding[5]; +}; + +#define TLV_BUFFER_SIZE 1024 + +enum { + PFVF_STATUS_WAITING, + PFVF_STATUS_SUCCESS, + PFVF_STATUS_FAILURE, + PFVF_STATUS_NOT_SUPPORTED, + PFVF_STATUS_NO_RESOURCE, + PFVF_STATUS_FORCED, +}; + +/* vf pf channel tlvs */ +/* general tlv header (used for both vf->pf request and pf->vf response) */ +struct channel_tlv { + u16 type; + u16 length; +}; + +/* header of first vf->pf tlv carries the offset used to calculate reponse + * buffer address + */ +struct vfpf_first_tlv { + struct channel_tlv tl; + u32 padding; + u64 reply_address; +}; + +/* header of pf->vf tlvs, carries the status of handling the request */ +struct pfvf_tlv { + struct channel_tlv tl; + u8 status; + u8 padding[3]; +}; + +/* response tlv used for most tlvs */ +struct pfvf_def_resp_tlv { + struct pfvf_tlv hdr; +}; + +/* used to terminate and pad a tlv list */ +struct channel_list_end_tlv { + struct channel_tlv tl; + u8 padding[4]; +}; + +#define VFPF_ACQUIRE_OS_LINUX (0) +#define VFPF_ACQUIRE_OS_WINDOWS (1) +#define VFPF_ACQUIRE_OS_ESX (2) +#define VFPF_ACQUIRE_OS_SOLARIS (3) +#define VFPF_ACQUIRE_OS_LINUX_USERSPACE (4) + +struct vfpf_acquire_tlv { + struct vfpf_first_tlv first_tlv; + + struct vf_pf_vfdev_info { +#define VFPF_ACQUIRE_CAP_OBSOLETE (1 << 0) +#define VFPF_ACQUIRE_CAP_100G (1 << 1) /* VF can support 100g */ + u64 capabilities; + u8 fw_major; + u8 fw_minor; + u8 fw_revision; + u8 fw_engineering; + u32 driver_version; + u16 opaque_fid; /* ME register value */ + u8 os_type; /* VFPF_ACQUIRE_OS_* value */ + u8 padding[5]; + } vfdev_info; + + struct vf_pf_resc_request resc_request; + + u64 bulletin_addr; + u32 bulletin_size; + u32 padding; +}; + +/* receive side scaling tlv */ +struct vfpf_vport_update_rss_tlv { + struct channel_tlv tl; + + u8 update_rss_flags; +#define VFPF_UPDATE_RSS_CONFIG_FLAG BIT(0) +#define VFPF_UPDATE_RSS_CAPS_FLAG BIT(1) +#define VFPF_UPDATE_RSS_IND_TABLE_FLAG BIT(2) +#define VFPF_UPDATE_RSS_KEY_FLAG BIT(3) + + u8 rss_enable; + u8 rss_caps; + u8 rss_table_size_log; /* The table size is 2 ^ rss_table_size_log */ + u16 rss_ind_table[T_ETH_INDIRECTION_TABLE_SIZE]; + u32 rss_key[T_ETH_RSS_KEY_SIZE]; +}; + +struct pfvf_storm_stats { + u32 address; + u32 len; +}; + +struct pfvf_stats_info { + struct pfvf_storm_stats mstats; + struct pfvf_storm_stats pstats; + struct pfvf_storm_stats tstats; + struct pfvf_storm_stats ustats; +}; + +struct pfvf_acquire_resp_tlv { + struct pfvf_tlv hdr; + + struct pf_vf_pfdev_info { + u32 chip_num; + u32 mfw_ver; + + u16 fw_major; + u16 fw_minor; + u16 fw_rev; + u16 fw_eng; + + u64 capabilities; +#define PFVF_ACQUIRE_CAP_DEFAULT_UNTAGGED BIT(0) +#define PFVF_ACQUIRE_CAP_100G BIT(1) /* If set, 100g PF */ +/* There are old PF versions where the PF might mistakenly override the sanity + * mechanism [version-based] and allow a VF that can't be supported to pass + * the acquisition phase. + * To overcome this, PFs now indicate that they're past that point and the new + * VFs would fail probe on the older PFs that fail to do so. + */ +#define PFVF_ACQUIRE_CAP_POST_FW_OVERRIDE BIT(2) + + u16 db_size; + u8 indices_per_sb; + u8 os_type; + + /* These should match the PF's qed_dev values */ + u16 chip_rev; + u8 dev_type; + + u8 padding; + + struct pfvf_stats_info stats_info; + + u8 port_mac[ETH_ALEN]; + u8 padding2[2]; + } pfdev_info; + + struct pf_vf_resc { +#define PFVF_MAX_QUEUES_PER_VF 16 +#define PFVF_MAX_SBS_PER_VF 16 + struct hw_sb_info hw_sbs[PFVF_MAX_SBS_PER_VF]; + u8 hw_qid[PFVF_MAX_QUEUES_PER_VF]; + u8 cid[PFVF_MAX_QUEUES_PER_VF]; + + u8 num_rxqs; + u8 num_txqs; + u8 num_sbs; + u8 num_mac_filters; + u8 num_vlan_filters; + u8 num_mc_filters; + u8 padding[2]; + } resc; + + u32 bulletin_size; + u32 padding; +}; + +struct pfvf_start_queue_resp_tlv { + struct pfvf_tlv hdr; + u32 offset; /* offset to consumer/producer of queue */ + u8 padding[4]; +}; + +/* Setup Queue */ +struct vfpf_start_rxq_tlv { + struct vfpf_first_tlv first_tlv; + + /* physical addresses */ + u64 rxq_addr; + u64 deprecated_sge_addr; + u64 cqe_pbl_addr; + + u16 cqe_pbl_size; + u16 hw_sb; + u16 rx_qid; + u16 hc_rate; /* desired interrupts per sec. */ + + u16 bd_max_bytes; + u16 stat_id; + u8 sb_index; + u8 padding[3]; +}; + +struct vfpf_start_txq_tlv { + struct vfpf_first_tlv first_tlv; + + /* physical addresses */ + u64 pbl_addr; + u16 pbl_size; + u16 stat_id; + u16 tx_qid; + u16 hw_sb; + + u32 flags; /* VFPF_QUEUE_FLG_X flags */ + u16 hc_rate; /* desired interrupts per sec. */ + u8 sb_index; + u8 padding[3]; +}; + +/* Stop RX Queue */ +struct vfpf_stop_rxqs_tlv { + struct vfpf_first_tlv first_tlv; + + u16 rx_qid; + u8 num_rxqs; + u8 cqe_completion; + u8 padding[4]; +}; + +/* Stop TX Queues */ +struct vfpf_stop_txqs_tlv { + struct vfpf_first_tlv first_tlv; + + u16 tx_qid; + u8 num_txqs; + u8 padding[5]; +}; + +struct vfpf_update_rxq_tlv { + struct vfpf_first_tlv first_tlv; + + u64 deprecated_sge_addr[PFVF_MAX_QUEUES_PER_VF]; + + u16 rx_qid; + u8 num_rxqs; + u8 flags; +#define VFPF_RXQ_UPD_INIT_SGE_DEPRECATE_FLAG BIT(0) +#define VFPF_RXQ_UPD_COMPLETE_CQE_FLAG BIT(1) +#define VFPF_RXQ_UPD_COMPLETE_EVENT_FLAG BIT(2) + + u8 padding[4]; +}; + +/* Set Queue Filters */ +struct vfpf_q_mac_vlan_filter { + u32 flags; +#define VFPF_Q_FILTER_DEST_MAC_VALID 0x01 +#define VFPF_Q_FILTER_VLAN_TAG_VALID 0x02 +#define VFPF_Q_FILTER_SET_MAC 0x100 /* set/clear */ + + u8 mac[ETH_ALEN]; + u16 vlan_tag; + + u8 padding[4]; +}; + +/* Start a vport */ +struct vfpf_vport_start_tlv { + struct vfpf_first_tlv first_tlv; + + u64 sb_addr[PFVF_MAX_SBS_PER_VF]; + + u32 tpa_mode; + u16 dep1; + u16 mtu; + + u8 vport_id; + u8 inner_vlan_removal; + + u8 only_untagged; + u8 max_buffers_per_cqe; + + u8 padding[4]; +}; + +/* Extended tlvs - need to add rss, mcast, accept mode tlvs */ +struct vfpf_vport_update_activate_tlv { + struct channel_tlv tl; + u8 update_rx; + u8 update_tx; + u8 active_rx; + u8 active_tx; +}; + +struct vfpf_vport_update_tx_switch_tlv { + struct channel_tlv tl; + u8 tx_switching; + u8 padding[3]; +}; + +struct vfpf_vport_update_vlan_strip_tlv { + struct channel_tlv tl; + u8 remove_vlan; + u8 padding[3]; +}; + +struct vfpf_vport_update_mcast_bin_tlv { + struct channel_tlv tl; + u8 padding[4]; + + u64 bins[8]; +}; + +struct vfpf_vport_update_accept_param_tlv { + struct channel_tlv tl; + u8 update_rx_mode; + u8 update_tx_mode; + u8 rx_accept_filter; + u8 tx_accept_filter; +}; + +struct vfpf_vport_update_accept_any_vlan_tlv { + struct channel_tlv tl; + u8 update_accept_any_vlan_flg; + u8 accept_any_vlan; + + u8 padding[2]; +}; + +struct vfpf_vport_update_sge_tpa_tlv { + struct channel_tlv tl; + + u16 sge_tpa_flags; +#define VFPF_TPA_IPV4_EN_FLAG BIT(0) +#define VFPF_TPA_IPV6_EN_FLAG BIT(1) +#define VFPF_TPA_PKT_SPLIT_FLAG BIT(2) +#define VFPF_TPA_HDR_DATA_SPLIT_FLAG BIT(3) +#define VFPF_TPA_GRO_CONSIST_FLAG BIT(4) + + u8 update_sge_tpa_flags; +#define VFPF_UPDATE_SGE_DEPRECATED_FLAG BIT(0) +#define VFPF_UPDATE_TPA_EN_FLAG BIT(1) +#define VFPF_UPDATE_TPA_PARAM_FLAG BIT(2) + + u8 max_buffers_per_cqe; + + u16 deprecated_sge_buff_size; + u16 tpa_max_size; + u16 tpa_min_size_to_start; + u16 tpa_min_size_to_cont; + + u8 tpa_max_aggs_num; + u8 padding[7]; +}; + +/* Primary tlv as a header for various extended tlvs for + * various functionalities in vport update ramrod. + */ +struct vfpf_vport_update_tlv { + struct vfpf_first_tlv first_tlv; +}; + +struct vfpf_ucast_filter_tlv { + struct vfpf_first_tlv first_tlv; + + u8 opcode; + u8 type; + + u8 mac[ETH_ALEN]; + + u16 vlan; + u16 padding[3]; +}; + +struct tlv_buffer_size { + u8 tlv_buffer[TLV_BUFFER_SIZE]; +}; + +union vfpf_tlvs { + struct vfpf_first_tlv first_tlv; + struct vfpf_acquire_tlv acquire; + struct vfpf_start_rxq_tlv start_rxq; + struct vfpf_start_txq_tlv start_txq; + struct vfpf_stop_rxqs_tlv stop_rxqs; + struct vfpf_stop_txqs_tlv stop_txqs; + struct vfpf_update_rxq_tlv update_rxq; + struct vfpf_vport_start_tlv start_vport; + struct vfpf_vport_update_tlv vport_update; + struct vfpf_ucast_filter_tlv ucast_filter; + struct channel_list_end_tlv list_end; + struct tlv_buffer_size tlv_buf_size; +}; + +union pfvf_tlvs { + struct pfvf_def_resp_tlv default_resp; + struct pfvf_acquire_resp_tlv acquire_resp; + struct tlv_buffer_size tlv_buf_size; + struct pfvf_start_queue_resp_tlv queue_start; +}; + +enum qed_bulletin_bit { + /* Alert the VF that a forced MAC was set by the PF */ + MAC_ADDR_FORCED = 0, + /* Alert the VF that a forced VLAN was set by the PF */ + VLAN_ADDR_FORCED = 2, + + /* Indicate that `default_only_untagged' contains actual data */ + VFPF_BULLETIN_UNTAGGED_DEFAULT = 3, + VFPF_BULLETIN_UNTAGGED_DEFAULT_FORCED = 4, + + /* Alert the VF that suggested mac was sent by the PF. + * MAC_ADDR will be disabled in case MAC_ADDR_FORCED is set. + */ + VFPF_BULLETIN_MAC_ADDR = 5 +}; + +struct qed_bulletin_content { + /* crc of structure to ensure is not in mid-update */ + u32 crc; + + u32 version; + + /* bitmap indicating which fields hold valid values */ + u64 valid_bitmap; + + /* used for MAC_ADDR or MAC_ADDR_FORCED */ + u8 mac[ETH_ALEN]; + + /* If valid, 1 => only untagged Rx if no vlan is configured */ + u8 default_only_untagged; + u8 padding; + + /* The following is a 'copy' of qed_mcp_link_state, + * qed_mcp_link_params and qed_mcp_link_capabilities. Since it's + * possible the structs will increase further along the road we cannot + * have it here; Instead we need to have all of its fields. + */ + u8 req_autoneg; + u8 req_autoneg_pause; + u8 req_forced_rx; + u8 req_forced_tx; + u8 padding2[4]; + + u32 req_adv_speed; + u32 req_forced_speed; + u32 req_loopback; + u32 padding3; + + u8 link_up; + u8 full_duplex; + u8 autoneg; + u8 autoneg_complete; + u8 parallel_detection; + u8 pfc_enabled; + u8 partner_tx_flow_ctrl_en; + u8 partner_rx_flow_ctrl_en; + u8 partner_adv_pause; + u8 sfp_tx_fault; + u8 padding4[6]; + + u32 speed; + u32 partner_adv_speed; + + u32 capability_speed; + + /* Forced vlan */ + u16 pvid; + u16 padding5; +}; + +struct qed_bulletin { + dma_addr_t phys; + struct qed_bulletin_content *p_virt; + u32 size; +}; + +enum { + CHANNEL_TLV_NONE, /* ends tlv sequence */ + CHANNEL_TLV_ACQUIRE, + CHANNEL_TLV_VPORT_START, + CHANNEL_TLV_VPORT_UPDATE, + CHANNEL_TLV_VPORT_TEARDOWN, + CHANNEL_TLV_START_RXQ, + CHANNEL_TLV_START_TXQ, + CHANNEL_TLV_STOP_RXQS, + CHANNEL_TLV_STOP_TXQS, + CHANNEL_TLV_UPDATE_RXQ, + CHANNEL_TLV_INT_CLEANUP, + CHANNEL_TLV_CLOSE, + CHANNEL_TLV_RELEASE, + CHANNEL_TLV_LIST_END, + CHANNEL_TLV_UCAST_FILTER, + CHANNEL_TLV_VPORT_UPDATE_ACTIVATE, + CHANNEL_TLV_VPORT_UPDATE_TX_SWITCH, + CHANNEL_TLV_VPORT_UPDATE_VLAN_STRIP, + CHANNEL_TLV_VPORT_UPDATE_MCAST, + CHANNEL_TLV_VPORT_UPDATE_ACCEPT_PARAM, + CHANNEL_TLV_VPORT_UPDATE_RSS, + CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN, + CHANNEL_TLV_VPORT_UPDATE_SGE_TPA, + CHANNEL_TLV_MAX, + + /* Required for iterating over vport-update tlvs. + * Will break in case non-sequential vport-update tlvs. + */ + CHANNEL_TLV_VPORT_UPDATE_MAX = CHANNEL_TLV_VPORT_UPDATE_SGE_TPA + 1, +}; + +/* This data is held in the qed_hwfn structure for VFs only. */ +struct qed_vf_iov { + union vfpf_tlvs *vf2pf_request; + dma_addr_t vf2pf_request_phys; + union pfvf_tlvs *pf2vf_reply; + dma_addr_t pf2vf_reply_phys; + + /* Should be taken whenever the mailbox buffers are accessed */ + struct mutex mutex; + u8 *offset; + + /* Bulletin Board */ + struct qed_bulletin bulletin; + struct qed_bulletin_content bulletin_shadow; + + /* we set aside a copy of the acquire response */ + struct pfvf_acquire_resp_tlv acquire_resp; +}; + +#ifdef CONFIG_QED_SRIOV +/** + * @brief Read the VF bulletin and act on it if needed + * + * @param p_hwfn + * @param p_change - qed fills 1 iff bulletin board has changed, 0 otherwise. + * + * @return enum _qed_status + */ +int qed_vf_read_bulletin(struct qed_hwfn *p_hwfn, u8 *p_change); + +/** + * @brief Get link paramters for VF from qed + * + * @param p_hwfn + * @param params - the link params structure to be filled for the VF + */ +void qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params *params); + +/** + * @brief Get link state for VF from qed + * + * @param p_hwfn + * @param link - the link state structure to be filled for the VF + */ +void qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *link); + +/** + * @brief Get link capabilities for VF from qed + * + * @param p_hwfn + * @param p_link_caps - the link capabilities structure to be filled for the VF + */ +void qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps); + +/** + * @brief Get number of Rx queues allocated for VF by qed + * + * @param p_hwfn + * @param num_rxqs - allocated RX queues + */ +void qed_vf_get_num_rxqs(struct qed_hwfn *p_hwfn, u8 *num_rxqs); + +/** + * @brief Get port mac address for VF + * + * @param p_hwfn + * @param port_mac - destination location for port mac + */ +void qed_vf_get_port_mac(struct qed_hwfn *p_hwfn, u8 *port_mac); + +/** + * @brief Get number of VLAN filters allocated for VF by qed + * + * @param p_hwfn + * @param num_rxqs - allocated VLAN filters + */ +void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, + u8 *num_vlan_filters); + +/** + * @brief Check if VF can set a MAC address + * + * @param p_hwfn + * @param mac + * + * @return bool + */ +bool qed_vf_check_mac(struct qed_hwfn *p_hwfn, u8 *mac); + +/** + * @brief Set firmware version information in dev_info from VFs acquire response tlv + * + * @param p_hwfn + * @param fw_major + * @param fw_minor + * @param fw_rev + * @param fw_eng + */ +void qed_vf_get_fw_version(struct qed_hwfn *p_hwfn, + u16 *fw_major, u16 *fw_minor, + u16 *fw_rev, u16 *fw_eng); + +/** + * @brief hw preparation for VF + * sends ACQUIRE message + * + * @param p_hwfn + * + * @return int + */ +int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn); + +/** + * @brief VF - start the RX Queue by sending a message to the PF + * @param p_hwfn + * @param cid - zero based within the VF + * @param rx_queue_id - zero based within the VF + * @param sb - VF status block for this queue + * @param sb_index - Index within the status block + * @param bd_max_bytes - maximum number of bytes per bd + * @param bd_chain_phys_addr - physical address of bd chain + * @param cqe_pbl_addr - physical address of pbl + * @param cqe_pbl_size - pbl size + * @param pp_prod - pointer to the producer to be + * used in fastpath + * + * @return int + */ +int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, + u8 rx_queue_id, + u16 sb, + u8 sb_index, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_addr, + dma_addr_t cqe_pbl_addr, + u16 cqe_pbl_size, void __iomem **pp_prod); + +/** + * @brief VF - start the TX queue by sending a message to the + * PF. + * + * @param p_hwfn + * @param tx_queue_id - zero based within the VF + * @param sb - status block for this queue + * @param sb_index - index within the status block + * @param bd_chain_phys_addr - physical address of tx chain + * @param pp_doorbell - pointer to address to which to + * write the doorbell too.. + * + * @return int + */ +int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, + u16 tx_queue_id, + u16 sb, + u8 sb_index, + dma_addr_t pbl_addr, + u16 pbl_size, void __iomem **pp_doorbell); + +/** + * @brief VF - stop the RX queue by sending a message to the PF + * + * @param p_hwfn + * @param rx_qid + * @param cqe_completion + * + * @return int + */ +int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, + u16 rx_qid, bool cqe_completion); + +/** + * @brief VF - stop the TX queue by sending a message to the PF + * + * @param p_hwfn + * @param tx_qid + * + * @return int + */ +int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid); + +/** + * @brief VF - send a vport update command + * + * @param p_hwfn + * @param params + * + * @return int + */ +int qed_vf_pf_vport_update(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_params); + +/** + * + * @brief VF - send a close message to PF + * + * @param p_hwfn + * + * @return enum _qed_status + */ +int qed_vf_pf_reset(struct qed_hwfn *p_hwfn); + +/** + * @brief VF - free vf`s memories + * + * @param p_hwfn + * + * @return enum _qed_status + */ +int qed_vf_pf_release(struct qed_hwfn *p_hwfn); + +/** + * @brief qed_vf_get_igu_sb_id - Get the IGU SB ID for a given + * sb_id. For VFs igu sbs don't have to be contiguous + * + * @param p_hwfn + * @param sb_id + * + * @return INLINE u16 + */ +u16 qed_vf_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id); + +/** + * @brief qed_vf_pf_vport_start - perform vport start for VF. + * + * @param p_hwfn + * @param vport_id + * @param mtu + * @param inner_vlan_removal + * @param tpa_mode + * @param max_buffers_per_cqe, + * @param only_untagged - default behavior regarding vlan acceptance + * + * @return enum _qed_status + */ +int qed_vf_pf_vport_start(struct qed_hwfn *p_hwfn, + u8 vport_id, + u16 mtu, + u8 inner_vlan_removal, + enum qed_tpa_mode tpa_mode, + u8 max_buffers_per_cqe, u8 only_untagged); + +/** + * @brief qed_vf_pf_vport_stop - stop the VF's vport + * + * @param p_hwfn + * + * @return enum _qed_status + */ +int qed_vf_pf_vport_stop(struct qed_hwfn *p_hwfn); + +int qed_vf_pf_filter_ucast(struct qed_hwfn *p_hwfn, + struct qed_filter_ucast *p_param); + +void qed_vf_pf_filter_mcast(struct qed_hwfn *p_hwfn, + struct qed_filter_mcast *p_filter_cmd); + +/** + * @brief qed_vf_pf_int_cleanup - clean the SB of the VF + * + * @param p_hwfn + * + * @return enum _qed_status + */ +int qed_vf_pf_int_cleanup(struct qed_hwfn *p_hwfn); + +/** + * @brief - return the link params in a given bulletin board + * + * @param p_hwfn + * @param p_params - pointer to a struct to fill with link params + * @param p_bulletin + */ +void __qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params *p_params, + struct qed_bulletin_content *p_bulletin); + +/** + * @brief - return the link state in a given bulletin board + * + * @param p_hwfn + * @param p_link - pointer to a struct to fill with link state + * @param p_bulletin + */ +void __qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *p_link, + struct qed_bulletin_content *p_bulletin); + +/** + * @brief - return the link capabilities in a given bulletin board + * + * @param p_hwfn + * @param p_link - pointer to a struct to fill with link capabilities + * @param p_bulletin + */ +void __qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps, + struct qed_bulletin_content *p_bulletin); + +void qed_iov_vf_task(struct work_struct *work); +#else +static inline void qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params *params) +{ +} + +static inline void qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *link) +{ +} + +static inline void +qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps) +{ +} + +static inline void qed_vf_get_num_rxqs(struct qed_hwfn *p_hwfn, u8 *num_rxqs) +{ +} + +static inline void qed_vf_get_port_mac(struct qed_hwfn *p_hwfn, u8 *port_mac) +{ +} + +static inline void qed_vf_get_num_vlan_filters(struct qed_hwfn *p_hwfn, + u8 *num_vlan_filters) +{ +} + +static inline bool qed_vf_check_mac(struct qed_hwfn *p_hwfn, u8 *mac) +{ + return false; +} + +static inline void qed_vf_get_fw_version(struct qed_hwfn *p_hwfn, + u16 *fw_major, u16 *fw_minor, + u16 *fw_rev, u16 *fw_eng) +{ +} + +static inline int qed_vf_hw_prepare(struct qed_hwfn *p_hwfn) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, + u8 rx_queue_id, + u16 sb, + u8 sb_index, + u16 bd_max_bytes, + dma_addr_t bd_chain_phys_adr, + dma_addr_t cqe_pbl_addr, + u16 cqe_pbl_size, void __iomem **pp_prod) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn, + u16 tx_queue_id, + u16 sb, + u8 sb_index, + dma_addr_t pbl_addr, + u16 pbl_size, void __iomem **pp_doorbell) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_rxq_stop(struct qed_hwfn *p_hwfn, + u16 rx_qid, bool cqe_completion) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_txq_stop(struct qed_hwfn *p_hwfn, u16 tx_qid) +{ + return -EINVAL; +} + +static inline int +qed_vf_pf_vport_update(struct qed_hwfn *p_hwfn, + struct qed_sp_vport_update_params *p_params) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_reset(struct qed_hwfn *p_hwfn) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_release(struct qed_hwfn *p_hwfn) +{ + return -EINVAL; +} + +static inline u16 qed_vf_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) +{ + return 0; +} + +static inline int qed_vf_pf_vport_start(struct qed_hwfn *p_hwfn, + u8 vport_id, + u16 mtu, + u8 inner_vlan_removal, + enum qed_tpa_mode tpa_mode, + u8 max_buffers_per_cqe, + u8 only_untagged) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_vport_stop(struct qed_hwfn *p_hwfn) +{ + return -EINVAL; +} + +static inline int qed_vf_pf_filter_ucast(struct qed_hwfn *p_hwfn, + struct qed_filter_ucast *p_param) +{ + return -EINVAL; +} + +static inline void qed_vf_pf_filter_mcast(struct qed_hwfn *p_hwfn, + struct qed_filter_mcast *p_filter_cmd) +{ +} + +static inline int qed_vf_pf_int_cleanup(struct qed_hwfn *p_hwfn) +{ + return -EINVAL; +} + +static inline void __qed_vf_get_link_params(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_params + *p_params, + struct qed_bulletin_content + *p_bulletin) +{ +} + +static inline void __qed_vf_get_link_state(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_state *p_link, + struct qed_bulletin_content + *p_bulletin) +{ +} + +static inline void +__qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, + struct qed_mcp_link_capabilities *p_link_caps, + struct qed_bulletin_content *p_bulletin) +{ +} + +static inline void qed_iov_vf_task(struct work_struct *work) +{ +} +#endif + +#endif diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index ff3ac0caad5b..47d6b22252f6 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -112,6 +112,10 @@ struct qede_dev { u32 dp_module; u8 dp_level; + u32 flags; +#define QEDE_FLAG_IS_VF BIT(0) +#define IS_VF(edev) (!!((edev)->flags & QEDE_FLAG_IS_VF)) + const struct qed_eth_ops *ops; struct qed_dev_eth_info dev_info; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 0d04f163ae45..1bc75358cbc4 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -151,6 +151,8 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) int i, j, k; for (i = 0, j = 0; i < QEDE_NUM_STATS; i++) { + if (IS_VF(edev) && qede_stats_arr[i].pf_only) + continue; strcpy(buf + j * ETH_GSTRING_LEN, qede_stats_arr[i].string); j++; @@ -194,8 +196,11 @@ static void qede_get_ethtool_stats(struct net_device *dev, mutex_lock(&edev->qede_lock); - for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++) + for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++) { + if (IS_VF(edev) && qede_stats_arr[sidx].pf_only) + continue; buf[cnt++] = QEDE_STATS_DATA(edev, sidx); + } for (sidx = 0; sidx < QEDE_NUM_RQSTATS; sidx++) { buf[cnt] = 0; @@ -214,6 +219,13 @@ static int qede_get_sset_count(struct net_device *dev, int stringset) switch (stringset) { case ETH_SS_STATS: + if (IS_VF(edev)) { + int i; + + for (i = 0; i < QEDE_NUM_STATS; i++) + if (qede_stats_arr[i].pf_only) + num_stats--; + } return num_stats + QEDE_NUM_RQSTATS; case ETH_SS_PRIV_FLAGS: return QEDE_PRI_FLAG_LEN; @@ -1142,7 +1154,34 @@ static const struct ethtool_ops qede_ethtool_ops = { .self_test = qede_self_test, }; +static const struct ethtool_ops qede_vf_ethtool_ops = { + .get_settings = qede_get_settings, + .get_drvinfo = qede_get_drvinfo, + .get_msglevel = qede_get_msglevel, + .set_msglevel = qede_set_msglevel, + .get_link = qede_get_link, + .get_ringparam = qede_get_ringparam, + .set_ringparam = qede_set_ringparam, + .get_strings = qede_get_strings, + .get_ethtool_stats = qede_get_ethtool_stats, + .get_priv_flags = qede_get_priv_flags, + .get_sset_count = qede_get_sset_count, + .get_rxnfc = qede_get_rxnfc, + .set_rxnfc = qede_set_rxnfc, + .get_rxfh_indir_size = qede_get_rxfh_indir_size, + .get_rxfh_key_size = qede_get_rxfh_key_size, + .get_rxfh = qede_get_rxfh, + .set_rxfh = qede_set_rxfh, + .get_channels = qede_get_channels, + .set_channels = qede_set_channels, +}; + void qede_set_ethtool_ops(struct net_device *dev) { - dev->ethtool_ops = &qede_ethtool_ops; + struct qede_dev *edev = netdev_priv(dev); + + if (IS_VF(edev)) + dev->ethtool_ops = &qede_vf_ethtool_ops; + else + dev->ethtool_ops = &qede_ethtool_ops; } diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 82d85ccc9ed1..901a9acc2d2d 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -63,6 +63,7 @@ static const struct qed_eth_ops *qed_ops; #define CHIP_NUM_57980S_100 0x1644 #define CHIP_NUM_57980S_50 0x1654 #define CHIP_NUM_57980S_25 0x1656 +#define CHIP_NUM_57980S_IOV 0x1664 #ifndef PCI_DEVICE_ID_NX2_57980E #define PCI_DEVICE_ID_57980S_40 CHIP_NUM_57980S_40 @@ -71,15 +72,22 @@ static const struct qed_eth_ops *qed_ops; #define PCI_DEVICE_ID_57980S_100 CHIP_NUM_57980S_100 #define PCI_DEVICE_ID_57980S_50 CHIP_NUM_57980S_50 #define PCI_DEVICE_ID_57980S_25 CHIP_NUM_57980S_25 +#define PCI_DEVICE_ID_57980S_IOV CHIP_NUM_57980S_IOV #endif +enum qede_pci_private { + QEDE_PRIVATE_PF, + QEDE_PRIVATE_VF +}; + static const struct pci_device_id qede_pci_tbl[] = { - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), 0 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), 0 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), 0 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), 0 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), 0 }, - { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), 0 }, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), QEDE_PRIVATE_PF}, + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_IOV), QEDE_PRIVATE_VF}, { 0 } }; @@ -94,17 +102,87 @@ static int qede_alloc_rx_buffer(struct qede_dev *edev, struct qede_rx_queue *rxq); static void qede_link_update(void *dev, struct qed_link_output *link); +#ifdef CONFIG_QED_SRIOV +static int qede_set_vf_vlan(struct net_device *ndev, int vf, u16 vlan, u8 qos) +{ + struct qede_dev *edev = netdev_priv(ndev); + + if (vlan > 4095) { + DP_NOTICE(edev, "Illegal vlan value %d\n", vlan); + return -EINVAL; + } + + DP_VERBOSE(edev, QED_MSG_IOV, "Setting Vlan 0x%04x to VF [%d]\n", + vlan, vf); + + return edev->ops->iov->set_vlan(edev->cdev, vlan, vf); +} + +static int qede_set_vf_mac(struct net_device *ndev, int vfidx, u8 *mac) +{ + struct qede_dev *edev = netdev_priv(ndev); + + DP_VERBOSE(edev, QED_MSG_IOV, + "Setting MAC %02x:%02x:%02x:%02x:%02x:%02x to VF [%d]\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], vfidx); + + if (!is_valid_ether_addr(mac)) { + DP_VERBOSE(edev, QED_MSG_IOV, "MAC address isn't valid\n"); + return -EINVAL; + } + + return edev->ops->iov->set_mac(edev->cdev, mac, vfidx); +} + +static int qede_sriov_configure(struct pci_dev *pdev, int num_vfs_param) +{ + struct qede_dev *edev = netdev_priv(pci_get_drvdata(pdev)); + struct qed_dev_info *qed_info = &edev->dev_info.common; + int rc; + + DP_VERBOSE(edev, QED_MSG_IOV, "Requested %d VFs\n", num_vfs_param); + + rc = edev->ops->iov->configure(edev->cdev, num_vfs_param); + + /* Enable/Disable Tx switching for PF */ + if ((rc == num_vfs_param) && netif_running(edev->ndev) && + qed_info->mf_mode != QED_MF_NPAR && qed_info->tx_switching) { + struct qed_update_vport_params params; + + memset(¶ms, 0, sizeof(params)); + params.vport_id = 0; + params.update_tx_switching_flg = 1; + params.tx_switching_flg = num_vfs_param ? 1 : 0; + edev->ops->vport_update(edev->cdev, ¶ms); + } + + return rc; +} +#endif + static struct pci_driver qede_pci_driver = { .name = "qede", .id_table = qede_pci_tbl, .probe = qede_probe, .remove = qede_remove, +#ifdef CONFIG_QED_SRIOV + .sriov_configure = qede_sriov_configure, +#endif }; +static void qede_force_mac(void *dev, u8 *mac) +{ + struct qede_dev *edev = dev; + + ether_addr_copy(edev->ndev->dev_addr, mac); + ether_addr_copy(edev->primary_mac, mac); +} + static struct qed_eth_cb_ops qede_ll_ops = { { .link_update = qede_link_update, }, + .force_mac = qede_force_mac, }; static int qede_netdev_event(struct notifier_block *this, unsigned long event, @@ -429,7 +507,7 @@ netdev_tx_t qede_start_xmit(struct sk_buff *skb, u8 xmit_type; u16 idx; u16 hlen; - bool data_split; + bool data_split = false; /* Get tx-queue context and netdev index */ txq_index = skb_get_queue_mapping(skb); @@ -998,8 +1076,7 @@ static void qede_tpa_start(struct qede_dev *edev, * start until its over and we don't want to risk allocation failing * here, so re-allocate when aggregation will be over. */ - dma_unmap_addr_set(sw_rx_data_prod, mapping, - dma_unmap_addr(replace_buf, mapping)); + sw_rx_data_prod->mapping = replace_buf->mapping; sw_rx_data_prod->data = replace_buf->data; rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(mapping)); @@ -1730,6 +1807,49 @@ static struct rtnl_link_stats64 *qede_get_stats64( return stats; } +#ifdef CONFIG_QED_SRIOV +static int qede_get_vf_config(struct net_device *dev, int vfidx, + struct ifla_vf_info *ivi) +{ + struct qede_dev *edev = netdev_priv(dev); + + if (!edev->ops) + return -EINVAL; + + return edev->ops->iov->get_config(edev->cdev, vfidx, ivi); +} + +static int qede_set_vf_rate(struct net_device *dev, int vfidx, + int min_tx_rate, int max_tx_rate) +{ + struct qede_dev *edev = netdev_priv(dev); + + return edev->ops->iov->set_rate(edev->cdev, vfidx, max_tx_rate, + max_tx_rate); +} + +static int qede_set_vf_spoofchk(struct net_device *dev, int vfidx, bool val) +{ + struct qede_dev *edev = netdev_priv(dev); + + if (!edev->ops) + return -EINVAL; + + return edev->ops->iov->set_spoof(edev->cdev, vfidx, val); +} + +static int qede_set_vf_link_state(struct net_device *dev, int vfidx, + int link_state) +{ + struct qede_dev *edev = netdev_priv(dev); + + if (!edev->ops) + return -EINVAL; + + return edev->ops->iov->set_link_state(edev->cdev, vfidx, link_state); +} +#endif + static void qede_config_accept_any_vlan(struct qede_dev *edev, bool action) { struct qed_update_vport_params params; @@ -2049,9 +2169,19 @@ static const struct net_device_ops qede_netdev_ops = { .ndo_set_mac_address = qede_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = qede_change_mtu, +#ifdef CONFIG_QED_SRIOV + .ndo_set_vf_mac = qede_set_vf_mac, + .ndo_set_vf_vlan = qede_set_vf_vlan, +#endif .ndo_vlan_rx_add_vid = qede_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = qede_vlan_rx_kill_vid, .ndo_get_stats64 = qede_get_stats64, +#ifdef CONFIG_QED_SRIOV + .ndo_set_vf_link_state = qede_set_vf_link_state, + .ndo_set_vf_spoofchk = qede_set_vf_spoofchk, + .ndo_get_vf_config = qede_get_vf_config, + .ndo_set_vf_rate = qede_set_vf_rate, +#endif #ifdef CONFIG_QEDE_VXLAN .ndo_add_vxlan_port = qede_add_vxlan_port, .ndo_del_vxlan_port = qede_del_vxlan_port, @@ -2094,8 +2224,6 @@ static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev, edev->q_num_rx_buffers = NUM_RX_BDS_DEF; edev->q_num_tx_buffers = NUM_TX_BDS_DEF; - DP_INFO(edev, "Allocated netdev with 64 tx queues and 64 rx queues\n"); - SET_NETDEV_DEV(ndev, &pdev->dev); memset(&edev->stats, 0, sizeof(edev->stats)); @@ -2274,9 +2402,9 @@ static void qede_update_pf_params(struct qed_dev *cdev) { struct qed_pf_params pf_params; - /* 16 rx + 16 tx */ + /* 64 rx + 64 tx */ memset(&pf_params, 0, sizeof(struct qed_pf_params)); - pf_params.eth_pf_params.num_cons = 32; + pf_params.eth_pf_params.num_cons = 128; qed_ops->common->update_pf_params(cdev, &pf_params); } @@ -2285,8 +2413,9 @@ enum qede_probe_mode { }; static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, - enum qede_probe_mode mode) + bool is_vf, enum qede_probe_mode mode) { + struct qed_probe_params probe_params; struct qed_slowpath_params params; struct qed_dev_eth_info dev_info; struct qede_dev *edev; @@ -2296,8 +2425,12 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, if (unlikely(dp_level & QED_LEVEL_INFO)) pr_notice("Starting qede probe\n"); - cdev = qed_ops->common->probe(pdev, QED_PROTOCOL_ETH, - dp_module, dp_level); + memset(&probe_params, 0, sizeof(probe_params)); + probe_params.protocol = QED_PROTOCOL_ETH; + probe_params.dp_module = dp_module; + probe_params.dp_level = dp_level; + probe_params.is_vf = is_vf; + cdev = qed_ops->common->probe(pdev, &probe_params); if (!cdev) { rc = -ENODEV; goto err0; @@ -2331,6 +2464,9 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, goto err2; } + if (is_vf) + edev->flags |= QEDE_FLAG_IS_VF; + qede_init_ndev(edev); rc = register_netdev(edev->ndev); @@ -2362,12 +2498,24 @@ err0: static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + bool is_vf = false; u32 dp_module = 0; u8 dp_level = 0; + switch ((enum qede_pci_private)id->driver_data) { + case QEDE_PRIVATE_VF: + if (debug & QED_LOG_VERBOSE_MASK) + dev_err(&pdev->dev, "Probing a VF\n"); + is_vf = true; + break; + default: + if (debug & QED_LOG_VERBOSE_MASK) + dev_err(&pdev->dev, "Probing a PF\n"); + } + qede_config_debug(debug, &dp_module, &dp_level); - return __qede_probe(pdev, dp_module, dp_level, + return __qede_probe(pdev, dp_module, dp_level, is_vf, QEDE_PROBE_NORMAL); } @@ -2506,7 +2654,7 @@ static void qede_free_sge_mem(struct qede_dev *edev, if (replace_buf->data) { dma_unmap_page(&edev->pdev->dev, - dma_unmap_addr(replace_buf, mapping), + replace_buf->mapping, PAGE_SIZE, DMA_FROM_DEVICE); __free_page(replace_buf->data); } @@ -2606,7 +2754,7 @@ static int qede_alloc_sge_mem(struct qede_dev *edev, goto err; } - dma_unmap_addr_set(replace_buf, mapping, mapping); + replace_buf->mapping = mapping; tpa_info->replace_buf.page_offset = 0; tpa_info->replace_buf_mapping = mapping; @@ -3064,6 +3212,7 @@ static int qede_start_queues(struct qede_dev *edev) struct qed_dev *cdev = edev->cdev; struct qed_update_vport_params vport_update_params; struct qed_queue_start_common_params q_params; + struct qed_dev_info *qed_info = &edev->dev_info.common; struct qed_start_vport_params start = {0}; bool reset_rss_indir = false; @@ -3157,6 +3306,12 @@ static int qede_start_queues(struct qede_dev *edev) vport_update_params.update_vport_active_flg = 1; vport_update_params.vport_active_flg = 1; + if ((qed_info->mf_mode == QED_MF_NPAR || pci_num_vf(edev->pdev)) && + qed_info->tx_switching) { + vport_update_params.update_tx_switching_flg = 1; + vport_update_params.tx_switching_flg = 1; + } + /* Fill struct with RSS params */ if (QEDE_RSS_CNT(edev) > 1) { vport_update_params.update_rss_flg = 1; @@ -3453,6 +3608,11 @@ static int qede_set_mac_addr(struct net_device *ndev, void *p) return -EFAULT; } + if (!edev->ops->check_mac(edev->cdev, addr->sa_data)) { + DP_NOTICE(edev, "qed prevents setting MAC\n"); + return -EINVAL; + } + ether_addr_copy(ndev->dev_addr, addr->sa_data); if (!netif_running(ndev)) { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index cda9e604a95f..0844b7c75767 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1417,6 +1417,7 @@ void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter) struct qlcnic_fw_dump *fw_dump = &ahw->fw_dump; struct pci_dev *pdev = adapter->pdev; bool extended = false; + int ret; prev_version = adapter->fw_version; current_version = qlcnic_83xx_get_fw_version(adapter); @@ -1427,8 +1428,11 @@ void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter) if (qlcnic_83xx_md_check_extended_dump_capability(adapter)) extended = !qlcnic_83xx_extend_md_capab(adapter); - if (!qlcnic_fw_cmd_get_minidump_temp(adapter)) - dev_info(&pdev->dev, "Supports FW dump capability\n"); + ret = qlcnic_fw_cmd_get_minidump_temp(adapter); + if (ret) + return; + + dev_info(&pdev->dev, "Supports FW dump capability\n"); /* Once we have minidump template with extended iSCSI dump * capability, update the minidump capture mask to 0x1f as diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c index 1ef03939d25f..6e2add979471 100644 --- a/drivers/net/ethernet/qualcomm/qca_spi.c +++ b/drivers/net/ethernet/qualcomm/qca_spi.c @@ -719,7 +719,7 @@ qcaspi_netdev_xmit(struct sk_buff *skb, struct net_device *dev) qca->stats.ring_full++; } - dev->trans_start = jiffies; + netif_trans_update(dev); if (qca->spi_thread && qca->spi_thread->state != TASK_RUNNING) @@ -734,7 +734,7 @@ qcaspi_netdev_tx_timeout(struct net_device *dev) struct qcaspi *qca = netdev_priv(dev); netdev_info(qca->net_dev, "Transmit timeout at %ld, latency %ld\n", - jiffies, jiffies - dev->trans_start); + jiffies, jiffies - dev_trans_start(dev)); qca->net_dev->stats.tx_errors++; /* Trigger tx queue flush and QCA7000 reset */ qca->sync = QCASPI_SYNC_UNKNOWN; diff --git a/drivers/net/ethernet/realtek/atp.c b/drivers/net/ethernet/realtek/atp.c index d77d60ea8202..5cb96785fb63 100644 --- a/drivers/net/ethernet/realtek/atp.c +++ b/drivers/net/ethernet/realtek/atp.c @@ -544,7 +544,7 @@ static void tx_timeout(struct net_device *dev) dev->stats.tx_errors++; /* Try to restart the adapter. */ hardware_init(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); dev->stats.tx_errors++; } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 94f08f1e841c..0e62d74b09b3 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -345,7 +345,7 @@ static const struct pci_device_id rtl8169_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); static int rx_buf_sz = 16383; -static int use_dac; +static int use_dac = -1; static struct { u32 msg_enable; } debug = { -1 }; @@ -8224,20 +8224,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_mwi_2; } - tp->cp_cmd = 0; - - if ((sizeof(dma_addr_t) > 4) && - !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && use_dac) { - tp->cp_cmd |= PCIDAC; - dev->features |= NETIF_F_HIGHDMA; - } else { - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (rc < 0) { - netif_err(tp, probe, dev, "DMA configuration failed\n"); - goto err_out_free_res_3; - } - } - /* ioremap MMIO region */ ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE); if (!ioaddr) { @@ -8253,6 +8239,25 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* Identify chip attached to board */ rtl8169_get_mac_version(tp, dev, cfg->default_ver); + tp->cp_cmd = 0; + + if ((sizeof(dma_addr_t) > 4) && + (use_dac == 1 || (use_dac == -1 && pci_is_pcie(pdev) && + tp->mac_version >= RTL_GIGA_MAC_VER_18)) && + !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + + /* CPlusCmd Dual Access Cycle is only needed for non-PCIe */ + if (!pci_is_pcie(pdev)) + tp->cp_cmd |= PCIDAC; + dev->features |= NETIF_F_HIGHDMA; + } else { + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (rc < 0) { + netif_err(tp, probe, dev, "DMA configuration failed\n"); + goto err_out_unmap_4; + } + } + rtl_init_rxcfg(tp); rtl_irq_disable(tp); @@ -8412,12 +8417,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) &tp->counters_phys_addr, GFP_KERNEL); if (!tp->counters) { rc = -ENOMEM; - goto err_out_msi_4; + goto err_out_msi_5; } rc = register_netdev(dev); if (rc < 0) - goto err_out_cnt_5; + goto err_out_cnt_6; pci_set_drvdata(pdev, dev); @@ -8451,12 +8456,13 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) out: return rc; -err_out_cnt_5: +err_out_cnt_6: dma_free_coherent(&pdev->dev, sizeof(*tp->counters), tp->counters, tp->counters_phys_addr); -err_out_msi_4: +err_out_msi_5: netif_napi_del(&tp->napi); rtl_disable_msi(pdev, tp); +err_out_unmap_4: iounmap(ioaddr); err_out_free_res_3: pci_release_regions(pdev); diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 238b56feb1ce..867caf6e7a5a 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -246,10 +246,9 @@ static void ravb_ring_format(struct net_device *ndev, int q) for (i = 0; i < priv->num_rx_ring[q]; i++) { /* RX descriptor */ rx_desc = &priv->rx_ring[q][i]; - /* The size of the buffer should be on 16-byte boundary. */ - rx_desc->ds_cc = cpu_to_le16(ALIGN(PKT_BUF_SZ, 16)); + rx_desc->ds_cc = cpu_to_le16(PKT_BUF_SZ); dma_addr = dma_map_single(ndev->dev.parent, priv->rx_skb[q][i]->data, - ALIGN(PKT_BUF_SZ, 16), + PKT_BUF_SZ, DMA_FROM_DEVICE); /* We just set the data size to 0 for a failed mapping which * should prevent DMA from happening... @@ -558,7 +557,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) skb = priv->rx_skb[q][entry]; priv->rx_skb[q][entry] = NULL; dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), - ALIGN(PKT_BUF_SZ, 16), + PKT_BUF_SZ, DMA_FROM_DEVICE); get_ts &= (q == RAVB_NC) ? RAVB_RXTSTAMP_TYPE_V2_L2_EVENT : @@ -588,8 +587,7 @@ static bool ravb_rx(struct net_device *ndev, int *quota, int q) for (; priv->cur_rx[q] - priv->dirty_rx[q] > 0; priv->dirty_rx[q]++) { entry = priv->dirty_rx[q] % priv->num_rx_ring[q]; desc = &priv->rx_ring[q][entry]; - /* The size of the buffer should be on 16-byte boundary. */ - desc->ds_cc = cpu_to_le16(ALIGN(PKT_BUF_SZ, 16)); + desc->ds_cc = cpu_to_le16(PKT_BUF_SZ); if (!priv->rx_skb[q][entry]) { skb = netdev_alloc_skb(ndev, @@ -1669,6 +1667,13 @@ static int ravb_close(struct net_device *ndev) priv->phydev = NULL; } + if (priv->chip_id != RCAR_GEN2) { + free_irq(priv->tx_irqs[RAVB_NC], ndev); + free_irq(priv->rx_irqs[RAVB_NC], ndev); + free_irq(priv->tx_irqs[RAVB_BE], ndev); + free_irq(priv->rx_irqs[RAVB_BE], ndev); + free_irq(priv->emac_irq, ndev); + } free_irq(ndev->irq, ndev); napi_disable(&priv->napi[RAVB_NC]); diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 07e29638299f..04cd39f66cc9 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -537,11 +537,7 @@ static struct sh_eth_cpu_data r7s72100_data = { static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) { - struct sh_eth_private *mdp = netdev_priv(ndev); - - /* reset device */ - sh_eth_tsu_write(mdp, ARSTR_ARST, ARSTR); - mdelay(1); + sh_eth_chip_reset(ndev); sh_eth_select_mii(ndev); } @@ -725,8 +721,8 @@ static struct sh_eth_cpu_data sh7757_data = { #define GIGA_MAHR(port) (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c0) static void sh_eth_chip_reset_giga(struct net_device *ndev) { - int i; u32 mahr[2], malr[2]; + int i; /* save MAHR and MALR */ for (i = 0; i < 2; i++) { @@ -734,9 +730,7 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev) mahr[i] = ioread32((void *)GIGA_MAHR(i)); } - /* reset device */ - iowrite32(ARSTR_ARST, (void *)(SH_GIGA_ETH_BASE + 0x1800)); - mdelay(1); + sh_eth_chip_reset(ndev); /* restore MAHR and MALR */ for (i = 0; i < 2; i++) { diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 0e758bcb26b0..1ca796316173 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -2727,7 +2727,7 @@ static int ofdpa_port_obj_fib4_add(struct rocker_port *rocker_port, return ofdpa_port_fib_ipv4(ofdpa_port, trans, htonl(fib4->dst), fib4->dst_len, - &fib4->fi, fib4->tb_id, 0); + fib4->fi, fib4->tb_id, 0); } static int ofdpa_port_obj_fib4_del(struct rocker_port *rocker_port, @@ -2737,7 +2737,7 @@ static int ofdpa_port_obj_fib4_del(struct rocker_port *rocker_port, return ofdpa_port_fib_ipv4(ofdpa_port, NULL, htonl(fib4->dst), fib4->dst_len, - &fib4->fi, fib4->tb_id, + fib4->fi, fib4->tb_id, OFDPA_OP_FLAG_REMOVE); } diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index ca7336605748..c2bd5378ffda 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -572,7 +572,7 @@ static inline int sgiseeq_reset(struct net_device *dev) if (err) return err; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); return 0; @@ -648,7 +648,7 @@ static void timeout(struct net_device *dev) printk(KERN_NOTICE "%s: transmit timed out, resetting\n", dev->name); sgiseeq_reset(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 98d33d462c6c..1681084cc96f 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1920,6 +1920,10 @@ static int efx_ef10_alloc_rss_context(struct efx_nic *efx, u32 *context, return 0; } + if (nic_data->datapath_caps & + 1 << MC_CMD_GET_CAPABILITIES_OUT_RX_RSS_LIMITED_LBN) + return -EOPNOTSUPP; + MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_UPSTREAM_PORT_ID, nic_data->vport_id); MCDI_SET_DWORD(inbuf, RSS_CONTEXT_ALLOC_IN_TYPE, alloc_type); @@ -2923,9 +2927,16 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx, bool replacing) { struct efx_ef10_nic_data *nic_data = efx->nic_data; + u32 flags = spec->flags; memset(inbuf, 0, MC_CMD_FILTER_OP_IN_LEN); + /* Remove RSS flag if we don't have an RSS context. */ + if (flags & EFX_FILTER_FLAG_RX_RSS && + spec->rss_context == EFX_FILTER_RSS_CONTEXT_DEFAULT && + nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID) + flags &= ~EFX_FILTER_FLAG_RX_RSS; + if (replacing) { MCDI_SET_DWORD(inbuf, FILTER_OP_IN_OP, MC_CMD_FILTER_OP_IN_OP_REPLACE); @@ -2985,10 +2996,10 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx, spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP ? 0 : spec->dmaq_id); MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_MODE, - (spec->flags & EFX_FILTER_FLAG_RX_RSS) ? + (flags & EFX_FILTER_FLAG_RX_RSS) ? MC_CMD_FILTER_OP_IN_RX_MODE_RSS : MC_CMD_FILTER_OP_IN_RX_MODE_SIMPLE); - if (spec->flags & EFX_FILTER_FLAG_RX_RSS) + if (flags & EFX_FILTER_FLAG_RX_RSS) MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_CONTEXT, spec->rss_context != EFX_FILTER_RSS_CONTEXT_DEFAULT ? diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index 5eac523b4b0c..aaa80f13859b 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -708,7 +708,7 @@ static int meth_tx(struct sk_buff *skb, struct net_device *dev) mace->eth.dma_ctrl = priv->dma_ctrl; meth_add_to_tx_ring(priv, skb); - dev->trans_start = jiffies; /* save the timestamp */ + netif_trans_update(dev); /* save the timestamp */ /* If TX ring is full, tell the upper layer to stop sending packets */ if (meth_tx_full(dev)) { @@ -756,7 +756,7 @@ static void meth_tx_timeout(struct net_device *dev) /* Enable interrupt */ spin_unlock_irqrestore(&priv->meth_lock, flags); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index fd812d2e5e1c..95001ee408ab 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -1575,7 +1575,7 @@ static void sis900_tx_timeout(struct net_device *net_dev) spin_unlock_irqrestore(&sis_priv->lock, flags); - net_dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(net_dev); /* prevent tx timeout */ /* load Transmit Descriptor Register */ sw32(txdp, sis_priv->tx_ring_dma); diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 443f1da9fc9e..7186b89269ad 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -889,7 +889,7 @@ static void epic_tx_timeout(struct net_device *dev) ew32(COMMAND, TxQueued); } - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; if (!ep->tx_full) netif_wake_queue(dev); diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index a733868a43aa..cb49c9654f0a 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -499,7 +499,7 @@ static void smc911x_hardware_send_pkt(struct net_device *dev) /* DMA complete IRQ will free buffer and set jiffies */ #else SMC_PUSH_DATA(lp, buf, len); - dev->trans_start = jiffies; + netif_trans_update(dev); dev_kfree_skb_irq(skb); #endif if (!lp->tx_throttle) { @@ -1189,7 +1189,7 @@ smc911x_tx_dma_irq(void *data) DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "TX DMA irq handler\n"); BUG_ON(skb == NULL); dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE); - dev->trans_start = jiffies; + netif_trans_update(dev); dev_kfree_skb_irq(skb); lp->current_tx_skb = NULL; if (lp->pending_tx_skb != NULL) @@ -1283,7 +1283,7 @@ static void smc911x_timeout(struct net_device *dev) schedule_work(&lp->phy_configure); /* We can accept TX packets again */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c index 664f596971b5..d496888b85d3 100644 --- a/drivers/net/ethernet/smsc/smc9194.c +++ b/drivers/net/ethernet/smsc/smc9194.c @@ -663,7 +663,7 @@ static void smc_hardware_send_packet( struct net_device * dev ) lp->saved_skb = NULL; dev_kfree_skb_any (skb); - dev->trans_start = jiffies; + netif_trans_update(dev); /* we can send another packet */ netif_wake_queue(dev); @@ -1104,7 +1104,7 @@ static void smc_timeout(struct net_device *dev) /* "kick" the adaptor */ smc_reset( dev->base_addr ); smc_enable( dev->base_addr ); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ /* clear anything saved */ ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL; netif_wake_queue(dev); diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 3449893aea8d..db3c696d7002 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -1172,7 +1172,7 @@ static void smc_hardware_send_packet(struct net_device * dev) smc->saved_skb = NULL; dev_kfree_skb_irq(skb); - dev->trans_start = jiffies; + netif_trans_update(dev); netif_start_queue(dev); } @@ -1187,7 +1187,7 @@ static void smc_tx_timeout(struct net_device *dev) inw(ioaddr)&0xff, inw(ioaddr + 2)); dev->stats.tx_errors++; smc_reset(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ smc->saved_skb = NULL; netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index c5ed27c54724..18ac52ded696 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -619,7 +619,7 @@ static void smc_hardware_send_pkt(unsigned long data) SMC_SET_MMU_CMD(lp, MC_ENQUEUE); smc_special_unlock(&lp->lock, flags); - dev->trans_start = jiffies; + netif_trans_update(dev); dev->stats.tx_packets++; dev->stats.tx_bytes += len; @@ -1364,7 +1364,7 @@ static void smc_timeout(struct net_device *dev) schedule_work(&lp->phy_configure); /* We can accept TX packets again */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index 784eb53361b5..f13499fa1f58 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -49,6 +49,7 @@ struct socfpga_dwmac { u32 reg_shift; struct device *dev; struct regmap *sys_mgr_base_addr; + struct reset_control *stmmac_rst; void __iomem *splitter_base; bool f2h_ptp_ref_clk; }; @@ -135,7 +136,7 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * return 0; } -static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) +static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) { struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr; int phymode = dwmac->interface; @@ -164,6 +165,10 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) if (dwmac->splitter_base) val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; + /* Assert reset to the enet controller before changing the phy mode */ + if (dwmac->stmmac_rst) + reset_control_assert(dwmac->stmmac_rst); + regmap_read(sys_mgr_base_addr, reg_offset, &ctrl); ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); ctrl |= val << reg_shift; @@ -181,57 +186,13 @@ static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) regmap_write(sys_mgr_base_addr, reg_offset, ctrl); - return 0; -} - -static int socfpga_dwmac_init(struct platform_device *pdev, void *priv) -{ - struct socfpga_dwmac *dwmac = priv; - struct net_device *ndev = platform_get_drvdata(pdev); - struct stmmac_priv *stpriv = NULL; - int ret = 0; - - if (!ndev) - return -EINVAL; - - stpriv = netdev_priv(ndev); - if (!stpriv) - return -EINVAL; - - /* Assert reset to the enet controller before changing the phy mode */ - if (stpriv->stmmac_rst) - reset_control_assert(stpriv->stmmac_rst); - - /* Setup the phy mode in the system manager registers according to - * devicetree configuration - */ - ret = socfpga_dwmac_setup(dwmac); - /* Deassert reset for the phy configuration to be sampled by * the enet controller, and operation to start in requested mode */ - if (stpriv->stmmac_rst) - reset_control_deassert(stpriv->stmmac_rst); - - /* Before the enet controller is suspended, the phy is suspended. - * This causes the phy clock to be gated. The enet controller is - * resumed before the phy, so the clock is still gated "off" when - * the enet controller is resumed. This code makes sure the phy - * is "resumed" before reinitializing the enet controller since - * the enet controller depends on an active phy clock to complete - * a DMA reset. A DMA reset will "time out" if executed - * with no phy clock input on the Synopsys enet controller. - * Verified through Synopsys Case #8000711656. - * - * Note that the phy clock is also gated when the phy is isolated. - * Phy "suspend" and "isolate" controls are located in phy basic - * control register 0, and can be modified by the phy driver - * framework. - */ - if (stpriv->phydev) - phy_resume(stpriv->phydev); + if (dwmac->stmmac_rst) + reset_control_deassert(dwmac->stmmac_rst); - return ret; + return 0; } static int socfpga_dwmac_probe(struct platform_device *pdev) @@ -261,16 +222,58 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) } plat_dat->bsp_priv = dwmac; - plat_dat->init = socfpga_dwmac_init; plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - if (!ret) - ret = socfpga_dwmac_init(pdev, dwmac); + if (!ret) { + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *stpriv = netdev_priv(ndev); + + /* The socfpga driver needs to control the stmmac reset to + * set the phy mode. Create a copy of the core reset handel + * so it can be used by the driver later. + */ + dwmac->stmmac_rst = stpriv->stmmac_rst; + + ret = socfpga_dwmac_set_phy_mode(dwmac); + } return ret; } +#ifdef CONFIG_PM_SLEEP +static int socfpga_dwmac_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct stmmac_priv *priv = netdev_priv(ndev); + + socfpga_dwmac_set_phy_mode(priv->plat->bsp_priv); + + /* Before the enet controller is suspended, the phy is suspended. + * This causes the phy clock to be gated. The enet controller is + * resumed before the phy, so the clock is still gated "off" when + * the enet controller is resumed. This code makes sure the phy + * is "resumed" before reinitializing the enet controller since + * the enet controller depends on an active phy clock to complete + * a DMA reset. A DMA reset will "time out" if executed + * with no phy clock input on the Synopsys enet controller. + * Verified through Synopsys Case #8000711656. + * + * Note that the phy clock is also gated when the phy is isolated. + * Phy "suspend" and "isolate" controls are located in phy basic + * control register 0, and can be modified by the phy driver + * framework. + */ + if (priv->phydev) + phy_resume(priv->phydev); + + return stmmac_resume(dev); +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(socfpga_dwmac_pm_ops, stmmac_suspend, + socfpga_dwmac_resume); + static const struct of_device_id socfpga_dwmac_match[] = { { .compatible = "altr,socfpga-stmmac" }, { } @@ -282,7 +285,7 @@ static struct platform_driver socfpga_dwmac_driver = { .remove = stmmac_pltfr_remove, .driver = { .name = "socfpga-dwmac", - .pm = &stmmac_pltfr_pm_ops, + .pm = &socfpga_dwmac_pm_ops, .of_match_table = socfpga_dwmac_match, }, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index ff6750621ff7..59ae6088cd22 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -148,9 +148,9 @@ void stmmac_set_ethtool_ops(struct net_device *netdev); int stmmac_ptp_register(struct stmmac_priv *priv); void stmmac_ptp_unregister(struct stmmac_priv *priv); -int stmmac_resume(struct net_device *ndev); -int stmmac_suspend(struct net_device *ndev); -int stmmac_dvr_remove(struct net_device *ndev); +int stmmac_resume(struct device *dev); +int stmmac_suspend(struct device *dev); +int stmmac_dvr_remove(struct device *dev); int stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, struct stmmac_resources *res); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b87edb72e80a..eac45d0c75e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3131,7 +3131,12 @@ static int stmmac_hw_init(struct stmmac_priv *priv) priv->plat->enh_desc = priv->dma_cap.enh_desc; priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; - priv->plat->tx_coe = priv->dma_cap.tx_coe; + /* TXCOE doesn't work in thresh DMA mode */ + if (priv->plat->force_thresh_dma_mode) + priv->plat->tx_coe = 0; + else + priv->plat->tx_coe = priv->dma_cap.tx_coe; + /* In case of GMAC4 rx_coe is from HW cap register. */ priv->plat->rx_coe = priv->dma_cap.rx_coe; @@ -3350,12 +3355,13 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_probe); /** * stmmac_dvr_remove - * @ndev: net device pointer + * @dev: device pointer * Description: this function resets the TX/RX processes, disables the MAC RX/TX * changes the link status, releases the DMA descriptor rings. */ -int stmmac_dvr_remove(struct net_device *ndev) +int stmmac_dvr_remove(struct device *dev) { + struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); pr_info("%s:\n\tremoving driver", __func__); @@ -3381,13 +3387,14 @@ EXPORT_SYMBOL_GPL(stmmac_dvr_remove); /** * stmmac_suspend - suspend callback - * @ndev: net device pointer + * @dev: device pointer * Description: this is the function to suspend the device and it is called * by the platform driver to stop the network queue, release the resources, * program the PMT register (for WoL), clean and release driver resources. */ -int stmmac_suspend(struct net_device *ndev) +int stmmac_suspend(struct device *dev) { + struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); unsigned long flags; @@ -3430,12 +3437,13 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); /** * stmmac_resume - resume callback - * @ndev: net device pointer + * @dev: device pointer * Description: when resume this function is invoked to setup the DMA and CORE * in a usable state. */ -int stmmac_resume(struct net_device *ndev) +int stmmac_resume(struct device *dev) { + struct net_device *ndev = dev_get_drvdata(dev); struct stmmac_priv *priv = netdev_priv(ndev); unsigned long flags; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index ae4388735b7f..56c8a2342c14 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -231,30 +231,10 @@ static int stmmac_pci_probe(struct pci_dev *pdev, */ static void stmmac_pci_remove(struct pci_dev *pdev) { - struct net_device *ndev = pci_get_drvdata(pdev); - - stmmac_dvr_remove(ndev); -} - -#ifdef CONFIG_PM_SLEEP -static int stmmac_pci_suspend(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct net_device *ndev = pci_get_drvdata(pdev); - - return stmmac_suspend(ndev); -} - -static int stmmac_pci_resume(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct net_device *ndev = pci_get_drvdata(pdev); - - return stmmac_resume(ndev); + stmmac_dvr_remove(&pdev->dev); } -#endif -static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume); +static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_suspend, stmmac_resume); #define STMMAC_VENDOR_ID 0x700 #define STMMAC_QUARK_ID 0x0937 diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index effaa4ff5ab7..409db913b117 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -386,7 +386,7 @@ int stmmac_pltfr_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); - int ret = stmmac_dvr_remove(ndev); + int ret = stmmac_dvr_remove(&pdev->dev); if (priv->plat->exit) priv->plat->exit(pdev, priv->plat->bsp_priv); @@ -410,7 +410,7 @@ static int stmmac_pltfr_suspend(struct device *dev) struct stmmac_priv *priv = netdev_priv(ndev); struct platform_device *pdev = to_platform_device(dev); - ret = stmmac_suspend(ndev); + ret = stmmac_suspend(dev); if (priv->plat->exit) priv->plat->exit(pdev, priv->plat->bsp_priv); @@ -433,7 +433,7 @@ static int stmmac_pltfr_resume(struct device *dev) if (priv->plat->init) priv->plat->init(pdev, priv->plat->bsp_priv); - return stmmac_resume(ndev); + return stmmac_resume(dev); } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 9cc45649f477..a2371aa14a49 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -6431,7 +6431,7 @@ static int niu_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void niu_netif_stop(struct niu *np) { - np->dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(np->dev); /* prevent tx timeout */ niu_disable_napi(np); diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 2437227712dc..d6ad0fbd054e 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -226,7 +226,7 @@ static void gem_put_cell(struct gem *gp) static inline void gem_netif_stop(struct gem *gp) { - gp->dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(gp->dev); /* prevent tx timeout */ napi_disable(&gp->napi); netif_tx_disable(gp->dev); } diff --git a/drivers/net/ethernet/synopsys/dwc_eth_qos.c b/drivers/net/ethernet/synopsys/dwc_eth_qos.c index af11ed1e0bcc..158213cd6cdd 100644 --- a/drivers/net/ethernet/synopsys/dwc_eth_qos.c +++ b/drivers/net/ethernet/synopsys/dwc_eth_qos.c @@ -949,7 +949,7 @@ static void dwceqos_adjust_link(struct net_device *ndev) if (status_change) { if (phydev->link) { - lp->ndev->trans_start = jiffies; + netif_trans_update(lp->ndev); dwceqos_link_up(lp); } else { dwceqos_link_down(lp); @@ -2203,7 +2203,7 @@ static int dwceqos_start_xmit(struct sk_buff *skb, struct net_device *ndev) netdev_sent_queue(ndev, skb->len); spin_unlock_bh(&lp->tx_lock); - ndev->trans_start = jiffies; + netif_trans_update(ndev); return 0; tx_error: diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 2524a69db318..7452b5f9d024 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -1701,7 +1701,7 @@ static netdev_tx_t bdx_tx_transmit(struct sk_buff *skb, #endif #ifdef BDX_LLTX - ndev->trans_start = jiffies; /* NETIF_F_LLTX driver :( */ + netif_trans_update(ndev); /* NETIF_F_LLTX driver :( */ #endif ndev->stats.tx_packets++; ndev->stats.tx_bytes += skb->len; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 0fa75a86b1c1..4b08a2f52b3e 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -367,7 +367,6 @@ struct cpsw_priv { spinlock_t lock; struct platform_device *pdev; struct net_device *ndev; - struct device_node *phy_node; struct napi_struct napi_rx; struct napi_struct napi_tx; struct device *dev; @@ -1142,25 +1141,34 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, 1 << slave_port, 0, 0, ALE_MCAST_FWD_2); - if (priv->phy_node) - slave->phy = of_phy_connect(priv->ndev, priv->phy_node, + if (slave->data->phy_node) { + slave->phy = of_phy_connect(priv->ndev, slave->data->phy_node, &cpsw_adjust_link, 0, slave->data->phy_if); - else + if (!slave->phy) { + dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", + slave->data->phy_node->full_name, + slave->slave_num); + return; + } + } else { slave->phy = phy_connect(priv->ndev, slave->data->phy_id, &cpsw_adjust_link, slave->data->phy_if); - if (IS_ERR(slave->phy)) { - dev_err(priv->dev, "phy %s not found on slave %d\n", - slave->data->phy_id, slave->slave_num); - slave->phy = NULL; - } else { - phy_attached_info(slave->phy); + if (IS_ERR(slave->phy)) { + dev_err(priv->dev, + "phy \"%s\" not found on slave %d, err %ld\n", + slave->data->phy_id, slave->slave_num, + PTR_ERR(slave->phy)); + slave->phy = NULL; + return; + } + } - phy_start(slave->phy); + phy_attached_info(slave->phy); - /* Configure GMII_SEL register */ - cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, - slave->slave_num); - } + phy_start(slave->phy); + + /* Configure GMII_SEL register */ + cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, slave->slave_num); } static inline void cpsw_add_default_vlan(struct cpsw_priv *priv) @@ -1381,7 +1389,7 @@ static netdev_tx_t cpsw_ndo_start_xmit(struct sk_buff *skb, struct cpsw_priv *priv = netdev_priv(ndev); int ret; - ndev->trans_start = jiffies; + netif_trans_update(ndev); if (skb_padto(skb, CPSW_MIN_PACKET_SIZE)) { cpsw_err(priv, tx_err, "packet pad failed\n"); @@ -1932,12 +1940,11 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, slave->port_vlan = data->dual_emac_res_vlan; } -static int cpsw_probe_dt(struct cpsw_priv *priv, +static int cpsw_probe_dt(struct cpsw_platform_data *data, struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; struct device_node *slave_node; - struct cpsw_platform_data *data = &priv->data; int i = 0, ret; u32 prop; @@ -2025,25 +2032,21 @@ static int cpsw_probe_dt(struct cpsw_priv *priv, if (strcmp(slave_node->name, "slave")) continue; - priv->phy_node = of_parse_phandle(slave_node, "phy-handle", 0); + slave_data->phy_node = of_parse_phandle(slave_node, + "phy-handle", 0); parp = of_get_property(slave_node, "phy_id", &lenp); - if (of_phy_is_fixed_link(slave_node)) { - struct device_node *phy_node; - struct phy_device *phy_dev; - + if (slave_data->phy_node) { + dev_dbg(&pdev->dev, + "slave[%d] using phy-handle=\"%s\"\n", + i, slave_data->phy_node->full_name); + } else if (of_phy_is_fixed_link(slave_node)) { /* In the case of a fixed PHY, the DT node associated * to the PHY is the Ethernet MAC DT node. */ ret = of_phy_register_fixed_link(slave_node); if (ret) return ret; - phy_node = of_node_get(slave_node); - phy_dev = of_phy_find_device(phy_node); - if (!phy_dev) - return -ENODEV; - snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), - PHY_ID_FMT, phy_dev->mdio.bus->id, - phy_dev->mdio.addr); + slave_data->phy_node = of_node_get(slave_node); } else if (parp) { u32 phyid; struct device_node *mdio_node; @@ -2064,7 +2067,9 @@ static int cpsw_probe_dt(struct cpsw_priv *priv, snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), PHY_ID_FMT, mdio->name, phyid); } else { - dev_err(&pdev->dev, "No slave[%d] phy_id or fixed-link property\n", i); + dev_err(&pdev->dev, + "No slave[%d] phy_id, phy-handle, or fixed-link property\n", + i); goto no_phy_slave; } slave_data->phy_if = of_get_phy_mode(slave_node); @@ -2266,7 +2271,7 @@ static int cpsw_probe(struct platform_device *pdev) /* Select default pin state */ pinctrl_pm_select_default_state(&pdev->dev); - if (cpsw_probe_dt(priv, pdev)) { + if (cpsw_probe_dt(&priv->data, pdev)) { dev_err(&pdev->dev, "cpsw: platform data missing\n"); ret = -ENODEV; goto clean_runtime_disable_ret; diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 442a7038e660..e50afd1b2eda 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -18,6 +18,7 @@ #include <linux/phy.h> struct cpsw_slave_data { + struct device_node *phy_node; char phy_id[MII_BUS_ID_SIZE]; int phy_if; u8 mac_addr[ETH_ALEN]; diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 58d58f002559..f56d66e6ec15 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1512,7 +1512,10 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) /* TODO: Add phy read and write and private statistics get feature */ - return phy_mii_ioctl(priv->phydev, ifrq, cmd); + if (priv->phydev) + return phy_mii_ioctl(priv->phydev, ifrq, cmd); + else + return -EOPNOTSUPP; } static int match_first_device(struct device *dev, void *data) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 1d0942c53120..32516661f180 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1272,7 +1272,7 @@ static int netcp_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (ret) goto drop; - ndev->trans_start = jiffies; + netif_trans_update(ndev); /* Check Tx pool count & stop subqueue if needed */ desc_count = knav_pool_count(netcp->tx_pool); @@ -1788,7 +1788,7 @@ static void netcp_ndo_tx_timeout(struct net_device *ndev) dev_err(netcp->ndev_dev, "transmit timed out tx descs(%d)\n", descs); netcp_process_tx_compl_packets(netcp, netcp->tx_pool_size); - ndev->trans_start = jiffies; + netif_trans_update(ndev); netif_tx_wake_all_queues(ndev); } diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index a274cd49afe9..561703317312 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -1007,7 +1007,7 @@ static void tlan_tx_timeout(struct net_device *dev) tlan_reset_lists(dev); tlan_read_and_clear_stats(dev, TLAN_IGNORE); tlan_reset_adapter(dev); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/tile/tilepro.c b/drivers/net/ethernet/tile/tilepro.c index 298e059d0498..922a443e3415 100644 --- a/drivers/net/ethernet/tile/tilepro.c +++ b/drivers/net/ethernet/tile/tilepro.c @@ -1883,7 +1883,7 @@ static int tile_net_tx(struct sk_buff *skb, struct net_device *dev) /* Save the timestamp. */ - dev->trans_start = jiffies; + netif_trans_update(dev); #ifdef TILE_NET_PARANOIA @@ -2026,7 +2026,7 @@ static void tile_net_tx_timeout(struct net_device *dev) { PDEBUG("tile_net_tx_timeout()\n"); PDEBUG("Transmit timeout at %ld, latency %ld\n", jiffies, - jiffies - dev->trans_start); + jiffies - dev_trans_start(dev)); /* XXX: ISSUE: This doesn't seem useful for us. */ netif_wake_queue(dev); diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c index 13214a6492ac..743b18266a7c 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.c @@ -1622,7 +1622,7 @@ static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl) continue; /* copy hw scan info */ - memcpy(target->hwinfo, scan_info, scan_info->size); + memcpy(target->hwinfo, scan_info, be16_to_cpu(scan_info->size)); target->essid_len = strnlen(scan_info->essid, sizeof(scan_info->essid)); target->rate_len = 0; diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index 67610270d171..36a6e8b54d94 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -705,7 +705,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, wmb(); descr->prev->hwdescr->next_descr_addr = descr->bus_addr; - card->netdev->trans_start = jiffies; /* set netdev watchdog timer */ + netif_trans_update(card->netdev); /* set netdev watchdog timer */ return 0; } diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 2b7550c43f78..9d14731cdcb1 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -1758,7 +1758,7 @@ static void rhine_reset_task(struct work_struct *work) spin_unlock_bh(&rp->lock); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ dev->stats.tx_errors++; netif_wake_queue(dev); diff --git a/drivers/net/ethernet/wiznet/w5100-spi.c b/drivers/net/ethernet/wiznet/w5100-spi.c index b868e458d0b5..93a2d3c07303 100644 --- a/drivers/net/ethernet/wiznet/w5100-spi.c +++ b/drivers/net/ethernet/wiznet/w5100-spi.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/netdevice.h> +#include <linux/of_net.h> #include <linux/spi/spi.h> #include "w5100.h" @@ -414,6 +415,7 @@ static int w5100_spi_probe(struct spi_device *spi) const struct spi_device_id *id = spi_get_device_id(spi); const struct w5100_ops *ops; int priv_size; + const void *mac = of_get_mac_address(spi->dev.of_node); switch (id->driver_data) { case W5100: @@ -432,7 +434,7 @@ static int w5100_spi_probe(struct spi_device *spi) return -EINVAL; } - return w5100_probe(&spi->dev, ops, priv_size, NULL, spi->irq, -EINVAL); + return w5100_probe(&spi->dev, ops, priv_size, mac, spi->irq, -EINVAL); } static int w5100_spi_remove(struct spi_device *spi) diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index 8ed0c7735ee3..4f6255cf62ce 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -63,8 +63,9 @@ MODULE_LICENSE("GPL"); #define S0_REGS(priv) ((priv)->s0_regs) #define W5100_S0_MR(priv) (S0_REGS(priv) + W5100_Sn_MR) -#define S0_MR_MACRAW 0x04 /* MAC RAW mode (promiscuous) */ -#define S0_MR_MACRAW_MF 0x44 /* MAC RAW mode (filtered) */ +#define S0_MR_MACRAW 0x04 /* MAC RAW mode */ +#define S0_MR_MF 0x40 /* MAC Filter for W5100 and W5200 */ +#define W5500_S0_MR_MF 0x80 /* MAC Filter for W5500 */ #define W5100_S0_CR(priv) (S0_REGS(priv) + W5100_Sn_CR) #define S0_CR_OPEN 0x01 /* OPEN command */ #define S0_CR_CLOSE 0x10 /* CLOSE command */ @@ -173,11 +174,6 @@ struct w5100_priv { struct work_struct restart_work; }; -static inline bool is_w5200(struct w5100_priv *priv) -{ - return priv->ops->chip_id == W5200; -} - /************************************************************************ * * Lowlevel I/O functions @@ -707,8 +703,16 @@ static int w5100_hw_reset(struct w5100_priv *priv) static void w5100_hw_start(struct w5100_priv *priv) { - w5100_write(priv, W5100_S0_MR(priv), priv->promisc ? - S0_MR_MACRAW : S0_MR_MACRAW_MF); + u8 mode = S0_MR_MACRAW; + + if (!priv->promisc) { + if (priv->ops->chip_id == W5500) + mode |= W5500_S0_MR_MF; + else + mode |= S0_MR_MF; + } + + w5100_write(priv, W5100_S0_MR(priv), mode); w5100_command(priv, S0_CR_OPEN); w5100_enable_intr(priv); } @@ -782,7 +786,7 @@ static void w5100_restart(struct net_device *ndev) w5100_hw_reset(priv); w5100_hw_start(priv); ndev->stats.tx_errors++; - ndev->trans_start = jiffies; + netif_trans_update(ndev); netif_wake_queue(ndev); } @@ -1048,7 +1052,7 @@ static const struct net_device_ops w5100_netdev_ops = { static int w5100_mmio_probe(struct platform_device *pdev) { struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev); - u8 *mac_addr = NULL; + const void *mac_addr = NULL; struct resource *mem; const struct w5100_ops *ops; int irq; @@ -1083,7 +1087,8 @@ void *w5100_ops_priv(const struct net_device *ndev) EXPORT_SYMBOL_GPL(w5100_ops_priv); int w5100_probe(struct device *dev, const struct w5100_ops *ops, - int sizeof_ops_priv, u8 *mac_addr, int irq, int link_gpio) + int sizeof_ops_priv, const void *mac_addr, int irq, + int link_gpio) { struct w5100_priv *priv; struct net_device *ndev; @@ -1138,7 +1143,6 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, ndev->netdev_ops = &w5100_netdev_ops; ndev->ethtool_ops = &w5100_ethtool_ops; - ndev->watchdog_timeo = HZ; netif_napi_add(ndev, &priv->napi, w5100_napi_poll, 16); /* This chip doesn't support VLAN packets with normal MTU, diff --git a/drivers/net/ethernet/wiznet/w5100.h b/drivers/net/ethernet/wiznet/w5100.h index f8a16fad807b..17983a3b8d6c 100644 --- a/drivers/net/ethernet/wiznet/w5100.h +++ b/drivers/net/ethernet/wiznet/w5100.h @@ -30,7 +30,8 @@ struct w5100_ops { void *w5100_ops_priv(const struct net_device *ndev); int w5100_probe(struct device *dev, const struct w5100_ops *ops, - int sizeof_ops_priv, u8 *mac_addr, int irq, int link_gpio); + int sizeof_ops_priv, const void *mac_addr, int irq, + int link_gpio); int w5100_remove(struct device *dev); extern const struct dev_pm_ops w5100_pm_ops; diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 8da7b930ff59..0b37ce9f28f1 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -362,7 +362,7 @@ static void w5300_tx_timeout(struct net_device *ndev) w5300_hw_reset(priv); w5300_hw_start(priv); ndev->stats.tx_errors++; - ndev->trans_start = jiffies; + netif_trans_update(ndev); netif_wake_queue(ndev); } diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 5a1068df7038..739708712022 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -584,7 +584,7 @@ static void temac_device_reset(struct net_device *ndev) dev_err(&ndev->dev, "Error setting TEMAC options\n"); /* Init Driver variable */ - ndev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(ndev); /* prevent tx timeout */ } static void temac_adjust_link(struct net_device *ndev) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 4684644703cc..8c7f5be51e62 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -508,7 +508,7 @@ static void axienet_device_reset(struct net_device *ndev) axienet_set_multicast_list(ndev); axienet_setoptions(ndev, lp->options); - ndev->trans_start = jiffies; + netif_trans_update(ndev); } /** diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index e324b3092380..3cee84a24815 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -531,7 +531,7 @@ static void xemaclite_tx_timeout(struct net_device *dev) } /* To exclude tx timeout */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ /* We're all ready to go. Start the queue */ netif_wake_queue(dev); @@ -563,7 +563,7 @@ static void xemaclite_tx_handler(struct net_device *dev) dev->stats.tx_bytes += lp->deferred_skb->len; dev_kfree_skb_irq(lp->deferred_skb); lp->deferred_skb = NULL; - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } } diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index d56f8693202b..7b44968e02e6 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1199,7 +1199,7 @@ xirc2ps_tx_timeout_task(struct work_struct *work) struct net_device *dev = local->dev; /* reset the card */ do_reset(dev,1); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index bb7e90368f8f..86c331bb5eb3 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -471,7 +471,7 @@ static void fjes_tx_stall_task(struct work_struct *work) int i; if (((long)jiffies - - (long)(netdev->trans_start)) > FJES_TX_TX_STALL_TIMEOUT) { + dev_trans_start(netdev)) > FJES_TX_TX_STALL_TIMEOUT) { netif_wake_queue(netdev); return; } @@ -718,7 +718,7 @@ fjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev) ret = NETDEV_TX_OK; } else { - netdev->trans_start = jiffies; + netif_trans_update(netdev); netif_tx_stop_queue(cur_queue); if (!work_pending(&adapter->tx_stall_task)) @@ -819,7 +819,6 @@ static int fjes_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; if (running) { - spin_lock_irqsave(&hw->rx_status_lock, flags); for (epidx = 0; epidx < hw->max_epid; epidx++) { if (epidx == hw->my_epid) continue; diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 9c40b88fabd5..a6dc11ce497f 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -495,8 +495,6 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, int gh_len; int err = -ENOSYS; - udp_tunnel_gro_complete(skb, nhoff); - gh = (struct genevehdr *)(skb->data + nhoff); gh_len = geneve_hlen(gh); type = gh->proto_type; @@ -507,6 +505,9 @@ static int geneve_gro_complete(struct sock *sk, struct sk_buff *skb, err = ptype->callbacks.gro_complete(skb, nhoff + gh_len); rcu_read_unlock(); + + skb_set_inner_mac_header(skb, nhoff + gh_len); + return err; } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c new file mode 100644 index 000000000000..4e976a0d5a76 --- /dev/null +++ b/drivers/net/gtp.c @@ -0,0 +1,1375 @@ +/* GTP according to GSM TS 09.60 / 3GPP TS 29.060 + * + * (C) 2012-2014 by sysmocom - s.f.m.c. GmbH + * (C) 2016 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * Author: Harald Welte <hwelte@sysmocom.de> + * Pablo Neira Ayuso <pablo@netfilter.org> + * Andreas Schultz <aschultz@travelping.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/skbuff.h> +#include <linux/udp.h> +#include <linux/rculist.h> +#include <linux/jhash.h> +#include <linux/if_tunnel.h> +#include <linux/net.h> +#include <linux/file.h> +#include <linux/gtp.h> + +#include <net/net_namespace.h> +#include <net/protocol.h> +#include <net/ip.h> +#include <net/udp.h> +#include <net/udp_tunnel.h> +#include <net/icmp.h> +#include <net/xfrm.h> +#include <net/genetlink.h> +#include <net/netns/generic.h> +#include <net/gtp.h> + +/* An active session for the subscriber. */ +struct pdp_ctx { + struct hlist_node hlist_tid; + struct hlist_node hlist_addr; + + union { + u64 tid; + struct { + u64 tid; + u16 flow; + } v0; + struct { + u32 i_tei; + u32 o_tei; + } v1; + } u; + u8 gtp_version; + u16 af; + + struct in_addr ms_addr_ip4; + struct in_addr sgsn_addr_ip4; + + atomic_t tx_seq; + struct rcu_head rcu_head; +}; + +/* One instance of the GTP device. */ +struct gtp_dev { + struct list_head list; + + struct socket *sock0; + struct socket *sock1u; + + struct net *net; + struct net_device *dev; + + unsigned int hash_size; + struct hlist_head *tid_hash; + struct hlist_head *addr_hash; +}; + +static int gtp_net_id __read_mostly; + +struct gtp_net { + struct list_head gtp_dev_list; +}; + +static u32 gtp_h_initval; + +static inline u32 gtp0_hashfn(u64 tid) +{ + u32 *tid32 = (u32 *) &tid; + return jhash_2words(tid32[0], tid32[1], gtp_h_initval); +} + +static inline u32 gtp1u_hashfn(u32 tid) +{ + return jhash_1word(tid, gtp_h_initval); +} + +static inline u32 ipv4_hashfn(__be32 ip) +{ + return jhash_1word((__force u32)ip, gtp_h_initval); +} + +/* Resolve a PDP context structure based on the 64bit TID. */ +static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid) +{ + struct hlist_head *head; + struct pdp_ctx *pdp; + + head = >p->tid_hash[gtp0_hashfn(tid) % gtp->hash_size]; + + hlist_for_each_entry_rcu(pdp, head, hlist_tid) { + if (pdp->gtp_version == GTP_V0 && + pdp->u.v0.tid == tid) + return pdp; + } + return NULL; +} + +/* Resolve a PDP context structure based on the 32bit TEI. */ +static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid) +{ + struct hlist_head *head; + struct pdp_ctx *pdp; + + head = >p->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size]; + + hlist_for_each_entry_rcu(pdp, head, hlist_tid) { + if (pdp->gtp_version == GTP_V1 && + pdp->u.v1.i_tei == tid) + return pdp; + } + return NULL; +} + +/* Resolve a PDP context based on IPv4 address of MS. */ +static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr) +{ + struct hlist_head *head; + struct pdp_ctx *pdp; + + head = >p->addr_hash[ipv4_hashfn(ms_addr) % gtp->hash_size]; + + hlist_for_each_entry_rcu(pdp, head, hlist_addr) { + if (pdp->af == AF_INET && + pdp->ms_addr_ip4.s_addr == ms_addr) + return pdp; + } + + return NULL; +} + +static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, + unsigned int hdrlen) +{ + struct iphdr *iph; + + if (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr))) + return false; + + iph = (struct iphdr *)(skb->data + hdrlen + sizeof(struct iphdr)); + + return iph->saddr != pctx->ms_addr_ip4.s_addr; +} + +/* Check if the inner IP source address in this packet is assigned to any + * existing mobile subscriber. + */ +static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, + unsigned int hdrlen) +{ + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + return gtp_check_src_ms_ipv4(skb, pctx, hdrlen); + } + return false; +} + +/* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ +static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, + bool xnet) +{ + unsigned int hdrlen = sizeof(struct udphdr) + + sizeof(struct gtp0_header); + struct gtp0_header *gtp0; + struct pdp_ctx *pctx; + int ret = 0; + + if (!pskb_may_pull(skb, hdrlen)) + return -1; + + gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr)); + + if ((gtp0->flags >> 5) != GTP_V0) + return 1; + + if (gtp0->type != GTP_TPDU) + return 1; + + rcu_read_lock(); + pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid)); + if (!pctx) { + netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); + ret = -1; + goto out_rcu; + } + + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); + ret = -1; + goto out_rcu; + } + rcu_read_unlock(); + + /* Get rid of the GTP + UDP headers. */ + return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); +out_rcu: + rcu_read_unlock(); + return ret; +} + +static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, + bool xnet) +{ + unsigned int hdrlen = sizeof(struct udphdr) + + sizeof(struct gtp1_header); + struct gtp1_header *gtp1; + struct pdp_ctx *pctx; + int ret = 0; + + if (!pskb_may_pull(skb, hdrlen)) + return -1; + + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + + if ((gtp1->flags >> 5) != GTP_V1) + return 1; + + if (gtp1->type != GTP_TPDU) + return 1; + + /* From 29.060: "This field shall be present if and only if any one or + * more of the S, PN and E flags are set.". + * + * If any of the bit is set, then the remaining ones also have to be + * set. + */ + if (gtp1->flags & GTP1_F_MASK) + hdrlen += 4; + + /* Make sure the header is larger enough, including extensions. */ + if (!pskb_may_pull(skb, hdrlen)) + return -1; + + gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr)); + + rcu_read_lock(); + pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid)); + if (!pctx) { + netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb); + ret = -1; + goto out_rcu; + } + + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); + ret = -1; + goto out_rcu; + } + rcu_read_unlock(); + + /* Get rid of the GTP + UDP headers. */ + return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); +out_rcu: + rcu_read_unlock(); + return ret; +} + +static void gtp_encap_disable(struct gtp_dev *gtp) +{ + if (gtp->sock0 && gtp->sock0->sk) { + udp_sk(gtp->sock0->sk)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sock0->sk, NULL); + } + if (gtp->sock1u && gtp->sock1u->sk) { + udp_sk(gtp->sock1u->sk)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sock1u->sk, NULL); + } + + gtp->sock0 = NULL; + gtp->sock1u = NULL; +} + +static void gtp_encap_destroy(struct sock *sk) +{ + struct gtp_dev *gtp; + + gtp = rcu_dereference_sk_user_data(sk); + if (gtp) + gtp_encap_disable(gtp); +} + +/* UDP encapsulation receive handler. See net/ipv4/udp.c. + * Return codes: 0: success, <0: error, >0: pass up to userspace UDP socket. + */ +static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) +{ + struct pcpu_sw_netstats *stats; + struct gtp_dev *gtp; + bool xnet; + int ret; + + gtp = rcu_dereference_sk_user_data(sk); + if (!gtp) + return 1; + + netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); + + xnet = !net_eq(gtp->net, dev_net(gtp->dev)); + + switch (udp_sk(sk)->encap_type) { + case UDP_ENCAP_GTP0: + netdev_dbg(gtp->dev, "received GTP0 packet\n"); + ret = gtp0_udp_encap_recv(gtp, skb, xnet); + break; + case UDP_ENCAP_GTP1U: + netdev_dbg(gtp->dev, "received GTP1U packet\n"); + ret = gtp1u_udp_encap_recv(gtp, skb, xnet); + break; + default: + ret = -1; /* Shouldn't happen. */ + } + + switch (ret) { + case 1: + netdev_dbg(gtp->dev, "pass up to the process\n"); + return 1; + case 0: + netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); + break; + case -1: + netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); + kfree_skb(skb); + return 0; + } + + /* Now that the UDP and the GTP header have been removed, set up the + * new network header. This is required by the upper layer to + * calculate the transport header. + */ + skb_reset_network_header(skb); + + skb->dev = gtp->dev; + + stats = this_cpu_ptr(gtp->dev->tstats); + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + netif_rx(skb); + + return 0; +} + +static int gtp_dev_init(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + gtp->dev = dev; + + dev->tstats = alloc_percpu(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + return 0; +} + +static void gtp_dev_uninit(struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + gtp_encap_disable(gtp); + free_percpu(dev->tstats); +} + +static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, + const struct sock *sk, __be32 daddr) +{ + memset(fl4, 0, sizeof(*fl4)); + fl4->flowi4_oif = sk->sk_bound_dev_if; + fl4->daddr = daddr; + fl4->saddr = inet_sk(sk)->inet_saddr; + fl4->flowi4_tos = RT_CONN_FLAGS(sk); + fl4->flowi4_proto = sk->sk_protocol; + + return ip_route_output_key(net, fl4); +} + +static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) +{ + int payload_len = skb->len; + struct gtp0_header *gtp0; + + gtp0 = (struct gtp0_header *) skb_push(skb, sizeof(*gtp0)); + + gtp0->flags = 0x1e; /* v0, GTP-non-prime. */ + gtp0->type = GTP_TPDU; + gtp0->length = htons(payload_len); + gtp0->seq = htons((atomic_inc_return(&pctx->tx_seq) - 1) % 0xffff); + gtp0->flow = htons(pctx->u.v0.flow); + gtp0->number = 0xff; + gtp0->spare[0] = gtp0->spare[1] = gtp0->spare[2] = 0xff; + gtp0->tid = cpu_to_be64(pctx->u.v0.tid); +} + +static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) +{ + int payload_len = skb->len; + struct gtp1_header *gtp1; + + gtp1 = (struct gtp1_header *) skb_push(skb, sizeof(*gtp1)); + + /* Bits 8 7 6 5 4 3 2 1 + * +--+--+--+--+--+--+--+--+ + * |version |PT| 1| E| S|PN| + * +--+--+--+--+--+--+--+--+ + * 0 0 1 1 1 0 0 0 + */ + gtp1->flags = 0x38; /* v1, GTP-non-prime. */ + gtp1->type = GTP_TPDU; + gtp1->length = htons(payload_len); + gtp1->tid = htonl(pctx->u.v1.o_tei); + + /* TODO: Suppport for extension header, sequence number and N-PDU. + * Update the length field if any of them is available. + */ +} + +struct gtp_pktinfo { + struct sock *sk; + struct iphdr *iph; + struct flowi4 fl4; + struct rtable *rt; + struct pdp_ctx *pctx; + struct net_device *dev; + __be16 gtph_port; +}; + +static void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) +{ + switch (pktinfo->pctx->gtp_version) { + case GTP_V0: + pktinfo->gtph_port = htons(GTP0_PORT); + gtp0_push_header(skb, pktinfo->pctx); + break; + case GTP_V1: + pktinfo->gtph_port = htons(GTP1U_PORT); + gtp1_push_header(skb, pktinfo->pctx); + break; + } +} + +static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo, + struct sock *sk, struct iphdr *iph, + struct pdp_ctx *pctx, struct rtable *rt, + struct flowi4 *fl4, + struct net_device *dev) +{ + pktinfo->sk = sk; + pktinfo->iph = iph; + pktinfo->pctx = pctx; + pktinfo->rt = rt; + pktinfo->fl4 = *fl4; + pktinfo->dev = dev; +} + +static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, + struct gtp_pktinfo *pktinfo) +{ + struct gtp_dev *gtp = netdev_priv(dev); + struct pdp_ctx *pctx; + struct rtable *rt; + struct flowi4 fl4; + struct iphdr *iph; + struct sock *sk; + __be16 df; + int mtu; + + /* Read the IP destination address and resolve the PDP context. + * Prepend PDP header with TEI/TID from PDP ctx. + */ + iph = ip_hdr(skb); + pctx = ipv4_pdp_find(gtp, iph->daddr); + if (!pctx) { + netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", + &iph->daddr); + return -ENOENT; + } + netdev_dbg(dev, "found PDP context %p\n", pctx); + + switch (pctx->gtp_version) { + case GTP_V0: + if (gtp->sock0) + sk = gtp->sock0->sk; + else + sk = NULL; + break; + case GTP_V1: + if (gtp->sock1u) + sk = gtp->sock1u->sk; + else + sk = NULL; + break; + default: + return -ENOENT; + } + + if (!sk) { + netdev_dbg(dev, "no userspace socket is available, skip\n"); + return -ENOENT; + } + + rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk, + pctx->sgsn_addr_ip4.s_addr); + if (IS_ERR(rt)) { + netdev_dbg(dev, "no route to SSGN %pI4\n", + &pctx->sgsn_addr_ip4.s_addr); + dev->stats.tx_carrier_errors++; + goto err; + } + + if (rt->dst.dev == dev) { + netdev_dbg(dev, "circular route to SSGN %pI4\n", + &pctx->sgsn_addr_ip4.s_addr); + dev->stats.collisions++; + goto err_rt; + } + + skb_dst_drop(skb); + + /* This is similar to tnl_update_pmtu(). */ + df = iph->frag_off; + if (df) { + mtu = dst_mtu(&rt->dst) - dev->hard_header_len - + sizeof(struct iphdr) - sizeof(struct udphdr); + switch (pctx->gtp_version) { + case GTP_V0: + mtu -= sizeof(struct gtp0_header); + break; + case GTP_V1: + mtu -= sizeof(struct gtp1_header); + break; + } + } else { + mtu = dst_mtu(&rt->dst); + } + + rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu); + + if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) && + mtu < ntohs(iph->tot_len)) { + netdev_dbg(dev, "packet too big, fragmentation needed\n"); + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, + htonl(mtu)); + goto err_rt; + } + + gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev); + gtp_push_header(skb, pktinfo); + + return 0; +err_rt: + ip_rt_put(rt); +err: + return -EBADMSG; +} + +static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned int proto = ntohs(skb->protocol); + struct gtp_pktinfo pktinfo; + int err; + + /* Ensure there is sufficient headroom. */ + if (skb_cow_head(skb, dev->needed_headroom)) + goto tx_err; + + skb_reset_inner_headers(skb); + + /* PDP context lookups in gtp_build_skb_*() need rcu read-side lock. */ + rcu_read_lock(); + switch (proto) { + case ETH_P_IP: + err = gtp_build_skb_ip4(skb, dev, &pktinfo); + break; + default: + err = -EOPNOTSUPP; + break; + } + rcu_read_unlock(); + + if (err < 0) + goto tx_err; + + switch (proto) { + case ETH_P_IP: + netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n", + &pktinfo.iph->saddr, &pktinfo.iph->daddr); + udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb, + pktinfo.fl4.saddr, pktinfo.fl4.daddr, + pktinfo.iph->tos, + ip4_dst_hoplimit(&pktinfo.rt->dst), + htons(IP_DF), + pktinfo.gtph_port, pktinfo.gtph_port, + true, false); + break; + } + + return NETDEV_TX_OK; +tx_err: + dev->stats.tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static const struct net_device_ops gtp_netdev_ops = { + .ndo_init = gtp_dev_init, + .ndo_uninit = gtp_dev_uninit, + .ndo_start_xmit = gtp_dev_xmit, + .ndo_get_stats64 = ip_tunnel_get_stats64, +}; + +static void gtp_link_setup(struct net_device *dev) +{ + dev->netdev_ops = >p_netdev_ops; + dev->destructor = free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + + /* Zero header length. */ + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + + dev->priv_flags |= IFF_NO_QUEUE; + dev->features |= NETIF_F_LLTX; + netif_keep_dst(dev); + + /* Assume largest header, ie. GTPv0. */ + dev->needed_headroom = LL_MAX_HEADER + + sizeof(struct iphdr) + + sizeof(struct udphdr) + + sizeof(struct gtp0_header); +} + +static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); +static void gtp_hashtable_free(struct gtp_dev *gtp); +static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, + int fd_gtp0, int fd_gtp1, struct net *src_net); + +static int gtp_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + int hashsize, err, fd0, fd1; + struct gtp_dev *gtp; + struct gtp_net *gn; + + if (!data[IFLA_GTP_FD0] || !data[IFLA_GTP_FD1]) + return -EINVAL; + + gtp = netdev_priv(dev); + + fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + + err = gtp_encap_enable(dev, gtp, fd0, fd1, src_net); + if (err < 0) + goto out_err; + + if (!data[IFLA_GTP_PDP_HASHSIZE]) + hashsize = 1024; + else + hashsize = nla_get_u32(data[IFLA_GTP_PDP_HASHSIZE]); + + err = gtp_hashtable_new(gtp, hashsize); + if (err < 0) + goto out_encap; + + err = register_netdevice(dev); + if (err < 0) { + netdev_dbg(dev, "failed to register new netdev %d\n", err); + goto out_hashtable; + } + + gn = net_generic(dev_net(dev), gtp_net_id); + list_add_rcu(>p->list, &gn->gtp_dev_list); + + netdev_dbg(dev, "registered new GTP interface\n"); + + return 0; + +out_hashtable: + gtp_hashtable_free(gtp); +out_encap: + gtp_encap_disable(gtp); +out_err: + return err; +} + +static void gtp_dellink(struct net_device *dev, struct list_head *head) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + gtp_encap_disable(gtp); + gtp_hashtable_free(gtp); + list_del_rcu(>p->list); + unregister_netdevice_queue(dev, head); +} + +static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { + [IFLA_GTP_FD0] = { .type = NLA_U32 }, + [IFLA_GTP_FD1] = { .type = NLA_U32 }, + [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, +}; + +static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (!data) + return -EINVAL; + + return 0; +} + +static size_t gtp_get_size(const struct net_device *dev) +{ + return nla_total_size(sizeof(__u32)); /* IFLA_GTP_PDP_HASHSIZE */ +} + +static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct gtp_dev *gtp = netdev_priv(dev); + + if (nla_put_u32(skb, IFLA_GTP_PDP_HASHSIZE, gtp->hash_size)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static struct rtnl_link_ops gtp_link_ops __read_mostly = { + .kind = "gtp", + .maxtype = IFLA_GTP_MAX, + .policy = gtp_policy, + .priv_size = sizeof(struct gtp_dev), + .setup = gtp_link_setup, + .validate = gtp_validate, + .newlink = gtp_newlink, + .dellink = gtp_dellink, + .get_size = gtp_get_size, + .fill_info = gtp_fill_info, +}; + +static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[]) +{ + struct net *net; + + /* Examine the link attributes and figure out which network namespace + * we are talking about. + */ + if (tb[GTPA_NET_NS_FD]) + net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD])); + else + net = get_net(src_net); + + return net; +} + +static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) +{ + int i; + + gtp->addr_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL); + if (gtp->addr_hash == NULL) + return -ENOMEM; + + gtp->tid_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL); + if (gtp->tid_hash == NULL) + goto err1; + + gtp->hash_size = hsize; + + for (i = 0; i < hsize; i++) { + INIT_HLIST_HEAD(>p->addr_hash[i]); + INIT_HLIST_HEAD(>p->tid_hash[i]); + } + return 0; +err1: + kfree(gtp->addr_hash); + return -ENOMEM; +} + +static void gtp_hashtable_free(struct gtp_dev *gtp) +{ + struct pdp_ctx *pctx; + int i; + + for (i = 0; i < gtp->hash_size; i++) { + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { + hlist_del_rcu(&pctx->hlist_tid); + hlist_del_rcu(&pctx->hlist_addr); + kfree_rcu(pctx, rcu_head); + } + } + synchronize_rcu(); + kfree(gtp->addr_hash); + kfree(gtp->tid_hash); +} + +static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, + int fd_gtp0, int fd_gtp1, struct net *src_net) +{ + struct udp_tunnel_sock_cfg tuncfg = {NULL}; + struct socket *sock0, *sock1u; + int err; + + netdev_dbg(dev, "enable gtp on %d, %d\n", fd_gtp0, fd_gtp1); + + sock0 = sockfd_lookup(fd_gtp0, &err); + if (sock0 == NULL) { + netdev_dbg(dev, "socket fd=%d not found (gtp0)\n", fd_gtp0); + return -ENOENT; + } + + if (sock0->sk->sk_protocol != IPPROTO_UDP) { + netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp0); + err = -EINVAL; + goto err1; + } + + sock1u = sockfd_lookup(fd_gtp1, &err); + if (sock1u == NULL) { + netdev_dbg(dev, "socket fd=%d not found (gtp1u)\n", fd_gtp1); + err = -ENOENT; + goto err1; + } + + if (sock1u->sk->sk_protocol != IPPROTO_UDP) { + netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp1); + err = -EINVAL; + goto err2; + } + + netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); + + gtp->sock0 = sock0; + gtp->sock1u = sock1u; + gtp->net = src_net; + + tuncfg.sk_user_data = gtp; + tuncfg.encap_rcv = gtp_encap_recv; + tuncfg.encap_destroy = gtp_encap_destroy; + + tuncfg.encap_type = UDP_ENCAP_GTP0; + setup_udp_tunnel_sock(sock_net(gtp->sock0->sk), gtp->sock0, &tuncfg); + + tuncfg.encap_type = UDP_ENCAP_GTP1U; + setup_udp_tunnel_sock(sock_net(gtp->sock1u->sk), gtp->sock1u, &tuncfg); + + err = 0; +err2: + sockfd_put(sock1u); +err1: + sockfd_put(sock0); + return err; +} + +static struct net_device *gtp_find_dev(struct net *net, int ifindex) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp; + + list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { + if (ifindex == gtp->dev->ifindex) + return gtp->dev; + } + return NULL; +} + +static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) +{ + pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); + pctx->af = AF_INET; + pctx->sgsn_addr_ip4.s_addr = + nla_get_be32(info->attrs[GTPA_SGSN_ADDRESS]); + pctx->ms_addr_ip4.s_addr = + nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + + switch (pctx->gtp_version) { + case GTP_V0: + /* According to TS 09.60, sections 7.5.1 and 7.5.2, the flow + * label needs to be the same for uplink and downlink packets, + * so let's annotate this. + */ + pctx->u.v0.tid = nla_get_u64(info->attrs[GTPA_TID]); + pctx->u.v0.flow = nla_get_u16(info->attrs[GTPA_FLOW]); + break; + case GTP_V1: + pctx->u.v1.i_tei = nla_get_u32(info->attrs[GTPA_I_TEI]); + pctx->u.v1.o_tei = nla_get_u32(info->attrs[GTPA_O_TEI]); + break; + default: + break; + } +} + +static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +{ + struct gtp_dev *gtp = netdev_priv(dev); + u32 hash_ms, hash_tid = 0; + struct pdp_ctx *pctx; + bool found = false; + __be32 ms_addr; + + ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size; + + hlist_for_each_entry_rcu(pctx, >p->addr_hash[hash_ms], hlist_addr) { + if (pctx->ms_addr_ip4.s_addr == ms_addr) { + found = true; + break; + } + } + + if (found) { + if (info->nlhdr->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE) + return -EOPNOTSUPP; + + ipv4_pdp_fill(pctx, info); + + if (pctx->gtp_version == GTP_V0) + netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n", + pctx->u.v0.tid, pctx); + else if (pctx->gtp_version == GTP_V1) + netdev_dbg(dev, "GTPv1-U: update tunnel id = %x/%x (pdp %p)\n", + pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); + + return 0; + + } + + pctx = kmalloc(sizeof(struct pdp_ctx), GFP_KERNEL); + if (pctx == NULL) + return -ENOMEM; + + ipv4_pdp_fill(pctx, info); + atomic_set(&pctx->tx_seq, 0); + + switch (pctx->gtp_version) { + case GTP_V0: + /* TS 09.60: "The flow label identifies unambiguously a GTP + * flow.". We use the tid for this instead, I cannot find a + * situation in which this doesn't unambiguosly identify the + * PDP context. + */ + hash_tid = gtp0_hashfn(pctx->u.v0.tid) % gtp->hash_size; + break; + case GTP_V1: + hash_tid = gtp1u_hashfn(pctx->u.v1.i_tei) % gtp->hash_size; + break; + } + + hlist_add_head_rcu(&pctx->hlist_addr, >p->addr_hash[hash_ms]); + hlist_add_head_rcu(&pctx->hlist_tid, >p->tid_hash[hash_tid]); + + switch (pctx->gtp_version) { + case GTP_V0: + netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n", + pctx->u.v0.tid, &pctx->sgsn_addr_ip4, + &pctx->ms_addr_ip4, pctx); + break; + case GTP_V1: + netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n", + pctx->u.v1.i_tei, pctx->u.v1.o_tei, + &pctx->sgsn_addr_ip4, &pctx->ms_addr_ip4, pctx); + break; + } + + return 0; +} + +static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev; + struct net *net; + + if (!info->attrs[GTPA_VERSION] || + !info->attrs[GTPA_LINK] || + !info->attrs[GTPA_SGSN_ADDRESS] || + !info->attrs[GTPA_MS_ADDRESS]) + return -EINVAL; + + switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + case GTP_V0: + if (!info->attrs[GTPA_TID] || + !info->attrs[GTPA_FLOW]) + return -EINVAL; + break; + case GTP_V1: + if (!info->attrs[GTPA_I_TEI] || + !info->attrs[GTPA_O_TEI]) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); + if (IS_ERR(net)) + return PTR_ERR(net); + + /* Check if there's an existing gtpX device to configure */ + dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); + if (dev == NULL) { + put_net(net); + return -ENODEV; + } + put_net(net); + + return ipv4_pdp_add(dev, info); +} + +static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device *dev; + struct pdp_ctx *pctx; + struct gtp_dev *gtp; + struct net *net; + + if (!info->attrs[GTPA_VERSION] || + !info->attrs[GTPA_LINK]) + return -EINVAL; + + net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); + if (IS_ERR(net)) + return PTR_ERR(net); + + /* Check if there's an existing gtpX device to configure */ + dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); + if (dev == NULL) { + put_net(net); + return -ENODEV; + } + put_net(net); + + gtp = netdev_priv(dev); + + switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + case GTP_V0: + if (!info->attrs[GTPA_TID]) + return -EINVAL; + pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); + break; + case GTP_V1: + if (!info->attrs[GTPA_I_TEI]) + return -EINVAL; + pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); + break; + + default: + return -EINVAL; + } + + if (pctx == NULL) + return -ENOENT; + + if (pctx->gtp_version == GTP_V0) + netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + pctx->u.v0.tid, pctx); + else if (pctx->gtp_version == GTP_V1) + netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); + + hlist_del_rcu(&pctx->hlist_tid); + hlist_del_rcu(&pctx->hlist_addr); + kfree_rcu(pctx, rcu_head); + + return 0; +} + +static struct genl_family gtp_genl_family = { + .id = GENL_ID_GENERATE, + .name = "gtp", + .version = 0, + .hdrsize = 0, + .maxattr = GTPA_MAX, + .netnsok = true, +}; + +static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, + u32 type, struct pdp_ctx *pctx) +{ + void *genlh; + + genlh = genlmsg_put(skb, snd_portid, snd_seq, >p_genl_family, 0, + type); + if (genlh == NULL) + goto nlmsg_failure; + + if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) || + nla_put_be32(skb, GTPA_SGSN_ADDRESS, pctx->sgsn_addr_ip4.s_addr) || + nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr)) + goto nla_put_failure; + + switch (pctx->gtp_version) { + case GTP_V0: + if (nla_put_u64_64bit(skb, GTPA_TID, pctx->u.v0.tid, GTPA_PAD) || + nla_put_u16(skb, GTPA_FLOW, pctx->u.v0.flow)) + goto nla_put_failure; + break; + case GTP_V1: + if (nla_put_u32(skb, GTPA_I_TEI, pctx->u.v1.i_tei) || + nla_put_u32(skb, GTPA_O_TEI, pctx->u.v1.o_tei)) + goto nla_put_failure; + break; + } + genlmsg_end(skb, genlh); + return 0; + +nlmsg_failure: +nla_put_failure: + genlmsg_cancel(skb, genlh); + return -EMSGSIZE; +} + +static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) +{ + struct pdp_ctx *pctx = NULL; + struct net_device *dev; + struct sk_buff *skb2; + struct gtp_dev *gtp; + u32 gtp_version; + struct net *net; + int err; + + if (!info->attrs[GTPA_VERSION] || + !info->attrs[GTPA_LINK]) + return -EINVAL; + + gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); + switch (gtp_version) { + case GTP_V0: + case GTP_V1: + break; + default: + return -EINVAL; + } + + net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); + if (IS_ERR(net)) + return PTR_ERR(net); + + /* Check if there's an existing gtpX device to configure */ + dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); + if (dev == NULL) { + put_net(net); + return -ENODEV; + } + put_net(net); + + gtp = netdev_priv(dev); + + rcu_read_lock(); + if (gtp_version == GTP_V0 && + info->attrs[GTPA_TID]) { + u64 tid = nla_get_u64(info->attrs[GTPA_TID]); + + pctx = gtp0_pdp_find(gtp, tid); + } else if (gtp_version == GTP_V1 && + info->attrs[GTPA_I_TEI]) { + u32 tid = nla_get_u32(info->attrs[GTPA_I_TEI]); + + pctx = gtp1_pdp_find(gtp, tid); + } else if (info->attrs[GTPA_MS_ADDRESS]) { + __be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + + pctx = ipv4_pdp_find(gtp, ip); + } + + if (pctx == NULL) { + err = -ENOENT; + goto err_unlock; + } + + skb2 = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + if (skb2 == NULL) { + err = -ENOMEM; + goto err_unlock; + } + + err = gtp_genl_fill_info(skb2, NETLINK_CB(skb).portid, + info->snd_seq, info->nlhdr->nlmsg_type, pctx); + if (err < 0) + goto err_unlock_free; + + rcu_read_unlock(); + return genlmsg_unicast(genl_info_net(info), skb2, info->snd_portid); + +err_unlock_free: + kfree_skb(skb2); +err_unlock: + rcu_read_unlock(); + return err; +} + +static int gtp_genl_dump_pdp(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct gtp_dev *last_gtp = (struct gtp_dev *)cb->args[2], *gtp; + struct net *net = sock_net(skb->sk); + struct gtp_net *gn = net_generic(net, gtp_net_id); + unsigned long tid = cb->args[1]; + int i, k = cb->args[0], ret; + struct pdp_ctx *pctx; + + if (cb->args[4]) + return 0; + + list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { + if (last_gtp && last_gtp != gtp) + continue; + else + last_gtp = NULL; + + for (i = k; i < gtp->hash_size; i++) { + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { + if (tid && tid != pctx->u.tid) + continue; + else + tid = 0; + + ret = gtp_genl_fill_info(skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + cb->nlh->nlmsg_type, pctx); + if (ret < 0) { + cb->args[0] = i; + cb->args[1] = pctx->u.tid; + cb->args[2] = (unsigned long)gtp; + goto out; + } + } + } + } + cb->args[4] = 1; +out: + return skb->len; +} + +static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { + [GTPA_LINK] = { .type = NLA_U32, }, + [GTPA_VERSION] = { .type = NLA_U32, }, + [GTPA_TID] = { .type = NLA_U64, }, + [GTPA_SGSN_ADDRESS] = { .type = NLA_U32, }, + [GTPA_MS_ADDRESS] = { .type = NLA_U32, }, + [GTPA_FLOW] = { .type = NLA_U16, }, + [GTPA_NET_NS_FD] = { .type = NLA_U32, }, + [GTPA_I_TEI] = { .type = NLA_U32, }, + [GTPA_O_TEI] = { .type = NLA_U32, }, +}; + +static const struct genl_ops gtp_genl_ops[] = { + { + .cmd = GTP_CMD_NEWPDP, + .doit = gtp_genl_new_pdp, + .policy = gtp_genl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = GTP_CMD_DELPDP, + .doit = gtp_genl_del_pdp, + .policy = gtp_genl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = GTP_CMD_GETPDP, + .doit = gtp_genl_get_pdp, + .dumpit = gtp_genl_dump_pdp, + .policy = gtp_genl_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + +static int __net_init gtp_net_init(struct net *net) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + + INIT_LIST_HEAD(&gn->gtp_dev_list); + return 0; +} + +static void __net_exit gtp_net_exit(struct net *net) +{ + struct gtp_net *gn = net_generic(net, gtp_net_id); + struct gtp_dev *gtp; + LIST_HEAD(list); + + rtnl_lock(); + list_for_each_entry(gtp, &gn->gtp_dev_list, list) + gtp_dellink(gtp->dev, &list); + + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations gtp_net_ops = { + .init = gtp_net_init, + .exit = gtp_net_exit, + .id = >p_net_id, + .size = sizeof(struct gtp_net), +}; + +static int __init gtp_init(void) +{ + int err; + + get_random_bytes(>p_h_initval, sizeof(gtp_h_initval)); + + err = rtnl_link_register(>p_link_ops); + if (err < 0) + goto error_out; + + err = genl_register_family_with_ops(>p_genl_family, gtp_genl_ops); + if (err < 0) + goto unreg_rtnl_link; + + err = register_pernet_subsys(>p_net_ops); + if (err < 0) + goto unreg_genl_family; + + pr_info("GTP module loaded (pdp ctx size %Zd bytes)\n", + sizeof(struct pdp_ctx)); + return 0; + +unreg_genl_family: + genl_unregister_family(>p_genl_family); +unreg_rtnl_link: + rtnl_link_unregister(>p_link_ops); +error_out: + pr_err("error loading GTP module loaded\n"); + return err; +} +late_initcall(gtp_init); + +static void __exit gtp_fini(void) +{ + unregister_pernet_subsys(>p_net_ops); + genl_unregister_family(>p_genl_family); + rtnl_link_unregister(>p_link_ops); + + pr_info("GTP module unloaded\n"); +} +module_exit(gtp_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <hwelte@sysmocom.de>"); +MODULE_DESCRIPTION("Interface driver for GTP encapsulated traffic"); +MODULE_ALIAS_RTNL_LINK("gtp"); diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index eb6663866c9f..78dbc44540f6 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -635,10 +635,10 @@ static int receive(struct net_device *dev, int cnt) #ifdef __i386__ #include <asm/msr.h> -#define GETTICK(x) \ -({ \ - if (cpu_has_tsc) \ - x = (unsigned int)rdtsc(); \ +#define GETTICK(x) \ +({ \ + if (boot_cpu_has(X86_FEATURE_TSC)) \ + x = (unsigned int)rdtsc(); \ }) #else /* __i386__ */ #define GETTICK(x) diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 85828f153445..1dfe2304daa7 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -519,7 +519,7 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) dev->stats.tx_packets++; dev->stats.tx_bytes += actual; - ax->dev->trans_start = jiffies; + netif_trans_update(ax->dev); ax->xleft = count - actual; ax->xhead = ax->xbuff + actual; } @@ -542,7 +542,7 @@ static netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev) * May be we must check transmitter timeout here ? * 14 Oct 1994 Dmitry Gorodchanin. */ - if (time_before(jiffies, dev->trans_start + 20 * HZ)) { + if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { /* 20 sec timeout not reached */ return NETDEV_TX_BUSY; } diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index ce88df33fe17..b8083161ef46 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1669,7 +1669,7 @@ static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb(skb_del); } skb_queue_tail(&scc->tx_queue, skb); - dev->trans_start = jiffies; + netif_trans_update(dev); /* diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 1a4729c36aa4..aaff07c10058 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -601,7 +601,7 @@ static netdev_tx_t yam_send_packet(struct sk_buff *skb, return ax25_ip_xmit(skb); skb_queue_tail(&yp->send_queue, skb); - dev->trans_start = jiffies; + netif_trans_update(dev); return NETDEV_TX_OK; } diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 6700a4dca7c8..c270c5a54f3a 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -158,7 +158,7 @@ enum rndis_device_state { }; struct rndis_device { - struct netvsc_device *net_dev; + struct net_device *ndev; enum rndis_device_state state; bool link_state; @@ -202,7 +202,7 @@ int rndis_filter_receive(struct hv_device *dev, int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac); -void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf); +void netvsc_switch_datapath(struct net_device *nv_dev, bool vf); #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) @@ -653,6 +653,8 @@ struct garp_wrk { struct net_device_context { /* point back to our device context */ struct hv_device *device_ctx; + /* netvsc_device */ + struct netvsc_device *nvdev; /* reconfigure work */ struct delayed_work dwork; /* last reconfig time */ @@ -672,17 +674,17 @@ struct net_device_context { /* Ethtool settings */ u8 duplex; u32 speed; + + /* the device is going away */ + bool start_remove; }; /* Per netvsc device */ struct netvsc_device { - struct hv_device *dev; - u32 nvsp_version; atomic_t num_outstanding_sends; wait_queue_head_t wait_drain; - bool start_remove; bool destroy; /* Receive buffer allocated by us but manages by NetVSP */ @@ -708,8 +710,6 @@ struct netvsc_device { struct nvsp_message revoke_packet; /* unsigned char HwMacAddr[HW_MACADDR_LEN]; */ - struct net_device *ndev; - struct vmbus_channel *chn_table[VRSS_CHANNEL_MAX]; u32 send_table[VRSS_SEND_TAB_SIZE]; u32 max_chn; @@ -732,9 +732,6 @@ struct netvsc_device { u32 max_pkt; /* max number of pkt in one send, e.g. 8 */ u32 pkt_align; /* alignment bytes, e.g. 8 */ - /* The net device context */ - struct net_device_context *nd_ctx; - /* 1: allocated, serial number is valid. 0: not allocated */ u32 vf_alloc; /* Serial number of the VF to team with */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index eddce3cdafa8..719cb3578e55 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -37,10 +37,12 @@ * Switch the data path from the synthetic interface to the VF * interface. */ -void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf) +void netvsc_switch_datapath(struct net_device *ndev, bool vf) { + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct hv_device *dev = net_device_ctx->device_ctx; + struct netvsc_device *nv_dev = net_device_ctx->nvdev; struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt; - struct hv_device *dev = nv_dev->dev; memset(init_pkt, 0, sizeof(struct nvsp_message)); init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH; @@ -58,10 +60,9 @@ void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf) } -static struct netvsc_device *alloc_net_device(struct hv_device *device) +static struct netvsc_device *alloc_net_device(void) { struct netvsc_device *net_device; - struct net_device *ndev = hv_get_drvdata(device); net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL); if (!net_device) @@ -74,19 +75,15 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device) } init_waitqueue_head(&net_device->wait_drain); - net_device->start_remove = false; net_device->destroy = false; atomic_set(&net_device->open_cnt, 0); atomic_set(&net_device->vf_use_cnt, 0); - net_device->dev = device; - net_device->ndev = ndev; net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; net_device->vf_netdev = NULL; net_device->vf_inject = false; - hv_set_drvdata(device, net_device); return net_device; } @@ -98,9 +95,10 @@ static void free_netvsc_device(struct netvsc_device *nvdev) static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { - struct netvsc_device *net_device; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; - net_device = hv_get_drvdata(device); if (net_device && net_device->destroy) net_device = NULL; @@ -109,9 +107,9 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device) static struct netvsc_device *get_inbound_net_device(struct hv_device *device) { - struct netvsc_device *net_device; - - net_device = hv_get_drvdata(device); + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; if (!net_device) goto get_in_err; @@ -125,11 +123,13 @@ get_in_err: } -static int netvsc_destroy_buf(struct netvsc_device *net_device) +static int netvsc_destroy_buf(struct hv_device *device) { struct nvsp_message *revoke_packet; int ret = 0; - struct net_device *ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; /* * If we got a section count, it means we received a @@ -147,7 +147,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) revoke_packet->msg.v1_msg. revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; - ret = vmbus_sendpacket(net_device->dev->channel, + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), (unsigned long)revoke_packet, @@ -165,8 +165,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) /* Teardown the gpadl on the vsp end */ if (net_device->recv_buf_gpadl_handle) { - ret = vmbus_teardown_gpadl(net_device->dev->channel, - net_device->recv_buf_gpadl_handle); + ret = vmbus_teardown_gpadl(device->channel, + net_device->recv_buf_gpadl_handle); /* If we failed here, we might as well return and have a leak * rather than continue and a bugchk @@ -207,7 +207,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) revoke_packet->msg.v1_msg.revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; - ret = vmbus_sendpacket(net_device->dev->channel, + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), (unsigned long)revoke_packet, @@ -223,7 +223,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) } /* Teardown the gpadl on the vsp end */ if (net_device->send_buf_gpadl_handle) { - ret = vmbus_teardown_gpadl(net_device->dev->channel, + ret = vmbus_teardown_gpadl(device->channel, net_device->send_buf_gpadl_handle); /* If we failed here, we might as well return and have a leak @@ -258,7 +258,7 @@ static int netvsc_init_buf(struct hv_device *device) net_device = get_outbound_net_device(device); if (!net_device) return -ENODEV; - ndev = net_device->ndev; + ndev = hv_get_drvdata(device); node = cpu_to_node(device->channel->target_cpu); net_device->recv_buf = vzalloc_node(net_device->recv_buf_size, node); @@ -435,7 +435,7 @@ static int netvsc_init_buf(struct hv_device *device) goto exit; cleanup: - netvsc_destroy_buf(net_device); + netvsc_destroy_buf(device); exit: return ret; @@ -448,6 +448,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, struct nvsp_message *init_packet, u32 nvsp_ver) { + struct net_device *ndev = hv_get_drvdata(device); int ret; unsigned long t; @@ -481,8 +482,7 @@ static int negotiate_nvsp_ver(struct hv_device *device, /* NVSPv2 or later: Send NDIS config */ memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; - init_packet->msg.v2_msg.send_ndis_config.mtu = net_device->ndev->mtu + - ETH_HLEN; + init_packet->msg.v2_msg.send_ndis_config.mtu = ndev->mtu + ETH_HLEN; init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1; if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5) @@ -502,7 +502,6 @@ static int netvsc_connect_vsp(struct hv_device *device) struct netvsc_device *net_device; struct nvsp_message *init_packet; int ndis_version; - struct net_device *ndev; u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 }; int i, num_ver = 4; /* number of different NVSP versions */ @@ -510,7 +509,6 @@ static int netvsc_connect_vsp(struct hv_device *device) net_device = get_outbound_net_device(device); if (!net_device) return -ENODEV; - ndev = net_device->ndev; init_packet = &net_device->channel_init_pkt; @@ -566,9 +564,9 @@ cleanup: return ret; } -static void netvsc_disconnect_vsp(struct netvsc_device *net_device) +static void netvsc_disconnect_vsp(struct hv_device *device) { - netvsc_destroy_buf(net_device); + netvsc_destroy_buf(device); } /* @@ -576,24 +574,13 @@ static void netvsc_disconnect_vsp(struct netvsc_device *net_device) */ int netvsc_device_remove(struct hv_device *device) { - struct netvsc_device *net_device; - unsigned long flags; - - net_device = hv_get_drvdata(device); - - netvsc_disconnect_vsp(net_device); + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; - /* - * Since we have already drained, we don't need to busy wait - * as was done in final_release_stor_device() - * Note that we cannot set the ext pointer to NULL until - * we have drained - to drain the outgoing packets, we need to - * allow incoming packets. - */ + netvsc_disconnect_vsp(device); - spin_lock_irqsave(&device->channel->inbound_lock, flags); - hv_set_drvdata(device, NULL); - spin_unlock_irqrestore(&device->channel->inbound_lock, flags); + net_device_ctx->nvdev = NULL; /* * At this point, no one should be accessing net_device @@ -641,12 +628,11 @@ static void netvsc_send_completion(struct netvsc_device *net_device, { struct nvsp_message *nvsp_packet; struct hv_netvsc_packet *nvsc_packet; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); u32 send_index; struct sk_buff *skb; - ndev = net_device->ndev; - nvsp_packet = (struct nvsp_message *)((unsigned long)packet + (packet->offset8 << 3)); @@ -691,7 +677,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, wake_up(&net_device->wait_drain); if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && - !net_device->start_remove && + !net_device_ctx->start_remove && (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) netif_tx_wake_queue(netdev_get_tx_queue( @@ -775,6 +761,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, } static inline int netvsc_send_pkt( + struct hv_device *device, struct hv_netvsc_packet *packet, struct netvsc_device *net_device, struct hv_page_buffer **pb, @@ -783,7 +770,7 @@ static inline int netvsc_send_pkt( struct nvsp_message nvmsg; u16 q_idx = packet->q_idx; struct vmbus_channel *out_channel = net_device->chn_table[q_idx]; - struct net_device *ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); u64 req_id; int ret; struct hv_page_buffer *pgbuf; @@ -978,7 +965,8 @@ int netvsc_send(struct hv_device *device, } if (msd_send) { - m_ret = netvsc_send_pkt(msd_send, net_device, NULL, msd_skb); + m_ret = netvsc_send_pkt(device, msd_send, net_device, + NULL, msd_skb); if (m_ret != 0) { netvsc_free_send_slot(net_device, @@ -989,7 +977,7 @@ int netvsc_send(struct hv_device *device, send_now: if (cur_send) - ret = netvsc_send_pkt(cur_send, net_device, pb, skb); + ret = netvsc_send_pkt(device, cur_send, net_device, pb, skb); if (ret != 0 && section_index != NETVSC_INVALID_INDEX) netvsc_free_send_slot(net_device, section_index); @@ -1005,9 +993,7 @@ static void netvsc_send_recv_completion(struct hv_device *device, struct nvsp_message recvcompMessage; int retries = 0; int ret; - struct net_device *ndev; - - ndev = net_device->ndev; + struct net_device *ndev = hv_get_drvdata(device); recvcompMessage.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE; @@ -1054,11 +1040,9 @@ static void netvsc_receive(struct netvsc_device *net_device, u32 status = NVSP_STAT_SUCCESS; int i; int count = 0; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); void *data; - ndev = net_device->ndev; - /* * All inbound packets other than send completion should be xfer page * packet @@ -1114,14 +1098,13 @@ static void netvsc_send_table(struct hv_device *hdev, struct nvsp_message *nvmsg) { struct netvsc_device *nvscdev; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(hdev); int i; u32 count, *tab; nvscdev = get_outbound_net_device(hdev); if (!nvscdev) return; - ndev = nvscdev->ndev; count = nvmsg->msg.v5_msg.send_table.count; if (count != VRSS_SEND_TAB_SIZE) { @@ -1180,7 +1163,7 @@ void netvsc_channel_cb(void *context) net_device = get_inbound_net_device(device); if (!net_device) return; - ndev = net_device->ndev; + ndev = hv_get_drvdata(device); buffer = get_per_channel_state(channel); do { @@ -1253,30 +1236,19 @@ void netvsc_channel_cb(void *context) */ int netvsc_device_add(struct hv_device *device, void *additional_info) { - int ret = 0; + int i, ret = 0; int ring_size = ((struct netvsc_device_info *)additional_info)->ring_size; struct netvsc_device *net_device; - struct net_device *ndev; + struct net_device *ndev = hv_get_drvdata(device); + struct net_device_context *net_device_ctx = netdev_priv(ndev); - net_device = alloc_net_device(device); + net_device = alloc_net_device(); if (!net_device) return -ENOMEM; net_device->ring_size = ring_size; - /* - * Coming into this function, struct net_device * is - * registered as the driver private data. - * In alloc_net_device(), we register struct netvsc_device * - * as the driver private data and stash away struct net_device * - * in struct netvsc_device *. - */ - ndev = net_device->ndev; - - /* Add netvsc_device context to netvsc_device */ - net_device->nd_ctx = netdev_priv(ndev); - /* Initialize the NetVSC channel extension */ init_completion(&net_device->channel_init_wait); @@ -1295,7 +1267,19 @@ int netvsc_device_add(struct hv_device *device, void *additional_info) /* Channel is opened */ pr_info("hv_netvsc channel opened successfully\n"); - net_device->chn_table[0] = device->channel; + /* If we're reopening the device we may have multiple queues, fill the + * chn_table with the default channel to use it before subchannels are + * opened. + */ + for (i = 0; i < VRSS_CHANNEL_MAX; i++) + net_device->chn_table[i] = device->channel; + + /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is + * populated. + */ + wmb(); + + net_device_ctx->nvdev = net_device; /* Connect with the NetVsp */ ret = netvsc_connect_vsp(device); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index ba3f3f3d48ef..6a69b5cc9fe2 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -67,18 +67,19 @@ static void do_set_multicast(struct work_struct *w) { struct net_device_context *ndevctx = container_of(w, struct net_device_context, work); - struct netvsc_device *nvdev; + struct hv_device *device_obj = ndevctx->device_ctx; + struct net_device *ndev = hv_get_drvdata(device_obj); + struct netvsc_device *nvdev = ndevctx->nvdev; struct rndis_device *rdev; - nvdev = hv_get_drvdata(ndevctx->device_ctx); - if (nvdev == NULL || nvdev->ndev == NULL) + if (!nvdev) return; rdev = nvdev->extension; if (rdev == NULL) return; - if (nvdev->ndev->flags & IFF_PROMISC) + if (ndev->flags & IFF_PROMISC) rndis_filter_set_packet_filter(rdev, NDIS_PACKET_TYPE_PROMISCUOUS); else @@ -99,7 +100,7 @@ static int netvsc_open(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *device_obj = net_device_ctx->device_ctx; - struct netvsc_device *nvdev; + struct netvsc_device *nvdev = net_device_ctx->nvdev; struct rndis_device *rdev; int ret = 0; @@ -114,7 +115,6 @@ static int netvsc_open(struct net_device *net) netif_tx_wake_all_queues(net); - nvdev = hv_get_drvdata(device_obj); rdev = nvdev->extension; if (!rdev->link_state) netif_carrier_on(net); @@ -126,7 +126,7 @@ static int netvsc_close(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *device_obj = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(device_obj); + struct netvsc_device *nvdev = net_device_ctx->nvdev; int ret; u32 aread, awrite, i, msec = 10, retry = 0, retry_max = 20; struct vmbus_channel *chn; @@ -205,8 +205,7 @@ static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { struct net_device_context *net_device_ctx = netdev_priv(ndev); - struct hv_device *hdev = net_device_ctx->device_ctx; - struct netvsc_device *nvsc_dev = hv_get_drvdata(hdev); + struct netvsc_device *nvsc_dev = net_device_ctx->nvdev; u32 hash; u16 q_idx = 0; @@ -580,7 +579,6 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, struct rndis_indicate_status *indicate = &resp->msg.indicate_status; struct net_device *net; struct net_device_context *ndev_ctx; - struct netvsc_device *net_device; struct netvsc_reconfig *event; unsigned long flags; @@ -590,8 +588,7 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, indicate->status != RNDIS_STATUS_MEDIA_DISCONNECT) return; - net_device = hv_get_drvdata(device_obj); - net = net_device->ndev; + net = hv_get_drvdata(device_obj); if (!net || net->reg_state != NETREG_REGISTERED) return; @@ -659,16 +656,15 @@ int netvsc_recv_callback(struct hv_device *device_obj, struct vmbus_channel *channel, u16 vlan_tci) { - struct net_device *net; - struct net_device_context *net_device_ctx; + struct net_device *net = hv_get_drvdata(device_obj); + struct net_device_context *net_device_ctx = netdev_priv(net); struct sk_buff *skb; struct sk_buff *vf_skb; struct netvsc_stats *rx_stats; - struct netvsc_device *netvsc_dev = hv_get_drvdata(device_obj); + struct netvsc_device *netvsc_dev = net_device_ctx->nvdev; u32 bytes_recvd = packet->total_data_buflen; int ret = 0; - net = netvsc_dev->ndev; if (!net || net->reg_state != NETREG_REGISTERED) return NVSP_STAT_FAIL; @@ -743,8 +739,7 @@ static void netvsc_get_channels(struct net_device *net, struct ethtool_channels *channel) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(dev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; if (nvdev) { channel->max_combined = nvdev->max_chn; @@ -757,14 +752,14 @@ static int netvsc_set_channels(struct net_device *net, { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(dev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; struct netvsc_device_info device_info; u32 num_chn; u32 max_chn; int ret = 0; bool recovering = false; - if (!nvdev || nvdev->destroy) + if (net_device_ctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; num_chn = nvdev->num_chn; @@ -793,14 +788,11 @@ static int netvsc_set_channels(struct net_device *net, goto out; do_set: - nvdev->start_remove = true; + net_device_ctx->start_remove = true; rndis_filter_device_remove(dev); nvdev->num_chn = channels->combined_count; - net_device_ctx->device_ctx = dev; - hv_set_drvdata(dev, net); - memset(&device_info, 0, sizeof(device_info)); device_info.num_chn = nvdev->num_chn; /* passed to RNDIS */ device_info.ring_size = ring_size; @@ -815,7 +807,7 @@ static int netvsc_set_channels(struct net_device *net, goto recover; } - nvdev = hv_get_drvdata(dev); + nvdev = net_device_ctx->nvdev; ret = netif_set_real_num_tx_queues(net, nvdev->num_chn); if (ret) { @@ -837,6 +829,9 @@ static int netvsc_set_channels(struct net_device *net, out: netvsc_open(net); + net_device_ctx->start_remove = false; + /* We may have missed link change notifications */ + schedule_delayed_work(&net_device_ctx->dwork, 0); return ret; @@ -905,14 +900,14 @@ static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); - struct hv_device *hdev = ndevctx->device_ctx; - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct netvsc_device *nvdev = ndevctx->nvdev; + struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; int limit = ETH_DATA_LEN; u32 num_chn; int ret = 0; - if (nvdev == NULL || nvdev->destroy) + if (ndevctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2) @@ -927,14 +922,11 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) num_chn = nvdev->num_chn; - nvdev->start_remove = true; + ndevctx->start_remove = true; rndis_filter_device_remove(hdev); ndev->mtu = mtu; - ndevctx->device_ctx = hdev; - hv_set_drvdata(hdev, ndev); - memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; device_info.num_chn = num_chn; @@ -943,6 +935,10 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) out: netvsc_open(ndev); + ndevctx->start_remove = false; + + /* We may have missed link change notifications */ + schedule_delayed_work(&ndevctx->dwork, 0); return ret; } @@ -1055,18 +1051,22 @@ static const struct net_device_ops device_ops = { */ static void netvsc_link_change(struct work_struct *w) { - struct net_device_context *ndev_ctx; - struct net_device *net; + struct net_device_context *ndev_ctx = + container_of(w, struct net_device_context, dwork.work); + struct hv_device *device_obj = ndev_ctx->device_ctx; + struct net_device *net = hv_get_drvdata(device_obj); struct netvsc_device *net_device; struct rndis_device *rdev; struct netvsc_reconfig *event = NULL; bool notify = false, reschedule = false; unsigned long flags, next_reconfig, delay; - ndev_ctx = container_of(w, struct net_device_context, dwork.work); - net_device = hv_get_drvdata(ndev_ctx->device_ctx); + rtnl_lock(); + if (ndev_ctx->start_remove) + goto out_unlock; + + net_device = ndev_ctx->nvdev; rdev = net_device->extension; - net = net_device->ndev; next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; if (time_is_after_jiffies(next_reconfig)) { @@ -1077,7 +1077,7 @@ static void netvsc_link_change(struct work_struct *w) delay = next_reconfig - jiffies; delay = delay < LINKCHANGE_INT ? delay : LINKCHANGE_INT; schedule_delayed_work(&ndev_ctx->dwork, delay); - return; + goto out_unlock; } ndev_ctx->last_reconfig = jiffies; @@ -1091,9 +1091,7 @@ static void netvsc_link_change(struct work_struct *w) spin_unlock_irqrestore(&ndev_ctx->lock, flags); if (!event) - return; - - rtnl_lock(); + goto out_unlock; switch (event->event) { /* Only the following events are possible due to the check in @@ -1142,6 +1140,11 @@ static void netvsc_link_change(struct work_struct *w) */ if (reschedule) schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT); + + return; + +out_unlock: + rtnl_unlock(); } static void netvsc_free_netdev(struct net_device *netdev) @@ -1164,10 +1167,9 @@ static void netvsc_notify_peers(struct work_struct *wrk) atomic_dec(&gwrk->netvsc_dev->vf_use_cnt); } -static struct netvsc_device *get_netvsc_device(char *mac) +static struct net_device *get_netvsc_net_device(char *mac) { - struct net_device *dev; - struct net_device_context *netvsc_ctx = NULL; + struct net_device *dev, *found = NULL; int rtnl_locked; rtnl_locked = rtnl_trylock(); @@ -1176,21 +1178,20 @@ static struct netvsc_device *get_netvsc_device(char *mac) if (memcmp(dev->dev_addr, mac, ETH_ALEN) == 0) { if (dev->netdev_ops != &device_ops) continue; - netvsc_ctx = netdev_priv(dev); + found = dev; break; } } if (rtnl_locked) rtnl_unlock(); - if (netvsc_ctx == NULL) - return NULL; - - return hv_get_drvdata(netvsc_ctx->device_ctx); + return found; } static int netvsc_register_vf(struct net_device *vf_netdev) { + struct net_device *ndev; + struct net_device_context *net_device_ctx; struct netvsc_device *netvsc_dev; const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; @@ -1202,11 +1203,16 @@ static int netvsc_register_vf(struct net_device *vf_netdev) * associate with the VF interface. If we don't find a matching * synthetic interface, move on. */ - netvsc_dev = get_netvsc_device(vf_netdev->dev_addr); + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; if (netvsc_dev == NULL) return NOTIFY_DONE; - netdev_info(netvsc_dev->ndev, "VF registering: %s\n", vf_netdev->name); + netdev_info(ndev, "VF registering: %s\n", vf_netdev->name); /* * Take a reference on the module. */ @@ -1218,6 +1224,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev) static int netvsc_vf_up(struct net_device *vf_netdev) { + struct net_device *ndev; struct netvsc_device *netvsc_dev; const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; struct net_device_context *net_device_ctx; @@ -1225,13 +1232,17 @@ static int netvsc_vf_up(struct net_device *vf_netdev) if (eth_ops == ðtool_ops) return NOTIFY_DONE; - netvsc_dev = get_netvsc_device(vf_netdev->dev_addr); + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL)) return NOTIFY_DONE; - netdev_info(netvsc_dev->ndev, "VF up: %s\n", vf_netdev->name); - net_device_ctx = netdev_priv(netvsc_dev->ndev); + netdev_info(ndev, "VF up: %s\n", vf_netdev->name); netvsc_dev->vf_inject = true; /* @@ -1242,11 +1253,10 @@ static int netvsc_vf_up(struct net_device *vf_netdev) /* * notify the host to switch the data path. */ - netvsc_switch_datapath(netvsc_dev, true); - netdev_info(netvsc_dev->ndev, "Data path switched to VF: %s\n", - vf_netdev->name); + netvsc_switch_datapath(ndev, true); + netdev_info(ndev, "Data path switched to VF: %s\n", vf_netdev->name); - netif_carrier_off(netvsc_dev->ndev); + netif_carrier_off(ndev); /* * Now notify peers. We are scheduling work to @@ -1264,6 +1274,7 @@ static int netvsc_vf_up(struct net_device *vf_netdev) static int netvsc_vf_down(struct net_device *vf_netdev) { + struct net_device *ndev; struct netvsc_device *netvsc_dev; struct net_device_context *net_device_ctx; const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; @@ -1271,13 +1282,17 @@ static int netvsc_vf_down(struct net_device *vf_netdev) if (eth_ops == ðtool_ops) return NOTIFY_DONE; - netvsc_dev = get_netvsc_device(vf_netdev->dev_addr); + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; if ((netvsc_dev == NULL) || (netvsc_dev->vf_netdev == NULL)) return NOTIFY_DONE; - netdev_info(netvsc_dev->ndev, "VF down: %s\n", vf_netdev->name); - net_device_ctx = netdev_priv(netvsc_dev->ndev); + netdev_info(ndev, "VF down: %s\n", vf_netdev->name); netvsc_dev->vf_inject = false; /* * Wait for currently active users to @@ -1286,16 +1301,15 @@ static int netvsc_vf_down(struct net_device *vf_netdev) while (atomic_read(&netvsc_dev->vf_use_cnt) != 0) udelay(50); - netvsc_switch_datapath(netvsc_dev, false); - netdev_info(netvsc_dev->ndev, "Data path switched from VF: %s\n", - vf_netdev->name); + netvsc_switch_datapath(ndev, false); + netdev_info(ndev, "Data path switched from VF: %s\n", vf_netdev->name); rndis_filter_close(net_device_ctx->device_ctx); - netif_carrier_on(netvsc_dev->ndev); + netif_carrier_on(ndev); /* * Notify peers. */ atomic_inc(&netvsc_dev->vf_use_cnt); - net_device_ctx->gwrk.netdev = netvsc_dev->ndev; + net_device_ctx->gwrk.netdev = ndev; net_device_ctx->gwrk.netvsc_dev = netvsc_dev; schedule_work(&net_device_ctx->gwrk.dwrk); @@ -1305,17 +1319,23 @@ static int netvsc_vf_down(struct net_device *vf_netdev) static int netvsc_unregister_vf(struct net_device *vf_netdev) { + struct net_device *ndev; struct netvsc_device *netvsc_dev; const struct ethtool_ops *eth_ops = vf_netdev->ethtool_ops; + struct net_device_context *net_device_ctx; if (eth_ops == ðtool_ops) return NOTIFY_DONE; - netvsc_dev = get_netvsc_device(vf_netdev->dev_addr); + ndev = get_netvsc_net_device(vf_netdev->dev_addr); + if (!ndev) + return NOTIFY_DONE; + + net_device_ctx = netdev_priv(ndev); + netvsc_dev = net_device_ctx->nvdev; if (netvsc_dev == NULL) return NOTIFY_DONE; - netdev_info(netvsc_dev->ndev, "VF unregistering: %s\n", - vf_netdev->name); + netdev_info(ndev, "VF unregistering: %s\n", vf_netdev->name); netvsc_dev->vf_netdev = NULL; module_put(THIS_MODULE); @@ -1358,6 +1378,9 @@ static int netvsc_probe(struct hv_device *dev, } hv_set_drvdata(dev, net); + + net_device_ctx->start_remove = false; + INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); INIT_WORK(&net_device_ctx->work, do_set_multicast); INIT_WORK(&net_device_ctx->gwrk.dwrk, netvsc_notify_peers); @@ -1389,7 +1412,7 @@ static int netvsc_probe(struct hv_device *dev, } memcpy(net->dev_addr, device_info.mac_adr, ETH_ALEN); - nvdev = hv_get_drvdata(dev); + nvdev = net_device_ctx->nvdev; netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); @@ -1411,17 +1434,24 @@ static int netvsc_remove(struct hv_device *dev) struct net_device_context *ndev_ctx; struct netvsc_device *net_device; - net_device = hv_get_drvdata(dev); - net = net_device->ndev; + net = hv_get_drvdata(dev); if (net == NULL) { dev_err(&dev->device, "No net device to remove\n"); return 0; } - net_device->start_remove = true; ndev_ctx = netdev_priv(net); + net_device = ndev_ctx->nvdev; + + /* Avoid racing with netvsc_change_mtu()/netvsc_set_channels() + * removing the device. + */ + rtnl_lock(); + ndev_ctx->start_remove = true; + rtnl_unlock(); + cancel_delayed_work_sync(&ndev_ctx->dwork); cancel_work_sync(&ndev_ctx->work); @@ -1436,6 +1466,8 @@ static int netvsc_remove(struct hv_device *dev) */ rndis_filter_device_remove(dev); + hv_set_drvdata(dev, NULL); + netvsc_free_netdev(net); return 0; } diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index a59cdebc9b4b..97c292b7dbea 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -126,11 +126,7 @@ static void put_rndis_request(struct rndis_device *dev, static void dump_rndis_message(struct hv_device *hv_dev, struct rndis_message *rndis_msg) { - struct net_device *netdev; - struct netvsc_device *net_device; - - net_device = hv_get_drvdata(hv_dev); - netdev = net_device->ndev; + struct net_device *netdev = hv_get_drvdata(hv_dev); switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: @@ -211,6 +207,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, struct hv_netvsc_packet *packet; struct hv_page_buffer page_buf[2]; struct hv_page_buffer *pb = page_buf; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); /* Setup the packet to send it */ packet = &req->pkt; @@ -236,7 +233,7 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } - ret = netvsc_send(dev->net_dev->dev, packet, NULL, &pb, NULL); + ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL); return ret; } @@ -262,9 +259,7 @@ static void rndis_filter_receive_response(struct rndis_device *dev, struct rndis_request *request = NULL; bool found = false; unsigned long flags; - struct net_device *ndev; - - ndev = dev->net_dev->ndev; + struct net_device *ndev = dev->ndev; spin_lock_irqsave(&dev->request_lock, flags); list_for_each_entry(request, &dev->req_list, list_ent) { @@ -355,6 +350,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, struct ndis_pkt_8021q_info *vlan; struct ndis_tcp_ip_checksum_info *csum_info; u16 vlan_tci = 0; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); rndis_pkt = &msg->msg.pkt; @@ -368,7 +364,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, * should be the data packet size plus the trailer padding size */ if (pkt->total_data_buflen < rndis_pkt->data_len) { - netdev_err(dev->net_dev->ndev, "rndis message buffer " + netdev_err(dev->ndev, "rndis message buffer " "overflow detected (got %u, min %u)" "...dropping this message!\n", pkt->total_data_buflen, rndis_pkt->data_len); @@ -390,7 +386,7 @@ static int rndis_filter_receive_data(struct rndis_device *dev, } csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - return netvsc_recv_callback(dev->net_dev->dev, pkt, data, + return netvsc_recv_callback(net_device_ctx->device_ctx, pkt, data, csum_info, channel, vlan_tci); } @@ -399,10 +395,11 @@ int rndis_filter_receive(struct hv_device *dev, void **data, struct vmbus_channel *channel) { - struct netvsc_device *net_dev = hv_get_drvdata(dev); + struct net_device *ndev = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_dev = net_device_ctx->nvdev; struct rndis_device *rndis_dev; struct rndis_message *rndis_msg; - struct net_device *ndev; int ret = 0; if (!net_dev) { @@ -410,8 +407,6 @@ int rndis_filter_receive(struct hv_device *dev, goto exit; } - ndev = net_dev->ndev; - /* Make sure the rndis device state is initialized */ if (!net_dev->extension) { netdev_err(ndev, "got rndis message but no rndis device - " @@ -430,7 +425,7 @@ int rndis_filter_receive(struct hv_device *dev, rndis_msg = *data; - if (netif_msg_rx_err(net_dev->nd_ctx)) + if (netif_msg_rx_err(net_device_ctx)) dump_rndis_message(dev, rndis_msg); switch (rndis_msg->ndis_msg_type) { @@ -550,9 +545,10 @@ static int rndis_filter_query_device_mac(struct rndis_device *dev) int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) { - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct net_device *ndev = hv_get_drvdata(hdev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; struct rndis_device *rdev = nvdev->extension; - struct net_device *ndev = nvdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct rndis_config_parameter_info *cpi; @@ -629,9 +625,10 @@ static int rndis_filter_set_offload_params(struct hv_device *hdev, struct ndis_offload_params *req_offloads) { - struct netvsc_device *nvdev = hv_get_drvdata(hdev); + struct net_device *ndev = hv_get_drvdata(hdev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; struct rndis_device *rdev = nvdev->extension; - struct net_device *ndev = nvdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct ndis_offload_params *offload_params; @@ -703,7 +700,7 @@ u8 netvsc_hash_key[HASH_KEYLEN] = { static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) { - struct net_device *ndev = rdev->net_dev->ndev; + struct net_device *ndev = rdev->ndev; struct rndis_request *request; struct rndis_set_request *set; struct rndis_set_complete *set_complete; @@ -799,9 +796,7 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) u32 status; int ret; unsigned long t; - struct net_device *ndev; - - ndev = dev->net_dev->ndev; + struct net_device *ndev = dev->ndev; request = get_rndis_request(dev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + @@ -856,7 +851,8 @@ static int rndis_filter_init_device(struct rndis_device *dev) u32 status; int ret; unsigned long t; - struct netvsc_device *nvdev = dev->net_dev; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; request = get_rndis_request(dev, RNDIS_MSG_INIT, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); @@ -879,7 +875,6 @@ static int rndis_filter_init_device(struct rndis_device *dev) goto cleanup; } - t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { @@ -910,8 +905,9 @@ static void rndis_filter_halt_device(struct rndis_device *dev) { struct rndis_request *request; struct rndis_halt_request *halt; - struct netvsc_device *nvdev = dev->net_dev; - struct hv_device *hdev = nvdev->dev; + struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct hv_device *hdev = net_device_ctx->device_ctx; ulong flags; /* Attempt to do a rndis device halt */ @@ -979,13 +975,14 @@ static int rndis_filter_close_device(struct rndis_device *dev) static void netvsc_sc_open(struct vmbus_channel *new_sc) { - struct netvsc_device *nvscdev; + struct net_device *ndev = + hv_get_drvdata(new_sc->primary_channel->device_obj); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *nvscdev = net_device_ctx->nvdev; u16 chn_index = new_sc->offermsg.offer.sub_channel_index; int ret; unsigned long flags; - nvscdev = hv_get_drvdata(new_sc->primary_channel->device_obj); - if (chn_index >= nvscdev->num_chn) return; @@ -1010,6 +1007,8 @@ int rndis_filter_device_add(struct hv_device *dev, void *additional_info) { int ret; + struct net_device *net = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(net); struct netvsc_device *net_device; struct rndis_device *rndis_device; struct netvsc_device_info *device_info = additional_info; @@ -1040,16 +1039,15 @@ int rndis_filter_device_add(struct hv_device *dev, return ret; } - /* Initialize the rndis device */ - net_device = hv_get_drvdata(dev); + net_device = net_device_ctx->nvdev; net_device->max_chn = 1; net_device->num_chn = 1; spin_lock_init(&net_device->sc_lock); net_device->extension = rndis_device; - rndis_device->net_dev = net_device; + rndis_device->ndev = net; /* Send the rndis initialization message */ ret = rndis_filter_init_device(rndis_device); @@ -1063,8 +1061,8 @@ int rndis_filter_device_add(struct hv_device *dev, ret = rndis_filter_query_device(rndis_device, RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE, &mtu, &size); - if (ret == 0 && size == sizeof(u32) && mtu < net_device->ndev->mtu) - net_device->ndev->mtu = mtu; + if (ret == 0 && size == sizeof(u32) && mtu < net->mtu) + net->mtu = mtu; /* Get the mac address */ ret = rndis_filter_query_device_mac(rndis_device); @@ -1198,7 +1196,9 @@ err_dev_remv: void rndis_filter_device_remove(struct hv_device *dev) { - struct netvsc_device *net_dev = hv_get_drvdata(dev); + struct net_device *ndev = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_dev = net_device_ctx->nvdev; struct rndis_device *rndis_dev = net_dev->extension; unsigned long t; @@ -1224,7 +1224,9 @@ void rndis_filter_device_remove(struct hv_device *dev) int rndis_filter_open(struct hv_device *dev) { - struct netvsc_device *net_device = hv_get_drvdata(dev); + struct net_device *ndev = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *net_device = net_device_ctx->nvdev; if (!net_device) return -EINVAL; @@ -1237,7 +1239,9 @@ int rndis_filter_open(struct hv_device *dev) int rndis_filter_close(struct hv_device *dev) { - struct netvsc_device *nvdev = hv_get_drvdata(dev); + struct net_device *ndev = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx = netdev_priv(ndev); + struct netvsc_device *nvdev = net_device_ctx->nvdev; if (!nvdev) return -EINVAL; diff --git a/drivers/net/ieee802154/adf7242.c b/drivers/net/ieee802154/adf7242.c index b82e39d24394..9fa7ac9f8e68 100644 --- a/drivers/net/ieee802154/adf7242.c +++ b/drivers/net/ieee802154/adf7242.c @@ -915,7 +915,6 @@ static void adf7242_debug(u8 irq1) (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "", (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "", (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : ""); - } #endif } diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index cc56fac3c3f8..66c0eeafcb5d 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -196,6 +196,7 @@ static const struct net_device_ops ifb_netdev_ops = { #define IFB_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | \ NETIF_F_TSO_ECN | NETIF_F_TSO | NETIF_F_TSO6 | \ + NETIF_F_GSO_ENCAP_ALL | \ NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_CTAG_TX | \ NETIF_F_HW_VLAN_STAG_TX) @@ -224,6 +225,8 @@ static void ifb_setup(struct net_device *dev) dev->tx_queue_len = TX_Q_LIMIT; dev->features |= IFB_FEATURES; + dev->hw_features |= dev->features; + dev->hw_enc_features |= dev->features; dev->vlan_features |= IFB_FEATURES & ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 64bb44d5d867..c285eafd3f1c 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1427,7 +1427,7 @@ static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, /* Check for empty frame */ if (!skb->len) { ali_ircc_change_speed(self, speed); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -1533,7 +1533,7 @@ static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, /* Restore bank register */ switch_bank(iobase, BANK0); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); @@ -1946,7 +1946,7 @@ static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, /* Check for empty frame */ if (!skb->len) { ali_ircc_change_speed(self, speed); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -1966,7 +1966,7 @@ static netdev_tx_t ali_ircc_sir_hard_xmit(struct sk_buff *skb, /* Turn on transmit finished interrupt. Will fire immediately! */ outb(UART_IER_THRI, iobase+UART_IER); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c index 303c4bd26e17..be5bb0b7f29c 100644 --- a/drivers/net/irda/bfin_sir.c +++ b/drivers/net/irda/bfin_sir.c @@ -531,7 +531,7 @@ static void bfin_sir_send_work(struct work_struct *work) bfin_sir_dma_tx_chars(dev); #endif bfin_sir_enable_tx(port); - dev->trans_start = jiffies; + netif_trans_update(dev); } static int bfin_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 25f21968fa5c..a198946bc54f 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -429,7 +429,7 @@ static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, * do an extra memcpy and increment packet counters... * Jean II */ irda_usb_change_speed_xbofs(self); - netdev->trans_start = jiffies; + netif_trans_update(netdev); /* Will netif_wake_queue() in callback */ goto drop; } @@ -526,7 +526,7 @@ static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; - netdev->trans_start = jiffies; + netif_trans_update(netdev); } spin_unlock_irqrestore(&self->lock, flags); diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index dc0dbd8dd0b5..aaecc3baaf30 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1253,7 +1253,7 @@ static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) */ static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) { - struct net_device *dev = self->netdev; + struct net_device *dev; __u8 mcr = MCR_SIR; int iobase; __u8 bank; @@ -1263,6 +1263,7 @@ static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) IRDA_ASSERT(self != NULL, return 0;); + dev = self->netdev; iobase = self->io.fir_base; /* Update accounting for new speed */ @@ -1399,7 +1400,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, * to make sure packets gets through the * proper xmit handler - Jean II */ } - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -1424,7 +1425,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_sir(struct sk_buff *skb, /* Restore bank register */ outb(bank, iobase+BSR); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); @@ -1470,7 +1471,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, * the speed change has been done. * Jean II */ } - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -1553,7 +1554,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, /* Restore bank register */ outb(bank, iobase+BSR); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index b455ffe8850c..dcf92ba80872 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -862,7 +862,7 @@ static void smsc_ircc_timeout(struct net_device *dev) spin_lock_irqsave(&self->lock, flags); smsc_ircc_sir_start(self); smsc_ircc_change_speed(self, self->io.speed); - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); spin_unlock_irqrestore(&self->lock, flags); } diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 83cc48a01802..42da094b68dd 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -718,7 +718,7 @@ static void stir_send(struct stir_cb *stir, struct sk_buff *skb) stir->netdev->stats.tx_packets++; stir->netdev->stats.tx_bytes += skb->len; - stir->netdev->trans_start = jiffies; + netif_trans_update(stir->netdev); pr_debug("send %d (%d)\n", skb->len, wraplen); if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index 6960d4cd3cae..ca4442a9d631 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -774,7 +774,7 @@ static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, /* Check for empty frame */ if (!skb->len) { via_ircc_change_speed(self, speed); - dev->trans_start = jiffies; + netif_trans_update(dev); dev_kfree_skb(skb); return NETDEV_TX_OK; } else @@ -821,7 +821,7 @@ static netdev_tx_t via_ircc_hard_xmit_sir(struct sk_buff *skb, RXStart(iobase, OFF); TXStart(iobase, ON); - dev->trans_start = jiffies; + netif_trans_update(dev); spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return NETDEV_TX_OK; @@ -849,7 +849,7 @@ static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, if ((speed != self->io.speed) && (speed != -1)) { if (!skb->len) { via_ircc_change_speed(self, speed); - dev->trans_start = jiffies; + netif_trans_update(dev); dev_kfree_skb(skb); return NETDEV_TX_OK; } else @@ -869,7 +869,7 @@ static netdev_tx_t via_ircc_hard_xmit_fir(struct sk_buff *skb, via_ircc_dma_xmit(self, iobase); //F01 } //F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev); - dev->trans_start = jiffies; + netif_trans_update(dev); dev_kfree_skb(skb); spin_unlock_irqrestore(&self->lock, flags); return NETDEV_TX_OK; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 3add2c4aac21..47ee2c840b55 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -85,7 +85,7 @@ struct gcm_iv { * @tfm: crypto struct, key storage */ struct macsec_key { - u64 id; + u8 id[MACSEC_KEYID_LEN]; struct crypto_aead *tfm; }; @@ -1530,7 +1530,8 @@ static const struct nla_policy macsec_genl_sa_policy[NUM_MACSEC_SA_ATTR] = { [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 }, [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 }, [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 }, - [MACSEC_SA_ATTR_KEYID] = { .type = NLA_U64 }, + [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY, + .len = MACSEC_KEYID_LEN, }, [MACSEC_SA_ATTR_KEY] = { .type = NLA_BINARY, .len = MACSEC_MAX_KEY_LEN, }, }; @@ -1577,6 +1578,9 @@ static bool validate_add_rxsa(struct nlattr **attrs) return false; } + if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN) + return false; + return true; } @@ -1642,7 +1646,7 @@ static int macsec_add_rxsa(struct sk_buff *skb, struct genl_info *info) if (tb_sa[MACSEC_SA_ATTR_ACTIVE]) rx_sa->active = !!nla_get_u8(tb_sa[MACSEC_SA_ATTR_ACTIVE]); - rx_sa->key.id = nla_get_u64(tb_sa[MACSEC_SA_ATTR_KEYID]); + nla_memcpy(rx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); rx_sa->sc = rx_sc; rcu_assign_pointer(rx_sc->sa[assoc_num], rx_sa); @@ -1723,6 +1727,9 @@ static bool validate_add_txsa(struct nlattr **attrs) return false; } + if (nla_len(attrs[MACSEC_SA_ATTR_KEYID]) != MACSEC_KEYID_LEN) + return false; + return true; } @@ -1778,7 +1785,7 @@ static int macsec_add_txsa(struct sk_buff *skb, struct genl_info *info) return -ENOMEM; } - tx_sa->key.id = nla_get_u64(tb_sa[MACSEC_SA_ATTR_KEYID]); + nla_memcpy(tx_sa->key.id, tb_sa[MACSEC_SA_ATTR_KEYID], MACSEC_KEYID_LEN); spin_lock_bh(&tx_sa->lock); tx_sa->next_pn = nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); @@ -2365,9 +2372,7 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev, if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || nla_put_u32(skb, MACSEC_SA_ATTR_PN, tx_sa->next_pn) || - nla_put_u64_64bit(skb, MACSEC_SA_ATTR_KEYID, - tx_sa->key.id, - MACSEC_SA_ATTR_PAD) || + nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, tx_sa->key.id) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, tx_sa->active)) { nla_nest_cancel(skb, txsa_nest); nla_nest_cancel(skb, txsa_list); @@ -2469,9 +2474,7 @@ static int dump_secy(struct macsec_secy *secy, struct net_device *dev, if (nla_put_u8(skb, MACSEC_SA_ATTR_AN, i) || nla_put_u32(skb, MACSEC_SA_ATTR_PN, rx_sa->next_pn) || - nla_put_u64_64bit(skb, MACSEC_SA_ATTR_KEYID, - rx_sa->key.id, - MACSEC_SA_ATTR_PAD) || + nla_put(skb, MACSEC_SA_ATTR_KEYID, MACSEC_KEYID_LEN, rx_sa->key.id) || nla_put_u8(skb, MACSEC_SA_ATTR_ACTIVE, rx_sa->active)) { nla_nest_cancel(skb, rxsa_nest); nla_nest_cancel(skb, rxsc_nest); diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 74cb15a2e032..bd6720962b1f 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -129,7 +129,18 @@ static DEFINE_MUTEX(minor_lock); static DEFINE_IDR(minor_idr); #define GOODCOPY_LEN 128 -static struct class *macvtap_class; +static const void *macvtap_net_namespace(struct device *d) +{ + struct net_device *dev = to_net_dev(d->parent); + return dev_net(dev); +} + +static struct class macvtap_class = { + .name = "macvtap", + .owner = THIS_MODULE, + .ns_type = &net_ns_type_operations, + .namespace = macvtap_net_namespace, +}; static struct cdev macvtap_cdev; static const struct proto_ops macvtap_socket_ops; @@ -373,7 +384,7 @@ static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb) goto wake_up; } - kfree_skb(skb); + consume_skb(skb); while (segs) { struct sk_buff *nskb = segs->next; @@ -1278,10 +1289,12 @@ static int macvtap_device_event(struct notifier_block *unused, struct device *classdev; dev_t devt; int err; + char tap_name[IFNAMSIZ]; if (dev->rtnl_link_ops != &macvtap_link_ops) return NOTIFY_DONE; + snprintf(tap_name, IFNAMSIZ, "tap%d", dev->ifindex); vlan = netdev_priv(dev); switch (event) { @@ -1295,19 +1308,24 @@ static int macvtap_device_event(struct notifier_block *unused, return notifier_from_errno(err); devt = MKDEV(MAJOR(macvtap_major), vlan->minor); - classdev = device_create(macvtap_class, &dev->dev, devt, - dev, "tap%d", dev->ifindex); + classdev = device_create(&macvtap_class, &dev->dev, devt, + dev, tap_name); if (IS_ERR(classdev)) { macvtap_free_minor(vlan); return notifier_from_errno(PTR_ERR(classdev)); } + err = sysfs_create_link(&dev->dev.kobj, &classdev->kobj, + tap_name); + if (err) + return notifier_from_errno(err); break; case NETDEV_UNREGISTER: /* vlan->minor == 0 if NETDEV_REGISTER above failed */ if (vlan->minor == 0) break; + sysfs_remove_link(&dev->dev.kobj, tap_name); devt = MKDEV(MAJOR(macvtap_major), vlan->minor); - device_destroy(macvtap_class, devt); + device_destroy(&macvtap_class, devt); macvtap_free_minor(vlan); break; } @@ -1333,11 +1351,9 @@ static int macvtap_init(void) if (err) goto out2; - macvtap_class = class_create(THIS_MODULE, "macvtap"); - if (IS_ERR(macvtap_class)) { - err = PTR_ERR(macvtap_class); + err = class_register(&macvtap_class); + if (err) goto out3; - } err = register_netdevice_notifier(&macvtap_notifier_block); if (err) @@ -1352,7 +1368,7 @@ static int macvtap_init(void) out5: unregister_netdevice_notifier(&macvtap_notifier_block); out4: - class_unregister(macvtap_class); + class_unregister(&macvtap_class); out3: cdev_del(&macvtap_cdev); out2: @@ -1366,7 +1382,7 @@ static void macvtap_exit(void) { rtnl_link_unregister(&macvtap_link_ops); unregister_netdevice_notifier(&macvtap_notifier_block); - class_unregister(macvtap_class); + class_unregister(&macvtap_class); cdev_del(&macvtap_cdev); unregister_chrdev_region(macvtap_major, MACVTAP_NUM_DEVS); idr_destroy(&minor_idr); diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index b3ffaee30858..f279a897a5c7 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -359,27 +359,25 @@ static void at803x_link_change_notify(struct phy_device *phydev) * in the FIFO. In such cases, the FIFO enters an error mode it * cannot recover from by software. */ - if (phydev->drv->phy_id == ATH8030_PHY_ID) { - if (phydev->state == PHY_NOLINK) { - if (priv->gpiod_reset && !priv->phy_reset) { - struct at803x_context context; - - at803x_context_save(phydev, &context); - - gpiod_set_value(priv->gpiod_reset, 1); - msleep(1); - gpiod_set_value(priv->gpiod_reset, 0); - msleep(1); - - at803x_context_restore(phydev, &context); - - phydev_dbg(phydev, "%s(): phy was reset\n", - __func__); - priv->phy_reset = true; - } - } else { - priv->phy_reset = false; + if (phydev->state == PHY_NOLINK) { + if (priv->gpiod_reset && !priv->phy_reset) { + struct at803x_context context; + + at803x_context_save(phydev, &context); + + gpiod_set_value(priv->gpiod_reset, 1); + msleep(1); + gpiod_set_value(priv->gpiod_reset, 0); + msleep(1); + + at803x_context_restore(phydev, &context); + + phydev_dbg(phydev, "%s(): phy was reset\n", + __func__); + priv->phy_reset = true; } + } else { + priv->phy_reset = false; } } @@ -391,7 +389,6 @@ static struct phy_driver at803x_driver[] = { .phy_id_mask = 0xffffffef, .probe = at803x_probe, .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, @@ -427,7 +424,6 @@ static struct phy_driver at803x_driver[] = { .phy_id_mask = 0xffffffef, .probe = at803x_probe, .config_init = at803x_config_init, - .link_change_notify = at803x_link_change_notify, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, .suspend = at803x_suspend, diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 9050f21e6f33..2d2e4339f0df 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -255,7 +255,8 @@ int fixed_phy_add(unsigned int irq, int phy_addr, memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); - fmb->mii_bus->irq[phy_addr] = irq; + if (irq != PHY_POLL) + fmb->mii_bus->irq[phy_addr] = irq; fp->addr = phy_addr; fp->status = *status; @@ -314,6 +315,9 @@ struct phy_device *fixed_phy_register(unsigned int irq, int phy_addr; int ret; + if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED) + return ERR_PTR(-EPROBE_DEFER); + /* Get the next available PHY address, up to PHY_MAX_ADDR */ spin_lock(&phy_fixed_addr_lock); if (phy_fixed_addr == PHY_MAX_ADDR) { diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index f6078376ef50..b9fde1bcf0f0 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -80,23 +80,15 @@ static int lxt970_ack_interrupt(struct phy_device *phydev) static int lxt970_config_intr(struct phy_device *phydev) { - int err; - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN); + return phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN); else - err = phy_write(phydev, MII_LXT970_IER, 0); - - return err; + return phy_write(phydev, MII_LXT970_IER, 0); } static int lxt970_config_init(struct phy_device *phydev) { - int err; - - err = phy_write(phydev, MII_LXT970_CONFIG, 0); - - return err; + return phy_write(phydev, MII_LXT970_CONFIG, 0); } @@ -112,14 +104,10 @@ static int lxt971_ack_interrupt(struct phy_device *phydev) static int lxt971_config_intr(struct phy_device *phydev) { - int err; - if (phydev->interrupts == PHY_INTERRUPT_ENABLED) - err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN); + return phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN); else - err = phy_write(phydev, MII_LXT971_IER, 0); - - return err; + return phy_write(phydev, MII_LXT971_IER, 0); } /* diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 388f9922647b..09deef4bed09 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -431,7 +431,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) err = phy_device_register(phydev); if (err) { phy_device_free(phydev); - return NULL; + return ERR_PTR(-ENODEV); } return phydev; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 4516c8a4fd82..5a8fefc25157 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -726,7 +726,7 @@ static int kszphy_probe(struct phy_device *phydev) static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KS8737", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, @@ -781,7 +781,7 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, }, { .phy_id = PHY_ID_KSZ8041, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8041", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), @@ -800,7 +800,7 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, }, { .phy_id = PHY_ID_KSZ8041RNLI, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8041RNLI", .features = PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause, @@ -819,7 +819,7 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, }, { .phy_id = PHY_ID_KSZ8051, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8051", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), @@ -857,7 +857,7 @@ static struct phy_driver ksphy_driver[] = { }, { .phy_id = PHY_ID_KSZ8081, .name = "Micrel KSZ8081 or KSZ8091", - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .driver_data = &ksz8081_type, @@ -875,7 +875,7 @@ static struct phy_driver ksphy_driver[] = { }, { .phy_id = PHY_ID_KSZ8061, .name = "Micrel KSZ8061", - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, .config_init = kszphy_config_init, @@ -909,7 +909,7 @@ static struct phy_driver ksphy_driver[] = { .write_mmd_indirect = ksz9021_wr_mmd_phyreg, }, { .phy_id = PHY_ID_KSZ9031, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ9031 Gigabit PHY", .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, @@ -926,7 +926,7 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, }, { .phy_id = PHY_ID_KSZ8873MLL, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ8873MLL Switch", .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause), .flags = PHY_HAS_MAGICANEG, @@ -940,7 +940,7 @@ static struct phy_driver ksphy_driver[] = { .resume = genphy_resume, }, { .phy_id = PHY_ID_KSZ886X, - .phy_id_mask = 0x00fffff0, + .phy_id_mask = MICREL_PHY_ID_MASK, .name = "Micrel KSZ886X Switch", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT, @@ -962,17 +962,17 @@ MODULE_LICENSE("GPL"); static struct mdio_device_id __maybe_unused micrel_tbl[] = { { PHY_ID_KSZ9021, 0x000ffffe }, - { PHY_ID_KSZ9031, 0x00fffff0 }, + { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK }, { PHY_ID_KSZ8001, 0x00ffffff }, - { PHY_ID_KS8737, 0x00fffff0 }, + { PHY_ID_KS8737, MICREL_PHY_ID_MASK }, { PHY_ID_KSZ8021, 0x00ffffff }, { PHY_ID_KSZ8031, 0x00ffffff }, - { PHY_ID_KSZ8041, 0x00fffff0 }, - { PHY_ID_KSZ8051, 0x00fffff0 }, - { PHY_ID_KSZ8061, 0x00fffff0 }, - { PHY_ID_KSZ8081, 0x00fffff0 }, - { PHY_ID_KSZ8873MLL, 0x00fffff0 }, - { PHY_ID_KSZ886X, 0x00fffff0 }, + { PHY_ID_KSZ8041, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ8051, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ8061, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ8081, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK }, + { PHY_ID_KSZ886X, MICREL_PHY_ID_MASK }, { } }; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 6f221c8c2a7f..c5dc2c363f96 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -871,9 +871,11 @@ void phy_start(struct phy_device *phydev) break; case PHY_HALTED: /* make sure interrupts are re-enabled for the PHY */ - err = phy_enable_interrupts(phydev); - if (err < 0) - break; + if (phydev->irq != PHY_POLL) { + err = phy_enable_interrupts(phydev); + if (err < 0) + break; + } phydev->state = PHY_RESUMING; do_resume = true; @@ -1347,3 +1349,27 @@ void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) phydev->drv->get_wol(phydev, wol); } EXPORT_SYMBOL(phy_ethtool_get_wol); + +int phy_ethtool_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_ksettings_get(phydev, cmd); +} +EXPORT_SYMBOL(phy_ethtool_get_link_ksettings); + +int phy_ethtool_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_ksettings_set(phydev, cmd); +} +EXPORT_SYMBOL(phy_ethtool_set_link_ksettings); diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index a17d86a57734..9ed6d1c1ee45 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -407,7 +407,7 @@ static void sl_encaps(struct slip *sl, unsigned char *icp, int len) set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); actual = sl->tty->ops->write(sl->tty, sl->xbuff, count); #ifdef SL_CHECK_TRANSMIT - sl->dev->trans_start = jiffies; + netif_trans_update(sl->dev); #endif sl->xleft = count - actual; sl->xhead = sl->xbuff + actual; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 425e983bab93..e16487cc6a9a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -580,11 +580,13 @@ static void tun_detach_all(struct net_device *dev) for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); BUG_ON(!tfile); + tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN; tfile->socket.sk->sk_data_ready(tfile->socket.sk); RCU_INIT_POINTER(tfile->tun, NULL); --tun->numqueues; } list_for_each_entry(tfile, &tun->disabled, next) { + tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN; tfile->socket.sk->sk_data_ready(tfile->socket.sk); RCU_INIT_POINTER(tfile->tun, NULL); } @@ -641,6 +643,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte goto out; } tfile->queue_index = tun->numqueues; + tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN; rcu_assign_pointer(tfile->tun, tun); rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); tun->numqueues++; @@ -1491,9 +1494,6 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, if (!iov_iter_count(to)) return 0; - if (tun->dev->reg_state != NETREG_REGISTERED) - return -EIO; - /* Read frames from queue */ skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0, &peeked, &off, &err); diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 0c5c22b84da8..7de5ab589e4e 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -66,7 +66,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, * buffer. */ if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { - offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); + offset = ((rx->remaining + 1) & 0xfffe); rx->header = get_unaligned_le32(skb->data + offset); offset = 0; diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 4e2b26a88b15..d9ca05d3ac8e 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -376,7 +376,7 @@ static int catc_tx_run(struct catc *catc) catc->tx_idx = !catc->tx_idx; catc->tx_ptr = 0; - catc->netdev->trans_start = jiffies; + netif_trans_update(catc->netdev); return status; } @@ -389,7 +389,7 @@ static void catc_tx_done(struct urb *urb) if (status == -ECONNRESET) { dev_dbg(&urb->dev->dev, "Tx Reset.\n"); urb->status = 0; - catc->netdev->trans_start = jiffies; + netif_trans_update(catc->netdev); catc->netdev->stats.tx_errors++; clear_bit(TX_RUNNING, &catc->flags); netif_wake_queue(catc->netdev); diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 2fb31edab125..53759c315b97 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -740,12 +740,14 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) int cdc_ncm_change_mtu(struct net_device *net, int new_mtu) { struct usbnet *dev = netdev_priv(net); - struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; - int maxmtu = ctx->max_datagram_size - cdc_ncm_eth_hlen(dev); + int maxmtu = cdc_ncm_max_dgram_size(dev) - cdc_ncm_eth_hlen(dev); if (new_mtu <= 0 || new_mtu > maxmtu) return -EINVAL; + net->mtu = new_mtu; + cdc_ncm_set_dgram_size(dev, new_mtu + cdc_ncm_eth_hlen(dev)); + return 0; } EXPORT_SYMBOL_GPL(cdc_ncm_change_mtu); diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index f64b25c221e8..770212baaf05 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -938,7 +938,7 @@ static void kaweth_tx_timeout(struct net_device *net) dev_warn(&net->dev, "%s: Tx timed out. Resetting.\n", net->name); kaweth->stats.tx_errors++; - net->trans_start = jiffies; + netif_trans_update(net); usb_unlink_urb(kaweth->tx_urb); } diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index f20890ee03f3..6a9d474b08b2 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -269,6 +269,7 @@ struct skb_data { /* skb->cb is one of these */ struct lan78xx_net *dev; enum skb_state state; size_t length; + int num_of_packet; }; struct usb_context { @@ -1803,7 +1804,34 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev) static void lan78xx_link_status_change(struct net_device *net) { - /* nothing to do */ + struct phy_device *phydev = net->phydev; + int ret, temp; + + /* At forced 100 F/H mode, chip may fail to set mode correctly + * when cable is switched between long(~50+m) and short one. + * As workaround, set to 10 before setting to 100 + * at forced 100 F/H mode. + */ + if (!phydev->autoneg && (phydev->speed == 100)) { + /* disable phy interrupt */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; + ret = phy_write(phydev, LAN88XX_INT_MASK, temp); + + temp = phy_read(phydev, MII_BMCR); + temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000); + phy_write(phydev, MII_BMCR, temp); /* set to 10 first */ + temp |= BMCR_SPEED100; + phy_write(phydev, MII_BMCR, temp); /* set to 100 later */ + + /* clear pending interrupt generated while workaround */ + temp = phy_read(phydev, LAN88XX_INT_STS); + + /* enable phy interrupt back */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp |= LAN88XX_INT_MASK_MDINTPIN_EN_; + ret = phy_write(phydev, LAN88XX_INT_MASK, temp); + } } static int lan78xx_phy_init(struct lan78xx_net *dev) @@ -2464,7 +2492,7 @@ static void tx_complete(struct urb *urb) struct lan78xx_net *dev = entry->dev; if (urb->status == 0) { - dev->net->stats.tx_packets++; + dev->net->stats.tx_packets += entry->num_of_packet; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; @@ -2681,10 +2709,11 @@ void lan78xx_skb_return(struct lan78xx_net *dev, struct sk_buff *skb) return; } - skb->protocol = eth_type_trans(skb, dev->net); dev->net->stats.rx_packets++; dev->net->stats.rx_bytes += skb->len; + skb->protocol = eth_type_trans(skb, dev->net); + netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", skb->len + sizeof(struct ethhdr), skb->protocol); memset(skb->cb, 0, sizeof(struct skb_data)); @@ -2934,13 +2963,16 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev) skb_totallen = 0; pkt_cnt = 0; + count = 0; + length = 0; for (skb = tqp->next; pkt_cnt < tqp->qlen; skb = skb->next) { if (skb_is_gso(skb)) { if (pkt_cnt) { /* handle previous packets first */ break; } - length = skb->len; + count = 1; + length = skb->len - TX_OVERHEAD; skb2 = skb_dequeue(tqp); goto gso_skb; } @@ -2961,14 +2993,13 @@ static void lan78xx_tx_bh(struct lan78xx_net *dev) for (count = pos = 0; count < pkt_cnt; count++) { skb2 = skb_dequeue(tqp); if (skb2) { + length += (skb2->len - TX_OVERHEAD); memcpy(skb->data + pos, skb2->data, skb2->len); pos += roundup(skb2->len, sizeof(u32)); dev_kfree_skb(skb2); } } - length = skb_totallen; - gso_skb: urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { @@ -2980,6 +3011,7 @@ gso_skb: entry->urb = urb; entry->dev = dev; entry->length = length; + entry->num_of_packet = count; spin_lock_irqsave(&dev->txq.lock, flags); ret = usb_autopm_get_interface_async(dev->intf); @@ -3013,7 +3045,7 @@ gso_skb: ret = usb_submit_urb(urb, GFP_ATOMIC); switch (ret) { case 0: - dev->net->trans_start = jiffies; + netif_trans_update(dev->net); lan78xx_queue_skb(&dev->txq, skb, tx_start); if (skb_queue_len(&dev->txq) >= dev->tx_qlen) netif_stop_queue(dev->net); @@ -3697,7 +3729,7 @@ int lan78xx_resume(struct usb_interface *intf) usb_free_urb(res); usb_autopm_put_interface_async(dev->intf); } else { - dev->net->trans_start = jiffies; + netif_trans_update(dev->net); lan78xx_queue_skb(&dev->txq, skb, tx_start); } } diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index f84080215915..36cd7f016a8d 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -411,7 +411,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) int ret; read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); - data[0] = 0xc9; + data[0] = 0xc8; /* TX & RX enable, append status, no CRC */ data[1] = 0; if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) data[1] |= 0x20; /* set full duplex */ @@ -497,7 +497,7 @@ static void read_bulk_callback(struct urb *urb) pkt_len = buf[count - 3] << 8; pkt_len += buf[count - 4]; pkt_len &= 0xfff; - pkt_len -= 8; + pkt_len -= 4; } /* @@ -528,7 +528,7 @@ static void read_bulk_callback(struct urb *urb) goon: usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); if (rx_status == -ENODEV) @@ -569,7 +569,7 @@ static void rx_fixup(unsigned long data) } usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); try_again: status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); @@ -615,7 +615,7 @@ static void write_bulk_callback(struct urb *urb) break; } - net->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(net); /* prevent tx timeout */ netif_wake_queue(net); } @@ -823,7 +823,7 @@ static int pegasus_open(struct net_device *net) usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), - pegasus->rx_skb->data, PEGASUS_MTU + 8, + pegasus->rx_skb->data, PEGASUS_MTU, read_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { if (res == -ENODEV) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index d1f78c2c97aa..3f9f6ed3eec4 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3366,7 +3366,7 @@ static void r8153_init(struct r8152 *tp) ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; - if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) + if (tp->version == RTL_VER_04 && tp->udev->speed < USB_SPEED_SUPER) ocp_data |= LPM_TIMER_500MS; else ocp_data |= LPM_TIMER_500US; @@ -4211,6 +4211,7 @@ static int rtl8152_probe(struct usb_interface *intf, switch (udev->speed) { case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: tp->coalesce = COALESCE_SUPER; break; case USB_SPEED_HIGH: diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index d37b7dce2d40..7c72bfac89d0 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -451,7 +451,7 @@ static void write_bulk_callback(struct urb *urb) if (status) dev_info(&urb->dev->dev, "%s: Tx status %d\n", dev->netdev->name, status); - dev->netdev->trans_start = jiffies; + netif_trans_update(dev->netdev); netif_wake_queue(dev->netdev); } @@ -694,7 +694,7 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, } else { netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; - netdev->trans_start = jiffies; + netif_trans_update(netdev); } return NETDEV_TX_OK; diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 30033dbe6662..9af9799935db 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -29,6 +29,7 @@ #include <linux/crc32.h> #include <linux/usb/usbnet.h> #include <linux/slab.h> +#include <linux/of_net.h> #include "smsc75xx.h" #define SMSC_CHIPNAME "smsc75xx" @@ -98,9 +99,11 @@ static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, &buf, 4); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", index, ret); + return ret; + } le32_to_cpus(&buf); *data = buf; @@ -761,6 +764,15 @@ static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) static void smsc75xx_init_mac_address(struct usbnet *dev) { + const u8 *mac_addr; + + /* maybe the boot loader passed the MAC address in devicetree */ + mac_addr = of_get_mac_address(dev->udev->dev.of_node); + if (mac_addr) { + memcpy(dev->net->dev_addr, mac_addr, ETH_ALEN); + return; + } + /* try reading mac address from EEPROM */ if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, dev->net->dev_addr) == 0) { @@ -772,7 +784,7 @@ static void smsc75xx_init_mac_address(struct usbnet *dev) } } - /* no eeprom, or eeprom values are invalid. generate random MAC */ + /* no useful static MAC address found. generate a random one */ eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); } diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 66b3ab9f614e..d9d2806a47b1 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -29,6 +29,7 @@ #include <linux/crc32.h> #include <linux/usb/usbnet.h> #include <linux/slab.h> +#include <linux/of_net.h> #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" @@ -91,9 +92,11 @@ static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, &buf, 4); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", index, ret); + return ret; + } le32_to_cpus(&buf); *data = buf; @@ -765,6 +768,15 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) static void smsc95xx_init_mac_address(struct usbnet *dev) { + const u8 *mac_addr; + + /* maybe the boot loader passed the MAC address in devicetree */ + mac_addr = of_get_mac_address(dev->udev->dev.of_node); + if (mac_addr) { + memcpy(dev->net->dev_addr, mac_addr, ETH_ALEN); + return; + } + /* try reading mac address from EEPROM */ if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, dev->net->dev_addr) == 0) { @@ -775,7 +787,7 @@ static void smsc95xx_init_mac_address(struct usbnet *dev) } } - /* no eeprom, or eeprom values are invalid. generate random MAC */ + /* no useful static MAC address found. generate a random one */ eth_hw_addr_random(dev->net); netif_dbg(dev, ifup, dev->net, "MAC address set to eth_random_addr\n"); } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 10798128c03f..61ba46404937 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -356,6 +356,7 @@ void usbnet_update_max_qlen(struct usbnet *dev) dev->tx_qlen = MAX_QUEUE_MEMORY / dev->hard_mtu; break; case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: /* * Not take default 5ms qlen for super speed HC to * save memory, and iperf tests show 2.5ms qlen can @@ -1415,7 +1416,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, "tx: submit urb err %d\n", retval); break; case 0: - net->trans_start = jiffies; + netif_trans_update(net); __usbnet_queue_skb(&dev->txq, skb, tx_start); if (dev->txq.qlen >= TX_QLEN (dev)) netif_stop_queue (net); @@ -1844,7 +1845,7 @@ int usbnet_resume (struct usb_interface *intf) usb_free_urb(res); usb_autopm_put_interface_async(dev->intf); } else { - dev->net->trans_start = jiffies; + netif_trans_update(dev->net); __skb_queue_tail(&dev->txq, skb); } } diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 8a8f1e58b415..dff08842f26d 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -42,12 +42,9 @@ #define DRV_NAME "vrf" #define DRV_VERSION "1.0" -#define vrf_master_get_rcu(dev) \ - ((struct net_device *)rcu_dereference(dev->rx_handler_data)) - struct net_vrf { - struct rtable *rth; - struct rt6_info *rt6; + struct rtable __rcu *rth; + struct rt6_info __rcu *rt6; u32 tb_id; }; @@ -60,90 +57,12 @@ struct pcpu_dstats { struct u64_stats_sync syncp; }; -/* neighbor handling is done with actual device; do not want - * to flip skb->dev for those ndisc packets. This really fails - * for multiple next protocols (e.g., NEXTHDR_HOP). But it is - * a start. - */ -#if IS_ENABLED(CONFIG_IPV6) -static bool check_ipv6_frame(const struct sk_buff *skb) -{ - const struct ipv6hdr *ipv6h; - struct ipv6hdr _ipv6h; - bool rc = true; - - ipv6h = skb_header_pointer(skb, 0, sizeof(_ipv6h), &_ipv6h); - if (!ipv6h) - goto out; - - if (ipv6h->nexthdr == NEXTHDR_ICMP) { - const struct icmp6hdr *icmph; - struct icmp6hdr _icmph; - - icmph = skb_header_pointer(skb, sizeof(_ipv6h), - sizeof(_icmph), &_icmph); - if (!icmph) - goto out; - - switch (icmph->icmp6_type) { - case NDISC_ROUTER_SOLICITATION: - case NDISC_ROUTER_ADVERTISEMENT: - case NDISC_NEIGHBOUR_SOLICITATION: - case NDISC_NEIGHBOUR_ADVERTISEMENT: - case NDISC_REDIRECT: - rc = false; - break; - } - } - -out: - return rc; -} -#else -static bool check_ipv6_frame(const struct sk_buff *skb) -{ - return false; -} -#endif - -static bool is_ip_rx_frame(struct sk_buff *skb) -{ - switch (skb->protocol) { - case htons(ETH_P_IP): - return true; - case htons(ETH_P_IPV6): - return check_ipv6_frame(skb); - } - return false; -} - static void vrf_tx_error(struct net_device *vrf_dev, struct sk_buff *skb) { vrf_dev->stats.tx_errors++; kfree_skb(skb); } -/* note: already called with rcu_read_lock */ -static rx_handler_result_t vrf_handle_frame(struct sk_buff **pskb) -{ - struct sk_buff *skb = *pskb; - - if (is_ip_rx_frame(skb)) { - struct net_device *dev = vrf_master_get_rcu(skb->dev); - struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats); - - u64_stats_update_begin(&dstats->syncp); - dstats->rx_pkts++; - dstats->rx_bytes += skb->len; - u64_stats_update_end(&dstats->syncp); - - skb->dev = dev; - - return RX_HANDLER_ANOTHER; - } - return RX_HANDLER_PASS; -} - static struct rtnl_link_stats64 *vrf_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { @@ -354,28 +273,40 @@ static int vrf_output6(struct net *net, struct sock *sk, struct sk_buff *skb) !(IP6CB(skb)->flags & IP6SKB_REROUTED)); } +/* holding rtnl */ static void vrf_rt6_release(struct net_vrf *vrf) { - dst_release(&vrf->rt6->dst); - vrf->rt6 = NULL; + struct rt6_info *rt6 = rtnl_dereference(vrf->rt6); + + rcu_assign_pointer(vrf->rt6, NULL); + + if (rt6) + dst_release(&rt6->dst); } static int vrf_rt6_create(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); struct net *net = dev_net(dev); + struct fib6_table *rt6i_table; struct rt6_info *rt6; int rc = -ENOMEM; + rt6i_table = fib6_new_table(net, vrf->tb_id); + if (!rt6i_table) + goto out; + rt6 = ip6_dst_alloc(net, dev, DST_HOST | DST_NOPOLICY | DST_NOXFRM | DST_NOCACHE); if (!rt6) goto out; - rt6->dst.output = vrf_output6; - rt6->rt6i_table = fib6_get_table(net, vrf->tb_id); dst_hold(&rt6->dst); - vrf->rt6 = rt6; + + rt6->rt6i_table = rt6i_table; + rt6->dst.output = vrf_output6; + rcu_assign_pointer(vrf->rt6, rt6); + rc = 0; out: return rc; @@ -449,26 +380,35 @@ static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb) !(IPCB(skb)->flags & IPSKB_REROUTED)); } +/* holding rtnl */ static void vrf_rtable_release(struct net_vrf *vrf) { - struct dst_entry *dst = (struct dst_entry *)vrf->rth; + struct rtable *rth = rtnl_dereference(vrf->rth); - dst_release(dst); - vrf->rth = NULL; + rcu_assign_pointer(vrf->rth, NULL); + + if (rth) + dst_release(&rth->dst); } -static struct rtable *vrf_rtable_create(struct net_device *dev) +static int vrf_rtable_create(struct net_device *dev) { struct net_vrf *vrf = netdev_priv(dev); struct rtable *rth; + if (!fib_new_table(dev_net(dev), vrf->tb_id)) + return -ENOMEM; + rth = rt_dst_alloc(dev, 0, RTN_UNICAST, 1, 1, 0); - if (rth) { - rth->dst.output = vrf_output; - rth->rt_table_id = vrf->tb_id; - } + if (!rth) + return -ENOMEM; - return rth; + rth->dst.output = vrf_output; + rth->rt_table_id = vrf->tb_id; + + rcu_assign_pointer(vrf->rth, rth); + + return 0; } /**************************** device handling ********************/ @@ -497,28 +437,14 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { int ret; - /* register the packet handler for slave ports */ - ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev); - if (ret) { - netdev_err(port_dev, - "Device %s failed to register rx_handler\n", - port_dev->name); - goto out_fail; - } - ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); if (ret < 0) - goto out_unregister; + return ret; port_dev->priv_flags |= IFF_L3MDEV_SLAVE; cycle_netdev(port_dev); return 0; - -out_unregister: - netdev_rx_handler_unregister(port_dev); -out_fail: - return ret; } static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) @@ -535,8 +461,6 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) netdev_upper_dev_unlink(port_dev, dev); port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; - netdev_rx_handler_unregister(port_dev); - cycle_netdev(port_dev); return 0; @@ -572,8 +496,7 @@ static int vrf_dev_init(struct net_device *dev) goto out_nomem; /* create the default dst which points back to us */ - vrf->rth = vrf_rtable_create(dev); - if (!vrf->rth) + if (vrf_rtable_create(dev) != 0) goto out_stats; if (vrf_rt6_create(dev) != 0) @@ -616,8 +539,13 @@ static struct rtable *vrf_get_rtable(const struct net_device *dev, if (!(fl4->flowi4_flags & FLOWI_FLAG_L3MDEV_SRC)) { struct net_vrf *vrf = netdev_priv(dev); - rth = vrf->rth; - dst_hold(&rth->dst); + rcu_read_lock(); + + rth = rcu_dereference(vrf->rth); + if (likely(rth)) + dst_hold(&rth->dst); + + rcu_read_unlock(); } return rth; @@ -639,6 +567,8 @@ static int vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) fl4->flowi4_flags |= FLOWI_FLAG_SKIP_NH_OIF; fl4->flowi4_iif = LOOPBACK_IFINDEX; + /* make sure oif is set to VRF device for lookup */ + fl4->flowi4_oif = dev->ifindex; fl4->flowi4_tos = tos & IPTOS_RT_MASK; fl4->flowi4_scope = ((tos & RTO_ONLINK) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); @@ -659,19 +589,116 @@ static int vrf_get_saddr(struct net_device *dev, struct flowi4 *fl4) } #if IS_ENABLED(CONFIG_IPV6) +/* neighbor handling is done with actual device; do not want + * to flip skb->dev for those ndisc packets. This really fails + * for multiple next protocols (e.g., NEXTHDR_HOP). But it is + * a start. + */ +static bool ipv6_ndisc_frame(const struct sk_buff *skb) +{ + const struct ipv6hdr *iph = ipv6_hdr(skb); + bool rc = false; + + if (iph->nexthdr == NEXTHDR_ICMP) { + const struct icmp6hdr *icmph; + struct icmp6hdr _icmph; + + icmph = skb_header_pointer(skb, sizeof(*iph), + sizeof(_icmph), &_icmph); + if (!icmph) + goto out; + + switch (icmph->icmp6_type) { + case NDISC_ROUTER_SOLICITATION: + case NDISC_ROUTER_ADVERTISEMENT: + case NDISC_NEIGHBOUR_SOLICITATION: + case NDISC_NEIGHBOUR_ADVERTISEMENT: + case NDISC_REDIRECT: + rc = true; + break; + } + } + +out: + return rc; +} + +static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev, + struct sk_buff *skb) +{ + /* if packet is NDISC keep the ingress interface */ + if (!ipv6_ndisc_frame(skb)) { + skb->dev = vrf_dev; + skb->skb_iif = vrf_dev->ifindex; + + skb_push(skb, skb->mac_len); + dev_queue_xmit_nit(skb, vrf_dev); + skb_pull(skb, skb->mac_len); + + IP6CB(skb)->flags |= IP6SKB_L3SLAVE; + } + + return skb; +} + +#else +static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev, + struct sk_buff *skb) +{ + return skb; +} +#endif + +static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev, + struct sk_buff *skb) +{ + skb->dev = vrf_dev; + skb->skb_iif = vrf_dev->ifindex; + + skb_push(skb, skb->mac_len); + dev_queue_xmit_nit(skb, vrf_dev); + skb_pull(skb, skb->mac_len); + + return skb; +} + +/* called with rcu lock held */ +static struct sk_buff *vrf_l3_rcv(struct net_device *vrf_dev, + struct sk_buff *skb, + u16 proto) +{ + switch (proto) { + case AF_INET: + return vrf_ip_rcv(vrf_dev, skb); + case AF_INET6: + return vrf_ip6_rcv(vrf_dev, skb); + } + + return skb; +} + +#if IS_ENABLED(CONFIG_IPV6) static struct dst_entry *vrf_get_rt6_dst(const struct net_device *dev, const struct flowi6 *fl6) { - struct rt6_info *rt = NULL; + struct dst_entry *dst = NULL; if (!(fl6->flowi6_flags & FLOWI_FLAG_L3MDEV_SRC)) { struct net_vrf *vrf = netdev_priv(dev); + struct rt6_info *rt; + + rcu_read_lock(); + + rt = rcu_dereference(vrf->rt6); + if (likely(rt)) { + dst = &rt->dst; + dst_hold(dst); + } - rt = vrf->rt6; - dst_hold(&rt->dst); + rcu_read_unlock(); } - return (struct dst_entry *)rt; + return dst; } #endif @@ -679,6 +706,7 @@ static const struct l3mdev_ops vrf_l3mdev_ops = { .l3mdev_fib_table = vrf_fib_table, .l3mdev_get_rtable = vrf_get_rtable, .l3mdev_get_saddr = vrf_get_saddr, + .l3mdev_l3_rcv = vrf_l3_rcv, #if IS_ENABLED(CONFIG_IPV6) .l3mdev_get_rt6_dst = vrf_get_rt6_dst, #endif diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 2668e528dee4..25ab6bf013c4 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -613,8 +613,9 @@ out: static int vxlan_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff) { - udp_tunnel_gro_complete(skb, nhoff); - + /* Sets 'skb->inner_mac_header' since we are always called with + * 'skb->encapsulation' set. + */ return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr)); } @@ -1380,6 +1381,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb) if (!vxlan_set_mac(vxlan, vs, skb)) goto drop; } else { + skb_reset_mac_header(skb); skb->dev = vxlan->dev; skb->pkt_type = PACKET_HOST; } diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 848ea6a399f2..b87fe0a01c69 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -739,7 +739,7 @@ static char *cosa_net_setup_rx(struct channel_data *chan, int size) chan->netdev->stats.rx_dropped++; return NULL; } - chan->netdev->trans_start = jiffies; + netif_trans_update(chan->netdev); return skb_put(chan->rx_skb, size); } diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 69b994f3b8c5..3c9cbf908ec7 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -831,7 +831,7 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, DMA_OWN | TX_STP | TX_ENP); dev->stats.tx_packets++; dev->stats.tx_bytes += len; - dev->trans_start = jiffies; + netif_trans_update(dev); } /* @@ -1389,7 +1389,7 @@ do_bottom_half_tx(struct fst_card_info *card) DMA_OWN | TX_STP | TX_ENP); dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; - dev->trans_start = jiffies; + netif_trans_update(dev); } else { /* Or do it through dma */ memcpy(card->tx_dma_handle_host, @@ -2258,7 +2258,7 @@ fst_tx_timeout(struct net_device *dev) card->card_no, port->index); fst_issue_cmd(port, ABORTTX); - dev->trans_start = jiffies; + netif_trans_update(dev); netif_wake_queue(dev); port->start = 0; } diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index bb33b242ab48..299140c04556 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -2105,7 +2105,7 @@ static void lmc_driver_timeout(struct net_device *dev) sc->lmc_device->stats.tx_errors++; sc->extra_stats.tx_ProcTimeout++; /* -baz */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ bug_out: diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index 8fef8d83436d..d98c7e57137d 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -860,9 +860,9 @@ prepare_to_send( struct sk_buff *skb, struct net_device *dev ) outb( inb( dev->base_addr + CSR0 ) | TR_REQ, dev->base_addr + CSR0 ); #ifdef CONFIG_SBNI_MULTILINE - nl->master->trans_start = jiffies; + netif_trans_update(nl->master); #else - dev->trans_start = jiffies; + netif_trans_update(dev); #endif } @@ -889,10 +889,10 @@ drop_xmit_queue( struct net_device *dev ) nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); #ifdef CONFIG_SBNI_MULTILINE netif_start_queue( nl->master ); - nl->master->trans_start = jiffies; + netif_trans_update(nl->master); #else netif_start_queue( dev ); - dev->trans_start = jiffies; + netif_trans_update(dev); #endif } diff --git a/drivers/net/wimax/i2400m/netdev.c b/drivers/net/wimax/i2400m/netdev.c index a9970f1af976..bb74f4b9a02f 100644 --- a/drivers/net/wimax/i2400m/netdev.c +++ b/drivers/net/wimax/i2400m/netdev.c @@ -334,7 +334,7 @@ int i2400m_net_tx(struct i2400m *i2400m, struct net_device *net_dev, d_fnstart(3, dev, "(i2400m %p net_dev %p skb %p)\n", i2400m, net_dev, skb); /* FIXME: check eth hdr, only IPv4 is routed by the device as of now */ - net_dev->trans_start = jiffies; + netif_trans_update(net_dev); i2400m_tx_prep_header(skb); d_printf(3, dev, "NETTX: skb %p sending %d bytes to radio\n", skb, skb->len); diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 8f8793004b9f..1b271b99c49e 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -274,6 +274,9 @@ void ar5008_hw_cmn_spur_mitigate(struct ath_hw *ah, }; static const int inc[4] = { 0, 100, 0, 0 }; + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + cur_bin = -6000; upper = bin + 100; lower = bin - 100; @@ -424,14 +427,9 @@ static void ar5008_hw_spur_mitigate(struct ath_hw *ah, int tmp, new; int i; - int8_t mask_m[123]; - int8_t mask_p[123]; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); if (AR_NO_SPUR == cur_bb_spur) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index db6624527d99..53d7445a5d12 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -178,14 +178,9 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, int i; struct chan_centers centers; - int8_t mask_m[123]; - int8_t mask_p[123]; int cur_bb_spur; bool is2GHz = IS_CHAN_2GHZ(chan); - memset(&mask_m, 0, sizeof(int8_t) * 123); - memset(&mask_p, 0, sizeof(int8_t) * 123); - ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index aa0b2a192faa..98b15a9a2779 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1368,7 +1368,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->ifnum = desc->bInterfaceNumber; - if (usb->speed == USB_SPEED_SUPER) + if (usb->speed == USB_SPEED_SUPER_PLUS) + brcmf_dbg(USB, "Broadcom super speed plus USB WLAN interface detected\n"); + else if (usb->speed == USB_SPEED_SUPER) brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n"); else if (usb->speed == USB_SPEED_HIGH) brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n"); diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 833b46c5ed42..ca3cd2102bd6 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -2026,7 +2026,7 @@ static int mpi_send_packet (struct net_device *dev) } else { *payloadLen = cpu_to_le16(len - sizeof(etherHead)); - dev->trans_start = jiffies; + netif_trans_update(dev); /* copy data into airo dma buffer */ memcpy(sendbuf, buffer, len); @@ -2107,7 +2107,7 @@ static void airo_end_xmit(struct net_device *dev) { i = 0; if ( status == SUCCESS ) { - dev->trans_start = jiffies; + netif_trans_update(dev); for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); } else { priv->fids[fid] &= 0xffff; @@ -2174,7 +2174,7 @@ static void airo_end_xmit11(struct net_device *dev) { i = MAX_FIDS / 2; if ( status == SUCCESS ) { - dev->trans_start = jiffies; + netif_trans_update(dev); for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); } else { priv->fids[fid] &= 0xffff; diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index e1e42ed6c412..bfa542c8d6f1 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -2954,7 +2954,7 @@ static int __ipw2100_tx_process(struct ipw2100_priv *priv) /* A packet was processed by the hardware, so update the * watchdog */ - priv->net_dev->trans_start = jiffies; + netif_trans_update(priv->net_dev); break; diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index dac13cf42e9f..5adb7cefb2fe 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -7707,7 +7707,7 @@ static void ipw_handle_data_packet(struct ipw_priv *priv, struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; + netif_trans_update(dev); /* We only process data packets if the * interface is open */ @@ -7770,7 +7770,7 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, unsigned short len = le16_to_cpu(pkt->u.frame.length); /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; + netif_trans_update(dev); /* We only process data packets if the * interface is open */ @@ -7952,7 +7952,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, return; /* We received data from the HW, so stop the watchdog */ - dev->trans_start = jiffies; + netif_trans_update(dev); if (unlikely((len + IPW_RX_FRAME_SIZE) > skb_tailroom(rxb->skb))) { dev->stats.rx_errors++; diff --git a/drivers/net/wireless/intersil/hostap/hostap_hw.c b/drivers/net/wireless/intersil/hostap/hostap_hw.c index 515aa3f993f3..a8a9bd8e176a 100644 --- a/drivers/net/wireless/intersil/hostap/hostap_hw.c +++ b/drivers/net/wireless/intersil/hostap/hostap_hw.c @@ -1794,7 +1794,7 @@ static int prism2_transmit(struct net_device *dev, int idx) netif_wake_queue(dev); return -1; } - dev->trans_start = jiffies; + netif_trans_update(dev); /* Since we did not wait for command completion, the card continues * to process on the background and we will finish handling when diff --git a/drivers/net/wireless/intersil/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c index 7b5c554323c7..7afe2004e930 100644 --- a/drivers/net/wireless/intersil/orinoco/main.c +++ b/drivers/net/wireless/intersil/orinoco/main.c @@ -1794,7 +1794,7 @@ void orinoco_reset(struct work_struct *work) printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", dev->name, err); } else - dev->trans_start = jiffies; + netif_trans_update(dev); } orinoco_unlock_irq(priv); diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index f2cd513d54b2..56f109bc8394 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -1275,7 +1275,7 @@ static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev) goto busy; } - dev->trans_start = jiffies; + netif_trans_update(dev); stats->tx_bytes += skb->len; goto ok; diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 517653b3adab..78c532f0d286 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -317,7 +317,7 @@ void mwifiex_set_trans_start(struct net_device *dev) for (i = 0; i < dev->num_tx_queues; i++) netdev_get_tx_queue(dev, i)->trans_start = jiffies; - dev->trans_start = jiffies; + netif_trans_update(dev); } /* diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index fe900badd468..71e4dd9965bb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -2315,14 +2315,14 @@ static void _rtl8821ae_clear_pci_pme_status(struct ieee80211_hw *hw) pci_read_config_byte(rtlpci->pdev, 0x34, &cap_pointer); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, - "PCI configration 0x34 = 0x%2x\n", cap_pointer); + "PCI configuration 0x34 = 0x%2x\n", cap_pointer); do { pci_read_config_word(rtlpci->pdev, cap_pointer, &cap_hdr); cap_id = cap_hdr & 0xFF; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, - "in pci configration, cap_pointer%x = %x\n", + "in pci configuration, cap_pointer%x = %x\n", cap_pointer, cap_id); if (cap_id == 0x01) { diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 99de07d14939..13fd734b61ec 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1287,7 +1287,7 @@ static void wl3501_tx_timeout(struct net_device *dev) printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n", dev->name, rc); else { - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ netif_wake_queue(dev); } } diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c index 6f5c793a7855..dea049b2556f 100644 --- a/drivers/net/wireless/zydas/zd1201.c +++ b/drivers/net/wireless/zydas/zd1201.c @@ -845,7 +845,7 @@ static void zd1201_tx_timeout(struct net_device *dev) usb_unlink_urb(zd->tx_urb); dev->stats.tx_errors++; /* Restart the timeout to quiet the watchdog: */ - dev->trans_start = jiffies; /* prevent tx timeout */ + netif_trans_update(dev); /* prevent tx timeout */ } static int zd1201_set_mac_address(struct net_device *dev, void *p) diff --git a/drivers/net/xen-netback/Makefile b/drivers/net/xen-netback/Makefile index e346e8125ef5..11e02be9db1a 100644 --- a/drivers/net/xen-netback/Makefile +++ b/drivers/net/xen-netback/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_XEN_NETDEV_BACKEND) := xen-netback.o -xen-netback-y := netback.o xenbus.o interface.o +xen-netback-y := netback.o xenbus.o interface.o hash.o diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index f44b38846420..84d6cbdd11b2 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -220,6 +220,35 @@ struct xenvif_mcast_addr { #define XEN_NETBK_MCAST_MAX 64 +#define XEN_NETBK_MAX_HASH_KEY_SIZE 40 +#define XEN_NETBK_MAX_HASH_MAPPING_SIZE 128 +#define XEN_NETBK_HASH_TAG_SIZE 40 + +struct xenvif_hash_cache_entry { + struct list_head link; + struct rcu_head rcu; + u8 tag[XEN_NETBK_HASH_TAG_SIZE]; + unsigned int len; + u32 val; + int seq; +}; + +struct xenvif_hash_cache { + spinlock_t lock; + struct list_head list; + unsigned int count; + atomic_t seq; +}; + +struct xenvif_hash { + unsigned int alg; + u32 flags; + u8 key[XEN_NETBK_MAX_HASH_KEY_SIZE]; + u32 mapping[XEN_NETBK_MAX_HASH_MAPPING_SIZE]; + unsigned int size; + struct xenvif_hash_cache cache; +}; + struct xenvif { /* Unique identifier for this interface. */ domid_t domid; @@ -251,6 +280,8 @@ struct xenvif { unsigned int num_queues; /* active queues, resource allocated */ unsigned int stalled_queues; + struct xenvif_hash hash; + struct xenbus_watch credit_watch; struct xenbus_watch mcast_ctrl_watch; @@ -260,6 +291,11 @@ struct xenvif { struct dentry *xenvif_dbg_root; #endif + struct xen_netif_ctrl_back_ring ctrl; + struct task_struct *ctrl_task; + wait_queue_head_t ctrl_wq; + unsigned int ctrl_irq; + /* Miscellaneous private stuff. */ struct net_device *dev; }; @@ -285,10 +321,15 @@ struct xenvif *xenvif_alloc(struct device *parent, int xenvif_init_queue(struct xenvif_queue *queue); void xenvif_deinit_queue(struct xenvif_queue *queue); -int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int tx_evtchn, - unsigned int rx_evtchn); -void xenvif_disconnect(struct xenvif *vif); +int xenvif_connect_data(struct xenvif_queue *queue, + unsigned long tx_ring_ref, + unsigned long rx_ring_ref, + unsigned int tx_evtchn, + unsigned int rx_evtchn); +void xenvif_disconnect_data(struct xenvif *vif); +int xenvif_connect_ctrl(struct xenvif *vif, grant_ref_t ring_ref, + unsigned int evtchn); +void xenvif_disconnect_ctrl(struct xenvif *vif); void xenvif_free(struct xenvif *vif); int xenvif_xenbus_init(void); @@ -300,10 +341,10 @@ int xenvif_queue_stopped(struct xenvif_queue *queue); void xenvif_wake_queue(struct xenvif_queue *queue); /* (Un)Map communication rings. */ -void xenvif_unmap_frontend_rings(struct xenvif_queue *queue); -int xenvif_map_frontend_rings(struct xenvif_queue *queue, - grant_ref_t tx_ring_ref, - grant_ref_t rx_ring_ref); +void xenvif_unmap_frontend_data_rings(struct xenvif_queue *queue); +int xenvif_map_frontend_data_rings(struct xenvif_queue *queue, + grant_ref_t tx_ring_ref, + grant_ref_t rx_ring_ref); /* Check for SKBs from frontend and schedule backend processing */ void xenvif_napi_schedule_or_enable_events(struct xenvif_queue *queue); @@ -318,6 +359,8 @@ void xenvif_kick_thread(struct xenvif_queue *queue); int xenvif_dealloc_kthread(void *data); +int xenvif_ctrl_kthread(void *data); + void xenvif_rx_queue_tail(struct xenvif_queue *queue, struct sk_buff *skb); void xenvif_carrier_on(struct xenvif *vif); @@ -341,6 +384,7 @@ extern bool separate_tx_rx_irq; extern unsigned int rx_drain_timeout_msecs; extern unsigned int rx_stall_timeout_msecs; extern unsigned int xenvif_max_queues; +extern unsigned int xenvif_hash_cache_size; #ifdef CONFIG_DEBUG_FS extern struct dentry *xen_netback_dbg_root; @@ -354,4 +398,18 @@ void xenvif_skb_zerocopy_complete(struct xenvif_queue *queue); bool xenvif_mcast_match(struct xenvif *vif, const u8 *addr); void xenvif_mcast_addr_list_free(struct xenvif *vif); +/* Hash */ +void xenvif_init_hash(struct xenvif *vif); +void xenvif_deinit_hash(struct xenvif *vif); + +u32 xenvif_set_hash_alg(struct xenvif *vif, u32 alg); +u32 xenvif_get_hash_flags(struct xenvif *vif, u32 *flags); +u32 xenvif_set_hash_flags(struct xenvif *vif, u32 flags); +u32 xenvif_set_hash_key(struct xenvif *vif, u32 gref, u32 len); +u32 xenvif_set_hash_mapping_size(struct xenvif *vif, u32 size); +u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len, + u32 off); + +void xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb); + #endif /* __XEN_NETBACK__COMMON_H__ */ diff --git a/drivers/net/xen-netback/hash.c b/drivers/net/xen-netback/hash.c new file mode 100644 index 000000000000..fb87cb39a56b --- /dev/null +++ b/drivers/net/xen-netback/hash.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2016 Citrix Systems Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 + * as published by the Free Softare Foundation; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#define XEN_NETIF_DEFINE_TOEPLITZ + +#include "common.h" +#include <linux/vmalloc.h> +#include <linux/rculist.h> + +static void xenvif_del_hash(struct rcu_head *rcu) +{ + struct xenvif_hash_cache_entry *entry; + + entry = container_of(rcu, struct xenvif_hash_cache_entry, rcu); + + kfree(entry); +} + +static void xenvif_add_hash(struct xenvif *vif, const u8 *tag, + unsigned int len, u32 val) +{ + struct xenvif_hash_cache_entry *new, *entry, *oldest; + unsigned long flags; + bool found; + + new = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!new) + return; + + memcpy(new->tag, tag, len); + new->len = len; + new->val = val; + + spin_lock_irqsave(&vif->hash.cache.lock, flags); + + found = false; + oldest = NULL; + list_for_each_entry_rcu(entry, &vif->hash.cache.list, link) { + /* Make sure we don't add duplicate entries */ + if (entry->len == len && + memcmp(entry->tag, tag, len) == 0) + found = true; + if (!oldest || entry->seq < oldest->seq) + oldest = entry; + } + + if (!found) { + new->seq = atomic_inc_return(&vif->hash.cache.seq); + list_add_rcu(&new->link, &vif->hash.cache.list); + + if (++vif->hash.cache.count > xenvif_hash_cache_size) { + list_del_rcu(&oldest->link); + vif->hash.cache.count--; + call_rcu(&oldest->rcu, xenvif_del_hash); + } + } + + spin_unlock_irqrestore(&vif->hash.cache.lock, flags); + + if (found) + kfree(new); +} + +static u32 xenvif_new_hash(struct xenvif *vif, const u8 *data, + unsigned int len) +{ + u32 val; + + val = xen_netif_toeplitz_hash(vif->hash.key, + sizeof(vif->hash.key), + data, len); + + if (xenvif_hash_cache_size != 0) + xenvif_add_hash(vif, data, len, val); + + return val; +} + +static void xenvif_flush_hash(struct xenvif *vif) +{ + struct xenvif_hash_cache_entry *entry; + unsigned long flags; + + if (xenvif_hash_cache_size == 0) + return; + + spin_lock_irqsave(&vif->hash.cache.lock, flags); + + list_for_each_entry_rcu(entry, &vif->hash.cache.list, link) { + list_del_rcu(&entry->link); + vif->hash.cache.count--; + call_rcu(&entry->rcu, xenvif_del_hash); + } + + spin_unlock_irqrestore(&vif->hash.cache.lock, flags); +} + +static u32 xenvif_find_hash(struct xenvif *vif, const u8 *data, + unsigned int len) +{ + struct xenvif_hash_cache_entry *entry; + u32 val; + bool found; + + if (len >= XEN_NETBK_HASH_TAG_SIZE) + return 0; + + if (xenvif_hash_cache_size == 0) + return xenvif_new_hash(vif, data, len); + + rcu_read_lock(); + + found = false; + + list_for_each_entry_rcu(entry, &vif->hash.cache.list, link) { + if (entry->len == len && + memcmp(entry->tag, data, len) == 0) { + val = entry->val; + entry->seq = atomic_inc_return(&vif->hash.cache.seq); + found = true; + break; + } + } + + rcu_read_unlock(); + + if (!found) + val = xenvif_new_hash(vif, data, len); + + return val; +} + +void xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb) +{ + struct flow_keys flow; + u32 hash = 0; + enum pkt_hash_types type = PKT_HASH_TYPE_NONE; + u32 flags = vif->hash.flags; + bool has_tcp_hdr; + + /* Quick rejection test: If the network protocol doesn't + * correspond to any enabled hash type then there's no point + * in parsing the packet header. + */ + switch (skb->protocol) { + case htons(ETH_P_IP): + if (flags & (XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP | + XEN_NETIF_CTRL_HASH_TYPE_IPV4)) + break; + + goto done; + + case htons(ETH_P_IPV6): + if (flags & (XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP | + XEN_NETIF_CTRL_HASH_TYPE_IPV6)) + break; + + goto done; + + default: + goto done; + } + + memset(&flow, 0, sizeof(flow)); + if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) + goto done; + + has_tcp_hdr = (flow.basic.ip_proto == IPPROTO_TCP) && + !(flow.control.flags & FLOW_DIS_IS_FRAGMENT); + + switch (skb->protocol) { + case htons(ETH_P_IP): + if (has_tcp_hdr && + (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)) { + u8 data[12]; + + memcpy(&data[0], &flow.addrs.v4addrs.src, 4); + memcpy(&data[4], &flow.addrs.v4addrs.dst, 4); + memcpy(&data[8], &flow.ports.src, 2); + memcpy(&data[10], &flow.ports.dst, 2); + + hash = xenvif_find_hash(vif, data, sizeof(data)); + type = PKT_HASH_TYPE_L4; + } else if (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4) { + u8 data[8]; + + memcpy(&data[0], &flow.addrs.v4addrs.src, 4); + memcpy(&data[4], &flow.addrs.v4addrs.dst, 4); + + hash = xenvif_find_hash(vif, data, sizeof(data)); + type = PKT_HASH_TYPE_L3; + } + + break; + + case htons(ETH_P_IPV6): + if (has_tcp_hdr && + (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)) { + u8 data[36]; + + memcpy(&data[0], &flow.addrs.v6addrs.src, 16); + memcpy(&data[16], &flow.addrs.v6addrs.dst, 16); + memcpy(&data[32], &flow.ports.src, 2); + memcpy(&data[34], &flow.ports.dst, 2); + + hash = xenvif_find_hash(vif, data, sizeof(data)); + type = PKT_HASH_TYPE_L4; + } else if (flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6) { + u8 data[32]; + + memcpy(&data[0], &flow.addrs.v6addrs.src, 16); + memcpy(&data[16], &flow.addrs.v6addrs.dst, 16); + + hash = xenvif_find_hash(vif, data, sizeof(data)); + type = PKT_HASH_TYPE_L3; + } + + break; + } + +done: + if (type == PKT_HASH_TYPE_NONE) + skb_clear_hash(skb); + else + __skb_set_sw_hash(skb, hash, type == PKT_HASH_TYPE_L4); +} + +u32 xenvif_set_hash_alg(struct xenvif *vif, u32 alg) +{ + switch (alg) { + case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE: + case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ: + break; + + default: + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + } + + vif->hash.alg = alg; + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +u32 xenvif_get_hash_flags(struct xenvif *vif, u32 *flags) +{ + if (vif->hash.alg == XEN_NETIF_CTRL_HASH_ALGORITHM_NONE) + return XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED; + + *flags = XEN_NETIF_CTRL_HASH_TYPE_IPV4 | + XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP | + XEN_NETIF_CTRL_HASH_TYPE_IPV6 | + XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP; + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +u32 xenvif_set_hash_flags(struct xenvif *vif, u32 flags) +{ + if (flags & ~(XEN_NETIF_CTRL_HASH_TYPE_IPV4 | + XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP | + XEN_NETIF_CTRL_HASH_TYPE_IPV6 | + XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + if (vif->hash.alg == XEN_NETIF_CTRL_HASH_ALGORITHM_NONE) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + vif->hash.flags = flags; + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +u32 xenvif_set_hash_key(struct xenvif *vif, u32 gref, u32 len) +{ + u8 *key = vif->hash.key; + struct gnttab_copy copy_op = { + .source.u.ref = gref, + .source.domid = vif->domid, + .dest.u.gmfn = virt_to_gfn(key), + .dest.domid = DOMID_SELF, + .dest.offset = xen_offset_in_page(key), + .len = len, + .flags = GNTCOPY_source_gref + }; + + if (len > XEN_NETBK_MAX_HASH_KEY_SIZE) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + if (copy_op.len != 0) { + gnttab_batch_copy(©_op, 1); + + if (copy_op.status != GNTST_okay) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + } + + /* Clear any remaining key octets */ + if (len < XEN_NETBK_MAX_HASH_KEY_SIZE) + memset(key + len, 0, XEN_NETBK_MAX_HASH_KEY_SIZE - len); + + xenvif_flush_hash(vif); + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +u32 xenvif_set_hash_mapping_size(struct xenvif *vif, u32 size) +{ + if (size > XEN_NETBK_MAX_HASH_MAPPING_SIZE) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + vif->hash.size = size; + memset(vif->hash.mapping, 0, sizeof(u32) * size); + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len, + u32 off) +{ + u32 *mapping = &vif->hash.mapping[off]; + struct gnttab_copy copy_op = { + .source.u.ref = gref, + .source.domid = vif->domid, + .dest.u.gmfn = virt_to_gfn(mapping), + .dest.domid = DOMID_SELF, + .dest.offset = xen_offset_in_page(mapping), + .len = len * sizeof(u32), + .flags = GNTCOPY_source_gref + }; + + if ((off + len > vif->hash.size) || copy_op.len > XEN_PAGE_SIZE) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + while (len-- != 0) + if (mapping[off++] >= vif->num_queues) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + + if (copy_op.len != 0) { + gnttab_batch_copy(©_op, 1); + + if (copy_op.status != GNTST_okay) + return XEN_NETIF_CTRL_STATUS_INVALID_PARAMETER; + } + + return XEN_NETIF_CTRL_STATUS_SUCCESS; +} + +void xenvif_init_hash(struct xenvif *vif) +{ + if (xenvif_hash_cache_size == 0) + return; + + spin_lock_init(&vif->hash.cache.lock); + INIT_LIST_HEAD(&vif->hash.cache.list); +} + +void xenvif_deinit_hash(struct xenvif *vif) +{ + xenvif_flush_hash(vif); +} diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index f5231a2dd2ac..83deeebfc2d1 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -128,6 +128,15 @@ irqreturn_t xenvif_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +irqreturn_t xenvif_ctrl_interrupt(int irq, void *dev_id) +{ + struct xenvif *vif = dev_id; + + wake_up(&vif->ctrl_wq); + + return IRQ_HANDLED; +} + int xenvif_queue_stopped(struct xenvif_queue *queue) { struct net_device *dev = queue->vif->dev; @@ -142,6 +151,33 @@ void xenvif_wake_queue(struct xenvif_queue *queue) netif_tx_wake_queue(netdev_get_tx_queue(dev, id)); } +static u16 xenvif_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, + select_queue_fallback_t fallback) +{ + struct xenvif *vif = netdev_priv(dev); + unsigned int size = vif->hash.size; + + if (vif->hash.alg == XEN_NETIF_CTRL_HASH_ALGORITHM_NONE) { + u16 index = fallback(dev, skb) % dev->real_num_tx_queues; + + /* Make sure there is no hash information in the socket + * buffer otherwise it would be incorrectly forwarded + * to the frontend. + */ + skb_clear_hash(skb); + + return index; + } + + xenvif_set_skb_hash(vif, skb); + + if (size == 0) + return skb_get_hash_raw(skb) % dev->real_num_tx_queues; + + return vif->hash.mapping[skb_get_hash_raw(skb) % size]; +} + static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct xenvif *vif = netdev_priv(dev); @@ -386,6 +422,7 @@ static const struct ethtool_ops xenvif_ethtool_ops = { }; static const struct net_device_ops xenvif_netdev_ops = { + .ndo_select_queue = xenvif_select_queue, .ndo_start_xmit = xenvif_start_xmit, .ndo_get_stats = xenvif_get_stats, .ndo_open = xenvif_open, @@ -527,9 +564,69 @@ void xenvif_carrier_on(struct xenvif *vif) rtnl_unlock(); } -int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int tx_evtchn, - unsigned int rx_evtchn) +int xenvif_connect_ctrl(struct xenvif *vif, grant_ref_t ring_ref, + unsigned int evtchn) +{ + struct net_device *dev = vif->dev; + void *addr; + struct xen_netif_ctrl_sring *shared; + struct task_struct *task; + int err = -ENOMEM; + + err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif), + &ring_ref, 1, &addr); + if (err) + goto err; + + shared = (struct xen_netif_ctrl_sring *)addr; + BACK_RING_INIT(&vif->ctrl, shared, XEN_PAGE_SIZE); + + init_waitqueue_head(&vif->ctrl_wq); + + err = bind_interdomain_evtchn_to_irqhandler(vif->domid, evtchn, + xenvif_ctrl_interrupt, + 0, dev->name, vif); + if (err < 0) + goto err_unmap; + + vif->ctrl_irq = err; + + xenvif_init_hash(vif); + + task = kthread_create(xenvif_ctrl_kthread, (void *)vif, + "%s-control", dev->name); + if (IS_ERR(task)) { + pr_warn("Could not allocate kthread for %s\n", dev->name); + err = PTR_ERR(task); + goto err_deinit; + } + + get_task_struct(task); + vif->ctrl_task = task; + + wake_up_process(vif->ctrl_task); + + return 0; + +err_deinit: + xenvif_deinit_hash(vif); + unbind_from_irqhandler(vif->ctrl_irq, vif); + vif->ctrl_irq = 0; + +err_unmap: + xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif), + vif->ctrl.sring); + vif->ctrl.sring = NULL; + +err: + return err; +} + +int xenvif_connect_data(struct xenvif_queue *queue, + unsigned long tx_ring_ref, + unsigned long rx_ring_ref, + unsigned int tx_evtchn, + unsigned int rx_evtchn) { struct task_struct *task; int err = -ENOMEM; @@ -538,7 +635,8 @@ int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, BUG_ON(queue->task); BUG_ON(queue->dealloc_task); - err = xenvif_map_frontend_rings(queue, tx_ring_ref, rx_ring_ref); + err = xenvif_map_frontend_data_rings(queue, tx_ring_ref, + rx_ring_ref); if (err < 0) goto err; @@ -614,7 +712,7 @@ err_tx_unbind: unbind_from_irqhandler(queue->tx_irq, queue); queue->tx_irq = 0; err_unmap: - xenvif_unmap_frontend_rings(queue); + xenvif_unmap_frontend_data_rings(queue); netif_napi_del(&queue->napi); err: module_put(THIS_MODULE); @@ -634,7 +732,7 @@ void xenvif_carrier_off(struct xenvif *vif) rtnl_unlock(); } -void xenvif_disconnect(struct xenvif *vif) +void xenvif_disconnect_data(struct xenvif *vif) { struct xenvif_queue *queue = NULL; unsigned int num_queues = vif->num_queues; @@ -668,12 +766,33 @@ void xenvif_disconnect(struct xenvif *vif) queue->tx_irq = 0; } - xenvif_unmap_frontend_rings(queue); + xenvif_unmap_frontend_data_rings(queue); } xenvif_mcast_addr_list_free(vif); } +void xenvif_disconnect_ctrl(struct xenvif *vif) +{ + if (vif->ctrl_task) { + kthread_stop(vif->ctrl_task); + put_task_struct(vif->ctrl_task); + vif->ctrl_task = NULL; + } + + if (vif->ctrl_irq) { + xenvif_deinit_hash(vif); + unbind_from_irqhandler(vif->ctrl_irq, vif); + vif->ctrl_irq = 0; + } + + if (vif->ctrl.sring) { + xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif), + vif->ctrl.sring); + vif->ctrl.sring = NULL; + } +} + /* Reverse the relevant parts of xenvif_init_queue(). * Used for queue teardown from xenvif_free(), and on the * error handling paths in xenbus.c:connect(). diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index b42f26029225..edbae0b1e8f0 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -89,6 +89,11 @@ module_param(fatal_skb_slots, uint, 0444); */ #define XEN_NETBACK_TX_COPY_LEN 128 +/* This is the maximum number of flows in the hash cache. */ +#define XENVIF_HASH_CACHE_SIZE_DEFAULT 64 +unsigned int xenvif_hash_cache_size = XENVIF_HASH_CACHE_SIZE_DEFAULT; +module_param_named(hash_cache_size, xenvif_hash_cache_size, uint, 0644); +MODULE_PARM_DESC(hash_cache_size, "Number of flows in the hash cache"); static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx, u8 status); @@ -163,6 +168,8 @@ static bool xenvif_rx_ring_slots_available(struct xenvif_queue *queue) needed = DIV_ROUND_UP(skb->len, XEN_PAGE_SIZE); if (skb_is_gso(skb)) needed++; + if (skb->sw_hash) + needed++; do { prod = queue->rx.sring->req_prod; @@ -280,6 +287,8 @@ struct gop_frag_copy { struct xenvif_rx_meta *meta; int head; int gso_type; + int protocol; + int hash_present; struct page *page; }; @@ -326,8 +335,15 @@ static void xenvif_setup_copy_gop(unsigned long gfn, npo->copy_off += *len; info->meta->size += *len; + if (!info->head) + return; + /* Leave a gap for the GSO descriptor. */ - if (info->head && ((1 << info->gso_type) & queue->vif->gso_mask)) + if ((1 << info->gso_type) & queue->vif->gso_mask) + queue->rx.req_cons++; + + /* Leave a gap for the hash extra segment. */ + if (info->hash_present) queue->rx.req_cons++; info->head = 0; /* There must be something in this buffer now */ @@ -362,6 +378,11 @@ static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb .npo = npo, .head = *head, .gso_type = XEN_NETIF_GSO_TYPE_NONE, + /* xenvif_set_skb_hash() will have either set a s/w + * hash or cleared the hash depending on + * whether the the frontend wants a hash for this skb. + */ + .hash_present = skb->sw_hash, }; unsigned long bytes; @@ -550,6 +571,7 @@ void xenvif_kick_thread(struct xenvif_queue *queue) static void xenvif_rx_action(struct xenvif_queue *queue) { + struct xenvif *vif = queue->vif; s8 status; u16 flags; struct xen_netif_rx_response *resp; @@ -585,9 +607,10 @@ static void xenvif_rx_action(struct xenvif_queue *queue) gnttab_batch_copy(queue->grant_copy_op, npo.copy_prod); while ((skb = __skb_dequeue(&rxq)) != NULL) { + struct xen_netif_extra_info *extra = NULL; if ((1 << queue->meta[npo.meta_cons].gso_type) & - queue->vif->gso_prefix_mask) { + vif->gso_prefix_mask) { resp = RING_GET_RESPONSE(&queue->rx, queue->rx.rsp_prod_pvt++); @@ -605,7 +628,7 @@ static void xenvif_rx_action(struct xenvif_queue *queue) queue->stats.tx_bytes += skb->len; queue->stats.tx_packets++; - status = xenvif_check_gop(queue->vif, + status = xenvif_check_gop(vif, XENVIF_RX_CB(skb)->meta_slots_used, &npo); @@ -627,21 +650,57 @@ static void xenvif_rx_action(struct xenvif_queue *queue) flags); if ((1 << queue->meta[npo.meta_cons].gso_type) & - queue->vif->gso_mask) { - struct xen_netif_extra_info *gso = - (struct xen_netif_extra_info *) + vif->gso_mask) { + extra = (struct xen_netif_extra_info *) RING_GET_RESPONSE(&queue->rx, queue->rx.rsp_prod_pvt++); resp->flags |= XEN_NETRXF_extra_info; - gso->u.gso.type = queue->meta[npo.meta_cons].gso_type; - gso->u.gso.size = queue->meta[npo.meta_cons].gso_size; - gso->u.gso.pad = 0; - gso->u.gso.features = 0; + extra->u.gso.type = queue->meta[npo.meta_cons].gso_type; + extra->u.gso.size = queue->meta[npo.meta_cons].gso_size; + extra->u.gso.pad = 0; + extra->u.gso.features = 0; - gso->type = XEN_NETIF_EXTRA_TYPE_GSO; - gso->flags = 0; + extra->type = XEN_NETIF_EXTRA_TYPE_GSO; + extra->flags = 0; + } + + if (skb->sw_hash) { + /* Since the skb got here via xenvif_select_queue() + * we know that the hash has been re-calculated + * according to a configuration set by the frontend + * and therefore we know that it is legitimate to + * pass it to the frontend. + */ + if (resp->flags & XEN_NETRXF_extra_info) + extra->flags |= XEN_NETIF_EXTRA_FLAG_MORE; + else + resp->flags |= XEN_NETRXF_extra_info; + + extra = (struct xen_netif_extra_info *) + RING_GET_RESPONSE(&queue->rx, + queue->rx.rsp_prod_pvt++); + + extra->u.hash.algorithm = + XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ; + + if (skb->l4_hash) + extra->u.hash.type = + skb->protocol == htons(ETH_P_IP) ? + _XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP : + _XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP; + else + extra->u.hash.type = + skb->protocol == htons(ETH_P_IP) ? + _XEN_NETIF_CTRL_HASH_TYPE_IPV4 : + _XEN_NETIF_CTRL_HASH_TYPE_IPV6; + + *(uint32_t *)extra->u.hash.value = + skb_get_hash_raw(skb); + + extra->type = XEN_NETIF_EXTRA_TYPE_HASH; + extra->flags = 0; } xenvif_add_frag_responses(queue, status, @@ -711,6 +770,7 @@ static void xenvif_tx_err(struct xenvif_queue *queue, if (cons == end) break; RING_COPY_REQUEST(&queue->tx, cons++, txp); + extra_count = 0; /* only the first frag can have extras */ } while (1); queue->tx.req_cons = cons; } @@ -1450,6 +1510,33 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, } } + if (extras[XEN_NETIF_EXTRA_TYPE_HASH - 1].type) { + struct xen_netif_extra_info *extra; + enum pkt_hash_types type = PKT_HASH_TYPE_NONE; + + extra = &extras[XEN_NETIF_EXTRA_TYPE_HASH - 1]; + + switch (extra->u.hash.type) { + case _XEN_NETIF_CTRL_HASH_TYPE_IPV4: + case _XEN_NETIF_CTRL_HASH_TYPE_IPV6: + type = PKT_HASH_TYPE_L3; + break; + + case _XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP: + case _XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP: + type = PKT_HASH_TYPE_L4; + break; + + default: + break; + } + + if (type != PKT_HASH_TYPE_NONE) + skb_set_hash(skb, + *(u32 *)extra->u.hash.value, + type); + } + XENVIF_TX_CB(skb)->pending_idx = pending_idx; __skb_put(skb, data_len); @@ -1925,7 +2012,7 @@ static inline bool tx_dealloc_work_todo(struct xenvif_queue *queue) return queue->dealloc_cons != queue->dealloc_prod; } -void xenvif_unmap_frontend_rings(struct xenvif_queue *queue) +void xenvif_unmap_frontend_data_rings(struct xenvif_queue *queue) { if (queue->tx.sring) xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(queue->vif), @@ -1935,9 +2022,9 @@ void xenvif_unmap_frontend_rings(struct xenvif_queue *queue) queue->rx.sring); } -int xenvif_map_frontend_rings(struct xenvif_queue *queue, - grant_ref_t tx_ring_ref, - grant_ref_t rx_ring_ref) +int xenvif_map_frontend_data_rings(struct xenvif_queue *queue, + grant_ref_t tx_ring_ref, + grant_ref_t rx_ring_ref) { void *addr; struct xen_netif_tx_sring *txs; @@ -1964,7 +2051,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue, return 0; err: - xenvif_unmap_frontend_rings(queue); + xenvif_unmap_frontend_data_rings(queue); return err; } @@ -2163,6 +2250,135 @@ int xenvif_dealloc_kthread(void *data) return 0; } +static void make_ctrl_response(struct xenvif *vif, + const struct xen_netif_ctrl_request *req, + u32 status, u32 data) +{ + RING_IDX idx = vif->ctrl.rsp_prod_pvt; + struct xen_netif_ctrl_response rsp = { + .id = req->id, + .type = req->type, + .status = status, + .data = data, + }; + + *RING_GET_RESPONSE(&vif->ctrl, idx) = rsp; + vif->ctrl.rsp_prod_pvt = ++idx; +} + +static void push_ctrl_response(struct xenvif *vif) +{ + int notify; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->ctrl, notify); + if (notify) + notify_remote_via_irq(vif->ctrl_irq); +} + +static void process_ctrl_request(struct xenvif *vif, + const struct xen_netif_ctrl_request *req) +{ + u32 status = XEN_NETIF_CTRL_STATUS_NOT_SUPPORTED; + u32 data = 0; + + switch (req->type) { + case XEN_NETIF_CTRL_TYPE_SET_HASH_ALGORITHM: + status = xenvif_set_hash_alg(vif, req->data[0]); + break; + + case XEN_NETIF_CTRL_TYPE_GET_HASH_FLAGS: + status = xenvif_get_hash_flags(vif, &data); + break; + + case XEN_NETIF_CTRL_TYPE_SET_HASH_FLAGS: + status = xenvif_set_hash_flags(vif, req->data[0]); + break; + + case XEN_NETIF_CTRL_TYPE_SET_HASH_KEY: + status = xenvif_set_hash_key(vif, req->data[0], + req->data[1]); + break; + + case XEN_NETIF_CTRL_TYPE_GET_HASH_MAPPING_SIZE: + status = XEN_NETIF_CTRL_STATUS_SUCCESS; + data = XEN_NETBK_MAX_HASH_MAPPING_SIZE; + break; + + case XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING_SIZE: + status = xenvif_set_hash_mapping_size(vif, + req->data[0]); + break; + + case XEN_NETIF_CTRL_TYPE_SET_HASH_MAPPING: + status = xenvif_set_hash_mapping(vif, req->data[0], + req->data[1], + req->data[2]); + break; + + default: + break; + } + + make_ctrl_response(vif, req, status, data); + push_ctrl_response(vif); +} + +static void xenvif_ctrl_action(struct xenvif *vif) +{ + for (;;) { + RING_IDX req_prod, req_cons; + + req_prod = vif->ctrl.sring->req_prod; + req_cons = vif->ctrl.req_cons; + + /* Make sure we can see requests before we process them. */ + rmb(); + + if (req_cons == req_prod) + break; + + while (req_cons != req_prod) { + struct xen_netif_ctrl_request req; + + RING_COPY_REQUEST(&vif->ctrl, req_cons, &req); + req_cons++; + + process_ctrl_request(vif, &req); + } + + vif->ctrl.req_cons = req_cons; + vif->ctrl.sring->req_event = req_cons + 1; + } +} + +static bool xenvif_ctrl_work_todo(struct xenvif *vif) +{ + if (likely(RING_HAS_UNCONSUMED_REQUESTS(&vif->ctrl))) + return 1; + + return 0; +} + +int xenvif_ctrl_kthread(void *data) +{ + struct xenvif *vif = data; + + for (;;) { + wait_event_interruptible(vif->ctrl_wq, + xenvif_ctrl_work_todo(vif) || + kthread_should_stop()); + if (kthread_should_stop()) + break; + + while (xenvif_ctrl_work_todo(vif)) + xenvif_ctrl_action(vif); + + cond_resched(); + } + + return 0; +} + static int __init netback_init(void) { int rc = 0; diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index bd182cd55dda..6a31f2610c23 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -38,7 +38,8 @@ struct backend_info { const char *hotplug_script; }; -static int connect_rings(struct backend_info *be, struct xenvif_queue *queue); +static int connect_data_rings(struct backend_info *be, + struct xenvif_queue *queue); static void connect(struct backend_info *be); static int read_xenbus_vif_flags(struct backend_info *be); static int backend_create_xenvif(struct backend_info *be); @@ -367,6 +368,12 @@ static int netback_probe(struct xenbus_device *dev, if (err) pr_debug("Error writing multi-queue-max-queues\n"); + err = xenbus_printf(XBT_NIL, dev->nodename, + "feature-ctrl-ring", + "%u", true); + if (err) + pr_debug("Error writing feature-ctrl-ring\n"); + script = xenbus_read(XBT_NIL, dev->nodename, "script", NULL); if (IS_ERR(script)) { err = PTR_ERR(script); @@ -457,7 +464,8 @@ static void backend_disconnect(struct backend_info *be) #ifdef CONFIG_DEBUG_FS xenvif_debugfs_delif(be->vif); #endif /* CONFIG_DEBUG_FS */ - xenvif_disconnect(be->vif); + xenvif_disconnect_data(be->vif); + xenvif_disconnect_ctrl(be->vif); } } @@ -825,6 +833,48 @@ static void hotplug_status_changed(struct xenbus_watch *watch, kfree(str); } +static int connect_ctrl_ring(struct backend_info *be) +{ + struct xenbus_device *dev = be->dev; + struct xenvif *vif = be->vif; + unsigned int val; + grant_ref_t ring_ref; + unsigned int evtchn; + int err; + + err = xenbus_gather(XBT_NIL, dev->otherend, + "ctrl-ring-ref", "%u", &val, NULL); + if (err) + goto done; /* The frontend does not have a control ring */ + + ring_ref = val; + + err = xenbus_gather(XBT_NIL, dev->otherend, + "event-channel-ctrl", "%u", &val, NULL); + if (err) { + xenbus_dev_fatal(dev, err, + "reading %s/event-channel-ctrl", + dev->otherend); + goto fail; + } + + evtchn = val; + + err = xenvif_connect_ctrl(vif, ring_ref, evtchn); + if (err) { + xenbus_dev_fatal(dev, err, + "mapping shared-frame %u port %u", + ring_ref, evtchn); + goto fail; + } + +done: + return 0; + +fail: + return err; +} + static void connect(struct backend_info *be) { int err; @@ -861,6 +911,12 @@ static void connect(struct backend_info *be) xen_register_watchers(dev, be->vif); read_xenbus_vif_flags(be); + err = connect_ctrl_ring(be); + if (err) { + xenbus_dev_fatal(dev, err, "connecting control ring"); + return; + } + /* Use the number of queues requested by the frontend */ be->vif->queues = vzalloc(requested_num_queues * sizeof(struct xenvif_queue)); @@ -896,11 +952,12 @@ static void connect(struct backend_info *be) queue->remaining_credit = credit_bytes; queue->credit_usec = credit_usec; - err = connect_rings(be, queue); + err = connect_data_rings(be, queue); if (err) { - /* connect_rings() cleans up after itself on failure, - * but we need to clean up after xenvif_init_queue() here, - * and also clean up any previously initialised queues. + /* connect_data_rings() cleans up after itself on + * failure, but we need to clean up after + * xenvif_init_queue() here, and also clean up any + * previously initialised queues. */ xenvif_deinit_queue(queue); be->vif->num_queues = queue_index; @@ -935,15 +992,17 @@ static void connect(struct backend_info *be) err: if (be->vif->num_queues > 0) - xenvif_disconnect(be->vif); /* Clean up existing queues */ + xenvif_disconnect_data(be->vif); /* Clean up existing queues */ vfree(be->vif->queues); be->vif->queues = NULL; be->vif->num_queues = 0; + xenvif_disconnect_ctrl(be->vif); return; } -static int connect_rings(struct backend_info *be, struct xenvif_queue *queue) +static int connect_data_rings(struct backend_info *be, + struct xenvif_queue *queue) { struct xenbus_device *dev = be->dev; unsigned int num_queues = queue->vif->num_queues; @@ -1007,8 +1066,8 @@ static int connect_rings(struct backend_info *be, struct xenvif_queue *queue) } /* Map the shared frame, irq etc. */ - err = xenvif_connect(queue, tx_ring_ref, rx_ring_ref, - tx_evtchn, rx_evtchn); + err = xenvif_connect_data(queue, tx_ring_ref, rx_ring_ref, + tx_evtchn, rx_evtchn); if (err) { xenbus_dev_fatal(dev, err, "mapping shared-frames %lu/%lu port tx %u rx %u", |