* [GIT PULL nf] IPVS Fixes for v3.19
From: Simon Horman @ 2015-01-30 1:22 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Simon Horman
Hi Pablo,
please consider this fix for v3.19.
It resolves a crash in xfrm reported by Florian Wiessner.
I believe this problem manifests since 0a5ebb8000c5 ("ipv4: Pass explicit
daddr arg to ip_send_reply().") which was included in v2.6.39.
Julian reports that the patch has been tested on net tree (Dec 7), 3.14.25,
3.12.33, 3.10.61. 3.4.104 needs a modified fix that resolves rejects. It
applies with little fuzz on 3.2.64. Please let us know if we should post
separate 3.2 and 3.4 patches.
The following changes since commit e8781f70a5b210a1b08cff8ce05895ebcec18d83:
netfilter: nf_tables: disable preemption when restoring chain counters (2015-01-26 11:50:02 +0100)
are available in the git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git tags/ipvs-fixes-for-v3.19
for you to fetch changes up to 579eb62ac35845686a7c4286c0a820b4eb1f96aa:
ipvs: rerouting to local clients is not needed anymore (2015-01-30 10:05:55 +0900)
----------------------------------------------------------------
Julian Anastasov (1):
ipvs: rerouting to local clients is not needed anymore
net/netfilter/ipvs/ip_vs_core.c | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
^ permalink raw reply
* [PATCH] ipvs: rerouting to local clients is not needed anymore
From: Simon Horman @ 2015-01-30 1:22 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: lvs-devel, netdev, netfilter-devel, Wensong Zhang,
Julian Anastasov, Simon Horman
In-Reply-To: <1422580938-31520-1-git-send-email-horms@verge.net.au>
From: Julian Anastasov <ja@ssi.bg>
commit f5a41847acc5 ("ipvs: move ip_route_me_harder for ICMP")
from 2.6.37 introduced ip_route_me_harder() call for responses to
local clients, so that we can provide valid rt_src after SNAT.
It was used by TCP to provide valid daddr for ip_send_reply().
After commit 0a5ebb8000c5 ("ipv4: Pass explicit daddr arg to
ip_send_reply()." from 3.0 this rerouting is not needed anymore
and should be avoided, especially in LOCAL_IN.
Fixes 3.12.33 crash in xfrm reported by Florian Wiessner:
"3.12.33 - BUG xfrm_selector_match+0x25/0x2f6"
Reported-by: Smart Weblications GmbH - Florian Wiessner <f.wiessner@smart-weblications.de>
Tested-by: Smart Weblications GmbH - Florian Wiessner <f.wiessner@smart-weblications.de>
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
---
net/netfilter/ipvs/ip_vs_core.c | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 990decb..b87ca32 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -659,16 +659,24 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
return err;
}
-static int ip_vs_route_me_harder(int af, struct sk_buff *skb)
+static int ip_vs_route_me_harder(int af, struct sk_buff *skb,
+ unsigned int hooknum)
{
+ if (!sysctl_snat_reroute(skb))
+ return 0;
+ /* Reroute replies only to remote clients (FORWARD and LOCAL_OUT) */
+ if (NF_INET_LOCAL_IN == hooknum)
+ return 0;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
- if (sysctl_snat_reroute(skb) && ip6_route_me_harder(skb) != 0)
+ struct dst_entry *dst = skb_dst(skb);
+
+ if (dst->dev && !(dst->dev->flags & IFF_LOOPBACK) &&
+ ip6_route_me_harder(skb) != 0)
return 1;
} else
#endif
- if ((sysctl_snat_reroute(skb) ||
- skb_rtable(skb)->rt_flags & RTCF_LOCAL) &&
+ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL) &&
ip_route_me_harder(skb, RTN_LOCAL) != 0)
return 1;
@@ -791,7 +799,8 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
union nf_inet_addr *snet,
__u8 protocol, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp,
- unsigned int offset, unsigned int ihl)
+ unsigned int offset, unsigned int ihl,
+ unsigned int hooknum)
{
unsigned int verdict = NF_DROP;
@@ -821,7 +830,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
#endif
ip_vs_nat_icmp(skb, pp, cp, 1);
- if (ip_vs_route_me_harder(af, skb))
+ if (ip_vs_route_me_harder(af, skb, hooknum))
goto out;
/* do the statistics and put it back */
@@ -916,7 +925,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
snet.ip = iph->saddr;
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
- pp, ciph.len, ihl);
+ pp, ciph.len, ihl, hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
@@ -981,7 +990,8 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
snet.in6 = ciph.saddr.in6;
writable = ciph.len;
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
- pp, writable, sizeof(struct ipv6hdr));
+ pp, writable, sizeof(struct ipv6hdr),
+ hooknum);
}
#endif
@@ -1040,7 +1050,8 @@ static inline bool is_new_conn(const struct sk_buff *skb,
*/
static unsigned int
handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph,
+ unsigned int hooknum)
{
struct ip_vs_protocol *pp = pd->pp;
@@ -1078,7 +1089,7 @@ handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* if it came from this machine itself. So re-compute
* the routing information.
*/
- if (ip_vs_route_me_harder(af, skb))
+ if (ip_vs_route_me_harder(af, skb, hooknum))
goto drop;
IP_VS_DBG_PKT(10, af, pp, skb, 0, "After SNAT");
@@ -1181,7 +1192,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
cp = pp->conn_out_get(af, skb, &iph, 0);
if (likely(cp))
- return handle_response(af, skb, pd, cp, &iph);
+ return handle_response(af, skb, pd, cp, &iph, hooknum);
if (sysctl_nat_icmp_send(net) &&
(pp->protocol == IPPROTO_TCP ||
pp->protocol == IPPROTO_UDP ||
--
2.1.4
^ permalink raw reply related
* [PATCH net] net: sched: fix panic in rate estimators
From: Eric Dumazet @ 2015-01-30 1:30 UTC (permalink / raw)
To: David Miller; +Cc: netdev, John Fastabend
From: Eric Dumazet <edumazet@google.com>
Doing the following commands on a non idle network device
panics the box instantly, because cpu_bstats gets overwritten
by stats.
tc qdisc add dev eth0 root <your_favorite_qdisc>
... some traffic (one packet is enough) ...
tc qdisc replace dev eth0 root est 1sec 4sec <your_favorite_qdisc>
[ 325.355596] BUG: unable to handle kernel paging request at ffff8841dc5a074c
[ 325.362609] IP: [<ffffffff81541c9e>] __gnet_stats_copy_basic+0x3e/0x90
[ 325.369158] PGD 1fa7067 PUD 0
[ 325.372254] Oops: 0000 [#1] SMP
[ 325.375514] Modules linked in: ...
[ 325.398346] CPU: 13 PID: 14313 Comm: tc Not tainted 3.19.0-smp-DEV #1163
[ 325.412042] task: ffff8800793ab5d0 ti: ffff881ff2fa4000 task.ti: ffff881ff2fa4000
[ 325.419518] RIP: 0010:[<ffffffff81541c9e>] [<ffffffff81541c9e>] __gnet_stats_copy_basic+0x3e/0x90
[ 325.428506] RSP: 0018:ffff881ff2fa7928 EFLAGS: 00010286
[ 325.433824] RAX: 000000000000000c RBX: ffff881ff2fa796c RCX: 000000000000000c
[ 325.440988] RDX: ffff8841dc5a0744 RSI: 0000000000000060 RDI: 0000000000000060
[ 325.448120] RBP: ffff881ff2fa7948 R08: ffffffff81cd4f80 R09: 0000000000000000
[ 325.455268] R10: ffff883ff223e400 R11: 0000000000000000 R12: 000000015cba0744
[ 325.462405] R13: ffffffff81cd4f80 R14: ffff883ff223e460 R15: ffff883feea0722c
[ 325.469536] FS: 00007f2ee30fa700(0000) GS:ffff88407fa20000(0000) knlGS:0000000000000000
[ 325.477630] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 325.483380] CR2: ffff8841dc5a074c CR3: 0000003feeae9000 CR4: 00000000001407e0
[ 325.490510] Stack:
[ 325.492524] ffff883feea0722c ffff883fef719dc0 ffff883feea0722c ffff883ff223e4a0
[ 325.499990] ffff881ff2fa79a8 ffffffff815424ee ffff883ff223e49c 000000015cba0744
[ 325.507460] 00000000f2fa7978 0000000000000000 ffff881ff2fa79a8 ffff883ff223e4a0
[ 325.514956] Call Trace:
[ 325.517412] [<ffffffff815424ee>] gen_new_estimator+0x8e/0x230
[ 325.523250] [<ffffffff815427aa>] gen_replace_estimator+0x4a/0x60
[ 325.529349] [<ffffffff815718ab>] tc_modify_qdisc+0x52b/0x590
[ 325.535117] [<ffffffff8155edd0>] rtnetlink_rcv_msg+0xa0/0x240
[ 325.540963] [<ffffffff8155ed30>] ? __rtnl_unlock+0x20/0x20
[ 325.546532] [<ffffffff8157f811>] netlink_rcv_skb+0xb1/0xc0
[ 325.552145] [<ffffffff8155b355>] rtnetlink_rcv+0x25/0x40
[ 325.557558] [<ffffffff8157f0d8>] netlink_unicast+0x168/0x220
[ 325.563317] [<ffffffff8157f47c>] netlink_sendmsg+0x2ec/0x3e0
Lets play safe and not use an union : percpu 'pointers' are mostly read
anyway, and we have typically few qdiscs per host.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: John Fastabend <john.fastabend@gmail.com>
Fixes: 22e0f8b9322c ("net: sched: make bstats per cpu and estimator RCU safe")
---
include/net/sch_generic.h | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 3d282cbb66bf..c605d305c577 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -79,6 +79,9 @@ struct Qdisc {
struct netdev_queue *dev_queue;
struct gnet_stats_rate_est64 rate_est;
+ struct gnet_stats_basic_cpu __percpu *cpu_bstats;
+ struct gnet_stats_queue __percpu *cpu_qstats;
+
struct Qdisc *next_sched;
struct sk_buff *gso_skb;
/*
@@ -86,15 +89,9 @@ struct Qdisc {
*/
unsigned long state;
struct sk_buff_head q;
- union {
- struct gnet_stats_basic_packed bstats;
- struct gnet_stats_basic_cpu __percpu *cpu_bstats;
- } __packed;
+ struct gnet_stats_basic_packed bstats;
unsigned int __state;
- union {
- struct gnet_stats_queue qstats;
- struct gnet_stats_queue __percpu *cpu_qstats;
- } __packed;
+ struct gnet_stats_queue qstats;
struct rcu_head rcu_head;
int padded;
atomic_t refcnt;
^ permalink raw reply related
* Re: [PATCHv3, ipsec-next] xfrm: Do not parse 32bits compiled xfrm netlink msg on 64bits host
From: Fan Du @ 2015-01-30 2:11 UTC (permalink / raw)
To: nicolas.dichtel; +Cc: Fan Du, steffen.klassert, herbert, davem, netdev
In-Reply-To: <54CA0B9F.8080104@6wind.com>
于 2015年01月29日 18:29, Nicolas Dichtel 写道:
> Le 27/01/2015 10:00, Fan Du a écrit :
>> structure like xfrm_usersa_info or xfrm_userpolicy_info
>> has different sizeof when compiled as 32bits and 64bits
>> due to not appending pack attribute in their definition.
>> This will result in broken SA and SP information when user
>> trying to configure them through netlink interface.
>>
>> Inform user land about this situation instead of keeping
>> silent, the upper test scripts would behave accordingly.
>>
>> Quotes from: http://marc.info/?l=linux-netdev&m=142226348715503&w=2
>>>
>>> Before a clean solution show up, I think it's better to warn user in some way
>>> like http://patchwork.ozlabs.org/patch/323842/ did. Otherwise, many people
>>> who stuck there will always spend time and try to fix this issue in whatever way.
>>
>> Yes, this is the first thing we should do. I'm willing to accept a patch
>>
>> Signed-off-by: Fan Du <fan.du@intel.com>
> A way to solve this problem was to provide to userland a xfrm compat header
> file, which match the ABI of the kernel. Something like:
>
> #include <linux/xfrm.h>
>
> #define xfrm_usersa_info xfrm_usersa_info_64
> #define xfrm_usersa_info_compat xfrm_usersa_info
> struct xfrm_usersa_info_compat {
> struct xfrm_selector sel;
> struct xfrm_id id;
> xfrm_address_t saddr;
> struct xfrm_lifetime_cfg lft;
> struct xfrm_lifetime_cur curlft;
> struct xfrm_stats stats;
> __u32 seq;
> __u32 reqid;
> __u16 family;
> __u8 mode;
> __u8 replay_window;
> __u8 flags;
> __u8 hole1;
> __u32 hole2;
> };
>
> The point I try to make is that patching userland apps allows to use xfrm on a
> 32bits userland / 64bits kernel.
>
> If I understand well your patch, it will not be possible anymore, all messages
> will be rejected. And this may break existing apps.
Add padding in user space does not fix existing 32bits ip binary, moreover not sure all other ARCHes
require the same padding. Maillist has various of proposals AFAICT, all is rejected for reasons
whatever for a very long time including padding user space structure. In fact those structures
are exposed by uapi from kernel to userspace.
Speaking of "break", run 32bits ip xfrm {state/policy/monitor} is _already_ broken on 64bits host.
This patch is making user not stumble there by seeing invalid xfrm information and wondering what's
going on. They can switch to setkey temporally.
last, thanks for your comments after looking at the code...
>
> Regards,
> Nicolas
^ permalink raw reply
* [PATCHv2 net-next] cxgb4: Remove preprocessor check for CONFIG_CXGB4_DCB
From: Hariprasad Shenai @ 2015-01-30 3:19 UTC (permalink / raw)
To: netdev; +Cc: davem, leedom, anish, nirranjan, praveenm, Hariprasad Shenai
In commit dc9daab226aa ("cxgb4: Added support in debugfs to dump
sge_qinfo") a preprocessor check for CONFIG_CXGB4_DCB got added, which should
have been CONFIG_CHELSIO_T4_DCB. After adding the right preprocessor, build
fails due to missing function ethqset2pinfo. Fixing that as well.
Reported-by: Paul Bolle <pebolle@tiscal.nl>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
V2: Updated description since the patch also fixes build failure
---
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c | 24 +++++++++++++++++++-
1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
index 47c0869..61c000a 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
@@ -1229,6 +1229,28 @@ static const struct file_operations rss_vf_config_debugfs_fops = {
.release = seq_release_private
};
+/**
+ * ethqset2pinfo - return port_info of an Ethernet Queue Set
+ * @adap: the adapter
+ * @qset: Ethernet Queue Set
+ */
+static inline struct port_info *ethqset2pinfo(struct adapter *adap, int qset)
+{
+ int pidx;
+
+ for_each_port(adap, pidx) {
+ struct port_info *pi = adap2pinfo(adap, pidx);
+
+ if (qset >= pi->first_qset &&
+ qset < pi->first_qset + pi->nqsets)
+ return pi;
+ }
+
+ /* should never happen! */
+ BUG_ON(1);
+ return NULL;
+}
+
static int sge_qinfo_show(struct seq_file *seq, void *v)
{
struct adapter *adap = seq->private;
@@ -1272,7 +1294,7 @@ do { \
T("TxQ inuse:", q.in_use);
T("TxQ CIDX:", q.cidx);
T("TxQ PIDX:", q.pidx);
-#ifdef CONFIG_CXGB4_DCB
+#ifdef CONFIG_CHELSIO_T4_DCB
T("DCB Prio:", dcb_prio);
S3("u", "DCB PGID:",
(ethqset2pinfo(adap, base_qset + i)->dcb.pgid >>
--
1.7.1
^ permalink raw reply related
* Re: [PATCH net-next v2 1/6] skbuff: Add skb_list_linearize()
From: Alexander Duyck @ 2015-01-30 3:28 UTC (permalink / raw)
To: Pravin B Shelar, davem; +Cc: netdev
In-Reply-To: <1422574164-1860-1-git-send-email-pshelar@nicira.com>
On 01/29/2015 03:29 PM, 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>
> ---
> Fixed according to comments from Eric.
> ---
> include/linux/skbuff.h | 2 ++
> net/core/skbuff.c | 34 ++++++++++++++++++++++++++++++++++
> 2 files changed, 36 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..d6358a7 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -2329,6 +2329,40 @@ 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;
What is the point in linearizing the head if you are just going to
reallocated it pskb_expand_head anyway? It seems like you should
probably just be calling __pskb_pull_tail after your call
pskb_expand_head below.
> + 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;
> + return 0;
> +}
> +EXPORT_SYMBOL(skb_list_linearize);
> +
> /**
> * skb_dequeue - remove from the head of the queue
> * @list: list to dequeue from
I'm not completely convinced this is even necessary. Seems like you are
wasting cycles copying the frames around when you could probably just
pull the header of the first frame and then use page frages to fill in
the rest.
- Alex
^ permalink raw reply
* Re: [PATCH net-next v2 0/6] net: Add STT support.
From: Alexander Duyck @ 2015-01-30 3:46 UTC (permalink / raw)
To: Pravin B Shelar, davem; +Cc: netdev
In-Reply-To: <1422574156-1831-1-git-send-email-pshelar@nicira.com>
On 01/29/2015 03:29 PM, Pravin B Shelar 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
>
> 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.
>
> Following are test results. All tests are done on net-next with
> STT and VXLAN kernel device without OVS.
>
> Single Netperf session:
> =======================
> VXLAN:
> CPU utilization
> - Send local: 1.26
> - Recv remote: 8.62
> Throughput: 4.9 Gbit/sec
> STT:
> CPU utilization
> - Send local: 1.01
> - Recv remote: 1.8
> Throughput: 9.45 Gbit/sec
>
> Five Netperf sessions:
> ======================
> VXLAN:
> CPU utilization
> - Send local: 9.7
> - Recv remote: 70 (varies from 60 to 80)
> Throughput: 9.05 Gbit/sec
> STT:
> CPU utilization
> - Send local: 5.85
> - Recv remote: 14
> Throughput: 9.47 Gbit/sec
>
What does the small packet or non-TCP performance look like for STT vs
VXLAN? My concern is that STT looks like it is a one trick pony since
all your numbers show is TCP TSO performance, and based on some of the
comments in your patches it seems like other protocols such as UDP are
going to suffer pretty badly due to things like the linearization overhead.
- Alex
^ permalink raw reply
* [PATCH] net: rocker: Add support for retrieving port level statistics
From: David Ahern @ 2015-01-30 3:59 UTC (permalink / raw)
To: netdev; +Cc: sfeldma, David Ahern
Add support for retrieving port level statistics from device.
Hook is added for ethtool's stats functionality. For example,
$ ethtool -S eth3
NIC statistics:
rx_packets: 12
rx_bytes: 2790
rx_dropped: 0
rx_errors: 0
tx_packets: 8
tx_bytes: 728
tx_dropped: 0
tx_errors: 0
Signed-off-by: David Ahern <dsahern@gmail.com>
---
drivers/net/ethernet/rocker/rocker.c | 134 +++++++++++++++++++++++++++++++++++
drivers/net/ethernet/rocker/rocker.h | 21 ++++++
2 files changed, 155 insertions(+)
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index 11f4ffcc113d..e6f13f912fa2 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -3833,11 +3833,145 @@ static void rocker_port_get_drvinfo(struct net_device *dev,
strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version));
}
+static struct rocker_port_stats {
+ char str[ETH_GSTRING_LEN];
+ int type;
+} rocker_port_stats[] = {
+ { "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, },
+ { "rx_bytes", ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, },
+ { "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, },
+ { "rx_errors", ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, },
+
+ { "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, },
+ { "tx_bytes", ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, },
+ { "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, },
+ { "tx_errors", ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, },
+};
+
+#define ROCKER_PORT_STATS_LEN ARRAY_SIZE(rocker_port_stats)
+
+static void rocker_port_get_strings(struct net_device *netdev, u32 stringset,
+ u8 *data)
+{
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
+ memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static int
+rocker_cmd_get_port_stats_prep(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ struct rocker_tlv *cmd_stats;
+
+ if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE,
+ ROCKER_TLV_CMD_TYPE_GET_PORT_STATS))
+ return -EMSGSIZE;
+
+ cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO);
+ if (!cmd_stats)
+ return -EMSGSIZE;
+
+ if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_LPORT,
+ rocker_port->lport))
+ return -EMSGSIZE;
+
+ rocker_tlv_nest_end(desc_info, cmd_stats);
+
+ return 0;
+}
+
+static int
+rocker_cmd_get_port_stats_ethtool_proc(struct rocker *rocker,
+ struct rocker_port *rocker_port,
+ struct rocker_desc_info *desc_info,
+ void *priv)
+{
+ struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1];
+ struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1];
+ struct rocker_tlv *pattr;
+ u32 lport;
+ u64 *data = priv;
+ int i;
+
+ rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info);
+
+ if (!attrs[ROCKER_TLV_CMD_INFO])
+ return -EIO;
+
+ rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX,
+ attrs[ROCKER_TLV_CMD_INFO]);
+
+ if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT])
+ return -EIO;
+
+ lport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT]);
+ if (lport != rocker_port->lport)
+ return -EIO;
+
+ for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) {
+ pattr = stats_attrs[rocker_port_stats[i].type];
+ if (!pattr)
+ continue;
+
+ data[i] = rocker_tlv_get_u64(pattr);
+ }
+
+ return 0;
+}
+
+static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port,
+ void *priv)
+{
+ return rocker_cmd_exec(rocker_port->rocker, rocker_port,
+ rocker_cmd_get_port_stats_prep, NULL,
+ rocker_cmd_get_port_stats_ethtool_proc,
+ priv, false);
+}
+
+static void rocker_port_get_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct rocker_port *rocker_port = netdev_priv(dev);
+
+ if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i)
+ data[i] = 0;
+ }
+
+ return;
+}
+
+static int rocker_port_get_sset_count(struct net_device *netdev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ROCKER_PORT_STATS_LEN;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static const struct ethtool_ops rocker_port_ethtool_ops = {
.get_settings = rocker_port_get_settings,
.set_settings = rocker_port_set_settings,
.get_drvinfo = rocker_port_get_drvinfo,
.get_link = ethtool_op_get_link,
+ .get_strings = rocker_port_get_strings,
+ .get_ethtool_stats = rocker_port_get_stats,
+ .get_sset_count = rocker_port_get_sset_count,
};
/*****************
diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h
index 8d2865ba634c..a5bc432feada 100644
--- a/drivers/net/ethernet/rocker/rocker.h
+++ b/drivers/net/ethernet/rocker/rocker.h
@@ -127,6 +127,9 @@ enum {
ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL,
ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS,
+ ROCKER_TLV_CMD_TYPE_CLEAR_PORT_STATS,
+ ROCKER_TLV_CMD_TYPE_GET_PORT_STATS,
+
__ROCKER_TLV_CMD_TYPE_MAX,
ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1,
};
@@ -146,6 +149,24 @@ enum {
__ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1,
};
+enum {
+ ROCKER_TLV_CMD_PORT_STATS_UNSPEC,
+ ROCKER_TLV_CMD_PORT_STATS_LPORT, /* u32 */
+
+ ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, /* u64 */
+
+ ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, /* u64 */
+ ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, /* u64 */
+
+ __ROCKER_TLV_CMD_PORT_STATS_MAX,
+ ROCKER_TLV_CMD_PORT_STATS_MAX = __ROCKER_TLV_CMD_PORT_STATS_MAX - 1,
+};
+
enum rocker_port_mode {
ROCKER_PORT_MODE_OF_DPA,
};
--
1.9.3
^ permalink raw reply related
* Re: [PATCH net-next v2 1/6] skbuff: Add skb_list_linearize()
From: Pravin Shelar @ 2015-01-30 4:03 UTC (permalink / raw)
To: Alexander Duyck; +Cc: David Miller, netdev
In-Reply-To: <54CAFA72.60207@gmail.com>
On Thu, Jan 29, 2015 at 7:28 PM, Alexander Duyck
<alexander.duyck@gmail.com> wrote:
> On 01/29/2015 03:29 PM, 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>
>> ---
>> Fixed according to comments from Eric.
>> ---
>> include/linux/skbuff.h | 2 ++
>> net/core/skbuff.c | 34 ++++++++++++++++++++++++++++++++++
>> 2 files changed, 36 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..d6358a7 100644
>> --- a/net/core/skbuff.c
>> +++ b/net/core/skbuff.c
>> @@ -2329,6 +2329,40 @@ 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;
>
> What is the point in linearizing the head if you are just going to
> reallocated it pskb_expand_head anyway? It seems like you should
> probably just be calling __pskb_pull_tail after your call
> pskb_expand_head below.
>
Sure.
But when linearization is involved most effective way to optimize is
to avoid linearization :) anyways I can improve current implementation
according to your comment.
>> + 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;
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(skb_list_linearize);
>> +
>> /**
>> * skb_dequeue - remove from the head of the queue
>> * @list: list to dequeue from
>
> I'm not completely convinced this is even necessary. Seems like you are
> wasting cycles copying the frames around when you could probably just
> pull the header of the first frame and then use page frages to fill in
> the rest.
>
This is what is done when possible. But skb merging is not always
possible, for example when MAX_SKB_FRAGS limit is reached. You can
have a look at STT implementation.
^ permalink raw reply
* Re: [PATCH net-next v2 0/6] net: Add STT support.
From: Pravin Shelar @ 2015-01-30 4:04 UTC (permalink / raw)
To: Alexander Duyck; +Cc: David Miller, netdev
In-Reply-To: <54CAFE89.4090509@gmail.com>
On Thu, Jan 29, 2015 at 7:46 PM, Alexander Duyck
<alexander.duyck@gmail.com> wrote:
> On 01/29/2015 03:29 PM, Pravin B Shelar 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
>>
>> 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.
>>
>> Following are test results. All tests are done on net-next with
>> STT and VXLAN kernel device without OVS.
>>
>> Single Netperf session:
>> =======================
>> VXLAN:
>> CPU utilization
>> - Send local: 1.26
>> - Recv remote: 8.62
>> Throughput: 4.9 Gbit/sec
>> STT:
>> CPU utilization
>> - Send local: 1.01
>> - Recv remote: 1.8
>> Throughput: 9.45 Gbit/sec
>>
>> Five Netperf sessions:
>> ======================
>> VXLAN:
>> CPU utilization
>> - Send local: 9.7
>> - Recv remote: 70 (varies from 60 to 80)
>> Throughput: 9.05 Gbit/sec
>> STT:
>> CPU utilization
>> - Send local: 5.85
>> - Recv remote: 14
>> Throughput: 9.47 Gbit/sec
>>
>
> What does the small packet or non-TCP performance look like for STT vs
> VXLAN? My concern is that STT looks like it is a one trick pony since
> all your numbers show is TCP TSO performance, and based on some of the
> comments in your patches it seems like other protocols such as UDP are
> going to suffer pretty badly due to things like the linearization overhead.
>
Current implementation is targeted for TCP workloads thats why I
posted numbers with TCP, once UDP is optimized we can discuss UDP
numbers. I am pretty sure the STT code can be optimized further
specially for protocols other than TCP.
^ permalink raw reply
* Re: [PATCH net-next v2 2/6] net: Add STT tunneling protocol.
From: Tom Herbert @ 2015-01-30 4:10 UTC (permalink / raw)
To: Pravin B Shelar; +Cc: David Miller, Linux Netdev List, Jesse Gross
In-Reply-To: <1422574170-1889-1-git-send-email-pshelar@nicira.com>
On Thu, Jan 29, 2015 at 3:29 PM, Pravin B Shelar <pshelar@nicira.com> wrote:
> 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 | 59 +++
> net/ipv4/Kconfig | 11 +
> net/ipv4/Makefile | 1 +
> net/ipv4/stt.c | 1398 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 1469 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..30db4d2
> --- /dev/null
> +++ b/include/net/stt.h
> @@ -0,0 +1,59 @@
> +#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
> +
> +#define STT_BASE_HLEN (sizeof(struct stthdr) + STT_ETH_PAD)
> +#define STT_HEADER_LEN (sizeof(struct tcphdr) + STT_BASE_HLEN)
> +
> +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;
> + int refcnt;
> +};
> +
> +struct stt_sock *stt_sock_add(struct net *net, __be16 port,
> + stt_rcv_t *rcv, void *data);
> +
> +void stt_sock_release(struct net *net, __be16 port);
> +
> +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..e3fde1f
> --- /dev/null
> +++ b/net/ipv4/stt.c
> @@ -0,0 +1,1398 @@
> +/*
> + * 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)
> +
> +/* 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);
Should be: err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &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));
I think there should also be a kernel_listen also. I'm a little
surprised that this could work without it, it doesn't seem like we
should be finding closed sockets on input. In any case even if that
did work, it is quite annoying that the socket doesn't appear in
"netstat -ant".
> + 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;
> + stt_sock->refcnt = 1;
> + 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) {
> + if (stt_sock->rcv == rcv &&
> + stt_sock->rcv_data == data)
> + stt_sock->refcnt++;
> + else
> + 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 net *net, __be16 port)
> +{
> + 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->refcnt--;
> + if (!stt_sock->refcnt) {
> + __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
>
> --
> 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 net-next v2 0/6] net: Add STT support.
From: Tom Herbert @ 2015-01-30 4:17 UTC (permalink / raw)
To: Pravin Shelar; +Cc: Alexander Duyck, David Miller, netdev
In-Reply-To: <CALnjE+pPh5nSvaLTnM-a0r2Gm6K8Hz5BU_CsiWzYmwijPCOgHg@mail.gmail.com>
On Thu, Jan 29, 2015 at 8:04 PM, Pravin Shelar <pshelar@nicira.com> wrote:
> On Thu, Jan 29, 2015 at 7:46 PM, Alexander Duyck
> <alexander.duyck@gmail.com> wrote:
>> On 01/29/2015 03:29 PM, Pravin B Shelar 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
>>>
>>> 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.
>>>
>>> Following are test results. All tests are done on net-next with
>>> STT and VXLAN kernel device without OVS.
>>>
>>> Single Netperf session:
>>> =======================
>>> VXLAN:
>>> CPU utilization
>>> - Send local: 1.26
>>> - Recv remote: 8.62
>>> Throughput: 4.9 Gbit/sec
>>> STT:
>>> CPU utilization
>>> - Send local: 1.01
>>> - Recv remote: 1.8
>>> Throughput: 9.45 Gbit/sec
>>>
>>> Five Netperf sessions:
>>> ======================
>>> VXLAN:
>>> CPU utilization
>>> - Send local: 9.7
>>> - Recv remote: 70 (varies from 60 to 80)
>>> Throughput: 9.05 Gbit/sec
>>> STT:
>>> CPU utilization
>>> - Send local: 5.85
>>> - Recv remote: 14
>>> Throughput: 9.47 Gbit/sec
>>>
>>
>> What does the small packet or non-TCP performance look like for STT vs
>> VXLAN? My concern is that STT looks like it is a one trick pony since
>> all your numbers show is TCP TSO performance, and based on some of the
>> comments in your patches it seems like other protocols such as UDP are
>> going to suffer pretty badly due to things like the linearization overhead.
>>
>
> Current implementation is targeted for TCP workloads thats why I
> posted numbers with TCP, once UDP is optimized we can discuss UDP
> numbers. I am pretty sure the STT code can be optimized further
> specially for protocols other than TCP.
> --
There are many TCP workloads that use small packets, it is critical to
test for these also. E.g. "super_netperf 200 -H <addr> -l 120 -t
TCP_RR -- -r 1,1"
Please provide the *exact* commands that you are using to configure
stt for optimal performance.
Tom
> 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
* [PATCH] net: restore lro after device leave forwarding state
From: Fan Du @ 2015-01-30 12:33 UTC (permalink / raw)
To: bhutchings; +Cc: davem, netdev, fengyuleidian0615
Either detaching a device from bridge or switching a device
out of FORWARDING state, the original lro feature should
possibly be enabled for good reason, e.g. hw feature like
receive side coalescing could come into play.
BEFORE:
echo 1 > /proc/sys/net/ipv4/conf/ens806f0/forwarding && ethtool -k ens806f0 | grep large
large-receive-offload: off
echo 0 > /proc/sys/net/ipv4/conf/ens806f0/forwarding && ethtool -k ens806f0 | grep large
large-receive-offload: off
AFTER:
echo 1 > /proc/sys/net/ipv4/conf/ens806f0/forwarding && ethtool -k ens806f0 | grep large
large-receive-offload: off
echo 0 > /proc/sys/net/ipv4/conf/ens806f0/forwarding && ethtool -k ens806f0 | grep large
large-receive-offload: on
Signed-off-by: Fan Du <fan.du@intel.com>
Fixes: 0187bdfb0567 ("net: Disable LRO on devices that are forwarding")
---
include/linux/netdevice.h | 1 +
net/bridge/br_if.c | 1 +
net/core/dev.c | 24 ++++++++++++++++++++++++
net/ipv4/devinet.c | 4 ++++
net/ipv6/addrconf.c | 2 ++
5 files changed, 32 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 642d426..904b1a4 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2153,6 +2153,7 @@ int dev_alloc_name(struct net_device *dev, const char *name);
int dev_open(struct net_device *dev);
int dev_close(struct net_device *dev);
void dev_disable_lro(struct net_device *dev);
+void dev_enable_lro(struct net_device *dev);
int dev_loopback_xmit(struct sk_buff *newskb);
int dev_queue_xmit(struct sk_buff *skb);
int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 81e49fb..4236f3a 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -565,6 +565,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
netdev_update_features(br->dev);
+ dev_enable_lro(dev);
return 0;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index 1e325ad..938d7f6 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1451,6 +1451,30 @@ void dev_disable_lro(struct net_device *dev)
}
EXPORT_SYMBOL(dev_disable_lro);
+/**
+ * dev_enable_lro - enable Large Receive Offload on a device
+ * @dev: device
+ *
+ * Enable Large Receive Offload (LRO) on a net device. Must be
+ * called under RTNL. This is needed if device is not attached
+ * to a bridge, or user change the forwarding state.
+ */
+void dev_enable_lro(struct net_device *dev)
+{
+ struct net_device *lower_dev;
+ struct list_head *iter;
+
+ dev->wanted_features |= NETIF_F_LRO;
+ netdev_update_features(dev);
+
+ if (unlikely(!(dev->features & NETIF_F_LRO)))
+ netdev_WARN(dev, "failed to enable LRO!\n");
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter)
+ dev_enable_lro(lower_dev);
+}
+EXPORT_SYMBOL(dev_enable_lro);
+
static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val,
struct net_device *dev)
{
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 214882e..3307196 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1956,6 +1956,8 @@ static void inet_forward_change(struct net *net)
struct in_device *in_dev;
if (on)
dev_disable_lro(dev);
+ else
+ dev_enable_lro(dev);
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
if (in_dev) {
@@ -2047,6 +2049,8 @@ static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
container_of(cnf, struct in_device, cnf);
if (*valp)
dev_disable_lro(idev->dev);
+ else
+ dev_enable_lro(idev->dev);
inet_netconf_notify_devconf(net,
NETCONFA_FORWARDING,
idev->dev->ifindex,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f7c8bbe..4c3b54c 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -669,6 +669,8 @@ static void dev_forward_change(struct inet6_dev *idev)
dev = idev->dev;
if (idev->cnf.forwarding)
dev_disable_lro(dev);
+ else
+ dev_enable_lro(dev);
if (dev->flags & IFF_MULTICAST) {
if (idev->cnf.forwarding) {
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH net-next v2 2/6] net: Add STT tunneling protocol.
From: Pravin Shelar @ 2015-01-30 5:02 UTC (permalink / raw)
To: Tom Herbert; +Cc: David Miller, Linux Netdev List, Jesse Gross
In-Reply-To: <CA+mtBx-gs0G5+uJX3kYc6-1piqVqetcUx2EgrmWOAzZ2j5Q1yA@mail.gmail.com>
On Thu, Jan 29, 2015 at 8:10 PM, Tom Herbert <therbert@google.com> wrote:
> On Thu, Jan 29, 2015 at 3:29 PM, Pravin B Shelar <pshelar@nicira.com> wrote:
>> 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 | 59 +++
>> net/ipv4/Kconfig | 11 +
>> net/ipv4/Makefile | 1 +
>> net/ipv4/stt.c | 1398 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 1469 insertions(+)
>> create mode 100644 include/net/stt.h
>> create mode 100644 net/ipv4/stt.c
>>
....
>> +
>> +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);
>
> Should be: err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
>
ok.
>> + 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));
>
> I think there should also be a kernel_listen also. I'm a little
> surprised that this could work without it, it doesn't seem like we
> should be finding closed sockets on input. In any case even if that
> did work, it is quite annoying that the socket doesn't appear in
> "netstat -ant".
>
This is fake TCP socket. it is created to gain exclusive access to the
TCP port. STT receives packet from nf-hook, so no need to listening
TCP socket.
I am not updating any socket stats so I do not think you will see any
socket stats.
>> + if (err < 0)
>> + goto error;
>> +
>> + *sockp = sock;
>> + return 0;
^ permalink raw reply
* Re: [PATCH net-next v2 0/6] net: Add STT support.
From: Pravin Shelar @ 2015-01-30 5:03 UTC (permalink / raw)
To: Tom Herbert; +Cc: Alexander Duyck, David Miller, netdev
In-Reply-To: <CA+mtBx_fFBY_FAsnAo8yu5bjoq5BqPY3Es_19oBdeqrjp4RdiA@mail.gmail.com>
On Thu, Jan 29, 2015 at 8:17 PM, Tom Herbert <therbert@google.com> wrote:
> On Thu, Jan 29, 2015 at 8:04 PM, Pravin Shelar <pshelar@nicira.com> wrote:
>> On Thu, Jan 29, 2015 at 7:46 PM, Alexander Duyck
>> <alexander.duyck@gmail.com> wrote:
>>> On 01/29/2015 03:29 PM, Pravin B Shelar 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
>>>>
>>>> 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.
>>>>
>>>> Following are test results. All tests are done on net-next with
>>>> STT and VXLAN kernel device without OVS.
>>>>
>>>> Single Netperf session:
>>>> =======================
>>>> VXLAN:
>>>> CPU utilization
>>>> - Send local: 1.26
>>>> - Recv remote: 8.62
>>>> Throughput: 4.9 Gbit/sec
>>>> STT:
>>>> CPU utilization
>>>> - Send local: 1.01
>>>> - Recv remote: 1.8
>>>> Throughput: 9.45 Gbit/sec
>>>>
>>>> Five Netperf sessions:
>>>> ======================
>>>> VXLAN:
>>>> CPU utilization
>>>> - Send local: 9.7
>>>> - Recv remote: 70 (varies from 60 to 80)
>>>> Throughput: 9.05 Gbit/sec
>>>> STT:
>>>> CPU utilization
>>>> - Send local: 5.85
>>>> - Recv remote: 14
>>>> Throughput: 9.47 Gbit/sec
>>>>
>>>
>>> What does the small packet or non-TCP performance look like for STT vs
>>> VXLAN? My concern is that STT looks like it is a one trick pony since
>>> all your numbers show is TCP TSO performance, and based on some of the
>>> comments in your patches it seems like other protocols such as UDP are
>>> going to suffer pretty badly due to things like the linearization overhead.
>>>
>>
>> Current implementation is targeted for TCP workloads thats why I
>> posted numbers with TCP, once UDP is optimized we can discuss UDP
>> numbers. I am pretty sure the STT code can be optimized further
>> specially for protocols other than TCP.
>> --
> There are many TCP workloads that use small packets, it is critical to
> test for these also. E.g. "super_netperf 200 -H <addr> -l 120 -t
> TCP_RR -- -r 1,1"
>
I have not tried it on STT device, I will collect those numbers.
> Please provide the *exact* commands that you are using to configure
> stt for optimal performance.
>
To create STT tunnel device.
`ip link add stt1 type stt key 1 remote 1.1.2.128`
No other configuration is needed.
^ permalink raw reply
* Re: [v4] QE: Move QE from arch/powerpc to drivers/soc
From: Scott Wood @ 2015-01-30 5:22 UTC (permalink / raw)
To: Zhao Qiang; +Cc: linux-kernel, netdev, linuxppc-dev, B07421, R63061
In-Reply-To: <1415763613-43674-1-git-send-email-B45475@freescale.com>
On Wed, Nov 12, 2014 at 11:40:13AM +0800, Zhao Qiang wrote:
> ls1 has qe and ls1 has arm cpu.
> move qe from arch/powerpc to drivers/soc/fsl
> to adapt to powerpc and arm
>
> Signed-off-by: Zhao Qiang <B45475@freescale.com>
> ---
> Changes for v2:
> - move code to driver/soc
> Changes for v3:
> - change drivers/soc/qe to drivers/soc/fsl-qe
> Changes for v4:
> - move drivers/soc/fsl-qe to drivers/soc/fsl/qe
> - move head files for qe from include/linux/fsl to include/soc/fsl
> - move qe_ic.c to drivers/irqchip/
Need MAINTAINERS update for drivers/soc/fsl/qe, as previously discussed.
-Scott
^ permalink raw reply
* [PATCH] ipv4: tcp: get rid of ugly unicast_sock
From: Eric Dumazet @ 2015-01-30 5:35 UTC (permalink / raw)
To: David Miller; +Cc: netdev
From: Eric Dumazet <edumazet@google.com>
In commit be9f4a44e7d41 ("ipv4: tcp: remove per net tcp_sock")
I tried to address contention on a socket lock, but the solution
I chose was horrible :
commit 3a7c384ffd57e ("ipv4: tcp: unicast_sock should not land outside
of TCP stack") addressed a selinux regression.
commit 0980e56e506b ("ipv4: tcp: set unicast_sock uc_ttl to -1")
took care of another regression.
commit b5ec8eeac46 ("ipv4: fix ip_send_skb()") fixed another regression.
commit 811230cd85 ("tcp: ipv4: initialize unicast_sock sk_pacing_rate")
was another shot in the dark.
Really, just use a proper socket per cpu, and remove the skb_orphan()
call, to re-enable flow control.
This solves a serious problem with FQ packet scheduler when used in
hostile environments, as we do not want to allocate a flow structure
for every RST packet sent in response to a spoofed packet.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/net/ip.h | 2 +-
include/net/netns/ipv4.h | 1 +
net/ipv4/ip_output.c | 30 +++---------------------------
net/ipv4/tcp_ipv4.c | 37 ++++++++++++++++++++++++++++++++-----
4 files changed, 37 insertions(+), 33 deletions(-)
diff --git a/include/net/ip.h b/include/net/ip.h
index f7cbd703d15d..09cf5aebb283 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -181,7 +181,7 @@ static inline __u8 ip_reply_arg_flowi_flags(const struct ip_reply_arg *arg)
return (arg->flags & IP_REPLY_ARG_NOSRCCHECK) ? FLOWI_FLAG_ANYSRC : 0;
}
-void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
+void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
const struct ip_options *sopt,
__be32 daddr, __be32 saddr,
const struct ip_reply_arg *arg,
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 24945cefc4fd..0ffef1a38efc 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -52,6 +52,7 @@ struct netns_ipv4 {
struct inet_peer_base *peers;
struct tcpm_hash_bucket *tcp_metrics_hash;
unsigned int tcp_metrics_hash_log;
+ struct sock * __percpu *tcp_sk;
struct netns_frags frags;
#ifdef CONFIG_NETFILTER
struct xt_table *iptable_filter;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 38a20a9cca1a..c373c0708d97 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1506,24 +1506,8 @@ static int ip_reply_glue_bits(void *dptr, char *to, int offset,
/*
* Generic function to send a packet as reply to another packet.
* Used to send some TCP resets/acks so far.
- *
- * Use a fake percpu inet socket to avoid false sharing and contention.
*/
-static DEFINE_PER_CPU(struct inet_sock, unicast_sock) = {
- .sk = {
- .__sk_common = {
- .skc_refcnt = ATOMIC_INIT(1),
- },
- .sk_wmem_alloc = ATOMIC_INIT(1),
- .sk_allocation = GFP_ATOMIC,
- .sk_flags = (1UL << SOCK_USE_WRITE_QUEUE),
- .sk_pacing_rate = ~0U,
- },
- .pmtudisc = IP_PMTUDISC_WANT,
- .uc_ttl = -1,
-};
-
-void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
+void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,
const struct ip_options *sopt,
__be32 daddr, __be32 saddr,
const struct ip_reply_arg *arg,
@@ -1533,9 +1517,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
struct ipcm_cookie ipc;
struct flowi4 fl4;
struct rtable *rt = skb_rtable(skb);
+ struct net *net = sock_net(sk);
struct sk_buff *nskb;
- struct sock *sk;
- struct inet_sock *inet;
int err;
if (__ip_options_echo(&replyopts.opt.opt, skb, sopt))
@@ -1566,15 +1549,11 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
if (IS_ERR(rt))
return;
- inet = &get_cpu_var(unicast_sock);
+ inet_sk(sk)->tos = arg->tos;
- inet->tos = arg->tos;
- sk = &inet->sk;
sk->sk_priority = skb->priority;
sk->sk_protocol = ip_hdr(skb)->protocol;
sk->sk_bound_dev_if = arg->bound_dev_if;
- sock_net_set(sk, net);
- __skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,
len, 0, &ipc, &rt, MSG_DONTWAIT);
@@ -1590,13 +1569,10 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE;
- skb_orphan(nskb);
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
out:
- put_cpu_var(unicast_sock);
-
ip_rt_put(rt);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a3f72d7fc06c..d22f54482bab 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -683,7 +683,8 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.bound_dev_if = sk->sk_bound_dev_if;
arg.tos = ip_hdr(skb)->tos;
- ip_send_unicast_reply(net, skb, &TCP_SKB_CB(skb)->header.h4.opt,
+ ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
+ skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
&arg, arg.iov[0].iov_len);
@@ -767,7 +768,8 @@ static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
- ip_send_unicast_reply(net, skb, &TCP_SKB_CB(skb)->header.h4.opt,
+ ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
+ skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
&arg, arg.iov[0].iov_len);
@@ -2428,14 +2430,39 @@ struct proto tcp_prot = {
};
EXPORT_SYMBOL(tcp_prot);
+static void __net_exit tcp_sk_exit(struct net *net)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu)
+ inet_ctl_sock_destroy(*per_cpu_ptr(net->ipv4.tcp_sk, cpu));
+ free_percpu(net->ipv4.tcp_sk);
+}
+
static int __net_init tcp_sk_init(struct net *net)
{
+ int res, cpu;
+
+ net->ipv4.tcp_sk = alloc_percpu(struct sock *);
+ if (!net->ipv4.tcp_sk)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ struct sock *sk;
+
+ res = inet_ctl_sock_create(&sk, PF_INET, SOCK_RAW,
+ IPPROTO_TCP, net);
+ if (res)
+ goto fail;
+ *per_cpu_ptr(net->ipv4.tcp_sk, cpu) = sk;
+ }
net->ipv4.sysctl_tcp_ecn = 2;
return 0;
-}
-static void __net_exit tcp_sk_exit(struct net *net)
-{
+fail:
+ tcp_sk_exit(net);
+
+ return res;
}
static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list)
^ permalink raw reply related
* [PATCH v2] stmmac: DMA threshold mode or SF mode can be different among multiple device instance
From: Sonic Zhang @ 2015-01-30 5:49 UTC (permalink / raw)
To: Giuseppe Cavallaro, David S. Miller
Cc: netdev, adi-buildroot-devel, Sonic Zhang
From: Sonic Zhang <sonic.zhang@analog.com>
- In tx_hard_error_bump_tc interrupt, tc should be bumped only when current
device instance is in DMA threshold mode. Check per device xstats.threshold
other than global tc.
- Set per device xstats.threshold to SF_DMA_MODE when current device
instance is set to SF mode.
v2-changes:
- fix ident style
Signed-off-by: Sonic Zhang <sonic.zhang@analog.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index cd6ebda..4d91147 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1287,7 +1287,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
* that needs to not insert csum in the TDES.
*/
priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE);
- tc = SF_DMA_MODE;
+ priv->xstats.threshold = SF_DMA_MODE;
} else
priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
}
@@ -1444,7 +1444,8 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv)
}
if (unlikely(status & tx_hard_error_bump_tc)) {
/* Try to bump up the dma threshold on this failure */
- if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) {
+ if (unlikely(priv->xstats.threshold != SF_DMA_MODE) &&
+ (tc <= 256)) {
tc += 64;
if (priv->plat->force_thresh_dma_mode)
priv->hw->dma->dma_mode(priv->ioaddr, tc, tc);
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 1/2] rhashtable: Introduce rhashtable_walk_*
From: Herbert Xu @ 2015-01-30 5:58 UTC (permalink / raw)
To: Patrick McHardy
Cc: Thomas Graf, David Miller, David.Laight, ying.xue, paulmck,
netdev, netfilter-devel
In-Reply-To: <20150128190715.GA4764@acer.localdomain>
On Wed, Jan 28, 2015 at 07:07:16PM +0000, Patrick McHardy wrote:
> On 28.01, Herbert Xu wrote:
> > On Tue, Jan 27, 2015 at 01:09:50PM +0000, Patrick McHardy wrote:
> > >
> > > Actually I have a patchset queued that adds runtime additions and
> > > removals, both active and timeout based. So netfilter won't have
> > > pure synchronous behaviour anymore.
> >
> > Can you show us the patchset?
>
> Sure, will send it over tomorrow.
So how are you handling the locking in these cases?
Cheers,
--
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
^ permalink raw reply
* [PATCH net-next v4 0/7] switchdev offload flags
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch series introduces new offload flags for switchdev.
Kernel network subsystems can use this flag to accelerate
network functions by offloading to hw.
I expect that there will be need for subsystem specific feature
flag in the future.
This patch series currently only addresses bridge driver link
attribute offloads to hardware.
Looking at the current state of bridge l2 offload in the kernel,
- flag 'self' is the way to directly manage the bridge device in hw via
the ndo_bridge_setlink/ndo_bridge_getlink calls
- flag 'master' is always used to manage the in kernel bridge devices
via the same ndo_bridge_setlink/ndo_bridge_getlink calls
Today these are used separately. The nic offloads use hwmode "vepa/veb" to go
directly to hw with the "self" flag.
At this point i am trying not to introduce any new user facing flags/attributes.
In the model where we want the kernel bridging to be accelerated with
hardware, we very much want the bridge driver to be involved.
In this proposal,
- The offload flag/bit helps switch asic drivers to indicate that they
accelerate the kernel networking objects/functions
- The user does not have to specify a new flag to do so. A bridge created with
switch asic ports will be accelerated if the switch driver supports it.
- The user can continue to directly manage l2 in nics (ixgbe) using the
existing hwmode/self flags
- It also does not stop users from using the 'self' flag to talk to the
switch asic driver directly
- Involving the bridge driver makes sure the add/del notifications to user
space go out after both kernel and hardware are programmed
(To selectively offload bridge port attributes,
example learning in hw only etc, we can introduce offload bits for
per bridge port flag attribute as in my previous patch
https://patchwork.ozlabs.org/patch/413211/. I have not included that in this
series)
v2
- try a different name for the offload flag/bit
- tries to solve the stacked netdev case by traversing the lowerdev
list to reach the switch port
v3 -
- Tested with bond as bridge port for the stacked device case.
Includes a bond_fix_features change to not ignore the
NETIF_F_HW_NETFUNC_OFFLOAD flag
- Some checkpatch fixes
v4 -
- rename flag to NETIF_F_HW_SWITCH_OFFLOAD
- add ndo_bridge_setlink/dellink handlers in bond and team drivers as
suggested by jiri.
- introduce default ndo_dflt_netdev_switch_port_bridge_setlink/dellink
handlers that masters can use to call offload api on lowerdevs.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Roopa Prabhu (7):
netdev: introduce new NETIF_F_HW_SWITCH_OFFLOAD feature flag for
switch device offloads
bridge: add flags argument to ndo_bridge_setlink and
ndo_bridge_dellink
swdevice: add new apis to set and del bridge port attributes
bridge: offload bridge port attributes to switch asic if feature flag
set
rocker: set feature NETIF_F_HW_SWITCH_OFFLOAD
bonding: handle NETIF_F_HW_SWITCH_OFFLOAD flag and add
ndo_bridge_setlink/dellink handlers
team: handle NETIF_F_HW_SWITCH_OFFLOAD flag and add
ndo_bridge_setlink/dellink handlers
drivers/net/bonding/bond_main.c | 9 +-
drivers/net/ethernet/emulex/benet/be_main.c | 3 +-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +-
drivers/net/ethernet/rocker/rocker.c | 5 +-
drivers/net/team/team.c | 5 +-
include/linux/netdev_features.h | 6 +-
include/linux/netdevice.h | 6 +-
include/net/switchdev.h | 37 ++++++++-
net/bridge/br_netlink.c | 30 +++++--
net/bridge/br_private.h | 4 +-
net/core/rtnetlink.c | 10 ++-
net/switchdev/switchdev.c | 110 +++++++++++++++++++++++++
12 files changed, 206 insertions(+), 21 deletions(-)
--
1.7.10.4
^ permalink raw reply
* [PATCH net-next v4 1/7] netdev: introduce new NETIF_F_HW_SWITCH_OFFLOAD feature flag for switch device offloads
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This is a high level feature flag for all switch asic offloads
switch drivers set this flag on switch ports. Logical devices like
bridge, bonds, vxlans can inherit this flag from their slaves/ports.
The patch also adds the flag to NETIF_F_ONE_FOR_ALL, so that it gets
propagated to the upperdevices (bridges and bonds).
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
include/linux/netdev_features.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index 8e30685..7d59dc6 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -66,6 +66,7 @@ enum {
NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */
NETIF_F_HW_L2FW_DOFFLOAD_BIT, /* Allow L2 Forwarding in Hardware */
NETIF_F_BUSY_POLL_BIT, /* Busy poll */
+ NETIF_F_HW_SWITCH_OFFLOAD_BIT, /* HW switch offload */
/*
* Add your fresh new feature above and remember to update
@@ -124,6 +125,7 @@ enum {
#define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX)
#define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD)
#define NETIF_F_BUSY_POLL __NETIF_F(BUSY_POLL)
+#define NETIF_F_HW_SWITCH_OFFLOAD __NETIF_F(HW_SWITCH_OFFLOAD)
/* Features valid for ethtool to change */
/* = all defined minus driver/device-class-related */
@@ -159,7 +161,9 @@ enum {
*/
#define NETIF_F_ONE_FOR_ALL (NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | \
NETIF_F_SG | NETIF_F_HIGHDMA | \
- NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED)
+ NETIF_F_FRAGLIST | NETIF_F_VLAN_CHALLENGED | \
+ NETIF_F_HW_SWITCH_OFFLOAD)
+
/*
* If one device doesn't support one of these features, then disable it
* for all in netdev_increment_features.
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next v4 2/7] bridge: add flags argument to ndo_bridge_setlink and ndo_bridge_dellink
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
bridge flags are needed inside ndo_bridge_setlink/dellink handlers to
avoid another call to parse IFLA_AF_SPEC inside these handlers
This is used later in this series
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
drivers/net/ethernet/emulex/benet/be_main.c | 3 ++-
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +-
drivers/net/ethernet/rocker/rocker.c | 2 +-
include/linux/netdevice.h | 6 ++++--
net/bridge/br_netlink.c | 4 ++--
net/bridge/br_private.h | 4 ++--
net/core/rtnetlink.c | 10 ++++++----
7 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 598c507..efed92c 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -4327,7 +4327,8 @@ fw_exit:
return status;
}
-static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh)
+static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
+ u16 flags)
{
struct be_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 7bb421b..e4086fe 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7786,7 +7786,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
}
static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
- struct nlmsghdr *nlh)
+ struct nlmsghdr *nlh, u16 flags)
{
struct ixgbe_adapter *adapter = netdev_priv(dev);
struct nlattr *attr, *br_spec;
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index 11f4ffc..f0d607c 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -3722,7 +3722,7 @@ skip:
}
static int rocker_port_bridge_setlink(struct net_device *dev,
- struct nlmsghdr *nlh)
+ struct nlmsghdr *nlh, u16 flags)
{
struct rocker_port *rocker_port = netdev_priv(dev);
struct nlattr *protinfo;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 642d426..6013b38 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1154,13 +1154,15 @@ struct net_device_ops {
int idx);
int (*ndo_bridge_setlink)(struct net_device *dev,
- struct nlmsghdr *nlh);
+ struct nlmsghdr *nlh,
+ u16 flags);
int (*ndo_bridge_getlink)(struct sk_buff *skb,
u32 pid, u32 seq,
struct net_device *dev,
u32 filter_mask);
int (*ndo_bridge_dellink)(struct net_device *dev,
- struct nlmsghdr *nlh);
+ struct nlmsghdr *nlh,
+ u16 flags);
int (*ndo_change_carrier)(struct net_device *dev,
bool new_carrier);
int (*ndo_get_phys_port_id)(struct net_device *dev,
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 3875ea5..5b38221 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -494,7 +494,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
}
/* Change state and parameters on port. */
-int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
{
struct nlattr *protinfo;
struct nlattr *afspec;
@@ -550,7 +550,7 @@ out:
}
/* Delete port information */
-int br_dellink(struct net_device *dev, struct nlmsghdr *nlh)
+int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
{
struct nlattr *afspec;
struct net_bridge_port *p;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index e8e3f36..de09199 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -819,8 +819,8 @@ extern struct rtnl_link_ops br_link_ops;
int br_netlink_init(void);
void br_netlink_fini(void);
void br_ifinfo_notify(int event, struct net_bridge_port *port);
-int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
-int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg);
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
+int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg, u16 flags);
int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev,
u32 filter_mask);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 07447d1..c693c8a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2991,7 +2991,7 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
goto out;
}
- err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh);
+ err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh, flags);
if (err)
goto out;
@@ -3002,7 +3002,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!dev->netdev_ops->ndo_bridge_setlink)
err = -EOPNOTSUPP;
else
- err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh);
+ err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh,
+ flags);
if (!err) {
flags &= ~BRIDGE_FLAGS_SELF;
@@ -3064,7 +3065,7 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
goto out;
}
- err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
+ err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh, flags);
if (err)
goto out;
@@ -3075,7 +3076,8 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (!dev->netdev_ops->ndo_bridge_dellink)
err = -EOPNOTSUPP;
else
- err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh);
+ err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh,
+ flags);
if (!err) {
flags &= ~BRIDGE_FLAGS_SELF;
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next v4 3/7] swdevice: add new apis to set and del bridge port attributes
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch adds two new api's netdev_switch_port_bridge_setlink
and netdev_switch_port_bridge_dellink to offload bridge port attributes
to switch port
(The names of the apis look odd with 'switch_port_bridge',
but am more inclined to change the prefix of the api to something else.
Will take any suggestions).
The api's look at the NETIF_F_HW_SWITCH_OFFLOAD feature flag to
pass bridge port attributes to the port device.
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
include/net/switchdev.h | 37 ++++++++++++++-
net/switchdev/switchdev.c | 110 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 146 insertions(+), 1 deletion(-)
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 205e636..cfcdac2 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -43,7 +43,14 @@ int register_netdev_switch_notifier(struct notifier_block *nb);
int unregister_netdev_switch_notifier(struct notifier_block *nb);
int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
struct netdev_switch_notifier_info *info);
-
+int netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags);
+int netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags);
+int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags);
+int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags);
#else
static inline int netdev_switch_parent_id_get(struct net_device *dev,
@@ -74,6 +81,34 @@ static inline int call_netdev_switch_notifiers(unsigned long val, struct net_dev
return NOTIFY_DONE;
}
+static inline int netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh,
+ u16 flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh,
+ u16 flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh,
+ u16 flags)
+{
+ return 0;
+}
+
+static inline int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh,
+ u16 flags)
+{
+ return 0;
+}
+
#endif
#endif /* _LINUX_SWITCHDEV_H_ */
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
index 22e02f4..8c1e558 100644
--- a/net/switchdev/switchdev.c
+++ b/net/switchdev/switchdev.c
@@ -115,3 +115,113 @@ int call_netdev_switch_notifiers(unsigned long val, struct net_device *dev,
return err;
}
EXPORT_SYMBOL(call_netdev_switch_notifiers);
+
+/**
+ * netdev_switch_port_bridge_setlink - Notify switch device port of bridge
+ * port attributes
+ *
+ * @dev: port device
+ * @nlh: netlink msg with bridge port attributes
+ * @flags: bridge setlink flags
+ *
+ * Notify switch device port of bridge port attributes
+ */
+int netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
+ return 0;
+
+ if (!ops->ndo_bridge_setlink)
+ return -EOPNOTSUPP;
+
+ return ops->ndo_bridge_setlink(dev, nlh, flags);
+}
+EXPORT_SYMBOL(netdev_switch_port_bridge_setlink);
+
+/**
+ * netdev_switch_port_bridge_dellink - Notify switch device port of bridge
+ * port attribute delete
+ *
+ * @dev: port device
+ * @nlh: netlink msg with bridge port attributes
+ * @flags: bridge setlink flags
+ *
+ * Notify switch device port of bridge port attribute delete
+ */
+int netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
+ return 0;
+
+ if (!ops->ndo_bridge_dellink)
+ return -EOPNOTSUPP;
+
+ return ops->ndo_bridge_dellink(dev, nlh, flags);
+}
+EXPORT_SYMBOL(netdev_switch_port_bridge_dellink);
+
+/**
+ * ndo_dflt_netdev_switch_port_bridge_setlink - default ndo bridge setlink
+ * op for master devices
+ *
+ * @dev: port device
+ * @nlh: netlink msg with bridge port attributes
+ * @flags: bridge setlink flags
+ *
+ * Notify master device slaves of bridge port attributes
+ */
+int ndo_dflt_netdev_switch_port_bridge_setlink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags)
+{
+ struct net_device *lower_dev;
+ struct list_head *iter;
+ int ret = 0, err = 0;
+
+ if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
+ return ret;
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter) {
+ err = netdev_switch_port_bridge_setlink(lower_dev, nlh, flags);
+ if (err && err != -EOPNOTSUPP)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_setlink);
+
+/**
+ * ndo_dflt_netdev_switch_port_bridge_dellink - default ndo bridge dellink
+ * op for master devices
+ *
+ * @dev: port device
+ * @nlh: netlink msg with bridge port attributes
+ * @flags: bridge dellink flags
+ *
+ * Notify master device slaves of bridge port attribute deletes
+ */
+int ndo_dflt_netdev_switch_port_bridge_dellink(struct net_device *dev,
+ struct nlmsghdr *nlh, u16 flags)
+{
+ struct net_device *lower_dev;
+ struct list_head *iter;
+ int ret = 0, err = 0;
+
+ if (!(dev->features & NETIF_F_HW_SWITCH_OFFLOAD))
+ return ret;
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter) {
+ err = netdev_switch_port_bridge_dellink(lower_dev, nlh, flags);
+ if (err && err != -EOPNOTSUPP)
+ ret = err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(ndo_dflt_netdev_switch_port_bridge_dellink);
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next v4 4/7] bridge: offload bridge port attributes to switch asic if feature flag set
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch adds support to set/del bridge port attributes in hardware from
the bridge driver.
With this, when the user sends a bridge setlink message with no flags or
master flags set,
- the bridge driver ndo_bridge_setlink handler sets settings in the kernel
- calls the swicthdev api to propagate the attrs to the switchdev
hardware
You can still use the self flag to go to the switch hw or switch port
driver directly.
With this, it also makes sure a notification goes out only after the
attributes are set both in the kernel and hw.
The patch calls switchdev api only if BRIDGE_FLAGS_SELF is not set.
This is because the offload cases with BRIDGE_FLAGS_SELF are handled in
the caller (in rtnetlink.c).
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
net/bridge/br_netlink.c | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 5b38221..a180706 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -16,6 +16,7 @@
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
+#include <net/switchdev.h>
#include <uapi/linux/if_bridge.h>
#include "br_private.h"
@@ -500,7 +501,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
struct nlattr *afspec;
struct net_bridge_port *p;
struct nlattr *tb[IFLA_BRPORT_MAX + 1];
- int err = 0;
+ int err = 0, ret_offload = 0;
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
@@ -542,9 +543,18 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
afspec, RTM_SETLINK);
}
+ if (!(flags & BRIDGE_FLAGS_SELF)) {
+ /* set bridge attributes in hardware if supported
+ */
+ ret_offload = netdev_switch_port_bridge_setlink(dev, nlh,
+ flags);
+ if (ret_offload && ret_offload != -EOPNOTSUPP)
+ br_warn(p->br, "error setting attrs on port %u(%s)\n",
+ (unsigned int)p->port_no, p->dev->name);
+ }
+
if (err == 0)
br_ifinfo_notify(RTM_NEWLINK, p);
-
out:
return err;
}
@@ -554,7 +564,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
{
struct nlattr *afspec;
struct net_bridge_port *p;
- int err;
+ int err = 0, ret_offload = 0;
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
if (!afspec)
@@ -573,6 +583,16 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
*/
br_ifinfo_notify(RTM_NEWLINK, p);
+ if (!(flags & BRIDGE_FLAGS_SELF)) {
+ /* del bridge attributes in hardware
+ */
+ ret_offload = netdev_switch_port_bridge_dellink(dev, nlh,
+ flags);
+ if (ret_offload && ret_offload != -EOPNOTSUPP)
+ br_warn(p->br, "error deleting attrs on port %u (%s)\n",
+ (unsigned int)p->port_no, p->dev->name);
+ }
+
return err;
}
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
--
1.7.10.4
^ permalink raw reply related
* [PATCH net-next v4 5/7] rocker: set feature NETIF_F_HW_SWITCH_OFFLOAD
From: roopa @ 2015-01-30 6:40 UTC (permalink / raw)
To: jiri, sfeldma, jhs, bcrl, tgraf, john.fastabend, stephen,
vyasevic, ronen.arad
Cc: netdev, davem, shm, gospo
From: Roopa Prabhu <roopa@cumulusnetworks.com>
This patch sets the NETIF_F_HW_SWITCH_OFFLOAD feature flag on rocker ports
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
---
drivers/net/ethernet/rocker/rocker.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
index f0d607c..3c17ef2 100644
--- a/drivers/net/ethernet/rocker/rocker.c
+++ b/drivers/net/ethernet/rocker/rocker.c
@@ -4030,7 +4030,8 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number)
NAPI_POLL_WEIGHT);
rocker_carrier_init(rocker_port);
- dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER |
+ NETIF_F_HW_SWITCH_OFFLOAD;
err = register_netdev(dev);
if (err) {
--
1.7.10.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox