DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 07/31] net/sfc: implement device operation to change MTU
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/features/sfc_efx.ini |  2 ++
 doc/guides/nics/sfc_efx.rst          |  4 +++
 drivers/net/sfc/sfc_ethdev.c         | 67 ++++++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini
index 693d35e..a845bfc 100644
--- a/doc/guides/nics/features/sfc_efx.ini
+++ b/doc/guides/nics/features/sfc_efx.ini
@@ -6,6 +6,8 @@
 [Features]
 Link status          = Y
 Link status event    = Y
+MTU update           = Y
+Jumbo frame          = Y
 Flow control         = Y
 L3 checksum offload  = P
 L4 checksum offload  = P
diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index 94eedd1..adab9fd 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -52,6 +52,10 @@ SFC EFX PMD has support for:
 
 - Basic flow control
 
+- MTU update
+
+- Jumbo frames up to 9K
+
 
 Non-supported Features
 ----------------------
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 8c46500..6690755 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -549,6 +549,72 @@ sfc_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
 	return -rc;
 }
 
+static int
+sfc_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	size_t pdu = EFX_MAC_PDU(mtu);
+	size_t old_pdu;
+	int rc;
+
+	sfc_log_init(sa, "mtu=%u", mtu);
+
+	rc = EINVAL;
+	if (pdu < EFX_MAC_PDU_MIN) {
+		sfc_err(sa, "too small MTU %u (PDU size %u less than min %u)",
+			(unsigned int)mtu, (unsigned int)pdu,
+			EFX_MAC_PDU_MIN);
+		goto fail_inval;
+	}
+	if (pdu > EFX_MAC_PDU_MAX) {
+		sfc_err(sa, "too big MTU %u (PDU size %u greater than max %u)",
+			(unsigned int)mtu, (unsigned int)pdu,
+			EFX_MAC_PDU_MAX);
+		goto fail_inval;
+	}
+
+	sfc_adapter_lock(sa);
+
+	if (pdu != sa->port.pdu) {
+		if (sa->state == SFC_ADAPTER_STARTED) {
+			sfc_stop(sa);
+
+			old_pdu = sa->port.pdu;
+			sa->port.pdu = pdu;
+			rc = sfc_start(sa);
+			if (rc != 0)
+				goto fail_start;
+		} else {
+			sa->port.pdu = pdu;
+		}
+	}
+
+	/*
+	 * The driver does not use it, but other PMDs update jumbo_frame
+	 * flag and max_rx_pkt_len when MTU is set.
+	 */
+	dev->data->dev_conf.rxmode.jumbo_frame = (mtu > ETHER_MAX_LEN);
+	dev->data->dev_conf.rxmode.max_rx_pkt_len = sa->port.pdu;
+
+	sfc_adapter_unlock(sa);
+
+	sfc_log_init(sa, "done");
+	return 0;
+
+fail_start:
+	sa->port.pdu = old_pdu;
+	if (sfc_start(sa) != 0)
+		sfc_err(sa, "cannot start with neither new (%u) nor old (%u) "
+			"PDU max size - port is stopped",
+			(unsigned int)pdu, (unsigned int)old_pdu);
+	sfc_adapter_unlock(sa);
+
+fail_inval:
+	sfc_log_init(sa, "failed %d", rc);
+	SFC_ASSERT(rc > 0);
+	return -rc;
+}
+
 static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_configure			= sfc_dev_configure,
 	.dev_start			= sfc_dev_start,
@@ -559,6 +625,7 @@ static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.xstats_get			= sfc_xstats_get,
 	.xstats_get_names		= sfc_xstats_get_names,
 	.dev_infos_get			= sfc_dev_infos_get,
+	.mtu_set			= sfc_dev_set_mtu,
 	.rx_queue_setup			= sfc_rx_queue_setup,
 	.rx_queue_release		= sfc_rx_queue_release,
 	.tx_queue_setup			= sfc_tx_queue_setup,
-- 
2.5.5

^ permalink raw reply related

* [PATCH 04/31] net/sfc: support extended statistics
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/features/sfc_efx.ini |  1 +
 drivers/net/sfc/efsys.h              |  2 +-
 drivers/net/sfc/sfc_ethdev.c         | 63 ++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini
index f55a988..698553c 100644
--- a/doc/guides/nics/features/sfc_efx.ini
+++ b/doc/guides/nics/features/sfc_efx.ini
@@ -8,6 +8,7 @@ Link status          = Y
 L3 checksum offload  = P
 L4 checksum offload  = P
 Basic stats          = Y
+Extended stats       = Y
 BSD nic_uio          = Y
 Linux UIO            = Y
 Linux VFIO           = Y
diff --git a/drivers/net/sfc/efsys.h b/drivers/net/sfc/efsys.h
index fe8615f..0f941e6 100644
--- a/drivers/net/sfc/efsys.h
+++ b/drivers/net/sfc/efsys.h
@@ -159,7 +159,7 @@ prefetch_read_once(const volatile void *addr)
 /* Code inclusion options */
 
 
-#define EFSYS_OPT_NAMES 0
+#define EFSYS_OPT_NAMES 1
 
 /* Disable SFN5xxx/SFN6xxx since it requires specific support in the PMD */
 #define EFSYS_OPT_SIENA 0
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index f31330c..d5ae1a0 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -392,6 +392,67 @@ sfc_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 	rte_spinlock_unlock(&port->mac_stats_lock);
 }
 
+static int
+sfc_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats,
+	       unsigned int xstats_count)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	struct sfc_port *port = &sa->port;
+	uint64_t *mac_stats;
+	int rc;
+	unsigned int i;
+	int nstats = 0;
+
+	rte_spinlock_lock(&port->mac_stats_lock);
+
+	rc = sfc_port_update_mac_stats(sa);
+	if (rc != 0) {
+		SFC_ASSERT(rc > 0);
+		nstats = -rc;
+		goto unlock;
+	}
+
+	mac_stats = port->mac_stats_buf;
+
+	for (i = 0; i < EFX_MAC_NSTATS; ++i) {
+		if (EFX_MAC_STAT_SUPPORTED(port->mac_stats_mask, i)) {
+			if (xstats != NULL && nstats < (int)xstats_count) {
+				xstats[nstats].id = nstats;
+				xstats[nstats].value = mac_stats[i];
+			}
+			nstats++;
+		}
+	}
+
+unlock:
+	rte_spinlock_unlock(&port->mac_stats_lock);
+
+	return nstats;
+}
+
+static int
+sfc_xstats_get_names(struct rte_eth_dev *dev,
+		     struct rte_eth_xstat_name *xstats_names,
+		     unsigned int xstats_count)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	struct sfc_port *port = &sa->port;
+	unsigned int i;
+	unsigned int nstats = 0;
+
+	for (i = 0; i < EFX_MAC_NSTATS; ++i) {
+		if (EFX_MAC_STAT_SUPPORTED(port->mac_stats_mask, i)) {
+			if (xstats_names != NULL && nstats < xstats_count)
+				strncpy(xstats_names[nstats].name,
+					efx_mac_stat_name(sa->nic, i),
+					sizeof(xstats_names[0].name));
+			nstats++;
+		}
+	}
+
+	return nstats;
+}
+
 static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_configure			= sfc_dev_configure,
 	.dev_start			= sfc_dev_start,
@@ -399,6 +460,8 @@ static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_close			= sfc_dev_close,
 	.link_update			= sfc_dev_link_update,
 	.stats_get			= sfc_stats_get,
+	.xstats_get			= sfc_xstats_get,
+	.xstats_get_names		= sfc_xstats_get_names,
 	.dev_infos_get			= sfc_dev_infos_get,
 	.rx_queue_setup			= sfc_rx_queue_setup,
 	.rx_queue_release		= sfc_rx_queue_release,
-- 
2.5.5

^ permalink raw reply related

* [PATCH 08/31] net/sfc: support link speed and duplex settings
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/features/sfc_efx.ini |  1 +
 drivers/net/sfc/sfc.c                | 38 ++++++++++++++++++++++++++++++++++--
 drivers/net/sfc/sfc.h                |  3 +++
 drivers/net/sfc/sfc_ethdev.c         |  9 +++++++++
 drivers/net/sfc/sfc_port.c           |  6 ++++++
 5 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini
index a845bfc..60ecca0 100644
--- a/doc/guides/nics/features/sfc_efx.ini
+++ b/doc/guides/nics/features/sfc_efx.ini
@@ -4,6 +4,7 @@
 ; Refer to default.ini for the full list of available PMD features.
 ;
 [Features]
+Speed capabilities   = Y
 Link status          = Y
 Link status event    = Y
 MTU update           = Y
diff --git a/drivers/net/sfc/sfc.c b/drivers/net/sfc/sfc.c
index 36044a0..e2e6c9e 100644
--- a/drivers/net/sfc/sfc.c
+++ b/drivers/net/sfc/sfc.c
@@ -85,6 +85,33 @@ sfc_dma_free(const struct sfc_adapter *sa, efsys_mem_t *esmp)
 	memset(esmp, 0, sizeof(*esmp));
 }
 
+static uint32_t
+sfc_phy_cap_from_link_speeds(uint32_t speeds)
+{
+	uint32_t phy_caps = 0;
+
+	if (~speeds & ETH_LINK_SPEED_FIXED) {
+		phy_caps |= (1 << EFX_PHY_CAP_AN);
+		/*
+		 * If no speeds are specified in the mask, any supported
+		 * may be negotiated
+		 */
+		if (speeds == ETH_LINK_SPEED_AUTONEG)
+			phy_caps |=
+				(1 << EFX_PHY_CAP_1000FDX) |
+				(1 << EFX_PHY_CAP_10000FDX) |
+				(1 << EFX_PHY_CAP_40000FDX);
+	}
+	if (speeds & ETH_LINK_SPEED_1G)
+		phy_caps |= (1 << EFX_PHY_CAP_1000FDX);
+	if (speeds & ETH_LINK_SPEED_10G)
+		phy_caps |= (1 << EFX_PHY_CAP_10000FDX);
+	if (speeds & ETH_LINK_SPEED_40G)
+		phy_caps |= (1 << EFX_PHY_CAP_40000FDX);
+
+	return phy_caps;
+}
+
 /*
  * Check requested device level configuration.
  * Receive and transmit configuration is checked in corresponding
@@ -96,8 +123,12 @@ sfc_check_conf(struct sfc_adapter *sa)
 	const struct rte_eth_conf *conf = &sa->eth_dev->data->dev_conf;
 	int rc = 0;
 
-	if (conf->link_speeds != ETH_LINK_SPEED_AUTONEG) {
-		sfc_err(sa, "Manual link speed/duplex choice not supported");
+	sa->port.phy_adv_cap =
+		sfc_phy_cap_from_link_speeds(conf->link_speeds) &
+		sa->port.phy_adv_cap_mask;
+	if ((sa->port.phy_adv_cap & ~(1 << EFX_PHY_CAP_AN)) == 0) {
+		sfc_err(sa, "No link speeds from mask %#x are supported",
+			conf->link_speeds);
 		rc = EINVAL;
 	}
 
@@ -516,6 +547,9 @@ sfc_attach(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_intr_attach;
 
+	efx_phy_adv_cap_get(sa->nic, EFX_PHY_CAP_PERM,
+			    &sa->port.phy_adv_cap_mask);
+
 	sfc_log_init(sa, "fini nic");
 	efx_nic_fini(enp);
 
diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 257622f..5883547 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -123,6 +123,9 @@ struct sfc_txq_info;
 struct sfc_port {
 	unsigned int			lsc_seq;
 
+	uint32_t			phy_adv_cap_mask;
+	uint32_t			phy_adv_cap;
+
 	unsigned int			flow_ctrl;
 	boolean_t			flow_ctrl_autoneg;
 	size_t				pdu;
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 6690755..42c488e 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -53,6 +53,15 @@ sfc_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
 
 	dev_info->max_rx_pktlen = EFX_MAC_PDU_MAX;
 
+	/* Autonegotiation may be disabled */
+	dev_info->speed_capa = ETH_LINK_SPEED_FIXED;
+	if (sa->port.phy_adv_cap_mask & EFX_PHY_CAP_1000FDX)
+		dev_info->speed_capa |= ETH_LINK_SPEED_1G;
+	if (sa->port.phy_adv_cap_mask & EFX_PHY_CAP_10000FDX)
+		dev_info->speed_capa |= ETH_LINK_SPEED_10G;
+	if (sa->port.phy_adv_cap_mask & EFX_PHY_CAP_40000FDX)
+		dev_info->speed_capa |= ETH_LINK_SPEED_40G;
+
 	dev_info->max_rx_queues = sa->rxq_max;
 	dev_info->max_tx_queues = sa->txq_max;
 
diff --git a/drivers/net/sfc/sfc_port.c b/drivers/net/sfc/sfc_port.c
index ccc0854..1241af7 100644
--- a/drivers/net/sfc/sfc_port.c
+++ b/drivers/net/sfc/sfc_port.c
@@ -86,6 +86,11 @@ sfc_port_start(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_mac_fcntl_set;
 
+	sfc_log_init(sa, "set phy adv caps to %#x", port->phy_adv_cap);
+	rc = efx_phy_adv_cap_set(sa->nic, port->phy_adv_cap);
+	if (rc != 0)
+		goto fail_phy_adv_cap_set;
+
 	sfc_log_init(sa, "set MAC PDU %u", (unsigned int)port->pdu);
 	rc = efx_mac_pdu_set(sa->nic, port->pdu);
 	if (rc != 0)
@@ -131,6 +136,7 @@ sfc_port_start(struct sfc_adapter *sa)
 fail_mac_filter_set:
 fail_mac_addr_set:
 fail_mac_pdu_set:
+fail_phy_adv_cap_set:
 fail_mac_fcntl_set:
 	efx_port_fini(sa->nic);
 
-- 
2.5.5

^ permalink raw reply related

* [PATCH 03/31] net/sfc: implement ethdev hook to get basic statistics
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Does not implement any deprecated statistics.
No per-queue statistics yet.

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/features/sfc_efx.ini |  1 +
 doc/guides/nics/sfc_efx.rst          |  2 +
 drivers/net/sfc/efsys.h              |  2 +-
 drivers/net/sfc/sfc.h                |  7 ++++
 drivers/net/sfc/sfc_ethdev.c         | 69 +++++++++++++++++++++++++++++++++
 drivers/net/sfc/sfc_port.c           | 75 ++++++++++++++++++++++++++++++++++++
 6 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini
index 67df1c6..f55a988 100644
--- a/doc/guides/nics/features/sfc_efx.ini
+++ b/doc/guides/nics/features/sfc_efx.ini
@@ -7,6 +7,7 @@
 Link status          = Y
 L3 checksum offload  = P
 L4 checksum offload  = P
+Basic stats          = Y
 BSD nic_uio          = Y
 Linux UIO            = Y
 Linux VFIO           = Y
diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index 36d0974..cbb51de 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -48,6 +48,8 @@ SFC EFX PMD has support for:
 
 - IPv4/IPv6 TCP/UDP transmit checksum offload
 
+- Port hardware statistics
+
 
 Non-supported Features
 ----------------------
diff --git a/drivers/net/sfc/efsys.h b/drivers/net/sfc/efsys.h
index d48eb4c..fe8615f 100644
--- a/drivers/net/sfc/efsys.h
+++ b/drivers/net/sfc/efsys.h
@@ -178,7 +178,7 @@ prefetch_read_once(const volatile void *addr)
 #define EFSYS_OPT_MCDI_LOGGING 1
 #define EFSYS_OPT_MCDI_PROXY_AUTH 0
 
-#define EFSYS_OPT_MAC_STATS 0
+#define EFSYS_OPT_MAC_STATS 1
 
 #define EFSYS_OPT_LOOPBACK 0
 
diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index d0aafa3..1189283 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -122,6 +122,12 @@ struct sfc_port {
 	unsigned int			flow_ctrl;
 	boolean_t			flow_ctrl_autoneg;
 	size_t				pdu;
+
+	rte_spinlock_t			mac_stats_lock;
+	uint64_t			*mac_stats_buf;
+	efsys_mem_t			mac_stats_dma_mem;
+
+	uint32_t		mac_stats_mask[EFX_MAC_STATS_MASK_NPAGES];
 };
 
 /* Adapter private data */
@@ -229,6 +235,7 @@ int sfc_port_start(struct sfc_adapter *sa);
 void sfc_port_stop(struct sfc_adapter *sa);
 void sfc_port_link_mode_to_info(efx_link_mode_t link_mode,
 				struct rte_eth_link *link_info);
+int sfc_port_update_mac_stats(struct sfc_adapter *sa);
 
 
 #ifdef __cplusplus
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 1df227e..f31330c 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -324,12 +324,81 @@ sfc_tx_queue_release(void *queue)
 	sfc_adapter_unlock(sa);
 }
 
