Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 12/13] nfp: remove unnecessary parameters from nfp_net_bpf_offload()
From: Jakub Kicinski @ 2016-11-03 13:58 UTC (permalink / raw)
  To: netdev; +Cc: Jakub Kicinski
In-Reply-To: <1478181538-20778-1-git-send-email-jakub.kicinski@netronome.com>

nfp_net_bpf_offload() takes all .setup_tc() parameters but it
doesn't use them at the moment.  Remove unnecessary ones to make
it possible for XDP to reuse this function.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 drivers/net/ethernet/netronome/nfp/nfp_net.h         | 4 +---
 drivers/net/ethernet/netronome/nfp/nfp_net_common.c  | 2 +-
 drivers/net/ethernet/netronome/nfp/nfp_net_offload.c | 4 +---
 3 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index abc9e56e93b8..fd29a6306991 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -817,8 +817,6 @@ static inline void nfp_net_debugfs_adapter_del(struct nfp_net *nn)
 #endif /* CONFIG_NFP_NET_DEBUG */
 
 void nfp_net_filter_stats_timer(unsigned long data);
-int
-nfp_net_bpf_offload(struct nfp_net *nn, u32 handle, __be16 proto,
-		    struct tc_cls_bpf_offload *cls_bpf);
+int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf);
 
 #endif /* _NFP_NET_H_ */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index fa43dbcecc4f..1e8e00d25c51 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -2694,7 +2694,7 @@ static bool nfp_net_ebpf_capable(struct nfp_net *nn)
 		return -ENOTSUPP;
 
 	if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn))
-		return nfp_net_bpf_offload(nn, handle, proto, tc->cls_bpf);
+		return nfp_net_bpf_offload(nn, tc->cls_bpf);
 
 	return -EINVAL;
 }
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
index cfed40c0e310..4bb6f16e2a7a 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
@@ -233,9 +233,7 @@ static int nfp_net_bpf_stop(struct nfp_net *nn)
 	return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN);
 }
 
-int
-nfp_net_bpf_offload(struct nfp_net *nn, u32 handle, __be16 proto,
-		    struct tc_cls_bpf_offload *cls_bpf)
+int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf)
 {
 	struct nfp_bpf_result res;
 	dma_addr_t dma_addr;
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 11/13] nfp: add XDP support in the driver
From: Jakub Kicinski @ 2016-11-03 13:58 UTC (permalink / raw)
  To: netdev; +Cc: Jakub Kicinski
In-Reply-To: <1478181538-20778-1-git-send-email-jakub.kicinski@netronome.com>

Add XDP support.  Separate stack's and XDP's TX rings logically.
Add functions for handling XDP_TX and cleanup of XDP's TX rings.
For XDP allocate all RX buffers as separate pages and map them
with DMA_BIDIRECTIONAL.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 drivers/net/ethernet/netronome/nfp/nfp_net.h       |  17 +-
 .../net/ethernet/netronome/nfp/nfp_net_common.c    | 474 +++++++++++++++++----
 .../net/ethernet/netronome/nfp/nfp_net_debugfs.c   |  37 +-
 .../net/ethernet/netronome/nfp/nfp_net_ethtool.c   |  22 +-
 4 files changed, 449 insertions(+), 101 deletions(-)

diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index 486e7c6453bc..abc9e56e93b8 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -171,7 +171,10 @@ struct nfp_net_tx_desc {
  *		on the head's buffer). Equal to skb->len for non-TSO packets.
  */
 struct nfp_net_tx_buf {
-	struct sk_buff *skb;
+	union {
+		struct sk_buff *skb;
+		void *frag;
+	};
 	dma_addr_t dma_addr;
 	short int fidx;
 	u16 pkt_cnt;
@@ -341,6 +344,7 @@ struct nfp_net_rx_ring {
  * @napi:           NAPI structure for this ring vec
  * @tx_ring:        Pointer to TX ring
  * @rx_ring:        Pointer to RX ring
+ * @xdp_ring:	    Pointer to an extra TX ring for XDP
  * @irq_idx:        Index into MSI-X table
  * @rx_sync:	    Seqlock for atomic updates of RX stats
  * @rx_pkts:        Number of received packets
@@ -384,6 +388,8 @@ struct nfp_net_r_vector {
 	u64 hw_csum_rx_inner_ok;
 	u64 hw_csum_rx_error;
 
+	struct nfp_net_tx_ring *xdp_ring;
+
 	struct u64_stats_sync tx_sync;
 	u64 tx_pkts;
 	u64 tx_bytes;
@@ -432,6 +438,7 @@ struct nfp_stat_pair {
  * @ctrl:               Local copy of the control register/word.
  * @fl_bufsz:           Currently configured size of the freelist buffers
  * @rx_offset:		Offset in the RX buffers where packet data starts
+ * @xdp_prog:		Installed XDP program
  * @cpp:                Pointer to the CPP handle
  * @nfp_dev_cpp:        Pointer to the NFP Device handle
  * @ctrl_area:          Pointer to the CPP area for the control BAR
@@ -451,6 +458,7 @@ struct nfp_stat_pair {
  * @max_tx_rings:       Maximum number of TX rings supported by the Firmware
  * @max_rx_rings:       Maximum number of RX rings supported by the Firmware
  * @num_tx_rings:       Currently configured number of TX rings
+ * @num_stack_tx_rings:	Number of TX rings used by the stack (not XDP)
  * @num_rx_rings:       Currently configured number of RX rings
  * @txd_cnt:            Size of the TX ring in number of descriptors
  * @rxd_cnt:            Size of the RX ring in number of descriptors
@@ -500,6 +508,8 @@ struct nfp_net {
 
 	u32 rx_offset;
 
+	struct bpf_prog *xdp_prog;
+
 	struct nfp_net_tx_ring *tx_rings;
 	struct nfp_net_rx_ring *rx_rings;
 
@@ -532,6 +542,7 @@ struct nfp_net {
 	unsigned int max_rx_rings;
 
 	unsigned int num_tx_rings;
+	unsigned int num_stack_tx_rings;
 	unsigned int num_rx_rings;
 
 	int stride_tx;
@@ -779,8 +790,8 @@ struct nfp_net *
 int nfp_net_irqs_alloc(struct nfp_net *nn);
 void nfp_net_irqs_disable(struct nfp_net *nn);
 int
-nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx,
-		      struct nfp_net_ring_set *tx);
+nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
+		      struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx);
 
 #ifdef CONFIG_NFP_NET_DEBUG
 void nfp_net_debugfs_create(void);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 2ab63661a6fd..fa43dbcecc4f 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -41,6 +41,7 @@
  *          Chris Telfer <chris.telfer@netronome.com>
  */
 
+#include <linux/bpf.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -490,6 +491,7 @@ static void nfp_net_irqs_assign(struct net_device *netdev)
 
 	nn->num_rx_rings = min(nn->num_r_vecs, nn->num_rx_rings);
 	nn->num_tx_rings = min(nn->num_r_vecs, nn->num_tx_rings);
+	nn->num_stack_tx_rings = nn->num_tx_rings;
 
 	nn->lsc_handler = nfp_net_irq_lsc;
 	nn->exn_handler = nfp_net_irq_exn;
@@ -713,6 +715,13 @@ static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec,
 	u64_stats_update_end(&r_vec->tx_sync);
 }
 
+static void nfp_net_tx_xmit_more_flush(struct nfp_net_tx_ring *tx_ring)
+{
+	wmb();
+	nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add);
+	tx_ring->wr_ptr_add = 0;
+}
+
 /**
  * nfp_net_tx() - Main transmit entry point
  * @skb:    SKB to transmit
@@ -827,12 +836,8 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev)
 		nfp_net_tx_ring_stop(nd_q, tx_ring);
 
 	tx_ring->wr_ptr_add += nr_frags + 1;
-	if (!skb->xmit_more || netif_xmit_stopped(nd_q)) {
-		/* force memory write before we let HW know */
-		wmb();
-		nfp_qcp_wr_ptr_add(tx_ring->qcp_q, tx_ring->wr_ptr_add);
-		tx_ring->wr_ptr_add = 0;
-	}
+	if (!skb->xmit_more || netif_xmit_stopped(nd_q))
+		nfp_net_tx_xmit_more_flush(tx_ring);
 
 	skb_tx_timestamp(skb);
 
@@ -954,6 +959,56 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
 		  tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
 }
 
+static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring)
+{
+	struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
+	struct nfp_net *nn = r_vec->nfp_net;
+	u32 done_pkts = 0, done_bytes = 0;
+	int idx, todo;
+	u32 qcp_rd_p;
+
+	/* Work out how many descriptors have been transmitted */
+	qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q);
+
+	if (qcp_rd_p == tx_ring->qcp_rd_p)
+		return;
+
+	if (qcp_rd_p > tx_ring->qcp_rd_p)
+		todo = qcp_rd_p - tx_ring->qcp_rd_p;
+	else
+		todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p;
+
+	while (todo--) {
+		idx = tx_ring->rd_p & (tx_ring->cnt - 1);
+		tx_ring->rd_p++;
+
+		if (!tx_ring->txbufs[idx].frag)
+			continue;
+
+		nfp_net_dma_unmap_rx(nn, tx_ring->txbufs[idx].dma_addr,
+				     nn->fl_bufsz, DMA_BIDIRECTIONAL);
+		__free_page(virt_to_page(tx_ring->txbufs[idx].frag));
+
+		done_pkts++;
+		done_bytes += tx_ring->txbufs[idx].real_len;
+
+		tx_ring->txbufs[idx].dma_addr = 0;
+		tx_ring->txbufs[idx].frag = NULL;
+		tx_ring->txbufs[idx].fidx = -2;
+	}
+
+	tx_ring->qcp_rd_p = qcp_rd_p;
+
+	u64_stats_update_begin(&r_vec->tx_sync);
+	r_vec->tx_bytes += done_bytes;
+	r_vec->tx_pkts += done_pkts;
+	u64_stats_update_end(&r_vec->tx_sync);
+
+	WARN_ONCE(tx_ring->wr_p - tx_ring->rd_p > tx_ring->cnt,
+		  "TX ring corruption rd_p=%u wr_p=%u cnt=%u\n",
+		  tx_ring->rd_p, tx_ring->wr_p, tx_ring->cnt);
+}
+
 /**
  * nfp_net_tx_ring_reset() - Free any untransmitted buffers and reset pointers
  * @nn:		NFP Net device
@@ -964,39 +1019,47 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
 static void
 nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring)
 {
+	struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
 	const struct skb_frag_struct *frag;
-	struct netdev_queue *nd_q;
 	struct pci_dev *pdev = nn->pdev;
+	struct netdev_queue *nd_q;
 
 	while (tx_ring->rd_p != tx_ring->wr_p) {
-		int nr_frags, fidx, idx;
-		struct sk_buff *skb;
+		struct nfp_net_tx_buf *tx_buf;
+		int idx;
 
 		idx = tx_ring->rd_p & (tx_ring->cnt - 1);
-		skb = tx_ring->txbufs[idx].skb;
-		nr_frags = skb_shinfo(skb)->nr_frags;
-		fidx = tx_ring->txbufs[idx].fidx;
+		tx_buf = &tx_ring->txbufs[idx];
 
-		if (fidx == -1) {
-			/* unmap head */
-			dma_unmap_single(&pdev->dev,
-					 tx_ring->txbufs[idx].dma_addr,
-					 skb_headlen(skb), DMA_TO_DEVICE);
+		if (tx_ring == r_vec->xdp_ring) {
+			nfp_net_dma_unmap_rx(nn, tx_buf->dma_addr,
+					     nn->fl_bufsz, DMA_BIDIRECTIONAL);
+			__free_page(virt_to_page(tx_ring->txbufs[idx].frag));
 		} else {
-			/* unmap fragment */
-			frag = &skb_shinfo(skb)->frags[fidx];
-			dma_unmap_page(&pdev->dev,
-				       tx_ring->txbufs[idx].dma_addr,
-				       skb_frag_size(frag), DMA_TO_DEVICE);
-		}
+			struct sk_buff *skb = tx_ring->txbufs[idx].skb;
+			int nr_frags = skb_shinfo(skb)->nr_frags;
+
+			if (tx_buf->fidx == -1) {
+				/* unmap head */
+				dma_unmap_single(&pdev->dev, tx_buf->dma_addr,
+						 skb_headlen(skb),
+						 DMA_TO_DEVICE);
+			} else {
+				/* unmap fragment */
+				frag = &skb_shinfo(skb)->frags[tx_buf->fidx];
+				dma_unmap_page(&pdev->dev, tx_buf->dma_addr,
+					       skb_frag_size(frag),
+					       DMA_TO_DEVICE);
+			}
 
-		/* check for last gather fragment */
-		if (fidx == nr_frags - 1)
-			dev_kfree_skb_any(skb);
+			/* check for last gather fragment */
+			if (tx_buf->fidx == nr_frags - 1)
+				dev_kfree_skb_any(skb);
+		}
 
-		tx_ring->txbufs[idx].dma_addr = 0;
-		tx_ring->txbufs[idx].skb = NULL;
-		tx_ring->txbufs[idx].fidx = -2;
+		tx_buf->dma_addr = 0;
+		tx_buf->skb = NULL;
+		tx_buf->fidx = -2;
 
 		tx_ring->qcp_rd_p++;
 		tx_ring->rd_p++;
@@ -1008,6 +1071,9 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring)
 	tx_ring->qcp_rd_p = 0;
 	tx_ring->wr_ptr_add = 0;
 
+	if (tx_ring == r_vec->xdp_ring)
+		return;
+
 	nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx);
 	netdev_tx_reset_queue(nd_q);
 }
@@ -1017,7 +1083,7 @@ static void nfp_net_tx_timeout(struct net_device *netdev)
 	struct nfp_net *nn = netdev_priv(netdev);
 	int i;
 
-	for (i = 0; i < nn->num_tx_rings; i++) {
+	for (i = 0; i < nn->netdev->real_num_tx_queues; i++) {
 		if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i)))
 			continue;
 		nn_warn(nn, "TX timeout on ring: %d\n", i);
@@ -1045,11 +1111,21 @@ static void nfp_net_tx_timeout(struct net_device *netdev)
 	return fl_bufsz;
 }
 
+static void
+nfp_net_free_frag(void *frag, bool xdp)
+{
+	if (!xdp)
+		skb_free_frag(frag);
+	else
+		__free_page(virt_to_page(frag));
+}
+
 /**
  * nfp_net_rx_alloc_one() - Allocate and map page frag for RX
  * @rx_ring:	RX ring structure of the skb
  * @dma_addr:	Pointer to storage for DMA address (output param)
  * @fl_bufsz:	size of freelist buffers
+ * @xdp:	Whether XDP is enabled
  *
  * This function will allcate a new page frag, map it for DMA.
  *
@@ -1057,20 +1133,26 @@ static void nfp_net_tx_timeout(struct net_device *netdev)
  */
 static void *
 nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr,
-		     unsigned int fl_bufsz)
+		     unsigned int fl_bufsz, bool xdp)
 {
 	struct nfp_net *nn = rx_ring->r_vec->nfp_net;
+	int direction;
 	void *frag;
 
-	frag = netdev_alloc_frag(fl_bufsz);
+	if (!xdp)
+		frag = netdev_alloc_frag(fl_bufsz);
+	else
+		frag = page_address(alloc_page(GFP_KERNEL | __GFP_COLD));
 	if (!frag) {
 		nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n");
 		return NULL;
 	}
 
-	*dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, DMA_FROM_DEVICE);
+	direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
+
+	*dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, direction);
 	if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) {
-		skb_free_frag(frag);
+		nfp_net_free_frag(frag, xdp);
 		nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n");
 		return NULL;
 	}
@@ -1078,19 +1160,23 @@ static void nfp_net_tx_timeout(struct net_device *netdev)
 	return frag;
 }
 
-static void *nfp_net_napi_alloc_one(struct nfp_net *nn, dma_addr_t *dma_addr)
+static void *
+nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr)
 {
 	void *frag;
 
-	frag = napi_alloc_frag(nn->fl_bufsz);
+	if (!nn->xdp_prog)
+		frag = napi_alloc_frag(nn->fl_bufsz);
+	else
+		frag = page_address(alloc_page(GFP_ATOMIC | __GFP_COLD));
 	if (!frag) {
 		nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n");
 		return NULL;
 	}
 
-	*dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, DMA_FROM_DEVICE);
+	*dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, direction);
 	if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) {
-		skb_free_frag(frag);
+		nfp_net_free_frag(frag, nn->xdp_prog);
 		nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n");
 		return NULL;
 	}
@@ -1161,14 +1247,17 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
  * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring
  * @nn:		NFP Net device
  * @rx_ring:	RX ring to remove buffers from
+ * @xdp:	Whether XDP is enabled
  *
  * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1)
  * entries.  After device is disabled nfp_net_rx_ring_reset() must be called
  * to restore required ring geometry.
  */
 static void
-nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring)
+nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
+			  bool xdp)
 {
+	int direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
 	unsigned int i;
 
 	for (i = 0; i < rx_ring->cnt - 1; i++) {
@@ -1180,8 +1269,8 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
 			continue;
 
 		nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[i].dma_addr,
-				     rx_ring->bufsz, DMA_FROM_DEVICE);
-		skb_free_frag(rx_ring->rxbufs[i].frag);
+				     rx_ring->bufsz, direction);
+		nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp);
 		rx_ring->rxbufs[i].dma_addr = 0;
 		rx_ring->rxbufs[i].frag = NULL;
 	}
