Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next 4/5] net: Add TLS TX offload features
From: Ilya Lesokhin @ 2017-09-14 10:46 UTC (permalink / raw)
  To: netdev, davem; +Cc: davejwatson, tom, hannes, borisp, ilyal, aviadye, liranl
In-Reply-To: <1505385988-94522-1-git-send-email-ilyal@mellanox.com>

This patch adds a netdev feature to configure TLS TX offloads.

Signed-off-by: Boris Pismenny <borisp@mellanox.com>
Signed-off-by: Ilya Lesokhin <ilyal@mellanox.com>
Signed-off-by: Aviad Yehezkel <aviadye@mellanox.com>
---
 include/linux/netdev_features.h | 2 ++
 net/core/ethtool.c              | 1 +
 2 files changed, 3 insertions(+)

diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index dc8b489..ed0648a 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -76,6 +76,7 @@ enum {
 	NETIF_F_HW_ESP_BIT,		/* Hardware ESP transformation offload */
 	NETIF_F_HW_ESP_TX_CSUM_BIT,	/* ESP with TX checksum offload */
 	NETIF_F_RX_UDP_TUNNEL_PORT_BIT, /* Offload of RX port for UDP tunnels */
+	NETIF_F_HW_TLS_TX_BIT,		/* Hardware TLS TX offload */
 
 	/*
 	 * Add your fresh new feature above and remember to update
@@ -140,6 +141,7 @@ enum {
 #define NETIF_F_HW_ESP		__NETIF_F(HW_ESP)
 #define NETIF_F_HW_ESP_TX_CSUM	__NETIF_F(HW_ESP_TX_CSUM)
 #define	NETIF_F_RX_UDP_TUNNEL_PORT  __NETIF_F(RX_UDP_TUNNEL_PORT)
+#define NETIF_F_HW_TLS_TX	__NETIF_F(HW_TLS_TX)
 
 #define for_each_netdev_feature(mask_addr, bit)	\
 	for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT)
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 6a582ae..2ae1fc4 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -106,6 +106,7 @@ int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
 	[NETIF_F_HW_ESP_BIT] =		 "esp-hw-offload",
 	[NETIF_F_HW_ESP_TX_CSUM_BIT] =	 "esp-tx-csum-hw-offload",
 	[NETIF_F_RX_UDP_TUNNEL_PORT_BIT] =	 "rx-udp_tunnel-port-offload",
+	[NETIF_F_HW_TLS_TX_BIT] =	 "tls-hw-tx-offload",
 };
 
 static const char
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH net-next 5/5] tls: Add generic NIC offload infrastructure.
From: Ilya Lesokhin @ 2017-09-14 10:46 UTC (permalink / raw)
  To: netdev, davem; +Cc: davejwatson, tom, hannes, borisp, ilyal, aviadye, liranl
In-Reply-To: <1505385988-94522-1-git-send-email-ilyal@mellanox.com>

This patch adds a generic infrastructure to offload TLS crypto to a
network devices. It enables the kernel TLS socket to skip encryption and
authentication operations on the transmit side of the data path. Leaving
those computationally expensive operations to the NIC.

The NIC offload infrastructure builds TLS records and pushes them to the
TCP layer just like the SW KTLS implementation and using the same API.
TCP segmentation is mostly unaffected. Currently the only exception is
that we prevent mixed SKBs where only part of the payload requires
offload. In the future we are likely to add a similar restriction
following a change cipher spec record.

The notable differences between SW KTLS and NIC offloaded TLS
implementations are as follows:
1. The offloaded implementation builds "plaintext TLS record", those
records contain plaintext instead of ciphertext and place holder bytes
instead of authentication tags.
2. The offloaded implementation maintains a mapping from TCP sequence
number to TLS records. Thus given a TCP SKB sent from a NIC offloaded
 TLS socket, we can use the tls NIC offload infrastructure to obtain
enough context to encrypt the payload of the SKB.
A TLS record is released when the last byte of the record is ack'ed,
this is done through the new icsk_clean_acked callback.

The infrastructure should be extendable to support various NIC offload
implementations.  However it is currently written with the
implementation below in mind:
The NIC assumes that packets from each offloaded stream are sent as
plaintext and in-order. It keeps track of the TLS records in the TCP
stream. When a packet marked for offload is transmitted, the NIC
encrypts the payload in-place and puts authentication tags in the
relevant place holders.

The responsibility for handling out-of-order packets (i.e. TCP
retransmission, qdisc drops) falls on the netdev driver.

The netdev driver keeps track of the expected TCP SN from the NIC's
perspective.  If the next packet to transmit matches the expected TCP
SN, the driver advances the expected TCP SN, and transmits the packet
with TLS offload indication.

If the next packet to transmit does not match the expected TCP SN. The
driver calls the TLS layer to obtain the TLS record that includes the
TCP of the packet for transmission. Using this TLS record, the driver
posts a work entry on the transmit queue to reconstruct the NIC TLS
state required for the offload of the out-of-order packet. It updates
the expected TCP SN accordingly and transmit the now in-order packet.
The same queue is used for packet transmission and TLS context
reconstruction to avoid the need for flushing the transmit queue before
issuing the context reconstruction request.

Signed-off-by: Boris Pismenny <borisp@mellanox.com>
Signed-off-by: Ilya Lesokhin <ilyal@mellanox.com>
Signed-off-by: Aviad Yehezkel <aviadye@mellanox.com>
---
 include/net/tls.h    |  41 +++-
 net/tls/Kconfig      |   9 +
 net/tls/Makefile     |   3 +
 net/tls/tls_device.c | 673 +++++++++++++++++++++++++++++++++++++++++++++++++++
 net/tls/tls_main.c   |  63 +++--
 5 files changed, 771 insertions(+), 18 deletions(-)
 create mode 100644 net/tls/tls_device.c

diff --git a/include/net/tls.h b/include/net/tls.h
index b89d397..1f83c8e 100644
--- a/include/net/tls.h
+++ b/include/net/tls.h
@@ -71,6 +71,24 @@ struct tls_sw_context {
 	struct scatterlist sg_aead_out[2];
 };
 
+struct tls_record_info {
+	struct list_head list;
+	u32 end_seq;
+	int len;
+	int num_frags;
+	skb_frag_t frags[MAX_SKB_FRAGS];
+};
+
+struct tls_offload_context {
+	struct list_head records_list;
+	struct scatterlist sg_tx_data[MAX_SKB_FRAGS];
+	void (*sk_destruct)(struct sock *sk);
+	struct tls_record_info *open_record;
+	struct tls_record_info *retransmit_hint;
+	u32 expected_seq;
+	spinlock_t lock;	/* protects records list */
+};
+
 enum {
 	TLS_PENDING_CLOSED_RECORD
 };
@@ -81,6 +99,9 @@ struct tls_context {
 		struct tls12_crypto_info_aes_gcm_128 crypto_send_aes_gcm_128;
 	};
 
+	struct list_head gclist;
+	struct sock *sk;
+	struct net_device *netdev;
 	void *priv_ctx;
 
 	u16 prepend_size;
@@ -123,9 +144,18 @@ int tls_sw_sendpage(struct sock *sk, struct page *page,
 		    int offset, size_t size, int flags);
 void tls_sw_close(struct sock *sk, long timeout);
 
-void tls_sk_destruct(struct sock *sk, struct tls_context *ctx);
-void tls_icsk_clean_acked(struct sock *sk);
+void tls_clear_device_offload(struct sock *sk, struct tls_context *ctx);
+int tls_set_device_offload(struct sock *sk, struct tls_context *ctx);
+int tls_device_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
+int tls_device_sendpage(struct sock *sk, struct page *page,
+			int offset, size_t size, int flags);
+void tls_device_sk_destruct(struct sock *sk);
+void tls_device_cleanup(void);
 
+struct tls_record_info *tls_get_record(struct tls_offload_context *context,
+				       u32 seq);
+
+void tls_sk_destruct(struct sock *sk, struct tls_context *ctx);
 int tls_push_sg(struct sock *sk, struct tls_context *ctx,
 		struct scatterlist *sg, u16 first_offset,
 		int flags);
@@ -162,6 +192,13 @@ static inline bool tls_is_pending_open_record(struct tls_context *tls_ctx)
 	return tls_ctx->pending_open_record_frags;
 }
 