+static void
+sfc_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	struct sfc_port *port = &sa->port;
+	uint64_t *mac_stats;
+
+	rte_spinlock_lock(&port->mac_stats_lock);
+
+	if (sfc_port_update_mac_stats(sa) != 0)
+		goto unlock;
+
+	mac_stats = port->mac_stats_buf;
+
+	if (EFX_MAC_STAT_SUPPORTED(port->mac_stats_mask,
+				   EFX_MAC_VADAPTER_RX_UNICAST_PACKETS)) {
+		stats->ipackets =
+			mac_stats[EFX_MAC_VADAPTER_RX_UNICAST_PACKETS] +
+			mac_stats[EFX_MAC_VADAPTER_RX_MULTICAST_PACKETS] +
+			mac_stats[EFX_MAC_VADAPTER_RX_BROADCAST_PACKETS];
+		stats->opackets =
+			mac_stats[EFX_MAC_VADAPTER_TX_UNICAST_PACKETS] +
+			mac_stats[EFX_MAC_VADAPTER_TX_MULTICAST_PACKETS] +
+			mac_stats[EFX_MAC_VADAPTER_TX_BROADCAST_PACKETS];
+		stats->ibytes =
+			mac_stats[EFX_MAC_VADAPTER_RX_UNICAST_BYTES] +
+			mac_stats[EFX_MAC_VADAPTER_RX_MULTICAST_BYTES] +
+			mac_stats[EFX_MAC_VADAPTER_RX_BROADCAST_BYTES];
+		stats->obytes =
+			mac_stats[EFX_MAC_VADAPTER_TX_UNICAST_BYTES] +
+			mac_stats[EFX_MAC_VADAPTER_TX_MULTICAST_BYTES] +
+			mac_stats[EFX_MAC_VADAPTER_TX_BROADCAST_BYTES];
+		stats->imissed = mac_stats[EFX_MAC_VADAPTER_RX_OVERFLOW];
+		stats->ierrors = mac_stats[EFX_MAC_VADAPTER_RX_BAD_PACKETS];
+		stats->oerrors = mac_stats[EFX_MAC_VADAPTER_TX_BAD_PACKETS];
+	} else {
+		stats->ipackets = mac_stats[EFX_MAC_RX_PKTS];
+		stats->opackets = mac_stats[EFX_MAC_TX_PKTS];
+		stats->ibytes = mac_stats[EFX_MAC_RX_OCTETS];
+		stats->obytes = mac_stats[EFX_MAC_TX_OCTETS];
+		/*
+		 * Take into account stats which are whenever supported
+		 * on EF10. If some stat is not supported by current
+		 * firmware variant or HW revision, it is guaranteed
+		 * to be zero in mac_stats.
+		 */
+		stats->imissed =
+			mac_stats[EFX_MAC_RX_NODESC_DROP_CNT] +
+			mac_stats[EFX_MAC_PM_TRUNC_BB_OVERFLOW] +
+			mac_stats[EFX_MAC_PM_DISCARD_BB_OVERFLOW] +
+			mac_stats[EFX_MAC_PM_TRUNC_VFIFO_FULL] +
+			mac_stats[EFX_MAC_PM_DISCARD_VFIFO_FULL] +
+			mac_stats[EFX_MAC_PM_TRUNC_QBB] +
+			mac_stats[EFX_MAC_PM_DISCARD_QBB] +
+			mac_stats[EFX_MAC_PM_DISCARD_MAPPING] +
+			mac_stats[EFX_MAC_RXDP_Q_DISABLED_PKTS] +
+			mac_stats[EFX_MAC_RXDP_DI_DROPPED_PKTS];
+		stats->ierrors =
+			mac_stats[EFX_MAC_RX_FCS_ERRORS] +
+			mac_stats[EFX_MAC_RX_ALIGN_ERRORS] +
+			mac_stats[EFX_MAC_RX_JABBER_PKTS];
+		/* no oerrors counters supported on EF10 */
+	}
+
+unlock:
+	rte_spinlock_unlock(&port->mac_stats_lock);
+}
+
 static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_configure			= sfc_dev_configure,
 	.dev_start			= sfc_dev_start,
 	.dev_stop			= sfc_dev_stop,
 	.dev_close			= sfc_dev_close,
 	.link_update			= sfc_dev_link_update,
+	.stats_get			= sfc_stats_get,
 	.dev_infos_get			= sfc_dev_infos_get,
 	.rx_queue_setup			= sfc_rx_queue_setup,
 	.rx_queue_release		= sfc_rx_queue_release,
diff --git a/drivers/net/sfc/sfc_port.c b/drivers/net/sfc/sfc_port.c
index c124181..d8ff097 100644
--- a/drivers/net/sfc/sfc_port.c
+++ b/drivers/net/sfc/sfc_port.c
@@ -32,6 +32,34 @@
 #include "sfc.h"
 #include "sfc_log.h"
 
+/**
+ * Update MAC statistics in the buffer.
+ *
+ * @param	sa	Adapter
+ *
+ * @return Status code
+ * @retval	0	Success
+ * @retval	EAGAIN	Try again
+ * @retval	ENOMEM	Memory allocation failure
+ */
+int
+sfc_port_update_mac_stats(struct sfc_adapter *sa)
+{
+	struct sfc_port *port = &sa->port;
+	int rc;
+
+	SFC_ASSERT(rte_spinlock_is_locked(&port->mac_stats_lock));
+
+	if (sa->state != SFC_ADAPTER_STARTED)
+		return EINVAL;
+
+	rc = efx_mac_stats_update(sa->nic, &port->mac_stats_dma_mem,
+				  port->mac_stats_buf, NULL);
+	if (rc != 0)
+		return rc;
+
+	return 0;
+}
 
 int
 sfc_port_start(struct sfc_adapter *sa)
@@ -67,6 +95,19 @@ sfc_port_start(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_mac_filter_set;
 
+	efx_mac_stats_get_mask(sa->nic, port->mac_stats_mask,
+			       sizeof(port->mac_stats_mask));
+
+	/* Update MAC stats using periodic DMA.
+	 * Common code always uses 1000ms update period, so period_ms
+	 * parameter only needs to be non-zero to start updates.
+	 */
+	sfc_log_init(sa, "request MAC stats DMA'ing");
+	rc = efx_mac_stats_periodic(sa->nic, &port->mac_stats_dma_mem,
+				    1000, B_FALSE);
+	if (rc != 0)
+		goto fail_mac_stats_periodic;
+
 	sfc_log_init(sa, "disable MAC drain");
 	rc = efx_mac_drain(sa->nic, B_FALSE);
 	if (rc != 0)
@@ -76,6 +117,10 @@ sfc_port_start(struct sfc_adapter *sa)
 	return 0;
 
 fail_mac_drain:
+	(void)efx_mac_stats_periodic(sa->nic, &port->mac_stats_dma_mem,
+				     0, B_FALSE);
+
+fail_mac_stats_periodic:
 fail_mac_filter_set:
 fail_mac_addr_set:
 fail_mac_pdu_set:
@@ -95,6 +140,10 @@ sfc_port_stop(struct sfc_adapter *sa)
 	sfc_log_init(sa, "entry");
 
 	efx_mac_drain(sa->nic, B_TRUE);
+
+	(void)efx_mac_stats_periodic(sa->nic, &sa->port.mac_stats_dma_mem,
+				     0, B_FALSE);
+
 	efx_port_fini(sa->nic);
 	efx_filter_fini(sa->nic);
 
@@ -106,6 +155,7 @@ sfc_port_init(struct sfc_adapter *sa)
 {
 	const struct rte_eth_dev_data *dev_data = sa->eth_dev->data;
 	struct sfc_port *port = &sa->port;
+	int rc;
 
 	sfc_log_init(sa, "entry");
 
@@ -118,15 +168,40 @@ sfc_port_init(struct sfc_adapter *sa)
 	else
 		port->pdu = EFX_MAC_PDU(dev_data->mtu);
 
+	rte_spinlock_init(&port->mac_stats_lock);
+
+	rc = ENOMEM;
+	port->mac_stats_buf = rte_calloc_socket("mac_stats_buf", EFX_MAC_NSTATS,
+						sizeof(uint64_t), 0,
+						sa->socket_id);
+	if (port->mac_stats_buf == NULL)
+		goto fail_mac_stats_buf_alloc;
+
+	rc = sfc_dma_alloc(sa, "mac_stats", 0, EFX_MAC_STATS_SIZE,
+			   sa->socket_id, &port->mac_stats_dma_mem);
+	if (rc != 0)
+		goto fail_mac_stats_dma_alloc;
+
 	sfc_log_init(sa, "done");
 	return 0;
+
+fail_mac_stats_dma_alloc:
+	rte_free(port->mac_stats_buf);
+fail_mac_stats_buf_alloc:
+	sfc_log_init(sa, "failed %d", rc);
+	return rc;
 }
 
 void
 sfc_port_fini(struct sfc_adapter *sa)
 {
+	struct sfc_port *port = &sa->port;
+
 	sfc_log_init(sa, "entry");
 
+	sfc_dma_free(sa, &port->mac_stats_dma_mem);
+	rte_free(port->mac_stats_buf);
+
 	sfc_log_init(sa, "done");
 }
 
-- 
2.5.5

^ permalink raw reply related

* [PATCH 05/31] net/sfc: support flow control settings get/set
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/features/sfc_efx.ini |  1 +
 doc/guides/nics/sfc_efx.rst          |  2 +
 drivers/net/sfc/sfc_ethdev.c         | 98 ++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/sfc_port.c           |  8 +++
 4 files changed, 109 insertions(+)

diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini
index 698553c..25472f8 100644
--- a/doc/guides/nics/features/sfc_efx.ini
+++ b/doc/guides/nics/features/sfc_efx.ini
@@ -5,6 +5,7 @@
 ;
 [Features]
 Link status          = Y
+Flow control         = Y
 L3 checksum offload  = P
 L4 checksum offload  = P
 Basic stats          = Y
diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index cbb51de..1cfed6a 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -50,6 +50,8 @@ SFC EFX PMD has support for:
 
 - Port hardware statistics
 
+- Basic flow control
+
 
 Non-supported Features
 ----------------------
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index d5ae1a0..eff648b 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -453,6 +453,102 @@ sfc_xstats_get_names(struct rte_eth_dev *dev,
 	return nstats;
 }
 
+static int
+sfc_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	unsigned int wanted_fc, link_fc;
+
+	memset(fc_conf, 0, sizeof(*fc_conf));
+
+	sfc_adapter_lock(sa);
+
+	if (sa->state == SFC_ADAPTER_STARTED)
+		efx_mac_fcntl_get(sa->nic, &wanted_fc, &link_fc);
+	else
+		link_fc = sa->port.flow_ctrl;
+
+	switch (link_fc) {
+	case 0:
+		fc_conf->mode = RTE_FC_NONE;
+		break;
+	case EFX_FCNTL_RESPOND:
+		fc_conf->mode = RTE_FC_RX_PAUSE;
+		break;
+	case EFX_FCNTL_GENERATE:
+		fc_conf->mode = RTE_FC_TX_PAUSE;
+		break;
+	case (EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE):
+		fc_conf->mode = RTE_FC_FULL;
+		break;
+	default:
+		sfc_err(sa, "%s: unexpected flow control value %#x",
+			__func__, link_fc);
+	}
+
+	fc_conf->autoneg = sa->port.flow_ctrl_autoneg;
+
+	sfc_adapter_unlock(sa);
+
+	return 0;
+}
+
+static int
+sfc_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
+{
+	struct sfc_adapter *sa = dev->data->dev_private;
+	struct sfc_port *port = &sa->port;
+	unsigned int fcntl;
+	int rc;
+
+	if (fc_conf->high_water != 0 || fc_conf->low_water != 0 ||
+	    fc_conf->pause_time != 0 || fc_conf->send_xon != 0 ||
+	    fc_conf->mac_ctrl_frame_fwd != 0) {
+		sfc_err(sa, "unsupported flow control settings specified");
+		rc = EINVAL;
+		goto fail_inval;
+	}
+
+	switch (fc_conf->mode) {
+	case RTE_FC_NONE:
+		fcntl = 0;
+		break;
+	case RTE_FC_RX_PAUSE:
+		fcntl = EFX_FCNTL_RESPOND;
+		break;
+	case RTE_FC_TX_PAUSE:
+		fcntl = EFX_FCNTL_GENERATE;
+		break;
+	case RTE_FC_FULL:
+		fcntl = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
+		break;
+	default:
+		rc = EINVAL;
+		goto fail_inval;
+	}
+
+	sfc_adapter_lock(sa);
+
+	if (sa->state == SFC_ADAPTER_STARTED) {
+		rc = efx_mac_fcntl_set(sa->nic, fcntl, fc_conf->autoneg);
+		if (rc != 0)
+			goto fail_mac_fcntl_set;
+	}
+
+	port->flow_ctrl = fcntl;
+	port->flow_ctrl_autoneg = fc_conf->autoneg;
+
+	sfc_adapter_unlock(sa);
+
+	return 0;
+
+fail_mac_fcntl_set:
+	sfc_adapter_unlock(sa);
+fail_inval:
+	SFC_ASSERT(rc > 0);
+	return -rc;
+}
+
 static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.dev_configure			= sfc_dev_configure,
 	.dev_start			= sfc_dev_start,
@@ -467,6 +563,8 @@ static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.rx_queue_release		= sfc_rx_queue_release,
 	.tx_queue_setup			= sfc_tx_queue_setup,
 	.tx_queue_release		= sfc_tx_queue_release,
+	.flow_ctrl_get			= sfc_flow_ctrl_get,
+	.flow_ctrl_set			= sfc_flow_ctrl_set,
 };
 
 static int