@@ -1191,9 +1280,11 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
  * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW)
  * @nn:		NFP Net device
  * @rx_ring:	RX ring to remove buffers from
+ * @xdp:	Whether XDP is enabled
  */
 static int
-nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring)
+nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
+			   bool xdp)
 {
 	struct nfp_net_rx_buf *rxbufs;
 	unsigned int i;
@@ -1203,9 +1294,9 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring)
 	for (i = 0; i < rx_ring->cnt - 1; i++) {
 		rxbufs[i].frag =
 			nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr,
-					     rx_ring->bufsz);
+					     rx_ring->bufsz, xdp);
 		if (!rxbufs[i].frag) {
-			nfp_net_rx_ring_bufs_free(nn, rx_ring);
+			nfp_net_rx_ring_bufs_free(nn, rx_ring, xdp);
 			return -ENOMEM;
 		}
 	}
@@ -1368,6 +1459,68 @@ static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb,
 		dev_kfree_skb_any(skb);
 }
 
+static void
+nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring,
+		   struct nfp_net_tx_ring *tx_ring,
+		   struct nfp_net_rx_buf *rxbuf, unsigned int pkt_off,
+		   unsigned int pkt_len)
+{
+	struct nfp_net_tx_buf *txbuf;
+	struct nfp_net_tx_desc *txd;
+	dma_addr_t new_dma_addr;
+	void *new_frag;
+	int wr_idx;
+
+	if (unlikely(nfp_net_tx_full(tx_ring, 1))) {
+		nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL);
+		return;
+	}
+
+	new_frag = nfp_net_napi_alloc_one(nn, DMA_BIDIRECTIONAL, &new_dma_addr);
+	if (unlikely(!new_frag)) {
+		nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL);
+		return;
+	}
+	nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr);
+
+	wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1);
+
+	/* Stash the soft descriptor of the head then initialize it */
+	txbuf = &tx_ring->txbufs[wr_idx];
+	txbuf->frag = rxbuf->frag;
+	txbuf->dma_addr = rxbuf->dma_addr;
+	txbuf->fidx = -1;
+	txbuf->pkt_cnt = 1;
+	txbuf->real_len = pkt_len;
+
+	dma_sync_single_for_device(&nn->pdev->dev, rxbuf->dma_addr + pkt_off,
+				   pkt_len, DMA_TO_DEVICE);
+
+	/* Build TX descriptor */
+	txd = &tx_ring->txds[wr_idx];
+	txd->offset_eop = PCIE_DESC_TX_EOP;
+	txd->dma_len = cpu_to_le16(pkt_len);
+	nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + pkt_off);
+	txd->data_len = cpu_to_le16(pkt_len);
+
+	txd->flags = 0;
+	txd->mss = 0;
+	txd->l4_offset = 0;
+
+	tx_ring->wr_p++;
+	tx_ring->wr_ptr_add++;
+}
+
+static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len)
+{
+	struct xdp_buff xdp;
+
+	xdp.data = data;
+	xdp.data_end = data + len;
+
+	return BPF_PROG_RUN(prog, (void *)&xdp);
+}
+
 /**
  * nfp_net_rx() - receive up to @budget packets on @rx_ring
  * @rx_ring:   RX ring to receive from
@@ -1383,10 +1536,20 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 {
 	struct nfp_net_r_vector *r_vec = rx_ring->r_vec;
 	struct nfp_net *nn = r_vec->nfp_net;
+	struct nfp_net_tx_ring *tx_ring;
+	struct bpf_prog *xdp_prog;
+	unsigned int true_bufsz;
 	struct sk_buff *skb;
 	int pkts_polled = 0;
+	int rx_dma_map_dir;
 	int idx;
 
+	rcu_read_lock();
+	xdp_prog = READ_ONCE(nn->xdp_prog);
+	rx_dma_map_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
+	true_bufsz = xdp_prog ? PAGE_SIZE : nn->fl_bufsz;
+	tx_ring = r_vec->xdp_ring;
+
 	while (pkts_polled < budget) {
 		unsigned int meta_len, data_len, data_off, pkt_len, pkt_off;
 		struct nfp_net_rx_buf *rxbuf;
@@ -1437,19 +1600,45 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 		r_vec->rx_bytes += pkt_len;
 		u64_stats_update_end(&r_vec->rx_sync);
 
-		skb = build_skb(rxbuf->frag, nn->fl_bufsz);
+		if (xdp_prog) {
+			int act;
+
+			dma_sync_single_for_cpu(&nn->pdev->dev,
+						rxbuf->dma_addr + pkt_off,
+						pkt_len, DMA_FROM_DEVICE);
+			act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off,
+					      pkt_len);
+			switch (act) {
+			case XDP_PASS:
+				break;
+			case XDP_TX:
+				nfp_net_tx_xdp_buf(nn, rx_ring, tx_ring, rxbuf,
+						   pkt_off, pkt_len);
+				continue;
+			default:
+				bpf_warn_invalid_xdp_action(act);
+			case XDP_ABORTED:
+			case XDP_DROP:
+				nfp_net_rx_give_one(rx_ring, rxbuf->frag,
+						    rxbuf->dma_addr);
+				continue;
+			}
+		}
+
+		skb = build_skb(rxbuf->frag, true_bufsz);
 		if (unlikely(!skb)) {
 			nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL);
 			continue;
 		}
-		new_frag = nfp_net_napi_alloc_one(nn, &new_dma_addr);
+		new_frag = nfp_net_napi_alloc_one(nn, rx_dma_map_dir,
+						  &new_dma_addr);
 		if (unlikely(!new_frag)) {
 			nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb);
 			continue;
 		}
 
-		nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[idx].dma_addr,
-				     nn->fl_bufsz, DMA_FROM_DEVICE);
+		nfp_net_dma_unmap_rx(nn, rxbuf->dma_addr, nn->fl_bufsz,
+				     rx_dma_map_dir);
 
 		nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr);
 
@@ -1481,6 +1670,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 		napi_gro_receive(&rx_ring->r_vec->napi, skb);
 	}
 
+	if (xdp_prog && tx_ring->wr_ptr_add)
+		nfp_net_tx_xmit_more_flush(tx_ring);
+	rcu_read_unlock();
+
 	return pkts_polled;
 }
 
@@ -1499,8 +1692,11 @@ static int nfp_net_poll(struct napi_struct *napi, int budget)
 
 	if (r_vec->tx_ring)
 		nfp_net_tx_complete(r_vec->tx_ring);
-	if (r_vec->rx_ring)
+	if (r_vec->rx_ring) {
 		pkts_polled = nfp_net_rx(r_vec->rx_ring, budget);
+		if (r_vec->xdp_ring)
+			nfp_net_xdp_complete(r_vec->xdp_ring);
+	}
 
 	if (pkts_polled < budget) {
 		napi_complete_done(napi, pkts_polled);
@@ -1540,10 +1736,12 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring)
  * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring
  * @tx_ring:   TX Ring structure to allocate
  * @cnt:       Ring buffer count
+ * @is_xdp:    True if ring will be used for XDP
  *
  * Return: 0 on success, negative errno otherwise.
  */
-static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt)
+static int
+nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp)
 {
 	struct nfp_net_r_vector *r_vec = tx_ring->r_vec;
 	struct nfp_net *nn = r_vec->nfp_net;
@@ -1563,11 +1761,14 @@ static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt)
 	if (!tx_ring->txbufs)
 		goto err_alloc;
 
-	netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx);
+	if (!is_xdp)
+		netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask,
+				    tx_ring->idx);
 
-	nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p\n",
+	nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p %s\n",
 	       tx_ring->idx, tx_ring->qcidx,
-	       tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds);
+	       tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds,
+	       is_xdp ? "XDP" : "");
 
 	return 0;
 
@@ -1577,7 +1778,8 @@ static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt)
 }
 
 static struct nfp_net_tx_ring *
-nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s)
+nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s,
+			    unsigned int num_stack_tx_rings)
 {
 	struct nfp_net_tx_ring *rings;
 	unsigned int r;
@@ -1587,9 +1789,14 @@ static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt)
 		return NULL;
 
 	for (r = 0; r < s->n_rings; r++) {
-		nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r], r);
+		int bias = 0;
+
+		if (r >= num_stack_tx_rings)
+			bias = num_stack_tx_rings;
 
-		if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt))
+		nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r - bias], r);
+
+		if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt, bias))
 			goto err_free_prev;
 	}
 
@@ -1694,7 +1901,8 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
 }
 
 static struct nfp_net_rx_ring *
-nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s)
+nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s,
+			    bool xdp)
 {
 	unsigned int fl_bufsz =	nfp_net_calc_fl_bufsz(nn, s->mtu);
 	struct nfp_net_rx_ring *rings;
@@ -1710,7 +1918,7 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
 		if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt))
 			goto err_free_prev;
 
-		if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r]))
+		if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r], xdp))
 			goto err_free_ring;
 	}
 
@@ -1718,7 +1926,7 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
 
 err_free_prev:
 	while (r--) {
-		nfp_net_rx_ring_bufs_free(nn, &rings[r]);
+		nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp);
 err_free_ring:
 		nfp_net_rx_ring_free(&rings[r]);
 	}
@@ -1744,13 +1952,14 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
 }
 
 static void
-nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s)
+nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s,
+			 bool xdp)
 {
 	struct nfp_net_rx_ring *rings = s->rings;
 	unsigned int r;
 
 	for (r = 0; r < s->n_rings; r++) {
-		nfp_net_rx_ring_bufs_free(nn, &rings[r]);
+		nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp);
 		nfp_net_rx_ring_free(&rings[r]);
 	}
 
@@ -1762,7 +1971,11 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring)
 			    int idx)
 {
 	r_vec->rx_ring = idx < nn->num_rx_rings ? &nn->rx_rings[idx] : NULL;
-	r_vec->tx_ring = idx < nn->num_tx_rings ? &nn->tx_rings[idx] : NULL;
+	r_vec->tx_ring =
+		idx < nn->num_stack_tx_rings ? &nn->tx_rings[idx] : NULL;
+
+	r_vec->xdp_ring = idx < nn->num_tx_rings - nn->num_stack_tx_rings ?
+		&nn->tx_rings[nn->num_stack_tx_rings + idx] : NULL;
 }
 
 static int
@@ -2083,13 +2296,14 @@ static int nfp_net_netdev_open(struct net_device *netdev)
 			goto err_cleanup_vec_p;
 	}
 
-	nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx);
+	nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->xdp_prog);
 	if (!nn->rx_rings) {
 		err = -ENOMEM;
 		goto err_cleanup_vec;
 	}
 
-	nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx);
+	nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx,
+						   nn->num_stack_tx_rings);
 	if (!nn->tx_rings) {
 		err = -ENOMEM;
 		goto err_free_rx_rings;
@@ -2098,7 +2312,7 @@ static int nfp_net_netdev_open(struct net_device *netdev)
 	for (r = 0; r < nn->max_r_vecs; r++)
 		nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r);
 
-	err = netif_set_real_num_tx_queues(netdev, nn->num_tx_rings);
+	err = netif_set_real_num_tx_queues(netdev, nn->num_stack_tx_rings);
 	if (err)
 		goto err_free_rings;
 
@@ -2130,7 +2344,7 @@ static int nfp_net_netdev_open(struct net_device *netdev)
 err_free_rings:
 	nfp_net_tx_ring_set_free(nn, &tx);
 err_free_rx_rings:
-	nfp_net_rx_ring_set_free(nn, &rx);
+	nfp_net_rx_ring_set_free(nn, &rx, nn->xdp_prog);
 err_cleanup_vec:
 	r = nn->num_r_vecs;
 err_cleanup_vec_p:
@@ -2171,7 +2385,7 @@ static void nfp_net_close_free_all(struct nfp_net *nn)
 	unsigned int r;
 
 	for (r = 0; r < nn->num_rx_rings; r++) {
-		nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r]);
+		nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r], nn->xdp_prog);
 		nfp_net_rx_ring_free(&nn->rx_rings[r]);
 	}
 	for (r = 0; r < nn->num_tx_rings; r++)
@@ -2251,6 +2465,8 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 
 static int
 nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs,
+			 unsigned int *stack_tx_rings,
+			 struct bpf_prog **xdp_prog,
 			 struct nfp_net_ring_set *rx,
 			 struct nfp_net_ring_set *tx)
 {
@@ -2263,6 +2479,8 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 		nfp_net_tx_ring_set_swap(nn, tx);
 
 	swap(*num_vecs, nn->num_r_vecs);
+	swap(*stack_tx_rings, nn->num_stack_tx_rings);
+	*xdp_prog = xchg(&nn->xdp_prog, *xdp_prog);
 
 	for (r = 0; r <	nn->max_r_vecs; r++)
 		nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r);
@@ -2277,9 +2495,9 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 			return err;
 	}
 
-	if (nn->netdev->real_num_tx_queues != nn->num_tx_rings) {
+	if (nn->netdev->real_num_tx_queues != nn->num_stack_tx_rings) {
 		err = netif_set_real_num_tx_queues(nn->netdev,
-						   nn->num_tx_rings);
+						   nn->num_stack_tx_rings);
 		if (err)
 			return err;
 	}
@@ -2287,11 +2505,30 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 	return __nfp_net_set_config_and_enable(nn);
 }
 
+static int
+nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog,
+		     struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx)
+{
+	/* XDP-enabled tests */
+	if (!xdp_prog)
+		return 0;
+	if (rx && nfp_net_calc_fl_bufsz(nn, rx->mtu) > PAGE_SIZE) {
+		nn_warn(nn, "MTU too large w/ XDP enabled\n");
+		return -EINVAL;
+	}
+	if (tx && tx->n_rings > nn->max_tx_rings) {
+		nn_warn(nn, "Insufficient number of TX rings w/ XDP enabled\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static void
-nfp_net_ring_reconfig_down(struct nfp_net *nn,
+nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog,
 			   struct nfp_net_ring_set *rx,
 			   struct nfp_net_ring_set *tx,
-			   unsigned int num_vecs)
+			   unsigned int stack_tx_rings, unsigned int num_vecs)
 {
 	nn->netdev->mtu = rx ? rx->mtu : nn->netdev->mtu;
 	nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, nn->netdev->mtu);
@@ -2299,24 +2536,34 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 	nn->txd_cnt = tx ? tx->dcnt : nn->txd_cnt;
 	nn->num_rx_rings = rx ? rx->n_rings : nn->num_rx_rings;
 	nn->num_tx_rings = tx ? tx->n_rings : nn->num_tx_rings;
+	nn->num_stack_tx_rings = stack_tx_rings;
 	nn->num_r_vecs = num_vecs;
+	*xdp_prog = xchg(&nn->xdp_prog, *xdp_prog);
 
 	if (!netif_is_rxfh_configured(nn->netdev))
 		nfp_net_rss_init_itbl(nn);
 }
 
 int
-nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_ring_set *rx,
-		      struct nfp_net_ring_set *tx)
+nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog,
+		      struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx)
 {
-	unsigned int num_vecs, r;
+	unsigned int stack_tx_rings, num_vecs, r;
 	int err;
 
-	num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings,
-		       tx ? tx->n_rings : nn->num_tx_rings);
+	stack_tx_rings = tx ? tx->n_rings : nn->num_tx_rings;
+	if (*xdp_prog)
+		stack_tx_rings -= rx ? rx->n_rings : nn->num_rx_rings;
+
+	num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, stack_tx_rings);
+
+	err = nfp_net_check_config(nn, *xdp_prog, rx, tx);
+	if (err)
+		return err;
 
 	if (!netif_running(nn->netdev)) {
-		nfp_net_ring_reconfig_down(nn, rx, tx, num_vecs);
+		nfp_net_ring_reconfig_down(nn, xdp_prog, rx, tx,
+					   stack_tx_rings, num_vecs);
 		return 0;
 	}
 
@@ -2329,13 +2576,13 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 		}
 	}
 	if (rx) {
-		if (!nfp_net_rx_ring_set_prepare(nn, rx)) {
+		if (!nfp_net_rx_ring_set_prepare(nn, rx, *xdp_prog)) {
 			err = -ENOMEM;
 			goto err_cleanup_vecs;
 		}
 	}
 	if (tx) {
-		if (!nfp_net_tx_ring_set_prepare(nn, tx)) {
+		if (!nfp_net_tx_ring_set_prepare(nn, tx, stack_tx_rings)) {
 			err = -ENOMEM;
 			goto err_free_rx;
 		}
@@ -2345,14 +2592,16 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 	nfp_net_close_stack(nn);
 	nfp_net_clear_config_and_disable(nn);
 
-	err = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx);
+	err = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings,
+				       xdp_prog, rx, tx);
 	if (err) {
 		int err2;
 
 		nfp_net_clear_config_and_disable(nn);
 
 		/* Try with old configuration and old rings */
-		err2 = nfp_net_ring_swap_enable(nn, &num_vecs, rx, tx);
+		err2 = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings,
+						xdp_prog, rx, tx);
 		if (err2)
 			nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n",
 			       err, err2);