+static inline bool tls_is_sk_tx_device_offloaded(struct sock *sk)
+{
+	/* matches smp_store_release in tls_set_device_offload */
+	return	smp_load_acquire(&sk->sk_destruct) ==
+			&tls_device_sk_destruct;
+}
+
 static inline void tls_err_abort(struct sock *sk)
 {
 	sk->sk_err = -EBADMSG;
diff --git a/net/tls/Kconfig b/net/tls/Kconfig
index eb58303..1a4ea55c 100644
--- a/net/tls/Kconfig
+++ b/net/tls/Kconfig
@@ -13,3 +13,12 @@ config TLS
 	encryption handling of the TLS protocol to be done in-kernel.
 
 	If unsure, say N.
+
+config TLS_DEVICE
+	bool "Transport Layer Security HW offload"
+	depends on TLS
+	default n
+	---help---
+	Enable kernel support for HW offload of the TLS protocol.
+
+	If unsure, say N.
diff --git a/net/tls/Makefile b/net/tls/Makefile
index a930fd1..9de5055 100644
--- a/net/tls/Makefile
+++ b/net/tls/Makefile
@@ -5,3 +5,6 @@
 obj-$(CONFIG_TLS) += tls.o
 
 tls-y := tls_main.o tls_sw.o
+
+tls-$(CONFIG_TLS_DEVICE) += tls_device.o
+
diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c
new file mode 100644
index 0000000..94a25c2
--- /dev/null
+++ b/net/tls/tls_device.c
@@ -0,0 +1,673 @@
+/* Copyright (c) 2016-2017, Mellanox Technologies All rights reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ *      - Neither the name of the Mellanox Technologies nor the
+ *        names of its contributors may be used to endorse or promote
+ *        products derived from this software without specific prior written
+ *        permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE
+ */
+
+#include <linux/module.h>
+#include <net/tcp.h>
+#include <net/inet_common.h>
+#include <linux/highmem.h>
+#include <linux/netdevice.h>
+
+#include <net/tls.h>
+
+static void tls_device_gc_task(struct work_struct *work);
+
+static DECLARE_WORK(tls_device_gc_work, tls_device_gc_task);
+static LIST_HEAD(tls_device_gc_list);
+static DEFINE_SPINLOCK(tls_device_gc_lock);
+
+static void tls_device_gc_task(struct work_struct *work)
+{
+	struct tls_context *ctx, *tmp;
+	struct list_head gc_list;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tls_device_gc_lock, flags);
+	INIT_LIST_HEAD(&gc_list);
+	list_splice_init(&tls_device_gc_list, &gc_list);
+	spin_unlock_irqrestore(&tls_device_gc_lock, flags);
+
+	list_for_each_entry_safe(ctx, tmp, &gc_list, gclist) {
+		struct tls_offload_context *offlad_ctx = tls_offload_ctx(ctx);
+		void (*sk_destruct)(struct sock *sk) = offlad_ctx->sk_destruct;
+		struct net_device *netdev = ctx->netdev;
+		struct sock *sk = ctx->sk;
+
+		netdev->tlsdev_ops->tls_dev_del(netdev, sk,
+						TLS_OFFLOAD_CTX_DIR_TX);
+
+		list_del(&ctx->gclist);
+		kfree(offlad_ctx);
+		kfree(ctx);
+		sk_destruct(sk);
+	}
+}
+
+static void tls_device_queue_ctx_destruction(struct tls_context *ctx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tls_device_gc_lock, flags);
+	list_add_tail(&ctx->gclist, &tls_device_gc_list);
+	spin_unlock_irqrestore(&tls_device_gc_lock, flags);
+
+	schedule_work(&tls_device_gc_work);
+}
+
+/* We assume that the socket is already connected */
+static struct net_device *get_netdev_for_sock(struct sock *sk)
+{
+	struct inet_sock *inet = inet_sk(sk);
+	struct net_device *netdev = NULL;
+
+	netdev = dev_get_by_index(sock_net(sk), inet->cork.fl.flowi_oif);
+
+	return netdev;
+}
+
+static void detach_sock_from_netdev(struct sock *sk, struct tls_context *ctx)
+{
+	struct net_device *netdev;
+
+	netdev = get_netdev_for_sock(sk);
+	if (!netdev) {
+		pr_err("got offloaded socket with no netdev\n");
+		return;
+	}
+
+	if (!netdev->tlsdev_ops) {
+		pr_err("attach_sock_to_netdev: netdev %s with no TLS offload\n",
+		       netdev->name);
+		return;
+	}
+
+	netdev->tlsdev_ops->tls_dev_del(netdev, sk, TLS_OFFLOAD_CTX_DIR_TX);
+	dev_put(netdev);
+}
+
+static int attach_sock_to_netdev(struct sock *sk, struct net_device *netdev,
+				 struct tls_context *ctx)
+{
+	int rc;
+
+	rc = netdev->tlsdev_ops->tls_dev_add(
+			netdev,
+			sk,
+			TLS_OFFLOAD_CTX_DIR_TX,
+			&ctx->crypto_send);
+	if (rc) {
+		pr_err("The netdev has refused to offload this socket\n");
+		goto out;
+	}
+
+	sk->sk_bound_dev_if = netdev->ifindex;
+	sk_dst_reset(sk);
+
+	rc = 0;
+out:
+	return rc;
+}
+
+static void destroy_record(struct tls_record_info *record)
+{
+	skb_frag_t *frag;
+	int nr_frags = record->num_frags;
+
+	while (nr_frags > 0) {
+		frag = &record->frags[nr_frags - 1];
+		__skb_frag_unref(frag);
+		--nr_frags;
+	}
+	kfree(record);
+}
+
+static void delete_all_records(struct tls_offload_context *offload_ctx)
+{
+	struct tls_record_info *info, *temp;
+
+	list_for_each_entry_safe(info, temp, &offload_ctx->records_list, list) {
+		list_del(&info->list);
+		destroy_record(info);
+	}
+
+	offload_ctx->retransmit_hint = NULL;
+}
+
+static void tls_icsk_clean_acked(struct sock *sk)
+{
+	struct tls_context *tls_ctx = tls_get_ctx(sk);
+	struct tls_offload_context *ctx;
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct tls_record_info *info, *temp;
+	unsigned long flags;
+
+	if (!tls_ctx)
+		return;
+
+	ctx = tls_offload_ctx(tls_ctx);
+
+	spin_lock_irqsave(&ctx->lock, flags);
+	info = ctx->retransmit_hint;
+	if (info && !before(tp->snd_una, info->end_seq)) {
+		ctx->retransmit_hint = NULL;
+		list_del(&info->list);
+		destroy_record(info);
+	}
+
+	list_for_each_entry_safe(info, temp, &ctx->records_list, list) {
+		if (before(tp->snd_una, info->end_seq))
+			break;
+		list_del(&info->list);
+
+		destroy_record(info);
+	}
+
+	spin_unlock_irqrestore(&ctx->lock, flags);
+}
+
+static void tls_device_free_resources(struct sock *sk)
+{
+	struct tls_context *tls_ctx = tls_get_ctx(sk);
+	struct tls_offload_context *ctx = tls_offload_ctx(tls_ctx);
+
+	if (ctx->open_record)
+		destroy_record(ctx->open_record);
+}
+
+/* At this point, there should be no references on this
+ * socket and no in-flight SKBs associated with this
+ * socket, so it is safe to free all the resources.
+ */
+void tls_device_sk_destruct(struct sock *sk)
+{
+	struct tls_context *tls_ctx = tls_get_ctx(sk);
+	struct tls_offload_context *ctx = tls_offload_ctx(tls_ctx);
+
+	delete_all_records(ctx);
+
+	tls_device_queue_ctx_destruction(tls_ctx);
+}
+EXPORT_SYMBOL(tls_device_sk_destruct);
+
+static inline void tls_append_frag(struct tls_record_info *record,
+				   struct page_frag *pfrag,
+				   int size)
+{
+	skb_frag_t *frag;
+
+	frag = &record->frags[record->num_frags - 1];
+	if (frag->page.p == pfrag->page &&
+	    frag->page_offset + frag->size == pfrag->offset) {
+		frag->size += size;
+	} else {
+		++frag;
+		frag->page.p = pfrag->page;
+		frag->page_offset = pfrag->offset;
+		frag->size = size;
+		++record->num_frags;
+		get_page(pfrag->page);
+	}
+
+	pfrag->offset += size;
+	record->len += size;
+}
+
+static inline int tls_push_record(struct sock *sk,
+				  struct tls_context *ctx,
+				  struct tls_offload_context *offload_ctx,
+				  struct tls_record_info *record,
+				  struct page_frag *pfrag,
+				  int flags,
+				  unsigned char record_type)
+{
+	skb_frag_t *frag;
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct page_frag fallback_frag;
+	struct page_frag  *tag_pfrag = pfrag;
+	int i;
+
+	/* fill prepand */
+	frag = &record->frags[0];
+	tls_fill_prepend(ctx,
+			 skb_frag_address(frag),
+			 record->len - ctx->prepend_size,
+			 record_type);
+
+	if (unlikely(!skb_page_frag_refill(
+				ctx->tag_size,
+				pfrag, GFP_KERNEL))) {
+		/* HW doesn't care about the data in the tag
+		 * so in case pfrag has no room
+		 * for a tag and we can't allocate a new pfrag
+		 * just use the page in the first frag
+		 * rather then write a complicated fall back code.
+		 */
+		tag_pfrag = &fallback_frag;
+		tag_pfrag->page = skb_frag_page(frag);
+		tag_pfrag->offset = 0;
+	}
+
+	tls_append_frag(record, tag_pfrag, ctx->tag_size);
+	record->end_seq = tp->write_seq + record->len;
+	spin_lock_irq(&offload_ctx->lock);
+	list_add_tail(&record->list, &offload_ctx->records_list);
+	spin_unlock_irq(&offload_ctx->lock);
+	offload_ctx->open_record = NULL;
+	set_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
+	tls_advance_record_sn(sk, ctx);
+
+	for (i = 0; i < record->num_frags; i++) {
+		frag = &record->frags[i];
+		sg_unmark_end(&offload_ctx->sg_tx_data[i]);
+		sg_set_page(&offload_ctx->sg_tx_data[i], skb_frag_page(frag),
+			    frag->size, frag->page_offset);
+		sk_mem_charge(sk, frag->size);
+		get_page(skb_frag_page(frag));
+	}
+	sg_mark_end(&offload_ctx->sg_tx_data[record->num_frags - 1]);
+
+	/* all ready, send */
+	return tls_push_sg(sk, ctx, offload_ctx->sg_tx_data, 0, flags);
+}
+
+static inline int tls_create_new_record(
+		struct tls_offload_context *offload_ctx,
+		struct page_frag *pfrag,
+		size_t prepend_size)
+{
+	skb_frag_t *frag;
+	struct tls_record_info *record;
+
+	record = kmalloc(sizeof(*record), GFP_KERNEL);
+	if (!record)
+		return -ENOMEM;
+
+	frag = &record->frags[0];
+	__skb_frag_set_page(frag, pfrag->page);
+	frag->page_offset = pfrag->offset;
+	skb_frag_size_set(frag, prepend_size);
+
+	get_page(pfrag->page);
+	pfrag->offset += prepend_size;
+
+	record->num_frags = 1;
+	record->len = prepend_size;
+	offload_ctx->open_record = record;
+	return 0;
+}
+
+static inline int tls_do_allocation(
+		struct sock *sk,
+		struct tls_offload_context *offload_ctx,
+		struct page_frag *pfrag,
+		size_t prepend_size)
+{
+	int ret;
+
+	if (!offload_ctx->open_record) {
+		if (unlikely(!skb_page_frag_refill(prepend_size, pfrag,
+						   sk->sk_allocation))) {
+			sk->sk_prot->enter_memory_pressure(sk);
+			sk_stream_moderate_sndbuf(sk);
+			return -ENOMEM;
+		}
+
+		ret = tls_create_new_record(offload_ctx, pfrag, prepend_size);
+		if (ret)
+			return ret;
+
+		if (pfrag->size > pfrag->offset)
+			return 0;
+	}
+
+	if (!sk_page_frag_refill(sk, pfrag))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int tls_push_data(struct sock *sk,
+			 struct iov_iter *msg_iter,
+			 size_t size, int flags,
+			 unsigned char record_type)
+{
+	struct tls_context *tls_ctx = tls_get_ctx(sk);
+	struct tls_offload_context *ctx = tls_offload_ctx(tls_ctx);
+	struct tls_record_info *record = ctx->open_record;
+	struct page_frag *pfrag;
+	int copy, rc = 0;
+	size_t orig_size = size;
+	u32 max_open_record_len;
+	long timeo;
+	int more = flags & (MSG_SENDPAGE_NOTLAST | MSG_MORE);
+	int tls_push_record_flags = flags | MSG_SENDPAGE_NOTLAST;
+	bool done = false;
+
+	if (sk->sk_err)
+		return -sk->sk_err;
+
+	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+	rc = tls_complete_pending_work(sk, tls_ctx, flags, &timeo);
+	if (rc < 0)
+		return rc;
+
+	pfrag = sk_page_frag(sk);
+
+	/* KTLS_TLS_HEADER_SIZE is not counted as part of the TLS record, and
+	 * we need to leave room for an authentication tag.
+	 */
+	max_open_record_len = TLS_MAX_PAYLOAD_SIZE +
+			      tls_ctx->prepend_size;
+	do {
+		if (tls_do_allocation(sk, ctx, pfrag,
+				      tls_ctx->prepend_size)) {
+			rc = sk_stream_wait_memory(sk, &timeo);
+			if (!rc)
+				continue;
+
+			record = ctx->open_record;
+			if (!record)
+				break;
+handle_error:
+			if (record_type != TLS_RECORD_TYPE_DATA) {
+				/* avoid sending partial
+				 * record with type !=
+				 * application_data
+				 */
+				size = orig_size;
+				destroy_record(record);
+				ctx->open_record = NULL;
+			} else if (record->len > tls_ctx->prepend_size) {
+				goto last_record;
+			}
+
+			break;
+		}
+
+		record = ctx->open_record;
+		copy = min_t(size_t, size, (pfrag->size - pfrag->offset));
+		copy = min_t(size_t, copy, (max_open_record_len - record->len));
+
+		if (copy_from_iter_nocache(
+				page_address(pfrag->page) + pfrag->offset,
+				copy, msg_iter) != copy) {
+			rc = -EFAULT;
+			goto handle_error;
+		}
+		tls_append_frag(record, pfrag, copy);
+
+		size -= copy;
+		if (!size) {
+last_record:
+			tls_push_record_flags = flags;
+			if (more) {
+				tls_ctx->pending_open_record_frags =
+						record->num_frags;
+				break;
+			}
+
+			done = true;
+		}
+
+		if ((done) ||
+		    (record->len >= max_open_record_len) ||
+		    (record->num_frags >= MAX_SKB_FRAGS - 1)) {
+			rc = tls_push_record(sk,
+					     tls_ctx,
+					     ctx,
+					     record,
+					     pfrag,
+					     tls_push_record_flags,
+					     record_type);
+			if (rc < 0)
+				break;
+		}
+	} while (!done);
+
+	if (orig_size - size > 0)
+		rc = orig_size - size;
+
+	return rc;
+}
+
+int tls_device_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
+{
+	unsigned char record_type = TLS_RECORD_TYPE_DATA;
+	int rc = 0;
+
+	lock_sock(sk);
+
+	if (unlikely(msg->msg_controllen)) {
+		rc = tls_proccess_cmsg(sk, msg, &record_type);
+		if (rc)
+			goto out;
+	}
+
+	rc = tls_push_data(sk, &msg->msg_iter, size,
+			   msg->msg_flags, record_type);
+
+out:
+	release_sock(sk);
+	return rc;
+}
+
+int tls_device_sendpage(struct sock *sk, struct page *page,
+			int offset, size_t size, int flags)
+{
+	struct iov_iter	msg_iter;
+	struct kvec iov;
+	char *kaddr = kmap(page);
+	int rc = 0;
+
+	if (flags & MSG_SENDPAGE_NOTLAST)
+		flags |= MSG_MORE;
+
+	lock_sock(sk);
+
+	if (flags & MSG_OOB) {
+		rc = -ENOTSUPP;
+		goto out;
+	}
+
+	iov.iov_base = kaddr + offset;
+	iov.iov_len = size;
+	iov_iter_kvec(&msg_iter, WRITE | ITER_KVEC, &iov, 1, size);
+	rc = tls_push_data(sk, &msg_iter, size,
+			   flags, TLS_RECORD_TYPE_DATA);
+	kunmap(page);
+
+out:
+	release_sock(sk);
+	return rc;
+}
+
+struct tls_record_info *tls_get_record(struct tls_offload_context *context,
+				       u32 seq)
+{
+	struct tls_record_info *info;
+
+	info = context->retransmit_hint;
+	if (!info ||
+	    before(seq, info->end_seq - info->len))
+		info = list_first_entry(&context->records_list,
+					struct tls_record_info, list);
+
+	list_for_each_entry_from(info, &context->records_list, list) {
+		if (before(seq, info->end_seq)) {
+			if (!context->retransmit_hint ||
+			    after(info->end_seq,
+				  context->retransmit_hint->end_seq))
+				context->retransmit_hint = info;
+			return info;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(tls_get_record);
+
+static int tls_device_push_pending_record(struct sock *sk, int flags)
+{
+	struct iov_iter	msg_iter;
+
+	iov_iter_kvec(&msg_iter, WRITE | ITER_KVEC, NULL, 0, 0);
+	return tls_push_data(sk, &msg_iter, 0, flags, TLS_RECORD_TYPE_DATA);
+}
+
+int tls_set_device_offload(struct sock *sk, struct tls_context *ctx)
+{
+	struct tls_crypto_info *crypto_info;
+	struct tls_offload_context *offload_ctx;
+	struct tls_record_info *start_marker_record;
+	u16 nonece_size, tag_size, iv_size, rec_seq_size;
+	char *iv, *rec_seq;
+	int rc;
+	struct net_device *netdev;
+	struct sk_buff *skb;
+
+	if (!ctx) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (ctx->priv_ctx) {
+		rc = -EEXIST;
+		goto out;
+	}
+
+	netdev = get_netdev_for_sock(sk);
+	if (!netdev) {
+		pr_err("%s: netdev not found\n", __func__);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (!(netdev->features & NETIF_F_HW_TLS_TX)) {
+		rc = -ENOTSUPP;
+		goto release_netdev;
+	}
+
+	crypto_info = &ctx->crypto_send;
+	switch (crypto_info->cipher_type) {
+	case TLS_CIPHER_AES_GCM_128: {
+		nonece_size = TLS_CIPHER_AES_GCM_128_IV_SIZE;
+		tag_size = TLS_CIPHER_AES_GCM_128_TAG_SIZE;
+		iv_size = TLS_CIPHER_AES_GCM_128_IV_SIZE;
+		iv = ((struct tls12_crypto_info_aes_gcm_128 *)crypto_info)->iv;
+		rec_seq_size = TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE;
+		rec_seq =
+		 ((struct tls12_crypto_info_aes_gcm_128 *)crypto_info)->rec_seq;
+		break;
+	}
+	default:
+		rc = -EINVAL;
+		goto release_netdev;
+	}
+
+	start_marker_record = kmalloc(sizeof(*start_marker_record), GFP_KERNEL);
+	if (!start_marker_record) {
+		rc = -ENOMEM;
+		goto release_netdev;
+	}
+
+	rc = attach_sock_to_netdev(sk, netdev, ctx);
+	if (rc)
+		goto free_marker_record;
+
+	ctx->netdev = netdev;
+	ctx->sk = sk;
+
+	ctx->prepend_size = TLS_HEADER_SIZE + nonece_size;
+	ctx->tag_size = tag_size;
+	ctx->iv_size = iv_size;
+	ctx->iv = kmalloc(iv_size + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
+			  GFP_KERNEL);
+	if (!ctx->iv) {
+		rc = -ENOMEM;
+		goto detach_sock;
+	}
+	memcpy(ctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE, iv, iv_size);
+	ctx->rec_seq_size = rec_seq_size;
+	ctx->rec_seq = kmalloc(rec_seq_size, GFP_KERNEL);
+	if (!ctx->rec_seq) {
+		rc = -ENOMEM;
+		goto err_iv;
+	}
+	memcpy(ctx->rec_seq, rec_seq, rec_seq_size);
+
+	offload_ctx = ctx->priv_ctx;
+	start_marker_record->end_seq = tcp_sk(sk)->write_seq;
+	start_marker_record->len = 0;
+	start_marker_record->num_frags = 0;
+
+	INIT_LIST_HEAD(&offload_ctx->records_list);
+	list_add_tail(&start_marker_record->list, &offload_ctx->records_list);
+	spin_lock_init(&offload_ctx->lock);
+
+	inet_csk(sk)->icsk_clean_acked = &tls_icsk_clean_acked;
+	ctx->push_pending_record = tls_device_push_pending_record;
+	ctx->free_resources = tls_device_free_resources;
+	offload_ctx->sk_destruct = sk->sk_destruct;
+
+	/* TLS offload is greatly simplified if we don't send
+	 * SKBs where only part of the payload needs to be encrypted.
+	 * So mark the last skb in the write queue as end of record.
+	 */
+	skb = tcp_write_queue_tail(sk);
+	if (skb)
+		TCP_SKB_CB(skb)->eor = 1;
+
+	/* After the next line tls_is_sk_tx_device_offloaded
+	 * will return true and ndo_start_xmit might access the
+	 * offload context
+	 */
+	smp_store_release(&sk->sk_destruct,
+			  &tls_device_sk_destruct);
+	goto release_netdev;
+
+err_iv:
+	kfree(ctx->iv);
+detach_sock:
+	detach_sock_from_netdev(sk, ctx);
+free_marker_record:
+	kfree(start_marker_record);
+release_netdev:
+	dev_put(netdev);
+out:
+	return rc;
+}
+
+void __exit tls_device_cleanup(void)
+{
+	flush_work(&tls_device_gc_work);
+}
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index ae20ee3..a93a712 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -45,8 +45,16 @@
 MODULE_DESCRIPTION("Transport Layer Security Support");
 MODULE_LICENSE("Dual BSD/GPL");
 
-static struct proto tls_base_prot;
-static struct proto tls_sw_prot;
+enum {
+	TLS_BASE_TX,
+	TLS_SW_TX,
+#ifdef CONFIG_TLS_DEVICE
+	TLS_HW_TX,
+#endif
+	TLS_NUM_CONFIG,
+};
+
+static struct proto tls_prots[TLS_NUM_CONFIG];
 
 int wait_on_pending_writer(struct sock *sk, long *timeo)
 {
@@ -393,11 +401,19 @@ static int do_tls_setsockopt_tx(struct sock *sk, char __user *optval,
 
 	ctx->sk_proto_close = sk->sk_prot->close;
 
-	/* currently SW is default, we will have ethtool in future */
-	rc = tls_set_sw_offload(sk, ctx);
-	prot = &tls_sw_prot;
-	if (rc)
-		goto err_crypto_info;
+#ifdef CONFIG_TLS_DEVICE
+	rc = tls_set_device_offload(sk, ctx);
+	prot = &tls_prots[TLS_HW_TX];
+	if (rc) {
+#else
+	{
+#endif
+		/* if HW offload fails fallback to SW */
+		rc = tls_set_sw_offload(sk, ctx);
+		prot = &tls_prots[TLS_SW_TX];
+		if (rc)
+			goto err_crypto_info;
+	}
 
 	sk->sk_prot = prot;
 	goto out;
@@ -452,7 +468,8 @@ static int tls_init(struct sock *sk)
 	icsk->icsk_ulp_data = ctx;
 	ctx->setsockopt = sk->sk_prot->setsockopt;
 	ctx->getsockopt = sk->sk_prot->getsockopt;
-	sk->sk_prot = &tls_base_prot;
+
+	sk->sk_prot = &tls_prots[TLS_BASE_TX];
 out:
 	return rc;
 }
@@ -463,16 +480,27 @@ static int tls_init(struct sock *sk)
 	.init			= tls_init,
 };
 
-static int __init tls_register(void)
+static void build_protos(struct proto *prot, struct proto *base)
 {
-	tls_base_prot			= tcp_prot;
-	tls_base_prot.setsockopt	= tls_setsockopt;
-	tls_base_prot.getsockopt	= tls_getsockopt;
+	prot[TLS_BASE_TX] = *base;
+	prot[TLS_BASE_TX].setsockopt = tls_setsockopt;
+	prot[TLS_BASE_TX].getsockopt = tls_getsockopt;
+
+	prot[TLS_SW_TX] = prot[TLS_BASE_TX];
+	prot[TLS_SW_TX].close		= tls_sk_proto_close;
+	prot[TLS_SW_TX].sendmsg		= tls_sw_sendmsg;
+	prot[TLS_SW_TX].sendpage	= tls_sw_sendpage;
+
+#ifdef CONFIG_TLS_DEVICE
+	prot[TLS_HW_TX] = prot[TLS_SW_TX];
+	prot[TLS_HW_TX].sendmsg		= tls_device_sendmsg;
+	prot[TLS_HW_TX].sendpage	= tls_device_sendpage;
+#endif
+}
 
-	tls_sw_prot			= tls_base_prot;
-	tls_sw_prot.sendmsg		= tls_sw_sendmsg;
-	tls_sw_prot.sendpage            = tls_sw_sendpage;
-	tls_sw_prot.close               = tls_sk_proto_close;
+static int __init tls_register(void)
+{
+	build_protos(tls_prots, &tcp_prot);
 
 	tcp_register_ulp(&tcp_tls_ulp_ops);
 
@@ -482,6 +510,9 @@ static int __init tls_register(void)
 static void __exit tls_unregister(void)
 {
 	tcp_unregister_ulp(&tcp_tls_ulp_ops);
+#ifdef CONFIG_TLS_DEVICE
+	tls_device_cleanup();
+#endif
 }
 
 module_init(tls_register);
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH net-next 0/5] tls: Add generic NIC offload infrastructure
From: Ilya Lesokhin @ 2017-09-14 10:46 UTC (permalink / raw)
  To: netdev, davem; +Cc: davejwatson, tom, hannes, borisp, ilyal, aviadye, liranl

This series add a generic infrastructure to offload TLS crypto to a
network devices. It enables the kernel TLS socket to skip encryption and
authentication operations on the transmit side of the data path. Leaving
those computationally expensive operations to the NIC.

The NIC offload infrastructure builds TLS records and pushes them to the
TCP layer just like the SW KTLS implementation and using the same API.
TCP segmentation is mostly unaffected. Currently the only exception is
that we prevent mixed SKBs where only part of the payload requires
offload. In the future we are likely to add a similar restriction
following a change cipher spec record.

The notable differences between SW KTLS and NIC offloaded TLS
implementations are as follows:
1. The offloaded implementation builds "plaintext TLS record", those
records contain plaintext instead of ciphertext and place holder bytes
instead of authentication tags.
2. The offloaded implementation maintains a mapping from TCP sequence
number to TLS records. Thus given a TCP SKB sent from a NIC offloaded
 TLS socket, we can use the tls NIC offload infrastructure to obtain
enough context to encrypt the payload of the SKB.
A TLS record is released when the last byte of the record is ack'ed,
this is done through the new icsk_clean_acked callback.

The infrastructure should be extendable to support various NIC offload
implementations.  However it is currently written with the
implementation below in mind:
The NIC assumes that packets from each offloaded stream are sent as
plaintext and in-order. It keeps track of the TLS records in the TCP
stream. When a packet marked for offload is transmitted, the NIC
encrypts the payload in-place and puts authentication tags in the
relevant place holders.

The responsibility for handling out-of-order packets (i.e. TCP
retransmission, qdisc drops) falls on the netdev driver.

The netdev driver keeps track of the expected TCP SN from the NIC's
perspective.  If the next packet to transmit matches the expected TCP
SN, the driver advances the expected TCP SN, and transmits the packet
with TLS offload indication.

If the next packet to transmit does not match the expected TCP SN. The
driver calls the TLS layer to obtain the TLS record that includes the
TCP of the packet for transmission. Using this TLS record, the driver
posts a work entry on the transmit queue to reconstruct the NIC TLS
state required for the offload of the out-of-order packet. It updates
the expected TCP SN accordingly and transmit the now in-order packet.
The same queue is used for packet transmission and TLS context
reconstruction to avoid the need for flushing the transmit queue before
issuing the context reconstruction request.

Expected TCP SN is accessed without a lock, under the assumption that
TCP doesn't transmit SKBs from different TX queue concurrently.

We assume that packets are not rerouted to a different network device.

Github with mlx5e TLS offload support:
https://github.com/Mellanox/tls-offload/tree/tls_device_v1

Paper: https://www.netdevconf.org/1.2/papers/netdevconf-TLS.pdf

Ilya Lesokhin (5):
  tls: Move release of tls_ctx into tls_sw_free_resources
  tcp: Add clean acked data hook
  net: Add TLS offload netdev ops
  net: Add TLS TX offload features
  tls: Add generic NIC offload infrastructure.

 include/linux/netdev_features.h    |   2 +
 include/linux/netdevice.h          |  21 ++
 include/net/inet_connection_sock.h |   2 +
 include/net/tls.h                  |  41 ++-
 net/core/ethtool.c                 |   1 +
 net/ipv4/tcp_input.c               |   3 +
 net/tls/Kconfig                    |   9 +
 net/tls/Makefile                   |   3 +
 net/tls/tls_device.c               | 673 +++++++++++++++++++++++++++++++++++++
 net/tls/tls_main.c                 |  68 ++--
 net/tls/tls_sw.c                   |   1 +
 11 files changed, 803 insertions(+), 21 deletions(-)
 create mode 100644 net/tls/tls_device.c

-- 
1.8.3.1

^ permalink raw reply

* [PATCH net-next 1/5] tls: Move release of tls_ctx into tls_sw_free_resources
From: Ilya Lesokhin @ 2017-09-14 10:46 UTC (permalink / raw)
  To: netdev, davem; +Cc: davejwatson, tom, hannes, borisp, ilyal, aviadye, liranl
In-Reply-To: <1505385988-94522-1-git-send-email-ilyal@mellanox.com>

Move release of tls_ctx into sw specific code.
This is required because the device offload implementation
requires this context to remain alive until there are
no more in-flight SKBs.

Signed-off-by: Boris Pismenny <borisp@mellanox.com>
Signed-off-by: Ilya Lesokhin <ilyal@mellanox.com>
Signed-off-by: Aviad Yehezkel <aviadye@mellanox.com>
---
 net/tls/tls_main.c | 5 ++---
 net/tls/tls_sw.c   | 1 +
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 60aff60..ae20ee3 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -232,12 +232,11 @@ static void tls_sk_proto_close(struct sock *sk, long timeout)
 			sg++;
 		}
 	}
-	ctx->free_resources(sk);
+
 	kfree(ctx->rec_seq);
 	kfree(ctx->iv);
-
 	sk_proto_close = ctx->sk_proto_close;
-	kfree(ctx);
+	ctx->free_resources(sk);
 
 	release_sock(sk);
 	sk_proto_close(sk, timeout);
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index fa596fa..db1e566 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -650,6 +650,7 @@ void tls_sw_free_resources(struct sock *sk)
 	tls_free_both_sg(sk);
 
 	kfree(ctx);
+	kfree(tls_ctx);
 }
 
 int tls_set_sw_offload(struct sock *sk, struct tls_context *ctx)
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH net-next 3/5] net: Add TLS offload netdev ops
From: Ilya Lesokhin @ 2017-09-14 10:46 UTC (permalink / raw)
  To: netdev, davem; +Cc: davejwatson, tom, hannes, borisp, ilyal, aviadye, liranl
In-Reply-To: <1505385988-94522-1-git-send-email-ilyal@mellanox.com>

Add new netdev ops to add and delete tls context

Signed-off-by: Boris Pismenny <borisp@mellanox.com>
Signed-off-by: Ilya Lesokhin <ilyal@mellanox.com>
Signed-off-by: Aviad Yehezkel <aviadye@mellanox.com>
---
 include/linux/netdevice.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index cdfd9ad..4ea81bab 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -826,6 +826,23 @@ struct xfrmdev_ops {
 };
 #endif
 
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+enum tls_offload_ctx_dir {
+	TLS_OFFLOAD_CTX_DIR_RX,
+	TLS_OFFLOAD_CTX_DIR_TX,
+};
+
+struct tls_crypto_info;
+
+struct tlsdev_ops {
+	int (*tls_dev_add)(struct net_device *netdev, struct sock *sk,
+			   enum tls_offload_ctx_dir direction,
+			   struct tls_crypto_info *crypto_info);
+	void (*tls_dev_del)(struct net_device *netdev, struct sock *sk,
+			    enum tls_offload_ctx_dir direction);
+};
+#endif
+
 /*
  * This structure defines the management hooks for network devices.
  * The following hooks can be defined; unless noted otherwise, they are
@@ -1713,6 +1730,10 @@ struct net_device {
 	const struct xfrmdev_ops *xfrmdev_ops;
 #endif
 
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+	const struct tlsdev_ops *tlsdev_ops;
+#endif
+
 	const struct header_ops *header_ops;
 
 	unsigned int		flags;
-- 
1.8.3.1

^ permalink raw reply related

* Re: [PATCH] dt-bindings: net: renesas-ravb: Add support for R8A77995 RAVB
From: Sergei Shtylyov @ 2017-09-14  9:44 UTC (permalink / raw)
  To: Yoshihiro Shimoda, davem, robh+dt, mark.rutland
  Cc: netdev, linux-renesas-soc, devicetree
In-Reply-To: <1505347598-4156-1-git-send-email-yoshihiro.shimoda.uh@renesas.com>

Hello!

On 9/14/2017 3:06 AM, Yoshihiro Shimoda wrote:

> Add a new compatible string for the R8A77995 (R-Car D3) RAVB.
> 
> Acked-by: Geert Uytterhoeven <geert+renesas@glider.be>

    Usually those are added after sign-off.

> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

Acked-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

> ---
>   Changes from v1:
>    - Based on r8a77970 patch.
>     https://marc.info/?l=linux-renesas-soc&m=150524655515476

    So this is against net-next, right? The net.git repo now has a patch 
making me a reviewer for the Renesas bindings too.

[...]

MBR, Sergei

^ permalink raw reply

* Re: [PATCH v2] net: smsc911x: Quieten netif during suspend
From: Geert Uytterhoeven @ 2017-09-14  9:09 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Geert Uytterhoeven, David S . Miller, Steve Glendinning,
	Andrew Lunn, netdev@vger.kernel.org, Linux PM list, Linux-Renesas,
	linux-kernel@vger.kernel.org
In-Reply-To: <16f16af0-383b-9089-93c7-75494ec5031e@gmail.com>

Hi Florian,

On Thu, Sep 14, 2017 at 1:28 AM, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 09/13/2017 10:42 AM, Geert Uytterhoeven wrote:
>> If the network interface is kept running during suspend, the net core
>> may call net_device_ops.ndo_start_xmit() while the Ethernet device is
>> still suspended, which may lead to a system crash.
>>
>> E.g. on sh73a0/kzm9g and r8a73a4/ape6evm, the external Ethernet chip is
>> driven by a PM controlled clock.  If the Ethernet registers are accessed
>> while the clock is not running, the system will crash with an imprecise
>> external abort.
>>
>> As this is a race condition with a small time window, it is not so easy
>> to trigger at will.  Using pm_test may increase your chances:
>>
>>     # echo 0 > /sys/module/printk/parameters/console_suspend
>>     # echo platform > /sys/power/pm_test
>>     # echo mem > /sys/power/state
>>
>> To fix this, make sure the network interface is quietened during
>> suspend.
>>
>> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>

> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

Thank you!

> You may want to take the opportunity to suspend the PHY device
> (conversely resume it) if WoL is not enabled on this device.

Despite the WoL comment visible in the context below, I believe this driver
doesn't support WoL yet (ethtool_ops.[gs]et_wol() not implemented).

>> --- a/drivers/net/ethernet/smsc/smsc911x.c
>> +++ b/drivers/net/ethernet/smsc/smsc911x.c
>> @@ -2595,6 +2595,11 @@ static int smsc911x_suspend(struct device *dev)
>>       struct net_device *ndev = dev_get_drvdata(dev);
>>       struct smsc911x_data *pdata = netdev_priv(ndev);
>>
>> +     if (netif_running(ndev)) {
>> +             netif_stop_queue(ndev);
>> +             netif_device_detach(ndev);
>> +     }
>> +
>>       /* enable wake on LAN, energy detection and the external PME
>>        * signal. */
>>       smsc911x_reg_write(pdata, PMT_CTRL,

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH net-next v9] openvswitch: enable NSH support
From: Jiri Benc @ 2017-09-14  9:09 UTC (permalink / raw)
  To: Yi Yang
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA, e,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q
In-Reply-To: <1505378279-123916-1-git-send-email-yi.y.yang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

On Thu, 14 Sep 2017 16:37:59 +0800, Yi Yang wrote:
> OVS master and 2.8 branch has merged NSH userspace
> patch series, this patch is to enable NSH support
> in kernel data path in order that OVS can support
> NSH in compat mode by porting this.

http://vger.kernel.org/~davem/net-next.html

^ permalink raw reply

* Re: scheduling while atomic from vmci_transport_recv_stream_cb in 3.16 kernels
From: Michal Hocko @ 2017-09-14  8:59 UTC (permalink / raw)
  To: Jorgen S. Hansen, Ben Hutchings
  Cc: Aditya Sarwade, Thomas Hellstrom, LKML, netdev@vger.kernel.org,
	Masik Petr, Sasha Levin, Stable tree
In-Reply-To: <AA27A6E6-2376-437A-9508-FD9C2427A465@vmware.com>

On Wed 13-09-17 18:58:13, Jorgen S. Hansen wrote:
[...]
> The patch series look good to me.

Thanks for double checking. Ben, could you merge this to 3.16 stable
branch, please?

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* [PATCH net-next v9] openvswitch: enable NSH support
From: Yi Yang @ 2017-09-14  8:37 UTC (permalink / raw)
  To: netdev-u79uwXL29TY76Z2rM5mHXA
  Cc: dev-yBygre7rU0TnMu66kgdUjQ, jbenc-H+wXaHxf7aLQT0dZR+AlfA, e,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q

v8->v9
 - Fix build error reported by daily intel build
   because nsh module isn't selected by openvswitch

v7->v8
 - Rework nested value and mask for OVS_KEY_ATTR_NSH
 - Change pop_nsh to adapt to nsh kernel module
 - Fix many issues per comments from Jiri Benc

v6->v7
 - Remove NSH GSO patches in v6 because Jiri Benc
   reworked it as another patch series and they have
   been merged.
 - Change it to adapt to nsh kernel module added by NSH
   GSO patch series

v5->v6
 - Fix the rest comments for v4.
 - Add NSH GSO support for VxLAN-gpe + NSH and
   Eth + NSH.

v4->v5
 - Fix many comments by Jiri Benc and Eric Garver
   for v4.

v3->v4
 - Add new NSH match field ttl
 - Update NSH header to the latest format
   which will be final format and won't change
   per its author's confirmation.
 - Fix comments for v3.

v2->v3
 - Change OVS_KEY_ATTR_NSH to nested key to handle
   length-fixed attributes and length-variable
   attriubte more flexibly.
 - Remove struct ovs_action_push_nsh completely
 - Add code to handle nested attribute for SET_MASKED
 - Change PUSH_NSH to use the nested OVS_KEY_ATTR_NSH
   to transfer NSH header data.
 - Fix comments and coding style issues by Jiri and Eric

v1->v2
 - Change encap_nsh and decap_nsh to push_nsh and pop_nsh
 - Dynamically allocate struct ovs_action_push_nsh for
   length-variable metadata.

OVS master and 2.8 branch has merged NSH userspace
patch series, this patch is to enable NSH support
in kernel data path in order that OVS can support
NSH in compat mode by porting this.

Signed-off-by: Yi Yang <yi.y.yang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 include/net/nsh.h                |   3 +
 include/uapi/linux/openvswitch.h |  29 ++++
 net/nsh/nsh.c                    |  53 +++++++
 net/openvswitch/Kconfig          |   1 +
 net/openvswitch/actions.c        | 112 ++++++++++++++
 net/openvswitch/flow.c           |  51 ++++++
 net/openvswitch/flow.h           |  11 ++
 net/openvswitch/flow_netlink.c   | 324 ++++++++++++++++++++++++++++++++++++++-
 net/openvswitch/flow_netlink.h   |   5 +
 9 files changed, 588 insertions(+), 1 deletion(-)

diff --git a/include/net/nsh.h b/include/net/nsh.h
index a1eaea2..b886d33 100644
--- a/include/net/nsh.h
+++ b/include/net/nsh.h
@@ -304,4 +304,7 @@ static inline void nsh_set_flags_ttl_len(struct nshhdr *nsh, u8 flags,
 			NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK);
 }
 
+int skb_push_nsh(struct sk_buff *skb, const struct nshhdr *src_nsh_hdr);
+int skb_pop_nsh(struct sk_buff *skb);
+
 #endif /* __NET_NSH_H */
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 156ee4c..c1a785c 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -333,6 +333,7 @@ enum ovs_key_attr {
 	OVS_KEY_ATTR_CT_LABELS,	/* 16-octet connection tracking label */
 	OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4,   /* struct ovs_key_ct_tuple_ipv4 */
 	OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6,   /* struct ovs_key_ct_tuple_ipv6 */
+	OVS_KEY_ATTR_NSH,       /* Nested set of ovs_nsh_key_* */
 
 #ifdef __KERNEL__
 	OVS_KEY_ATTR_TUNNEL_INFO,  /* struct ip_tunnel_info */
@@ -491,6 +492,30 @@ struct ovs_key_ct_tuple_ipv6 {
 	__u8   ipv6_proto;
 };
 
+enum ovs_nsh_key_attr {
+	OVS_NSH_KEY_ATTR_UNSPEC,
+	OVS_NSH_KEY_ATTR_BASE,  /* struct ovs_nsh_key_base. */
+	OVS_NSH_KEY_ATTR_MD1,   /* struct ovs_nsh_key_md1. */
+	OVS_NSH_KEY_ATTR_MD2,   /* variable-length octets for MD type 2. */
+	__OVS_NSH_KEY_ATTR_MAX
+};
+
+#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1)
+
+struct ovs_nsh_key_base {
+	__u8 flags;
+	__u8 ttl;
+	__u8 mdtype;
+	__u8 np;
+	__be32 path_hdr;
+};
+
+#define NSH_MD1_CONTEXT_SIZE 4
+
+struct ovs_nsh_key_md1 {
+	__be32 context[NSH_MD1_CONTEXT_SIZE];
+};
+
 /**
  * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
  * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
@@ -806,6 +831,8 @@ struct ovs_action_push_eth {
  * packet.
  * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the
  * packet.
+ * @OVS_ACTION_ATTR_PUSH_NSH: push NSH header to the packet.
+ * @OVS_ACTION_ATTR_POP_NSH: pop the outermost NSH header off the packet.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -835,6 +862,8 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_TRUNC,        /* u32 struct ovs_action_trunc. */
 	OVS_ACTION_ATTR_PUSH_ETH,     /* struct ovs_action_push_eth. */
 	OVS_ACTION_ATTR_POP_ETH,      /* No argument. */
+	OVS_ACTION_ATTR_PUSH_NSH,     /* Nested OVS_NSH_KEY_ATTR_*. */
+	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
 
 	__OVS_ACTION_ATTR_MAX,	      /* Nothing past this will be accepted
 				       * from userspace. */
diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c
index 58fb827..54334ca 100644
--- a/net/nsh/nsh.c
+++ b/net/nsh/nsh.c
@@ -14,6 +14,59 @@
 #include <net/nsh.h>
 #include <net/tun_proto.h>
 
+int skb_push_nsh(struct sk_buff *skb, const struct nshhdr *src_nsh_hdr)
+{
+	struct nshhdr *nsh_hdr;
+	size_t length = nsh_hdr_len(src_nsh_hdr);
+	u8 next_proto;
+
+	if (skb->mac_len) {
+		next_proto = TUN_P_ETHERNET;
+	} else {
+		next_proto = tun_p_from_eth_p(skb->protocol);
+		if (!next_proto)
+			return -EAFNOSUPPORT;
+	}
+
+	/* Add the NSH header */
+	if (skb_cow_head(skb, length) < 0)
+		return -ENOMEM;
+
+	skb_push(skb, length);
+	nsh_hdr = (struct nshhdr *)(skb->data);
+	memcpy(nsh_hdr, src_nsh_hdr, length);
+	nsh_hdr->np = next_proto;
+
+	skb->protocol = htons(ETH_P_NSH);
+	skb_reset_mac_header(skb);
+	skb_reset_mac_len(skb);
+	skb_reset_network_header(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(skb_push_nsh);
+
+int skb_pop_nsh(struct sk_buff *skb)
+{
+	struct nshhdr *nsh_hdr = (struct nshhdr *)(skb->data);
+	size_t length;
+	u16 inner_proto;
+
+	inner_proto = tun_p_to_eth_p(nsh_hdr->np);
+	if (!inner_proto)
+		return -EAFNOSUPPORT;
+
+	length = nsh_hdr_len(nsh_hdr);
+	skb_pull(skb, length);
+	skb_reset_mac_header(skb);
+	skb_reset_mac_len(skb);
+	skb_reset_network_header(skb);
+	skb->protocol = inner_proto;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(skb_pop_nsh);
+
 static struct sk_buff *nsh_gso_segment(struct sk_buff *skb,
 				       netdev_features_t features)
 {
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index ce94729..2650205 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -14,6 +14,7 @@ config OPENVSWITCH
 	select MPLS
 	select NET_MPLS_GSO
 	select DST_CACHE
+	select NET_NSH
 	---help---
 	  Open vSwitch is a multilayer Ethernet switch targeted at virtualized
 	  environments.  In addition to supporting a variety of features
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index a54a556..d026b85 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -43,6 +43,7 @@
 #include "flow.h"
 #include "conntrack.h"
 #include "vport.h"
+#include "flow_netlink.h"
 
 struct deferred_action {
 	struct sk_buff *skb;
@@ -380,6 +381,45 @@ static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
 	return 0;
 }
 
+static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
+		    const struct nshhdr *src_nsh_hdr)
+{
+	int err;
+
+	err = skb_push_nsh(skb, src_nsh_hdr);
+	if (err)
+		return err;
+
+	key->eth.type = htons(ETH_P_NSH);
+
+	/* safe right before invalidate_flow_key */
+	key->mac_proto = MAC_PROTO_NONE;
+	invalidate_flow_key(key);
+	return 0;
+}
+
+static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+{
+	int err;
+
+	if (ovs_key_mac_proto(key) != MAC_PROTO_NONE ||
+	    skb->protocol != htons(ETH_P_NSH)) {
+		return -EINVAL;
+	}
+
+	err = skb_pop_nsh(skb);
+	if (err)
+		return err;
+
+	/* safe right before invalidate_flow_key */
+	if (skb->protocol == htons(ETH_P_TEB))
+		key->mac_proto = MAC_PROTO_ETHERNET;
+	else
+		key->mac_proto = MAC_PROTO_NONE;
+	invalidate_flow_key(key);
+	return 0;
+}
+
 static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
 				  __be32 addr, __be32 new_addr)
 {
@@ -602,6 +642,59 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key,
 	return 0;
 }
 
+static int set_nsh(struct sk_buff *skb, struct sw_flow_key *flow_key,
+		   const struct nlattr *a)
+{
+	struct nshhdr *nsh_hdr;
+	int err;
+	u8 flags;
+	u8 ttl;
+	int i;
+
+	struct ovs_key_nsh key;
+	struct ovs_key_nsh mask;
+
+	err = nsh_key_from_nlattr(a, &key, &mask);
+	if (err)
+		return err;
+
+	err = skb_ensure_writable(skb, skb_network_offset(skb) +
+				  sizeof(struct nshhdr));
+	if (unlikely(err))
+		return err;
+
+	nsh_hdr = (struct nshhdr *)skb_network_header(skb);
+
+	flags = nsh_get_flags(nsh_hdr);
+	flags = OVS_MASKED(flags, key.flags, mask.flags);
+	flow_key->nsh.flags = flags;
+	ttl = nsh_get_ttl(nsh_hdr);
+	ttl = OVS_MASKED(ttl, key.ttl, mask.ttl);
+	flow_key->nsh.ttl = ttl;
+	nsh_set_flags_and_ttl(nsh_hdr, flags, ttl);
+	nsh_hdr->path_hdr = OVS_MASKED(nsh_hdr->path_hdr, key.path_hdr,
+				       mask.path_hdr);
+	flow_key->nsh.path_hdr = nsh_hdr->path_hdr;
+	switch (nsh_hdr->mdtype) {
+	case NSH_M_TYPE1:
+		for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) {
+			nsh_hdr->md1.context[i] =
+			    OVS_MASKED(nsh_hdr->md1.context[i], key.context[i],
+				       mask.context[i]);
+		}
+		memcpy(flow_key->nsh.context, nsh_hdr->md1.context,
+		       sizeof(nsh_hdr->md1.context));
+		break;
+	case NSH_M_TYPE2:
+		memset(flow_key->nsh.context, 0,
+		       sizeof(flow_key->nsh.context));
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 /* Must follow skb_ensure_writable() since that can move the skb data. */
 static void set_tp_port(struct sk_buff *skb, __be16 *port,
 			__be16 new_port, __sum16 *check)
@@ -1024,6 +1117,10 @@ static int execute_masked_set_action(struct sk_buff *skb,
 				   get_mask(a, struct ovs_key_ethernet *));
 		break;
 
+	case OVS_KEY_ATTR_NSH:
+		err = set_nsh(skb, flow_key, a);
+		break;
+
 	case OVS_KEY_ATTR_IPV4:
 		err = set_ipv4(skb, flow_key, nla_data(a),
 			       get_mask(a, struct ovs_key_ipv4 *));
@@ -1210,6 +1307,21 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 		case OVS_ACTION_ATTR_POP_ETH:
 			err = pop_eth(skb, key);
 			break;
+
+		case OVS_ACTION_ATTR_PUSH_NSH: {
+			u8 buffer[NSH_HDR_MAX_LEN];
+			struct nshhdr *nsh_hdr = (struct nshhdr *)buffer;
+			const struct nshhdr *src_nsh_hdr = nsh_hdr;
+
+			nsh_hdr_from_nlattr(nla_data(a), nsh_hdr,
+					    NSH_HDR_MAX_LEN);
+			err = push_nsh(skb, key, src_nsh_hdr);
+			break;
+		}
+
+		case OVS_ACTION_ATTR_POP_NSH:
+			err = pop_nsh(skb, key);
+			break;
 		}
 
 		if (unlikely(err)) {
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 8c94cef..67fb6d9 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -46,6 +46,7 @@
 #include <net/ipv6.h>
 #include <net/mpls.h>
 #include <net/ndisc.h>
+#include <net/nsh.h>
 
 #include "conntrack.h"
 #include "datapath.h"
@@ -490,6 +491,52 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
 	return 0;
 }
 
+static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+{
+	struct nshhdr *nsh_hdr;
+	unsigned int nh_ofs = skb_network_offset(skb);
+	u8 version, length;
+	int err;
+
+	err = check_header(skb, nh_ofs + NSH_BASE_HDR_LEN);
+	if (unlikely(err))
+		return err;
+
+	nsh_hdr = (struct nshhdr *)skb_network_header(skb);
+	version = nsh_get_ver(nsh_hdr);
+	length = nsh_hdr_len(nsh_hdr);
+
+	if (version != 0)
+		return -EINVAL;
+
+	err = check_header(skb, nh_ofs + length);
+	if (unlikely(err))
+		return err;
+
+	nsh_hdr = (struct nshhdr *)skb_network_header(skb);
+	key->nsh.flags = nsh_get_flags(nsh_hdr);
+	key->nsh.ttl = nsh_get_ttl(nsh_hdr);
+	key->nsh.mdtype = nsh_hdr->mdtype;
+	key->nsh.np = nsh_hdr->np;
+	key->nsh.path_hdr = nsh_hdr->path_hdr;
+	switch (key->nsh.mdtype) {
+	case NSH_M_TYPE1:
+		if (length != NSH_M_TYPE1_LEN)
+			return -EINVAL;
+		memcpy(key->nsh.context, nsh_hdr->md1.context,
+		       sizeof(nsh_hdr->md1));
+		break;
+	case NSH_M_TYPE2:
+		memset(key->nsh.context, 0,
+		       sizeof(nsh_hdr->md1));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /**
  * key_extract - extracts a flow key from an Ethernet frame.
  * @skb: sk_buff that contains the frame, with skb->data pointing to the
@@ -735,6 +782,10 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 				memset(&key->tp, 0, sizeof(key->tp));
 			}
 		}
+	} else if (key->eth.type == htons(ETH_P_NSH)) {
+		error = parse_nsh(skb, key);
+		if (error)
+			return error;
 	}
 	return 0;
 }
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 1875bba..6a3cd9c 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -35,6 +35,7 @@
 #include <net/inet_ecn.h>
 #include <net/ip_tunnels.h>
 #include <net/dst_metadata.h>
+#include <net/nsh.h>
 
 struct sk_buff;
 
@@ -66,6 +67,15 @@ struct vlan_head {
 	(offsetof(struct sw_flow_key, recirc_id) +	\
 	FIELD_SIZEOF(struct sw_flow_key, recirc_id))
 
+struct ovs_key_nsh {
+	u8 flags;
+	u8 ttl;
+	u8 mdtype;
+	u8 np;
+	__be32 path_hdr;
+	__be32 context[NSH_MD1_CONTEXT_SIZE];
+};
+
 struct sw_flow_key {
 	u8 tun_opts[IP_TUNNEL_OPTS_MAX];
 	u8 tun_opts_len;
@@ -144,6 +154,7 @@ struct sw_flow_key {
 			};
 		} ipv6;
 	};
+	struct ovs_key_nsh nsh;         /* network service header */
 	struct {
 		/* Connection tracking fields not packed above. */
 		struct {
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index e8eb427..278bbb3 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -48,6 +48,7 @@
 #include <net/ndisc.h>
 #include <net/mpls.h>
 #include <net/vxlan.h>
+#include <net/tun_proto.h>
 
 #include "flow_netlink.h"
 
@@ -78,9 +79,11 @@ static bool actions_may_change_flow(const struct nlattr *actions)
 		case OVS_ACTION_ATTR_HASH:
 		case OVS_ACTION_ATTR_POP_ETH:
 		case OVS_ACTION_ATTR_POP_MPLS:
+		case OVS_ACTION_ATTR_POP_NSH:
 		case OVS_ACTION_ATTR_POP_VLAN:
 		case OVS_ACTION_ATTR_PUSH_ETH:
 		case OVS_ACTION_ATTR_PUSH_MPLS:
+		case OVS_ACTION_ATTR_PUSH_NSH:
 		case OVS_ACTION_ATTR_PUSH_VLAN:
 		case OVS_ACTION_ATTR_SAMPLE:
 		case OVS_ACTION_ATTR_SET:
@@ -322,12 +325,27 @@ size_t ovs_tun_key_attr_size(void)
 		+ nla_total_size(2);   /* OVS_TUNNEL_KEY_ATTR_TP_DST */
 }
 
+size_t ovs_nsh_key_attr_size(void)
+{
+	/* Whenever adding new OVS_NSH_KEY_ FIELDS, we should consider
+	 * updating this function.
+	 */
+	return  nla_total_size(NSH_BASE_HDR_LEN) /* OVS_NSH_KEY_ATTR_BASE */
+		/* OVS_NSH_KEY_ATTR_MD1 and OVS_NSH_KEY_ATTR_MD2 are
+		 * mutually exclusive, so the bigger one can cover
+		 * the small one.
+		 *
+		 * OVS_NSH_KEY_ATTR_MD2
+		 */
+		+ nla_total_size(NSH_CTX_HDRS_MAX_LEN);
+}
+
 size_t ovs_key_attr_size(void)
 {
 	/* Whenever adding new OVS_KEY_ FIELDS, we should consider
 	 * updating this function.
 	 */
-	BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 28);
+	BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29);
 
 	return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
 		+ nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */
@@ -341,6 +359,8 @@ size_t ovs_key_attr_size(void)
 		+ nla_total_size(4)   /* OVS_KEY_ATTR_CT_MARK */
 		+ nla_total_size(16)  /* OVS_KEY_ATTR_CT_LABELS */
 		+ nla_total_size(40)  /* OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6 */
+		+ nla_total_size(0)   /* OVS_KEY_ATTR_NSH */
+		  + ovs_nsh_key_attr_size()
 		+ nla_total_size(12)  /* OVS_KEY_ATTR_ETHERNET */
 		+ nla_total_size(2)   /* OVS_KEY_ATTR_ETHERTYPE */
 		+ nla_total_size(4)   /* OVS_KEY_ATTR_VLAN */
@@ -373,6 +393,13 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
 	[OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = sizeof(struct in6_addr) },
 };
 
+static const struct ovs_len_tbl
+ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
+	[OVS_NSH_KEY_ATTR_BASE] = { .len = sizeof(struct ovs_nsh_key_base) },
+	[OVS_NSH_KEY_ATTR_MD1]  = { .len = sizeof(struct ovs_nsh_key_md1) },
+	[OVS_NSH_KEY_ATTR_MD2]  = { .len = OVS_ATTR_VARIABLE },
+};
+
 /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute.  */
 static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 	[OVS_KEY_ATTR_ENCAP]	 = { .len = OVS_ATTR_NESTED },
@@ -405,6 +432,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
 		.len = sizeof(struct ovs_key_ct_tuple_ipv4) },
 	[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = {
 		.len = sizeof(struct ovs_key_ct_tuple_ipv6) },
+	[OVS_KEY_ATTR_NSH]       = { .len = OVS_ATTR_NESTED,
+				     .next = ovs_nsh_key_attr_lens, },
 };
 
 static bool check_attr_len(unsigned int attr_len, unsigned int expected_len)
@@ -1179,6 +1208,222 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match,
 	return 0;
 }
 
+int nsh_hdr_from_nlattr(const struct nlattr *attr,
+			struct nshhdr *nsh, size_t size)
+{
+	struct nlattr *a;
+	int rem;
+	u8 flags = 0;
+	u8 ttl = 0;
+	int mdlen = 0;
+
+	/* validate_nsh has check this, so we needn't do duplicate check here
+	 */
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+
+		switch (type) {
+		case OVS_NSH_KEY_ATTR_BASE: {
+			const struct ovs_nsh_key_base *base =
+				(struct ovs_nsh_key_base *)nla_data(a);
+			flags = base->flags;
+			ttl = base->ttl;
+			nsh->np = base->np;
+			nsh->mdtype = base->mdtype;
+			nsh->path_hdr = base->path_hdr;
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD1: {
+			const struct ovs_nsh_key_md1 *md1 =
+				(struct ovs_nsh_key_md1 *)nla_data(a);
+			struct nsh_md1_ctx *md1_dst = &nsh->md1;
+
+			mdlen = nla_len(a);
+			memcpy(md1_dst, md1, mdlen);
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD2: {
+			const struct u8 *md2 = nla_data(a);
+			struct nsh_md2_tlv *md2_dst = &nsh->md2;
+
+			mdlen = nla_len(a);
+			memcpy(md2_dst, md2, mdlen);
+			break;
+		}
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* nsh header length  = NSH_BASE_HDR_LEN + mdlen */
+	nsh->ver_flags_ttl_len = 0;
+	nsh_set_flags_ttl_len(nsh, flags, ttl, NSH_BASE_HDR_LEN + mdlen);
+
+	return 0;
+}
+
+int nsh_key_from_nlattr(const struct nlattr *attr,
+			struct ovs_key_nsh *nsh, struct ovs_key_nsh *nsh_mask)
+{
+	struct nlattr *a;
+	int rem;
+
+	/* validate_nsh has check this, so we needn't do duplicate check here
+	 */
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+
+		switch (type) {
+		case OVS_NSH_KEY_ATTR_BASE: {
+			const struct ovs_nsh_key_base *base =
+				(struct ovs_nsh_key_base *)nla_data(a);
+			const struct ovs_nsh_key_base *base_mask = base + 1;
+
+			memcpy(nsh, base, sizeof(*base));
+			memcpy(nsh, base_mask, sizeof(*base_mask));
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD1: {
+			const struct ovs_nsh_key_md1 *md1 =
+				(struct ovs_nsh_key_md1 *)nla_data(a);
+			const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
+
+			memcpy(nsh->context, md1->context, sizeof(*md1));
+			memcpy(nsh_mask->context, md1_mask->context,
+			       sizeof(*md1_mask));
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD2:
+			/* Not supported yet */
+			return -ENOTSUPP;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int nsh_key_put_from_nlattr(const struct nlattr *attr,
+				   struct sw_flow_match *match, bool is_mask,
+				   bool is_push_nsh, bool log)
+{
+	struct nlattr *a;
+	int rem;
+	bool has_base = false;
+	bool has_md1 = false;
+	bool has_md2 = false;
+	u8 mdtype = 0;
+	int mdlen = 0;
+
+	if (unlikely(is_push_nsh && is_mask))
+		return -EINVAL;
+
+	nla_for_each_nested(a, attr, rem) {
+		int type = nla_type(a);
+		int i;
+
+		if (type > OVS_NSH_KEY_ATTR_MAX) {
+			OVS_NLERR(log, "nsh attr %d is out of range max %d",
+				  type, OVS_NSH_KEY_ATTR_MAX);
+			return -EINVAL;
+		}
+
+		if (!check_attr_len(nla_len(a),
+				    ovs_nsh_key_attr_lens[type].len)) {
+			OVS_NLERR(
+			    log,
+			    "nsh attr %d has unexpected len %d expected %d",
+			    type,
+			    nla_len(a),
+			    ovs_nsh_key_attr_lens[type].len
+			);
+			return -EINVAL;
+		}
+
+		switch (type) {
+		case OVS_NSH_KEY_ATTR_BASE: {
+			const struct ovs_nsh_key_base *base =
+				(struct ovs_nsh_key_base *)nla_data(a);
+
+			has_base = true;
+			mdtype = base->mdtype;
+			SW_FLOW_KEY_PUT(match, nsh.flags,
+					base->flags, is_mask);
+			SW_FLOW_KEY_PUT(match, nsh.ttl,
+					base->ttl, is_mask);
+			SW_FLOW_KEY_PUT(match, nsh.mdtype,
+					base->mdtype, is_mask);
+			SW_FLOW_KEY_PUT(match, nsh.np,
+					base->np, is_mask);
+			SW_FLOW_KEY_PUT(match, nsh.path_hdr,
+					base->path_hdr, is_mask);
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD1: {
+			const struct ovs_nsh_key_md1 *md1 =
+				(struct ovs_nsh_key_md1 *)nla_data(a);
+
+			has_md1 = true;
+			for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++)
+				SW_FLOW_KEY_PUT(match, nsh.context[i],
+						md1->context[i], is_mask);
+			break;
+		}
+		case OVS_NSH_KEY_ATTR_MD2:
+			if (!is_push_nsh) /* Not supported MD type 2 yet */
+				return -ENOTSUPP;
+
+			has_md2 = true;
+			mdlen = nla_len(a);
+			if ((mdlen > NSH_CTX_HDRS_MAX_LEN) ||
+			    (mdlen <= 0)) {
+				WARN_ON_ONCE(1);
+				return -EINVAL;
+			}
+			break;
+		default:
+			OVS_NLERR(log, "Unknown nsh attribute %d",
+				  type);
+			return -EINVAL;
+		}
+	}
+
+	if (rem > 0) {
+		OVS_NLERR(log, "nsh attribute has %d unknown bytes.", rem);
+		return -EINVAL;
+	}
+
+	if (has_md1 && has_md2) {
+		OVS_NLERR(
+		    1,
+		    "invalid nsh attribute: md1 and md2 are exclusive."
+		);
+		return -EINVAL;
+	}
+
+	if (!is_mask) {
+		if ((has_md1 && mdtype != NSH_M_TYPE1) ||
+		    (has_md2 && mdtype != NSH_M_TYPE2)) {
+			OVS_NLERR(1, "nsh attribute has unmatched MD type %d.",
+				  mdtype);
+			return -EINVAL;
+		}
+
+		if (is_push_nsh &&
+		    (!has_base || (!has_md1 && !has_md2))) {
+			OVS_NLERR(
+			    1,
+			    "push nsh attributes are invalid for type %d.",
+			    mdtype
+			);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 				u64 attrs, const struct nlattr **a,
 				bool is_mask, bool log)
@@ -1306,6 +1551,13 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
 		attrs &= ~(1 << OVS_KEY_ATTR_ARP);
 	}
 
+	if (attrs & (1 << OVS_KEY_ATTR_NSH)) {
+		if (nsh_key_put_from_nlattr(a[OVS_KEY_ATTR_NSH], match,
+					    is_mask, false, log) < 0)
+			return -EINVAL;
+		attrs &= ~(1 << OVS_KEY_ATTR_NSH);
+	}
+
 	if (attrs & (1 << OVS_KEY_ATTR_MPLS)) {
 		const struct ovs_key_mpls *mpls_key;
 
@@ -1622,6 +1874,40 @@ static int ovs_nla_put_vlan(struct sk_buff *skb, const struct vlan_head *vh,
 	return 0;
 }
 
+static int nsh_key_to_nlattr(const struct ovs_key_nsh *nsh, bool is_mask,
+			     struct sk_buff *skb)
+{
+	struct nlattr *start;
+	struct ovs_nsh_key_base base;
+	struct ovs_nsh_key_md1 md1;
+
+	memcpy(&base, nsh, sizeof(base));
+
+	if (is_mask || nsh->mdtype == NSH_M_TYPE1)
+		memcpy(md1.context, nsh->context, sizeof(md1));
+
+	start = nla_nest_start(skb, OVS_KEY_ATTR_NSH);
+	if (!start)
+		return -EMSGSIZE;
+
+	if (nla_put(skb, OVS_NSH_KEY_ATTR_BASE, sizeof(base), &base))
+		goto nla_put_failure;
+
+	if (is_mask || nsh->mdtype == NSH_M_TYPE1) {
+		if (nla_put(skb, OVS_NSH_KEY_ATTR_MD1, sizeof(md1), &md1))
+			goto nla_put_failure;
+	}
+
+	/* Don't support MD type 2 yet */
+
+	nla_nest_end(skb, start);
+
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
 static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 			     const struct sw_flow_key *output, bool is_mask,
 			     struct sk_buff *skb)
@@ -1750,6 +2036,9 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
 		ipv6_key->ipv6_tclass = output->ip.tos;
 		ipv6_key->ipv6_hlimit = output->ip.ttl;
 		ipv6_key->ipv6_frag = output->ip.frag;
+	} else if (swkey->eth.type == htons(ETH_P_NSH)) {
+		if (nsh_key_to_nlattr(&output->nsh, is_mask, skb))
+			goto nla_put_failure;
 	} else if (swkey->eth.type == htons(ETH_P_ARP) ||
 		   swkey->eth.type == htons(ETH_P_RARP)) {
 		struct ovs_key_arp *arp_key;
@@ -2242,6 +2531,19 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
 	return err;
 }
 
+static bool validate_nsh(const struct nlattr *attr, bool is_mask,
+			 bool is_push_nsh, bool log)
+{
+	struct sw_flow_match match;
+	struct sw_flow_key key;
+	int ret = 0;
+
+	ovs_match_init(&match, &key, true, NULL);
+	ret = nsh_key_put_from_nlattr(attr, &match, is_mask,
+				      is_push_nsh, log);
+	return ((ret != 0) ? false : true);
+}
+
 /* Return false if there are any non-masked bits set.
  * Mask follows data immediately, before any netlink padding.
  */
@@ -2384,6 +2686,11 @@ static int validate_set(const struct nlattr *a,
 
 		break;
 
+	case OVS_KEY_ATTR_NSH:
+		if (!validate_nsh(nla_data(a), masked, false, log))
+			return -EINVAL;
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -2482,6 +2789,8 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
 			[OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc),
 			[OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth),
 			[OVS_ACTION_ATTR_POP_ETH] = 0,
+			[OVS_ACTION_ATTR_PUSH_NSH] = (u32)-1,
+			[OVS_ACTION_ATTR_POP_NSH] = 0,
 		};
 		const struct ovs_action_push_vlan *vlan;
 		int type = nla_type(a);
@@ -2636,6 +2945,19 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
 			mac_proto = MAC_PROTO_ETHERNET;
 			break;
 
+		case OVS_ACTION_ATTR_PUSH_NSH:
+			mac_proto = MAC_PROTO_NONE;
+			if (!validate_nsh(nla_data(a), false, true, true))
+				return -EINVAL;
+			break;
+
+		case OVS_ACTION_ATTR_POP_NSH:
+			if (key->nsh.np == TUN_P_ETHERNET)
+				mac_proto = MAC_PROTO_ETHERNET;
+			else
+				mac_proto = MAC_PROTO_NONE;
+			break;
+
 		default:
 			OVS_NLERR(log, "Unknown Action type %d", type);
 			return -EINVAL;
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 929c665..4b80083 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -79,4 +79,9 @@ int ovs_nla_put_actions(const struct nlattr *attr,
 void ovs_nla_free_flow_actions(struct sw_flow_actions *);
 void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *);
 
+int nsh_key_from_nlattr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
+			struct ovs_key_nsh *nsh_mask);
+int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *src_nsh_hdr,
+			size_t size);
+
 #endif /* flow_netlink.h */
-- 
2.5.5

^ permalink raw reply related

* Re: [PATCH net] tcp: update skb->skb_mstamp more carefully
From: liujian @ 2017-09-14  8:17 UTC (permalink / raw)
  To: Eric Dumazet, David Miller
  Cc: edumazet, ycheng, hkchu, netdev, weiyongjun1, wangkefeng 00227729
In-Reply-To: <1505359839.15310.209.camel@edumazet-glaptop3.roam.corp.google.com>



On 2017/9/14 11:30, Eric Dumazet worte:
> From: Eric Dumazet <edumazet@googl.com>
> 
> liujian reported a problem in TCP_USER_TIMEOUT processing with a patch
> in tcp_probe_timer() :
>       https://www.spinics.net/lists/netdev/msg454496.html
> 
> After investigations, the root cause of the problem is that we update
> skb->skb_mstamp of skbs in write queue, even if the attempt to send a
> clone or copy of it failed. One reason being a routing problem.
> 
> This patch prevents this, solving liujian issue.
> 
> It also removes a potential RTT miscalculation, since
> __tcp_retransmit_skb() is not OR-ing TCP_SKB_CB(skb)->sacked with
> TCPCB_EVER_RETRANS if a failure happens, but skb->skb_mstamp has
> been changed.
> 
> A future ACK would then lead to a very small RTT sample and min_rtt
> would then be lowered to this too small value.
> 
>

I test  on 4.13, it can work.
thank you!

^ permalink raw reply

* Re: [RFC PATCH v3 7/7] i40e: Enable cloud filters via tc-flower
From: Nambiar, Amritha @ 2017-09-14  8:00 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: intel-wired-lan, jeffrey.t.kirsher, alexander.h.duyck, netdev,
	mlxsw
In-Reply-To: <20170913132611.GC1981@nanopsycho>

On 9/13/2017 6:26 AM, Jiri Pirko wrote:
> Wed, Sep 13, 2017 at 11:59:50AM CEST, amritha.nambiar@intel.com wrote:
>> This patch enables tc-flower based hardware offloads. tc flower
>> filter provided by the kernel is configured as driver specific
>> cloud filter. The patch implements functions and admin queue
>> commands needed to support cloud filters in the driver and
>> adds cloud filters to configure these tc-flower filters.
>>
>> The only action supported is to redirect packets to a traffic class
>> on the same device.
> 
> So basically you are not doing redirect, you are just setting tclass for
> matched packets, right? Why you use mirred for this? I think that
> you might consider extending g_act for that:
> 
> # tc filter add dev eth0 protocol ip ingress \
>   prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw \
>   action tclass 0
> 
Yes, this doesn't work like a typical egress redirect, but is aimed at
forwarding the matched packets to a different queue-group/traffic class
on the same device, so some sort-of ingress redirect in the hardware. I
possibly may not need the mirred-redirect as you say, I'll look into the
g_act way of doing this with a new gact tc action.

> 
>>
>> # tc qdisc add dev eth0 ingress
>> # ethtool -K eth0 hw-tc-offload on
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>  prio 1 flower dst_mac 3c:fd:fe:a0:d6:70 skip_sw\
>>  action mirred ingress redirect dev eth0 tclass 0
>>
>> # tc filter add dev eth0 protocol ip parent ffff:\
>>  prio 2 flower dst_ip 192.168.3.5/32\
>>  ip_proto udp dst_port 25 skip_sw\
>>  action mirred ingress redirect dev eth0 tclass 1
>>
>> # tc filter add dev eth0 protocol ipv6 parent ffff:\
>>  prio 3 flower dst_ip fe8::200:1\
>>  ip_proto udp dst_port 66 skip_sw\
>>  action mirred ingress redirect dev eth0 tclass 1
>>
>> Delete tc flower filter:
>> Example:
>>
>> # tc filter del dev eth0 parent ffff: prio 3 handle 0x1 flower
>> # tc filter del dev eth0 parent ffff:
>>
>> Flow Director Sideband is disabled while configuring cloud filters
>> via tc-flower and until any cloud filter exists.
>>
>> Unsupported matches when cloud filters are added using enhanced
>> big buffer cloud filter mode of underlying switch include:
>> 1. source port and source IP
>> 2. Combined MAC address and IP fields.
>> 3. Not specifying L4 port
>>
>> These filter matches can however be used to redirect traffic to
>> the main VSI (tc 0) which does not require the enhanced big buffer
>> cloud filter support.
>>
>> v3: Cleaned up some lengthy function names. Changed ipv6 address to
>> __be32 array instead of u8 array. Used macro for IP version. Minor
>> formatting changes.
>> v2:
>> 1. Moved I40E_SWITCH_MODE_MASK definition to i40e_type.h
>> 2. Moved dev_info for add/deleting cloud filters in else condition
>> 3. Fixed some format specifier in dev_err logs
>> 4. Refactored i40e_get_capabilities to take an additional
>>   list_type parameter and use it to query device and function
>>   level capabilities.
>> 5. Fixed parsing tc redirect action to check for the is_tcf_mirred_tc()
>>   to verify if redirect to a traffic class is supported.
>> 6. Added comments for Geneve fix in cloud filter big buffer AQ
>>   function definitions.
>> 7. Cleaned up setup_tc interface to rebase and work with Jiri's
>>   updates, separate function to process tc cls flower offloads.
>> 8. Changes to make Flow Director Sideband and Cloud filters mutually
>>   exclusive.
>>
>> Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
>> Signed-off-by: Kiran Patil <kiran.patil@intel.com>
>> Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
>> Signed-off-by: Jingjing Wu <jingjing.wu@intel.com>
>> ---
>> drivers/net/ethernet/intel/i40e/i40e.h             |   49 +
>> drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h  |    3 
>> drivers/net/ethernet/intel/i40e/i40e_common.c      |  189 ++++
>> drivers/net/ethernet/intel/i40e/i40e_main.c        |  971 +++++++++++++++++++-
>> drivers/net/ethernet/intel/i40e/i40e_prototype.h   |   16 
>> drivers/net/ethernet/intel/i40e/i40e_type.h        |    1 
>> .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h    |    3 
>> 7 files changed, 1202 insertions(+), 30 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
>> index 6018fb6..b110519 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e.h
>> @@ -55,6 +55,8 @@
>> #include <linux/net_tstamp.h>
>> #include <linux/ptp_clock_kernel.h>
>> #include <net/pkt_cls.h>
>> +#include <net/tc_act/tc_gact.h>
>> +#include <net/tc_act/tc_mirred.h>
>> #include "i40e_type.h"
>> #include "i40e_prototype.h"
>> #include "i40e_client.h"
>> @@ -252,9 +254,52 @@ struct i40e_fdir_filter {
>> 	u32 fd_id;
>> };
>>
>> +#define IPV4_VERSION 4
>> +#define IPV6_VERSION 6
>> +
>> +#define I40E_CLOUD_FIELD_OMAC	0x01
>> +#define I40E_CLOUD_FIELD_IMAC	0x02
>> +#define I40E_CLOUD_FIELD_IVLAN	0x04
>> +#define I40E_CLOUD_FIELD_TEN_ID	0x08
>> +#define I40E_CLOUD_FIELD_IIP	0x10
>> +
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC	I40E_CLOUD_FIELD_OMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC	I40E_CLOUD_FIELD_IMAC
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_IVLAN)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID	(I40E_CLOUD_FIELD_IMAC | \
>> +						 I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC (I40E_CLOUD_FIELD_OMAC | \
>> +						  I40E_CLOUD_FIELD_IMAC | \
>> +						  I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID (I40E_CLOUD_FIELD_IMAC | \
>> +						   I40E_CLOUD_FIELD_IVLAN | \
>> +						   I40E_CLOUD_FIELD_TEN_ID)
>> +#define I40E_CLOUD_FILTER_FLAGS_IIP	I40E_CLOUD_FIELD_IIP
>> +
>> struct i40e_cloud_filter {
>> 	struct hlist_node cloud_node;
>> 	unsigned long cookie;
>> +	/* cloud filter input set follows */
>> +	u8 dst_mac[ETH_ALEN];
>> +	u8 src_mac[ETH_ALEN];
>> +	__be16 vlan_id;
>> +	__be32 dst_ip;
>> +	__be32 src_ip;
>> +	__be32 dst_ipv6[4];
>> +	__be32 src_ipv6[4];
>> +	__be16 dst_port;
>> +	__be16 src_port;
>> +	u32 ip_version;
>> +	u8 ip_proto;	/* IPPROTO value */
>> +	/* L4 port type: src or destination port */
>> +#define I40E_CLOUD_FILTER_PORT_SRC	0x01
>> +#define I40E_CLOUD_FILTER_PORT_DEST	0x02
>> +	u8 port_type;
>> +	u32 tenant_id;
>> +	u8 flags;
>> +#define I40E_CLOUD_TNL_TYPE_NONE	0xff
>> +	u8 tunnel_type;
>> 	u16 seid;	/* filter control */
>> };
>>
>> @@ -491,6 +536,8 @@ struct i40e_pf {
>> #define I40E_FLAG_LINK_DOWN_ON_CLOSE_ENABLED	BIT(27)
>> #define I40E_FLAG_SOURCE_PRUNING_DISABLED	BIT(28)
>> #define I40E_FLAG_TC_MQPRIO			BIT(29)
>> +#define I40E_FLAG_FD_SB_INACTIVE		BIT(30)
>> +#define I40E_FLAG_FD_SB_TO_CLOUD_FILTER		BIT(31)
>>
>> 	struct i40e_client_instance *cinst;
>> 	bool stat_offsets_loaded;
>> @@ -573,6 +620,8 @@ struct i40e_pf {
>> 	u16 phy_led_val;
>>
>> 	u16 override_q_count;
>> +	u16 last_sw_conf_flags;
>> +	u16 last_sw_conf_valid_flags;
>> };
>>
>> /**
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> index 2e567c2..feb3d42 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h
>> @@ -1392,6 +1392,9 @@ struct i40e_aqc_cloud_filters_element_data {
>> 		struct {
>> 			u8 data[16];
>> 		} v6;
>> +		struct {
>> +			__le16 data[8];
>> +		} raw_v6;
>> 	} ipaddr;
>> 	__le16	flags;
>> #define I40E_AQC_ADD_CLOUD_FILTER_SHIFT			0
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> index 9567702..d9c9665 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_common.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c
>> @@ -5434,5 +5434,194 @@ i40e_add_pinfo_to_list(struct i40e_hw *hw,
>>
>> 	status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end,
>> 				   track_id, &offset, &info, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_cloud_filters_element_data are filled in by the caller
>> + * of the function.
>> + *
>> + **/
>> +enum i40e_status_code
>> +i40e_aq_add_cloud_filters(struct i40e_hw *hw, u16 seid,
>> +			  struct i40e_aqc_cloud_filters_element_data *filters,
>> +			  u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_add_cloud_filters_bb
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to add cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be added
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Set the big buffer cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_cloud_filters_element_bb are filled in by the caller of the
>> + * function.
>> + *
>> + **/
>> +i40e_status
>> +i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
>> +			     struct i40e_aqc_cloud_filters_element_bb *filters,
>> +			     u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_add_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +
>> +		/* For Geneve, the VNI should be placed in offset shifted by a
>> +		 * byte than the offset for the Tenant ID for rest of the
>> +		 * tunnels.
>> +		 */
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_rem_cloud_filters
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_cloud_filters_element_data are filled in by the caller
>> + * of the function.
>> + *
>> + **/
>> +enum i40e_status_code
>> +i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 seid,
>> +			  struct i40e_aqc_cloud_filters_element_data *filters,
>> +			  u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	enum i40e_status_code status;
>> +	u16 buff_len;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> +	return status;
>> +}
>> +
>> +/**
>> + * i40e_aq_rem_cloud_filters_bb
>> + * @hw: pointer to the hardware structure
>> + * @seid: VSI seid to remove cloud filters from
>> + * @filters: Buffer which contains the filters in big buffer to be removed
>> + * @filter_count: number of filters contained in the buffer
>> + *
>> + * Remove the big buffer cloud filters for a given VSI.  The contents of the
>> + * i40e_aqc_cloud_filters_element_bb are filled in by the caller of the
>> + * function.
>> + *
>> + **/
>> +i40e_status
>> +i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
>> +			     struct i40e_aqc_cloud_filters_element_bb *filters,
>> +			     u8 filter_count)
>> +{
>> +	struct i40e_aq_desc desc;
>> +	struct i40e_aqc_add_remove_cloud_filters *cmd =
>> +	(struct i40e_aqc_add_remove_cloud_filters *)&desc.params.raw;
>> +	i40e_status status;
>> +	u16 buff_len;
>> +	int i;
>> +
>> +	i40e_fill_default_direct_cmd_desc(&desc,
>> +					  i40e_aqc_opc_remove_cloud_filters);
>> +
>> +	buff_len = filter_count * sizeof(*filters);
>> +	desc.datalen = cpu_to_le16(buff_len);
>> +	desc.flags |= cpu_to_le16((u16)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
>> +	cmd->num_filters = filter_count;
>> +	cmd->seid = cpu_to_le16(seid);
>> +	cmd->big_buffer_flag = I40E_AQC_ADD_CLOUD_CMD_BB;
>> +
>> +	for (i = 0; i < filter_count; i++) {
>> +		u16 tnl_type;
>> +		u32 ti;
>> +
>> +		tnl_type = (le16_to_cpu(filters[i].element.flags) &
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_MASK) >>
>> +			   I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT;
>> +
>> +		/* For Geneve, the VNI should be placed in offset shifted by a
>> +		 * byte than the offset for the Tenant ID for rest of the
>> +		 * tunnels.
>> +		 */
>> +		if (tnl_type == I40E_AQC_ADD_CLOUD_TNL_TYPE_GENEVE) {
>> +			ti = le32_to_cpu(filters[i].element.tenant_id);
>> +			filters[i].element.tenant_id = cpu_to_le32(ti << 8);
>> +		}
>> +	}
>> +
>> +	status = i40e_asq_send_command(hw, &desc, filters, buff_len, NULL);
>> +
>> 	return status;
>> }
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> index afcf08a..96ee608 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_main.c
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
>> @@ -69,6 +69,15 @@ static int i40e_reset(struct i40e_pf *pf);
>> static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
>> static void i40e_fdir_sb_setup(struct i40e_pf *pf);
>> static int i40e_veb_get_bw_info(struct i40e_veb *veb);
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter,
>> +				     bool add);
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add);
>> +static int i40e_get_capabilities(struct i40e_pf *pf,
>> +				 enum i40e_admin_queue_opc list_type);
>> +
>>
>> /* i40e_pci_tbl - PCI Device ID Table
>>  *
>> @@ -5478,7 +5487,11 @@ int i40e_set_bw_limit(struct i40e_vsi *vsi, u16 seid, u64 max_tx_rate)
>>  **/
>> static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>> {
>> +	enum i40e_admin_queue_err last_aq_status;
>> +	struct i40e_cloud_filter *cfilter;
>> 	struct i40e_channel *ch, *ch_tmp;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>> 	int ret, i;
>>
>> 	/* Reset rss size that was stored when reconfiguring rss for
>> @@ -5519,6 +5532,29 @@ static void i40e_remove_queue_channels(struct i40e_vsi *vsi)
>> 				 "Failed to reset tx rate for ch->seid %u\n",
>> 				 ch->seid);
>>
>> +		/* delete cloud filters associated with this channel */
>> +		hlist_for_each_entry_safe(cfilter, node,
>> +					  &pf->cloud_filter_list, cloud_node) {
>> +			if (cfilter->seid != ch->seid)
>> +				continue;
>> +
>> +			hash_del(&cfilter->cloud_node);
>> +			if (cfilter->dst_port)
>> +				ret = i40e_add_del_cloud_filter_big_buf(vsi,
>> +									cfilter,
>> +									false);
>> +			else
>> +				ret = i40e_add_del_cloud_filter(vsi, cfilter,
>> +								false);
>> +			last_aq_status = pf->hw.aq.asq_last_status;
>> +			if (ret)
>> +				dev_info(&pf->pdev->dev,
>> +					 "Failed to delete cloud filter, err %s aq_err %s\n",
>> +					 i40e_stat_str(&pf->hw, ret),
>> +					 i40e_aq_str(&pf->hw, last_aq_status));
>> +			kfree(cfilter);
>> +		}
>> +
>> 		/* delete VSI from FW */
>> 		ret = i40e_aq_delete_element(&vsi->back->hw, ch->seid,
>> 					     NULL);
>> @@ -5970,6 +6006,74 @@ static bool i40e_setup_channel(struct i40e_pf *pf, struct i40e_vsi *vsi,
>> }
>>
>> /**
>> + * i40e_validate_and_set_switch_mode - sets up switch mode correctly
>> + * @vsi: ptr to VSI which has PF backing
>> + * @l4type: true for TCP ond false for UDP
>> + * @port_type: true if port is destination and false if port is source
>> + *
>> + * Sets up switch mode correctly if it needs to be changed and perform
>> + * what are allowed modes.
>> + **/
>> +static int i40e_validate_and_set_switch_mode(struct i40e_vsi *vsi, bool l4type,
>> +					     bool port_type)
>> +{
>> +	u8 mode;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct i40e_hw *hw = &pf->hw;
>> +	int ret;
>> +
>> +	ret = i40e_get_capabilities(pf, i40e_aqc_opc_list_dev_capabilities);
>> +	if (ret)
>> +		return -EINVAL;
>> +
>> +	if (hw->dev_caps.switch_mode) {
>> +		/* if switch mode is set, support mode2 (non-tunneled for
>> +		 * cloud filter) for now
>> +		 */
>> +		u32 switch_mode = hw->dev_caps.switch_mode &
>> +							I40E_SWITCH_MODE_MASK;
>> +		if (switch_mode >= I40E_NVM_IMAGE_TYPE_MODE1) {
>> +			if (switch_mode == I40E_NVM_IMAGE_TYPE_MODE2)
>> +				return 0;
>> +			dev_err(&pf->pdev->dev,
>> +				"Invalid switch_mode (%d), only non-tunneled mode for cloud filter is supported\n",
>> +				hw->dev_caps.switch_mode);
>> +			return -EINVAL;
>> +		}
>> +	}
>> +
>> +	/* port_type: true for destination port and false for source port
>> +	 * For now, supports only destination port type
>> +	 */
>> +	if (!port_type) {
>> +		dev_err(&pf->pdev->dev, "src port type not supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Set Bit 7 to be valid */
>> +	mode = I40E_AQ_SET_SWITCH_BIT7_VALID;
>> +
>> +	/* Set L4type to both TCP and UDP support */
>> +	mode |= I40E_AQ_SET_SWITCH_L4_TYPE_BOTH;
>> +
>> +	/* Set cloud filter mode */
>> +	mode |= I40E_AQ_SET_SWITCH_MODE_NON_TUNNEL;
>> +
>> +	/* Prep mode field for set_switch_config */
>> +	ret = i40e_aq_set_switch_config(hw, pf->last_sw_conf_flags,
>> +					pf->last_sw_conf_valid_flags,
>> +					mode, NULL);
>> +	if (ret && hw->aq.asq_last_status != I40E_AQ_RC_ESRCH)
>> +		dev_err(&pf->pdev->dev,
>> +			"couldn't set switch config bits, err %s aq_err %s\n",
>> +			i40e_stat_str(hw, ret),
>> +			i40e_aq_str(hw,
>> +				    hw->aq.asq_last_status));
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>>  * i40e_create_queue_channel - function to create channel
>>  * @vsi: VSI to be configured
>>  * @ch: ptr to channel (it contains channel specific params)
>> @@ -6735,13 +6839,726 @@ static int i40e_setup_tc(struct net_device *netdev, void *type_data)
>> 	return ret;
>> }
>>
>> +/**
>> + * i40e_set_cld_element - sets cloud filter element data
>> + * @filter: cloud filter rule
>> + * @cld: ptr to cloud filter element data
>> + *
>> + * This is helper function to copy data into cloud filter element
>> + **/
>> +static inline void
>> +i40e_set_cld_element(struct i40e_cloud_filter *filter,
>> +		     struct i40e_aqc_cloud_filters_element_data *cld)
>> +{
>> +	int i, j;
>> +	u32 ipa;
>> +
>> +	memset(cld, 0, sizeof(*cld));
>> +	ether_addr_copy(cld->outer_mac, filter->dst_mac);
>> +	ether_addr_copy(cld->inner_mac, filter->src_mac);
>> +
>> +	if (filter->ip_version == IPV6_VERSION) {
>> +#define IPV6_MAX_INDEX	(ARRAY_SIZE(filter->dst_ipv6) - 1)
>> +		for (i = 0, j = 0; i < 4; i++, j += 2) {
>> +			ipa = be32_to_cpu(filter->dst_ipv6[IPV6_MAX_INDEX - i]);
>> +			ipa = cpu_to_le32(ipa);
>> +			memcpy(&cld->ipaddr.raw_v6.data[j], &ipa, 4);
>> +		}
>> +	} else {
>> +		ipa = be32_to_cpu(filter->dst_ip);
>> +		memcpy(&cld->ipaddr.v4.data, &ipa, 4);
>> +	}
>> +
>> +	cld->inner_vlan = cpu_to_le16(ntohs(filter->vlan_id));
>> +
>> +	/* tenant_id is not supported by FW now, once the support is enabled
>> +	 * fill the cld->tenant_id with cpu_to_le32(filter->tenant_id)
>> +	 */
>> +	if (filter->tenant_id)
>> +		return;
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter - Add/del cloud filter
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter(struct i40e_vsi *vsi,
>> +				     struct i40e_cloud_filter *filter, bool add)
>> +{
>> +	struct i40e_aqc_cloud_filters_element_data cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +	static const u16 flag_table[128] = {
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN]  =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_OMAC_TEN_ID_IMAC] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_OMAC_TEN_ID_IMAC,
>> +		[I40E_CLOUD_FILTER_FLAGS_IMAC_IVLAN_TEN_ID] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IMAC_IVLAN_TEN_ID,
>> +		[I40E_CLOUD_FILTER_FLAGS_IIP] =
>> +			I40E_AQC_ADD_CLOUD_FILTER_IIP,
>> +	};
>> +
>> +	if (filter->flags >= ARRAY_SIZE(flag_table))
>> +		return I40E_ERR_CONFIG;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter);
>> +
>> +	if (filter->tunnel_type != I40E_CLOUD_TNL_TYPE_NONE)
>> +		cld_filter.flags = cpu_to_le16(filter->tunnel_type <<
>> +					     I40E_AQC_ADD_CLOUD_TNL_TYPE_SHIFT);
>> +
>> +	if (filter->ip_version == IPV6_VERSION)
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +	else
>> +		cld_filter.flags |= cpu_to_le16(flag_table[filter->flags] |
>> +						I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
>> +
>> +	if (add)
>> +		ret = i40e_aq_add_cloud_filters(&pf->hw, filter->seid,
>> +						&cld_filter, 1);
>> +	else
>> +		ret = i40e_aq_rem_cloud_filters(&pf->hw, filter->seid,
>> +						&cld_filter, 1);
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter using l4 port %u, err %d aq_err %d\n",
>> +			add ? "add" : "delete", filter->dst_port, ret,
>> +			pf->hw.aq.asq_last_status);
>> +	else
>> +		dev_info(&pf->pdev->dev,
>> +			 "%s cloud filter for VSI: %d\n",
>> +			 add ? "Added" : "Deleted", filter->seid);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_add_del_cloud_filter_big_buf - Add/del cloud filter using big_buf
>> + * @vsi: pointer to VSI
>> + * @filter: cloud filter rule
>> + * @add: if true, add, if false, delete
>> + *
>> + * Add or delete a cloud filter for a specific flow spec using big buffer.
>> + * Returns 0 if the filter were successfully added.
>> + **/
>> +static int i40e_add_del_cloud_filter_big_buf(struct i40e_vsi *vsi,
>> +					     struct i40e_cloud_filter *filter,
>> +					     bool add)
>> +{
>> +	struct i40e_aqc_cloud_filters_element_bb cld_filter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int ret;
>> +
>> +	/* Both (Outer/Inner) valid mac_addr are not supported */
>> +	if (is_valid_ether_addr(filter->dst_mac) &&
>> +	    is_valid_ether_addr(filter->src_mac))
>> +		return -EINVAL;
>> +
>> +	/* Make sure port is specified, otherwise bail out, for channel
>> +	 * specific cloud filter needs 'L4 port' to be non-zero
>> +	 */
>> +	if (!filter->dst_port)
>> +		return -EINVAL;
>> +
>> +	/* adding filter using src_port/src_ip is not supported at this stage */
>> +	if (filter->src_port || filter->src_ip ||
>> +	    !ipv6_addr_any((struct in6_addr *)&filter->src_ipv6))
>> +		return -EINVAL;
>> +
>> +	/* copy element needed to add cloud filter from filter */
>> +	i40e_set_cld_element(filter, &cld_filter.element);
>> +
>> +	if (is_valid_ether_addr(filter->dst_mac) ||
>> +	    is_valid_ether_addr(filter->src_mac) ||
>> +	    is_multicast_ether_addr(filter->dst_mac) ||
>> +	    is_multicast_ether_addr(filter->src_mac)) {
>> +		/* MAC + IP : unsupported mode */
>> +		if (filter->dst_ip)
>> +			return -EINVAL;
>> +
>> +		/* since we validated that L4 port must be valid before
>> +		 * we get here, start with respective "flags" value
>> +		 * and update if vlan is present or not
>> +		 */
>> +		cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_PORT);
>> +
>> +		if (filter->vlan_id) {
>> +			cld_filter.element.flags =
>> +			cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_MAC_VLAN_PORT);
>> +		}
>> +
>> +	} else if (filter->dst_ip || filter->ip_version == IPV6_VERSION) {
>> +		cld_filter.element.flags =
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FILTER_IP_PORT);
>> +		if (filter->ip_version == IPV6_VERSION)
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV6);
>> +		else
>> +			cld_filter.element.flags |=
>> +				cpu_to_le16(I40E_AQC_ADD_CLOUD_FLAGS_IPV4);
>> +	} else {
>> +		dev_err(&pf->pdev->dev,
>> +			"either mac or ip has to be valid for cloud filter\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Now copy L4 port in Byte 6..7 in general fields */
>> +	cld_filter.general_fields[I40E_AQC_ADD_CLOUD_FV_FLU_0X16_WORD0] =
>> +						be16_to_cpu(filter->dst_port);
>> +
>> +	if (add) {
>> +		bool proto_type, port_type;
>> +
>> +		proto_type = (filter->ip_proto == IPPROTO_TCP) ? true : false;
>> +		port_type = (filter->port_type & I40E_CLOUD_FILTER_PORT_DEST) ?
>> +			     true : false;
>> +
>> +		/* For now, src port based cloud filter for channel is not
>> +		 * supported
>> +		 */
>> +		if (!port_type) {
>> +			dev_err(&pf->pdev->dev,
>> +				"unsupported port type (src port)\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Validate current device switch mode, change if necessary */
>> +		ret = i40e_validate_and_set_switch_mode(vsi, proto_type,
>> +							port_type);
>> +		if (ret) {
>> +			dev_err(&pf->pdev->dev,
>> +				"failed to set switch mode, ret %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		ret = i40e_aq_add_cloud_filters_bb(&pf->hw, filter->seid,
>> +						   &cld_filter, 1);
>> +	} else {
>> +		ret = i40e_aq_rem_cloud_filters_bb(&pf->hw, filter->seid,
>> +						   &cld_filter, 1);
>> +	}
>> +
>> +	if (ret)
>> +		dev_dbg(&pf->pdev->dev,
>> +			"Failed to %s cloud filter(big buffer) err %d aq_err %d\n",
>> +			add ? "add" : "delete", ret, pf->hw.aq.asq_last_status);
>> +	else
>> +		dev_info(&pf->pdev->dev,
>> +			 "%s cloud filter for VSI: %d, L4 port: %d\n",
>> +			 add ? "add" : "delete", filter->seid,
>> +			 ntohs(filter->dst_port));
>> +	return ret;
>> +}
>> +
>> +/**
>> + * i40e_parse_cls_flower - Parse tc flower filters provided by kernel
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_cls_flower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *f,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_pf *pf = vsi->back;
>> +	u16 addr_type = 0;
>> +	u8 field_flags = 0;
>> +
>> +	if (f->dissector->used_keys &
>> +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
>> +	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
>> +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_PORTS) |
>> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID))) {
>> +		dev_err(&pf->pdev->dev, "Unsupported key used: 0x%x\n",
>> +			f->dissector->used_keys);
>> +		return -EOPNOTSUPP;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
>> +		struct flow_dissector_key_keyid *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_keyid *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ENC_KEYID,
>> +						  f->mask);
>> +
>> +		if (mask->keyid != 0)
>> +			field_flags |= I40E_CLOUD_FIELD_TEN_ID;
>> +
>> +		filter->tenant_id = be32_to_cpu(key->keyid);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
>> +		struct flow_dissector_key_basic *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_BASIC,
>> +						  f->key);
>> +
>> +		filter->ip_proto = key->ip_proto;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
>> +		struct flow_dissector_key_eth_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->key);
>> +
>> +		struct flow_dissector_key_eth_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
>> +						  f->mask);
>> +
>> +		/* use is_broadcast and is_zero to check for all 0xf or 0 */
>> +		if (!is_zero_ether_addr(mask->dst)) {
>> +			if (is_broadcast_ether_addr(mask->dst)) {
>> +				field_flags |= I40E_CLOUD_FIELD_OMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether dest mask %pM\n",
>> +					mask->dst);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (!is_zero_ether_addr(mask->src)) {
>> +			if (is_broadcast_ether_addr(mask->src)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IMAC;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ether src mask %pM\n",
>> +					mask->src);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +		ether_addr_copy(filter->dst_mac, key->dst);
>> +		ether_addr_copy(filter->src_mac, key->src);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
>> +		struct flow_dissector_key_vlan *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->key);
>> +		struct flow_dissector_key_vlan *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_VLAN,
>> +						  f->mask);
>> +
>> +		if (mask->vlan_id) {
>> +			if (mask->vlan_id == VLAN_VID_MASK) {
>> +				field_flags |= I40E_CLOUD_FIELD_IVLAN;
>> +
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad vlan mask 0x%04x\n",
>> +					mask->vlan_id);
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->vlan_id = cpu_to_be16(key->vlan_id);
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
>> +		struct flow_dissector_key_control *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_CONTROL,
>> +						  f->key);
>> +
>> +		addr_type = key->addr_type;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
>> +		struct flow_dissector_key_ipv4_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv4_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
>> +						  f->mask);
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip dst mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be32(0xffffffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad ip src mask 0x%08x\n",
>> +					be32_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (field_flags & I40E_CLOUD_FIELD_TEN_ID) {
>> +			dev_err(&pf->pdev->dev, "Tenant id not allowed for ip filter\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		filter->dst_ip = key->dst;
>> +		filter->src_ip = key->src;
>> +		filter->ip_version = IPV4_VERSION;
>> +	}
>> +
>> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
>> +		struct flow_dissector_key_ipv6_addrs *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->key);
>> +		struct flow_dissector_key_ipv6_addrs *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
>> +						  f->mask);
>> +
>> +		/* src and dest IPV6 address should not be LOOPBACK
>> +		 * (0:0:0:0:0:0:0:1), which can be represented as ::1
>> +		 */
>> +		if (ipv6_addr_loopback(&key->dst) ||
>> +		    ipv6_addr_loopback(&key->src)) {
>> +			dev_err(&pf->pdev->dev,
>> +				"Bad ipv6, addr is LOOPBACK\n");
>> +			return I40E_ERR_CONFIG;
>> +		}
>> +		if (!ipv6_addr_any(&mask->dst) || !ipv6_addr_any(&mask->src))
>> +			field_flags |= I40E_CLOUD_FIELD_IIP;
>> +
>> +		memcpy(&filter->src_ipv6, &key->src.s6_addr32,
>> +		       sizeof(filter->src_ipv6));
>> +		memcpy(&filter->dst_ipv6, &key->dst.s6_addr32,
>> +		       sizeof(filter->dst_ipv6));
>> +
>> +		/* mark it as IPv6 filter, to be used later */
>> +		filter->ip_version = IPV6_VERSION;
>> +	}
>> +
>> +	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
>> +		struct flow_dissector_key_ports *key =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->key);
>> +		struct flow_dissector_key_ports *mask =
>> +			skb_flow_dissector_target(f->dissector,
>> +						  FLOW_DISSECTOR_KEY_PORTS,
>> +						  f->mask);
>> +
>> +		if (mask->src) {
>> +			if (mask->src == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad src port mask 0x%04x\n",
>> +					be16_to_cpu(mask->src));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		if (mask->dst) {
>> +			if (mask->dst == cpu_to_be16(0xffff)) {
>> +				field_flags |= I40E_CLOUD_FIELD_IIP;
>> +			} else {
>> +				dev_err(&pf->pdev->dev, "Bad dst port mask 0x%04x\n",
>> +					be16_to_cpu(mask->dst));
>> +				return I40E_ERR_CONFIG;
>> +			}
>> +		}
>> +
>> +		filter->dst_port = key->dst;
>> +		filter->src_port = key->src;
>> +
>> +		/* For now, only supports destination port*/
>> +		filter->port_type |= I40E_CLOUD_FILTER_PORT_DEST;
>> +
>> +		switch (filter->ip_proto) {
>> +		case IPPROTO_TCP:
>> +		case IPPROTO_UDP:
>> +			break;
>> +		default:
>> +			dev_err(&pf->pdev->dev,
>> +				"Only UDP and TCP transport are supported\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>> +	filter->flags = field_flags;
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_handle_redirect_action: Forward to a traffic class on the device
>> + * @vsi: Pointer to VSI
>> + * @ifindex: ifindex of the device to forwared to
>> + * @tc: traffic class index on the device
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_handle_redirect_action(struct i40e_vsi *vsi, int ifindex, u8 tc,
>> +				       struct i40e_cloud_filter *filter)
>> +{
>> +	struct i40e_channel *ch, *ch_tmp;
>> +
>> +	/* redirect to a traffic class on the same device */
>> +	if (vsi->netdev->ifindex == ifindex) {
>> +		if (tc == 0) {
>> +			filter->seid = vsi->seid;
>> +			return 0;
>> +		} else if (vsi->tc_config.enabled_tc & BIT(tc)) {
>> +			if (!filter->dst_port) {
>> +				dev_err(&vsi->back->pdev->dev,
>> +					"Specify destination port to redirect to traffic class that is not default\n");
>> +				return -EINVAL;
>> +			}
>> +			if (list_empty(&vsi->ch_list))
>> +				return -EINVAL;
>> +			list_for_each_entry_safe(ch, ch_tmp, &vsi->ch_list,
>> +						 list) {
>> +				if (ch->seid == vsi->tc_seid_map[tc])
>> +					filter->seid = ch->seid;
>> +			}
>> +			return 0;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_parse_tc_actions - Parse tc actions
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + * @filter: Pointer to cloud filter structure
>> + *
>> + **/
>> +static int i40e_parse_tc_actions(struct i40e_vsi *vsi, struct tcf_exts *exts,
>> +				 struct i40e_cloud_filter *filter)
>> +{
>> +	const struct tc_action *a;
>> +	LIST_HEAD(actions);
>> +	int err;
>> +
>> +	if (!tcf_exts_has_actions(exts))
>> +		return -EINVAL;
>> +
>> +	tcf_exts_to_list(exts, &actions);
>> +	list_for_each_entry(a, &actions, list) {
>> +		/* Drop action */
>> +		if (is_tcf_gact_shot(a)) {
>> +			dev_err(&vsi->back->pdev->dev,
>> +				"Cloud filters do not support the drop action.\n");
>> +			return -EOPNOTSUPP;
>> +		}
>> +
>> +		/* Redirect to a traffic class on the same device */
>> +		if (!is_tcf_mirred_egress_redirect(a) && is_tcf_mirred_tc(a)) {
>> +			int ifindex = tcf_mirred_ifindex(a);
>> +			u8 tc = tcf_mirred_tc(a);
>> +
>> +			err = i40e_handle_redirect_action(vsi, ifindex, tc,
>> +							  filter);
>> +			if (err == 0)
>> +				return err;
>> +		}
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * i40e_configure_clsflower - Configure tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_configure_clsflower(struct i40e_vsi *vsi,
>> +				    struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) ||
>> +	    test_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
>> +		return -EBUSY;
>> +
>> +	if (pf->fdir_pf_active_filters ||
>> +	    (!hlist_empty(&pf->fdir_filter_list))) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Flow Director Sideband filters exists, turn ntuple off to configure cloud filters\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (vsi->back->flags & I40E_FLAG_FD_SB_ENABLED) {
>> +		dev_err(&vsi->back->pdev->dev,
>> +			"Disable Flow Director Sideband, configuring Cloud filters via tc-flower\n");
>> +		vsi->back->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +		vsi->back->flags |= I40E_FLAG_FD_SB_TO_CLOUD_FILTER;
>> +	}
>> +
>> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
>> +	if (!filter)
>> +		return -ENOMEM;
>> +
>> +	filter->cookie = cls_flower->cookie;
>> +
>> +	err = i40e_parse_cls_flower(vsi, cls_flower, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	err = i40e_parse_tc_actions(vsi, cls_flower->exts, filter);
>> +	if (err < 0)
>> +		goto err;
>> +
>> +	/* Add cloud filter */
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, true);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, true);
>> +
>> +	if (err) {
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to add cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		err = i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
>> +		goto err;
>> +	}
>> +
>> +	/* add filter to the ordered list */
>> +	INIT_HLIST_NODE(&filter->cloud_node);
>> +
>> +	hlist_add_head(&filter->cloud_node, &pf->cloud_filter_list);
>> +
>> +	pf->num_cloud_filters++;
>> +
>> +	return err;
>> +err:
>> +	kfree(filter);
>> +	return err;
>> +}
>> +
>> +/**
>> + * i40e_find_cloud_filter - Find the could filter in the list
>> + * @vsi: Pointer to VSI
>> + * @cookie: filter specific cookie
>> + *
>> + **/
>> +static struct i40e_cloud_filter *i40e_find_cloud_filter(struct i40e_vsi *vsi,
>> +							unsigned long *cookie)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct hlist_node *node2;
>> +
>> +	hlist_for_each_entry_safe(filter, node2,
>> +				  &vsi->back->cloud_filter_list, cloud_node)
>> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
>> +			return filter;
>> +	return NULL;
>> +}
>> +
>> +/**
>> + * i40e_delete_clsflower - Remove tc flower filters
>> + * @vsi: Pointer to VSI
>> + * @cls_flower: Pointer to struct tc_cls_flower_offload
>> + *
>> + **/
>> +static int i40e_delete_clsflower(struct i40e_vsi *vsi,
>> +				 struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_cloud_filter *filter = NULL;
>> +	struct i40e_pf *pf = vsi->back;
>> +	int err = 0;
>> +
>> +	filter = i40e_find_cloud_filter(vsi, &cls_flower->cookie);
>> +
>> +	if (!filter)
>> +		return -EINVAL;
>> +
>> +	hash_del(&filter->cloud_node);
>> +
>> +	if (filter->dst_port)
>> +		err = i40e_add_del_cloud_filter_big_buf(vsi, filter, false);
>> +	else
>> +		err = i40e_add_del_cloud_filter(vsi, filter, false);
>> +	if (err) {
>> +		kfree(filter);
>> +		dev_err(&pf->pdev->dev,
>> +			"Failed to delete cloud filter, err %s\n",
>> +			i40e_stat_str(&pf->hw, err));
>> +		return i40e_aq_rc_to_posix(err, pf->hw.aq.asq_last_status);
>> +	}
>> +
>> +	kfree(filter);
>> +	pf->num_cloud_filters--;
>> +
>> +	if (!pf->num_cloud_filters)
>> +		if ((pf->flags & I40E_FLAG_FD_SB_TO_CLOUD_FILTER) &&
>> +		    !(pf->flags & I40E_FLAG_FD_SB_INACTIVE)) {
>> +			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
>> +			pf->flags &= ~I40E_FLAG_FD_SB_TO_CLOUD_FILTER;
>> +			pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE;
>> +		}
>> +	return 0;
>> +}
>> +
>> +/**
>> + * i40e_setup_tc_cls_flower - flower classifier offloads
>> + * @netdev: net device to configure
>> + * @type_data: offload data
>> + **/
>> +static int i40e_setup_tc_cls_flower(struct net_device *netdev,
>> +				    struct tc_cls_flower_offload *cls_flower)
>> +{
>> +	struct i40e_netdev_priv *np = netdev_priv(netdev);
>> +	struct i40e_vsi *vsi = np->vsi;
>> +
>> +	if (!is_classid_clsact_ingress(cls_flower->common.classid) ||
>> +	    cls_flower->common.chain_index)
>> +		return -EOPNOTSUPP;
>> +
>> +	switch (cls_flower->command) {
>> +	case TC_CLSFLOWER_REPLACE:
>> +		return i40e_configure_clsflower(vsi, cls_flower);
>> +	case TC_CLSFLOWER_DESTROY:
>> +		return i40e_delete_clsflower(vsi, cls_flower);
>> +	case TC_CLSFLOWER_STATS:
>> +		return -EOPNOTSUPP;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>> 			   void *type_data)
>> {
>> -	if (type != TC_SETUP_MQPRIO)
>> +	switch (type) {
>> +	case TC_SETUP_MQPRIO:
>> +		return i40e_setup_tc(netdev, type_data);
>> +	case TC_SETUP_CLSFLOWER:
>> +		return i40e_setup_tc_cls_flower(netdev, type_data);
>> +	default:
>> 		return -EOPNOTSUPP;
>> -
>> -	return i40e_setup_tc(netdev, type_data);
>> +	}
>> }
>>
>> /**
>> @@ -6939,6 +7756,13 @@ static void i40e_cloud_filter_exit(struct i40e_pf *pf)
>> 		kfree(cfilter);
>> 	}
>> 	pf->num_cloud_filters = 0;
>> +
>> +	if ((pf->flags & I40E_FLAG_FD_SB_TO_CLOUD_FILTER) &&
>> +	    !(pf->flags & I40E_FLAG_FD_SB_INACTIVE)) {
>> +		pf->flags |= I40E_FLAG_FD_SB_ENABLED;
>> +		pf->flags &= ~I40E_FLAG_FD_SB_TO_CLOUD_FILTER;
>> +		pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE;
>> +	}
>> }
>>
>> /**
>> @@ -8046,7 +8870,8 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
>>  * i40e_get_capabilities - get info about the HW
>>  * @pf: the PF struct
>>  **/
>> -static int i40e_get_capabilities(struct i40e_pf *pf)
>> +static int i40e_get_capabilities(struct i40e_pf *pf,
>> +				 enum i40e_admin_queue_opc list_type)
>> {
>> 	struct i40e_aqc_list_capabilities_element_resp *cap_buf;
>> 	u16 data_size;
>> @@ -8061,9 +8886,8 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
>>
>> 		/* this loads the data into the hw struct for us */
>> 		err = i40e_aq_discover_capabilities(&pf->hw, cap_buf, buf_len,
>> -					    &data_size,
>> -					    i40e_aqc_opc_list_func_capabilities,
>> -					    NULL);
>> +						    &data_size, list_type,
>> +						    NULL);
>> 		/* data loaded, buffer no longer needed */
>> 		kfree(cap_buf);
>>
>> @@ -8080,26 +8904,44 @@ static int i40e_get_capabilities(struct i40e_pf *pf)
>> 		}
>> 	} while (err);
>>
>> -	if (pf->hw.debug_mask & I40E_DEBUG_USER)
>> -		dev_info(&pf->pdev->dev,
>> -			 "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n",
>> -			 pf->hw.pf_id, pf->hw.func_caps.num_vfs,
>> -			 pf->hw.func_caps.num_msix_vectors,
>> -			 pf->hw.func_caps.num_msix_vectors_vf,
>> -			 pf->hw.func_caps.fd_filters_guaranteed,
>> -			 pf->hw.func_caps.fd_filters_best_effort,
>> -			 pf->hw.func_caps.num_tx_qp,
>> -			 pf->hw.func_caps.num_vsis);
>> -
>> +	if (pf->hw.debug_mask & I40E_DEBUG_USER) {
>> +		if (list_type == i40e_aqc_opc_list_func_capabilities) {
>> +			dev_info(&pf->pdev->dev,
>> +				 "pf=%d, num_vfs=%d, msix_pf=%d, msix_vf=%d, fd_g=%d, fd_b=%d, pf_max_q=%d num_vsi=%d\n",
>> +				 pf->hw.pf_id, pf->hw.func_caps.num_vfs,
>> +				 pf->hw.func_caps.num_msix_vectors,
>> +				 pf->hw.func_caps.num_msix_vectors_vf,
>> +				 pf->hw.func_caps.fd_filters_guaranteed,
>> +				 pf->hw.func_caps.fd_filters_best_effort,
>> +				 pf->hw.func_caps.num_tx_qp,
>> +				 pf->hw.func_caps.num_vsis);
>> +		} else if (list_type == i40e_aqc_opc_list_dev_capabilities) {
>> +			dev_info(&pf->pdev->dev,
>> +				 "switch_mode=0x%04x, function_valid=0x%08x\n",
>> +				 pf->hw.dev_caps.switch_mode,
>> +				 pf->hw.dev_caps.valid_functions);
>> +			dev_info(&pf->pdev->dev,
>> +				 "SR-IOV=%d, num_vfs for all function=%u\n",
>> +				 pf->hw.dev_caps.sr_iov_1_1,
>> +				 pf->hw.dev_caps.num_vfs);
>> +			dev_info(&pf->pdev->dev,
>> +				 "num_vsis=%u, num_rx:%u, num_tx=%u\n",
>> +				 pf->hw.dev_caps.num_vsis,
>> +				 pf->hw.dev_caps.num_rx_qp,
>> +				 pf->hw.dev_caps.num_tx_qp);
>> +		}
>> +	}
>> +	if (list_type == i40e_aqc_opc_list_func_capabilities) {
>> #define DEF_NUM_VSI (1 + (pf->hw.func_caps.fcoe ? 1 : 0) \
>> 		       + pf->hw.func_caps.num_vfs)
>> -	if (pf->hw.revision_id == 0 && (DEF_NUM_VSI > pf->hw.func_caps.num_vsis)) {
>> -		dev_info(&pf->pdev->dev,
>> -			 "got num_vsis %d, setting num_vsis to %d\n",
>> -			 pf->hw.func_caps.num_vsis, DEF_NUM_VSI);
>> -		pf->hw.func_caps.num_vsis = DEF_NUM_VSI;
>> +		if (pf->hw.revision_id == 0 &&
>> +		    (pf->hw.func_caps.num_vsis < DEF_NUM_VSI)) {
>> +			dev_info(&pf->pdev->dev,
>> +				 "got num_vsis %d, setting num_vsis to %d\n",
>> +				 pf->hw.func_caps.num_vsis, DEF_NUM_VSI);
>> +			pf->hw.func_caps.num_vsis = DEF_NUM_VSI;
>> +		}
>> 	}
>> -
>> 	return 0;
>> }
>>
>> @@ -8141,6 +8983,7 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
>> 		if (!vsi) {
>> 			dev_info(&pf->pdev->dev, "Couldn't create FDir VSI\n");
>> 			pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +			pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> 			return;
>> 		}
>> 	}
>> @@ -8163,6 +9006,48 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
>> }
>>
>> /**
>> + * i40e_rebuild_cloud_filters - Rebuilds cloud filters for VSIs
>> + * @vsi: PF main vsi
>> + * @seid: seid of main or channel VSIs
>> + *
>> + * Rebuilds cloud filters associated with main VSI and channel VSIs if they
>> + * existed before reset
>> + **/
>> +static int i40e_rebuild_cloud_filters(struct i40e_vsi *vsi, u16 seid)
>> +{
>> +	struct i40e_cloud_filter *cfilter;
>> +	struct i40e_pf *pf = vsi->back;
>> +	struct hlist_node *node;
>> +	i40e_status ret;
>> +
>> +	/* Add cloud filters back if they exist */
>> +	if (hlist_empty(&pf->cloud_filter_list))
>> +		return 0;
>> +
>> +	hlist_for_each_entry_safe(cfilter, node, &pf->cloud_filter_list,
>> +				  cloud_node) {
>> +		if (cfilter->seid != seid)
>> +			continue;
>> +
>> +		if (cfilter->dst_port)
>> +			ret = i40e_add_del_cloud_filter_big_buf(vsi, cfilter,
>> +								true);
>> +		else
>> +			ret = i40e_add_del_cloud_filter(vsi, cfilter, true);
>> +
>> +		if (ret) {
>> +			dev_dbg(&pf->pdev->dev,
>> +				"Failed to rebuild cloud filter, err %s aq_err %s\n",
>> +				i40e_stat_str(&pf->hw, ret),
>> +				i40e_aq_str(&pf->hw,
>> +					    pf->hw.aq.asq_last_status));
>> +			return ret;
>> +		}
>> +	}
>> +	return 0;
>> +}
>> +
>> +/**
>>  * i40e_rebuild_channels - Rebuilds channel VSIs if they existed before reset
>>  * @vsi: PF main vsi
>>  *
>> @@ -8199,6 +9084,13 @@ static int i40e_rebuild_channels(struct i40e_vsi *vsi)
>> 						I40E_BW_CREDIT_DIVISOR,
>> 				ch->seid);
>> 		}
>> +		ret = i40e_rebuild_cloud_filters(vsi, ch->seid);
>> +		if (ret) {
>> +			dev_dbg(&vsi->back->pdev->dev,
>> +				"Failed to rebuild cloud filters for channel VSI %u\n",
>> +				ch->seid);
>> +			return ret;
>> +		}
>> 	}
>> 	return 0;
>> }
>> @@ -8365,7 +9257,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>> 		i40e_verify_eeprom(pf);
>>
>> 	i40e_clear_pxe_mode(hw);
>> -	ret = i40e_get_capabilities(pf);
>> +	ret = i40e_get_capabilities(pf, i40e_aqc_opc_list_func_capabilities);
>> 	if (ret)
>> 		goto end_core_reset;
>>
>> @@ -8482,6 +9374,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired)
>> 			goto end_unlock;
>> 	}
>>
>> +	ret = i40e_rebuild_cloud_filters(vsi, vsi->seid);
>> +	if (ret)
>> +		goto end_unlock;
>> +
>> 	/* PF Main VSI is rebuild by now, go ahead and rebuild channel VSIs
>> 	 * for this main VSI if they exist
>> 	 */
>> @@ -9404,6 +10300,7 @@ static int i40e_init_msix(struct i40e_pf *pf)
>> 	    (pf->num_fdsb_msix == 0)) {
>> 		dev_info(&pf->pdev->dev, "Sideband Flowdir disabled, not enough MSI-X vectors\n");
>> 		pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +		pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> 	}
>> 	if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) &&
>> 	    (pf->num_vmdq_msix == 0)) {
>> @@ -9521,6 +10418,7 @@ static int i40e_init_interrupt_scheme(struct i40e_pf *pf)
>> 				       I40E_FLAG_FD_SB_ENABLED	|
>> 				       I40E_FLAG_FD_ATR_ENABLED	|
>> 				       I40E_FLAG_VMDQ_ENABLED);
>> +			pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>>
>> 			/* rework the queue expectations without MSIX */
>> 			i40e_determine_queue_usage(pf);
>> @@ -10263,9 +11161,13 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features)
>> 		/* Enable filters and mark for reset */
>> 		if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED))
>> 			need_reset = true;
>> -		/* enable FD_SB only if there is MSI-X vector */
>> -		if (pf->num_fdsb_msix > 0)
>> +		/* enable FD_SB only if there is MSI-X vector and no cloud
>> +		 * filters exist
>> +		 */
>> +		if (pf->num_fdsb_msix > 0 && !pf->num_cloud_filters) {
>> 			pf->flags |= I40E_FLAG_FD_SB_ENABLED;
>> +			pf->flags &= ~I40E_FLAG_FD_SB_INACTIVE;
>> +		}
>> 	} else {
>> 		/* turn off filters, mark for reset and clear SW filter list */
>> 		if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
>> @@ -10274,6 +11176,8 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features)
>> 		}
>> 		pf->flags &= ~(I40E_FLAG_FD_SB_ENABLED |
>> 			       I40E_FLAG_FD_SB_AUTO_DISABLED);
>> +		pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> +
>> 		/* reset fd counters */
>> 		pf->fd_add_err = 0;
>> 		pf->fd_atr_cnt = 0;
>> @@ -10857,7 +11761,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
>> 		netdev->hw_features |= NETIF_F_NTUPLE;
>> 	hw_features = hw_enc_features		|
>> 		      NETIF_F_HW_VLAN_CTAG_TX	|
>> -		      NETIF_F_HW_VLAN_CTAG_RX;
>> +		      NETIF_F_HW_VLAN_CTAG_RX	|
>> +		      NETIF_F_HW_TC;
>>
>> 	netdev->hw_features |= hw_features;
>>
>> @@ -12159,8 +13064,10 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>> 	*/
>>
>> 	if ((pf->hw.pf_id == 0) &&
>> -	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT))
>> +	    !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) {
>> 		flags = I40E_AQ_SET_SWITCH_CFG_PROMISC;
>> +		pf->last_sw_conf_flags = flags;
>> +	}
>>
>> 	if (pf->hw.pf_id == 0) {
>> 		u16 valid_flags;
>> @@ -12176,6 +13083,7 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit)
>> 					     pf->hw.aq.asq_last_status));
>> 			/* not a fatal problem, just keep going */
>> 		}
>> +		pf->last_sw_conf_valid_flags = valid_flags;
>> 	}
>>
>> 	/* first time setup */
>> @@ -12273,6 +13181,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
>> 			       I40E_FLAG_DCB_ENABLED	|
>> 			       I40E_FLAG_SRIOV_ENABLED	|
>> 			       I40E_FLAG_VMDQ_ENABLED);
>> +		pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> 	} else if (!(pf->flags & (I40E_FLAG_RSS_ENABLED |
>> 				  I40E_FLAG_FD_SB_ENABLED |
>> 				  I40E_FLAG_FD_ATR_ENABLED |
>> @@ -12287,6 +13196,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
>> 			       I40E_FLAG_FD_ATR_ENABLED	|
>> 			       I40E_FLAG_DCB_ENABLED	|
>> 			       I40E_FLAG_VMDQ_ENABLED);
>> +		pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> 	} else {
>> 		/* Not enough queues for all TCs */
>> 		if ((pf->flags & I40E_FLAG_DCB_CAPABLE) &&
>> @@ -12310,6 +13220,7 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf)
>> 			queues_left -= 1; /* save 1 queue for FD */
>> 		} else {
>> 			pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
>> +			pf->flags |= I40E_FLAG_FD_SB_INACTIVE;
>> 			dev_info(&pf->pdev->dev, "not enough queues for Flow Director. Flow Director feature is disabled\n");
>> 		}
>> 	}
>> @@ -12613,7 +13524,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
>> 		dev_warn(&pdev->dev, "This device is a pre-production adapter/LOM. Please be aware there may be issues with your hardware. If you are experiencing problems please contact your Intel or hardware representative who provided you with this hardware.\n");
>>
>> 	i40e_clear_pxe_mode(hw);
>> -	err = i40e_get_capabilities(pf);
>> +	err = i40e_get_capabilities(pf, i40e_aqc_opc_list_func_capabilities);
>> 	if (err)
>> 		goto err_adminq_setup;
>>
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> index 92869f5..3bb6659 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h
>> @@ -283,6 +283,22 @@ i40e_status i40e_aq_query_switch_comp_bw_config(struct i40e_hw *hw,
>> 		struct i40e_asq_cmd_details *cmd_details);
>> i40e_status i40e_aq_resume_port_tx(struct i40e_hw *hw,
>> 				   struct i40e_asq_cmd_details *cmd_details);
>> +i40e_status
>> +i40e_aq_add_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
>> +			     struct i40e_aqc_cloud_filters_element_bb *filters,
>> +			     u8 filter_count);
>> +enum i40e_status_code
>> +i40e_aq_add_cloud_filters(struct i40e_hw *hw, u16 vsi,
>> +			  struct i40e_aqc_cloud_filters_element_data *filters,
>> +			  u8 filter_count);
>> +enum i40e_status_code
>> +i40e_aq_rem_cloud_filters(struct i40e_hw *hw, u16 vsi,
>> +			  struct i40e_aqc_cloud_filters_element_data *filters,
>> +			  u8 filter_count);
>> +i40e_status
>> +i40e_aq_rem_cloud_filters_bb(struct i40e_hw *hw, u16 seid,
>> +			     struct i40e_aqc_cloud_filters_element_bb *filters,
>> +			     u8 filter_count);
>> i40e_status i40e_read_lldp_cfg(struct i40e_hw *hw,
>> 			       struct i40e_lldp_variables *lldp_cfg);
>> /* i40e_common */
>> diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h
>> index c019f46..af38881 100644
>> --- a/drivers/net/ethernet/intel/i40e/i40e_type.h
>> +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h
>> @@ -287,6 +287,7 @@ struct i40e_hw_capabilities {
>> #define I40E_NVM_IMAGE_TYPE_MODE1	0x6
>> #define I40E_NVM_IMAGE_TYPE_MODE2	0x7
>> #define I40E_NVM_IMAGE_TYPE_MODE3	0x8
>> +#define I40E_SWITCH_MODE_MASK		0xF
>>
>> 	u32  management_mode;
>> 	u32  mng_protocols_over_mctp;
>> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
>> index b8c78bf..4fe27f0 100644
>> --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
>> +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h
>> @@ -1360,6 +1360,9 @@ struct i40e_aqc_cloud_filters_element_data {
>> 		struct {
>> 			u8 data[16];
>> 		} v6;
>> +		struct {
>> +			__le16 data[8];
>> +		} raw_v6;
>> 	} ipaddr;
>> 	__le16	flags;
>> #define I40E_AQC_ADD_CLOUD_FILTER_SHIFT			0
>>

^ permalink raw reply

* Re: [RFC PATCH v3 2/7] sched: act_mirred: Traffic class option for mirror/redirect action
From: Nambiar, Amritha @ 2017-09-14  7:58 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: intel-wired-lan, jeffrey.t.kirsher, alexander.h.duyck, netdev,
	mlxsw
In-Reply-To: <20170913131843.GB1981@nanopsycho>

On 9/13/2017 6:18 AM, Jiri Pirko wrote:
> Wed, Sep 13, 2017 at 11:59:24AM CEST, amritha.nambiar@intel.com wrote:
>> Adds optional traffic class parameter to the mirror/redirect action.
>> The mirror/redirect action is extended to forward to a traffic
>> class on the device if the traffic class index is provided in
>> addition to the device's ifindex.
> 
> Do I understand it correctly that you just abuse mirred to pas tcclass
> index down to the driver, without actually doing anything with the value
> inside mirred-code ? That is a bit confusing for me.
> 

I think I get your point, I was looking at it more from a hardware
angle, and the 'redirect' action looked quite close to how this actually
works in the hardware. I agree the tclass value in the mirred-code is
not very useful other than offloading it.

^ permalink raw reply

* Re: [PATCH 1/1] forcedeth: remove tx_stop variable
From: Yanjun Zhu @ 2017-09-14  7:55 UTC (permalink / raw)
  To: davem, netdev
In-Reply-To: <1504873725-30180-1-git-send-email-yanjun.zhu@oracle.com>

Hi, all

After this patch is applied, the TCP && UDP tests are made.

The TCP bandwidth is 939 Mbits/sec. The UDP bandwidth is 806 Mbits/sec.

So I think this patch can work well.

host1 <-----> host2

host1: forcedeth NIC
IP: 1.1.1.107
iperf -s

host2: forcedeth NIC
IP:1.1.1.105
iperf -c 1.1.1.107

The TCP Bandwidth is as below:
------------------------------------------------------------
Client connecting to 1.1.1.107, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 1.1.1.105 port 46092 connected with 1.1.1.107 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes   939 Mbits/sec

The UDP is as below:

iperf -c 1.1.1.107 -u -b 1000m
------------------------------------------------------------
Client connecting to 1.1.1.107, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size:  208 KByte (default)
------------------------------------------------------------
[  3] local 1.1.1.105 port 47265 connected with 1.1.1.107 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec   964 MBytes   809 Mbits/sec
[  3] Sent 687990 datagrams
[  3] Server Report:
[  3]  0.0-10.0 sec   960 MBytes   806 Mbits/sec   0.019 ms 2942/687989 
(0.43%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

Zhu Yanjun

On 2017/9/8 20:28, Zhu Yanjun wrote:
> The variable tx_stop is used to indicate the tx queue state: started
> or stopped. In fact, the inline function netif_queue_stopped can do
> the same work. So replace the variable tx_stop with the
> function netif_queue_stopped.
>
> Signed-off-by: Zhu Yanjun <yanjun.zhu@oracle.com>
> ---
>   drivers/net/ethernet/nvidia/forcedeth.c | 13 ++++---------
>   1 file changed, 4 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
> index 994a83a..e6e0de4 100644
> --- a/drivers/net/ethernet/nvidia/forcedeth.c
> +++ b/drivers/net/ethernet/nvidia/forcedeth.c
> @@ -834,7 +834,6 @@ struct fe_priv {
>   	u32 tx_pkts_in_progress;
>   	struct nv_skb_map *tx_change_owner;
>   	struct nv_skb_map *tx_end_flip;
> -	int tx_stop;
>   
>   	/* TX software stats */
>   	struct u64_stats_sync swstats_tx_syncp;
> @@ -1939,7 +1938,6 @@ static void nv_init_tx(struct net_device *dev)
>   	np->tx_pkts_in_progress = 0;
>   	np->tx_change_owner = NULL;
>   	np->tx_end_flip = NULL;
> -	np->tx_stop = 0;
>   
>   	for (i = 0; i < np->tx_ring_size; i++) {
>   		if (!nv_optimized(np)) {
> @@ -2211,7 +2209,6 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
>   	empty_slots = nv_get_empty_tx_slots(np);
>   	if (unlikely(empty_slots <= entries)) {
>   		netif_stop_queue(dev);
> -		np->tx_stop = 1;
>   		spin_unlock_irqrestore(&np->lock, flags);
>   		return NETDEV_TX_BUSY;
>   	}
> @@ -2359,7 +2356,6 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
>   	empty_slots = nv_get_empty_tx_slots(np);
>   	if (unlikely(empty_slots <= entries)) {
>   		netif_stop_queue(dev);
> -		np->tx_stop = 1;
>   		spin_unlock_irqrestore(&np->lock, flags);
>   		return NETDEV_TX_BUSY;
>   	}
> @@ -2583,8 +2579,8 @@ static int nv_tx_done(struct net_device *dev, int limit)
>   
>   	netdev_completed_queue(np->dev, tx_work, bytes_compl);
>   
> -	if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx))) {
> -		np->tx_stop = 0;
> +	if (unlikely(netif_queue_stopped(dev) &&
> +		     (np->get_tx.orig != orig_get_tx))) {
>   		netif_wake_queue(dev);
>   	}
>   	return tx_work;
> @@ -2637,8 +2633,8 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit)
>   
>   	netdev_completed_queue(np->dev, tx_work, bytes_cleaned);
>   
> -	if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx))) {
> -		np->tx_stop = 0;
> +	if (unlikely(netif_queue_stopped(dev) &&
> +		     (np->get_tx.ex != orig_get_tx))) {
>   		netif_wake_queue(dev);
>   	}
>   	return tx_work;
> @@ -2724,7 +2720,6 @@ static void nv_tx_timeout(struct net_device *dev)
>   	/* 2) complete any outstanding tx and do not give HW any limited tx pkts */
>   	saved_tx_limit = np->tx_limit;
>   	np->tx_limit = 0; /* prevent giving HW any limited pkts */
> -	np->tx_stop = 0;  /* prevent waking tx queue */
>   	if (!nv_optimized(np))
>   		nv_tx_done(dev, np->tx_ring_size);
>   	else

^ permalink raw reply

* [PATCH] net: phy: Fix mask value write on gmii2rgmii converter speed register.
From: Fahad Kunnathadi @ 2017-09-14  7:16 UTC (permalink / raw)
  To: f.fainelli
  Cc: netdev, michal.simek, linux-kernel, Fahad Kunnathadi,
	soren.brinkmann, linux-arm-kernel

To clear Speed Selection in MDIO control register(0x10),
ie, clear bits 6 and 13 to zero while keeping other bits same.
Before AND operation,The Mask value has to be perform with bitwise NOT
operation (ie, ~ operator)

This patch clears current speed selection before writing the
new speed settings to gmii2rgmii converter

Signed-off-by: Fahad Kunnathadi <fahad.kunnathadi@dexceldesigns.com>
---
 drivers/net/phy/xilinx_gmii2rgmii.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
index d15dd39..2e5150b 100644
--- a/drivers/net/phy/xilinx_gmii2rgmii.c
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -44,7 +44,7 @@ static int xgmiitorgmii_read_status(struct phy_device *phydev)
 	priv->phy_drv->read_status(phydev);
 
 	val = mdiobus_read(phydev->mdio.bus, priv->addr, XILINX_GMII2RGMII_REG);
-	val &= XILINX_GMII2RGMII_SPEED_MASK;
+	val &= ~XILINX_GMII2RGMII_SPEED_MASK;
 
 	if (phydev->speed == SPEED_1000)
 		val |= BMCR_SPEED1000;
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v2] ipv4: Namespaceify tcp_fastopen knob
From: 严海双 @ 2017-09-14  6:19 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S. Miller, Alexey Kuznetsov, Eric Dumazet, Luca BRUNO,
	netdev, linux-kernel