diff --git a/drivers/net/sfc/sfc_port.c b/drivers/net/sfc/sfc_port.c
index d8ff097..ccc0854 100644
--- a/drivers/net/sfc/sfc_port.c
+++ b/drivers/net/sfc/sfc_port.c
@@ -79,6 +79,13 @@ sfc_port_start(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_port_init;
 
+	sfc_log_init(sa, "set flow control to %#x autoneg=%u",
+		     port->flow_ctrl, port->flow_ctrl_autoneg);
+	rc = efx_mac_fcntl_set(sa->nic, port->flow_ctrl,
+			       port->flow_ctrl_autoneg);
+	if (rc != 0)
+		goto fail_mac_fcntl_set;
+
 	sfc_log_init(sa, "set MAC PDU %u", (unsigned int)port->pdu);
 	rc = efx_mac_pdu_set(sa->nic, port->pdu);
 	if (rc != 0)
@@ -124,6 +131,7 @@ sfc_port_start(struct sfc_adapter *sa)
 fail_mac_filter_set:
 fail_mac_addr_set:
 fail_mac_pdu_set:
+fail_mac_fcntl_set:
 	efx_port_fini(sa->nic);
 
 fail_port_init:
-- 
2.5.5

^ permalink raw reply related

* [PATCH 02/31] net/sfc: support parameter to choose performance profile
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Supported options are auto (based on NIC firmware variant and
installed licences), throughput, low-latency.

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/sfc_efx.rst  |  7 +++++++
 drivers/net/sfc/sfc.h        |  1 +
 drivers/net/sfc/sfc_ethdev.c |  1 +
 drivers/net/sfc/sfc_ev.c     | 36 +++++++++++++++++++++++++++++++++---
 drivers/net/sfc/sfc_ev.h     |  2 ++
 drivers/net/sfc/sfc_kvargs.c |  1 +
 drivers/net/sfc/sfc_kvargs.h | 10 ++++++++++
 7 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index 2cca287..36d0974 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -152,6 +152,13 @@ whitelist option like "-w 02:00.0,arg1=value1,...".
 Case-insensitive 1/y/yes/on or 0/n/no/off may be used to specify
 boolean parameters value.
 
+- ``perf_profile`` [auto|throughput|low-latency] (default **throughput**)
+
+  Choose hardware tunning to be optimized for either throughput or
+  low-latency.
+  **auto** allows NIC firmware to make a choice based on
+  installed licences and firmware variant configured using **sfboot**.
+
 - ``debug_init`` [bool] (default **n**)
 
   Enable extra logging during device intialization and startup.
diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 0064fcb..d0aafa3 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -152,6 +152,7 @@ struct sfc_adapter {
 
 	unsigned int			txq_max_entries;
 
+	uint32_t			evq_flags;
 	unsigned int			evq_count;
 	struct sfc_evq_info		*evq_info;
 
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index 12309ee..1df227e 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -464,5 +464,6 @@ static struct eth_driver sfc_efx_pmd = {
 RTE_PMD_REGISTER_PCI(net_sfc_efx, sfc_efx_pmd.pci_drv);
 RTE_PMD_REGISTER_PCI_TABLE(net_sfc_efx, pci_id_sfc_efx_map);
 RTE_PMD_REGISTER_PARAM_STRING(net_sfc_efx,
+	SFC_KVARG_PERF_PROFILE "=" SFC_KVARG_VALUES_PERF_PROFILE " "
 	SFC_KVARG_MCDI_LOGGING "=" SFC_KVARG_VALUES_BOOL " "
 	SFC_KVARG_DEBUG_INIT "=" SFC_KVARG_VALUES_BOOL);
diff --git a/drivers/net/sfc/sfc_ev.c b/drivers/net/sfc/sfc_ev.c
index 96b95cc..34c1127 100644
--- a/drivers/net/sfc/sfc_ev.c
+++ b/drivers/net/sfc/sfc_ev.c
@@ -39,6 +39,7 @@
 #include "sfc_ev.h"
 #include "sfc_rx.h"
 #include "sfc_tx.h"
+#include "sfc_kvargs.h"
 
 
 /* Initial delay when waiting for event queue init complete event */
@@ -365,9 +366,7 @@ sfc_ev_qstart(struct sfc_adapter *sa, unsigned int sw_index)
 
 	/* Create the common code event queue */
 	rc = efx_ev_qcreate(sa->nic, sw_index, esmp, evq_info->entries,
-			    0 /* unused on EF10 */, 0,
-			    EFX_EVQ_FLAGS_TYPE_THROUGHPUT |
-			    EFX_EVQ_FLAGS_NOTIFY_DISABLED,
+			    0 /* unused on EF10 */, 0, evq_info->flags,
 			    &evq->common);
 	if (rc != 0)
 		goto fail_ev_qcreate;
@@ -600,6 +599,25 @@ sfc_ev_qinit_info(struct sfc_adapter *sa, unsigned int sw_index)
 	SFC_ASSERT(rte_is_power_of_2(max_entries));
 
 	evq_info->max_entries = max_entries;
+	evq_info->flags = sa->evq_flags | EFX_EVQ_FLAGS_NOTIFY_DISABLED;
+
+	return 0;
+}
+
+static int
+sfc_kvarg_perf_profile_handler(__rte_unused const char *key,
+			       const char *value_str, void *opaque)
+{
+	uint64_t *value = opaque;
+
+	if (strcasecmp(value_str, SFC_KVARG_PERF_PROFILE_THROUGHPUT) == 0)
+		*value = EFX_EVQ_FLAGS_TYPE_THROUGHPUT;
+	else if (strcasecmp(value_str, SFC_KVARG_PERF_PROFILE_LOW_LATENCY) == 0)
+		*value = EFX_EVQ_FLAGS_TYPE_LOW_LATENCY;
+	else if (strcasecmp(value_str, SFC_KVARG_PERF_PROFILE_AUTO) == 0)
+		*value = EFX_EVQ_FLAGS_TYPE_AUTO;
+	else
+		return -EINVAL;
 
 	return 0;
 }
@@ -620,6 +638,16 @@ sfc_ev_init(struct sfc_adapter *sa)
 
 	sfc_log_init(sa, "entry");
 
+	sa->evq_flags = EFX_EVQ_FLAGS_TYPE_THROUGHPUT;
+	rc = sfc_kvargs_process(sa, SFC_KVARG_PERF_PROFILE,
+				sfc_kvarg_perf_profile_handler,
+				&sa->evq_flags);
+	if (rc != 0) {
+		sfc_err(sa, "invalid %s parameter value",
+			SFC_KVARG_PERF_PROFILE);
+		goto fail_kvarg_perf_profile;
+	}
+
 	sa->evq_count = sfc_ev_qcount(sa);
 	sa->mgmt_evq_index = 0;
 	rte_spinlock_init(&sa->mgmt_evq_lock);
@@ -660,6 +688,8 @@ sfc_ev_init(struct sfc_adapter *sa)
 
 fail_evqs_alloc:
 	sa->evq_count = 0;
+
+fail_kvarg_perf_profile:
 	sfc_log_init(sa, "failed %d", rc);
 	return rc;
 }
diff --git a/drivers/net/sfc/sfc_ev.h b/drivers/net/sfc/sfc_ev.h
index 110f3b6..346e3ec 100644
--- a/drivers/net/sfc/sfc_ev.h
+++ b/drivers/net/sfc/sfc_ev.h
@@ -74,6 +74,8 @@ struct sfc_evq_info {
 	unsigned int		max_entries;
 	/* Real number of EVQ entries, less or equal to max_entries */
 	unsigned int		entries;
+	/* Event queue creation flags */
+	uint32_t		flags;
 	/* NUMA-aware EVQ data structure used on datapath */
 	struct sfc_evq		*evq;
 };
diff --git a/drivers/net/sfc/sfc_kvargs.c b/drivers/net/sfc/sfc_kvargs.c
index bbbd026..2ced47c 100644
--- a/drivers/net/sfc/sfc_kvargs.c
+++ b/drivers/net/sfc/sfc_kvargs.c
@@ -43,6 +43,7 @@ sfc_kvargs_parse(struct sfc_adapter *sa)
 	const char **params = (const char *[]){
 		SFC_KVARG_DEBUG_INIT,
 		SFC_KVARG_MCDI_LOGGING,
+		SFC_KVARG_PERF_PROFILE,
 		NULL,
 	};
 
diff --git a/drivers/net/sfc/sfc_kvargs.h b/drivers/net/sfc/sfc_kvargs.h
index ffce851..2fea9c7 100644
--- a/drivers/net/sfc/sfc_kvargs.h
+++ b/drivers/net/sfc/sfc_kvargs.h
@@ -42,6 +42,16 @@ extern "C" {
 
 #define SFC_KVARG_MCDI_LOGGING		"mcdi_logging"
 
+#define SFC_KVARG_PERF_PROFILE		"perf_profile"
+
+#define SFC_KVARG_PERF_PROFILE_AUTO		"auto"
+#define SFC_KVARG_PERF_PROFILE_THROUGHPUT	"throughput"
+#define SFC_KVARG_PERF_PROFILE_LOW_LATENCY	"low-latency"
+#define SFC_KVARG_VALUES_PERF_PROFILE \
+	"[" SFC_KVARG_PERF_PROFILE_AUTO "|" \
+	    SFC_KVARG_PERF_PROFILE_THROUGHPUT "|" \
+	    SFC_KVARG_PERF_PROFILE_LOW_LATENCY "]"
+
 struct sfc_adapter;
 
 int sfc_kvargs_parse(struct sfc_adapter *sa);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 00/31] Support more features in Solarflare PMD
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev

The patch series adds a number of features to Solarflare libefx-based
PMD. Basically one patch per feature.

The patches are grouped into one series since they touch nearby lines
in either PMD feature list, or dev_ops structure, or documentation.
So, patches cannot be applied in arbitrary order.

The patch series should be applied after
[PATCH v2 00/55] Solarflare libefx-based PMD
(Message-ID: 1480436367-20749-1-git-send-email-arybchenko@solarflare.com)


Andrew Rybchenko (16):
  net/sfc: implement MCDI logging callback
  net/sfc: support parameter to choose performance profile
  net/sfc: implement ethdev hook to get basic statistics
  net/sfc: support extended statistics
  net/sfc: support flow control settings get/set
  net/sfc: support link status change interrupt
  net/sfc: implement device operation to change MTU
  net/sfc: support link speed and duplex settings
  net/sfc: support checksum offloads on receive
  net/sfc: handle received packet type info provided by HW
  net/sfc: support callback to get receive queue information
  net/sfc: support Rx free threshold
  net/sfc: add callback to get RxQ pending descriptors count
  net/sfc: add RxQ descriptor done callback
  net/sfc: support scattered Rx DMA
  net/sfc: support deferred start of receive queues

Artem Andreev (1):
  net/sfc: support link up/down

Ivan Malov (14):
  net/sfc: support promiscuous and all-multicast control
  net/sfc: support main (the first) MAC address change
  net/sfc: support multicast addresses list controls
  net/sfc: add callback to get transmit queue information
  net/sfc: support Tx free threshold
  net/sfc: support deferred start of transmit queues
  net/sfc: support VLAN offload on transmit path
  net/sfc: add basic stubs for RSS support on driver attach
  net/sfc: support RSS hash offload
  net/sfc: add callback to query RSS key and hash types config
  net/sfc: add callback to set RSS key and hash types config
  net/sfc: add callback to query RSS redirection table
  net/sfc: add callback to update RSS redirection table
  net/sfc: support firmware-assisted TSOv2

 config/common_base                   |   1 +
 doc/guides/nics/features/sfc_efx.ini |  22 +-
 doc/guides/nics/sfc_efx.rst          |  55 ++-
 drivers/net/sfc/Makefile             |   4 +
 drivers/net/sfc/efsys.h              |   8 +-
 drivers/net/sfc/sfc.c                | 126 ++++-
 drivers/net/sfc/sfc.h                |  46 ++
 drivers/net/sfc/sfc_ethdev.c         | 893 ++++++++++++++++++++++++++++++++++-
 drivers/net/sfc/sfc_ev.c             |  64 ++-
 drivers/net/sfc/sfc_ev.h             |   2 +
 drivers/net/sfc/sfc_intr.c           | 204 ++++++++
 drivers/net/sfc/sfc_kvargs.c         |   2 +
 drivers/net/sfc/sfc_kvargs.h         |  12 +
 drivers/net/sfc/sfc_mcdi.c           |  69 +++
 drivers/net/sfc/sfc_port.c           | 107 ++++-
 drivers/net/sfc/sfc_rx.c             | 288 ++++++++++-
 drivers/net/sfc/sfc_rx.h             |  16 +
 drivers/net/sfc/sfc_tso.c            | 203 ++++++++
 drivers/net/sfc/sfc_tweak.h          |   3 +
 drivers/net/sfc/sfc_tx.c             | 165 ++++++-
 drivers/net/sfc/sfc_tx.h             |  41 +-
 21 files changed, 2259 insertions(+), 72 deletions(-)
 create mode 100644 drivers/net/sfc/sfc_tso.c

-- 
2.5.5

^ permalink raw reply

* [PATCH 01/31] net/sfc: implement MCDI logging callback
From: Andrew Rybchenko @ 2016-12-02  7:44 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com>

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 doc/guides/nics/sfc_efx.rst  |  6 ++++
 drivers/net/sfc/efsys.h      |  2 +-
 drivers/net/sfc/sfc.h        |  1 +
 drivers/net/sfc/sfc_ethdev.c |  1 +
 drivers/net/sfc/sfc_kvargs.c |  1 +
 drivers/net/sfc/sfc_kvargs.h |  2 ++
 drivers/net/sfc/sfc_mcdi.c   | 69 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index aadd775..2cca287 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -155,3 +155,9 @@ boolean parameters value.
 - ``debug_init`` [bool] (default **n**)
 
   Enable extra logging during device intialization and startup.
+
+- ``mcdi_logging`` [bool] (default **n**)
+
+  Enable extra logging of the communication with the NIC's management CPU.
+  The logging is done using RTE_LOG() with INFO level and PMD type.
+  The format is consumed by the Solarflare netlogdecode cross-platform tool.
diff --git a/drivers/net/sfc/efsys.h b/drivers/net/sfc/efsys.h
index e4d5035..d48eb4c 100644
--- a/drivers/net/sfc/efsys.h
+++ b/drivers/net/sfc/efsys.h
@@ -175,7 +175,7 @@ prefetch_read_once(const volatile void *addr)
 
 /* MCDI is required for SFN7xxx and SFN8xx */
 #define EFSYS_OPT_MCDI 1
-#define EFSYS_OPT_MCDI_LOGGING 0
+#define EFSYS_OPT_MCDI_LOGGING 1
 #define EFSYS_OPT_MCDI_PROXY_AUTH 0
 
 #define EFSYS_OPT_MAC_STATS 0
diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 29d3a6b..0064fcb 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -107,6 +107,7 @@ struct sfc_mcdi {
 	efsys_mem_t			mem;
 	enum sfc_mcdi_state		state;
 	efx_mcdi_transport_t		transport;
+	bool				logging;
 };
 
 struct sfc_intr {
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index c28082c..12309ee 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -464,4 +464,5 @@ static struct eth_driver sfc_efx_pmd = {
 RTE_PMD_REGISTER_PCI(net_sfc_efx, sfc_efx_pmd.pci_drv);
 RTE_PMD_REGISTER_PCI_TABLE(net_sfc_efx, pci_id_sfc_efx_map);
 RTE_PMD_REGISTER_PARAM_STRING(net_sfc_efx,
+	SFC_KVARG_MCDI_LOGGING "=" SFC_KVARG_VALUES_BOOL " "
 	SFC_KVARG_DEBUG_INIT "=" SFC_KVARG_VALUES_BOOL);
diff --git a/drivers/net/sfc/sfc_kvargs.c b/drivers/net/sfc/sfc_kvargs.c
index f1bab28..bbbd026 100644
--- a/drivers/net/sfc/sfc_kvargs.c
+++ b/drivers/net/sfc/sfc_kvargs.c
@@ -42,6 +42,7 @@ sfc_kvargs_parse(struct sfc_adapter *sa)
 	struct rte_devargs *devargs = sa->eth_dev->pci_dev->device.devargs;
 	const char **params = (const char *[]){
 		SFC_KVARG_DEBUG_INIT,
+		SFC_KVARG_MCDI_LOGGING,
 		NULL,
 	};
 
diff --git a/drivers/net/sfc/sfc_kvargs.h b/drivers/net/sfc/sfc_kvargs.h
index 0b53963..ffce851 100644
--- a/drivers/net/sfc/sfc_kvargs.h
+++ b/drivers/net/sfc/sfc_kvargs.h
@@ -40,6 +40,8 @@ extern "C" {
 
 #define SFC_KVARG_DEBUG_INIT		"debug_init"
 
+#define SFC_KVARG_MCDI_LOGGING		"mcdi_logging"
+
 struct sfc_adapter;
 
 int sfc_kvargs_parse(struct sfc_adapter *sa);
diff --git a/drivers/net/sfc/sfc_mcdi.c b/drivers/net/sfc/sfc_mcdi.c
index 9ba28e1..3bed2e0 100644
--- a/drivers/net/sfc/sfc_mcdi.c
+++ b/drivers/net/sfc/sfc_mcdi.c
@@ -35,6 +35,7 @@
 
 #include "sfc.h"
 #include "sfc_log.h"
+#include "sfc_kvargs.h"
 
 #define SFC_MCDI_POLL_INTERVAL_MIN_US	10		/* 10us in 1us units */
 #define SFC_MCDI_POLL_INTERVAL_MAX_US	(US_PER_S / 10)	/* 100ms in 1us units */
@@ -125,6 +126,65 @@ sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
 	sfc_panic(sa, "MCDI exceptions handling is not implemented\n");
 }
 
+#define SFC_MCDI_LOG_BUF_SIZE	128
+
+static size_t
+sfc_mcdi_do_log(const struct sfc_adapter *sa,
+		char *buffer, void *data, size_t data_size,
+		size_t pfxsize, size_t position)
+{
+	uint32_t *words = data;
+	/* Space separator plus 2 characters per byte */
+	const size_t word_str_space = 1 + 2 * sizeof(*words);
+	size_t i;
+
+	for (i = 0; i < data_size; i += sizeof(*words)) {
+		if (position + word_str_space >=
+		    SFC_MCDI_LOG_BUF_SIZE) {
+			/* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash
+			 * at the end which is required by netlogdecode.
+			 */
+			buffer[position] = '\0';
+			sfc_info(sa, "%s \\", buffer);
+			/* Preserve prefix for the next log message */
+			position = pfxsize;
+		}
+		position += snprintf(buffer + position,
+				     SFC_MCDI_LOG_BUF_SIZE - position,
+				     " %08x", *words);
+		words++;
+	}
+	return position;
+}
+
+static void
+sfc_mcdi_logger(void *arg, efx_log_msg_t type,
+		void *header, size_t header_size,
+		void *data, size_t data_size)
+{
+	struct sfc_adapter *sa = (struct sfc_adapter *)arg;
+	char buffer[SFC_MCDI_LOG_BUF_SIZE];
+	size_t pfxsize;
+	size_t start;
+
+	if (!sa->mcdi.logging)
+		return;
+
+	/* The format including prefix added by sfc_info() is the format
+	 * consumed by the Solarflare netlogdecode tool.
+	 */
+	pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:",
+			   type == EFX_LOG_MCDI_REQUEST ? "REQ" :
+			   type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???");
+	start = sfc_mcdi_do_log(sa, buffer, header, header_size,
+				pfxsize, pfxsize);
+	start = sfc_mcdi_do_log(sa, buffer, data, data_size, pfxsize, start);
+	if (start != pfxsize) {
+		buffer[start] = '\0';
+		sfc_info(sa, "%s", buffer);
+	}
+}
+
 int
 sfc_mcdi_init(struct sfc_adapter *sa)
 {
@@ -149,12 +209,19 @@ sfc_mcdi_init(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_dma_alloc;
 
+	/* Convert negative error to positive used in the driver */
+	rc = sfc_kvargs_process(sa, SFC_KVARG_MCDI_LOGGING,
+				sfc_kvarg_bool_handler, &mcdi->logging);
+	if (rc != 0)
+		goto fail_kvargs_process;
+
 	emtp = &mcdi->transport;
 	emtp->emt_context = sa;
 	emtp->emt_dma_mem = &mcdi->mem;
 	emtp->emt_execute = sfc_mcdi_execute;
 	emtp->emt_ev_cpl = sfc_mcdi_ev_cpl;
 	emtp->emt_exception = sfc_mcdi_exception;
+	emtp->emt_logger = sfc_mcdi_logger;
 
 	sfc_log_init(sa, "init MCDI");
 	rc = efx_mcdi_init(sa->nic, emtp);
@@ -165,6 +232,8 @@ sfc_mcdi_init(struct sfc_adapter *sa)
 
 fail_mcdi_init:
 	memset(emtp, 0, sizeof(*emtp));
+
+fail_kvargs_process:
 	sfc_dma_free(sa, &mcdi->mem);
 
 fail_dma_alloc:
-- 
2.5.5

^ permalink raw reply related

* [PATCH 2/2] net/sfc: restart TxQ in case of exception on its event queue
From: Andrew Rybchenko @ 2016-12-02  7:41 UTC (permalink / raw)
  To: dev; +Cc: Ivan Malov
In-Reply-To: <1480664481-26427-1-git-send-email-arybchenko@solarflare.com>

From: Ivan Malov <ivan.malov@oktetlabs.ru>

Examples of recoverable exceptions because of Tx error are:
 - Tx descriptor PCI read error
 - invalid Tx option descriptor
 - Tx option descriptor not supported by the firmware variant
 - unexpected Tx option descriptor (e.g. missing FATSO2A before
   FATSO2B, missing FATSO2B after FATSO2A)
 - incomplete packet push (CONT bit set in the latest pushed DMA
   descriptor)

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 drivers/net/sfc/sfc_ev.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/net/sfc/sfc_ev.c b/drivers/net/sfc/sfc_ev.c
index 36aede8..af3c7b2 100644
--- a/drivers/net/sfc/sfc_ev.c
+++ b/drivers/net/sfc/sfc_ev.c
@@ -339,6 +339,20 @@ sfc_ev_qpoll(struct sfc_evq *evq)
 					rxq_sw_index);
 		}
 
+		if (evq->txq != NULL) {
+			unsigned int txq_sw_index = sfc_txq_sw_index(evq->txq);
+
+			sfc_warn(sa,
+				 "restart TxQ %u because of exception on its EvQ %u",
+				 txq_sw_index, evq->evq_index);
+
+			sfc_tx_qstop(sa, txq_sw_index);
+			rc = sfc_tx_qstart(sa, txq_sw_index);
+			if (rc != 0)
+				sfc_err(sa, "cannot restart TxQ %u",
+					txq_sw_index);
+		}
+
 		if (evq->exception)
 			sfc_panic(sa, "unrecoverable exception on EvQ %u",
 				  evq->evq_index);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 1/2] net/sfc: restart RxQ in case of exception on its event queue
From: Andrew Rybchenko @ 2016-12-02  7:41 UTC (permalink / raw)
  To: dev
In-Reply-To: <1480664481-26427-1-git-send-email-arybchenko@solarflare.com>

Examples of recoverable exceptions are:
 - unexpected Rx event (Rx scatter abort with non-zero size,
   too big Rx descriptors batch completed)
 - Rx error due to invalid Rx descriptors push
 - Rx error due to Rx descriptor read error (e.g. unmapped Rx ring
   and denied by IOMMU)

Reviewed-by: Andrew Lee <alee@solarflare.com>
Reviewed-by: Robert Stonehouse <rstonehouse@solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 drivers/net/sfc/sfc.h    |  6 ++++++
 drivers/net/sfc/sfc_ev.c | 26 ++++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h
index 29d3a6b..995dfe6 100644
--- a/drivers/net/sfc/sfc.h
+++ b/drivers/net/sfc/sfc.h
@@ -187,6 +187,12 @@ sfc_adapter_lock(struct sfc_adapter *sa)
 	rte_spinlock_lock(&sa->lock);
 }
 
+static inline int
+sfc_adapter_trylock(struct sfc_adapter *sa)
+{
+	return rte_spinlock_trylock(&sa->lock);
+}
+
 static inline void
 sfc_adapter_unlock(struct sfc_adapter *sa)
 {
diff --git a/drivers/net/sfc/sfc_ev.c b/drivers/net/sfc/sfc_ev.c
index 96b95cc..36aede8 100644
--- a/drivers/net/sfc/sfc_ev.c
+++ b/drivers/net/sfc/sfc_ev.c
@@ -30,6 +30,7 @@
 #include <rte_debug.h>
 #include <rte_cycles.h>
 #include <rte_alarm.h>
+#include <rte_branch_prediction.h>
 
 #include "efx.h"
 
@@ -320,6 +321,31 @@ sfc_ev_qpoll(struct sfc_evq *evq)
 
 	efx_ev_qpoll(evq->common, &evq->read_ptr, &sfc_ev_callbacks, evq);
 
+	if (unlikely(evq->exception) && sfc_adapter_trylock(evq->sa)) {
+		struct sfc_adapter *sa = evq->sa;
+		int rc;
+
+		if ((evq->rxq != NULL) && (evq->rxq->state & SFC_RXQ_RUNNING)) {
+			unsigned int rxq_sw_index = sfc_rxq_sw_index(evq->rxq);
+
+			sfc_warn(sa,
+				 "restart RxQ %u because of exception on its EvQ %u",
+				 rxq_sw_index, evq->evq_index);
+
+			sfc_rx_qstop(sa, rxq_sw_index);
+			rc = sfc_rx_qstart(sa, rxq_sw_index);
+			if (rc != 0)
+				sfc_err(sa, "cannot restart RxQ %u",
+					rxq_sw_index);
+		}
+
+		if (evq->exception)
+			sfc_panic(sa, "unrecoverable exception on EvQ %u",
+				  evq->evq_index);
+
+		sfc_adapter_unlock(sa);
+	}
+
 	/* Poll-mode driver does not re-prime the event queue for interrupts */
 }
 
-- 
2.5.5

^ permalink raw reply related

* [PATCH 0/2] Event queue exception handling in Solarflare PMD
From: Andrew Rybchenko @ 2016-12-02  7:41 UTC (permalink / raw)
  To: dev

The patch series adds event queue exception handling.

It is not required when everything goes fine, but has chances to
recover if something goes wrong in either driver or HW.

The patch series should be applied after
[PATCH v2 00/55] Solarflare libefx-based PMD
(Message-ID: 1480436367-20749-1-git-send-email-arybchenko@solarflare.com)


Andrew Rybchenko (1):
  net/sfc: restart RxQ in case of exception on its event queue

Ivan Malov (1):
  net/sfc: restart TxQ in case of exception on its event queue

 drivers/net/sfc/sfc.h    |  6 ++++++
 drivers/net/sfc/sfc_ev.c | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 46 insertions(+)

-- 
2.5.5

^ permalink raw reply

* [PATCH 24/24] net/i40e: flush tunnel filters
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch is to flush all tunnel filters.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 26a8c5a..71d1f37 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -529,6 +529,7 @@ static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow_error *error);
 static int i40e_ethertype_filter_flush(struct i40e_pf *pf);
 static int i40e_macvlan_filter_flush(struct i40e_pf *pf);