@@ -2361,7 +2610,7 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 		nfp_net_cleanup_vector(nn, &nn->r_vecs[r]);
 
 	if (rx)
-		nfp_net_rx_ring_set_free(nn, rx);
+		nfp_net_rx_ring_set_free(nn, rx, *xdp_prog);
 	if (tx)
 		nfp_net_tx_ring_set_free(nn, tx);
 
@@ -2371,7 +2620,7 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn)
 
 err_free_rx:
 	if (rx)
-		nfp_net_rx_ring_set_free(nn, rx);
+		nfp_net_rx_ring_set_free(nn, rx, *xdp_prog);
 err_cleanup_vecs:
 	for (r = num_vecs - 1; r >= nn->num_r_vecs; r--)
 		nfp_net_cleanup_vector(nn, &nn->r_vecs[r]);
@@ -2387,7 +2636,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu)
 		.dcnt = nn->rxd_cnt,
 	};
 
-	return nfp_net_ring_reconfig(nn, &rx, NULL);
+	return nfp_net_ring_reconfig(nn, &nn->xdp_prog, &rx, NULL);
 }
 
 static struct rtnl_link_stats64 *nfp_net_stat64(struct net_device *netdev,
@@ -2653,6 +2902,56 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev,
 		nfp_net_set_vxlan_port(nn, idx, 0);
 }
 
+static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
+{
+	struct nfp_net_ring_set rx = {
+		.n_rings = nn->num_rx_rings,
+		.mtu = nn->netdev->mtu,
+		.dcnt = nn->rxd_cnt,
+	};
+	struct nfp_net_ring_set tx = {
+		.n_rings = nn->num_tx_rings,
+		.dcnt = nn->txd_cnt,
+	};
+	int err;
+
+	if (!prog && !nn->xdp_prog)
+		return 0;
+	if (prog && nn->xdp_prog) {
+		prog = xchg(&nn->xdp_prog, prog);
+		bpf_prog_put(prog);
+		return 0;
+	}
+
+	tx.n_rings += prog ? nn->num_rx_rings : -nn->num_rx_rings;
+
+	/* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */
+	err = nfp_net_ring_reconfig(nn, &prog, &rx, &tx);
+	if (err)
+		return err;
+
+	/* @prog got swapped and is now the old one */
+	if (prog)
+		bpf_prog_put(prog);
+
+	return 0;
+}
+
+static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp)
+{
+	struct nfp_net *nn = netdev_priv(netdev);
+
+	switch (xdp->command) {
+	case XDP_SETUP_PROG:
+		return nfp_net_xdp_setup(nn, xdp->prog);
+	case XDP_QUERY_PROG:
+		xdp->prog_attached = !!nn->xdp_prog;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct net_device_ops nfp_net_netdev_ops = {
 	.ndo_open		= nfp_net_netdev_open,
 	.ndo_stop		= nfp_net_netdev_close,
@@ -2667,6 +2966,7 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev,
 	.ndo_features_check	= nfp_net_features_check,
 	.ndo_udp_tunnel_add	= nfp_net_add_vxlan_port,
 	.ndo_udp_tunnel_del	= nfp_net_del_vxlan_port,
+	.ndo_xdp		= nfp_net_xdp,
 };
 
 /**
@@ -2929,5 +3229,9 @@ int nfp_net_netdev_init(struct net_device *netdev)
  */
 void nfp_net_netdev_clean(struct net_device *netdev)
 {
-	unregister_netdev(netdev);
+	struct nfp_net *nn = netdev_priv(netdev);
+
+	if (nn->xdp_prog)
+		bpf_prog_put(nn->xdp_prog);
+	unregister_netdev(nn->netdev);
 }
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
index e90df8145323..c66f3f954aa8 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c
@@ -114,6 +114,16 @@ static int nfp_net_debugfs_rx_q_open(struct inode *inode, struct file *f)
 	.llseek = seq_lseek
 };
 
+static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f);
+
+static const struct file_operations nfp_tx_q_fops = {
+	.owner = THIS_MODULE,
+	.open = nfp_net_debugfs_tx_q_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek
+};
+
 static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
 {
 	struct nfp_net_r_vector *r_vec = file->private;
@@ -126,10 +136,13 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
 
 	rtnl_lock();
 
-	if (!r_vec->nfp_net || !r_vec->tx_ring)
+	if (debugfs_real_fops(file->file) == &nfp_tx_q_fops)
+		tx_ring = r_vec->tx_ring;
+	else
+		tx_ring = r_vec->xdp_ring;
+	if (!r_vec->nfp_net || !tx_ring)
 		goto out;
 	nn = r_vec->nfp_net;
-	tx_ring = r_vec->tx_ring;
 	if (!netif_running(nn->netdev))
 		goto out;
 
@@ -148,9 +161,14 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data)
 			   txd->vals[2], txd->vals[3]);
 
 		skb = READ_ONCE(tx_ring->txbufs[i].skb);
-		if (skb)
-			seq_printf(file, " skb->head=%p skb->data=%p",
-				   skb->head, skb->data);
+		if (skb) {
+			if (tx_ring == r_vec->tx_ring)
+				seq_printf(file, " skb->head=%p skb->data=%p",
+					   skb->head, skb->data);
+			else
+				seq_printf(file, " frag=%p", skb);
+		}
+
 		if (tx_ring->txbufs[i].dma_addr)
 			seq_printf(file, " dma_addr=%pad",
 				   &tx_ring->txbufs[i].dma_addr);
@@ -176,7 +194,7 @@ static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
 	return single_open(f, nfp_net_debugfs_tx_q_read, inode->i_private);
 }
 
-static const struct file_operations nfp_tx_q_fops = {
+static const struct file_operations nfp_xdp_q_fops = {
 	.owner = THIS_MODULE,
 	.open = nfp_net_debugfs_tx_q_open,
 	.release = single_release,
@@ -186,7 +204,7 @@ static int nfp_net_debugfs_tx_q_open(struct inode *inode, struct file *f)
 
 void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
 {
-	struct dentry *queues, *tx, *rx;
+	struct dentry *queues, *tx, *rx, *xdp;
 	char int_name[16];
 	int i;
 
@@ -204,13 +222,16 @@ void nfp_net_debugfs_adapter_add(struct nfp_net *nn)
 
 	rx = debugfs_create_dir("rx", queues);
 	tx = debugfs_create_dir("tx", queues);
-	if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx))
+	xdp = debugfs_create_dir("xdp", queues);
+	if (IS_ERR_OR_NULL(rx) || IS_ERR_OR_NULL(tx) || IS_ERR_OR_NULL(xdp))
 		return;
 
 	for (i = 0; i < min(nn->max_rx_rings, nn->max_r_vecs); i++) {
 		sprintf(int_name, "%d", i);
 		debugfs_create_file(int_name, S_IRUSR, rx,
 				    &nn->r_vecs[i], &nfp_rx_q_fops);
+		debugfs_create_file(int_name, S_IRUSR, xdp,
+				    &nn->r_vecs[i], &nfp_xdp_q_fops);
 	}
 
 	for (i = 0; i < min(nn->max_tx_rings, nn->max_r_vecs); i++) {
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
index b87f1b73f200..1b26e9646574 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -176,7 +176,8 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt)
 	if (nn->txd_cnt != txd_cnt)
 		reconfig_tx = &tx;
 
-	return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx);
+	return nfp_net_ring_reconfig(nn, &nn->xdp_prog,
+				     reconfig_rx, reconfig_tx);
 }
 
 static int nfp_net_set_ringparam(struct net_device *netdev,
@@ -639,14 +640,19 @@ static void nfp_net_get_channels(struct net_device *netdev,
 				 struct ethtool_channels *channel)
 {
 	struct nfp_net *nn = netdev_priv(netdev);
+	unsigned int num_tx_rings;
+
+	num_tx_rings = nn->num_tx_rings;
+	if (nn->xdp_prog)
+		num_tx_rings -= nn->num_rx_rings;
 
 	channel->max_rx = min(nn->max_rx_rings, nn->max_r_vecs);
 	channel->max_tx = min(nn->max_tx_rings, nn->max_r_vecs);
 	channel->max_combined = min(channel->max_rx, channel->max_tx);
 	channel->max_other = NFP_NET_NON_Q_VECTORS;
-	channel->combined_count = min(nn->num_rx_rings, nn->num_tx_rings);
+	channel->combined_count = min(nn->num_rx_rings, num_tx_rings);
 	channel->rx_count = nn->num_rx_rings - channel->combined_count;
-	channel->tx_count = nn->num_tx_rings - channel->combined_count;
+	channel->tx_count = num_tx_rings - channel->combined_count;
 	channel->other_count = NFP_NET_NON_Q_VECTORS;
 }
 
@@ -666,10 +672,16 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx,
 
 	if (nn->num_rx_rings != total_rx)
 		reconfig_rx = &rx;
-	if (nn->num_tx_rings != total_tx)
+	if (nn->num_stack_tx_rings != total_tx ||
+	    (nn->xdp_prog && reconfig_rx))
 		reconfig_tx = &tx;
 
-	return nfp_net_ring_reconfig(nn, reconfig_rx, reconfig_tx);
+	/* nfp_net_check_config() will catch tx.n_rings > nn->max_tx_rings */
+	if (nn->xdp_prog)
+		tx.n_rings += total_rx;
+
+	return nfp_net_ring_reconfig(nn, &nn->xdp_prog,
+				     reconfig_rx, reconfig_tx);
 }
 
 static int nfp_net_set_channels(struct net_device *netdev,
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 13/13] nfp: add support for offload of XDP programs
From: Jakub Kicinski @ 2016-11-03 13:58 UTC (permalink / raw)
  To: netdev; +Cc: Jakub Kicinski
In-Reply-To: <1478181538-20778-1-git-send-email-jakub.kicinski@netronome.com>

Most infrastructure can be reused, provide separate handling
of context offsets and exit codes.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 drivers/net/ethernet/netronome/nfp/nfp_bpf.h       |  1 +
 drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c   | 92 +++++++++++++++++++++-
 .../net/ethernet/netronome/nfp/nfp_bpf_verifier.c  |  3 +
 drivers/net/ethernet/netronome/nfp/nfp_net.h       |  2 +
 .../net/ethernet/netronome/nfp/nfp_net_common.c    | 44 ++++++++++-
 .../net/ethernet/netronome/nfp/nfp_net_offload.c   |  3 +
 6 files changed, 139 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf.h b/drivers/net/ethernet/netronome/nfp/nfp_bpf.h
index 87aa8a3e9112..76a19f1796af 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_bpf.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf.h
@@ -62,6 +62,7 @@ enum nfp_bpf_action_type {
 	NN_ACT_TC_DROP,
 	NN_ACT_TC_REDIR,
 	NN_ACT_DIRECT,
+	NN_ACT_XDP,
 };
 
 /* Software register representation, hardware encoding in asm.h */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
index f8df5300f49c..335beb8b8b45 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c
@@ -1126,7 +1126,7 @@ static int data_ind_ld4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 				     meta->insn.src_reg * 2, true, 4);
 }
 
-static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+static int mem_ldx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
 	if (meta->insn.off == offsetof(struct sk_buff, len))
 		emit_alu(nfp_prog, reg_both(meta->insn.dst_reg * 2),
@@ -1134,12 +1134,42 @@ static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 	else
 		return -ENOTSUPP;
 
-	wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0);
+	return 0;
+}
+
+static int mem_ldx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+	u32 dst = reg_both(meta->insn.dst_reg * 2);
+
+	if (meta->insn.off != offsetof(struct xdp_md, data) &&
+	    meta->insn.off != offsetof(struct xdp_md, data_end))
+		return -ENOTSUPP;
+
+	emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, NFP_BPF_ABI_PKT);
+
+	if (meta->insn.off == offsetof(struct xdp_md, data))
+		return 0;
+
+	emit_alu(nfp_prog, dst,	dst, ALU_OP_ADD, NFP_BPF_ABI_LEN);
 
 	return 0;
 }
 
-static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+static int mem_ldx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+	int ret;
+
+	if (nfp_prog->act == NN_ACT_XDP)
+		ret = mem_ldx4_xdp(nfp_prog, meta);
+	else
+		ret = mem_ldx4_skb(nfp_prog, meta);
+
+	wrp_immed(nfp_prog, reg_both(meta->insn.dst_reg * 2 + 1), 0);
+
+	return ret;
+}
+
+static int mem_stx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
 	if (meta->insn.off == offsetof(struct sk_buff, mark))
 		return wrp_set_mark(nfp_prog, meta->insn.src_reg * 2);
@@ -1147,6 +1177,18 @@ static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 	return -ENOTSUPP;
 }
 
+static int mem_stx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+	return -ENOTSUPP;
+}
+
+static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+{
+	if (nfp_prog->act == NN_ACT_XDP)
+		return mem_stx4_xdp(nfp_prog, meta);
+	return mem_stx4_skb(nfp_prog, meta);
+}
+
 static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
 	if (meta->insn.off < 0) /* TODO */
@@ -1530,6 +1572,47 @@ static void nfp_outro_tc_da(struct nfp_prog *nfp_prog)
 	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16);
 }
 
+static void nfp_outro_xdp(struct nfp_prog *nfp_prog)
+{
+	/* XDP return codes:
+	 *   0 aborted  0x82 -> drop,  count as stat3
+	 *   1    drop  0x22 -> drop,  count as stat1
+	 *   2    pass  0x11 -> pass,  count as stat0
+	 *   3      tx  0x44 -> redir, count as stat2
+	 *   * unknown  0x82 -> drop,  count as stat3
+	 */
+	/* Target for aborts */
+	nfp_prog->tgt_abort = nfp_prog_current_offset(nfp_prog);
+
+	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2);
+
+	emit_alu(nfp_prog, reg_a(0),
+		 reg_none(), ALU_OP_NONE, NFP_BPF_ABI_FLAGS);
+	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_imm(0x82), SHF_SC_L_SHF, 16);
+
+	/* Target for normal exits */
+	nfp_prog->tgt_out = nfp_prog_current_offset(nfp_prog);
+
+	/* if R0 > 3 jump to abort */
+	emit_alu(nfp_prog, reg_none(), reg_imm(3), ALU_OP_SUB, reg_b(0));
+	emit_br(nfp_prog, BR_BLO, nfp_prog->tgt_abort, 0);
+
+	wrp_immed(nfp_prog, reg_b(2), 0x44112282);
+
+	emit_shf(nfp_prog, reg_a(1),
+		 reg_none(), SHF_OP_NONE, reg_b(0), SHF_SC_L_SHF, 3);
+
+	emit_alu(nfp_prog, reg_none(), reg_a(1), ALU_OP_OR, reg_imm(0));
+	emit_shf(nfp_prog, reg_b(2),
+		 reg_imm(0xff), SHF_OP_AND, reg_b(2), SHF_SC_R_SHF, 0);
+
+	emit_br_def(nfp_prog, nfp_prog->tgt_done, 2);
+
+	emit_alu(nfp_prog, reg_a(0),
+		 reg_none(), ALU_OP_NONE, NFP_BPF_ABI_FLAGS);
+	emit_ld_field(nfp_prog, reg_a(0), 0xc, reg_b(2), SHF_SC_L_SHF, 16);
+}
+
 static void nfp_outro(struct nfp_prog *nfp_prog)
 {
 	switch (nfp_prog->act) {
@@ -1540,6 +1623,9 @@ static void nfp_outro(struct nfp_prog *nfp_prog)
 	case NN_ACT_TC_REDIR:
 		nfp_outro_tc_legacy(nfp_prog);
 		break;
+	case NN_ACT_XDP:
+		nfp_outro_xdp(nfp_prog);
+		break;
 	}
 }
 
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c
index 144cae87f63a..b3361f9b8e5c 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_verifier.c
@@ -80,6 +80,9 @@ struct nfp_bpf_analyzer_priv {
 {
 	const struct bpf_reg_state *reg0 = &env->cur_state.regs[0];
 
+	if (nfp_prog->act == NN_ACT_XDP)
+		return 0;
+
 	if (reg0->type != CONST_IMM) {
 		pr_info("unsupported exit state: %d, imm: %llx\n",
 			reg0->type, reg0->imm);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index fd29a6306991..2115f446031e 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -435,6 +435,7 @@ struct nfp_stat_pair {
  * @is_vf:              Is the driver attached to a VF?
  * @fw_loaded:          Is the firmware loaded?
  * @bpf_offload_skip_sw:  Offloaded BPF program will not be rerun by cls_bpf
+ * @bpf_offload_xdp:	Offloaded BPF program is XDP
  * @ctrl:               Local copy of the control register/word.
  * @fl_bufsz:           Currently configured size of the freelist buffers
  * @rx_offset:		Offset in the RX buffers where packet data starts
@@ -502,6 +503,7 @@ struct nfp_net {
 	unsigned is_vf:1;
 	unsigned fw_loaded:1;
 	unsigned bpf_offload_skip_sw:1;
+	unsigned bpf_offload_xdp:1;
 
 	u32 ctrl;
 	u32 fl_bufsz;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 1e8e00d25c51..99edb9fd84bf 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -1600,7 +1600,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
 		r_vec->rx_bytes += pkt_len;
 		u64_stats_update_end(&r_vec->rx_sync);
 
-		if (xdp_prog) {
+		if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF &&
+				  nn->bpf_offload_xdp)) {
 			int act;
 
 			dma_sync_single_for_cpu(&nn->pdev->dev,
@@ -2693,8 +2694,12 @@ static bool nfp_net_ebpf_capable(struct nfp_net *nn)
 	if (proto != htons(ETH_P_ALL))
 		return -ENOTSUPP;
 
-	if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn))
-		return nfp_net_bpf_offload(nn, tc->cls_bpf);
+	if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) {
+		if (!nn->bpf_offload_xdp)
+			return nfp_net_bpf_offload(nn, tc->cls_bpf);
+		else
+			return -EBUSY;
+	}
 
 	return -EINVAL;
 }
@@ -2902,6 +2907,34 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev,
 		nfp_net_set_vxlan_port(nn, idx, 0);
 }
 
+static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog)
+{
+	struct tc_cls_bpf_offload cmd = {
+		.prog = prog,
+	};
+	int ret;
+
+	if (!nfp_net_ebpf_capable(nn))
+		return -EINVAL;
+
+	if (nn->ctrl & NFP_NET_CFG_CTRL_BPF) {
+		if (!nn->bpf_offload_xdp)
+			return prog ? -EBUSY : 0;
+		cmd.command = prog ? TC_CLSBPF_REPLACE : TC_CLSBPF_DESTROY;
+	} else {
+		if (!prog)
+			return 0;
+		cmd.command = TC_CLSBPF_ADD;
+	}
+
+	ret = nfp_net_bpf_offload(nn, &cmd);
+	/* Stop offload if replace not possible */
+	if (ret && cmd.command == TC_CLSBPF_REPLACE)
+		nfp_net_xdp_offload(nn, NULL);
+	nn->bpf_offload_xdp = prog && !ret;
+	return ret;
+}
+
 static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
 {
 	struct nfp_net_ring_set rx = {
@@ -2920,6 +2953,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
 	if (prog && nn->xdp_prog) {
 		prog = xchg(&nn->xdp_prog, prog);
 		bpf_prog_put(prog);
+		nfp_net_xdp_offload(nn, nn->xdp_prog);
 		return 0;
 	}
 
@@ -2934,6 +2968,8 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog)
 	if (prog)
 		bpf_prog_put(prog);
 
+	nfp_net_xdp_offload(nn, nn->xdp_prog);
+
 	return 0;
 }
 