In-Reply-To: <1505307779.15310.157.camel@edumazet-glaptop3.roam.corp.google.com>



> On 2017年9月13日, at 下午9:02, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> 
> On Wed, 2017-09-13 at 05:44 -0700, Eric Dumazet wrote:
>> On Wed, 2017-09-13 at 19:19 +0800, Haishuang Yan wrote:
>>> Different namespace application might require enable TCP Fast Open
>>> feature independently of the host.
>>> 
>> 
>> Poor changelog, no actual description / list of sysctls that are moved
>> to per netns.
>> 
>> And looking at the patch, it seems your conversion is not complete.
>> 
>> So I will ask you to provide more evidence that you tested your patch
>> next time you submit it.
> 
> I suggest you move one sysctl at a time, in a patch series.
> 
> It will be easier to document and test for you, and review for us.
> 
> Thanks.
> 

Okay, I will split my patch for each sysctl change. Thanks.

^ permalink raw reply

* Re: [Outreachy kernel] [PATCH] staging: irda: Remove typedef struct
From: Julia Lawall @ 2017-09-14  5:59 UTC (permalink / raw)
  To: Haneen Mohammed
  Cc: devel, Samuel Ortiz, netdev, linux-kernel, outreachy-kernel,
	Greg Kroah-Hartman