+static int i40e_tunnel_filter_flush(struct i40e_pf *pf);
 static int i40e_flow_flush(struct rte_eth_dev *dev,
 			     struct rte_flow_error *error);
 
@@ -11849,6 +11850,21 @@ i40e_macvlan_filter_flush(struct i40e_pf *pf)
 	return ret;
 }
 
+/* Flush all tunnel filters */
+static int
+i40e_tunnel_filter_flush(struct i40e_pf *pf)
+{
+	struct i40e_tunnel_filter_list
+		*tunnel_list = &pf->tunnel.tunnel_list;
+	struct i40e_tunnel_filter *f;
+	int ret = 0;
+
+	while ((f = TAILQ_FIRST(tunnel_list)))
+		ret = i40e_dev_destroy_tunnel_filter(pf, f);
+
+	return ret;
+}
+
 static int
 i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
@@ -11873,5 +11889,11 @@ i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 		return ret;
 	}
 
+	ret = i40e_tunnel_filter_flush(pf);
+	if (ret) {
+		error->type = RTE_FLOW_ERROR_TYPE_HANDLE;
+		return ret;
+	}
+
 	return ret;
 }
-- 
2.5.5

^ permalink raw reply related

* [PATCH 23/24] net/i40e: flush macvlan filters
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch is to flush all macvlan filters.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index e685c99..26a8c5a 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -528,6 +528,7 @@ static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow *flow,
 			     struct rte_flow_error *error);
 static int i40e_ethertype_filter_flush(struct i40e_pf *pf);
+static int i40e_macvlan_filter_flush(struct i40e_pf *pf);
 static int i40e_flow_flush(struct rte_eth_dev *dev,
 			     struct rte_flow_error *error);
 
@@ -11831,6 +11832,23 @@ i40e_ethertype_filter_flush(struct i40e_pf *pf)
 	return ret;
 }
 
+/* Flush all macvlan filters */
+static int
+i40e_macvlan_filter_flush(struct i40e_pf *pf)
+{
+	struct i40e_mac_filter *f;
+	struct i40e_vsi *vsi;
+	int i, ret = 0;
+
+	for (i = 0; i < pf->vf_num; i++) {
+		vsi = pf->vfs[i].vsi;
+		while ((f = TAILQ_FIRST(&vsi->mac_list)))
+			ret = i40e_dev_destroy_macvlan_filter(pf, vsi, f);
+	}
+
+	return ret;
+}
+
 static int
 i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
@@ -11849,5 +11867,11 @@ i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 		return ret;
 	}
 
+	ret = i40e_macvlan_filter_flush(pf);
+	if (ret) {
+		error->type = RTE_FLOW_ERROR_TYPE_HANDLE;
+		return ret;
+	}
+
 	return ret;
 }
-- 
2.5.5

^ permalink raw reply related

* [PATCH 22/24] net/i40e: flush ethertype filters
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch is to flush all ethertype filters.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 1a5b767..e685c99 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -527,6 +527,7 @@ static int i40e_dev_destroy_tunnel_filter(struct i40e_pf *pf,
 static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow *flow,
 			     struct rte_flow_error *error);
+static int i40e_ethertype_filter_flush(struct i40e_pf *pf);
 static int i40e_flow_flush(struct rte_eth_dev *dev,
 			     struct rte_flow_error *error);
 
@@ -11815,9 +11816,25 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 	return ret;
 }
 
+/* Flush all ethertype filters */
+static int
+i40e_ethertype_filter_flush(struct i40e_pf *pf)
+{
+	struct i40e_ethertype_filter_list
+		*ethertype_list = &pf->ethertype.ethertype_list;
+	struct i40e_ethertype_filter *f;
+	int ret = 0;
+
+	while ((f = TAILQ_FIRST(ethertype_list)))
+		ret = i40e_dev_destroy_ethertype_filter(pf, f);
+
+	return ret;
+}
+
 static int
 i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 {
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
 	int ret = 0;
 
 	ret = i40e_fdir_flush(dev);
@@ -11826,5 +11843,11 @@ i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
 		return ret;
 	}
 
+	ret = i40e_ethertype_filter_flush(pf);
+	if (ret) {
+		error->type = RTE_FLOW_ERROR_TYPE_HANDLE;
+		return ret;
+	}
+
 	return ret;
 }
-- 
2.5.5

^ permalink raw reply related

* [PATCH 21/24] net/i40e: add flow flush function
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds flow flush ops.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 17 +++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  1 +
 drivers/net/i40e/i40e_fdir.c   |  3 +--
 3 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 34e431b..1a5b767 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -527,6 +527,8 @@ static int i40e_dev_destroy_tunnel_filter(struct i40e_pf *pf,
 static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow *flow,
 			     struct rte_flow_error *error);
