diff options
| author | Sunil Goutham <sgoutham@cavium.com> | 2017-05-02 09:06:54 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-05-02 15:41:21 -0400 |
| commit | 05c773f52b96ef3fbc7d9bfa21caadc6247ef7a8 (patch) | |
| tree | 9d20a83f0b3f08bd3fcd3d0c00920abaa19dfb8c | |
| parent | 927987f39f116db477fcd74ced2a2aea940e585c (diff) | |
net: thunderx: Add basic XDP support
Adds basic XDP support i.e attaching a BPF program to an
interface. Also takes care of allocating separate Tx queues
for XDP path and for network stack packet transmission.
This patch doesn't support handling of any of the XDP actions,
all are treated as XDP_PASS i.e packets will be handed over to
the network stack.
Changes also involve allocating one receive buffer per page in XDP
mode and multiple in normal mode i.e when no BPF program is attached.
Signed-off-by: Sunil Goutham <sgoutham@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | drivers/net/ethernet/cavium/thunder/nic.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c | 26 | ||||
| -rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_main.c | 162 | ||||
| -rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 15 | ||||
| -rw-r--r-- | drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 9 |
5 files changed, 199 insertions, 19 deletions
diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index dca6aed49094..4a02e618e318 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h | |||
| @@ -268,9 +268,9 @@ struct nicvf { | |||
| 268 | struct net_device *netdev; | 268 | struct net_device *netdev; |
| 269 | struct pci_dev *pdev; | 269 | struct pci_dev *pdev; |
| 270 | void __iomem *reg_base; | 270 | void __iomem *reg_base; |
| 271 | struct bpf_prog *xdp_prog; | ||
| 271 | #define MAX_QUEUES_PER_QSET 8 | 272 | #define MAX_QUEUES_PER_QSET 8 |
| 272 | struct queue_set *qs; | 273 | struct queue_set *qs; |
| 273 | struct nicvf_cq_poll *napi[8]; | ||
| 274 | void *iommu_domain; | 274 | void *iommu_domain; |
| 275 | u8 vf_id; | 275 | u8 vf_id; |
| 276 | u8 sqs_id; | 276 | u8 sqs_id; |
| @@ -296,6 +296,7 @@ struct nicvf { | |||
| 296 | /* Queue count */ | 296 | /* Queue count */ |
| 297 | u8 rx_queues; | 297 | u8 rx_queues; |
| 298 | u8 tx_queues; | 298 | u8 tx_queues; |
| 299 | u8 xdp_tx_queues; | ||
| 299 | u8 max_queues; | 300 | u8 max_queues; |
| 300 | 301 | ||
| 301 | u8 node; | 302 | u8 node; |
| @@ -320,6 +321,9 @@ struct nicvf { | |||
| 320 | struct nicvf_drv_stats __percpu *drv_stats; | 321 | struct nicvf_drv_stats __percpu *drv_stats; |
| 321 | struct bgx_stats bgx_stats; | 322 | struct bgx_stats bgx_stats; |
| 322 | 323 | ||
| 324 | /* Napi */ | ||
| 325 | struct nicvf_cq_poll *napi[8]; | ||
| 326 | |||
| 323 | /* MSI-X */ | 327 | /* MSI-X */ |
| 324 | u8 num_vec; | 328 | u8 num_vec; |
| 325 | char irq_name[NIC_VF_MSIX_VECTORS][IFNAMSIZ + 15]; | 329 | char irq_name[NIC_VF_MSIX_VECTORS][IFNAMSIZ + 15]; |
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index a89db5f3e26e..b9ece9cbf98b 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c | |||
| @@ -721,7 +721,7 @@ static int nicvf_set_channels(struct net_device *dev, | |||
| 721 | struct nicvf *nic = netdev_priv(dev); | 721 | struct nicvf *nic = netdev_priv(dev); |
| 722 | int err = 0; | 722 | int err = 0; |
| 723 | bool if_up = netif_running(dev); | 723 | bool if_up = netif_running(dev); |
| 724 | int cqcount; | 724 | u8 cqcount, txq_count; |
| 725 | 725 | ||
| 726 | if (!channel->rx_count || !channel->tx_count) | 726 | if (!channel->rx_count || !channel->tx_count) |
| 727 | return -EINVAL; | 727 | return -EINVAL; |
| @@ -730,10 +730,26 @@ static int nicvf_set_channels(struct net_device *dev, | |||
| 730 | if (channel->tx_count > nic->max_queues) | 730 | if (channel->tx_count > nic->max_queues) |
| 731 | return -EINVAL; | 731 | return -EINVAL; |
| 732 | 732 | ||
| 733 | if (nic->xdp_prog && | ||
| 734 | ((channel->tx_count + channel->rx_count) > nic->max_queues)) { | ||
| 735 | netdev_err(nic->netdev, | ||
| 736 | "XDP mode, RXQs + TXQs > Max %d\n", | ||
| 737 | nic->max_queues); | ||
| 738 | return -EINVAL; | ||
| 739 | } | ||
| 740 | |||
| 733 | if (if_up) | 741 | if (if_up) |
| 734 | nicvf_stop(dev); | 742 | nicvf_stop(dev); |
| 735 | 743 | ||
| 736 | cqcount = max(channel->rx_count, channel->tx_count); | 744 | nic->rx_queues = channel->rx_count; |
| 745 | nic->tx_queues = channel->tx_count; | ||
| 746 | if (!nic->xdp_prog) | ||
| 747 | nic->xdp_tx_queues = 0; | ||
| 748 | else | ||
| 749 | nic->xdp_tx_queues = channel->rx_count; | ||
| 750 | |||
| 751 | txq_count = nic->xdp_tx_queues + nic->tx_queues; | ||
| 752 | cqcount = max(nic->rx_queues, txq_count); | ||
| 737 | 753 | ||
| 738 | if (cqcount > MAX_CMP_QUEUES_PER_QS) { | 754 | if (cqcount > MAX_CMP_QUEUES_PER_QS) { |
| 739 | nic->sqs_count = roundup(cqcount, MAX_CMP_QUEUES_PER_QS); | 755 | nic->sqs_count = roundup(cqcount, MAX_CMP_QUEUES_PER_QS); |
| @@ -742,12 +758,10 @@ static int nicvf_set_channels(struct net_device *dev, | |||
| 742 | nic->sqs_count = 0; | 758 | nic->sqs_count = 0; |
| 743 | } | 759 | } |
| 744 | 760 | ||
| 745 | nic->qs->rq_cnt = min_t(u32, channel->rx_count, MAX_RCV_QUEUES_PER_QS); | 761 | nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS); |
| 746 | nic->qs->sq_cnt = min_t(u32, channel->tx_count, MAX_SND_QUEUES_PER_QS); | 762 | nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS); |
| 747 | nic->qs->cq_cnt = max(nic->qs->rq_cnt, nic->qs->sq_cnt); | 763 | nic->qs->cq_cnt = max(nic->qs->rq_cnt, nic->qs->sq_cnt); |
| 748 | 764 | ||
| 749 | nic->rx_queues = channel->rx_count; | ||
| 750 | nic->tx_queues = channel->tx_count; | ||
| 751 | err = nicvf_set_real_num_queues(dev, nic->tx_queues, nic->rx_queues); | 765 | err = nicvf_set_real_num_queues(dev, nic->tx_queues, nic->rx_queues); |
| 752 | if (err) | 766 | if (err) |
| 753 | return err; | 767 | return err; |
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 0d79894400ab..9c48873350f8 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | #include <linux/prefetch.h> | 17 | #include <linux/prefetch.h> |
| 18 | #include <linux/irq.h> | 18 | #include <linux/irq.h> |
| 19 | #include <linux/iommu.h> | 19 | #include <linux/iommu.h> |
| 20 | #include <linux/bpf.h> | ||
| 21 | #include <linux/filter.h> | ||
| 20 | 22 | ||
| 21 | #include "nic_reg.h" | 23 | #include "nic_reg.h" |
| 22 | #include "nic.h" | 24 | #include "nic.h" |
| @@ -397,8 +399,10 @@ static void nicvf_request_sqs(struct nicvf *nic) | |||
| 397 | 399 | ||
| 398 | if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS) | 400 | if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS) |
| 399 | rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS; | 401 | rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS; |
| 400 | if (nic->tx_queues > MAX_SND_QUEUES_PER_QS) | 402 | |
| 401 | tx_queues = nic->tx_queues - MAX_SND_QUEUES_PER_QS; | 403 | tx_queues = nic->tx_queues + nic->xdp_tx_queues; |
| 404 | if (tx_queues > MAX_SND_QUEUES_PER_QS) | ||
| 405 | tx_queues = tx_queues - MAX_SND_QUEUES_PER_QS; | ||
| 402 | 406 | ||
| 403 | /* Set no of Rx/Tx queues in each of the SQsets */ | 407 | /* Set no of Rx/Tx queues in each of the SQsets */ |
| 404 | for (sqs = 0; sqs < nic->sqs_count; sqs++) { | 408 | for (sqs = 0; sqs < nic->sqs_count; sqs++) { |
| @@ -496,6 +500,43 @@ static int nicvf_init_resources(struct nicvf *nic) | |||
| 496 | return 0; | 500 | return 0; |
| 497 | } | 501 | } |
| 498 | 502 | ||
| 503 | static inline bool nicvf_xdp_rx(struct nicvf *nic, | ||
| 504 | struct bpf_prog *prog, | ||
| 505 | struct cqe_rx_t *cqe_rx) | ||
| 506 | { | ||
| 507 | struct xdp_buff xdp; | ||
| 508 | u32 action; | ||
| 509 | u16 len; | ||
| 510 | u64 dma_addr, cpu_addr; | ||
| 511 | |||
| 512 | /* Retrieve packet buffer's DMA address and length */ | ||
| 513 | len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64)))); | ||
| 514 | dma_addr = *((u64 *)((void *)cqe_rx + (7 * sizeof(u64)))); | ||
| 515 | |||
| 516 | cpu_addr = nicvf_iova_to_phys(nic, dma_addr); | ||
| 517 | if (!cpu_addr) | ||
| 518 | return false; | ||
| 519 | |||
| 520 | xdp.data = phys_to_virt(cpu_addr); | ||
| 521 | xdp.data_end = xdp.data + len; | ||
| 522 | |||
| 523 | rcu_read_lock(); | ||
| 524 | action = bpf_prog_run_xdp(prog, &xdp); | ||
| 525 | rcu_read_unlock(); | ||
| 526 | |||
| 527 | switch (action) { | ||
| 528 | case XDP_PASS: | ||
| 529 | case XDP_TX: | ||
| 530 | case XDP_ABORTED: | ||
| 531 | case XDP_DROP: | ||
| 532 | /* Pass on all packets to network stack */ | ||
| 533 | return false; | ||
| 534 | default: | ||
| 535 | bpf_warn_invalid_xdp_action(action); | ||
| 536 | } | ||
| 537 | return false; | ||
| 538 | } | ||
| 539 | |||
| 499 | static void nicvf_snd_pkt_handler(struct net_device *netdev, | 540 | static void nicvf_snd_pkt_handler(struct net_device *netdev, |
| 500 | struct cqe_send_t *cqe_tx, | 541 | struct cqe_send_t *cqe_tx, |
| 501 | int budget, int *subdesc_cnt, | 542 | int budget, int *subdesc_cnt, |
| @@ -599,6 +640,11 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, | |||
| 599 | return; | 640 | return; |
| 600 | } | 641 | |