In-Reply-To: <20170914045538.GA24121@Haneen>



On Wed, 13 Sep 2017, Haneen Mohammed wrote:

> This patch remove typedef from a structure with all its ocurrences
> since using typedefs for structures is discouraged.
> Issue found using Coccinelle:
>
> @r1@
> type T;
> @@
>
> typedef struct { ... } T;
>
> @script:python c1@
> T2;
> T << r1.T;
> @@
> if T[-2:] =="_t" or T[-2:] == "_T":
> 	coccinelle.T2 = T[:-2];
> else:
> 	coccinelle.T2 = T;
>
> print T, coccinelle.T2
>
> @r2@
> type r1.T;
> identifier c1.T2;
> @@
> -typedef
> struct
> + T2
> { ... }
> -T
> ;
>
> @r3@
> type r1.T;
> identifier c1.T2;
> @@
> -T
> +struct T2
>
> Signed-off-by: Haneen Mohammed <hamohammed.sa@gmail.com>

Acked-by: Julia Lawall <julia.lawall@lip6.fr>

> ---
>  drivers/staging/irda/include/net/irda/qos.h | 20 ++++++++++----------
>  1 file changed, 10 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/staging/irda/include/net/irda/qos.h b/drivers/staging/irda/include/net/irda/qos.h
> index 05a5a24..a0315b5 100644
> --- a/drivers/staging/irda/include/net/irda/qos.h
> +++ b/drivers/staging/irda/include/net/irda/qos.h
> @@ -58,23 +58,23 @@
>  #define IR_16000000 0x02
>
>  /* Quality of Service information */
> -typedef struct {
> +struct qos_value {
>  	__u32 value;
>  	__u16 bits; /* LSB is first byte, MSB is second byte */
> -} qos_value_t;
> +};
>
>  struct qos_info {
>  	magic_t magic;
>
> -	qos_value_t baud_rate;       /* IR_11520O | ... */
> -	qos_value_t max_turn_time;
> -	qos_value_t data_size;
> -	qos_value_t window_size;
> -	qos_value_t additional_bofs;
> -	qos_value_t min_turn_time;
> -	qos_value_t link_disc_time;
> +	struct qos_value baud_rate;       /* IR_11520O | ... */
> +	struct qos_value max_turn_time;
> +	struct qos_value data_size;
> +	struct qos_value window_size;
> +	struct qos_value additional_bofs;
> +	struct qos_value min_turn_time;
> +	struct qos_value link_disc_time;
>
> -	qos_value_t power;
> +	struct qos_value power;
>  };
>
>  extern int sysctl_max_baud_rate;
> --
> 2.7.4
>
> --
> You received this message because you are subscribed to the Google Groups "outreachy-kernel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to outreachy-kernel+unsubscribe@googlegroups.com.
> To post to this group, send email to outreachy-kernel@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/outreachy-kernel/20170914045538.GA24121%40Haneen.
> For more options, visit https://groups.google.com/d/optout.
>