+static int i40e_flow_flush(struct rte_eth_dev *dev,
+			     struct rte_flow_error *error);
 
 struct i40e_flow {
 	enum rte_filter_type filter_type;
@@ -632,6 +634,7 @@ static const struct rte_flow_ops i40e_flow_ops = {
 	.validate = i40e_flow_validate,
 	.create = (void *)i40e_flow_create,
 	.destroy = i40e_flow_destroy,
+	.flush = i40e_flow_flush,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -11811,3 +11814,17 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+static int
+i40e_flow_flush(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+	int ret = 0;
+
+	ret = i40e_fdir_flush(dev);
+	if (ret) {
+		error->type = RTE_FLOW_ERROR_TYPE_HANDLE;
+		return ret;
+	}
+
+	return ret;
+}
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 9b60651..8e6cc51 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -684,6 +684,7 @@ int i40e_select_filter_input_set(struct i40e_hw *hw,
 				 struct rte_eth_input_set_conf *conf,
 				 enum rte_filter_type filter);
 void i40e_fdir_filter_restore(struct i40e_pf *pf);
+int i40e_fdir_flush(struct rte_eth_dev *dev);
 int i40e_add_del_fdir_filter(struct rte_eth_dev *dev,
 			     const struct rte_eth_fdir_filter *filter,
 			     bool add);
diff --git a/drivers/net/i40e/i40e_fdir.c b/drivers/net/i40e/i40e_fdir.c
index 30fcd5c..8329987 100644
--- a/drivers/net/i40e/i40e_fdir.c
+++ b/drivers/net/i40e/i40e_fdir.c
@@ -119,7 +119,6 @@ static int i40e_fdir_filter_programming(struct i40e_pf *pf,
 			enum i40e_filter_pctype pctype,
 			const struct rte_eth_fdir_filter *filter,
 			bool add);
-static int i40e_fdir_flush(struct rte_eth_dev *dev);
 
 static int i40e_fdir_filter_convert(const struct rte_eth_fdir_filter *input,
 			 struct i40e_fdir_filter *filter);
@@ -1315,7 +1314,7 @@ i40e_fdir_filter_programming(struct i40e_pf *pf,
  * i40e_fdir_flush - clear all filters of Flow Director table
  * @pf: board private structure
  */
-static int
+int
 i40e_fdir_flush(struct rte_eth_dev *dev)
 {
 	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 20/24] net/i40e: destroy flow directory filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds a function to destroy the flow directory filter.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 4847c04..34e431b 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -11795,6 +11795,10 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 		ret = i40e_dev_destroy_tunnel_filter(pf,
 			     (struct i40e_tunnel_filter *)pmd_flow->rule);
 		break;
+	case RTE_ETH_FILTER_FDIR:
+		ret = i40e_add_del_fdir_filter(dev,
+		       &((struct i40e_fdir_filter *)pmd_flow->rule)->fdir, 0);
+		break;
 	default:
 		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
 			    filter_type);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 19/24] net/i40e: destroy tunnel filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds a function to destroy the tunnel filter.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 43 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index fddd46d..4847c04 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -522,6 +522,8 @@ static int i40e_dev_destroy_ethertype_filter(struct i40e_pf *pf,
 static int i40e_dev_destroy_macvlan_filter(struct i40e_pf *pf,
 					   struct i40e_vsi *vsi,
 					   struct i40e_mac_filter *filter);
+static int i40e_dev_destroy_tunnel_filter(struct i40e_pf *pf,
+					  struct i40e_tunnel_filter *filter);
 static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow *flow,
 			     struct rte_flow_error *error);
@@ -11734,6 +11736,43 @@ i40e_dev_destroy_macvlan_filter(struct i40e_pf *pf,
 }
 
 static int
+i40e_dev_destroy_tunnel_filter(struct i40e_pf *pf,
+			       struct i40e_tunnel_filter *filter)
+{
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	struct i40e_vsi *vsi = pf->main_vsi;
+	struct i40e_aqc_add_remove_cloud_filters_element_data cld_filter;
+	struct i40e_tunnel_info *tunnel_info = &pf->tunnel;
+	struct i40e_tunnel_filter *node;
+	int ret = 0;
+
+	memset(&cld_filter, 0, sizeof(cld_filter));
+	ether_addr_copy((struct ether_addr *)&filter->input.outer_mac,
+			(struct ether_addr *)&cld_filter.outer_mac);
+	ether_addr_copy((struct ether_addr *)&filter->input.inner_mac,
+			(struct ether_addr *)&cld_filter.inner_mac);
+	cld_filter.inner_vlan = filter->input.inner_vlan;
+	cld_filter.flags = filter->input.flags;
+	cld_filter.tenant_id = filter->input.tenant_id;
+	rte_memcpy(&cld_filter.ipaddr, &filter->input.ipaddr,
+		   sizeof(cld_filter.ipaddr));
+	cld_filter.queue_number = filter->queue;
+
+	ret = i40e_aq_remove_cloud_filters(hw, vsi->seid,
+					   &cld_filter, 1);
+	if (ret < 0)
+		return ret;
+
+	node = i40e_sw_tunnel_filter_lookup(tunnel_info, &filter->input);
+	if (node)
+		ret = i40e_sw_tunnel_filter_del(pf, node);
+	else
+		return -EINVAL;
+
+	return ret;
+}
+
+static int
 i40e_flow_destroy(struct rte_eth_dev *dev,
 		  struct rte_flow *flow,
 		  struct rte_flow_error *error)
@@ -11752,6 +11791,10 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 		ret = i40e_dev_destroy_macvlan_filter(pf,
 		      pmd_flow->vsi, (struct i40e_mac_filter *)pmd_flow->rule);
 		break;
+	case RTE_ETH_FILTER_TUNNEL:
+		ret = i40e_dev_destroy_tunnel_filter(pf,
+			     (struct i40e_tunnel_filter *)pmd_flow->rule);
+		break;
 	default:
 		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
 			    filter_type);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 18/24] net/i40e: destroy macvlan filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds a function to destroy the macvlan filter.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index a3ed1f0..fddd46d 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -519,6 +519,9 @@ static struct i40e_flow *i40e_flow_create(struct rte_eth_dev *dev,
 				   struct rte_flow_error *error);
 static int i40e_dev_destroy_ethertype_filter(struct i40e_pf *pf,
 				     struct i40e_ethertype_filter *filter);
+static int i40e_dev_destroy_macvlan_filter(struct i40e_pf *pf,
+					   struct i40e_vsi *vsi,
+					   struct i40e_mac_filter *filter);
 static int i40e_flow_destroy(struct rte_eth_dev *dev,
 			     struct rte_flow *flow,
 			     struct rte_flow_error *error);
@@ -11708,6 +11711,29 @@ i40e_dev_destroy_ethertype_filter(struct i40e_pf *pf,
 }
 
 static int
+i40e_dev_destroy_macvlan_filter(struct i40e_pf *pf,
+				struct i40e_vsi *vsi,
+				struct i40e_mac_filter *filter)
+{
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	int ret;
+
+	(void)rte_memcpy(hw->mac.addr, hw->mac.perm_addr,
+			 ETHER_ADDR_LEN);
+	ret = i40e_vsi_delete_mac(vsi, &filter->mac_info.mac_addr);
+	if (ret != I40E_SUCCESS) {
+		PMD_DRV_LOG(ERR, "Failed to delete MAC filter.");
+		return -1;
+	}
+
+	/* Clear device address as it has been removed. */
+	if (is_same_ether_addr(&pf->dev_addr, &filter->mac_info.mac_addr))
+		memset(&pf->dev_addr, 0, sizeof(struct ether_addr));
+
+	return 0;
+}
+
+static int
 i40e_flow_destroy(struct rte_eth_dev *dev,
 		  struct rte_flow *flow,
 		  struct rte_flow_error *error)
@@ -11722,6 +11748,10 @@ i40e_flow_destroy(struct rte_eth_dev *dev,
 		ret = i40e_dev_destroy_ethertype_filter(pf,
 			(struct i40e_ethertype_filter *)pmd_flow->rule);
 		break;
+	case RTE_ETH_FILTER_MACVLAN:
+		ret = i40e_dev_destroy_macvlan_filter(pf,
+		      pmd_flow->vsi, (struct i40e_mac_filter *)pmd_flow->rule);
+		break;
 	default:
 		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
 			    filter_type);
-- 
2.5.5

^ permalink raw reply related

* [PATCH 17/24] net/i40e: destroy ethertype filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds a function to destroy the ethertype filter.
And this patch also adds flow destroy function.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 69 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index ac93489..a3ed1f0 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -517,6 +517,11 @@ static struct i40e_flow *i40e_flow_create(struct rte_eth_dev *dev,
 				   const struct rte_flow_item *pattern,
 				   const struct rte_flow_action *actions,
 				   struct rte_flow_error *error);
+static int i40e_dev_destroy_ethertype_filter(struct i40e_pf *pf,
+				     struct i40e_ethertype_filter *filter);
+static int i40e_flow_destroy(struct rte_eth_dev *dev,
+			     struct rte_flow *flow,
+			     struct rte_flow_error *error);
 
 struct i40e_flow {
 	enum rte_filter_type filter_type;
@@ -621,6 +626,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 static const struct rte_flow_ops i40e_flow_ops = {
 	.validate = i40e_flow_validate,
 	.create = (void *)i40e_flow_create,
+	.destroy = i40e_flow_destroy,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -11665,3 +11671,66 @@ i40e_flow_create(struct rte_eth_dev *dev,
 	rte_free(flow);
 	return NULL;
 }
+
+static int
+i40e_dev_destroy_ethertype_filter(struct i40e_pf *pf,
+				  struct i40e_ethertype_filter *filter)
+{
+	struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+	struct i40e_ethertype_info *ethertype_info = &pf->ethertype;
+	struct i40e_ethertype_filter *node;
+	struct i40e_control_filter_stats stats;
+	uint16_t flags = 0;
+	int ret = 0;
+
+	if (!(filter->flags & RTE_ETHTYPE_FLAGS_MAC))
+		flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC;
+	if (filter->flags & RTE_ETHTYPE_FLAGS_DROP)
+		flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP;
+	flags |= I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TO_QUEUE;
+
+	memset(&stats, 0, sizeof(stats));
+	ret = i40e_aq_add_rem_control_packet_filter(hw,
+				    filter->input.mac_addr.addr_bytes,
+				    filter->input.ether_type,
+				    flags, pf->main_vsi->seid,
+				    filter->queue, 0, &stats, NULL);
+	if (ret < 0)
+		return ret;
+
+	node = i40e_sw_ethertype_filter_lookup(ethertype_info, &filter->input);
+	if (node)
+		ret = i40e_sw_ethertype_filter_del(pf, node);
+	else
+		return -EINVAL;
+
+	return ret;
+}
+
+static int
+i40e_flow_destroy(struct rte_eth_dev *dev,
+		  struct rte_flow *flow,
+		  struct rte_flow_error *error)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct i40e_flow *pmd_flow = (struct i40e_flow *)flow;
+	enum rte_filter_type filter_type = pmd_flow->filter_type;
+	int ret;
+
+	switch (filter_type) {
+	case RTE_ETH_FILTER_ETHERTYPE:
+		ret = i40e_dev_destroy_ethertype_filter(pf,
+			(struct i40e_ethertype_filter *)pmd_flow->rule);
+		break;
+	default:
+		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
+			    filter_type);
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret)
+		error->type = RTE_FLOW_ERROR_TYPE_HANDLE;
+
+	return ret;
+}
-- 
2.5.5

^ permalink raw reply related

* [PATCH 15/24] net/i40e: parse flow director filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

Check if the rule is a flow director rule, and get the
flow director info.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 537 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 537 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 1ffafa0..12255fa 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -502,6 +502,11 @@ static int i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 				    const struct rte_flow_action *actions,
 				    struct rte_eth_tunnel_filter_conf *filter,
 				    struct rte_flow_error *error);
+static int i40e_parse_fdir_filter(const struct rte_flow_attr *attr,
+				  const struct rte_flow_item *pattern,
+				  const struct rte_flow_action *actions,
+				  struct rte_eth_fdir_filter *filter,
+				  struct rte_flow_error *error);
 static int i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 			      const struct rte_flow_attr *attr,
 			      const struct rte_flow_item *pattern,
@@ -11006,6 +11011,532 @@ i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 	return ret;
 }
 