@@ -3233,5 +3269,7 @@ void nfp_net_netdev_clean(struct net_device *netdev)
 
 	if (nn->xdp_prog)
 		bpf_prog_put(nn->xdp_prog);
+	if (nn->bpf_offload_xdp)
+		nfp_net_xdp_offload(nn, NULL);
 	unregister_netdev(nn->netdev);
 }
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
index 4bb6f16e2a7a..18a851eb3508 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c
@@ -111,6 +111,9 @@ static void nfp_net_bpf_stats_reset(struct nfp_net *nn)
 	const struct tc_action *a;
 	LIST_HEAD(actions);
 
+	if (!cls_bpf->exts)
+		return NN_ACT_XDP;
+
 	/* TC direct action */
 	if (cls_bpf->exts_integrated) {
 		if (tc_no_actions(cls_bpf->exts))
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next v1 01/21] amd-xgbe: Fix formatting of PCS register dump
From: Tom Lendacky @ 2016-11-03 13:27 UTC (permalink / raw)
  To: netdev; +Cc: Florian Fainelli, David Miller
In-Reply-To: <20161103132744.3437.27332.stgit@tlendack-t1.amdoffice.net>

Fix the length value used for the PCS register dump so that the full
value can be displayed.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 drivers/net/ethernet/amd/xgbe/xgbe-mdio.c |   24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 84c5d29..e9b01fc 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -1257,33 +1257,33 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
 
 	dev_dbg(dev, "\n************* PHY Reg dump **********************\n");
 
-	dev_dbg(dev, "PCS Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+	dev_dbg(dev, "PCS Control Reg (%#06x) = %#06x\n", MDIO_CTRL1,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1));
-	dev_dbg(dev, "PCS Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+	dev_dbg(dev, "PCS Status Reg (%#06x) = %#06x\n", MDIO_STAT1,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1));
-	dev_dbg(dev, "Phy Id (PHYS ID 1 %#04x)= %#04x\n", MDIO_DEVID1,
+	dev_dbg(dev, "Phy Id (PHYS ID 1 %#06x)= %#06x\n", MDIO_DEVID1,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1));
-	dev_dbg(dev, "Phy Id (PHYS ID 2 %#04x)= %#04x\n", MDIO_DEVID2,
+	dev_dbg(dev, "Phy Id (PHYS ID 2 %#06x)= %#06x\n", MDIO_DEVID2,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2));
-	dev_dbg(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS1,
+	dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS1,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1));
-	dev_dbg(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS2,
+	dev_dbg(dev, "Devices in Package (%#06x)= %#06x\n", MDIO_DEVS2,
 		XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2));
 
-	dev_dbg(dev, "Auto-Neg Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
+	dev_dbg(dev, "Auto-Neg Control Reg (%#06x) = %#06x\n", MDIO_CTRL1,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1));
-	dev_dbg(dev, "Auto-Neg Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
+	dev_dbg(dev, "Auto-Neg Status Reg (%#06x) = %#06x\n", MDIO_STAT1,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1));
-	dev_dbg(dev, "Auto-Neg Ad Reg 1 (%#04x) = %#04x\n",
+	dev_dbg(dev, "Auto-Neg Ad Reg 1 (%#06x) = %#06x\n",
 		MDIO_AN_ADVERTISE,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE));
-	dev_dbg(dev, "Auto-Neg Ad Reg 2 (%#04x) = %#04x\n",
+	dev_dbg(dev, "Auto-Neg Ad Reg 2 (%#06x) = %#06x\n",
 		MDIO_AN_ADVERTISE + 1,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1));
-	dev_dbg(dev, "Auto-Neg Ad Reg 3 (%#04x) = %#04x\n",
+	dev_dbg(dev, "Auto-Neg Ad Reg 3 (%#06x) = %#06x\n",
 		MDIO_AN_ADVERTISE + 2,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2));
-	dev_dbg(dev, "Auto-Neg Completion Reg (%#04x) = %#04x\n",
+	dev_dbg(dev, "Auto-Neg Completion Reg (%#06x) = %#06x\n",
 		MDIO_AN_COMP_STAT,
 		XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT));
 

^ permalink raw reply related

* [PATCH net-next v1 21/21] amd-xgbe: Add support for a KR redriver
From: Tom Lendacky @ 2016-11-03 13:31 UTC (permalink / raw)
  To: netdev; +Cc: Florian Fainelli, David Miller
In-Reply-To: <20161103132744.3437.27332.stgit@tlendack-t1.amdoffice.net>

This patch provides support for the presence of a KR redriver chip in
between the device PCS and an external PHY.  When a redriver chip is
present the device must perform clause 73 auto-negotiation in order to
set the redriver chip for the downstream connection.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 drivers/net/ethernet/amd/xgbe/xgbe-common.h |   10 +
 drivers/net/ethernet/amd/xgbe/xgbe-mdio.c   |   45 ++-
 drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c |    7 
 drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c |  427 +++++++++++++++++++++++++--
 drivers/net/ethernet/amd/xgbe/xgbe.h        |    6 
 5 files changed, 458 insertions(+), 37 deletions(-)

diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
index ecd4f4d..5b7ba25 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -1062,6 +1062,16 @@
 #define XP_PROP_4_MUX_ADDR_LO_WIDTH		3
 #define XP_PROP_4_MUX_CHAN_INDEX		4
 #define XP_PROP_4_MUX_CHAN_WIDTH		3
+#define XP_PROP_4_REDRV_ADDR_INDEX		16
+#define XP_PROP_4_REDRV_ADDR_WIDTH		7
+#define XP_PROP_4_REDRV_IF_INDEX		23
+#define XP_PROP_4_REDRV_IF_WIDTH		1
+#define XP_PROP_4_REDRV_LANE_INDEX		24
+#define XP_PROP_4_REDRV_LANE_WIDTH		3
+#define XP_PROP_4_REDRV_MODEL_INDEX		28
+#define XP_PROP_4_REDRV_MODEL_WIDTH		3
+#define XP_PROP_4_REDRV_PRESENT_INDEX		31
+#define XP_PROP_4_REDRV_PRESENT_WIDTH		1
 
 /* I2C Control register offsets */
 #define IC_CON					0x0000
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 622675a..0ecae70 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -179,6 +179,7 @@ static void xgbe_an_enable_interrupts(struct xgbe_prv_data *pdata)
 {
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_enable_interrupts(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -254,6 +255,10 @@ static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)
 
 static void xgbe_sfi_mode(struct xgbe_prv_data *pdata)
 {
+	/* If a KR re-driver is present, change to KR mode instead */
+	if (pdata->kr_redrv)
+		return xgbe_kr_mode(pdata);
+
 	/* Disable KR training */
 	xgbe_an73_disable_kr_training(pdata);
 
@@ -433,6 +438,7 @@ static void xgbe_an_restart(struct xgbe_prv_data *pdata)
 {
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_restart(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -448,6 +454,7 @@ static void xgbe_an_disable(struct xgbe_prv_data *pdata)
 {
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_disable(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -687,6 +694,7 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)
 
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_isr(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -895,6 +903,7 @@ static void xgbe_an_state_machine(struct work_struct *work)
 
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_state_machine(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -910,16 +919,18 @@ static void xgbe_an_state_machine(struct work_struct *work)
 
 static void xgbe_an37_init(struct xgbe_prv_data *pdata)
 {
-	unsigned int reg;
+	unsigned int advertising, reg;
+
+	advertising = pdata->phy_if.phy_impl.an_advertising(pdata);
 
 	/* Set up Advertisement register */
 	reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE);
-	if (pdata->phy.advertising & ADVERTISED_Pause)
+	if (advertising & ADVERTISED_Pause)
 		reg |= 0x100;
 	else
 		reg &= ~0x100;
 
-	if (pdata->phy.advertising & ADVERTISED_Asym_Pause)
+	if (advertising & ADVERTISED_Asym_Pause)
 		reg |= 0x80;
 	else
 		reg &= ~0x80;
@@ -954,11 +965,13 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata)
 
 static void xgbe_an73_init(struct xgbe_prv_data *pdata)
 {
-	unsigned int reg;
+	unsigned int advertising, reg;
+
+	advertising = pdata->phy_if.phy_impl.an_advertising(pdata);
 
 	/* Set up Advertisement register 3 first */
 	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
-	if (pdata->phy.advertising & ADVERTISED_10000baseR_FEC)
+	if (advertising & ADVERTISED_10000baseR_FEC)
 		reg |= 0xc000;
 	else
 		reg &= ~0xc000;
@@ -967,13 +980,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)
 
 	/* Set up Advertisement register 2 next */
 	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
-	if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full)
+	if (advertising & ADVERTISED_10000baseKR_Full)
 		reg |= 0x80;
 	else
 		reg &= ~0x80;
 
-	if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) ||
-	    (pdata->phy.advertising & ADVERTISED_2500baseX_Full))
+	if ((advertising & ADVERTISED_1000baseKX_Full) ||
+	    (advertising & ADVERTISED_2500baseX_Full))
 		reg |= 0x20;
 	else
 		reg &= ~0x20;
@@ -982,12 +995,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata)
 
 	/* Set up Advertisement register 1 last */
 	reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
-	if (pdata->phy.advertising & ADVERTISED_Pause)
+	if (advertising & ADVERTISED_Pause)
 		reg |= 0x400;
 	else
 		reg &= ~0x400;
 
-	if (pdata->phy.advertising & ADVERTISED_Asym_Pause)
+	if (advertising & ADVERTISED_Asym_Pause)
 		reg |= 0x800;
 	else
 		reg &= ~0x800;
@@ -1006,6 +1019,7 @@ static void xgbe_an_init(struct xgbe_prv_data *pdata)
 	pdata->an_mode = pdata->phy_if.phy_impl.an_mode(pdata);
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
+	case XGBE_AN_MODE_CL73_REDRV:
 		xgbe_an73_init(pdata);
 		break;
 	case XGBE_AN_MODE_CL37:
@@ -1149,10 +1163,15 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
 	if (ret)
 		return ret;
 
-	if (pdata->phy.autoneg != AUTONEG_ENABLE)
-		return xgbe_phy_config_fixed(pdata);
+	if (pdata->phy.autoneg != AUTONEG_ENABLE) {
+		ret = xgbe_phy_config_fixed(pdata);
+		if (ret || !pdata->kr_redrv)
+			return ret;
 
-	netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n");
+		netif_dbg(pdata, link, pdata->netdev, "AN redriver support\n");
+	} else {
+		netif_dbg(pdata, link, pdata->netdev, "AN PHY configuration\n");
+	}
 
 	/* Disable auto-negotiation interrupt */
 	disable_irq(pdata->an_irq);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c
index 6c64d11..c75edca 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c
@@ -295,6 +295,11 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
 	return mode;
 }
 
+static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
+{
+	return pdata->phy.advertising;
+}
+
 static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
 {
 	/* Nothing uniquely required for an configuration */
@@ -831,6 +836,8 @@ void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if)
 
 	phy_impl->an_config		= xgbe_phy_an_config;
 
+	phy_impl->an_advertising	= xgbe_phy_an_advertising;
+
 	phy_impl->an_outcome		= xgbe_phy_an_outcome;
 
 	phy_impl->kr_training_pre	= xgbe_phy_kr_training_pre;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
index 180e70e..2a134e6 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -277,6 +277,26 @@ enum xgbe_mdio_reset {
 	XGBE_MDIO_RESET_MAX,
 };
 
+/* Re-driver related definitions */
+enum xgbe_phy_redrv_if {
+	XGBE_PHY_REDRV_IF_MDIO = 0,
+	XGBE_PHY_REDRV_IF_I2C,
+	XGBE_PHY_REDRV_IF_MAX,
+};
+
+enum xgbe_phy_redrv_model {
+	XGBE_PHY_REDRV_MODEL_4223 = 0,
+	XGBE_PHY_REDRV_MODEL_4227,
+	XGBE_PHY_REDRV_MODEL_MAX,
+};
+
+enum xgbe_phy_redrv_mode {
+	XGBE_PHY_REDRV_MODE_CX = 5,
+	XGBE_PHY_REDRV_MODE_SR = 9,
+};
+
+#define XGBE_PHY_REDRV_MODE_REG	0x12b0
+
 /* PHY related configuration information */
 struct xgbe_phy_data {
 	enum xgbe_port_mode port_mode;
@@ -327,6 +347,13 @@ struct xgbe_phy_data {
 	enum xgbe_mdio_reset mdio_reset;
 	unsigned int mdio_reset_addr;
 	unsigned int mdio_reset_gpio;
+
+	/* Re-driver support */
+	unsigned int redrv;
+	unsigned int redrv_if;
+	unsigned int redrv_addr;
+	unsigned int redrv_lane;
+	unsigned int redrv_model;
 };
 
 /* I2C, MDIO and GPIO lines are muxed, so only one device at a time */
@@ -346,6 +373,68 @@ static int xgbe_phy_i2c_xfer(struct xgbe_prv_data *pdata,
 	return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
 }
 
+static int xgbe_phy_redrv_write(struct xgbe_prv_data *pdata, unsigned int reg,
+				unsigned int val)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	struct xgbe_i2c_op i2c_op;
+	__be16 *redrv_val;
+	u8 redrv_data[5], csum;
+	unsigned int i, retry;
+	int ret;
+
+	/* High byte of register contains read/write indicator */
+	redrv_data[0] = ((reg >> 8) & 0xff) << 1;
+	redrv_data[1] = reg & 0xff;
+	redrv_val = (__be16 *)&redrv_data[2];
+	*redrv_val = cpu_to_be16(val);
+
+	/* Calculate 1 byte checksum */
+	csum = 0;
+	for (i = 0; i < 4; i++) {
+		csum += redrv_data[i];
+		if (redrv_data[i] > csum)
+			csum++;
+	}
+	redrv_data[4] = ~csum;
+
+	retry = 1;
+again1:
+	i2c_op.cmd = XGBE_I2C_CMD_WRITE;
+	i2c_op.target = phy_data->redrv_addr;
+	i2c_op.len = sizeof(redrv_data);
+	i2c_op.buf = redrv_data;
+	ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
+	if (ret) {
+		if ((ret == -EAGAIN) && retry--)
+			goto again1;
+
+		return ret;
+	}
+
+	retry = 1;
+again2:
+	i2c_op.cmd = XGBE_I2C_CMD_READ;
+	i2c_op.target = phy_data->redrv_addr;
+	i2c_op.len = 1;
+	i2c_op.buf = redrv_data;
+	ret = xgbe_phy_i2c_xfer(pdata, &i2c_op);
+	if (ret) {
+		if ((ret == -EAGAIN) && retry--)
+			goto again2;
+
+		return ret;
+	}
+
+	if (redrv_data[0] != 0xff) {
+		netif_dbg(pdata, drv, pdata->netdev,
+			  "Redriver write checksum error\n");
+		ret = -EIO;
+	}
+
+	return ret;
+}
+
 static int xgbe_phy_i2c_write(struct xgbe_prv_data *pdata, unsigned int target,
 			      void *val, unsigned int val_len)
 {
@@ -1144,38 +1233,49 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data *pdata)
 	xgbe_put_comm_ownership(pdata);
 }
 
-static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
+static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
 {
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
-	enum xgbe_mode mode;
+	u16 lcl_adv = 0, rmt_adv = 0;
+	u8 fc;
 
-	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
-	pdata->phy.lp_advertising |= ADVERTISED_TP;
+	pdata->phy.tx_pause = 0;
+	pdata->phy.rx_pause = 0;
 
-	if (pdata->phy.pause_autoneg && phy_data->phydev) {
-		/* Flow control is obtained from the attached PHY */
-		u16 lcl_adv = 0, rmt_adv = 0;
-		u8 fc;
+	if (!phy_data->phydev)
+		return;
 
-		pdata->phy.tx_pause = 0;
-		pdata->phy.rx_pause = 0;
+	if (phy_data->phydev->advertising & ADVERTISED_Pause)
+		lcl_adv |= ADVERTISE_PAUSE_CAP;
+	if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause)
+		lcl_adv |= ADVERTISE_PAUSE_ASYM;
 
-		if (phy_data->phydev->advertising & ADVERTISED_Pause)
-			lcl_adv |= ADVERTISE_PAUSE_CAP;
-		if (phy_data->phydev->advertising & ADVERTISED_Asym_Pause)
-			lcl_adv |= ADVERTISE_PAUSE_ASYM;
+	if (phy_data->phydev->pause) {
+		pdata->phy.lp_advertising |= ADVERTISED_Pause;
+		rmt_adv |= LPA_PAUSE_CAP;
+	}
+	if (phy_data->phydev->asym_pause) {
+		pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
+		rmt_adv |= LPA_PAUSE_ASYM;
+	}
 
-		if (phy_data->phydev->pause)
-			rmt_adv |= LPA_PAUSE_CAP;
-		if (phy_data->phydev->asym_pause)
-			rmt_adv |= LPA_PAUSE_ASYM;
+	fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+	if (fc & FLOW_CTRL_TX)
+		pdata->phy.tx_pause = 1;
+	if (fc & FLOW_CTRL_RX)
+		pdata->phy.rx_pause = 1;
+}
 
-		fc = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
-		if (fc & FLOW_CTRL_TX)
-			pdata->phy.tx_pause = 1;
-		if (fc & FLOW_CTRL_RX)
-			pdata->phy.rx_pause = 1;
-	}
+static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata)
+{
+	enum xgbe_mode mode;
+
+	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
+	pdata->phy.lp_advertising |= ADVERTISED_TP;
+
+	/* Use external PHY to determine flow control */
+	if (pdata->phy.pause_autoneg)
+		xgbe_phy_phydev_flowctrl(pdata);
 
 	switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) {
 	case XGBE_SGMII_AN_LINK_SPEED_100:
@@ -1249,6 +1349,83 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata)
 	return mode;
 }
 
