diff options
Diffstat (limited to 'drivers/net/ethernet/pensando')
-rw-r--r-- | drivers/net/ethernet/pensando/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic.h | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_debugfs.c | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_dev.c | 237 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_dev.h | 115 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 64 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_if.h | 259 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.c | 609 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_lif.h | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_main.c | 120 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_stats.c | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 1169 | ||||
-rw-r--r-- | drivers/net/ethernet/pensando/ionic/ionic_txrx.h | 8 |
15 files changed, 1810 insertions, 852 deletions
diff --git a/drivers/net/ethernet/pensando/Kconfig b/drivers/net/ethernet/pensando/Kconfig index 3f7519e435b8..01fe76786f77 100644 --- a/drivers/net/ethernet/pensando/Kconfig +++ b/drivers/net/ethernet/pensando/Kconfig @@ -23,6 +23,7 @@ config IONIC depends on PTP_1588_CLOCK_OPTIONAL select NET_DEVLINK select DIMLIB + select PAGE_POOL help This enables the support for the Pensando family of Ethernet adapters. More specific information on this driver can be diff --git a/drivers/net/ethernet/pensando/ionic/ionic.h b/drivers/net/ethernet/pensando/ionic/ionic.h index 9ffef2e06885..04f00ea94230 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic.h +++ b/drivers/net/ethernet/pensando/ionic/ionic.h @@ -47,6 +47,7 @@ struct ionic { struct ionic_dev_bar bars[IONIC_BARS_MAX]; unsigned int num_bars; struct ionic_identity ident; + struct workqueue_struct *wq; struct ionic_lif *lif; unsigned int nnqs_per_lif; unsigned int neqs_per_lif; @@ -54,7 +55,8 @@ struct ionic { unsigned int nrxqs_per_lif; unsigned int nintrs; DECLARE_BITMAP(intrs, IONIC_INTR_CTRL_REGS_MAX); - struct work_struct nb_work; + cpumask_var_t *affinity_masks; + struct delayed_work doorbell_check_dwork; struct notifier_block nb; struct rw_semaphore vf_op_lock; /* lock for VF operations */ struct ionic_vf *vfs; @@ -76,6 +78,8 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx); int ionic_adminq_post_wait_nomsg(struct ionic_lif *lif, struct ionic_admin_ctx *ctx); void ionic_adminq_netdev_err_print(struct ionic_lif *lif, u8 opcode, u8 status, int err); +bool ionic_notifyq_service(struct ionic_cq *cq); +bool ionic_adminq_service(struct ionic_cq *cq); int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_wait); int ionic_dev_cmd_wait_nomsg(struct ionic *ionic, unsigned long max_wait); @@ -91,4 +95,6 @@ int ionic_port_identify(struct ionic *ionic); int ionic_port_init(struct ionic *ionic); int ionic_port_reset(struct ionic *ionic); +bool ionic_doorbell_wa(struct ionic *ionic); + #endif /* _IONIC_H_ */ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c index 6ba8d4aca0a0..f5dc876eb500 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c @@ -326,6 +326,11 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out; } +#ifdef CONFIG_PPC64 + /* Ensure MSI/MSI-X interrupts lie within addressable physical memory */ + pdev->no_64bit_msi = 1; +#endif + err = ionic_setup_one(ionic); if (err) goto err_out; @@ -372,6 +377,7 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mod_timer(&ionic->watchdog_timer, round_jiffies(jiffies + ionic->watchdog_period)); + ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE); return 0; @@ -388,6 +394,7 @@ err_out_free_irqs: err_out_pci: ionic_dev_teardown(ionic); ionic_clear_pci(ionic); + ionic_debugfs_del_dev(ionic); err_out: mutex_destroy(&ionic->dev_cmd_lock); ionic_devlink_free(ionic); @@ -406,6 +413,8 @@ static void ionic_remove(struct pci_dev *pdev) if (test_and_clear_bit(IONIC_LIF_F_FW_RESET, ionic->lif->state)) set_bit(IONIC_LIF_F_FW_STOPPING, ionic->lif->state); + if (ionic->lif->doorbell_wa) + cancel_delayed_work_sync(&ionic->doorbell_check_dwork); ionic_lif_unregister(ionic->lif); ionic_devlink_unregister(ionic); ionic_lif_deinit(ionic->lif); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c index 91327ef670c7..c98b4e75e288 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_debugfs.c @@ -113,8 +113,8 @@ static const struct debugfs_reg32 intr_ctrl_regs[] = { void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq) { struct dentry *qcq_dentry, *q_dentry, *cq_dentry; - struct dentry *intr_dentry, *stats_dentry; struct ionic_dev *idev = &lif->ionic->idev; + struct dentry *intr_dentry, *stats_dentry; struct debugfs_regset32 *intr_ctrl_regset; struct ionic_intr_info *intr = &qcq->intr; struct debugfs_blob_wrapper *desc_blob; @@ -123,7 +123,7 @@ void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq) struct ionic_cq *cq = &qcq->cq; qcq_dentry = debugfs_create_dir(q->name, lif->dentry); - if (IS_ERR_OR_NULL(qcq_dentry)) + if (IS_ERR(qcq_dentry)) return; qcq->dentry = qcq_dentry; @@ -220,7 +220,7 @@ static int netdev_show(struct seq_file *seq, void *v) { struct net_device *netdev = seq->private; - seq_printf(seq, "%s\n", netdev->name); + seq_printf(seq, "%s\n", netdev_name(netdev)); return 0; } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 746072b4dbd0..57edcde9e6f8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -43,11 +43,99 @@ static void ionic_watchdog_cb(struct timer_list *t) work->type = IONIC_DW_TYPE_RX_MODE; netdev_dbg(lif->netdev, "deferred: rx_mode\n"); - ionic_lif_deferred_enqueue(&lif->deferred, work); + ionic_lif_deferred_enqueue(lif, work); } } -static void ionic_watchdog_init(struct ionic *ionic) +static void ionic_napi_schedule_do_softirq(struct napi_struct *napi) +{ + local_bh_disable(); + napi_schedule(napi); + local_bh_enable(); +} + +void ionic_doorbell_napi_work(struct work_struct *work) +{ + struct ionic_qcq *qcq = container_of(work, struct ionic_qcq, + doorbell_napi_work); + unsigned long now, then, dif; + + now = READ_ONCE(jiffies); + then = qcq->q.dbell_jiffies; + dif = now - then; + + if (dif > qcq->q.dbell_deadline) + ionic_napi_schedule_do_softirq(&qcq->napi); +} + +static int ionic_get_preferred_cpu(struct ionic *ionic, + struct ionic_intr_info *intr) +{ + int cpu; + + cpu = cpumask_first_and(*intr->affinity_mask, cpu_online_mask); + if (cpu >= nr_cpu_ids) + cpu = cpumask_local_spread(0, dev_to_node(ionic->dev)); + + return cpu; +} + +static void ionic_queue_dbell_napi_work(struct ionic *ionic, + struct ionic_qcq *qcq) +{ + int cpu; + + if (!(qcq->flags & IONIC_QCQ_F_INTR)) + return; + + cpu = ionic_get_preferred_cpu(ionic, &qcq->intr); + queue_work_on(cpu, ionic->wq, &qcq->doorbell_napi_work); +} + +static void ionic_doorbell_check_dwork(struct work_struct *work) +{ + struct ionic *ionic = container_of(work, struct ionic, + doorbell_check_dwork.work); + struct ionic_lif *lif = ionic->lif; + + mutex_lock(&lif->queue_lock); + + if (test_bit(IONIC_LIF_F_FW_STOPPING, lif->state) || + test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { + mutex_unlock(&lif->queue_lock); + return; + } + + ionic_napi_schedule_do_softirq(&lif->adminqcq->napi); + + if (test_bit(IONIC_LIF_F_UP, lif->state)) { + int i; + + for (i = 0; i < lif->nxqs; i++) { + ionic_queue_dbell_napi_work(ionic, lif->txqcqs[i]); + ionic_queue_dbell_napi_work(ionic, lif->rxqcqs[i]); + } + + if (lif->hwstamp_txq && + lif->hwstamp_txq->flags & IONIC_QCQ_F_INTR) + ionic_napi_schedule_do_softirq(&lif->hwstamp_txq->napi); + if (lif->hwstamp_rxq && + lif->hwstamp_rxq->flags & IONIC_QCQ_F_INTR) + ionic_napi_schedule_do_softirq(&lif->hwstamp_rxq->napi); + } + mutex_unlock(&lif->queue_lock); + + ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE); +} + +bool ionic_doorbell_wa(struct ionic *ionic) +{ + u8 asic_type = ionic->idev.dev_info.asic_type; + + return !asic_type || asic_type == IONIC_ASIC_TYPE_ELBA; +} + +static int ionic_watchdog_init(struct ionic *ionic) { struct ionic_dev *idev = &ionic->idev; @@ -63,6 +151,31 @@ static void ionic_watchdog_init(struct ionic *ionic) idev->fw_status_ready = true; idev->fw_generation = IONIC_FW_STS_F_GENERATION & ioread8(&idev->dev_info_regs->fw_status); + + ionic->wq = alloc_workqueue("%s-wq", WQ_UNBOUND, 0, + dev_name(ionic->dev)); + if (!ionic->wq) { + dev_err(ionic->dev, "alloc_workqueue failed"); + return -ENOMEM; + } + + if (ionic_doorbell_wa(ionic)) + INIT_DELAYED_WORK(&ionic->doorbell_check_dwork, + ionic_doorbell_check_dwork); + + return 0; +} + +void ionic_queue_doorbell_check(struct ionic *ionic, int delay) +{ + int cpu; + + if (!ionic->lif->doorbell_wa) + return; + + cpu = ionic_get_preferred_cpu(ionic, &ionic->lif->adminqcq->intr); + queue_delayed_work_on(cpu, ionic->wq, &ionic->doorbell_check_dwork, + delay); } void ionic_init_devinfo(struct ionic *ionic) @@ -94,6 +207,7 @@ int ionic_dev_setup(struct ionic *ionic) struct device *dev = ionic->dev; int size; u32 sig; + int err; /* BAR0: dev_cmd and interrupts */ if (num_bars < 1) { @@ -129,7 +243,9 @@ int ionic_dev_setup(struct ionic *ionic) return -EFAULT; } - ionic_watchdog_init(ionic); + err = ionic_watchdog_init(ionic); + if (err) + return err; idev->db_pages = bar->vaddr; idev->phy_db_pages = bar->bus_addr; @@ -161,6 +277,10 @@ void ionic_dev_teardown(struct ionic *ionic) idev->phy_cmb_pages = 0; idev->cmb_npages = 0; + if (ionic->wq) { + destroy_workqueue(ionic->wq); + ionic->wq = NULL; + } mutex_destroy(&idev->cmb_inuse_lock); } @@ -273,7 +393,7 @@ do_check_time: if (work) { work->type = IONIC_DW_TYPE_LIF_RESET; work->fw_status = fw_status_ready; - ionic_lif_deferred_enqueue(&lif->deferred, work); + ionic_lif_deferred_enqueue(lif, work); } } } @@ -629,43 +749,25 @@ int ionic_cq_init(struct ionic_lif *lif, struct ionic_cq *cq, cq->desc_size = desc_size; cq->tail_idx = 0; cq->done_color = 1; + cq->idev = &lif->ionic->idev; return 0; } -void ionic_cq_map(struct ionic_cq *cq, void *base, dma_addr_t base_pa) -{ - struct ionic_cq_info *cur; - unsigned int i; - - cq->base = base; - cq->base_pa = base_pa; - - for (i = 0, cur = cq->info; i < cq->num_descs; i++, cur++) - cur->cq_desc = base + (i * cq->desc_size); -} - -void ionic_cq_bind(struct ionic_cq *cq, struct ionic_queue *q) -{ - cq->bound_q = q; -} - unsigned int ionic_cq_service(struct ionic_cq *cq, unsigned int work_to_do, ionic_cq_cb cb, ionic_cq_done_cb done_cb, void *done_arg) { - struct ionic_cq_info *cq_info; unsigned int work_done = 0; if (work_to_do == 0) return 0; - cq_info = &cq->info[cq->tail_idx]; - while (cb(cq, cq_info)) { + while (cb(cq)) { if (cq->tail_idx == cq->num_descs - 1) cq->done_color = !cq->done_color; + cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1); - cq_info = &cq->info[cq->tail_idx]; if (++work_done >= work_to_do) break; @@ -692,7 +794,6 @@ int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev, return -EINVAL; q->lif = lif; - q->idev = idev; q->index = index; q->num_descs = num_descs; q->desc_size = desc_size; @@ -706,53 +807,11 @@ int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev, return 0; } -void ionic_q_map(struct ionic_queue *q, void *base, dma_addr_t base_pa) +void ionic_q_post(struct ionic_queue *q, bool ring_doorbell) { - struct ionic_desc_info *cur; - unsigned int i; - - q->base = base; - q->base_pa = base_pa; - - for (i = 0, cur = q->info; i < q->num_descs; i++, cur++) - cur->desc = base + (i * q->desc_size); -} - -void ionic_q_cmb_map(struct ionic_queue *q, void __iomem *base, dma_addr_t base_pa) -{ - struct ionic_desc_info *cur; - unsigned int i; - - q->cmb_base = base; - q->cmb_base_pa = base_pa; - - for (i = 0, cur = q->info; i < q->num_descs; i++, cur++) - cur->cmb_desc = base + (i * q->desc_size); -} - -void ionic_q_sg_map(struct ionic_queue *q, void *base, dma_addr_t base_pa) -{ - struct ionic_desc_info *cur; - unsigned int i; - - q->sg_base = base; - q->sg_base_pa = base_pa; - - for (i = 0, cur = q->info; i < q->num_descs; i++, cur++) - cur->sg_desc = base + (i * q->sg_desc_size); -} - -void ionic_q_post(struct ionic_queue *q, bool ring_doorbell, ionic_desc_cb cb, - void *cb_arg) -{ - struct ionic_desc_info *desc_info; struct ionic_lif *lif = q->lif; struct device *dev = q->dev; - desc_info = &q->info[q->head_idx]; - desc_info->cb = cb; - desc_info->cb_arg = cb_arg; - q->head_idx = (q->head_idx + 1) & (q->num_descs - 1); dev_dbg(dev, "lif=%d qname=%s qid=%d qtype=%d p_index=%d ringdb=%d\n", @@ -764,14 +823,10 @@ void ionic_q_post(struct ionic_queue *q, bool ring_doorbell, ionic_desc_cb cb, q->dbval | q->head_idx); q->dbell_jiffies = jiffies; - - if (q_to_qcq(q)->napi_qcq) - mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline, - jiffies + IONIC_NAPI_DEADLINE); } } -static bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos) +bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos) { unsigned int mask, tail, head; @@ -781,37 +836,3 @@ static bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos) return ((pos - tail) & mask) < ((head - tail) & mask); } - -void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info, - unsigned int stop_index) -{ - struct ionic_desc_info *desc_info; - ionic_desc_cb cb; - void *cb_arg; - u16 index; - - /* check for empty queue */ - if (q->tail_idx == q->head_idx) - return; - - /* stop index must be for a descriptor that is not yet completed */ - if (unlikely(!ionic_q_is_posted(q, stop_index))) - dev_err(q->dev, - "ionic stop is not posted %s stop %u tail %u head %u\n", - q->name, stop_index, q->tail_idx, q->head_idx); - - do { - desc_info = &q->info[q->tail_idx]; - index = q->tail_idx; - q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); - - cb = desc_info->cb; - cb_arg = desc_info->cb_arg; - - desc_info->cb = NULL; - desc_info->cb_arg = NULL; - - if (cb) - cb(q, desc_info, cq_info, cb_arg); - } while (index != stop_index); -} diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 2667e1cde16b..c8c710cfe70c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -8,6 +8,7 @@ #include <linux/mutex.h> #include <linux/workqueue.h> #include <linux/skbuff.h> +#include <linux/bpf_trace.h> #include "ionic_if.h" #include "ionic_regs.h" @@ -15,9 +16,10 @@ #define IONIC_MAX_TX_DESC 8192 #define IONIC_MAX_RX_DESC 16384 #define IONIC_MIN_TXRX_DESC 64 -#define IONIC_DEF_TXRX_DESC 4096 +#define IONIC_DEF_TXRX_DESC 1024 #define IONIC_RX_FILL_THRESHOLD 16 #define IONIC_RX_FILL_DIV 8 +#define IONIC_TSO_DESCS_NEEDED 44 /* 64K TSO @1500B */ #define IONIC_LIFS_MAX 1024 #define IONIC_WATCHDOG_SECS 5 #define IONIC_ITR_COAL_USEC_DEFAULT 64 @@ -26,11 +28,11 @@ #define IONIC_DEV_INFO_REG_COUNT 32 #define IONIC_DEV_CMD_REG_COUNT 32 -#define IONIC_NAPI_DEADLINE (HZ / 200) /* 5ms */ +#define IONIC_NAPI_DEADLINE (HZ) /* 1 sec */ #define IONIC_ADMIN_DOORBELL_DEADLINE (HZ / 2) /* 500ms */ #define IONIC_TX_DOORBELL_DEADLINE (HZ / 100) /* 10ms */ #define IONIC_RX_MIN_DOORBELL_DEADLINE (HZ / 100) /* 10ms */ -#define IONIC_RX_MAX_DOORBELL_DEADLINE (HZ * 5) /* 5s */ +#define IONIC_RX_MAX_DOORBELL_DEADLINE (HZ * 4) /* 4s */ struct ionic_dev_bar { void __iomem *vaddr; @@ -120,11 +122,13 @@ static_assert(sizeof(struct ionic_log_event) == 64); /* I/O */ static_assert(sizeof(struct ionic_txq_desc) == 16); static_assert(sizeof(struct ionic_txq_sg_desc) == 128); +static_assert(sizeof(struct ionic_txq_sg_desc_v1) == 256); static_assert(sizeof(struct ionic_txq_comp) == 16); static_assert(sizeof(struct ionic_rxq_desc) == 16); static_assert(sizeof(struct ionic_rxq_sg_desc) == 128); static_assert(sizeof(struct ionic_rxq_comp) == 16); +static_assert(sizeof(struct ionic_rxq_comp) == sizeof(struct ionic_txq_comp)); /* SR/IOV */ static_assert(sizeof(struct ionic_vf_setattr_cmd) == 64); @@ -173,27 +177,16 @@ struct ionic_dev { struct ionic_devinfo dev_info; }; -struct ionic_cq_info { - union { - void *cq_desc; - struct ionic_admin_comp *admincq; - struct ionic_notifyq_event *notifyq; - }; -}; - struct ionic_queue; struct ionic_qcq; -struct ionic_desc_info; - -typedef void (*ionic_desc_cb)(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_cq_info *cq_info, void *cb_arg); #define IONIC_MAX_BUF_LEN ((u16)-1) -#define IONIC_PAGE_SIZE PAGE_SIZE -#define IONIC_PAGE_SPLIT_SZ (PAGE_SIZE / 2) -#define IONIC_PAGE_GFP_MASK (GFP_ATOMIC | __GFP_NOWARN |\ - __GFP_COMP | __GFP_MEMALLOC) +#define IONIC_PAGE_SIZE MIN(PAGE_SIZE, IONIC_MAX_BUF_LEN) + +#define IONIC_XDP_MAX_LINEAR_MTU (IONIC_PAGE_SIZE - \ + (VLAN_ETH_HLEN + \ + XDP_PACKET_HEADROOM + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))) struct ionic_buf_info { struct page *page; @@ -202,26 +195,25 @@ struct ionic_buf_info { u32 len; }; -#define IONIC_MAX_FRAGS (1 + IONIC_TX_MAX_SG_ELEMS_V1) +#define IONIC_TX_MAX_FRAGS (1 + IONIC_TX_MAX_SG_ELEMS_V1) +#define IONIC_RX_MAX_FRAGS (1 + IONIC_RX_MAX_SG_ELEMS) -struct ionic_desc_info { - union { - void *desc; - struct ionic_txq_desc *txq_desc; - struct ionic_rxq_desc *rxq_desc; - struct ionic_admin_cmd *adminq_desc; - }; - void __iomem *cmb_desc; - union { - void *sg_desc; - struct ionic_txq_sg_desc *txq_sg_desc; - struct ionic_rxq_sg_desc *rxq_sgl_desc; - }; +struct ionic_tx_desc_info { unsigned int bytes; unsigned int nbufs; + struct sk_buff *skb; + struct xdp_frame *xdpf; + enum xdp_action act; struct ionic_buf_info bufs[MAX_SKB_FRAGS + 1]; - ionic_desc_cb cb; - void *cb_arg; +}; + +struct ionic_rx_desc_info { + unsigned int nbufs; + struct ionic_buf_info bufs[IONIC_RX_MAX_FRAGS]; +}; + +struct ionic_admin_desc_info { + void *ctx; }; #define IONIC_QUEUE_NAME_MAX_SZ 16 @@ -229,7 +221,12 @@ struct ionic_desc_info { struct ionic_queue { struct device *dev; struct ionic_lif *lif; - struct ionic_desc_info *info; + union { + void *info; + struct ionic_tx_desc_info *tx_info; + struct ionic_rx_desc_info *rx_info; + struct ionic_admin_desc_info *admin_info; + }; u64 dbval; unsigned long dbell_deadline; unsigned long dbell_jiffies; @@ -238,27 +235,38 @@ struct ionic_queue { unsigned int index; unsigned int num_descs; unsigned int max_sg_elems; + u64 features; - u64 drop; - struct ionic_dev *idev; - unsigned int type; - unsigned int hw_index; unsigned int hw_type; + bool xdp_flush; union { void *base; struct ionic_txq_desc *txq; struct ionic_rxq_desc *rxq; struct ionic_admin_cmd *adminq; }; - void __iomem *cmb_base; union { void *sg_base; struct ionic_txq_sg_desc *txq_sgl; + struct ionic_txq_sg_desc_v1 *txq_sgl_v1; struct ionic_rxq_sg_desc *rxq_sgl; }; + struct xdp_rxq_info *xdp_rxq_info; + struct bpf_prog *xdp_prog; + struct page_pool *page_pool; + struct ionic_queue *partner; + + union { + void __iomem *cmb_base; + struct ionic_txq_desc __iomem *cmb_txq; + struct ionic_rxq_desc __iomem *cmb_rxq; + }; + unsigned int type; + unsigned int hw_index; dma_addr_t base_pa; dma_addr_t cmb_base_pa; dma_addr_t sg_base_pa; + u64 drop; unsigned int desc_size; unsigned int sg_desc_size; unsigned int pid; @@ -273,14 +281,13 @@ struct ionic_intr_info { u64 rearm_count; unsigned int index; unsigned int vector; - unsigned int cpu; u32 dim_coal_hw; - cpumask_t affinity_mask; + cpumask_var_t *affinity_mask; + struct irq_affinity_notify aff_notify; }; struct ionic_cq { struct ionic_lif *lif; - struct ionic_cq_info *info; struct ionic_queue *bound_q; struct ionic_intr_info *bound_intr; u16 tail_idx; @@ -289,6 +296,7 @@ struct ionic_cq { unsigned int desc_size; void *base; dma_addr_t base_pa; + struct ionic_dev *idev; } ____cacheline_aligned_in_smp; struct ionic; @@ -363,25 +371,26 @@ int ionic_cq_init(struct ionic_lif *lif, struct ionic_cq *cq, unsigned int num_descs, size_t desc_size); void ionic_cq_map(struct ionic_cq *cq, void *base, dma_addr_t base_pa); void ionic_cq_bind(struct ionic_cq *cq, struct ionic_queue *q); -typedef bool (*ionic_cq_cb)(struct ionic_cq *cq, struct ionic_cq_info *cq_info); +typedef bool (*ionic_cq_cb)(struct ionic_cq *cq); typedef void (*ionic_cq_done_cb)(void *done_arg); unsigned int ionic_cq_service(struct ionic_cq *cq, unsigned int work_to_do, ionic_cq_cb cb, ionic_cq_done_cb done_cb, void *done_arg); +unsigned int ionic_tx_cq_service(struct ionic_cq *cq, + unsigned int work_to_do, + bool in_napi); int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev, struct ionic_queue *q, unsigned int index, const char *name, unsigned int num_descs, size_t desc_size, size_t sg_desc_size, unsigned int pid); -void ionic_q_map(struct ionic_queue *q, void *base, dma_addr_t base_pa); -void ionic_q_cmb_map(struct ionic_queue *q, void __iomem *base, dma_addr_t base_pa); -void ionic_q_sg_map(struct ionic_queue *q, void *base, dma_addr_t base_pa); -void ionic_q_post(struct ionic_queue *q, bool ring_doorbell, ionic_desc_cb cb, - void *cb_arg); -void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info, - unsigned int stop_index); +void ionic_q_post(struct ionic_queue *q, bool ring_doorbell); +bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos); + int ionic_heartbeat_check(struct ionic *ionic); bool ionic_is_fw_running(struct ionic_dev *idev); +void ionic_doorbell_napi_work(struct work_struct *work); +void ionic_queue_doorbell_check(struct ionic *ionic, int delay); bool ionic_adminq_poke_doorbell(struct ionic_queue *q); bool ionic_txq_poke_doorbell(struct ionic_queue *q); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 0ffc9c4904ac..a2d4336d2766 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -11,6 +11,8 @@ #include "ionic_ethtool.h" #include "ionic_stats.h" +#define IONIC_MAX_RX_COPYBREAK min(U16_MAX, IONIC_MAX_BUF_LEN) + static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf) { u32 i; @@ -156,6 +158,20 @@ static int ionic_get_link_ksettings(struct net_device *netdev, 25000baseCR_Full); copper_seen++; break; + case IONIC_XCVR_PID_QSFP_50G_CR2_FC: + case IONIC_XCVR_PID_QSFP_50G_CR2: + ethtool_link_ksettings_add_link_mode(ks, supported, + 50000baseCR2_Full); + copper_seen++; + break; + case IONIC_XCVR_PID_QSFP_200G_CR4: + ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseCR4_Full); + copper_seen++; + break; + case IONIC_XCVR_PID_QSFP_400G_CR4: + ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseCR4_Full); + copper_seen++; + break; case IONIC_XCVR_PID_SFP_10GBASE_AOC: case IONIC_XCVR_PID_SFP_10GBASE_CU: ethtool_link_ksettings_add_link_mode(ks, supported, @@ -194,6 +210,31 @@ static int ionic_get_link_ksettings(struct net_device *netdev, ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseSR_Full); break; + case IONIC_XCVR_PID_QSFP_200G_AOC: + case IONIC_XCVR_PID_QSFP_200G_SR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 200000baseSR4_Full); + break; + case IONIC_XCVR_PID_QSFP_200G_FR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 200000baseLR4_ER4_FR4_Full); + break; + case IONIC_XCVR_PID_QSFP_200G_DR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 200000baseDR4_Full); + break; + case IONIC_XCVR_PID_QSFP_400G_FR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 400000baseLR4_ER4_FR4_Full); + break; + case IONIC_XCVR_PID_QSFP_400G_DR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 400000baseDR4_Full); + break; + case IONIC_XCVR_PID_QSFP_400G_SR4: + ethtool_link_ksettings_add_link_mode(ks, supported, + 400000baseSR4_Full); + break; case IONIC_XCVR_PID_SFP_10GBASE_SR: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); @@ -726,6 +767,11 @@ static int ionic_set_channels(struct net_device *netdev, ionic_init_queue_params(lif, &qparam); + if ((ch->rx_count || ch->tx_count) && lif->xdp_prog) { + netdev_info(lif->netdev, "Split Tx/Rx interrupts not available when using XDP\n"); + return -EOPNOTSUPP; + } + if (ch->rx_count != ch->tx_count) { netdev_info(netdev, "The rx and tx count must be equal\n"); return -EINVAL; @@ -867,10 +913,17 @@ static int ionic_set_tunable(struct net_device *dev, const void *data) { struct ionic_lif *lif = netdev_priv(dev); + u32 rx_copybreak; switch (tuna->id) { case ETHTOOL_RX_COPYBREAK: - lif->rx_copybreak = *(u32 *)data; + rx_copybreak = *(u32 *)data; + if (rx_copybreak > IONIC_MAX_RX_COPYBREAK) { + netdev_err(dev, "Max supported rx_copybreak size: %u\n", + IONIC_MAX_RX_COPYBREAK); + return -EINVAL; + } + lif->rx_copybreak = (u16)rx_copybreak; break; default: return -EOPNOTSUPP; @@ -915,6 +968,7 @@ static int ionic_get_module_info(struct net_device *netdev, break; case SFF8024_ID_QSFP_8436_8636: case SFF8024_ID_QSFP28_8636: + case SFF8024_ID_QSFP_PLUS_CMIS: modinfo->type = ETH_MODULE_SFF_8436; modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; break; @@ -947,8 +1001,8 @@ static int ionic_get_module_eeprom(struct net_device *netdev, len = min_t(u32, sizeof(xcvr->sprom), ee->len); do { - memcpy(data, xcvr->sprom, len); - memcpy(tbuf, xcvr->sprom, len); + memcpy(data, &xcvr->sprom[ee->offset], len); + memcpy(tbuf, &xcvr->sprom[ee->offset], len); /* Let's make sure we got a consistent copy */ if (!memcmp(data, tbuf, len)) @@ -963,7 +1017,7 @@ static int ionic_get_module_eeprom(struct net_device *netdev, } static int ionic_get_ts_info(struct net_device *netdev, - struct ethtool_ts_info *info) + struct kernel_ethtool_ts_info *info) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; @@ -975,8 +1029,6 @@ static int ionic_get_ts_info(struct net_device *netdev, info->phc_index = ptp_clock_index(lif->phc->ptp); info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h index 9a1825edf0d0..830c8adbfbee 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_if.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h @@ -71,7 +71,7 @@ enum ionic_cmd_opcode { IONIC_CMD_FW_CONTROL_V1 = 255, }; -/** +/* * enum ionic_status_code - Device command return codes */ enum ionic_status_code { @@ -112,6 +112,7 @@ enum ionic_notifyq_opcode { /** * struct ionic_admin_cmd - General admin command format * @opcode: Opcode for the command + * @rsvd: reserved byte(s) * @lif_index: LIF index * @cmd_data: Opcode-specific command bytes */ @@ -125,6 +126,7 @@ struct ionic_admin_cmd { /** * struct ionic_admin_comp - General admin command completion format * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @cmd_data: Command-specific bytes * @color: Color bit (Always 0 for commands issued to the @@ -147,6 +149,7 @@ static inline u8 color_match(u8 color, u8 done_color) /** * struct ionic_nop_cmd - NOP command * @opcode: opcode + * @rsvd: reserved byte(s) */ struct ionic_nop_cmd { u8 opcode; @@ -156,6 +159,7 @@ struct ionic_nop_cmd { /** * struct ionic_nop_comp - NOP command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) */ struct ionic_nop_comp { u8 status; @@ -166,6 +170,7 @@ struct ionic_nop_comp { * struct ionic_dev_init_cmd - Device init command * @opcode: opcode * @type: Device type + * @rsvd: reserved byte(s) */ struct ionic_dev_init_cmd { u8 opcode; @@ -176,6 +181,7 @@ struct ionic_dev_init_cmd { /** * struct ionic_dev_init_comp - Device init command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) */ struct ionic_dev_init_comp { u8 status; @@ -185,6 +191,7 @@ struct ionic_dev_init_comp { /** * struct ionic_dev_reset_cmd - Device reset command * @opcode: opcode + * @rsvd: reserved byte(s) */ struct ionic_dev_reset_cmd { u8 opcode; @@ -194,6 +201,7 @@ struct ionic_dev_reset_cmd { /** * struct ionic_dev_reset_comp - Reset command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) */ struct ionic_dev_reset_comp { u8 status; @@ -207,6 +215,7 @@ struct ionic_dev_reset_comp { * struct ionic_dev_identify_cmd - Driver/device identify command * @opcode: opcode * @ver: Highest version of identify supported by driver + * @rsvd: reserved byte(s) */ struct ionic_dev_identify_cmd { u8 opcode; @@ -218,6 +227,7 @@ struct ionic_dev_identify_cmd { * struct ionic_dev_identify_comp - Driver/device identify command completion * @status: Status of the command (enum ionic_status_code) * @ver: Version of identify returned by device + * @rsvd: reserved byte(s) */ struct ionic_dev_identify_comp { u8 status; @@ -242,6 +252,7 @@ enum ionic_os_type { * @kernel_ver: Kernel version, numeric format * @kernel_ver_str: Kernel version, string format * @driver_ver_str: Driver version, string format + * @words: word access to struct contents */ union ionic_drv_identity { struct { @@ -267,7 +278,9 @@ enum ionic_dev_capability { * union ionic_dev_identity - device identity information * @version: Version of device identify * @type: Identify type (0 for now) + * @rsvd: reserved byte(s) * @nports: Number of ports provisioned + * @rsvd2: reserved byte(s) * @nlifs: Number of LIFs provisioned * @nintrs: Number of interrupts provisioned * @ndbpgs_per_lif: Number of doorbell pages per LIF @@ -284,6 +297,7 @@ enum ionic_dev_capability { * @hwstamp_mult: Hardware tick to nanosecond multiplier. * @hwstamp_shift: Hardware tick to nanosecond divisor (power of two). * @capabilities: Device capabilities + * @words: word access to struct contents */ union ionic_dev_identity { struct { @@ -317,6 +331,7 @@ enum ionic_lif_type { * @opcode: opcode * @type: LIF type (enum ionic_lif_type) * @ver: Version of identify returned by device + * @rsvd: reserved byte(s) */ struct ionic_lif_identify_cmd { u8 opcode; @@ -329,6 +344,7 @@ struct ionic_lif_identify_cmd { * struct ionic_lif_identify_comp - LIF identify command completion * @status: Status of the command (enum ionic_status_code) * @ver: Version of identify returned by device + * @rsvd2: reserved byte(s) */ struct ionic_lif_identify_comp { u8 status; @@ -416,7 +432,7 @@ enum ionic_txq_feature { }; /** - * struct ionic_hwstamp_bits - Hardware timestamp decoding bits + * enum ionic_hwstamp_bits - Hardware timestamp decoding bits * @IONIC_HWSTAMP_INVALID: Invalid hardware timestamp value * @IONIC_HWSTAMP_CQ_NEGOFFSET: Timestamp field negative offset * from the base cq descriptor. @@ -429,6 +445,7 @@ enum ionic_hwstamp_bits { /** * struct ionic_lif_logical_qtype - Descriptor of logical to HW queue type * @qtype: Hardware Queue Type + * @rsvd: reserved byte(s) * @qid_count: Number of Queue IDs of the logical type * @qid_base: Minimum Queue ID of the logical type */ @@ -454,12 +471,14 @@ enum ionic_lif_state { /** * union ionic_lif_config - LIF configuration * @state: LIF state (enum ionic_lif_state) + * @rsvd: reserved byte(s) * @name: LIF name * @mtu: MTU * @mac: Station MAC address * @vlan: Default Vlan ID * @features: Features (enum ionic_eth_hw_features) * @queue_count: Queue counts per queue-type + * @words: word access to struct contents */ union ionic_lif_config { struct { @@ -481,33 +500,39 @@ union ionic_lif_config { * @capabilities: LIF capabilities * * @eth: Ethernet identify structure - * @version: Ethernet identify structure version - * @max_ucast_filters: Number of perfect unicast addresses supported - * @max_mcast_filters: Number of perfect multicast addresses supported - * @min_frame_size: Minimum size of frames to be sent - * @max_frame_size: Maximum size of frames to be sent - * @hwstamp_tx_modes: Bitmask of BIT_ULL(enum ionic_txstamp_mode) - * @hwstamp_rx_filters: Bitmask of enum ionic_pkt_class - * @config: LIF config struct with features, mtu, mac, q counts + * @eth.version: Ethernet identify structure version + * @eth.rsvd: reserved byte(s) + * @eth.max_ucast_filters: Number of perfect unicast addresses supported + * @eth.max_mcast_filters: Number of perfect multicast addresses supported + * @eth.min_frame_size: Minimum size of frames to be sent + * @eth.max_frame_size: Maximum size of frames to be sent + * @eth.rsvd2: reserved byte(s) + * @eth.hwstamp_tx_modes: Bitmask of BIT_ULL(enum ionic_txstamp_mode) + * @eth.hwstamp_rx_filters: Bitmask of enum ionic_pkt_class + * @eth.rsvd3: reserved byte(s) + * @eth.config: LIF config struct with features, mtu, mac, q counts * * @rdma: RDMA identify structure - * @version: RDMA version of opcodes and queue descriptors - * @qp_opcodes: Number of RDMA queue pair opcodes supported - * @admin_opcodes: Number of RDMA admin opcodes supported - * @npts_per_lif: Page table size per LIF - * @nmrs_per_lif: Number of memory regions per LIF - * @nahs_per_lif: Number of address handles per LIF - * @max_stride: Max work request stride - * @cl_stride: Cache line stride - * @pte_stride: Page table entry stride - * @rrq_stride: Remote RQ work request stride - * @rsq_stride: Remote SQ work request stride - * @dcqcn_profiles: Number of DCQCN profiles - * @aq_qtype: RDMA Admin Qtype - * @sq_qtype: RDMA Send Qtype - * @rq_qtype: RDMA Receive Qtype - * @cq_qtype: RDMA Completion Qtype - * @eq_qtype: RDMA Event Qtype + * @rdma.version: RDMA version of opcodes and queue descriptors + * @rdma.qp_opcodes: Number of RDMA queue pair opcodes supported + * @rdma.admin_opcodes: Number of RDMA admin opcodes supported + * @rdma.rsvd: reserved byte(s) + * @rdma.npts_per_lif: Page table size per LIF + * @rdma.nmrs_per_lif: Number of memory regions per LIF + * @rdma.nahs_per_lif: Number of address handles per LIF + * @rdma.max_stride: Max work request stride + * @rdma.cl_stride: Cache line stride + * @rdma.pte_stride: Page table entry stride + * @rdma.rrq_stride: Remote RQ work request stride + * @rdma.rsq_stride: Remote SQ work request stride + * @rdma.dcqcn_profiles: Number of DCQCN profiles + * @rdma.rsvd_dimensions: reserved byte(s) + * @rdma.aq_qtype: RDMA Admin Qtype + * @rdma.sq_qtype: RDMA Send Qtype + * @rdma.rq_qtype: RDMA Receive Qtype + * @rdma.cq_qtype: RDMA Completion Qtype + * @rdma.eq_qtype: RDMA Event Qtype + * @words: word access to struct contents */ union ionic_lif_identity { struct { @@ -558,7 +583,9 @@ union ionic_lif_identity { * @opcode: Opcode * @type: LIF type (enum ionic_lif_type) * @index: LIF index + * @rsvd: reserved byte(s) * @info_pa: Destination address for LIF info (struct ionic_lif_info) + * @rsvd2: reserved byte(s) */ struct ionic_lif_init_cmd { u8 opcode; @@ -572,7 +599,9 @@ struct ionic_lif_init_cmd { /** * struct ionic_lif_init_comp - LIF init command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @hw_index: Hardware index of the initialized LIF + * @rsvd2: reserved byte(s) */ struct ionic_lif_init_comp { u8 status; @@ -584,9 +613,11 @@ struct ionic_lif_init_comp { /** * struct ionic_q_identify_cmd - queue identify command * @opcode: opcode + * @rsvd: reserved byte(s) * @lif_type: LIF type (enum ionic_lif_type) * @type: Logical queue type (enum ionic_logical_qtype) * @ver: Highest queue type version that the driver supports + * @rsvd2: reserved byte(s) */ struct ionic_q_identify_cmd { u8 opcode; @@ -600,8 +631,10 @@ struct ionic_q_identify_cmd { /** * struct ionic_q_identify_comp - queue identify command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @ver: Queue type version that can be used with FW + * @rsvd2: reserved byte(s) */ struct ionic_q_identify_comp { u8 status; @@ -615,12 +648,14 @@ struct ionic_q_identify_comp { * union ionic_q_identity - queue identity information * @version: Queue type version that can be used with FW * @supported: Bitfield of queue versions, first bit = ver 0 + * @rsvd: reserved byte(s) * @features: Queue features (enum ionic_q_feature, etc) * @desc_sz: Descriptor size * @comp_sz: Completion descriptor size * @sg_desc_sz: Scatter/Gather descriptor size * @max_sg_elems: Maximum number of Scatter/Gather elements * @sg_desc_stride: Number of Scatter/Gather elements per descriptor + * @words: word access to struct contents */ union ionic_q_identity { struct { @@ -640,8 +675,10 @@ union ionic_q_identity { /** * struct ionic_q_init_cmd - Queue init command * @opcode: opcode + * @rsvd: reserved byte(s) * @type: Logical queue type * @ver: Queue type version + * @rsvd1: reserved byte(s) * @lif_index: LIF index * @index: (LIF, qtype) relative admin queue index * @intr_index: Interrupt control register index, or Event queue index @@ -667,6 +704,7 @@ union ionic_q_identity { * @ring_base: Queue ring base address * @cq_ring_base: Completion queue ring base address * @sg_ring_base: Scatter/Gather ring base address + * @rsvd2: reserved byte(s) * @features: Mask of queue features to enable, if not in the flags above. */ struct ionic_q_init_cmd { @@ -698,9 +736,11 @@ struct ionic_q_init_cmd { /** * struct ionic_q_init_comp - Queue init command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @hw_index: Hardware Queue ID * @hw_type: Hardware Queue type + * @rsvd2: reserved byte(s) * @color: Color */ struct ionic_q_init_comp { @@ -800,7 +840,7 @@ enum ionic_txq_desc_opcode { * will set CWR flag in the first segment if * CWR is set in the template header, and * clear CWR in remaining segments. - * @flags: + * flags: * vlan: * Insert an L2 VLAN header using @vlan_tci * encap: @@ -813,13 +853,14 @@ enum ionic_txq_desc_opcode { * TSO start * tso_eot: * TSO end - * @num_sg_elems: Number of scatter-gather elements in SG + * num_sg_elems: Number of scatter-gather elements in SG * descriptor - * @addr: First data buffer's DMA address + * addr: First data buffer's DMA address * (Subsequent data buffers are on txq_sg_desc) * @len: First data buffer's length, in bytes * @vlan_tci: VLAN tag to insert in the packet (if requested * by @V-bit). Includes .1p and .1q tags + * @hword0: half word padding * @hdr_len: Length of packet headers, including * encapsulating outer header, if applicable * Valid for opcodes IONIC_TXQ_DESC_OPCODE_CALC_CSUM and @@ -830,10 +871,12 @@ enum ionic_txq_desc_opcode { * IONIC_TXQ_DESC_OPCODE_TSO, @hdr_len is up to * inner-most L4 payload, so inclusive of * inner-most L4 header. + * @hword1: half word padding * @mss: Desired MSS value for TSO; only applicable for * IONIC_TXQ_DESC_OPCODE_TSO * @csum_start: Offset from packet to first byte checked in L4 checksum * @csum_offset: Offset from csum_start to L4 checksum field + * @hword2: half word padding */ struct ionic_txq_desc { __le64 cmd; @@ -901,6 +944,7 @@ static inline void decode_txq_desc_cmd(u64 cmd, u8 *opcode, u8 *flags, * struct ionic_txq_sg_elem - Transmit scatter-gather (SG) descriptor element * @addr: DMA address of SG element data buffer * @len: Length of SG element data buffer, in bytes + * @rsvd: reserved byte(s) */ struct ionic_txq_sg_elem { __le64 addr; @@ -927,7 +971,9 @@ struct ionic_txq_sg_desc_v1 { /** * struct ionic_txq_comp - Ethernet transmit queue completion descriptor * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_txq_comp { @@ -953,6 +999,7 @@ enum ionic_rxq_desc_opcode { * receive, including actual bytes received, * are recorded in Rx completion descriptor. * + * @rsvd: reserved byte(s) * @len: Data buffer's length, in bytes * @addr: Data buffer's DMA address */ @@ -967,6 +1014,7 @@ struct ionic_rxq_desc { * struct ionic_rxq_sg_elem - Receive scatter-gather (SG) descriptor element * @addr: DMA address of SG element data buffer * @len: Length of SG element data buffer, in bytes + * @rsvd: reserved byte(s) */ struct ionic_rxq_sg_elem { __le64 addr; @@ -1170,6 +1218,7 @@ enum ionic_pkt_class { * @lif_index: LIF index * @index: Queue index * @oper: Operation (enum ionic_q_control_oper) + * @rsvd: reserved byte(s) */ struct ionic_q_control_cmd { u8 opcode; @@ -1182,7 +1231,7 @@ struct ionic_q_control_cmd { typedef struct ionic_admin_comp ionic_q_control_comp; -enum q_control_oper { +enum ionic_q_control_oper { IONIC_Q_DISABLE = 0, IONIC_Q_ENABLE = 1, IONIC_Q_HANG_RESET = 2, @@ -1216,7 +1265,7 @@ enum ionic_xcvr_state { IONIC_XCVR_STATE_SPROM_READ_ERR = 4, }; -/** +/* * enum ionic_xcvr_pid - Supported link modes */ enum ionic_xcvr_pid { @@ -1228,7 +1277,10 @@ enum ionic_xcvr_pid { IONIC_XCVR_PID_SFP_25GBASE_CR_S = 3, IONIC_XCVR_PID_SFP_25GBASE_CR_L = 4, IONIC_XCVR_PID_SFP_25GBASE_CR_N = 5, - + IONIC_XCVR_PID_QSFP_50G_CR2_FC = 6, + IONIC_XCVR_PID_QSFP_50G_CR2 = 7, + IONIC_XCVR_PID_QSFP_200G_CR4 = 8, + IONIC_XCVR_PID_QSFP_400G_CR4 = 9, /* Fiber */ IONIC_XCVR_PID_QSFP_100G_AOC = 50, IONIC_XCVR_PID_QSFP_100G_ACC = 51, @@ -1254,6 +1306,15 @@ enum ionic_xcvr_pid { IONIC_XCVR_PID_SFP_25GBASE_ACC = 71, IONIC_XCVR_PID_SFP_10GBASE_T = 72, IONIC_XCVR_PID_SFP_1000BASE_T = 73, + IONIC_XCVR_PID_QSFP_200G_AOC = 74, + IONIC_XCVR_PID_QSFP_200G_FR4 = 75, + IONIC_XCVR_PID_QSFP_200G_DR4 = 76, + IONIC_XCVR_PID_QSFP_200G_SR4 = 77, + IONIC_XCVR_PID_QSFP_200G_ACC = 78, + IONIC_XCVR_PID_QSFP_400G_FR4 = 79, + IONIC_XCVR_PID_QSFP_400G_DR4 = 80, + IONIC_XCVR_PID_QSFP_400G_SR4 = 81, + IONIC_XCVR_PID_QSFP_400G_VR4 = 82, }; /** @@ -1351,9 +1412,12 @@ struct ionic_xcvr_status { * @fec_type: fec type (enum ionic_port_fec_type) * @pause_type: pause type (enum ionic_port_pause_type) * @loopback_mode: loopback mode (enum ionic_port_loopback_mode) + * @words: word access to struct contents */ union ionic_port_config { struct { +#define IONIC_SPEED_400G 400000 /* 400G in Mbps */ +#define IONIC_SPEED_200G 200000 /* 200G in Mbps */ #define IONIC_SPEED_100G 100000 /* 100G in Mbps */ #define IONIC_SPEED_50G 50000 /* 50G in Mbps */ #define IONIC_SPEED_40G 40000 /* 40G in Mbps */ @@ -1382,6 +1446,7 @@ union ionic_port_config { * @speed: link speed (in Mbps) * @link_down_count: number of times link went from up to down * @fec_type: fec type (enum ionic_port_fec_type) + * @rsvd: reserved byte(s) * @xcvr: transceiver status */ struct ionic_port_status { @@ -1399,6 +1464,7 @@ struct ionic_port_status { * @opcode: opcode * @index: port index * @ver: Highest version of identify supported by driver + * @rsvd: reserved byte(s) */ struct ionic_port_identify_cmd { u8 opcode; @@ -1411,6 +1477,7 @@ struct ionic_port_identify_cmd { * struct ionic_port_identify_comp - Port identify command completion * @status: Status of the command (enum ionic_status_code) * @ver: Version of identify returned by device + * @rsvd: reserved byte(s) */ struct ionic_port_identify_comp { u8 status; @@ -1422,7 +1489,9 @@ struct ionic_port_identify_comp { * struct ionic_port_init_cmd - Port initialization command * @opcode: opcode * @index: port index + * @rsvd: reserved byte(s) * @info_pa: destination address for port info (struct ionic_port_info) + * @rsvd2: reserved byte(s) */ struct ionic_port_init_cmd { u8 opcode; @@ -1435,6 +1504,7 @@ struct ionic_port_init_cmd { /** * struct ionic_port_init_comp - Port initialization command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) */ struct ionic_port_init_comp { u8 status; @@ -1445,6 +1515,7 @@ struct ionic_port_init_comp { * struct ionic_port_reset_cmd - Port reset command * @opcode: opcode * @index: port index + * @rsvd: reserved byte(s) */ struct ionic_port_reset_cmd { u8 opcode; @@ -1455,6 +1526,7 @@ struct ionic_port_reset_cmd { /** * struct ionic_port_reset_comp - Port reset command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) */ struct ionic_port_reset_comp { u8 status; @@ -1510,6 +1582,7 @@ enum ionic_port_attr { * @opcode: Opcode * @index: Port index * @attr: Attribute type (enum ionic_port_attr) + * @rsvd: reserved byte(s) * @state: Port state * @speed: Port speed * @mtu: Port MTU @@ -1518,6 +1591,7 @@ enum ionic_port_attr { * @pause_type: Port pause type setting * @loopback_mode: Port loopback mode * @stats_ctl: Port stats setting + * @rsvd2: reserved byte(s) */ struct ionic_port_setattr_cmd { u8 opcode; @@ -1540,6 +1614,7 @@ struct ionic_port_setattr_cmd { /** * struct ionic_port_setattr_comp - Port set attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @color: Color bit */ struct ionic_port_setattr_comp { @@ -1553,6 +1628,7 @@ struct ionic_port_setattr_comp { * @opcode: Opcode * @index: port index * @attr: Attribute type (enum ionic_port_attr) + * @rsvd: reserved byte(s) */ struct ionic_port_getattr_cmd { u8 opcode; @@ -1564,6 +1640,7 @@ struct ionic_port_getattr_cmd { /** * struct ionic_port_getattr_comp - Port get attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @state: Port state * @speed: Port speed * @mtu: Port MTU @@ -1571,6 +1648,7 @@ struct ionic_port_getattr_cmd { * @fec_type: Port FEC type setting * @pause_type: Port pause type setting * @loopback_mode: Port loopback mode + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_port_getattr_comp { @@ -1593,9 +1671,11 @@ struct ionic_port_getattr_comp { * struct ionic_lif_status - LIF status register * @eid: most recent NotifyQ event id * @port_num: port the LIF is connected to + * @rsvd: reserved byte(s) * @link_status: port status (enum ionic_port_oper_status) * @link_speed: speed of link in Mbps * @link_down_count: number of times link went from up to down + * @rsvd2: reserved byte(s) */ struct ionic_lif_status { __le64 eid; @@ -1610,7 +1690,9 @@ struct ionic_lif_status { /** * struct ionic_lif_reset_cmd - LIF reset command * @opcode: opcode + * @rsvd: reserved byte(s) * @index: LIF index + * @rsvd2: reserved byte(s) */ struct ionic_lif_reset_cmd { u8 opcode; @@ -1643,9 +1725,11 @@ enum ionic_dev_attr { * struct ionic_dev_setattr_cmd - Set Device attributes on the NIC * @opcode: Opcode * @attr: Attribute type (enum ionic_dev_attr) + * @rsvd: reserved byte(s) * @state: Device state (enum ionic_dev_state) * @name: The bus info, e.g. PCI slot-device-function, 0 terminated * @features: Device features + * @rsvd2: reserved byte(s) */ struct ionic_dev_setattr_cmd { u8 opcode; @@ -1662,7 +1746,9 @@ struct ionic_dev_setattr_cmd { /** * struct ionic_dev_setattr_comp - Device set attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @features: Device features + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_dev_setattr_comp { @@ -1679,6 +1765,7 @@ struct ionic_dev_setattr_comp { * struct ionic_dev_getattr_cmd - Get Device attributes from the NIC * @opcode: opcode * @attr: Attribute type (enum ionic_dev_attr) + * @rsvd: reserved byte(s) */ struct ionic_dev_getattr_cmd { u8 opcode; @@ -1687,9 +1774,11 @@ struct ionic_dev_getattr_cmd { }; /** - * struct ionic_dev_setattr_comp - Device set attr command completion + * struct ionic_dev_getattr_comp - Device set attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @features: Device features + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_dev_getattr_comp { @@ -1702,7 +1791,7 @@ struct ionic_dev_getattr_comp { u8 color; }; -/** +/* * RSS parameters */ #define IONIC_RSS_HASH_KEY_SIZE 40 @@ -1726,6 +1815,7 @@ enum ionic_rss_hash_types { * @IONIC_LIF_ATTR_RSS: LIF RSS attribute * @IONIC_LIF_ATTR_STATS_CTRL: LIF statistics control attribute * @IONIC_LIF_ATTR_TXSTAMP: LIF TX timestamping mode + * @IONIC_LIF_ATTR_MAX: maximum attribute value */ enum ionic_lif_attr { IONIC_LIF_ATTR_STATE = 0, @@ -1736,6 +1826,7 @@ enum ionic_lif_attr { IONIC_LIF_ATTR_RSS = 5, IONIC_LIF_ATTR_STATS_CTRL = 6, IONIC_LIF_ATTR_TXSTAMP = 7, + IONIC_LIF_ATTR_MAX = 255, }; /** @@ -1749,11 +1840,13 @@ enum ionic_lif_attr { * @mac: Station mac * @features: Features (enum ionic_eth_hw_features) * @rss: RSS properties - * @types: The hash types to enable (see rss_hash_types) - * @key: The hash secret key - * @addr: Address for the indirection table shared memory + * @rss.types: The hash types to enable (see rss_hash_types) + * @rss.key: The hash secret key + * @rss.rsvd: reserved byte(s) + * @rss.addr: Address for the indirection table shared memory * @stats_ctl: stats control commands (enum ionic_stats_ctl_cmd) - * @txstamp: TX Timestamping Mode (enum ionic_txstamp_mode) + * @txstamp_mode: TX Timestamping Mode (enum ionic_txstamp_mode) + * @rsvd: reserved byte(s) */ struct ionic_lif_setattr_cmd { u8 opcode; @@ -1772,7 +1865,7 @@ struct ionic_lif_setattr_cmd { __le64 addr; } rss; u8 stats_ctl; - __le16 txstamp_mode; + __le16 txstamp_mode; u8 rsvd[60]; } __packed; }; @@ -1780,8 +1873,10 @@ struct ionic_lif_setattr_cmd { /** * struct ionic_lif_setattr_comp - LIF set attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @features: features (enum ionic_eth_hw_features) + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_lif_setattr_comp { @@ -1800,6 +1895,7 @@ struct ionic_lif_setattr_comp { * @opcode: Opcode * @attr: Attribute type (enum ionic_lif_attr) * @index: LIF index + * @rsvd: reserved byte(s) */ struct ionic_lif_getattr_cmd { u8 opcode; @@ -1811,13 +1907,14 @@ struct ionic_lif_getattr_cmd { /** * struct ionic_lif_getattr_comp - LIF get attr command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @state: LIF state (enum ionic_lif_state) - * @name: The netdev name string, 0 terminated * @mtu: Mtu * @mac: Station mac * @features: Features (enum ionic_eth_hw_features) - * @txstamp: TX Timestamping Mode (enum ionic_txstamp_mode) + * @txstamp_mode: TX Timestamping Mode (enum ionic_txstamp_mode) + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_lif_getattr_comp { @@ -1838,12 +1935,15 @@ struct ionic_lif_getattr_comp { /** * struct ionic_lif_setphc_cmd - Set LIF PTP Hardware Clock * @opcode: Opcode + * @rsvd1: reserved byte(s) * @lif_index: LIF index + * @rsvd2: reserved byte(s) * @tick: Hardware stamp tick of an instant in time. * @nsec: Nanosecond stamp of the same instant. * @frac: Fractional nanoseconds at the same instant. * @mult: Cycle to nanosecond multiplier. * @shift: Cycle to nanosecond divisor (power of two). + * @rsvd3: reserved byte(s) */ struct ionic_lif_setphc_cmd { u8 opcode; @@ -1870,6 +1970,7 @@ enum ionic_rx_mode { /** * struct ionic_rx_mode_set_cmd - Set LIF's Rx mode command * @opcode: opcode + * @rsvd: reserved byte(s) * @lif_index: LIF index * @rx_mode: Rx mode flags: * IONIC_RX_MODE_F_UNICAST: Accept known unicast packets @@ -1878,6 +1979,7 @@ enum ionic_rx_mode { * IONIC_RX_MODE_F_PROMISC: Accept any packets * IONIC_RX_MODE_F_ALLMULTI: Accept any multicast packets * IONIC_RX_MODE_F_RDMA_SNIFFER: Sniff RDMA packets + * @rsvd2: reserved byte(s) */ struct ionic_rx_mode_set_cmd { u8 opcode; @@ -1904,13 +2006,14 @@ enum ionic_rx_filter_match_type { * @qid: Queue ID * @match: Rx filter match type (see IONIC_RX_FILTER_MATCH_xxx) * @vlan: VLAN filter - * @vlan: VLAN ID + * @vlan.vlan: VLAN ID * @mac: MAC filter - * @addr: MAC address (network-byte order) + * @mac.addr: MAC address (network-byte order) * @mac_vlan: MACVLAN filter - * @vlan: VLAN ID - * @addr: MAC address (network-byte order) + * @mac_vlan.vlan: VLAN ID + * @mac_vlan.addr: MAC address (network-byte order) * @pkt_class: Packet classification filter + * @rsvd: reserved byte(s) */ struct ionic_rx_filter_add_cmd { u8 opcode; @@ -1937,8 +2040,10 @@ struct ionic_rx_filter_add_cmd { /** * struct ionic_rx_filter_add_comp - Add LIF Rx filter command completion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @filter_id: Filter ID + * @rsvd2: reserved byte(s) * @color: Color bit */ struct ionic_rx_filter_add_comp { @@ -1953,8 +2058,10 @@ struct ionic_rx_filter_add_comp { /** * struct ionic_rx_filter_del_cmd - Delete LIF Rx filter command * @opcode: opcode + * @rsvd: reserved byte(s) * @lif_index: LIF index * @filter_id: Filter ID + * @rsvd2: reserved byte(s) */ struct ionic_rx_filter_del_cmd { u8 opcode; @@ -2000,6 +2107,7 @@ enum ionic_vf_link_status { * @trust: enable VF trust * @linkstate: set link up or down * @stats_pa: set DMA address for VF stats + * @pad: reserved byte(s) */ struct ionic_vf_setattr_cmd { u8 opcode; @@ -2031,6 +2139,7 @@ struct ionic_vf_setattr_comp { * @opcode: Opcode * @attr: Attribute type (enum ionic_vf_attr) * @vf_index: VF index + * @rsvd: reserved byte(s) */ struct ionic_vf_getattr_cmd { u8 opcode; @@ -2064,8 +2173,8 @@ enum ionic_vf_ctrl_opcode { /** * struct ionic_vf_ctrl_cmd - VF control command * @opcode: Opcode for the command - * @vf_index: VF Index. It is unused if op START_ALL is used. * @ctrl_opcode: VF control operation type + * @vf_index: VF Index. It is unused if op START_ALL is used. */ struct ionic_vf_ctrl_cmd { u8 opcode; @@ -2089,7 +2198,7 @@ struct ionic_vf_ctrl_comp { * struct ionic_qos_identify_cmd - QoS identify command * @opcode: opcode * @ver: Highest version of identify supported by driver - * + * @rsvd: reserved byte(s) */ struct ionic_qos_identify_cmd { u8 opcode; @@ -2101,6 +2210,7 @@ struct ionic_qos_identify_cmd { * struct ionic_qos_identify_comp - QoS identify command completion * @status: Status of the command (enum ionic_status_code) * @ver: Version of identify returned by device + * @rsvd: reserved byte(s) */ struct ionic_qos_identify_comp { u8 status; @@ -2118,7 +2228,7 @@ struct ionic_qos_identify_comp { #define IONIC_QOS_ALL_PCP 0xFF #define IONIC_DSCP_BLOCK_SIZE 8 -/** +/* * enum ionic_qos_class */ enum ionic_qos_class { @@ -2174,6 +2284,7 @@ enum ionic_qos_sched_type { * @dot1q_pcp: Dot1q pcp value * @ndscp: Number of valid dscp values in the ip_dscp field * @ip_dscp: IP dscp values + * @words: word access to struct contents */ union ionic_qos_config { struct { @@ -2219,8 +2330,9 @@ union ionic_qos_config { * union ionic_qos_identity - QoS identity structure * @version: Version of the identify structure * @type: QoS system type - * @nclasses: Number of usable QoS classes + * @rsvd: reserved byte(s) * @config: Current configuration of classes + * @words: word access to struct contents */ union ionic_qos_identity { struct { @@ -2236,7 +2348,9 @@ union ionic_qos_identity { * struct ionic_qos_init_cmd - QoS config init command * @opcode: Opcode * @group: QoS class id + * @rsvd: reserved byte(s) * @info_pa: destination address for qos info + * @rsvd1: reserved byte(s) */ struct ionic_qos_init_cmd { u8 opcode; @@ -2252,6 +2366,7 @@ typedef struct ionic_admin_comp ionic_qos_init_comp; * struct ionic_qos_reset_cmd - QoS config reset command * @opcode: Opcode * @group: QoS class id + * @rsvd: reserved byte(s) */ struct ionic_qos_reset_cmd { u8 opcode; @@ -2260,8 +2375,10 @@ struct ionic_qos_reset_cmd { }; /** - * struct ionic_qos_clear_port_stats_cmd - Qos config reset command + * struct ionic_qos_clear_stats_cmd - Qos config reset command * @opcode: Opcode + * @group_bitmap: bitmap of groups to be cleared + * @rsvd: reserved byte(s) */ struct ionic_qos_clear_stats_cmd { u8 opcode; @@ -2274,6 +2391,7 @@ typedef struct ionic_admin_comp ionic_qos_reset_comp; /** * struct ionic_fw_download_cmd - Firmware download command * @opcode: opcode + * @rsvd: reserved byte(s) * @addr: dma address of the firmware buffer * @offset: offset of the firmware buffer within the full image * @length: number of valid bytes in the firmware buffer @@ -2297,6 +2415,7 @@ typedef struct ionic_admin_comp ionic_fw_download_comp; * @IONIC_FW_INSTALL_STATUS: Firmware installation status * @IONIC_FW_ACTIVATE_ASYNC: Activate firmware asynchronously * @IONIC_FW_ACTIVATE_STATUS: Firmware activate status + * @IONIC_FW_UPDATE_CLEANUP: Clean up after an interrupted fw update */ enum ionic_fw_control_oper { IONIC_FW_RESET = 0, @@ -2312,8 +2431,10 @@ enum ionic_fw_control_oper { /** * struct ionic_fw_control_cmd - Firmware control command * @opcode: opcode + * @rsvd: reserved byte(s) * @oper: firmware control operation (enum ionic_fw_control_oper) * @slot: slot to activate + * @rsvd1: reserved byte(s) */ struct ionic_fw_control_cmd { u8 opcode; @@ -2326,8 +2447,10 @@ struct ionic_fw_control_cmd { /** * struct ionic_fw_control_comp - Firmware control copletion * @status: Status of the command (enum ionic_status_code) + * @rsvd: reserved byte(s) * @comp_index: Index in the descriptor ring for which this is the completion * @slot: Slot where the firmware was installed + * @rsvd1: reserved byte(s) * @color: Color bit */ struct ionic_fw_control_comp { @@ -2346,7 +2469,9 @@ struct ionic_fw_control_comp { /** * struct ionic_rdma_reset_cmd - Reset RDMA LIF cmd * @opcode: opcode + * @rsvd: reserved byte(s) * @lif_index: LIF index + * @rsvd2: reserved byte(s) * * There is no RDMA specific dev command completion struct. Completion uses * the common struct ionic_admin_comp. Only the status is indicated. @@ -2362,6 +2487,7 @@ struct ionic_rdma_reset_cmd { /** * struct ionic_rdma_queue_cmd - Create RDMA Queue command * @opcode: opcode, 52, 53 + * @rsvd: reserved byte(s) * @lif_index: LIF index * @qid_ver: (qid | (RDMA version << 24)) * @cid: intr, eq_id, or cq_id @@ -2369,6 +2495,7 @@ struct ionic_rdma_reset_cmd { * @depth_log2: log base two of queue depth * @stride_log2: log base two of queue stride * @dma_addr: address of the queue memory + * @rsvd2: reserved byte(s) * * The same command struct is used to create an RDMA event queue, completion * queue, or RDMA admin queue. The cid is an interrupt number for an event @@ -2425,6 +2552,7 @@ struct ionic_notifyq_event { * @ecode: event code = IONIC_EVENT_LINK_CHANGE * @link_status: link up/down, with error bits (enum ionic_port_status) * @link_speed: speed of the network link + * @rsvd: reserved byte(s) * * Sent when the network link state changes between UP and DOWN */ @@ -2442,6 +2570,7 @@ struct ionic_link_change_event { * @ecode: event code = IONIC_EVENT_RESET * @reset_code: reset type * @state: 0=pending, 1=complete, 2=error + * @rsvd: reserved byte(s) * * Sent when the NIC or some subsystem is going to be or * has been reset. @@ -2458,6 +2587,7 @@ struct ionic_reset_event { * struct ionic_heartbeat_event - Sent periodically by NIC to indicate health * @eid: event number * @ecode: event code = IONIC_EVENT_HEARTBEAT + * @rsvd: reserved byte(s) */ struct ionic_heartbeat_event { __le64 eid; @@ -2481,6 +2611,7 @@ struct ionic_log_event { * struct ionic_xcvr_event - Transceiver change event * @eid: event number * @ecode: event code = IONIC_EVENT_XCVR + * @rsvd: reserved byte(s) */ struct ionic_xcvr_event { __le64 eid; @@ -2488,7 +2619,7 @@ struct ionic_xcvr_event { u8 rsvd[54]; }; -/** +/* * struct ionic_port_stats - Port statistics structure */ struct ionic_port_stats { @@ -2646,8 +2777,7 @@ enum ionic_oflow_drop_stats { IONIC_OFLOW_DROP_MAX, }; -/** - * struct port_pb_stats - packet buffers system stats +/* struct ionic_port_pb_stats - packet buffers system stats * uses ionic_pb_buffer_drop_stats for drop_counts[] */ struct ionic_port_pb_stats { @@ -2681,7 +2811,9 @@ struct ionic_port_pb_stats { * @pause_type: supported pause types * @loopback_mode: supported loopback mode * @speeds: supported speeds + * @rsvd2: reserved byte(s) * @config: current port configuration + * @words: word access to struct contents */ union ionic_port_identity { struct { @@ -2707,7 +2839,8 @@ union ionic_port_identity { * @status: Port status data * @stats: Port statistics data * @mgmt_stats: Port management statistics data - * @port_pb_drop_stats: uplink pb drop stats + * @rsvd: reserved byte(s) + * @pb_stats: uplink pb drop stats */ struct ionic_port_info { union ionic_port_config config; @@ -2721,7 +2854,7 @@ struct ionic_port_info { struct ionic_port_pb_stats pb_stats; }; -/** +/* * struct ionic_lif_stats - LIF statistics structure */ struct ionic_lif_stats { @@ -2983,8 +3116,10 @@ struct ionic_hwstamp_regs { * bit 4-7 - 4 bit generation number, changes on fw restart * @fw_heartbeat: Firmware heartbeat counter * @serial_num: Serial number + * @rsvd_pad1024: reserved byte(s) * @fw_version: Firmware version - * @hwstamp_regs: Hardware current timestamp registers + * @hwstamp: Hardware current timestamp registers + * @words: word access to struct contents */ union ionic_dev_info_regs { #define IONIC_DEVINFO_FWVERS_BUFLEN 32 @@ -3014,7 +3149,9 @@ union ionic_dev_info_regs { * @done: Done indicator, bit 0 == 1 when command is complete * @cmd: Opcode-specific command bytes * @comp: Opcode-specific response bytes + * @rsvd: reserved byte(s) * @data: Opcode-specific side-data + * @words: word access to struct contents */ union ionic_dev_cmd_regs { struct { @@ -3032,6 +3169,7 @@ union ionic_dev_cmd_regs { * union ionic_dev_regs - Device register format for bar 0 page 0 * @info: Device info registers * @devcmd: Device command registers + * @words: word access to struct contents */ union ionic_dev_regs { struct { @@ -3085,7 +3223,11 @@ union ionic_adminq_comp { #define IONIC_BAR0_INTR_CTRL_OFFSET 0x2000 #define IONIC_DEV_CMD_DONE 0x00000001 -#define IONIC_ASIC_TYPE_CAPRI 0 +#define IONIC_ASIC_TYPE_NONE 0 +#define IONIC_ASIC_TYPE_CAPRI 1 +#define IONIC_ASIC_TYPE_ELBA 2 +#define IONIC_ASIC_TYPE_GIGLIO 3 +#define IONIC_ASIC_TYPE_SALINA 4 /** * struct ionic_doorbell - Doorbell register layout @@ -3098,6 +3240,7 @@ union ionic_adminq_comp { * interrupts when armed. * @qid_lo: Queue destination for the producer index and flags (low bits) * @qid_hi: Queue destination for the producer index and flags (high bits) + * @rsvd2: reserved byte(s) */ struct ionic_doorbell { __le16 p_index; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index fcb44ceeb6aa..7707a9e53c43 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -13,6 +13,7 @@ #include <linux/cpumask.h> #include <linux/crash_dump.h> #include <linux/vmalloc.h> +#include <net/page_pool/helpers.h> #include "ionic.h" #include "ionic_bus.h" @@ -46,18 +47,27 @@ static int ionic_start_queues(struct ionic_lif *lif); static void ionic_stop_queues(struct ionic_lif *lif); static void ionic_lif_queue_identify(struct ionic_lif *lif); +static void ionic_xdp_rxqs_prog_update(struct ionic_lif *lif); +static void ionic_unregister_rxq_info(struct ionic_queue *q); +static int ionic_register_rxq_info(struct ionic_queue *q, unsigned int napi_id); + static void ionic_dim_work(struct work_struct *work) { struct dim *dim = container_of(work, struct dim, work); - struct ionic_intr_info *intr; struct dim_cq_moder cur_moder; + struct ionic_intr_info *intr; struct ionic_qcq *qcq; struct ionic_lif *lif; + struct ionic_queue *q; u32 new_coal; - cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); qcq = container_of(dim, struct ionic_qcq, dim); - lif = qcq->q.lif; + q = &qcq->q; + if (q->type == IONIC_QTYPE_RXQ) + cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix); + else + cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix); + lif = q->lif; new_coal = ionic_coal_usec_to_hw(lif->ionic, cur_moder.usec); new_coal = new_coal ? new_coal : 1; @@ -118,13 +128,13 @@ static void ionic_lif_deferred_work(struct work_struct *work) } while (true); } -void ionic_lif_deferred_enqueue(struct ionic_deferred *def, +void ionic_lif_deferred_enqueue(struct ionic_lif *lif, struct ionic_deferred_work *work) { - spin_lock_bh(&def->lock); - list_add_tail(&work->list, &def->list); - spin_unlock_bh(&def->lock); - schedule_work(&def->work); + spin_lock_bh(&lif->deferred.lock); + list_add_tail(&work->list, &lif->deferred.list); + spin_unlock_bh(&lif->deferred.lock); + queue_work(lif->ionic->wq, &lif->deferred.work); } static void ionic_link_status_check(struct ionic_lif *lif) @@ -199,19 +209,12 @@ void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep) } work->type = IONIC_DW_TYPE_LINK_STATUS; - ionic_lif_deferred_enqueue(&lif->deferred, work); + ionic_lif_deferred_enqueue(lif, work); } else { ionic_link_status_check(lif); } } -static void ionic_napi_deadline(struct timer_list *timer) -{ - struct ionic_qcq *qcq = container_of(timer, struct ionic_qcq, napi_deadline); - - napi_schedule(&qcq->napi); -} - static irqreturn_t ionic_isr(int irq, void *data) { struct napi_struct *napi = data; @@ -229,12 +232,12 @@ static int ionic_request_irq(struct ionic_lif *lif, struct ionic_qcq *qcq) const char *name; if (lif->registered) - name = lif->netdev->name; + name = netdev_name(lif->netdev); else name = dev_name(dev); snprintf(intr->name, sizeof(intr->name), - "%s-%s-%s", IONIC_DRV_NAME, name, q->name); + "%.5s-%.16s-%.8s", IONIC_DRV_NAME, name, q->name); return devm_request_irq(dev, intr->vector, ionic_isr, 0, intr->name, &qcq->napi); @@ -264,6 +267,18 @@ static void ionic_intr_free(struct ionic *ionic, int index) clear_bit(index, ionic->intrs); } +static void ionic_irq_aff_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct ionic_intr_info *intr = container_of(notify, struct ionic_intr_info, aff_notify); + + cpumask_copy(*intr->affinity_mask, mask); +} + +static void ionic_irq_aff_release(struct kref __always_unused *ref) +{ +} + static int ionic_qcq_enable(struct ionic_qcq *qcq) { struct ionic_queue *q = &qcq->q; @@ -296,12 +311,12 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq) if (ret) return ret; - if (qcq->napi.poll) - napi_enable(&qcq->napi); - if (qcq->flags & IONIC_QCQ_F_INTR) { + napi_enable(&qcq->napi); + irq_set_affinity_notifier(qcq->intr.vector, + &qcq->intr.aff_notify); irq_set_affinity_hint(qcq->intr.vector, - &qcq->intr.affinity_mask); + *qcq->intr.affinity_mask); ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, IONIC_INTR_MASK_CLEAR); } @@ -331,13 +346,15 @@ static int ionic_qcq_disable(struct ionic_lif *lif, struct ionic_qcq *qcq, int f if (qcq->flags & IONIC_QCQ_F_INTR) { struct ionic_dev *idev = &lif->ionic->idev; + if (lif->doorbell_wa) + cancel_work_sync(&qcq->doorbell_napi_work); cancel_work_sync(&qcq->dim.work); ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, IONIC_INTR_MASK_SET); synchronize_irq(qcq->intr.vector); + irq_set_affinity_notifier(qcq->intr.vector, NULL); irq_set_affinity_hint(qcq->intr.vector, NULL); napi_disable(&qcq->napi); - del_timer_sync(&qcq->napi_deadline); } /* If there was a previous fw communcation error, don't bother with @@ -365,6 +382,7 @@ static void ionic_lif_qcq_deinit(struct ionic_lif *lif, struct ionic_qcq *qcq) if (!(qcq->flags & IONIC_QCQ_F_INITED)) return; + ionic_unregister_rxq_info(&qcq->q); if (qcq->flags & IONIC_QCQ_F_INTR) { ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, IONIC_INTR_MASK_SET); @@ -422,10 +440,10 @@ static void ionic_qcq_free(struct ionic_lif *lif, struct ionic_qcq *qcq) qcq->sg_base_pa = 0; } - ionic_qcq_intr_free(lif, qcq); + page_pool_destroy(qcq->q.page_pool); + qcq->q.page_pool = NULL; - vfree(qcq->cq.info); - qcq->cq.info = NULL; + ionic_qcq_intr_free(lif, qcq); vfree(qcq->q.info); qcq->q.info = NULL; } @@ -473,11 +491,11 @@ static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq, { n_qcq->intr.vector = src_qcq->intr.vector; n_qcq->intr.index = src_qcq->intr.index; - n_qcq->napi_qcq = src_qcq->napi_qcq; } static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qcq) { + cpumask_var_t *affinity_mask; int err; if (!(qcq->flags & IONIC_QCQ_F_INTR)) { @@ -509,10 +527,19 @@ static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qc } /* try to get the irq on the local numa node first */ - qcq->intr.cpu = cpumask_local_spread(qcq->intr.index, - dev_to_node(lif->ionic->dev)); - if (qcq->intr.cpu != -1) - cpumask_set_cpu(qcq->intr.cpu, &qcq->intr.affinity_mask); + affinity_mask = &lif->ionic->affinity_masks[qcq->intr.index]; + if (cpumask_empty(*affinity_mask)) { + unsigned int cpu; + + cpu = cpumask_local_spread(qcq->intr.index, + dev_to_node(lif->ionic->dev)); + if (cpu != -1) + cpumask_set_cpu(cpu, *affinity_mask); + } + + qcq->intr.affinity_mask = affinity_mask; + qcq->intr.aff_notify.notify = ionic_irq_aff_notify; + qcq->intr.aff_notify.release = ionic_irq_aff_release; netdev_dbg(lif->netdev, "%s: Interrupt index %d\n", qcq->q.name, qcq->intr.index); return 0; @@ -529,14 +556,12 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, unsigned int num_descs, unsigned int desc_size, unsigned int cq_desc_size, unsigned int sg_desc_size, - unsigned int pid, struct ionic_qcq **qcq) + unsigned int desc_info_size, + unsigned int pid, struct bpf_prog *xdp_prog, + struct ionic_qcq **qcq) { struct ionic_dev *idev = &lif->ionic->idev; struct device *dev = lif->ionic->dev; - void *q_base, *cq_base, *sg_base; - dma_addr_t cq_base_pa = 0; - dma_addr_t sg_base_pa = 0; - dma_addr_t q_base_pa = 0; struct ionic_qcq *new; int err; @@ -552,13 +577,38 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, new->q.dev = dev; new->flags = flags; - new->q.info = vcalloc(num_descs, sizeof(*new->q.info)); + new->q.info = vcalloc(num_descs, desc_info_size); if (!new->q.info) { netdev_err(lif->netdev, "Cannot allocate queue info\n"); err = -ENOMEM; goto err_out_free_qcq; } + if (type == IONIC_QTYPE_RXQ) { + struct page_pool_params pp_params = { + .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, + .order = 0, + .pool_size = num_descs, + .nid = NUMA_NO_NODE, + .dev = lif->ionic->dev, + .napi = &new->napi, + .dma_dir = DMA_FROM_DEVICE, + .max_len = PAGE_SIZE, + .netdev = lif->netdev, + }; + + if (xdp_prog) + pp_params.dma_dir = DMA_BIDIRECTIONAL; + + new->q.page_pool = page_pool_create(&pp_params); + if (IS_ERR(new->q.page_pool)) { + netdev_err(lif->netdev, "Cannot create page_pool\n"); + err = PTR_ERR(new->q.page_pool); + new->q.page_pool = NULL; + goto err_out_free_q_info; + } + } + new->q.type = type; new->q.max_sg_elems = lif->qtype_info[type].max_sg_elems; @@ -566,24 +616,17 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, desc_size, sg_desc_size, pid); if (err) { netdev_err(lif->netdev, "Cannot initialize queue\n"); - goto err_out_free_q_info; + goto err_out_free_page_pool; } err = ionic_alloc_qcq_interrupt(lif, new); if (err) - goto err_out; - - new->cq.info = vcalloc(num_descs, sizeof(*new->cq.info)); - if (!new->cq.info) { - netdev_err(lif->netdev, "Cannot allocate completion queue info\n"); - err = -ENOMEM; - goto err_out_free_irq; - } + goto err_out_free_page_pool; err = ionic_cq_init(lif, &new->cq, &new->intr, num_descs, cq_desc_size); if (err) { netdev_err(lif->netdev, "Cannot initialize completion queue\n"); - goto err_out_free_cq_info; + goto err_out_free_irq; } if (flags & IONIC_QCQ_F_NOTIFYQ) { @@ -601,16 +644,15 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, if (!new->q_base) { netdev_err(lif->netdev, "Cannot allocate qcq DMA memory\n"); err = -ENOMEM; - goto err_out_free_cq_info; + goto err_out_free_irq; } - q_base = PTR_ALIGN(new->q_base, PAGE_SIZE); - q_base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); - ionic_q_map(&new->q, q_base, q_base_pa); - - cq_base = PTR_ALIGN(q_base + q_size, PAGE_SIZE); - cq_base_pa = ALIGN(new->q_base_pa + q_size, PAGE_SIZE); - ionic_cq_map(&new->cq, cq_base, cq_base_pa); - ionic_cq_bind(&new->cq, &new->q); + new->q.base = PTR_ALIGN(new->q_base, PAGE_SIZE); + new->q.base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); + + /* Base the NotifyQ cq.base off of the ALIGNed q.base */ + new->cq.base = PTR_ALIGN(new->q.base + q_size, PAGE_SIZE); + new->cq.base_pa = ALIGN(new->q_base_pa + q_size, PAGE_SIZE); + new->cq.bound_q = &new->q; } else { /* regular DMA q descriptors */ new->q_size = PAGE_SIZE + (num_descs * desc_size); @@ -619,11 +661,10 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, if (!new->q_base) { netdev_err(lif->netdev, "Cannot allocate queue DMA memory\n"); err = -ENOMEM; - goto err_out_free_cq_info; + goto err_out_free_irq; } - q_base = PTR_ALIGN(new->q_base, PAGE_SIZE); - q_base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); - ionic_q_map(&new->q, q_base, q_base_pa); + new->q.base = PTR_ALIGN(new->q_base, PAGE_SIZE); + new->q.base_pa = ALIGN(new->q_base_pa, PAGE_SIZE); if (flags & IONIC_QCQ_F_CMB_RINGS) { /* on-chip CMB q descriptors */ @@ -648,7 +689,8 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, } new->cmb_q_base_pa -= idev->phy_cmb_pages; - ionic_q_cmb_map(&new->q, new->cmb_q_base, new->cmb_q_base_pa); + new->q.cmb_base = new->cmb_q_base; + new->q.cmb_base_pa = new->cmb_q_base_pa; } /* cq DMA descriptors */ @@ -660,10 +702,9 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, err = -ENOMEM; goto err_out_free_q; } - cq_base = PTR_ALIGN(new->cq_base, PAGE_SIZE); - cq_base_pa = ALIGN(new->cq_base_pa, PAGE_SIZE); - ionic_cq_map(&new->cq, cq_base, cq_base_pa); - ionic_cq_bind(&new->cq, &new->q); + new->cq.base = PTR_ALIGN(new->cq_base, PAGE_SIZE); + new->cq.base_pa = ALIGN(new->cq_base_pa, PAGE_SIZE); + new->cq.bound_q = &new->q; } if (flags & IONIC_QCQ_F_SG) { @@ -675,13 +716,14 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, err = -ENOMEM; goto err_out_free_cq; } - sg_base = PTR_ALIGN(new->sg_base, PAGE_SIZE); - sg_base_pa = ALIGN(new->sg_base_pa, PAGE_SIZE); - ionic_q_sg_map(&new->q, sg_base, sg_base_pa); + new->q.sg_base = PTR_ALIGN(new->sg_base, PAGE_SIZE); + new->q.sg_base_pa = ALIGN(new->sg_base_pa, PAGE_SIZE); } INIT_WORK(&new->dim.work, ionic_dim_work); - new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; + new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE; + if (lif->doorbell_wa) + INIT_WORK(&new->doorbell_napi_work, ionic_doorbell_napi_work); *qcq = new; @@ -695,13 +737,13 @@ err_out_free_q: ionic_put_cmb(lif, new->cmb_pgid, new->cmb_order); } dma_free_coherent(dev, new->q_size, new->q_base, new->q_base_pa); -err_out_free_cq_info: - vfree(new->cq.info); err_out_free_irq: if (flags & IONIC_QCQ_F_INTR) { devm_free_irq(dev, new->intr.vector, &new->napi); ionic_intr_free(lif->ionic, new->intr.index); } +err_out_free_page_pool: + page_pool_destroy(new->q.page_pool); err_out_free_q_info: vfree(new->q.info); err_out_free_qcq: @@ -722,7 +764,9 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) IONIC_ADMINQ_LENGTH, sizeof(struct ionic_admin_cmd), sizeof(struct ionic_admin_comp), - 0, lif->kern_pid, &lif->adminqcq); + 0, + sizeof(struct ionic_admin_desc_info), + lif->kern_pid, NULL, &lif->adminqcq); if (err) return err; ionic_debugfs_add_qcq(lif, lif->adminqcq); @@ -733,7 +777,9 @@ static int ionic_qcqs_alloc(struct ionic_lif *lif) flags, IONIC_NOTIFYQ_LENGTH, sizeof(struct ionic_notifyq_cmd), sizeof(union ionic_notifyq_comp), - 0, lif->kern_pid, &lif->notifyqcq); + 0, + sizeof(struct ionic_admin_desc_info), + lif->kern_pid, NULL, &lif->notifyqcq); if (err) goto err_out; ionic_debugfs_add_qcq(lif, lif->notifyqcq); @@ -838,11 +884,8 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) q->dbell_deadline = IONIC_TX_DOORBELL_DEADLINE; q->dbell_jiffies = jiffies; - if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) { + if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi); - qcq->napi_qcq = qcq; - timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); - } qcq->flags |= IONIC_QCQ_F_INITED; @@ -862,8 +905,7 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) .type = q->type, .ver = lif->qtype_info[q->type].version, .index = cpu_to_le32(q->index), - .flags = cpu_to_le16(IONIC_QINIT_F_IRQ | - IONIC_QINIT_F_SG), + .flags = cpu_to_le16(IONIC_QINIT_F_IRQ), .intr_index = cpu_to_le16(cq->bound_intr->index), .pid = cpu_to_le16(q->pid), .ring_size = ilog2(q->num_descs), @@ -875,6 +917,13 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) }; int err; + q->partner = &lif->txqcqs[q->index]->q; + q->partner->partner = q; + + if (!lif->xdp_prog || + (lif->xdp_prog->aux && lif->xdp_prog->aux->xdp_has_frags)) + ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_SG); + if (qcq->flags & IONIC_QCQ_F_CMB_RINGS) { ctx.cmd.q_init.flags |= cpu_to_le16(IONIC_QINIT_F_CMB); ctx.cmd.q_init.ring_base = cpu_to_le64(qcq->cmb_q_base_pa); @@ -908,9 +957,11 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq) netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi); else netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi); - - qcq->napi_qcq = qcq; - timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); + err = ionic_register_rxq_info(q, qcq->napi.napi_id); + if (err) { + netif_napi_del(&qcq->napi); + return err; + } qcq->flags |= IONIC_QCQ_F_INITED; @@ -945,7 +996,8 @@ int ionic_lif_create_hwstamp_txq(struct ionic_lif *lif) err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, txq_i, "hwstamp_tx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &txq); + sizeof(struct ionic_tx_desc_info), + lif->kern_pid, NULL, &txq); if (err) goto err_qcq_alloc; @@ -1004,7 +1056,8 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, rxq_i, "hwstamp_rx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &rxq); + sizeof(struct ionic_rx_desc_info), + lif->kern_pid, NULL, &rxq); if (err) goto err_qcq_alloc; @@ -1021,7 +1074,7 @@ int ionic_lif_create_hwstamp_rxq(struct ionic_lif *lif) goto err_qcq_init; if (test_bit(IONIC_LIF_F_UP, lif->state)) { - ionic_rx_fill(&rxq->q); + ionic_rx_fill(&rxq->q, NULL); err = ionic_qcq_enable(rxq); if (err) goto err_qcq_enable; @@ -1157,71 +1210,6 @@ int ionic_lif_set_hwstamp_rxfilt(struct ionic_lif *lif, u64 pkt_class) return ionic_lif_add_hwstamp_rxfilt(lif, pkt_class); } -static bool ionic_notifyq_service(struct ionic_cq *cq, - struct ionic_cq_info *cq_info) -{ - union ionic_notifyq_comp *comp = cq_info->cq_desc; - struct ionic_deferred_work *work; - struct net_device *netdev; - struct ionic_queue *q; - struct ionic_lif *lif; - u64 eid; - - q = cq->bound_q; - lif = q->info[0].cb_arg; - netdev = lif->netdev; - eid = le64_to_cpu(comp->event.eid); - - /* Have we run out of new completions to process? */ - if ((s64)(eid - lif->last_eid) <= 0) - return false; - - lif->last_eid = eid; - - dev_dbg(lif->ionic->dev, "notifyq event:\n"); - dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, - comp, sizeof(*comp), true); - - switch (le16_to_cpu(comp->event.ecode)) { - case IONIC_EVENT_LINK_CHANGE: - ionic_link_status_check_request(lif, CAN_NOT_SLEEP); - break; - case IONIC_EVENT_RESET: - if (lif->ionic->idev.fw_status_ready && - !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && - !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - netdev_err(lif->netdev, "Reset event dropped\n"); - clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); - } else { - work->type = IONIC_DW_TYPE_LIF_RESET; - ionic_lif_deferred_enqueue(&lif->deferred, work); - } - } - break; - default: - netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", - comp->event.ecode, eid); - break; - } - - return true; -} - -static bool ionic_adminq_service(struct ionic_cq *cq, - struct ionic_cq_info *cq_info) -{ - struct ionic_admin_comp *comp = cq_info->cq_desc; - - if (!color_match(comp->color, cq->done_color)) - return false; - - ionic_q_service(cq->bound_q, cq_info, le16_to_cpu(comp->comp_index)); - - return true; -} - static int ionic_adminq_napi(struct napi_struct *napi, int budget) { struct ionic_intr_info *intr = napi_to_cq(napi)->bound_intr; @@ -1229,7 +1217,6 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget) struct ionic_dev *idev = &lif->ionic->idev; unsigned long irqflags; unsigned int flags = 0; - bool resched = false; int rx_work = 0; int tx_work = 0; int n_work = 0; @@ -1245,6 +1232,7 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget) if (lif->adminqcq && lif->adminqcq->flags & IONIC_QCQ_F_INITED) a_work = ionic_cq_service(&lif->adminqcq->cq, budget, ionic_adminq_service, NULL, NULL); + spin_unlock_irqrestore(&lif->adminq_lock, irqflags); if (lif->hwstamp_rxq) @@ -1252,8 +1240,7 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget) ionic_rx_service, NULL, NULL); if (lif->hwstamp_txq) - tx_work = ionic_cq_service(&lif->hwstamp_txq->cq, budget, - ionic_tx_service, NULL, NULL); + tx_work = ionic_tx_cq_service(&lif->hwstamp_txq->cq, budget, !!budget); work_done = max(max(n_work, a_work), max(rx_work, tx_work)); if (work_done < budget && napi_complete_done(napi, work_done)) { @@ -1267,15 +1254,14 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget) ionic_intr_credits(idev->intr_ctrl, intr->index, credits, flags); } - if (!a_work && ionic_adminq_poke_doorbell(&lif->adminqcq->q)) - resched = true; - if (lif->hwstamp_rxq && !rx_work && ionic_rxq_poke_doorbell(&lif->hwstamp_rxq->q)) - resched = true; - if (lif->hwstamp_txq && !tx_work && ionic_txq_poke_doorbell(&lif->hwstamp_txq->q)) - resched = true; - if (resched) - mod_timer(&lif->adminqcq->napi_deadline, - jiffies + IONIC_NAPI_DEADLINE); + if (lif->doorbell_wa) { + if (!a_work) + ionic_adminq_poke_doorbell(&lif->adminqcq->q); + if (lif->hwstamp_rxq && !rx_work) + ionic_rxq_poke_doorbell(&lif->hwstamp_rxq->q); + if (lif->hwstamp_txq && !tx_work) + ionic_txq_poke_doorbell(&lif->hwstamp_txq->q); + } return work_done; } @@ -1447,7 +1433,7 @@ static void ionic_ndo_set_rx_mode(struct net_device *netdev) } work->type = IONIC_DW_TYPE_RX_MODE; netdev_dbg(lif->netdev, "deferred: rx_mode\n"); - ionic_lif_deferred_enqueue(&lif->deferred, work); + ionic_lif_deferred_enqueue(lif, work); } static __le64 ionic_netdev_features_to_nic(netdev_features_t features) @@ -1640,6 +1626,12 @@ static int ionic_init_nic_features(struct ionic_lif *lif) netdev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; + netdev->xdp_features = NETDEV_XDP_ACT_BASIC | + NETDEV_XDP_ACT_REDIRECT | + NETDEV_XDP_ACT_RX_SG | + NETDEV_XDP_ACT_NDO_XMIT | + NETDEV_XDP_ACT_NDO_XMIT_SG; + return 0; } @@ -1777,6 +1769,21 @@ static int ionic_start_queues_reconfig(struct ionic_lif *lif) return err; } +static bool ionic_xdp_is_valid_mtu(struct ionic_lif *lif, u32 mtu, + struct bpf_prog *xdp_prog) +{ + if (!xdp_prog) + return true; + + if (mtu <= IONIC_XDP_MAX_LINEAR_MTU) + return true; + + if (xdp_prog->aux && xdp_prog->aux->xdp_has_frags) + return true; + + return false; +} + static int ionic_change_mtu(struct net_device *netdev, int new_mtu) { struct ionic_lif *lif = netdev_priv(netdev); @@ -1789,21 +1796,26 @@ static int ionic_change_mtu(struct net_device *netdev, int new_mtu) .mtu = cpu_to_le32(new_mtu), }, }; + struct bpf_prog *xdp_prog; int err; + xdp_prog = READ_ONCE(lif->xdp_prog); + if (!ionic_xdp_is_valid_mtu(lif, new_mtu, xdp_prog)) + return -EINVAL; + err = ionic_adminq_post_wait(lif, &ctx); if (err) return err; /* if we're not running, nothing more to do */ if (!netif_running(netdev)) { - netdev->mtu = new_mtu; + WRITE_ONCE(netdev->mtu, new_mtu); return 0; } mutex_lock(&lif->queue_lock); ionic_stop_queues_reconfig(lif); - netdev->mtu = new_mtu; + WRITE_ONCE(netdev->mtu, new_mtu); err = ionic_start_queues_reconfig(lif); mutex_unlock(&lif->queue_lock); @@ -2070,7 +2082,8 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) for (i = 0; i < lif->nxqs; i++) { err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &lif->txqcqs[i]); + sizeof(struct ionic_tx_desc_info), + lif->kern_pid, NULL, &lif->txqcqs[i]); if (err) goto err_out; @@ -2101,7 +2114,9 @@ static int ionic_txrx_alloc(struct ionic_lif *lif) for (i = 0; i < lif->nxqs; i++) { err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &lif->rxqcqs[i]); + sizeof(struct ionic_rx_desc_info), + lif->kern_pid, lif->xdp_prog, + &lif->rxqcqs[i]); if (err) goto err_out; @@ -2166,6 +2181,8 @@ static int ionic_txrx_enable(struct ionic_lif *lif) int derr = 0; int i, err; + ionic_xdp_rxqs_prog_update(lif); + for (i = 0; i < lif->nxqs; i++) { if (!(lif->rxqcqs[i] && lif->txqcqs[i])) { dev_err(lif->ionic->dev, "%s: bad qcq %d\n", __func__, i); @@ -2173,7 +2190,8 @@ static int ionic_txrx_enable(struct ionic_lif *lif) goto err_out; } - ionic_rx_fill(&lif->rxqcqs[i]->q); + ionic_rx_fill(&lif->rxqcqs[i]->q, + READ_ONCE(lif->rxqcqs[i]->q.xdp_prog)); err = ionic_qcq_enable(lif->rxqcqs[i]); if (err) goto err_out; @@ -2186,7 +2204,7 @@ static int ionic_txrx_enable(struct ionic_lif *lif) } if (lif->hwstamp_rxq) { - ionic_rx_fill(&lif->hwstamp_rxq->q); + ionic_rx_fill(&lif->hwstamp_rxq->q, NULL); err = ionic_qcq_enable(lif->hwstamp_rxq); if (err) goto err_out_hwstamp_rx; @@ -2211,6 +2229,8 @@ err_out: derr = ionic_qcq_disable(lif, lif->rxqcqs[i], derr); } + ionic_xdp_rxqs_prog_update(lif); + return err; } @@ -2668,11 +2688,133 @@ static void ionic_vf_attr_replay(struct ionic_lif *lif) ionic_vf_start(ionic); } +static void ionic_unregister_rxq_info(struct ionic_queue *q) +{ + struct xdp_rxq_info *xi; + + if (!q->xdp_rxq_info) + return; + + xi = q->xdp_rxq_info; + q->xdp_rxq_info = NULL; + + xdp_rxq_info_unreg(xi); + kfree(xi); +} + +static int ionic_register_rxq_info(struct ionic_queue *q, unsigned int napi_id) +{ + struct xdp_rxq_info *rxq_info; + int err; + + rxq_info = kzalloc(sizeof(*rxq_info), GFP_KERNEL); + if (!rxq_info) + return -ENOMEM; + + err = xdp_rxq_info_reg(rxq_info, q->lif->netdev, q->index, napi_id); + if (err) { + netdev_err(q->lif->netdev, "q%d xdp_rxq_info_reg failed, err %d\n", + q->index, err); + goto err_out; + } + + err = xdp_rxq_info_reg_mem_model(rxq_info, MEM_TYPE_PAGE_POOL, q->page_pool); + if (err) { + netdev_err(q->lif->netdev, "q%d xdp_rxq_info_reg_mem_model failed, err %d\n", + q->index, err); + xdp_rxq_info_unreg(rxq_info); + goto err_out; + } + + q->xdp_rxq_info = rxq_info; + + return 0; + +err_out: + kfree(rxq_info); + return err; +} + +static void ionic_xdp_rxqs_prog_update(struct ionic_lif *lif) +{ + struct bpf_prog *xdp_prog; + unsigned int i; + + if (!lif->rxqcqs) + return; + + xdp_prog = READ_ONCE(lif->xdp_prog); + for (i = 0; i < lif->ionic->nrxqs_per_lif && lif->rxqcqs[i]; i++) { + struct ionic_queue *q = &lif->rxqcqs[i]->q; + + WRITE_ONCE(q->xdp_prog, xdp_prog); + } +} + +static int ionic_xdp_config(struct net_device *netdev, struct netdev_bpf *bpf) +{ + struct ionic_lif *lif = netdev_priv(netdev); + struct bpf_prog *old_prog; + u32 maxfs; + + if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) { +#define XDP_ERR_SPLIT "XDP not available with split Tx/Rx interrupts" + NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_SPLIT); + netdev_info(lif->netdev, XDP_ERR_SPLIT); + return -EOPNOTSUPP; + } + + if (!ionic_xdp_is_valid_mtu(lif, netdev->mtu, bpf->prog)) { +#define XDP_ERR_MTU "MTU is too large for XDP without frags support" + NL_SET_ERR_MSG_MOD(bpf->extack, XDP_ERR_MTU); + netdev_info(lif->netdev, XDP_ERR_MTU); + return -EINVAL; + } + + maxfs = __le32_to_cpu(lif->identity->eth.max_frame_size) - VLAN_ETH_HLEN; + if (bpf->prog && !(bpf->prog->aux && bpf->prog->aux->xdp_has_frags)) + maxfs = min_t(u32, maxfs, IONIC_XDP_MAX_LINEAR_MTU); + netdev->max_mtu = maxfs; + + if (!netif_running(netdev)) { + old_prog = xchg(&lif->xdp_prog, bpf->prog); + } else if (lif->xdp_prog && bpf->prog) { + old_prog = xchg(&lif->xdp_prog, bpf->prog); + ionic_xdp_rxqs_prog_update(lif); + } else { + struct ionic_queue_params qparams; + + ionic_init_queue_params(lif, &qparams); + qparams.xdp_prog = bpf->prog; + mutex_lock(&lif->queue_lock); + ionic_reconfigure_queues(lif, &qparams); + old_prog = xchg(&lif->xdp_prog, bpf->prog); + mutex_unlock(&lif->queue_lock); + } + + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +static int ionic_xdp(struct net_device *netdev, struct netdev_bpf *bpf) +{ + switch (bpf->command) { + case XDP_SETUP_PROG: + return ionic_xdp_config(netdev, bpf); + default: + return -EINVAL; + } +} + static const struct net_device_ops ionic_netdev_ops = { .ndo_open = ionic_open, .ndo_stop = ionic_stop, .ndo_eth_ioctl = ionic_eth_ioctl, .ndo_start_xmit = ionic_start_xmit, + .ndo_bpf = ionic_xdp, + .ndo_xdp_xmit = ionic_xdp_xmit, .ndo_get_stats64 = ionic_get_stats64, .ndo_set_rx_mode = ionic_ndo_set_rx_mode, .ndo_set_features = ionic_set_features, @@ -2748,13 +2890,25 @@ err_out: static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b) { - /* only swapping the queues, not the napi, flags, or other stuff */ + /* only swapping the queues and napi, not flags or other stuff */ + swap(a->napi, b->napi); + + if (a->q.type == IONIC_QTYPE_RXQ) { + swap(a->q.page_pool, b->q.page_pool); + a->q.page_pool->p.napi = &a->napi; + if (b->q.page_pool) /* is NULL when increasing queue count */ + b->q.page_pool->p.napi = &b->napi; + } + swap(a->q.features, b->q.features); swap(a->q.num_descs, b->q.num_descs); swap(a->q.desc_size, b->q.desc_size); swap(a->q.base, b->q.base); swap(a->q.base_pa, b->q.base_pa); swap(a->q.info, b->q.info); + swap(a->q.xdp_prog, b->q.xdp_prog); + swap(a->q.xdp_rxq_info, b->q.xdp_rxq_info); + swap(a->q.partner, b->q.partner); swap(a->q_base, b->q_base); swap(a->q_base_pa, b->q_base_pa); swap(a->q_size, b->q_size); @@ -2770,7 +2924,6 @@ static void ionic_swap_queues(struct ionic_qcq *a, struct ionic_qcq *b) swap(a->cq.desc_size, b->cq.desc_size); swap(a->cq.base, b->cq.base); swap(a->cq.base_pa, b->cq.base_pa); - swap(a->cq.info, b->cq.info); swap(a->cq_base, b->cq_base); swap(a->cq_base_pa, b->cq_base_pa); swap(a->cq_size, b->cq_size); @@ -2804,7 +2957,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, } if (qparam->nxqs != lif->nxqs || qparam->nrxq_descs != lif->nrxq_descs || - qparam->rxq_features != lif->rxq_features) { + qparam->rxq_features != lif->rxq_features || + qparam->xdp_prog != lif->xdp_prog) { rx_qcqs = devm_kcalloc(lif->ionic->dev, lif->ionic->nrxqs_per_lif, sizeof(struct ionic_qcq *), GFP_KERNEL); if (!rx_qcqs) { @@ -2834,7 +2988,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, flags = IONIC_QCQ_F_TX_STATS | IONIC_QCQ_F_SG; err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, 4, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &lif->txqcqs[i]); + sizeof(struct ionic_tx_desc_info), + lif->kern_pid, NULL, &lif->txqcqs[i]); if (err) goto err_out; } @@ -2842,7 +2997,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, flags = lif->txqcqs[i]->flags & ~IONIC_QCQ_F_INTR; err = ionic_qcq_alloc(lif, IONIC_QTYPE_TXQ, i, "tx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &tx_qcqs[i]); + sizeof(struct ionic_tx_desc_info), + lif->kern_pid, NULL, &tx_qcqs[i]); if (err) goto err_out; } @@ -2863,7 +3019,8 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, flags = IONIC_QCQ_F_RX_STATS | IONIC_QCQ_F_SG; err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, 4, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &lif->rxqcqs[i]); + sizeof(struct ionic_rx_desc_info), + lif->kern_pid, NULL, &lif->rxqcqs[i]); if (err) goto err_out; } @@ -2871,11 +3028,13 @@ int ionic_reconfigure_queues(struct ionic_lif *lif, flags = lif->rxqcqs[i]->flags & ~IONIC_QCQ_F_INTR; err = ionic_qcq_alloc(lif, IONIC_QTYPE_RXQ, i, "rx", flags, num_desc, desc_sz, comp_sz, sg_desc_sz, - lif->kern_pid, &rx_qcqs[i]); + sizeof(struct ionic_rx_desc_info), + lif->kern_pid, qparam->xdp_prog, &rx_qcqs[i]); if (err) goto err_out; rx_qcqs[i]->q.features = qparam->rxq_features; + rx_qcqs[i]->q.xdp_prog = qparam->xdp_prog; } } @@ -3024,6 +3183,44 @@ err_out: return err; } +static int ionic_affinity_masks_alloc(struct ionic *ionic) +{ + cpumask_var_t *affinity_masks; + int nintrs = ionic->nintrs; + int i; + + affinity_masks = kcalloc(nintrs, sizeof(cpumask_var_t), GFP_KERNEL); + if (!affinity_masks) + return -ENOMEM; + + for (i = 0; i < nintrs; i++) { + if (!zalloc_cpumask_var_node(&affinity_masks[i], GFP_KERNEL, + dev_to_node(ionic->dev))) + goto err_out; + } + + ionic->affinity_masks = affinity_masks; + + return 0; + +err_out: + for (--i; i >= 0; i--) + free_cpumask_var(affinity_masks[i]); + kfree(affinity_masks); + + return -ENOMEM; +} + +static void ionic_affinity_masks_free(struct ionic *ionic) +{ + int i; + + for (i = 0; i < ionic->nintrs; i++) + free_cpumask_var(ionic->affinity_masks[i]); + kfree(ionic->affinity_masks); + ionic->affinity_masks = NULL; +} + int ionic_lif_alloc(struct ionic *ionic) { struct device *dev = ionic->dev; @@ -3054,7 +3251,7 @@ int ionic_lif_alloc(struct ionic *ionic) netdev->netdev_ops = &ionic_netdev_ops; ionic_ethtool_set_ops(netdev); - netdev->watchdog_timeo = 2 * HZ; + netdev->watchdog_timeo = 5 * HZ; netif_carrier_off(netdev); lif->identity = lid; @@ -3068,7 +3265,7 @@ int ionic_lif_alloc(struct ionic *ionic) lif->netdev->min_mtu = max_t(unsigned int, ETH_MIN_MTU, le32_to_cpu(lif->identity->eth.min_frame_size)); lif->netdev->max_mtu = - le32_to_cpu(lif->identity->eth.max_frame_size) - ETH_HLEN - VLAN_HLEN; + le32_to_cpu(lif->identity->eth.max_frame_size) - VLAN_ETH_HLEN; lif->neqs = ionic->neqs_per_lif; lif->nxqs = ionic->ntxqs_per_lif; @@ -3115,11 +3312,15 @@ int ionic_lif_alloc(struct ionic *ionic) ionic_debugfs_add_lif(lif); + err = ionic_affinity_masks_alloc(ionic); + if (err) + goto err_out_free_lif_info; + /* allocate control queues and txrx queue arrays */ ionic_lif_queue_identify(lif); err = ionic_qcqs_alloc(lif); if (err) - goto err_out_free_lif_info; + goto err_out_free_affinity_masks; /* allocate rss indirection table */ tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); @@ -3141,6 +3342,8 @@ int ionic_lif_alloc(struct ionic *ionic) err_out_free_qcqs: ionic_qcqs_free(lif); +err_out_free_affinity_masks: + ionic_affinity_masks_free(lif->ionic); err_out_free_lif_info: dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa); lif->info = NULL; @@ -3241,6 +3444,7 @@ int ionic_restart_lif(struct ionic_lif *lif) clear_bit(IONIC_LIF_F_FW_RESET, lif->state); ionic_link_status_check_request(lif, CAN_SLEEP); netif_device_attach(lif->netdev); + ionic_queue_doorbell_check(ionic, IONIC_NAPI_DEADLINE); return 0; @@ -3271,6 +3475,7 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif) * just need to reanimate it. */ ionic_init_devinfo(ionic); + ionic_reset(ionic); err = ionic_identify(ionic); if (err) goto err_out; @@ -3313,6 +3518,8 @@ void ionic_lif_free(struct ionic_lif *lif) if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) ionic_lif_reset(lif); + ionic_affinity_masks_free(lif->ionic); + /* free lif info */ kfree(lif->identity); dma_free_coherent(dev, lif->info_sz, lif->info, lif->info_pa); @@ -3386,14 +3593,14 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif) netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi); - qcq->napi_qcq = qcq; - timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0); - napi_enable(&qcq->napi); - if (qcq->flags & IONIC_QCQ_F_INTR) + if (qcq->flags & IONIC_QCQ_F_INTR) { + irq_set_affinity_hint(qcq->intr.vector, + *qcq->intr.affinity_mask); ionic_intr_mask(idev->intr_ctrl, qcq->intr.index, IONIC_INTR_MASK_CLEAR); + } qcq->flags |= IONIC_QCQ_F_INITED; @@ -3442,7 +3649,7 @@ static int ionic_lif_notifyq_init(struct ionic_lif *lif) dev_dbg(dev, "notifyq->hw_index %d\n", q->hw_index); /* preset the callback info */ - q->info[0].cb_arg = lif; + q->admin_info[0].ctx = lif; qcq->flags |= IONIC_QCQ_F_INITED; @@ -3577,6 +3784,7 @@ int ionic_lif_init(struct ionic_lif *lif) goto err_out_notifyq_deinit; lif->rx_copybreak = IONIC_RX_COPYBREAK_DEFAULT; + lif->doorbell_wa = ionic_doorbell_wa(lif->ionic); set_bit(IONIC_LIF_F_INITED, lif->state); @@ -3596,10 +3804,6 @@ err_out_adminq_deinit: return err; } -static void ionic_lif_notify_work(struct work_struct *ws) -{ -} - static void ionic_lif_set_netdev_info(struct ionic_lif *lif) { struct ionic_admin_ctx ctx = { @@ -3611,7 +3815,7 @@ static void ionic_lif_set_netdev_info(struct ionic_lif *lif) }, }; - strscpy(ctx.cmd.lif_setattr.name, lif->netdev->name, + strscpy(ctx.cmd.lif_setattr.name, netdev_name(lif->netdev), sizeof(ctx.cmd.lif_setattr.name)); ionic_adminq_post_wait(lif, &ctx); @@ -3650,8 +3854,6 @@ int ionic_lif_register(struct ionic_lif *lif) ionic_lif_register_phc(lif); - INIT_WORK(&lif->ionic->nb_work, ionic_lif_notify_work); - lif->ionic->nb.notifier_call = ionic_lif_notify; err = register_netdevice_notifier(&lif->ionic->nb); @@ -3661,8 +3863,8 @@ int ionic_lif_register(struct ionic_lif *lif) /* only register LIF0 for now */ err = register_netdev(lif->netdev); if (err) { - dev_err(lif->ionic->dev, "Cannot register net device, aborting\n"); - ionic_lif_unregister_phc(lif); + dev_err(lif->ionic->dev, "Cannot register net device: %d, aborting\n", err); + ionic_lif_unregister(lif); return err; } @@ -3677,7 +3879,6 @@ void ionic_lif_unregister(struct ionic_lif *lif) { if (lif->ionic->nb.notifier_call) { unregister_netdevice_notifier(&lif->ionic->nb); - cancel_work_sync(&lif->ionic->nb_work); lif->ionic->nb.notifier_call = NULL; } @@ -3694,6 +3895,7 @@ static void ionic_lif_queue_identify(struct ionic_lif *lif) union ionic_q_identity __iomem *q_ident; struct ionic *ionic = lif->ionic; struct ionic_dev *idev; + u16 max_frags; int qtype; int err; @@ -3761,17 +3963,16 @@ static void ionic_lif_queue_identify(struct ionic_lif *lif) dev_dbg(ionic->dev, " qtype[%d].sg_desc_stride = %d\n", qtype, qti->sg_desc_stride); - if (qti->max_sg_elems >= IONIC_MAX_FRAGS) { - qti->max_sg_elems = IONIC_MAX_FRAGS - 1; - dev_dbg(ionic->dev, "limiting qtype %d max_sg_elems to IONIC_MAX_FRAGS-1 %d\n", - qtype, qti->max_sg_elems); - } + if (qtype == IONIC_QTYPE_TXQ) + max_frags = IONIC_TX_MAX_FRAGS; + else if (qtype == IONIC_QTYPE_RXQ) + max_frags = IONIC_RX_MAX_FRAGS; + else + max_frags = 1; - if (qti->max_sg_elems > MAX_SKB_FRAGS) { - qti->max_sg_elems = MAX_SKB_FRAGS; - dev_dbg(ionic->dev, "limiting qtype %d max_sg_elems to MAX_SKB_FRAGS %d\n", - qtype, qti->max_sg_elems); - } + qti->max_sg_elems = min_t(u16, max_frags - 1, MAX_SKB_FRAGS); + dev_dbg(ionic->dev, "qtype %d max_sg_elems %d\n", + qtype, qti->max_sg_elems); } } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h index 61548b3eea93..e01756fb7fdd 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h @@ -37,6 +37,7 @@ struct ionic_tx_stats { u64 dma_map_err; u64 hwstamp_valid; u64 hwstamp_invalid; + u64 xdp_frames; }; struct ionic_rx_stats { @@ -51,6 +52,11 @@ struct ionic_rx_stats { u64 alloc_err; u64 hwstamp_valid; u64 hwstamp_invalid; + u64 xdp_drop; + u64 xdp_aborted; + u64 xdp_pass; + u64 xdp_tx; + u64 xdp_redirect; }; #define IONIC_QCQ_F_INITED BIT(0) @@ -65,12 +71,13 @@ struct ionic_qcq { void *q_base; dma_addr_t q_base_pa; u32 q_size; + u32 cq_size; void *cq_base; dma_addr_t cq_base_pa; - u32 cq_size; void *sg_base; dma_addr_t sg_base_pa; u32 sg_size; + unsigned int flags; void __iomem *cmb_q_base; phys_addr_t cmb_q_base_pa; u32 cmb_q_size; @@ -79,11 +86,9 @@ struct ionic_qcq { struct dim dim; struct ionic_queue q; struct ionic_cq cq; - struct ionic_intr_info intr; - struct timer_list napi_deadline; struct napi_struct napi; - unsigned int flags; - struct ionic_qcq *napi_qcq; + struct ionic_intr_info intr; + struct work_struct doorbell_napi_work; struct dentry *dentry; }; @@ -135,6 +140,12 @@ struct ionic_lif_sw_stats { u64 hw_rx_over_errors; u64 hw_rx_missed_errors; u64 hw_tx_aborted_errors; + u64 xdp_drop; + u64 xdp_aborted; + u64 xdp_pass; + u64 xdp_tx; + u64 xdp_redirect; + u64 xdp_frames; }; enum ionic_lif_state_flags { @@ -195,11 +206,12 @@ struct ionic_lif { unsigned int nxqs; unsigned int ntxq_descs; unsigned int nrxq_descs; - u32 rx_copybreak; u64 rxq_features; - u16 rx_mode; u64 hw_features; + u16 rx_copybreak; + u16 rx_mode; bool registered; + bool doorbell_wa; u16 lif_type; unsigned int link_down_count; unsigned int nmcast; @@ -214,11 +226,11 @@ struct ionic_lif { u32 info_sz; struct ionic_qtype_info qtype_info[IONIC_QTYPE_MAX]; - u16 rss_types; u8 rss_hash_key[IONIC_RSS_HASH_KEY_SIZE]; u8 *rss_ind_tbl; dma_addr_t rss_ind_tbl_pa; u32 rss_ind_tbl_sz; + u16 rss_types; struct ionic_rx_filters rx_filters; u32 rx_coalesce_usecs; /* what the user asked for */ @@ -230,6 +242,7 @@ struct ionic_lif { struct ionic_phc *phc; struct dentry *dentry; + struct bpf_prog *xdp_prog; }; struct ionic_phc { @@ -255,6 +268,7 @@ struct ionic_queue_params { unsigned int ntxq_descs; unsigned int nrxq_descs; u64 rxq_features; + struct bpf_prog *xdp_prog; bool intr_split; bool cmb_tx; bool cmb_rx; @@ -267,6 +281,7 @@ static inline void ionic_init_queue_params(struct ionic_lif *lif, qparam->ntxq_descs = lif->ntxq_descs; qparam->nrxq_descs = lif->nrxq_descs; qparam->rxq_features = lif->rxq_features; + qparam->xdp_prog = lif->xdp_prog; qparam->intr_split = test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); qparam->cmb_tx = test_bit(IONIC_LIF_F_CMB_TX_RINGS, lif->state); qparam->cmb_rx = test_bit(IONIC_LIF_F_CMB_RX_RINGS, lif->state); @@ -314,13 +329,13 @@ static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs) static inline bool ionic_txq_hwstamp_enabled(struct ionic_queue *q) { - return unlikely(q->features & IONIC_TXQ_F_HWSTAMP); + return q->features & IONIC_TXQ_F_HWSTAMP; } void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep); void ionic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *ns); -void ionic_lif_deferred_enqueue(struct ionic_deferred *def, +void ionic_lif_deferred_enqueue(struct ionic_lif *lif, struct ionic_deferred_work *work); int ionic_lif_alloc(struct ionic *ionic); int ionic_lif_init(struct ionic_lif *lif); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index 2f479de329fe..daf1e82cb76b 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -15,7 +15,7 @@ #include "ionic_debugfs.h" MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION); -MODULE_AUTHOR("Pensando Systems, Inc"); +MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>"); MODULE_LICENSE("GPL"); static const char *ionic_error_to_str(enum ionic_status_code code) @@ -81,8 +81,9 @@ static int ionic_error_to_errno(enum ionic_status_code code) case IONIC_RC_EQTYPE: case IONIC_RC_EQID: case IONIC_RC_EINVAL: - case IONIC_RC_ENOSUPP: return -EINVAL; + case IONIC_RC_ENOSUPP: + return -EOPNOTSUPP; case IONIC_RC_EPERM: return -EPERM; case IONIC_RC_ENOENT: @@ -190,7 +191,8 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) static void ionic_adminq_flush(struct ionic_lif *lif) { - struct ionic_desc_info *desc_info; + struct ionic_admin_desc_info *desc_info; + struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; @@ -203,10 +205,10 @@ static void ionic_adminq_flush(struct ionic_lif *lif) q = &lif->adminqcq->q; while (q->tail_idx != q->head_idx) { - desc_info = &q->info[q->tail_idx]; - memset(desc_info->desc, 0, sizeof(union ionic_adminq_cmd)); - desc_info->cb = NULL; - desc_info->cb_arg = NULL; + desc = &q->adminq[q->tail_idx]; + desc_info = &q->admin_info[q->tail_idx]; + memset(desc, 0, sizeof(union ionic_adminq_cmd)); + desc_info->ctx = NULL; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); } spin_unlock_irqrestore(&lif->adminq_lock, irqflags); @@ -246,25 +248,93 @@ static int ionic_adminq_check_err(struct ionic_lif *lif, return err; } -static void ionic_adminq_cb(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_cq_info *cq_info, void *cb_arg) +bool ionic_notifyq_service(struct ionic_cq *cq) +{ + struct ionic_deferred_work *work; + union ionic_notifyq_comp *comp; + struct net_device *netdev; + struct ionic_queue *q; + struct ionic_lif *lif; + u64 eid; + + comp = &((union ionic_notifyq_comp *)cq->base)[cq->tail_idx]; + + q = cq->bound_q; + lif = q->admin_info[0].ctx; + netdev = lif->netdev; + eid = le64_to_cpu(comp->event.eid); + + /* Have we run out of new completions to process? */ + if ((s64)(eid - lif->last_eid) <= 0) + return false; + + lif->last_eid = eid; + + dev_dbg(lif->ionic->dev, "notifyq event:\n"); + dynamic_hex_dump("event ", DUMP_PREFIX_OFFSET, 16, 1, + comp, sizeof(*comp), true); + + switch (le16_to_cpu(comp->event.ecode)) { + case IONIC_EVENT_LINK_CHANGE: + ionic_link_status_check_request(lif, CAN_NOT_SLEEP); + break; + case IONIC_EVENT_RESET: + if (lif->ionic->idev.fw_status_ready && + !test_bit(IONIC_LIF_F_FW_RESET, lif->state) && + !test_and_set_bit(IONIC_LIF_F_FW_STOPPING, lif->state)) { + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + netdev_err(lif->netdev, "Reset event dropped\n"); + clear_bit(IONIC_LIF_F_FW_STOPPING, lif->state); + } else { + work->type = IONIC_DW_TYPE_LIF_RESET; + ionic_lif_deferred_enqueue(lif, work); + } + } + break; + default: + netdev_warn(netdev, "Notifyq event ecode=%d eid=%lld\n", + comp->event.ecode, eid); + break; + } + + return true; +} + +bool ionic_adminq_service(struct ionic_cq *cq) { - struct ionic_admin_ctx *ctx = cb_arg; + struct ionic_admin_desc_info *desc_info; + struct ionic_queue *q = cq->bound_q; struct ionic_admin_comp *comp; + u16 index; - if (!ctx) - return; + comp = &((struct ionic_admin_comp *)cq->base)[cq->tail_idx]; - comp = cq_info->cq_desc; + if (!color_match(comp->color, cq->done_color)) + return false; - memcpy(&ctx->comp, comp, sizeof(*comp)); + /* check for empty queue */ + if (q->tail_idx == q->head_idx) + return false; - dev_dbg(q->dev, "comp admin queue command:\n"); - dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, - &ctx->comp, sizeof(ctx->comp), true); + do { + desc_info = &q->admin_info[q->tail_idx]; + index = q->tail_idx; + q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); + if (likely(desc_info->ctx)) { + struct ionic_admin_ctx *ctx = desc_info->ctx; + + memcpy(&ctx->comp, comp, sizeof(*comp)); + + dev_dbg(q->dev, "comp admin queue command:\n"); + dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, + &ctx->comp, sizeof(ctx->comp), true); + complete_all(&ctx->work); + desc_info->ctx = NULL; + } + } while (index != le16_to_cpu(comp->comp_index)); - complete_all(&ctx->work); + return true; } bool ionic_adminq_poke_doorbell(struct ionic_queue *q) @@ -298,7 +368,8 @@ bool ionic_adminq_poke_doorbell(struct ionic_queue *q) int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) { - struct ionic_desc_info *desc_info; + struct ionic_admin_desc_info *desc_info; + struct ionic_admin_cmd *desc; unsigned long irqflags; struct ionic_queue *q; int err = 0; @@ -320,14 +391,17 @@ int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) if (err) goto err_out; - desc_info = &q->info[q->head_idx]; - memcpy(desc_info->desc, &ctx->cmd, sizeof(ctx->cmd)); + desc_info = &q->admin_info[q->head_idx]; + desc_info->ctx = ctx; + + desc = &q->adminq[q->head_idx]; + memcpy(desc, &ctx->cmd, sizeof(ctx->cmd)); dev_dbg(&lif->netdev->dev, "post admin queue command:\n"); dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1, &ctx->cmd, sizeof(ctx->cmd), true); - ionic_q_post(q, true, ionic_adminq_cb, ctx); + ionic_q_post(q, true); err_out: spin_unlock_irqrestore(&lif->adminq_lock, irqflags); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c index 1ee2f285cb42..528114877677 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c @@ -312,8 +312,8 @@ static int ionic_lif_filter_add(struct ionic_lif *lif, int err = 0; ctx.cmd.rx_filter_add = *ac; - ctx.cmd.rx_filter_add.opcode = IONIC_CMD_RX_FILTER_ADD, - ctx.cmd.rx_filter_add.lif_index = cpu_to_le16(lif->index), + ctx.cmd.rx_filter_add.opcode = IONIC_CMD_RX_FILTER_ADD; + ctx.cmd.rx_filter_add.lif_index = cpu_to_le16(lif->index); spin_lock_bh(&lif->rx_filters.lock); f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c index 1f6022fb7679..0107599a9dd4 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c @@ -27,6 +27,12 @@ static const struct ionic_stat_desc ionic_lif_stats_desc[] = { IONIC_LIF_STAT_DESC(hw_rx_over_errors), IONIC_LIF_STAT_DESC(hw_rx_missed_errors), IONIC_LIF_STAT_DESC(hw_tx_aborted_errors), + IONIC_LIF_STAT_DESC(xdp_drop), + IONIC_LIF_STAT_DESC(xdp_aborted), + IONIC_LIF_STAT_DESC(xdp_pass), + IONIC_LIF_STAT_DESC(xdp_tx), + IONIC_LIF_STAT_DESC(xdp_redirect), + IONIC_LIF_STAT_DESC(xdp_frames), }; static const struct ionic_stat_desc ionic_port_stats_desc[] = { @@ -135,6 +141,7 @@ static const struct ionic_stat_desc ionic_tx_stats_desc[] = { IONIC_TX_STAT_DESC(csum_none), IONIC_TX_STAT_DESC(csum), IONIC_TX_STAT_DESC(vlan_inserted), + IONIC_TX_STAT_DESC(xdp_frames), }; static const struct ionic_stat_desc ionic_rx_stats_desc[] = { @@ -149,6 +156,11 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = { IONIC_RX_STAT_DESC(hwstamp_invalid), IONIC_RX_STAT_DESC(dropped), IONIC_RX_STAT_DESC(vlan_stripped), + IONIC_RX_STAT_DESC(xdp_drop), + IONIC_RX_STAT_DESC(xdp_aborted), + IONIC_RX_STAT_DESC(xdp_pass), + IONIC_RX_STAT_DESC(xdp_tx), + IONIC_RX_STAT_DESC(xdp_redirect), }; #define IONIC_NUM_LIF_STATS ARRAY_SIZE(ionic_lif_stats_desc) @@ -171,6 +183,7 @@ static void ionic_add_lif_txq_stats(struct ionic_lif *lif, int q_num, stats->tx_csum += txstats->csum; stats->tx_hwstamp_valid += txstats->hwstamp_valid; stats->tx_hwstamp_invalid += txstats->hwstamp_invalid; + stats->xdp_frames += txstats->xdp_frames; } static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, @@ -185,6 +198,11 @@ static void ionic_add_lif_rxq_stats(struct ionic_lif *lif, int q_num, stats->rx_csum_error += rxstats->csum_error; stats->rx_hwstamp_valid += rxstats->hwstamp_valid; stats->rx_hwstamp_invalid += rxstats->hwstamp_invalid; + stats->xdp_drop += rxstats->xdp_drop; + stats->xdp_aborted += rxstats->xdp_aborted; + stats->xdp_pass += rxstats->xdp_pass; + stats->xdp_tx += rxstats->xdp_tx; + stats->xdp_redirect += rxstats->xdp_redirect; } static void ionic_get_lif_stats(struct ionic_lif *lif, diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 6f4776759863..2ac59564ded1 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -5,27 +5,42 @@ #include <linux/ipv6.h> #include <linux/if_vlan.h> #include <net/ip6_checksum.h> +#include <net/netdev_queues.h> +#include <net/page_pool/helpers.h> #include "ionic.h" #include "ionic_lif.h" #include "ionic_txrx.h" -static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, - ionic_desc_cb cb_func, void *cb_arg) +static dma_addr_t ionic_tx_map_single(struct ionic_queue *q, + void *data, size_t len); + +static dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, + const skb_frag_t *frag, + size_t offset, size_t len); + +static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, + struct ionic_tx_desc_info *desc_info); + +static void ionic_tx_clean(struct ionic_queue *q, + struct ionic_tx_desc_info *desc_info, + struct ionic_txq_comp *comp, + bool in_napi); + +static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell) { - ionic_q_post(q, ring_dbell, cb_func, cb_arg); + ionic_q_post(q, ring_dbell); } -static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell, - ionic_desc_cb cb_func, void *cb_arg) +static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell) { - ionic_q_post(q, ring_dbell, cb_func, cb_arg); + ionic_q_post(q, ring_dbell); } bool ionic_txq_poke_doorbell(struct ionic_queue *q) { - unsigned long now, then, dif; struct netdev_queue *netdev_txq; + unsigned long now, then, dif; struct net_device *netdev; netdev = q->lif->netdev; @@ -83,224 +98,555 @@ bool ionic_rxq_poke_doorbell(struct ionic_queue *q) return true; } -static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) +static inline struct ionic_txq_sg_elem *ionic_tx_sg_elems(struct ionic_queue *q) { - return netdev_get_tx_queue(q->lif->netdev, q->index); + if (likely(q->sg_desc_size == sizeof(struct ionic_txq_sg_desc_v1))) + return q->txq_sgl_v1[q->head_idx].elems; + else + return q->txq_sgl[q->head_idx].elems; } -static int ionic_rx_page_alloc(struct ionic_queue *q, - struct ionic_buf_info *buf_info) +static inline struct netdev_queue *q_to_ndq(struct net_device *netdev, + struct ionic_queue *q) { - struct net_device *netdev = q->lif->netdev; - struct ionic_rx_stats *stats; - struct device *dev; - struct page *page; - - dev = q->dev; - stats = q_to_rx_stats(q); - - if (unlikely(!buf_info)) { - net_err_ratelimited("%s: %s invalid buf_info in alloc\n", - netdev->name, q->name); - return -EINVAL; - } - - page = alloc_pages(IONIC_PAGE_GFP_MASK, 0); - if (unlikely(!page)) { - net_err_ratelimited("%s: %s page alloc failed\n", - netdev->name, q->name); - stats->alloc_err++; - return -ENOMEM; - } - - buf_info->dma_addr = dma_map_page(dev, page, 0, - IONIC_PAGE_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) { - __free_pages(page, 0); - net_err_ratelimited("%s: %s dma map failed\n", - netdev->name, q->name); - stats->dma_map_err++; - return -EIO; - } - - buf_info->page = page; - buf_info->page_offset = 0; - - return 0; + return netdev_get_tx_queue(netdev, q->index); } -static void ionic_rx_page_free(struct ionic_queue *q, - struct ionic_buf_info *buf_info) +static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info) { - struct net_device *netdev = q->lif->netdev; - struct device *dev = q->dev; + return page_address(buf_info->page) + buf_info->page_offset; +} - if (unlikely(!buf_info)) { - net_err_ratelimited("%s: %s invalid buf_info in free\n", - netdev->name, q->name); - return; - } +static dma_addr_t ionic_rx_buf_pa(struct ionic_buf_info *buf_info) +{ + return page_pool_get_dma_addr(buf_info->page) + buf_info->page_offset; +} +static void __ionic_rx_put_buf(struct ionic_queue *q, + struct ionic_buf_info *buf_info, + bool recycle_direct) +{ if (!buf_info->page) return; - dma_unmap_page(dev, buf_info->dma_addr, IONIC_PAGE_SIZE, DMA_FROM_DEVICE); - __free_pages(buf_info->page, 0); + page_pool_put_full_page(q->page_pool, buf_info->page, recycle_direct); buf_info->page = NULL; + buf_info->len = 0; + buf_info->page_offset = 0; } -static bool ionic_rx_buf_recycle(struct ionic_queue *q, - struct ionic_buf_info *buf_info, u32 used) -{ - u32 size; - - /* don't re-use pages allocated in low-mem condition */ - if (page_is_pfmemalloc(buf_info->page)) - return false; - - /* don't re-use buffers from non-local numa nodes */ - if (page_to_nid(buf_info->page) != numa_mem_id()) - return false; - size = ALIGN(used, IONIC_PAGE_SPLIT_SZ); - buf_info->page_offset += size; - if (buf_info->page_offset >= IONIC_PAGE_SIZE) - return false; +static void ionic_rx_put_buf(struct ionic_queue *q, + struct ionic_buf_info *buf_info) +{ + __ionic_rx_put_buf(q, buf_info, false); +} - get_page(buf_info->page); +static void ionic_rx_put_buf_direct(struct ionic_queue *q, + struct ionic_buf_info *buf_info) +{ + __ionic_rx_put_buf(q, buf_info, true); +} - return true; +static void ionic_rx_add_skb_frag(struct ionic_queue *q, + struct sk_buff *skb, + struct ionic_buf_info *buf_info, + u32 headroom, u32 len, + bool synced) +{ + if (!synced) + page_pool_dma_sync_for_cpu(q->page_pool, + buf_info->page, + buf_info->page_offset + headroom, + len); + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + buf_info->page, buf_info->page_offset + headroom, + len, buf_info->len); + + /* napi_gro_frags() will release/recycle the + * page_pool buffers from the frags list + */ + buf_info->page = NULL; + buf_info->len = 0; + buf_info->page_offset = 0; } -static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_rxq_comp *comp) +static struct sk_buff *ionic_rx_build_skb(struct ionic_queue *q, + struct ionic_rx_desc_info *desc_info, + unsigned int headroom, + unsigned int len, + unsigned int num_sg_elems, + bool synced) { - struct net_device *netdev = q->lif->netdev; struct ionic_buf_info *buf_info; - struct ionic_rx_stats *stats; - struct device *dev = q->dev; struct sk_buff *skb; unsigned int i; u16 frag_len; - u16 len; - - stats = q_to_rx_stats(q); buf_info = &desc_info->bufs[0]; - len = le16_to_cpu(comp->len); - prefetchw(buf_info->page); skb = napi_get_frags(&q_to_qcq(q)->napi); if (unlikely(!skb)) { net_warn_ratelimited("%s: SKB alloc failed on %s!\n", - netdev->name, q->name); - stats->alloc_err++; + dev_name(q->dev), q->name); + q_to_rx_stats(q)->alloc_err++; return NULL; } + skb_mark_for_recycle(skb); - i = comp->num_sg_elems + 1; - do { - if (unlikely(!buf_info->page)) { - dev_kfree_skb(skb); - return NULL; - } - - frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - buf_info->page_offset)); - len -= frag_len; - - dma_sync_single_for_cpu(dev, - buf_info->dma_addr + buf_info->page_offset, - frag_len, DMA_FROM_DEVICE); - - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - buf_info->page, buf_info->page_offset, frag_len, - IONIC_PAGE_SIZE); - - if (!ionic_rx_buf_recycle(q, buf_info, frag_len)) { - dma_unmap_page(dev, buf_info->dma_addr, - IONIC_PAGE_SIZE, DMA_FROM_DEVICE); - buf_info->page = NULL; - } + if (headroom) + frag_len = min_t(u16, len, + IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); + else + frag_len = min_t(u16, len, IONIC_PAGE_SIZE); - buf_info++; + if (unlikely(!buf_info->page)) + goto err_bad_buf_page; + ionic_rx_add_skb_frag(q, skb, buf_info, headroom, frag_len, synced); + len -= frag_len; + buf_info++; - i--; - } while (i > 0); + for (i = 0; i < num_sg_elems; i++, buf_info++) { + if (unlikely(!buf_info->page)) + goto err_bad_buf_page; + frag_len = min_t(u16, len, buf_info->len); + ionic_rx_add_skb_frag(q, skb, buf_info, 0, frag_len, synced); + len -= frag_len; + } return skb; + +err_bad_buf_page: + dev_kfree_skb(skb); + return NULL; } -static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_rxq_comp *comp) +static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev, + struct ionic_queue *q, + struct ionic_rx_desc_info *desc_info, + unsigned int headroom, + unsigned int len, + unsigned int num_sg_elems, + bool synced) { - struct net_device *netdev = q->lif->netdev; struct ionic_buf_info *buf_info; - struct ionic_rx_stats *stats; struct device *dev = q->dev; struct sk_buff *skb; - u16 len; - - stats = q_to_rx_stats(q); + int i; buf_info = &desc_info->bufs[0]; - len = le16_to_cpu(comp->len); skb = napi_alloc_skb(&q_to_qcq(q)->napi, len); if (unlikely(!skb)) { net_warn_ratelimited("%s: SKB alloc failed on %s!\n", - netdev->name, q->name); - stats->alloc_err++; + dev_name(dev), q->name); + q_to_rx_stats(q)->alloc_err++; return NULL; } + skb_mark_for_recycle(skb); - if (unlikely(!buf_info->page)) { - dev_kfree_skb(skb); - return NULL; - } + if (!synced) + page_pool_dma_sync_for_cpu(q->page_pool, + buf_info->page, + buf_info->page_offset + headroom, + len); - dma_sync_single_for_cpu(dev, buf_info->dma_addr + buf_info->page_offset, - len, DMA_FROM_DEVICE); - skb_copy_to_linear_data(skb, page_address(buf_info->page) + buf_info->page_offset, len); - dma_sync_single_for_device(dev, buf_info->dma_addr + buf_info->page_offset, - len, DMA_FROM_DEVICE); + skb_copy_to_linear_data(skb, ionic_rx_buf_va(buf_info) + headroom, len); skb_put(skb, len); - skb->protocol = eth_type_trans(skb, q->lif->netdev); + skb->protocol = eth_type_trans(skb, netdev); + + /* recycle the Rx buffer now that we're done with it */ + ionic_rx_put_buf_direct(q, buf_info); + buf_info++; + for (i = 0; i < num_sg_elems; i++, buf_info++) + ionic_rx_put_buf_direct(q, buf_info); return skb; } +static void ionic_xdp_tx_desc_clean(struct ionic_queue *q, + struct ionic_tx_desc_info *desc_info, + bool in_napi) +{ + struct xdp_frame_bulk bq; + + if (!desc_info->nbufs) + return; + + xdp_frame_bulk_init(&bq); + rcu_read_lock(); /* need for xdp_return_frame_bulk */ + + if (desc_info->act == XDP_TX) { + if (likely(in_napi)) + xdp_return_frame_rx_napi(desc_info->xdpf); + else + xdp_return_frame(desc_info->xdpf); + } else if (desc_info->act == XDP_REDIRECT) { + ionic_tx_desc_unmap_bufs(q, desc_info); + xdp_return_frame_bulk(desc_info->xdpf, &bq); + } + + xdp_flush_frame_bulk(&bq); + rcu_read_unlock(); + + desc_info->nbufs = 0; + desc_info->xdpf = NULL; + desc_info->act = 0; +} + +static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame, + enum xdp_action act, struct page *page, int off, + bool ring_doorbell) +{ + struct ionic_tx_desc_info *desc_info; + struct ionic_buf_info *buf_info; + struct ionic_tx_stats *stats; + struct ionic_txq_desc *desc; + size_t len = frame->len; + dma_addr_t dma_addr; + u64 cmd; + + desc_info = &q->tx_info[q->head_idx]; + desc = &q->txq[q->head_idx]; + buf_info = desc_info->bufs; + stats = q_to_tx_stats(q); + + if (act == XDP_TX) { + dma_addr = page_pool_get_dma_addr(page) + + off + XDP_PACKET_HEADROOM; + dma_sync_single_for_device(q->dev, dma_addr, + len, DMA_TO_DEVICE); + } else /* XDP_REDIRECT */ { + dma_addr = ionic_tx_map_single(q, frame->data, len); + if (!dma_addr) + return -EIO; + } + + buf_info->dma_addr = dma_addr; + buf_info->len = len; + buf_info->page = page; + buf_info->page_offset = off; + + desc_info->nbufs = 1; + desc_info->xdpf = frame; + desc_info->act = act; + + if (xdp_frame_has_frags(frame)) { + struct ionic_txq_sg_elem *elem; + struct skb_shared_info *sinfo; + struct ionic_buf_info *bi; + skb_frag_t *frag; + int i; + + bi = &buf_info[1]; + sinfo = xdp_get_shared_info_from_frame(frame); + frag = sinfo->frags; + elem = ionic_tx_sg_elems(q); + for (i = 0; i < sinfo->nr_frags; i++, frag++, bi++) { + if (act == XDP_TX) { + struct page *pg = skb_frag_page(frag); + + dma_addr = page_pool_get_dma_addr(pg) + + skb_frag_off(frag); + dma_sync_single_for_device(q->dev, dma_addr, + skb_frag_size(frag), + DMA_TO_DEVICE); + } else { + dma_addr = ionic_tx_map_frag(q, frag, 0, + skb_frag_size(frag)); + if (dma_mapping_error(q->dev, dma_addr)) { + ionic_tx_desc_unmap_bufs(q, desc_info); + return -EIO; + } + } + bi->dma_addr = dma_addr; + bi->len = skb_frag_size(frag); + bi->page = skb_frag_page(frag); + + elem->addr = cpu_to_le64(bi->dma_addr); + elem->len = cpu_to_le16(bi->len); + elem++; + + desc_info->nbufs++; + } + } + + cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE, + 0, (desc_info->nbufs - 1), buf_info->dma_addr); + desc->cmd = cpu_to_le64(cmd); + desc->len = cpu_to_le16(len); + desc->csum_start = 0; + desc->csum_offset = 0; + + stats->xdp_frames++; + stats->pkts++; + stats->bytes += len; + + ionic_txq_post(q, ring_doorbell); + + return 0; +} + +int ionic_xdp_xmit(struct net_device *netdev, int n, + struct xdp_frame **xdp_frames, u32 flags) +{ + struct ionic_lif *lif = netdev_priv(netdev); + struct ionic_queue *txq; + struct netdev_queue *nq; + int nxmit; + int space; + int cpu; + int qi; + + if (unlikely(!test_bit(IONIC_LIF_F_UP, lif->state))) + return -ENETDOWN; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + /* AdminQ is assumed on cpu 0, while we attempt to affinitize the + * TxRx queue pairs 0..n-1 on cpus 1..n. We try to keep with that + * affinitization here, but of course irqbalance and friends might + * have juggled things anyway, so we have to check for the 0 case. + */ + cpu = smp_processor_id(); + qi = cpu ? (cpu - 1) % lif->nxqs : cpu; + + txq = &lif->txqcqs[qi]->q; + nq = netdev_get_tx_queue(netdev, txq->index); + __netif_tx_lock(nq, cpu); + txq_trans_cond_update(nq); + + if (netif_tx_queue_stopped(nq) || + !netif_txq_maybe_stop(q_to_ndq(netdev, txq), + ionic_q_space_avail(txq), + 1, 1)) { + __netif_tx_unlock(nq); + return -EIO; + } + + space = min_t(int, n, ionic_q_space_avail(txq)); + for (nxmit = 0; nxmit < space ; nxmit++) { + if (ionic_xdp_post_frame(txq, xdp_frames[nxmit], + XDP_REDIRECT, + virt_to_page(xdp_frames[nxmit]->data), + 0, false)) { + nxmit--; + break; + } + } + + if (flags & XDP_XMIT_FLUSH) + ionic_dbell_ring(lif->kern_dbpage, txq->hw_type, + txq->dbval | txq->head_idx); + + netif_txq_maybe_stop(q_to_ndq(netdev, txq), + ionic_q_space_avail(txq), + 4, 4); + __netif_tx_unlock(nq); + + return nxmit; +} + +static void ionic_xdp_rx_unlink_bufs(struct ionic_queue *q, + struct ionic_buf_info *buf_info, + int nbufs) +{ + int i; + + for (i = 0; i < nbufs; i++) { + buf_info->page = NULL; + buf_info++; + } +} + +static bool ionic_run_xdp(struct ionic_rx_stats *stats, + struct net_device *netdev, + struct bpf_prog *xdp_prog, + struct ionic_queue *rxq, + struct ionic_buf_info *buf_info, + int len) +{ + u32 xdp_action = XDP_ABORTED; + struct xdp_buff xdp_buf; + struct ionic_queue *txq; + struct netdev_queue *nq; + struct xdp_frame *xdpf; + int remain_len; + int nbufs = 1; + int frag_len; + int err = 0; + + xdp_init_buff(&xdp_buf, IONIC_PAGE_SIZE, rxq->xdp_rxq_info); + frag_len = min_t(u16, len, IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN); + xdp_prepare_buff(&xdp_buf, ionic_rx_buf_va(buf_info), + XDP_PACKET_HEADROOM, frag_len, false); + page_pool_dma_sync_for_cpu(rxq->page_pool, buf_info->page, + buf_info->page_offset + XDP_PACKET_HEADROOM, + frag_len); + prefetchw(&xdp_buf.data_hard_start); + + /* We limit MTU size to one buffer if !xdp_has_frags, so + * if the recv len is bigger than one buffer + * then we know we have frag info to gather + */ + remain_len = len - frag_len; + if (remain_len) { + struct skb_shared_info *sinfo; + struct ionic_buf_info *bi; + skb_frag_t *frag; + + bi = buf_info; + sinfo = xdp_get_shared_info_from_buff(&xdp_buf); + sinfo->nr_frags = 0; + sinfo->xdp_frags_size = 0; + xdp_buff_set_frags_flag(&xdp_buf); + + do { + if (unlikely(sinfo->nr_frags >= MAX_SKB_FRAGS)) { + err = -ENOSPC; + break; + } + + frag = &sinfo->frags[sinfo->nr_frags]; + sinfo->nr_frags++; + bi++; + frag_len = min_t(u16, remain_len, bi->len); + page_pool_dma_sync_for_cpu(rxq->page_pool, bi->page, + buf_info->page_offset, + frag_len); + skb_frag_fill_page_desc(frag, bi->page, 0, frag_len); + sinfo->xdp_frags_size += frag_len; + remain_len -= frag_len; + + if (page_is_pfmemalloc(bi->page)) + xdp_buff_set_frag_pfmemalloc(&xdp_buf); + } while (remain_len > 0); + nbufs += sinfo->nr_frags; + } + + xdp_action = bpf_prog_run_xdp(xdp_prog, &xdp_buf); + + switch (xdp_action) { + case XDP_PASS: + stats->xdp_pass++; + return false; /* false = we didn't consume the packet */ + + case XDP_DROP: + ionic_rx_put_buf_direct(rxq, buf_info); + stats->xdp_drop++; + break; + + case XDP_TX: + xdpf = xdp_convert_buff_to_frame(&xdp_buf); + if (!xdpf) { + err = -ENOSPC; + break; + } + + txq = rxq->partner; + nq = netdev_get_tx_queue(netdev, txq->index); + __netif_tx_lock(nq, smp_processor_id()); + txq_trans_cond_update(nq); + + if (netif_tx_queue_stopped(nq) || + !netif_txq_maybe_stop(q_to_ndq(netdev, txq), + ionic_q_space_avail(txq), + 1, 1)) { + __netif_tx_unlock(nq); + err = -EIO; + break; + } + + err = ionic_xdp_post_frame(txq, xdpf, XDP_TX, + buf_info->page, + buf_info->page_offset, + true); + __netif_tx_unlock(nq); + if (unlikely(err)) { + netdev_dbg(netdev, "tx ionic_xdp_post_frame err %d\n", err); + break; + } + ionic_xdp_rx_unlink_bufs(rxq, buf_info, nbufs); + stats->xdp_tx++; + break; + + case XDP_REDIRECT: + err = xdp_do_redirect(netdev, &xdp_buf, xdp_prog); + if (unlikely(err)) { + netdev_dbg(netdev, "xdp_do_redirect err %d\n", err); + break; + } + ionic_xdp_rx_unlink_bufs(rxq, buf_info, nbufs); + rxq->xdp_flush = true; + stats->xdp_redirect++; + break; + + case XDP_ABORTED: + default: + err = -EIO; + break; + } + + if (err) { + ionic_rx_put_buf_direct(rxq, buf_info); + trace_xdp_exception(netdev, xdp_prog, xdp_action); + stats->xdp_aborted++; + } + + return true; +} + static void ionic_rx_clean(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_cq_info *cq_info, - void *cb_arg) + struct ionic_rx_desc_info *desc_info, + struct ionic_rxq_comp *comp, + struct bpf_prog *xdp_prog) { struct net_device *netdev = q->lif->netdev; struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_rx_stats *stats; - struct ionic_rxq_comp *comp; + unsigned int headroom = 0; struct sk_buff *skb; - - comp = cq_info->cq_desc + qcq->cq.desc_size - sizeof(*comp); + bool synced = false; + bool use_copybreak; + u16 len; stats = q_to_rx_stats(q); - if (comp->status) { + if (unlikely(comp->status)) { + /* Most likely status==2 and the pkt received was bigger + * than the buffer available: comp->len will show the + * pkt size received that didn't fit the advertised desc.len + */ + dev_dbg(q->dev, "q%d drop comp->status %d comp->len %d desc->len %d\n", + q->index, comp->status, comp->len, q->rxq[q->head_idx].len); + stats->dropped++; return; } + len = le16_to_cpu(comp->len); stats->pkts++; - stats->bytes += le16_to_cpu(comp->len); + stats->bytes += len; - if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) - skb = ionic_rx_copybreak(q, desc_info, comp); + if (xdp_prog) { + if (ionic_run_xdp(stats, netdev, xdp_prog, q, desc_info->bufs, len)) + return; + synced = true; + headroom = XDP_PACKET_HEADROOM; + } + + use_copybreak = len <= q->lif->rx_copybreak; + if (use_copybreak) + skb = ionic_rx_copybreak(netdev, q, desc_info, + headroom, len, + comp->num_sg_elems, synced); else - skb = ionic_rx_frags(q, desc_info, comp); + skb = ionic_rx_build_skb(q, desc_info, headroom, len, + comp->num_sg_elems, synced); if (unlikely(!skb)) { stats->dropped++; @@ -352,7 +698,7 @@ static void ionic_rx_clean(struct ionic_queue *q, u64 hwstamp; cq_desc_hwstamp = - cq_info->cq_desc + + (void *)comp + qcq->cq.desc_size - sizeof(struct ionic_rxq_comp) - IONIC_HWSTAMP_CQ_NEGOFFSET; @@ -367,19 +713,19 @@ static void ionic_rx_clean(struct ionic_queue *q, } } - if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) + if (use_copybreak) napi_gro_receive(&qcq->napi, skb); else napi_gro_frags(&qcq->napi); } -bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) +static bool __ionic_rx_service(struct ionic_cq *cq, struct bpf_prog *xdp_prog) { + struct ionic_rx_desc_info *desc_info; struct ionic_queue *q = cq->bound_q; - struct ionic_desc_info *desc_info; struct ionic_rxq_comp *comp; - comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp); + comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx]; if (!color_match(comp->pkt_type_color, cq->done_color)) return false; @@ -391,41 +737,48 @@ bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) if (q->tail_idx != le16_to_cpu(comp->comp_index)) return false; - desc_info = &q->info[q->tail_idx]; + desc_info = &q->rx_info[q->tail_idx]; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); /* clean the related q entry, only one per qc completion */ - ionic_rx_clean(q, desc_info, cq_info, desc_info->cb_arg); - - desc_info->cb = NULL; - desc_info->cb_arg = NULL; + ionic_rx_clean(q, desc_info, comp, xdp_prog); return true; } +bool ionic_rx_service(struct ionic_cq *cq) +{ + return __ionic_rx_service(cq, NULL); +} + static inline void ionic_write_cmb_desc(struct ionic_queue *q, - void __iomem *cmb_desc, void *desc) { - if (q_to_qcq(q)->flags & IONIC_QCQ_F_CMB_RINGS) - memcpy_toio(cmb_desc, desc, q->desc_size); + /* Since Rx and Tx descriptors are the same size, we can + * save an instruction or two and skip the qtype check. + */ + if (unlikely(q_to_qcq(q)->flags & IONIC_QCQ_F_CMB_RINGS)) + memcpy_toio(&q->cmb_txq[q->head_idx], desc, sizeof(q->cmb_txq[0])); } -void ionic_rx_fill(struct ionic_queue *q) +void ionic_rx_fill(struct ionic_queue *q, struct bpf_prog *xdp_prog) { struct net_device *netdev = q->lif->netdev; - struct ionic_desc_info *desc_info; - struct ionic_rxq_sg_desc *sg_desc; + struct ionic_rx_desc_info *desc_info; struct ionic_rxq_sg_elem *sg_elem; struct ionic_buf_info *buf_info; unsigned int fill_threshold; struct ionic_rxq_desc *desc; + unsigned int first_frag_len; + unsigned int first_buf_len; + unsigned int headroom = 0; unsigned int remain_len; unsigned int frag_len; unsigned int nfrags; unsigned int n_fill; - unsigned int i, j; unsigned int len; + unsigned int i; + unsigned int j; n_fill = ionic_q_space_avail(q); @@ -434,48 +787,74 @@ void ionic_rx_fill(struct ionic_queue *q) if (n_fill < fill_threshold) return; - len = netdev->mtu + ETH_HLEN + VLAN_HLEN; + len = netdev->mtu + VLAN_ETH_HLEN; + + if (xdp_prog) { + /* Always alloc the full size buffer, but only need + * the actual frag_len in the descriptor + * XDP uses space in the first buffer, so account for + * head room, tail room, and ip header in the first frag size. + */ + headroom = XDP_PACKET_HEADROOM; + first_buf_len = IONIC_XDP_MAX_LINEAR_MTU + VLAN_ETH_HLEN + headroom; + first_frag_len = min_t(u16, len + headroom, first_buf_len); + } else { + /* Use MTU size if smaller than max buffer size */ + first_frag_len = min_t(u16, len, IONIC_PAGE_SIZE); + first_buf_len = first_frag_len; + } for (i = n_fill; i; i--) { + /* fill main descriptor - buf[0] */ nfrags = 0; remain_len = len; - desc_info = &q->info[q->head_idx]; - desc = desc_info->desc; + desc = &q->rxq[q->head_idx]; + desc_info = &q->rx_info[q->head_idx]; buf_info = &desc_info->bufs[0]; - if (!buf_info->page) { /* alloc a new buffer? */ - if (unlikely(ionic_rx_page_alloc(q, buf_info))) { - desc->addr = 0; - desc->len = 0; - return; - } + buf_info->len = first_buf_len; + frag_len = first_frag_len - headroom; + + /* get a new buffer if we can't reuse one */ + if (!buf_info->page) + buf_info->page = page_pool_alloc(q->page_pool, + &buf_info->page_offset, + &buf_info->len, + GFP_ATOMIC); + if (unlikely(!buf_info->page)) { + buf_info->len = 0; + return; } - /* fill main descriptor - buf[0] */ - desc->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - buf_info->page_offset)); + desc->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info) + headroom); desc->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; nfrags++; /* fill sg descriptors - buf[1..n] */ - sg_desc = desc_info->sg_desc; - for (j = 0; remain_len > 0 && j < q->max_sg_elems; j++) { - sg_elem = &sg_desc->elems[j]; - if (!buf_info->page) { /* alloc a new sg buffer? */ - if (unlikely(ionic_rx_page_alloc(q, buf_info))) { - sg_elem->addr = 0; - sg_elem->len = 0; + sg_elem = q->rxq_sgl[q->head_idx].elems; + for (j = 0; remain_len > 0 && j < q->max_sg_elems; j++, sg_elem++) { + frag_len = min_t(u16, remain_len, IONIC_PAGE_SIZE); + + /* Recycle any leftover buffers that are too small to reuse */ + if (unlikely(buf_info->page && buf_info->len < frag_len)) + ionic_rx_put_buf_direct(q, buf_info); + + /* Get new buffer if needed */ + if (!buf_info->page) { + buf_info->len = frag_len; + buf_info->page = page_pool_alloc(q->page_pool, + &buf_info->page_offset, + &buf_info->len, + GFP_ATOMIC); + if (unlikely(!buf_info->page)) { + buf_info->len = 0; return; } } - sg_elem->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, remain_len, min_t(u32, IONIC_MAX_BUF_LEN, - IONIC_PAGE_SIZE - - buf_info->page_offset)); + sg_elem->addr = cpu_to_le64(ionic_rx_buf_pa(buf_info)); sg_elem->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; @@ -483,18 +862,16 @@ void ionic_rx_fill(struct ionic_queue *q) } /* clear end sg element as a sentinel */ - if (j < q->max_sg_elems) { - sg_elem = &sg_desc->elems[j]; + if (j < q->max_sg_elems) memset(sg_elem, 0, sizeof(*sg_elem)); - } desc->opcode = (nfrags > 1) ? IONIC_RXQ_DESC_OPCODE_SG : IONIC_RXQ_DESC_OPCODE_SIMPLE; desc_info->nbufs = nfrags; - ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); + ionic_write_cmb_desc(q, desc); - ionic_rxq_post(q, false, ionic_rx_clean, NULL); + ionic_rxq_post(q, false); } ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, @@ -502,28 +879,18 @@ void ionic_rx_fill(struct ionic_queue *q) q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE; q->dbell_jiffies = jiffies; - - mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline, - jiffies + IONIC_NAPI_DEADLINE); } void ionic_rx_empty(struct ionic_queue *q) { - struct ionic_desc_info *desc_info; - struct ionic_buf_info *buf_info; + struct ionic_rx_desc_info *desc_info; unsigned int i, j; for (i = 0; i < q->num_descs; i++) { - desc_info = &q->info[i]; - for (j = 0; j < IONIC_RX_MAX_SG_ELEMS + 1; j++) { - buf_info = &desc_info->bufs[j]; - if (buf_info->page) - ionic_rx_page_free(q, buf_info); - } - + desc_info = &q->rx_info[i]; + for (j = 0; j < ARRAY_SIZE(desc_info->bufs); j++) + ionic_rx_put_buf(q, &desc_info->bufs[j]); desc_info->nbufs = 0; - desc_info->cb = NULL; - desc_info->cb_arg = NULL; } q->head_idx = 0; @@ -561,23 +928,17 @@ static void ionic_dim_update(struct ionic_qcq *qcq, int napi_mode) dim_update_sample(qcq->cq.bound_intr->rearm_count, pkts, bytes, &dim_sample); - net_dim(&qcq->dim, dim_sample); + net_dim(&qcq->dim, &dim_sample); } int ionic_tx_napi(struct napi_struct *napi, int budget) { struct ionic_qcq *qcq = napi_to_qcq(napi); struct ionic_cq *cq = napi_to_cq(napi); - struct ionic_dev *idev; - struct ionic_lif *lif; u32 work_done = 0; u32 flags = 0; - lif = cq->bound_q->lif; - idev = &lif->ionic->idev; - - work_done = ionic_cq_service(cq, budget, - ionic_tx_service, NULL, NULL); + work_done = ionic_tx_cq_service(cq, budget, !!budget); if (unlikely(!budget)) return budget; @@ -590,13 +951,47 @@ int ionic_tx_napi(struct napi_struct *napi, int budget) if (work_done || flags) { flags |= IONIC_INTR_CRED_RESET_COALESCE; - ionic_intr_credits(idev->intr_ctrl, + ionic_intr_credits(cq->idev->intr_ctrl, cq->bound_intr->index, work_done, flags); } - if (!work_done && ionic_txq_poke_doorbell(&qcq->q)) - mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); + if (!work_done && cq->bound_q->lif->doorbell_wa) + ionic_txq_poke_doorbell(&qcq->q); + + return work_done; +} + +static void ionic_xdp_do_flush(struct ionic_cq *cq) +{ + if (cq->bound_q->xdp_flush) { + xdp_do_flush(); + cq->bound_q->xdp_flush = false; + } +} + +static unsigned int ionic_rx_cq_service(struct ionic_cq *cq, + unsigned int work_to_do) +{ + struct ionic_queue *q = cq->bound_q; + unsigned int work_done = 0; + struct bpf_prog *xdp_prog; + + if (work_to_do == 0) + return 0; + + xdp_prog = READ_ONCE(q->xdp_prog); + while (__ionic_rx_service(cq, xdp_prog)) { + if (cq->tail_idx == cq->num_descs - 1) + cq->done_color = !cq->done_color; + + cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1); + + if (++work_done >= work_to_do) + break; + } + ionic_rx_fill(q, xdp_prog); + ionic_xdp_do_flush(cq); return work_done; } @@ -605,21 +1000,13 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) { struct ionic_qcq *qcq = napi_to_qcq(napi); struct ionic_cq *cq = napi_to_cq(napi); - struct ionic_dev *idev; - struct ionic_lif *lif; u32 work_done = 0; u32 flags = 0; if (unlikely(!budget)) return budget; - lif = cq->bound_q->lif; - idev = &lif->ionic->idev; - - work_done = ionic_cq_service(cq, budget, - ionic_rx_service, NULL, NULL); - - ionic_rx_fill(cq->bound_q); + work_done = ionic_rx_cq_service(cq, budget); if (work_done < budget && napi_complete_done(napi, work_done)) { ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); @@ -629,13 +1016,13 @@ int ionic_rx_napi(struct napi_struct *napi, int budget) if (work_done || flags) { flags |= IONIC_INTR_CRED_RESET_COALESCE; - ionic_intr_credits(idev->intr_ctrl, + ionic_intr_credits(cq->idev->intr_ctrl, cq->bound_intr->index, work_done, flags); } - if (!work_done && ionic_rxq_poke_doorbell(&qcq->q)) - mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); + if (!work_done && cq->bound_q->lif->doorbell_wa) + ionic_rxq_poke_doorbell(&qcq->q); return work_done; } @@ -646,29 +1033,22 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) struct ionic_cq *rxcq = napi_to_cq(napi); unsigned int qi = rxcq->bound_q->index; struct ionic_qcq *txqcq; - struct ionic_dev *idev; struct ionic_lif *lif; struct ionic_cq *txcq; - bool resched = false; u32 rx_work_done = 0; u32 tx_work_done = 0; u32 flags = 0; lif = rxcq->bound_q->lif; - idev = &lif->ionic->idev; txqcq = lif->txqcqs[qi]; txcq = &lif->txqcqs[qi]->cq; - tx_work_done = ionic_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT, - ionic_tx_service, NULL, NULL); + tx_work_done = ionic_tx_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT, !!budget); if (unlikely(!budget)) return budget; - rx_work_done = ionic_cq_service(rxcq, budget, - ionic_rx_service, NULL, NULL); - - ionic_rx_fill(rxcq->bound_q); + rx_work_done = ionic_rx_cq_service(rxcq, budget); if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { ionic_dim_update(rxqcq, 0); @@ -678,16 +1058,16 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) if (rx_work_done || flags) { flags |= IONIC_INTR_CRED_RESET_COALESCE; - ionic_intr_credits(idev->intr_ctrl, rxcq->bound_intr->index, + ionic_intr_credits(rxcq->idev->intr_ctrl, rxcq->bound_intr->index, tx_work_done + rx_work_done, flags); } - if (!rx_work_done && ionic_rxq_poke_doorbell(&rxqcq->q)) - resched = true; - if (!tx_work_done && ionic_txq_poke_doorbell(&txqcq->q)) - resched = true; - if (resched) - mod_timer(&rxqcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE); + if (lif->doorbell_wa) { + if (!rx_work_done) + ionic_rxq_poke_doorbell(&rxqcq->q); + if (!tx_work_done) + ionic_txq_poke_doorbell(&txqcq->q); + } return rx_work_done; } @@ -695,15 +1075,14 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) static dma_addr_t ionic_tx_map_single(struct ionic_queue *q, void *data, size_t len) { - struct ionic_tx_stats *stats = q_to_tx_stats(q); struct device *dev = q->dev; dma_addr_t dma_addr; dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma_addr)) { + if (unlikely(dma_mapping_error(dev, dma_addr))) { net_warn_ratelimited("%s: DMA single map failed on %s!\n", - q->lif->netdev->name, q->name); - stats->dma_map_err++; + dev_name(dev), q->name); + q_to_tx_stats(q)->dma_map_err++; return 0; } return dma_addr; @@ -713,24 +1092,23 @@ static dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, const skb_frag_t *frag, size_t offset, size_t len) { - struct ionic_tx_stats *stats = q_to_tx_stats(q); struct device *dev = q->dev; dma_addr_t dma_addr; dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma_addr)) { + if (unlikely(dma_mapping_error(dev, dma_addr))) { net_warn_ratelimited("%s: DMA frag map failed on %s!\n", - q->lif->netdev->name, q->name); - stats->dma_map_err++; + dev_name(dev), q->name); + q_to_tx_stats(q)->dma_map_err++; + return 0; } return dma_addr; } static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) + struct ionic_tx_desc_info *desc_info) { struct ionic_buf_info *buf_info = desc_info->bufs; - struct ionic_tx_stats *stats = q_to_tx_stats(q); struct device *dev = q->dev; dma_addr_t dma_addr; unsigned int nfrags; @@ -738,10 +1116,8 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, int frag_idx; dma_addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb)); - if (dma_mapping_error(dev, dma_addr)) { - stats->dma_map_err++; + if (!dma_addr) return -EIO; - } buf_info->dma_addr = dma_addr; buf_info->len = skb_headlen(skb); buf_info++; @@ -750,10 +1126,8 @@ static int ionic_tx_map_skb(struct ionic_queue *q, struct sk_buff *skb, nfrags = skb_shinfo(skb)->nr_frags; for (frag_idx = 0; frag_idx < nfrags; frag_idx++, frag++) { dma_addr = ionic_tx_map_frag(q, frag, 0, skb_frag_size(frag)); - if (dma_mapping_error(dev, dma_addr)) { - stats->dma_map_err++; + if (!dma_addr) goto dma_fail; - } buf_info->dma_addr = dma_addr; buf_info->len = skb_frag_size(frag); buf_info++; @@ -771,12 +1145,13 @@ dma_fail: dma_unmap_page(dev, buf_info->dma_addr, buf_info->len, DMA_TO_DEVICE); } - dma_unmap_single(dev, buf_info->dma_addr, buf_info->len, DMA_TO_DEVICE); + dma_unmap_single(dev, desc_info->bufs[0].dma_addr, + desc_info->bufs[0].len, DMA_TO_DEVICE); return -EIO; } static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, - struct ionic_desc_info *desc_info) + struct ionic_tx_desc_info *desc_info) { struct ionic_buf_info *buf_info = desc_info->bufs; struct device *dev = q->dev; @@ -785,41 +1160,49 @@ static void ionic_tx_desc_unmap_bufs(struct ionic_queue *q, if (!desc_info->nbufs) return; - dma_unmap_single(dev, (dma_addr_t)buf_info->dma_addr, + dma_unmap_single(dev, buf_info->dma_addr, buf_info->len, DMA_TO_DEVICE); buf_info++; for (i = 1; i < desc_info->nbufs; i++, buf_info++) - dma_unmap_page(dev, (dma_addr_t)buf_info->dma_addr, + dma_unmap_page(dev, buf_info->dma_addr, buf_info->len, DMA_TO_DEVICE); desc_info->nbufs = 0; } static void ionic_tx_clean(struct ionic_queue *q, - struct ionic_desc_info *desc_info, - struct ionic_cq_info *cq_info, - void *cb_arg) + struct ionic_tx_desc_info *desc_info, + struct ionic_txq_comp *comp, + bool in_napi) { struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_qcq *qcq = q_to_qcq(q); - struct sk_buff *skb = cb_arg; - u16 qi; + struct sk_buff *skb; + + if (desc_info->xdpf) { + ionic_xdp_tx_desc_clean(q->partner, desc_info, in_napi); + stats->clean++; + + if (unlikely(__netif_subqueue_stopped(q->lif->netdev, q->index))) + netif_wake_subqueue(q->lif->netdev, q->index); + + return; + } ionic_tx_desc_unmap_bufs(q, desc_info); + skb = desc_info->skb; if (!skb) return; - qi = skb_get_queue_mapping(skb); - - if (ionic_txq_hwstamp_enabled(q)) { - if (cq_info) { + if (unlikely(ionic_txq_hwstamp_enabled(q))) { + if (comp) { struct skb_shared_hwtstamps hwts = {}; __le64 *cq_desc_hwstamp; u64 hwstamp; cq_desc_hwstamp = - cq_info->cq_desc + + (void *)comp + qcq->cq.desc_size - sizeof(struct ionic_txq_comp) - IONIC_HWSTAMP_CQ_NEGOFFSET; @@ -837,27 +1220,27 @@ static void ionic_tx_clean(struct ionic_queue *q, stats->hwstamp_invalid++; } } - - } else if (unlikely(__netif_subqueue_stopped(q->lif->netdev, qi))) { - netif_wake_subqueue(q->lif->netdev, qi); } desc_info->bytes = skb->len; stats->clean++; - dev_consume_skb_any(skb); + napi_consume_skb(skb, likely(in_napi) ? 1 : 0); } -bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) +static bool ionic_tx_service(struct ionic_cq *cq, + unsigned int *total_pkts, + unsigned int *total_bytes, + bool in_napi) { + struct ionic_tx_desc_info *desc_info; struct ionic_queue *q = cq->bound_q; - struct ionic_desc_info *desc_info; struct ionic_txq_comp *comp; - int bytes = 0; - int pkts = 0; + unsigned int bytes = 0; + unsigned int pkts = 0; u16 index; - comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp); + comp = &((struct ionic_txq_comp *)cq->base)[cq->tail_idx]; if (!color_match(comp->color, cq->done_color)) return false; @@ -866,59 +1249,92 @@ bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) * several q entries completed for each cq completion */ do { - desc_info = &q->info[q->tail_idx]; + desc_info = &q->tx_info[q->tail_idx]; desc_info->bytes = 0; index = q->tail_idx; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); - ionic_tx_clean(q, desc_info, cq_info, desc_info->cb_arg); - if (desc_info->cb_arg) { + ionic_tx_clean(q, desc_info, comp, in_napi); + if (desc_info->skb) { pkts++; bytes += desc_info->bytes; + desc_info->skb = NULL; } - desc_info->cb = NULL; - desc_info->cb_arg = NULL; } while (index != le16_to_cpu(comp->comp_index)); - if (pkts && bytes && !ionic_txq_hwstamp_enabled(q)) - netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); + (*total_pkts) += pkts; + (*total_bytes) += bytes; return true; } +unsigned int ionic_tx_cq_service(struct ionic_cq *cq, + unsigned int work_to_do, + bool in_napi) +{ + unsigned int work_done = 0; + unsigned int bytes = 0; + unsigned int pkts = 0; + + if (work_to_do == 0) + return 0; + + while (ionic_tx_service(cq, &pkts, &bytes, in_napi)) { + if (cq->tail_idx == cq->num_descs - 1) + cq->done_color = !cq->done_color; + cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1); + + if (++work_done >= work_to_do) + break; + } + + if (work_done) { + struct ionic_queue *q = cq->bound_q; + + if (likely(!ionic_txq_hwstamp_enabled(q))) + netif_txq_completed_wake(q_to_ndq(q->lif->netdev, q), + pkts, bytes, + ionic_q_space_avail(q), + IONIC_TSO_DESCS_NEEDED); + } + + return work_done; +} + void ionic_tx_flush(struct ionic_cq *cq) { - struct ionic_dev *idev = &cq->lif->ionic->idev; u32 work_done; - work_done = ionic_cq_service(cq, cq->num_descs, - ionic_tx_service, NULL, NULL); + work_done = ionic_tx_cq_service(cq, cq->num_descs, false); if (work_done) - ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, + ionic_intr_credits(cq->idev->intr_ctrl, cq->bound_intr->index, work_done, IONIC_INTR_CRED_RESET_COALESCE); } void ionic_tx_empty(struct ionic_queue *q) { - struct ionic_desc_info *desc_info; + struct ionic_tx_desc_info *desc_info; int bytes = 0; int pkts = 0; /* walk the not completed tx entries, if any */ while (q->head_idx != q->tail_idx) { - desc_info = &q->info[q->tail_idx]; + desc_info = &q->tx_info[q->tail_idx]; desc_info->bytes = 0; q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); - ionic_tx_clean(q, desc_info, NULL, desc_info->cb_arg); - if (desc_info->cb_arg) { + ionic_tx_clean(q, desc_info, NULL, false); + if (desc_info->skb) { pkts++; bytes += desc_info->bytes; + desc_info->skb = NULL; } - desc_info->cb = NULL; - desc_info->cb_arg = NULL; } - if (pkts && bytes && !ionic_txq_hwstamp_enabled(q)) - netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); + if (likely(!ionic_txq_hwstamp_enabled(q))) { + struct netdev_queue *ndq = q_to_ndq(q->lif->netdev, q); + + netdev_tx_completed_queue(ndq, pkts, bytes); + netdev_tx_reset_queue(ndq); + } } static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) @@ -926,7 +1342,7 @@ static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) int err; err = skb_cow_head(skb, 0); - if (err) + if (unlikely(err)) return err; if (skb->protocol == cpu_to_be16(ETH_P_IP)) { @@ -950,7 +1366,7 @@ static int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb) int err; err = skb_cow_head(skb, 0); - if (err) + if (unlikely(err)) return err; if (skb->protocol == cpu_to_be16(ETH_P_IP)) { @@ -966,8 +1382,8 @@ static int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb) return 0; } -static void ionic_tx_tso_post(struct ionic_queue *q, - struct ionic_desc_info *desc_info, +static void ionic_tx_tso_post(struct net_device *netdev, struct ionic_queue *q, + struct ionic_txq_desc *desc, struct sk_buff *skb, dma_addr_t addr, u8 nsge, u16 len, unsigned int hdrlen, unsigned int mss, @@ -975,7 +1391,6 @@ static void ionic_tx_tso_post(struct ionic_queue *q, u16 vlan_tci, bool has_vlan, bool start, bool done) { - struct ionic_txq_desc *desc = desc_info->desc; u8 flags = 0; u64 cmd; @@ -991,22 +1406,23 @@ static void ionic_tx_tso_post(struct ionic_queue *q, desc->hdr_len = cpu_to_le16(hdrlen); desc->mss = cpu_to_le16(mss); - ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); + ionic_write_cmb_desc(q, desc); if (start) { skb_tx_timestamp(skb); - if (!ionic_txq_hwstamp_enabled(q)) - netdev_tx_sent_queue(q_to_ndq(q), skb->len); - ionic_txq_post(q, false, ionic_tx_clean, skb); + if (likely(!ionic_txq_hwstamp_enabled(q))) + netdev_tx_sent_queue(q_to_ndq(netdev, q), skb->len); + ionic_txq_post(q, false); } else { - ionic_txq_post(q, done, NULL, NULL); + ionic_txq_post(q, done); } } -static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) +static int ionic_tx_tso(struct net_device *netdev, struct ionic_queue *q, + struct sk_buff *skb) { struct ionic_tx_stats *stats = q_to_tx_stats(q); - struct ionic_desc_info *desc_info; + struct ionic_tx_desc_info *desc_info; struct ionic_buf_info *buf_info; struct ionic_txq_sg_elem *elem; struct ionic_txq_desc *desc; @@ -1028,8 +1444,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) bool encap; int err; - desc_info = &q->info[q->head_idx]; - buf_info = desc_info->bufs; + desc_info = &q->tx_info[q->head_idx]; if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) return -EIO; @@ -1055,7 +1470,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) err = ionic_tx_tcp_inner_pseudo_csum(skb); else err = ionic_tx_tcp_pseudo_csum(skb); - if (err) { + if (unlikely(err)) { /* clean up mapping from ionic_tx_map_skb */ ionic_tx_desc_unmap_bufs(q, desc_info); return err; @@ -1066,6 +1481,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) else hdrlen = skb_tcp_all_headers(skb); + desc_info->skb = skb; + buf_info = desc_info->bufs; tso_rem = len; seg_rem = min(tso_rem, hdrlen + mss); @@ -1092,8 +1509,8 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) chunk_len = min(frag_rem, seg_rem); if (!desc) { /* fill main descriptor */ - desc = desc_info->txq_desc; - elem = desc_info->txq_sg_desc->elems; + desc = &q->txq[q->head_idx]; + elem = ionic_tx_sg_elems(q); desc_addr = frag_addr; desc_len = chunk_len; } else { @@ -1111,13 +1528,12 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) seg_rem = min(tso_rem, mss); done = (tso_rem == 0); /* post descriptor */ - ionic_tx_tso_post(q, desc_info, skb, - desc_addr, desc_nsge, desc_len, - hdrlen, mss, outer_csum, vlan_tci, has_vlan, - start, done); + ionic_tx_tso_post(netdev, q, desc, skb, desc_addr, desc_nsge, + desc_len, hdrlen, mss, outer_csum, vlan_tci, + has_vlan, start, done); start = false; /* Buffer information is stored with the first tso descriptor */ - desc_info = &q->info[q->head_idx]; + desc_info = &q->tx_info[q->head_idx]; desc_info->nbufs = 0; } @@ -1130,9 +1546,9 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) } static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) + struct ionic_tx_desc_info *desc_info) { - struct ionic_txq_desc *desc = desc_info->txq_desc; + struct ionic_txq_desc *desc = &q->txq[q->head_idx]; struct ionic_buf_info *buf_info = desc_info->bufs; struct ionic_tx_stats *stats = q_to_tx_stats(q); bool has_vlan; @@ -1160,7 +1576,7 @@ static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, desc->csum_start = cpu_to_le16(skb_checksum_start_offset(skb)); desc->csum_offset = cpu_to_le16(skb->csum_offset); - ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); + ionic_write_cmb_desc(q, desc); if (skb_csum_is_sctp(skb)) stats->crc32_csum++; @@ -1169,9 +1585,9 @@ static void ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb, } static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) + struct ionic_tx_desc_info *desc_info) { - struct ionic_txq_desc *desc = desc_info->txq_desc; + struct ionic_txq_desc *desc = &q->txq[q->head_idx]; struct ionic_buf_info *buf_info = desc_info->bufs; struct ionic_tx_stats *stats = q_to_tx_stats(q); bool has_vlan; @@ -1199,20 +1615,20 @@ static void ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb, desc->csum_start = 0; desc->csum_offset = 0; - ionic_write_cmb_desc(q, desc_info->cmb_desc, desc); + ionic_write_cmb_desc(q, desc); stats->csum_none++; } static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, - struct ionic_desc_info *desc_info) + struct ionic_tx_desc_info *desc_info) { - struct ionic_txq_sg_desc *sg_desc = desc_info->txq_sg_desc; struct ionic_buf_info *buf_info = &desc_info->bufs[1]; - struct ionic_txq_sg_elem *elem = sg_desc->elems; struct ionic_tx_stats *stats = q_to_tx_stats(q); + struct ionic_txq_sg_elem *elem; unsigned int i; + elem = ionic_tx_sg_elems(q); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, buf_info++, elem++) { elem->addr = cpu_to_le64(buf_info->dma_addr); elem->len = cpu_to_le16(buf_info->len); @@ -1221,14 +1637,18 @@ static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, stats->frags += skb_shinfo(skb)->nr_frags; } -static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) +static int ionic_tx(struct net_device *netdev, struct ionic_queue *q, + struct sk_buff *skb) { - struct ionic_desc_info *desc_info = &q->info[q->head_idx]; + struct ionic_tx_desc_info *desc_info = &q->tx_info[q->head_idx]; struct ionic_tx_stats *stats = q_to_tx_stats(q); + bool ring_dbell = true; if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) return -EIO; + desc_info->skb = skb; + /* set up the initial descriptor */ if (skb->ip_summed == CHECKSUM_PARTIAL) ionic_tx_calc_csum(q, skb, desc_info); @@ -1242,16 +1662,22 @@ static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) stats->pkts++; stats->bytes += skb->len; - if (!ionic_txq_hwstamp_enabled(q)) - netdev_tx_sent_queue(q_to_ndq(q), skb->len); - ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb); + if (likely(!ionic_txq_hwstamp_enabled(q))) { + struct netdev_queue *ndq = q_to_ndq(netdev, q); + + if (unlikely(!ionic_q_has_space(q, MAX_SKB_FRAGS + 1))) + netif_tx_stop_queue(ndq); + ring_dbell = __netdev_tx_sent_queue(ndq, skb->len, + netdev_xmit_more()); + } + ionic_txq_post(q, ring_dbell); return 0; } static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) { - struct ionic_tx_stats *stats = q_to_tx_stats(q); + int nr_frags = skb_shinfo(skb)->nr_frags; bool too_many_frags = false; skb_frag_t *frag; int desc_bufs; @@ -1267,17 +1693,20 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) /* Each desc is mss long max, so a descriptor for each gso_seg */ if (skb_is_gso(skb)) { ndescs = skb_shinfo(skb)->gso_segs; + if (!nr_frags) + return ndescs; } else { ndescs = 1; - if (skb_shinfo(skb)->nr_frags > q->max_sg_elems) { + if (!nr_frags) + return ndescs; + + if (unlikely(nr_frags > q->max_sg_elems)) { too_many_frags = true; goto linearize; } - } - /* If non-TSO, or no frags to check, we're done */ - if (!skb_is_gso(skb) || !skb_shinfo(skb)->nr_frags) return ndescs; + } /* We need to scan the skb to be sure that none of the MTU sized * packets in the TSO will require more sgs per descriptor than we @@ -1326,38 +1755,19 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) linearize: if (too_many_frags) { err = skb_linearize(skb); - if (err) + if (unlikely(err)) return err; - stats->linearize++; + q_to_tx_stats(q)->linearize++; } return ndescs; } -static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs) -{ - int stopped = 0; - - if (unlikely(!ionic_q_has_space(q, ndescs))) { - netif_stop_subqueue(q->lif->netdev, q->index); - stopped = 1; - - /* Might race with ionic_tx_clean, check again */ - smp_rmb(); - if (ionic_q_has_space(q, ndescs)) { - netif_wake_subqueue(q->lif->netdev, q->index); - stopped = 0; - } - } - - return stopped; -} - static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ionic_lif *lif = netdev_priv(netdev); - struct ionic_queue *q = &lif->hwstamp_txq->q; + struct ionic_queue *q; int err, ndescs; /* Does not stop/start txq, because we post to a separate tx queue @@ -1365,6 +1775,7 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, * the timestamping queue, it is dropped. */ + q = &lif->hwstamp_txq->q; ndescs = ionic_tx_descs_needed(q, skb); if (unlikely(ndescs < 0)) goto err_out_drop; @@ -1374,11 +1785,11 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, skb_shinfo(skb)->tx_flags |= SKBTX_HW_TSTAMP; if (skb_is_gso(skb)) - err = ionic_tx_tso(q, skb); + err = ionic_tx_tso(netdev, q, skb); else - err = ionic_tx(q, skb); + err = ionic_tx(netdev, q, skb); - if (err) + if (unlikely(err)) goto err_out_drop; return NETDEV_TX_OK; @@ -1414,23 +1825,19 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) if (ndescs < 0) goto err_out_drop; - if (unlikely(ionic_maybe_stop_tx(q, ndescs))) + if (!netif_txq_maybe_stop(q_to_ndq(netdev, q), + ionic_q_space_avail(q), + ndescs, ndescs)) return NETDEV_TX_BUSY; if (skb_is_gso(skb)) - err = ionic_tx_tso(q, skb); + err = ionic_tx_tso(netdev, q, skb); else - err = ionic_tx(q, skb); + err = ionic_tx(netdev, q, skb); - if (err) + if (unlikely(err)) goto err_out_drop; - /* Stop the queue if there aren't descriptors for the next packet. - * Since our SG lists per descriptor take care of most of the possible - * fragmentation, we don't need to have many descriptors available. - */ - ionic_maybe_stop_tx(q, 4); - return NETDEV_TX_OK; err_out_drop: diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h index d7cbaad8a6fb..b2b9a2dc9eb8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h @@ -4,9 +4,11 @@ #ifndef _IONIC_TXRX_H_ #define _IONIC_TXRX_H_ +struct bpf_prog; + void ionic_tx_flush(struct ionic_cq *cq); -void ionic_rx_fill(struct ionic_queue *q); +void ionic_rx_fill(struct ionic_queue *q, struct bpf_prog *xdp_prog); void ionic_rx_empty(struct ionic_queue *q); void ionic_tx_empty(struct ionic_queue *q); int ionic_rx_napi(struct napi_struct *napi, int budget); @@ -14,7 +16,7 @@ int ionic_tx_napi(struct napi_struct *napi, int budget); int ionic_txrx_napi(struct napi_struct *napi, int budget); netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); -bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); -bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); +bool ionic_rx_service(struct ionic_cq *cq); +int ionic_xdp_xmit(struct net_device *netdev, int n, struct xdp_frame **xdp, u32 flags); #endif /* _IONIC_TXRX_H_ */ |