^ permalink raw reply

* Re: RFC: Audit Kernel Container IDs
From: Richard Guy Briggs @ 2017-09-14  5:30 UTC (permalink / raw)
  To: Carlos O'Donell
  Cc: cgroups-u79uwXL29TY76Z2rM5mHXA, Linux Containers, Linux API,
	Linux Audit, Linux FS Devel, Linux Kernel,
	Linux Network Development, Aristeu Rozanski, David Howells,
	Eric W. Biederman, Eric Paris, jlayton-H+wXaHxf7aLQT0dZR+AlfA,
	Andy Lutomirski, mszeredi-H+wXaHxf7aLQT0dZR+AlfA, Paul Moore,
	Serge E. Hallyn, Steve Grubb, trondmy-7I+n7zu2hftEKMMhf/gKZA,
	Al Viro
In-Reply-To: <9043cc5a-e624-10c9-1906-f29010c5f57c-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

On 2017-09-13 14:33, Carlos O'Donell wrote:
> On 09/13/2017 12:13 PM, Richard Guy Briggs wrote:
> > Containers are a userspace concept.  The kernel knows nothing of them.
> 
> I am looking at this RFC from a userspace perspective, particularly from
> the loader's point of view and the unshare syscall and the semantics that
> arise from the use of it.
> 
> At a high level what you are doing is providing a way to group, without
> hierarchy, processes and namespaces. The processes can move between
> container's if they have CAP_CONTAINER_ADMIN and can open and write to
> a special proc file.
> 
> * With unshare a thread may dissociate part of its execution context and
>   therefore see a distinct mount namespace. When you say "process" in this
>   particular RFC do you exclude the fact that a thread might be in a
>   distinct container from the rest of the threads in the process?
> 
> > The Linux audit system needs a way to be able to track the container
> > provenance of events and actions.  Audit needs the kernel's help to do
> > this.
> 
> * Why does the Linux audit system need to tracker container provenance?

- ability to filter unwanted, irrelevant or unimportant messages before
  they fill queue so important messages don't get lost.  This is a
  certification requirement.

- ability to make security claims about containers, require tracking of
  actions within those containers to ensure compliance with established
  security policies.

- ability to route messages from events to relevant audit daemon
  instance or host audit daemon instance or both, as required or
  determined by user-initiated rules

>   - How does it help to provide better audit messages?
> 
>   - Is it be enough to list the namespace that a process occupies?

We started with that approach back more than 4 years ago and found it
helped, but didn't go far enough in terms of quick and inexpensive
record filtering and left some doubt about provenance of events in the
case of non-user context events (incoming network packets).

> * Why does it need the kernel's help?
> 
>   - Is there a race condition that is only fixable with kernel support?

This was a concern, but relatively minor compared with the other benefits.

>   - Or is it easier with kernel help but not required?

It is much easier and much less expensive.

> Providing background on these questions would help clarify the
> design requirements.

Here are some references that should help provide some background:
	https://github.com/linux-audit/audit-kernel/issues/32
	RFE: add namespace IDs to audit records

	https://github.com/linux-audit/audit-documentation/wiki/SPEC-Virtualization-Manager-Guest-Lifecycle-Events
	SPEC Virtualization Manager Guest Lifecycle Events

	https://lwn.net/Articles/699819/
	Audit, namespaces, and containers

	https://lwn.net/Articles/723561/
	Containers as kernel objects
	(my reply, with references: https://lkml.org/lkml/2017/8/14/15 )

	https://bugzilla.redhat.com/show_bug.cgi?id=1045666
	audit: add namespace IDs to log records

> > Since the concept of a container is entirely a userspace concept, a
> > trigger signal from the userspace container orchestration system
> > initiates this.  This will define a point in time and a set of resources
> > associated with a particular container with an audit container ID.
> 
> Please don't use the word 'signal', I suggest 'register' since you are
> writing to a filesystem.

Ok, that's a very reasonable request.  'signal' has a previous meaning.

> > The trigger is a pseudo filesystem (proc, since PID tree already exists)
> > write of a u64 representing the container ID to a file representing a
> > process that will become the first process in a new container.
> > This might place restrictions on mount namespaces required to define a
> > container, or at least careful checking of namespaces in the kernel to
> > verify permissions of the orchestrator so it can't change its own
> > container ID.
> > A bind mount of nsfs may be necessary in the container orchestrator's
> > mntNS.
> > 
> > Require a new CAP_CONTAINER_ADMIN to be able to write to the pseudo
> > filesystem to have this action permitted.  At that time, record the
> > child container's user-supplied 64-bit container identifier along with
> 
> What is a "child container?" Containers don't have any hierarchy.

Maybe some don't, but that's not likely to last long given the
abstraction and nesting of orchestration tools.  This must be nestable.

> I assume that if you don't have CAP_CONTAINER_ADMIN, that nothing prevents
> your continued operation as we have today?

Correct.  It won't prevent processes that otherwise have permissions
today from creating all the namespaces it wishes.

> > the child container's first process (which may become the container's
> > "init" process) process ID (referenced from the initial PID namespace),
> > all namespace IDs (in the form of a nsfs device number and inode number
> > tuple) in a new auxilliary record AUDIT_CONTAINER with a qualifying
> > op=$action field.
> 
> What kind of requirement is there on the first tid/pid registering
> the container ID? What if the 8th tid/pid does the registration?
> Would that mean that the first process of the container did not
> register? It seems like you are suggesting that the registration
> by the 8th tid/pid causes a cascading registration progress,
> registering all tid/pids in the same grouping? Is that true?