+/**
+ * Parse the rule to see if it is a flow firector rule.
+ * And get the flow director filter info.
+ */
+static int
+i40e_parse_fdir_filter(const struct rte_flow_attr *attr,
+		       const struct rte_flow_item *pattern,
+		       const struct rte_flow_action *actions,
+		       struct rte_eth_fdir_filter *filter,
+		       struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_action *act;
+	const struct rte_flow_item_eth *eth_spec, *eth_mask;
+	const struct rte_flow_item_vlan *vlan_spec, *vlan_mask;
+	const struct rte_flow_item_ipv4 *ipv4_spec, *ipv4_mask;
+	const struct rte_flow_item_ipv6 *ipv6_spec, *ipv6_mask;
+	const struct rte_flow_item_tcp *tcp_spec, *tcp_mask;
+	const struct rte_flow_item_udp *udp_spec, *udp_mask;
+	const struct rte_flow_item_sctp *sctp_spec, *sctp_mask;
+	const struct rte_flow_item_vf *vf_spec;
+	const struct rte_flow_action_mark *mark_spec;
+	const struct rte_flow_action_queue *act_q;
+	struct ether_addr macaddr_masked = {
+		.addr_bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+	};
+	uint32_t i;
+	uint32_t flow_type = RTE_ETH_FLOW_UNKNOWN;
+	enum i40e_filter_pctype pctype;
+	uint64_t input_set = I40E_INSET_NONE;
+	uint16_t l3 = 0;
+	uint16_t flag_offset;
+
+	/* parse pattern */
+	i = 0;
+
+	/* The first not void item should be ETH or IPv4 or IPv6 */
+	PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
+	    item->type != RTE_FLOW_ITEM_TYPE_VLAN &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Check if the not void item is ETH. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+		eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if ((!eth_spec && eth_mask) || (eth_spec && !eth_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (eth_spec) {
+			filter->input.flow.l2_flow.ether_type =
+				(uint16_t)eth_spec->type;
+		}
+
+		if (eth_mask) {
+			/* SRC address and DST address should be masked. */
+			if (!is_same_ether_addr(&eth_mask->src,
+						&macaddr_masked) ||
+			    !is_same_ether_addr(&eth_mask->dst,
+						&macaddr_masked)) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if (eth_mask->type && eth_mask->type != 0xFFFF) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			flow_type = RTE_ETH_FLOW_L2_PAYLOAD;
+			input_set |= I40E_INSET_LAST_ETHER_TYPE;
+		}
+
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_VLAN &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV6 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_VF &&
+		    item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	/* Check if the not void item is VLAN. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+		vlan_spec = (const struct rte_flow_item_vlan *)item->spec;
+		vlan_mask = (const struct rte_flow_item_vlan *)item->mask;
+
+		if (i == 1) {
+			if ((vlan_spec && !vlan_mask) ||
+			    (!vlan_spec && vlan_mask)) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else if (i == 2) {
+			if (!vlan_spec || !vlan_mask) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+
+		if (vlan_spec && vlan_mask) {
+			filter->input.flow_ext.vlan_tci =
+				rte_be_to_cpu_16(vlan_spec->tci) & 0x0FFF;
+			if (vlan_mask->tpid ||
+			    (vlan_mask->tci && vlan_mask->tci !=
+			     rte_cpu_to_be_16(0x0FFF))) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			input_set |= I40E_INSET_VLAN_INNER;
+		}
+
+		/* Check if the next not void item is IPV4 or IPV6. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	/* Check if the not void item is IPV4. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) {
+		l3 = RTE_FLOW_ITEM_TYPE_IPV4;
+		ipv4_spec = (const struct rte_flow_item_ipv4 *)item->spec;
+		ipv4_mask = (const struct rte_flow_item_ipv4 *)item->mask;
+
+		/* Check if it is fragment. */
+		flag_offset = rte_be_to_cpu_16(ipv4_spec->hdr.fragment_offset);
+		if (flag_offset & !IPV4_HDR_MF_FLAG)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_OTHER;
+		if (flag_offset & IPV4_HDR_OFFSET_MASK ||
+		    flag_offset & IPV4_HDR_MF_FLAG)
+			flow_type = RTE_ETH_FLOW_FRAG_IPV4;
+
+		if (ipv4_spec) {
+			filter->input.flow.ip4_flow.proto =
+				ipv4_spec->hdr.next_proto_id;
+			filter->input.flow.ip4_flow.tos =
+				ipv4_spec->hdr.type_of_service;
+			filter->input.flow.ip4_flow.ttl =
+				ipv4_spec->hdr.time_to_live;
+			filter->input.flow.ip4_flow.src_ip =
+				ipv4_spec->hdr.src_addr;
+			filter->input.flow.ip4_flow.dst_ip =
+				ipv4_spec->hdr.dst_addr;
+		}
+
+		if (ipv4_mask) {
+			if (ipv4_mask->hdr.version_ihl ||
+			    ipv4_mask->hdr.total_length ||
+			    ipv4_mask->hdr.packet_id ||
+			    ipv4_mask->hdr.fragment_offset ||
+			    ipv4_mask->hdr.hdr_checksum) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if (ipv4_mask->hdr.src_addr == 0xFFFFFFFF)
+				input_set |= I40E_INSET_IPV4_SRC;
+			if (ipv4_mask->hdr.dst_addr == 0xFFFFFFFF)
+				input_set |= I40E_INSET_IPV4_DST;
+			if (ipv4_mask->hdr.type_of_service == 0xFF)
+				input_set |= I40E_INSET_IPV4_TOS;
+			if (ipv4_mask->hdr.time_to_live == 0xFF)
+				input_set |= I40E_INSET_IPV4_TTL;
+			if (ipv4_mask->hdr.next_proto_id == 0xFF)
+				input_set |= I40E_INSET_IPV4_PROTO;
+		}
+	}
+
+	/* Check if the not void item is IPV6. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
+		l3 = RTE_FLOW_ITEM_TYPE_IPV6;
+		ipv6_spec = (const struct rte_flow_item_ipv6 *)item->spec;
+		ipv6_mask = (const struct rte_flow_item_ipv6 *)item->mask;
+
+		/* Check if it is fragment. */
+		if (ipv6_spec->hdr.proto == 44)
+			flow_type = RTE_ETH_FLOW_FRAG_IPV6;
+		else
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV6_OTHER;
+
+		uint32_t j;
+
+		if (ipv6_spec) {
+			filter->input.flow.ipv6_flow.tc =
+				(uint8_t)(ipv6_spec->hdr.vtc_flow << 4);
+			filter->input.flow.ipv6_flow.proto =
+				ipv6_spec->hdr.proto;
+			filter->input.flow.ipv6_flow.hop_limits =
+				ipv6_spec->hdr.hop_limits;
+
+			rte_memcpy(filter->input.flow.ipv6_flow.src_ip,
+				   ipv6_spec->hdr.src_addr, 16);
+			rte_memcpy(filter->input.flow.ipv6_flow.dst_ip,
+				   ipv6_spec->hdr.dst_addr, 16);
+		}
+
+		if (ipv6_mask) {
+			if (ipv6_mask->hdr.payload_len) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			for (j = 0; j < RTE_DIM(ipv6_mask->hdr.src_addr); j++) {
+				if (ipv6_mask->hdr.src_addr[j] != 0xFF ||
+				    ipv6_mask->hdr.dst_addr[j] != 0xFF) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+			}
+
+			input_set |= I40E_INSET_IPV6_SRC;
+			input_set |= I40E_INSET_IPV6_DST;
+
+			if ((ipv6_mask->hdr.vtc_flow & rte_cpu_to_be_16(0xFF0))
+			    == rte_cpu_to_be_16(0xFF0))
+				input_set |= I40E_INSET_IPV6_TC;
+			if (ipv6_mask->hdr.proto == 0xFF)
+				input_set |= I40E_INSET_IPV6_NEXT_HDR;
+			if (ipv6_mask->hdr.hop_limits == 0xFF)
+				input_set |= I40E_INSET_IPV6_HOP_LIMIT;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
+	    item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
+		/* Check the next not void item */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM);
+		if ((flow_type == RTE_ETH_FLOW_FRAG_IPV4) ||
+		    (flow_type == RTE_ETH_FLOW_FRAG_IPV6)) {
+			if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			if (item->type != RTE_FLOW_ITEM_TYPE_TCP &&
+			    item->type != RTE_FLOW_ITEM_TYPE_UDP &&
+			    item->type != RTE_FLOW_ITEM_TYPE_SCTP &&
+			    item->type != RTE_FLOW_ITEM_TYPE_VF &&
+			    item->type != RTE_FLOW_ITEM_TYPE_END) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* Check if the next not void item is TCP. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_TCP) {
+		tcp_spec = (const struct rte_flow_item_tcp *)item->spec;
+		tcp_mask = (const struct rte_flow_item_tcp *)item->mask;
+
+		if (!tcp_spec || !tcp_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_TCP;
+		else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV6_TCP;
+
+		if (tcp_spec) {
+			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
+				filter->input.flow.tcp4_flow.src_port =
+					tcp_spec->hdr.src_port;
+				filter->input.flow.tcp4_flow.dst_port =
+					tcp_spec->hdr.dst_port;
+			} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
+				filter->input.flow.tcp6_flow.src_port =
+					tcp_spec->hdr.src_port;
+				filter->input.flow.tcp6_flow.dst_port =
+					tcp_spec->hdr.dst_port;
+			}
+		}
+
+		if (tcp_mask) {
+			if (tcp_mask->hdr.sent_seq ||
+			    tcp_mask->hdr.recv_ack ||
+			    tcp_mask->hdr.data_off ||
+			    tcp_mask->hdr.tcp_flags ||
+			    tcp_mask->hdr.rx_win ||
+			    tcp_mask->hdr.cksum ||
+			    tcp_mask->hdr.tcp_urp) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if ((tcp_mask->hdr.src_port != 0xFFFF &&
+			     tcp_mask->hdr.src_port != 0) ||
+			    (tcp_mask->hdr.dst_port != 0xFFFF &&
+			     tcp_mask->hdr.dst_port != 0)) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if (tcp_mask->hdr.src_port == 0xFFFF)
+				input_set |= I40E_INSET_SRC_PORT;
+			if (tcp_mask->hdr.dst_port == 0xFFFF)
+				input_set |= I40E_INSET_DST_PORT;
+		}
+	}
+
+	/* Check if the not void item is UDP. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
+		udp_spec = (const struct rte_flow_item_udp *)item->spec;
+		udp_mask = (const struct rte_flow_item_udp *)item->mask;
+
+		if (!udp_spec || !udp_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_UDP;
+		else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV6_UDP;
+
+		if (udp_spec) {
+			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
+				filter->input.flow.udp4_flow.src_port =
+					udp_spec->hdr.src_port;
+				filter->input.flow.udp4_flow.dst_port =
+					udp_spec->hdr.dst_port;
+			} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
+				filter->input.flow.udp6_flow.src_port =
+					udp_spec->hdr.src_port;
+				filter->input.flow.udp6_flow.dst_port =
+					udp_spec->hdr.dst_port;
+			}
+		}
+
+		if (udp_mask) {
+			if (udp_mask->hdr.dgram_len ||
+			    udp_mask->hdr.dgram_cksum) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if ((udp_mask->hdr.src_port != 0xFFFF &&
+			     udp_mask->hdr.src_port != 0) ||
+			    (udp_mask->hdr.dst_port != 0xFFFF &&
+			     udp_mask->hdr.dst_port != 0)) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if (udp_mask->hdr.src_port == 0xFFFF)
+				input_set |= I40E_INSET_SRC_PORT;
+			if (udp_mask->hdr.dst_port == 0xFFFF)
+				input_set |= I40E_INSET_DST_PORT;
+		}
+	}
+
+	/* Check if the not void item is SCTP. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
+		sctp_spec = (const struct rte_flow_item_sctp *)item->spec;
+		sctp_mask = (const struct rte_flow_item_sctp *)item->mask;
+
+		if (!sctp_spec || !sctp_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (l3 == RTE_FLOW_ITEM_TYPE_IPV4)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV4_SCTP;
+		else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6)
+			flow_type = RTE_ETH_FLOW_NONFRAG_IPV6_SCTP;
+
+		if (sctp_spec) {
+			if (l3 == RTE_FLOW_ITEM_TYPE_IPV4) {
+				filter->input.flow.sctp4_flow.src_port =
+					sctp_spec->hdr.src_port;
+				filter->input.flow.sctp4_flow.dst_port =
+					sctp_spec->hdr.dst_port;
+				filter->input.flow.sctp4_flow.verify_tag =
+					sctp_spec->hdr.tag;
+			} else if (l3 == RTE_FLOW_ITEM_TYPE_IPV6) {
+				filter->input.flow.sctp6_flow.src_port =
+					sctp_spec->hdr.src_port;
+				filter->input.flow.sctp6_flow.dst_port =
+					sctp_spec->hdr.dst_port;
+				filter->input.flow.sctp6_flow.verify_tag =
+					sctp_spec->hdr.tag;
+			}
+		}
+
+		if (sctp_mask) {
+			if (sctp_mask->hdr.cksum) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if ((sctp_mask->hdr.src_port != 0xFFFF &&
+			     sctp_mask->hdr.src_port != 0) ||
+			    (sctp_mask->hdr.dst_port != 0xFFFF &&
+			     sctp_mask->hdr.dst_port != 0) ||
+			    (sctp_mask->hdr.tag != 0xFFFFFFFF &&
+			     sctp_mask->hdr.tag != 0)) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+
+			if (sctp_mask->hdr.src_port == 0xFFFF)
+				input_set |= I40E_INSET_SRC_PORT;
+			if (sctp_mask->hdr.dst_port == 0xFFFF)
+				input_set |= I40E_INSET_DST_PORT;
+			if (sctp_mask->hdr.tag == 0xFFFFFFFF)
+				input_set |= I40E_INSET_SCTP_VT;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_TCP ||
+	    item->type == RTE_FLOW_ITEM_TYPE_UDP ||
+	    item->type == RTE_FLOW_ITEM_TYPE_SCTP) {
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_VF &&
+		    item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	/* Check if the next not void item is VF. */
+	if (item->type == RTE_FLOW_ITEM_TYPE_VF) {
+		vf_spec = (const struct rte_flow_item_vf *)item->spec;
+		filter->input.flow_ext.is_vf = 1;
+		filter->input.flow_ext.dst_id = vf_spec->id;
+	}
+
+	if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+		/* Check if the next not void item is END. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	pctype = i40e_flowtype_to_pctype(flow_type);
+	if (pctype == 0 || pctype > I40E_FILTER_PCTYPE_L2_PAYLOAD) {
+		PMD_DRV_LOG(ERR, "Not supported flow type (%u)",
+			    conf->flow_type);
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (input_set != i40e_get_default_input_set(pctype)) {
+		PMD_DRV_LOG(ERR, "Invalid input set");
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	filter->input.flow_type = flow_type;
+
+	/* parse action */
+	i = 0;
+
+	/* Check if the first not void action is QUEUE or DROP. */
+	ACTION_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE &&
+	    act->type != RTE_FLOW_ACTION_TYPE_DROP) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	act_q = (const struct rte_flow_action_queue *)act->conf;
+	filter->action.flex_off = 0;
+	if (act->type == RTE_FLOW_ACTION_TYPE_QUEUE)
+		filter->action.behavior = RTE_ETH_FDIR_ACCEPT;
+	else
+		filter->action.behavior = RTE_ETH_FDIR_REJECT;
+
+	filter->action.report_status = RTE_ETH_FDIR_REPORT_ID;
+	filter->action.rx_queue = act_q->index;
+
+	/* Check if the next not void item is MARK or END. */
+	i++;
+	ACTION_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_MARK &&
+	    act->type != RTE_FLOW_ACTION_TYPE_END) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	if (act->type == RTE_FLOW_ACTION_TYPE_MARK) {
+		mark_spec = (const struct rte_flow_action_mark *)act->conf;
+		filter->soft_id = mark_spec->id;
+
+		/* Check if the next not void item is END. */
+		i++;
+		ACTION_SKIP_VOID(filter, struct rte_eth_fdir_filter,
+				 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+		if (act->type != RTE_FLOW_ACTION_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+			return -EINVAL;
+		}
+	}
+
+	return i40e_parse_attr(attr, error);
+}
+
 static int
 i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 		   const struct rte_flow_attr *attr,
@@ -11014,6 +11545,7 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 		   struct rte_flow_error *error)
 {
 	struct rte_eth_ethertype_filter ethertype_filter;
+	struct rte_eth_fdir_filter fdir_filter;
 	struct rte_eth_mac_filter macvlan_filter;
 	struct rte_eth_tunnel_filter_conf tunnel_filter;
 	int ret;
@@ -11023,6 +11555,11 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 	if (!ret)
 		return 0;
 
+	ret = i40e_parse_fdir_filter(attr, pattern, actions,
+				     &fdir_filter, error);
+	if (!ret)
+		return 0;
+
 	ret = i40e_parse_macvlan_filter(attr, pattern, actions,
 					&macvlan_filter, error);
 	if (!ret)
-- 
2.5.5

^ permalink raw reply related

* [PATCH 16/24] net/i40e: add flow create function
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds flow create ops.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 93 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/i40e/i40e_ethdev.h |  3 ++
 drivers/net/i40e/i40e_fdir.c   |  2 +-
 3 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 12255fa..ac93489 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -512,6 +512,17 @@ static int i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 			      const struct rte_flow_item *pattern,
 			      const struct rte_flow_action *actions,
 			      struct rte_flow_error *error);
+static struct i40e_flow *i40e_flow_create(struct rte_eth_dev *dev,
+				   const struct rte_flow_attr *attr,
+				   const struct rte_flow_item *pattern,
+				   const struct rte_flow_action *actions,
+				   struct rte_flow_error *error);
+
+struct i40e_flow {
+	enum rte_filter_type filter_type;
+	void *rule;
+	struct i40e_vsi *vsi;
+};
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
 	{ RTE_PCI_DEVICE(I40E_INTEL_VENDOR_ID, I40E_DEV_ID_SFP_XL710) },
@@ -609,6 +620,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 
 static const struct rte_flow_ops i40e_flow_ops = {
 	.validate = i40e_flow_validate,
+	.create = (void *)i40e_flow_create,
 };
 
 /* store statistics names and its offset in stats structure */
@@ -11572,3 +11584,84 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 
 	return ret;
 }