+static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	enum xgbe_mode mode;
+	unsigned int ad_reg, lp_reg;
+
+	pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
+	pdata->phy.lp_advertising |= ADVERTISED_Backplane;
+
+	/* Use external PHY to determine flow control */
+	if (pdata->phy.pause_autoneg)
+		xgbe_phy_phydev_flowctrl(pdata);
+
+	/* Compare Advertisement and Link Partner register 2 */
+	ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+	lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+	if (lp_reg & 0x80)
+		pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full;
+	if (lp_reg & 0x20)
+		pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;
+
+	ad_reg &= lp_reg;
+	if (ad_reg & 0x80) {
+		switch (phy_data->port_mode) {
+		case XGBE_PORT_MODE_BACKPLANE:
+			mode = XGBE_MODE_KR;
+			break;
+		default:
+			mode = XGBE_MODE_SFI;
+			break;
+		}
+	} else if (ad_reg & 0x20) {
+		switch (phy_data->port_mode) {
+		case XGBE_PORT_MODE_BACKPLANE:
+			mode = XGBE_MODE_KX_1000;
+			break;
+		case XGBE_PORT_MODE_1000BASE_X:
+			mode = XGBE_MODE_X;
+			break;
+		case XGBE_PORT_MODE_SFP:
+			switch (phy_data->sfp_base) {
+			case XGBE_SFP_BASE_1000_T:
+				if (phy_data->phydev &&
+				    (phy_data->phydev->speed == SPEED_100))
+					mode = XGBE_MODE_SGMII_100;
+				else
+					mode = XGBE_MODE_SGMII_1000;
+				break;
+			case XGBE_SFP_BASE_1000_SX:
+			case XGBE_SFP_BASE_1000_LX:
+			case XGBE_SFP_BASE_1000_CX:
+			default:
+				mode = XGBE_MODE_X;
+				break;
+			}
+			break;
+		default:
+			if (phy_data->phydev &&
+			    (phy_data->phydev->speed == SPEED_100))
+				mode = XGBE_MODE_SGMII_100;
+			else
+				mode = XGBE_MODE_SGMII_1000;
+			break;
+		}
+	} else {
+		mode = XGBE_MODE_UNKNOWN;
+	}
+
+	/* Compare Advertisement and Link Partner register 3 */
+	ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+	lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
+	if (lp_reg & 0xc000)
+		pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC;
+
+	return mode;
+}
+
 static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata)
 {
 	enum xgbe_mode mode;
@@ -1311,6 +1488,8 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
 	switch (pdata->an_mode) {
 	case XGBE_AN_MODE_CL73:
 		return xgbe_phy_an73_outcome(pdata);
+	case XGBE_AN_MODE_CL73_REDRV:
+		return xgbe_phy_an73_redrv_outcome(pdata);
 	case XGBE_AN_MODE_CL37:
 		return xgbe_phy_an37_outcome(pdata);
 	case XGBE_AN_MODE_CL37_SGMII:
@@ -1320,6 +1499,63 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
 	}
 }
 
+static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	unsigned int advertising;
+
+	/* Without a re-driver, just return current advertising */
+	if (!phy_data->redrv)
+		return pdata->phy.advertising;
+
+	/* With the KR re-driver we need to advertise a single speed */
+	advertising = pdata->phy.advertising;
+	advertising &= ~ADVERTISED_1000baseKX_Full;
+	advertising &= ~ADVERTISED_10000baseKR_Full;
+
+	switch (phy_data->port_mode) {
+	case XGBE_PORT_MODE_BACKPLANE:
+		advertising |= ADVERTISED_10000baseKR_Full;
+		break;
+	case XGBE_PORT_MODE_BACKPLANE_2500:
+		advertising |= ADVERTISED_1000baseKX_Full;
+		break;
+	case XGBE_PORT_MODE_1000BASE_T:
+	case XGBE_PORT_MODE_1000BASE_X:
+	case XGBE_PORT_MODE_NBASE_T:
+		advertising |= ADVERTISED_1000baseKX_Full;
+		break;
+	case XGBE_PORT_MODE_10GBASE_T:
+		if (phy_data->phydev &&
+		    (phy_data->phydev->speed == SPEED_10000))
+			advertising |= ADVERTISED_10000baseKR_Full;
+		else
+			advertising |= ADVERTISED_1000baseKX_Full;
+		break;
+	case XGBE_PORT_MODE_10GBASE_R:
+		advertising |= ADVERTISED_10000baseKR_Full;
+		break;
+	case XGBE_PORT_MODE_SFP:
+		switch (phy_data->sfp_base) {
+		case XGBE_SFP_BASE_1000_T:
+		case XGBE_SFP_BASE_1000_SX:
+		case XGBE_SFP_BASE_1000_LX:
+		case XGBE_SFP_BASE_1000_CX:
+			advertising |= ADVERTISED_1000baseKX_Full;
+			break;
+		default:
+			advertising |= ADVERTISED_10000baseKR_Full;
+			break;
+		}
+		break;
+	default:
+		advertising |= ADVERTISED_10000baseKR_Full;
+		break;
+	}
+
+	return advertising;
+}
+
 static int xgbe_phy_an_config(struct xgbe_prv_data *pdata)
 {
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -1364,6 +1600,10 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
 {
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 
+	/* A KR re-driver will always require CL73 AN */
+	if (phy_data->redrv)
+		return XGBE_AN_MODE_CL73_REDRV;
+
 	switch (phy_data->port_mode) {
 	case XGBE_PORT_MODE_BACKPLANE:
 		return XGBE_AN_MODE_CL73;
@@ -1386,6 +1626,61 @@ static enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
 	}
 }
 
+static int xgbe_phy_set_redrv_mode_mdio(struct xgbe_prv_data *pdata,
+					enum xgbe_phy_redrv_mode mode)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	u16 redrv_reg, redrv_val;
+
+	redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);
+	redrv_val = (u16)mode;
+
+	return pdata->hw_if.write_ext_mii_regs(pdata, phy_data->redrv_addr,
+					       redrv_reg, redrv_val);
+}
+
+static int xgbe_phy_set_redrv_mode_i2c(struct xgbe_prv_data *pdata,
+				       enum xgbe_phy_redrv_mode mode)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	unsigned int redrv_reg;
+	int ret;
+
+	/* Calculate the register to write */
+	redrv_reg = XGBE_PHY_REDRV_MODE_REG + (phy_data->redrv_lane * 0x1000);
+
+	ret = xgbe_phy_redrv_write(pdata, redrv_reg, mode);
+
+	return ret;
+}
+
+static void xgbe_phy_set_redrv_mode(struct xgbe_prv_data *pdata)
+{
+	struct xgbe_phy_data *phy_data = pdata->phy_data;
+	enum xgbe_phy_redrv_mode mode;
+	int ret;
+
+	if (!phy_data->redrv)
+		return;
+
+	mode = XGBE_PHY_REDRV_MODE_CX;
+	if ((phy_data->port_mode == XGBE_PORT_MODE_SFP) &&
+	    (phy_data->sfp_base != XGBE_SFP_BASE_1000_CX) &&
+	    (phy_data->sfp_base != XGBE_SFP_BASE_10000_CR))
+		mode = XGBE_PHY_REDRV_MODE_SR;
+
+	ret = xgbe_phy_get_comm_ownership(pdata);
+	if (ret)
+		return;
+
+	if (phy_data->redrv_if)
+		xgbe_phy_set_redrv_mode_i2c(pdata, mode);
+	else
+		xgbe_phy_set_redrv_mode_mdio(pdata, mode);
+
+	xgbe_put_comm_ownership(pdata);
+}
+
 static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata)
 {
 	if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS))
@@ -1457,6 +1752,8 @@ static void xgbe_phy_sfi_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 10G/SFI */
@@ -1492,6 +1789,8 @@ static void xgbe_phy_x_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 1G/X */
@@ -1516,6 +1815,8 @@ static void xgbe_phy_sgmii_1000_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 1G/SGMII */
@@ -1540,6 +1841,8 @@ static void xgbe_phy_sgmii_100_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 1G/SGMII */
@@ -1564,6 +1867,8 @@ static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 10G/KR */
@@ -1588,6 +1893,8 @@ static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 2.5G/KX */
@@ -1612,6 +1919,8 @@ static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata)
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
 	unsigned int s0;
 
+	xgbe_phy_set_redrv_mode(pdata);
+
 	xgbe_phy_start_ratechange(pdata);
 
 	/* 1G/KX */
@@ -2232,6 +2541,30 @@ static int xgbe_phy_mdio_reset(struct xgbe_prv_data *pdata)
 	return ret;
 }
 
+static bool xgbe_phy_redrv_error(struct xgbe_phy_data *phy_data)
+{
+	if (!phy_data->redrv)
+		return false;
+
+	if (phy_data->redrv_if >= XGBE_PHY_REDRV_IF_MAX)
+		return true;
+
+	switch (phy_data->redrv_model) {
+	case XGBE_PHY_REDRV_MODEL_4223:
+		if (phy_data->redrv_lane > 3)
+			return true;
+		break;
+	case XGBE_PHY_REDRV_MODEL_4227:
+		if (phy_data->redrv_lane > 1)
+			return true;
+		break;
+	default:
+		return true;
+	}
+
+	return false;
+}
+
 static int xgbe_phy_mdio_reset_setup(struct xgbe_prv_data *pdata)
 {
 	struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2481,6 +2814,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 		dev_dbg(pdata->dev, "mdio addr=%u\n", phy_data->mdio_addr);
 	}
 