Ah, good question, I forgot to address that fact.  The intent is that
either threaded processes after initiating threading will not have
permission to execute this, or all the processes in the thread group
will be forced into the same container.  I don't have a strong opinion
on whether or not it must be the lead thread process that must be the
one to receive that registration, but I suspect that would be wise.

> > Issue a new auxilliary record AUDIT_CONTAINER_INFO for each valid
> > container ID present on an auditable action or event.
> > 
> > Forked and cloned processes inherit their parent's container ID,
> > referenced in the process' audit_context struct.
> 
> So a cloned process with CLONE_NEWNS has the came container ID
> as the parent process that called clone, at least until the clone
> has time to change to a new container ID?

Yes.

> Do you forsee any case where someone might need a semantic that is
> slightly different? For example wanting to set the container ID on
> clone?

I could envision that situation and I think that might be workable but
for the synchronicity of having one initiated by a specific syscall and
the other initiated by a /proc write.

> > Log the creation of every namespace, inheriting/adding its spawning
> > process' containerID(s), if applicable.  Include the spawning and
> > spawned namespace IDs (device and inode number tuples).
> > [AUDIT_NS_CREATE, AUDIT_NS_DESTROY] [clone(2), unshare(2), setns(2)]
> > Note: At this point it appears only network namespaces may need to track
> > container IDs apart from processes since incoming packets may cause an
> > auditable event before being associated with a process.
> 
> OK.
> 
> > Log the destruction of every namespace when it is no longer used by any
> > process, include the namespace IDs (device and inode number tuples).
> > [AUDIT_NS_DESTROY] [process exit, unshare(2), setns(2)]
> > 
> > Issue a new auxilliary record AUDIT_NS_CHANGE listing (opt: op=$action)
> > the parent and child namespace IDs for any changes to a process'
> > namespaces. [setns(2)]
> > Note: It may be possible to combine AUDIT_NS_* record formats and
> > distinguish them with an op=$action field depending on the fields
> > required for each message type.
> > 
> > A process can be moved from one container to another by using the
> > container assignment method outlined above a second time.
> 
> OK.
> 
> > When a container ceases to exist because the last process in that
> > container has exited and hence the last namespace has been destroyed and
> > its refcount dropping to zero, log the fact.
> > (This latter is likely needed for certification accountability.)  A
> > container object may need a list of processes and/or namespaces.
> 
> OK.
> 
> > A namespace cannot directly migrate from one container to another but
> > could be assigned to a newly spawned container.  A namespace can be
> > moved from one container to another indirectly by having that namespace
> > used in a second process in another container and then ending all the
> > processes in the first container.
> 
> OK.
> 
> > Feedback please.

Thank you sir!

> Carlos.

- RGB

--
Richard Guy Briggs <rgb-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Sr. S/W Engineer, Kernel Security, Base Operating Systems
Remote, Ottawa, Red Hat Canada
IRC: rgb, SunRaycer
Voice: +1.647.777.2635, Internal: (81) 32635

^ permalink raw reply

* Re: [RFC PATCH] can: m_can: Support higher speed CAN-FD bitrates
From: Sekhar Nori @ 2017-09-14  5:06 UTC (permalink / raw)
  To: Franklin S Cooper Jr, wg, mkl, mario.huettel, socketcan,
	quentin.schulz, edumazet, linux-can, netdev, linux-kernel
  Cc: Wenyou Yang, Dong Aisheng
In-Reply-To: <4f8f6b64-b2a2-b9dc-665c-f1c155daf994@ti.com>


On Thursday 14 September 2017 03:28 AM, Franklin S Cooper Jr wrote:
> 
> 
> On 08/18/2017 02:39 PM, Franklin S Cooper Jr wrote:
>> During test transmitting using CAN-FD at high bitrates (4 Mbps) only
>> resulted in errors. Scoping the signals I noticed that only a single bit
>> was being transmitted and with a bit more investigation realized the actual
>> MCAN IP would go back to initialization mode automatically.
>>
>> It appears this issue is due to the MCAN needing to use the Transmitter
>> Delay Compensation Mode as defined in the MCAN User's Guide. When this
>> mode is used the User's Guide indicates that the Transmitter Delay
>> Compensation Offset register should be set. The document mentions that this
>> register should be set to (1/dbitrate)/2*(Func Clk Freq).
>>
>> Additional CAN-CIA's "Bit Time Requirements for CAN FD" document indicates
>> that this TDC mode is only needed for data bit rates above 2.5 Mbps.
>> Therefore, only enable this mode and only set TDCO when the data bit rate
>> is above 2.5 Mbps.
>>
>> Signed-off-by: Franklin S Cooper Jr <fcooper@ti.com>
>> ---
>> I'm pretty surprised that this hasn't been implemented already since
>> the primary purpose of CAN-FD is to go beyond 1 Mbps and the MCAN IP
>> supports up to 10 Mbps.
>>
>> So it will be nice to get comments from users of this driver to understand
>> if they have been able to use CAN-FD beyond 2.5 Mbps without this patch.
>> If they haven't what did they do to get around it if they needed higher
>> speeds.
>>
>> Meanwhile I plan on testing this using a more "realistic" CAN bus to insure
>> everything still works at 5 Mbps which is the max speed of my CAN
>> transceiver.
> 
> ping. Anyone has any thoughts on this?