+
+static struct i40e_flow *
+i40e_flow_create(struct rte_eth_dev *dev,
+		 const struct rte_flow_attr *attr,
+		 const struct rte_flow_item *pattern,
+		 const struct rte_flow_action *actions,
+		 struct rte_flow_error *error)
+{
+	struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+	struct rte_eth_ethertype_filter ethertype_filter;
+	struct rte_eth_fdir_filter fdir_filter;
+	struct rte_eth_mac_filter macvlan_filter;
+	struct rte_eth_tunnel_filter_conf tunnel_filter;
+	struct i40e_flow *flow = NULL;
+	int ret;
+
+	flow = rte_zmalloc("i40e_flow", sizeof(struct i40e_flow), 0);
+	if (!flow) {
+		PMD_DRV_LOG(ERR, "failed to allocate memory");
+		return flow;
+	}
+
+	memset(&ethertype_filter, 0, sizeof(struct rte_eth_ethertype_filter));
+	ret = cons_parse_ethertype_filter(attr, pattern, actions,
+					  &ethertype_filter, error);
+	if (!ret) {
+		ret = i40e_ethertype_filter_set(pf, &ethertype_filter, 1);
+		if (ret)
+			goto free;
+		flow->filter_type = RTE_ETH_FILTER_ETHERTYPE;
+		flow->rule = TAILQ_LAST(&pf->ethertype.ethertype_list,
+					i40e_ethertype_filter_list);
+		return flow;
+	}
+
+	memset(&fdir_filter, 0, sizeof(struct rte_eth_fdir_filter));
+	ret = i40e_parse_fdir_filter(attr, pattern, actions,
+				     &fdir_filter, error);
+	if (!ret) {
+		ret = i40e_add_del_fdir_filter(dev, &fdir_filter, 1);
+		if (ret)
+			goto free;
+		flow->filter_type = RTE_ETH_FILTER_FDIR;
+		flow->rule = TAILQ_LAST(&pf->fdir.fdir_list,
+					i40e_fdir_filter_list);
+		return flow;
+	}
+
+	memset(&macvlan_filter, 0, sizeof(struct rte_eth_mac_filter));
+	ret = i40e_parse_macvlan_filter(attr, pattern, actions,
+					&macvlan_filter, error);
+	if (!ret) {
+		struct i40e_vsi *vsi;
+
+		ret = i40e_vf_mac_filter_set(pf, &macvlan_filter, 1);
+		if (ret)
+			goto free;
+		flow->filter_type = RTE_ETH_FILTER_MACVLAN;
+		vsi = pf->vfs[macvlan_filter.dst_id].vsi;
+		flow->vsi = vsi;
+		flow->rule = TAILQ_LAST(&vsi->mac_list, i40e_mac_filter_list);
+		return flow;
+	}
+
+	memset(&tunnel_filter, 0, sizeof(struct rte_eth_tunnel_filter_conf));
+	ret = i40e_parse_tunnel_filter(attr, pattern, actions,
+				       &tunnel_filter, error);
+	if (!ret) {
+		ret = i40e_dev_tunnel_filter_set(pf, &tunnel_filter, 1);
+		if (ret)
+			goto free;
+		flow->filter_type = RTE_ETH_FILTER_TUNNEL;
+		flow->rule = TAILQ_LAST(&pf->tunnel.tunnel_list,
+					i40e_tunnel_filter_list);
+		return flow;
+	}
+
+free:
+	rte_free(flow);
+	return NULL;
+}
diff --git a/drivers/net/i40e/i40e_ethdev.h b/drivers/net/i40e/i40e_ethdev.h
index 35ac6d6..9b60651 100644
--- a/drivers/net/i40e/i40e_ethdev.h
+++ b/drivers/net/i40e/i40e_ethdev.h
@@ -684,6 +684,9 @@ int i40e_select_filter_input_set(struct i40e_hw *hw,
 				 struct rte_eth_input_set_conf *conf,
 				 enum rte_filter_type filter);
 void i40e_fdir_filter_restore(struct i40e_pf *pf);
+int i40e_add_del_fdir_filter(struct rte_eth_dev *dev,
+			     const struct rte_eth_fdir_filter *filter,
+			     bool add);
 int i40e_hash_filter_inset_select(struct i40e_hw *hw,
 			     struct rte_eth_input_set_conf *conf);
 int i40e_fdir_filter_inset_select(struct i40e_pf *pf,
diff --git a/drivers/net/i40e/i40e_fdir.c b/drivers/net/i40e/i40e_fdir.c
index e47a949..30fcd5c 100644
--- a/drivers/net/i40e/i40e_fdir.c
+++ b/drivers/net/i40e/i40e_fdir.c
@@ -1093,7 +1093,7 @@ i40e_sw_fdir_filter_del(struct i40e_pf *pf, struct i40e_fdir_filter *filter)
  * @filter: fdir filter entry
  * @add: 0 - delete, 1 - add
  */
-static int
+int
 i40e_add_del_fdir_filter(struct rte_eth_dev *dev,
 			    const struct rte_eth_fdir_filter *filter,
 			    bool add)
-- 
2.5.5

^ permalink raw reply related

* [PATCH 13/24] net/i40e: parse VXLAN filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

Check if the rule is a VXLAN rule, and get the VXLAN
info.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 349 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 349 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 18247c0..3bdef8e 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -497,6 +497,11 @@ static int i40e_parse_macvlan_filter(const struct rte_flow_attr *attr,
 				     const struct rte_flow_action *actions,
 				     struct rte_eth_mac_filter *filter,
 				     struct rte_flow_error *error);
+static int i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
+				    const struct rte_flow_item *pattern,
+				    const struct rte_flow_action *actions,
+				    struct rte_eth_tunnel_filter_conf *filter,
+				    struct rte_flow_error *error);
 static int i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 			      const struct rte_flow_attr *attr,
 			      const struct rte_flow_item *pattern,
@@ -10394,6 +10399,344 @@ i40e_parse_macvlan_filter(const struct rte_flow_attr *attr,
 	return i40e_parse_attr(attr, error);
 }
 