+	reg = XP_IOREAD(pdata, XP_PROP_4);
+	phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT);
+	phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF);
+	phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR);
+	phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE);
+	phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL);
+	if (phy_data->redrv && netif_msg_probe(pdata)) {
+		dev_dbg(pdata->dev, "redrv present\n");
+		dev_dbg(pdata->dev, "redrv i/f=%u\n", phy_data->redrv_if);
+		dev_dbg(pdata->dev, "redrv addr=%#x\n", phy_data->redrv_addr);
+		dev_dbg(pdata->dev, "redrv lane=%u\n", phy_data->redrv_lane);
+		dev_dbg(pdata->dev, "redrv model=%u\n", phy_data->redrv_model);
+	}
+
 	/* Validate the connection requested */
 	if (xgbe_phy_conn_type_mismatch(pdata)) {
 		dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n",
@@ -2499,6 +2846,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 	if (ret)
 		return ret;
 
+	/* Validate the re-driver information */
+	if (xgbe_phy_redrv_error(phy_data)) {
+		dev_err(pdata->dev, "phy re-driver settings error\n");
+		return -EINVAL;
+	}
+	pdata->kr_redrv = phy_data->redrv;
+
 	/* Indicate current mode is unknown */
 	phy_data->cur_mode = XGBE_MODE_UNKNOWN;
 
@@ -2651,6 +3005,29 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 		dev_dbg(pdata->dev, "phy supported=%#x\n",
 			pdata->phy.supported);
 
+	if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) &&
+	    (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) {
+		ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr,
+						    phy_data->phydev_mode);
+		if (ret) {
+			dev_err(pdata->dev,
+				"mdio port/clause not compatible (%d/%u)\n",
+				phy_data->mdio_addr, phy_data->phydev_mode);
+			return -EINVAL;
+		}
+	}
+
+	if (phy_data->redrv && !phy_data->redrv_if) {
+		ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr,
+						    XGBE_MDIO_MODE_CL22);
+		if (ret) {
+			dev_err(pdata->dev,
+				"redriver mdio port not compatible (%u)\n",
+				phy_data->redrv_addr);
+			return -EINVAL;
+		}
+	}
+
 	/* Register for driving external PHYs */
 	mii = devm_mdiobus_alloc(pdata->dev);
 	if (!mii) {
@@ -2700,5 +3077,7 @@ void xgbe_init_function_ptrs_phy_v2(struct xgbe_phy_if *phy_if)
 
 	phy_impl->an_config		= xgbe_phy_an_config;
 
+	phy_impl->an_advertising	= xgbe_phy_an_advertising;
+
 	phy_impl->an_outcome		= xgbe_phy_an_outcome;
 }
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index f3cbdb4..d821d1e 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -508,6 +508,7 @@ enum xgbe_xpcs_access {
 
 enum xgbe_an_mode {
 	XGBE_AN_MODE_CL73 = 0,
+	XGBE_AN_MODE_CL73_REDRV,
 	XGBE_AN_MODE_CL37,
 	XGBE_AN_MODE_CL37_SGMII,
 	XGBE_AN_MODE_NONE,
@@ -807,6 +808,9 @@ struct xgbe_phy_impl_if {
 	/* Configure auto-negotiation settings */
 	int (*an_config)(struct xgbe_prv_data *);
 
+	/* Set/override auto-negotiation advertisement settings */
+	unsigned int (*an_advertising)(struct xgbe_prv_data *);
+
 	/* Process results of auto-negotiation */
 	enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);
 
@@ -1124,6 +1128,8 @@ struct xgbe_prv_data {
 	unsigned long link_check;
 	struct completion mdio_complete;
 
+	unsigned int kr_redrv;
+
 	char an_name[IFNAMSIZ + 32];
 	struct workqueue_struct *an_workqueue;
 

^ permalink raw reply related

* RE: [PATCH net-next v1 02/21] amd-xgbe: Prepare for priority-based FIFO allocation
From: Mintz, Yuval @ 2016-11-03 13:51 UTC (permalink / raw)
  To: Tom Lendacky, netdev@vger.kernel.org; +Cc: Florian Fainelli, David Miller
In-Reply-To: <20161103132804.3437.40002.stgit@tlendack-t1.amdoffice.net>

> +static void xgbe_calculate_equal_fifo(unsigned int fifo_size,
> +				      unsigned int queue_count,
> +				      unsigned int *fifo)
> +{
...
> +
> +	return;
>  }

No need for explicit return.


^ permalink raw reply

* Re: [mm PATCH v2 01/26] swiotlb: Drop unused functions swiotlb_map_sg and swiotlb_unmap_sg
From: Christoph Hellwig @ 2016-11-03 14:14 UTC (permalink / raw)
  To: Alexander Duyck
  Cc: linux-mm, akpm, netdev, linux-kernel, Konrad Rzeszutek Wilk
In-Reply-To: <20161102111211.79519.39931.stgit@ahduyck-blue-test.jf.intel.com>

On Wed, Nov 02, 2016 at 07:12:31AM -0400, Alexander Duyck wrote:
> There are no users for swiotlb_map_sg or swiotlb_unmap_sg so we might as
> well just drop them.

FYI, I sent the same patch already on Sep, 11 and Konrad already ACKed
it:

https://lkml.org/lkml/2016/9/11/112
https://lkml.org/lkml/2016/9/16/474

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: bpf: kernel BUG in htab_elem_free
From: Dmitry Vyukov @ 2016-11-03 14:15 UTC (permalink / raw)
  To: Alexei Starovoitov, netdev, Daniel Borkmann; +Cc: LKML, syzkaller
In-Reply-To: <CACT4Y+YUWArFF=6VGJvzTEr7C7E323gOCno7fyw36xBYC7MH=w@mail.gmail.com>

On Wed, Nov 2, 2016 at 11:14 PM, Dmitry Vyukov <dvyukov@google.com> wrote:
> Here we go.
>
> The following program triggers kernel BUG in htab_elem_free.
> On commit 0c183d92b20b5c84ca655b45ef57b3318b83eb9e (Oct 31).
> Run as "while true; do ./a.out; done".
>
> ------------[ cut here ]------------
> kernel BUG at mm/slub.c:3866!
> invalid opcode: 0000 [#1] SMP KASAN
> Modules linked in:
> CPU: 1 PID: 1542 Comm: kworker/1:2 Not tainted 4.9.0-rc3+ #20
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> Workqueue: events bpf_map_free_deferred
> task: ffff88003b9c0040 task.stack: ffff88003cb70000
> RIP: 0010:[<ffffffff814c9f00>]  [<ffffffff814c9f00>] kfree+0x140/0x1a0
> RSP: 0018:ffff88003cb77c50  EFLAGS: 00010246
> RAX: ffffea0000fb0aa0 RBX: ffff88003ec2a1a8 RCX: 0000000000000000
> RDX: 0000000000000000 RSI: 1ffff10007b50401 RDI: ffff88003ec2a1a8
> RBP: ffff88003cb77c70 R08: 0000000000021800 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000000 R12: ffffea0000fb0a80
> R13: ffffffff81392bcb R14: 0000000000000000 R15: ffff88003ec2a1a8
> FS:  0000000000000000(0000) GS:ffff88003ed00000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00000000205d7000 CR3: 0000000037d29000 CR4: 00000000000006e0
> Stack:
>  dffffc0000000000 ffff88003da82008 ffff88003b75bb88 0000000000000000
>  ffff88003cb77ce0 ffffffff81392bcb ffffffff81acf4f8 ffff88003b75bc04
>  ffff88003b75bbe0 ffffed00076eb772 ffff88003b75bb90 000000003cb77ce0
> Call Trace:
>  [<     inline     >] htab_elem_free kernel/bpf/hashtab.c:388
>  [<     inline     >] delete_all_elements kernel/bpf/hashtab.c:690
>  [<ffffffff81392bcb>] htab_map_free+0x30b/0x470 kernel/bpf/hashtab.c:711
>  [<ffffffff8137e9dc>] bpf_map_free_deferred+0xac/0xd0 kernel/bpf/syscall.c:97
>  [<ffffffff8114f937>] process_one_work+0x8a7/0x1300 kernel/workqueue.c:2096
>  [<ffffffff8115047d>] worker_thread+0xed/0x14e0 kernel/workqueue.c:2230
>  [<ffffffff811619dc>] kthread+0x1ec/0x260 kernel/kthread.c:209
>  [<ffffffff82fa78c5>] ret_from_fork+0x25/0x30 arch/x86/entry/entry_64.S:433
> Code: 83 c4 18 48 89 da 4c 89 ee ff d0 49 8b 04 24 48 85 c0 75 e6 e9
> e9 fe ff ff 49 8b 04 24 f6 c4 40 75 0b 49 8b 44 24 20 a8 01 75 02 <0f>
> 0b 48 89 df e8 56 35 00 00 49 8b 04 24 31 f6 f6 c4 40 74 05
> RIP  [<     inline     >] PageCompound ./include/linux/page-flags.h:157
> RIP  [<ffffffff814c9f00>] kfree+0x140/0x1a0 mm/slub.c:3866
>  RSP <ffff88003cb77c50>
> ---[ end trace 1dc58d6aeb2596aa ]---
> ==================================================================
> BUG: KASAN: stack-out-of-bounds in complete+0x68/0x70 at addr ffff88003cb77ed8
> Read of size 4 by task kworker/1:2/1542
> page:ffffea0000f2ddc0 count:0 mapcount:0 mapping:          (null) index:0x0
> flags: 0x100000000000000()
> page dumped because: kasan: bad access detected
> CPU: 1 PID: 1542 Comm: kworker/1:2 Tainted: G      D         4.9.0-rc3+ #20
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
>  ffff88003cb77ce0 ffffffff81acf609 ffffed000796efdb ffffed000796efdb
>  0000000000000004 0000000000000000 ffff88003cb77d60 ffffffff814cdbfb
>  ffff88003c8d97c8 dffffc0000000000 ffffffff811dd038 0000000000000097
> Call Trace:
>  [<     inline     >] __dump_stack lib/dump_stack.c:15
>  [<ffffffff81acf609>] dump_stack+0x83/0xba lib/dump_stack.c:51
>  [<     inline     >] kasan_report_error mm/kasan/report.c:204
>  [<ffffffff814cdbfb>] kasan_report+0x4cb/0x500 mm/kasan/report.c:303
>  [<ffffffff814cdc84>] __asan_report_load4_noabort+0x14/0x20
> mm/kasan/report.c:328
>  [<ffffffff811dd038>] complete+0x68/0x70 kernel/sched/completion.c:34
>  [<     inline     >] complete_vfork_done kernel/fork.c:1030
>  [<ffffffff810fa8c2>] mm_release+0x222/0x3f0 kernel/fork.c:1114
>  [<     inline     >] exit_mm kernel/exit.c:467
>  [<ffffffff8110b501>] do_exit+0x3a1/0x2960 kernel/exit.c:815
>  [<ffffffff82fa8b97>] rewind_stack_do_exit+0x17/0x20
> arch/x86/entry/entry_64.S:1526
> Memory state around the buggy address:
>  ffff88003cb77d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  ffff88003cb77e00: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 f4 f4 f4
>>ffff88003cb77e80: f2 f2 f2 f2 00 f4 f4 f4 f2 f2 f2 f2 00 00 f4 f4
>                                                     ^
>  ffff88003cb77f00: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
>  ffff88003cb77f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> ==================================================================
> BUG: unable to handle kernel
> paging request at ffffffffffffffd8
> IP: [<ffffffff81163c5d>] kthread_data+0x4d/0x70 kernel/kthread.c:137
> PGD 360d067 [   48.581115] PUD 360f067
> PMD 0 [   48.581840]
> Oops: 0000 [#2] SMP KASAN
> Modules linked in:
> CPU: 1 PID: 1542 Comm: kworker/1:2 Tainted: G    B D         4.9.0-rc3+ #20
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> task: ffff88003b9c0040 task.stack: ffff88003cb70000
> RIP: 0010:[<ffffffff81163c5d>]  [<ffffffff81163c5d>] kthread_data+0x4d/0x70
> RSP: 0018:ffff88003cb77c78  EFLAGS: 00010046
> RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000
> RDX: 1ffffffffffffffb RSI: ffff88003b9c00c0 RDI: ffffffffffffffd8
> RBP: ffff88003cb77c80 R08: ffff88003ed20a48 R09: ffff88003ed20a40
> R10: 0000000000000000 R11: 0000000000000000 R12: ffff88003ed20980
> R13: ffff88003b9c0040 R14: ffff88003b9c0094 R15: 0000000000000040
> FS:  0000000000000000(0000) GS:ffff88003ed00000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000000000000028 CR3: 000000000360c000 CR4: 00000000000006e0
> Stack:
>  ffff88003b9c0040 ffff88003cb77ca0 ffffffff81155e77 0000000000020980
>  ffff88003ed20980 ffff88003cb77db8 ffffffff82f9b1a2 0000000000000000
>  ffff88003ddd2670 00ff88003ddd2640 ffff88003ed211f8 1ffff1000796ef9e
> Call Trace:
>  [<ffffffff81155e77>] wq_worker_sleeping+0x17/0x210 kernel/workqueue.c:876
>  [<ffffffff82f9b1a2>] __schedule+0xc62/0x1730 kernel/sched/core.c:3380
>  [<ffffffff8118c781>] do_task_dead+0x81/0xa0 kernel/sched/core.c:3431
>  [<ffffffff8110c7f8>] do_exit+0x1698/0x2960 kernel/exit.c:885
>  [<ffffffff82fa8b97>] rewind_stack_do_exit+0x17/0x20
> arch/x86/entry/entry_64.S:1526
> Code: c1 ea 03 80 3c 02 00 75 29 48 8b 9b 30 05 00 00 48 b8 00 00 00
> 00 00 fc ff df 48 8d 7b d8 48 89 fa 48 c1 ea 03 80 3c 02 00 75 0e <48>
> 8b 43 d8 5b 5d c3 e8 27 a0 36 00 eb d0 e8 20 a0 36 00 eb eb
> RIP  [<ffffffff81163c5d>] kthread_data+0x4d/0x70 kernel/kthread.c:137
>  RSP <ffff88003cb77c78>
> CR2: ffffffffffffffd8
> ---[ end trace 1dc58d6aeb2596ab ]---
> Fixing recursive fault but reboot is needed!
>
>
> // autogenerated by syzkaller (http://github.com/google/syzkaller)
>
> #ifndef __NR_bpf
> #define __NR_bpf 321
> #endif
>
> #include <fcntl.h>
> #include <pthread.h>
> #include <signal.h>
> #include <stddef.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <sys/ioctl.h>
> #include <sys/stat.h>
> #include <sys/syscall.h>
> #include <sys/types.h>
> #include <unistd.h>
>
> long r[14];
> void* thr(void* arg)
> {
>   switch ((long)arg) {
>   case 0:
>     r[0] =
>         syscall(__NR_mmap, 0x20000000ul, 0x16000ul, 0x3ul,
>                         0x32ul, 0xfffffffffffffffful, 0x0ul, 0, 0, 0);
>     break;
>   case 1:
>     (*(uint32_t*)0x20011000 = (uint32_t)0x1);
>     (*(uint32_t*)0x20011004 = (uint32_t)0x8);
>     (*(uint32_t*)0x20011008 = (uint32_t)0x3);
>     (*(uint32_t*)0x2001100c = (uint32_t)0x1);
>     (*(uint32_t*)0x20011010 = (uint32_t)0x1);
>     r[6] = syscall(__NR_bpf, 0x0ul, 0x20011000ul, 0x14ul, 0, 0,
>                            0, 0, 0, 0);
>     break;
>   case 2:
>     (*(uint32_t*)0x20013000 = r[6]);
>     (*(uint64_t*)0x20013008 = (uint64_t)0x20013fb3);
>     (*(uint64_t*)0x20013010 = (uint64_t)0x20012ff1);
>     (*(uint64_t*)0x20013018 = (uint64_t)0x0);
>     (memcpy(
>         (void*)0x20013fb3,
>         "\x3e\x51\x32\xbe\xd5\x24\x20\xb2\x50\x7a\x4d\xb5\xec\xb3\x8f"
>         "\x65\x7f\xac\x61\x9a\xf0\x29\x3f\x77\x07\x2f\x2f\x60\xe9\x78"
>         "\xc5\x79\x45\x16\x67\xf6\x64\xb4\xd5\xb2\x11\x88\x5f\x4f\x32"
>         "\xba\xa8\x80\x8f\x7a\xea\x01\x1d\xe4\x08\xa4\x65\x73\x07\x91"
>         "\x48\xd5\xc3\xf2\xc4\x08\x29\x8f\x88\x95\xc3\xd5\xf6\x86\x0f"
>         "\x42\xab\x05\xf7\xfa\x2b\x12\x78\xb3\x4d\x17\x8c\x27\x57\x8b"
>         "\x79\xdc\x4f\x8a\x7a\xf5\x8c\x8a\xc2\x18\x03\xa0\xf9\x5f\x7a",
>         105));
>     (memcpy(
>         (void*)0x20012ff1,
>         "\xb1\xb0\x4b\x14\x9e\xfa\xbc\xb2\xaf\x4b\x4a\x02\xbc\x9b\xc5",
>         15));
>     r[13] = syscall(__NR_bpf, 0x2ul, 0x20013000ul, 0x20ul, 0, 0,
>                             0, 0, 0, 0);
>     break;
>   }
>   return 0;
> }
>
> int main()
> {
>   long i;
>   pthread_t th[6];
>
>   memset(r, -1, sizeof(r));
>   srand(getpid());
>   for (i = 0; i < 3; i++) {
>     pthread_create(&th[i], 0, thr, (void*)i);
>     usleep(10000);
>   }
>   for (i = 0; i < 3; i++) {
>     pthread_create(&th[3 + i], 0, thr, (void*)i);
>     if (rand() % 2)
>       usleep(rand() % 10000);
>   }
>   usleep(100000);
>   return 0;
> }


Sometimes it crashes with "unable to handle kernel paging request" :

BUG: unable to handle kernel paging request at ffffeb83fffec020
IP: [<     inline     >] virt_to_head_page include/linux/mm.h:555
IP: [<ffffffff814c9e15>] kfree+0x55/0x1a0 mm/slub.c:3864
PGD 0 [ 1103.309066]
Oops: 0000 [#1] SMP KASAN
Dumping ftrace buffer:
   (ftrace buffer empty)
Modules linked in:
CPU: 3 PID: 5460 Comm: kworker/3:3 Not tainted 4.9.0-rc3+ #20
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
Workqueue: events bpf_map_free_deferred
task: ffff8800676b6e40 task.stack: ffff880067680000
RIP: 0010:[<ffffffff814c9e15>]  [<     inline     >] virt_to_head_page
include/linux/mm.h:555
RIP: 0010:[<ffffffff814c9e15>]  [<ffffffff814c9e15>] kfree+0x55/0x1a0
mm/slub.c:3864
RSP: 0018:ffff880067687c50  EFLAGS: 00010286
RAX: ffffea0000000000 RBX: ffffe8ffffb00748 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 1ffff1000db56cb5 RDI: ffffe8ffffb00748
RBP: ffff880067687c70 R08: 0000000000003400 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffffeb83fffec000
R13: ffffffff81392bcb R14: 0000000000000000 R15: ffffe8ffffb00748
FS:  0000000000000000(0000) GS:ffff88006e500000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: ffffeb83fffec020 CR3: 000000003d24f000 CR4: 00000000000006e0
DR0: 0000000000000007 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000600
Stack:
 dffffc0000000000 ffff88006dab65a8 ffff88006d0490c8 0000000000000000
 ffff880067687ce0 ffffffff81392bcb ffffffff81acf4f8 ffff88006d049144
 ffff88006d049120 ffffed000da0921a ffff88006d0490d0 0000000067687ce0
Call Trace:
 [<     inline     >] htab_elem_free kernel/bpf/hashtab.c:388
 [<     inline     >] delete_all_elements kernel/bpf/hashtab.c:690
 [<ffffffff81392bcb>] htab_map_free+0x30b/0x470 kernel/bpf/hashtab.c:711
 [<ffffffff8137e9dc>] bpf_map_free_deferred+0xac/0xd0 kernel/bpf/syscall.c:97
 [<ffffffff8114f937>] process_one_work+0x8a7/0x1300 kernel/workqueue.c:2096
 [<ffffffff8115047d>] worker_thread+0xed/0x14e0 kernel/workqueue.c:2230
 [<ffffffff811619dc>] kthread+0x1ec/0x260 kernel/kthread.c:209
 [<ffffffff82fa78c5>] ret_from_fork+0x25/0x30 arch/x86/entry/entry_64.S:433
Code: 48 01 d8 0f 82 53 01 00 00 49 bc 00 00 00 80 ff 77 00 00 49 01
c4 48 b8 00 00 00 00 00 ea ff ff 49 c1 ec 0c 49 c1 e4 06 49 01 c4 <49>
8b 44 24 20 48 8d 50 ff a8 01 4c 0f 45 e2 49 8b 54 24 20 48
RIP  [<     inline     >] virt_to_head_page include/linux/mm.h:555
RIP  [<ffffffff814c9e15>] kfree+0x55/0x1a0 mm/slub.c:3864
 RSP <ffff880067687c50>
CR2: ffffeb83fffec020
---[ end trace 9271605118c02ee3 ]---
Kernel panic - not syncing: Fatal exception
Dumping ftrace buffer:
   (ftrace buffer empty)
Kernel Offset: disabled
reboot: cpu_has_vmx: ecx=80a02021 1

^ permalink raw reply

* Re: [PATCH net-next v2 0/5] bpf: BPF for lightweight tunnel encapsulation
From: Thomas Graf @ 2016-11-03 14:16 UTC (permalink / raw)
  To: Hannes Frederic Sowa
  Cc: Tom Herbert, David S. Miller, Alexei Starovoitov, Daniel Borkmann,
	roopa, netdev
In-Reply-To: <7ac775f4-4638-3bd0-f6f8-e54c7d26e8d8@stressinduktion.org>

On 2 November 2016 at 04:48, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> On Wed, Nov 2, 2016, at 00:07, Tom Herbert wrote:
>> On the other hand, I'm not really sure how to implement for this level
>> of performance this in LWT+BPF either. It seems like one way to do
>> that would be to create a program each destination and set it each
>> host. As you point out would create a million different programs which
>> doesn't seem manageable. I don't think the BPF map works either since
>> that implies we need a lookup (?). It seems like what we need is one
>> program but allow it to be parameterized with per destination
>> information saved in the route (LWT structure).
>
> Yes, that is my proposal. Just using the dst entry as meta-data (which
> can actually also be an ID for the network namespace the packet is
> coming from).

I have no objection to doing this on top of this series.

> My concern with using BPF is that the rest of the kernel doesn't really
> see the semantics and can't optimize or cache at specific points,
> because the kernel cannot introspect what the BPF program does (for
> metadata manipulation, one can e.g. specifiy that the program is "pure",
> and always provides the same output for some specified given input, thus
> things can be cached and memorized, but that framework seems very hard
> to build).

So you want to reintroduce a routing cache? Each packet needs to pass
through the BPF program anyway for accounting purposes. This is not
just about getting the packets out the right nexthop in the fastest
possible way.

> I also fear this becomes a kernel by-pass:
>
> It might be very hard e.g. to apply NFT/netfilter to such packets, if
> e.g. a redirect happens suddenly and packet flow is diverted from the
> one the user sees currently based on the interfaces and routing tables.

The LWT xmit hook is after the POST_ROUTING hook. The input and output
hook cannot redirect and output will become read-only just like input
already is. We are not bypassing anything. Please stop throwing the
word bypass around. This is just a false claim.

> That's why I am in favor of splitting this patchset down and allow the
> policies that should be expressed by BPF programs being applied to the
> specific subsystems (I am not totally against a generic BPF hook in
> input or output of the protocol engines). E.g. can we deal with static
> rewriting of L2 addresses in the neighbor cache? We already provide a
> fast header cache for L2 data which might be used here?

Split what? What policies?

I have two primary use cases for this:
1) Traffic into local containers: Containers are only supposed to do
L3, all L2 traffic is dropped for security reasons. The L2 header for
any packets in and out of the container is fixed and does not require
any sort of resolving. I in order to feed packets from the local host
into the containers, a route with the container prefix is set up. It
points to a nexthop address which appears behind a veth pair. A BPF
program is listening at tc ingress on the veth pair and will enforce
policies and do accounting. It requires very ugly hacks because Linux
does not like to do forwarding to an address which is considered
local. It works but it is a hack.

What I want to do instead is to run the BPF program for the route
directly, apply the policies, do accounting, push the fixed dummy L2
header and redirect it to the container. If someone has netfilter
rules installed, they will still apply. Nothing is hidden.

2) For external traffic that is coming in. We have a BPF program
listening on tc ingress which matches on the destination address on
all incoming traffic. If the packet is a for a container, we perform
the same actions as above. In this case we are bypassing the routing
table. This is ugly. What I want to do instead is to have the
container prefix invoke the BPF program so all packets have a route
lookup performed and netfilter filtering performed, only after that,
the BPF program is invoked exclusively for the packets destined for
local containers. Yes, it would be possible to redirect into a
temporary veth again and listen on that but it again requires to fake
a L2 segment which is just unnecessary and slow.

This is not hiding anything and it is not bypassing anything.

^ permalink raw reply

* Re: [PATCH net-next v1 17/21] amd-xgbe: Add I2C support for determining SFP media types
From: Andrew Lunn @ 2016-11-03 14:28 UTC (permalink / raw)
  To: Tom Lendacky; +Cc: netdev, Florian Fainelli, David Miller
In-Reply-To: <20161103133036.3437.71231.stgit@tlendack-t1.amdoffice.net>

On Thu, Nov 03, 2016 at 08:30:36AM -0500, Tom Lendacky wrote:
> Add support to initialize and use the I2C controller within the hardware
> in order to determine the SFP media type that is installed.

Hi Tom

Did you see the work Russell King did for phylink?

https://lwn.net/Articles/667055/

Rather than doing your own proprietary i2c infrastructure, use the
standard Linux i2c infrastructure. The SFP modules are then just
normal i2c devices on a normal i2c bus. The work Russell did can then
access them, export them to user space, get the PHY modes correctly
setup, etc.

       Andrew

^ permalink raw reply

* Re: [mm PATCH v2 01/26] swiotlb: Drop unused functions swiotlb_map_sg and swiotlb_unmap_sg
From: Konrad Rzeszutek Wilk @ 2016-11-03 14:29 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Alexander Duyck, linux-mm, akpm, netdev, linux-kernel
In-Reply-To: <20161103141446.GA29720@infradead.org>

On Thu, Nov 03, 2016 at 07:14:46AM -0700, Christoph Hellwig wrote:
> On Wed, Nov 02, 2016 at 07:12:31AM -0400, Alexander Duyck wrote:
> > There are no users for swiotlb_map_sg or swiotlb_unmap_sg so we might as
> > well just drop them.
> 
> FYI, I sent the same patch already on Sep, 11 and Konrad already ACKed
> it:
> 
> https://lkml.org/lkml/2016/9/11/112
> https://lkml.org/lkml/2016/9/16/474

Somehow I thought you wanted to put them through your tree (which
is why I acked them).

I can take them and also the first couple of Alexander through
my tree. Or if it makes it simpler - they can go through the -mm tree?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH net-next v2 1/1] driver: veth: Refine the statistics codes of veth driver
From: Eric Dumazet @ 2016-11-03 14:31 UTC (permalink / raw)
  To: Gao Feng
  Cc: David S. Miller, Cong Wang, Vijay Pandurangan, Evan Jones, pabeni,
	Linux Kernel Network Developers
In-Reply-To: <CA+6hz4r9zkPp7-mG7b98ocPvjTiheSnEBMjffSkLXD1MM-aMtg@mail.gmail.com>

On Thu, 2016-11-03 at 21:39 +0800, Gao Feng wrote:
> Hi Eric,
> 
> On Thu, Nov 3, 2016 at 9:30 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> > On Thu, 2016-11-03 at 21:03 +0800, fgao@ikuai8.com wrote:
> >> From: Gao Feng <fgao@ikuai8.com>
> >>
> >> The dropped count of veth is located in struct veth_priv, but other
> >> statistics like packets and bytes are in another struct pcpu_vstats.
> >> Now keep these three counters in the same struct.
> >>
> >> Signed-off-by: Gao Feng <fgao@ikuai8.com>
> >> ---
> >>  v2: Use right "peer" instead of "dev";
> >>  v1: Initial version
> >
> > May I ask : Why ?
> 
> Just because I think statistics should be in the same struct.

That is not a good reason then.

> 
> >
> > We did that because there was no point making per-cpu requirements
> > bigger, for a counter that is hardly ever updated.
> >
> > Do you have a real case where performance dropping packets in a driver
> > is needed ?
> 
> No, I haven't met the performance issue now.

OK then kill this patch.

> 
> >
> > At some point we will have to stop dumb percpu explosion, when we have
> > 128+ cores per host. Folding all these percpu counters is taking a lot
> > of time too.
> >
> >
> >
> Ok, I get it. It is designed specially to put the dropped counter as
> atomic counter, not percpu.
> But I have one question that when put the counters as percpu, and when not?

Because the regular fast path needs to be fast ?

Try to _use_ veth without these percpu stats and be prepared to be
shocked.

^ permalink raw reply

* Re: [PATCH net-next v2 1/1] driver: veth: Refine the statistics codes of veth driver
From: Gao Feng @ 2016-11-03 14:38 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S. Miller, Cong Wang, Vijay Pandurangan, Evan Jones, pabeni,
	Linux Kernel Network Developers
In-Reply-To: <1478183516.7065.440.camel@edumazet-glaptop3.roam.corp.google.com>

On Thu, Nov 3, 2016 at 10:31 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Thu, 2016-11-03 at 21:39 +0800, Gao Feng wrote:
>> Hi Eric,
>>
>> On Thu, Nov 3, 2016 at 9:30 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
>> > On Thu, 2016-11-03 at 21:03 +0800, fgao@ikuai8.com wrote:
>> >> From: Gao Feng <fgao@ikuai8.com>
>> >>
>> >> The dropped count of veth is located in struct veth_priv, but other
>> >> statistics like packets and bytes are in another struct pcpu_vstats.
>> >> Now keep these three counters in the same struct.
>> >>
>> >> Signed-off-by: Gao Feng <fgao@ikuai8.com>
>> >> ---
>> >>  v2: Use right "peer" instead of "dev";
>> >>  v1: Initial version
>> >
>> > May I ask : Why ?
>>
>> Just because I think statistics should be in the same struct.
>
> That is not a good reason then.

Because other net devices put the statistics together.
Take tun/tap as example, it is a virtual device, but its all
statistics are percpu including dropped.

Regards
Feng

>
>>
>> >
>> > We did that because there was no point making per-cpu requirements
>> > bigger, for a counter that is hardly ever updated.
>> >
>> > Do you have a real case where performance dropping packets in a driver
>> > is needed ?
>>
>> No, I haven't met the performance issue now.
>
> OK then kill this patch.
>
>>
>> >
>> > At some point we will have to stop dumb percpu explosion, when we have
>> > 128+ cores per host. Folding all these percpu counters is taking a lot
>> > of time too.
>> >
>> >
>> >
>> Ok, I get it. It is designed specially to put the dropped counter as
>> atomic counter, not percpu.
>> But I have one question that when put the counters as percpu, and when not?
>
> Because the regular fast path needs to be fast ?
>
> Try to _use_ veth without these percpu stats and be prepared to be
> shocked.
>
>
>

^ permalink raw reply

* Re: [mm PATCH v2 01/26] swiotlb: Drop unused functions swiotlb_map_sg and swiotlb_unmap_sg
From: Christoph Hellwig @ 2016-11-03 14:45 UTC (permalink / raw)
  To: Konrad Rzeszutek Wilk
  Cc: Christoph Hellwig, Alexander Duyck, linux-mm, akpm, netdev,
	linux-kernel
In-Reply-To: <20161103142952.GJ28691@localhost.localdomain>

On Thu, Nov 03, 2016 at 10:29:52AM -0400, Konrad Rzeszutek Wilk wrote:
> Somehow I thought you wanted to put them through your tree (which
> is why I acked them).
> 
> I can take them and also the first couple of Alexander through
> my tree. Or if it makes it simpler - they can go through the -mm tree?

I don't have a tree for it, so I kinda expected you to pick it up.
But I'm also fine with you just Acking the version from Alex and having
him funnel it through whatever tree he wants to get his patches in
through.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* [PATCH 08/25] net/dev: Convert to hotplug state machine
From: Sebastian Andrzej Siewior @ 2016-11-03 14:50 UTC (permalink / raw)
  To: linux-kernel
  Cc: rt, Sebastian Andrzej Siewior, David S. Miller, netdev,
	Thomas Gleixner
In-Reply-To: <20161103145021.28528-1-bigeasy@linutronix.de>

Install the callbacks via the state machine.

Cc: "David S. Miller" <davem@davemloft.net>
Cc: netdev@vger.kernel.org
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/cpuhotplug.h |  1 +
 net/core/dev.c             | 16 ++++++----------
 2 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 31c58f6ec3c6..394eb7ed53be 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -36,6 +36,7 @@ enum cpuhp_state {
 	CPUHP_PERCPU_CNT_DEAD,
 	CPUHP_RADIX_DEAD,
 	CPUHP_PAGE_ALLOC_DEAD,
+	CPUHP_NET_DEV_DEAD,
 	CPUHP_WORKQUEUE_PREP,
 	CPUHP_POWER_NUMA_PREPARE,
 	CPUHP_HRTIMERS_PREPARE,
diff --git a/net/core/dev.c b/net/core/dev.c
index 4bc19a164ba5..71693729bdd5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -7947,18 +7947,13 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
 }
 EXPORT_SYMBOL_GPL(dev_change_net_namespace);
 
-static int dev_cpu_callback(struct notifier_block *nfb,
-			    unsigned long action,
-			    void *ocpu)
+static int dev_cpu_dead(unsigned int oldcpu)
 {
 	struct sk_buff **list_skb;
 	struct sk_buff *skb;
-	unsigned int cpu, oldcpu = (unsigned long)ocpu;
+	unsigned int cpu;
 	struct softnet_data *sd, *oldsd;
 
-	if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
-		return NOTIFY_OK;
-
 	local_irq_disable();
 	cpu = smp_processor_id();
 	sd = &per_cpu(softnet_data, cpu);
@@ -8008,10 +8003,9 @@ static int dev_cpu_callback(struct notifier_block *nfb,
 		input_queue_head_incr(oldsd);
 	}
 
-	return NOTIFY_OK;
+	return 0;
 }
 
-
 /**
  *	netdev_increment_features - increment feature set by one
  *	@all: current feature set
@@ -8345,7 +8339,9 @@ static int __init net_dev_init(void)
 	open_softirq(NET_TX_SOFTIRQ, net_tx_action);
 	open_softirq(NET_RX_SOFTIRQ, net_rx_action);
 
-	hotcpu_notifier(dev_cpu_callback, 0);
+	rc = cpuhp_setup_state_nocalls(CPUHP_NET_DEV_DEAD, "net/dev:dead",
+				       NULL, dev_cpu_dead);
+	WARN_ON(rc < 0);
 	dst_subsys_init();
 	rc = 0;
 out:
-- 
2.10.2

^ permalink raw reply related

* [PATCH 09/25] net/flowcache: Convert to hotplug state machine
From: Sebastian Andrzej Siewior @ 2016-11-03 14:50 UTC (permalink / raw)
  To: linux-kernel
  Cc: rt, Sebastian Andrzej Siewior, David S. Miller, Steffen Klassert,
	Herbert Xu, netdev, Thomas Gleixner
In-Reply-To: <20161103145021.28528-1-bigeasy@linutronix.de>

Install the callbacks via the state machine. Use multi state support to avoid
custom list handling for the multiple instances.

Cc: "David S. Miller" <davem@davemloft.net>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: netdev@vger.kernel.org
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 include/linux/cpuhotplug.h |  1 +
 include/net/flow.h         |  1 +
 include/net/flowcache.h    |  2 +-
 net/core/flow.c            | 60 ++++++++++++++++++++--------------------------
 net/xfrm/xfrm_policy.c     |  1 +
 5 files changed, 30 insertions(+), 35 deletions(-)

diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 394eb7ed53be..86b940f19df8 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -56,6 +56,7 @@ enum cpuhp_state {
 	CPUHP_ARM_SHMOBILE_SCU_PREPARE,
 	CPUHP_SH_SH3X_PREPARE,
 	CPUHP_BLK_MQ_PREPARE,
+	CPUHP_NET_FLOW_PREPARE,
 	CPUHP_TIMERS_DEAD,
 	CPUHP_NOTF_ERR_INJ_PREPARE,
 	CPUHP_MIPS_SOC_PREPARE,
diff --git a/include/net/flow.h b/include/net/flow.h
index 035aa7716967..2e386bd6ee63 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -239,6 +239,7 @@ struct flow_cache_object *flow_cache_lookup(struct net *net,
 					    void *ctx);
 int flow_cache_init(struct net *net);
 void flow_cache_fini(struct net *net);
+void flow_cache_hp_init(void);
 
 void flow_cache_flush(struct net *net);
 void flow_cache_flush_deferred(struct net *net);
diff --git a/include/net/flowcache.h b/include/net/flowcache.h
index c8f665ec6e0d..9caf3bfc8d2d 100644
--- a/include/net/flowcache.h
+++ b/include/net/flowcache.h
@@ -17,7 +17,7 @@ struct flow_cache_percpu {
 struct flow_cache {
 	u32				hash_shift;
 	struct flow_cache_percpu __percpu *percpu;
-	struct notifier_block		hotcpu_notifier;
+	struct hlist_node		node;
 	int				low_watermark;
 	int				high_watermark;
 	struct timer_list		rnd_timer;
diff --git a/net/core/flow.c b/net/core/flow.c
index 3937b1b68d5b..841fd7f87b30 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -419,28 +419,20 @@ static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu)
 	return 0;
 }
 
-static int flow_cache_cpu(struct notifier_block *nfb,
-			  unsigned long action,
-			  void *hcpu)
+static int flow_cache_cpu_up_prep(unsigned int cpu, struct hlist_node *node)
 {
-	struct flow_cache *fc = container_of(nfb, struct flow_cache,
-						hotcpu_notifier);
-	int res, cpu = (unsigned long) hcpu;
+	struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node);
+
+	return flow_cache_cpu_prepare(fc, cpu);
+}
+
+static int flow_cache_cpu_dead(unsigned int cpu, struct hlist_node *node)
+{
+	struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node);
 	struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu);
 
-	switch (action) {
-	case CPU_UP_PREPARE:
-	case CPU_UP_PREPARE_FROZEN:
-		res = flow_cache_cpu_prepare(fc, cpu);
-		if (res)
-			return notifier_from_errno(res);
-		break;
-	case CPU_DEAD:
-	case CPU_DEAD_FROZEN:
-		__flow_cache_shrink(fc, fcp, 0);
-		break;
-	}
-	return NOTIFY_OK;
+	__flow_cache_shrink(fc, fcp, 0);
+	return 0;
 }
 
 int flow_cache_init(struct net *net)
@@ -467,18 +459,8 @@ int flow_cache_init(struct net *net)
 	if (!fc->percpu)
 		return -ENOMEM;
 
-	cpu_notifier_register_begin();
-
-	for_each_online_cpu(i) {
-		if (flow_cache_cpu_prepare(fc, i))
-			goto err;
-	}
-	fc->hotcpu_notifier = (struct notifier_block){
-		.notifier_call = flow_cache_cpu,
-	};
-	__register_hotcpu_notifier(&fc->hotcpu_notifier);
-
-	cpu_notifier_register_done();
+	if (cpuhp_state_add_instance(CPUHP_NET_FLOW_PREPARE, &fc->node))
+		goto err;
 
 	setup_timer(&fc->rnd_timer, flow_cache_new_hashrnd,
 		    (unsigned long) fc);
@@ -494,8 +476,6 @@ int flow_cache_init(struct net *net)
 		fcp->hash_table = NULL;
 	}
 
-	cpu_notifier_register_done();
-
 	free_percpu(fc->percpu);
 	fc->percpu = NULL;
 
@@ -509,7 +489,8 @@ void flow_cache_fini(struct net *net)
 	struct flow_cache *fc = &net->xfrm.flow_cache_global;
 
 	del_timer_sync(&fc->rnd_timer);
-	unregister_hotcpu_notifier(&fc->hotcpu_notifier);
+
+	cpuhp_state_remove_instance_nocalls(CPUHP_NET_FLOW_PREPARE, &fc->node);
 
 	for_each_possible_cpu(i) {
 		struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i);
@@ -521,3 +502,14 @@ void flow_cache_fini(struct net *net)
 	fc->percpu = NULL;
 }
 EXPORT_SYMBOL(flow_cache_fini);
+
+void __init flow_cache_hp_init(void)
+{
+	int ret;
+
+	ret = cpuhp_setup_state_multi(CPUHP_NET_FLOW_PREPARE,
+				      "net/flow:prepare",
+				      flow_cache_cpu_up_prep,
+				      flow_cache_cpu_dead);
+	WARN_ON(ret < 0);
+}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index fd6986634e6f..4a8eff11bdad 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3111,6 +3111,7 @@ static struct pernet_operations __net_initdata xfrm_net_ops = {
 
 void __init xfrm_init(void)
 {
+	flow_cache_hp_init();
 	register_pernet_subsys(&xfrm_net_ops);
 	seqcount_init(&xfrm_policy_hash_generation);
 	xfrm_input_init();
-- 
2.10.2

^ permalink raw reply related

* Re: [PATCH net-next v1 02/21] amd-xgbe: Prepare for priority-based FIFO allocation
From: Tom Lendacky @ 2016-11-03 14:50 UTC (permalink / raw)
  To: Mintz, Yuval, netdev@vger.kernel.org; +Cc: Florian Fainelli, David Miller
In-Reply-To: <BL2PR07MB23065CE0C48D76AB76E78ACF8DA30@BL2PR07MB2306.namprd07.prod.outlook.com>

On 11/03/2016 08:51 AM, Mintz, Yuval wrote:
>> +static void xgbe_calculate_equal_fifo(unsigned int fifo_size,
>> +				      unsigned int queue_count,
>> +				      unsigned int *fifo)
>> +{
> ...
>> +
>> +	return;
>>  }
> 
> No need for explicit return.
> 

Ok, if there's feedback that requires a v2 I'll remove the return,
otherwise I plan to submit a checkpatch cleanup series as a follow-up
to fix some of the new warnings issued by the newer version of
checkpatch.

Thanks,
Tom

^ permalink raw reply

* Re: [PATCH net-next v2 0/5] bpf: BPF for lightweight tunnel encapsulation
From: Hannes Frederic Sowa @ 2016-11-03 14:52 UTC (permalink / raw)
  To: Thomas Graf
  Cc: David S. Miller, Alexei Starovoitov, Daniel Borkmann, Tom Herbert,
	roopa, netdev
In-Reply-To: <CACby=pnSX15XGuKBcw2FJkLz_qZt6XE+JS4ySN1Z6OZ=+w_eoQ@mail.gmail.com>

On 02.11.2016 23:54, Thomas Graf wrote:
> On 1 November 2016 at 16:12, Hannes Frederic Sowa
> <hannes@stressinduktion.org> wrote:
>> On 01.11.2016 21:59, Thomas Graf wrote:
>>>> Dumping and verifying which routes get used might actually already be
>>>> quite complex on its own. Thus my fear.
>>>
>>> We even have an API to query which route is used for a tuple. What
>>> else would you like to see?
>>
>> I am not sure here. Some ideas I had were to allow tcpdump (pf_packet)
>> sockets sniff at interfaces and also gather and dump the metadata to
>> user space (this would depend on bpf programs only doing the
>> modifications in metadata and not in the actual packet).
> 
> Not sure I understand. Why does this depend on BPF?

It doesn't. My hope was, if BPF merely tries to modify meta-data, we can
provide better debugging tools as if we mangle the packet directly.

>> Or maybe just tracing support (without depending on the eBPF program
>> developer to have added debugging in the BPF program).
> 
> Absolutely in favour of that.
> 
>>> This will be addressed with signing AFAIK.
>>
>> This sounds a bit unrealistic. Signing lots of small programs can be a
>> huge burden to the entity doing the signing (if it is not on the same
>> computer). And as far as I understood the programs should be generated
>> dynamically?
> 
> Right, for generated programs, a hash is a better fit and still sufficient.
> 
>>> Would it help if we allow to store the original source used for
>>> bytecode generation. What would make it clear which program was used.
>>
>> I would also be fine with just a strong hash of the bytecode, so the
>> program can be identified accurately. Maybe helps with deduplication
>> later on, too. ;)
> 
> OK, I think we all already agreed on doing this.
> 
>> Even though I read through the patchset I am not absolutely sure which
>> problem it really solves. Especially because lots of things can be done
>> already at the ingress vs. egress interface (I looked at patch 4 but I
>> am not sure how realistic they are).
> 
> Filtering at egress requires to attach the BPF program to all
> potential outgoing interface and then pass every single packet through
> the program whereas with LWT BPF, I'm only taking the cost where
> actually needed.

I do certainly see this point as a big plus. I definitely also thought
about this a lot when thinking about how flower can/should be used with
multiple interfaces and how to keep its flow tables synchronized.

>>> I also don't see how this could possibly scale if all packets must go
>>> through a single BPF program. The overhead will be tremendous if you
>>> only want to filter a couple of prefixes.
>>
>> In case of hash table lookup it should be fast. llvm will probably also
>> generate jump table for a few 100 ip addresses, no? Additionally the
>> routing table lookup could be not done at all.
> 
> Why would I want to accept the overhead if I simply avoid it? Just
> parsing the header and doing the hash lookup will add cost, cost for
> each packet.

That is true, but in case you are outside of the namespace, you still
have to calculate the cost of doing the FIB lookup for the BPF program
each time, too.

E.g. given the lookup cost in a hash for a netnwork namespace pointer
vs. the cost of doing a FIB lookup to get a program that does a specific
transformation sounds at least in the big O-notiation to be in a better
place. ;)

If you have to do both anyway, probably your patchset will perform
better, I agree.

Bye,
Hannes

^ permalink raw reply

* Re: [PATCH net-next v1 17/21] amd-xgbe: Add I2C support for determining SFP media types
From: Tom Lendacky @ 2016-11-03 15:01 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, Florian Fainelli, David Miller
In-Reply-To: <20161103142815.GB1768@lunn.ch>

On 11/03/2016 09:28 AM, Andrew Lunn wrote:
> On Thu, Nov 03, 2016 at 08:30:36AM -0500, Tom Lendacky wrote:
>> Add support to initialize and use the I2C controller within the hardware
>> in order to determine the SFP media type that is installed.
> 
> Hi Tom

Hi Andrew

> 
> Did you see the work Russell King did for phylink?
> 
> https://lwn.net/Articles/667055/
> 
> Rather than doing your own proprietary i2c infrastructure, use the
> standard Linux i2c infrastructure. The SFP modules are then just
> normal i2c devices on a normal i2c bus. The work Russell did can then
> access them, export them to user space, get the PHY modes correctly
> setup, etc.

There are a couple of things about this. Russel's work isn't part of
the kernel yet so I can't make use of it.  Additionally, the I2C device
is integrated into the IP of the network device with register addresses
being offsets of the network device BAR so I'm not sure how I would go
about getting it setup in order to use the i2c infrastructure.

Thanks,
Tom

> 
>        Andrew
> 

^ permalink raw reply

* RE: [mm PATCH v2 01/26] swiotlb: Drop unused functions swiotlb_map_sg and swiotlb_unmap_sg
From: Duyck, Alexander H @ 2016-11-03 15:02 UTC (permalink / raw)
  To: Christoph Hellwig, Konrad Rzeszutek Wilk
  Cc: linux-mm@kvack.org, akpm@linux-foundation.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20161103144532.GA14340@infradead.org>

> -----Original Message-----
> From: Christoph Hellwig [mailto:hch@infradead.org]
> Sent: Thursday, November 3, 2016 7:46 AM
> To: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> Cc: Christoph Hellwig <hch@infradead.org>; Duyck, Alexander H
> <alexander.h.duyck@intel.com>; linux-mm@kvack.org; akpm@linux-
> foundation.org; netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: Re: [mm PATCH v2 01/26] swiotlb: Drop unused functions
> swiotlb_map_sg and swiotlb_unmap_sg
> 
> On Thu, Nov 03, 2016 at 10:29:52AM -0400, Konrad Rzeszutek Wilk wrote:
> > Somehow I thought you wanted to put them through your tree (which is
> > why I acked them).
> >
> > I can take them and also the first couple of Alexander through my
> > tree. Or if it makes it simpler - they can go through the -mm tree?
> 
> I don't have a tree for it, so I kinda expected you to pick it up.
> But I'm also fine with you just Acking the version from Alex and having him
> funnel it through whatever tree he wants to get his patches in through.

For the first 3 patches in my series I am fine with them being pulled into the swiotlb tree.  So if you want to pull Christoph's two patches, and then drop my duplicate patch and instead pull the next 2 I could submit a v3 of my series without the swiotlb patches in it.

At this point I have redone my series so that I technically don't have anything with a hard dependency on the DMA_ATTR_SKIP_CPU_SYNC actually doing anything yet.  My plan is to get this all into Linus's tree first via whatever tree I can get these patches pulled into and once I have all that I will start updating drivers in net-next.

Thanks.

- Alex

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH net-next v2 1/1] driver: veth: Refine the statistics codes of veth driver
From: Eric Dumazet @ 2016-11-03 15:07 UTC (permalink / raw)
  To: Gao Feng
  Cc: David S. Miller, Cong Wang, Vijay Pandurangan, Evan Jones, pabeni,
	Linux Kernel Network Developers
In-Reply-To: <CA+6hz4owi2WgeGWd98=8d=Q5DBPY4_ZtYAOgcM1V_0+OU8UHrw@mail.gmail.com>

On Thu, 2016-11-03 at 22:38 +0800, Gao Feng wrote:

> Because other net devices put the statistics together.
> Take tun/tap as example, it is a virtual device, but its all
> statistics are percpu including dropped.

Take a look at 2681128f0ced8aa4e66f221197e183cc16d244fe
("veth: reduce stat overhead")

Feel free to fix tun/tap, not bloat veth and undo my work,
without knowing why it was done this way.

Thanks.

^ permalink raw reply

* Re: [PATCH net-next v2 1/1] driver: veth: Refine the statistics codes of veth driver
From: Gao Feng @ 2016-11-03 15:13 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S. Miller, Cong Wang, Vijay Pandurangan, Evan Jones, pabeni,
	Linux Kernel Network Developers
In-Reply-To: <1478185643.7065.446.camel@edumazet-glaptop3.roam.corp.google.com>

Hi Eric,

On Thu, Nov 3, 2016 at 11:07 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Thu, 2016-11-03 at 22:38 +0800, Gao Feng wrote:
>
>> Because other net devices put the statistics together.
>> Take tun/tap as example, it is a virtual device, but its all
>> statistics are percpu including dropped.
>
> Take a look at 2681128f0ced8aa4e66f221197e183cc16d244fe
> ("veth: reduce stat overhead")
>
> Feel free to fix tun/tap, not bloat veth and undo my work,
> without knowing why it was done this way.
>
> Thanks.
>
>

Thanks your detail explanations.

Best Regards
Feng

^ permalink raw reply

* Re: [PATCH net-next v1 17/21] amd-xgbe: Add I2C support for determining SFP media types
From: Andrew Lunn @ 2016-11-03 15:18 UTC (permalink / raw)
  To: Tom Lendacky; +Cc: netdev, Florian Fainelli, David Miller
In-Reply-To: <1af561af-65e2-8422-1901-d4b1c317ad12@amd.com>

> There are a couple of things about this. Russel's work isn't part of
> the kernel yet so I can't make use of it.

Well, you could guide it into the kernel. Part of it has already made
the way in. And i know of other platforms which would benefit from it.

> Additionally, the I2C device is integrated into the IP of the
> network device with register addresses being offsets of the network
> device BAR so I'm not sure how I would go about getting it setup in
> order to use the i2c infrastructure.

Have you looked at the core i2c stuff? All you need is an
i2c_algorithim structure:

http://lxr.free-electrons.com/source/include/linux/i2c.h#L407

and an i2c_adaptor structure:

http://lxr.free-electrons.com/source/include/linux/i2c.h#L532

and then you can call i2c_add_adapter() to register your i2c bus with
the i2c core. Embedded such an i2c driver inside another driver is not
a problem.

  Andrew

^ permalink raw reply

* Re: [PATCH net-next v2 0/5] bpf: BPF for lightweight tunnel encapsulation
From: Thomas Graf @ 2016-11-03 15:20 UTC (permalink / raw)
  To: Hannes Frederic Sowa
  Cc: David S. Miller, Alexei Starovoitov, Daniel Borkmann, Tom Herbert,
	roopa, netdev
In-Reply-To: <c24f3ad9-764a-0ffa-51bf-2b42fb3ae585@stressinduktion.org>

On 3 November 2016 at 08:52, Hannes Frederic Sowa
<hannes@stressinduktion.org> wrote:
> On 02.11.2016 23:54, Thomas Graf wrote:
>> Why would I want to accept the overhead if I simply avoid it? Just
>> parsing the header and doing the hash lookup will add cost, cost for
>> each packet.
>
> That is true, but in case you are outside of the namespace, you still
> have to calculate the cost of doing the FIB lookup for the BPF program
> each time, too.
>
> E.g. given the lookup cost in a hash for a netnwork namespace pointer
> vs. the cost of doing a FIB lookup to get a program that does a specific
> transformation sounds at least in the big O-notiation to be in a better
> place. ;)
>
> If you have to do both anyway, probably your patchset will perform
> better, I agree.

Most containers are unprivileged, the route inside the container's
namespace is owned by the host and we can attach the BPF program
directly to the default route inside the container and all packets
egressing from the container will pass through it. That fib lookup is
needed anyway so we can leverage the cost of that lookup. We can drop
hostile packets early without ever going on L2 level.

^ permalink raw reply

* Re: net/netlink: global-out-of-bounds in genl_family_rcv_msg/validate_nla
From: Andrey Konovalov @ 2016-11-03 15:28 UTC (permalink / raw)
  To: Cong Wang
  Cc: David S. Miller, Nicolas Dichtel, LKML, Matti Vaittinen,
	Tycho Andersen, stephen hemminger, Tom Herbert, Florian Westphal,
	netdev
In-Reply-To: <CAM_iQpXAqgW5LxAXmHbBqppM-eo1spyOJhyCpkkZ_3J7DRUUuw@mail.gmail.com>

Hi Cong,

Yes, the last patch fixes the issue.

Tested-by: Andrey Konovalov <andreyknvl@google.com>

Thanks!

On Thu, Nov 3, 2016 at 6:29 AM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
> On Wed, Nov 2, 2016 at 10:25 PM, Cong Wang <xiyou.wangcong@gmail.com> wrote:
>> On Wed, Nov 2, 2016 at 5:25 PM, Andrey Konovalov <andreyknvl@google.com> wrote:
>>> Hi,
>>>
>>> I've got the following error report while running the syzkaller fuzzer:
>>>
>>> ==================================================================
>>> BUG: KASAN: global-out-of-bounds in validate_nla+0x49b/0x4e0 at addr
>>> ffffffff8407e3ac
>>> Read of size 2 by task a.out/3877
>>> Address belongs to variable[<        none        >]
>>> cgroupstats_cmd_get_policy+0xc/0x40 ??:?
>>
>> Seems taskstats doesn't use genetlink correctly, CGROUPSTATS_CMD_ATTR_FD
>> is not within 0~TASKSTATS_CMD_ATTR_MAX.
>>
>> I guess we need the following patch, but it certainly breaks user-space... :-/
>
>
> Wait, maybe just this one-line fix is enough:
>
> diff --git a/kernel/taskstats.c b/kernel/taskstats.c
> index b3f05ee..e6b342e 100644
> --- a/kernel/taskstats.c
> +++ b/kernel/taskstats.c
> @@ -54,7 +54,7 @@ static const struct nla_policy
> taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1
>         [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING },
>         [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },};
>
> -static const struct nla_policy
> cgroupstats_cmd_get_policy[CGROUPSTATS_CMD_ATTR_MAX+1] = {
> +static const struct nla_policy
> cgroupstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = {
>         [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 },
>  };

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox