Netdev List
 help / color / mirror / Atom feed
* [net-next PATCH v3 12/12] net: rocker: implement delete flow routine
From: John Fastabend @ 2015-01-20 20:31 UTC (permalink / raw)
  To: tgraf, simon.horman, sfeldma; +Cc: netdev, jhs, davem, gerlitz.or, andy, ast
In-Reply-To: <20150120202404.1741.8658.stgit@nitbit.x32>

Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
---
 drivers/net/ethernet/rocker/rocker.c |   46 +++++++++++++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index 3ceb313..ba48e88 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4436,7 +4436,51 @@ static int rocker_set_rules(struct net_device *dev,
 static int rocker_del_rules(struct net_device *dev,
 			    struct net_flow_rule *rule)
 {
-	return -EOPNOTSUPP;
+	struct rocker_port *rocker_port = netdev_priv(dev);
+	struct rocker_flow_tbl_entry *entry;
+	struct rocker_group_tbl_entry *group;
+	struct hlist_node *tmp;
+	int bkt, err = -EEXIST;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rocker_port->rocker->flow_tbl_lock, flags);
+	hash_for_each_safe(rocker_port->rocker->flow_tbl,
+			   bkt, tmp, entry, entry) {
+		if (rocker_goto_value(rule->table_id) != entry->key.tbl_id ||
+		    rule->uid != entry->cookie)
+			continue;
+
+		hash_del(&entry->entry);
+		err = 0;
+		break;
+	}
+	spin_unlock_irqrestore(&rocker_port->rocker->flow_tbl_lock, flags);
+
+	if (!err)
+		goto done;
+
+	spin_lock_irqsave(&rocker_port->rocker->group_tbl_lock, flags);
+	hash_for_each_safe(rocker_port->rocker->group_tbl,
+			   bkt, tmp, group, entry) {
+		if (rocker_goto_value(rule->table_id) !=
+			ROCKER_GROUP_TYPE_GET(group->group_id) ||
+		    rule->uid != group->cookie)
+			continue;
+
+		hash_del(&group->entry);
+		err = 0;
+		break;
+	}
+	spin_unlock_irqrestore(&rocker_port->rocker->group_tbl_lock, flags);
+
+done:
+	if (!err) {
+		err = rocker_cmd_exec(rocker_port->rocker, rocker_port,
+				      rocker_cmd_flow_tbl_del,
+				      entry, NULL, NULL, true);
+		kfree(entry);
+	}
+	return err;
 }
 #endif
 

^ permalink raw reply related

* Re: Re: Re: [PATCH tip 0/9] tracing: attach eBPF programs to tracepoints/syscalls/kprobe
From: Alexei Starovoitov @ 2015-01-20 20:33 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ingo Molnar, Steven Rostedt, Namhyung Kim,
	Arnaldo Carvalho de Melo, Jiri Olsa, David S. Miller,
	Daniel Borkmann, Hannes Frederic Sowa, Brendan Gregg, Linux API,
	Network Development, LKML, yrl.pp-manager.tt@hitachi.com,
	Jovi Zhangwei

On Tue, Jan 20, 2015 at 3:57 AM, Masami Hiramatsu
<masami.hiramatsu.pt@hitachi.com> wrote:
>
> Ok, BTW, would you think is it possible to use a reusable small scratchpad
> memory for passing arguments? (just a thought)

sure. doable, but what's the use case?

>> It's not usable for high frequency events which
>> need this in-kernel aggregation.
>> If events are rare, then just dumping everything
>> into trace buffer is just fine. No in-kernel program is needed.
>
> Hmm, let me ensure your point, the performance number is the reason why
> we need to do it in the kernel, right? Not mainly for the flexibility but speed.

if user space can do X at the same speed as kernel,
then user space is a better choice and more flexible.
In case of bpf programs two things user space cannot do:
- fast aggregation without adding penalty to things being traced
- access to in-kernel data structures
And often both used together.
Say, we want to monitor amount of network traffic per user.
So we'd use trace_net_dev_xmit() tracepoint and do
map[current_uid()] += skb_len
as part of the program.
Overhead will be tiny and users won't notice any slowdown.
Trying to do the same in user space by enabling
this tracepoint has two problems:
high overhead and events are hard to aggregate
per user, since trace has 'pid', but short lived
processes will have dead pids in trace output.

> - perf probe and kprobe-event gives us a complete understandable
>  interface for what will be recorded at where.
>  (we can see the event definitions via kprobe_events interface,
>   without any tools)
> - kprobe-event gives a completely same interface as other tracepoint
>   events.
> - it also doesn't require any build-binary parts :) nor special tools.
>   We can play with ftrace on just a small busybox.

yeah, when debugging in busybox is the goal
and 'cat' and 'echo' are your only tools, then
debugfs interface is the only choice :)

> However, this does NOT interfere your patch upstreaming. I just said current
> ftrace method is also meaningful for some reasons :)

of course :)
To emphasize the point I was trying to make with tracex1:
The program is a filter/aggregator. The bpf maps
are not suitable for streaming the events. That's the job
of ring buffer/trace_pipe. The program may choose
to aggregate some events and discard them (by
returning 0 from the program), and the rest of
the events will be streamed to user space via
ring buffer in the format statically defined by tracepoint
or by kprobe arguments.
The tracex1 example loads the program and then
reads /sys/kernel/debugfs/tracing/trace_pipe...

That part I was trying to improve with bpf_trace_printk:
to give ability to programs to stream data in a format
different from the one statically defined by tracepoints.
But trace_printk has its disadvantages, so probably
something cleaner is needed.
Like in my earlier example of trace_net_dev_xmit,
if the program could add printing of uid to arguments
already printed, it would have helped user space.

> By the way, I concern about that bpf compiler can become another systemtap,
> especially if you build it on llvm.
> Would you plan to develop it on kernel
> tree? or apart from the kernel-side development?

I'm not sure I completely understand the concern.
perf is using a bunch of out-of-tree libraries.
mcjit of llvm or libgccjit are another libraries.
Or may be eventually eBPF can be generated
by something like libpcap.

Ideally I would like to see 'perf run script.txt'
where script.txt is a program in a language suited
for tracing. The tracing language not necessary
will fit networking use cases. Currently I'm
using C for both and it's the most convenient,
but some folks complained that 'restricted'
nature of this C is hard to grasp, so I can only
encourage Jovi to do ktap language to bpf
translator. If it generates bpf directly that's great,
if it uses gcc or llvm backend that's fine too.

> I think it is hard to sync the development if you do it out-of-tree.

I think some pieces would have to be out of tree.
I've kept standalone llvm backend across 3.2, 3.3 and 3.4
but it gets polluted with ifdefs and not really a long term
solution, so now I'm working on upstreaming it
and feedback/codereviews I got, definitely improved
the quality of the bpf backend.
In case of backends the only bit to sync is instruction
set itself, which is stable. New instructions may be
added, but that's not a concern.
llvm backend doesn't care what language is
used in front-end or how programs are attached
to tracepoints or what set of bpf helper
functions is available.
All such bits and the main interface for
dynamic tracer, imo, should be in perf binary.
What it does underneath and how
many times it calls into llvm/gcc lib, won't be visible.
In case of systemtap compile time, for whatever
reason, is slow to the point of being annoying,
but here it should be instant.

^ permalink raw reply

* [PATCH net-next] amd-xgbe: Let OS arch support adjust DMA mask as needed
From: Tom Lendacky @ 2015-01-20 20:47 UTC (permalink / raw)
  To: netdev; +Cc: David Miller

Set the DMA mask to 64-bit and let the underlying arch support adjust
it as appropriate based on device tree DMA ranges, etc.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
 drivers/net/ethernet/amd/xgbe/xgbe-main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index 32dd651..080c3a8 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -377,7 +377,7 @@ static int xgbe_probe(struct platform_device *pdev)
 	/* Set the DMA mask */
 	if (!dev->dma_mask)
 		dev->dma_mask = &dev->coherent_dma_mask;
-	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
 	if (ret) {
 		dev_err(dev, "dma_set_mask_and_coherent failed\n");
 		goto err_io;

^ permalink raw reply related

* [PATCH net-next 0/3] openvswitch: Add STT support.
From: Pravin B Shelar @ 2015-01-20 20:25 UTC (permalink / raw)
  To: davem; +Cc: netdev, Pravin B Shelar

Following patch series adds support for Stateless Transport
Tunneling protocol.
STT uses TCP segmentation offload available in most of NIC. On
packet xmit STT driver appends STT header along with TCP header
to the packet. For GSO packet GSO parameters are set according
to tunnel configuration and packet is handed over to networking
stack. This allows use of segmentation offload available in NICs

Netperf unidirectional test gives ~9.4 Gbits/s performance on 10Gbit
NIC with 1500 byte MTU with two TCP streams.

The protocol is documented at
http://www.ietf.org/archive/id/draft-davie-stt-06.txt

I will send out OVS userspace patch on ovs-dev mailing list.

Pravin B Shelar (3):
  skbuff: Add skb_list_linearize()
  net: Add STT tunneling protocol.
  openvswitch: Add support for STT tunneling.

 include/linux/skbuff.h           |    2 +
 include/net/stt.h                |   55 ++
 include/uapi/linux/openvswitch.h |    1 +
 net/core/skbuff.c                |   35 +
 net/ipv4/Kconfig                 |   11 +
 net/ipv4/Makefile                |    1 +
 net/ipv4/stt.c                   | 1386 ++++++++++++++++++++++++++++++++++++++
 net/openvswitch/Kconfig          |   10 +
 net/openvswitch/Makefile         |    1 +
 net/openvswitch/vport-stt.c      |  214 ++++++
 10 files changed, 1716 insertions(+)
 create mode 100644 include/net/stt.h
 create mode 100644 net/ipv4/stt.c
 create mode 100644 net/openvswitch/vport-stt.c

-- 
1.9.1

^ permalink raw reply

* [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
From: Pravin B Shelar @ 2015-01-20 20:25 UTC (permalink / raw)
  To: davem; +Cc: netdev, Pravin B Shelar

similar to skb_linearize(), this API takes skb list as arg and
linearize it into one big skb. STT driver patch will use this.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
---
 include/linux/skbuff.h |  2 ++
 net/core/skbuff.c      | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 85ab7d7..c9194c1 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -2532,6 +2532,8 @@ static inline int skb_linearize(struct sk_buff *skb)
 	return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0;
 }
 
+int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask);
+
 /**
  * skb_has_shared_frag - can any frag be overwritten
  * @skb: buffer to test
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 56db472..9ef2881 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2329,6 +2329,41 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
 }
 EXPORT_SYMBOL(skb_copy_and_csum_dev);
 
+int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask)
+{
+	struct sk_buff *skb;
+	int tlen = 0;
+	int err;
+
+	err = skb_linearize(head);
+	if (err)
+		return err;
+
+	skb = head->next;
+	while (skb) {
+		tlen += skb->len;
+		skb = skb->next;
+	}
+	err = pskb_expand_head(head, 0, tlen, gfp_mask);
+	if (err)
+		return err;
+
+	skb = head->next;
+	while (skb) {
+		err = skb_copy_bits(skb, 0, skb_tail_pointer(head), skb->len);
+		if (err)
+			return err;
+		head->tail += skb->len;
+		skb = skb->next;
+	}
+	kfree_skb_list(head->next);
+	head->next = NULL;
+	head->len += tlen;
+	head->truesize = SKB_TRUESIZE(skb_end_offset(head));
+	return 0;
+}
+EXPORT_SYMBOL(skb_list_linearize);
+
 /**
  *	skb_dequeue - remove from the head of the queue
  *	@list: list to dequeue from
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 2/3] net: Add STT tunneling protocol.
From: Pravin B Shelar @ 2015-01-20 20:25 UTC (permalink / raw)
  To: davem; +Cc: netdev, Pravin B Shelar, Jesse Gross

This adds a device level support for Stateless TCP Tunnel (STT)
protocol encapsulation. NF-hook is used for receiving STT
packets from networking stack.
Open vSwitch can be used for configuring, set up and tear down
STT tunnels.

The protocol is documented at
http://www.ietf.org/archive/id/draft-davie-stt-06.txt

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
---
 include/net/stt.h |   55 +++
 net/ipv4/Kconfig  |   11 +
 net/ipv4/Makefile |    1 +
 net/ipv4/stt.c    | 1386 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1453 insertions(+)
 create mode 100644 include/net/stt.h
 create mode 100644 net/ipv4/stt.c

diff --git a/include/net/stt.h b/include/net/stt.h
new file mode 100644
index 0000000..d3a9242
--- /dev/null
+++ b/include/net/stt.h
@@ -0,0 +1,55 @@
+#ifndef __NET_STT_H
+#define __NET_STT_H  1
+
+#include <net/ip_tunnels.h>
+
+struct stthdr {
+	__u8		version;
+	__u8		flags;
+	__u8		l4_offset;
+	__u8		reserved;
+	__be16		mss;
+	__be16		vlan_tci;
+	__be64		key;
+};
+
+/* Padding after the end of the tunnel headers to provide alignment
+ * for inner packet IP header after 14 byte Ethernet header.
+ */
+#define STT_ETH_PAD 2
+
+static inline struct stthdr *stt_hdr(const struct sk_buff *skb)
+{
+	return (struct stthdr *)(skb_transport_header(skb) +
+				 sizeof(struct tcphdr));
+}
+
+struct stt_sock;
+typedef void (stt_rcv_t)(struct stt_sock *stt_sock, struct sk_buff *skb);
+
+/* @list: Per-net list of STT ports.
+ * @rcv: The callback is called on STT packet recv, STT reassembly can generate
+ * multiple packets, in this case first packet has tunnel outer header, rest
+ * of the packets are inner packet segments with no stt header.
+ * @rcv_data: user data.
+ * @sock: Fake TCP socket for the STT port.
+ */
+struct stt_sock {
+	struct list_head	list;
+	stt_rcv_t		*rcv;
+	void			*rcv_data;
+	struct socket		*sock;
+	struct rcu_head		rcu;
+};
+
+struct stt_sock *stt_sock_add(struct net *net, __be16 port,
+			      stt_rcv_t *rcv, void *data);
+
+void stt_sock_release(struct stt_sock *vs);
+
+int stt_xmit_skb(struct sk_buff *skb, struct rtable *rt,
+		 __be32 src, __be32 dst, __u8 tos,
+		 __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port,
+		 __be64 tun_id);
+
+#endif /*ifdef__NET_STT_H */
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index bd29016..3ab00be 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -344,6 +344,17 @@ config GENEVE
 
 	  To compile this driver as a module, choose M here: the module
 
+config STT
+	tristate "STT Encapsulation"
+	depends on INET
+	depends on NETFILTER
+	---help---
+	This allows one to create STT virtual interfaces that provide
+	Layer 2 Networks over Layer 3 Networks. The STT protocol
+	described in the draft:
+	http://www.ietf.org/archive/id/draft-davie-stt-06.txt
+
+	  To compile this driver as a module, choose M here: the module
 
 config INET_AH
 	tristate "IP: AH transformation"
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 518c04e..d504fde 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
 obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
 obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
 obj-$(CONFIG_GENEVE) += geneve.o
+obj-$(CONFIG_STT) += stt.o
 
 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
 		      xfrm4_output.o xfrm4_protocol.o
diff --git a/net/ipv4/stt.c b/net/ipv4/stt.c
new file mode 100644
index 0000000..a444df9
--- /dev/null
+++ b/net/ipv4/stt.c
@@ -0,0 +1,1386 @@
+/*
+ * Stateless TCP Tunnel (STT) vport.
+ *
+ * Copyright (c) 2015 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/delay.h>
+#include <linux/flex_array.h>
+#include <linux/if.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/percpu.h>
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include <linux/workqueue.h>
+
+#include <net/icmp.h>
+#include <net/inet_ecn.h>
+#include <net/ip.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/sock.h>
+#include <net/stt.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+#define STT_VER 0
+
+#define STT_CSUM_VERIFIED	BIT(0)
+#define STT_CSUM_PARTIAL	BIT(1)
+#define STT_PROTO_IPV4		BIT(2)
+#define STT_PROTO_TCP		BIT(3)
+#define STT_PROTO_TYPES		(STT_PROTO_IPV4 | STT_PROTO_TCP)
+
+#define STT_BASE_HLEN   (sizeof(struct stthdr) + STT_ETH_PAD)
+#define STT_HEADER_LEN	(sizeof(struct tcphdr) + STT_BASE_HLEN)
+
+/* The length and offset of a fragment are encoded in the sequence number.
+ * STT_SEQ_LEN_SHIFT is the left shift needed to store the length.
+ * STT_SEQ_OFFSET_MASK is the mask to extract the offset.
+ */
+#define STT_SEQ_LEN_SHIFT 16
+#define STT_SEQ_OFFSET_MASK (BIT(STT_SEQ_LEN_SHIFT) - 1)
+
+/* The maximum amount of memory used to store packets waiting to be reassembled
+ * on a given CPU.  Once this threshold is exceeded we will begin freeing the
+ * least recently used fragments.
+ */
+#define REASM_HI_THRESH (4 * 1024 * 1024)
+/* The target for the high memory evictor.  Once we have exceeded
+ * REASM_HI_THRESH, we will continue freeing fragments until we hit
+ * this limit.
+ */
+#define REASM_LO_THRESH (3 * 1024 * 1024)
+/* The length of time a given packet has to be reassembled from the time the
+ * first fragment arrives.  Once this limit is exceeded it becomes available
+ * for cleaning.
+ */
+#define FRAG_EXP_TIME (30 * HZ)
+/* Number of hash entries.  Each entry has only a single slot to hold a packet
+ * so if there are collisions, we will drop packets.  This is allocated
+ * per-cpu and each entry consists of struct pkt_frag.
+ */
+#define FRAG_HASH_SHIFT		8
+#define FRAG_HASH_ENTRIES	BIT(FRAG_HASH_SHIFT)
+#define FRAG_HASH_SEGS		((sizeof(u32) * 8) / FRAG_HASH_SHIFT)
+
+#define CLEAN_PERCPU_INTERVAL (30 * HZ)
+
+struct pkt_key {
+	__be32 saddr;
+	__be32 daddr;
+	__be32 pkt_seq;
+	u32 mark;
+};
+
+struct pkt_frag {
+	struct sk_buff *skbs;
+	unsigned long timestamp;
+	struct list_head lru_node;
+	struct pkt_key key;
+};
+
+struct stt_percpu {
+	struct flex_array *frag_hash;
+	struct list_head frag_lru;
+	unsigned int frag_mem_used;
+
+	/* Protect frags table. */
+	spinlock_t lock;
+};
+
+struct first_frag {
+	struct sk_buff *last_skb;
+	unsigned int mem_used;
+	u16 tot_len;
+	u16 rcvd_len;
+	bool ecn_ce;
+};
+
+struct frag_skb_cb {
+	u16 offset;
+
+	/* Only valid for the first skb in the chain. */
+	struct first_frag first;
+};
+
+#define FRAG_CB(skb) ((struct frag_skb_cb *)(skb)->cb)
+
+static struct stt_percpu __percpu *stt_percpu_data __read_mostly;
+static u32 frag_hash_seed __read_mostly;
+
+/* Protects sock-hash and refcounts. */
+static DEFINE_MUTEX(stt_mutex);
+
+/* per-network namespace private data for this module */
+struct stt_net {
+	struct list_head sock_list;
+};
+
+static int stt_net_id;
+static int n_tunnels;
+static DEFINE_PER_CPU(u32, pkt_seq_counter);
+
+static void clean_percpu(struct work_struct *work);
+static DECLARE_DELAYED_WORK(clean_percpu_wq, clean_percpu);
+
+static struct stt_sock *stt_find_sock(struct net *net, __be16 port)
+{
+	struct stt_net *sn = net_generic(net, stt_net_id);
+	struct stt_sock *stt_sock;
+
+	list_for_each_entry_rcu(stt_sock, &sn->sock_list, list) {
+		if (inet_sk(stt_sock->sock->sk)->inet_sport == port)
+			return stt_sock;
+	}
+	return NULL;
+}
+
+static __be32 ack_seq(void)
+{
+#if NR_CPUS <= 65536
+	u32 pkt_seq, ack;
+
+	pkt_seq = this_cpu_read(pkt_seq_counter);
+	ack = pkt_seq << ilog2(NR_CPUS) | smp_processor_id();
+	this_cpu_inc(pkt_seq_counter);
+
+	return (__force __be32)ack;
+#else
+#error "Support for greater than 64k CPUs not implemented"
+#endif
+}
+
+static int clear_gso(struct sk_buff *skb)
+{
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	int err;
+
+	if (shinfo->gso_type == 0 && shinfo->gso_size == 0 &&
+	    shinfo->gso_segs == 0)
+		return 0;
+
+	err = skb_unclone(skb, GFP_ATOMIC);
+	if (unlikely(err))
+		return err;
+
+	shinfo = skb_shinfo(skb);
+	shinfo->gso_type = 0;
+	shinfo->gso_size = 0;
+	shinfo->gso_segs = 0;
+	return 0;
+}
+
+static struct sk_buff *stt_build_header(struct sk_buff *skb, __be64 tun_id,
+					__be16 s_port, __be16 d_port,
+					__be32 saddr, __be32 dst,
+					__be16 h_proto, u8 nw_proto,
+					int dst_mtu)
+{
+	int data_len = skb->len + sizeof(struct stthdr) + STT_ETH_PAD;
+	unsigned short encap_mss;
+	struct tcphdr *tcph;
+	struct stthdr *stth;
+
+	skb_push(skb, STT_HEADER_LEN);
+	skb_reset_transport_header(skb);
+	tcph = tcp_hdr(skb);
+	memset(tcph, 0, STT_HEADER_LEN);
+	stth = stt_hdr(skb);
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		stth->flags |= STT_CSUM_PARTIAL;
+
+		stth->l4_offset = skb->csum_start -
+					(skb_headroom(skb) +
+					skb_transport_offset(skb) +
+					STT_HEADER_LEN);
+
+		if (h_proto == htons(ETH_P_IP))
+			stth->flags |= STT_PROTO_IPV4;
+
+		if (nw_proto == IPPROTO_TCP)
+			stth->flags |= STT_PROTO_TCP;
+
+		stth->mss = htons(skb_shinfo(skb)->gso_size);
+	} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
+		stth->flags |= STT_CSUM_VERIFIED;
+	}
+
+	stth->vlan_tci = htons(skb->vlan_tci);
+	skb->vlan_tci = 0;
+	put_unaligned(tun_id, &stth->key);
+
+	tcph->source	= s_port;
+	tcph->dest	= d_port;
+	tcph->doff	= sizeof(struct tcphdr) / 4;
+	tcph->ack	= 1;
+	tcph->psh	= 1;
+	tcph->window	= htons(USHRT_MAX);
+	tcph->seq	= htonl(data_len << STT_SEQ_LEN_SHIFT);
+	tcph->ack_seq	= ack_seq();
+	tcph->check	= ~tcp_v4_check(skb->len - skb_transport_offset(skb),
+					saddr, dst, 0);
+
+	skb->csum_start = skb_transport_header(skb) - skb->head;
+	skb->csum_offset = offsetof(struct tcphdr, check);
+	skb->ip_summed = CHECKSUM_PARTIAL;
+
+	encap_mss = dst_mtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+	if (data_len > encap_mss) {
+		/* It's pretty rare to hit this case, so just fall back to
+		 * linearizing for now.
+		 */
+		if (skb_shinfo(skb)->frag_list &&
+		    unlikely(__skb_linearize(skb)))
+			goto error;
+
+		if (unlikely(skb_unclone(skb, GFP_ATOMIC)))
+			goto error;
+
+		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+		skb_shinfo(skb)->gso_size = encap_mss;
+		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(data_len, encap_mss);
+	} else {
+		if (unlikely(clear_gso(skb)))
+			goto error;
+	}
+
+	return skb;
+error:
+	kfree_skb(skb);
+	return NULL;
+}
+
+static bool stt_can_offload(struct sk_buff *skb, __be16 h_proto, u8 nw_proto)
+{
+	if (skb_is_gso(skb) && skb->ip_summed != CHECKSUM_PARTIAL) {
+		int csum_offset;
+		int len;
+		__sum16 *csum;
+
+		if (nw_proto == IPPROTO_TCP)
+			csum_offset = offsetof(struct tcphdr, check);
+		else if (nw_proto == IPPROTO_UDP)
+			csum_offset = offsetof(struct udphdr, check);
+		else
+			return false;
+
+		len = skb->len - skb_transport_offset(skb);
+		csum = (__sum16 *)(skb_transport_header(skb) + csum_offset);
+
+		if (h_proto == htons(ETH_P_IP)) {
+			struct iphdr *iph = ip_hdr(skb);
+			*csum = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+						   len, nw_proto, 0);
+		} else if (h_proto == htons(ETH_P_IPV6)) {
+			struct ipv6hdr *ip6h = ipv6_hdr(skb);
+			*csum = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+						 len, nw_proto, 0);
+		} else {
+			return false;
+		}
+		skb->csum_start = skb_transport_header(skb) - skb->head;
+		skb->csum_offset = csum_offset;
+		skb->ip_summed = CHECKSUM_PARTIAL;
+	}
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		/* Assume receiver can only offload TCP/UDP over IPv4/6,
+		 * and require 802.1Q VLANs to be accelerated.
+		 */
+		if (h_proto != htons(ETH_P_IP) &&
+		    h_proto != htons(ETH_P_IPV6))
+			return false;
+		if (nw_proto != IPPROTO_TCP && nw_proto != IPPROTO_UDP)
+			return false;
+
+		/* L4 offset must fit in a 1-byte field. */
+		if (skb->csum_start - skb_headroom(skb) > 255)
+			return false;
+
+		if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN)
+			return false;
+	}
+	/* Total size of encapsulated packet must fit in 16 bits. */
+	if (skb->len + STT_HEADER_LEN + sizeof(struct iphdr) > 65535)
+		return false;
+
+	return true;
+}
+
+static bool need_linearize(const struct sk_buff *skb)
+{
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	int i;
+
+	if (unlikely(shinfo->frag_list))
+		return true;
+
+	/* Generally speaking we should linearize if there are paged frags.
+	 * However, if all of the refcounts are 1 we know nobody else can
+	 * change them from underneath us and we can skip the linearization.
+	 */
+	for (i = 0; i < shinfo->nr_frags; i++)
+		if (unlikely(page_count(skb_frag_page(&shinfo->frags[i])) > 1))
+			return true;
+
+	return false;
+}
+
+static struct sk_buff *handle_offloads(struct sk_buff *skb)
+{
+	int err;
+
+	if (skb_is_gso(skb)) {
+		struct sk_buff *nskb;
+		char cb[sizeof(skb->cb)];
+
+		memcpy(cb, skb->cb, sizeof(cb));
+
+		nskb = __skb_gso_segment(skb, 0, false);
+		if (IS_ERR(nskb)) {
+			err = PTR_ERR(nskb);
+			goto error;
+		}
+
+		consume_skb(skb);
+		skb = nskb;
+		while (nskb) {
+			memcpy(nskb->cb, cb, sizeof(cb));
+			nskb = nskb->next;
+		}
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		/* Pages aren't locked and could change at any time.
+		 * If this happens after we compute the checksum, the
+		 * checksum will be wrong.  We linearize now to avoid
+		 * this problem.
+		 */
+		if (unlikely(need_linearize(skb))) {
+			err = __skb_linearize(skb);
+			if (unlikely(err))
+				goto error;
+		}
+
+		err = skb_checksum_help(skb);
+		if (unlikely(err))
+			goto error;
+	}
+
+	skb->ip_summed = CHECKSUM_NONE;
+
+	return skb;
+error:
+	return ERR_PTR(err);
+}
+
+int stt_xmit_skb(struct sk_buff *skb, struct rtable *rt,
+		 __be32 src, __be32 dst, __u8 tos,
+		 __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port,
+		 __be64 tun_id)
+{
+	struct ethhdr *eh = eth_hdr(skb);
+	struct iphdr *iph = ip_hdr(skb);
+	__be16 inner_h_proto;
+	 u8 inner_nw_proto;
+	int ret = 0, min_headroom;
+
+	inner_h_proto = eh->h_proto;
+	inner_nw_proto = iph->protocol;
+
+	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+			+ STT_HEADER_LEN + sizeof(struct iphdr);
+
+	if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) {
+		int head_delta = SKB_DATA_ALIGN(min_headroom -
+						skb_headroom(skb) +
+						16);
+
+		ret = pskb_expand_head(skb, max_t(int, head_delta, 0),
+				       0, GFP_ATOMIC);
+		if (unlikely(ret))
+			goto err_free_rt;
+	}
+
+	if (!stt_can_offload(skb, inner_h_proto, inner_nw_proto)) {
+		struct sk_buff *nskb;
+
+		nskb = handle_offloads(skb);
+		if (IS_ERR(nskb)) {
+			ret = PTR_ERR(nskb);
+			goto err_free_rt;
+		}
+		skb = nskb;
+	}
+
+	while (skb) {
+		struct sk_buff *next_skb = skb->next;
+
+		skb->next = NULL;
+
+		if (next_skb)
+			dst_clone(&rt->dst);
+
+		/* Push STT and TCP header. */
+		skb = stt_build_header(skb, tun_id, src_port, dst_port, src,
+				       dst, inner_h_proto, inner_nw_proto,
+				       dst_mtu(&rt->dst));
+		if (unlikely(!skb))
+			goto next;
+		/* Push IP header. */
+		ret += iptunnel_xmit(NULL, rt, skb, src, dst, IPPROTO_TCP,
+				     tos, ttl, df, false);
+
+next:
+		skb = next_skb;
+	}
+
+	return ret;
+
+err_free_rt:
+	ip_rt_put(rt);
+	kfree_skb(skb);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stt_xmit_skb);
+
+static struct sk_buff *normalize_frag_list(struct sk_buff *head,
+					   struct sk_buff **skbp)
+{
+	struct sk_buff *skb = *skbp;
+	struct sk_buff *last;
+
+	do {
+		struct sk_buff *frags;
+
+		if (skb_shared(skb)) {
+			struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
+
+			if (unlikely(!nskb))
+				return ERR_PTR(-ENOMEM);
+
+			nskb->next = skb->next;
+			consume_skb(skb);
+			skb = nskb;
+			*skbp = skb;
+		}
+
+		if (head) {
+			head->len -= skb->len;
+			head->data_len -= skb->len;
+			head->truesize -= skb->truesize;
+		}
+
+		frags = skb_shinfo(skb)->frag_list;
+		if (frags) {
+			int err;
+
+			err = skb_unclone(skb, GFP_ATOMIC);
+			if (unlikely(err))
+				return ERR_PTR(err);
+
+			last = normalize_frag_list(skb, &frags);
+			if (IS_ERR(last))
+				return last;
+
+			skb_shinfo(skb)->frag_list = NULL;
+			last->next = skb->next;
+			skb->next = frags;
+		} else {
+			last = skb;
+		}
+
+		skbp = &skb->next;
+	} while ((skb = skb->next));
+
+	return last;
+}
+
+/* Takes a linked list of skbs, which potentially contain frag_list
+ * (whose members in turn potentially contain frag_lists, etc.) and
+ * converts them into a single linear linked list.
+ */
+static int straighten_frag_list(struct sk_buff **skbp)
+{
+	struct sk_buff *err_skb;
+
+	err_skb = normalize_frag_list(NULL, skbp);
+	if (IS_ERR(err_skb))
+		return PTR_ERR(err_skb);
+
+	return 0;
+}
+
+static void copy_skb_metadata(struct sk_buff *to, struct sk_buff *from)
+{
+	to->tstamp = from->tstamp;
+	to->priority = from->priority;
+	to->mark = from->mark;
+	to->vlan_tci = from->vlan_tci;
+	skb_copy_secmark(to, from);
+}
+
+static void update_seg_headers(struct sk_buff *skb, bool head,
+			       unsigned int l4_offset, unsigned int hdr_len,
+			       bool ipv4, u32 tcp_seq)
+{
+	u16 old_len, new_len;
+	__be32 delta;
+	struct tcphdr *tcph;
+	int gso_size;
+
+	if (ipv4) {
+		struct iphdr *iph = (struct iphdr *)(skb->data + ETH_HLEN);
+
+		old_len = ntohs(iph->tot_len);
+		new_len = skb->len - ETH_HLEN;
+		iph->tot_len = htons(new_len);
+
+		ip_send_check(iph);
+	} else {
+		struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + ETH_HLEN);
+
+		old_len = ntohs(ip6h->payload_len);
+		new_len = skb->len - ETH_HLEN - sizeof(struct ipv6hdr);
+		ip6h->payload_len = htons(new_len);
+	}
+
+	tcph = (struct tcphdr *)(skb->data + l4_offset);
+	if (!head) {
+		tcph->seq = htonl(tcp_seq);
+		tcph->cwr = 0;
+	}
+
+	if (skb->next) {
+		tcph->fin = 0;
+		tcph->psh = 0;
+	}
+
+	delta = htonl(~old_len + new_len);
+	tcph->check = ~csum_fold((__force __wsum)((__force u32)tcph->check +
+				 (__force u32)delta));
+
+	gso_size = skb_shinfo(skb)->gso_size;
+	if (gso_size && skb->len - hdr_len <= gso_size)
+		BUG_ON(clear_gso(skb));
+}
+
+static int __linearize(struct sk_buff *head, bool *ipv4)
+{
+	struct stthdr *stth = stt_hdr(head);
+
+	/* If no offloading is in use then we don't have enough information
+	 * to process the headers.
+	 */
+	if (!(stth->flags & STT_CSUM_PARTIAL))
+		goto linearize;
+
+	/* Handling UDP packets requires IP fragmentation, which means that
+	 * the L4 checksum can no longer be calculated by hardware (since the
+	 * fragments are in different packets.  If we have to compute the
+	 * checksum it's faster just to linearize and large UDP packets are
+	 * pretty uncommon anyways, so it's not worth dealing with for now.
+	 */
+	if (!(stth->flags & STT_PROTO_TCP))
+		goto linearize;
+
+	if ((stth->flags & STT_PROTO_IPV4)) {
+		struct iphdr *iph = (struct iphdr *)(head->data + ETH_HLEN);
+
+		/* It's difficult to get the IP IDs exactly right here due to
+		 * varying segment sizes and potentially multiple layers of
+		 * segmentation.  IP ID isn't important when DF is set and DF
+		 * is generally set for TCP packets, so just linearize if it's
+		 * not.
+		 */
+		if (!(iph->frag_off & htons(IP_DF)))
+			goto linearize;
+
+		*ipv4 = true;
+	} else {
+		struct ipv6hdr *ip6h = (struct ipv6hdr *)(head->data + ETH_HLEN);
+
+		/* Jumbograms require more processing to update and we'll
+		 * probably never see them, so just linearize.
+		 */
+		if (ip6h->payload_len == 0)
+			goto linearize;
+
+		*ipv4 = false;
+	}
+	return false;
+
+linearize:
+	return true;
+}
+
+static int update_seg(struct sk_buff *head, struct sk_buff *frag,
+		      bool ipv4, int l4_offset, int hdr_len, u32 seq)
+{
+	u16 csum_start = head->csum_start - skb_headroom(head);
+
+	if (skb_cloned(frag) || skb_headroom(frag) < hdr_len) {
+		int extra_head = hdr_len - skb_headroom(frag);
+
+		extra_head = extra_head > 0 ? extra_head : 0;
+
+		if (unlikely(pskb_expand_head(frag, extra_head, 0,
+					      GFP_ATOMIC)))
+			return -ENOMEM;
+	}
+
+	memcpy(__skb_push(frag, hdr_len), head->data, hdr_len);
+
+	frag->csum_start = skb_headroom(frag) + csum_start;
+	frag->csum_offset = head->csum_offset;
+	frag->ip_summed = head->ip_summed;
+
+	skb_shinfo(frag)->gso_size = skb_shinfo(head)->gso_size;
+	skb_shinfo(frag)->gso_type = skb_shinfo(head)->gso_type;
+	skb_shinfo(frag)->gso_segs = 0;
+
+	copy_skb_metadata(frag, head);
+
+	update_seg_headers(frag, false, l4_offset, hdr_len, ipv4, seq);
+	return 0;
+}
+
+static int __build_segments(struct sk_buff **headp)
+{
+	struct sk_buff *head = *headp;
+	struct sk_buff *nskb = NULL;
+	struct sk_buff *rskb, *skb;
+	struct tcphdr *tcph;
+	int seg_len = 0;
+	int l4_offset;
+	int hdr_len;
+	int tcp_len;
+	bool ipv4;
+	u32 seq;
+
+	/* GRO can produce skbs with only the headers, which we've
+	 * already pulled off.  We can just dump them.
+	 */
+	while (head->len == 0) {
+		nskb = head->next;
+		copy_skb_metadata(nskb, head);
+		consume_skb(head);
+		head = nskb;
+	}
+	*headp = head;
+
+	if (__linearize(head, &ipv4))
+		return skb_list_linearize(head, GFP_ATOMIC);
+
+	l4_offset = stt_hdr(head)->l4_offset;
+	tcph = (struct tcphdr *)(head->data + l4_offset);
+	tcp_len = tcph->doff * 4;
+	hdr_len = l4_offset + tcp_len;
+
+	if (unlikely((tcp_len < sizeof(struct tcphdr)) ||
+		     (head->len < hdr_len)))
+		return -EINVAL;
+
+	if (unlikely(!pskb_may_pull(head, hdr_len)))
+		return -ENOMEM;
+
+	seq = ntohl(tcph->seq);
+	rskb = head;
+	for (skb = head->next; ; skb = nskb) {
+		bool headstolen;
+		int delta;
+
+		if (!skb)
+			goto update_seg; /* Update current segment. */
+
+		if (unlikely(skb_unclone(rskb, GFP_ATOMIC)))
+			return -ENOMEM;
+
+		nskb = skb->next;
+		if (!skb_try_coalesce(rskb, skb, &headstolen, &delta))
+			goto update_seg;
+
+		rskb->next = skb->next;
+		kfree_skb_partial(skb, headstolen);
+		continue;
+update_seg:
+		if (rskb == head) {
+			/* update head segment at the end. */
+			seg_len = head->len - hdr_len;
+		} else {
+			int err;
+
+			seq += seg_len;
+			seg_len = rskb->len;
+
+			err = update_seg(head, rskb, ipv4, l4_offset,
+					 hdr_len, seq);
+			if (err)
+				return err;
+		}
+		if (!skb)
+			break;
+		rskb->truesize = SKB_TRUESIZE(skb_end_offset(rskb)) +
+				 rskb->data_len;
+		rskb = skb;
+	}
+	update_seg_headers(head, true, l4_offset, hdr_len, ipv4, 0);
+	return 0;
+}
+
+static int build_segments(struct sk_buff **headp)
+{
+	int err;
+
+	err = straighten_frag_list(headp);
+	if (unlikely(err))
+		return err;
+
+	if ((*headp)->next) {
+		err = __build_segments(headp);
+		if (unlikely(err))
+			return err;
+	}
+	return 0;
+}
+
+static void free_frag(struct stt_percpu *stt_percpu,
+		      struct pkt_frag *frag)
+{
+	stt_percpu->frag_mem_used -= FRAG_CB(frag->skbs)->first.mem_used;
+	kfree_skb_list(frag->skbs);
+	list_del(&frag->lru_node);
+	frag->skbs = NULL;
+}
+
+static void evict_frags(struct stt_percpu *stt_percpu)
+{
+	while (!list_empty(&stt_percpu->frag_lru) &&
+	       stt_percpu->frag_mem_used > REASM_LO_THRESH) {
+		struct pkt_frag *frag = list_first_entry(&stt_percpu->frag_lru,
+							 struct pkt_frag,
+							 lru_node);
+		free_frag(stt_percpu, frag);
+	}
+}
+
+static bool pkt_key_match(struct net *net,
+			  const struct pkt_frag *a, const struct pkt_key *b)
+{
+	return a->key.saddr == b->saddr && a->key.daddr == b->daddr &&
+	       a->key.pkt_seq == b->pkt_seq && a->key.mark == b->mark &&
+	       net_eq(dev_net(a->skbs->dev), net);
+}
+
+static u32 pkt_key_hash(const struct net *net, const struct pkt_key *key)
+{
+	u32 initval = frag_hash_seed ^ (u32)(unsigned long)net ^ key->mark;
+
+	return jhash_3words((__force u32)key->saddr, (__force u32)key->daddr,
+			    (__force u32)key->pkt_seq, initval);
+}
+
+static struct pkt_frag *lookup_frag(struct net *net,
+				    struct stt_percpu *stt_percpu,
+				    const struct pkt_key *key, u32 hash)
+{
+	struct pkt_frag *frag, *victim_frag = NULL;
+	int i;
+
+	for (i = 0; i < FRAG_HASH_SEGS; i++) {
+		frag = flex_array_get(stt_percpu->frag_hash,
+				      hash & (FRAG_HASH_ENTRIES - 1));
+
+		if (frag->skbs &&
+		    time_before(jiffies, frag->timestamp + FRAG_EXP_TIME) &&
+		    pkt_key_match(net, frag, key))
+			return frag;
+
+		if (!victim_frag ||
+		    (victim_frag->skbs &&
+		     (!frag->skbs ||
+		      time_before(frag->timestamp, victim_frag->timestamp))))
+			victim_frag = frag;
+
+		hash >>= FRAG_HASH_SHIFT;
+	}
+
+	if (victim_frag->skbs)
+		free_frag(stt_percpu, victim_frag);
+
+	return victim_frag;
+}
+
+static struct sk_buff *reassemble(struct sk_buff *skb)
+{
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *tcph = tcp_hdr(skb);
+	u32 seq = ntohl(tcph->seq);
+	int tot_len;
+	struct pkt_key key;
+	struct stt_percpu *stt_percpu;
+	u32 hash;
+	struct pkt_frag *frag;
+	struct sk_buff *last_skb;
+
+	tot_len = seq >> STT_SEQ_LEN_SHIFT;
+	FRAG_CB(skb)->offset = seq & STT_SEQ_OFFSET_MASK;
+
+	if (unlikely(skb->len == 0))
+		goto out_free;
+
+	if (unlikely(FRAG_CB(skb)->offset + skb->len > tot_len))
+		goto out_free;
+
+	if (tot_len == skb->len)
+		goto out;
+
+	key.saddr = iph->saddr;
+	key.daddr = iph->daddr;
+	key.pkt_seq = tcph->ack_seq;
+	key.mark = skb->mark;
+	hash = pkt_key_hash(dev_net(skb->dev), &key);
+
+	stt_percpu = per_cpu_ptr(stt_percpu_data, smp_processor_id());
+
+	spin_lock(&stt_percpu->lock);
+
+	if (unlikely(stt_percpu->frag_mem_used + skb->truesize > REASM_HI_THRESH))
+		evict_frags(stt_percpu);
+
+	frag = lookup_frag(dev_net(skb->dev), stt_percpu, &key, hash);
+	if (!frag->skbs) {
+		frag->skbs = skb;
+		frag->key = key;
+		frag->timestamp = jiffies;
+		FRAG_CB(skb)->first.last_skb = skb;
+		FRAG_CB(skb)->first.mem_used = skb->truesize;
+		FRAG_CB(skb)->first.tot_len = tot_len;
+		FRAG_CB(skb)->first.rcvd_len = skb->len;
+		FRAG_CB(skb)->first.ecn_ce = INET_ECN_is_ce(iph->tos);
+		list_add_tail(&frag->lru_node, &stt_percpu->frag_lru);
+		stt_percpu->frag_mem_used += skb->truesize;
+
+		skb = NULL;
+		goto unlock;
+	}
+
+	/* Optimize for the common case where fragments are received in-order
+	 * and not overlapping.
+	 */
+	last_skb = FRAG_CB(frag->skbs)->first.last_skb;
+	if (likely(FRAG_CB(last_skb)->offset + last_skb->len ==
+		   FRAG_CB(skb)->offset)) {
+		last_skb->next = skb;
+		FRAG_CB(frag->skbs)->first.last_skb = skb;
+	} else {
+		struct sk_buff *prev = NULL, *next;
+
+		for (next = frag->skbs; next; next = next->next) {
+			if (FRAG_CB(next)->offset >= FRAG_CB(skb)->offset)
+				break;
+			prev = next;
+		}
+
+		/* Overlapping fragments aren't allowed.  We shouldn't start
+		 * before the end of the previous fragment.
+		 */
+		if (prev &&
+		    FRAG_CB(prev)->offset + prev->len > FRAG_CB(skb)->offset)
+			goto unlock_free;
+
+		/* We also shouldn't end after the beginning of the next
+		 * fragment.
+		 */
+		if (next &&
+		    FRAG_CB(skb)->offset + skb->len > FRAG_CB(next)->offset)
+			goto unlock_free;
+
+		if (prev) {
+			prev->next = skb;
+		} else {
+			FRAG_CB(skb)->first = FRAG_CB(frag->skbs)->first;
+			frag->skbs = skb;
+		}
+
+		if (next)
+			skb->next = next;
+		else
+			FRAG_CB(frag->skbs)->first.last_skb = skb;
+	}
+
+	FRAG_CB(frag->skbs)->first.ecn_ce |= INET_ECN_is_ce(iph->tos);
+	FRAG_CB(frag->skbs)->first.rcvd_len += skb->len;
+	FRAG_CB(frag->skbs)->first.mem_used += skb->truesize;
+	stt_percpu->frag_mem_used += skb->truesize;
+
+	if (FRAG_CB(frag->skbs)->first.tot_len ==
+	    FRAG_CB(frag->skbs)->first.rcvd_len) {
+		struct sk_buff *frag_head = frag->skbs;
+
+		frag_head->tstamp = skb->tstamp;
+
+		list_del(&frag->lru_node);
+		stt_percpu->frag_mem_used -= FRAG_CB(frag_head)->first.mem_used;
+		frag->skbs = NULL;
+		skb = frag_head;
+	} else {
+		list_move_tail(&frag->lru_node, &stt_percpu->frag_lru);
+		skb = NULL;
+	}
+
+	goto unlock;
+
+unlock_free:
+	kfree_skb(skb);
+	skb = NULL;
+unlock:
+	spin_unlock(&stt_percpu->lock);
+	return skb;
+out_free:
+	kfree_skb(skb);
+	skb = NULL;
+out:
+	return skb;
+}
+
+static bool validate_checksum(struct sk_buff *skb)
+{
+	struct iphdr *iph = ip_hdr(skb);
+
+	if (skb_csum_unnecessary(skb))
+		return true;
+
+	if (skb->ip_summed == CHECKSUM_COMPLETE &&
+	    !tcp_v4_check(skb->len, iph->saddr, iph->daddr, skb->csum))
+		return true;
+
+	skb->csum = csum_tcpudp_nofold(iph->saddr, iph->daddr, skb->len,
+				       IPPROTO_TCP, 0);
+
+	return __tcp_checksum_complete(skb) == 0;
+}
+
+static bool set_offloads(struct sk_buff *skb)
+{
+	struct stthdr *stth = stt_hdr(skb);
+	u8 proto_type;
+	u16 csum_offset;
+	unsigned short gso_type;
+	int l3_header_size;
+	int l4_header_size;
+
+	skb->vlan_tci = ntohs(stth->vlan_tci);
+
+	if (!(stth->flags & STT_CSUM_PARTIAL)) {
+		if (stth->flags & STT_CSUM_VERIFIED)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+		else
+			skb->ip_summed = CHECKSUM_NONE;
+
+		return clear_gso(skb) == 0;
+	}
+
+	proto_type = stth->flags & STT_PROTO_TYPES;
+
+	if (proto_type == (STT_PROTO_IPV4 | STT_PROTO_TCP)) {
+		/* TCP/IPv4 */
+		csum_offset = offsetof(struct tcphdr, check);
+		gso_type = SKB_GSO_TCPV4;
+		l3_header_size = sizeof(struct iphdr);
+		l4_header_size = sizeof(struct tcphdr);
+	} else if (proto_type == STT_PROTO_TCP) {
+		/* TCP/IPv6 */
+		csum_offset = offsetof(struct tcphdr, check);
+		gso_type = SKB_GSO_TCPV6;
+		l3_header_size = sizeof(struct ipv6hdr);
+		l4_header_size = sizeof(struct tcphdr);
+	} else if (proto_type == STT_PROTO_IPV4) {
+		/* UDP/IPv4 */
+		csum_offset = offsetof(struct udphdr, check);
+		gso_type = SKB_GSO_UDP;
+		l3_header_size = sizeof(struct iphdr);
+		l4_header_size = sizeof(struct udphdr);
+	} else {
+		/* UDP/IPv6 */
+		csum_offset = offsetof(struct udphdr, check);
+		gso_type = SKB_GSO_UDP;
+		l3_header_size = sizeof(struct ipv6hdr);
+		l4_header_size = sizeof(struct udphdr);
+	}
+
+	if (unlikely(stth->l4_offset < ETH_HLEN + l3_header_size))
+		return false;
+
+	if (unlikely(!pskb_may_pull(skb, stth->l4_offset + l4_header_size)))
+		return false;
+	stth = stt_hdr(skb);
+
+	skb->csum_start = skb_headroom(skb) + stth->l4_offset;
+	skb->csum_offset = csum_offset;
+	skb->ip_summed = CHECKSUM_PARTIAL;
+
+	if (stth->mss) {
+		if (unlikely(skb_unclone(skb, GFP_ATOMIC)))
+			return false;
+
+		skb_shinfo(skb)->gso_type = gso_type | SKB_GSO_DODGY;
+		skb_shinfo(skb)->gso_size = ntohs(stth->mss);
+		skb_shinfo(skb)->gso_segs = 0;
+	} else {
+		if (unlikely(clear_gso(skb)))
+			return false;
+	}
+
+	return true;
+}
+
+static void stt_rcv(struct stt_sock *stt_sock, struct sk_buff *skb)
+{
+	int err;
+
+	if (unlikely(!validate_checksum(skb)))
+		goto drop;
+
+	skb = reassemble(skb);
+	if (!skb)
+		return;
+
+	if (unlikely(stt_hdr(skb)->version != 0))
+		goto drop;
+
+	err = iptunnel_pull_header(skb,
+				   sizeof(struct stthdr) + STT_ETH_PAD,
+				   htons(ETH_P_TEB));
+	if (unlikely(err))
+		goto drop;
+
+	if (unlikely(!set_offloads(skb)))
+		goto drop;
+
+	if (unlikely(build_segments(&skb)))
+		goto drop;
+
+	stt_sock->rcv(stt_sock, skb);
+	return;
+drop:
+	/* Consume bad packet */
+	kfree_skb_list(skb);
+}
+
+static void tcp_sock_release(struct socket *sock)
+{
+	kernel_sock_shutdown(sock, SHUT_RDWR);
+	sk_release_kernel(sock->sk);
+}
+
+static int tcp_sock_create4(struct net *net, __be16 port,
+			    struct socket **sockp)
+{
+	int err;
+	struct socket *sock = NULL;
+	struct sockaddr_in tcp_addr;
+
+	err = sock_create_kern(AF_INET, SOCK_STREAM, 0, &sock);
+	if (err < 0)
+		goto error;
+
+	sk_change_net(sock->sk, net);
+
+	memset(&tcp_addr, 0, sizeof(tcp_addr));
+	tcp_addr.sin_family = AF_INET;
+	tcp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+	tcp_addr.sin_port = port;
+	err = kernel_bind(sock, (struct sockaddr *)&tcp_addr,
+			  sizeof(tcp_addr));
+	if (err < 0)
+		goto error;
+
+	*sockp = sock;
+	return 0;
+
+error:
+	if (sock)
+		tcp_sock_release(sock);
+	*sockp = NULL;
+	return err;
+}
+
+static void schedule_clean_percpu(void)
+{
+	schedule_delayed_work(&clean_percpu_wq, CLEAN_PERCPU_INTERVAL);
+}
+
+static void clean_percpu(struct work_struct *work)
+{
+	int i;
+
+	for_each_possible_cpu(i) {
+		struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i);
+		int j;
+
+		for (j = 0; j < FRAG_HASH_ENTRIES; j++) {
+			struct pkt_frag *frag;
+
+			frag = flex_array_get(stt_percpu->frag_hash, j);
+			if (!frag->skbs ||
+			    time_before(jiffies, frag->timestamp + FRAG_EXP_TIME))
+				continue;
+
+			spin_lock_bh(&stt_percpu->lock);
+
+			if (frag->skbs &&
+			    time_after(jiffies, frag->timestamp + FRAG_EXP_TIME))
+				free_frag(stt_percpu, frag);
+
+			spin_unlock_bh(&stt_percpu->lock);
+		}
+	}
+	schedule_clean_percpu();
+}
+
+static unsigned int nf_ip_hook(const struct nf_hook_ops *ops,
+			       struct sk_buff *skb,
+			       const struct net_device *in,
+			       const struct net_device *out,
+			       int (*okfn)(struct sk_buff *))
+{
+	struct stt_sock *stt_sock;
+	int ip_hdr_len;
+
+	if (ip_hdr(skb)->protocol != IPPROTO_TCP)
+		return NF_ACCEPT;
+
+	ip_hdr_len = ip_hdrlen(skb);
+	if (unlikely(!pskb_may_pull(skb, ip_hdr_len + sizeof(struct tcphdr))))
+		return NF_ACCEPT;
+
+	skb_set_transport_header(skb, ip_hdr_len);
+
+	stt_sock = stt_find_sock(dev_net(skb->dev), tcp_hdr(skb)->dest);
+	if (unlikely(!stt_sock))
+		return NF_ACCEPT;
+
+	if (unlikely(!pskb_pull(skb, ip_hdr_len + tcp_hdrlen(skb)))) {
+		kfree_skb(skb);
+		return NF_STOLEN;
+	}
+
+	stt_rcv(stt_sock, skb);
+	return NF_STOLEN;
+}
+
+static struct nf_hook_ops nf_hook_ops __read_mostly = {
+	.hook           = nf_ip_hook,
+	.owner          = THIS_MODULE,
+	.pf             = NFPROTO_IPV4,
+	.hooknum        = NF_INET_LOCAL_IN,
+	.priority       = INT_MAX,
+};
+
+static int stt_start(void)
+{
+	int err;
+	int i;
+
+	if (n_tunnels) {
+		n_tunnels++;
+		return 0;
+	}
+
+	get_random_bytes(&frag_hash_seed, sizeof(u32));
+
+	stt_percpu_data = alloc_percpu(struct stt_percpu);
+	if (!stt_percpu_data) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	for_each_possible_cpu(i) {
+		struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i);
+		struct flex_array *frag_hash;
+
+		spin_lock_init(&stt_percpu->lock);
+		INIT_LIST_HEAD(&stt_percpu->frag_lru);
+		get_random_bytes(&per_cpu(pkt_seq_counter, i), sizeof(u32));
+
+		frag_hash = flex_array_alloc(sizeof(struct pkt_frag),
+					     FRAG_HASH_ENTRIES,
+					     GFP_KERNEL | __GFP_ZERO);
+		if (!frag_hash) {
+			err = -ENOMEM;
+			goto free_percpu;
+		}
+		stt_percpu->frag_hash = frag_hash;
+
+		err = flex_array_prealloc(stt_percpu->frag_hash, 0,
+					  FRAG_HASH_ENTRIES,
+					  GFP_KERNEL | __GFP_ZERO);
+		if (err)
+			goto free_percpu;
+	}
+	err = nf_register_hook(&nf_hook_ops);
+	if (err)
+		goto free_percpu;
+
+	schedule_clean_percpu();
+	n_tunnels++;
+	return 0;
+
+free_percpu:
+	for_each_possible_cpu(i) {
+		struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i);
+
+		if (stt_percpu->frag_hash)
+			flex_array_free(stt_percpu->frag_hash);
+	}
+
+	free_percpu(stt_percpu_data);
+
+error:
+	return err;
+}
+
+static void stt_cleanup(void)
+{
+	int i;
+
+	n_tunnels--;
+	if (n_tunnels)
+		return;
+
+	cancel_delayed_work_sync(&clean_percpu_wq);
+	nf_unregister_hook(&nf_hook_ops);
+
+	for_each_possible_cpu(i) {
+		struct stt_percpu *stt_percpu = per_cpu_ptr(stt_percpu_data, i);
+		int j;
+
+		for (j = 0; j < FRAG_HASH_ENTRIES; j++) {
+			struct pkt_frag *frag;
+
+			frag = flex_array_get(stt_percpu->frag_hash, j);
+			kfree_skb_list(frag->skbs);
+		}
+
+		flex_array_free(stt_percpu->frag_hash);
+	}
+
+	free_percpu(stt_percpu_data);
+}
+
+static struct stt_sock *stt_socket_create(struct net *net, __be16 port,
+					  stt_rcv_t *rcv, void *data)
+{
+	struct stt_net *sn = net_generic(net, stt_net_id);
+	struct stt_sock *stt_sock;
+	struct socket *sock;
+	int err;
+
+	stt_sock = kzalloc(sizeof(*stt_sock), GFP_KERNEL);
+	if (!stt_sock)
+		return ERR_PTR(-ENOMEM);
+
+	err = tcp_sock_create4(net, port, &sock);
+	if (err) {
+		kfree(stt_sock);
+		return ERR_PTR(err);
+	}
+
+	stt_sock->sock = sock;
+	stt_sock->rcv = rcv;
+	stt_sock->rcv_data = data;
+
+	list_add_rcu(&stt_sock->list, &sn->sock_list);
+
+	return stt_sock;
+}
+
+static void __stt_sock_release(struct stt_sock *stt_sock)
+{
+	list_del_rcu(&stt_sock->list);
+	tcp_sock_release(stt_sock->sock);
+	kfree_rcu(stt_sock, rcu);
+}
+
+struct stt_sock *stt_sock_add(struct net *net, __be16 port,
+			      stt_rcv_t *rcv, void *data)
+{
+	struct stt_sock *stt_sock;
+
+	mutex_lock(&stt_mutex);
+	rcu_read_lock();
+	stt_sock = stt_find_sock(net, port);
+	rcu_read_unlock();
+	if (stt_sock) {
+		stt_sock = ERR_PTR(-EBUSY);
+	} else {
+		stt_sock = stt_socket_create(net, port, rcv, data);
+		if (!IS_ERR(stt_sock)) {
+			int err;
+
+			err = stt_start();
+			if (err) {
+				__stt_sock_release(stt_sock);
+				stt_sock = ERR_PTR(err);
+			}
+		}
+	}
+	mutex_unlock(&stt_mutex);
+
+	return stt_sock;
+}
+EXPORT_SYMBOL_GPL(stt_sock_add);
+
+void stt_sock_release(struct stt_sock *stt_sock)
+{
+	mutex_lock(&stt_mutex);
+	__stt_sock_release(stt_sock);
+	stt_cleanup();
+	mutex_unlock(&stt_mutex);
+}
+EXPORT_SYMBOL_GPL(stt_sock_release);
+
+static __net_init int stt_init_net(struct net *net)
+{
+	struct stt_net *sn = net_generic(net, stt_net_id);
+
+	INIT_LIST_HEAD(&sn->sock_list);
+	return 0;
+}
+
+static struct pernet_operations stt_net_ops = {
+	.init = stt_init_net,
+	.id   = &stt_net_id,
+	.size = sizeof(struct stt_net),
+};
+
+static int __init stt_init_module(void)
+{
+	return register_pernet_subsys(&stt_net_ops);
+}
+module_init(stt_init_module);
+
+static void __exit stt_cleanup_module(void)
+{
+	unregister_pernet_subsys(&stt_net_ops);
+}
+module_exit(stt_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for STT encapsulated traffic");
+MODULE_ALIAS_RTNL_LINK("stt");
-- 
1.9.1

^ permalink raw reply related

* [PATCH net-next 3/3] openvswitch: Add support for STT tunneling.
From: Pravin B Shelar @ 2015-01-20 20:25 UTC (permalink / raw)
  To: davem; +Cc: netdev, Pravin B Shelar, Jesse Gross

Use STT driver to create STT tunnel port.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
---
 include/uapi/linux/openvswitch.h |   1 +
 net/openvswitch/Kconfig          |  10 ++
 net/openvswitch/Makefile         |   1 +
 net/openvswitch/vport-stt.c      | 214 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 226 insertions(+)
 create mode 100644 net/openvswitch/vport-stt.c

diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index cd8d933..a7396a7 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -204,6 +204,7 @@ enum ovs_vport_type {
 	OVS_VPORT_TYPE_GRE,      /* GRE tunnel. */
 	OVS_VPORT_TYPE_VXLAN,	 /* VXLAN tunnel. */
 	OVS_VPORT_TYPE_GENEVE,	 /* Geneve tunnel. */
+	OVS_VPORT_TYPE_STT,	 /* STT tunnel. */
 	__OVS_VPORT_TYPE_MAX
 };
 
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index b7d818c..65b5fb7 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -64,3 +64,13 @@ config OPENVSWITCH_GENEVE
 	  If you say Y here, then the Open vSwitch will be able create geneve vport.
 
 	  Say N to exclude this support and reduce the binary size.
+
+config OPENVSWITCH_STT
+	tristate "Open vSwitch STT tunneling support"
+	depends on OPENVSWITCH
+	depends on STT
+	default OPENVSWITCH
+	---help---
+	  If you say Y here, then the Open vSwitch will be able create stt vport.
+
+	  Say N to exclude this support and reduce the binary size.
diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile
index 91b9478..352bd05 100644
--- a/net/openvswitch/Makefile
+++ b/net/openvswitch/Makefile
@@ -18,3 +18,4 @@ openvswitch-y := \
 obj-$(CONFIG_OPENVSWITCH_GENEVE)+= vport-geneve.o
 obj-$(CONFIG_OPENVSWITCH_VXLAN)	+= vport-vxlan.o
 obj-$(CONFIG_OPENVSWITCH_GRE)	+= vport-gre.o
+obj-$(CONFIG_OPENVSWITCH_STT)	+= vport-stt.o
diff --git a/net/openvswitch/vport-stt.c b/net/openvswitch/vport-stt.c
new file mode 100644
index 0000000..f614b3d
--- /dev/null
+++ b/net/openvswitch/vport-stt.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/if_vlan.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/rculist.h>
+#include <linux/udp.h>
+
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/stt.h>
+#include <net/udp.h>
+
+#include "datapath.h"
+#include "vport.h"
+
+static struct vport_ops ovs_stt_vport_ops;
+
+/**
+ * struct stt_port - Keeps track of open UDP ports
+ * @stt_sock: The socket created for this port number.
+ * @name: vport name.
+ */
+struct stt_port {
+	struct stt_sock *stt_sock;
+	char name[IFNAMSIZ];
+};
+
+static inline struct stt_port *stt_vport(const struct vport *vport)
+{
+	return vport_priv(vport);
+}
+
+static void stt_rcv(struct stt_sock *stt_sock, struct sk_buff *skb)
+{
+	struct vport *vport = stt_sock->rcv_data;
+	struct stthdr *stth = stt_hdr(skb);
+	struct ovs_tunnel_info tun_info;
+	struct sk_buff *next;
+
+	ovs_flow_tun_info_init(&tun_info, ip_hdr(skb),
+			       tcp_hdr(skb)->source, tcp_hdr(skb)->dest,
+			       get_unaligned(&stth->key),
+			       TUNNEL_KEY | TUNNEL_CSUM,
+			       NULL, 0);
+	do {
+		next = skb->next;
+		skb->next = NULL;
+		ovs_vport_receive(vport, skb, &tun_info);
+	} while ((skb = next));
+}
+
+static int stt_tnl_get_options(const struct vport *vport,
+			       struct sk_buff *skb)
+{
+	struct stt_port *stt_port = stt_vport(vport);
+	struct inet_sock *sk = inet_sk(stt_port->stt_sock->sock->sk);
+
+	if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(sk->inet_sport)))
+		return -EMSGSIZE;
+	return 0;
+}
+
+static void stt_tnl_destroy(struct vport *vport)
+{
+	struct stt_port *stt_port = stt_vport(vport);
+
+	stt_sock_release(stt_port->stt_sock);
+	ovs_vport_deferred_free(vport);
+}
+
+static struct vport *stt_tnl_create(const struct vport_parms *parms)
+{
+	struct net *net = ovs_dp_get_net(parms->dp);
+	struct nlattr *options = parms->options;
+	struct stt_port *stt_port;
+	struct stt_sock *stt_sock;
+	struct vport *vport;
+	struct nlattr *a;
+	int err;
+	u16 dst_port;
+
+	if (!options) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
+	if (a && nla_len(a) == sizeof(u16)) {
+		dst_port = nla_get_u16(a);
+	} else {
+		/* Require destination port from userspace. */
+		err = -EINVAL;
+		goto error;
+	}
+
+	vport = ovs_vport_alloc(sizeof(struct stt_port),
+				&ovs_stt_vport_ops, parms);
+	if (IS_ERR(vport))
+		return vport;
+
+	stt_port = stt_vport(vport);
+	strncpy(stt_port->name, parms->name, IFNAMSIZ);
+
+	stt_sock = stt_sock_add(net, htons(dst_port), stt_rcv, vport);
+	if (IS_ERR(stt_sock)) {
+		ovs_vport_free(vport);
+		return (void *)stt_sock;
+	}
+	stt_port->stt_sock = stt_sock;
+
+	return vport;
+error:
+	return ERR_PTR(err);
+}
+
+static int stt_tnl_send(struct vport *vport, struct sk_buff *skb)
+{
+	struct net *net = ovs_dp_get_net(vport->dp);
+	struct stt_port *stt_port = stt_vport(vport);
+	__be16 dport = inet_sk(stt_port->stt_sock->sock->sk)->inet_sport;
+	const struct ovs_key_ipv4_tunnel *tun_key;
+	const struct ovs_tunnel_info *tun_info;
+	struct rtable *rt;
+	struct flowi4 fl;
+	__be16 sport;
+	__be16 df;
+	int err;
+
+	tun_info = OVS_CB(skb)->egress_tun_info;
+	if (unlikely(!tun_info)) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	tun_key = &tun_info->tunnel;
+	rt = ovs_tunnel_route_lookup(net, tun_key, skb->mark, &fl, IPPROTO_TCP);
+	if (IS_ERR(rt)) {
+		err = PTR_ERR(rt);
+		goto error;
+	}
+
+	df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
+	sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
+	skb->ignore_df = 1;
+
+	return stt_xmit_skb(skb, rt, fl.saddr, tun_key->ipv4_dst,
+			   tun_key->ipv4_tos, tun_key->ipv4_ttl,
+			   df, sport, dport, tun_key->tun_id);
+error:
+	kfree_skb(skb);
+	return err;
+}
+
+static const char *stt_tnl_get_name(const struct vport *vport)
+{
+	return stt_vport(vport)->name;
+}
+
+static int stt_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+				   struct ovs_tunnel_info *egress_tun_info)
+{
+	struct stt_port *stt_port = stt_vport(vport);
+	struct net *net = ovs_dp_get_net(vport->dp);
+	__be16 dport = inet_sk(stt_port->stt_sock->sock->sk)->inet_sport;
+	__be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
+
+	/* Get tp_src and tp_dst, refert to stt_build_header().
+	 */
+	return ovs_tunnel_get_egress_info(egress_tun_info,
+					  ovs_dp_get_net(vport->dp),
+					  OVS_CB(skb)->egress_tun_info,
+					  IPPROTO_UDP, skb->mark, sport, dport);
+}
+
+static struct vport_ops ovs_stt_vport_ops = {
+	.type		= OVS_VPORT_TYPE_STT,
+	.create		= stt_tnl_create,
+	.destroy	= stt_tnl_destroy,
+	.get_name	= stt_tnl_get_name,
+	.get_options	= stt_tnl_get_options,
+	.send		= stt_tnl_send,
+	.owner          = THIS_MODULE,
+	.get_egress_tun_info	= stt_get_egress_tun_info,
+};
+
+static int __init ovs_stt_tnl_init(void)
+{
+	return ovs_vport_ops_register(&ovs_stt_vport_ops);
+}
+
+static void __exit ovs_stt_tnl_exit(void)
+{
+	ovs_vport_ops_unregister(&ovs_stt_vport_ops);
+}
+
+module_init(ovs_stt_tnl_init);
+module_exit(ovs_stt_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: STT swiching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-6");
-- 
1.9.1

^ permalink raw reply related

* Re: [net-next PATCH v3 04/12] net: flow_table: create a set of common headers and actions
From: John W. Linville @ 2015-01-20 20:59 UTC (permalink / raw)
  To: John Fastabend
  Cc: tgraf, simon.horman, sfeldma, netdev, jhs, davem, gerlitz.or,
	andy, ast
In-Reply-To: <20150120202752.1741.13810.stgit@nitbit.x32>

On Tue, Jan 20, 2015 at 12:27:53PM -0800, John Fastabend wrote:
> This adds common headers and actions that drivers can use.
> 
> I have not yet moved the header graphs into the common header
> because I'm not entirely convinced its re-usable. The devices
> I have been looking at have different enough header graphs that
> they wouldn't be re-usable. However possibly many 40Gbp NICs
> for example could share a common header graph. When we get
> multiple implementations we can move this into the common file
> if it makes sense.
> 
> And table structures seem to be unique enough that there is
> little value in putting each devices table layout into the
> common file so its left for device specific implementation.
> 
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
>  include/linux/if_flow_common.h |  257 ++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 257 insertions(+)
>  create mode 100644 include/linux/if_flow_common.h
> 
> diff --git a/include/linux/if_flow_common.h b/include/linux/if_flow_common.h
> new file mode 100644
> index 0000000..ef2d66f
> --- /dev/null
> +++ b/include/linux/if_flow_common.h

<snip>

> +struct net_flow_action net_flow_pop_vlan = {
> +	.name = "pop_vlan",
> +	.uid = ACTION_POP_VLAN,
> +	.args = net_flow_null_args,
> +};

Random thought, should there be a "push_vlan" (for double VLAN tagging)?


-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply

* RE: [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
From: Harout Hedeshian @ 2015-01-20 21:16 UTC (permalink / raw)
  To: 'Pravin B Shelar', davem; +Cc: netdev
In-Reply-To: <1421785543-19823-1-git-send-email-pshelar@nicira.com>



> -----Original Message-----
> From: netdev-owner@vger.kernel.org [mailto:netdev-owner@vger.kernel.org]
> On Behalf Of Pravin B Shelar
> Sent: Tuesday, January 20, 2015 1:26 PM
> To: davem@davemloft.net
> Cc: netdev@vger.kernel.org; Pravin B Shelar
> Subject: [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
> 
> similar to skb_linearize(), this API takes skb list as arg and linearize
> it into one big skb. STT driver patch will use this.
> 
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
> ---
>  include/linux/skbuff.h |  2 ++
>  net/core/skbuff.c      | 35 +++++++++++++++++++++++++++++++++++
>  2 files changed, 37 insertions(+)
> 
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index
> 85ab7d7..c9194c1 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -2532,6 +2532,8 @@ static inline int skb_linearize(struct sk_buff
> *skb)
>  	return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0;  }
> 
> +int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask);
> +
>  /**
>   * skb_has_shared_frag - can any frag be overwritten
>   * @skb: buffer to test
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c index
> 56db472..9ef2881 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -2329,6 +2329,41 @@ void skb_copy_and_csum_dev(const struct sk_buff
> *skb, u8 *to)  }  EXPORT_SYMBOL(skb_copy_and_csum_dev);
> 
> +int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask) {
> +	struct sk_buff *skb;
> +	int tlen = 0;
> +	int err;
> +
> +	err = skb_linearize(head);
> +	if (err)
> +		return err;
> +
> +	skb = head->next;
> +	while (skb) {
> +		tlen += skb->len;
> +		skb = skb->next;
> +	}
> +	err = pskb_expand_head(head, 0, tlen, gfp_mask);
> +	if (err)
> +		return err;
> +
> +	skb = head->next;
> +	while (skb) {
> +		err = skb_copy_bits(skb, 0, skb_tail_pointer(head),
skb->len);
> +		if (err)
> +			return err;
> +		head->tail += skb->len;
> +		skb = skb->next;
> +	}
> +	kfree_skb_list(head->next);
> +	head->next = NULL;
> +	head->len += tlen;
> +	head->truesize = SKB_TRUESIZE(skb_end_offset(head));
> +	return 0;
> +}
> +EXPORT_SYMBOL(skb_list_linearize);
> +
>  /**
>   *	skb_dequeue - remove from the head of the queue
>   *	@list: list to dequeue from
> --
> 1.9.1
> 

This looks useful for us as well.

Acked-by: Harout Hedeshian <harouth@codeaurora.org>

Thanks,
Harout

--
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux
Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH] net: ipv4: Fix incorrect free in ICMP receive
From: Eric Dumazet @ 2015-01-20 21:35 UTC (permalink / raw)
  To: Cong Wang; +Cc: subashab, David Miller, netdev
In-Reply-To: <CAHA+R7N-j5-iizM2pfjtAExn4Y3rq=UWGn1xyw8Z5OFO9u95ag@mail.gmail.com>

On Tue, 2015-01-20 at 11:42 -0800, Cong Wang wrote:

> 
> Or let the socket layer drop the packet instead of unconditionally
> dropping all icmp packets after success?

Sure, but that's probably at least 50 lines patch.

net-next candidate most likely.

^ permalink raw reply

* Re: [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
From: Eric Dumazet @ 2015-01-20 21:43 UTC (permalink / raw)
  To: Pravin B Shelar; +Cc: davem, netdev
In-Reply-To: <1421785543-19823-1-git-send-email-pshelar@nicira.com>

On Tue, 2015-01-20 at 12:25 -0800, Pravin B Shelar wrote:
> similar to skb_linearize(), this API takes skb list as arg and
> linearize it into one big skb. STT driver patch will use this.
> 
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
> ---
>  include/linux/skbuff.h |  2 ++
>  net/core/skbuff.c      | 35 +++++++++++++++++++++++++++++++++++
>  2 files changed, 37 insertions(+)
> 
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 85ab7d7..c9194c1 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -2532,6 +2532,8 @@ static inline int skb_linearize(struct sk_buff *skb)
>  	return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0;
>  }
>  
> +int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask);
> +
>  /**
>   * skb_has_shared_frag - can any frag be overwritten
>   * @skb: buffer to test
> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index 56db472..9ef2881 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -2329,6 +2329,41 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
>  }
>  EXPORT_SYMBOL(skb_copy_and_csum_dev);
>  
> +int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask)
> +{
> +	struct sk_buff *skb;
> +	int tlen = 0;
> +	int err;
> +
> +	err = skb_linearize(head);
> +	if (err)
> +		return err;
> +
> +	skb = head->next;
> +	while (skb) {
> +		tlen += skb->len;
> +		skb = skb->next;
> +	}
> +	err = pskb_expand_head(head, 0, tlen, gfp_mask);
> +	if (err)
> +		return err;
> +
> +	skb = head->next;
> +	while (skb) {
> +		err = skb_copy_bits(skb, 0, skb_tail_pointer(head), skb->len);
> +		if (err)
> +			return err;
> +		head->tail += skb->len;
> +		skb = skb->next;
> +	}
> +	kfree_skb_list(head->next);
> +	head->next = NULL;
> +	head->len += tlen;
> +	head->truesize = SKB_TRUESIZE(skb_end_offset(head));

This is buggy : skb can have a destructor, like sock_wfree()

^ permalink raw reply

* [PATCH v5 1/5] can: kvaser_usb: Update net interface state before exiting on OOM
From: Ahmed S. Darwish @ 2015-01-20 21:44 UTC (permalink / raw)
  To: Olivier Sobrie, Oliver Hartkopp, Wolfgang Grandegger,
	Marc Kleine-Budde
  Cc: Andri Yngvason, Linux-CAN, netdev, LKML
In-Reply-To: <20141223154654.GB6460@vivalin-002>

From: Ahmed S. Darwish <ahmed.darwish@valeo.com>

Update all of the can interface's state and error counters before
trying any skb allocation that can actually fail with -ENOMEM.

Suggested-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
---
 drivers/net/can/usb/kvaser_usb.c | 182 +++++++++++++++++++++++----------------
 1 file changed, 106 insertions(+), 76 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index c32cd61..971c5f9 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -273,6 +273,10 @@ struct kvaser_msg {
 	} u;
 } __packed;
 
+struct kvaser_usb_error_summary {
+	u8 channel, status, txerr, rxerr, error_factor;
+};
+
 struct kvaser_usb_tx_urb_context {
 	struct kvaser_usb_net_priv *priv;
 	u32 echo_index;
@@ -615,6 +619,55 @@ static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
 		priv->tx_contexts[i].echo_index = MAX_TX_URBS;
 }
 
+static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
+						 const struct kvaser_usb_error_summary *es)
+{
+	struct net_device_stats *stats;
+	enum can_state new_state;
+
+	stats = &priv->netdev->stats;
+	new_state = priv->can.state;
+
+	netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status);
+
+	if (es->status & M16C_STATE_BUS_OFF) {
+		priv->can.can_stats.bus_off++;
+		new_state = CAN_STATE_BUS_OFF;
+	} else if (es->status & M16C_STATE_BUS_PASSIVE) {
+		if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
+			priv->can.can_stats.error_passive++;
+		new_state = CAN_STATE_ERROR_PASSIVE;
+	}
+
+	if (es->status == M16C_STATE_BUS_ERROR) {
+		if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+		    ((es->txerr >= 96) || (es->rxerr >= 96))) {
+			priv->can.can_stats.error_warning++;
+			new_state = CAN_STATE_ERROR_WARNING;
+		} else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+			new_state = CAN_STATE_ERROR_ACTIVE;
+		}
+	}
+
+	if (!es->status)
+		new_state = CAN_STATE_ERROR_ACTIVE;
+
+	if (priv->can.restart_ms &&
+	    (priv->can.state >= CAN_STATE_BUS_OFF) &&
+	    (new_state < CAN_STATE_BUS_OFF)) {
+		priv->can.can_stats.restarts++;
+	}
+
+	if (es->error_factor) {
+		priv->can.can_stats.bus_error++;
+		stats->rx_errors++;
+	}
+
+	priv->bec.txerr = es->txerr;
+	priv->bec.rxerr = es->rxerr;
+	priv->can.state = new_state;
+}
+
 static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 				const struct kvaser_msg *msg)
 {
@@ -622,30 +675,30 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 	struct sk_buff *skb;
 	struct net_device_stats *stats;
 	struct kvaser_usb_net_priv *priv;
-	unsigned int new_state;
-	u8 channel, status, txerr, rxerr, error_factor;
+	struct kvaser_usb_error_summary es = { };
+	enum can_state old_state;
 
 	switch (msg->id) {
 	case CMD_CAN_ERROR_EVENT:
-		channel = msg->u.error_event.channel;
-		status =  msg->u.error_event.status;
-		txerr = msg->u.error_event.tx_errors_count;
-		rxerr = msg->u.error_event.rx_errors_count;
-		error_factor = msg->u.error_event.error_factor;
+		es.channel = msg->u.error_event.channel;
+		es.status =  msg->u.error_event.status;
+		es.txerr = msg->u.error_event.tx_errors_count;
+		es.rxerr = msg->u.error_event.rx_errors_count;
+		es.error_factor = msg->u.error_event.error_factor;
 		break;
 	case CMD_LOG_MESSAGE:
-		channel = msg->u.log_message.channel;
-		status = msg->u.log_message.data[0];
-		txerr = msg->u.log_message.data[2];
-		rxerr = msg->u.log_message.data[3];
-		error_factor = msg->u.log_message.data[1];
+		es.channel = msg->u.log_message.channel;
+		es.status = msg->u.log_message.data[0];
+		es.txerr = msg->u.log_message.data[2];
+		es.rxerr = msg->u.log_message.data[3];
+		es.error_factor = msg->u.log_message.data[1];
 		break;
 	case CMD_CHIP_STATE_EVENT:
-		channel = msg->u.chip_state_event.channel;
-		status =  msg->u.chip_state_event.status;
-		txerr = msg->u.chip_state_event.tx_errors_count;
-		rxerr = msg->u.chip_state_event.rx_errors_count;
-		error_factor = 0;
+		es.channel = msg->u.chip_state_event.channel;
+		es.status =  msg->u.chip_state_event.status;
+		es.txerr = msg->u.chip_state_event.tx_errors_count;
+		es.rxerr = msg->u.chip_state_event.rx_errors_count;
+		es.error_factor = 0;
 		break;
 	default:
 		dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
@@ -653,122 +706,99 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 		return;
 	}
 
-	if (channel >= dev->nchannels) {
+	if (es.channel >= dev->nchannels) {
 		dev_err(dev->udev->dev.parent,
-			"Invalid channel number (%d)\n", channel);
+			"Invalid channel number (%d)\n", es.channel);
 		return;
 	}
 
-	priv = dev->nets[channel];
+	priv = dev->nets[es.channel];
 	stats = &priv->netdev->stats;
 
-	if (status & M16C_STATE_BUS_RESET) {
+	if (es.status & M16C_STATE_BUS_RESET) {
 		kvaser_usb_unlink_tx_urbs(priv);
 		return;
 	}
 
+	/* Update all of the can interface's state and error counters before
+	 * trying any skb allocation that can actually fail with -ENOMEM.
+	 */
+	old_state = priv->can.state;
+	kvaser_usb_rx_error_update_can_state(priv, &es);
+
 	skb = alloc_can_err_skb(priv->netdev, &cf);
 	if (!skb) {
 		stats->rx_dropped++;
 		return;
 	}
 
-	new_state = priv->can.state;
-
-	netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
-
-	if (status & M16C_STATE_BUS_OFF) {
+	if (es.status & M16C_STATE_BUS_OFF) {
 		cf->can_id |= CAN_ERR_BUSOFF;
 
-		priv->can.can_stats.bus_off++;
 		if (!priv->can.restart_ms)
 			kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
-
 		netif_carrier_off(priv->netdev);
-
-		new_state = CAN_STATE_BUS_OFF;
-	} else if (status & M16C_STATE_BUS_PASSIVE) {
-		if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+	} else if (es.status & M16C_STATE_BUS_PASSIVE) {
+		if (old_state != CAN_STATE_ERROR_PASSIVE) {
 			cf->can_id |= CAN_ERR_CRTL;
 
-			if (txerr || rxerr)
-				cf->data[1] = (txerr > rxerr)
+			if (es.txerr || es.rxerr)
+				cf->data[1] = (es.txerr > es.rxerr)
 						? CAN_ERR_CRTL_TX_PASSIVE
 						: CAN_ERR_CRTL_RX_PASSIVE;
 			else
 				cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
 					      CAN_ERR_CRTL_RX_PASSIVE;
-
-			priv->can.can_stats.error_passive++;
 		}
-
-		new_state = CAN_STATE_ERROR_PASSIVE;
 	}
 
-	if (status == M16C_STATE_BUS_ERROR) {
-		if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
-		    ((txerr >= 96) || (rxerr >= 96))) {
+	if (es.status == M16C_STATE_BUS_ERROR) {
+		if ((old_state < CAN_STATE_ERROR_WARNING) &&
+		    ((es.txerr >= 96) || (es.rxerr >= 96))) {
 			cf->can_id |= CAN_ERR_CRTL;
-			cf->data[1] = (txerr > rxerr)
+			cf->data[1] = (es.txerr > es.rxerr)
 					? CAN_ERR_CRTL_TX_WARNING
 					: CAN_ERR_CRTL_RX_WARNING;
-
-			priv->can.can_stats.error_warning++;
-			new_state = CAN_STATE_ERROR_WARNING;
-		} else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+		} else if (old_state > CAN_STATE_ERROR_ACTIVE) {
 			cf->can_id |= CAN_ERR_PROT;
 			cf->data[2] = CAN_ERR_PROT_ACTIVE;
-
-			new_state = CAN_STATE_ERROR_ACTIVE;
 		}
 	}
 
-	if (!status) {
+	if (!es.status) {
 		cf->can_id |= CAN_ERR_PROT;
 		cf->data[2] = CAN_ERR_PROT_ACTIVE;
-
-		new_state = CAN_STATE_ERROR_ACTIVE;
 	}
 
 	if (priv->can.restart_ms &&
-	    (priv->can.state >= CAN_STATE_BUS_OFF) &&
-	    (new_state < CAN_STATE_BUS_OFF)) {
+	    (old_state >= CAN_STATE_BUS_OFF) &&
+	    (priv->can.state < CAN_STATE_BUS_OFF)) {
 		cf->can_id |= CAN_ERR_RESTARTED;
 		netif_carrier_on(priv->netdev);
-
-		priv->can.can_stats.restarts++;
 	}
 
-	if (error_factor) {
-		priv->can.can_stats.bus_error++;
-		stats->rx_errors++;
-
+	if (es.error_factor) {
 		cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
 
-		if (error_factor & M16C_EF_ACKE)
+		if (es.error_factor & M16C_EF_ACKE)
 			cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
-		if (error_factor & M16C_EF_CRCE)
+		if (es.error_factor & M16C_EF_CRCE)
 			cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
 					CAN_ERR_PROT_LOC_CRC_DEL);
-		if (error_factor & M16C_EF_FORME)
+		if (es.error_factor & M16C_EF_FORME)
 			cf->data[2] |= CAN_ERR_PROT_FORM;
-		if (error_factor & M16C_EF_STFE)
+		if (es.error_factor & M16C_EF_STFE)
 			cf->data[2] |= CAN_ERR_PROT_STUFF;
-		if (error_factor & M16C_EF_BITE0)
+		if (es.error_factor & M16C_EF_BITE0)
 			cf->data[2] |= CAN_ERR_PROT_BIT0;
-		if (error_factor & M16C_EF_BITE1)
+		if (es.error_factor & M16C_EF_BITE1)
 			cf->data[2] |= CAN_ERR_PROT_BIT1;
-		if (error_factor & M16C_EF_TRE)
+		if (es.error_factor & M16C_EF_TRE)
 			cf->data[2] |= CAN_ERR_PROT_TX;
 	}
 
-	cf->data[6] = txerr;
-	cf->data[7] = rxerr;
-
-	priv->bec.txerr = txerr;
-	priv->bec.rxerr = rxerr;
-
-	priv->can.state = new_state;
+	cf->data[6] = es.txerr;
+	cf->data[7] = es.rxerr;
 
 	stats->rx_packets++;
 	stats->rx_bytes += cf->can_dlc;
@@ -792,6 +822,9 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
 	}
 
 	if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+
 		skb = alloc_can_err_skb(priv->netdev, &cf);
 		if (!skb) {
 			stats->rx_dropped++;
@@ -801,9 +834,6 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
 		cf->can_id |= CAN_ERR_CRTL;
 		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
 
-		stats->rx_over_errors++;
-		stats->rx_errors++;
-
 		stats->rx_packets++;
 		stats->rx_bytes += cf->can_dlc;
 		netif_rx(skb);
-- 
1.9.1


^ permalink raw reply related

* [PATCH v5 2/5] can: kvaser_usb: Consolidate and unify state change handling
From: Ahmed S. Darwish @ 2015-01-20 21:45 UTC (permalink / raw)
  To: Olivier Sobrie, Oliver Hartkopp, Wolfgang Grandegger,
	Marc Kleine-Budde
  Cc: Andri Yngvason, Linux-CAN, netdev, LKML
In-Reply-To: <20150120214409.GA16828@linux>

From: Ahmed S. Darwish <ahmed.darwish@valeo.com>

Replace most of the can interface's state and error counters
handling with the new can-dev can_change_state() mechanism.

Suggested-by: Andri Yngvason <andri.yngvason@marel.com>
Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
---
 drivers/net/can/usb/kvaser_usb.c | 114 +++++++++++++++++++--------------------
 1 file changed, 55 insertions(+), 59 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 971c5f9..0386d3f 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -620,40 +620,43 @@ static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
 }
 
 static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
-						 const struct kvaser_usb_error_summary *es)
+						 const struct kvaser_usb_error_summary *es,
+						 struct can_frame *cf)
 {
 	struct net_device_stats *stats;
-	enum can_state new_state;
-
-	stats = &priv->netdev->stats;
-	new_state = priv->can.state;
+	enum can_state cur_state, new_state, tx_state, rx_state;
 
 	netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status);
 
-	if (es->status & M16C_STATE_BUS_OFF) {
-		priv->can.can_stats.bus_off++;
+	stats = &priv->netdev->stats;
+	new_state = cur_state = priv->can.state;
+
+	if (es->status & M16C_STATE_BUS_OFF)
 		new_state = CAN_STATE_BUS_OFF;
-	} else if (es->status & M16C_STATE_BUS_PASSIVE) {
-		if (priv->can.state != CAN_STATE_ERROR_PASSIVE)
-			priv->can.can_stats.error_passive++;
+	else if (es->status & M16C_STATE_BUS_PASSIVE)
 		new_state = CAN_STATE_ERROR_PASSIVE;
-	}
 
 	if (es->status == M16C_STATE_BUS_ERROR) {
-		if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
-		    ((es->txerr >= 96) || (es->rxerr >= 96))) {
-			priv->can.can_stats.error_warning++;
+		if ((cur_state < CAN_STATE_ERROR_WARNING) &&
+		    ((es->txerr >= 96) || (es->rxerr >= 96)))
 			new_state = CAN_STATE_ERROR_WARNING;
-		} else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+		else if (cur_state > CAN_STATE_ERROR_ACTIVE)
 			new_state = CAN_STATE_ERROR_ACTIVE;
-		}
 	}
 
 	if (!es->status)
 		new_state = CAN_STATE_ERROR_ACTIVE;
 