+/* Parse to get the action and attr info of a tunnle filter */
+static int
+i40e_parse_tunnel_act_attr(const struct rte_flow_attr *attr,
+			   const struct rte_flow_action *actions,
+			   struct rte_eth_tunnel_filter_conf *filter,
+			   struct rte_flow_error *error)
+{
+	const struct rte_flow_action *act;
+	const struct rte_flow_action_queue *act_q;
+	uint32_t i;
+
+	/* parse action */
+	i = 0;
+
+	/* Check if the first not void action is QUEUE. */
+	ACTION_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_QUEUE) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	act_q = (const struct rte_flow_action_queue *)act->conf;
+	filter->queue_id = act_q->index;
+
+	/* Check if the next not void item is END */
+	i++;
+	ACTION_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_END) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	return i40e_parse_attr(attr, error);
+}
+
+/**
+ * Parse the rule to see if it is a vxlan rule.
+ * And get the tunnel filter info BTW.
+ */
+static int
+i40e_parse_vxlan_tunnel_filter(const struct rte_flow_attr *attr,
+			       const struct rte_flow_item *pattern,
+			       const struct rte_flow_action *actions,
+			       struct rte_eth_tunnel_filter_conf *filter,
+			       struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_item_eth *o_eth_spec = NULL;
+	const struct rte_flow_item_eth *o_eth_mask = NULL;
+	const struct rte_flow_item_vxlan *vxlan_spec = NULL;
+	const struct rte_flow_item_vxlan *vxlan_mask = NULL;
+	const struct rte_flow_item_eth *i_eth_spec, *i_eth_mask;
+	const struct rte_flow_item_vlan *vlan_spec = NULL;
+	const struct rte_flow_item_vlan *vlan_mask = NULL;
+	struct ether_addr macaddr_unmasked = {
+		.addr_bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
+	};
+	struct ether_addr macaddr_masked = {
+		.addr_bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+	};
+	bool is_vni_masked = 0;
+	uint32_t i;
+
+	/* parse pattern */
+	i = 0;
+
+	/* The first not void item should be ETH or IP or UDP or VXLAN */
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV6 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_UDP &&
+	    item->type != RTE_FLOW_ITEM_TYPE_VXLAN) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+		o_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		o_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if ((!o_eth_spec && o_eth_mask) ||
+		    (o_eth_spec && !o_eth_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (o_eth_spec)
+			rte_memcpy(&filter->outer_mac, &o_eth_spec->dst,
+				   ETHER_ADDR_LEN);
+
+		if (o_eth_mask) {
+			/**
+			 * DST MAC address shouldn't be masked.
+			 * SRC MAC address should be masked.
+			 * Ethertype should be masked.
+			 */
+			if (!is_same_ether_addr(&o_eth_mask->dst,
+						&macaddr_unmasked) ||
+			    !is_same_ether_addr(&o_eth_mask->src,
+						&macaddr_masked) ||
+			    o_eth_mask->type) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
+	    item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
+		/**
+		 * If the item is IP, the content should be NULL.
+		 * Only used to describe the protocol stack.
+		 */
+		if (item->spec || item->mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is UDP */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_UDP) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_UDP) {
+		/**
+		 * If the item is UDP, the content should be NULL
+		 * Only used to describe the protocol stack.
+		 */
+		if (item->spec || item->mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is VXLAN */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	}
+
+	if (item->type != RTE_FLOW_ITEM_TYPE_VXLAN) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	vxlan_spec = (const struct rte_flow_item_vxlan *)item->spec;
+	vxlan_mask = (const struct rte_flow_item_vxlan *)item->mask;
+
+	/**
+	 * Check if VXLAN item is used to describe the protocol stack.
+	 * If yes, both vxlan_spec and vxlan_mask should be NULL.
+	 * If no, either vxlan_spec or vxlan_mask shouldn't be NULL.
+	 */
+	if ((!vxlan_spec && vxlan_mask) ||
+	    (vxlan_spec && !vxlan_mask)) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Check if VNI is masked. */
+	if (vxlan_mask) {
+		for (uint32_t j = 0; j < RTE_DIM(vxlan_mask->vni); j++) {
+			if (vxlan_mask->vni[j] == 0xFF) {
+				if (j > 0 &&
+				    (vxlan_mask->vni[j] !=
+				     vxlan_mask->vni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_vni_masked = 0;
+			} else if (vxlan_mask->vni[j] == 0) {
+				if (j > 0 &&
+				    (vxlan_mask->vni[j] !=
+				     vxlan_mask->vni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_vni_masked = 1;
+			} else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* Check if the next not void item is ETH. */
+	i++;
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+		i_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		i_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if (!i_eth_spec || !i_eth_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/**
+		 * DST address of inner MAC shouldn't be masked.
+		 * SRC address of Inner MAC should be masked.
+		 */
+		if (!is_same_ether_addr(&i_eth_mask->dst, &macaddr_unmasked) ||
+		    !is_same_ether_addr(&i_eth_mask->src, &macaddr_masked) ||
+		    i_eth_mask->type) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		rte_memcpy(&filter->inner_mac, &i_eth_spec->dst,
+			   ETHER_ADDR_LEN);
+
+		/* Check if the next not void item is VLAN or END. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_VLAN &&
+		    item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+		vlan_spec = (const struct rte_flow_item_vlan *)item->spec;
+		vlan_mask = (const struct rte_flow_item_vlan *)item->mask;
+
+		if (!(vlan_spec && vlan_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is END. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (vlan_spec && vlan_mask &&
+	    (vlan_mask->tci == rte_cpu_to_be_16(0x0FFF))) {
+		filter->inner_vlan = rte_be_to_cpu_16(vlan_spec->tci) & 0xFFF;
+		if (vxlan_spec && vxlan_mask && !is_vni_masked) {
+			rte_memcpy(&filter->tenant_id, vxlan_spec->vni,
+				   RTE_DIM(vxlan_spec->vni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN_TENID;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else if (!vxlan_spec && !vxlan_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else if ((!vlan_spec && !vlan_mask) ||
+		   (vlan_spec && vlan_mask && vlan_mask->tci == 0x0)) {
+		if (vxlan_spec && vxlan_mask && !is_vni_masked) {
+			rte_memcpy(&filter->tenant_id, vxlan_spec->vni,
+				   RTE_DIM(vxlan_spec->vni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_TENID;
+			else
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_OMAC_TENID_IMAC;
+		} else if (!vxlan_spec && !vxlan_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type = ETH_TUNNEL_FILTER_IMAC;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	filter->tunnel_type = RTE_TUNNEL_TYPE_VXLAN;
+
+	return i40e_parse_tunnel_act_attr(attr, actions, filter, error);
+}
+
+static int
+i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
+			 const struct rte_flow_item *pattern,
+			 const struct rte_flow_action *actions,
+			 struct rte_eth_tunnel_filter_conf *rule,
+			 struct rte_flow_error *error)
+{
+	int ret;
+
+	ret = i40e_parse_vxlan_tunnel_filter(attr, pattern,
+					     actions, rule, error);
+	if (!ret)
+		return 0;
+
+	return ret;
+}
+
 static int
 i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 		   const struct rte_flow_attr *attr,
@@ -10403,6 +10746,7 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 {
 	struct rte_eth_ethertype_filter ethertype_filter;
 	struct rte_eth_mac_filter macvlan_filter;
+	struct rte_eth_tunnel_filter_conf tunnel_filter;
 	int ret;
 
 	ret = cons_parse_ethertype_filter(attr, pattern, actions,
@@ -10415,5 +10759,10 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 	if (!ret)
 		return 0;
 
+	ret = i40e_parse_tunnel_filter(attr, pattern, actions,
+				       &tunnel_filter, error);
+	if (!ret)
+		return 0;
+
 	return ret;
 }
-- 
2.5.5

^ permalink raw reply related

* [PATCH 14/24] net/i40e: parse NVGRE filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

Check if the rule is a NVGRE rule, and get the NVGRE
info.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 269 +++++++++++++++++++++++++++++++++++++++++
 lib/librte_ether/rte_flow.h    |  23 ++++
 2 files changed, 292 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 3bdef8e..1ffafa0 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -10720,6 +10720,270 @@ i40e_parse_vxlan_tunnel_filter(const struct rte_flow_attr *attr,
 	return i40e_parse_tunnel_act_attr(attr, actions, filter, error);
 }
 
+/* whether it is NVGRE tunnel rule */
+static int
+i40e_parse_nvgre_tunnel_filter(const struct rte_flow_attr *attr,
+			       const struct rte_flow_item *pattern,
+			       const struct rte_flow_action *actions,
+			       struct rte_eth_tunnel_filter_conf *filter,
+			       struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_item_eth *o_eth_spec = NULL;
+	const struct rte_flow_item_eth *o_eth_mask = NULL;
+	const struct rte_flow_item_nvgre *nvgre_spec = NULL;
+	const struct rte_flow_item_nvgre *nvgre_mask = NULL;
+	const struct rte_flow_item_eth *i_eth_spec, *i_eth_mask;
+	const struct rte_flow_item_vlan *vlan_spec = NULL;
+	const struct rte_flow_item_vlan *vlan_mask = NULL;
+	struct ether_addr macaddr_unmasked = {
+		.addr_bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
+	};
+	struct ether_addr macaddr_masked = {
+		.addr_bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+	};
+	bool is_tni_masked = 0;
+	uint32_t i;
+
+	/* parse pattern */
+	i = 0;
+
+	/* The first not void item should be ETH or IP or UDP or VXLAN. */
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_IPV6 &&
+	    item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_ETH) {
+		o_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		o_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if ((!o_eth_spec && o_eth_mask) ||
+		    (o_eth_spec && !o_eth_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		if (o_eth_spec)
+			rte_memcpy(&filter->outer_mac, &o_eth_spec->dst,
+				   ETHER_ADDR_LEN);
+
+		if (o_eth_mask) {
+			/**
+			 * DST MAC address shouldn't be masked.
+			 * SRC MAC address should be masked.
+			 * Ethertype should be masked.
+			 */
+			if (!is_same_ether_addr(&o_eth_mask->dst,
+						&macaddr_unmasked) ||
+			    !is_same_ether_addr(&o_eth_mask->src,
+						&macaddr_masked) ||
+			    o_eth_mask->type) {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 &&
+		    item->type != RTE_FLOW_ITEM_TYPE_IPV6) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
+	    item->type == RTE_FLOW_ITEM_TYPE_IPV6) {
+		/**
+		 * If the item is IP, the content should be NULL.
+		 * Only used to describe the protocol stack.
+		 */
+		if (item->spec || item->mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is UDP. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type != RTE_FLOW_ITEM_TYPE_NVGRE) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	nvgre_spec = (const struct rte_flow_item_nvgre *)item->spec;
+	nvgre_mask = (const struct rte_flow_item_nvgre *)item->mask;
+
+	/**
+	 * Check if NVGRE item is used to describe the protocol stack.
+	 * If yes, both nvgre_spec and nvgre_mask should be NULL.
+	 * If no, either nvgre_spec or nvgre_mask shouldn't be NULL.
+	 */
+	if ((!nvgre_spec && nvgre_mask) ||
+	    (nvgre_spec && !nvgre_mask)) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Check if TNI isn't masked */
+	if (nvgre_mask) {
+		for (uint32_t j = 0; j < RTE_DIM(nvgre_mask->tni); j++) {
+			if (nvgre_mask->tni[j] == 0xFF) {
+				if (j > 0 &&
+				    (nvgre_mask->tni[j] !=
+				     nvgre_mask->tni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_tni_masked = 0;
+			} else if (nvgre_mask->tni[j] == 0) {
+				if (j > 0 &&
+				    (nvgre_mask->tni[j] !=
+				     nvgre_mask->tni[j - 1])) {
+					error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+					return -EINVAL;
+				}
+				is_tni_masked = 1;
+			} else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		}
+	}
+
+	/* check if the next not void item is ETH*/
+	i++;
+	PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	if (item->type ==  RTE_FLOW_ITEM_TYPE_ETH) {
+		i_eth_spec = (const struct rte_flow_item_eth *)item->spec;
+		i_eth_mask = (const struct rte_flow_item_eth *)item->mask;
+
+		if (!i_eth_spec || !i_eth_mask) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		rte_memcpy(&filter->inner_mac, &i_eth_spec->dst,
+			   ETHER_ADDR_LEN);
+
+		/**
+		 * DST address of inner MAC shouldn't be masked.
+		 * SRC address of Inner MAC should be masked.
+		 */
+		if (!is_same_ether_addr(&i_eth_mask->dst, &macaddr_unmasked) ||
+		    !is_same_ether_addr(&i_eth_mask->src, &macaddr_masked) ||
+		    i_eth_mask->type) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* Check if the next not void item is VLAN or END. */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_VLAN &&
+		    item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+		vlan_spec = (const struct rte_flow_item_vlan *)item->spec;
+		vlan_mask = (const struct rte_flow_item_vlan *)item->mask;
+
+		if (!(vlan_spec && vlan_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* check if the next not void item is END */
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_tunnel_filter_conf,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+		if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	}
+
+	if (vlan_spec && vlan_mask &&
+	    (vlan_mask->tci == rte_cpu_to_be_16(0x0FFF))) {
+		filter->inner_vlan = rte_be_to_cpu_16(vlan_spec->tci) & 0xFFF;
+		if (nvgre_spec && nvgre_mask && !is_tni_masked) {
+			rte_memcpy(&filter->tenant_id, nvgre_spec->tni,
+				   RTE_DIM(nvgre_spec->tni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN_TENID;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else if (!nvgre_spec && !nvgre_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_IVLAN;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else if ((!vlan_spec && !vlan_mask) ||
+		   (vlan_spec && vlan_mask && vlan_mask->tci == 0x0)) {
+		if (nvgre_spec && nvgre_mask && !is_tni_masked) {
+			rte_memcpy(&filter->tenant_id, nvgre_spec->tni,
+				   RTE_DIM(nvgre_spec->tni));
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_IMAC_TENID;
+			else
+				filter->filter_type =
+					RTE_TUNNEL_FILTER_OMAC_TENID_IMAC;
+		} else if (!nvgre_spec && !nvgre_mask) {
+			if (!o_eth_spec && !o_eth_mask)
+				filter->filter_type = ETH_TUNNEL_FILTER_IMAC;
+			else {
+				error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+				return -EINVAL;
+			}
+		} else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+	} else {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	filter->tunnel_type = RTE_TUNNEL_TYPE_NVGRE;
+
+	return i40e_parse_tunnel_act_attr(attr, actions, filter, error);
+}
+
 static int
 i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 			 const struct rte_flow_item *pattern,
@@ -10734,6 +10998,11 @@ i40e_parse_tunnel_filter(const struct rte_flow_attr *attr,
 	if (!ret)
 		return 0;
 
+	ret = i40e_parse_nvgre_tunnel_filter(attr, pattern,
+					     actions, rule, error);
+	if (!ret)
+		return 0;
+
 	return ret;
 }
 
diff --git a/lib/librte_ether/rte_flow.h b/lib/librte_ether/rte_flow.h
index 211f307..6bdbba1 100644
--- a/lib/librte_ether/rte_flow.h
+++ b/lib/librte_ether/rte_flow.h
@@ -270,6 +270,13 @@ enum rte_flow_item_type {
 	 * See struct rte_flow_item_vxlan.
 	 */
 	RTE_FLOW_ITEM_TYPE_VXLAN,
+
+	/**
+	 * Matches a NVGRE header.
+	 *
+	 * See struct rte_flow_item_nvgre.
+	 */
+	RTE_FLOW_ITEM_TYPE_NVGRE,
 };
 
 /**
@@ -461,6 +468,22 @@ struct rte_flow_item_vxlan {
 };
 
 /**
+ * RTE_FLOW_ITEM_TYPE_NVGRE.
+ *
+ * Matches a NVGRE header.
+ */
+struct rte_flow_item_nvgre {
+	uint32_t flags0:1; /**< 0 */
+	uint32_t rsvd1:1; /**< 1 bit not defined */
+	uint32_t flags1:2; /**< 2 bits, 1 0 */
+	uint32_t rsvd0:9; /**< Reserved0 */
+	uint32_t ver:3; /**< version */
+	uint32_t protocol:16; /**< protocol type, 0x6558 */
+	uint8_t tni[3]; /**< tenant network ID or virtual subnet ID */
+	uint8_t flow_id; /**< flow ID or Reserved */
+};
+
+/**
  * Matching pattern item definition.
  *
  * A pattern is formed by stacking items starting from the lowest protocol
-- 
2.5.5

^ permalink raw reply related

* [PATCH 12/24] net/i40e: parse macvlan filter
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

Check if the rule is a macvlan rule, and get the macvlan
info BTW.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 160 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 160 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index c1623c4..18247c0 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -53,6 +53,7 @@
 #include <rte_tailq.h>
 #include <rte_hash_crc.h>
 #include <rte_flow_driver.h>
+#include <rte_flow.h>
 
 #include "i40e_logs.h"
 #include "base/i40e_prototype.h"
@@ -491,6 +492,11 @@ static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
 static void i40e_rss_hash_restore(struct i40e_pf *pf);
 static void i40e_filter_restore(struct i40e_pf *pf);
 
+static int i40e_parse_macvlan_filter(const struct rte_flow_attr *attr,
+				     const struct rte_flow_item *pattern,
+				     const struct rte_flow_action *actions,
+				     struct rte_eth_mac_filter *filter,
+				     struct rte_flow_error *error);
 static int i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 			      const struct rte_flow_attr *attr,
 			      const struct rte_flow_item *pattern,
@@ -10241,6 +10247,154 @@ i40e_filter_restore(struct i40e_pf *pf)
 }
 
 static int
+i40e_parse_attr(const struct rte_flow_attr *attr,
+		struct rte_flow_error *error)
+{
+	/* Must be input direction */
+	if (!attr->ingress) {
+		error->type = RTE_FLOW_ERROR_TYPE_ATTR_INGRESS;
+		return -EINVAL;
+	}
+
+	/* Not supported */
+	if (attr->egress) {
+		error->type = RTE_FLOW_ERROR_TYPE_ATTR_EGRESS;
+		return -EINVAL;
+	}
+
+	/* Not supported */
+	if (attr->priority) {
+		error->type = RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * Parse the rule to see if it is a macvlan rule.
+ * And get the macvlan filter info BTW.
+ */
+static int
+i40e_parse_macvlan_filter(const struct rte_flow_attr *attr,
+			  const struct rte_flow_item *pattern,
+			  const struct rte_flow_action *actions,
+			  struct rte_eth_mac_filter *filter,
+			  struct rte_flow_error *error)
+{
+	const struct rte_flow_item *item;
+	const struct rte_flow_action *act;
+	const struct rte_flow_item_eth *eth_spec, *eth_mask;
+	const struct rte_flow_item_vlan *vlan_spec, *vlan_mask;
+	const struct rte_flow_action_vf *act_vf;
+	struct ether_addr macaddr_unmasked = {
+		.addr_bytes = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
+	};
+	struct ether_addr macaddr_masked = {
+		.addr_bytes = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
+	};
+	uint32_t i;
+
+	/* Parse pattern */
+	i = 0;
+
+	/* the first item not void item should be ETH */
+	PATTERN_SKIP_VOID(filter, struct rte_eth_mac_filter,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	if (item->type != RTE_FLOW_ITEM_TYPE_ETH) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	eth_spec = (const struct rte_flow_item_eth *)item->spec;
+	eth_mask = (const struct rte_flow_item_eth *)item->mask;
+	if (!eth_spec || !eth_mask) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/**
+	 * SRC MAC address should be masked.
+	 * DST MAC address shouldn't be masked.
+	 */
+	if (!is_same_ether_addr(&eth_mask->src, &macaddr_masked) ||
+	    !is_same_ether_addr(&eth_mask->dst, &macaddr_unmasked)) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Ethertype should be masked. */
+	if (eth_mask->type) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	rte_memcpy(&filter->mac_addr, &eth_spec->dst, ETHER_ADDR_LEN);
+	filter->filter_type = RTE_MAC_PERFECT_MATCH;
+
+	i++;
+	PATTERN_SKIP_VOID(filter, struct rte_eth_mac_filter,
+			  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+
+	if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) {
+		vlan_spec = (const struct rte_flow_item_vlan *)item->spec;
+		vlan_mask = (const struct rte_flow_item_vlan *)item->mask;
+
+		if (!(vlan_spec && vlan_mask)) {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		/* PRI and CFI should be masked. */
+		if (vlan_mask->tci == rte_cpu_to_be_16(0x0FFF))
+			filter->filter_type = RTE_MACVLAN_PERFECT_MATCH;
+		else if (vlan_mask->tci == 0x0)
+			filter->filter_type = RTE_MAC_PERFECT_MATCH;
+		else {
+			error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+			return -EINVAL;
+		}
+
+		i++;
+		PATTERN_SKIP_VOID(filter, struct rte_eth_mac_filter,
+				  RTE_FLOW_ERROR_TYPE_ITEM_NUM);
+	}
+
+	/* Check if the next not void item is END. */
+	if (item->type != RTE_FLOW_ITEM_TYPE_END) {
+		error->type = RTE_FLOW_ERROR_TYPE_ITEM;
+		return -EINVAL;
+	}
+
+	/* Parse action */
+	i = 0;
+
+	/* Check if the next not void item is VF. */
+	ACTION_SKIP_VOID(filter, struct rte_eth_mac_filter,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_VF) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	filter->is_vf = 1;
+
+	act_vf = (const struct rte_flow_action_vf *)act->conf;
+	filter->dst_id = act_vf->id;
+
+	/* Check if the next not void item is END. */
+	i++;
+	ACTION_SKIP_VOID(filter, struct rte_eth_mac_filter,
+			 RTE_FLOW_ERROR_TYPE_ACTION_NUM);
+	if (act->type != RTE_FLOW_ACTION_TYPE_END) {
+		error->type = RTE_FLOW_ERROR_TYPE_ACTION;
+		return -EINVAL;
+	}
+
+	return i40e_parse_attr(attr, error);
+}
+
+static int
 i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 		   const struct rte_flow_attr *attr,
 		   const struct rte_flow_item *pattern,
@@ -10248,6 +10402,7 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 		   struct rte_flow_error *error)
 {
 	struct rte_eth_ethertype_filter ethertype_filter;
+	struct rte_eth_mac_filter macvlan_filter;
 	int ret;
 
 	ret = cons_parse_ethertype_filter(attr, pattern, actions,
@@ -10255,5 +10410,10 @@ i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
 	if (!ret)
 		return 0;
 
+	ret = i40e_parse_macvlan_filter(attr, pattern, actions,
+					&macvlan_filter, error);
+	if (!ret)
+		return 0;
+
 	return ret;
 }
-- 
2.5.5

^ permalink raw reply related

* [PATCH 11/24] net/i40e: add flow validate function
From: Beilei Xing @ 2016-12-02 11:53 UTC (permalink / raw)
  To: jingjing.wu, helin.zhang; +Cc: dev, wenzhuo.lu
In-Reply-To: <1480679625-4157-1-git-send-email-beilei.xing@intel.com>

This patch adds handling RTE_ETH_FILTER_GENERIC filter type in
.filter_ctrl function, and result in a pointer to i40e_flow_ops.
This patch also adds flow validate ops.

Signed-off-by: Beilei Xing <beilei.xing@intel.com>
---
 drivers/net/i40e/i40e_ethdev.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/drivers/net/i40e/i40e_ethdev.c b/drivers/net/i40e/i40e_ethdev.c
index 997e2fe..c1623c4 100644
--- a/drivers/net/i40e/i40e_ethdev.c
+++ b/drivers/net/i40e/i40e_ethdev.c
@@ -52,6 +52,7 @@
 #include <rte_eth_ctrl.h>
 #include <rte_tailq.h>
 #include <rte_hash_crc.h>
+#include <rte_flow_driver.h>
 
 #include "i40e_logs.h"
 #include "base/i40e_prototype.h"
@@ -490,6 +491,12 @@ static void i40e_tunnel_filter_restore(struct i40e_pf *pf);
 static void i40e_rss_hash_restore(struct i40e_pf *pf);
 static void i40e_filter_restore(struct i40e_pf *pf);
 
+static int i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
+			      const struct rte_flow_attr *attr,
+			      const struct rte_flow_item *pattern,
+			      const struct rte_flow_action *actions,
+			      struct rte_flow_error *error);
+
 static const struct rte_pci_id pci_id_i40e_map[] = {
 	{ RTE_PCI_DEVICE(I40E_INTEL_VENDOR_ID, I40E_DEV_ID_SFP_XL710) },
 	{ RTE_PCI_DEVICE(I40E_INTEL_VENDOR_ID, I40E_DEV_ID_QEMU) },
@@ -584,6 +591,10 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
 	.mtu_set                      = i40e_dev_mtu_set,
 };
 
+static const struct rte_flow_ops i40e_flow_ops = {
+	.validate = i40e_flow_validate,
+};
+
 /* store statistics names and its offset in stats structure */
 struct rte_i40e_xstats_name_off {
 	char name[RTE_ETH_XSTATS_NAME_SIZE];
@@ -8505,6 +8516,11 @@ i40e_dev_filter_ctrl(struct rte_eth_dev *dev,
 	case RTE_ETH_FILTER_FDIR:
 		ret = i40e_fdir_ctrl_func(dev, filter_op, arg);
 		break;
+	case RTE_ETH_FILTER_GENERIC:
+		if (filter_op != RTE_ETH_FILTER_GET)
+			return -EINVAL;
+		*(const void **)arg = &i40e_flow_ops;
+		break;
 	default:
 		PMD_DRV_LOG(WARNING, "Filter type (%d) not supported",
 							filter_type);
@@ -10223,3 +10239,21 @@ i40e_filter_restore(struct i40e_pf *pf)
 	i40e_fdir_filter_restore(pf);
 	i40e_rss_hash_restore(pf);
 }
+
+static int
+i40e_flow_validate(__rte_unused struct rte_eth_dev *dev,
+		   const struct rte_flow_attr *attr,
+		   const struct rte_flow_item *pattern,
+		   const struct rte_flow_action *actions,
+		   struct rte_flow_error *error)
+{
+	struct rte_eth_ethertype_filter ethertype_filter;
+	int ret;
+
+	ret = cons_parse_ethertype_filter(attr, pattern, actions,
+					  &ethertype_filter, error);
+	if (!ret)
+		return 0;
+
+	return ret;
+}
-- 
2.5.5

^ permalink raw reply related


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