I added Dong who authored the m_can driver and Wenyou who added the only
in-kernel user of the driver for any help.

Thanks,
Sekhar

>>
>>  drivers/net/can/m_can/m_can.c | 24 +++++++++++++++++++++++-
>>  1 file changed, 23 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
>> index f4947a7..720e073 100644
>> --- a/drivers/net/can/m_can/m_can.c
>> +++ b/drivers/net/can/m_can/m_can.c
>> @@ -126,6 +126,12 @@ enum m_can_mram_cfg {
>>  #define DBTP_DSJW_SHIFT		0
>>  #define DBTP_DSJW_MASK		(0xf << DBTP_DSJW_SHIFT)
>>  
>> +/* Transmitter Delay Compensation Register (TDCR) */
>> +#define TDCR_TDCO_SHIFT		8
>> +#define TDCR_TDCO_MASK		(0x7F << TDCR_TDCO_SHIFT)
>> +#define TDCR_TDCF_SHIFT		0
>> +#define TDCR_TDCF_MASK		(0x7F << TDCR_TDCO_SHIFT)
>> +
>>  /* Test Register (TEST) */
>>  #define TEST_LBCK		BIT(4)
>>  
>> @@ -977,6 +983,8 @@ static int m_can_set_bittiming(struct net_device *dev)
>>  	const struct can_bittiming *dbt = &priv->can.data_bittiming;
>>  	u16 brp, sjw, tseg1, tseg2;
>>  	u32 reg_btp;
>> +	u32 enable_tdc = 0;
>> +	u32 tdco;
>>  
>>  	brp = bt->brp - 1;
>>  	sjw = bt->sjw - 1;
>> @@ -991,9 +999,23 @@ static int m_can_set_bittiming(struct net_device *dev)
>>  		sjw = dbt->sjw - 1;
>>  		tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
>>  		tseg2 = dbt->phase_seg2 - 1;
>> +
>> +		/* TDC is only needed for bitrates beyond 2.5 MBit/s
>> +		 * Specified in the "Bit Time Requirements for CAN FD" document
>> +		 */
>> +		if (dbt->bitrate > 2500000) {
>> +			enable_tdc = DBTP_TDC;
>> +			/* Equation based on Bosch's M_CAN User Manual's
>> +			 * Transmitter Delay Compensation Section
>> +			 */
>> +			tdco = priv->can.clock.freq / (dbt->bitrate * 2);
>> +			m_can_write(priv, M_CAN_TDCR, tdco << TDCR_TDCO_SHIFT);
>> +		}
>> +
>>  		reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) |
>>  			(tseg1 << DBTP_DTSEG1_SHIFT) |
>> -			(tseg2 << DBTP_DTSEG2_SHIFT);
>> +			(tseg2 << DBTP_DTSEG2_SHIFT) | enable_tdc;
>> +
>>  		m_can_write(priv, M_CAN_DBTP, reg_btp);
>>  	}
>>  
>>

^ permalink raw reply

* [PATCH] staging: irda: Remove typedef struct
From: Haneen Mohammed @ 2017-09-14  4:55 UTC (permalink / raw)
  To: outreachy-kernel
  Cc: Samuel Ortiz, Greg Kroah-Hartman, netdev, devel, linux-kernel

This patch remove typedef from a structure with all its ocurrences
since using typedefs for structures is discouraged.
Issue found using Coccinelle:

@r1@
type T;
@@

typedef struct { ... } T;

@script:python c1@
T2;
T << r1.T;
@@
if T[-2:] =="_t" or T[-2:] == "_T":
	coccinelle.T2 = T[:-2];
else:
	coccinelle.T2 = T;

print T, coccinelle.T2

@r2@
type r1.T;
identifier c1.T2;
@@
-typedef
struct
+ T2
{ ... }
-T
;

@r3@
type r1.T;
identifier c1.T2;
@@
-T
+struct T2

Signed-off-by: Haneen Mohammed <hamohammed.sa@gmail.com>
---
 drivers/staging/irda/include/net/irda/qos.h | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/irda/include/net/irda/qos.h b/drivers/staging/irda/include/net/irda/qos.h
index 05a5a24..a0315b5 100644
--- a/drivers/staging/irda/include/net/irda/qos.h
+++ b/drivers/staging/irda/include/net/irda/qos.h
@@ -58,23 +58,23 @@
 #define IR_16000000 0x02
 
 /* Quality of Service information */
-typedef struct {
+struct qos_value {
 	__u32 value;
 	__u16 bits; /* LSB is first byte, MSB is second byte */
-} qos_value_t;
+};
 
 struct qos_info {
 	magic_t magic;
 
-	qos_value_t baud_rate;       /* IR_11520O | ... */
-	qos_value_t max_turn_time;
-	qos_value_t data_size;
-	qos_value_t window_size;
-	qos_value_t additional_bofs;
-	qos_value_t min_turn_time;
-	qos_value_t link_disc_time;
+	struct qos_value baud_rate;       /* IR_11520O | ... */
+	struct qos_value max_turn_time;
+	struct qos_value data_size;
+	struct qos_value window_size;
+	struct qos_value additional_bofs;
+	struct qos_value min_turn_time;
+	struct qos_value link_disc_time;
 	
-	qos_value_t power;
+	struct qos_value power;
 };
 
 extern int sysctl_max_baud_rate;
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH 01/10] arch:powerpc: return -ENOMEM on failed allocation
From: Allen @ 2017-09-14  4:43 UTC (permalink / raw)
  To: Joe Perches
  Cc: linux-kernel, nouveau, linux-crypto, dri-devel,
	MPT-FusionLinux.pdl, linux-scsi, netdev, megaraidlinux.pdl,
	target-devel, linux-fbdev, linux-btrfs
In-Reply-To: <1505314401.8969.11.camel@perches.com>

> I think the changelog for this series of conversions
> should show that you've validated the change by
> inspecting the return call chain at each modified line.
>
> Also, it seems you've cc'd the same mailing lists for
> all of the patches modified by this series.
>
> It would be better to individually address each patch
> in the series only cc'ing the appropriate maintainers
> and mailing lists.
>
> A cover letter would be good too.

 Point noted. Thanks.

- Allen

^ permalink raw reply

* Re: Regression in throughput between kvm guests over virtual bridge
From: Jason Wang @ 2017-09-14  4:21 UTC (permalink / raw)
  To: Matthew Rosato, netdev; +Cc: davem, mst
In-Reply-To: <627d0c7a-dce5-3094-d5d4-c1507fcb8080@linux.vnet.ibm.com>



On 2017年09月14日 00:59, Matthew Rosato wrote:
> On 09/13/2017 04:13 AM, Jason Wang wrote:
>>
>> On 2017年09月13日 09:16, Jason Wang wrote:
>>>
>>> On 2017年09月13日 01:56, Matthew Rosato wrote:
>>>> We are seeing a regression for a subset of workloads across KVM guests
>>>> over a virtual bridge between host kernel 4.12 and 4.13. Bisecting
>>>> points to c67df11f "vhost_net: try batch dequing from skb array"
>>>>
>>>> In the regressed environment, we are running 4 kvm guests, 2 running as
>>>> uperf servers and 2 running as uperf clients, all on a single host.
>>>> They are connected via a virtual bridge.  The uperf client profile looks
>>>> like:
>>>>
>>>> <?xml version="1.0"?>
>>>> <profile name="TCP_STREAM">
>>>>     <group nprocs="1">
>>>>       <transaction iterations="1">
>>>>         <flowop type="connect" options="remotehost=192.168.122.103
>>>> protocol=tcp"/>
>>>>       </transaction>
>>>>       <transaction duration="300">
>>>>         <flowop type="write" options="count=16 size=30000"/>
>>>>       </transaction>
>>>>       <transaction iterations="1">
>>>>         <flowop type="disconnect"/>
>>>>       </transaction>
>>>>     </group>
>>>> </profile>
>>>>
>>>> So, 1 tcp streaming instance per client.  When upgrading the host kernel
>>>> from 4.12->4.13, we see about a 30% drop in throughput for this
>>>> scenario.  After the bisect, I further verified that reverting c67df11f
>>>> on 4.13 "fixes" the throughput for this scenario.
>>>>
>>>> On the other hand, if we increase the load by upping the number of
>>>> streaming instances to 50 (nprocs="50") or even 10, we see instead a
>>>> ~10% increase in throughput when upgrading host from 4.12->4.13.
>>>>
>>>> So it may be the issue is specific to "light load" scenarios.  I would
>>>> expect some overhead for the batching, but 30% seems significant...  Any
>>>> thoughts on what might be happening here?
>>>>
>>> Hi, thanks for the bisecting. Will try to see if I can reproduce.
>>> Various factors could have impact on stream performance. If possible,
>>> could you collect the #pkts and average packet size during the test?
>>> And if you guest version is above 4.12, could you please retry with
>>> napi_tx=true?
> Original runs were done with guest kernel 4.4 (from ubuntu 16.04.3 -
> 4.4.0-93-generic specifically).  Here's a throughput report (uperf) and
> #pkts and average packet size (tcpstat) for one of the uperf clients:
>
> host 4.12 / guest 4.4:
> throughput: 29.98Gb/s
> #pkts=33465571 avg packet size=33755.70
>
> host 4.13 / guest 4.4:
> throughput: 20.36Gb/s
> #pkts=21233399 avg packet size=36130.69

I test guest 4.4 on Intel machine, still can reproduce :(

>
> I ran the test again using net-next.git as guest kernel, with and
> without napi_tx=true.  napi_tx did not seem to have any significant
> impact on throughput.  However, the guest kernel shift from
> 4.4->net-next improved things.  I can still see a regression between
> host 4.12 and 4.13, but it's more on the order of 10-15% - another sample:
>
> host 4.12 / guest net-next (without napi_tx):
> throughput: 28.88Gb/s
> #pkts=31743116 avg packet size=33779.78
>
> host 4.13 / guest net-next (without napi_tx):
> throughput: 24.34Gb/s
> #pkts=25532724 avg packet size=35963.20

Thanks for the numbers. I originally suspect batching will lead more 
pkts but less size, but looks not. The less packets is also a hint that 
there's delay somewhere.

>
>>> Thanks
>> Unfortunately, I could not reproduce it locally. I'm using net-next.git
>> as guest. I can get ~42Gb/s on Intel(R) Xeon(R) CPU E5-2650 0 @ 2.00GHz
>> for both before and after the commit. I use 1 vcpu and 1 queue, and pin
>> vcpu and vhost threads into separate cpu on host manually (in same numa
>> node).
> The environment is quite a bit different -- I'm running in an LPAR on a
> z13 (s390x).  We've seen the issue in various configurations, the
> smallest thus far was a host partition w/ 40G and 20 CPUs defined (the
> numbers above were gathered w/ this configuration).  Each guest has 4GB
> and 4 vcpus.  No pinning / affinity configured.

Unfortunately, I don't have s390x on hand. Will try to get one.

>
>> Can you hit this regression constantly and what's you qemu command line
> Yes, the regression seems consistent.  I can try tweaking some of the
> host and guest definitions to see if it makes a difference.

Is the issue gone if you reduce VHOST_RX_BATCH to 1? And it would be 
also helpful to collect perf diff to see if anything interesting. 
(Consider 4.4 shows more obvious regression, please use 4.4).

>
> The guests are instantiated from libvirt - Here's one of the resulting
> qemu command lines:
>
> /usr/bin/qemu-system-s390x -name guest=mjrs34g1,debug-threads=on -S
> -object
> secret,id=masterKey0,format=raw,file=/var/lib/libvirt/qemu/domain-1-mjrs34g1/master-key.aes
> -machine s390-ccw-virtio-2.10,accel=kvm,usb=off,dump-guest-core=off -m
> 4096 -realtime mlock=off -smp 4,sockets=4,cores=1,threads=1 -uuid
> 44710587-e783-4bd8-8590-55ff421431b1 -display none -no-user-config
> -nodefaults -chardev
> socket,id=charmonitor,path=/var/lib/libvirt/qemu/domain-1-mjrs34g1/monitor.sock,server,nowait
> -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc
> -no-shutdown -boot strict=on -drive
> file=/dev/disk/by-id/scsi-3600507630bffc0380000000000001803,format=raw,if=none,id=drive-virtio-disk0
> -device
> virtio-blk-ccw,scsi=off,devno=fe.0.0000,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1
> -netdev tap,fd=25,id=hostnet0,vhost=on,vhostfd=27 -device
> virtio-net-ccw,netdev=hostnet0,id=net0,mac=02:de:26:53:14:01,devno=fe.0.0001
> -netdev tap,fd=28,id=hostnet1,vhost=on,vhostfd=29 -device
> virtio-net-ccw,netdev=hostnet1,id=net1,mac=02:54:00:89:d4:01,devno=fe.0.00a1
> -chardev pty,id=charconsole0 -device
> sclpconsole,chardev=charconsole0,id=console0 -device
> virtio-balloon-ccw,id=balloon0,devno=fe.0.0002 -msg timestamp=on
>
> In the above, net0 is used for a macvtap connection (not used in the
> experiment, just for a reliable ssh connection - can remove if needed).
> net1 is the bridge connection used for the uperf tests.
>
>
>> and #cpus on host? Is zerocopy enabled?
> Host info provided above.
>
> cat /sys/module/vhost_net/parameters/experimental_zcopytx
> 1

May worth to try disable zerocopy or do the test form host to guest 
instead of guest to guest to exclude the possible issue of sender.

Thanks

^ permalink raw reply

* Re: [PATCH RFC v1 0/3] Support for tap user-space access with veth interfaces
From: Jason Wang @ 2017-09-14  4:12 UTC (permalink / raw)
  To: sainath.grandhi, netdev; +Cc: davem
In-Reply-To: <1504744467-79590-1-git-send-email-sainath.grandhi@intel.com>



On 2017年09月07日 08:34, sainath.grandhi@intel.com wrote:
> From: Sainath Grandhi <sainath.grandhi@intel.com>
>
> This patchset adds a tap device driver for veth virtual network interface.
> With this implementation, tap character interface can be added only to the
> peer veth interface. Adding tap interface to veth is for usecases that forwards
> packets between host and VMs. This eliminates the need for an additional
> software bridge. This can be extended to create both the peer interfaces as
> tap interfaces. These patches are a step in that direction.
>
> Sainath Grandhi (3):
>    net: Adding API to parse IFLA_LINKINFO attribute
>    net: Abstracting out common routines from veth for use by vethtap
>    vethtap: veth based tap driver
>
>   drivers/net/Kconfig                 |   1 +
>   drivers/net/Makefile                |   2 +
>   drivers/net/{veth.c => veth_main.c} |  80 ++++++++++---
>   drivers/net/vethtap.c               | 216 ++++++++++++++++++++++++++++++++++++
>   include/linux/if_veth.h             |  13 +++
>   include/net/rtnetlink.h             |   3 +
>   net/core/rtnetlink.c                |   8 ++
>   7 files changed, 308 insertions(+), 15 deletions(-)
>   rename drivers/net/{veth.c => veth_main.c} (89%)
>   create mode 100644 drivers/net/vethtap.c
>   create mode 100644 include/linux/if_veth.h
>

Interesting, plan to add vhost support for this? And we can enable 
zerocopy without any worries I think.

Thanks

^ permalink raw reply

* [PATCH net] tcp: update skb->skb_mstamp more carefully
From: Eric Dumazet @ 2017-09-14  3:30 UTC (permalink / raw)
  To: liujian, David Miller
  Cc: edumazet, ycheng, hkchu, netdev, weiyongjun1, wangkefeng 00227729
In-Reply-To: <1505313647.15310.165.camel@edumazet-glaptop3.roam.corp.google.com>

From: Eric Dumazet <edumazet@googl.com>

liujian reported a problem in TCP_USER_TIMEOUT processing with a patch
in tcp_probe_timer() :
      https://www.spinics.net/lists/netdev/msg454496.html

After investigations, the root cause of the problem is that we update
skb->skb_mstamp of skbs in write queue, even if the attempt to send a
clone or copy of it failed. One reason being a routing problem.

This patch prevents this, solving liujian issue.

It also removes a potential RTT miscalculation, since
__tcp_retransmit_skb() is not OR-ing TCP_SKB_CB(skb)->sacked with
TCPCB_EVER_RETRANS if a failure happens, but skb->skb_mstamp has
been changed.

A future ACK would then lead to a very small RTT sample and min_rtt
would then be lowered to this too small value.

Tested:

# cat user_timeout.pkt
--local_ip=192.168.102.64

    0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
   +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
   +0 bind(3, ..., ...) = 0
   +0 listen(3, 1) = 0

   +0 `ifconfig tun0 192.168.102.64/16; ip ro add 192.0.2.1 dev tun0`

   +0 < S 0:0(0) win 0 <mss 1460>
   +0 > S. 0:0(0) ack 1 <mss 1460>

  +.1 < . 1:1(0) ack 1 win 65530
   +0 accept(3, ..., ...) = 4

   +0 setsockopt(4, SOL_TCP, TCP_USER_TIMEOUT, [3000], 4) = 0
   +0 write(4, ..., 24) = 24
   +0 > P. 1:25(24) ack 1 win 29200
   +.1 < . 1:1(0) ack 25 win 65530

//change the ipaddress
   +1 `ifconfig tun0 192.168.0.10/16`

   +1 write(4, ..., 24) = 24
   +1 write(4, ..., 24) = 24
   +1 write(4, ..., 24) = 24
   +1 write(4, ..., 24) = 24

   +0 `ifconfig tun0 192.168.102.64/16`
   +0 < . 1:2(1) ack 25 win 65530
   +0 `ifconfig tun0 192.168.0.10/16`

   +3 write(4, ..., 24) = -1

# ./packetdrill user_timeout.pkt

Signed-off-by: Eric Dumazet <edumazet@googl.com>
Reported-by: liujian <liujian56@huawei.com>
---
 net/ipv4/tcp_output.c |   19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5b6690d05abb98884adfa1693f97d896dd202893..a85a8c2948e54b931f8cd956aa7938f7efd355bd 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -991,6 +991,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	struct tcp_skb_cb *tcb;
 	struct tcp_out_options opts;
 	unsigned int tcp_options_size, tcp_header_size;
+	struct sk_buff *oskb = NULL;
 	struct tcp_md5sig_key *md5;
 	struct tcphdr *th;
 	int err;
@@ -998,12 +999,12 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 	BUG_ON(!skb || !tcp_skb_pcount(skb));
 	tp = tcp_sk(sk);
 
-	skb->skb_mstamp = tp->tcp_mstamp;
 	if (clone_it) {
 		TCP_SKB_CB(skb)->tx.in_flight = TCP_SKB_CB(skb)->end_seq
 			- tp->snd_una;
 		tcp_rate_skb_sent(sk, skb);
 
+		oskb = skb;
 		if (unlikely(skb_cloned(skb)))
 			skb = pskb_copy(skb, gfp_mask);
 		else
@@ -1011,6 +1012,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 		if (unlikely(!skb))
 			return -ENOBUFS;
 	}
+	skb->skb_mstamp = tp->tcp_mstamp;
 
 	inet = inet_sk(sk);
 	tcb = TCP_SKB_CB(skb);
@@ -1122,12 +1124,14 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 
 	err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);
 
-	if (likely(err <= 0))
-		return err;
-
-	tcp_enter_cwr(sk);
+	if (unlikely(err > 0)) {
+		tcp_enter_cwr(sk);
+		err = net_xmit_eval(err);
+	}
+	if (!err && oskb)
+		oskb->skb_mstamp = tp->tcp_mstamp;
 
-	return net_xmit_eval(err);
+	return err;
 }
 
 /* This routine just queues the buffer for sending.
@@ -2869,10 +2873,11 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
 		     skb_headroom(skb) >= 0xFFFF)) {
 		struct sk_buff *nskb;
 
-		skb->skb_mstamp = tp->tcp_mstamp;
 		nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
 		err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
 			     -ENOBUFS;
+		if (!err)
+			skb->skb_mstamp = tp->tcp_mstamp;
 	} else {
 		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
 	}

^ permalink raw reply related

* [PATCH] net/packet: fix race condition between fanout_add and __unregister_prot_hook
From: nixiaoming @ 2017-09-14  2:44 UTC (permalink / raw)
  To: davem, edumazet, waltje, gw4pts, andreyknvl, tklauser,
	philip.pettersson, glider
  Cc: netdev, linux-kernel, nixiaoming, dede.wu

If fanout_add is preempted after running po-> fanout = match
and before running __fanout_link,
it will cause BUG_ON when __unregister_prot_hook call __fanout_unlink

so, we need add mutex_lock(&fanout_mutex) to __unregister_prot_hook
or add spin_lock(&po->bind_lock) before po-> fanout = match

test on linux 4.1.12:
./trinity -c setsockopt -C 2 -X &

BUG: failure at net/packet/af_packet.c:1414/__fanout_unlink()!
Kernel panic - not syncing: BUG!
CPU: 2 PID: 2271 Comm: trinity-c0 Tainted: G        W  O    4.1.12 #1
Hardware name: Hisilicon PhosphorHi1382 FPGA (DT)
Call trace:
[<ffffffc000209414>] dump_backtrace+0x0/0xf8
[<ffffffc00020952c>] show_stack+0x20/0x28
[<ffffffc000635574>] dump_stack+0xac/0xe4
[<ffffffc000633fb8>] panic+0xf8/0x268
[<ffffffc0005fa778>] __unregister_prot_hook+0xa0/0x144
[<ffffffc0005fba48>] packet_set_ring+0x280/0x5b4
[<ffffffc0005fc33c>] packet_setsockopt+0x320/0x950
[<ffffffc000554a04>] SyS_setsockopt+0xa4/0xd4

Signed-off-by: nixiaoming <nixiaoming@huawei.com>
Tested-by: wudesheng <dede.wu@huawei.com>
---
 net/packet/af_packet.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 008a45c..0300146 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -365,10 +365,12 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
 
 	po->running = 0;
 
+	mutex_lock(&fanout_mutex);
 	if (po->fanout)
 		__fanout_unlink(sk, po);
 	else
 		__dev_remove_pack(&po->prot_hook);
+	mutex_unlock(&fanout_mutex);
 
 	__sock_put(sk);
 
-- 
2.11.0.1

^ 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