diff options
Diffstat (limited to 'drivers/net/ethernet')
61 files changed, 3558 insertions, 875 deletions
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 61ca4eb7c6fa..1d865ae201db 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1706,12 +1706,16 @@ static int bnxt_async_event_process(struct bnxt *bp, if (BNXT_VF(bp)) goto async_event_process_exit; - if (data1 & 0x20000) { + + /* print unsupported speed warning in forced speed mode only */ + if (!(link_info->autoneg & BNXT_AUTONEG_SPEED) && + (data1 & 0x20000)) { u16 fw_speed = link_info->force_link_speed; u32 speed = bnxt_fw_to_ethtool_speed(fw_speed); - netdev_warn(bp->dev, "Link speed %d no longer supported\n", - speed); + if (speed != SPEED_UNKNOWN) + netdev_warn(bp->dev, "Link speed %d no longer supported\n", + speed); } set_bit(BNXT_LINK_SPEED_CHNG_SP_EVENT, &bp->sp_event); /* fall thru */ @@ -7800,8 +7804,6 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_dcb_free(bp); kfree(bp->edev); bp->edev = NULL; - if (bp->xdp_prog) - bpf_prog_put(bp->xdp_prog); bnxt_cleanup_pci(bp); free_netdev(dev); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index b13ce5ebde8d..fe7599f404bf 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -1376,6 +1376,9 @@ static int bnxt_firmware_reset(struct net_device *dev, req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP; req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP; break; + case BNXT_FW_RESET_AP: + req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP; + break; default: return -EINVAL; } @@ -2522,6 +2525,14 @@ static int bnxt_reset(struct net_device *dev, u32 *flags) rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_CHIP); if (!rc) netdev_info(dev, "Reset request successful. Reload driver to complete reset\n"); + } else if (*flags == ETH_RESET_AP) { + /* This feature is not supported in older firmware versions */ + if (bp->hwrm_spec_code < 0x10803) + return -EOPNOTSUPP; + + rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_AP); + if (!rc) + netdev_info(dev, "Reset Application Processor request successful.\n"); } else { rc = -EINVAL; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index ff601b42fcc8..836ef682f24c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -34,6 +34,7 @@ struct bnxt_led_cfg { #define BNXT_LED_DFLT_ENABLES(x) \ cpu_to_le32(BNXT_LED_DFLT_ENA << (BNXT_LED_DFLT_ENA_SHIFT * (x))) +#define BNXT_FW_RESET_AP 0xfffe #define BNXT_FW_RESET_CHIP 0xffff extern const struct ethtool_ops bnxt_ethtool_ops; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 3d201d7324bd..9807214da206 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -54,12 +54,10 @@ static int bnxt_tc_parse_redir(struct bnxt *bp, struct bnxt_tc_actions *actions, const struct tc_action *tc_act) { - int ifindex = tcf_mirred_ifindex(tc_act); - struct net_device *dev; + struct net_device *dev = tcf_mirred_dev(tc_act); - dev = __dev_get_by_index(dev_net(bp->dev), ifindex); if (!dev) { - netdev_info(bp->dev, "no dev for ifindex=%d", ifindex); + netdev_info(bp->dev, "no dev in mirred action"); return -EINVAL; } @@ -148,9 +146,6 @@ static int bnxt_tc_parse_actions(struct bnxt *bp, } } - if (rc) - return rc; - if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { /* dst_fid is PF's fid */ @@ -164,7 +159,7 @@ static int bnxt_tc_parse_actions(struct bnxt *bp, } } - return rc; + return 0; } #define GET_KEY(flow_cmd, key_type) \ diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index c93f3a2dc6c1..c50c5ec49b1d 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -164,14 +164,38 @@ #define GEM_DCFG5 0x0290 /* Design Config 5 */ #define GEM_DCFG6 0x0294 /* Design Config 6 */ #define GEM_DCFG7 0x0298 /* Design Config 7 */ +#define GEM_DCFG8 0x029C /* Design Config 8 */ #define GEM_TXBDCTRL 0x04cc /* TX Buffer Descriptor control register */ #define GEM_RXBDCTRL 0x04d0 /* RX Buffer Descriptor control register */ +/* Screener Type 2 match registers */ +#define GEM_SCRT2 0x540 + +/* EtherType registers */ +#define GEM_ETHT 0x06E0 + +/* Type 2 compare registers */ +#define GEM_T2CMPW0 0x0700 +#define GEM_T2CMPW1 0x0704 +#define T2CMP_OFST(t2idx) (t2idx * 2) + +/* type 2 compare registers + * each location requires 3 compare regs + */ +#define GEM_IP4SRC_CMP(idx) (idx * 3) +#define GEM_IP4DST_CMP(idx) (idx * 3 + 1) +#define GEM_PORT_CMP(idx) (idx * 3 + 2) + +/* Which screening type 2 EtherType register will be used (0 - 7) */ +#define SCRT2_ETHT 0 + #define GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) #define GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) #define GEM_TBQPH(hw_q) (0x04C8) #define GEM_RBQP(hw_q) (0x0480 + ((hw_q) << 2)) +#define GEM_RBQS(hw_q) (0x04A0 + ((hw_q) << 2)) +#define GEM_RBQPH(hw_q) (0x04D4) #define GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) #define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) #define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) @@ -455,6 +479,16 @@ #define GEM_DAW64_OFFSET 23 #define GEM_DAW64_SIZE 1 +/* Bitfields in DCFG8. */ +#define GEM_T1SCR_OFFSET 24 +#define GEM_T1SCR_SIZE 8 +#define GEM_T2SCR_OFFSET 16 +#define GEM_T2SCR_SIZE 8 +#define GEM_SCR2ETH_OFFSET 8 +#define GEM_SCR2ETH_SIZE 8 +#define GEM_SCR2CMP_OFFSET 0 +#define GEM_SCR2CMP_SIZE 8 + /* Bitfields in TISUBN */ #define GEM_SUBNSINCR_OFFSET 0 #define GEM_SUBNSINCR_SIZE 16 @@ -483,6 +517,66 @@ #define GEM_RXTSMODE_OFFSET 4 /* RX Descriptor Timestamp Insertion mode */ #define GEM_RXTSMODE_SIZE 2 +/* Bitfields in SCRT2 */ +#define GEM_QUEUE_OFFSET 0 /* Queue Number */ +#define GEM_QUEUE_SIZE 4 +#define GEM_VLANPR_OFFSET 4 /* VLAN Priority */ +#define GEM_VLANPR_SIZE 3 +#define GEM_VLANEN_OFFSET 8 /* VLAN Enable */ +#define GEM_VLANEN_SIZE 1 +#define GEM_ETHT2IDX_OFFSET 9 /* Index to screener type 2 EtherType register */ +#define GEM_ETHT2IDX_SIZE 3 +#define GEM_ETHTEN_OFFSET 12 /* EtherType Enable */ +#define GEM_ETHTEN_SIZE 1 +#define GEM_CMPA_OFFSET 13 /* Compare A - Index to screener type 2 Compare register */ +#define GEM_CMPA_SIZE 5 +#define GEM_CMPAEN_OFFSET 18 /* Compare A Enable */ +#define GEM_CMPAEN_SIZE 1 +#define GEM_CMPB_OFFSET 19 /* Compare B - Index to screener type 2 Compare register */ +#define GEM_CMPB_SIZE 5 +#define GEM_CMPBEN_OFFSET 24 /* Compare B Enable */ +#define GEM_CMPBEN_SIZE 1 +#define GEM_CMPC_OFFSET 25 /* Compare C - Index to screener type 2 Compare register */ +#define GEM_CMPC_SIZE 5 +#define GEM_CMPCEN_OFFSET 30 /* Compare C Enable */ +#define GEM_CMPCEN_SIZE 1 + +/* Bitfields in ETHT */ +#define GEM_ETHTCMP_OFFSET 0 /* EtherType compare value */ +#define GEM_ETHTCMP_SIZE 16 + +/* Bitfields in T2CMPW0 */ +#define GEM_T2CMP_OFFSET 16 /* 0xFFFF0000 compare value */ +#define GEM_T2CMP_SIZE 16 +#define GEM_T2MASK_OFFSET 0 /* 0x0000FFFF compare value or mask */ +#define GEM_T2MASK_SIZE 16 + +/* Bitfields in T2CMPW1 */ +#define GEM_T2DISMSK_OFFSET 9 /* disable mask */ +#define GEM_T2DISMSK_SIZE 1 +#define GEM_T2CMPOFST_OFFSET 7 /* compare offset */ +#define GEM_T2CMPOFST_SIZE 2 +#define GEM_T2OFST_OFFSET 0 /* offset value */ +#define GEM_T2OFST_SIZE 7 + +/* Offset for screener type 2 compare values (T2CMPOFST). + * Note the offset is applied after the specified point, + * e.g. GEM_T2COMPOFST_ETYPE denotes the EtherType field, so an offset + * of 12 bytes from this would be the source IP address in an IP header + */ +#define GEM_T2COMPOFST_SOF 0 +#define GEM_T2COMPOFST_ETYPE 1 +#define GEM_T2COMPOFST_IPHDR 2 +#define GEM_T2COMPOFST_TCPUDP 3 + +/* offset from EtherType to IP address */ +#define ETYPE_SRCIP_OFFSET 12 +#define ETYPE_DSTIP_OFFSET 16 + +/* offset from IP header to port */ +#define IPHDR_SRCPORT_OFFSET 0 +#define IPHDR_DSTPORT_OFFSET 2 + /* Transmit DMA buffer descriptor Word 1 */ #define GEM_DMA_TXVALID_OFFSET 23 /* timestamp has been captured in the Buffer Descriptor */ #define GEM_DMA_TXVALID_SIZE 1 @@ -583,6 +677,8 @@ #define gem_writel(port, reg, value) (port)->macb_reg_writel((port), GEM_##reg, (value)) #define queue_readl(queue, reg) (queue)->bp->macb_reg_readl((queue)->bp, (queue)->reg) #define queue_writel(queue, reg, value) (queue)->bp->macb_reg_writel((queue)->bp, (queue)->reg, (value)) +#define gem_readl_n(port, reg, idx) (port)->macb_reg_readl((port), GEM_##reg + idx * 4) +#define gem_writel_n(port, reg, idx, value) (port)->macb_reg_writel((port), GEM_##reg + idx * 4, (value)) #define PTP_TS_BUFFER_SIZE 128 /* must be power of 2 */ @@ -920,13 +1016,42 @@ static const struct gem_statistic gem_statistics[] = { #define GEM_STATS_LEN ARRAY_SIZE(gem_statistics) +#define QUEUE_STAT_TITLE(title) { \ + .stat_string = title, \ +} + +/* per queue statistics, each should be unsigned long type */ +struct queue_stats { + union { + unsigned long first; + unsigned long rx_packets; + }; + unsigned long rx_bytes; + unsigned long rx_dropped; + unsigned long tx_packets; + unsigned long tx_bytes; + unsigned long tx_dropped; +}; + +static const struct gem_statistic queue_statistics[] = { + QUEUE_STAT_TITLE("rx_packets"), + QUEUE_STAT_TITLE("rx_bytes"), + QUEUE_STAT_TITLE("rx_dropped"), + QUEUE_STAT_TITLE("tx_packets"), + QUEUE_STAT_TITLE("tx_bytes"), + QUEUE_STAT_TITLE("tx_dropped"), +}; + +#define QUEUE_STATS_LEN ARRAY_SIZE(queue_statistics) + struct macb; +struct macb_queue; struct macb_or_gem_ops { int (*mog_alloc_rx_buffers)(struct macb *bp); void (*mog_free_rx_buffers)(struct macb *bp); void (*mog_init_rings)(struct macb *bp); - int (*mog_rx)(struct macb *bp, int budget); + int (*mog_rx)(struct macb_queue *queue, int budget); }; /* MACB-PTP interface: adapt to platform needs. */ @@ -968,6 +1093,9 @@ struct macb_queue { unsigned int IMR; unsigned int TBQP; unsigned int TBQPH; + unsigned int RBQS; + unsigned int RBQP; + unsigned int RBQPH; unsigned int tx_head, tx_tail; struct macb_dma_desc *tx_ring; @@ -975,6 +1103,16 @@ struct macb_queue { dma_addr_t tx_ring_dma; struct work_struct tx_error_task; + dma_addr_t rx_ring_dma; + dma_addr_t rx_buffers_dma; + unsigned int rx_tail; + unsigned int rx_prepared_head; + struct macb_dma_desc *rx_ring; + struct sk_buff **rx_skbuff; + void *rx_buffers; + struct napi_struct napi; + struct queue_stats stats; + #ifdef CONFIG_MACB_USE_HWSTAMP struct work_struct tx_ts_task; unsigned int tx_ts_head, tx_ts_tail; @@ -982,6 +1120,16 @@ struct macb_queue { #endif }; +struct ethtool_rx_fs_item { + struct ethtool_rx_flow_spec fs; + struct list_head list; +}; + +struct ethtool_rx_fs_list { + struct list_head list; + unsigned int count; +}; + struct macb { void __iomem *regs; bool native_io; @@ -990,11 +1138,6 @@ struct macb { u32 (*macb_reg_readl)(struct macb *bp, int offset); void (*macb_reg_writel)(struct macb *bp, int offset, u32 value); - unsigned int rx_tail; - unsigned int rx_prepared_head; - struct macb_dma_desc *rx_ring; - struct sk_buff **rx_skbuff; - void *rx_buffers; size_t rx_buffer_size; unsigned int rx_ring_size; @@ -1011,15 +1154,11 @@ struct macb { struct clk *tx_clk; struct clk *rx_clk; struct net_device *dev; - struct napi_struct napi; union { struct macb_stats macb; struct gem_stats gem; } hw_stats; - dma_addr_t rx_ring_dma; - dma_addr_t rx_buffers_dma; - struct macb_or_gem_ops macbgem_ops; struct mii_bus *mii_bus; @@ -1032,7 +1171,6 @@ struct macb { unsigned int dma_burst_length; phy_interface_t phy_interface; - struct gpio_desc *reset_gpio; /* AT91RM9200 transmit */ struct sk_buff *skb; /* holds skb until xmit interrupt completes */ @@ -1040,7 +1178,7 @@ struct macb { int skb_length; /* saved skb length for pci_unmap_single */ unsigned int max_tx_length; - u64 ethtool_stats[GEM_STATS_LEN]; + u64 ethtool_stats[GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES]; unsigned int rx_frm_len_mask; unsigned int jumbo_max_len; @@ -1057,6 +1195,11 @@ struct macb { struct ptp_clock_info ptp_clock_info; struct tsu_incr tsu_incr; struct hwtstamp_config tstamp_config; + + /* RX queue filer rule set*/ + struct ethtool_rx_fs_list rx_fs_list; + spinlock_t rx_fs_lock; + unsigned int max_tuples; }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 72a67f74b97b..234667eaaa92 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -194,17 +194,17 @@ static unsigned int macb_rx_ring_wrap(struct macb *bp, unsigned int index) return index & (bp->rx_ring_size - 1); } -static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index) +static struct macb_dma_desc *macb_rx_desc(struct macb_queue *queue, unsigned int index) { - index = macb_rx_ring_wrap(bp, index); - index = macb_adj_dma_desc_idx(bp, index); - return &bp->rx_ring[index]; + index = macb_rx_ring_wrap(queue->bp, index); + index = macb_adj_dma_desc_idx(queue->bp, index); + return &queue->rx_ring[index]; } -static void *macb_rx_buffer(struct macb *bp, unsigned int index) +static void *macb_rx_buffer(struct macb_queue *queue, unsigned int index) { - return bp->rx_buffers + bp->rx_buffer_size * - macb_rx_ring_wrap(bp, index); + return queue->rx_buffers + queue->bp->rx_buffer_size * + macb_rx_ring_wrap(queue->bp, index); } /* I/O accessors */ @@ -759,7 +759,9 @@ static void macb_tx_error_task(struct work_struct *work) macb_tx_ring_wrap(bp, tail), skb->data); bp->dev->stats.tx_packets++; + queue->stats.tx_packets++; bp->dev->stats.tx_bytes += skb->len; + queue->stats.tx_bytes += skb->len; } } else { /* "Buffers exhausted mid-frame" errors may only happen @@ -859,7 +861,9 @@ static void macb_tx_interrupt(struct macb_queue *queue) macb_tx_ring_wrap(bp, tail), skb->data); bp->dev->stats.tx_packets++; + queue->stats.tx_packets++; bp->dev->stats.tx_bytes += skb->len; + queue->stats.tx_bytes += skb->len; } /* Now we can safely release resources */ @@ -881,24 +885,25 @@ static void macb_tx_interrupt(struct macb_queue *queue) netif_wake_subqueue(bp->dev, queue_index); } -static void gem_rx_refill(struct macb *bp) +static void gem_rx_refill(struct macb_queue *queue) { unsigned int entry; struct sk_buff *skb; dma_addr_t paddr; + struct macb *bp = queue->bp; struct macb_dma_desc *desc; - while (CIRC_SPACE(bp->rx_prepared_head, bp->rx_tail, - bp->rx_ring_size) > 0) { - entry = macb_rx_ring_wrap(bp, bp->rx_prepared_head); + while (CIRC_SPACE(queue->rx_prepared_head, queue->rx_tail, + bp->rx_ring_size) > 0) { + entry = macb_rx_ring_wrap(bp, queue->rx_prepared_head); /* Make hw descriptor updates visible to CPU */ rmb(); - bp->rx_prepared_head++; - desc = macb_rx_desc(bp, entry); + queue->rx_prepared_head++; + desc = macb_rx_desc(queue, entry); - if (!bp->rx_skbuff[entry]) { + if (!queue->rx_skbuff[entry]) { /* allocate sk_buff for this free entry in ring */ skb = netdev_alloc_skb(bp->dev, bp->rx_buffer_size); if (unlikely(!skb)) { @@ -916,7 +921,7 @@ static void gem_rx_refill(struct macb *bp) break; } - bp->rx_skbuff[entry] = skb; + queue->rx_skbuff[entry] = skb; if (entry == bp->rx_ring_size - 1) paddr |= MACB_BIT(RX_WRAP); @@ -934,18 +939,18 @@ static void gem_rx_refill(struct macb *bp) /* Make descriptor updates visible to hardware */ wmb(); - netdev_vdbg(bp->dev, "rx ring: prepared head %d, tail %d\n", - bp->rx_prepared_head, bp->rx_tail); + netdev_vdbg(bp->dev, "rx ring: queue: %p, prepared head %d, tail %d\n", + queue, queue->rx_prepared_head, queue->rx_tail); } /* Mark DMA descriptors from begin up to and not including end as unused */ -static void discard_partial_frame(struct macb *bp, unsigned int begin, +static void discard_partial_frame(struct macb_queue *queue, unsigned int begin, unsigned int end) { unsigned int frag; for (frag = begin; frag != end; frag++) { - struct macb_dma_desc *desc = macb_rx_desc(bp, frag); + struct macb_dma_desc *desc = macb_rx_desc(queue, frag); desc->addr &= ~MACB_BIT(RX_USED); } @@ -959,8 +964,9 @@ static void discard_partial_frame(struct macb *bp, unsigned int begin, */ } -static int gem_rx(struct macb *bp, int budget) +static int gem_rx(struct macb_queue *queue, int budget) { + struct macb *bp = queue->bp; unsigned int len; unsigned int entry; struct sk_buff *skb; @@ -972,8 +978,8 @@ static int gem_rx(struct macb *bp, int budget) dma_addr_t addr; bool rxused; - entry = macb_rx_ring_wrap(bp, bp->rx_tail); - desc = macb_rx_desc(bp, entry); + entry = macb_rx_ring_wrap(bp, queue->rx_tail); + desc = macb_rx_desc(queue, entry); /* Make hw descriptor updates visible to CPU */ rmb(); @@ -985,24 +991,26 @@ static int gem_rx(struct macb *bp, int budget) if (!rxused) break; - bp->rx_tail++; + queue->rx_tail++; count++; if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) { netdev_err(bp->dev, "not whole frame pointed by descriptor\n"); bp->dev->stats.rx_dropped++; + queue->stats.rx_dropped++; break; } - skb = bp->rx_skbuff[entry]; + skb = queue->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(bp->dev, "inconsistent Rx descriptor chain\n"); bp->dev->stats.rx_dropped++; + queue->stats.rx_dropped++; break; } /* now everything is ready for receiving packet */ - bp->rx_skbuff[entry] = NULL; + queue->rx_skbuff[entry] = NULL; len = ctrl & bp->rx_frm_len_mask; netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len); @@ -1019,7 +1027,9 @@ static int gem_rx(struct macb *bp, int budget) skb->ip_summed = CHECKSUM_UNNECESSARY; bp->dev->stats.rx_packets++; + queue->stats.rx_packets++; bp->dev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; gem_ptp_do_rxstamp(bp, skb, desc); @@ -1035,12 +1045,12 @@ static int gem_rx(struct macb *bp, int budget) netif_receive_skb(skb); } - gem_rx_refill(bp); + gem_rx_refill(queue); return count; } -static int macb_rx_frame(struct macb *bp, unsigned int first_frag, +static int macb_rx_frame(struct macb_queue *queue, unsigned int first_frag, unsigned int last_frag) { unsigned int len; @@ -1048,8 +1058,9 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, unsigned int offset; struct sk_buff *skb; struct macb_dma_desc *desc; + struct macb *bp = queue->bp; - desc = macb_rx_desc(bp, last_frag); + desc = macb_rx_desc(queue, last_frag); len = desc->ctrl & bp->rx_frm_len_mask; netdev_vdbg(bp->dev, "macb_rx_frame frags %u - %u (len %u)\n", @@ -1068,7 +1079,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, if (!skb) { bp->dev->stats.rx_dropped++; for (frag = first_frag; ; frag++) { - desc = macb_rx_desc(bp, frag); + desc = macb_rx_desc(queue, frag); desc->addr &= ~MACB_BIT(RX_USED); if (frag == last_frag) break; @@ -1096,10 +1107,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, frag_len = len - offset; } skb_copy_to_linear_data_offset(skb, offset, - macb_rx_buffer(bp, frag), + macb_rx_buffer(queue, frag), frag_len); offset += bp->rx_buffer_size; - desc = macb_rx_desc(bp, frag); + desc = macb_rx_desc(queue, frag); desc->addr &= ~MACB_BIT(RX_USED); if (frag == last_frag) @@ -1121,32 +1132,34 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, return 0; } -static inline void macb_init_rx_ring(struct macb *bp) +static inline void macb_init_rx_ring(struct macb_queue *queue) { + struct macb *bp = queue->bp; dma_addr_t addr; struct macb_dma_desc *desc = NULL; int i; - addr = bp->rx_buffers_dma; + addr = queue->rx_buffers_dma; for (i = 0; i < bp->rx_ring_size; i++) { - desc = macb_rx_desc(bp, i); + desc = macb_rx_desc(queue, i); macb_set_addr(bp, desc, addr); desc->ctrl = 0; addr += bp->rx_buffer_size; } desc->addr |= MACB_BIT(RX_WRAP); - bp->rx_tail = 0; + queue->rx_tail = 0; } -static int macb_rx(struct macb *bp, int budget) +static int macb_rx(struct macb_queue *queue, int budget) { + struct macb *bp = queue->bp; bool reset_rx_queue = false; int received = 0; unsigned int tail; int first_frag = -1; - for (tail = bp->rx_tail; budget > 0; tail++) { - struct macb_dma_desc *desc = macb_rx_desc(bp, tail); + for (tail = queue->rx_tail; budget > 0; tail++) { + struct macb_dma_desc *desc = macb_rx_desc(queue, tail); u32 ctrl; /* Make hw descriptor updates visible to CPU */ @@ -1159,7 +1172,7 @@ static int macb_rx(struct macb *bp, int budget) if (ctrl & MACB_BIT(RX_SOF)) { if (first_frag != -1) - discard_partial_frame(bp, first_frag, tail); + discard_partial_frame(queue, first_frag, tail); first_frag = tail; } @@ -1171,7 +1184,7 @@ static int macb_rx(struct macb *bp, int budget) continue; } - dropped = macb_rx_frame(bp, first_frag, tail); + dropped = macb_rx_frame(queue, first_frag, tail); first_frag = -1; if (unlikely(dropped < 0)) { reset_rx_queue = true; @@ -1195,8 +1208,8 @@ static int macb_rx(struct macb *bp, int budget) ctrl = macb_readl(bp, NCR); macb_writel(bp, NCR, ctrl & ~MACB_BIT(RE)); - macb_init_rx_ring(bp); - macb_writel(bp, RBQP, bp->rx_ring_dma); + macb_init_rx_ring(queue); + queue_writel(queue, RBQP, queue->rx_ring_dma); macb_writel(bp, NCR, ctrl | MACB_BIT(RE)); @@ -1205,16 +1218,17 @@ static int macb_rx(struct macb *bp, int budget) } if (first_frag != -1) - bp->rx_tail = first_frag; + queue->rx_tail = first_frag; else - bp->rx_tail = tail; + queue->rx_tail = tail; return received; } static int macb_poll(struct napi_struct *napi, int budget) { - struct macb *bp = container_of(napi, struct macb, napi); + struct macb_queue *queue = container_of(napi, struct macb_queue, napi); + struct macb *bp = queue->bp; int work_done; u32 status; @@ -1224,7 +1238,7 @@ static int macb_poll(struct napi_struct *napi, int budget) netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n", (unsigned long)status, budget); - work_done = bp->macbgem_ops.mog_rx(bp, budget); + work_done = bp->macbgem_ops.mog_rx(queue, budget); if (work_done < budget) { napi_complete_done(napi, work_done); @@ -1232,10 +1246,10 @@ static int macb_poll(struct napi_struct *napi, int budget) status = macb_readl(bp, RSR); if (status) { if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) - macb_writel(bp, ISR, MACB_BIT(RCOMP)); + queue_writel(queue, ISR, MACB_BIT(RCOMP)); napi_reschedule(napi); } else { - macb_writel(bp, IER, MACB_RX_INT_FLAGS); + queue_writel(queue, IER, MACB_RX_INT_FLAGS); } } @@ -1282,9 +1296,9 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) queue_writel(queue, ISR, MACB_BIT(RCOMP)); - if (napi_schedule_prep(&bp->napi)) { + if (napi_schedule_prep(&queue->napi)) { netdev_vdbg(bp->dev, "scheduling RX softirq\n"); - __napi_schedule(&bp->napi); + __napi_schedule(&queue->napi); } } @@ -1708,38 +1722,44 @@ static void gem_free_rx_buffers(struct macb *bp) { struct sk_buff *skb; struct macb_dma_desc *desc; + struct macb_queue *queue; dma_addr_t addr; + unsigned int q; int i; - if (!bp->rx_skbuff) - return; + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + if (!queue->rx_skbuff) + continue; - for (i = 0; i < bp->rx_ring_size; i++) { - skb = bp->rx_skbuff[i]; + for (i = 0; i < bp->rx_ring_size; i++) { + skb = queue->rx_skbuff[i]; - if (!skb) - continue; + if (!skb) + continue; - desc = macb_rx_desc(bp, i); - addr = macb_get_addr(bp, desc); + desc = macb_rx_desc(queue, i); + addr = macb_get_addr(bp, desc); - dma_unmap_single(&bp->pdev->dev, addr, bp->rx_buffer_size, - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - skb = NULL; - } + dma_unmap_single(&bp->pdev->dev, addr, bp->rx_buffer_size, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } - kfree(bp->rx_skbuff); - bp->rx_skbuff = NULL; + kfree(queue->rx_skbuff); + queue->rx_skbuff = NULL; + } } static void macb_free_rx_buffers(struct macb *bp) { - if (bp->rx_buffers) { + struct macb_queue *queue = &bp->queues[0]; + + if (queue->rx_buffers) { dma_free_coherent(&bp->pdev->dev, bp->rx_ring_size * bp->rx_buffer_size, - bp->rx_buffers, bp->rx_buffers_dma); - bp->rx_buffers = NULL; + queue->rx_buffers, queue->rx_buffers_dma); + queue->rx_buffers = NULL; } } @@ -1748,11 +1768,12 @@ static void macb_free_consistent(struct macb *bp) struct macb_queue *queue; unsigned int q; + queue = &bp->queues[0]; bp->macbgem_ops.mog_free_rx_buffers(bp); - if (bp->rx_ring) { + if (queue->rx_ring) { dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES(bp), - bp->rx_ring, bp->rx_ring_dma); - bp->rx_ring = NULL; + queue->rx_ring, queue->rx_ring_dma); + queue->rx_ring = NULL; } for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { @@ -1768,32 +1789,37 @@ static void macb_free_consistent(struct macb *bp) static int gem_alloc_rx_buffers(struct macb *bp) { + struct macb_queue *queue; + unsigned int q; int size; - size = bp->rx_ring_size * sizeof(struct sk_buff *); - bp->rx_skbuff = kzalloc(size, GFP_KERNEL); - if (!bp->rx_skbuff) - return -ENOMEM; - else - netdev_dbg(bp->dev, - "Allocated %d RX struct sk_buff entries at %p\n", - bp->rx_ring_size, bp->rx_skbuff); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + size = bp->rx_ring_size * sizeof(struct sk_buff *); + queue->rx_skbuff = kzalloc(size, GFP_KERNEL); + if (!queue->rx_skbuff) + return -ENOMEM; + else + netdev_dbg(bp->dev, + "Allocated %d RX struct sk_buff entries at %p\n", + bp->rx_ring_size, queue->rx_skbuff); + } return 0; } static int macb_alloc_rx_buffers(struct macb *bp) { + struct macb_queue *queue = &bp->queues[0]; int size; size = bp->rx_ring_size * bp->rx_buffer_size; - bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, - &bp->rx_buffers_dma, GFP_KERNEL); - if (!bp->rx_buffers) + queue->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, + &queue->rx_buffers_dma, GFP_KERNEL); + if (!queue->rx_buffers) return -ENOMEM; netdev_dbg(bp->dev, "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n", - size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers); + size, (unsigned long)queue->rx_buffers_dma, queue->rx_buffers); return 0; } @@ -1819,17 +1845,16 @@ static int macb_alloc_consistent(struct macb *bp) queue->tx_skb = kmalloc(size, GFP_KERNEL); if (!queue->tx_skb) goto out_err; - } - - size = RX_RING_BYTES(bp); - bp->rx_ring = dma_alloc_coherent(&bp->pdev->dev, size, - &bp->rx_ring_dma, GFP_KERNEL); - if (!bp->rx_ring) - goto out_err; - netdev_dbg(bp->dev, - "Allocated RX ring of %d bytes at %08lx (mapped %p)\n", - size, (unsigned long)bp->rx_ring_dma, bp->rx_ring); + size = RX_RING_BYTES(bp); + queue->rx_ring = dma_alloc_coherent(&bp->pdev->dev, size, + &queue->rx_ring_dma, GFP_KERNEL); + if (!queue->rx_ring) + goto out_err; + netdev_dbg(bp->dev, + "Allocated RX ring of %d bytes at %08lx (mapped %p)\n", + size, (unsigned long)queue->rx_ring_dma, queue->rx_ring); + } if (bp->macbgem_ops.mog_alloc_rx_buffers(bp)) goto out_err; @@ -1856,12 +1881,13 @@ static void gem_init_rings(struct macb *bp) desc->ctrl |= MACB_BIT(TX_WRAP); queue->tx_head = 0; queue->tx_tail = 0; - } - bp->rx_tail = 0; - bp->rx_prepared_head = 0; + queue->rx_tail = 0; + queue->rx_prepared_head = 0; + + gem_rx_refill(queue); + } - gem_rx_refill(bp); } static void macb_init_rings(struct macb *bp) @@ -1869,7 +1895,7 @@ static void macb_init_rings(struct macb *bp) int i; struct macb_dma_desc *desc = NULL; - macb_init_rx_ring(bp); + macb_init_rx_ring(&bp->queues[0]); for (i = 0; i < bp->tx_ring_size; i++) { desc = macb_tx_desc(&bp->queues[0], i); @@ -1978,11 +2004,20 @@ static u32 macb_dbw(struct macb *bp) */ static void macb_configure_dma(struct macb *bp) { + struct macb_queue *queue; + u32 buffer_size; + unsigned int q; u32 dmacfg; + buffer_size = bp->rx_buffer_size / RX_BUFFER_MULTIPLE; if (macb_is_gem(bp)) { dmacfg = gem_readl(bp, DMACFG) & ~GEM_BF(RXBS, -1L); - dmacfg |= GEM_BF(RXBS, bp->rx_buffer_size / RX_BUFFER_MULTIPLE); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + if (q) + queue_writel(queue, RBQS, buffer_size); + else + dmacfg |= GEM_BF(RXBS, buffer_size); + } if (bp->dma_burst_length) dmacfg = GEM_BFINS(FBLDO, bp->dma_burst_length, dmacfg); dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L); @@ -2051,12 +2086,12 @@ static void macb_init_hw(struct macb *bp) macb_configure_dma(bp); /* Initialize TX and RX buffers */ - macb_writel(bp, RBQP, lower_32_bits(bp->rx_ring_dma)); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + queue_writel(queue, RBQP, lower_32_bits(queue->rx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap & HW_DMA_CAP_64B) - macb_writel(bp, RBQPH, upper_32_bits(bp->rx_ring_dma)); + if (bp->hw_dma_cap & HW_DMA_CAP_64B) + queue_writel(queue, RBQPH, upper_32_bits(queue->rx_ring_dma)); #endif - for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma)); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT if (bp->hw_dma_cap & HW_DMA_CAP_64B) @@ -2197,6 +2232,8 @@ static int macb_open(struct net_device *dev) { struct macb *bp = netdev_priv(dev); size_t bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + NET_IP_ALIGN; + struct macb_queue *queue; + unsigned int q; int err; netdev_dbg(bp->dev, "open\n"); @@ -2218,11 +2255,12 @@ static int macb_open(struct net_device *dev) return err; } - napi_enable(&bp->napi); - bp->macbgem_ops.mog_init_rings(bp); macb_init_hw(bp); + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) + napi_enable(&queue->napi); + /* schedule a link state check */ phy_start(dev->phydev); @@ -2237,10 +2275,14 @@ static int macb_open(struct net_device *dev) static int macb_close(struct net_device *dev) { struct macb *bp = netdev_priv(dev); + struct macb_queue *queue; unsigned long flags; + unsigned int q; netif_tx_stop_all_queues(dev); - napi_disable(&bp->napi); + + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) + napi_disable(&queue->napi); if (dev->phydev) phy_stop(dev->phydev); @@ -2270,7 +2312,10 @@ static int macb_change_mtu(struct net_device *dev, int new_mtu) static void gem_update_stats(struct macb *bp) { - unsigned int i; + struct macb_queue *queue; + unsigned int i, q, idx; + unsigned long *stat; + u32 *p = &bp->hw_stats.gem.tx_octets_31_0; for (i = 0; i < GEM_STATS_LEN; ++i, ++p) { @@ -2287,6 +2332,11 @@ static void gem_update_stats(struct macb *bp) *(++p) += val; } } + + idx = GEM_STATS_LEN; + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) + for (i = 0, stat = &queue->stats.first; i < QUEUE_STATS_LEN; ++i, ++stat) + bp->ethtool_stats[idx++] = *stat; } static struct net_device_stats *gem_get_stats(struct macb *bp) @@ -2334,14 +2384,17 @@ static void gem_get_ethtool_stats(struct net_device *dev, bp = netdev_priv(dev); gem_update_stats(bp); - memcpy(data, &bp->ethtool_stats, sizeof(u64) * GEM_STATS_LEN); + memcpy(data, &bp->ethtool_stats, sizeof(u64) + * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); } static int gem_get_sset_count(struct net_device *dev, int sset) { + struct macb *bp = netdev_priv(dev); + switch (sset) { case ETH_SS_STATS: - return GEM_STATS_LEN; + return GEM_STATS_LEN + bp->num_queues * QUEUE_STATS_LEN; default: return -EOPNOTSUPP; } @@ -2349,13 +2402,25 @@ static int gem_get_sset_count(struct net_device *dev, int sset) static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) { + char stat_string[ETH_GSTRING_LEN]; + struct macb *bp = netdev_priv(dev); + struct macb_queue *queue; unsigned int i; + unsigned int q; switch (sset) { case ETH_SS_STATS: for (i = 0; i < GEM_STATS_LEN; i++, p += ETH_GSTRING_LEN) memcpy(p, gem_statistics[i].stat_string, ETH_GSTRING_LEN); + + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + for (i = 0; i < QUEUE_STATS_LEN; i++, p += ETH_GSTRING_LEN) { + snprintf(stat_string, ETH_GSTRING_LEN, "q%d_%s", + q, queue_statistics[i].stat_string); + memcpy(p, stat_string, ETH_GSTRING_LEN); + } + } break; } } @@ -2603,6 +2668,307 @@ static int macb_get_ts_info(struct net_device *netdev, return ethtool_op_get_ts_info(netdev, info); } +static void gem_enable_flow_filters(struct macb *bp, bool enable) +{ + struct ethtool_rx_fs_item *item; + u32 t2_scr; + int num_t2_scr; + + num_t2_scr = GEM_BFEXT(T2SCR, gem_readl(bp, DCFG8)); + + list_for_each_entry(item, &bp->rx_fs_list.list, list) { + struct ethtool_rx_flow_spec *fs = &item->fs; + struct ethtool_tcpip4_spec *tp4sp_m; + + if (fs->location >= num_t2_scr) + continue; + + t2_scr = gem_readl_n(bp, SCRT2, fs->location); + + /* enable/disable screener regs for the flow entry */ + t2_scr = GEM_BFINS(ETHTEN, enable, t2_scr); + + /* only enable fields with no masking */ + tp4sp_m = &(fs->m_u.tcp_ip4_spec); + + if (enable && (tp4sp_m->ip4src == 0xFFFFFFFF)) + t2_scr = GEM_BFINS(CMPAEN, 1, t2_scr); + else + t2_scr = GEM_BFINS(CMPAEN, 0, t2_scr); + + if (enable && (tp4sp_m->ip4dst == 0xFFFFFFFF)) + t2_scr = GEM_BFINS(CMPBEN, 1, t2_scr); + else + t2_scr = GEM_BFINS(CMPBEN, 0, t2_scr); + + if (enable && ((tp4sp_m->psrc == 0xFFFF) || (tp4sp_m->pdst == 0xFFFF))) + t2_scr = GEM_BFINS(CMPCEN, 1, t2_scr); + else + t2_scr = GEM_BFINS(CMPCEN, 0, t2_scr); + + gem_writel_n(bp, SCRT2, fs->location, t2_scr); + } +} + +static void gem_prog_cmp_regs(struct macb *bp, struct ethtool_rx_flow_spec *fs) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + uint16_t index = fs->location; + u32 w0, w1, t2_scr; + bool cmp_a = false; + bool cmp_b = false; + bool cmp_c = false; + + tp4sp_v = &(fs->h_u.tcp_ip4_spec); + tp4sp_m = &(fs->m_u.tcp_ip4_spec); + + /* ignore field if any masking set */ + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + /* 1st compare reg - IP source address */ + w0 = 0; + w1 = 0; + w0 = tp4sp_v->ip4src; + w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */ + w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_ETYPE, w1); + w1 = GEM_BFINS(T2OFST, ETYPE_SRCIP_OFFSET, w1); + gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_IP4SRC_CMP(index)), w0); + gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_IP4SRC_CMP(index)), w1); + cmp_a = true; + } + + /* ignore field if any masking set */ + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + /* 2nd compare reg - IP destination address */ + w0 = 0; + w1 = 0; + w0 = tp4sp_v->ip4dst; + w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */ + w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_ETYPE, w1); + w1 = GEM_BFINS(T2OFST, ETYPE_DSTIP_OFFSET, w1); + gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_IP4DST_CMP(index)), w0); + gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_IP4DST_CMP(index)), w1); + cmp_b = true; + } + + /* ignore both port fields if masking set in both */ + if ((tp4sp_m->psrc == 0xFFFF) || (tp4sp_m->pdst == 0xFFFF)) { + /* 3rd compare reg - source port, destination port */ + w0 = 0; + w1 = 0; + w1 = GEM_BFINS(T2CMPOFST, GEM_T2COMPOFST_IPHDR, w1); + if (tp4sp_m->psrc == tp4sp_m->pdst) { + w0 = GEM_BFINS(T2MASK, tp4sp_v->psrc, w0); + w0 = GEM_BFINS(T2CMP, tp4sp_v->pdst, w0); + w1 = GEM_BFINS(T2DISMSK, 1, w1); /* 32-bit compare */ + w1 = GEM_BFINS(T2OFST, IPHDR_SRCPORT_OFFSET, w1); + } else { + /* only one port definition */ + w1 = GEM_BFINS(T2DISMSK, 0, w1); /* 16-bit compare */ + w0 = GEM_BFINS(T2MASK, 0xFFFF, w0); + if (tp4sp_m->psrc == 0xFFFF) { /* src port */ + w0 = GEM_BFINS(T2CMP, tp4sp_v->psrc, w0); + w1 = GEM_BFINS(T2OFST, IPHDR_SRCPORT_OFFSET, w1); + } else { /* dst port */ + w0 = GEM_BFINS(T2CMP, tp4sp_v->pdst, w0); + w1 = GEM_BFINS(T2OFST, IPHDR_DSTPORT_OFFSET, w1); + } + } + gem_writel_n(bp, T2CMPW0, T2CMP_OFST(GEM_PORT_CMP(index)), w0); + gem_writel_n(bp, T2CMPW1, T2CMP_OFST(GEM_PORT_CMP(index)), w1); + cmp_c = true; + } + + t2_scr = 0; + t2_scr = GEM_BFINS(QUEUE, (fs->ring_cookie) & 0xFF, t2_scr); + t2_scr = GEM_BFINS(ETHT2IDX, SCRT2_ETHT, t2_scr); + if (cmp_a) + t2_scr = GEM_BFINS(CMPA, GEM_IP4SRC_CMP(index), t2_scr); + if (cmp_b) + t2_scr = GEM_BFINS(CMPB, GEM_IP4DST_CMP(index), t2_scr); + if (cmp_c) + t2_scr = GEM_BFINS(CMPC, GEM_PORT_CMP(index), t2_scr); + gem_writel_n(bp, SCRT2, index, t2_scr); +} + +static int gem_add_flow_filter(struct net_device *netdev, + struct ethtool_rxnfc *cmd) +{ + struct macb *bp = netdev_priv(netdev); + struct ethtool_rx_flow_spec *fs = &cmd->fs; + struct ethtool_rx_fs_item *item, *newfs; + unsigned long flags; + int ret = -EINVAL; + bool added = false; + + newfs = kmalloc(sizeof(*newfs), GFP_KERNEL); + if (newfs == NULL) + return -ENOMEM; + memcpy(&newfs->fs, fs, sizeof(newfs->fs)); + + netdev_dbg(netdev, + "Adding flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), htons(fs->h_u.tcp_ip4_spec.pdst)); + + spin_lock_irqsave(&bp->rx_fs_lock, flags); + + /* find correct place to add in list */ + list_for_each_entry(item, &bp->rx_fs_list.list, list) { + if (item->fs.location > newfs->fs.location) { + list_add_tail(&newfs->list, &item->list); + added = true; + break; + } else if (item->fs.location == fs->location) { + netdev_err(netdev, "Rule not added: location %d not free!\n", + fs->location); + ret = -EBUSY; + goto err; + } + } + if (!added) + list_add_tail(&newfs->list, &bp->rx_fs_list.list); + + gem_prog_cmp_regs(bp, fs); + bp->rx_fs_list.count++; + /* enable filtering if NTUPLE on */ + if (netdev->features & NETIF_F_NTUPLE) + gem_enable_flow_filters(bp, 1); + + spin_unlock_irqrestore(&bp->rx_fs_lock, flags); + return 0; + +err: + spin_unlock_irqrestore(&bp->rx_fs_lock, flags); + kfree(newfs); + return ret; +} + +static int gem_del_flow_filter(struct net_device *netdev, + struct ethtool_rxnfc *cmd) +{ + struct macb *bp = netdev_priv(netdev); + struct ethtool_rx_fs_item *item; + struct ethtool_rx_flow_spec *fs; + unsigned long flags; + + spin_lock_irqsave(&bp->rx_fs_lock, flags); + + list_for_each_entry(item, &bp->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + /* disable screener regs for the flow entry */ + fs = &(item->fs); + netdev_dbg(netdev, + "Deleting flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), + htons(fs->h_u.tcp_ip4_spec.pdst)); + + gem_writel_n(bp, SCRT2, fs->location, 0); + + list_del(&item->list); + bp->rx_fs_list.count--; + spin_unlock_irqrestore(&bp->rx_fs_lock, flags); + kfree(item); + return 0; + } + } + + spin_unlock_irqrestore(&bp->rx_fs_lock, flags); + return -EINVAL; +} + +static int gem_get_flow_entry(struct net_device *netdev, + struct ethtool_rxnfc *cmd) +{ + struct macb *bp = netdev_priv(netdev); + struct ethtool_rx_fs_item *item; + + list_for_each_entry(item, &bp->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + memcpy(&cmd->fs, &item->fs, sizeof(cmd->fs)); + return 0; + } + } + return -EINVAL; +} + +static int gem_get_all_flow_entries(struct net_device *netdev, + struct ethtool_rxnfc *cmd, u32 *rule_locs) +{ + struct macb *bp = netdev_priv(netdev); + struct ethtool_rx_fs_item *item; + uint32_t cnt = 0; + + list_for_each_entry(item, &bp->rx_fs_list.list, list) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + rule_locs[cnt] = item->fs.location; + cnt++; + } + cmd->data = bp->max_tuples; + cmd->rule_cnt = cnt; + + return 0; +} + +static int gem_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct macb *bp = netdev_priv(netdev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = bp->num_queues; + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = bp->rx_fs_list.count; + break; + case ETHTOOL_GRXCLSRULE: + ret = gem_get_flow_entry(netdev, cmd); + break; + case ETHTOOL_GRXCLSRLALL: + ret = gem_get_all_flow_entries(netdev, cmd, rule_locs); + break; + default: + netdev_err(netdev, + "Command parameter %d is not supported\n", cmd->cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int gem_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) +{ + struct macb *bp = netdev_priv(netdev); + int ret; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + if ((cmd->fs.location >= bp->max_tuples) + || (cmd->fs.ring_cookie >= bp->num_queues)) { + ret = -EINVAL; + break; + } + ret = gem_add_flow_filter(netdev, cmd); + break; + case ETHTOOL_SRXCLSRLDEL: + ret = gem_del_flow_filter(netdev, cmd); + break; + default: + netdev_err(netdev, + "Command parameter %d is not supported\n", cmd->cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + static const struct ethtool_ops macb_ethtool_ops = { .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, @@ -2628,6 +2994,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, .get_ringparam = macb_get_ringparam, .set_ringparam = macb_set_ringparam, + .get_rxnfc = gem_get_rxnfc, + .set_rxnfc = gem_set_rxnfc, }; static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -2685,6 +3053,12 @@ static int macb_set_features(struct net_device *netdev, gem_writel(bp, NCFGR, netcfg); } + /* RX Flow Filters */ + if ((changed & NETIF_F_NTUPLE) && macb_is_gem(bp)) { + bool turn_on = features & NETIF_F_NTUPLE; + + gem_enable_flow_filters(bp, turn_on); + } return 0; } @@ -2850,7 +3224,7 @@ static int macb_init(struct platform_device *pdev) struct macb *bp = netdev_priv(dev); struct macb_queue *queue; int err; - u32 val; + u32 val, reg; bp->tx_ring_size = DEFAULT_TX_RING_SIZE; bp->rx_ring_size = DEFAULT_RX_RING_SIZE; @@ -2865,15 +3239,20 @@ static int macb_init(struct platform_device *pdev) queue = &bp->queues[q]; queue->bp = bp; + netif_napi_add(dev, &queue->napi, macb_poll, 64); if (hw_q) { queue->ISR = GEM_ISR(hw_q - 1); queue->IER = GEM_IER(hw_q - 1); queue->IDR = GEM_IDR(hw_q - 1); queue->IMR = GEM_IMR(hw_q - 1); queue->TBQP = GEM_TBQP(hw_q - 1); + queue->RBQP = GEM_RBQP(hw_q - 1); + queue->RBQS = GEM_RBQS(hw_q - 1); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap & HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { queue->TBQPH = GEM_TBQPH(hw_q - 1); + queue->RBQPH = GEM_RBQPH(hw_q - 1); + } #endif } else { /* queue0 uses legacy registers */ @@ -2882,9 +3261,12 @@ static int macb_init(struct platform_device *pdev) queue->IDR = MACB_IDR; queue->IMR = MACB_IMR; queue->TBQP = MACB_TBQP; + queue->RBQP = MACB_RBQP; #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - if (bp->hw_dma_cap & HW_DMA_CAP_64B) + if (bp->hw_dma_cap & HW_DMA_CAP_64B) { queue->TBQPH = MACB_TBQPH; + queue->RBQPH = MACB_RBQPH; + } #endif } @@ -2908,7 +3290,6 @@ static int macb_init(struct platform_device *pdev) } dev->netdev_ops = &macb_netdev_ops; - netif_napi_add(dev, &bp->napi, macb_poll, 64); /* setup appropriated routines according to adapter type */ if (macb_is_gem(bp)) { @@ -2941,6 +3322,30 @@ static int macb_init(struct platform_device *pdev) dev->hw_features &= ~NETIF_F_SG; dev->features = dev->hw_features; + /* Check RX Flow Filters support. + * Max Rx flows set by availability of screeners & compare regs: + * each 4-tuple define requires 1 T2 screener reg + 3 compare regs + */ + reg = gem_readl(bp, DCFG8); + bp->max_tuples = min((GEM_BFEXT(SCR2CMP, reg) / 3), + GEM_BFEXT(T2SCR, reg)); + if (bp->max_tuples > 0) { + /* also needs one ethtype match to check IPv4 */ + if (GEM_BFEXT(SCR2ETH, reg) > 0) { + /* program this reg now */ + reg = 0; + reg = GEM_BFINS(ETHTCMP, (uint16_t)ETH_P_IP, reg); + gem_writel_n(bp, ETHT, SCRT2_ETHT, reg); + /* Filtering is supported in hw but don't enable it in kernel now */ + dev->hw_features |= NETIF_F_NTUPLE; + /* init Rx flow definitions */ + INIT_LIST_HEAD(&bp->rx_fs_list.list); + bp->rx_fs_list.count = 0; + spin_lock_init(&bp->rx_fs_lock); + } else + bp->max_tuples = 0; + } + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) { val = 0; if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII) @@ -2977,34 +3382,35 @@ static int macb_init(struct platform_device *pdev) static int at91ether_start(struct net_device *dev) { struct macb *lp = netdev_priv(dev); + struct macb_queue *q = &lp->queues[0]; struct macb_dma_desc *desc; dma_addr_t addr; u32 ctl; int i; - lp->rx_ring = dma_alloc_coherent(&lp->pdev->dev, + q->rx_ring = dma_alloc_coherent(&lp->pdev->dev, (AT91ETHER_MAX_RX_DESCR * macb_dma_desc_get_size(lp)), - &lp->rx_ring_dma, GFP_KERNEL); - if (!lp->rx_ring) + &q->rx_ring_dma, GFP_KERNEL); + if (!q->rx_ring) return -ENOMEM; - lp->rx_buffers = dma_alloc_coherent(&lp->pdev->dev, + q->rx_buffers = dma_alloc_coherent(&lp->pdev->dev, AT91ETHER_MAX_RX_DESCR * AT91ETHER_MAX_RBUFF_SZ, - &lp->rx_buffers_dma, GFP_KERNEL); - if (!lp->rx_buffers) { + &q->rx_buffers_dma, GFP_KERNEL); + if (!q->rx_buffers) { dma_free_coherent(&lp->pdev->dev, AT91ETHER_MAX_RX_DESCR * macb_dma_desc_get_size(lp), - lp->rx_ring, lp->rx_ring_dma); - lp->rx_ring = NULL; + q->rx_ring, q->rx_ring_dma); + q->rx_ring = NULL; return -ENOMEM; } - addr = lp->rx_buffers_dma; + addr = q->rx_buffers_dma; for (i = 0; i < AT91ETHER_MAX_RX_DESCR; i++) { - desc = macb_rx_desc(lp, i); + desc = macb_rx_desc(q, i); macb_set_addr(lp, desc, addr); desc->ctrl = 0; addr += AT91ETHER_MAX_RBUFF_SZ; @@ -3014,10 +3420,10 @@ static int at91ether_start(struct net_device *dev) desc->addr |= MACB_BIT(RX_WRAP); /* Reset buffer index */ - lp->rx_tail = 0; + q->rx_tail = 0; /* Program address of descriptor list in Rx Buffer Queue register */ - macb_writel(lp, RBQP, lp->rx_ring_dma); + macb_writel(lp, RBQP, q->rx_ring_dma); /* Enable Receive and Transmit */ ctl = macb_readl(lp, NCR); @@ -3064,6 +3470,7 @@ static int at91ether_open(struct net_device *dev) static int at91ether_close(struct net_device *dev) { struct macb *lp = netdev_priv(dev); + struct macb_queue *q = &lp->queues[0]; u32 ctl; /* Disable Receiver and Transmitter */ @@ -3084,13 +3491,13 @@ static int at91ether_close(struct net_device *dev) dma_free_coherent(&lp->pdev->dev, AT91ETHER_MAX_RX_DESCR * macb_dma_desc_get_size(lp), - lp->rx_ring, lp->rx_ring_dma); - lp->rx_ring = NULL; + q->rx_ring, q->rx_ring_dma); + q->rx_ring = NULL; dma_free_coherent(&lp->pdev->dev, AT91ETHER_MAX_RX_DESCR * AT91ETHER_MAX_RBUFF_SZ, - lp->rx_buffers, lp->rx_buffers_dma); - lp->rx_buffers = NULL; + q->rx_buffers, q->rx_buffers_dma); + q->rx_buffers = NULL; return 0; } @@ -3134,14 +3541,15 @@ static int at91ether_start_xmit(struct sk_buff *skb, struct net_device *dev) static void at91ether_rx(struct net_device *dev) { struct macb *lp = netdev_priv(dev); + struct macb_queue *q = &lp->queues[0]; struct macb_dma_desc *desc; unsigned char *p_recv; struct sk_buff *skb; unsigned int pktlen; - desc = macb_rx_desc(lp, lp->rx_tail); + desc = macb_rx_desc(q, q->rx_tail); while (desc->addr & MACB_BIT(RX_USED)) { - p_recv = lp->rx_buffers + lp->rx_tail * AT91ETHER_MAX_RBUFF_SZ; + p_recv = q->rx_buffers + q->rx_tail * AT91ETHER_MAX_RBUFF_SZ; pktlen = MACB_BF(RX_FRMLEN, desc->ctrl); skb = netdev_alloc_skb(dev, pktlen + 2); if (skb) { @@ -3163,12 +3571,12 @@ static void at91ether_rx(struct net_device *dev) desc->addr &= ~MACB_BIT(RX_USED); /* wrap after last buffer */ - if (lp->rx_tail == AT91ETHER_MAX_RX_DESCR - 1) - lp->rx_tail = 0; + if (q->rx_tail == AT91ETHER_MAX_RX_DESCR - 1) + q->rx_tail = 0; else - lp->rx_tail++; + q->rx_tail++; - desc = macb_rx_desc(lp, lp->rx_tail); + desc = macb_rx_desc(q, q->rx_tail); } } @@ -3394,7 +3802,6 @@ static int macb_probe(struct platform_device *pdev) = macb_config->clk_init; int (*init)(struct platform_device *) = macb_config->init; struct device_node *np = pdev->dev.of_node; - struct device_node *phy_node; struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL; unsigned int queue_mask, num_queues; struct macb_platform_data *pdata; @@ -3500,18 +3907,6 @@ static int macb_probe(struct platform_device *pdev) else macb_get_hwaddr(bp); - /* Power up the PHY if there is a GPIO reset */ - phy_node = of_get_next_available_child(np, NULL); - if (phy_node) { - int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0); - - if (gpio_is_valid(gpio)) { - bp->reset_gpio = gpio_to_desc(gpio); - gpiod_direction_output(bp->reset_gpio, 1); - } - } - of_node_put(phy_node); - err = of_get_phy_mode(np); if (err < 0) { pdata = dev_get_platdata(&pdev->dev); @@ -3558,10 +3953,6 @@ err_out_unregister_mdio: of_phy_deregister_fixed_link(np); 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); @@ -3592,10 +3983,6 @@ static int macb_remove(struct platform_device *pdev) dev->phydev = NULL; 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); - unregister_netdev(dev); clk_disable_unprepare(bp->tx_clk); clk_disable_unprepare(bp->hclk); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index a063c36c4c58..52b3a6044f85 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -65,6 +65,11 @@ module_param(cpi_alg, int, S_IRUGO); MODULE_PARM_DESC(cpi_alg, "PFC algorithm (0=none, 1=VLAN, 2=VLAN16, 3=IP Diffserv)"); +struct nicvf_xdp_tx { + u64 dma_addr; + u8 qidx; +}; + static inline u8 nicvf_netdev_qidx(struct nicvf *nic, u8 qidx) { if (nic->sqs_mode) @@ -500,14 +505,29 @@ static int nicvf_init_resources(struct nicvf *nic) return 0; } +static void nicvf_unmap_page(struct nicvf *nic, struct page *page, u64 dma_addr) +{ + /* Check if it's a recycled page, if not unmap the DMA mapping. + * Recycled page holds an extra reference. + */ + if (page_ref_count(page) == 1) { + dma_addr &= PAGE_MASK; + dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, + RCV_FRAG_LEN + XDP_HEADROOM, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + } +} + static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, struct cqe_rx_t *cqe_rx, struct snd_queue *sq, struct sk_buff **skb) { struct xdp_buff xdp; struct page *page; + struct nicvf_xdp_tx *xdp_tx = NULL; u32 action; - u16 len, offset = 0; + u16 len, err, offset = 0; u64 dma_addr, cpu_addr; void *orig_data; @@ -521,7 +541,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, cpu_addr = (u64)phys_to_virt(cpu_addr); page = virt_to_page((void *)cpu_addr); - xdp.data_hard_start = page_address(page); + xdp.data_hard_start = page_address(page) + RCV_BUF_HEADROOM; xdp.data = (void *)cpu_addr; xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + len; @@ -540,18 +560,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, switch (action) { case XDP_PASS: - /* Check if it's a recycled page, if not - * unmap the DMA mapping. - * - * Recycled page holds an extra reference. - */ - if (page_ref_count(page) == 1) { - dma_addr &= PAGE_MASK; - dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, - RCV_FRAG_LEN + XDP_PACKET_HEADROOM, - DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - } + nicvf_unmap_page(nic, page, dma_addr); /* Build SKB and pass on packet to network stack */ *skb = build_skb(xdp.data, @@ -564,6 +573,20 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, case XDP_TX: nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len); return true; + case XDP_REDIRECT: + /* Save DMA address for use while transmitting */ + xdp_tx = (struct nicvf_xdp_tx *)page_address(page); + xdp_tx->dma_addr = dma_addr; + xdp_tx->qidx = nicvf_netdev_qidx(nic, cqe_rx->rq_idx); + + err = xdp_do_redirect(nic->pnicvf->netdev, &xdp, prog); + if (!err) + return true; + + /* Free the page on error */ + nicvf_unmap_page(nic, page, dma_addr); + put_page(page); + break; default: bpf_warn_invalid_xdp_action(action); /* fall through */ @@ -571,18 +594,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, trace_xdp_exception(nic->netdev, prog, action); /* fall through */ case XDP_DROP: - /* Check if it's a recycled page, if not - * unmap the DMA mapping. - * - * Recycled page holds an extra reference. - */ - if (page_ref_count(page) == 1) { - dma_addr &= PAGE_MASK; - dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, - RCV_FRAG_LEN + XDP_PACKET_HEADROOM, - DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - } + nicvf_unmap_page(nic, page, dma_addr); put_page(page); return true; } @@ -1764,6 +1776,50 @@ static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp) } } +static int nicvf_xdp_xmit(struct net_device *netdev, struct xdp_buff *xdp) +{ + struct nicvf *nic = netdev_priv(netdev); + struct nicvf *snic = nic; + struct nicvf_xdp_tx *xdp_tx; + struct snd_queue *sq; + struct page *page; + int err, qidx; + + if (!netif_running(netdev) || !nic->xdp_prog) + return -EINVAL; + + page = virt_to_page(xdp->data); + xdp_tx = (struct nicvf_xdp_tx *)page_address(page); + qidx = xdp_tx->qidx; + + if (xdp_tx->qidx >= nic->xdp_tx_queues) + return -EINVAL; + + /* Get secondary Qset's info */ + if (xdp_tx->qidx >= MAX_SND_QUEUES_PER_QS) { + qidx = xdp_tx->qidx / MAX_SND_QUEUES_PER_QS; + snic = (struct nicvf *)nic->snicvf[qidx - 1]; + if (!snic) + return -EINVAL; + qidx = xdp_tx->qidx % MAX_SND_QUEUES_PER_QS; + } + + sq = &snic->qs->sq[qidx]; + err = nicvf_xdp_sq_append_pkt(snic, sq, (u64)xdp->data, + xdp_tx->dma_addr, + xdp->data_end - xdp->data); + if (err) + return -ENOMEM; + + nicvf_xdp_sq_doorbell(snic, sq, qidx); + return 0; +} + +static void nicvf_xdp_flush(struct net_device *dev) +{ + return; +} + static const struct net_device_ops nicvf_netdev_ops = { .ndo_open = nicvf_open, .ndo_stop = nicvf_stop, @@ -1775,6 +1831,8 @@ static const struct net_device_ops nicvf_netdev_ops = { .ndo_fix_features = nicvf_fix_features, .ndo_set_features = nicvf_set_features, .ndo_bpf = nicvf_xdp, + .ndo_xdp_xmit = nicvf_xdp_xmit, + .ndo_xdp_flush = nicvf_xdp_flush, }; static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -1833,6 +1891,11 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) nic->pdev = pdev; nic->pnicvf = nic; nic->max_queues = qcount; + /* If no of CPUs are too low, there won't be any queues left + * for XDP_TX, hence double it. + */ + if (!nic->t88) + nic->max_queues *= 2; /* MAP VF's configuration registers */ nic->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index a3d12dbde95b..f38ea349aa00 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -204,7 +204,7 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, /* Reserve space for header modifications by BPF program */ if (rbdr->is_xdp) - buf_len += XDP_PACKET_HEADROOM; + buf_len += XDP_HEADROOM; /* Check if it's recycled */ if (pgcache) @@ -224,8 +224,9 @@ ret: nic->rb_page = NULL; return -ENOMEM; } + if (pgcache) - pgcache->dma_addr = *rbuf + XDP_PACKET_HEADROOM; + pgcache->dma_addr = *rbuf + XDP_HEADROOM; nic->rb_page_offset += buf_len; } @@ -1236,7 +1237,7 @@ int nicvf_xdp_sq_append_pkt(struct nicvf *nic, struct snd_queue *sq, int qentry; if (subdesc_cnt > sq->xdp_free_cnt) - return 0; + return -1; qentry = nicvf_get_sq_desc(sq, subdesc_cnt); @@ -1247,7 +1248,7 @@ int nicvf_xdp_sq_append_pkt(struct nicvf *nic, struct snd_queue *sq, sq->xdp_desc_cnt += subdesc_cnt; - return 1; + return 0; } /* Calculate no of SQ subdescriptors needed to transmit all @@ -1625,7 +1626,7 @@ static void nicvf_unmap_rcv_buffer(struct nicvf *nic, u64 dma_addr, if (page_ref_count(page) != 1) return; - len += XDP_PACKET_HEADROOM; + len += XDP_HEADROOM; /* Receive buffers in XDP mode are mapped from page start */ dma_addr &= PAGE_MASK; } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 67d1a3230773..178ab6e8e3c5 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -11,6 +11,7 @@ #include <linux/netdevice.h> #include <linux/iommu.h> +#include <linux/bpf.h> #include "q_struct.h" #define MAX_QUEUE_SET 128 @@ -92,6 +93,9 @@ #define RCV_FRAG_LEN (SKB_DATA_ALIGN(DMA_BUFFER_LEN + NET_SKB_PAD) + \ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#define RCV_BUF_HEADROOM 128 /* To store dma address for XDP redirect */ +#define XDP_HEADROOM (XDP_PACKET_HEADROOM + RCV_BUF_HEADROOM) + #define MAX_CQES_FOR_TX ((SND_QUEUE_LEN / MIN_SQ_DESC_PER_PKT_XMIT) * \ MAX_CQE_PER_PKT_XMIT) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h index 605689957496..2e71e334d819 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_entity.h @@ -18,17 +18,15 @@ #ifndef __CUDBG_ENTITY_H__ #define __CUDBG_ENTITY_H__ -#define EDC0_FLAG 3 -#define EDC1_FLAG 4 +#define EDC0_FLAG 0 +#define EDC1_FLAG 1 +#define MC_FLAG 2 +#define MC0_FLAG 3 +#define MC1_FLAG 4 +#define HMA_FLAG 5 #define CUDBG_ENTITY_SIGNATURE 0xCCEDB001 -struct card_mem { - u16 size_edc0; - u16 size_edc1; - u16 mem_flag; -}; - struct cudbg_mbox_log { struct mbox_cmd entry; u32 hi[MBOX_LEN / 8]; @@ -87,6 +85,48 @@ struct cudbg_tp_la { u8 data[0]; }; +static const char * const cudbg_region[] = { + "DBQ contexts:", "IMSG contexts:", "FLM cache:", "TCBs:", + "Pstructs:", "Timers:", "Rx FL:", "Tx FL:", "Pstruct FL:", + "Tx payload:", "Rx payload:", "LE hash:", "iSCSI region:", + "TDDP region:", "TPT region:", "STAG region:", "RQ region:", + "RQUDP region:", "PBL region:", "TXPBL region:", + "DBVFIFO region:", "ULPRX state:", "ULPTX state:", + "On-chip queues:" +}; + +/* Memory region info relative to current memory (i.e. wrt 0). */ +struct cudbg_region_info { + bool exist; /* Does region exists in current memory? */ + u32 start; /* Start wrt 0 */ + u32 end; /* End wrt 0 */ +}; + +struct cudbg_mem_desc { + u32 base; + u32 limit; + u32 idx; +}; + +struct cudbg_meminfo { + struct cudbg_mem_desc avail[4]; + struct cudbg_mem_desc mem[ARRAY_SIZE(cudbg_region) + 3]; + u32 avail_c; + u32 mem_c; + u32 up_ram_lo; + u32 up_ram_hi; + u32 up_extmem2_lo; + u32 up_extmem2_hi; + u32 rx_pages_data[3]; + u32 tx_pages_data[4]; + u32 p_structs; + u32 reserved[12]; + u32 port_used[4]; + u32 port_alloc[4]; + u32 loopback_used[NCHAN]; + u32 loopback_alloc[NCHAN]; +}; + struct cudbg_cim_pif_la { int size; u8 data[0]; @@ -145,6 +185,7 @@ struct cudbg_tid_info_region_rev1 { u32 reserved[16]; }; +#define CUDBG_LOWMEM_MAX_CTXT_QIDS 256 #define CUDBG_MAX_FL_QIDS 1024 struct cudbg_ch_cntxt { @@ -334,6 +375,25 @@ static const u32 t5_pm_tx_array[][IREG_NUM_ELEM] = { {0x8FF0, 0x8FF4, 0x10021, 0x1D}, /* t5_pm_tx_regs_10021_to_1003c */ }; +#define CUDBG_NUM_PCIE_CONFIG_REGS 0x61 + +static const u32 t5_pcie_config_array[][2] = { + {0x0, 0x34}, + {0x3c, 0x40}, + {0x50, 0x64}, + {0x70, 0x80}, + {0x94, 0xa0}, + {0xb0, 0xb8}, + {0xd0, 0xd4}, + {0x100, 0x128}, + {0x140, 0x148}, + {0x150, 0x164}, + {0x170, 0x178}, + {0x180, 0x194}, + {0x1a0, 0x1b8}, + {0x1c0, 0x208}, +}; + static const u32 t6_ma_ireg_array[][IREG_NUM_ELEM] = { {0x78f8, 0x78fc, 0xa000, 23}, /* t6_ma_regs_a000_to_a016 */ {0x78f8, 0x78fc, 0xa400, 30}, /* t6_ma_regs_a400_to_a41e */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h index e10ff1ee62c5..e8173ae32158 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_if.h @@ -47,6 +47,8 @@ enum cudbg_dbg_entity_type { CUDBG_CIM_OBQ_NCSI = 17, CUDBG_EDC0 = 18, CUDBG_EDC1 = 19, + CUDBG_MC0 = 20, + CUDBG_MC1 = 21, CUDBG_RSS = 22, CUDBG_RSS_VF_CONF = 25, CUDBG_PATH_MTU = 27, @@ -56,6 +58,7 @@ enum cudbg_dbg_entity_type { CUDBG_SGE_INDIRECT = 37, CUDBG_ULPRX_LA = 41, CUDBG_TP_LA = 43, + CUDBG_MEMINFO = 44, CUDBG_CIM_PIF_LA = 45, CUDBG_CLK = 46, CUDBG_CIM_OBQ_RXQ0 = 47, @@ -63,6 +66,7 @@ enum cudbg_dbg_entity_type { CUDBG_PCIE_INDIRECT = 50, CUDBG_PM_INDIRECT = 51, CUDBG_TID_INFO = 54, + CUDBG_PCIE_CONFIG = 55, CUDBG_DUMP_CONTEXT = 56, CUDBG_MPS_TCAM = 57, CUDBG_VPD_DATA = 58, @@ -74,6 +78,7 @@ enum cudbg_dbg_entity_type { CUDBG_PBT_TABLE = 65, CUDBG_MBOX_LOG = 66, CUDBG_HMA_INDIRECT = 67, + CUDBG_HMA = 68, CUDBG_MAX_ENTITY = 70, }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c index d699bf88d18f..d73fb6a85f8e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c @@ -15,12 +15,14 @@ * */ +#include <linux/sort.h> + #include "t4_regs.h" #include "cxgb4.h" #include "cudbg_if.h" #include "cudbg_lib_common.h" -#include "cudbg_lib.h" #include "cudbg_entity.h" +#include "cudbg_lib.h" static void cudbg_write_and_release_buff(struct cudbg_buffer *pin_buff, struct cudbg_buffer *dbg_buff) @@ -84,6 +86,277 @@ static int cudbg_read_vpd_reg(struct adapter *padap, u32 addr, u32 len, return 0; } +static int cudbg_mem_desc_cmp(const void *a, const void *b) +{ + return ((const struct cudbg_mem_desc *)a)->base - + ((const struct cudbg_mem_desc *)b)->base; +} + +int cudbg_fill_meminfo(struct adapter *padap, + struct cudbg_meminfo *meminfo_buff) +{ + struct cudbg_mem_desc *md; + u32 lo, hi, used, alloc; + int n, i; + + memset(meminfo_buff->avail, 0, + ARRAY_SIZE(meminfo_buff->avail) * + sizeof(struct cudbg_mem_desc)); + memset(meminfo_buff->mem, 0, + (ARRAY_SIZE(cudbg_region) + 3) * sizeof(struct cudbg_mem_desc)); + md = meminfo_buff->mem; + + for (i = 0; i < ARRAY_SIZE(meminfo_buff->mem); i++) { + meminfo_buff->mem[i].limit = 0; + meminfo_buff->mem[i].idx = i; + } + + /* Find and sort the populated memory ranges */ + i = 0; + lo = t4_read_reg(padap, MA_TARGET_MEM_ENABLE_A); + if (lo & EDRAM0_ENABLE_F) { + hi = t4_read_reg(padap, MA_EDRAM0_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EDRAM0_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EDRAM0_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 0; + i++; + } + + if (lo & EDRAM1_ENABLE_F) { + hi = t4_read_reg(padap, MA_EDRAM1_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EDRAM1_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EDRAM1_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 1; + i++; + } + + if (is_t5(padap->params.chip)) { + if (lo & EXT_MEM0_ENABLE_F) { + hi = t4_read_reg(padap, MA_EXT_MEMORY0_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EXT_MEM_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EXT_MEM_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 3; + i++; + } + + if (lo & EXT_MEM1_ENABLE_F) { + hi = t4_read_reg(padap, MA_EXT_MEMORY1_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EXT_MEM1_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EXT_MEM1_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 4; + i++; + } + } else { + if (lo & EXT_MEM_ENABLE_F) { + hi = t4_read_reg(padap, MA_EXT_MEMORY_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EXT_MEM_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EXT_MEM_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 2; + i++; + } + + if (lo & HMA_MUX_F) { + hi = t4_read_reg(padap, MA_EXT_MEMORY1_BAR_A); + meminfo_buff->avail[i].base = + cudbg_mbytes_to_bytes(EXT_MEM1_BASE_G(hi)); + meminfo_buff->avail[i].limit = + meminfo_buff->avail[i].base + + cudbg_mbytes_to_bytes(EXT_MEM1_SIZE_G(hi)); + meminfo_buff->avail[i].idx = 5; + i++; + } + } + + if (!i) /* no memory available */ + return CUDBG_STATUS_ENTITY_NOT_FOUND; + + meminfo_buff->avail_c = i; + sort(meminfo_buff->avail, i, sizeof(struct cudbg_mem_desc), + cudbg_mem_desc_cmp, NULL); + (md++)->base = t4_read_reg(padap, SGE_DBQ_CTXT_BADDR_A); + (md++)->base = t4_read_reg(padap, SGE_IMSG_CTXT_BADDR_A); + (md++)->base = t4_read_reg(padap, SGE_FLM_CACHE_BADDR_A); + (md++)->base = t4_read_reg(padap, TP_CMM_TCB_BASE_A); + (md++)->base = t4_read_reg(padap, TP_CMM_MM_BASE_A); + (md++)->base = t4_read_reg(padap, TP_CMM_TIMER_BASE_A); + (md++)->base = t4_read_reg(padap, TP_CMM_MM_RX_FLST_BASE_A); + (md++)->base = t4_read_reg(padap, TP_CMM_MM_TX_FLST_BASE_A); + (md++)->base = t4_read_reg(padap, TP_CMM_MM_PS_FLST_BASE_A); + + /* the next few have explicit upper bounds */ + md->base = t4_read_reg(padap, TP_PMM_TX_BASE_A); + md->limit = md->base - 1 + + t4_read_reg(padap, TP_PMM_TX_PAGE_SIZE_A) * + PMTXMAXPAGE_G(t4_read_reg(padap, TP_PMM_TX_MAX_PAGE_A)); + md++; + + md->base = t4_read_reg(padap, TP_PMM_RX_BASE_A); + md->limit = md->base - 1 + + t4_read_reg(padap, TP_PMM_RX_PAGE_SIZE_A) * + PMRXMAXPAGE_G(t4_read_reg(padap, TP_PMM_RX_MAX_PAGE_A)); + md++; + + if (t4_read_reg(padap, LE_DB_CONFIG_A) & HASHEN_F) { + if (CHELSIO_CHIP_VERSION(padap->params.chip) <= CHELSIO_T5) { + hi = t4_read_reg(padap, LE_DB_TID_HASHBASE_A) / 4; + md->base = t4_read_reg(padap, LE_DB_HASH_TID_BASE_A); + } else { + hi = t4_read_reg(padap, LE_DB_HASH_TID_BASE_A); + md->base = t4_read_reg(padap, + LE_DB_HASH_TBL_BASE_ADDR_A); + } + md->limit = 0; + } else { + md->base = 0; + md->idx = ARRAY_SIZE(cudbg_region); /* hide it */ + } + md++; + +#define ulp_region(reg) do { \ + md->base = t4_read_reg(padap, ULP_ ## reg ## _LLIMIT_A);\ + (md++)->limit = t4_read_reg(padap, ULP_ ## reg ## _ULIMIT_A);\ +} while (0) + + ulp_region(RX_ISCSI); + ulp_region(RX_TDDP); + ulp_region(TX_TPT); + ulp_region(RX_STAG); + ulp_region(RX_RQ); + ulp_region(RX_RQUDP); + ulp_region(RX_PBL); + ulp_region(TX_PBL); +#undef ulp_region + md->base = 0; + md->idx = ARRAY_SIZE(cudbg_region); + if (!is_t4(padap->params.chip)) { + u32 fifo_size = t4_read_reg(padap, SGE_DBVFIFO_SIZE_A); + u32 sge_ctrl = t4_read_reg(padap, SGE_CONTROL2_A); + u32 size = 0; + + if (is_t5(padap->params.chip)) { + if (sge_ctrl & VFIFO_ENABLE_F) + size = DBVFIFO_SIZE_G(fifo_size); + } else { + size = T6_DBVFIFO_SIZE_G(fifo_size); + } + + if (size) { + md->base = BASEADDR_G(t4_read_reg(padap, + SGE_DBVFIFO_BADDR_A)); + md->limit = md->base + (size << 2) - 1; + } + } + + md++; + + md->base = t4_read_reg(padap, ULP_RX_CTX_BASE_A); + md->limit = 0; + md++; + md->base = t4_read_reg(padap, ULP_TX_ERR_TABLE_BASE_A); + md->limit = 0; + md++; + + md->base = padap->vres.ocq.start; + if (padap->vres.ocq.size) + md->limit = md->base + padap->vres.ocq.size - 1; + else + md->idx = ARRAY_SIZE(cudbg_region); /* hide it */ + md++; + + /* add any address-space holes, there can be up to 3 */ + for (n = 0; n < i - 1; n++) + if (meminfo_buff->avail[n].limit < + meminfo_buff->avail[n + 1].base) + (md++)->base = meminfo_buff->avail[n].limit; + + if (meminfo_buff->avail[n].limit) + (md++)->base = meminfo_buff->avail[n].limit; + + n = md - meminfo_buff->mem; + meminfo_buff->mem_c = n; + + sort(meminfo_buff->mem, n, sizeof(struct cudbg_mem_desc), + cudbg_mem_desc_cmp, NULL); + + lo = t4_read_reg(padap, CIM_SDRAM_BASE_ADDR_A); + hi = t4_read_reg(padap, CIM_SDRAM_ADDR_SIZE_A) + lo - 1; + meminfo_buff->up_ram_lo = lo; + meminfo_buff->up_ram_hi = hi; + + lo = t4_read_reg(padap, CIM_EXTMEM2_BASE_ADDR_A); + hi = t4_read_reg(padap, CIM_EXTMEM2_ADDR_SIZE_A) + lo - 1; + meminfo_buff->up_extmem2_lo = lo; + meminfo_buff->up_extmem2_hi = hi; + + lo = t4_read_reg(padap, TP_PMM_RX_MAX_PAGE_A); + meminfo_buff->rx_pages_data[0] = PMRXMAXPAGE_G(lo); + meminfo_buff->rx_pages_data[1] = + t4_read_reg(padap, TP_PMM_RX_PAGE_SIZE_A) >> 10; + meminfo_buff->rx_pages_data[2] = (lo & PMRXNUMCHN_F) ? 2 : 1; + + lo = t4_read_reg(padap, TP_PMM_TX_MAX_PAGE_A); + hi = t4_read_reg(padap, TP_PMM_TX_PAGE_SIZE_A); + meminfo_buff->tx_pages_data[0] = PMTXMAXPAGE_G(lo); + meminfo_buff->tx_pages_data[1] = + hi >= (1 << 20) ? (hi >> 20) : (hi >> 10); + meminfo_buff->tx_pages_data[2] = + hi >= (1 << 20) ? 'M' : 'K'; + meminfo_buff->tx_pages_data[3] = 1 << PMTXNUMCHN_G(lo); + + meminfo_buff->p_structs = t4_read_reg(padap, TP_CMM_MM_MAX_PSTRUCT_A); + + for (i = 0; i < 4; i++) { + if (CHELSIO_CHIP_VERSION(padap->params.chip) > CHELSIO_T5) + lo = t4_read_reg(padap, + MPS_RX_MAC_BG_PG_CNT0_A + i * 4); + else + lo = t4_read_reg(padap, MPS_RX_PG_RSV0_A + i * 4); + if (is_t5(padap->params.chip)) { + used = T5_USED_G(lo); + alloc = T5_ALLOC_G(lo); + } else { + used = USED_G(lo); + alloc = ALLOC_G(lo); + } + meminfo_buff->port_used[i] = used; + meminfo_buff->port_alloc[i] = alloc; + } + + for (i = 0; i < padap->params.arch.nchan; i++) { + if (CHELSIO_CHIP_VERSION(padap->params.chip) > CHELSIO_T5) + lo = t4_read_reg(padap, + MPS_RX_LPBK_BG_PG_CNT0_A + i * 4); + else + lo = t4_read_reg(padap, MPS_RX_PG_RSV4_A + i * 4); + if (is_t5(padap->params.chip)) { + used = T5_USED_G(lo); + alloc = T5_ALLOC_G(lo); + } else { + used = USED_G(lo); + alloc = ALLOC_G(lo); + } + meminfo_buff->loopback_used[i] = used; + meminfo_buff->loopback_alloc[i] = alloc; + } + + return 0; +} + int cudbg_collect_reg_dump(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err) @@ -420,23 +693,211 @@ int cudbg_collect_obq_sge_rx_q1(struct cudbg_init *pdbg_init, return cudbg_read_cim_obq(pdbg_init, dbg_buff, cudbg_err, 7); } +static int cudbg_meminfo_get_mem_index(struct adapter *padap, + struct cudbg_meminfo *mem_info, + u8 mem_type, u8 *idx) +{ + u8 i, flag; + + switch (mem_type) { + case MEM_EDC0: + flag = EDC0_FLAG; + break; + case MEM_EDC1: + flag = EDC1_FLAG; + break; + case MEM_MC0: + /* Some T5 cards have both MC0 and MC1. */ + flag = is_t5(padap->params.chip) ? MC0_FLAG : MC_FLAG; + break; + case MEM_MC1: + flag = MC1_FLAG; + break; + case MEM_HMA: + flag = HMA_FLAG; + break; + default: + return CUDBG_STATUS_ENTITY_NOT_FOUND; + } + + for (i = 0; i < mem_info->avail_c; i++) { + if (mem_info->avail[i].idx == flag) { + *idx = i; + return 0; + } + } + + return CUDBG_STATUS_ENTITY_NOT_FOUND; +} + +/* Fetch the @region_name's start and end from @meminfo. */ +static int cudbg_get_mem_region(struct adapter *padap, + struct cudbg_meminfo *meminfo, + u8 mem_type, const char *region_name, + struct cudbg_mem_desc *mem_desc) +{ + u8 mc, found = 0; + u32 i, idx = 0; + int rc; + + rc = cudbg_meminfo_get_mem_index(padap, meminfo, mem_type, &mc); + if (rc) + return rc; + + for (i = 0; i < ARRAY_SIZE(cudbg_region); i++) { + if (!strcmp(cudbg_region[i], region_name)) { + found = 1; + idx = i; + break; + } + } + if (!found) + return -EINVAL; + + found = 0; + for (i = 0; i < meminfo->mem_c; i++) { + if (meminfo->mem[i].idx >= ARRAY_SIZE(cudbg_region)) + continue; /* Skip holes */ + + if (!(meminfo->mem[i].limit)) + meminfo->mem[i].limit = + i < meminfo->mem_c - 1 ? + meminfo->mem[i + 1].base - 1 : ~0; + + if (meminfo->mem[i].idx == idx) { + /* Check if the region exists in @mem_type memory */ + if (meminfo->mem[i].base < meminfo->avail[mc].base && + meminfo->mem[i].limit < meminfo->avail[mc].base) + return -EINVAL; + + if (meminfo->mem[i].base > meminfo->avail[mc].limit) + return -EINVAL; + + memcpy(mem_desc, &meminfo->mem[i], + sizeof(struct cudbg_mem_desc)); + found = 1; + break; + } + } + if (!found) + return -EINVAL; + + return 0; +} + +/* Fetch and update the start and end of the requested memory region w.r.t 0 + * in the corresponding EDC/MC/HMA. + */ +static int cudbg_get_mem_relative(struct adapter *padap, + struct cudbg_meminfo *meminfo, + u8 mem_type, u32 *out_base, u32 *out_end) +{ + u8 mc_idx; + int rc; + + rc = cudbg_meminfo_get_mem_index(padap, meminfo, mem_type, &mc_idx); + if (rc) + return rc; + + if (*out_base < meminfo->avail[mc_idx].base) + *out_base = 0; + else + *out_base -= meminfo->avail[mc_idx].base; + + if (*out_end > meminfo->avail[mc_idx].limit) + *out_end = meminfo->avail[mc_idx].limit; + else + *out_end -= meminfo->avail[mc_idx].base; + + return 0; +} + +/* Get TX and RX Payload region */ +static int cudbg_get_payload_range(struct adapter *padap, u8 mem_type, + const char *region_name, + struct cudbg_region_info *payload) +{ + struct cudbg_mem_desc mem_desc = { 0 }; + struct cudbg_meminfo meminfo; + int rc; + + rc = cudbg_fill_meminfo(padap, &meminfo); + if (rc) + return rc; + + rc = cudbg_get_mem_region(padap, &meminfo, mem_type, region_name, + &mem_desc); + if (rc) { + payload->exist = false; + return 0; + } + + payload->exist = true; + payload->start = mem_desc.base; + payload->end = mem_desc.limit; + + return cudbg_get_mem_relative(padap, &meminfo, mem_type, + &payload->start, &payload->end); +} + +#define CUDBG_YIELD_ITERATION 256 + static int cudbg_read_fw_mem(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, u8 mem_type, unsigned long tot_len, struct cudbg_error *cudbg_err) { + static const char * const region_name[] = { "Tx payload:", + "Rx payload:" }; unsigned long bytes, bytes_left, bytes_read = 0; struct adapter *padap = pdbg_init->adap; struct cudbg_buffer temp_buff = { 0 }; + struct cudbg_region_info payload[2]; + u32 yield_count = 0; int rc = 0; + u8 i; + + /* Get TX/RX Payload region range if they exist */ + memset(payload, 0, sizeof(payload)); + for (i = 0; i < ARRAY_SIZE(region_name); i++) { + rc = cudbg_get_payload_range(padap, mem_type, region_name[i], + &payload[i]); + if (rc) + return rc; + + if (payload[i].exist) { + /* Align start and end to avoid wrap around */ + payload[i].start = roundup(payload[i].start, + CUDBG_CHUNK_SIZE); + payload[i].end = rounddown(payload[i].end, + CUDBG_CHUNK_SIZE); + } + } bytes_left = tot_len; while (bytes_left > 0) { + /* As MC size is huge and read through PIO access, this + * loop will hold cpu for a longer time. OS may think that + * the process is hanged and will generate CPU stall traces. + * So yield the cpu regularly. + */ + yield_count++; + if (!(yield_count % CUDBG_YIELD_ITERATION)) + schedule(); + bytes = min_t(unsigned long, bytes_left, (unsigned long)CUDBG_CHUNK_SIZE); rc = cudbg_get_buff(dbg_buff, bytes, &temp_buff); if (rc) return rc; + + for (i = 0; i < ARRAY_SIZE(payload); i++) + if (payload[i].exist && + bytes_read >= payload[i].start && + bytes_read + bytes <= payload[i].end) + /* TX and RX Payload regions can't overlap */ + goto skip_read; + spin_lock(&padap->win0_lock); rc = t4_memory_rw(padap, MEMWIN_NIC, mem_type, bytes_read, bytes, @@ -448,6 +909,8 @@ static int cudbg_read_fw_mem(struct cudbg_init *pdbg_init, cudbg_put_buff(&temp_buff, dbg_buff); return rc; } + +skip_read: bytes_left -= bytes; bytes_read += bytes; cudbg_write_and_release_buff(&temp_buff, dbg_buff); @@ -455,27 +918,6 @@ static int cudbg_read_fw_mem(struct cudbg_init *pdbg_init, return rc; } -static void cudbg_collect_mem_info(struct cudbg_init *pdbg_init, - struct card_mem *mem_info) -{ - struct adapter *padap = pdbg_init->adap; - u32 value; - - value = t4_read_reg(padap, MA_EDRAM0_BAR_A); - value = EDRAM0_SIZE_G(value); - mem_info->size_edc0 = (u16)value; - - value = t4_read_reg(padap, MA_EDRAM1_BAR_A); - value = EDRAM1_SIZE_G(value); - mem_info->size_edc1 = (u16)value; - - value = t4_read_reg(padap, MA_TARGET_MEM_ENABLE_A); - if (value & EDRAM0_ENABLE_F) - mem_info->mem_flag |= (1 << EDC0_FLAG); - if (value & EDRAM1_ENABLE_F) - mem_info->mem_flag |= (1 << EDC1_FLAG); -} - static void cudbg_t4_fwcache(struct cudbg_init *pdbg_init, struct cudbg_error *cudbg_err) { @@ -495,37 +937,25 @@ static int cudbg_collect_mem_region(struct cudbg_init *pdbg_init, struct cudbg_error *cudbg_err, u8 mem_type) { - struct card_mem mem_info = {0}; - unsigned long flag, size; + struct adapter *padap = pdbg_init->adap; + struct cudbg_meminfo mem_info; + unsigned long size; + u8 mc_idx; int rc; + memset(&mem_info, 0, sizeof(struct cudbg_meminfo)); + rc = cudbg_fill_meminfo(padap, &mem_info); + if (rc) + return rc; + cudbg_t4_fwcache(pdbg_init, cudbg_err); - cudbg_collect_mem_info(pdbg_init, &mem_info); - switch (mem_type) { - case MEM_EDC0: - flag = (1 << EDC0_FLAG); - size = cudbg_mbytes_to_bytes(mem_info.size_edc0); - break; - case MEM_EDC1: - flag = (1 << EDC1_FLAG); - size = cudbg_mbytes_to_bytes(mem_info.size_edc1); - break; - default: - rc = CUDBG_STATUS_ENTITY_NOT_FOUND; - goto err; - } + rc = cudbg_meminfo_get_mem_index(padap, &mem_info, mem_type, &mc_idx); + if (rc) + return rc; - if (mem_info.mem_flag & flag) { - rc = cudbg_read_fw_mem(pdbg_init, dbg_buff, mem_type, - size, cudbg_err); - if (rc) - goto err; - } else { - rc = CUDBG_STATUS_ENTITY_NOT_FOUND; - goto err; - } -err: - return rc; + size = mem_info.avail[mc_idx].limit - mem_info.avail[mc_idx].base; + return cudbg_read_fw_mem(pdbg_init, dbg_buff, mem_type, size, + cudbg_err); } int cudbg_collect_edc0_meminfo(struct cudbg_init *pdbg_init, @@ -544,6 +974,30 @@ int cudbg_collect_edc1_meminfo(struct cudbg_init *pdbg_init, MEM_EDC1); } +int cudbg_collect_mc0_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) +{ + return cudbg_collect_mem_region(pdbg_init, dbg_buff, cudbg_err, + MEM_MC0); +} + +int cudbg_collect_mc1_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) +{ + return cudbg_collect_mem_region(pdbg_init, dbg_buff, cudbg_err, + MEM_MC1); +} + +int cudbg_collect_hma_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) +{ + return cudbg_collect_mem_region(pdbg_init, dbg_buff, cudbg_err, + MEM_HMA); +} + int cudbg_collect_rss(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err) @@ -843,6 +1297,31 @@ int cudbg_collect_tp_la(struct cudbg_init *pdbg_init, return rc; } +int cudbg_collect_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) +{ + struct adapter *padap = pdbg_init->adap; + struct cudbg_buffer temp_buff = { 0 }; + struct cudbg_meminfo *meminfo_buff; + int rc; + + rc = cudbg_get_buff(dbg_buff, sizeof(struct cudbg_meminfo), &temp_buff); + if (rc) + return rc; + + meminfo_buff = (struct cudbg_meminfo *)temp_buff.data; + rc = cudbg_fill_meminfo(padap, meminfo_buff); + if (rc) { + cudbg_err->sys_err = rc; + cudbg_put_buff(&temp_buff, dbg_buff); + return rc; + } + + cudbg_write_and_release_buff(&temp_buff, dbg_buff); + return rc; +} + int cudbg_collect_cim_pif_la(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err) @@ -1115,22 +1594,135 @@ int cudbg_collect_tid(struct cudbg_init *pdbg_init, return rc; } -int cudbg_dump_context_size(struct adapter *padap) +int cudbg_collect_pcie_config(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err) { - u32 value, size; + struct adapter *padap = pdbg_init->adap; + struct cudbg_buffer temp_buff = { 0 }; + u32 size, *value, j; + int i, rc, n; + + size = sizeof(u32) * CUDBG_NUM_PCIE_CONFIG_REGS; + n = sizeof(t5_pcie_config_array) / (2 * sizeof(u32)); + rc = cudbg_get_buff(dbg_buff, size, &temp_buff); + if (rc) + return rc; + + value = (u32 *)temp_buff.data; + for (i = 0; i < n; i++) { + for (j = t5_pcie_config_array[i][0]; + j <= t5_pcie_config_array[i][1]; j += 4) { + t4_hw_pci_read_cfg4(padap, j, value); + value++; + } + } + cudbg_write_and_release_buff(&temp_buff, dbg_buff); + return rc; +} + +static int cudbg_sge_ctxt_check_valid(u32 *buf, int type) +{ + int index, bit, bit_pos = 0; + + switch (type) { + case CTXT_EGRESS: + bit_pos = 176; + break; + case CTXT_INGRESS: + bit_pos = 141; + break; + case CTXT_FLM: + bit_pos = 89; + break; + } + index = bit_pos / 32; + bit = bit_pos % 32; + return buf[index] & (1U << bit); +} + +static int cudbg_get_ctxt_region_info(struct adapter *padap, + struct cudbg_region_info *ctx_info, + u8 *mem_type) +{ + struct cudbg_mem_desc mem_desc; + struct cudbg_meminfo meminfo; + u32 i, j, value, found; u8 flq; + int rc; + rc = cudbg_fill_meminfo(padap, &meminfo); + if (rc) + return rc; + + /* Get EGRESS and INGRESS context region size */ + for (i = CTXT_EGRESS; i <= CTXT_INGRESS; i++) { + found = 0; + memset(&mem_desc, 0, sizeof(struct cudbg_mem_desc)); + for (j = 0; j < ARRAY_SIZE(meminfo.avail); j++) { + rc = cudbg_get_mem_region(padap, &meminfo, j, + cudbg_region[i], + &mem_desc); + if (!rc) { + found = 1; + rc = cudbg_get_mem_relative(padap, &meminfo, j, + &mem_desc.base, + &mem_desc.limit); + if (rc) { + ctx_info[i].exist = false; + break; + } + ctx_info[i].exist = true; + ctx_info[i].start = mem_desc.base; + ctx_info[i].end = mem_desc.limit; + mem_type[i] = j; + break; + } + } + if (!found) + ctx_info[i].exist = false; + } + + /* Get FLM and CNM max qid. */ value = t4_read_reg(padap, SGE_FLM_CFG_A); /* Get number of data freelist queues */ flq = HDRSTARTFLQ_G(value); - size = CUDBG_MAX_FL_QIDS >> flq; + ctx_info[CTXT_FLM].exist = true; + ctx_info[CTXT_FLM].end = (CUDBG_MAX_FL_QIDS >> flq) * SGE_CTXT_SIZE; - /* Add extra space for congestion manager contexts. - * The number of CONM contexts are same as number of freelist + /* The number of CONM contexts are same as number of freelist * queues. */ - size += size; + ctx_info[CTXT_CNM].exist = true; + ctx_info[CTXT_CNM].end = ctx_info[CTXT_FLM].end; + + return 0; +} + +int cudbg_dump_context_size(struct adapter *padap) +{ + struct cudbg_region_info region_info[CTXT_CNM + 1] = { {0} }; + u8 mem_type[CTXT_INGRESS + 1] = { 0 }; + u32 i, size = 0; + int rc; + + /* Get max valid qid for each type of queue */ + rc = cudbg_get_ctxt_region_info(padap, region_info, mem_type); + if (rc) + return rc; + + for (i = 0; i < CTXT_CNM; i++) { + if (!region_info[i].exist) { + if (i == CTXT_EGRESS || i == CTXT_INGRESS) + size += CUDBG_LOWMEM_MAX_CTXT_QIDS * + SGE_CTXT_SIZE; + continue; + } + + size += (region_info[i].end - region_info[i].start + 1) / + SGE_CTXT_SIZE; + } return size * sizeof(struct cudbg_ch_cntxt); } @@ -1153,16 +1745,54 @@ static void cudbg_read_sge_ctxt(struct cudbg_init *pdbg_init, u32 cid, t4_sge_ctxt_rd_bd(padap, cid, ctype, data); } +static void cudbg_get_sge_ctxt_fw(struct cudbg_init *pdbg_init, u32 max_qid, + u8 ctxt_type, + struct cudbg_ch_cntxt **out_buff) +{ + struct cudbg_ch_cntxt *buff = *out_buff; + int rc; + u32 j; + + for (j = 0; j < max_qid; j++) { + cudbg_read_sge_ctxt(pdbg_init, j, ctxt_type, buff->data); + rc = cudbg_sge_ctxt_check_valid(buff->data, ctxt_type); + if (!rc) + continue; + + buff->cntxt_type = ctxt_type; + buff->cntxt_id = j; + buff++; + if (ctxt_type == CTXT_FLM) { + cudbg_read_sge_ctxt(pdbg_init, j, CTXT_CNM, buff->data); + buff->cntxt_type = CTXT_CNM; + buff->cntxt_id = j; + buff++; + } + } + + *out_buff = buff; +} + int cudbg_collect_dump_context(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err) { + struct cudbg_region_info region_info[CTXT_CNM + 1] = { {0} }; struct adapter *padap = pdbg_init->adap; + u32 j, size, max_ctx_size, max_ctx_qid; + u8 mem_type[CTXT_INGRESS + 1] = { 0 }; struct cudbg_buffer temp_buff = { 0 }; struct cudbg_ch_cntxt *buff; - u32 size, i = 0; + u64 *dst_off, *src_off; + u8 *ctx_buf; + u8 i, k; int rc; + /* Get max valid qid for each type of queue */ + rc = cudbg_get_ctxt_region_info(padap, region_info, mem_type); + if (rc) + return rc; + rc = cudbg_dump_context_size(padap); if (rc <= 0) return CUDBG_STATUS_ENTITY_NOT_FOUND; @@ -1172,23 +1802,79 @@ int cudbg_collect_dump_context(struct cudbg_init *pdbg_init, if (rc) return rc; + /* Get buffer with enough space to read the biggest context + * region in memory. + */ + max_ctx_size = max(region_info[CTXT_EGRESS].end - + region_info[CTXT_EGRESS].start + 1, + region_info[CTXT_INGRESS].end - + region_info[CTXT_INGRESS].start + 1); + + ctx_buf = kvzalloc(max_ctx_size, GFP_KERNEL); + if (!ctx_buf) { + cudbg_put_buff(&temp_buff, dbg_buff); + return -ENOMEM; + } + buff = (struct cudbg_ch_cntxt *)temp_buff.data; - while (size > 0) { - buff->cntxt_type = CTXT_FLM; - buff->cntxt_id = i; - cudbg_read_sge_ctxt(pdbg_init, i, CTXT_FLM, buff->data); - buff++; - size -= sizeof(struct cudbg_ch_cntxt); - buff->cntxt_type = CTXT_CNM; - buff->cntxt_id = i; - cudbg_read_sge_ctxt(pdbg_init, i, CTXT_CNM, buff->data); - buff++; - size -= sizeof(struct cudbg_ch_cntxt); + /* Collect EGRESS and INGRESS context data. + * In case of failures, fallback to collecting via FW or + * backdoor access. + */ + for (i = CTXT_EGRESS; i <= CTXT_INGRESS; i++) { + if (!region_info[i].exist) { + max_ctx_qid = CUDBG_LOWMEM_MAX_CTXT_QIDS; + cudbg_get_sge_ctxt_fw(pdbg_init, max_ctx_qid, i, + &buff); + continue; + } - i++; + max_ctx_size = region_info[i].end - region_info[i].start + 1; + max_ctx_qid = max_ctx_size / SGE_CTXT_SIZE; + + t4_sge_ctxt_flush(padap, padap->mbox, i); + rc = t4_memory_rw(padap, MEMWIN_NIC, mem_type[i], + region_info[i].start, max_ctx_size, + (__be32 *)ctx_buf, 1); + if (rc) { + max_ctx_qid = CUDBG_LOWMEM_MAX_CTXT_QIDS; + cudbg_get_sge_ctxt_fw(pdbg_init, max_ctx_qid, i, + &buff); + continue; + } + + for (j = 0; j < max_ctx_qid; j++) { + src_off = (u64 *)(ctx_buf + j * SGE_CTXT_SIZE); + dst_off = (u64 *)buff->data; + + /* The data is stored in 64-bit cpu order. Convert it + * to big endian before parsing. + */ + for (k = 0; k < SGE_CTXT_SIZE / sizeof(u64); k++) + dst_off[k] = cpu_to_be64(src_off[k]); + + rc = cudbg_sge_ctxt_check_valid(buff->data, i); + if (!rc) + continue; + + buff->cntxt_type = i; + buff->cntxt_id = j; + buff++; + } } + kvfree(ctx_buf); + + /* Collect FREELIST and CONGESTION MANAGER contexts */ + max_ctx_size = region_info[CTXT_FLM].end - + region_info[CTXT_FLM].start + 1; + max_ctx_qid = max_ctx_size / SGE_CTXT_SIZE; + /* Since FLM and CONM are 1-to-1 mapped, the below function + * will fetch both FLM and CONM contexts. + */ + cudbg_get_sge_ctxt_fw(pdbg_init, max_ctx_qid, CTXT_FLM, &buff); + cudbg_write_and_release_buff(&temp_buff, dbg_buff); return rc; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h index caeee8e33e86..eebefe7cd18e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.h @@ -75,6 +75,12 @@ int cudbg_collect_edc0_meminfo(struct cudbg_init *pdbg_init, int cudbg_collect_edc1_meminfo(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); +int cudbg_collect_mc0_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); +int cudbg_collect_mc1_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); int cudbg_collect_rss(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); @@ -102,6 +108,9 @@ int cudbg_collect_ulprx_la(struct cudbg_init *pdbg_init, int cudbg_collect_tp_la(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); +int cudbg_collect_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); int cudbg_collect_cim_pif_la(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); @@ -123,6 +132,9 @@ int cudbg_collect_pm_indirect(struct cudbg_init *pdbg_init, int cudbg_collect_tid(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); +int cudbg_collect_pcie_config(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); int cudbg_collect_dump_context(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); @@ -156,6 +168,9 @@ int cudbg_collect_mbox_log(struct cudbg_init *pdbg_init, int cudbg_collect_hma_indirect(struct cudbg_init *pdbg_init, struct cudbg_buffer *dbg_buff, struct cudbg_error *cudbg_err); +int cudbg_collect_hma_meminfo(struct cudbg_init *pdbg_init, + struct cudbg_buffer *dbg_buff, + struct cudbg_error *cudbg_err); struct cudbg_entity_hdr *cudbg_get_entity_hdr(void *outbuf, int i); void cudbg_align_debug_buffer(struct cudbg_buffer *dbg_buff, @@ -163,7 +178,8 @@ void cudbg_align_debug_buffer(struct cudbg_buffer *dbg_buff, u32 cudbg_cim_obq_size(struct adapter *padap, int qid); int cudbg_dump_context_size(struct adapter *padap); -struct cudbg_tcam; +int cudbg_fill_meminfo(struct adapter *padap, + struct cudbg_meminfo *meminfo_buff); void cudbg_fill_le_tcam_info(struct adapter *padap, struct cudbg_tcam *tcam_region); #endif /* __CUDBG_LIB_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 6f9fa6e3c42a..97dc3efeb234 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -77,7 +77,8 @@ enum { MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, - MEM_MC1 + MEM_MC1, + MEM_HMA, }; enum { @@ -1653,7 +1654,7 @@ int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int eqid); int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int eqid); -int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox); +int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox, int ctxt_type); void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl); int t4_update_port_info(struct port_info *pi); int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c index 29cc625e9833..41c8736314f8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.c @@ -18,11 +18,13 @@ #include "t4_regs.h" #include "cxgb4.h" #include "cxgb4_cudbg.h" -#include "cudbg_entity.h" static const struct cxgb4_collect_entity cxgb4_collect_mem_dump[] = { { CUDBG_EDC0, cudbg_collect_edc0_meminfo }, { CUDBG_EDC1, cudbg_collect_edc1_meminfo }, + { CUDBG_MC0, cudbg_collect_mc0_meminfo }, + { CUDBG_MC1, cudbg_collect_mc1_meminfo }, + { CUDBG_HMA, cudbg_collect_hma_meminfo }, }; static const struct cxgb4_collect_entity cxgb4_collect_hw_dump[] = { @@ -53,6 +55,7 @@ static const struct cxgb4_collect_entity cxgb4_collect_hw_dump[] = { { CUDBG_SGE_INDIRECT, cudbg_collect_sge_indirect }, { CUDBG_ULPRX_LA, cudbg_collect_ulprx_la }, { CUDBG_TP_LA, cudbg_collect_tp_la }, + { CUDBG_MEMINFO, cudbg_collect_meminfo }, { CUDBG_CIM_PIF_LA, cudbg_collect_cim_pif_la }, { CUDBG_CLK, cudbg_collect_clk_info }, { CUDBG_CIM_OBQ_RXQ0, cudbg_collect_obq_sge_rx_q0 }, @@ -60,6 +63,7 @@ static const struct cxgb4_collect_entity cxgb4_collect_hw_dump[] = { { CUDBG_PCIE_INDIRECT, cudbg_collect_pcie_indirect }, { CUDBG_PM_INDIRECT, cudbg_collect_pm_indirect }, { CUDBG_TID_INFO, cudbg_collect_tid }, + { CUDBG_PCIE_CONFIG, cudbg_collect_pcie_config }, { CUDBG_DUMP_CONTEXT, cudbg_collect_dump_context }, { CUDBG_MPS_TCAM, cudbg_collect_mps_tcam }, { CUDBG_VPD_DATA, cudbg_collect_vpd_data }, @@ -158,6 +162,22 @@ static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity) } len = cudbg_mbytes_to_bytes(len); break; + case CUDBG_MC0: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EXT_MEM0_ENABLE_F) { + value = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A); + len = EXT_MEM0_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; + case CUDBG_MC1: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & EXT_MEM1_ENABLE_F) { + value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); + len = EXT_MEM1_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; case CUDBG_RSS: len = RSS_NENTRIES * sizeof(u16); break; @@ -201,6 +221,9 @@ static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity) case CUDBG_TP_LA: len = sizeof(struct cudbg_tp_la) + TPLA_SIZE * sizeof(u64); break; + case CUDBG_MEMINFO: + len = sizeof(struct cudbg_meminfo); + break; case CUDBG_CIM_PIF_LA: len = sizeof(struct cudbg_cim_pif_la); len += 2 * CIM_PIFLA_SIZE * 6 * sizeof(u32); @@ -219,6 +242,9 @@ static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity) case CUDBG_TID_INFO: len = sizeof(struct cudbg_tid_info_region_rev1); break; + case CUDBG_PCIE_CONFIG: + len = sizeof(u32) * CUDBG_NUM_PCIE_CONFIG_REGS; + break; case CUDBG_DUMP_CONTEXT: len = cudbg_dump_context_size(adap); break; @@ -264,6 +290,17 @@ static u32 cxgb4_get_entity_length(struct adapter *adap, u32 entity) len = sizeof(struct ireg_buf) * n; } break; + case CUDBG_HMA: + value = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); + if (value & HMA_MUX_F) { + /* In T6, there's no MC1. So, HMA shares MC1 + * address space. + */ + value = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); + len = EXT_MEM1_SIZE_G(value); + } + len = cudbg_mbytes_to_bytes(len); + break; default: break; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h index c099b5aa2214..7ceeb0bc9fa8 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_cudbg.h @@ -20,6 +20,7 @@ #include "cudbg_if.h" #include "cudbg_lib_common.h" +#include "cudbg_entity.h" #include "cudbg_lib.h" typedef int (*cudbg_collect_callback_t)(struct cudbg_init *pdbg_init, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index 917663b35603..4956e429ae1d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -45,6 +45,10 @@ #include "cxgb4_debugfs.h" #include "clip_tbl.h" #include "l2t.h" +#include "cudbg_if.h" +#include "cudbg_lib_common.h" +#include "cudbg_entity.h" +#include "cudbg_lib.h" /* generic seq_file support for showing a table of size rows x width. */ static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos) @@ -2794,18 +2798,6 @@ static const struct file_operations blocked_fl_fops = { .llseek = generic_file_llseek, }; -struct mem_desc { - unsigned int base; - unsigned int limit; - unsigned int idx; -}; - -static int mem_desc_cmp(const void *a, const void *b) -{ - return ((const struct mem_desc *)a)->base - - ((const struct mem_desc *)b)->base; -} - static void mem_region_show(struct seq_file *seq, const char *name, unsigned int from, unsigned int to) { @@ -2819,250 +2811,60 @@ static void mem_region_show(struct seq_file *seq, const char *name, static int meminfo_show(struct seq_file *seq, void *v) { static const char * const memory[] = { "EDC0:", "EDC1:", "MC:", - "MC0:", "MC1:"}; - static const char * const region[] = { - "DBQ contexts:", "IMSG contexts:", "FLM cache:", "TCBs:", - "Pstructs:", "Timers:", "Rx FL:", "Tx FL:", "Pstruct FL:", - "Tx payload:", "Rx payload:", "LE hash:", "iSCSI region:", - "TDDP region:", "TPT region:", "STAG region:", "RQ region:", - "RQUDP region:", "PBL region:", "TXPBL region:", - "DBVFIFO region:", "ULPRX state:", "ULPTX state:", - "On-chip queues:" - }; - - int i, n; - u32 lo, hi, used, alloc; - struct mem_desc avail[4]; - struct mem_desc mem[ARRAY_SIZE(region) + 3]; /* up to 3 holes */ - struct mem_desc *md = mem; + "MC0:", "MC1:", "HMA:"}; struct adapter *adap = seq->private; + struct cudbg_meminfo meminfo; + int i, rc; - for (i = 0; i < ARRAY_SIZE(mem); i++) { - mem[i].limit = 0; - mem[i].idx = i; - } - - /* Find and sort the populated memory ranges */ - i = 0; - lo = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); - if (lo & EDRAM0_ENABLE_F) { - hi = t4_read_reg(adap, MA_EDRAM0_BAR_A); - avail[i].base = EDRAM0_BASE_G(hi) << 20; - avail[i].limit = avail[i].base + (EDRAM0_SIZE_G(hi) << 20); - avail[i].idx = 0; - i++; - } - if (lo & EDRAM1_ENABLE_F) { - hi = t4_read_reg(adap, MA_EDRAM1_BAR_A); - avail[i].base = EDRAM1_BASE_G(hi) << 20; - avail[i].limit = avail[i].base + (EDRAM1_SIZE_G(hi) << 20); - avail[i].idx = 1; - i++; - } - - if (is_t5(adap->params.chip)) { - if (lo & EXT_MEM0_ENABLE_F) { - hi = t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A); - avail[i].base = EXT_MEM0_BASE_G(hi) << 20; - avail[i].limit = - avail[i].base + (EXT_MEM0_SIZE_G(hi) << 20); - avail[i].idx = 3; - i++; - } - if (lo & EXT_MEM1_ENABLE_F) { - hi = t4_read_reg(adap, MA_EXT_MEMORY1_BAR_A); - avail[i].base = EXT_MEM1_BASE_G(hi) << 20; - avail[i].limit = - avail[i].base + (EXT_MEM1_SIZE_G(hi) << 20); - avail[i].idx = 4; - i++; - } - } else { - if (lo & EXT_MEM_ENABLE_F) { - hi = t4_read_reg(adap, MA_EXT_MEMORY_BAR_A); - avail[i].base = EXT_MEM_BASE_G(hi) << 20; - avail[i].limit = - avail[i].base + (EXT_MEM_SIZE_G(hi) << 20); - avail[i].idx = 2; - i++; - } - } - if (!i) /* no memory available */ - return 0; - sort(avail, i, sizeof(struct mem_desc), mem_desc_cmp, NULL); - - (md++)->base = t4_read_reg(adap, SGE_DBQ_CTXT_BADDR_A); - (md++)->base = t4_read_reg(adap, SGE_IMSG_CTXT_BADDR_A); - (md++)->base = t4_read_reg(adap, SGE_FLM_CACHE_BADDR_A); - (md++)->base = t4_read_reg(adap, TP_CMM_TCB_BASE_A); - (md++)->base = t4_read_reg(adap, TP_CMM_MM_BASE_A); - (md++)->base = t4_read_reg(adap, TP_CMM_TIMER_BASE_A); - (md++)->base = t4_read_reg(adap, TP_CMM_MM_RX_FLST_BASE_A); - (md++)->base = t4_read_reg(adap, TP_CMM_MM_TX_FLST_BASE_A); - (md++)->base = t4_read_reg(adap, TP_CMM_MM_PS_FLST_BASE_A); - - /* the next few have explicit upper bounds */ - md->base = t4_read_reg(adap, TP_PMM_TX_BASE_A); - md->limit = md->base - 1 + - t4_read_reg(adap, TP_PMM_TX_PAGE_SIZE_A) * - PMTXMAXPAGE_G(t4_read_reg(adap, TP_PMM_TX_MAX_PAGE_A)); - md++; - - md->base = t4_read_reg(adap, TP_PMM_RX_BASE_A); - md->limit = md->base - 1 + - t4_read_reg(adap, TP_PMM_RX_PAGE_SIZE_A) * - PMRXMAXPAGE_G(t4_read_reg(adap, TP_PMM_RX_MAX_PAGE_A)); - md++; - - if (t4_read_reg(adap, LE_DB_CONFIG_A) & HASHEN_F) { - if (CHELSIO_CHIP_VERSION(adap->params.chip) <= CHELSIO_T5) { - hi = t4_read_reg(adap, LE_DB_TID_HASHBASE_A) / 4; - md->base = t4_read_reg(adap, LE_DB_HASH_TID_BASE_A); - } else { - hi = t4_read_reg(adap, LE_DB_HASH_TID_BASE_A); - md->base = t4_read_reg(adap, - LE_DB_HASH_TBL_BASE_ADDR_A); - } - md->limit = 0; - } else { - md->base = 0; - md->idx = ARRAY_SIZE(region); /* hide it */ - } - md++; - -#define ulp_region(reg) do { \ - md->base = t4_read_reg(adap, ULP_ ## reg ## _LLIMIT_A);\ - (md++)->limit = t4_read_reg(adap, ULP_ ## reg ## _ULIMIT_A); \ -} while (0) - - ulp_region(RX_ISCSI); - ulp_region(RX_TDDP); - ulp_region(TX_TPT); - ulp_region(RX_STAG); - ulp_region(RX_RQ); - ulp_region(RX_RQUDP); - ulp_region(RX_PBL); - ulp_region(TX_PBL); -#undef ulp_region - md->base = 0; - md->idx = ARRAY_SIZE(region); - if (!is_t4(adap->params.chip)) { - u32 size = 0; - u32 sge_ctrl = t4_read_reg(adap, SGE_CONTROL2_A); - u32 fifo_size = t4_read_reg(adap, SGE_DBVFIFO_SIZE_A); - - if (is_t5(adap->params.chip)) { - if (sge_ctrl & VFIFO_ENABLE_F) - size = DBVFIFO_SIZE_G(fifo_size); - } else { - size = T6_DBVFIFO_SIZE_G(fifo_size); - } - - if (size) { - md->base = BASEADDR_G(t4_read_reg(adap, - SGE_DBVFIFO_BADDR_A)); - md->limit = md->base + (size << 2) - 1; - } - } - - md++; - - md->base = t4_read_reg(adap, ULP_RX_CTX_BASE_A); - md->limit = 0; - md++; - md->base = t4_read_reg(adap, ULP_TX_ERR_TABLE_BASE_A); - md->limit = 0; - md++; - - md->base = adap->vres.ocq.start; - if (adap->vres.ocq.size) - md->limit = md->base + adap->vres.ocq.size - 1; - else - md->idx = ARRAY_SIZE(region); /* hide it */ - md++; - - /* add any address-space holes, there can be up to 3 */ - for (n = 0; n < i - 1; n++) - if (avail[n].limit < avail[n + 1].base) - (md++)->base = avail[n].limit; - if (avail[n].limit) - (md++)->base = avail[n].limit; - - n = md - mem; - sort(mem, n, sizeof(struct mem_desc), mem_desc_cmp, NULL); + memset(&meminfo, 0, sizeof(struct cudbg_meminfo)); + rc = cudbg_fill_meminfo(adap, &meminfo); + if (rc) + return -ENXIO; - for (lo = 0; lo < i; lo++) - mem_region_show(seq, memory[avail[lo].idx], avail[lo].base, - avail[lo].limit - 1); + for (i = 0; i < meminfo.avail_c; i++) + mem_region_show(seq, memory[meminfo.avail[i].idx], + meminfo.avail[i].base, + meminfo.avail[i].limit - 1); seq_putc(seq, '\n'); - for (i = 0; i < n; i++) { - if (mem[i].idx >= ARRAY_SIZE(region)) + for (i = 0; i < meminfo.mem_c; i++) { + if (meminfo.mem[i].idx >= ARRAY_SIZE(cudbg_region)) continue; /* skip holes */ - if (!mem[i].limit) - mem[i].limit = i < n - 1 ? mem[i + 1].base - 1 : ~0; - mem_region_show(seq, region[mem[i].idx], mem[i].base, - mem[i].limit); + if (!meminfo.mem[i].limit) + meminfo.mem[i].limit = + i < meminfo.mem_c - 1 ? + meminfo.mem[i + 1].base - 1 : ~0; + mem_region_show(seq, cudbg_region[meminfo.mem[i].idx], + meminfo.mem[i].base, meminfo.mem[i].limit); } seq_putc(seq, '\n'); - lo = t4_read_reg(adap, CIM_SDRAM_BASE_ADDR_A); - hi = t4_read_reg(adap, CIM_SDRAM_ADDR_SIZE_A) + lo - 1; - mem_region_show(seq, "uP RAM:", lo, hi); + mem_region_show(seq, "uP RAM:", meminfo.up_ram_lo, meminfo.up_ram_hi); + mem_region_show(seq, "uP Extmem2:", meminfo.up_extmem2_lo, + meminfo.up_extmem2_hi); - lo = t4_read_reg(adap, CIM_EXTMEM2_BASE_ADDR_A); - hi = t4_read_reg(adap, CIM_EXTMEM2_ADDR_SIZE_A) + lo - 1; - mem_region_show(seq, "uP Extmem2:", lo, hi); - - lo = t4_read_reg(adap, TP_PMM_RX_MAX_PAGE_A); seq_printf(seq, "\n%u Rx pages of size %uKiB for %u channels\n", - PMRXMAXPAGE_G(lo), - t4_read_reg(adap, TP_PMM_RX_PAGE_SIZE_A) >> 10, - (lo & PMRXNUMCHN_F) ? 2 : 1); + meminfo.rx_pages_data[0], meminfo.rx_pages_data[1], + meminfo.rx_pages_data[2]); - lo = t4_read_reg(adap, TP_PMM_TX_MAX_PAGE_A); - hi = t4_read_reg(adap, TP_PMM_TX_PAGE_SIZE_A); seq_printf(seq, "%u Tx pages of size %u%ciB for %u channels\n", - PMTXMAXPAGE_G(lo), - hi >= (1 << 20) ? (hi >> 20) : (hi >> 10), - hi >= (1 << 20) ? 'M' : 'K', 1 << PMTXNUMCHN_G(lo)); - seq_printf(seq, "%u p-structs\n\n", - t4_read_reg(adap, TP_CMM_MM_MAX_PSTRUCT_A)); - - for (i = 0; i < 4; i++) { - if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) - lo = t4_read_reg(adap, MPS_RX_MAC_BG_PG_CNT0_A + i * 4); - else - lo = t4_read_reg(adap, MPS_RX_PG_RSV0_A + i * 4); - if (is_t5(adap->params.chip)) { - used = T5_USED_G(lo); - alloc = T5_ALLOC_G(lo); - } else { - used = USED_G(lo); - alloc = ALLOC_G(lo); - } + meminfo.tx_pages_data[0], meminfo.tx_pages_data[1], + meminfo.tx_pages_data[2], meminfo.tx_pages_data[3]); + + seq_printf(seq, "%u p-structs\n\n", meminfo.p_structs); + + for (i = 0; i < 4; i++) /* For T6 these are MAC buffer groups */ seq_printf(seq, "Port %d using %u pages out of %u allocated\n", - i, used, alloc); - } - for (i = 0; i < adap->params.arch.nchan; i++) { - if (CHELSIO_CHIP_VERSION(adap->params.chip) > CHELSIO_T5) - lo = t4_read_reg(adap, - MPS_RX_LPBK_BG_PG_CNT0_A + i * 4); - else - lo = t4_read_reg(adap, MPS_RX_PG_RSV4_A + i * 4); - if (is_t5(adap->params.chip)) { - used = T5_USED_G(lo); - alloc = T5_ALLOC_G(lo); - } else { - used = USED_G(lo); - alloc = ALLOC_G(lo); - } + i, meminfo.port_used[i], meminfo.port_alloc[i]); + + for (i = 0; i < adap->params.arch.nchan; i++) /* For T6 these are MAC buffer groups */ seq_printf(seq, "Loopback %d using %u pages out of %u allocated\n", - i, used, alloc); - } + i, meminfo.loopback_used[i], + meminfo.loopback_alloc[i]); + return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 6f900ffe25cc..87ac1e4dafc1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -1673,7 +1673,7 @@ int cxgb4_flush_eq_cache(struct net_device *dev) { struct adapter *adap = netdev2adap(dev); - return t4_sge_ctxt_flush(adap, adap->mbox); + return t4_sge_ctxt_flush(adap, adap->mbox, CTXT_EGRESS); } EXPORT_SYMBOL(cxgb4_flush_eq_cache); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c index d4a548a6a55c..a12b894f135d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c @@ -405,9 +405,7 @@ static void cxgb4_process_flow_actions(struct net_device *in, } else if (is_tcf_gact_shot(a)) { fs->action = FILTER_DROP; } else if (is_tcf_mirred_egress_redirect(a)) { - int ifindex = tcf_mirred_ifindex(a); - struct net_device *out = __dev_get_by_index(dev_net(in), - ifindex); + struct net_device *out = tcf_mirred_dev(a); struct port_info *pi = netdev_priv(out); fs->action = FILTER_SWITCH; @@ -582,14 +580,14 @@ static int cxgb4_validate_flow_actions(struct net_device *dev, /* Do nothing */ } else if (is_tcf_mirred_egress_redirect(a)) { struct adapter *adap = netdev2adap(dev); - struct net_device *n_dev; - unsigned int i, ifindex; + struct net_device *n_dev, *target_dev; + unsigned int i; bool found = false; - ifindex = tcf_mirred_ifindex(a); + target_dev = tcf_mirred_dev(a); for_each_port(adap, i) { n_dev = adap->port[i]; - if (ifindex == n_dev->ifindex) { + if (target_dev == n_dev) { found = true; break; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index cd0cd13a964d..ab174bcfbfb0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -114,14 +114,14 @@ static int fill_action_fields(struct adapter *adap, /* Re-direct to specified port in hardware. */ if (is_tcf_mirred_egress_redirect(a)) { - struct net_device *n_dev; - unsigned int i, index; + struct net_device *n_dev, *target_dev; bool found = false; + unsigned int i; - index = tcf_mirred_ifindex(a); + target_dev = tcf_mirred_dev(a); for_each_port(adap, i) { n_dev = adap->port[i]; - if (index == n_dev->ifindex) { + if (target_dev == n_dev) { fs->action = FILTER_SWITCH; fs->eport = i; found = true; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index f63210f15579..112963defd0b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -524,11 +524,14 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, * MEM_EDC1 = 1 * MEM_MC = 2 -- MEM_MC for chips with only 1 memory controller * MEM_MC1 = 3 -- for chips with 2 memory controllers (e.g. T5) + * MEM_HMA = 4 */ edc_size = EDRAM0_SIZE_G(t4_read_reg(adap, MA_EDRAM0_BAR_A)); - if (mtype != MEM_MC1) + if (mtype == MEM_HMA) { + memoffset = 2 * (edc_size * 1024 * 1024); + } else if (mtype != MEM_MC1) { memoffset = (mtype * (edc_size * 1024 * 1024)); - else { + } else { mc_size = EXT_MEM0_SIZE_G(t4_read_reg(adap, MA_EXT_MEMORY0_BAR_A)); memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024; @@ -6527,18 +6530,21 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) * t4_sge_ctxt_flush - flush the SGE context cache * @adap: the adapter * @mbox: mailbox to use for the FW command + * @ctx_type: Egress or Ingress * * Issues a FW command through the given mailbox to flush the * SGE context cache. */ -int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox) +int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox, int ctxt_type) { int ret; u32 ldst_addrspace; struct fw_ldst_cmd c; memset(&c, 0, sizeof(c)); - ldst_addrspace = FW_LDST_CMD_ADDRSPACE_V(FW_LDST_ADDRSPC_SGE_EGRC); + ldst_addrspace = FW_LDST_CMD_ADDRSPACE_V(ctxt_type == CTXT_EGRESS ? + FW_LDST_ADDRSPC_SGE_EGRC : + FW_LDST_ADDRSPC_SGE_INGC); c.op_to_addrspace = cpu_to_be32(FW_CMD_OP_V(FW_LDST_CMD) | FW_CMD_REQUEST_F | FW_CMD_READ_F | ldst_addrspace); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index a964ed184356..83afb32c8491 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -70,7 +70,9 @@ enum { /* SGE context types */ enum ctxt_type { - CTXT_FLM = 2, + CTXT_EGRESS, + CTXT_INGRESS, + CTXT_FLM, CTXT_CNM, }; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index a7cfece72828..f6701e0a6701 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -961,6 +961,10 @@ #define MA_EXT_MEMORY1_BAR_A 0x7808 +#define HMA_MUX_S 5 +#define HMA_MUX_V(x) ((x) << HMA_MUX_S) +#define HMA_MUX_F HMA_MUX_V(1U) + #define EXT_MEM1_BASE_S 16 #define EXT_MEM1_BASE_M 0xfffU #define EXT_MEM1_BASE_G(x) (((x) >> EXT_MEM1_BASE_S) & EXT_MEM1_BASE_M) diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 462d0ce51240..efb9333c7cf8 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -18,6 +18,7 @@ #include <linux/netdevice.h> #include <linux/ethtool.h> +#include <linux/net_tstamp.h> #include "enic_res.h" #include "enic.h" @@ -578,6 +579,16 @@ static int enic_set_rxfh(struct net_device *netdev, const u32 *indir, return __enic_set_rsskey(enic); } +static int enic_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *info) +{ + info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + + return 0; +} + static const struct ethtool_ops enic_ethtool_ops = { .get_drvinfo = enic_get_drvinfo, .get_msglevel = enic_get_msglevel, @@ -597,6 +608,7 @@ static const struct ethtool_ops enic_ethtool_ops = { .get_rxfh = enic_get_rxfh, .set_rxfh = enic_set_rxfh, .get_link_ksettings = enic_get_ksettings, + .get_ts_info = enic_get_ts_info, }; void enic_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index e130fb757e7b..d98676e43e03 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -856,6 +856,7 @@ static netdev_tx_t enic_hard_start_xmit(struct sk_buff *skb, if (vnic_wq_desc_avail(wq) < MAX_SKB_FRAGS + ENIC_DESC_MAX_SPLITS) netif_tx_stop_queue(txq); + skb_tx_timestamp(skb); if (!skb->xmit_more || netif_xmit_stopped(txq)) vnic_wq_doorbell(wq); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 59ed806a52c3..d07c700c7ff8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -17,7 +17,7 @@ #include <linux/netdevice.h> #include <linux/pci.h> #include <linux/platform_device.h> - +#include <net/rtnetlink.h> #include "hclge_cmd.h" #include "hclge_dcb.h" #include "hclge_main.h" @@ -2226,6 +2226,12 @@ static int hclge_mac_init(struct hclge_dev *hdev) return hclge_cfg_func_mta_filter(hdev, 0, hdev->accept_mta_mc); } +static void hclge_reset_task_schedule(struct hclge_dev *hdev) +{ + if (!test_and_set_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state)) + schedule_work(&hdev->rst_service_task); +} + static void hclge_task_schedule(struct hclge_dev *hdev) { if (!test_bit(HCLGE_STATE_DOWN, &hdev->state) && @@ -2362,6 +2368,46 @@ static void hclge_service_complete(struct hclge_dev *hdev) clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); } +static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) +{ + u32 rst_src_reg; + + /* fetch the events from their corresponding regs */ + rst_src_reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG); + + /* check for vector0 reset event sources */ + if (BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) & rst_src_reg) { + set_bit(HNAE3_GLOBAL_RESET, &hdev->reset_pending); + *clearval = BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B); + return HCLGE_VECTOR0_EVENT_RST; + } + + if (BIT(HCLGE_VECTOR0_CORERESET_INT_B) & rst_src_reg) { + set_bit(HNAE3_CORE_RESET, &hdev->reset_pending); + *clearval = BIT(HCLGE_VECTOR0_CORERESET_INT_B); + return HCLGE_VECTOR0_EVENT_RST; + } + + if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & rst_src_reg) { + set_bit(HNAE3_IMP_RESET, &hdev->reset_pending); + *clearval = BIT(HCLGE_VECTOR0_IMPRESET_INT_B); + return HCLGE_VECTOR0_EVENT_RST; + } + + /* mailbox event sharing vector 0 interrupt would be placed here */ + + return HCLGE_VECTOR0_EVENT_OTHER; +} + +static void hclge_clear_event_cause(struct hclge_dev *hdev, u32 event_type, + u32 regclr) +{ + if (event_type == HCLGE_VECTOR0_EVENT_RST) + hclge_write_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG, regclr); + + /* mailbox event sharing vector 0 interrupt would be placed here */ +} + static void hclge_enable_vector(struct hclge_misc_vector *vector, bool enable) { writel(enable ? 1 : 0, vector->addr); @@ -2370,10 +2416,28 @@ static void hclge_enable_vector(struct hclge_misc_vector *vector, bool enable) static irqreturn_t hclge_misc_irq_handle(int irq, void *data) { struct hclge_dev *hdev = data; + u32 event_cause; + u32 clearval; hclge_enable_vector(&hdev->misc_vector, false); - if (!test_and_set_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)) - schedule_work(&hdev->service_task); + event_cause = hclge_check_event_cause(hdev, &clearval); + + /* vector 0 interrupt is shared with reset and mailbox source events. + * For now, we are not handling mailbox events. + */ + switch (event_cause) { + case HCLGE_VECTOR0_EVENT_RST: + hclge_reset_task_schedule(hdev); + break; + default: + dev_dbg(&hdev->pdev->dev, + "received unknown or unhandled event of vector0\n"); + break; + } + + /* we should clear the source of interrupt */ + hclge_clear_event_cause(hdev, event_cause, clearval); + hclge_enable_vector(&hdev->misc_vector, true); return IRQ_HANDLED; } @@ -2404,9 +2468,9 @@ static int hclge_misc_irq_init(struct hclge_dev *hdev) hclge_get_misc_vector(hdev); - ret = devm_request_irq(&hdev->pdev->dev, - hdev->misc_vector.vector_irq, - hclge_misc_irq_handle, 0, "hclge_misc", hdev); + /* this would be explicitly freed in the end */ + ret = request_irq(hdev->misc_vector.vector_irq, hclge_misc_irq_handle, + 0, "hclge_misc", hdev); if (ret) { hclge_free_vector(hdev, 0); dev_err(&hdev->pdev->dev, "request misc irq(%d) fail\n", @@ -2416,6 +2480,12 @@ static int hclge_misc_irq_init(struct hclge_dev *hdev) return ret; } +static void hclge_misc_irq_uninit(struct hclge_dev *hdev) +{ + free_irq(hdev->misc_vector.vector_irq, hdev); + hclge_free_vector(hdev, 0); +} + static int hclge_notify_client(struct hclge_dev *hdev, enum hnae3_reset_notify_type type) { @@ -2471,12 +2541,6 @@ static int hclge_reset_wait(struct hclge_dev *hdev) cnt++; } - /* must clear reset status register to - * prevent driver detect reset interrupt again - */ - reg = hclge_read_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG); - hclge_write_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG, reg); - if (cnt >= HCLGE_RESET_WAIT_CNT) { dev_warn(&hdev->pdev->dev, "Wait for reset timeout: %d\n", hdev->reset_type); @@ -2505,12 +2569,12 @@ static int hclge_func_reset_cmd(struct hclge_dev *hdev, int func_id) return ret; } -static void hclge_do_reset(struct hclge_dev *hdev, enum hnae3_reset_type type) +static void hclge_do_reset(struct hclge_dev *hdev) { struct pci_dev *pdev = hdev->pdev; u32 val; - switch (type) { + switch (hdev->reset_type) { case HNAE3_GLOBAL_RESET: val = hclge_read_dev(&hdev->hw, HCLGE_GLOBAL_RESET_REG); hnae_set_bit(val, HCLGE_GLOBAL_RESET_BIT, 1); @@ -2526,30 +2590,62 @@ static void hclge_do_reset(struct hclge_dev *hdev, enum hnae3_reset_type type) case HNAE3_FUNC_RESET: dev_info(&pdev->dev, "PF Reset requested\n"); hclge_func_reset_cmd(hdev, 0); + /* schedule again to check later */ + set_bit(HNAE3_FUNC_RESET, &hdev->reset_pending); + hclge_reset_task_schedule(hdev); break; default: dev_warn(&pdev->dev, - "Unsupported reset type: %d\n", type); + "Unsupported reset type: %d\n", hdev->reset_type); break; } } -static enum hnae3_reset_type hclge_detected_reset_event(struct hclge_dev *hdev) +static enum hnae3_reset_type hclge_get_reset_level(struct hclge_dev *hdev, + unsigned long *addr) { enum hnae3_reset_type rst_level = HNAE3_NONE_RESET; - u32 rst_reg_val; - rst_reg_val = hclge_read_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG); - if (BIT(HCLGE_VECTOR0_GLOBALRESET_INT_B) & rst_reg_val) + /* return the highest priority reset level amongst all */ + if (test_bit(HNAE3_GLOBAL_RESET, addr)) rst_level = HNAE3_GLOBAL_RESET; - else if (BIT(HCLGE_VECTOR0_CORERESET_INT_B) & rst_reg_val) + else if (test_bit(HNAE3_CORE_RESET, addr)) rst_level = HNAE3_CORE_RESET; - else if (BIT(HCLGE_VECTOR0_IMPRESET_INT_B) & rst_reg_val) + else if (test_bit(HNAE3_IMP_RESET, addr)) rst_level = HNAE3_IMP_RESET; + else if (test_bit(HNAE3_FUNC_RESET, addr)) + rst_level = HNAE3_FUNC_RESET; + + /* now, clear all other resets */ + clear_bit(HNAE3_GLOBAL_RESET, addr); + clear_bit(HNAE3_CORE_RESET, addr); + clear_bit(HNAE3_IMP_RESET, addr); + clear_bit(HNAE3_FUNC_RESET, addr); return rst_level; } +static void hclge_reset(struct hclge_dev *hdev) +{ + /* perform reset of the stack & ae device for a client */ + + hclge_notify_client(hdev, HNAE3_DOWN_CLIENT); + + if (!hclge_reset_wait(hdev)) { + rtnl_lock(); + hclge_notify_client(hdev, HNAE3_UNINIT_CLIENT); + hclge_reset_ae_dev(hdev->ae_dev); + hclge_notify_client(hdev, HNAE3_INIT_CLIENT); + rtnl_unlock(); + } else { + /* schedule again to check pending resets later */ + set_bit(hdev->reset_type, &hdev->reset_pending); + hclge_reset_task_schedule(hdev); + } + + hclge_notify_client(hdev, HNAE3_UP_CLIENT); +} + static void hclge_reset_event(struct hnae3_handle *handle, enum hnae3_reset_type reset) { @@ -2563,14 +2659,9 @@ static void hclge_reset_event(struct hnae3_handle *handle, case HNAE3_FUNC_RESET: case HNAE3_CORE_RESET: case HNAE3_GLOBAL_RESET: - if (test_bit(HCLGE_STATE_RESET_INT, &hdev->state)) { - dev_err(&hdev->pdev->dev, "Already in reset state"); - return; - } - hdev->reset_type = reset; - set_bit(HCLGE_STATE_RESET_INT, &hdev->state); - set_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); - schedule_work(&hdev->service_task); + /* request reset & schedule reset task */ + set_bit(reset, &hdev->reset_request); + hclge_reset_task_schedule(hdev); break; default: dev_warn(&hdev->pdev->dev, "Unsupported reset event:%d", reset); @@ -2580,49 +2671,40 @@ static void hclge_reset_event(struct hnae3_handle *handle, static void hclge_reset_subtask(struct hclge_dev *hdev) { - bool do_reset; - - do_reset = hdev->reset_type != HNAE3_NONE_RESET; - - /* Reset is detected by interrupt */ - if (hdev->reset_type == HNAE3_NONE_RESET) - hdev->reset_type = hclge_detected_reset_event(hdev); - - if (hdev->reset_type == HNAE3_NONE_RESET) - return; - - switch (hdev->reset_type) { - case HNAE3_FUNC_RESET: - case HNAE3_CORE_RESET: - case HNAE3_GLOBAL_RESET: - case HNAE3_IMP_RESET: - hclge_notify_client(hdev, HNAE3_DOWN_CLIENT); + /* check if there is any ongoing reset in the hardware. This status can + * be checked from reset_pending. If there is then, we need to wait for + * hardware to complete reset. + * a. If we are able to figure out in reasonable time that hardware + * has fully resetted then, we can proceed with driver, client + * reset. + * b. else, we can come back later to check this status so re-sched + * now. + */ + hdev->reset_type = hclge_get_reset_level(hdev, &hdev->reset_pending); + if (hdev->reset_type != HNAE3_NONE_RESET) + hclge_reset(hdev); - if (do_reset) - hclge_do_reset(hdev, hdev->reset_type); - else - set_bit(HCLGE_STATE_RESET_INT, &hdev->state); + /* check if we got any *new* reset requests to be honored */ + hdev->reset_type = hclge_get_reset_level(hdev, &hdev->reset_request); + if (hdev->reset_type != HNAE3_NONE_RESET) + hclge_do_reset(hdev); - if (!hclge_reset_wait(hdev)) { - hclge_notify_client(hdev, HNAE3_UNINIT_CLIENT); - hclge_reset_ae_dev(hdev->ae_dev); - hclge_notify_client(hdev, HNAE3_INIT_CLIENT); - clear_bit(HCLGE_STATE_RESET_INT, &hdev->state); - } - hclge_notify_client(hdev, HNAE3_UP_CLIENT); - break; - default: - dev_err(&hdev->pdev->dev, "Unsupported reset type:%d\n", - hdev->reset_type); - break; - } hdev->reset_type = HNAE3_NONE_RESET; } -static void hclge_misc_irq_service_task(struct hclge_dev *hdev) +static void hclge_reset_service_task(struct work_struct *work) { + struct hclge_dev *hdev = + container_of(work, struct hclge_dev, rst_service_task); + + if (test_and_set_bit(HCLGE_STATE_RST_HANDLING, &hdev->state)) + return; + + clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state); + hclge_reset_subtask(hdev); - hclge_enable_vector(&hdev->misc_vector, true); + + clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state); } static void hclge_service_task(struct work_struct *work) @@ -2630,7 +2712,6 @@ static void hclge_service_task(struct work_struct *work) struct hclge_dev *hdev = container_of(work, struct hclge_dev, service_task); - hclge_misc_irq_service_task(hdev); hclge_update_speed_duplex(hdev); hclge_update_link_status(hdev); hclge_update_stats_for_all(hdev); @@ -4661,6 +4742,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) hdev->pdev = pdev; hdev->ae_dev = ae_dev; hdev->reset_type = HNAE3_NONE_RESET; + hdev->reset_request = 0; + hdev->reset_pending = 0; ae_dev->priv = hdev; ret = hclge_pci_init(hdev); @@ -4772,12 +4855,15 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) timer_setup(&hdev->service_timer, hclge_service_timer, 0); INIT_WORK(&hdev->service_task, hclge_service_task); + INIT_WORK(&hdev->rst_service_task, hclge_reset_service_task); /* Enable MISC vector(vector0) */ hclge_enable_vector(&hdev->misc_vector, true); set_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state); set_bit(HCLGE_STATE_DOWN, &hdev->state); + clear_bit(HCLGE_STATE_RST_SERVICE_SCHED, &hdev->state); + clear_bit(HCLGE_STATE_RST_HANDLING, &hdev->state); pr_info("%s driver initialization finished.\n", HCLGE_DRIVER_NAME); return 0; @@ -4889,14 +4975,16 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) del_timer_sync(&hdev->service_timer); if (hdev->service_task.func) cancel_work_sync(&hdev->service_task); + if (hdev->rst_service_task.func) + cancel_work_sync(&hdev->rst_service_task); if (mac->phydev) mdiobus_unregister(mac->mdio_bus); /* Disable MISC vector(vector0) */ hclge_enable_vector(&hdev->misc_vector, false); - hclge_free_vector(hdev, 0); hclge_destroy_cmd_queue(&hdev->hw); + hclge_misc_irq_uninit(hdev); hclge_pci_uninit(hdev); ae_dev->priv = NULL; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 7027814ea5d7..aacec438b933 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -99,12 +99,19 @@ enum HCLGE_DEV_STATE { HCLGE_STATE_REMOVING, HCLGE_STATE_SERVICE_INITED, HCLGE_STATE_SERVICE_SCHED, + HCLGE_STATE_RST_SERVICE_SCHED, + HCLGE_STATE_RST_HANDLING, HCLGE_STATE_MBX_HANDLING, HCLGE_STATE_MBX_IRQ, - HCLGE_STATE_RESET_INT, HCLGE_STATE_MAX }; +enum hclge_evt_cause { + HCLGE_VECTOR0_EVENT_RST, + HCLGE_VECTOR0_EVENT_MBX, + HCLGE_VECTOR0_EVENT_OTHER, +}; + #define HCLGE_MPF_ENBALE 1 struct hclge_caps { u16 num_tqp; @@ -420,6 +427,8 @@ struct hclge_dev { unsigned long state; enum hnae3_reset_type reset_type; + unsigned long reset_request; /* reset has been requested */ + unsigned long reset_pending; /* client rst is pending to be served */ u32 fw_version; u16 num_vmdq_vport; /* Num vmdq vport this PF has set up */ u16 num_tqps; /* Num task queue pairs of this PF */ @@ -469,6 +478,7 @@ struct hclge_dev { unsigned long service_timer_previous; struct timer_list service_timer; struct work_struct service_task; + struct work_struct rst_service_task; bool cur_promisc; int num_alloc_vfs; /* Actual number of VFs allocated */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 62a18914f00f..7737a05c717c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9101,9 +9101,11 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, /* Redirect to a VF or a offloaded macvlan */ if (is_tcf_mirred_egress_redirect(a)) { - int ifindex = tcf_mirred_ifindex(a); + struct net_device *dev = tcf_mirred_dev(a); - err = handle_redirect_action(adapter, ifindex, queue, + if (!dev) + return -EINVAL; + err = handle_redirect_action(adapter, dev->ifindex, queue, action); if (err == 0) return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d2b057a3e512..0f5c012de52e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4308,9 +4308,6 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) { mlx5e_ipsec_cleanup(priv); mlx5e_vxlan_cleanup(priv); - - if (priv->channels.params.xdp_prog) - bpf_prog_put(priv->channels.params.xdp_prog); } static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 55979ec2e88a..3e03d2e8f96a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1982,11 +1982,10 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, } if (is_tcf_mirred_egress_redirect(a)) { - int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; struct mlx5e_priv *out_priv; - out_dev = __dev_get_by_index(dev_net(priv->netdev), ifindex); + out_dev = tcf_mirred_dev(a); if (switchdev_port_same_parent_id(priv->netdev, out_dev)) { @@ -1996,7 +1995,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, rpriv = out_priv->ppriv; attr->out_rep = rpriv->rep; } else if (encap) { - parse_attr->mirred_ifindex = ifindex; + parse_attr->mirred_ifindex = out_dev->ifindex; parse_attr->tun_info = *info; attr->parse_attr = parse_attr; attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2d0897b7d860..3b9c8a0437bf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1571,14 +1571,11 @@ mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port, const struct tc_action *a, bool ingress) { - struct net *net = dev_net(mlxsw_sp_port->dev); enum mlxsw_sp_span_type span_type; struct mlxsw_sp_port *to_port; struct net_device *to_dev; - int ifindex; - ifindex = tcf_mirred_ifindex(a); - to_dev = __dev_get_by_index(net, ifindex); + to_dev = tcf_mirred_dev(a); if (!to_dev) { netdev_err(mlxsw_sp_port->dev, "Could not find requested device\n"); return -EINVAL; @@ -1838,6 +1835,54 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, } } + +static int mlxsw_sp_feature_hw_tc(struct net_device *dev, bool enable) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + + if (!enable && (mlxsw_sp_port->acl_rule_count || + !list_empty(&mlxsw_sp_port->mall_tc_list))) { + netdev_err(dev, "Active offloaded tc filters, can't turn hw_tc_offload off\n"); + return -EINVAL; + } + return 0; +} + +typedef int (*mlxsw_sp_feature_handler)(struct net_device *dev, bool enable); + +static int mlxsw_sp_handle_feature(struct net_device *dev, + netdev_features_t wanted_features, + netdev_features_t feature, + mlxsw_sp_feature_handler feature_handler) +{ + netdev_features_t changes = wanted_features ^ dev->features; + bool enable = !!(wanted_features & feature); + int err; + + if (!(changes & feature)) + return 0; + + err = feature_handler(dev, enable); + if (err) { + netdev_err(dev, "%s feature %pNF failed, err %d\n", + enable ? "Enable" : "Disable", &feature, err); + return err; + } + + if (enable) + dev->features |= feature; + else + dev->features &= ~feature; + + return 0; +} +static int mlxsw_sp_set_features(struct net_device *dev, + netdev_features_t features) +{ + return mlxsw_sp_handle_feature(dev, features, NETIF_F_HW_TC, + mlxsw_sp_feature_hw_tc); +} + static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_open = mlxsw_sp_port_open, .ndo_stop = mlxsw_sp_port_stop, @@ -1852,6 +1897,7 @@ static const struct net_device_ops mlxsw_sp_port_netdev_ops = { .ndo_vlan_rx_add_vid = mlxsw_sp_port_add_vid, .ndo_vlan_rx_kill_vid = mlxsw_sp_port_kill_vid, .ndo_get_phys_port_name = mlxsw_sp_port_get_phys_port_name, + .ndo_set_features = mlxsw_sp_set_features, }; static void mlxsw_sp_port_get_drvinfo(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 432ab9b12b7f..a0adcd886589 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -270,6 +270,7 @@ struct mlxsw_sp_port { struct mlxsw_sp_port_sample *sample; struct list_head vlans_list; struct mlxsw_sp_qdisc root_qdisc; + unsigned acl_rule_count; }; static inline bool diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 2f0e57857ea4..42e8a36b9b95 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -92,7 +92,6 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, if (err) return err; } else if (is_tcf_mirred_egress_redirect(a)) { - int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; struct mlxsw_sp_fid *fid; u16 fid_index; @@ -104,7 +103,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, if (err) return err; - out_dev = __dev_get_by_index(dev_net(dev), ifindex); + out_dev = tcf_mirred_dev(a); if (out_dev == dev) out_dev = NULL; @@ -424,6 +423,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, goto err_rule_add; mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); + mlxsw_sp_port->acl_rule_count++; return 0; err_rule_add: @@ -455,6 +455,7 @@ void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, } mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); + mlxsw_sp_port->acl_rule_count--; } int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 24c4408b5734..6e5ef984398b 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -22,6 +22,7 @@ nfp-objs := \ nfp_hwmon.o \ nfp_main.o \ nfp_net_common.o \ + nfp_net_debugdump.o \ nfp_net_ethtool.o \ nfp_net_main.o \ nfp_net_repr.o \ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 995e95410b11..3419ad495962 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -66,12 +66,6 @@ next2 = nfp_meta_next(next)) static bool -nfp_meta_has_next(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) -{ - return meta->l.next != &nfp_prog->insns; -} - -static bool nfp_meta_has_prev(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { return meta->l.prev != &nfp_prog->insns; @@ -102,7 +96,7 @@ nfp_prog_offset_to_index(struct nfp_prog *nfp_prog, unsigned int offset) /* --- Emitters --- */ static void __emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, - u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, bool sync) + u8 mode, u8 xfer, u8 areg, u8 breg, u8 size, bool sync, bool indir) { enum cmd_ctx_swap ctx; u64 insn; @@ -120,14 +114,15 @@ __emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, FIELD_PREP(OP_CMD_CNT, size) | FIELD_PREP(OP_CMD_SIG, sync) | FIELD_PREP(OP_CMD_TGT_CMD, cmd_tgt_act[op].tgt_cmd) | + FIELD_PREP(OP_CMD_INDIR, indir) | FIELD_PREP(OP_CMD_MODE, mode); nfp_prog_push(nfp_prog, insn); } static void -emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, - u8 mode, u8 xfer, swreg lreg, swreg rreg, u8 size, bool sync) +emit_cmd_any(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, + swreg lreg, swreg rreg, u8 size, bool sync, bool indir) { struct nfp_insn_re_regs reg; int err; @@ -148,7 +143,22 @@ emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, return; } - __emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, sync); + __emit_cmd(nfp_prog, op, mode, xfer, reg.areg, reg.breg, size, sync, + indir); +} + +static void +emit_cmd(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, + swreg lreg, swreg rreg, u8 size, bool sync) +{ + emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, false); +} + +static void +emit_cmd_indir(struct nfp_prog *nfp_prog, enum cmd_tgt_map op, u8 mode, u8 xfer, + swreg lreg, swreg rreg, u8 size, bool sync) +{ + emit_cmd_any(nfp_prog, op, mode, xfer, lreg, rreg, size, sync, true); } static void @@ -230,9 +240,11 @@ emit_immed(struct nfp_prog *nfp_prog, swreg dst, u16 imm, return; } - __emit_immed(nfp_prog, reg.areg, reg.breg, imm >> 8, width, - invert, shift, reg.wr_both, - reg.dst_lmextn, reg.src_lmextn); + /* Use reg.dst when destination is No-Dest. */ + __emit_immed(nfp_prog, + swreg_type(dst) == NN_REG_NONE ? reg.dst : reg.areg, + reg.breg, imm >> 8, width, invert, shift, + reg.wr_both, reg.dst_lmextn, reg.src_lmextn); } static void @@ -510,6 +522,147 @@ static void wrp_reg_mov(struct nfp_prog *nfp_prog, u16 dst, u16 src) wrp_mov(nfp_prog, reg_both(dst), reg_b(src)); } +/* wrp_reg_subpart() - load @field_len bytes from @offset of @src, write the + * result to @dst from low end. + */ +static void +wrp_reg_subpart(struct nfp_prog *nfp_prog, swreg dst, swreg src, u8 field_len, + u8 offset) +{ + enum shf_sc sc = offset ? SHF_SC_R_SHF : SHF_SC_NONE; + u8 mask = (1 << field_len) - 1; + + emit_ld_field_any(nfp_prog, dst, mask, src, sc, offset * 8, true); +} + +/* NFP has Command Push Pull bus which supports bluk memory operations. */ +static int nfp_cpp_memcpy(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + bool descending_seq = meta->ldst_gather_len < 0; + s16 len = abs(meta->ldst_gather_len); + swreg src_base, off; + unsigned int i; + u8 xfer_num; + + off = re_load_imm_any(nfp_prog, meta->insn.off, imm_b(nfp_prog)); + src_base = reg_a(meta->insn.src_reg * 2); + xfer_num = round_up(len, 4) / 4; + + /* Setup PREV_ALU fields to override memory read length. */ + if (len > 32) + wrp_immed(nfp_prog, reg_none(), + CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); + + /* Memory read from source addr into transfer-in registers. */ + emit_cmd_any(nfp_prog, CMD_TGT_READ32_SWAP, CMD_MODE_32b, 0, src_base, + off, xfer_num - 1, true, len > 32); + + /* Move from transfer-in to transfer-out. */ + for (i = 0; i < xfer_num; i++) + wrp_mov(nfp_prog, reg_xfer(i), reg_xfer(i)); + + off = re_load_imm_any(nfp_prog, meta->paired_st->off, imm_b(nfp_prog)); + + if (len <= 8) { + /* Use single direct_ref write8. */ + emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, len - 1, + true); + } else if (len <= 32 && IS_ALIGNED(len, 4)) { + /* Use single direct_ref write32. */ + emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, xfer_num - 1, + true); + } else if (len <= 32) { + /* Use single indirect_ref write8. */ + wrp_immed(nfp_prog, reg_none(), + CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, len - 1)); + emit_cmd_indir(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, + len - 1, true); + } else if (IS_ALIGNED(len, 4)) { + /* Use single indirect_ref write32. */ + wrp_immed(nfp_prog, reg_none(), + CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 1)); + emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, + xfer_num - 1, true); + } else if (len <= 40) { + /* Use one direct_ref write32 to write the first 32-bytes, then + * another direct_ref write8 to write the remaining bytes. + */ + emit_cmd(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, 7, + true); + + off = re_load_imm_any(nfp_prog, meta->paired_st->off + 32, + imm_b(nfp_prog)); + emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, 8, + reg_a(meta->paired_st->dst_reg * 2), off, len - 33, + true); + } else { + /* Use one indirect_ref write32 to write 4-bytes aligned length, + * then another direct_ref write8 to write the remaining bytes. + */ + u8 new_off; + + wrp_immed(nfp_prog, reg_none(), + CMD_OVE_LEN | FIELD_PREP(CMD_OV_LEN, xfer_num - 2)); + emit_cmd_indir(nfp_prog, CMD_TGT_WRITE32_SWAP, CMD_MODE_32b, 0, + reg_a(meta->paired_st->dst_reg * 2), off, + xfer_num - 2, true); + new_off = meta->paired_st->off + (xfer_num - 1) * 4; + off = re_load_imm_any(nfp_prog, new_off, imm_b(nfp_prog)); + emit_cmd(nfp_prog, CMD_TGT_WRITE8_SWAP, CMD_MODE_32b, + xfer_num - 1, reg_a(meta->paired_st->dst_reg * 2), off, + (len & 0x3) - 1, true); + } + + /* TODO: The following extra load is to make sure data flow be identical + * before and after we do memory copy optimization. + * + * The load destination register is not guaranteed to be dead, so we + * need to make sure it is loaded with the value the same as before + * this transformation. + * + * These extra loads could be removed once we have accurate register + * usage information. + */ + if (descending_seq) + xfer_num = 0; + else if (BPF_SIZE(meta->insn.code) != BPF_DW) + xfer_num = xfer_num - 1; + else + xfer_num = xfer_num - 2; + + switch (BPF_SIZE(meta->insn.code)) { + case BPF_B: + wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), + reg_xfer(xfer_num), 1, + IS_ALIGNED(len, 4) ? 3 : (len & 3) - 1); + break; + case BPF_H: + wrp_reg_subpart(nfp_prog, reg_both(meta->insn.dst_reg * 2), + reg_xfer(xfer_num), 2, (len & 3) ^ 2); + break; + case BPF_W: + wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), + reg_xfer(0)); + break; + case BPF_DW: + wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2), + reg_xfer(xfer_num)); + wrp_mov(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), + reg_xfer(xfer_num + 1)); + break; + } + + if (BPF_SIZE(meta->insn.code) != BPF_DW) + wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0); + + return 0; +} + static int data_ld(struct nfp_prog *nfp_prog, swreg offset, u8 dst_gpr, int size) { @@ -975,9 +1128,6 @@ wrp_test_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, { const struct bpf_insn *insn = &meta->insn; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - wrp_test_reg_one(nfp_prog, insn->dst_reg * 2, alu_op, insn->src_reg * 2, br_mask, insn->off); wrp_test_reg_one(nfp_prog, insn->dst_reg * 2 + 1, alu_op, @@ -995,9 +1145,6 @@ wrp_cmp_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 reg = insn->dst_reg * 2; swreg tmp_reg; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); if (!swap) emit_alu(nfp_prog, reg_none(), reg_a(reg), ALU_OP_SUB, tmp_reg); @@ -1027,9 +1174,6 @@ wrp_cmp_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, areg = insn->dst_reg * 2; breg = insn->src_reg * 2; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - if (swap) { areg ^= breg; breg ^= areg; @@ -1494,6 +1638,9 @@ static int mem_ldx(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, unsigned int size) { + if (meta->ldst_gather_len) + return nfp_cpp_memcpy(nfp_prog, meta); + if (meta->ptr.type == PTR_TO_CTX) { if (nfp_prog->type == BPF_PROG_TYPE_XDP) return mem_ldx_xdp(nfp_prog, meta, size); @@ -1630,8 +1777,6 @@ static int mem_stx8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { - if (meta->insn.off < 0) /* TODO */ - return -EOPNOTSUPP; emit_br(nfp_prog, BR_UNC, meta->insn.off, 0); return 0; @@ -1646,9 +1791,6 @@ static int jeq_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) or1 = reg_a(insn->dst_reg * 2); or2 = reg_b(insn->dst_reg * 2 + 1); - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - if (imm & ~0U) { tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); emit_alu(nfp_prog, imm_a(nfp_prog), @@ -1695,9 +1837,6 @@ static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) u64 imm = insn->imm; /* sign extend */ swreg tmp_reg; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - if (!imm) { meta->skip = true; return 0; @@ -1726,9 +1865,6 @@ static int jne_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) u64 imm = insn->imm; /* sign extend */ swreg tmp_reg; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - if (!imm) { emit_alu(nfp_prog, reg_none(), reg_a(insn->dst_reg * 2), ALU_OP_OR, reg_b(insn->dst_reg * 2 + 1)); @@ -1753,9 +1889,6 @@ static int jeq_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { const struct bpf_insn *insn = &meta->insn; - if (insn->off < 0) /* TODO */ - return -EOPNOTSUPP; - emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(insn->dst_reg * 2), ALU_OP_XOR, reg_b(insn->src_reg * 2)); emit_alu(nfp_prog, imm_b(nfp_prog), reg_a(insn->dst_reg * 2 + 1), @@ -1887,17 +2020,22 @@ static void br_set_offset(u64 *instr, u16 offset) /* --- Assembler logic --- */ static int nfp_fixup_branches(struct nfp_prog *nfp_prog) { - struct nfp_insn_meta *meta, *next; - u32 off, br_idx; - u32 idx; + struct nfp_insn_meta *meta, *jmp_dst; + u32 idx, br_idx; - nfp_for_each_insn_walk2(nfp_prog, meta, next) { + list_for_each_entry(meta, &nfp_prog->insns, l) { if (meta->skip) continue; if (BPF_CLASS(meta->insn.code) != BPF_JMP) continue; - br_idx = nfp_prog_offset_to_index(nfp_prog, next->off) - 1; + if (list_is_last(&meta->l, &nfp_prog->insns)) + idx = nfp_prog->last_bpf_off; + else + idx = list_next_entry(meta, l)->off - 1; + + br_idx = nfp_prog_offset_to_index(nfp_prog, idx); + if (!nfp_is_br(nfp_prog->prog[br_idx])) { pr_err("Fixup found block not ending in branch %d %02x %016llx!!\n", br_idx, meta->insn.code, nfp_prog->prog[br_idx]); @@ -1907,23 +2045,14 @@ static int nfp_fixup_branches(struct nfp_prog *nfp_prog) if (FIELD_GET(OP_BR_SPECIAL, nfp_prog->prog[br_idx])) continue; - /* Find the target offset in assembler realm */ - off = meta->insn.off; - if (!off) { - pr_err("Fixup found zero offset!!\n"); + if (!meta->jmp_dst) { + pr_err("Non-exit jump doesn't have destination info recorded!!\n"); return -ELOOP; } - while (off && nfp_meta_has_next(nfp_prog, next)) { - next = nfp_meta_next(next); - off--; - } - if (off) { - pr_err("Fixup found too large jump!! %d\n", off); - return -ELOOP; - } + jmp_dst = meta->jmp_dst; - if (next->skip) { + if (jmp_dst->skip) { pr_err("Branch landing on removed instruction!!\n"); return -ELOOP; } @@ -1932,7 +2061,7 @@ static int nfp_fixup_branches(struct nfp_prog *nfp_prog) idx <= br_idx; idx++) { if (!nfp_is_br(nfp_prog->prog[idx])) continue; - br_set_offset(&nfp_prog->prog[idx], next->off); + br_set_offset(&nfp_prog->prog[idx], jmp_dst->off); } } @@ -2105,6 +2234,8 @@ static int nfp_translate(struct nfp_prog *nfp_prog) nfp_prog->n_translated++; } + nfp_prog->last_bpf_off = nfp_prog_current_offset(nfp_prog) - 1; + nfp_outro(nfp_prog); if (nfp_prog->error) return nfp_prog->error; @@ -2173,6 +2304,9 @@ static void nfp_bpf_opt_ld_mask(struct nfp_prog *nfp_prog) if (next.src_reg || next.dst_reg) continue; + if (meta2->flags & FLAG_INSN_IS_JUMP_DST) + continue; + meta2->skip = true; } } @@ -2209,17 +2343,258 @@ static void nfp_bpf_opt_ld_shift(struct nfp_prog *nfp_prog) if (next1.imm != 0x20 || next2.imm != 0x20) continue; + if (meta2->flags & FLAG_INSN_IS_JUMP_DST || + meta3->flags & FLAG_INSN_IS_JUMP_DST) + continue; + meta2->skip = true; meta3->skip = true; } } +/* load/store pair that forms memory copy sould look like the following: + * + * ld_width R, [addr_src + offset_src] + * st_width [addr_dest + offset_dest], R + * + * The destination register of load and source register of store should + * be the same, load and store should also perform at the same width. + * If either of addr_src or addr_dest is stack pointer, we don't do the + * CPP optimization as stack is modelled by registers on NFP. + */ +static bool +curr_pair_is_memcpy(struct nfp_insn_meta *ld_meta, + struct nfp_insn_meta *st_meta) +{ + struct bpf_insn *ld = &ld_meta->insn; + struct bpf_insn *st = &st_meta->insn; + + if (!is_mbpf_load(ld_meta) || !is_mbpf_store(st_meta)) + return false; + + if (ld_meta->ptr.type != PTR_TO_PACKET) + return false; + + if (st_meta->ptr.type != PTR_TO_PACKET) + return false; + + if (BPF_SIZE(ld->code) != BPF_SIZE(st->code)) + return false; + + if (ld->dst_reg != st->src_reg) + return false; + + /* There is jump to the store insn in this pair. */ + if (st_meta->flags & FLAG_INSN_IS_JUMP_DST) + return false; + + return true; +} + +/* Currently, we only support chaining load/store pairs if: + * + * - Their address base registers are the same. + * - Their address offsets are in the same order. + * - They operate at the same memory width. + * - There is no jump into the middle of them. + */ +static bool +curr_pair_chain_with_previous(struct nfp_insn_meta *ld_meta, + struct nfp_insn_meta *st_meta, + struct bpf_insn *prev_ld, + struct bpf_insn *prev_st) +{ + u8 prev_size, curr_size, prev_ld_base, prev_st_base, prev_ld_dst; + struct bpf_insn *ld = &ld_meta->insn; + struct bpf_insn *st = &st_meta->insn; + s16 prev_ld_off, prev_st_off; + + /* This pair is the start pair. */ + if (!prev_ld) + return true; + + prev_size = BPF_LDST_BYTES(prev_ld); + curr_size = BPF_LDST_BYTES(ld); + prev_ld_base = prev_ld->src_reg; + prev_st_base = prev_st->dst_reg; + prev_ld_dst = prev_ld->dst_reg; + prev_ld_off = prev_ld->off; + prev_st_off = prev_st->off; + + if (ld->dst_reg != prev_ld_dst) + return false; + + if (ld->src_reg != prev_ld_base || st->dst_reg != prev_st_base) + return false; + + if (curr_size != prev_size) + return false; + + /* There is jump to the head of this pair. */ + if (ld_meta->flags & FLAG_INSN_IS_JUMP_DST) + return false; + + /* Both in ascending order. */ + if (prev_ld_off + prev_size == ld->off && + prev_st_off + prev_size == st->off) + return true; + + /* Both in descending order. */ + if (ld->off + curr_size == prev_ld_off && + st->off + curr_size == prev_st_off) + return true; + + return false; +} + +/* Return TRUE if cross memory access happens. Cross memory access means + * store area is overlapping with load area that a later load might load + * the value from previous store, for this case we can't treat the sequence + * as an memory copy. + */ +static bool +cross_mem_access(struct bpf_insn *ld, struct nfp_insn_meta *head_ld_meta, + struct nfp_insn_meta *head_st_meta) +{ + s16 head_ld_off, head_st_off, ld_off; + + /* Different pointer types does not overlap. */ + if (head_ld_meta->ptr.type != head_st_meta->ptr.type) + return false; + + /* load and store are both PTR_TO_PACKET, check ID info. */ + if (head_ld_meta->ptr.id != head_st_meta->ptr.id) + return true; + + /* Canonicalize the offsets. Turn all of them against the original + * base register. + */ + head_ld_off = head_ld_meta->insn.off + head_ld_meta->ptr.off; + head_st_off = head_st_meta->insn.off + head_st_meta->ptr.off; + ld_off = ld->off + head_ld_meta->ptr.off; + + /* Ascending order cross. */ + if (ld_off > head_ld_off && + head_ld_off < head_st_off && ld_off >= head_st_off) + return true; + + /* Descending order cross. */ + if (ld_off < head_ld_off && + head_ld_off > head_st_off && ld_off <= head_st_off) + return true; + + return false; +} + +/* This pass try to identify the following instructoin sequences. + * + * load R, [regA + offA] + * store [regB + offB], R + * load R, [regA + offA + const_imm_A] + * store [regB + offB + const_imm_A], R + * load R, [regA + offA + 2 * const_imm_A] + * store [regB + offB + 2 * const_imm_A], R + * ... + * + * Above sequence is typically generated by compiler when lowering + * memcpy. NFP prefer using CPP instructions to accelerate it. + */ +static void nfp_bpf_opt_ldst_gather(struct nfp_prog *nfp_prog) +{ + struct nfp_insn_meta *head_ld_meta = NULL; + struct nfp_insn_meta *head_st_meta = NULL; + struct nfp_insn_meta *meta1, *meta2; + struct bpf_insn *prev_ld = NULL; + struct bpf_insn *prev_st = NULL; + u8 count = 0; + + nfp_for_each_insn_walk2(nfp_prog, meta1, meta2) { + struct bpf_insn *ld = &meta1->insn; + struct bpf_insn *st = &meta2->insn; + + /* Reset record status if any of the following if true: + * - The current insn pair is not load/store. + * - The load/store pair doesn't chain with previous one. + * - The chained load/store pair crossed with previous pair. + * - The chained load/store pair has a total size of memory + * copy beyond 128 bytes which is the maximum length a + * single NFP CPP command can transfer. + */ + if (!curr_pair_is_memcpy(meta1, meta2) || + !curr_pair_chain_with_previous(meta1, meta2, prev_ld, + prev_st) || + (head_ld_meta && (cross_mem_access(ld, head_ld_meta, + head_st_meta) || + head_ld_meta->ldst_gather_len >= 128))) { + if (!count) + continue; + + if (count > 1) { + s16 prev_ld_off = prev_ld->off; + s16 prev_st_off = prev_st->off; + s16 head_ld_off = head_ld_meta->insn.off; + + if (prev_ld_off < head_ld_off) { + head_ld_meta->insn.off = prev_ld_off; + head_st_meta->insn.off = prev_st_off; + head_ld_meta->ldst_gather_len = + -head_ld_meta->ldst_gather_len; + } + + head_ld_meta->paired_st = &head_st_meta->insn; + head_st_meta->skip = true; + } else { + head_ld_meta->ldst_gather_len = 0; + } + + /* If the chain is ended by an load/store pair then this + * could serve as the new head of the the next chain. + */ + if (curr_pair_is_memcpy(meta1, meta2)) { + head_ld_meta = meta1; + head_st_meta = meta2; + head_ld_meta->ldst_gather_len = + BPF_LDST_BYTES(ld); + meta1 = nfp_meta_next(meta1); + meta2 = nfp_meta_next(meta2); + prev_ld = ld; + prev_st = st; + count = 1; + } else { + head_ld_meta = NULL; + head_st_meta = NULL; + prev_ld = NULL; + prev_st = NULL; + count = 0; + } + + continue; + } + + if (!head_ld_meta) { + head_ld_meta = meta1; + head_st_meta = meta2; + } else { + meta1->skip = true; + meta2->skip = true; + } + + head_ld_meta->ldst_gather_len += BPF_LDST_BYTES(ld); + meta1 = nfp_meta_next(meta1); + meta2 = nfp_meta_next(meta2); + prev_ld = ld; + prev_st = st; + count++; + } +} + static int nfp_bpf_optimize(struct nfp_prog *nfp_prog) { nfp_bpf_opt_reg_init(nfp_prog); nfp_bpf_opt_ld_mask(nfp_prog); nfp_bpf_opt_ld_shift(nfp_prog); + nfp_bpf_opt_ldst_gather(nfp_prog); return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index e379b78e86ef..54bfd7846f6d 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -82,12 +82,6 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn) return nfp_net_ebpf_capable(nn) ? "BPF" : ""; } -static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn) -{ - if (nn->dp.bpf_offload_xdp) - nfp_bpf_xdp_offload(app, nn, NULL); -} - static int nfp_bpf_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv) { @@ -168,7 +162,6 @@ const struct nfp_app_type app_bpf = { .extra_cap = nfp_bpf_extra_cap, .vnic_alloc = nfp_app_nic_vnic_alloc, - .vnic_free = nfp_bpf_vnic_free, .setup_tc = nfp_bpf_setup_tc, .tc_busy = nfp_bpf_tc_busy, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.h b/drivers/net/ethernet/netronome/nfp/bpf/main.h index 082a15f6dfb5..5884291ddba5 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.h +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -89,23 +89,37 @@ typedef int (*instr_cb_t)(struct nfp_prog *, struct nfp_insn_meta *); #define nfp_meta_next(meta) list_next_entry(meta, l) #define nfp_meta_prev(meta) list_prev_entry(meta, l) +#define FLAG_INSN_IS_JUMP_DST BIT(0) + /** * struct nfp_insn_meta - BPF instruction wrapper * @insn: BPF instruction * @ptr: pointer type for memory operations + * @ldst_gather_len: memcpy length gathered from load/store sequence + * @paired_st: the paired store insn at the head of the sequence * @ptr_not_const: pointer is not always constant + * @jmp_dst: destination info for jump instructions * @off: index of first generated machine instruction (in nfp_prog.prog) * @n: eBPF instruction number + * @flags: eBPF instruction extra optimization flags * @skip: skip this instruction (optimized out) * @double_cb: callback for second part of the instruction * @l: link on nfp_prog->insns list */ struct nfp_insn_meta { struct bpf_insn insn; - struct bpf_reg_state ptr; - bool ptr_not_const; + union { + struct { + struct bpf_reg_state ptr; + struct bpf_insn *paired_st; + s16 ldst_gather_len; + bool ptr_not_const; + }; + struct nfp_insn_meta *jmp_dst; + }; unsigned int off; unsigned short n; + unsigned short flags; bool skip; instr_cb_t double_cb; @@ -134,6 +148,16 @@ static inline u8 mbpf_mode(const struct nfp_insn_meta *meta) return BPF_MODE(meta->insn.code); } +static inline bool is_mbpf_load(const struct nfp_insn_meta *meta) +{ + return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM); +} + +static inline bool is_mbpf_store(const struct nfp_insn_meta *meta) +{ + return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM); +} + /** * struct nfp_prog - nfp BPF program * @prog: machine code @@ -142,6 +166,7 @@ static inline u8 mbpf_mode(const struct nfp_insn_meta *meta) * @verifier_meta: temporary storage for verifier's insn meta * @type: BPF program type * @start_off: address of the first instruction in the memory + * @last_bpf_off: address of the last instruction translated from BPF * @tgt_out: jump target for normal exit * @tgt_abort: jump target for abort (e.g. access outside of packet buffer) * @tgt_done: jump target to get the next packet @@ -160,6 +185,7 @@ struct nfp_prog { enum bpf_prog_type type; unsigned int start_off; + unsigned int last_bpf_off; unsigned int tgt_out; unsigned int tgt_abort; unsigned int tgt_done; @@ -189,4 +215,7 @@ int nfp_bpf_translate(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); int nfp_bpf_destroy(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); +struct nfp_insn_meta * +nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, + unsigned int insn_idx, unsigned int n_insns); #endif diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index bc879aeb62d4..377976ce92dd 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -55,11 +55,10 @@ static int nfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog, unsigned int cnt) { + struct nfp_insn_meta *meta; unsigned int i; for (i = 0; i < cnt; i++) { - struct nfp_insn_meta *meta; - meta = kzalloc(sizeof(*meta), GFP_KERNEL); if (!meta) return -ENOMEM; @@ -70,6 +69,24 @@ nfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog, list_add_tail(&meta->l, &nfp_prog->insns); } + /* Another pass to record jump information. */ + list_for_each_entry(meta, &nfp_prog->insns, l) { + u64 code = meta->insn.code; + + if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_EXIT && + BPF_OP(code) != BPF_CALL) { + struct nfp_insn_meta *dst_meta; + unsigned short dst_indx; + + dst_indx = meta->n + 1 + meta->insn.off; + dst_meta = nfp_bpf_goto_meta(nfp_prog, meta, dst_indx, + cnt); + + meta->jmp_dst = dst_meta; + dst_meta->flags |= FLAG_INSN_IS_JUMP_DST; + } + } + return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index 8d43491ddd6b..d2bf29c90226 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -40,7 +40,7 @@ #include "main.h" -static struct nfp_insn_meta * +struct nfp_insn_meta * nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, unsigned int insn_idx, unsigned int n_insns) { @@ -180,10 +180,10 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) if (meta->insn.code == (BPF_JMP | BPF_EXIT)) return nfp_bpf_check_exit(nfp_prog, env); - if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM)) + if (is_mbpf_load(meta)) return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.src_reg); - if ((meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_MEM)) + if (is_mbpf_store(meta)) return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.dst_reg); diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index c1c595f8bb87..ca74c517f626 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -93,13 +93,11 @@ nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action, size_t act_size = sizeof(struct nfp_fl_output); struct net_device *out_dev; u16 tmp_flags; - int ifindex; output->head.jump_id = NFP_FL_ACTION_OPCODE_OUTPUT; output->head.len_lw = act_size >> NFP_FL_LW_SIZ; - ifindex = tcf_mirred_ifindex(action); - out_dev = __dev_get_by_index(dev_net(in_dev), ifindex); + out_dev = tcf_mirred_dev(action); if (!out_dev) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.c b/drivers/net/ethernet/netronome/nfp/nfp_asm.c index 830f6de25f47..d3610987fb07 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_asm.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.c @@ -41,6 +41,7 @@ const struct cmd_tgt_act cmd_tgt_act[__CMD_TGT_MAP_SIZE] = { [CMD_TGT_WRITE8_SWAP] = { 0x02, 0x42 }, + [CMD_TGT_WRITE32_SWAP] = { 0x02, 0x5f }, [CMD_TGT_READ8] = { 0x01, 0x43 }, [CMD_TGT_READ32] = { 0x00, 0x5c }, [CMD_TGT_READ32_LE] = { 0x01, 0x5c }, @@ -120,7 +121,8 @@ int swreg_to_unrestricted(swreg dst, swreg lreg, swreg rreg, reg->dst = nfp_swreg_to_unreg(dst, true); /* Decode source operands */ - if (swreg_type(lreg) == swreg_type(rreg)) + if (swreg_type(lreg) == swreg_type(rreg) && + swreg_type(lreg) != NN_REG_NONE) return -EFAULT; if (swreg_type(lreg) == NN_REG_GPR_B || @@ -200,7 +202,8 @@ int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg, reg->dst = nfp_swreg_to_rereg(dst, true, false, NULL); /* Decode source operands */ - if (swreg_type(lreg) == swreg_type(rreg)) + if (swreg_type(lreg) == swreg_type(rreg) && + swreg_type(lreg) != NN_REG_NONE) return -EFAULT; if (swreg_type(lreg) == NN_REG_GPR_B || diff --git a/drivers/net/ethernet/netronome/nfp/nfp_asm.h b/drivers/net/ethernet/netronome/nfp/nfp_asm.h index 74d0c11ab2f9..3387e6926eb0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_asm.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_asm.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 Netronome Systems, Inc. + * Copyright (C) 2016-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -209,6 +209,7 @@ enum alu_dst_ab { #define OP_CMD_CNT 0x0000e000000ULL #define OP_CMD_SIG 0x000f0000000ULL #define OP_CMD_TGT_CMD 0x07f00000000ULL +#define OP_CMD_INDIR 0x20000000000ULL #define OP_CMD_MODE 0x1c0000000000ULL struct cmd_tgt_act { @@ -219,6 +220,7 @@ struct cmd_tgt_act { enum cmd_tgt_map { CMD_TGT_READ8, CMD_TGT_WRITE8_SWAP, + CMD_TGT_WRITE32_SWAP, CMD_TGT_READ32, CMD_TGT_READ32_LE, CMD_TGT_READ32_SWAP, @@ -240,6 +242,9 @@ enum cmd_ctx_swap { CMD_CTX_NO_SWAP = 3, }; +#define CMD_OVE_LEN BIT(7) +#define CMD_OV_LEN GENMASK(12, 8) + #define OP_LCSR_BASE 0x0fc00000000ULL #define OP_LCSR_A_SRC 0x000000003ffULL #define OP_LCSR_B_SRC 0x000000ffc00ULL @@ -257,6 +262,7 @@ enum lcsr_wr_src { #define OP_CARB_BASE 0x0e000000000ULL #define OP_CARB_OR 0x00000010000ULL +#define NFP_CSR_CTX_PTR 0x20 #define NFP_CSR_ACT_LM_ADDR0 0x64 #define NFP_CSR_ACT_LM_ADDR1 0x6c #define NFP_CSR_ACT_LM_ADDR2 0x94 @@ -377,4 +383,13 @@ int swreg_to_restricted(swreg dst, swreg lreg, swreg rreg, int nfp_ustore_check_valid_no_ecc(u64 insn); u64 nfp_ustore_calc_ecc_insn(u64 insn); +#define NFP_IND_ME_REFL_WR_SIG_INIT 3 +#define NFP_IND_ME_CTX_PTR_BASE_MASK GENMASK(9, 0) +#define NFP_IND_NUM_CONTEXTS 8 + +static inline u32 nfp_get_ind_csr_ctx_ptr_offs(u32 read_offset) +{ + return (read_offset & ~NFP_IND_ME_CTX_PTR_BASE_MASK) | NFP_CSR_CTX_PTR; +} + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 35eaccbece36..0953fa8f3109 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -45,6 +45,7 @@ #include <linux/pci.h> #include <linux/firmware.h> #include <linux/vermagic.h> +#include <linux/vmalloc.h> #include <net/devlink.h> #include "nfpcore/nfp.h" @@ -509,6 +510,9 @@ static int nfp_pci_probe(struct pci_dev *pdev, pf->mip = nfp_mip_open(pf->cpp); pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip); + pf->dump_flag = NFP_DUMP_NSP_DIAG; + pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl); + err = nfp_pcie_sriov_read_nfd_limit(pf); if (err) goto err_fw_unload; @@ -544,6 +548,7 @@ err_fw_unload: nfp_fw_unload(pf); kfree(pf->eth_tbl); kfree(pf->nspi); + vfree(pf->dumpspec); err_devlink_unreg: devlink_unregister(devlink); err_hwinfo_free: @@ -579,6 +584,7 @@ static void nfp_pci_remove(struct pci_dev *pdev) devlink_unregister(devlink); + vfree(pf->dumpspec); kfree(pf->rtbl); nfp_mip_close(pf->mip); if (pf->fw_loaded) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index be0ee59f2eb9..add46e28212b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -39,6 +39,7 @@ #ifndef NFP_MAIN_H #define NFP_MAIN_H +#include <linux/ethtool.h> #include <linux/list.h> #include <linux/types.h> #include <linux/msi.h> @@ -62,6 +63,17 @@ struct nfp_port; struct nfp_rtsym_table; /** + * struct nfp_dumpspec - NFP FW dump specification structure + * @size: Size of the data + * @data: Sequence of TLVs, each being an instruction to dump some data + * from FW + */ +struct nfp_dumpspec { + u32 size; + u8 data[0]; +}; + +/** * struct nfp_pf - NFP PF-specific device structure * @pdev: Backpointer to PCI device * @cpp: Pointer to the CPP handle @@ -83,6 +95,9 @@ struct nfp_rtsym_table; * @mip: MIP handle * @rtbl: RTsym table * @hwinfo: HWInfo table + * @dumpspec: Debug dump specification + * @dump_flag: Store dump flag between set_dump and get_dump_flag + * @dump_len: Store dump length between set_dump and get_dump_flag * @eth_tbl: NSP ETH table * @nspi: NSP identification info * @hwmon_dev: pointer to hwmon device @@ -124,6 +139,9 @@ struct nfp_pf { const struct nfp_mip *mip; struct nfp_rtsym_table *rtbl; struct nfp_hwinfo *hwinfo; + struct nfp_dumpspec *dumpspec; + u32 dump_flag; + u32 dump_len; struct nfp_eth_table *eth_tbl; struct nfp_nsp_identify *nspi; @@ -157,4 +175,15 @@ void nfp_net_get_mac_addr(struct nfp_pf *pf, struct nfp_port *port); bool nfp_ctrl_tx(struct nfp_net *nn, struct sk_buff *skb); +enum nfp_dump_diag { + NFP_DUMP_NSP_DIAG = 0, +}; + +struct nfp_dumpspec * +nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl); +s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec, + u32 flag); +int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec, + struct ethtool_dump *dump_param, void *dest); + #endif /* NFP_MAIN_H */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 7f9857c276b1..3801c52098d5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -548,6 +548,8 @@ struct nfp_net_dp { * @max_r_vecs: Number of allocated interrupt vectors for RX/TX * @max_tx_rings: Maximum number of TX rings supported by the Firmware * @max_rx_rings: Maximum number of RX rings supported by the Firmware + * @stride_rx: Queue controller RX queue spacing + * @stride_tx: Queue controller TX queue spacing * @r_vecs: Pre-allocated array of ring vectors * @irq_entries: Pre-allocated array of MSI-X entries * @lsc_handler: Handler for Link State Change interrupt diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 1a603fdd9e80..ad3e9f6a61e5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -3392,6 +3392,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp) if (nn->dp.bpf_offload_xdp) xdp->prog_attached = XDP_ATTACHED_HW; xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0; + xdp->flags = nn->xdp_prog ? nn->xdp_flags : 0; return 0; case BPF_OFFLOAD_VERIFIER_PREP: return nfp_app_bpf_verifier_prep(nn->app, nn, xdp); @@ -3561,9 +3562,6 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev, */ void nfp_net_free(struct nfp_net *nn) { - if (nn->xdp_prog) - bpf_prog_put(nn->xdp_prog); - if (nn->dp.netdev) free_netdev(nn->dp.netdev); else diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c new file mode 100644 index 000000000000..cb74602f0907 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 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/ethtool.h> +#include <linux/vmalloc.h> + +#include "nfp_asm.h" +#include "nfp_main.h" +#include "nfpcore/nfp.h" +#include "nfpcore/nfp_nffw.h" + +#define NFP_DUMP_SPEC_RTSYM "_abi_dump_spec" + +#define ALIGN8(x) ALIGN(x, 8) + +enum nfp_dumpspec_type { + NFP_DUMPSPEC_TYPE_CPP_CSR = 0, + NFP_DUMPSPEC_TYPE_XPB_CSR = 1, + NFP_DUMPSPEC_TYPE_ME_CSR = 2, + NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3, + NFP_DUMPSPEC_TYPE_RTSYM = 4, + NFP_DUMPSPEC_TYPE_HWINFO = 5, + NFP_DUMPSPEC_TYPE_FWNAME = 6, + NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7, + NFP_DUMPSPEC_TYPE_PROLOG = 10000, + NFP_DUMPSPEC_TYPE_ERROR = 10001, +}; + +/* The following structs must be carefully aligned so that they can be used to + * interpret the binary dumpspec and populate the dump data in a deterministic + * way. + */ + +/* generic type plus length */ +struct nfp_dump_tl { + __be32 type; + __be32 length; /* chunk length to follow, aligned to 8 bytes */ + char data[0]; +}; + +/* NFP CPP parameters */ +struct nfp_dumpspec_cpp_isl_id { + u8 target; + u8 action; + u8 token; + u8 island; +}; + +struct nfp_dump_common_cpp { + struct nfp_dumpspec_cpp_isl_id cpp_id; + __be32 offset; /* address to start dump */ + __be32 dump_length; /* total bytes to dump, aligned to reg size */ +}; + +/* CSR dumpables */ +struct nfp_dumpspec_csr { + struct nfp_dump_tl tl; + struct nfp_dump_common_cpp cpp; + __be32 register_width; /* in bits */ +}; + +struct nfp_dumpspec_rtsym { + struct nfp_dump_tl tl; + char rtsym[0]; +}; + +/* header for register dumpable */ +struct nfp_dump_csr { + struct nfp_dump_tl tl; + struct nfp_dump_common_cpp cpp; + __be32 register_width; /* in bits */ + __be32 error; /* error code encountered while reading */ + __be32 error_offset; /* offset being read when error occurred */ +}; + +struct nfp_dump_rtsym { + struct nfp_dump_tl tl; + struct nfp_dump_common_cpp cpp; + __be32 error; /* error code encountered while reading */ + u8 padded_name_length; /* pad so data starts at 8 byte boundary */ + char rtsym[0]; + /* after padded_name_length, there is dump_length data */ +}; + +struct nfp_dump_prolog { + struct nfp_dump_tl tl; + __be32 dump_level; +}; + +struct nfp_dump_error { + struct nfp_dump_tl tl; + __be32 error; + char padding[4]; + char spec[0]; +}; + +/* to track state through debug size calculation TLV traversal */ +struct nfp_level_size { + u32 requested_level; /* input */ + u32 total_size; /* output */ +}; + +/* to track state during debug dump creation TLV traversal */ +struct nfp_dump_state { + u32 requested_level; /* input param */ + u32 dumped_size; /* adds up to size of dumped data */ + u32 buf_size; /* size of buffer pointer to by p */ + void *p; /* current point in dump buffer */ +}; + +typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl, + void *param); + +static int +nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param, + nfp_tlv_visit tlv_visit) +{ + long long remaining = data_length; + struct nfp_dump_tl *tl; + u32 total_tlv_size; + void *p = data; + int err; + + while (remaining >= sizeof(*tl)) { + tl = p; + if (!tl->type && !tl->length) + break; + + if (be32_to_cpu(tl->length) > remaining - sizeof(*tl)) + return -EINVAL; + + total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length); + + /* Spec TLVs should be aligned to 4 bytes. */ + if (total_tlv_size % 4 != 0) + return -EINVAL; + + p += total_tlv_size; + remaining -= total_tlv_size; + err = tlv_visit(pf, tl, param); + if (err) + return err; + } + + return 0; +} + +static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id) +{ + return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token, + cpp_id->island); +} + +struct nfp_dumpspec * +nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl) +{ + const struct nfp_rtsym *specsym; + struct nfp_dumpspec *dumpspec; + int bytes_read; + u32 cpp_id; + + specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM); + if (!specsym) + return NULL; + + /* expected size of this buffer is in the order of tens of kilobytes */ + dumpspec = vmalloc(sizeof(*dumpspec) + specsym->size); + if (!dumpspec) + return NULL; + + dumpspec->size = specsym->size; + + cpp_id = NFP_CPP_ISLAND_ID(specsym->target, NFP_CPP_ACTION_RW, 0, + specsym->domain); + + bytes_read = nfp_cpp_read(cpp, cpp_id, specsym->addr, dumpspec->data, + specsym->size); + if (bytes_read != specsym->size) { + vfree(dumpspec); + nfp_warn(cpp, "Debug dump specification read failed.\n"); + return NULL; + } + + return dumpspec; +} + +static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec) +{ + return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) + + be32_to_cpu(spec->length)); +} + +static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf) +{ + u32 fwname_len = strlen(nfp_mip_name(pf->mip)); + + return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1); +} + +static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec) +{ + u32 tl_len, key_len; + const char *value; + + tl_len = be32_to_cpu(spec->length); + key_len = strnlen(spec->data, tl_len); + if (key_len == tl_len) + return nfp_dump_error_tlv_size(spec); + + value = nfp_hwinfo_lookup(pf->hwinfo, spec->data); + if (!value) + return nfp_dump_error_tlv_size(spec); + + return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2); +} + +static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr) +{ + u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl); + u32 available_sz = be32_to_cpu(spec_csr->tl.length); + u32 reg_width; + + if (available_sz < required_read_sz) + return false; + + reg_width = be32_to_cpu(spec_csr->register_width); + + return reg_width == 32 || reg_width == 64; +} + +static int +nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec) +{ + struct nfp_rtsym_table *rtbl = pf->rtbl; + struct nfp_dumpspec_rtsym *spec_rtsym; + const struct nfp_rtsym *sym; + u32 tl_len, key_len; + + spec_rtsym = (struct nfp_dumpspec_rtsym *)spec; + tl_len = be32_to_cpu(spec->length); + key_len = strnlen(spec_rtsym->rtsym, tl_len); + if (key_len == tl_len) + return nfp_dump_error_tlv_size(spec); + + sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym); + if (!sym) + return nfp_dump_error_tlv_size(spec); + + return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) + + ALIGN8(sym->size); +} + +static int +nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param) +{ + struct nfp_dumpspec_csr *spec_csr; + u32 *size = param; + u32 hwinfo_size; + + switch (be32_to_cpu(tl->type)) { + case NFP_DUMPSPEC_TYPE_FWNAME: + *size += nfp_calc_fwname_tlv_size(pf); + break; + case NFP_DUMPSPEC_TYPE_CPP_CSR: + case NFP_DUMPSPEC_TYPE_XPB_CSR: + case NFP_DUMPSPEC_TYPE_ME_CSR: + spec_csr = (struct nfp_dumpspec_csr *)tl; + if (!nfp_csr_spec_valid(spec_csr)) + *size += nfp_dump_error_tlv_size(tl); + else + *size += ALIGN8(sizeof(struct nfp_dump_csr)) + + ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length)); + break; + case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR: + spec_csr = (struct nfp_dumpspec_csr *)tl; + if (!nfp_csr_spec_valid(spec_csr)) + *size += nfp_dump_error_tlv_size(tl); + else + *size += ALIGN8(sizeof(struct nfp_dump_csr)) + + ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) * + NFP_IND_NUM_CONTEXTS); + break; + case NFP_DUMPSPEC_TYPE_RTSYM: + *size += nfp_calc_rtsym_dump_sz(pf, tl); + break; + case NFP_DUMPSPEC_TYPE_HWINFO: + hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo); + *size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size); + break; + case NFP_DUMPSPEC_TYPE_HWINFO_FIELD: + *size += nfp_calc_hwinfo_field_sz(pf, tl); + break; + default: + *size += nfp_dump_error_tlv_size(tl); + break; + } + + return 0; +} + +static int +nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level, + void *param) +{ + struct nfp_level_size *lev_sz = param; + + if (be32_to_cpu(dump_level->type) != lev_sz->requested_level) + return 0; + + return nfp_traverse_tlvs(pf, dump_level->data, + be32_to_cpu(dump_level->length), + &lev_sz->total_size, nfp_add_tlv_size); +} + +s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec, + u32 flag) +{ + struct nfp_level_size lev_sz; + int err; + + lev_sz.requested_level = flag; + lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog)); + + err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz, + nfp_calc_specific_level_size); + if (err) + return err; + + return lev_sz.total_size; +} + +static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump) +{ + struct nfp_dump_tl *tl = dump->p; + + if (total_tlv_sz > dump->buf_size) + return -ENOSPC; + + if (dump->buf_size - total_tlv_sz < dump->dumped_size) + return -ENOSPC; + + tl->type = cpu_to_be32(type); + tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl)); + + dump->dumped_size += total_tlv_sz; + dump->p += total_tlv_sz; + + return 0; +} + +static int +nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error, + struct nfp_dump_state *dump) +{ + struct nfp_dump_error *dump_header = dump->p; + u32 total_spec_size, total_size; + int err; + + total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length); + total_size = ALIGN8(sizeof(*dump_header) + total_spec_size); + + err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump); + if (err) + return err; + + dump_header->error = cpu_to_be32(error); + memcpy(dump_header->spec, spec, total_spec_size); + + return 0; +} + +static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump) +{ + struct nfp_dump_tl *dump_header = dump->p; + u32 fwname_len, total_size; + const char *fwname; + int err; + + fwname = nfp_mip_name(pf->mip); + fwname_len = strlen(fwname); + total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1); + + err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump); + if (err) + return err; + + memcpy(dump_header->data, fwname, fwname_len); + + return 0; +} + +static int +nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec, + struct nfp_dump_state *dump) +{ + struct nfp_dump_tl *dump_header = dump->p; + u32 hwinfo_size, total_size; + char *hwinfo; + int err; + + hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo); + hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo); + total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size); + + err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump); + if (err) + return err; + + memcpy(dump_header->data, hwinfo, hwinfo_size); + + return 0; +} + +static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec, + struct nfp_dump_state *dump) +{ + struct nfp_dump_tl *dump_header = dump->p; + u32 tl_len, key_len, val_len; + const char *key, *value; + u32 total_size; + int err; + + tl_len = be32_to_cpu(spec->length); + key_len = strnlen(spec->data, tl_len); + if (key_len == tl_len) + return nfp_dump_error_tlv(spec, -EINVAL, dump); + + key = spec->data; + value = nfp_hwinfo_lookup(pf->hwinfo, key); + if (!value) + return nfp_dump_error_tlv(spec, -ENOENT, dump); + + val_len = strlen(value); + total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2); + err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump); + if (err) + return err; + + memcpy(dump_header->data, key, key_len + 1); + memcpy(dump_header->data + key_len + 1, value, val_len + 1); + + return 0; +} + +static int +nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr, + struct nfp_dump_state *dump) +{ + struct nfp_dump_csr *dump_header = dump->p; + u32 reg_sz, header_size, total_size; + u32 cpp_rd_addr, max_rd_addr; + int bytes_read; + void *dest; + u32 cpp_id; + int err; + + if (!nfp_csr_spec_valid(spec_csr)) + return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump); + + reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE; + header_size = ALIGN8(sizeof(*dump_header)); + total_size = header_size + + ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length)); + dest = dump->p + header_size; + + err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump); + if (err) + return err; + + dump_header->cpp = spec_csr->cpp; + dump_header->register_width = spec_csr->register_width; + + cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id); + cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset); + max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length); + + while (cpp_rd_addr < max_rd_addr) { + bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr, dest, + reg_sz); + if (bytes_read != reg_sz) { + if (bytes_read >= 0) + bytes_read = -EIO; + dump_header->error = cpu_to_be32(bytes_read); + dump_header->error_offset = cpu_to_be32(cpp_rd_addr); + break; + } + cpp_rd_addr += reg_sz; + dest += reg_sz; + } + + return 0; +} + +/* Write context to CSRCtxPtr, then read from it. Then the value can be read + * from IndCtxStatus. + */ +static int +nfp_read_indirect_csr(struct nfp_cpp *cpp, + struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset, + u32 reg_sz, u32 context, void *dest) +{ + u32 csr_ctx_ptr_offs; + u32 cpp_id; + int result; + + csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset); + cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target, + NFP_IND_ME_REFL_WR_SIG_INIT, + cpp_params.token, cpp_params.island); + result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context); + if (result != sizeof(context)) + return result < 0 ? result : -EIO; + + cpp_id = nfp_get_numeric_cpp_id(&cpp_params); + result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz); + if (result != reg_sz) + return result < 0 ? result : -EIO; + + result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz); + if (result != reg_sz) + return result < 0 ? result : -EIO; + + return 0; +} + +static int +nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp, + struct nfp_dumpspec_csr *spec_csr, u32 address, + u32 reg_sz, void *dest) +{ + u32 ctx; + int err; + + for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) { + err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address, + reg_sz, ctx, dest + ctx * reg_sz); + if (err) + return err; + } + + return 0; +} + +static int +nfp_dump_indirect_csr_range(struct nfp_pf *pf, + struct nfp_dumpspec_csr *spec_csr, + struct nfp_dump_state *dump) +{ + struct nfp_dump_csr *dump_header = dump->p; + u32 reg_sz, header_size, total_size; + u32 cpp_rd_addr, max_rd_addr; + u32 reg_data_length; + void *dest; + int err; + + if (!nfp_csr_spec_valid(spec_csr)) + return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump); + + reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE; + header_size = ALIGN8(sizeof(*dump_header)); + reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) * + NFP_IND_NUM_CONTEXTS; + total_size = header_size + ALIGN8(reg_data_length); + dest = dump->p + header_size; + + err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump); + if (err) + return err; + + dump_header->cpp = spec_csr->cpp; + dump_header->register_width = spec_csr->register_width; + + cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset); + max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length); + while (cpp_rd_addr < max_rd_addr) { + err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr, + cpp_rd_addr, reg_sz, dest); + if (err) { + dump_header->error = cpu_to_be32(err); + dump_header->error_offset = cpu_to_be32(cpp_rd_addr); + break; + } + cpp_rd_addr += reg_sz; + dest += reg_sz * NFP_IND_NUM_CONTEXTS; + } + + return 0; +} + +static int +nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec, + struct nfp_dump_state *dump) +{ + struct nfp_dump_rtsym *dump_header = dump->p; + struct nfp_dumpspec_cpp_isl_id cpp_params; + struct nfp_rtsym_table *rtbl = pf->rtbl; + const struct nfp_rtsym *sym; + u32 header_size, total_size; + u32 tl_len, key_len; + int bytes_read; + u32 cpp_id; + void *dest; + int err; + + tl_len = be32_to_cpu(spec->tl.length); + key_len = strnlen(spec->rtsym, tl_len); + if (key_len == tl_len) + return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump); + + sym = nfp_rtsym_lookup(rtbl, spec->rtsym); + if (!sym) + return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump); + + header_size = + ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1); + total_size = header_size + ALIGN8(sym->size); + dest = dump->p + header_size; + + err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump); + if (err) + return err; + + dump_header->padded_name_length = + header_size - offsetof(struct nfp_dump_rtsym, rtsym); + memcpy(dump_header->rtsym, spec->rtsym, key_len + 1); + + cpp_params.target = sym->target; + cpp_params.action = NFP_CPP_ACTION_RW; + cpp_params.token = 0; + cpp_params.island = sym->domain; + cpp_id = nfp_get_numeric_cpp_id(&cpp_params); + + dump_header->cpp.cpp_id = cpp_params; + dump_header->cpp.offset = cpu_to_be32(sym->addr); + dump_header->cpp.dump_length = cpu_to_be32(sym->size); + + bytes_read = nfp_cpp_read(pf->cpp, cpp_id, sym->addr, dest, sym->size); + if (bytes_read != sym->size) { + if (bytes_read >= 0) + bytes_read = -EIO; + dump_header->error = cpu_to_be32(bytes_read); + } + + return 0; +} + +static int +nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param) +{ + struct nfp_dumpspec_rtsym *spec_rtsym; + struct nfp_dump_state *dump = param; + struct nfp_dumpspec_csr *spec_csr; + int err; + + switch (be32_to_cpu(tl->type)) { + case NFP_DUMPSPEC_TYPE_FWNAME: + err = nfp_dump_fwname(pf, dump); + if (err) + return err; + break; + case NFP_DUMPSPEC_TYPE_CPP_CSR: + case NFP_DUMPSPEC_TYPE_XPB_CSR: + case NFP_DUMPSPEC_TYPE_ME_CSR: + spec_csr = (struct nfp_dumpspec_csr *)tl; + err = nfp_dump_csr_range(pf, spec_csr, dump); + if (err) + return err; + break; + case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR: + spec_csr = (struct nfp_dumpspec_csr *)tl; + err = nfp_dump_indirect_csr_range(pf, spec_csr, dump); + if (err) + return err; + break; + case NFP_DUMPSPEC_TYPE_RTSYM: + spec_rtsym = (struct nfp_dumpspec_rtsym *)tl; + err = nfp_dump_single_rtsym(pf, spec_rtsym, dump); + if (err) + return err; + break; + case NFP_DUMPSPEC_TYPE_HWINFO: + err = nfp_dump_hwinfo(pf, tl, dump); + if (err) + return err; + break; + case NFP_DUMPSPEC_TYPE_HWINFO_FIELD: + err = nfp_dump_hwinfo_field(pf, tl, dump); + if (err) + return err; + break; + default: + err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump); + if (err) + return err; + } + + return 0; +} + +static int +nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level, + void *param) +{ + struct nfp_dump_state *dump = param; + + if (be32_to_cpu(dump_level->type) != dump->requested_level) + return 0; + + return nfp_traverse_tlvs(pf, dump_level->data, + be32_to_cpu(dump_level->length), dump, + nfp_dump_for_tlv); +} + +static int nfp_dump_populate_prolog(struct nfp_dump_state *dump) +{ + struct nfp_dump_prolog *prolog = dump->p; + u32 total_size; + int err; + + total_size = ALIGN8(sizeof(*prolog)); + + err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump); + if (err) + return err; + + prolog->dump_level = cpu_to_be32(dump->requested_level); + + return 0; +} + +int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec, + struct ethtool_dump *dump_param, void *dest) +{ + struct nfp_dump_state dump; + int err; + + dump.requested_level = dump_param->flag; + dump.dumped_size = 0; + dump.p = dest; + dump.buf_size = dump_param->len; + + err = nfp_dump_populate_prolog(&dump); + if (err) + return err; + + err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump, + nfp_dump_specific_level); + if (err) + return err; + + /* Set size of actual dump, to trigger warning if different from + * calculated size. + */ + dump_param->len = dump.dumped_size; + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 2801ecd09eab..2cde0eb00ee3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -51,14 +51,11 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_nsp.h" #include "nfp_app.h" +#include "nfp_main.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" #include "nfp_port.h" -enum nfp_dump_diag { - NFP_DUMP_NSP_DIAG = 0, -}; - struct nfp_et_stat { char name[ETH_GSTRING_LEN]; int off; @@ -1066,15 +1063,34 @@ exit_release: return ret; } +/* Set the dump flag/level. Calculate the dump length for flag > 0 only (new TLV + * based dumps), since flag 0 (default) calculates the length in + * nfp_app_get_dump_flag(), and we need to support triggering a level 0 dump + * without setting the flag first, for backward compatibility. + */ static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val) { struct nfp_app *app = nfp_app_from_netdev(netdev); + s64 len; if (!app) return -EOPNOTSUPP; - if (val->flag != NFP_DUMP_NSP_DIAG) - return -EINVAL; + if (val->flag == NFP_DUMP_NSP_DIAG) { + app->pf->dump_flag = val->flag; + return 0; + } + + if (!app->pf->dumpspec) + return -EOPNOTSUPP; + + len = nfp_net_dump_calculate_size(app->pf, app->pf->dumpspec, + val->flag); + if (len < 0) + return len; + + app->pf->dump_flag = val->flag; + app->pf->dump_len = len; return 0; } @@ -1082,14 +1098,37 @@ static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val) static int nfp_app_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) { - return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, NULL); + struct nfp_app *app = nfp_app_from_netdev(netdev); + + if (!app) + return -EOPNOTSUPP; + + if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG) + return nfp_dump_nsp_diag(app, dump, NULL); + + dump->flag = app->pf->dump_flag; + dump->len = app->pf->dump_len; + + return 0; } static int nfp_app_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, void *buffer) { - return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, buffer); + struct nfp_app *app = nfp_app_from_netdev(netdev); + + if (!app) + return -EOPNOTSUPP; + + if (app->pf->dump_flag == NFP_DUMP_NSP_DIAG) + return nfp_dump_nsp_diag(app, dump, buffer); + + dump->flag = app->pf->dump_flag; + dump->len = app->pf->dump_len; + + return nfp_net_dump_populate_buffer(app->pf, app->pf->dumpspec, dump, + buffer); } static int nfp_net_set_coalesce(struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 3ce51f03126f..ced62d112aa2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -49,6 +49,8 @@ struct nfp_hwinfo; struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp); const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup); +char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo); +u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo); /* Implemented in nfp_nsp.c, low level functions */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 04dd5758ecf5..3fcb522d2e85 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -372,8 +372,7 @@ nfp_cpp_area_alloc(struct nfp_cpp *cpp, u32 dest, * that it can be accessed directly. * * NOTE: @address and @size must be 32-bit aligned values. - * - * NOTE: The area must also be 'released' when the structure is freed. + * The area must also be 'released' when the structure is freed. * * Return: NFP CPP Area handle, or NULL */ @@ -536,8 +535,7 @@ void nfp_cpp_area_release_free(struct nfp_cpp_area *area) * Read data from indicated CPP region. * * NOTE: @offset and @length must be 32-bit aligned values. - * - * NOTE: Area must have been locked down with an 'acquire'. + * Area must have been locked down with an 'acquire'. * * Return: length of io, or -ERRNO */ @@ -558,8 +556,7 @@ int nfp_cpp_area_read(struct nfp_cpp_area *area, * Write data to indicated CPP region. * * NOTE: @offset and @length must be 32-bit aligned values. - * - * NOTE: Area must have been locked down with an 'acquire'. + * Area must have been locked down with an 'acquire'. * * Return: length of io, or -ERRNO */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c index 4f24aff1e772..063a9a6243d6 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_hwinfo.c @@ -302,3 +302,13 @@ const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup) return NULL; } + +char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo) +{ + return hwinfo->data; +} + +u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo) +{ + return le32_to_cpu(hwinfo->size) - sizeof(u32); +} diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 481876b5424c..53614ed694fc 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -2563,7 +2563,7 @@ static int nv_tx_done(struct net_device *dev, int limit) if (np->desc_ver == DESC_VER_1) { if (flags & NV_TX_LASTPACKET) { - if (flags & NV_TX_ERROR) { + if (unlikely(flags & NV_TX_ERROR)) { if ((flags & NV_TX_RETRYERROR) && !(flags & NV_TX_RETRYCOUNT_MASK)) nv_legacybackoff_reseed(dev); @@ -2580,7 +2580,7 @@ static int nv_tx_done(struct net_device *dev, int limit) } } else { if (flags & NV_TX2_LASTPACKET) { - if (flags & NV_TX2_ERROR) { + if (unlikely(flags & NV_TX2_ERROR)) { if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK)) nv_legacybackoff_reseed(dev); @@ -2626,7 +2626,7 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit) nv_unmap_txskb(np, np->get_tx_ctx); if (flags & NV_TX2_LASTPACKET) { - if (flags & NV_TX2_ERROR) { + if (unlikely(flags & NV_TX2_ERROR)) { if ((flags & NV_TX2_RETRYERROR) && !(flags & NV_TX2_RETRYCOUNT_MASK)) { if (np->driver_data & DEV_HAS_GEAR_MODE) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index 0a66389c06c2..1cd39c9a0345 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -2502,12 +2502,10 @@ netxen_collect_minidump(struct netxen_adapter *adapter) { int ret = 0; struct netxen_minidump_template_hdr *hdr; - struct timespec val; hdr = (struct netxen_minidump_template_hdr *) adapter->mdump.md_template; hdr->driver_capture_mask = adapter->mdump.md_capture_mask; - jiffies_to_timespec(jiffies, &val); - hdr->driver_timestamp = (u32) val.tv_sec; + hdr->driver_timestamp = ktime_get_seconds(); hdr->driver_info_word2 = adapter->fw_version; hdr->driver_info_word3 = NXRD32(adapter, CRB_DRIVER_VERSION); ret = netxen_parse_md_template(adapter); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 8f9b3eb82137..57332b3e5e64 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1068,10 +1068,6 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) pci_set_drvdata(pdev, NULL); - /* Release edev's reference to XDP's bpf if such exist */ - if (edev->xdp_prog) - bpf_prog_put(edev->xdp_prog); - /* Use global ops since we've freed edev */ qed_ops->common->slowpath_stop(cdev); if (system_state == SYSTEM_POWER_OFF) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d7250539d0bd..c52a9963c19d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1997,22 +1997,60 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, static void stmmac_dma_interrupt(struct stmmac_priv *priv) { u32 tx_channel_count = priv->plat->tx_queues_to_use; - int status; + u32 rx_channel_count = priv->plat->rx_queues_to_use; + u32 channels_to_check = tx_channel_count > rx_channel_count ? + tx_channel_count : rx_channel_count; u32 chan; + bool poll_scheduled = false; + int status[channels_to_check]; + + /* Each DMA channel can be used for rx and tx simultaneously, yet + * napi_struct is embedded in struct stmmac_rx_queue rather than in a + * stmmac_channel struct. + * Because of this, stmmac_poll currently checks (and possibly wakes) + * all tx queues rather than just a single tx queue. + */ + for (chan = 0; chan < channels_to_check; chan++) + status[chan] = priv->hw->dma->dma_interrupt(priv->ioaddr, + &priv->xstats, + chan); - for (chan = 0; chan < tx_channel_count; chan++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; + for (chan = 0; chan < rx_channel_count; chan++) { + if (likely(status[chan] & handle_rx)) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; - status = priv->hw->dma->dma_interrupt(priv->ioaddr, - &priv->xstats, chan); - if (likely((status & handle_rx)) || (status & handle_tx)) { if (likely(napi_schedule_prep(&rx_q->napi))) { stmmac_disable_dma_irq(priv, chan); __napi_schedule(&rx_q->napi); + poll_scheduled = true; + } + } + } + + /* If we scheduled poll, we already know that tx queues will be checked. + * If we didn't schedule poll, see if any DMA channel (used by tx) has a + * completed transmission, if so, call stmmac_poll (once). + */ + if (!poll_scheduled) { + for (chan = 0; chan < tx_channel_count; chan++) { + if (status[chan] & handle_tx) { + /* It doesn't matter what rx queue we choose + * here. We use 0 since it always exists. + */ + struct stmmac_rx_queue *rx_q = + &priv->rx_queue[0]; + + if (likely(napi_schedule_prep(&rx_q->napi))) { + stmmac_disable_dma_irq(priv, chan); + __napi_schedule(&rx_q->napi); + } + break; } } + } - if (unlikely(status & tx_hard_error_bump_tc)) { + for (chan = 0; chan < tx_channel_count; chan++) { + if (unlikely(status[chan] & tx_hard_error_bump_tc)) { /* Try to bump up the dma threshold on this failure */ if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && (tc <= 256)) { @@ -2029,7 +2067,7 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) chan); priv->xstats.threshold = tc; } - } else if (unlikely(status == tx_hard_error)) { + } else if (unlikely(status[chan] == tx_hard_error)) { stmmac_tx_err(priv, chan); } } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index a73600dceb8b..a60a378b8b29 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -88,6 +88,7 @@ do { \ #define CPSW_VERSION_4 0x190112 #define HOST_PORT_NUM 0 +#define CPSW_ALE_PORTS_NUM 3 #define SLIVER_SIZE 0x40 #define CPSW1_HOST_PORT_OFFSET 0x028 @@ -352,6 +353,27 @@ struct cpsw_hw_stats { u32 rxdmaoverruns; }; +struct cpsw_slave_data { + struct device_node *phy_node; + char phy_id[MII_BUS_ID_SIZE]; + int phy_if; + u8 mac_addr[ETH_ALEN]; + u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ +}; + +struct cpsw_platform_data { + struct cpsw_slave_data *slave_data; + u32 ss_reg_ofs; /* Subsystem control register offset */ + u32 channels; /* number of cpdma channels (symmetric) */ + u32 slaves; /* number of slave cpgmac ports */ + u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ + u32 ale_entries; /* ale table size */ + u32 bd_ram_size; /*buffer descriptor ram size */ + u32 mac_control; /* Mac control register */ + u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ + bool dual_emac; /* Enable Dual EMAC mode */ +}; + struct cpsw_slave { void __iomem *regs; struct cpsw_sliver_regs __iomem *sliver; @@ -365,12 +387,12 @@ struct cpsw_slave { static inline u32 slave_read(struct cpsw_slave *slave, u32 offset) { - return __raw_readl(slave->regs + offset); + return readl_relaxed(slave->regs + offset); } static inline void slave_write(struct cpsw_slave *slave, u32 val, u32 offset) { - __raw_writel(val, slave->regs + offset); + writel_relaxed(val, slave->regs + offset); } struct cpsw_vector { @@ -660,8 +682,8 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) static void cpsw_intr_enable(struct cpsw_common *cpsw) { - __raw_writel(0xFF, &cpsw->wr_regs->tx_en); - __raw_writel(0xFF, &cpsw->wr_regs->rx_en); + writel_relaxed(0xFF, &cpsw->wr_regs->tx_en); + writel_relaxed(0xFF, &cpsw->wr_regs->rx_en); cpdma_ctlr_int_ctrl(cpsw->dma, true); return; @@ -669,8 +691,8 @@ static void cpsw_intr_enable(struct cpsw_common *cpsw) static void cpsw_intr_disable(struct cpsw_common *cpsw) { - __raw_writel(0, &cpsw->wr_regs->tx_en); - __raw_writel(0, &cpsw->wr_regs->rx_en); + writel_relaxed(0, &cpsw->wr_regs->tx_en); + writel_relaxed(0, &cpsw->wr_regs->rx_en); cpdma_ctlr_int_ctrl(cpsw->dma, false); return; @@ -949,18 +971,14 @@ static inline void soft_reset(const char *module, void __iomem *reg) { unsigned long timeout = jiffies + HZ; - __raw_writel(1, reg); + writel_relaxed(1, reg); do { cpu_relax(); - } while ((__raw_readl(reg) & 1) && time_after(timeout, jiffies)); + } while ((readl_relaxed(reg) & 1) && time_after(timeout, jiffies)); - WARN(__raw_readl(reg) & 1, "failed to soft-reset %s\n", module); + WARN(readl_relaxed(reg) & 1, "failed to soft-reset %s\n", module); } -#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ - ((mac)[2] << 16) | ((mac)[3] << 24)) -#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) - static void cpsw_set_slave_mac(struct cpsw_slave *slave, struct cpsw_priv *priv) { @@ -1015,7 +1033,7 @@ static void _cpsw_adjust_link(struct cpsw_slave *slave, if (mac_control != slave->mac_control) { phy_print_status(phy); - __raw_writel(mac_control, &slave->sliver->mac_control); + writel_relaxed(mac_control, &slave->sliver->mac_control); } slave->mac_control = mac_control; @@ -1278,7 +1296,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) soft_reset_slave(slave); /* setup priority mapping */ - __raw_writel(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map); + writel_relaxed(RX_PRIORITY_MAPPING, &slave->sliver->rx_pri_map); switch (cpsw->version) { case CPSW_VERSION_1: @@ -1304,7 +1322,7 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) } /* setup max packet size, and mac address */ - __raw_writel(cpsw->rx_packet_max, &slave->sliver->rx_maxlen); + writel_relaxed(cpsw->rx_packet_max, &slave->sliver->rx_maxlen); cpsw_set_slave_mac(slave, priv); slave->mac_control = 0; /* no link yet */ @@ -1395,9 +1413,9 @@ static void cpsw_init_host_port(struct cpsw_priv *priv) writel(fifo_mode, &cpsw->host_port_regs->tx_in_ctl); /* setup host port priority mapping */ - __raw_writel(CPDMA_TX_PRIORITY_MAP, - &cpsw->host_port_regs->cpdma_tx_pri_map); - __raw_writel(0, &cpsw->host_port_regs->cpdma_rx_chan_map); + writel_relaxed(CPDMA_TX_PRIORITY_MAP, + &cpsw->host_port_regs->cpdma_tx_pri_map); + writel_relaxed(0, &cpsw->host_port_regs->cpdma_rx_chan_map); cpsw_ale_control_set(cpsw->ale, HOST_PORT_NUM, ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); @@ -1514,10 +1532,10 @@ static int cpsw_ndo_open(struct net_device *ndev) /* initialize shared resources for every ndev */ if (!cpsw->usage_count) { /* disable priority elevation */ - __raw_writel(0, &cpsw->regs->ptype); + writel_relaxed(0, &cpsw->regs->ptype); /* enable statistics collection only on all ports */ - __raw_writel(0x7, &cpsw->regs->stat_port_en); + writel_relaxed(0x7, &cpsw->regs->stat_port_en); /* Enable internal fifo flow control */ writel(0x7, &cpsw->regs->flow_control); @@ -1701,7 +1719,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) slave_write(slave, mtype, CPSW2_TS_SEQ_MTYPE); slave_write(slave, ctrl, CPSW2_CONTROL); - __raw_writel(ETH_P_1588, &cpsw->regs->ts_ltype); + writel_relaxed(ETH_P_1588, &cpsw->regs->ts_ltype); } static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) @@ -2298,7 +2316,6 @@ static int cpsw_check_ch_settings(struct cpsw_common *cpsw, static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) { - int (*poll)(struct napi_struct *, int); struct cpsw_common *cpsw = priv->cpsw; void (*handler)(void *, int, int); struct netdev_queue *queue; @@ -2309,12 +2326,10 @@ static int cpsw_update_channels_res(struct cpsw_priv *priv, int ch_num, int rx) ch = &cpsw->rx_ch_num; vec = cpsw->rxv; handler = cpsw_rx_handler; - poll = cpsw_rx_poll; } else { ch = &cpsw->tx_ch_num; vec = cpsw->txv; handler = cpsw_tx_handler; - poll = cpsw_tx_poll; } while (*ch < ch_num) { @@ -3060,7 +3075,7 @@ static int cpsw_probe(struct platform_device *pdev) ale_params.dev = &pdev->dev; ale_params.ale_ageout = ale_ageout; ale_params.ale_entries = data->ale_entries; - ale_params.ale_ports = data->slaves; + ale_params.ale_ports = CPSW_ALE_PORTS_NUM; cpsw->ale = cpsw_ale_create(&ale_params); if (!cpsw->ale) { @@ -3072,14 +3087,14 @@ static int cpsw_probe(struct platform_device *pdev) cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpsw->dev->of_node); if (IS_ERR(cpsw->cpts)) { ret = PTR_ERR(cpsw->cpts); - goto clean_ale_ret; + goto clean_dma_ret; } ndev->irq = platform_get_irq(pdev, 1); if (ndev->irq < 0) { dev_err(priv->dev, "error getting irq resource\n"); ret = ndev->irq; - goto clean_ale_ret; + goto clean_dma_ret; } of_id = of_match_device(cpsw_of_mtable, &pdev->dev); @@ -3103,7 +3118,7 @@ static int cpsw_probe(struct platform_device *pdev) if (ret) { dev_err(priv->dev, "error registering net device\n"); ret = -ENODEV; - goto clean_ale_ret; + goto clean_dma_ret; } if (cpsw->data.dual_emac) { @@ -3126,7 +3141,7 @@ static int cpsw_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 1); if (irq < 0) { ret = irq; - goto clean_ale_ret; + goto clean_dma_ret; } cpsw->irqs_table[0] = irq; @@ -3134,14 +3149,14 @@ static int cpsw_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), cpsw); if (ret < 0) { dev_err(priv->dev, "error attaching irq (%d)\n", ret); - goto clean_ale_ret; + goto clean_dma_ret; } /* TX IRQ */ irq = platform_get_irq(pdev, 2); if (irq < 0) { ret = irq; - goto clean_ale_ret; + goto clean_dma_ret; } cpsw->irqs_table[1] = irq; @@ -3149,7 +3164,7 @@ static int cpsw_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), cpsw); if (ret < 0) { dev_err(priv->dev, "error attaching irq (%d)\n", ret); - goto clean_ale_ret; + goto clean_dma_ret; } cpsw_notice(priv, probe, @@ -3162,8 +3177,6 @@ static int cpsw_probe(struct platform_device *pdev) clean_unregister_netdev_ret: unregister_netdev(ndev); -clean_ale_ret: - cpsw_ale_destroy(cpsw->ale); clean_dma_ret: cpdma_ctlr_destroy(cpsw->dma); clean_dt_ret: @@ -3193,7 +3206,6 @@ static int cpsw_remove(struct platform_device *pdev) unregister_netdev(ndev); cpts_release(cpsw->cpts); - cpsw_ale_destroy(cpsw->ale); cpdma_ctlr_destroy(cpsw->dma); cpsw_remove_dt(pdev); pm_runtime_put_sync(&pdev->dev); diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 6c3037aa2cd3..cf111db3dc27 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -17,26 +17,9 @@ #include <linux/if_ether.h> #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]; - u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ -}; - -struct cpsw_platform_data { - struct cpsw_slave_data *slave_data; - u32 ss_reg_ofs; /* Subsystem control register offset */ - u32 channels; /* number of cpdma channels (symmetric) */ - u32 slaves; /* number of slave cpgmac ports */ - u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ - u32 ale_entries; /* ale table size */ - u32 bd_ram_size; /*buffer descriptor ram size */ - u32 mac_control; /* Mac control register */ - u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ - bool dual_emac; /* Enable Dual EMAC mode */ -}; +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave); int ti_cm_get_macid(struct device *dev, int slave, u8 *mac_addr); diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index b432a75fb874..93dc05c194d3 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -150,11 +150,11 @@ static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry) WARN_ON(idx > ale->params.ale_entries); - __raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL); + writel_relaxed(idx, ale->params.ale_regs + ALE_TABLE_CONTROL); for (i = 0; i < ALE_ENTRY_WORDS; i++) - ale_entry[i] = __raw_readl(ale->params.ale_regs + - ALE_TABLE + 4 * i); + ale_entry[i] = readl_relaxed(ale->params.ale_regs + + ALE_TABLE + 4 * i); return idx; } @@ -166,11 +166,11 @@ static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry) WARN_ON(idx > ale->params.ale_entries); for (i = 0; i < ALE_ENTRY_WORDS; i++) - __raw_writel(ale_entry[i], ale->params.ale_regs + - ALE_TABLE + 4 * i); + writel_relaxed(ale_entry[i], ale->params.ale_regs + + ALE_TABLE + 4 * i); - __raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs + - ALE_TABLE_CONTROL); + writel_relaxed(idx | ALE_TABLE_WRITE, ale->params.ale_regs + + ALE_TABLE_CONTROL); return idx; } @@ -723,7 +723,7 @@ int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, if (info->port_offset == 0 && info->port_shift == 0) port = 0; /* global, port is a dont care */ - if (port < 0 || port > ale->params.ale_ports) + if (port < 0 || port >= ale->params.ale_ports) return -EINVAL; mask = BITMASK(info->bits); @@ -733,9 +733,9 @@ int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, offset = info->offset + (port * info->port_offset); shift = info->shift + (port * info->port_shift); - tmp = __raw_readl(ale->params.ale_regs + offset); + tmp = readl_relaxed(ale->params.ale_regs + offset); tmp = (tmp & ~(mask << shift)) | (value << shift); - __raw_writel(tmp, ale->params.ale_regs + offset); + writel_relaxed(tmp, ale->params.ale_regs + offset); return 0; } @@ -754,13 +754,13 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) if (info->port_offset == 0 && info->port_shift == 0) port = 0; /* global, port is a dont care */ - if (port < 0 || port > ale->params.ale_ports) + if (port < 0 || port >= ale->params.ale_ports) return -EINVAL; offset = info->offset + (port * info->port_offset); shift = info->shift + (port * info->port_shift); - tmp = __raw_readl(ale->params.ale_regs + offset) >> shift; + tmp = readl_relaxed(ale->params.ale_regs + offset) >> shift; return tmp & BITMASK(info->bits); } EXPORT_SYMBOL_GPL(cpsw_ale_control_get); @@ -779,9 +779,37 @@ static void cpsw_ale_timer(struct timer_list *t) void cpsw_ale_start(struct cpsw_ale *ale) { + cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1); + cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); + + timer_setup(&ale->timer, cpsw_ale_timer, 0); + if (ale->ageout) { + ale->timer.expires = jiffies + ale->ageout; + add_timer(&ale->timer); + } +} +EXPORT_SYMBOL_GPL(cpsw_ale_start); + +void cpsw_ale_stop(struct cpsw_ale *ale) +{ + del_timer_sync(&ale->timer); + cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0); +} +EXPORT_SYMBOL_GPL(cpsw_ale_stop); + +struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) +{ + struct cpsw_ale *ale; u32 rev, ale_entries; - rev = __raw_readl(ale->params.ale_regs + ALE_IDVER); + ale = devm_kzalloc(params->dev, sizeof(*ale), GFP_KERNEL); + if (!ale) + return NULL; + + ale->params = *params; + ale->ageout = ale->params.ale_ageout * HZ; + + rev = readl_relaxed(ale->params.ale_regs + ALE_IDVER); if (!ale->params.major_ver_mask) ale->params.major_ver_mask = 0xff; ale->version = @@ -793,8 +821,8 @@ void cpsw_ale_start(struct cpsw_ale *ale) if (!ale->params.ale_entries) { ale_entries = - __raw_readl(ale->params.ale_regs + ALE_STATUS) & - ALE_STATUS_SIZE_MASK; + readl_relaxed(ale->params.ale_regs + ALE_STATUS) & + ALE_STATUS_SIZE_MASK; /* ALE available on newer NetCP switches has introduced * a register, ALE_STATUS, to indicate the size of ALE * table which shows the size as a multiple of 1024 entries. @@ -816,9 +844,9 @@ void cpsw_ale_start(struct cpsw_ale *ale) "ALE Table size %ld\n", ale->params.ale_entries); /* set default bits for existing h/w */ - ale->port_mask_bits = 3; - ale->port_num_bits = 2; - ale->vlan_field_bits = 3; + ale->port_mask_bits = ale->params.ale_ports; + ale->port_num_bits = order_base_2(ale->params.ale_ports); + ale->vlan_field_bits = ale->params.ale_ports; /* Set defaults override for ALE on NetCP NU switch and for version * 1R3 @@ -847,57 +875,12 @@ void cpsw_ale_start(struct cpsw_ale *ale) ale_controls[ALE_PORT_UNTAGGED_EGRESS].shift = 0; ale_controls[ALE_PORT_UNTAGGED_EGRESS].offset = ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS; - ale->port_mask_bits = ale->params.ale_ports; - ale->port_num_bits = ale->params.ale_ports - 1; - ale->vlan_field_bits = ale->params.ale_ports; - } else if (ale->version == ALE_VERSION_1R3) { - ale->port_mask_bits = ale->params.ale_ports; - ale->port_num_bits = 3; - ale->vlan_field_bits = ale->params.ale_ports; } - cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1); - cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); - - timer_setup(&ale->timer, cpsw_ale_timer, 0); - if (ale->ageout) { - ale->timer.expires = jiffies + ale->ageout; - add_timer(&ale->timer); - } -} -EXPORT_SYMBOL_GPL(cpsw_ale_start); - -void cpsw_ale_stop(struct cpsw_ale *ale) -{ - del_timer_sync(&ale->timer); -} -EXPORT_SYMBOL_GPL(cpsw_ale_stop); - -struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) -{ - struct cpsw_ale *ale; - - ale = kzalloc(sizeof(*ale), GFP_KERNEL); - if (!ale) - return NULL; - - ale->params = *params; - ale->ageout = ale->params.ale_ageout * HZ; - return ale; } EXPORT_SYMBOL_GPL(cpsw_ale_create); -int cpsw_ale_destroy(struct cpsw_ale *ale) -{ - if (!ale) - return -EINVAL; - cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0); - kfree(ale); - return 0; -} -EXPORT_SYMBOL_GPL(cpsw_ale_destroy); - void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) { int i; diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index 25d24e8d0904..d4fe9016429b 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -100,7 +100,6 @@ enum cpsw_ale_port_state { #define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32) struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params); -int cpsw_ale_destroy(struct cpsw_ale *ale); void cpsw_ale_start(struct cpsw_ale *ale); void cpsw_ale_stop(struct cpsw_ale *ale); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 4bb561856af5..f58c0c620356 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1385,11 +1385,6 @@ static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd) return -EOPNOTSUPP; } -static int match_first_device(struct device *dev, void *data) -{ - return !strncmp(dev_name(dev), "davinci_mdio", 12); -} - /** * emac_dev_open - EMAC device open * @ndev: The DaVinci EMAC network adapter @@ -1489,8 +1484,8 @@ static int emac_dev_open(struct net_device *ndev) /* use the first phy on the bus if pdata did not give us a phy id */ if (!phydev && !priv->phy_id) { - phy = bus_find_device(&mdio_bus_type, NULL, NULL, - match_first_device); + phy = bus_find_device_by_name(&mdio_bus_type, NULL, + "davinci_mdio"); if (phy) { priv->phy_id = dev_name(phy); if (!priv->phy_id || !*priv->phy_id) diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index e831c49713ee..56dbc0b9fedc 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -27,6 +27,7 @@ #include <linux/net_tstamp.h> #include <linux/ethtool.h> +#include "cpsw.h" #include "cpsw_ale.h" #include "netcp.h" #include "cpts.h" @@ -2047,10 +2048,6 @@ static const struct ethtool_ops keystone_ethtool_ops = { .get_ts_info = keystone_get_ts_info, }; -#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ - ((mac)[2] << 16) | ((mac)[3] << 24)) -#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) - static void gbe_set_slave_mac(struct gbe_slave *slave, struct gbe_intf *gbe_intf) { @@ -3692,7 +3689,6 @@ static int gbe_remove(struct netcp_device *netcp_device, void *inst_priv) del_timer_sync(&gbe_dev->timer); cpts_release(gbe_dev->cpts); cpsw_ale_stop(gbe_dev->ale); - cpsw_ale_destroy(gbe_dev->ale); netcp_txpipe_close(&gbe_dev->tx_pipe); free_secondary_ports(gbe_dev); |