+	if (new_state != cur_state) {
+		tx_state = (es->txerr >= es->rxerr) ? new_state : 0;
+		rx_state = (es->txerr <= es->rxerr) ? new_state : 0;
+
+		can_change_state(priv->netdev, cf, tx_state, rx_state);
+		new_state = priv->can.state;
+	}
+
 	if (priv->can.restart_ms &&
-	    (priv->can.state >= CAN_STATE_BUS_OFF) &&
+	    (cur_state >= CAN_STATE_BUS_OFF) &&
 	    (new_state < CAN_STATE_BUS_OFF)) {
 		priv->can.can_stats.restarts++;
 	}
@@ -665,18 +668,17 @@ static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *pri
 
 	priv->bec.txerr = es->txerr;
 	priv->bec.rxerr = es->rxerr;
-	priv->can.state = new_state;
 }
 
 static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 				const struct kvaser_msg *msg)
 {
-	struct can_frame *cf;
+	struct can_frame *cf, tmp_cf = { .can_id = CAN_ERR_FLAG, .can_dlc = CAN_ERR_DLC };
 	struct sk_buff *skb;
 	struct net_device_stats *stats;
 	struct kvaser_usb_net_priv *priv;
 	struct kvaser_usb_error_summary es = { };
-	enum can_state old_state;
+	enum can_state old_state, new_state;
 
 	switch (msg->id) {
 	case CMD_CAN_ERROR_EVENT:
@@ -721,60 +723,54 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 	}
 
 	/* Update all of the can interface's state and error counters before
-	 * trying any skb allocation that can actually fail with -ENOMEM.
+	 * trying any memory allocation that can actually fail with -ENOMEM.
+	 *
+	 * We send a temporary stack-allocated error can frame to
+	 * can_change_state() for the very same reason.
+	 *
+	 * TODO: Split can_change_state() responsibility between updating the
+	 * can interface's state and counters, and the setting up of can error
+	 * frame ID and data to userspace. Remove stack allocation afterwards.
 	 */
 	old_state = priv->can.state;
-	kvaser_usb_rx_error_update_can_state(priv, &es);
+	kvaser_usb_rx_error_update_can_state(priv, &es, &tmp_cf);
+	new_state = priv->can.state;
 
 	skb = alloc_can_err_skb(priv->netdev, &cf);
 	if (!skb) {
 		stats->rx_dropped++;
 		return;
 	}
+	memcpy(cf, &tmp_cf, sizeof(*cf));
 
-	if (es.status & M16C_STATE_BUS_OFF) {
-		cf->can_id |= CAN_ERR_BUSOFF;
-
-		if (!priv->can.restart_ms)
-			kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
-		netif_carrier_off(priv->netdev);
-	} else if (es.status & M16C_STATE_BUS_PASSIVE) {
-		if (old_state != CAN_STATE_ERROR_PASSIVE) {
-			cf->can_id |= CAN_ERR_CRTL;
-
-			if (es.txerr || es.rxerr)
-				cf->data[1] = (es.txerr > es.rxerr)
-						? CAN_ERR_CRTL_TX_PASSIVE
-						: CAN_ERR_CRTL_RX_PASSIVE;
-			else
-				cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
-					      CAN_ERR_CRTL_RX_PASSIVE;
+	if (new_state != old_state) {
+		if (es.status & M16C_STATE_BUS_OFF) {
+			if (!priv->can.restart_ms)
+				kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+			netif_carrier_off(priv->netdev);
+		}
+
+		if (es.status == M16C_STATE_BUS_ERROR) {
+			if ((old_state >= CAN_STATE_ERROR_WARNING) ||
+			    (es.txerr < 96 && es.rxerr < 96)) {
+				if (old_state > CAN_STATE_ERROR_ACTIVE) {
+					cf->can_id |= CAN_ERR_PROT;
+					cf->data[2] = CAN_ERR_PROT_ACTIVE;
+				}
+			}
 		}
-	}
 
-	if (es.status == M16C_STATE_BUS_ERROR) {
-		if ((old_state < CAN_STATE_ERROR_WARNING) &&
-		    ((es.txerr >= 96) || (es.rxerr >= 96))) {
-			cf->can_id |= CAN_ERR_CRTL;
-			cf->data[1] = (es.txerr > es.rxerr)
-					? CAN_ERR_CRTL_TX_WARNING
-					: CAN_ERR_CRTL_RX_WARNING;
-		} else if (old_state > CAN_STATE_ERROR_ACTIVE) {
+		if (!es.status) {
 			cf->can_id |= CAN_ERR_PROT;
 			cf->data[2] = CAN_ERR_PROT_ACTIVE;
 		}
-	}
 
-	if (!es.status) {
-		cf->can_id |= CAN_ERR_PROT;
-		cf->data[2] = CAN_ERR_PROT_ACTIVE;
-	}
-
-	if (priv->can.restart_ms &&
-	    (old_state >= CAN_STATE_BUS_OFF) &&
-	    (priv->can.state < CAN_STATE_BUS_OFF)) {
-		cf->can_id |= CAN_ERR_RESTARTED;
-		netif_carrier_on(priv->netdev);
+		if (priv->can.restart_ms &&
+		    (old_state >= CAN_STATE_BUS_OFF) &&
+		    (new_state < CAN_STATE_BUS_OFF)) {
+			cf->can_id |= CAN_ERR_RESTARTED;
+			netif_carrier_on(priv->netdev);
+		}
 	}
 
 	if (es.error_factor) {
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 3/5] can: kvaser_usb: Fix state handling upon BUS_ERROR events
From: Ahmed S. Darwish @ 2015-01-20 21:47 UTC (permalink / raw)
  To: Olivier Sobrie, Oliver Hartkopp, Wolfgang Grandegger,
	Marc Kleine-Budde
  Cc: Andri Yngvason, Linux-CAN, netdev, LKML
In-Reply-To: <20150120214537.GB16828@linux>

From: Ahmed S. Darwish <ahmed.darwish@valeo.com>

While being in an ERROR_WARNING state and receiving further
bus error events with error counts in the range of 97-127 inclusive,
the state handling code erroneously reverts back to ERROR_ACTIVE.

As per the CAN standard recommendations, only revert to ERROR_ACTIVE
when the error counters are less than 96.

Moreover, in certain Kvaser models, the BUS_ERROR flag is always set
along with undefined bits in the M16C status register. Thus use
bitwise ops instead of full equality for checking the register
against bus errors.

Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
---
 drivers/net/can/usb/kvaser_usb.c | 24 +++++++++++-------------
 1 file changed, 11 insertions(+), 13 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 0386d3f..640b0eb 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -635,10 +635,12 @@ static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *pri
 		new_state = CAN_STATE_BUS_OFF;
 	else if (es->status & M16C_STATE_BUS_PASSIVE)
 		new_state = CAN_STATE_ERROR_PASSIVE;
-
-	if (es->status == M16C_STATE_BUS_ERROR) {
-		if ((cur_state < CAN_STATE_ERROR_WARNING) &&
-		    ((es->txerr >= 96) || (es->rxerr >= 96)))
+	else if (es->status & M16C_STATE_BUS_ERROR) {
+		if ((es->txerr >= 256) || (es->rxerr >= 256))
+			new_state = CAN_STATE_BUS_OFF;
+		else if ((es->txerr >= 128) || (es->rxerr >= 128))
+			new_state = CAN_STATE_ERROR_PASSIVE;
+		else if ((es->txerr >= 96) || (es->rxerr >= 96))
 			new_state = CAN_STATE_ERROR_WARNING;
 		else if (cur_state > CAN_STATE_ERROR_ACTIVE)
 			new_state = CAN_STATE_ERROR_ACTIVE;
@@ -748,15 +750,11 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 			if (!priv->can.restart_ms)
 				kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
 			netif_carrier_off(priv->netdev);
-		}
-
-		if (es.status == M16C_STATE_BUS_ERROR) {
-			if ((old_state >= CAN_STATE_ERROR_WARNING) ||
-			    (es.txerr < 96 && es.rxerr < 96)) {
-				if (old_state > CAN_STATE_ERROR_ACTIVE) {
-					cf->can_id |= CAN_ERR_PROT;
-					cf->data[2] = CAN_ERR_PROT_ACTIVE;
-				}
+		} else if (es.status & M16C_STATE_BUS_ERROR) {
+			if ((es.txerr < 96 && es.rxerr < 96) &&
+			    (old_state > CAN_STATE_ERROR_ACTIVE)) {
+				cf->can_id |= CAN_ERR_PROT;
+				cf->data[2] = CAN_ERR_PROT_ACTIVE;
 			}
 		}
 
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH net-next 1/2] udp: Do not require sock in udp_tunnel_xmit_skb
From: Or Gerlitz @ 2015-01-20 21:47 UTC (permalink / raw)
  To: Tom Herbert; +Cc: David Miller, Thomas Graf, Linux Netdev List
In-Reply-To: <CA+mtBx8dihFYb1v_WeyukfV5hk==2=3_b_GNUJoxCuL8_GUf6A@mail.gmail.com>

On Tue, Jan 20, 2015 at 7:36 PM, Tom Herbert <therbert@google.com> wrote:
> On Sun, Jan 18, 2015 at 2:43 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
>> On Sat, Jan 17, 2015 at 8:18 PM, Tom Herbert <therbert@google.com> wrote:
>>> The UDP tunnel transmit functions udp_tunnel_xmit_skb and
>>> udp_tunnel6_xmit_skb include a socket argument. The socket being
>>> passed to the functions (from VXLAN) is a UDP created for receive
>>> side. The only thing that the socket is used for in the transmit
>>> functions is to get the setting for checksum (enabled or zero).
>>
>> Tom, just to clarify - re the sockets usage in the transmit side,
>> somewhere bind or alike is done on them such that we have multiple
>> source UDP ports for given host VXLAN traffic. Here for example the
>> sender host is 192.168.31.17 and two ports are seen here 54206 and
>> 50795.
>>
>> Just wanted to make sure this series doesn't change that, since if
>> this is the case, we introduce here a regression w.r.t RSS hash
>> spreading from the outer UDP header data at the receiver side (which
>> is the right thing to do, per your LKS session...)
>>
> Hi Or,
>
> Using or not using a socket on transmit should have no bearing to the
> receive side. RSS works based on the hash of the UDP 5-tuple which
> should include a source port set to a value for the inner flow. Since
> the UDP socket is unconnected it should have no bearing on RFS or XPS
> either.

Hi Tom,

You say "which should include a source port set to a value for the
inner flow", well this series doesn't add such logic, nor I am fully
clear what piece exactly is responsible for the fact that I see
multiple source udp ports used from vxlan traffic flowing out of a
certain host. I just wanted to make sure that these patches don't
introduce a regression w.r.t to the **current** (not future) state of
things, can you confirm this?

^ permalink raw reply

* [PATCH v5 4/5] can: kvaser_usb: Retry the first bulk transfer on -ETIMEDOUT
From: Ahmed S. Darwish @ 2015-01-20 21:48 UTC (permalink / raw)
  To: Olivier Sobrie, Oliver Hartkopp, Wolfgang Grandegger,
	Marc Kleine-Budde
  Cc: Andri Yngvason, Linux-CAN, netdev, LKML
In-Reply-To: <20150120214709.GC16828@linux>

From: Ahmed S. Darwish <ahmed.darwish@valeo.com>

On some x86 laptops, plugging a Kvaser device again after an
unplug makes the firmware always ignore the very first command.
For such a case, provide some room for retries instead of
completly exiting the driver init code.

Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
---
 drivers/net/can/usb/kvaser_usb.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 640b0eb..068e76c 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -1614,7 +1614,7 @@ static int kvaser_usb_probe(struct usb_interface *intf,
 {
 	struct kvaser_usb *dev;
 	int err = -ENOMEM;
-	int i;
+	int i, retry = 3;
 
 	dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
 	if (!dev)
@@ -1632,7 +1632,15 @@ static int kvaser_usb_probe(struct usb_interface *intf,
 
 	usb_set_intfdata(intf, dev);
 
-	err = kvaser_usb_get_software_info(dev);
+	/* On some x86 laptops, plugging a Kvaser device again after
+	 * an unplug makes the firmware always ignore the very first
+	 * command. For such a case, provide some room for retries
+	 * instead of completly exiting the driver.
+	 */
+	do {
+		err = kvaser_usb_get_software_info(dev);
+	} while (--retry && err == -ETIMEDOUT);
+
 	if (err) {
 		dev_err(&intf->dev,
 			"Cannot get software infos, error %d\n", err);
-- 
1.9.1


^ permalink raw reply related

* [PATCH v5 5/5] can: kvaser_usb: Add support for the USBcan-II family
From: Ahmed S. Darwish @ 2015-01-20 21:50 UTC (permalink / raw)
  To: Olivier Sobrie, Oliver Hartkopp, Wolfgang Grandegger,
	Marc Kleine-Budde
  Cc: Andri Yngvason, Linux-CAN, netdev, LKML
In-Reply-To: <20150120214834.GD16828@linux>

From: Ahmed S. Darwish <ahmed.darwish@valeo.com>

CAN to USB interfaces sold by the Swedish manufacturer Kvaser are
divided into two major families: 'Leaf', and 'USBcanII'.  From an
Operating System perspective, the firmware of both families behave
in a not too drastically different fashion.

This patch adds support for the USBcanII family of devices to the
current Kvaser Leaf-only driver.

CAN frames sending, receiving, and error handling paths has been
tested using the dual-channel "Kvaser USBcan II HS/LS" dongle. It
should also work nicely with other products in the same category.

List of new devices supported by this driver update:

         - Kvaser USBcan II HS/HS
         - Kvaser USBcan II HS/LS
         - Kvaser USBcan Rugged ("USBcan Rev B")
         - Kvaser Memorator HS/HS
         - Kvaser Memorator HS/LS
         - Scania VCI2 (if you have the Kvaser logo on top)

Signed-off-by: Ahmed S. Darwish <ahmed.darwish@valeo.com>
---
 drivers/net/can/usb/Kconfig      |   8 +-
 drivers/net/can/usb/kvaser_usb.c | 598 ++++++++++++++++++++++++++++++---------
 2 files changed, 478 insertions(+), 128 deletions(-)

** V5 Changelog:
- Rebase on the new CAN error state changes added for the Leaf driver
- Add minor changes (remove unused commands, constify poniters, etc.)

** V4 Changelog:
- Use type-safe C methods instead of cpp macros
- Remove defensive checks against non-existing families
- Re-order methods to remove forward declarations
- Smaller stuff spotted by earlier review (function prefexes, etc.)

** V3 Changelog:
- Fix padding for the usbcan_msg_tx_acknowledge command
- Remove kvaser_usb->max_channels and the MAX_NET_DEVICES macro
- Rename commands to CMD_LEAF_xxx and CMD_USBCAN_xxx
- Apply checkpatch.pl suggestions ('net/' comments, multi-line strings, etc.)

** V2 Changelog:
- Update Kconfig entries
- Use actual number of CAN channels (instead of max) where appropriate
- Rebase over a new set of UsbcanII-independent driver fixes

diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index a77db919..f6f5500 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -25,7 +25,7 @@ config CAN_KVASER_USB
 	tristate "Kvaser CAN/USB interface"
 	---help---
 	  This driver adds support for Kvaser CAN/USB devices like Kvaser
-	  Leaf Light.
+	  Leaf Light and Kvaser USBcan II.
 
 	  The driver provides support for the following devices:
 	    - Kvaser Leaf Light
@@ -46,6 +46,12 @@ config CAN_KVASER_USB
 	    - Kvaser USBcan R
 	    - Kvaser Leaf Light v2
 	    - Kvaser Mini PCI Express HS
+	    - Kvaser USBcan II HS/HS
+	    - Kvaser USBcan II HS/LS
+	    - Kvaser USBcan Rugged ("USBcan Rev B")
+	    - Kvaser Memorator HS/HS
+	    - Kvaser Memorator HS/LS
+	    - Scania VCI2 (if you have the Kvaser logo on top)
 
 	  If unsure, say N.
 
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index 068e76c..3e1eb5d 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -6,10 +6,12 @@
  * Parts of this driver are based on the following:
  *  - Kvaser linux leaf driver (version 4.78)
  *  - CAN driver for esd CAN-USB/2
+ *  - Kvaser linux usbcanII driver (version 5.3)
  *
  * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
  * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
  * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
+ * Copyright (C) 2015 Valeo A.S.
  */
 
 #include <linux/completion.h>
@@ -30,8 +32,9 @@
 #define RX_BUFFER_SIZE			3072
 #define CAN_USB_CLOCK			8000000
 #define MAX_NET_DEVICES			3
+#define MAX_USBCAN_NET_DEVICES		2
 
-/* Kvaser USB devices */
+/* Kvaser Leaf USB devices */
 #define KVASER_VENDOR_ID		0x0bfd
 #define USB_LEAF_DEVEL_PRODUCT_ID	10
 #define USB_LEAF_LITE_PRODUCT_ID	11
@@ -56,6 +59,24 @@
 #define USB_LEAF_LITE_V2_PRODUCT_ID	288
 #define USB_MINI_PCIE_HS_PRODUCT_ID	289
 
+static inline bool kvaser_is_leaf(const struct usb_device_id *id)
+{
+	return id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID &&
+	       id->idProduct <= USB_MINI_PCIE_HS_PRODUCT_ID;
+}
+
+/* Kvaser USBCan-II devices */
+#define USB_USBCAN_REVB_PRODUCT_ID	2
+#define USB_VCI2_PRODUCT_ID		3
+#define USB_USBCAN2_PRODUCT_ID		4
+#define USB_MEMORATOR_PRODUCT_ID	5
+
+static inline bool kvaser_is_usbcan(const struct usb_device_id *id)
+{
+	return id->idProduct >= USB_USBCAN_REVB_PRODUCT_ID &&
+	       id->idProduct <= USB_MEMORATOR_PRODUCT_ID;
+}
+
 /* USB devices features */
 #define KVASER_HAS_SILENT_MODE		BIT(0)
 #define KVASER_HAS_TXRX_ERRORS		BIT(1)
@@ -73,7 +94,7 @@
 #define MSG_FLAG_TX_ACK			BIT(6)
 #define MSG_FLAG_TX_REQUEST		BIT(7)
 
-/* Can states */
+/* Can states (M16C CxSTRH register) */
 #define M16C_STATE_BUS_RESET		BIT(0)
 #define M16C_STATE_BUS_ERROR		BIT(4)
 #define M16C_STATE_BUS_PASSIVE		BIT(5)
@@ -98,7 +119,11 @@
 #define CMD_START_CHIP_REPLY		27
 #define CMD_STOP_CHIP			28
 #define CMD_STOP_CHIP_REPLY		29
-#define CMD_GET_CARD_INFO2		32
+
+#define CMD_LEAF_GET_CARD_INFO2		32
+#define CMD_USBCAN_RESET_CLOCK		32
+#define CMD_USBCAN_CLOCK_OVERFLOW_EVENT	33
+
 #define CMD_GET_CARD_INFO		34
 #define CMD_GET_CARD_INFO_REPLY		35
 #define CMD_GET_SOFTWARE_INFO		38
@@ -108,8 +133,9 @@
 #define CMD_RESET_ERROR_COUNTER		49
 #define CMD_TX_ACKNOWLEDGE		50
 #define CMD_CAN_ERROR_EVENT		51
-#define CMD_USB_THROTTLE		77
-#define CMD_LOG_MESSAGE			106
+
+#define CMD_LEAF_USB_THROTTLE		77
+#define CMD_LEAF_LOG_MESSAGE		106
 
 /* error factors */
 #define M16C_EF_ACKE			BIT(0)
@@ -121,6 +147,14 @@
 #define M16C_EF_RCVE			BIT(6)
 #define M16C_EF_TRE			BIT(7)
 
+/* Only Leaf-based devices can report M16C error factors,
+ * thus define our own error status flags for USBCANII
+ */
+#define USBCAN_ERROR_STATE_NONE		0
+#define USBCAN_ERROR_STATE_TX_ERROR	BIT(0)
+#define USBCAN_ERROR_STATE_RX_ERROR	BIT(1)
+#define USBCAN_ERROR_STATE_BUSERROR	BIT(2)
+
 /* bittiming parameters */
 #define KVASER_USB_TSEG1_MIN		1
 #define KVASER_USB_TSEG1_MAX		16
@@ -137,9 +171,18 @@
 #define KVASER_CTRL_MODE_SELFRECEPTION	3
 #define KVASER_CTRL_MODE_OFF		4
 
-/* log message */
+/* Extended CAN identifier flag */
 #define KVASER_EXTENDED_FRAME		BIT(31)
 
+/* Kvaser USB CAN dongles are divided into two major families:
+ * - Leaf: Based on Renesas M32C, running firmware labeled as 'filo'
+ * - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios'
+ */
+enum kvaser_usb_family {
+	KVASER_LEAF,
+	KVASER_USBCAN,
+};
+
 struct kvaser_msg_simple {
 	u8 tid;
 	u8 channel;
@@ -148,30 +191,55 @@ struct kvaser_msg_simple {
 struct kvaser_msg_cardinfo {
 	u8 tid;
 	u8 nchannels;
-	__le32 serial_number;
-	__le32 padding;
+	union {
+		struct {
+			__le32 serial_number;
+			__le32 padding;
+		} __packed leaf0;
+		struct {
+			__le32 serial_number_low;
+			__le32 serial_number_high;
+		} __packed usbcan0;
+	} __packed;
 	__le32 clock_resolution;
 	__le32 mfgdate;
 	u8 ean[8];
 	u8 hw_revision;
-	u8 usb_hs_mode;
-	__le16 padding2;
+	union {
+		struct {
+			u8 usb_hs_mode;
+		} __packed leaf1;
+		struct {
+			u8 padding;
+		} __packed usbcan1;
+	} __packed;
+	__le16 padding;
 } __packed;
 
 struct kvaser_msg_cardinfo2 {
 	u8 tid;
-	u8 channel;
+	u8 reserved;
 	u8 pcb_id[24];
 	__le32 oem_unlock_code;
 } __packed;
 
-struct kvaser_msg_softinfo {
+struct leaf_msg_softinfo {
 	u8 tid;
-	u8 channel;
+	u8 padding0;
 	__le32 sw_options;
 	__le32 fw_version;
 	__le16 max_outstanding_tx;
-	__le16 padding[9];
+	__le16 padding1[9];
+} __packed;
+
+struct usbcan_msg_softinfo {
+	u8 tid;
+	u8 fw_name[5];
+	__le16 max_outstanding_tx;
+	u8 padding[6];
+	__le32 fw_version;
+	__le16 checksum;
+	__le16 sw_options;
 } __packed;
 
 struct kvaser_msg_busparams {
@@ -188,36 +256,86 @@ struct kvaser_msg_tx_can {
 	u8 channel;
 	u8 tid;
 	u8 msg[14];
-	u8 padding;
-	u8 flags;
+	union {
+		struct {
+			u8 padding;
+			u8 flags;
+		} __packed leaf;
+		struct {
+			u8 flags;
+			u8 padding;
+		} __packed usbcan;
+	} __packed;
+} __packed;
+
+struct kvaser_msg_rx_can_header {
+	u8 channel;
+	u8 flag;
 } __packed;
 
-struct kvaser_msg_rx_can {
+struct leaf_msg_rx_can {
 	u8 channel;
 	u8 flag;
+
 	__le16 time[3];
 	u8 msg[14];
 } __packed;
 
-struct kvaser_msg_chip_state_event {
+struct usbcan_msg_rx_can {
+	u8 channel;
+	u8 flag;
+
+	u8 msg[14];
+	__le16 time;
+} __packed;
+
+struct leaf_msg_chip_state_event {
 	u8 tid;
 	u8 channel;
+
 	__le16 time[3];
 	u8 tx_errors_count;
 	u8 rx_errors_count;
+
+	u8 status;
+	u8 padding[3];
+} __packed;
+
+struct usbcan_msg_chip_state_event {
+	u8 tid;
+	u8 channel;
+
+	u8 tx_errors_count;
+	u8 rx_errors_count;
+	__le16 time;
+
 	u8 status;
 	u8 padding[3];
 } __packed;
 
-struct kvaser_msg_tx_acknowledge {
+struct kvaser_msg_tx_acknowledge_header {
 	u8 channel;
 	u8 tid;
+} __packed;
+
+struct leaf_msg_tx_acknowledge {
+	u8 channel;
+	u8 tid;
+
 	__le16 time[3];
 	u8 flags;
 	u8 time_offset;
 } __packed;
 
-struct kvaser_msg_error_event {
+struct usbcan_msg_tx_acknowledge {
+	u8 channel;
+	u8 tid;
+
+	__le16 time;
+	__le16 padding;
+} __packed;
+
+struct leaf_msg_error_event {
 	u8 tid;
 	u8 flags;
 	__le16 time[3];
@@ -229,6 +347,18 @@ struct kvaser_msg_error_event {
 	u8 error_factor;
 } __packed;
 
+struct usbcan_msg_error_event {
+	u8 tid;
+	u8 padding;
+	u8 tx_errors_count_ch0;
+	u8 rx_errors_count_ch0;
+	u8 tx_errors_count_ch1;
+	u8 rx_errors_count_ch1;
+	u8 status_ch0;
+	u8 status_ch1;
+	__le16 time;
+} __packed;
+
 struct kvaser_msg_ctrl_mode {
 	u8 tid;
 	u8 channel;
@@ -243,7 +373,7 @@ struct kvaser_msg_flush_queue {
 	u8 padding[3];
 } __packed;
 
-struct kvaser_msg_log_message {
+struct leaf_msg_log_message {
 	u8 channel;
 	u8 flags;
 	__le16 time[3];
@@ -260,21 +390,55 @@ struct kvaser_msg {
 		struct kvaser_msg_simple simple;
 		struct kvaser_msg_cardinfo cardinfo;
 		struct kvaser_msg_cardinfo2 cardinfo2;
-		struct kvaser_msg_softinfo softinfo;
 		struct kvaser_msg_busparams busparams;
+
+		struct kvaser_msg_rx_can_header rx_can_header;
+		struct kvaser_msg_tx_acknowledge_header tx_acknowledge_header;
+
+		union {
+			struct leaf_msg_softinfo softinfo;
+			struct leaf_msg_rx_can rx_can;
+			struct leaf_msg_chip_state_event chip_state_event;
+			struct leaf_msg_tx_acknowledge tx_acknowledge;
+			struct leaf_msg_error_event error_event;
+			struct leaf_msg_log_message log_message;
+		} __packed leaf;
+
+		union {
+			struct usbcan_msg_softinfo softinfo;
+			struct usbcan_msg_rx_can rx_can;
+			struct usbcan_msg_chip_state_event chip_state_event;
+			struct usbcan_msg_tx_acknowledge tx_acknowledge;
+			struct usbcan_msg_error_event error_event;
+		} __packed usbcan;
+
 		struct kvaser_msg_tx_can tx_can;
-		struct kvaser_msg_rx_can rx_can;
-		struct kvaser_msg_chip_state_event chip_state_event;
-		struct kvaser_msg_tx_acknowledge tx_acknowledge;
-		struct kvaser_msg_error_event error_event;
 		struct kvaser_msg_ctrl_mode ctrl_mode;
 		struct kvaser_msg_flush_queue flush_queue;
-		struct kvaser_msg_log_message log_message;
 	} u;
 } __packed;
 
+/* Summary of a kvaser error event, for a unified Leaf/Usbcan error
+ * handling. Some discrepancies between the two families exist:
+ *
+ * - USBCAN firmware does not report M16C "error factors"
+ * - USBCAN controllers has difficulties reporting if the raised error
+ *   event is for ch0 or ch1. They leave such arbitration to the OS
+ *   driver by letting it compare error counters with previous values
+ *   and decide the error event's channel. Thus for USBCAN, the channel
+ *   field is only advisory.
+ */
 struct kvaser_usb_error_summary {
-	u8 channel, status, txerr, rxerr, error_factor;
+	u8 channel, status, txerr, rxerr;
+	union {
+		struct {
+			u8 error_factor;
+		} leaf;
+		struct {
+			u8 other_ch_status;
+			u8 error_state;
+		} usbcan;
+	};
 };
 
 struct kvaser_usb_tx_urb_context {
@@ -292,6 +456,7 @@ struct kvaser_usb {
 
 	u32 fw_version;
 	unsigned int nchannels;
+	enum kvaser_usb_family family;
 
 	bool rxinitdone;
 	void *rxbuf[MAX_RX_URBS];
@@ -315,6 +480,7 @@ struct kvaser_usb_net_priv {
 };
 
 static const struct usb_device_id kvaser_usb_table[] = {
+	/* Leaf family IDs */
 	{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
 	{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
 	{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
@@ -364,6 +530,17 @@ static const struct usb_device_id kvaser_usb_table[] = {
 		.driver_info = KVASER_HAS_TXRX_ERRORS },
 	{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
 	{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
+
+	/* USBCANII family IDs */
+	{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID),
+		.driver_info = KVASER_HAS_TXRX_ERRORS },
+	{ USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID),
+		.driver_info = KVASER_HAS_TXRX_ERRORS },
+	{ USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID),
+		.driver_info = KVASER_HAS_TXRX_ERRORS },
+	{ USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID),
+		.driver_info = KVASER_HAS_TXRX_ERRORS },
+
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
@@ -467,7 +644,14 @@ static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
 	if (err)
 		return err;
 
-	dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+	switch (dev->family) {
+	case KVASER_LEAF:
+		dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version);
+		break;
+	case KVASER_USBCAN:
+		dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version);
+		break;
+	}
 
 	return 0;
 }
@@ -486,7 +670,9 @@ static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
 		return err;
 
 	dev->nchannels = msg.u.cardinfo.nchannels;
-	if (dev->nchannels > MAX_NET_DEVICES)
+	if ((dev->nchannels > MAX_NET_DEVICES) ||
+	    (dev->family == KVASER_USBCAN &&
+	     dev->nchannels > MAX_USBCAN_NET_DEVICES))
 		return -EINVAL;
 
 	return 0;
@@ -500,8 +686,10 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
 	struct kvaser_usb_net_priv *priv;
 	struct sk_buff *skb;
 	struct can_frame *cf;
-	u8 channel = msg->u.tx_acknowledge.channel;
-	u8 tid = msg->u.tx_acknowledge.tid;
+	u8 channel, tid;
+
+	channel = msg->u.tx_acknowledge_header.channel;
+	tid = msg->u.tx_acknowledge_header.tid;
 
 	if (channel >= dev->nchannels) {
 		dev_err(dev->udev->dev.parent,
@@ -623,12 +811,12 @@ static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *pri
 						 const struct kvaser_usb_error_summary *es,
 						 struct can_frame *cf)
 {
-	struct net_device_stats *stats;
+	struct kvaser_usb *dev = priv->dev;
+	struct net_device_stats *stats = &priv->netdev->stats;
 	enum can_state cur_state, new_state, tx_state, rx_state;
 
 	netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status);
 
-	stats = &priv->netdev->stats;
 	new_state = cur_state = priv->can.state;
 
 	if (es->status & M16C_STATE_BUS_OFF)
@@ -663,9 +851,22 @@ static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *pri
 		priv->can.can_stats.restarts++;
 	}
 
-	if (es->error_factor) {
-		priv->can.can_stats.bus_error++;
-		stats->rx_errors++;
+	switch (dev->family) {
+	case KVASER_LEAF:
+		if (es->leaf.error_factor) {
+			priv->can.can_stats.bus_error++;
+			stats->rx_errors++;
+		}
+		break;
+	case KVASER_USBCAN:
+		if (es->usbcan.error_state & USBCAN_ERROR_STATE_TX_ERROR)
+			stats->tx_errors++;
+		if (es->usbcan.error_state & USBCAN_ERROR_STATE_RX_ERROR)
+			stats->rx_errors++;
+		if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) {
+			priv->can.can_stats.bus_error++;
+		}
+		break;
 	}
 
 	priv->bec.txerr = es->txerr;
@@ -673,53 +874,24 @@ static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *pri
 }
 
 static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
-				const struct kvaser_msg *msg)
+				const struct kvaser_usb_error_summary *es)
 {
 	struct can_frame *cf, tmp_cf = { .can_id = CAN_ERR_FLAG, .can_dlc = CAN_ERR_DLC };
 	struct sk_buff *skb;
 	struct net_device_stats *stats;
 	struct kvaser_usb_net_priv *priv;
-	struct kvaser_usb_error_summary es = { };
 	enum can_state old_state, new_state;
 
-	switch (msg->id) {
-	case CMD_CAN_ERROR_EVENT:
-		es.channel = msg->u.error_event.channel;
-		es.status =  msg->u.error_event.status;
-		es.txerr = msg->u.error_event.tx_errors_count;
-		es.rxerr = msg->u.error_event.rx_errors_count;
-		es.error_factor = msg->u.error_event.error_factor;
-		break;
-	case CMD_LOG_MESSAGE:
-		es.channel = msg->u.log_message.channel;
-		es.status = msg->u.log_message.data[0];
-		es.txerr = msg->u.log_message.data[2];
-		es.rxerr = msg->u.log_message.data[3];
-		es.error_factor = msg->u.log_message.data[1];
-		break;
-	case CMD_CHIP_STATE_EVENT:
-		es.channel = msg->u.chip_state_event.channel;
-		es.status =  msg->u.chip_state_event.status;
-		es.txerr = msg->u.chip_state_event.tx_errors_count;
-		es.rxerr = msg->u.chip_state_event.rx_errors_count;
-		es.error_factor = 0;
-		break;
-	default:
-		dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
-			msg->id);
-		return;
-	}
-
-	if (es.channel >= dev->nchannels) {
+	if (es->channel >= dev->nchannels) {
 		dev_err(dev->udev->dev.parent,
-			"Invalid channel number (%d)\n", es.channel);
+			"Invalid channel number (%d)\n", es->channel);
 		return;
 	}
 
-	priv = dev->nets[es.channel];
+	priv = dev->nets[es->channel];
 	stats = &priv->netdev->stats;
 
-	if (es.status & M16C_STATE_BUS_RESET) {
+	if (es->status & M16C_STATE_BUS_RESET) {
 		kvaser_usb_unlink_tx_urbs(priv);
 		return;
 	}
@@ -735,7 +907,7 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 	 * frame ID and data to userspace. Remove stack allocation afterwards.
 	 */
 	old_state = priv->can.state;
-	kvaser_usb_rx_error_update_can_state(priv, &es, &tmp_cf);
+	kvaser_usb_rx_error_update_can_state(priv, es, &tmp_cf);
 	new_state = priv->can.state;
 
 	skb = alloc_can_err_skb(priv->netdev, &cf);
@@ -746,19 +918,19 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 	memcpy(cf, &tmp_cf, sizeof(*cf));
 
 	if (new_state != old_state) {
-		if (es.status & M16C_STATE_BUS_OFF) {
+		if (es->status & M16C_STATE_BUS_OFF) {
 			if (!priv->can.restart_ms)
 				kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
 			netif_carrier_off(priv->netdev);
-		} else if (es.status & M16C_STATE_BUS_ERROR) {
-			if ((es.txerr < 96 && es.rxerr < 96) &&
+		} else if (es->status & M16C_STATE_BUS_ERROR) {
+			if ((es->txerr < 96 && es->rxerr < 96) &&
 			    (old_state > CAN_STATE_ERROR_ACTIVE)) {
 				cf->can_id |= CAN_ERR_PROT;
 				cf->data[2] = CAN_ERR_PROT_ACTIVE;
 			}
 		}
 
-		if (!es.status) {
+		if (!es->status) {
 			cf->can_id |= CAN_ERR_PROT;
 			cf->data[2] = CAN_ERR_PROT_ACTIVE;
 		}
@@ -771,34 +943,161 @@ static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
 		}
 	}
 
-	if (es.error_factor) {
-		cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
-
-		if (es.error_factor & M16C_EF_ACKE)
-			cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
-		if (es.error_factor & M16C_EF_CRCE)
-			cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
-					CAN_ERR_PROT_LOC_CRC_DEL);
-		if (es.error_factor & M16C_EF_FORME)
-			cf->data[2] |= CAN_ERR_PROT_FORM;
-		if (es.error_factor & M16C_EF_STFE)
-			cf->data[2] |= CAN_ERR_PROT_STUFF;
-		if (es.error_factor & M16C_EF_BITE0)
-			cf->data[2] |= CAN_ERR_PROT_BIT0;
-		if (es.error_factor & M16C_EF_BITE1)
-			cf->data[2] |= CAN_ERR_PROT_BIT1;
-		if (es.error_factor & M16C_EF_TRE)
-			cf->data[2] |= CAN_ERR_PROT_TX;
+	switch (dev->family) {
+	case KVASER_LEAF:
+		if (es->leaf.error_factor) {
+			cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+
+			if (es->leaf.error_factor & M16C_EF_ACKE)
+				cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+			if (es->leaf.error_factor & M16C_EF_CRCE)
+				cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+						CAN_ERR_PROT_LOC_CRC_DEL);
+			if (es->leaf.error_factor & M16C_EF_FORME)
+				cf->data[2] |= CAN_ERR_PROT_FORM;
+			if (es->leaf.error_factor & M16C_EF_STFE)
+				cf->data[2] |= CAN_ERR_PROT_STUFF;
+			if (es->leaf.error_factor & M16C_EF_BITE0)
+				cf->data[2] |= CAN_ERR_PROT_BIT0;
+			if (es->leaf.error_factor & M16C_EF_BITE1)
+				cf->data[2] |= CAN_ERR_PROT_BIT1;
+			if (es->leaf.error_factor & M16C_EF_TRE)
+				cf->data[2] |= CAN_ERR_PROT_TX;
+		}
+		break;
+	case KVASER_USBCAN:
+		if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) {
+			cf->can_id |= CAN_ERR_BUSERROR;
+		}
+		break;
 	}
 
-	cf->data[6] = es.txerr;
-	cf->data[7] = es.rxerr;
+	cf->data[6] = es->txerr;
+	cf->data[7] = es->rxerr;
 
 	stats->rx_packets++;
 	stats->rx_bytes += cf->can_dlc;
 	netif_rx(skb);
 }
 
+/* For USBCAN, report error to userspace iff the channels's errors counter
+ * has increased, or we're the only channel seeing a bus error state.
+ */
+static void kvaser_usbcan_conditionally_rx_error(const struct kvaser_usb *dev,
+						 struct kvaser_usb_error_summary *es)
+{
+	struct kvaser_usb_net_priv *priv;
+	int channel;
+	bool report_error;
+
+	channel = es->channel;
+	if (channel >= dev->nchannels) {
+		dev_err(dev->udev->dev.parent,
+			"Invalid channel number (%d)\n", channel);
+		return;
+	}
+
+	priv = dev->nets[channel];
+	report_error = false;
+
+	if (es->txerr > priv->bec.txerr) {
+		es->usbcan.error_state |= USBCAN_ERROR_STATE_TX_ERROR;
+		report_error = true;
+	}
+	if (es->rxerr > priv->bec.rxerr) {
+		es->usbcan.error_state |= USBCAN_ERROR_STATE_RX_ERROR;
+		report_error = true;
+	}
+	if ((es->status & M16C_STATE_BUS_ERROR) &&
+	    !(es->usbcan.other_ch_status & M16C_STATE_BUS_ERROR)) {
+		es->usbcan.error_state |= USBCAN_ERROR_STATE_BUSERROR;
+		report_error = true;
+	}
+
+	if (report_error)
+		kvaser_usb_rx_error(dev, es);
+}
+
+static void kvaser_usbcan_rx_error(const struct kvaser_usb *dev,
+				   const struct kvaser_msg *msg)
+{
+	struct kvaser_usb_error_summary es = { };
+
+	switch (msg->id) {
+	/* Sometimes errors are sent as unsolicited chip state events */
+	case CMD_CHIP_STATE_EVENT:
+		es.channel = msg->u.usbcan.chip_state_event.channel;
+		es.status =  msg->u.usbcan.chip_state_event.status;
+		es.txerr = msg->u.usbcan.chip_state_event.tx_errors_count;
+		es.rxerr = msg->u.usbcan.chip_state_event.rx_errors_count;
+		kvaser_usbcan_conditionally_rx_error(dev, &es);
+		break;
+
+	case CMD_CAN_ERROR_EVENT:
+		es.channel = 0;
+		es.status = msg->u.usbcan.error_event.status_ch0;
+		es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch0;
+		es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch0;
+		es.usbcan.other_ch_status =
+			msg->u.usbcan.error_event.status_ch1;
+		kvaser_usbcan_conditionally_rx_error(dev, &es);
+
+		/* The USBCAN firmware supports up to 2 channels.
+		 * Now that ch0 was checked, check if ch1 has any errors.
+		 */
+		if (dev->nchannels == MAX_USBCAN_NET_DEVICES) {
+			es.channel = 1;
+			es.status = msg->u.usbcan.error_event.status_ch1;
+			es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch1;
+			es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch1;
+			es.usbcan.other_ch_status =
+				msg->u.usbcan.error_event.status_ch0;
+			kvaser_usbcan_conditionally_rx_error(dev, &es);
+		}
+		break;
+
+	default:
+		dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+			msg->id);
+	}
+}
+
+static void kvaser_leaf_rx_error(const struct kvaser_usb *dev,
+				 const struct kvaser_msg *msg)
+{
+	struct kvaser_usb_error_summary es = { };
+
+	switch (msg->id) {
+	case CMD_CAN_ERROR_EVENT:
+		es.channel = msg->u.leaf.error_event.channel;
+		es.status =  msg->u.leaf.error_event.status;
+		es.txerr = msg->u.leaf.error_event.tx_errors_count;
+		es.rxerr = msg->u.leaf.error_event.rx_errors_count;
+		es.leaf.error_factor = msg->u.leaf.error_event.error_factor;
+		break;
+	case CMD_LEAF_LOG_MESSAGE:
+		es.channel = msg->u.leaf.log_message.channel;
+		es.status = msg->u.leaf.log_message.data[0];
+		es.txerr = msg->u.leaf.log_message.data[2];
+		es.rxerr = msg->u.leaf.log_message.data[3];
+		es.leaf.error_factor = msg->u.leaf.log_message.data[1];
+		break;
+	case CMD_CHIP_STATE_EVENT:
+		es.channel = msg->u.leaf.chip_state_event.channel;
+		es.status =  msg->u.leaf.chip_state_event.status;
+		es.txerr = msg->u.leaf.chip_state_event.tx_errors_count;
+		es.rxerr = msg->u.leaf.chip_state_event.rx_errors_count;
+		es.leaf.error_factor = 0;
+		break;
+	default:
+		dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+			msg->id);
+		return;
+	}
+
+	kvaser_usb_rx_error(dev, &es);
+}
+
 static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
 				  const struct kvaser_msg *msg)
 {
@@ -806,16 +1105,16 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
 	struct sk_buff *skb;
 	struct net_device_stats *stats = &priv->netdev->stats;
 
-	if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+	if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME |
 					 MSG_FLAG_NERR)) {
 		netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
-			   msg->u.rx_can.flag);
+			   msg->u.rx_can_header.flag);
 
 		stats->rx_errors++;
 		return;
 	}
 
-	if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+	if (msg->u.rx_can_header.flag & MSG_FLAG_OVERRUN) {
 		stats->rx_over_errors++;
 		stats->rx_errors++;
 
@@ -841,7 +1140,8 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
 	struct can_frame *cf;
 	struct sk_buff *skb;
 	struct net_device_stats *stats;
-	u8 channel = msg->u.rx_can.channel;
+	u8 channel = msg->u.rx_can_header.channel;
+	const u8 *rx_msg = NULL;	/* GCC */
 
 	if (channel >= dev->nchannels) {
 		dev_err(dev->udev->dev.parent,
@@ -852,60 +1152,68 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
 	priv = dev->nets[channel];
 	stats = &priv->netdev->stats;
 
-	if ((msg->u.rx_can.flag & MSG_FLAG_ERROR_FRAME) &&
-	    (msg->id == CMD_LOG_MESSAGE)) {
-		kvaser_usb_rx_error(dev, msg);
+	if ((msg->u.rx_can_header.flag & MSG_FLAG_ERROR_FRAME) &&
+	    (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE)) {
+		kvaser_leaf_rx_error(dev, msg);
 		return;
-	} else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
-					 MSG_FLAG_NERR |
-					 MSG_FLAG_OVERRUN)) {
+	} else if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME |
+						MSG_FLAG_NERR |
+						MSG_FLAG_OVERRUN)) {
 		kvaser_usb_rx_can_err(priv, msg);
 		return;
-	} else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
+	} else if (msg->u.rx_can_header.flag & ~MSG_FLAG_REMOTE_FRAME) {
 		netdev_warn(priv->netdev,
 			    "Unhandled frame (flags: 0x%02x)",
-			    msg->u.rx_can.flag);
+			    msg->u.rx_can_header.flag);
 		return;
 	}
 
+	switch (dev->family) {
+	case KVASER_LEAF:
+		rx_msg = msg->u.leaf.rx_can.msg;
+		break;
+	case KVASER_USBCAN:
+		rx_msg = msg->u.usbcan.rx_can.msg;
+		break;
+	}
+
 	skb = alloc_can_skb(priv->netdev, &cf);
 	if (!skb) {
 		stats->tx_dropped++;
 		return;
 	}
 
-	if (msg->id == CMD_LOG_MESSAGE) {
-		cf->can_id = le32_to_cpu(msg->u.log_message.id);
+	if (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE) {
+		cf->can_id = le32_to_cpu(msg->u.leaf.log_message.id);
 		if (cf->can_id & KVASER_EXTENDED_FRAME)
 			cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG;
 		else
 			cf->can_id &= CAN_SFF_MASK;
 
-		cf->can_dlc = get_can_dlc(msg->u.log_message.dlc);
+		cf->can_dlc = get_can_dlc(msg->u.leaf.log_message.dlc);
 
-		if (msg->u.log_message.flags & MSG_FLAG_REMOTE_FRAME)
+		if (msg->u.leaf.log_message.flags & MSG_FLAG_REMOTE_FRAME)
 			cf->can_id |= CAN_RTR_FLAG;
 		else
-			memcpy(cf->data, &msg->u.log_message.data,
+			memcpy(cf->data, &msg->u.leaf.log_message.data,
 			       cf->can_dlc);
 	} else {
-		cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
-			     (msg->u.rx_can.msg[1] & 0x3f);
+		cf->can_id = ((rx_msg[0] & 0x1f) << 6) | (rx_msg[1] & 0x3f);
 
 		if (msg->id == CMD_RX_EXT_MESSAGE) {
 			cf->can_id <<= 18;
-			cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
-				      ((msg->u.rx_can.msg[3] & 0xff) << 6) |
-				      (msg->u.rx_can.msg[4] & 0x3f);
+			cf->can_id |= ((rx_msg[2] & 0x0f) << 14) |
+				      ((rx_msg[3] & 0xff) << 6) |
+				      (rx_msg[4] & 0x3f);
 			cf->can_id |= CAN_EFF_FLAG;
 		}
 
-		cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+		cf->can_dlc = get_can_dlc(rx_msg[5]);
 
-		if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
+		if (msg->u.rx_can_header.flag & MSG_FLAG_REMOTE_FRAME)
 			cf->can_id |= CAN_RTR_FLAG;
 		else
-			memcpy(cf->data, &msg->u.rx_can.msg[6],
+			memcpy(cf->data, &rx_msg[6],
 			       cf->can_dlc);
 	}
 
@@ -968,21 +1276,35 @@ static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
 
 	case CMD_RX_STD_MESSAGE:
 	case CMD_RX_EXT_MESSAGE:
-	case CMD_LOG_MESSAGE:
+		kvaser_usb_rx_can_msg(dev, msg);
+		break;
+
+	case CMD_LEAF_LOG_MESSAGE:
+		if (dev->family != KVASER_LEAF)
+			goto warn;
 		kvaser_usb_rx_can_msg(dev, msg);
 		break;
 
 	case CMD_CHIP_STATE_EVENT:
 	case CMD_CAN_ERROR_EVENT:
-		kvaser_usb_rx_error(dev, msg);
+		if (dev->family == KVASER_LEAF)
+			kvaser_leaf_rx_error(dev, msg);
+		else
+			kvaser_usbcan_rx_error(dev, msg);
 		break;
 
 	case CMD_TX_ACKNOWLEDGE:
 		kvaser_usb_tx_acknowledge(dev, msg);
 		break;
 
+	/* Ignored messages */
+	case CMD_USBCAN_CLOCK_OVERFLOW_EVENT:
+		if (dev->family != KVASER_USBCAN)
+			goto warn;
+		break;
+
 	default:
-		dev_warn(dev->udev->dev.parent,
+warn:		dev_warn(dev->udev->dev.parent,
 			 "Unhandled message (%d)\n", msg->id);
 		break;
 	}
@@ -1202,7 +1524,7 @@ static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
 				  dev->rxbuf[i],
 				  dev->rxbuf_dma[i]);
 
-	for (i = 0; i < MAX_NET_DEVICES; i++) {
+	for (i = 0; i < dev->nchannels; i++) {
 		struct kvaser_usb_net_priv *priv = dev->nets[i];
 
 		if (priv)
@@ -1310,6 +1632,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 	struct kvaser_msg *msg;
 	int i, err;
 	int ret = NETDEV_TX_OK;
+	u8 *msg_tx_can_flags = NULL;		/* GCC */
 
 	if (can_dropped_invalid_skb(netdev, skb))
 		return NETDEV_TX_OK;
@@ -1331,9 +1654,19 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 
 	msg = buf;
 	msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
-	msg->u.tx_can.flags = 0;
 	msg->u.tx_can.channel = priv->channel;
 
+	switch (dev->family) {
+	case KVASER_LEAF:
+		msg_tx_can_flags = &msg->u.tx_can.leaf.flags;
+		break;
+	case KVASER_USBCAN:
+		msg_tx_can_flags = &msg->u.tx_can.usbcan.flags;
+		break;
+	}
+
+	*msg_tx_can_flags = 0;
+
 	if (cf->can_id & CAN_EFF_FLAG) {
 		msg->id = CMD_TX_EXT_MESSAGE;
 		msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
@@ -1351,7 +1684,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 	memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
 
 	if (cf->can_id & CAN_RTR_FLAG)
-		msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+		*msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME;
 
 	for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
 		if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
@@ -1620,6 +1953,17 @@ static int kvaser_usb_probe(struct usb_interface *intf,
 	if (!dev)
 		return -ENOMEM;
 
+	if (kvaser_is_leaf(id)) {
+		dev->family = KVASER_LEAF;
+	} else if (kvaser_is_usbcan(id)) {
+		dev->family = KVASER_USBCAN;
+	} else {
+		dev_err(&intf->dev,
+			"Product ID (%d) does not belong to any known Kvaser USB family",
+			id->idProduct);
+		return -ENODEV;
+	}
+
 	err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
 	if (err) {
 		dev_err(&intf->dev, "Cannot get usb endpoint(s)");
-- 
1.9.1

^ permalink raw reply related

* [PATCH net] ipv4: try to cache dst_entries which would cause a redirect
From: Hannes Frederic Sowa @ 2015-01-20 22:08 UTC (permalink / raw)
  To: netdev; +Cc: Julian Anastasov, Marcelo Leitner, Florian Westphal

Not caching dst_entries which cause redirects could be exploited by hosts
on the same subnet, causing a severe DoS attack. This effect aggravated
since commit f88649721268999 ("ipv4: fix dst race in sk_dst_get()").

Lookups causing redirects will be allocated with DST_NOCACHE set which
will force dst_release to free them via RCU.  Unfortunately waiting for
RCU grace period just takes too long, we can end up with >1M dst_entries
waiting to be released and the system will run OOM. rcuos threads cannot
catch up under high softirq load.

Attaching the flag to emit a redirect later on to the specific skb allows
us to cache those dst_entries thus reducing the pressure on allocation
and deallocation.

This issue was discovered by Marcelo Leitner.

Cc: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Marcelo Leitner <mleitner@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
---
 include/net/ip.h      | 11 ++++++-----
 net/ipv4/ip_forward.c |  3 ++-
 net/ipv4/route.c      |  6 ++----
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/include/net/ip.h b/include/net/ip.h
index 0e5a0ba..14211ea 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -39,11 +39,12 @@ struct inet_skb_parm {
 	struct ip_options	opt;		/* Compiled IP options		*/
 	unsigned char		flags;
 
-#define IPSKB_FORWARDED		1
-#define IPSKB_XFRM_TUNNEL_SIZE	2
-#define IPSKB_XFRM_TRANSFORMED	4
-#define IPSKB_FRAG_COMPLETE	8
-#define IPSKB_REROUTED		16
+#define IPSKB_FORWARDED		BIT(0)
+#define IPSKB_XFRM_TUNNEL_SIZE	BIT(1)
+#define IPSKB_XFRM_TRANSFORMED	BIT(2)
+#define IPSKB_FRAG_COMPLETE	BIT(3)
+#define IPSKB_REROUTED		BIT(4)
+#define IPSKB_DOREDIRECT	BIT(5)
 
 	u16			frag_max_size;
 };
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index 3a83ce5..787b3c2 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -129,7 +129,8 @@ int ip_forward(struct sk_buff *skb)
 	 *	We now generate an ICMP HOST REDIRECT giving the route
 	 *	we calculated.
 	 */
-	if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb_sec_path(skb))
+	if (IPCB(skb)->flags & IPSKB_DOREDIRECT && !opt->srr &&
+	    !skb_sec_path(skb))
 		ip_rt_send_redirect(skb);
 
 	skb->priority = rt_tos2priority(iph->tos);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 2000110..f41cd21 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1568,10 +1568,8 @@ static int __mkroute_input(struct sk_buff *skb,
 	do_cache = res->fi && !itag;
 	if (out_dev == in_dev && err && IN_DEV_TX_REDIRECTS(out_dev) &&
 	    (IN_DEV_SHARED_MEDIA(out_dev) ||
-	     inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) {
-		flags |= RTCF_DOREDIRECT;
-		do_cache = false;
-	}
+	     inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res))))
+		IPCB(skb)->flags |= IPSKB_DOREDIRECT;
 
 	if (skb->protocol != htons(ETH_P_IP)) {
 		/* Not IP (i.e. ARP). Do not create route, if it is
-- 
2.1.0

^ permalink raw reply related

* Re: [net-next PATCH v3 04/12] net: flow_table: create a set of common headers and actions
From: John Fastabend @ 2015-01-20 22:10 UTC (permalink / raw)
  To: John W. Linville
  Cc: tgraf, simon.horman, sfeldma, netdev, jhs, davem, gerlitz.or,
	andy, ast
In-Reply-To: <20150120205938.GD1854@tuxdriver.com>

On 01/20/2015 12:59 PM, John W. Linville wrote:
> On Tue, Jan 20, 2015 at 12:27:53PM -0800, John Fastabend wrote:
>> This adds common headers and actions that drivers can use.
>>
>> I have not yet moved the header graphs into the common header
>> because I'm not entirely convinced its re-usable. The devices
>> I have been looking at have different enough header graphs that
>> they wouldn't be re-usable. However possibly many 40Gbp NICs
>> for example could share a common header graph. When we get
>> multiple implementations we can move this into the common file
>> if it makes sense.
>>
>> And table structures seem to be unique enough that there is
>> little value in putting each devices table layout into the
>> common file so its left for device specific implementation.
>>
>> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
>> ---
>>   include/linux/if_flow_common.h |  257 ++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 257 insertions(+)
>>   create mode 100644 include/linux/if_flow_common.h
>>
>> diff --git a/include/linux/if_flow_common.h b/include/linux/if_flow_common.h
>> new file mode 100644
>> index 0000000..ef2d66f
>> --- /dev/null
>> +++ b/include/linux/if_flow_common.h
>
> <snip>
>
>> +struct net_flow_action net_flow_pop_vlan = {
>> +	.name = "pop_vlan",
>> +	.uid = ACTION_POP_VLAN,
>> +	.args = net_flow_null_args,
>> +};
>
> Random thought, should there be a "push_vlan" (for double VLAN tagging)?
>
>

Yep I should add that one there are also some other actions
on my todo list but some of them require updates to the driver.

Assuming its not a big issue for anyone I would like like to
get this series in with the match/actions it has and then extend
the action and match lists.

.John

-- 
John Fastabend         Intel Corporation

^ permalink raw reply

* Re: [PATCH net-next 1/2] udp: Do not require sock in udp_tunnel_xmit_skb
From: Tom Herbert @ 2015-01-20 22:24 UTC (permalink / raw)
  To: Or Gerlitz; +Cc: David Miller, Thomas Graf, Linux Netdev List
In-Reply-To: <CAJ3xEMit2By8S=SwRGJKcwii9J3LKj29sX=tAbCC3bcAMDkr5Q@mail.gmail.com>

On Tue, Jan 20, 2015 at 1:47 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
> On Tue, Jan 20, 2015 at 7:36 PM, Tom Herbert <therbert@google.com> wrote:
>> On Sun, Jan 18, 2015 at 2:43 PM, Or Gerlitz <gerlitz.or@gmail.com> wrote:
>>> On Sat, Jan 17, 2015 at 8:18 PM, Tom Herbert <therbert@google.com> wrote:
>>>> The UDP tunnel transmit functions udp_tunnel_xmit_skb and
>>>> udp_tunnel6_xmit_skb include a socket argument. The socket being
>>>> passed to the functions (from VXLAN) is a UDP created for receive
>>>> side. The only thing that the socket is used for in the transmit
>>>> functions is to get the setting for checksum (enabled or zero).
>>>
>>> Tom, just to clarify - re the sockets usage in the transmit side,
>>> somewhere bind or alike is done on them such that we have multiple
>>> source UDP ports for given host VXLAN traffic. Here for example the
>>> sender host is 192.168.31.17 and two ports are seen here 54206 and
>>> 50795.
>>>
>>> Just wanted to make sure this series doesn't change that, since if
>>> this is the case, we introduce here a regression w.r.t RSS hash
>>> spreading from the outer UDP header data at the receiver side (which
>>> is the right thing to do, per your LKS session...)
>>>
>> Hi Or,
>>
>> Using or not using a socket on transmit should have no bearing to the
>> receive side. RSS works based on the hash of the UDP 5-tuple which
>> should include a source port set to a value for the inner flow. Since
>> the UDP socket is unconnected it should have no bearing on RFS or XPS
>> either.
>
> Hi Tom,
>
> You say "which should include a source port set to a value for the
> inner flow", well this series doesn't add such logic, nor I am fully
> clear what piece exactly is responsible for the fact that I see
> multiple source udp ports used from vxlan traffic flowing out of a
> certain host. I just wanted to make sure that these patches don't
> introduce a regression w.r.t to the **current** (not future) state of
> things, can you confirm this?

vxlan_xmit_one calls udp_flow_src_port to get a source port value
based on the encapsulated flow.

^ permalink raw reply

* Re: [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
From: Pravin Shelar @ 2015-01-20 22:46 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <1421790195.4832.12.camel@edumazet-glaptop2.roam.corp.google.com>

On Tue, Jan 20, 2015 at 1:43 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> On Tue, 2015-01-20 at 12:25 -0800, Pravin B Shelar wrote:
>> similar to skb_linearize(), this API takes skb list as arg and
>> linearize it into one big skb. STT driver patch will use this.
>>
>> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
>> ---
...
>> @@ -2329,6 +2329,41 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to)
>>  }
>>  EXPORT_SYMBOL(skb_copy_and_csum_dev);
>>
>> +int skb_list_linearize(struct sk_buff *head, gfp_t gfp_mask)
>> +{
>> +     struct sk_buff *skb;
>> +     int tlen = 0;
>> +     int err;
>> +
>> +     err = skb_linearize(head);
>> +     if (err)
>> +             return err;
>> +
>> +     skb = head->next;
>> +     while (skb) {
>> +             tlen += skb->len;
>> +             skb = skb->next;
>> +     }
>> +     err = pskb_expand_head(head, 0, tlen, gfp_mask);
>> +     if (err)
>> +             return err;
>> +
>> +     skb = head->next;
>> +     while (skb) {
>> +             err = skb_copy_bits(skb, 0, skb_tail_pointer(head), skb->len);
>> +             if (err)
>> +                     return err;
>> +             head->tail += skb->len;
>> +             skb = skb->next;
>> +     }
>> +     kfree_skb_list(head->next);
>> +     head->next = NULL;
>> +     head->len += tlen;
>> +     head->truesize = SKB_TRUESIZE(skb_end_offset(head));
>
> This is buggy : skb can have a destructor, like sock_wfree()
>

In current use-case it is never called for skb which is tied to
socket. But I agree it need to be fixed. I can call skb_orphan() to
fix it.

Thanks.

^ permalink raw reply

* Re: [PATCH net-next 2/2] vxlan: Eliminate dependency on UDP socket in transmit path
From: Tom Herbert @ 2015-01-20 22:53 UTC (permalink / raw)
  To: Thomas Graf; +Cc: David Miller, Linux Netdev List
In-Reply-To: <20150120181335.GL20315@casper.infradead.org>

On Tue, Jan 20, 2015 at 10:13 AM, Thomas Graf <tgraf@suug.ch> wrote:
> On 01/20/15 at 09:29am, Tom Herbert wrote:
>> I didn't see any reason to preclude that, if it needs to be symmetric
>> in that case it can be forced at the configuration. Being able to
>> receive RCO but not have to send it to certain peers is important use
>> case. You may want to consider this also for GBP if there are cases
>> where we accept GBP from different peers, but only send it to certain
>> ones.
>
> I think asymmetric configurations are fine, in particular
> receive-only. I was reluctant to the send-only scenario initially
> as I would expect a VTEP sending RCO frames on UDP dport 8472 to
> also always be able to accept RCO frames on that port. I can't
> come up with any specific cases where this would lead to problems
> though so I have no objections.
>
> As for GBP, as processing of the policy group requires additional
> iptables or OVS rules anyway, such behaviour would be implemented
> in those rules by either ignoring the mark or dropping such frames.

It's risky from a protocol perspective to assume that sending
something will be properly ignored. Just because we're willing to
receive something, doesn't mean we necessarily want to send it--
that's the robustness principle :-)

^ permalink raw reply

* Re: [PATCH net-next 0/3] openvswitch: Add STT support.
From: Tom Herbert @ 2015-01-20 23:06 UTC (permalink / raw)
  To: Pravin B Shelar; +Cc: David Miller, Linux Netdev List
In-Reply-To: <1421785536-19793-1-git-send-email-pshelar@nicira.com>

On Tue, Jan 20, 2015 at 12:25 PM, Pravin B Shelar <pshelar@nicira.com> wrote:
> Following patch series adds support for Stateless Transport
> Tunneling protocol.
> STT uses TCP segmentation offload available in most of NIC. On
> packet xmit STT driver appends STT header along with TCP header
> to the packet. For GSO packet GSO parameters are set according
> to tunnel configuration and packet is handed over to networking
> stack. This allows use of segmentation offload available in NICs
>
> Netperf unidirectional test gives ~9.4 Gbits/s performance on 10Gbit
> NIC with 1500 byte MTU with two TCP streams.
>
Having packets marked TCP which really aren't TCP is a rather scary
prospect to deploy in a real data center (TCP is kind of an important
protocol ;-) ). Can you give some more motivation on this, more data
that shows what the benefits are and how this compares to equivalent
encapsulation protocols that implement GRO and GSO.

Thanks,
Tom

> The protocol is documented at
> http://www.ietf.org/archive/id/draft-davie-stt-06.txt
>
> I will send out OVS userspace patch on ovs-dev mailing list.
>
> Pravin B Shelar (3):
>   skbuff: Add skb_list_linearize()
>   net: Add STT tunneling protocol.
>   openvswitch: Add support for STT tunneling.
>
>  include/linux/skbuff.h           |    2 +
>  include/net/stt.h                |   55 ++
>  include/uapi/linux/openvswitch.h |    1 +
>  net/core/skbuff.c                |   35 +
>  net/ipv4/Kconfig                 |   11 +
>  net/ipv4/Makefile                |    1 +
>  net/ipv4/stt.c                   | 1386 ++++++++++++++++++++++++++++++++++++++
>  net/openvswitch/Kconfig          |   10 +
>  net/openvswitch/Makefile         |    1 +
>  net/openvswitch/vport-stt.c      |  214 ++++++
>  10 files changed, 1716 insertions(+)
>  create mode 100644 include/net/stt.h
>  create mode 100644 net/ipv4/stt.c
>  create mode 100644 net/openvswitch/vport-stt.c
>
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 1/2] if_link: Add VF multicast promiscuous mode control
From: Hiroshi Shimamoto @ 2015-01-20 23:40 UTC (permalink / raw)
  To: Bjørn Mork
  Cc: e1000-devel@lists.sourceforge.net, netdev@vger.kernel.org,
	Choi, Sy Jong, linux-kernel@vger.kernel.org, Hayato Momma
In-Reply-To: <874mrlu18e.fsf@nemi.mork.no>

> Subject: Re: [PATCH 1/2] if_link: Add VF multicast promiscuous mode control
> 
> Hiroshi Shimamoto <h-shimamoto@ct.jp.nec.com> writes:
> 
> > From: Hiroshi Shimamoto <h-shimamoto@ct.jp.nec.com>
> >
> > Add netlink directives and ndo entry to control VF multicast promiscuous mode.
> >
> > Intel ixgbe and ixgbevf driver can handle only 30 multicast MAC addresses
> > per VF. It means that we cannot assign over 30 IPv6 addresses to a single
> > VF interface on VM. We want thousands IPv6 addresses in VM.
> >
> > There is capability of multicast promiscuous mode in Intel 82599 chip.
> > It enables all multicast packets are delivered to the target VF.
> >
> > This patch prepares to control that VF multicast promiscuous functionality.
> 
> Adding a new hook for this seems over-complicated to me.  And it still
> doesn't solve the real problems that
>  a) the user has to know about this limit, and
>  b) manually configure the feature
> 
> Most of us, lacking the ability to imagine such arbitrary hardware
> limitations, will go through a few hours of frustrating debugging before
> we figure this one out...
> 
> Why can't the ixgbevf driver just automatically signal the ixgbe driver
> to enable multicast promiscuous mode whenever the list grows past the
> limit?

I had submitted a patch to change ixgbe and ixgbevf driver for this issue.
https://lkml.org/lkml/2014/11/27/269

The previous patch introduces API between ixgbe and ixgbevf driver to
enable multicast promiscuous mode, and ixgbevf enables it automatically
if the number of addresses is over than 30.

I got some comment and I would like to clarify the point, but there was no
answer.
That's the reason I submitted this patch.

Do you think a patch for the ixgbe/ixgbevf driver is preferred?


thanks,
Hiroshi

> 
> I'd also like to note that this comment in
> drivers/net/ethernet/intel/ixgbevf/vf.c
> indicates that the author had some ideas about how more than 30
> addresses could/should be handled:
> 
> static s32 ixgbevf_update_mc_addr_list_vf(struct ixgbe_hw *hw,
> 					  struct net_device *netdev)
> {
> 	struct netdev_hw_addr *ha;
> 	u32 msgbuf[IXGBE_VFMAILBOX_SIZE];
> 	u16 *vector_list = (u16 *)&msgbuf[1];
> 	u32 cnt, i;
> 
> 	/* Each entry in the list uses 1 16 bit word.  We have 30
> 	 * 16 bit words available in our HW msg buffer (minus 1 for the
> 	 * msg type).  That's 30 hash values if we pack 'em right.  If
> 	 * there are more than 30 MC addresses to add then punt the
> 	 * extras for now and then add code to handle more than 30 later.
> 	 * It would be unusual for a server to request that many multi-cast
> 	 * addresses except for in large enterprise network environments.
> 	 */
> 
> 
> 
> The last 2 lines of that comment are of course totally bogus and
> pointless and should be deleted in any case...  It's obvious that 30
> multicast addresses is ridiculously low for lots of normal use cases.
> 
> 
> Bjørn
------------------------------------------------------------------------------
New Year. New Location. New Benefits. New Data Center in Ashburn, VA.
GigeNET is offering a free month of service with a new server in Ashburn.
Choose from 2 high performing configs, both with 100TB of bandwidth.
Higher redundancy.Lower latency.Increased capacity.Completely compliant.
http://p.sf.net/sfu/gigenet
_______________________________________________
E1000-devel mailing list
E1000-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/e1000-devel
To learn more about Intel&#174; Ethernet, visit http://communities.intel.com/community/wired

^ permalink raw reply

* Re: [PATCH net-next 1/3] skbuff: Add skb_list_linearize()
From: Eric Dumazet @ 2015-01-20 23:51 UTC (permalink / raw)
  To: Pravin Shelar; +Cc: David Miller, netdev
In-Reply-To: <CALnjE+pDdvcSdQV83J_DLJwmcM8OsC94xDnH2mSSsxDCNnsztg@mail.gmail.com>

On Tue, 2015-01-20 at 14:46 -0800, Pravin Shelar wrote:

> In current use-case it is never called for skb which is tied to
> socket. But I agree it need to be fixed. I can call skb_orphan() to
> fix it.

I would do nothing at all, like skb_linearize()

^ permalink raw reply


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