1864 lines · 44.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/ip.h>#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 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){	/* Ensure TX descriptor writes reach memory before NIC reads them.	 * Prevents device from fetching stale descriptors.	 */	dma_wmb();	ionic_q_post(q, ring_dbell);} static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell){	ionic_q_post(q, ring_dbell);} bool ionic_txq_poke_doorbell(struct ionic_queue *q){	struct netdev_queue *netdev_txq;	unsigned long now, then, dif;	struct net_device *netdev; 	netdev = q->lif->netdev;	netdev_txq = netdev_get_tx_queue(netdev, q->index); 	HARD_TX_LOCK(netdev, netdev_txq, smp_processor_id()); 	if (q->tail_idx == q->head_idx) {		HARD_TX_UNLOCK(netdev, netdev_txq);		return false;	} 	now = READ_ONCE(jiffies);	then = q->dbell_jiffies;	dif = now - then; 	if (dif > q->dbell_deadline) {		ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,				 q->dbval | q->head_idx); 		q->dbell_jiffies = now;	} 	HARD_TX_UNLOCK(netdev, netdev_txq); 	return true;} bool ionic_rxq_poke_doorbell(struct ionic_queue *q){	unsigned long now, then, dif; 	/* no lock, called from rx napi or txrx napi, nothing else can fill */ 	if (q->tail_idx == q->head_idx)		return false; 	now = READ_ONCE(jiffies);	then = q->dbell_jiffies;	dif = now - then; 	if (dif > q->dbell_deadline) {		ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,				 q->dbval | q->head_idx); 		q->dbell_jiffies = now; 		dif = 2 * q->dbell_deadline;		if (dif > IONIC_RX_MAX_DOORBELL_DEADLINE)			dif = IONIC_RX_MAX_DOORBELL_DEADLINE; 		q->dbell_deadline = dif;	} 	return true;} static inline struct ionic_txq_sg_elem *ionic_tx_sg_elems(struct ionic_queue *q){	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 inline struct netdev_queue *q_to_ndq(struct net_device *netdev,					    struct ionic_queue *q){	return netdev_get_tx_queue(netdev, q->index);} static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info){	return page_address(buf_info->page) + buf_info->page_offset;} 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; 	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 void ionic_rx_put_buf(struct ionic_queue *q,			     struct ionic_buf_info *buf_info){	__ionic_rx_put_buf(q, buf_info, false);} 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);} 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_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 ionic_buf_info *buf_info;	struct sk_buff *skb;	unsigned int i;	u16 frag_len; 	buf_info = &desc_info->bufs[0];	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",				     dev_name(q->dev), q->name);		q_to_rx_stats(q)->alloc_err++;		return NULL;	}	skb_mark_for_recycle(skb); 	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); 	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++; 	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 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 ionic_buf_info *buf_info;	struct device *dev = q->dev;	struct sk_buff *skb;	int i; 	buf_info = &desc_info->bufs[0]; 	skb = napi_alloc_skb(&q_to_qcq(q)->napi, len);	if (unlikely(!skb)) {		net_warn_ratelimited("%s: SKB alloc failed on %s!\n",				     dev_name(dev), q->name);		q_to_rx_stats(q)->alloc_err++;		return NULL;	}	skb_mark_for_recycle(skb); 	if (!synced)		page_pool_dma_sync_for_cpu(q->page_pool,					   buf_info->page,					   buf_info->page_offset + headroom,					   len); 	skb_copy_to_linear_data(skb, ionic_rx_buf_va(buf_info) + headroom, len); 	skb_put(skb, len);	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 == DMA_MAPPING_ERROR)			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_addr == DMA_MAPPING_ERROR) {					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;

Lines 1–500 of 1864.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t