* Re: [net-next PATCH V6 0/2] qdisc: bulk dequeue support
From: David Miller @ 2014-10-03 22:30 UTC (permalink / raw)
To: eric.dumazet
Cc: brouer, netdev, therbert, hannes, fw, dborkman, jhs,
alexander.duyck, john.r.fastabend, dave.taht, toke
In-Reply-To: <1412374535.17245.7.camel@edumazet-glaptop2.roam.corp.google.com>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 03 Oct 2014 15:15:35 -0700
> On Fri, 2014-10-03 at 14:57 -0700, Eric Dumazet wrote:
>> On Fri, 2014-10-03 at 14:56 -0700, David Miller wrote:
>>
>> > I completely agree, and I sort of intended this to happen when
>> > I split all the code into that new function.
>> >
>> > GSO segmentation of TX checksuming should not prevent other
>> > > cpus from queueing other skbs in the qdisc.
>> > >
>> > > I will spend some time on this.
>> >
>> > Thanks!
>>
>> I just did my first reboot, will make sure everything is working well
>> before sending the patch ;)
>
> This is awesome...
>
> 40Gb rate, with TSO=on or TSO=off, it does not matter anymore.
Kick ass!
^ permalink raw reply
* Re: [PATCH] net: ethernet: Remove superfluous ether_setup after alloc_etherdev
From: David Miller @ 2014-10-03 22:32 UTC (permalink / raw)
To: tklauser
Cc: netdev, maxime.ripard, vbridgers2013, nicolas.ferre, mcuos.com,
nico, steve.glendinning, peppe.cavallaro
In-Reply-To: <1412237730-5811-1-git-send-email-tklauser@distanz.ch>
From: Tobias Klauser <tklauser@distanz.ch>
Date: Thu, 2 Oct 2014 10:15:30 +0200
> There is no need to call ether_setup after alloc_ethdev since it was
> already called there.
>
> Follow commits c706471b2601 ("net: axienet: remove unnecessary
> ether_setup after alloc_etherdev") and 3c87dcbfb36c ("net: ll_temac:
> Remove unnecessary ether_setup after alloc_etherdev") and fix the
> pattern in all remaining ethernet drivers.
>
> Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Applied, thanks.
^ permalink raw reply
* [PATCH net-next] qdisc: validate skb without holding lock
From: Eric Dumazet @ 2014-10-03 22:31 UTC (permalink / raw)
To: David Miller
Cc: brouer, netdev, therbert, hannes, fw, dborkman, jhs,
alexander.duyck, john.r.fastabend, dave.taht, toke
In-Reply-To: <1412373477.17245.5.camel@edumazet-glaptop2.roam.corp.google.com>
From: Eric Dumazet <edumazet@google.com>
Validation of skb can be pretty expensive :
GSO segmentation and/or checksum computations.
We can do this without holding qdisc lock, so that other cpus
can queue additional packets.
Trick is that requeued packets were already validated, so we carry
a boolean so that sch_direct_xmit() can validate a fresh skb list,
or directly use an old one.
Tested on 40Gb NIC (8 TX queues) and 200 concurrent flows, 48 threads
host.
Turning TSO on or off had no effect on throughput, only few more cpu
cycles. Lock contention on qdisc lock disappeared.
Same if disabling TX checksum offload.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/linux/netdevice.h | 2 -
include/net/pkt_sched.h | 2 -
net/core/dev.c | 29 +++++++++++++++--
net/sched/sch_generic.c | 61 ++++++++++++++++--------------------
4 files changed, 56 insertions(+), 38 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9b7fbacb6296..910fb17ad148 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2821,7 +2821,7 @@ int dev_set_mac_address(struct net_device *, struct sockaddr *);
int dev_change_carrier(struct net_device *, bool new_carrier);
int dev_get_phys_port_id(struct net_device *dev,
struct netdev_phys_port_id *ppid);
-struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev);
+struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret);
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index 8bbe626e9ece..e4b3c828c1c2 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -99,7 +99,7 @@ void qdisc_put_stab(struct qdisc_size_table *tab);
void qdisc_warn_nonwc(const char *txt, struct Qdisc *qdisc);
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
struct net_device *dev, struct netdev_queue *txq,
- spinlock_t *root_lock);
+ spinlock_t *root_lock, bool validate);
void __qdisc_run(struct Qdisc *q);
diff --git a/net/core/dev.c b/net/core/dev.c
index e55c546717d4..1a90530f83ff 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2655,7 +2655,7 @@ struct sk_buff *validate_xmit_vlan(struct sk_buff *skb, netdev_features_t featur
return skb;
}
-struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)
+static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device *dev)
{
netdev_features_t features;
@@ -2720,6 +2720,30 @@ out_null:
return NULL;
}
+struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sk_buff *next, *head = NULL, *tail;
+
+ while (skb) {
+ next = skb->next;
+ skb->next = NULL;
+ skb = validate_xmit_skb(skb, dev);
+ if (skb) {
+ struct sk_buff *end = skb;
+
+ while (end->next)
+ end = end->next;
+ if (!head)
+ head = skb;
+ else
+ tail->next = skb;
+ tail = end;
+ }
+ skb = next;
+ }
+ return head;
+}
+
static void qdisc_pkt_len_init(struct sk_buff *skb)
{
const struct skb_shared_info *shinfo = skb_shinfo(skb);
@@ -2786,8 +2810,7 @@ static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
qdisc_bstats_update(q, skb);
- skb = validate_xmit_skb(skb, dev);
- if (skb && sch_direct_xmit(skb, q, dev, txq, root_lock)) {
+ if (sch_direct_xmit(skb, q, dev, txq, root_lock, true)) {
if (unlikely(contended)) {
spin_unlock(&q->busylock);
contended = false;
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 797ebef73642..2b349a4de3c8 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -56,40 +56,34 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
return 0;
}
-static struct sk_buff *try_bulk_dequeue_skb(struct Qdisc *q,
- struct sk_buff *head_skb,
- int bytelimit)
+static void try_bulk_dequeue_skb(struct Qdisc *q,
+ struct sk_buff *skb,
+ const struct netdev_queue *txq)
{
- struct sk_buff *skb, *tail_skb = head_skb;
+ int bytelimit = qdisc_avail_bulklimit(txq) - skb->len;
while (bytelimit > 0) {
- skb = q->dequeue(q);
- if (!skb)
- break;
+ struct sk_buff *nskb = q->dequeue(q);
- bytelimit -= skb->len; /* covers GSO len */
- skb = validate_xmit_skb(skb, qdisc_dev(q));
- if (!skb)
+ if (!nskb)
break;
- while (tail_skb->next) /* GSO list goto tail */
- tail_skb = tail_skb->next;
-
- tail_skb->next = skb;
- tail_skb = skb;
+ bytelimit -= nskb->len; /* covers GSO len */
+ skb->next = nskb;
+ skb = nskb;
}
-
- return head_skb;
+ skb->next = NULL;
}
/* Note that dequeue_skb can possibly return a SKB list (via skb->next).
* A requeued skb (via q->gso_skb) can also be a SKB list.
*/
-static inline struct sk_buff *dequeue_skb(struct Qdisc *q)
+static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate)
{
struct sk_buff *skb = q->gso_skb;
const struct netdev_queue *txq = q->dev_queue;
+ *validate = true;
if (unlikely(skb)) {
/* check the reason of requeuing without tx lock first */
txq = skb_get_tx_queue(txq->dev, skb);
@@ -98,21 +92,16 @@ static inline struct sk_buff *dequeue_skb(struct Qdisc *q)
q->q.qlen--;
} else
skb = NULL;
+ /* skb in gso_skb were already validated */
+ *validate = false;
} else {
if (!(q->flags & TCQ_F_ONETXQUEUE) ||
!netif_xmit_frozen_or_stopped(txq)) {
- int bytelimit = qdisc_avail_bulklimit(txq);
-
skb = q->dequeue(q);
- if (skb) {
- bytelimit -= skb->len;
- skb = validate_xmit_skb(skb, qdisc_dev(q));
- }
if (skb && qdisc_may_bulk(q))
- skb = try_bulk_dequeue_skb(q, skb, bytelimit);
+ try_bulk_dequeue_skb(q, skb, txq);
}
}
-
return skb;
}
@@ -156,19 +145,24 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb,
*/
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
struct net_device *dev, struct netdev_queue *txq,
- spinlock_t *root_lock)
+ spinlock_t *root_lock, bool validate)
{
int ret = NETDEV_TX_BUSY;
/* And release qdisc */
spin_unlock(root_lock);
- HARD_TX_LOCK(dev, txq, smp_processor_id());
- if (!netif_xmit_frozen_or_stopped(txq))
- skb = dev_hard_start_xmit(skb, dev, txq, &ret);
+ /* Note that we validate skb (GSO, checksum, ...) outside of locks */
+ if (validate)
+ skb = validate_xmit_skb_list(skb, dev);
- HARD_TX_UNLOCK(dev, txq);
+ if (skb) {
+ HARD_TX_LOCK(dev, txq, smp_processor_id());
+ if (!netif_xmit_frozen_or_stopped(txq))
+ skb = dev_hard_start_xmit(skb, dev, txq, &ret);
+ HARD_TX_UNLOCK(dev, txq);
+ }
spin_lock(root_lock);
if (dev_xmit_complete(ret)) {
@@ -217,9 +211,10 @@ static inline int qdisc_restart(struct Qdisc *q)
struct net_device *dev;
spinlock_t *root_lock;
struct sk_buff *skb;
+ bool validate;
/* Dequeue packet */
- skb = dequeue_skb(q);
+ skb = dequeue_skb(q, &validate);
if (unlikely(!skb))
return 0;
@@ -229,7 +224,7 @@ static inline int qdisc_restart(struct Qdisc *q)
dev = qdisc_dev(q);
txq = skb_get_tx_queue(dev, skb);
- return sch_direct_xmit(skb, q, dev, txq, root_lock);
+ return sch_direct_xmit(skb, q, dev, txq, root_lock, validate);
}
void __qdisc_run(struct Qdisc *q)
^ permalink raw reply related
* Re: [PATCH net-next] qdisc: validate skb without holding lock
From: David Miller @ 2014-10-03 22:36 UTC (permalink / raw)
To: eric.dumazet
Cc: brouer, netdev, therbert, hannes, fw, dborkman, jhs,
alexander.duyck, john.r.fastabend, dave.taht, toke
In-Reply-To: <1412375467.17245.16.camel@edumazet-glaptop2.roam.corp.google.com>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 03 Oct 2014 15:31:07 -0700
> From: Eric Dumazet <edumazet@google.com>
>
> Validation of skb can be pretty expensive :
>
> GSO segmentation and/or checksum computations.
>
> We can do this without holding qdisc lock, so that other cpus
> can queue additional packets.
>
> Trick is that requeued packets were already validated, so we carry
> a boolean so that sch_direct_xmit() can validate a fresh skb list,
> or directly use an old one.
>
> Tested on 40Gb NIC (8 TX queues) and 200 concurrent flows, 48 threads
> host.
>
> Turning TSO on or off had no effect on throughput, only few more cpu
> cycles. Lock contention on qdisc lock disappeared.
>
> Same if disabling TX checksum offload.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Applied, thanks Eric!
^ permalink raw reply
* Re: [PATCH] Don't reset PHY on if_up for ASIX 88772
From: David Miller @ 2014-10-03 22:39 UTC (permalink / raw)
To: m.stam; +Cc: netdev, freddy, grundler
In-Reply-To: <1412238122-1914-1-git-send-email-m.stam@fugro.nl>
From: Michel Stam <m.stam@fugro.nl>
Date: Thu, 2 Oct 2014 10:22:02 +0200
> I've noticed every time the interface is set to 'up,', the kernel
> reports that the link speed is set to 100 Mbps/Full Duplex, even
> when ethtool is used to set autonegotiation to 'off', half
> duplex, 10 Mbps.
> It can be tested by:
> ifconfig eth0 down
> ethtool -s eth0 autoneg off speed 10 duplex half
> ifconfig eth0 up
>
> Then checking 'dmesg' for the link speed.
>
> Signed-off-by: Michel Stam <m.stam@fugro.nl>
Applied, thank you.
^ permalink raw reply
* Re: [PATCH v1 2/2] net: sched: replace ematch calls to use struct net
From: Cong Wang @ 2014-10-03 22:40 UTC (permalink / raw)
To: John Fastabend
Cc: Cong Wang, David Miller, netdev, Jamal Hadi Salim, Eric Dumazet
In-Reply-To: <20141003054624.20925.28150.stgit@nitbit.x32>
On Thu, Oct 2, 2014 at 10:46 PM, John Fastabend
<john.fastabend@gmail.com> wrote:
> diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
> index 81ddfa6..f37e4fb 100644
> --- a/net/sched/cls_basic.c
> +++ b/net/sched/cls_basic.c
> @@ -32,7 +32,7 @@ struct basic_filter {
> struct tcf_exts exts;
> struct tcf_ematch_tree ematches;
> struct tcf_result res;
> - struct tcf_proto *tp;
> + struct net *net;
> struct list_head link;
> struct rcu_head rcu;
> };
I guess storing this net pointer to struct tcf_ematch_tree is better,
since it is only used by ematch?
^ permalink raw reply
* Re: [PATCH net v2] r8152: autoresume before setting MAC address
From: David Miller @ 2014-10-03 22:40 UTC (permalink / raw)
To: hayeswang-Rasf1IRRPZFBDgjK7y7TUQ
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, nic_swsd-Rasf1IRRPZFBDgjK7y7TUQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1394712342-15778-56-Taiwan-albertk-Rasf1IRRPZFBDgjK7y7TUQ@public.gmane.org>
From: Hayes Wang <hayeswang-Rasf1IRRPZFBDgjK7y7TUQ@public.gmane.org>
Date: Thu, 2 Oct 2014 17:03:12 +0800
> Resume the device before setting the MAC address.
>
> Signed-off-by: Hayes Wang <hayeswang-Rasf1IRRPZFBDgjK7y7TUQ@public.gmane.org>
Applied, thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH V2 net-next 0/5] mlx5 update for 3.18
From: David Miller @ 2014-10-03 22:42 UTC (permalink / raw)
To: eli; +Cc: netdev, ogerlitz, yevgenyp
In-Reply-To: <1412241586-10108-1-git-send-email-eli@mellanox.com>
From: Eli Cohen <eli@mellanox.com>
Date: Thu, 2 Oct 2014 12:19:41 +0300
> This series integrates a new mechanism for populating and extracting field values
> used in the driver/firmware interaction around command mailboxes.
>
> Changes from V1:
> - Remove unused definition of memcpy_cpu_to_be32()
> - Remove definitions of non_existent_*() and use BUILD_BUG_ON() instead.
> - Added a patch one line patch to add support for ConnectX-4 devices.
>
> Changes from V0:
> - trimmed the auto-generated file to a minimum, as required by the reviewers.
Series applied, thanks Eli.
^ permalink raw reply
* [net-next v2 0/6] Add Geneve tunnel protocol support
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou
This patch series adds kernel support for Geneve (Generic Network
Virtualization Encapsulation) based on Geneve IETF draft:
http://www.ietf.org/id/draft-gross-geneve-01.txt
Patch 1 implements Geneve tunneling protocol driver
Patch 2-6 adds openvswitch support for creating and using
Geneve tunnels by OVS user space.
---
v1->v2: Style fixes: use tab instead space for Kconfig
Patch 2-6 are reviewed by Pravin Shetty, add him to acked-by
Patch 6 was reviewed by Thomas Graf when commiting
to openvswitch.org, add him to acked-by.
Andy Zhou (1):
net: Add Geneve tunneling protocol driver
Jesse Gross (5):
openvswitch: Eliminate memset() from flow_extract.
openvswitch: Add support for matching on OAM packets.
openvswitch: Wrap struct ovs_key_ipv4_tunnel in a new structure.
openvswitch: Factor out allocation and verification of actions.
openvswitch: Add support for Geneve tunneling.
include/net/geneve.h | 91 ++++++++++
include/net/ip_tunnels.h | 19 +-
include/uapi/linux/openvswitch.h | 5 +-
net/ipv4/Kconfig | 14 ++
net/ipv4/Makefile | 1 +
net/ipv4/geneve.c | 373 ++++++++++++++++++++++++++++++++++++++
net/openvswitch/Kconfig | 11 ++
net/openvswitch/Makefile | 4 +
net/openvswitch/actions.c | 5 +-
net/openvswitch/datapath.c | 44 +++--
net/openvswitch/datapath.h | 2 +-
net/openvswitch/flow.c | 76 ++++++--
net/openvswitch/flow.h | 48 +++--
net/openvswitch/flow_netlink.c | 227 +++++++++++++++++++----
net/openvswitch/vport-geneve.c | 236 ++++++++++++++++++++++++
net/openvswitch/vport-gre.c | 16 +-
net/openvswitch/vport-vxlan.c | 10 +-
net/openvswitch/vport.c | 9 +-
net/openvswitch/vport.h | 3 +-
19 files changed, 1093 insertions(+), 101 deletions(-)
create mode 100644 include/net/geneve.h
create mode 100644 net/ipv4/geneve.c
create mode 100644 net/openvswitch/vport-geneve.c
--
1.7.9.5
*** BLURB HERE ***
Andy Zhou (1):
net: Add Geneve tunneling protocol driver
Jesse Gross (5):
openvswitch: Eliminate memset() from flow_extract.
openvswitch: Add support for matching on OAM packets.
openvswitch: Wrap struct ovs_key_ipv4_tunnel in a new structure.
openvswitch: Factor out allocation and verification of actions.
openvswitch: Add support for Geneve tunneling.
include/net/geneve.h | 91 ++++++++++
include/net/ip_tunnels.h | 19 +-
include/uapi/linux/openvswitch.h | 5 +-
net/ipv4/Kconfig | 14 ++
net/ipv4/Makefile | 1 +
net/ipv4/geneve.c | 373 ++++++++++++++++++++++++++++++++++++++
net/openvswitch/Kconfig | 11 ++
net/openvswitch/Makefile | 4 +
net/openvswitch/actions.c | 5 +-
net/openvswitch/datapath.c | 44 +++--
net/openvswitch/datapath.h | 2 +-
net/openvswitch/flow.c | 76 ++++++--
net/openvswitch/flow.h | 48 +++--
net/openvswitch/flow_netlink.c | 227 +++++++++++++++++++----
net/openvswitch/vport-geneve.c | 236 ++++++++++++++++++++++++
net/openvswitch/vport-gre.c | 16 +-
net/openvswitch/vport-vxlan.c | 10 +-
net/openvswitch/vport.c | 9 +-
net/openvswitch/vport.h | 3 +-
19 files changed, 1093 insertions(+), 101 deletions(-)
create mode 100644 include/net/geneve.h
create mode 100644 net/ipv4/geneve.c
create mode 100644 net/openvswitch/vport-geneve.c
--
1.7.9.5
^ permalink raw reply
* [net-next v2 1/6] net: Add Geneve tunneling protocol driver
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Andy Zhou, Jesse Gross
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
This adds a device level support for Geneve -- Generic Network
Virtualization Encapsulation. The protocol is documented at
http://tools.ietf.org/html/draft-gross-geneve-01
Only protocol layer Geneve support is provided by this driver.
Openvswitch can be used for configuring, set up and tear down
functional Geneve tunnels.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
---
include/net/geneve.h | 91 +++++++++++
include/net/ip_tunnels.h | 2 +
net/ipv4/Kconfig | 14 ++
net/ipv4/Makefile | 1 +
net/ipv4/geneve.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 481 insertions(+)
create mode 100644 include/net/geneve.h
create mode 100644 net/ipv4/geneve.c
diff --git a/include/net/geneve.h b/include/net/geneve.h
new file mode 100644
index 0000000..ce98865
--- /dev/null
+++ b/include/net/geneve.h
@@ -0,0 +1,91 @@
+#ifndef __NET_GENEVE_H
+#define __NET_GENEVE_H 1
+
+#include <net/udp_tunnel.h>
+
+struct geneve_sock;
+
+typedef void (geneve_rcv_t)(struct geneve_sock *gs, struct sk_buff *skb);
+
+struct geneve_sock {
+ struct hlist_node hlist;
+ geneve_rcv_t *rcv;
+ void *rcv_data;
+ struct work_struct del_work;
+ struct socket *sock;
+ struct rcu_head rcu;
+ atomic_t refcnt;
+ struct udp_offload udp_offloads;
+};
+
+/* Geneve Header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Virtual Network Identifier (VNI) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Variable Length Options |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Option Header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Class | Type |R|R|R| Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Variable Option Data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct geneve_opt {
+ __be16 opt_class;
+ u8 type;
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u8 length:5;
+ u8 r3:1;
+ u8 r2:1;
+ u8 r1:1;
+#else
+ u8 r1:1;
+ u8 r2:1;
+ u8 r3:1;
+ u8 length:5;
+#endif
+ u8 opt_data[];
+};
+
+#define GENEVE_CRIT_OPT_TYPE (1 << 7)
+
+struct genevehdr {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u8 opt_len:6;
+ u8 ver:2;
+ u8 rsvd1:6;
+ u8 critical:1;
+ u8 oam:1;
+#else
+ u8 ver:2;
+ u8 opt_len:6;
+ u8 oam:1;
+ u8 critical:1;
+ u8 rsvd1:6;
+#endif
+ __be16 proto_type;
+ u8 vni[3];
+ u8 rsvd2;
+ struct geneve_opt options[];
+};
+
+#define GENEVE_VER 0
+#define GENEVE_BASE_HLEN (sizeof(struct udphdr) + sizeof(struct genevehdr))
+
+struct geneve_sock *geneve_sock_add(struct net *net, __be16 port,
+ geneve_rcv_t *rcv, void *data,
+ bool no_share, bool ipv6);
+
+void geneve_sock_release(struct geneve_sock *vs);
+
+int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos,
+ __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port,
+ __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+ bool xnet);
+#endif
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 7f538ba..a9ce155 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -95,6 +95,8 @@ struct ip_tunnel {
#define TUNNEL_VERSION __cpu_to_be16(0x40)
#define TUNNEL_NO_KEY __cpu_to_be16(0x80)
#define TUNNEL_DONT_FRAGMENT __cpu_to_be16(0x0100)
+#define TUNNEL_OAM __cpu_to_be16(0x0200)
+#define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400)
struct tnl_ptk_info {
__be16 flags;
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 69fb378..c203544 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -453,6 +453,20 @@ config TCP_CONG_BIC
increase provides TCP friendliness.
See http://www.csc.ncsu.edu/faculty/rhee/export/bitcp/
+config GENEVE
+ tristate "Generic Network Virtualization Encapsulation (Geneve)"
+ depends on INET
+ select NET_IP_TUNNEL
+ select NET_UDP_TUNNEL
+ ---help---
+ This allows one to create Geneve virtual interfaces that provide
+ Layer 2 Networks over Layer 3 Networks. Geneve is often used
+ to tunnel virtual network infrastructure in virtualized environments.
+ For more information see:
+ http://tools.ietf.org/html/draft-gross-geneve-01
+
+ To compile this driver as a module, choose M here: the module
+
config TCP_CONG_CUBIC
tristate "CUBIC TCP"
default y
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index d810578..518c04e 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
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_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o xfrm4_protocol.o
diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c
new file mode 100644
index 0000000..f008c55
--- /dev/null
+++ b/net/ipv4/geneve.c
@@ -0,0 +1,373 @@
+/*
+ * Geneve: Generic Network Virtualization Encapsulation
+ *
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/rculist.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/igmp.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/hash.h>
+#include <linux/ethtool.h>
+#include <net/arp.h>
+#include <net/ndisc.h>
+#include <net/ip.h>
+#include <net/ip_tunnels.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/rtnetlink.h>
+#include <net/route.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/geneve.h>
+#include <net/protocol.h>
+#include <net/udp_tunnel.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#include <net/ip6_tunnel.h>
+#include <net/ip6_checksum.h>
+#endif
+
+#define PORT_HASH_BITS 8
+#define PORT_HASH_SIZE (1<<PORT_HASH_BITS)
+
+/* per-network namespace private data for this module */
+struct geneve_net {
+ struct hlist_head sock_list[PORT_HASH_SIZE];
+ spinlock_t sock_lock; /* Protects sock_list */
+};
+
+static int geneve_net_id;
+
+static struct workqueue_struct *geneve_wq;
+
+static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
+{
+ return (struct genevehdr *)(udp_hdr(skb) + 1);
+}
+
+static struct hlist_head *gs_head(struct net *net, __be16 port)
+{
+ struct geneve_net *gn = net_generic(net, geneve_net_id);
+
+ return &gn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];
+}
+
+/* Find geneve socket based on network namespace and UDP port */
+static struct geneve_sock *geneve_find_sock(struct net *net, __be16 port)
+{
+ struct geneve_sock *gs;
+
+ hlist_for_each_entry_rcu(gs, gs_head(net, port), hlist) {
+ if (inet_sk(gs->sock->sk)->inet_sport == port)
+ return gs;
+ }
+
+ return NULL;
+}
+
+static void geneve_build_header(struct genevehdr *geneveh,
+ __be16 tun_flags, u8 vni[3],
+ u8 options_len, u8 *options)
+{
+ geneveh->ver = GENEVE_VER;
+ geneveh->opt_len = options_len / 4;
+ geneveh->oam = !!(tun_flags & TUNNEL_OAM);
+ geneveh->critical = !!(tun_flags & TUNNEL_CRIT_OPT);
+ geneveh->rsvd1 = 0;
+ memcpy(geneveh->vni, vni, 3);
+ geneveh->proto_type = htons(ETH_P_TEB);
+ geneveh->rsvd2 = 0;
+
+ memcpy(geneveh->options, options, options_len);
+}
+
+/* Transmit a fully formated Geneve frame.
+ *
+ * When calling this function. The skb->data should point
+ * to the geneve header which is fully formed.
+ *
+ * This function will add other UDP tunnel headers.
+ */
+int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
+ struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos,
+ __u8 ttl, __be16 df, __be16 src_port, __be16 dst_port,
+ __be16 tun_flags, u8 vni[3], u8 opt_len, u8 *opt,
+ bool xnet)
+{
+ struct genevehdr *gnvh;
+ int min_headroom;
+ int err;
+
+ skb = udp_tunnel_handle_offloads(skb, !gs->sock->sk->sk_no_check_tx);
+
+ min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len
+ + GENEVE_BASE_HLEN + opt_len + sizeof(struct iphdr)
+ + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0);
+
+ err = skb_cow_head(skb, min_headroom);
+ if (unlikely(err))
+ return err;
+
+ if (vlan_tx_tag_present(skb)) {
+ if (unlikely(!__vlan_put_tag(skb,
+ skb->vlan_proto,
+ vlan_tx_tag_get(skb)))) {
+ err = -ENOMEM;
+ return err;
+ }
+ skb->vlan_tci = 0;
+ }
+
+ gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
+ geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+
+ return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst,
+ tos, ttl, df, src_port, dst_port, xnet);
+}
+EXPORT_SYMBOL_GPL(geneve_xmit_skb);
+
+static void geneve_notify_add_rx_port(struct geneve_sock *gs)
+{
+ struct sock *sk = gs->sock->sk;
+ sa_family_t sa_family = sk->sk_family;
+ int err;
+
+ if (sa_family == AF_INET) {
+ err = udp_add_offload(&gs->udp_offloads);
+ if (err)
+ pr_warn("geneve: udp_add_offload failed with status %d\n",
+ err);
+ }
+}
+
+/* Callback from net/ipv4/udp.c to receive packets */
+static int geneve_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct genevehdr *geneveh;
+ struct geneve_sock *gs;
+ int opts_len;
+
+ /* Need Geneve and inner Ethernet header to be present */
+ if (unlikely(!pskb_may_pull(skb, GENEVE_BASE_HLEN)))
+ goto error;
+
+ /* Return packets with reserved bits set */
+ geneveh = geneve_hdr(skb);
+
+ if (unlikely(geneveh->ver != GENEVE_VER))
+ goto error;
+
+ if (unlikely(geneveh->proto_type != htons(ETH_P_TEB)))
+ goto error;
+
+ opts_len = geneveh->opt_len * 4;
+ if (iptunnel_pull_header(skb, GENEVE_BASE_HLEN + opts_len,
+ htons(ETH_P_TEB)))
+ goto drop;
+
+ gs = rcu_dereference_sk_user_data(sk);
+ if (!gs)
+ goto drop;
+
+ gs->rcv(gs, skb);
+ return 0;
+
+drop:
+ /* Consume bad packet */
+ kfree_skb(skb);
+ return 0;
+
+error:
+ /* Let the UDP layer deal with the skb */
+ return 1;
+}
+
+static void geneve_del_work(struct work_struct *work)
+{
+ struct geneve_sock *gs = container_of(work, struct geneve_sock,
+ del_work);
+
+ udp_tunnel_sock_release(gs->sock);
+ kfree_rcu(gs, rcu);
+}
+
+static struct socket *geneve_create_sock(struct net *net, bool ipv6,
+ __be16 port)
+{
+ struct socket *sock;
+ struct udp_port_cfg udp_conf;
+ int err;
+
+ memset(&udp_conf, 0, sizeof(udp_conf));
+
+ if (ipv6) {
+ udp_conf.family = AF_INET6;
+ } else {
+ udp_conf.family = AF_INET;
+ udp_conf.local_ip.s_addr = INADDR_ANY;
+ }
+
+ udp_conf.local_udp_port = port;
+
+ /* Open UDP socket */
+ err = udp_sock_create(net, &udp_conf, &sock);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return sock;
+}
+
+/* Create new listen socket if needed */
+static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
+ geneve_rcv_t *rcv, void *data,
+ bool ipv6)
+{
+ struct geneve_net *gn = net_generic(net, geneve_net_id);
+ struct geneve_sock *gs;
+ struct socket *sock;
+ struct udp_tunnel_sock_cfg tunnel_cfg;
+
+ gs = kzalloc(sizeof(*gs), GFP_KERNEL);
+ if (!gs)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_WORK(&gs->del_work, geneve_del_work);
+
+ sock = geneve_create_sock(net, ipv6, port);
+ if (IS_ERR(sock)) {
+ kfree(gs);
+ return ERR_CAST(sock);
+ }
+
+ gs->sock = sock;
+ atomic_set(&gs->refcnt, 1);
+ gs->rcv = rcv;
+ gs->rcv_data = data;
+
+ /* Initialize the geneve udp offloads structure */
+ gs->udp_offloads.port = port;
+ gs->udp_offloads.callbacks.gro_receive = NULL;
+ gs->udp_offloads.callbacks.gro_complete = NULL;
+
+ spin_lock(&gn->sock_lock);
+ hlist_add_head_rcu(&gs->hlist, gs_head(net, port));
+ geneve_notify_add_rx_port(gs);
+ spin_unlock(&gn->sock_lock);
+
+ /* Mark socket as an encapsulation socket */
+ tunnel_cfg.sk_user_data = gs;
+ tunnel_cfg.encap_type = 1;
+ tunnel_cfg.encap_rcv = geneve_udp_encap_recv;
+ tunnel_cfg.encap_destroy = NULL;
+ setup_udp_tunnel_sock(net, sock, &tunnel_cfg);
+
+ return gs;
+}
+
+struct geneve_sock *geneve_sock_add(struct net *net, __be16 port,
+ geneve_rcv_t *rcv, void *data,
+ bool no_share, bool ipv6)
+{
+ struct geneve_sock *gs;
+
+ gs = geneve_socket_create(net, port, rcv, data, ipv6);
+ if (!IS_ERR(gs))
+ return gs;
+
+ if (no_share) /* Return error if sharing is not allowed. */
+ return ERR_PTR(-EINVAL);
+
+ gs = geneve_find_sock(net, port);
+ if (gs) {
+ if (gs->rcv == rcv)
+ atomic_inc(&gs->refcnt);
+ else
+ gs = ERR_PTR(-EBUSY);
+ } else {
+ gs = ERR_PTR(-EINVAL);
+ }
+
+ return gs;
+}
+EXPORT_SYMBOL_GPL(geneve_sock_add);
+
+void geneve_sock_release(struct geneve_sock *gs)
+{
+ if (!atomic_dec_and_test(&gs->refcnt))
+ return;
+
+ queue_work(geneve_wq, &gs->del_work);
+}
+EXPORT_SYMBOL_GPL(geneve_sock_release);
+
+static __net_init int geneve_init_net(struct net *net)
+{
+ struct geneve_net *gn = net_generic(net, geneve_net_id);
+ unsigned int h;
+
+ spin_lock_init(&gn->sock_lock);
+
+ for (h = 0; h < PORT_HASH_SIZE; ++h)
+ INIT_HLIST_HEAD(&gn->sock_list[h]);
+
+ return 0;
+}
+
+static struct pernet_operations geneve_net_ops = {
+ .init = geneve_init_net,
+ .exit = NULL,
+ .id = &geneve_net_id,
+ .size = sizeof(struct geneve_net),
+};
+
+static int __init geneve_init_module(void)
+{
+ int rc;
+
+ geneve_wq = alloc_workqueue("geneve", 0, 0);
+ if (!geneve_wq)
+ return -ENOMEM;
+
+ rc = register_pernet_subsys(&geneve_net_ops);
+ if (rc)
+ return rc;
+
+ pr_info("Geneve driver\n");
+
+ return 0;
+}
+late_initcall(geneve_init_module);
+
+static void __exit geneve_cleanup_module(void)
+{
+ destroy_workqueue(geneve_wq);
+}
+module_exit(geneve_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jesse Gross <jesse@nicira.com>");
+MODULE_DESCRIPTION("Driver for GENEVE encapsulated traffic");
+MODULE_ALIAS_RTNL_LINK("geneve");
--
1.7.9.5
^ permalink raw reply related
* [net-next v2 3/6] openvswitch: Add support for matching on OAM packets.
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Jesse Gross, Andy Zhou
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
From: Jesse Gross <jesse@nicira.com>
Some tunnel formats have mechanisms for indicating that packets are
OAM frames that should be handled specially (either as high priority or
not forwarded beyond an endpoint). This provides support for allowing
those types of packets to be matched.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
---
include/uapi/linux/openvswitch.h | 1 +
net/openvswitch/datapath.c | 1 +
net/openvswitch/flow_netlink.c | 17 ++++++++++++-----
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index f7fc507..7c06106 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -309,6 +309,7 @@ enum ovs_tunnel_key_attr {
OVS_TUNNEL_KEY_ATTR_TTL, /* u8 Tunnel IP TTL. */
OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT, /* No argument, set DF. */
OVS_TUNNEL_KEY_ATTR_CSUM, /* No argument. CSUM packet. */
+ OVS_TUNNEL_KEY_ATTR_OAM, /* No argument. OAM frame. */
__OVS_TUNNEL_KEY_ATTR_MAX
};
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 9e3a2fa..f6bd93d 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -369,6 +369,7 @@ static size_t key_attr_size(void)
+ nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
+ nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
+ nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index f4c8daa..22c855f 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -346,6 +346,7 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
[OVS_TUNNEL_KEY_ATTR_TTL] = 1,
[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
[OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
+ [OVS_TUNNEL_KEY_ATTR_OAM] = 0,
};
if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
@@ -390,6 +391,9 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
case OVS_TUNNEL_KEY_ATTR_CSUM:
tun_flags |= TUNNEL_CSUM;
break;
+ case OVS_TUNNEL_KEY_ATTR_OAM:
+ tun_flags |= TUNNEL_OAM;
+ break;
default:
return -EINVAL;
}
@@ -431,21 +435,24 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
return -EMSGSIZE;
if (output->ipv4_src &&
- nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
+ nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src))
return -EMSGSIZE;
if (output->ipv4_dst &&
- nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
+ nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst))
return -EMSGSIZE;
if (output->ipv4_tos &&
- nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
+ nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos))
return -EMSGSIZE;
if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl))
return -EMSGSIZE;
if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) &&
- nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
+ nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT))
return -EMSGSIZE;
if ((output->tun_flags & TUNNEL_CSUM) &&
- nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
+ nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
+ return -EMSGSIZE;
+ if ((output->tun_flags & TUNNEL_OAM) &&
+ nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
return -EMSGSIZE;
nla_nest_end(skb, nla);
--
1.7.9.5
^ permalink raw reply related
* [net-next v2 2/6] openvswitch: Eliminate memset() from flow_extract.
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Jesse Gross, Andy Zhou
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
From: Jesse Gross <jesse@nicira.com>
As new protocols are added, the size of the flow key tends to
increase although few protocols care about all of the fields. In
order to optimize this for hashing and matching, OVS uses a variable
length portion of the key. However, when fields are extracted from
the packet we must still zero out the entire key.
This is no longer necessary now that OVS implements masking. Any
fields (or holes in the structure) which are not part of a given
protocol will be by definition not part of the mask and zeroed out
during lookup. Furthermore, since masking already uses variable
length keys this zeroing operation automatically benefits as well.
In principle, the only thing that needs to be done at this point
is remove the memset() at the beginning of flow. However, some
fields assume that they are initialized to zero, which now must be
done explicitly. In addition, in the event of an error we must also
zero out corresponding fields to signal that there is no valid data
present. These increase the total amount of code but very little of
it is executed in non-error situations.
Removing the memset() reduces the profile of ovs_flow_extract()
from 0.64% to 0.56% when tested with large packets on a 10G link.
Suggested-by: Pravin Shelar <pshelar@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
---
net/openvswitch/flow.c | 54 ++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 45 insertions(+), 9 deletions(-)
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 4010423..913bdc1 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -462,6 +462,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
* update skb->csum here.
*/
+ key->eth.tci = 0;
if (vlan_tx_tag_present(skb))
key->eth.tci = htons(skb->vlan_tci);
else if (eth->h_proto == htons(ETH_P_8021Q))
@@ -482,6 +483,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
error = check_iphdr(skb);
if (unlikely(error)) {
+ memset(&key->ip, 0, sizeof(key->ip));
+ memset(&key->ipv4, 0, sizeof(key->ipv4));
if (error == -EINVAL) {
skb->transport_header = skb->network_header;
error = 0;
@@ -503,8 +506,10 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
return 0;
}
if (nh->frag_off & htons(IP_MF) ||
- skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+ skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
key->ip.frag = OVS_FRAG_TYPE_FIRST;
+ else
+ key->ip.frag = OVS_FRAG_TYPE_NONE;
/* Transport layer. */
if (key->ip.proto == IPPROTO_TCP) {
@@ -513,18 +518,25 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
key->tp.src = tcp->source;
key->tp.dst = tcp->dest;
key->tp.flags = TCP_FLAGS_BE16(tcp);
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
+
} else if (key->ip.proto == IPPROTO_UDP) {
if (udphdr_ok(skb)) {
struct udphdr *udp = udp_hdr(skb);
key->tp.src = udp->source;
key->tp.dst = udp->dest;
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
} else if (key->ip.proto == IPPROTO_SCTP) {
if (sctphdr_ok(skb)) {
struct sctphdr *sctp = sctp_hdr(skb);
key->tp.src = sctp->source;
key->tp.dst = sctp->dest;
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
} else if (key->ip.proto == IPPROTO_ICMP) {
if (icmphdr_ok(skb)) {
@@ -534,33 +546,44 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
* them in 16-bit network byte order. */
key->tp.src = htons(icmp->type);
key->tp.dst = htons(icmp->code);
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
}
- } else if ((key->eth.type == htons(ETH_P_ARP) ||
- key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) {
+ } else if (key->eth.type == htons(ETH_P_ARP) ||
+ key->eth.type == htons(ETH_P_RARP)) {
struct arp_eth_header *arp;
arp = (struct arp_eth_header *)skb_network_header(skb);
- if (arp->ar_hrd == htons(ARPHRD_ETHER)
- && arp->ar_pro == htons(ETH_P_IP)
- && arp->ar_hln == ETH_ALEN
- && arp->ar_pln == 4) {
+ if (arphdr_ok(skb) &&
+ arp->ar_hrd == htons(ARPHRD_ETHER) &&
+ arp->ar_pro == htons(ETH_P_IP) &&
+ arp->ar_hln == ETH_ALEN &&
+ arp->ar_pln == 4) {
/* We only match on the lower 8 bits of the opcode. */
if (ntohs(arp->ar_op) <= 0xff)
key->ip.proto = ntohs(arp->ar_op);
+ else
+ key->ip.proto = 0;
+
memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src));
memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
ether_addr_copy(key->ipv4.arp.sha, arp->ar_sha);
ether_addr_copy(key->ipv4.arp.tha, arp->ar_tha);
+ } else {
+ memset(&key->ip, 0, sizeof(key->ip));
+ memset(&key->ipv4, 0, sizeof(key->ipv4));
}
} else if (key->eth.type == htons(ETH_P_IPV6)) {
int nh_len; /* IPv6 Header + Extensions */
nh_len = parse_ipv6hdr(skb, key);
if (unlikely(nh_len < 0)) {
+ memset(&key->ip, 0, sizeof(key->ip));
+ memset(&key->ipv6.addr, 0, sizeof(key->ipv6.addr));
if (nh_len == -EINVAL) {
skb->transport_header = skb->network_header;
error = 0;
@@ -582,24 +605,32 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
key->tp.src = tcp->source;
key->tp.dst = tcp->dest;
key->tp.flags = TCP_FLAGS_BE16(tcp);
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
} else if (key->ip.proto == NEXTHDR_UDP) {
if (udphdr_ok(skb)) {
struct udphdr *udp = udp_hdr(skb);
key->tp.src = udp->source;
key->tp.dst = udp->dest;
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
} else if (key->ip.proto == NEXTHDR_SCTP) {
if (sctphdr_ok(skb)) {
struct sctphdr *sctp = sctp_hdr(skb);
key->tp.src = sctp->source;
key->tp.dst = sctp->dest;
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
} else if (key->ip.proto == NEXTHDR_ICMP) {
if (icmp6hdr_ok(skb)) {
error = parse_icmpv6(skb, key, nh_len);
if (error)
return error;
+ } else {
+ memset(&key->tp, 0, sizeof(key->tp));
}
}
}
@@ -615,13 +646,19 @@ int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key,
struct sk_buff *skb, struct sw_flow_key *key)
{
/* Extract metadata from packet. */
- memset(key, 0, sizeof(*key));
if (tun_key)
memcpy(&key->tun_key, tun_key, sizeof(key->tun_key));
+ else
+ memset(&key->tun_key, 0, sizeof(key->tun_key));
key->phy.priority = skb->priority;
key->phy.in_port = OVS_CB(skb)->input_vport->port_no;
key->phy.skb_mark = skb->mark;
+ key->ovs_flow_hash = 0;
+ key->recirc_id = 0;
+
+ /* Flags are always used as part of stats */
+ key->tp.flags = 0;
return key_extract(skb, key);
}
@@ -632,7 +669,6 @@ int ovs_flow_key_extract_userspace(const struct nlattr *attr,
{
int err;
- memset(key, 0, sizeof(*key));
/* Extract metadata from netlink attributes. */
err = ovs_nla_get_flow_metadata(attr, key);
if (err)
--
1.7.9.5
^ permalink raw reply related
* [net-next v2 4/6] openvswitch: Wrap struct ovs_key_ipv4_tunnel in a new structure.
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Jesse Gross, Andy Zhou
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
From: Jesse Gross <jesse@nicira.com>
Currently, the flow information that is matched for tunnels and
the tunnel data passed around with packets is the same. However,
as additional information is added this is not necessarily desirable,
as in the case of pointers.
This adds a new structure for tunnel metadata which currently contains
only the existing struct. This change is purely internal to the kernel
since the current OVS_KEY_ATTR_IPV4_TUNNEL is simply a compressed version
of OVS_KEY_ATTR_TUNNEL that is translated at flow setup.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
---
include/uapi/linux/openvswitch.h | 2 +-
net/openvswitch/actions.c | 5 +++--
net/openvswitch/datapath.h | 2 +-
net/openvswitch/flow.c | 6 +++---
net/openvswitch/flow.h | 30 +++++++++++++++++-------------
net/openvswitch/flow_netlink.c | 38 +++++++++++++++++++++++++++++++-------
net/openvswitch/vport-gre.c | 16 +++++++++-------
net/openvswitch/vport-vxlan.c | 10 +++++-----
net/openvswitch/vport.c | 6 +++---
net/openvswitch/vport.h | 2 +-
10 files changed, 74 insertions(+), 43 deletions(-)
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 7c06106..6753032 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -294,7 +294,7 @@ enum ovs_key_attr {
OVS_KEY_ATTR_RECIRC_ID, /* u32 recirc id */
#ifdef __KERNEL__
- OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */
+ OVS_KEY_ATTR_TUNNEL_INFO, /* struct ovs_tunnel_info */
#endif
__OVS_KEY_ATTR_MAX
};
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 6932a42..006886d 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -590,8 +590,8 @@ static int execute_set_action(struct sk_buff *skb,
skb->mark = nla_get_u32(nested_attr);
break;
- case OVS_KEY_ATTR_IPV4_TUNNEL:
- OVS_CB(skb)->egress_tun_key = nla_data(nested_attr);
+ case OVS_KEY_ATTR_TUNNEL_INFO:
+ OVS_CB(skb)->egress_tun_info = nla_data(nested_attr);
break;
case OVS_KEY_ATTR_ETHERNET:
@@ -778,6 +778,7 @@ int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
this_cpu_inc(exec_actions_level);
+ OVS_CB(skb)->egress_tun_info = NULL;
err = do_execute_actions(dp, skb, key,
acts->actions, acts->actions_len);
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index ac3f3df..9741354 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -102,8 +102,8 @@ struct datapath {
*/
struct ovs_skb_cb {
struct sw_flow *flow;
+ struct ovs_tunnel_info *egress_tun_info;
struct vport *input_vport;
- struct ovs_key_ipv4_tunnel *egress_tun_key;
};
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 913bdc1..2924cb3 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -642,12 +642,12 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
return key_extract(skb, key);
}
-int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key,
+int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info,
struct sk_buff *skb, struct sw_flow_key *key)
{
/* Extract metadata from packet. */
- if (tun_key)
- memcpy(&key->tun_key, tun_key, sizeof(key->tun_key));
+ if (tun_info)
+ memcpy(&key->tun_key, &tun_info->tunnel, sizeof(key->tun_key));
else
memset(&key->tun_key, 0, sizeof(key->tun_key));
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 0f5db4e..fe5a71b 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -49,20 +49,24 @@ struct ovs_key_ipv4_tunnel {
u8 ipv4_ttl;
} __packed __aligned(4); /* Minimize padding. */
-static inline void ovs_flow_tun_key_init(struct ovs_key_ipv4_tunnel *tun_key,
- const struct iphdr *iph, __be64 tun_id,
- __be16 tun_flags)
+struct ovs_tunnel_info {
+ struct ovs_key_ipv4_tunnel tunnel;
+};
+
+static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
+ const struct iphdr *iph,
+ __be64 tun_id, __be16 tun_flags)
{
- tun_key->tun_id = tun_id;
- tun_key->ipv4_src = iph->saddr;
- tun_key->ipv4_dst = iph->daddr;
- tun_key->ipv4_tos = iph->tos;
- tun_key->ipv4_ttl = iph->ttl;
- tun_key->tun_flags = tun_flags;
+ tun_info->tunnel.tun_id = tun_id;
+ tun_info->tunnel.ipv4_src = iph->saddr;
+ tun_info->tunnel.ipv4_dst = iph->daddr;
+ tun_info->tunnel.ipv4_tos = iph->tos;
+ tun_info->tunnel.ipv4_ttl = iph->ttl;
+ tun_info->tunnel.tun_flags = tun_flags;
/* clear struct padding. */
- memset((unsigned char *) tun_key + OVS_TUNNEL_KEY_SIZE, 0,
- sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
+ memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0,
+ sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);
}
struct sw_flow_key {
@@ -190,8 +194,8 @@ void ovs_flow_stats_clear(struct sw_flow *);
u64 ovs_flow_used_time(unsigned long flow_jiffies);
int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key);
-int ovs_flow_key_extract(struct ovs_key_ipv4_tunnel *tun_key,
- struct sk_buff *skb, struct sw_flow_key *key);
+int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info, struct sk_buff *skb,
+ struct sw_flow_key *key);
/* Extract key from packet coming from userspace. */
int ovs_flow_key_extract_userspace(const struct nlattr *attr,
struct sk_buff *skb,
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 22c855f..5d6194d 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -1148,13 +1148,14 @@ out:
return (struct nlattr *) ((unsigned char *)(*sfa) + next_offset);
}
-static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len)
+static struct nlattr *__add_action(struct sw_flow_actions **sfa,
+ int attrtype, void *data, int len)
{
struct nlattr *a;
a = reserve_sfa_size(sfa, nla_attr_size(len));
if (IS_ERR(a))
- return PTR_ERR(a);
+ return a;
a->nla_type = attrtype;
a->nla_len = nla_attr_size(len);
@@ -1163,6 +1164,18 @@ static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, in
memcpy(nla_data(a), data, len);
memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len));
+ return a;
+}
+
+static int add_action(struct sw_flow_actions **sfa, int attrtype,
+ void *data, int len)
+{
+ struct nlattr *a;
+
+ a = __add_action(sfa, attrtype, data, len);
+ if (IS_ERR(a))
+ return PTR_ERR(a);
+
return 0;
}
@@ -1268,6 +1281,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
{
struct sw_flow_match match;
struct sw_flow_key key;
+ struct ovs_tunnel_info *tun_info;
+ struct nlattr *a;
int err, start;
ovs_match_init(&match, &key, NULL);
@@ -1279,8 +1294,14 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
if (start < 0)
return start;
- err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
- sizeof(match.key->tun_key));
+ a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
+ sizeof(*tun_info));
+ if (IS_ERR(a))
+ return PTR_ERR(a);
+
+ tun_info = nla_data(a);
+ tun_info->tunnel = key.tun_key;
+
add_nested_action_end(*sfa, start);
return err;
@@ -1563,17 +1584,20 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
int err;
switch (key_type) {
- case OVS_KEY_ATTR_IPV4_TUNNEL:
+ case OVS_KEY_ATTR_TUNNEL_INFO: {
+ struct ovs_tunnel_info *tun_info = nla_data(ovs_key);
+
start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
if (!start)
return -EMSGSIZE;
- err = ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
- nla_data(ovs_key));
+ err = ipv4_tun_to_nlattr(skb, &tun_info->tunnel,
+ nla_data(ovs_key));
if (err)
return err;
nla_nest_end(skb, start);
break;
+ }
default:
if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key))
return -EMSGSIZE;
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
index 309cca6..fe768bd 100644
--- a/net/openvswitch/vport-gre.c
+++ b/net/openvswitch/vport-gre.c
@@ -63,8 +63,10 @@ static __be16 filter_tnl_flags(__be16 flags)
static struct sk_buff *__build_header(struct sk_buff *skb,
int tunnel_hlen)
{
- const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->egress_tun_key;
struct tnl_ptk_info tpi;
+ const struct ovs_key_ipv4_tunnel *tun_key;
+
+ tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
skb = gre_handle_offloads(skb, !!(tun_key->tun_flags & TUNNEL_CSUM));
if (IS_ERR(skb))
@@ -92,7 +94,7 @@ static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
static int gre_rcv(struct sk_buff *skb,
const struct tnl_ptk_info *tpi)
{
- struct ovs_key_ipv4_tunnel tun_key;
+ struct ovs_tunnel_info tun_info;
struct ovs_net *ovs_net;
struct vport *vport;
__be64 key;
@@ -103,10 +105,10 @@ static int gre_rcv(struct sk_buff *skb,
return PACKET_REJECT;
key = key_to_tunnel_id(tpi->key, tpi->seq);
- ovs_flow_tun_key_init(&tun_key, ip_hdr(skb), key,
- filter_tnl_flags(tpi->flags));
+ ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key,
+ filter_tnl_flags(tpi->flags));
- ovs_vport_receive(vport, skb, &tun_key);
+ ovs_vport_receive(vport, skb, &tun_info);
return PACKET_RCVD;
}
@@ -137,12 +139,12 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
__be16 df;
int err;
- if (unlikely(!OVS_CB(skb)->egress_tun_key)) {
+ if (unlikely(!OVS_CB(skb)->egress_tun_info)) {
err = -EINVAL;
goto error;
}
- tun_key = OVS_CB(skb)->egress_tun_key;
+ tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
/* Route lookup */
memset(&fl, 0, sizeof(fl));
fl.daddr = tun_key->ipv4_dst;
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index f19539b..5fbff2c 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -58,7 +58,7 @@ static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
/* Called with rcu_read_lock and BH disabled. */
static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
{
- struct ovs_key_ipv4_tunnel tun_key;
+ struct ovs_tunnel_info tun_info;
struct vport *vport = vs->data;
struct iphdr *iph;
__be64 key;
@@ -66,9 +66,9 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
/* Save outer tunnel values */
iph = ip_hdr(skb);
key = cpu_to_be64(ntohl(vx_vni) >> 8);
- ovs_flow_tun_key_init(&tun_key, iph, key, TUNNEL_KEY);
+ ovs_flow_tun_info_init(&tun_info, iph, key, TUNNEL_KEY);
- ovs_vport_receive(vport, skb, &tun_key);
+ ovs_vport_receive(vport, skb, &tun_info);
}
static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
@@ -147,12 +147,12 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb)
__be16 df;
int err;
- if (unlikely(!OVS_CB(skb)->egress_tun_key)) {
+ if (unlikely(!OVS_CB(skb)->egress_tun_info)) {
err = -EINVAL;
goto error;
}
- tun_key = OVS_CB(skb)->egress_tun_key;
+ tun_key = &OVS_CB(skb)->egress_tun_info->tunnel;
/* Route lookup */
memset(&fl, 0, sizeof(fl));
fl.daddr = tun_key->ipv4_dst;
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 5df8377..3e50ee8 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -432,7 +432,7 @@ u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
* skb->data should point to the Ethernet header.
*/
void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
- struct ovs_key_ipv4_tunnel *tun_key)
+ struct ovs_tunnel_info *tun_info)
{
struct pcpu_sw_netstats *stats;
struct sw_flow_key key;
@@ -445,9 +445,9 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
u64_stats_update_end(&stats->syncp);
OVS_CB(skb)->input_vport = vport;
- OVS_CB(skb)->egress_tun_key = NULL;
+ OVS_CB(skb)->egress_tun_info = NULL;
/* Extract flow from 'skb' into 'key'. */
- error = ovs_flow_key_extract(tun_key, skb, &key);
+ error = ovs_flow_key_extract(tun_info, skb, &key);
if (unlikely(error)) {
kfree_skb(skb);
return;
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 0efd62f..e28964a 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -207,7 +207,7 @@ static inline struct vport *vport_from_priv(void *priv)
}
void ovs_vport_receive(struct vport *, struct sk_buff *,
- struct ovs_key_ipv4_tunnel *);
+ struct ovs_tunnel_info *);
/* List of statically compiled vport implementations. Don't forget to also
* add yours to the list at the top of vport.c. */
--
1.7.9.5
^ permalink raw reply related
* [net-next v2 5/6] openvswitch: Factor out allocation and verification of actions.
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Jesse Gross, Andy Zhou
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
From: Jesse Gross <jesse@nicira.com>
As the size of the flow key grows, it can put some pressure on the
stack. This is particularly true in ovs_flow_cmd_set(), which needs several
copies of the key on the stack. One of those uses is logically separate,
so this factors it out to reduce stack pressure and improve readibility.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
---
net/openvswitch/datapath.c | 38 +++++++++++++++++++++++++++-----------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index f6bd93d..010125c 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -933,11 +933,34 @@ error:
return error;
}
+static struct sw_flow_actions *get_flow_actions(const struct nlattr *a,
+ const struct sw_flow_key *key,
+ const struct sw_flow_mask *mask)
+{
+ struct sw_flow_actions *acts;
+ struct sw_flow_key masked_key;
+ int error;
+
+ acts = ovs_nla_alloc_flow_actions(nla_len(a));
+ if (IS_ERR(acts))
+ return acts;
+
+ ovs_flow_mask_key(&masked_key, key, mask);
+ error = ovs_nla_copy_actions(a, &masked_key, 0, &acts);
+ if (error) {
+ OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
+ kfree(acts);
+ return ERR_PTR(error);
+ }
+
+ return acts;
+}
+
static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr **a = info->attrs;
struct ovs_header *ovs_header = info->userhdr;
- struct sw_flow_key key, masked_key;
+ struct sw_flow_key key;
struct sw_flow *flow;
struct sw_flow_mask mask;
struct sk_buff *reply = NULL;
@@ -959,17 +982,10 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
/* Validate actions. */
if (a[OVS_FLOW_ATTR_ACTIONS]) {
- acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
- error = PTR_ERR(acts);
- if (IS_ERR(acts))
+ acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask);
+ if (IS_ERR(acts)) {
+ error = PTR_ERR(acts);
goto error;
-
- ovs_flow_mask_key(&masked_key, &key, &mask);
- error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
- &masked_key, 0, &acts);
- if (error) {
- OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
- goto err_kfree_acts;
}
}
--
1.7.9.5
^ permalink raw reply related
* [net-next v2 6/6] openvswitch: Add support for Geneve tunneling.
From: Andy Zhou @ 2014-10-03 22:35 UTC (permalink / raw)
To: davem; +Cc: netdev, Jesse Gross, Andy Zhou
In-Reply-To: <1412375733-30981-1-git-send-email-azhou@nicira.com>
From: Jesse Gross <jesse@nicira.com>
The Openvswitch implementation is completely agnostic to the options
that are in use and can handle newly defined options without
further work. It does this by simply matching on a byte array
of options and allowing userspace to setup flows on this array.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Singed-off-by: Ansis Atteka <aatteka@nicira.com>
Signed-off-by: Andy Zhou <azhou@nicira.com>
Acked-by: Thomas Graf <tgraf@noironetworks.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
---
include/net/ip_tunnels.h | 21 ++--
include/uapi/linux/openvswitch.h | 2 +
net/openvswitch/Kconfig | 11 ++
net/openvswitch/Makefile | 4 +
net/openvswitch/datapath.c | 5 +-
net/openvswitch/flow.c | 20 +++-
net/openvswitch/flow.h | 20 +++-
net/openvswitch/flow_netlink.c | 176 +++++++++++++++++++++++-----
net/openvswitch/vport-geneve.c | 236 ++++++++++++++++++++++++++++++++++++++
net/openvswitch/vport-gre.c | 2 +-
net/openvswitch/vport-vxlan.c | 2 +-
net/openvswitch/vport.c | 3 +
net/openvswitch/vport.h | 1 +
13 files changed, 461 insertions(+), 42 deletions(-)
create mode 100644 net/openvswitch/vport-geneve.c
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index a9ce155..5bc6ede 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -86,17 +86,18 @@ struct ip_tunnel {
struct gro_cells gro_cells;
};
-#define TUNNEL_CSUM __cpu_to_be16(0x01)
-#define TUNNEL_ROUTING __cpu_to_be16(0x02)
-#define TUNNEL_KEY __cpu_to_be16(0x04)
-#define TUNNEL_SEQ __cpu_to_be16(0x08)
-#define TUNNEL_STRICT __cpu_to_be16(0x10)
-#define TUNNEL_REC __cpu_to_be16(0x20)
-#define TUNNEL_VERSION __cpu_to_be16(0x40)
-#define TUNNEL_NO_KEY __cpu_to_be16(0x80)
+#define TUNNEL_CSUM __cpu_to_be16(0x01)
+#define TUNNEL_ROUTING __cpu_to_be16(0x02)
+#define TUNNEL_KEY __cpu_to_be16(0x04)
+#define TUNNEL_SEQ __cpu_to_be16(0x08)
+#define TUNNEL_STRICT __cpu_to_be16(0x10)
+#define TUNNEL_REC __cpu_to_be16(0x20)
+#define TUNNEL_VERSION __cpu_to_be16(0x40)
+#define TUNNEL_NO_KEY __cpu_to_be16(0x80)
#define TUNNEL_DONT_FRAGMENT __cpu_to_be16(0x0100)
-#define TUNNEL_OAM __cpu_to_be16(0x0200)
-#define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400)
+#define TUNNEL_OAM __cpu_to_be16(0x0200)
+#define TUNNEL_CRIT_OPT __cpu_to_be16(0x0400)
+#define TUNNEL_OPTIONS_PRESENT __cpu_to_be16(0x0800)
struct tnl_ptk_info {
__be16 flags;
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 6753032..435eabc 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -192,6 +192,7 @@ enum ovs_vport_type {
OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */
OVS_VPORT_TYPE_GRE, /* GRE tunnel. */
OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */
+ OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */
__OVS_VPORT_TYPE_MAX
};
@@ -310,6 +311,7 @@ enum ovs_tunnel_key_attr {
OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT, /* No argument, set DF. */
OVS_TUNNEL_KEY_ATTR_CSUM, /* No argument. CSUM packet. */
OVS_TUNNEL_KEY_ATTR_OAM, /* No argument. OAM frame. */
+ OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, /* Array of Geneve options. */
__OVS_TUNNEL_KEY_ATTR_MAX
};
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index 6ecf491..ba3bb82 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -54,3 +54,14 @@ config OPENVSWITCH_VXLAN
Say N to exclude this support and reduce the binary size.
If unsure, say Y.
+
+config OPENVSWITCH_GENEVE
+ bool "Open vSwitch Geneve tunneling support"
+ depends on INET
+ depends on OPENVSWITCH
+ depends on GENEVE && !(OPENVSWITCH=y && GENEVE=m)
+ default y
+ ---help---
+ If you say Y here, then the Open vSwitch will be able create geneve vport.
+
+ Say N to exclude this support and reduce the binary size.
diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile
index 3591cb5..9a33a27 100644
--- a/net/openvswitch/Makefile
+++ b/net/openvswitch/Makefile
@@ -15,6 +15,10 @@ openvswitch-y := \
vport-internal_dev.o \
vport-netdev.o
+ifneq ($(CONFIG_OPENVSWITCH_GENEVE),)
+openvswitch-y += vport-geneve.o
+endif
+
ifneq ($(CONFIG_OPENVSWITCH_VXLAN),)
openvswitch-y += vport-vxlan.o
endif
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 010125c..2e31d9e 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -370,6 +370,7 @@ static size_t key_attr_size(void)
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
+ + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
+ nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
+ nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
@@ -556,10 +557,12 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS],
&flow->key, 0, &acts);
- rcu_assign_pointer(flow->sf_acts, acts);
if (err)
goto err_flow_free;
+ rcu_assign_pointer(flow->sf_acts, acts);
+
+ OVS_CB(packet)->egress_tun_info = NULL;
OVS_CB(packet)->flow = flow;
packet->priority = flow->key.phy.priority;
packet->mark = flow->key.phy.skb_mark;
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 2924cb3..62db02b 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -448,6 +448,9 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
int error;
struct ethhdr *eth;
+ /* Flags are always used as part of stats */
+ key->tp.flags = 0;
+
skb_reset_mac_header(skb);
/* Link layer. We are guaranteed to have at least the 14 byte Ethernet
@@ -646,10 +649,23 @@ int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info,
struct sk_buff *skb, struct sw_flow_key *key)
{
/* Extract metadata from packet. */
- if (tun_info)
+ if (tun_info) {
memcpy(&key->tun_key, &tun_info->tunnel, sizeof(key->tun_key));
- else
+
+ if (tun_info->options) {
+ BUILD_BUG_ON((1 << (sizeof(tun_info->options_len) *
+ 8)) - 1
+ > sizeof(key->tun_opts));
+ memcpy(GENEVE_OPTS(key, tun_info->options_len),
+ tun_info->options, tun_info->options_len);
+ key->tun_opts_len = tun_info->options_len;
+ } else {
+ key->tun_opts_len = 0;
+ }
+ } else {
+ key->tun_opts_len = 0;
memset(&key->tun_key, 0, sizeof(key->tun_key));
+ }
key->phy.priority = skb->priority;
key->phy.in_port = OVS_CB(skb)->input_vport->port_no;
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index fe5a71b..7181331 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -51,11 +51,24 @@ struct ovs_key_ipv4_tunnel {
struct ovs_tunnel_info {
struct ovs_key_ipv4_tunnel tunnel;
+ struct geneve_opt *options;
+ u8 options_len;
};
+/* Store options at the end of the array if they are less than the
+ * maximum size. This allows us to get the benefits of variable length
+ * matching for small options.
+ */
+#define GENEVE_OPTS(flow_key, opt_len) \
+ ((struct geneve_opt *)((flow_key)->tun_opts + \
+ FIELD_SIZEOF(struct sw_flow_key, tun_opts) - \
+ opt_len))
+
static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
const struct iphdr *iph,
- __be64 tun_id, __be16 tun_flags)
+ __be64 tun_id, __be16 tun_flags,
+ struct geneve_opt *opts,
+ u8 opts_len)
{
tun_info->tunnel.tun_id = tun_id;
tun_info->tunnel.ipv4_src = iph->saddr;
@@ -67,9 +80,14 @@ static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
/* clear struct padding. */
memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0,
sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);
+
+ tun_info->options = opts;
+ tun_info->options_len = opts_len;
}
struct sw_flow_key {
+ u8 tun_opts[255];
+ u8 tun_opts_len;
struct ovs_key_ipv4_tunnel tun_key; /* Encapsulating tunnel key. */
struct {
u32 priority; /* Packet QoS priority. */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 5d6194d..368f233 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -42,6 +42,7 @@
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/rculist.h>
+#include <net/geneve.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
@@ -88,18 +89,20 @@ static void update_range__(struct sw_flow_match *match,
} \
} while (0)
-#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \
- do { \
- update_range__(match, offsetof(struct sw_flow_key, field), \
- len, is_mask); \
- if (is_mask) { \
- if ((match)->mask) \
- memcpy(&(match)->mask->key.field, value_p, len);\
- } else { \
- memcpy(&(match)->key->field, value_p, len); \
- } \
+#define SW_FLOW_KEY_MEMCPY_OFFSET(match, offset, value_p, len, is_mask) \
+ do { \
+ update_range__(match, offset, len, is_mask); \
+ if (is_mask) \
+ memcpy((u8 *)&(match)->mask->key + offset, value_p, \
+ len); \
+ else \
+ memcpy((u8 *)(match)->key + offset, value_p, len); \
} while (0)
+#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \
+ SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \
+ value_p, len, is_mask)
+
static u16 range_n_bytes(const struct sw_flow_key_range *range)
{
return range->end - range->start;
@@ -335,6 +338,7 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
int rem;
bool ttl = false;
__be16 tun_flags = 0;
+ unsigned long opt_key_offset;
nla_for_each_nested(a, attr, rem) {
int type = nla_type(a);
@@ -347,6 +351,7 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
[OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
[OVS_TUNNEL_KEY_ATTR_OAM] = 0,
+ [OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = -1,
};
if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
@@ -355,7 +360,8 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
return -EINVAL;
}
- if (ovs_tunnel_key_lens[type] != nla_len(a)) {
+ if (ovs_tunnel_key_lens[type] != nla_len(a) &&
+ ovs_tunnel_key_lens[type] != -1) {
OVS_NLERR("IPv4 tunnel attribute type has unexpected "
" length (type=%d, length=%d, expected=%d).\n",
type, nla_len(a), ovs_tunnel_key_lens[type]);
@@ -394,7 +400,60 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
case OVS_TUNNEL_KEY_ATTR_OAM:
tun_flags |= TUNNEL_OAM;
break;
+ case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+ tun_flags |= TUNNEL_OPTIONS_PRESENT;
+ if (nla_len(a) > sizeof(match->key->tun_opts)) {
+ OVS_NLERR("Geneve option length exceeds maximum size (len %d, max %zu).\n",
+ nla_len(a),
+ sizeof(match->key->tun_opts));
+ return -EINVAL;
+ }
+
+ if (nla_len(a) % 4 != 0) {
+ OVS_NLERR("Geneve option length is not a multiple of 4 (len %d).\n",
+ nla_len(a));
+ return -EINVAL;
+ }
+
+ /* We need to record the length of the options passed
+ * down, otherwise packets with the same format but
+ * additional options will be silently matched.
+ */
+ if (!is_mask) {
+ SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a),
+ false);
+ } else {
+ /* This is somewhat unusual because it looks at
+ * both the key and mask while parsing the
+ * attributes (and by extension assumes the key
+ * is parsed first). Normally, we would verify
+ * that each is the correct length and that the
+ * attributes line up in the validate function.
+ * However, that is difficult because this is
+ * variable length and we won't have the
+ * information later.
+ */
+ if (match->key->tun_opts_len != nla_len(a)) {
+ OVS_NLERR("Geneve option key length (%d) is different from mask length (%d).",
+ match->key->tun_opts_len,
+ nla_len(a));
+ return -EINVAL;
+ }
+
+ SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff,
+ true);
+ }
+
+ opt_key_offset = (unsigned long)GENEVE_OPTS(
+ (struct sw_flow_key *)0,
+ nla_len(a));
+ SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset,
+ nla_data(a), nla_len(a),
+ is_mask);
+ break;
default:
+ OVS_NLERR("Unknown IPv4 tunnel attribute (%d).\n",
+ type);
return -EINVAL;
}
}
@@ -421,16 +480,11 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
return 0;
}
-static int ipv4_tun_to_nlattr(struct sk_buff *skb,
- const struct ovs_key_ipv4_tunnel *tun_key,
- const struct ovs_key_ipv4_tunnel *output)
+static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
+ const struct ovs_key_ipv4_tunnel *output,
+ const struct geneve_opt *tun_opts,
+ int swkey_tun_opts_len)
{
- struct nlattr *nla;
-
- nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
- if (!nla)
- return -EMSGSIZE;
-
if (output->tun_flags & TUNNEL_KEY &&
nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id))
return -EMSGSIZE;
@@ -454,12 +508,35 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
if ((output->tun_flags & TUNNEL_OAM) &&
nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
return -EMSGSIZE;
+ if (tun_opts &&
+ nla_put(skb, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
+ swkey_tun_opts_len, tun_opts))
+ return -EMSGSIZE;
- nla_nest_end(skb, nla);
return 0;
}
+static int ipv4_tun_to_nlattr(struct sk_buff *skb,
+ const struct ovs_key_ipv4_tunnel *output,
+ const struct geneve_opt *tun_opts,
+ int swkey_tun_opts_len)
+{
+ struct nlattr *nla;
+ int err;
+
+ nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
+ if (!nla)
+ return -EMSGSIZE;
+
+ err = __ipv4_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len);
+ if (err)
+ return err;
+
+ nla_nest_end(skb, nla);
+ return 0;
+}
+
static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
const struct nlattr **a, bool is_mask)
{
@@ -905,9 +982,16 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority))
goto nla_put_failure;
- if ((swkey->tun_key.ipv4_dst || is_mask) &&
- ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key))
- goto nla_put_failure;
+ if ((swkey->tun_key.ipv4_dst || is_mask)) {
+ const struct geneve_opt *opts = NULL;
+
+ if (output->tun_key.tun_flags & TUNNEL_OPTIONS_PRESENT)
+ opts = GENEVE_OPTS(output, swkey->tun_opts_len);
+
+ if (ipv4_tun_to_nlattr(skb, &output->tun_key, opts,
+ swkey->tun_opts_len))
+ goto nla_put_failure;
+ }
if (swkey->phy.in_port == DP_MAX_PORTS) {
if (is_mask && (output->phy.in_port == 0xffff))
@@ -1290,17 +1374,55 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
if (err)
return err;
+ if (key.tun_opts_len) {
+ struct geneve_opt *option = GENEVE_OPTS(&key,
+ key.tun_opts_len);
+ int opts_len = key.tun_opts_len;
+ bool crit_opt = false;
+
+ while (opts_len > 0) {
+ int len;
+
+ if (opts_len < sizeof(*option))
+ return -EINVAL;
+
+ len = sizeof(*option) + option->length * 4;
+ if (len > opts_len)
+ return -EINVAL;
+
+ crit_opt |= !!(option->type & GENEVE_CRIT_OPT_TYPE);
+
+ option = (struct geneve_opt *)((u8 *)option + len);
+ opts_len -= len;
+ };
+
+ key.tun_key.tun_flags |= crit_opt ? TUNNEL_CRIT_OPT : 0;
+ };
+
start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
if (start < 0)
return start;
a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
- sizeof(*tun_info));
+ sizeof(*tun_info) + key.tun_opts_len);
if (IS_ERR(a))
return PTR_ERR(a);
tun_info = nla_data(a);
tun_info->tunnel = key.tun_key;
+ tun_info->options_len = key.tun_opts_len;
+
+ if (tun_info->options_len) {
+ /* We need to store the options in the action itself since
+ * everything else will go away after flow setup. We can append
+ * it to tun_info and then point there.
+ */
+ memcpy((tun_info + 1), GENEVE_OPTS(&key, key.tun_opts_len),
+ key.tun_opts_len);
+ tun_info->options = (struct geneve_opt *)(tun_info + 1);
+ } else {
+ tun_info->options = NULL;
+ }
add_nested_action_end(*sfa, start);
@@ -1592,7 +1714,9 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
return -EMSGSIZE;
err = ipv4_tun_to_nlattr(skb, &tun_info->tunnel,
- nla_data(ovs_key));
+ tun_info->options_len ?
+ tun_info->options : NULL,
+ tun_info->options_len);
if (err)
return err;
nla_nest_end(skb, start);
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
new file mode 100644
index 0000000..5572d48
--- /dev/null
+++ b/net/openvswitch/vport-geneve.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2014 Nicira, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/version.h>
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/net.h>
+#include <linux/rculist.h>
+#include <linux/udp.h>
+#include <linux/if_vlan.h>
+
+#include <net/geneve.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/udp.h>
+#include <net/xfrm.h>
+
+#include "datapath.h"
+#include "vport.h"
+
+/**
+ * struct geneve_port - Keeps track of open UDP ports
+ * @sock: The socket created for this port number.
+ * @name: vport name.
+ */
+struct geneve_port {
+ struct geneve_sock *gs;
+ char name[IFNAMSIZ];
+};
+
+static LIST_HEAD(geneve_ports);
+
+static inline struct geneve_port *geneve_vport(const struct vport *vport)
+{
+ return vport_priv(vport);
+}
+
+static inline struct genevehdr *geneve_hdr(const struct sk_buff *skb)
+{
+ return (struct genevehdr *)(udp_hdr(skb) + 1);
+}
+
+/* Convert 64 bit tunnel ID to 24 bit VNI. */
+static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
+{
+#ifdef __BIG_ENDIAN
+ vni[0] = (__force __u8)(tun_id >> 16);
+ vni[1] = (__force __u8)(tun_id >> 8);
+ vni[2] = (__force __u8)tun_id;
+#else
+ vni[0] = (__force __u8)((__force u64)tun_id >> 40);
+ vni[1] = (__force __u8)((__force u64)tun_id >> 48);
+ vni[2] = (__force __u8)((__force u64)tun_id >> 56);
+#endif
+}
+
+/* Convert 24 bit VNI to 64 bit tunnel ID. */
+static __be64 vni_to_tunnel_id(__u8 *vni)
+{
+#ifdef __BIG_ENDIAN
+ return (vni[0] << 16) | (vni[1] << 8) | vni[2];
+#else
+ return (__force __be64)(((__force u64)vni[0] << 40) |
+ ((__force u64)vni[1] << 48) |
+ ((__force u64)vni[2] << 56));
+#endif
+}
+
+static void geneve_rcv(struct geneve_sock *gs, struct sk_buff *skb)
+{
+ struct vport *vport = gs->rcv_data;
+ struct genevehdr *geneveh = geneve_hdr(skb);
+ int opts_len;
+ struct ovs_tunnel_info tun_info;
+ __be64 key;
+ __be16 flags;
+
+ opts_len = geneveh->opt_len * 4;
+
+ flags = TUNNEL_KEY | TUNNEL_OPTIONS_PRESENT |
+ (udp_hdr(skb)->check != 0 ? TUNNEL_CSUM : 0) |
+ (geneveh->oam ? TUNNEL_OAM : 0) |
+ (geneveh->critical ? TUNNEL_CRIT_OPT : 0);
+
+ key = vni_to_tunnel_id(geneveh->vni);
+
+ ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key, flags,
+ geneveh->options, opts_len);
+
+ ovs_vport_receive(vport, skb, &tun_info);
+}
+
+static int geneve_get_options(const struct vport *vport,
+ struct sk_buff *skb)
+{
+ struct geneve_port *geneve_port = geneve_vport(vport);
+ __be16 sport;
+
+ sport = ntohs(inet_sk(geneve_port->gs->sock->sk)->inet_sport);
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, sport))
+ return -EMSGSIZE;
+ return 0;
+}
+
+static void geneve_tnl_destroy(struct vport *vport)
+{
+ struct geneve_port *geneve_port = geneve_vport(vport);
+
+ geneve_sock_release(geneve_port->gs);
+
+ ovs_vport_deferred_free(vport);
+}
+
+static struct vport *geneve_tnl_create(const struct vport_parms *parms)
+{
+ struct net *net = ovs_dp_get_net(parms->dp);
+ struct nlattr *options = parms->options;
+ struct geneve_port *geneve_port;
+ struct geneve_sock *gs;
+ struct vport *vport;
+ struct nlattr *a;
+ int err;
+ u16 dst_port;
+
+ if (!options) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT);
+ if (a && nla_len(a) == sizeof(u16)) {
+ dst_port = nla_get_u16(a);
+ } else {
+ /* Require destination port from userspace. */
+ err = -EINVAL;
+ goto error;
+ }
+
+ vport = ovs_vport_alloc(sizeof(struct geneve_port),
+ &ovs_geneve_vport_ops, parms);
+ if (IS_ERR(vport))
+ return vport;
+
+ geneve_port = geneve_vport(vport);
+ strncpy(geneve_port->name, parms->name, IFNAMSIZ);
+
+ gs = geneve_sock_add(net, htons(dst_port), geneve_rcv, vport, true, 0);
+ if (IS_ERR(gs)) {
+ ovs_vport_free(vport);
+ return (void *)gs;
+ }
+ geneve_port->gs = gs;
+
+ return vport;
+error:
+ return ERR_PTR(err);
+}
+
+static int geneve_tnl_send(struct vport *vport, struct sk_buff *skb)
+{
+ struct ovs_key_ipv4_tunnel *tun_key;
+ struct ovs_tunnel_info *tun_info;
+ struct net *net = ovs_dp_get_net(vport->dp);
+ struct geneve_port *geneve_port = geneve_vport(vport);
+ __be16 dport = inet_sk(geneve_port->gs->sock->sk)->inet_sport;
+ __be16 sport;
+ struct rtable *rt;
+ struct flowi4 fl;
+ u8 vni[3];
+ __be16 df;
+ int err;
+
+ tun_info = OVS_CB(skb)->egress_tun_info;
+ if (unlikely(!tun_info)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ tun_key = &tun_info->tunnel;
+
+ /* Route lookup */
+ memset(&fl, 0, sizeof(fl));
+ fl.daddr = tun_key->ipv4_dst;
+ fl.saddr = tun_key->ipv4_src;
+ fl.flowi4_tos = RT_TOS(tun_key->ipv4_tos);
+ fl.flowi4_mark = skb->mark;
+ fl.flowi4_proto = IPPROTO_UDP;
+
+ rt = ip_route_output_key(net, &fl);
+ if (IS_ERR(rt)) {
+ err = PTR_ERR(rt);
+ goto error;
+ }
+
+ df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
+ sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
+ tunnel_id_to_vni(tun_key->tun_id, vni);
+ skb->ignore_df = 1;
+
+ err = geneve_xmit_skb(geneve_port->gs, rt, skb, fl.saddr,
+ tun_key->ipv4_dst, tun_key->ipv4_tos,
+ tun_key->ipv4_ttl, df, sport, dport,
+ tun_key->tun_flags, vni,
+ tun_info->options_len, (u8 *)tun_info->options,
+ false);
+ if (err < 0)
+ ip_rt_put(rt);
+error:
+ return err;
+}
+
+static const char *geneve_get_name(const struct vport *vport)
+{
+ struct geneve_port *geneve_port = geneve_vport(vport);
+
+ return geneve_port->name;
+}
+
+const struct vport_ops ovs_geneve_vport_ops = {
+ .type = OVS_VPORT_TYPE_GENEVE,
+ .create = geneve_tnl_create,
+ .destroy = geneve_tnl_destroy,
+ .get_name = geneve_get_name,
+ .get_options = geneve_get_options,
+ .send = geneve_tnl_send,
+};
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
index fe768bd..108b82d 100644
--- a/net/openvswitch/vport-gre.c
+++ b/net/openvswitch/vport-gre.c
@@ -106,7 +106,7 @@ static int gre_rcv(struct sk_buff *skb,
key = key_to_tunnel_id(tpi->key, tpi->seq);
ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key,
- filter_tnl_flags(tpi->flags));
+ filter_tnl_flags(tpi->flags), NULL, 0);
ovs_vport_receive(vport, skb, &tun_info);
return PACKET_RCVD;
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index 5fbff2c..2735e01 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -66,7 +66,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
/* Save outer tunnel values */
iph = ip_hdr(skb);
key = cpu_to_be64(ntohl(vx_vni) >> 8);
- ovs_flow_tun_info_init(&tun_info, iph, key, TUNNEL_KEY);
+ ovs_flow_tun_info_init(&tun_info, iph, key, TUNNEL_KEY, NULL, 0);
ovs_vport_receive(vport, skb, &tun_info);
}
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 3e50ee8..53001b0 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -48,6 +48,9 @@ static const struct vport_ops *vport_ops_list[] = {
#ifdef CONFIG_OPENVSWITCH_VXLAN
&ovs_vxlan_vport_ops,
#endif
+#ifdef CONFIG_OPENVSWITCH_GENEVE
+ &ovs_geneve_vport_ops,
+#endif
};
/* Protected by RCU read lock for reading, ovs_mutex for writing. */
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index e28964a..8942125 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -215,6 +215,7 @@ extern const struct vport_ops ovs_netdev_vport_ops;
extern const struct vport_ops ovs_internal_vport_ops;
extern const struct vport_ops ovs_gre_vport_ops;
extern const struct vport_ops ovs_vxlan_vport_ops;
+extern const struct vport_ops ovs_geneve_vport_ops;
static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
const void *start, unsigned int len)
--
1.7.9.5
^ permalink raw reply related
* Re: [net-next 0/9][pull request] Intel Wired LAN Driver Updates 2014-10-02
From: David Miller @ 2014-10-03 22:45 UTC (permalink / raw)
To: jeffrey.t.kirsher; +Cc: netdev, nhorman, sassmann, jogreene
In-Reply-To: <1412244994-23636-1-git-send-email-jeffrey.t.kirsher@intel.com>
From: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Date: Thu, 2 Oct 2014 03:16:25 -0700
> This series contains updates to fm10k, igb, ixgbe and i40e.
Pulled, thanks a lot Jeff!
^ permalink raw reply
* Re: [patch 1/2 -next] cxgb4: clean up a type issue
From: David Miller @ 2014-10-03 22:46 UTC (permalink / raw)
To: dan.carpenter; +Cc: hariprasad, netdev, kernel-janitors
In-Reply-To: <20141002112219.GA25606@mwanda>
From: Dan Carpenter <dan.carpenter@oracle.com>
Date: Thu, 2 Oct 2014 14:22:19 +0300
> The tx_desc struct hold 8 __be64 values. The original code took a
> tx_desc pointer then casted it to an int pointer and then casted it to a
> u64 pointer. It was confusing and triggered some static checker
> warnings.
>
> I have changed the cxgb_pio_copy() to only take tx_desc pointers. This
> isn't really a loss of flexibility because anything else was buggy to
> begin with.
>
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Please address the feedback you've received, resubmit this series, and actually
number this second change "2/2" instead of "1/2" :-)
Thanks!
^ permalink raw reply
* [PATCH v2 net-next 0/4] net: Generic UDP Encapsulation
From: Tom Herbert @ 2014-10-03 22:48 UTC (permalink / raw)
To: davem, netdev
Generic UDP Encapsulation (GUE) is UDP encapsulation protocol which
encapsulates packets of various IP protocols. The GUE protocol is
described in http://tools.ietf.org/html/draft-herbert-gue-01.
The receive path of GUE is implemented in the FOU over UDP module (FOU).
This includes a UDP encap receive function for GUE as well as GUE
specific GRO functions. Management and configuration of GUE ports shares
most of the same code with FOU.
For the transmit path, the previous FOU support for IPIP, sit, and GRE
was simply extended for GUE (when GUE is enabled insert the GUE
header on transmit in addition to UDP header inserted for FOU).
Semantically GUE is the same as FOU in that the encapsulation (UDP
and GUE headers) that are inserted on transmission and removed on
reception so that IP packet is processed with the inner header.
This patch set includes:
- Some fixes to FOU, removal of IPv4,v6 specific GRO functions
- Support to configure a GUE receive port
- Implementation of GUE receive path (normal and GRO)
- Additions to ip_tunnel netlink to configure GUE
- GUE header inserion in ip_tunnel transmit path
v2:
- Include net/gue.h in patch set
Testing:
I ran performance numbers using netperf TCP_RR with 200 streams,
comparing encapsulation without GUE, encapsulation with GUE, and
encapsulation with FOU.
GRE
TCP_STREAM
IPv4, FOU, UDP checksum enabled
14.04% TX CPU utilization
13.17% RX CPU utilization
9211 Mbps
IPv4, GUE, UDP checksum enabled
14.99% TX CPU utilization
13.79% RX CPU utilization
9185 Mbps
IPv4, FOU, UDP checksum disabled
13.14% TX CPU utilization
23.18% RX CPU utilization
9277 Mbps
IPv4, GUE, UDP checksum disabled
13.66% TX CPU utilization
23.57% RX CPU utilization
9184 Mbps
TCP_RR
IPv4, FOU, UDP checksum enabled
94.2% CPU utilization
155/249/460 90/95/99% latencies
1.17018e+06 tps
IPv4, GUE, UDP checksum enabled
93.9% CPU utilization
158/253/472 90/95/99% latencies
1.15045e+06 tps
IPIP
TCP_STREAM
FOU, UDP checksum enabled
15.28% TX CPU utilization
13.92% RX CPU utilization
9342 Mbps
GUE, UDP checksum enabled
13.99% TX CPU utilization
13.34% RX CPU utilization
9210 Mbps
FOU, UDP checksum disabled
15.08% TX CPU utilization
24.64% RX CPU utilization
9226 Mbps
GUE, UDP checksum disabled
15.90% TX CPU utilization
24.77% RX CPU utilization
9197 Mbps
TCP_RR
FOU, UDP checksum enabled
94.23% CPU utilization
149/237/429 90/95/99% latencies
1.19553e+06 tps
GUE, UDP checksum enabled
93.75% CPU utilization
152/243/442 90/95/99% latencies
1.17027e+06 tps
SIT
TCP_STREAM
FOU, UDP checksum enabled
14.47% TX CPU utilization
14.58% RX CPU utilization
9106 Mbps
GUE, UDP checksum enabled
15.09% TX CPU utilization
14.84% RX CPU utilization
9080 Mbps
FOU, UDP checksum disabled
15.70% TX CPU utilization
27.93% RX CPU utilization
9097 Mbps
GUE, UDP checksum disabled
15.04% TX CPU utilization
27.54% RX CPU utilization
9073 Mbps
TCP_RR
FOU, UDP checksum enabled
96.9% CPU utilization
170/281/581 90/95/99% latencies
1.03372e+06 tps
GUE, UDP checksum enabled
97.16% CPU utilization
172/286/576 90/95/99% latencies
1.00469e+06 tps
Tom Herbert (4):
ip_tunnel: Account for secondary encapsulation header in max_headroom
fou: eliminate IPv4,v6 specific GRO functions
gue: Receive side for Generic UDP Encapsulation
ip_tunnel: Add GUE support
include/linux/netdevice.h | 3 +
include/net/gue.h | 23 +++++
include/uapi/linux/fou.h | 7 ++
include/uapi/linux/if_tunnel.h | 1 +
net/ipv4/fou.c | 224 ++++++++++++++++++++++++++++++++++-------
net/ipv4/ip_tunnel.c | 15 ++-
net/ipv4/udp_offload.c | 1 +
net/ipv6/udp_offload.c | 1 +
8 files changed, 235 insertions(+), 40 deletions(-)
create mode 100644 include/net/gue.h
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply
* [PATCH v2 net-next 1/4] ip_tunnel: Account for secondary encapsulation header in max_headroom
From: Tom Herbert @ 2014-10-03 22:48 UTC (permalink / raw)
To: davem, netdev
In-Reply-To: <1412376490-8774-1-git-send-email-therbert@google.com>
When adjusting max_header for the tunnel interface based on egress
device we need to account for any extra bytes in secondary encapsulation
(e.g. FOU).
Signed-off-by: Tom Herbert <therbert@google.com>
---
net/ipv4/ip_tunnel.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index b75b47b..54ace25 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -759,7 +759,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
df |= (inner_iph->frag_off&htons(IP_DF));
max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr)
- + rt->dst.header_len;
+ + rt->dst.header_len + ip_encap_hlen(&tunnel->encap);
if (max_headroom > dev->needed_headroom)
dev->needed_headroom = max_headroom;
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v2 net-next 2/4] fou: eliminate IPv4,v6 specific GRO functions
From: Tom Herbert @ 2014-10-03 22:48 UTC (permalink / raw)
To: davem, netdev
In-Reply-To: <1412376490-8774-1-git-send-email-therbert@google.com>
This patch removes fou[46]_gro_receive and fou[46]_gro_complete
functions. The v4 or v6 variants were chosen for the UDP offloads
based on the address family of the socket this is not necessary
or correct. Alternatively, this patch adds is_ipv6 to napi_gro_skb.
This is set in udp6_gro_receive and unset in udp4_gro_receive. In
fou_gro_receive the value is used to select the correct inet_offloads
for the protocol of the outer IP header.
Signed-off-by: Tom Herbert <therbert@google.com>
---
include/linux/netdevice.h | 3 +++
net/ipv4/fou.c | 48 ++++++++---------------------------------------
net/ipv4/udp_offload.c | 1 +
net/ipv6/udp_offload.c | 1 +
4 files changed, 13 insertions(+), 40 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9b7fbac..640f8d8 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1886,6 +1886,9 @@ struct napi_gro_cb {
/* Number of checksums via CHECKSUM_UNNECESSARY */
u8 csum_cnt:3;
+ /* Used in foo-over-udp, set in udp[46]_gro_receive */
+ u8 is_ipv6:1;
+
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
__wsum csum;
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index dced89f..7e2126a 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -65,14 +65,15 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
}
static struct sk_buff **fou_gro_receive(struct sk_buff **head,
- struct sk_buff *skb,
- const struct net_offload **offloads)
+ struct sk_buff *skb)
{
const struct net_offload *ops;
struct sk_buff **pp = NULL;
u8 proto = NAPI_GRO_CB(skb)->proto;
+ const struct net_offload **offloads;
rcu_read_lock();
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
if (!ops || !ops->callbacks.gro_receive)
goto out_unlock;
@@ -85,14 +86,15 @@ out_unlock:
return pp;
}
-static int fou_gro_complete(struct sk_buff *skb, int nhoff,
- const struct net_offload **offloads)
+static int fou_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
u8 proto = NAPI_GRO_CB(skb)->proto;
int err = -ENOSYS;
+ const struct net_offload **offloads;
rcu_read_lock();
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;
@@ -105,28 +107,6 @@ out_unlock:
return err;
}
-static struct sk_buff **fou4_gro_receive(struct sk_buff **head,
- struct sk_buff *skb)
-{
- return fou_gro_receive(head, skb, inet_offloads);
-}
-
-static int fou4_gro_complete(struct sk_buff *skb, int nhoff)
-{
- return fou_gro_complete(skb, nhoff, inet_offloads);
-}
-
-static struct sk_buff **fou6_gro_receive(struct sk_buff **head,
- struct sk_buff *skb)
-{
- return fou_gro_receive(head, skb, inet6_offloads);
-}
-
-static int fou6_gro_complete(struct sk_buff *skb, int nhoff)
-{
- return fou_gro_complete(skb, nhoff, inet6_offloads);
-}
-
static int fou_add_to_port_list(struct fou *fou)
{
struct fou *fout;
@@ -199,20 +179,8 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
sk->sk_allocation = GFP_ATOMIC;
- switch (cfg->udp_config.family) {
- case AF_INET:
- fou->udp_offloads.callbacks.gro_receive = fou4_gro_receive;
- fou->udp_offloads.callbacks.gro_complete = fou4_gro_complete;
- break;
- case AF_INET6:
- fou->udp_offloads.callbacks.gro_receive = fou6_gro_receive;
- fou->udp_offloads.callbacks.gro_complete = fou6_gro_complete;
- break;
- default:
- err = -EPFNOSUPPORT;
- goto error;
- }
-
+ fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
+ fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
fou->udp_offloads.port = cfg->udp_config.local_udp_port;
fou->udp_offloads.ipproto = cfg->protocol;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 8c35f2c..507310e 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -334,6 +334,7 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head,
skb_gro_checksum_try_convert(skb, IPPROTO_UDP, uh->check,
inet_gro_compute_pseudo);
skip:
+ NAPI_GRO_CB(skb)->is_ipv6 = 0;
return udp_gro_receive(head, skb, uh);
flush:
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 8f96988..6b8f543 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -140,6 +140,7 @@ static struct sk_buff **udp6_gro_receive(struct sk_buff **head,
ip6_gro_compute_pseudo);
skip:
+ NAPI_GRO_CB(skb)->is_ipv6 = 1;
return udp_gro_receive(head, skb, uh);
flush:
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v2 net-next 3/4] gue: Receive side for Generic UDP Encapsulation
From: Tom Herbert @ 2014-10-03 22:48 UTC (permalink / raw)
To: davem, netdev
In-Reply-To: <1412376490-8774-1-git-send-email-therbert@google.com>
This patch adds support receiving for GUE packets in the fou module. The
fou module now supports direct foo-over-udp (no encapsulation header)
and GUE. To support this a type parameter is added to the fou netlink
parameters.
For a GUE socket we define gue_udp_recv, gue_gro_receive, and
gue_gro_complete to handle the specifics of the GUE protocol. Most
of the code to manage and configure sockets is common with the fou.
Signed-off-by: Tom Herbert <therbert@google.com>
---
include/net/gue.h | 23 ++++++
include/uapi/linux/fou.h | 7 ++
net/ipv4/fou.c | 196 ++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 217 insertions(+), 9 deletions(-)
create mode 100644 include/net/gue.h
diff --git a/include/net/gue.h b/include/net/gue.h
new file mode 100644
index 0000000..b6c3327
--- /dev/null
+++ b/include/net/gue.h
@@ -0,0 +1,23 @@
+#ifndef __NET_GUE_H
+#define __NET_GUE_H
+
+struct guehdr {
+ union {
+ struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 hlen:4,
+ version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ hlen:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 next_hdr;
+ __u16 flags;
+ };
+ __u32 word;
+ };
+};
+
+#endif
diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h
index e03376d..8df0689 100644
--- a/include/uapi/linux/fou.h
+++ b/include/uapi/linux/fou.h
@@ -13,6 +13,7 @@ enum {
FOU_ATTR_PORT, /* u16 */
FOU_ATTR_AF, /* u8 */
FOU_ATTR_IPPROTO, /* u8 */
+ FOU_ATTR_TYPE, /* u8 */
__FOU_ATTR_MAX,
};
@@ -27,6 +28,12 @@ enum {
__FOU_CMD_MAX,
};
+enum {
+ FOU_ENCAP_UNSPEC,
+ FOU_ENCAP_DIRECT,
+ FOU_ENCAP_GUE,
+};
+
#define FOU_CMD_MAX (__FOU_CMD_MAX - 1)
#endif /* _UAPI_LINUX_FOU_H */
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 7e2126a..efa70ad 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
+#include <net/gue.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/udp.h>
@@ -27,6 +28,7 @@ struct fou {
};
struct fou_cfg {
+ u16 type;
u8 protocol;
struct udp_port_cfg udp_config;
};
@@ -64,6 +66,41 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
sizeof(struct udphdr));
}
+static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct fou *fou = fou_from_sock(sk);
+ size_t len;
+ struct guehdr *guehdr;
+ struct udphdr *uh;
+
+ if (!fou)
+ return 1;
+
+ len = sizeof(struct udphdr) + sizeof(struct guehdr);
+ if (!pskb_may_pull(skb, len))
+ goto drop;
+
+ uh = udp_hdr(skb);
+ guehdr = (struct guehdr *)&uh[1];
+
+ len += guehdr->hlen << 2;
+ if (!pskb_may_pull(skb, len))
+ goto drop;
+
+ if (guehdr->version != 0)
+ goto drop;
+
+ if (guehdr->flags) {
+ /* No support yet */
+ goto drop;
+ }
+
+ return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len);
+drop:
+ kfree_skb(skb);
+ return 0;
+}
+
static struct sk_buff **fou_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
@@ -107,6 +144,112 @@ out_unlock:
return err;
}
+static struct sk_buff **gue_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ const struct net_offload **offloads;
+ const struct net_offload *ops;
+ struct sk_buff **pp = NULL;
+ struct sk_buff *p;
+ u8 proto;
+ struct guehdr *guehdr;
+ unsigned int hlen, guehlen;
+ unsigned int off;
+ int flush = 1;
+
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*guehdr);
+ guehdr = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ guehdr = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!guehdr))
+ goto out;
+ }
+
+ proto = guehdr->next_hdr;
+
+ rcu_read_lock();
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_receive))
+ goto out_unlock;
+
+ guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+
+ hlen = off + guehlen;
+ if (skb_gro_header_hard(skb, hlen)) {
+ guehdr = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!guehdr))
+ goto out_unlock;
+ }
+
+ flush = 0;
+
+ for (p = *head; p; p = p->next) {
+ const struct guehdr *guehdr2;
+
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ guehdr2 = (struct guehdr *)(p->data + off);
+
+ /* Compare base GUE header to be equal (covers
+ * hlen, version, next_hdr, and flags.
+ */
+ if (guehdr->word != guehdr2->word) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ /* Compare optional fields are the same. */
+ if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
+ guehdr->hlen << 2)) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+ }
+
+ skb_gro_pull(skb, guehlen);
+
+ /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
+ skb_gro_postpull_rcsum(skb, guehdr, guehlen);
+
+ pp = ops->callbacks.gro_receive(head, skb);
+
+out_unlock:
+ rcu_read_unlock();
+out:
+ NAPI_GRO_CB(skb)->flush |= flush;
+
+ return pp;
+}
+
+static int gue_gro_complete(struct sk_buff *skb, int nhoff)
+{
+ const struct net_offload **offloads;
+ struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
+ const struct net_offload *ops;
+ unsigned int guehlen;
+ u8 proto;
+ int err = -ENOENT;
+
+ proto = guehdr->next_hdr;
+
+ guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+
+ rcu_read_lock();
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_complete))
+ goto out_unlock;
+
+ err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
+
+out_unlock:
+ rcu_read_unlock();
+ return err;
+}
+
static int fou_add_to_port_list(struct fou *fou)
{
struct fou *fout;
@@ -142,6 +285,28 @@ static void fou_release(struct fou *fou)
kfree(fou);
}
+static int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
+{
+ udp_sk(sk)->encap_rcv = fou_udp_recv;
+ fou->protocol = cfg->protocol;
+ fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
+ fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
+ fou->udp_offloads.port = cfg->udp_config.local_udp_port;
+ fou->udp_offloads.ipproto = cfg->protocol;
+
+ return 0;
+}
+
+static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
+{
+ udp_sk(sk)->encap_rcv = gue_udp_recv;
+ fou->udp_offloads.callbacks.gro_receive = gue_gro_receive;
+ fou->udp_offloads.callbacks.gro_complete = gue_gro_complete;
+ fou->udp_offloads.port = cfg->udp_config.local_udp_port;
+
+ return 0;
+}
+
static int fou_create(struct net *net, struct fou_cfg *cfg,
struct socket **sockp)
{
@@ -164,10 +329,24 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
sk = sock->sk;
- /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
- fou->protocol = cfg->protocol;
- fou->port = cfg->udp_config.local_udp_port;
- udp_sk(sk)->encap_rcv = fou_udp_recv;
+ fou->port = cfg->udp_config.local_udp_port;
+
+ /* Initial for fou type */
+ switch (cfg->type) {
+ case FOU_ENCAP_DIRECT:
+ err = fou_encap_init(sk, fou, cfg);
+ if (err)
+ goto error;
+ break;
+ case FOU_ENCAP_GUE:
+ err = gue_encap_init(sk, fou, cfg);
+ if (err)
+ goto error;
+ break;
+ default:
+ err = -EINVAL;
+ goto error;
+ }
udp_sk(sk)->encap_type = 1;
udp_encap_enable();
@@ -179,11 +358,6 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
sk->sk_allocation = GFP_ATOMIC;
- fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
- fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
- fou->udp_offloads.port = cfg->udp_config.local_udp_port;
- fou->udp_offloads.ipproto = cfg->protocol;
-
if (cfg->udp_config.family == AF_INET) {
err = udp_add_offload(&fou->udp_offloads);
if (err)
@@ -240,6 +414,7 @@ static struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
[FOU_ATTR_PORT] = { .type = NLA_U16, },
[FOU_ATTR_AF] = { .type = NLA_U8, },
[FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
+ [FOU_ATTR_TYPE] = { .type = NLA_U8, },
};
static int parse_nl_config(struct genl_info *info,
@@ -267,6 +442,9 @@ static int parse_nl_config(struct genl_info *info,
if (info->attrs[FOU_ATTR_IPPROTO])
cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
+ if (info->attrs[FOU_ATTR_TYPE])
+ cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
+
return 0;
}
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [PATCH v2 net-next 4/4] ip_tunnel: Add GUE support
From: Tom Herbert @ 2014-10-03 22:48 UTC (permalink / raw)
To: davem, netdev
In-Reply-To: <1412376490-8774-1-git-send-email-therbert@google.com>
This patch allows configuring IPIP, sit, and GRE tunnels to use GUE.
This is very similar to fou excpet that we need to insert the GUE header
in addition to the UDP header on transmit.
Signed-off-by: Tom Herbert <therbert@google.com>
---
include/uapi/linux/if_tunnel.h | 1 +
net/ipv4/ip_tunnel.c | 13 +++++++++++++
2 files changed, 14 insertions(+)
diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h
index 7c832af..280d9e0 100644
--- a/include/uapi/linux/if_tunnel.h
+++ b/include/uapi/linux/if_tunnel.h
@@ -64,6 +64,7 @@ enum {
enum tunnel_encap_types {
TUNNEL_ENCAP_NONE,
TUNNEL_ENCAP_FOU,
+ TUNNEL_ENCAP_GUE,
};
#define TUNNEL_ENCAP_FLAG_CSUM (1<<0)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 54ace25..79f2ac0 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -56,6 +56,7 @@
#include <net/netns/generic.h>
#include <net/rtnetlink.h>
#include <net/udp.h>
+#include <net/gue.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
@@ -495,6 +496,8 @@ static int ip_encap_hlen(struct ip_tunnel_encap *e)
return 0;
case TUNNEL_ENCAP_FOU:
return sizeof(struct udphdr);
+ case TUNNEL_ENCAP_GUE:
+ return sizeof(struct udphdr) + sizeof(struct guehdr);
default:
return -EINVAL;
}
@@ -546,6 +549,15 @@ static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
skb_reset_transport_header(skb);
uh = udp_hdr(skb);
+ if (e->type == TUNNEL_ENCAP_GUE) {
+ struct guehdr *guehdr = (struct guehdr *)&uh[1];
+
+ guehdr->version = 0;
+ guehdr->hlen = 0;
+ guehdr->flags = 0;
+ guehdr->next_hdr = *protocol;
+ }
+
uh->dest = e->dport;
uh->source = sport;
uh->len = htons(skb->len);
@@ -565,6 +577,7 @@ int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t,
case TUNNEL_ENCAP_NONE:
return 0;
case TUNNEL_ENCAP_FOU:
+ case TUNNEL_ENCAP_GUE:
return fou_build_header(skb, &t->encap, t->encap_hlen,
protocol, fl4);
default:
--
2.1.0.rc2.206.gedb03e5
^ permalink raw reply related
* [Patch net-next] net_sched: refactor out tcf_exts
From: Cong Wang @ 2014-10-03 22:51 UTC (permalink / raw)
To: netdev; +Cc: Cong Wang, Jamal Hadi Salim, John Fastabend, David S. Miller
As Jamal pointed it out, tcf_exts is really unnecessary,
we can definitely refactor it out without losing any functionality.
This could also remove an indirect layer which makes the code
much easier to read.
This patch:
1) moves exts->action and exts->police into tp->ops, since they
are statically assigned
2) moves exts->actions list head out
3) removes exts->type, act->type does the same thing
4) renames tcf_exts_*() functions to tcf_act_*()
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: John Fastabend <john.r.fastabend@intel.com>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
---
include/net/pkt_cls.h | 80 ++++++++++++++++++-----------------------------
include/net/sch_generic.h | 2 ++
net/sched/act_api.c | 9 ++++--
net/sched/cls_api.c | 77 ++++++++++++++++++++++++---------------------
net/sched/cls_basic.c | 23 +++++++-------
net/sched/cls_bpf.c | 23 +++++++-------
net/sched/cls_cgroup.c | 24 +++++++-------
net/sched/cls_flow.c | 23 +++++++-------
net/sched/cls_fw.c | 27 ++++++++--------
net/sched/cls_route.c | 25 ++++++++-------
net/sched/cls_rsvp.h | 27 ++++++++--------
net/sched/cls_tcindex.c | 36 ++++++++++-----------
net/sched/cls_u32.c | 26 +++++++--------
13 files changed, 198 insertions(+), 204 deletions(-)
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index ef44ad9..24bc41f 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -56,88 +56,68 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r)
tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);
}
-struct tcf_exts {
-#ifdef CONFIG_NET_CLS_ACT
- __u32 type; /* for backward compat(TCA_OLD_COMPAT) */
- struct list_head actions;
-#endif
- /* Map to export classifier specific extension TLV types to the
- * generic extensions API. Unsupported extensions must be set to 0.
- */
- int action;
- int police;
-};
-
-static inline void tcf_exts_init(struct tcf_exts *exts, int action, int police)
-{
-#ifdef CONFIG_NET_CLS_ACT
- exts->type = 0;
- INIT_LIST_HEAD(&exts->actions);
-#endif
- exts->action = action;
- exts->police = police;
-}
-
/**
- * tcf_exts_is_predicative - check if a predicative extension is present
- * @exts: tc filter extensions handle
+ * tcf_act_is_predicative - check if a predicative action is present
+ * @actions: tc filter actions
*
- * Returns 1 if a predicative extension is present, i.e. an extension which
+ * Returns 1 if a predicative action is present, i.e. an action which
* might cause further actions and thus overrule the regular tcf_result.
*/
static inline int
-tcf_exts_is_predicative(struct tcf_exts *exts)
+tcf_act_is_predicative(struct list_head *actions)
{
#ifdef CONFIG_NET_CLS_ACT
- return !list_empty(&exts->actions);
+ return !list_empty(actions);
#else
return 0;
#endif
}
/**
- * tcf_exts_is_available - check if at least one extension is present
- * @exts: tc filter extensions handle
+ * tcf_act_is_available - check if at least one action is present
+ * @actions: tc filter actions
*
- * Returns 1 if at least one extension is present.
+ * Returns 1 if at least one action is present.
*/
static inline int
-tcf_exts_is_available(struct tcf_exts *exts)
+tcf_act_is_available(struct list_head *actions)
{
- /* All non-predicative extensions must be added here. */
- return tcf_exts_is_predicative(exts);
+ /* All non-predicative actions must be added here. */
+ return tcf_act_is_predicative(actions);
}
/**
- * tcf_exts_exec - execute tc filter extensions
+ * tcf_act_exec - execute tc filter actions
* @skb: socket buffer
- * @exts: tc filter extensions handle
+ * @actions: list of actions
* @res: desired result
*
- * Executes all configured extensions. Returns 0 on a normal execution,
+ * Executes all configured actions. Returns 0 on a normal execution,
* a negative number if the filter must be considered unmatched or
* a positive action code (TC_ACT_*) which must be returned to the
* underlying layer.
*/
-static inline int
-tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
- struct tcf_result *res)
-{
#ifdef CONFIG_NET_CLS_ACT
- if (!list_empty(&exts->actions))
- return tcf_action_exec(skb, &exts->actions, res);
-#endif
+int tcf_act_exec(struct sk_buff *skb, struct list_head *actions,
+ struct tcf_result *res);
+#else
+static inline
+int tcf_act_exec(struct sk_buff *skb, struct list_head *actions,
+ struct tcf_result *res)
+{
return 0;
}
+#endif
-int tcf_exts_validate(struct net *net, struct tcf_proto *tp,
+int tcf_act_validate(struct net *net, struct tcf_proto *tp,
struct nlattr **tb, struct nlattr *rate_tlv,
- struct tcf_exts *exts, bool ovr);
-void tcf_exts_destroy(struct tcf_exts *exts);
-void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
- struct tcf_exts *src);
-int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts);
-int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts);
+ struct list_head *actions, bool ovr);
+void tcf_act_destroy(struct list_head *actions);
+void tcf_act_change(struct tcf_proto *tp, struct list_head *dst,
+ struct list_head *src);
+int tcf_act_dump(struct sk_buff *skb, const struct tcf_proto *tp,
+ struct list_head *actions);
+int tcf_act_dump_stats(struct sk_buff *skb, struct list_head *actions);
/**
* struct tcf_pkt_info - packet information
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index d17ed6f..3d9fac9 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -211,6 +211,8 @@ struct tcf_result {
struct tcf_proto_ops {
struct list_head head;
char kind[IFNAMSIZ];
+ int action;
+ int police;
int (*classify)(struct sk_buff *,
const struct tcf_proto *,
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 3d43e49..a350598 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -378,12 +378,15 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
return res;
}
-int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
- struct tcf_result *res)
+int tcf_act_exec(struct sk_buff *skb, const struct list_head *actions,
+ struct tcf_result *res)
{
const struct tc_action *a;
int ret = -1;
+ if (list_empty(actions))
+ return 0;
+
if (skb->tc_verd & TC_NCLS) {
skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
ret = TC_ACT_OK;
@@ -405,7 +408,7 @@ int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
exec_done:
return ret;
}
-EXPORT_SYMBOL(tcf_action_exec);
+EXPORT_SYMBOL(tcf_act_exec);
int tcf_action_destroy(struct list_head *actions, int bind)
{
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 77147c8..d6f0059 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -496,89 +496,94 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-void tcf_exts_destroy(struct tcf_exts *exts)
+void tcf_act_destroy(struct list_head *actions)
{
#ifdef CONFIG_NET_CLS_ACT
- tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND);
- INIT_LIST_HEAD(&exts->actions);
+ tcf_action_destroy(actions, TCA_ACT_UNBIND);
+ INIT_LIST_HEAD(actions);
#endif
}
-EXPORT_SYMBOL(tcf_exts_destroy);
+EXPORT_SYMBOL(tcf_act_destroy);
-int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
- struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr)
+int tcf_act_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
+ struct nlattr *rate_tlv, struct list_head *actions,
+ bool ovr)
{
+ int police = tp->ops->police;
+ int action = tp->ops->action;
+
#ifdef CONFIG_NET_CLS_ACT
{
struct tc_action *act;
- INIT_LIST_HEAD(&exts->actions);
- if (exts->police && tb[exts->police]) {
- act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
+ INIT_LIST_HEAD(actions);
+ if (police && tb[police]) {
+ act = tcf_action_init_1(net, tb[police], rate_tlv,
"police", ovr,
TCA_ACT_BIND);
if (IS_ERR(act))
return PTR_ERR(act);
- act->type = exts->type = TCA_OLD_COMPAT;
- list_add(&act->list, &exts->actions);
- } else if (exts->action && tb[exts->action]) {
+ act->type = TCA_OLD_COMPAT;
+ list_add(&act->list, actions);
+ } else if (action && tb[action]) {
int err;
- err = tcf_action_init(net, tb[exts->action], rate_tlv,
+ err = tcf_action_init(net, tb[action], rate_tlv,
NULL, ovr,
- TCA_ACT_BIND, &exts->actions);
+ TCA_ACT_BIND, actions);
if (err)
return err;
}
}
#else
- if ((exts->action && tb[exts->action]) ||
- (exts->police && tb[exts->police]))
+ if ((action && tb[action]) ||
+ (police && tb[police]))
return -EOPNOTSUPP;
#endif
return 0;
}
-EXPORT_SYMBOL(tcf_exts_validate);
+EXPORT_SYMBOL(tcf_act_validate);
-void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
- struct tcf_exts *src)
+void tcf_act_change(struct tcf_proto *tp, struct list_head *dst,
+ struct list_head *src)
{
#ifdef CONFIG_NET_CLS_ACT
LIST_HEAD(tmp);
tcf_tree_lock(tp);
- list_splice_init(&dst->actions, &tmp);
- list_splice(&src->actions, &dst->actions);
+ list_splice_init(dst, &tmp);
+ list_splice(src, dst);
tcf_tree_unlock(tp);
tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
#endif
}
-EXPORT_SYMBOL(tcf_exts_change);
+EXPORT_SYMBOL(tcf_act_change);
-#define tcf_exts_first_act(ext) \
- list_first_entry(&(exts)->actions, struct tc_action, list)
+#define tcf_act_first_act(actions) \
+ list_first_entry(actions, struct tc_action, list)
-int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
+int tcf_act_dump(struct sk_buff *skb, const struct tcf_proto *tp,
+ struct list_head *actions)
{
#ifdef CONFIG_NET_CLS_ACT
struct nlattr *nest;
- if (exts->action && !list_empty(&exts->actions)) {
+ if (tp->ops->action && !list_empty(actions)) {
+ struct tc_action *act = tcf_act_first_act(actions);
/*
* again for backward compatible mode - we want
* to work with both old and new modes of entering
* tc data even if iproute2 was newer - jhs
*/
- if (exts->type != TCA_OLD_COMPAT) {
- nest = nla_nest_start(skb, exts->action);
+ if (act->type != TCA_OLD_COMPAT) {
+ nest = nla_nest_start(skb, tp->ops->action);
if (nest == NULL)
goto nla_put_failure;
- if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0)
+ if (tcf_action_dump(skb, actions, 0, 0) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- } else if (exts->police) {
- struct tc_action *act = tcf_exts_first_act(exts);
- nest = nla_nest_start(skb, exts->police);
+ } else if (tp->ops->police) {
+ nest = nla_nest_start(skb, tp->ops->police);
if (nest == NULL || !act)
goto nla_put_failure;
if (tcf_action_dump_old(skb, act, 0, 0) < 0)
@@ -595,19 +600,19 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
return 0;
#endif
}
-EXPORT_SYMBOL(tcf_exts_dump);
+EXPORT_SYMBOL(tcf_act_dump);
-int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
+int tcf_act_dump_stats(struct sk_buff *skb, struct list_head *actions)
{
#ifdef CONFIG_NET_CLS_ACT
- struct tc_action *a = tcf_exts_first_act(exts);
+ struct tc_action *a = tcf_act_first_act(actions);
if (tcf_action_copy_stats(skb, a, 1) < 0)
return -1;
#endif
return 0;
}
-EXPORT_SYMBOL(tcf_exts_dump_stats);
+EXPORT_SYMBOL(tcf_act_dump_stats);
static int __init tc_filter_init(void)
{
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index fe20826..09c8db6 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -29,7 +29,7 @@ struct basic_head {
struct basic_filter {
u32 handle;
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_ematch_tree ematches;
struct tcf_result res;
struct tcf_proto *tp;
@@ -48,7 +48,7 @@ static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
if (!tcf_em_tree_match(skb, &f->ematches, NULL))
continue;
*res = f->res;
- r = tcf_exts_exec(skb, &f->exts, res);
+ r = tcf_act_exec(skb, &f->actions, res);
if (r < 0)
continue;
return r;
@@ -94,7 +94,7 @@ static void basic_delete_filter(struct rcu_head *head)
struct tcf_proto *tp = f->tp;
tcf_unbind_filter(tp, &f->res);
- tcf_exts_destroy(&f->exts);
+ tcf_act_destroy(&f->actions);
tcf_em_tree_destroy(tp, &f->ematches);
kfree(f);
}
@@ -138,11 +138,10 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
struct nlattr *est, bool ovr)
{
int err;
- struct tcf_exts e;
struct tcf_ematch_tree t;
+ struct list_head actions;
- tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
- err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+ err = tcf_act_validate(net, tp, tb, est, &actions, ovr);
if (err < 0)
return err;
@@ -155,13 +154,13 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
tcf_bind_filter(tp, &f->res, base);
}
- tcf_exts_change(tp, &f->exts, &e);
+ tcf_act_change(tp, &f->actions, &actions);
tcf_em_tree_change(tp, &f->ematches, &t);
f->tp = tp;
return 0;
errout:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -193,7 +192,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (fnew == NULL)
goto errout;
- tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
+ INIT_LIST_HEAD(&fnew->actions);
err = -EINVAL;
if (handle) {
fnew->handle = handle;
@@ -270,13 +269,13 @@ static int basic_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts) < 0 ||
+ if (tcf_act_dump(skb, tp, &f->actions) < 0 ||
tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
+ if (tcf_act_dump_stats(skb, &f->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -288,6 +287,8 @@ static int basic_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops cls_basic_ops __read_mostly = {
.kind = "basic",
+ .police = TCA_BASIC_POLICE,
+ .action = TCA_BASIC_ACT,
.classify = basic_classify,
.init = basic_init,
.destroy = basic_destroy,
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 4318d06..dbdf38c 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -33,7 +33,7 @@ struct cls_bpf_head {
struct cls_bpf_prog {
struct bpf_prog *filter;
struct sock_filter *bpf_ops;
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_result res;
struct list_head link;
u32 handle;
@@ -66,7 +66,7 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
if (filter_res != -1)
res->classid = filter_res;
- ret = tcf_exts_exec(skb, &prog->exts, res);
+ ret = tcf_act_exec(skb, &prog->actions, res);
if (ret < 0)
continue;
@@ -93,7 +93,7 @@ static int cls_bpf_init(struct tcf_proto *tp)
static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog)
{
tcf_unbind_filter(tp, &prog->res);
- tcf_exts_destroy(&prog->exts);
+ tcf_act_destroy(&prog->actions);
bpf_prog_destroy(prog->filter);
@@ -167,7 +167,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
struct nlattr *est, bool ovr)
{
struct sock_filter *bpf_ops;
- struct tcf_exts exts;
+ struct list_head actions;
struct sock_fprog_kern tmp;
struct bpf_prog *fp;
u16 bpf_size, bpf_len;
@@ -177,8 +177,7 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID])
return -EINVAL;
- tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
- ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
+ ret = tcf_act_validate(net, tp, tb, est, &actions, ovr);
if (ret < 0)
return ret;
@@ -211,13 +210,13 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
prog->res.classid = classid;
tcf_bind_filter(tp, &prog->res, base);
- tcf_exts_change(tp, &prog->exts, &exts);
+ tcf_act_change(tp, &prog->actions, &actions);
return 0;
errout_free:
kfree(bpf_ops);
errout:
- tcf_exts_destroy(&exts);
+ tcf_act_destroy(&actions);
return ret;
}
@@ -258,7 +257,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
if (!prog)
return -ENOBUFS;
- tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE);
+ INIT_LIST_HEAD(&prog->actions);
if (oldprog) {
if (handle && oldprog->handle != handle) {
@@ -322,12 +321,12 @@ static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla));
- if (tcf_exts_dump(skb, &prog->exts) < 0)
+ if (tcf_act_dump(skb, tp, &prog->actions) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &prog->exts) < 0)
+ if (tcf_act_dump_stats(skb, &prog->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -356,6 +355,8 @@ static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg)
static struct tcf_proto_ops cls_bpf_ops __read_mostly = {
.kind = "bpf",
+ .action = TCA_BPF_ACT,
+ .police = TCA_BPF_POLICE,
.owner = THIS_MODULE,
.classify = cls_bpf_classify,
.init = cls_bpf_init,
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 3409f16..9640b20 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -20,7 +20,7 @@
struct cls_cgroup_head {
u32 handle;
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_ematch_tree ematches;
struct tcf_proto *tp;
struct rcu_head rcu;
@@ -59,7 +59,7 @@ static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp,
res->classid = classid;
res->class = 0;
- return tcf_exts_exec(skb, &head->exts, res);
+ return tcf_act_exec(skb, &head->actions, res);
}
static unsigned long cls_cgroup_get(struct tcf_proto *tp, u32 handle)
@@ -86,7 +86,7 @@ static void cls_cgroup_destroy_rcu(struct rcu_head *root)
struct cls_cgroup_head,
rcu);
- tcf_exts_destroy(&head->exts);
+ tcf_act_destroy(&head->actions);
tcf_em_tree_destroy(head->tp, &head->ematches);
kfree(head);
}
@@ -100,7 +100,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
struct cls_cgroup_head *head = rtnl_dereference(tp->root);
struct cls_cgroup_head *new;
struct tcf_ematch_tree t;
- struct tcf_exts e;
+ struct list_head actions;
int err;
if (!tca[TCA_OPTIONS])
@@ -116,7 +116,6 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
if (!new)
return -ENOBUFS;
- tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
if (head)
new->handle = head->handle;
else
@@ -128,18 +127,17 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
goto errout;
- tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
+ err = tcf_act_validate(net, tp, tb, tca[TCA_RATE], &actions, ovr);
if (err < 0)
goto errout;
err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t);
if (err < 0) {
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
goto errout;
}
- tcf_exts_change(tp, &new->exts, &e);
+ tcf_act_change(tp, &new->actions, &actions);
tcf_em_tree_change(tp, &new->ematches, &t);
rcu_assign_pointer(tp->root, new);
@@ -156,7 +154,7 @@ static void cls_cgroup_destroy(struct tcf_proto *tp)
struct cls_cgroup_head *head = rtnl_dereference(tp->root);
if (head) {
- tcf_exts_destroy(&head->exts);
+ tcf_act_destroy(&head->actions);
tcf_em_tree_destroy(tp, &head->ematches);
RCU_INIT_POINTER(tp->root, NULL);
kfree_rcu(head, rcu);
@@ -196,13 +194,13 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
if (nest == NULL)
goto nla_put_failure;
- if (tcf_exts_dump(skb, &head->exts) < 0 ||
+ if (tcf_act_dump(skb, tp, &head->actions) < 0 ||
tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &head->exts) < 0)
+ if (tcf_act_dump_stats(skb, &head->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -214,6 +212,8 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
.kind = "cgroup",
+ .action = TCA_CGROUP_ACT,
+ .police = TCA_CGROUP_POLICE,
.init = cls_cgroup_init,
.change = cls_cgroup_change,
.classify = cls_cgroup_classify,
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index f18d27f7..5cbc556 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -39,7 +39,7 @@ struct flow_head {
struct flow_filter {
struct list_head list;
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_ematch_tree ematches;
struct tcf_proto *tp;
struct timer_list perturb_timer;
@@ -317,7 +317,7 @@ static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp,
res->class = 0;
res->classid = TC_H_MAKE(f->baseclass, f->baseclass + classid);
- r = tcf_exts_exec(skb, &f->exts, res);
+ r = tcf_act_exec(skb, &f->actions, res);
if (r < 0)
continue;
return r;
@@ -354,7 +354,7 @@ static void flow_destroy_filter(struct rcu_head *head)
struct flow_filter *f = container_of(head, struct flow_filter, rcu);
del_timer_sync(&f->perturb_timer);
- tcf_exts_destroy(&f->exts);
+ tcf_act_destroy(&f->actions);
tcf_em_tree_destroy(f->tp, &f->ematches);
kfree(f);
}
@@ -368,7 +368,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
struct flow_filter *fold, *fnew;
struct nlattr *opt = tca[TCA_OPTIONS];
struct nlattr *tb[TCA_FLOW_MAX + 1];
- struct tcf_exts e;
+ struct list_head actions;
struct tcf_ematch_tree t;
unsigned int nkeys = 0;
unsigned int perturb_period = 0;
@@ -405,8 +405,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
return -EOPNOTSUPP;
}
- tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
+ err = tcf_act_validate(net, tp, tb, tca[TCA_RATE], &actions, ovr);
if (err < 0)
return err;
@@ -483,14 +482,14 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
fnew->mask = ~0U;
fnew->tp = tp;
get_random_bytes(&fnew->hashrnd, 4);
- tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
+ INIT_LIST_HEAD(&fnew->actions);
}
fnew->perturb_timer.function = flow_perturbation;
fnew->perturb_timer.data = (unsigned long)fnew;
init_timer_deferrable(&fnew->perturb_timer);
- tcf_exts_change(tp, &fnew->exts, &e);
+ tcf_act_change(tp, &fnew->actions, &actions);
tcf_em_tree_change(tp, &fnew->ematches, &t);
if (tb[TCA_FLOW_KEYS]) {
@@ -533,7 +532,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
tcf_em_tree_destroy(tp, &t);
kfree(fnew);
err1:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -628,7 +627,7 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts) < 0)
+ if (tcf_act_dump(skb, tp, &f->actions) < 0)
goto nla_put_failure;
#ifdef CONFIG_NET_EMATCH
if (f->ematches.hdr.nmatches &&
@@ -637,7 +636,7 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
#endif
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
+ if (tcf_act_dump_stats(skb, &f->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -666,6 +665,8 @@ static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg)
static struct tcf_proto_ops cls_flow_ops __read_mostly = {
.kind = "flow",
+ .action = TCA_FLOW_ACT,
+ .police = TCA_FLOW_POLICE,
.classify = flow_classify,
.init = flow_init,
.destroy = flow_destroy,
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index da805ae..ba955c4 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -44,7 +44,7 @@ struct fw_filter {
#ifdef CONFIG_NET_CLS_IND
int ifindex;
#endif /* CONFIG_NET_CLS_IND */
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_proto *tp;
struct rcu_head rcu;
};
@@ -75,7 +75,7 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp,
if (!tcf_match_indev(skb, f->ifindex))
continue;
#endif /* CONFIG_NET_CLS_IND */
- r = tcf_exts_exec(skb, &f->exts, res);
+ r = tcf_act_exec(skb, &f->actions, res);
if (r < 0)
continue;
@@ -126,7 +126,7 @@ static void fw_delete_filter(struct rcu_head *head)
struct tcf_proto *tp = f->tp;
tcf_unbind_filter(tp, &f->res);
- tcf_exts_destroy(&f->exts);
+ tcf_act_destroy(&f->actions);
kfree(f);
}
@@ -185,12 +185,11 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
struct nlattr **tb, struct nlattr **tca, unsigned long base, bool ovr)
{
struct fw_head *head = rtnl_dereference(tp->root);
- struct tcf_exts e;
+ struct list_head actions;
u32 mask;
int err;
- tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE);
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
+ err = tcf_act_validate(net, tp, tb, tca[TCA_RATE], &actions, ovr);
if (err < 0)
return err;
@@ -219,11 +218,11 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
} else if (head->mask != 0xFFFFFFFF)
goto errout;
- tcf_exts_change(tp, &f->exts, &e);
+ tcf_act_change(tp, &f->actions, &actions);
return 0;
errout:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -264,7 +263,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
#endif /* CONFIG_NET_CLS_IND */
fnew->tp = f->tp;
- tcf_exts_init(&fnew->exts, TCA_FW_ACT, TCA_FW_POLICE);
+ INIT_LIST_HEAD(&fnew->actions);
err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr);
if (err < 0) {
@@ -306,7 +305,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
return -ENOBUFS;
- tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE);
+ INIT_LIST_HEAD(&f->actions);
f->id = handle;
f->tp = tp;
@@ -367,7 +366,7 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
t->tcm_handle = f->id;
- if (!f->res.classid && !tcf_exts_is_available(&f->exts))
+ if (!f->res.classid && !tcf_act_is_available(&f->actions))
return skb->len;
nest = nla_nest_start(skb, TCA_OPTIONS);
@@ -389,12 +388,12 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_FW_MASK, head->mask))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts) < 0)
+ if (tcf_act_dump(skb, tp, &f->actions) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
+ if (tcf_act_dump_stats(skb, &f->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -406,6 +405,8 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops cls_fw_ops __read_mostly = {
.kind = "fw",
+ .action = TCA_FW_ACT,
+ .police = TCA_FW_POLICE,
.classify = fw_classify,
.init = fw_init,
.destroy = fw_destroy,
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index b665aee..03b6e20 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -53,7 +53,7 @@ struct route4_filter {
int iif;
struct tcf_result res;
- struct tcf_exts exts;
+ struct list_head actions;
u32 handle;
struct route4_bucket *bkt;
struct tcf_proto *tp;
@@ -113,8 +113,8 @@ static inline int route4_hash_wild(void)
#define ROUTE4_APPLY_RESULT() \
{ \
*res = f->res; \
- if (tcf_exts_is_available(&f->exts)) { \
- int r = tcf_exts_exec(skb, &f->exts, res); \
+ if (tcf_act_is_available(&f->actions)) { \
+ int r = tcf_act_exec(skb, &f->actions, res); \
if (r < 0) { \
dont_cache = 1; \
continue; \
@@ -272,7 +272,7 @@ route4_delete_filter(struct rcu_head *head)
struct tcf_proto *tp = f->tp;
tcf_unbind_filter(tp, &f->res);
- tcf_exts_destroy(&f->exts);
+ tcf_act_destroy(&f->actions);
kfree(f);
}
@@ -377,10 +377,9 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
struct route4_filter *fp;
unsigned int h1;
struct route4_bucket *b;
- struct tcf_exts e;
+ struct list_head actions;
- tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
- err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+ err = tcf_act_validate(net, tp, tb, est, &actions, ovr);
if (err < 0)
return err;
@@ -452,11 +451,11 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
tcf_bind_filter(tp, &f->res, base);
}
- tcf_exts_change(tp, &f->exts, &e);
+ tcf_act_change(tp, &f->actions, &actions);
return 0;
errout:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -499,7 +498,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
if (!f)
goto errout;
- tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
+ INIT_LIST_HEAD(&f->actions);
if (fold) {
f->id = fold->id;
f->iif = fold->iif;
@@ -625,12 +624,12 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts) < 0)
+ if (tcf_act_dump(skb, tp, &f->actions) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
+ if (tcf_act_dump_stats(skb, &f->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -642,6 +641,8 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops cls_route4_ops __read_mostly = {
.kind = "route",
+ .action = TCA_ROUTE4_ACT,
+ .police = TCA_ROUTE4_POLICE,
.classify = route4_classify,
.init = route4_init,
.destroy = route4_destroy,
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index 6bb55f2..64e5b31 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -93,7 +93,7 @@ struct rsvp_filter {
u8 tunnelhdr;
struct tcf_result res;
- struct tcf_exts exts;
+ struct list_head actions;
u32 handle;
struct rsvp_session *sess;
@@ -121,7 +121,7 @@ static inline unsigned int hash_src(__be32 *src)
#define RSVP_APPLY_RESULT() \
{ \
- int r = tcf_exts_exec(skb, &f->exts, res); \
+ int r = tcf_act_exec(skb, &f->actions, res); \
if (r < 0) \
continue; \
else if (r > 0) \
@@ -291,7 +291,7 @@ static void
rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f)
{
tcf_unbind_filter(tp, &f->res);
- tcf_exts_destroy(&f->exts);
+ tcf_act_destroy(&f->actions);
kfree_rcu(f, rcu);
}
@@ -461,7 +461,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
struct tc_rsvp_pinfo *pinfo = NULL;
struct nlattr *opt = tca[TCA_OPTIONS];
struct nlattr *tb[TCA_RSVP_MAX + 1];
- struct tcf_exts e;
+ struct list_head actions;
unsigned int h1, h2;
__be32 *dst;
int err;
@@ -473,8 +473,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
return err;
- tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
+ err = tcf_act_validate(net, tp, tb, tca[TCA_RATE], &actions, ovr);
if (err < 0)
return err;
@@ -492,14 +491,14 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
goto errout2;
}
- tcf_exts_init(&n->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
+ INIT_LIST_HEAD(&n->actions);
if (tb[TCA_RSVP_CLASSID]) {
n->res.classid = nla_get_u32(tb[TCA_RSVP_CLASSID]);
tcf_bind_filter(tp, &n->res, base);
}
- tcf_exts_change(tp, &n->exts, &e);
+ tcf_act_change(tp, &n->actions, &actions);
rsvp_replace(tp, n, handle);
return 0;
}
@@ -516,7 +515,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
goto errout2;
- tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
+ INIT_LIST_HEAD(&f->actions);
h2 = 16;
if (tb[TCA_RSVP_SRC]) {
memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
@@ -570,7 +569,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
if (f->tunnelhdr == 0)
tcf_bind_filter(tp, &f->res, base);
- tcf_exts_change(tp, &f->exts, &e);
+ tcf_act_change(tp, &f->actions, &actions);
fp = &s->ht[h2];
for (nfp = rtnl_dereference(*fp); nfp;
@@ -615,7 +614,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
errout:
kfree(f);
errout2:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -688,12 +687,12 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts) < 0)
+ if (tcf_act_dump(skb, tp, &f->actions) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts) < 0)
+ if (tcf_act_dump_stats(skb, &f->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -704,6 +703,8 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops RSVP_OPS __read_mostly = {
.kind = RSVP_ID,
+ .action = TCA_RSVP_ACT,
+ .police = TCA_RSVP_POLICE,
.classify = rsvp_classify,
.init = rsvp_init,
.destroy = rsvp_destroy,
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 30f10fb..c584195 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -25,7 +25,7 @@
struct tcindex_filter_result {
- struct tcf_exts exts;
+ struct list_head actions;
struct tcf_result res;
};
@@ -52,7 +52,7 @@ struct tcindex_data {
static inline int
tcindex_filter_is_set(struct tcindex_filter_result *r)
{
- return tcf_exts_is_predicative(&r->exts) || r->res.classid;
+ return tcf_act_is_predicative(&r->actions) || r->res.classid;
}
static struct tcindex_filter_result *
@@ -100,7 +100,7 @@ static int tcindex_classify(struct sk_buff *skb, const struct tcf_proto *tp,
*res = f->res;
pr_debug("map 0x%x\n", res->classid);
- return tcf_exts_exec(skb, &f->exts, res);
+ return tcf_act_exec(skb, &f->actions, res);
}
@@ -169,7 +169,7 @@ tcindex_delete(struct tcf_proto *tp, unsigned long arg)
rcu_assign_pointer(*walk, rtnl_dereference(f->next));
}
tcf_unbind_filter(tp, &r->res);
- tcf_exts_destroy(&r->exts);
+ tcf_act_destroy(&r->actions);
if (f)
kfree_rcu(f, rcu);
return 0;
@@ -208,7 +208,7 @@ static const struct nla_policy tcindex_policy[TCA_TCINDEX_MAX + 1] = {
static void tcindex_filter_result_init(struct tcindex_filter_result *r)
{
memset(r, 0, sizeof(*r));
- tcf_exts_init(&r->exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+ INIT_LIST_HEAD(&r->actions);
}
static void __tcindex_partial_destroy(struct rcu_head *head)
@@ -230,10 +230,9 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
struct tcindex_filter_result cr;
struct tcindex_data *cp, *oldp;
struct tcindex_filter *f = NULL; /* make gcc behave */
- struct tcf_exts e;
+ struct list_head actions;
- tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
- err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+ err = tcf_act_validate(net, tp, tb, est, &actions, ovr);
if (err < 0)
return err;
@@ -261,8 +260,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
if (!cp->perfect)
goto errout;
for (i = 0; i < cp->hash; i++)
- tcf_exts_init(&cp->perfect[i].exts,
- TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+ INIT_LIST_HEAD(&cp->perfect[i].actions);
balloc = 1;
}
cp->h = p->h;
@@ -330,9 +328,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
if (!cp->perfect)
goto errout_alloc;
for (i = 0; i < cp->hash; i++)
- tcf_exts_init(&cp->perfect[i].exts,
- TCA_TCINDEX_ACT,
- TCA_TCINDEX_POLICE);
+ INIT_LIST_HEAD(&cp->perfect[i].actions);
balloc = 1;
} else {
struct tcindex_filter __rcu **hash;
@@ -369,9 +365,9 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
}
if (old_r)
- tcf_exts_change(tp, &r->exts, &e);
+ tcf_act_change(tp, &r->actions, &actions);
else
- tcf_exts_change(tp, &cr.exts, &e);
+ tcf_act_change(tp, &cr.actions, &actions);
if (old_r && old_r != r)
tcindex_filter_result_init(old_r);
@@ -384,7 +380,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
struct tcindex_filter *nfp;
struct tcindex_filter __rcu **fp;
- tcf_exts_change(tp, &f->result.exts, &r->exts);
+ tcf_act_change(tp, &f->result.actions, &r->actions);
fp = cp->h + (handle % cp->hash);
for (nfp = rtnl_dereference(*fp);
@@ -406,7 +402,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
kfree(cp->h);
errout:
kfree(cp);
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -539,11 +535,11 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_TCINDEX_CLASSID, r->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &r->exts) < 0)
+ if (tcf_act_dump(skb, tp, &r->actions) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &r->exts) < 0)
+ if (tcf_act_dump_stats(skb, &r->actions) < 0)
goto nla_put_failure;
}
@@ -556,6 +552,8 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops cls_tcindex_ops __read_mostly = {
.kind = "tcindex",
+ .action = TCA_TCINDEX_ACT,
+ .police = TCA_TCINDEX_POLICE,
.classify = tcindex_classify,
.init = tcindex_init,
.destroy = tcindex_destroy,
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 0472909..973e7f3 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -48,7 +48,7 @@ struct tc_u_knode {
struct tc_u_knode __rcu *next;
u32 handle;
struct tc_u_hnode __rcu *ht_up;
- struct tcf_exts exts;
+ struct list_head actions;
#ifdef CONFIG_NET_CLS_IND
int ifindex;
#endif
@@ -173,7 +173,7 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct
#ifdef CONFIG_CLS_U32_PERF
__this_cpu_inc(n->pf->rhit);
#endif
- r = tcf_exts_exec(skb, &n->exts, res);
+ r = tcf_act_exec(skb, &n->actions, res);
if (r < 0) {
n = rcu_dereference_bh(n->next);
goto next_knode;
@@ -358,7 +358,7 @@ static int u32_destroy_key(struct tcf_proto *tp,
struct tc_u_knode *n,
bool free_pf)
{
- tcf_exts_destroy(&n->exts);
+ tcf_act_destroy(&n->actions);
if (n->ht_down)
n->ht_down->refcnt--;
#ifdef CONFIG_CLS_U32_PERF
@@ -560,10 +560,9 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
struct nlattr *est, bool ovr)
{
int err;
- struct tcf_exts e;
+ struct list_head actions;
- tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE);
- err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
+ err = tcf_act_validate(net, tp, tb, est, &actions, ovr);
if (err < 0)
return err;
@@ -603,11 +602,11 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
n->ifindex = ret;
}
#endif
- tcf_exts_change(tp, &n->exts, &e);
+ tcf_act_change(tp, &n->actions, &actions);
return 0;
errout:
- tcf_exts_destroy(&e);
+ tcf_act_destroy(&actions);
return err;
}
@@ -681,8 +680,7 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp,
#endif
new->tp = tp;
memcpy(&new->sel, s, sizeof(*s) + s->nkeys*sizeof(struct tc_u32_key));
-
- tcf_exts_init(&new->exts, TCA_U32_ACT, TCA_U32_POLICE);
+ INIT_LIST_HEAD(&new->actions);
return new;
}
@@ -810,7 +808,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
RCU_INIT_POINTER(n->ht_up, ht);
n->handle = handle;
n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
- tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE);
+ INIT_LIST_HEAD(&n->actions);
n->tp = tp;
#ifdef CONFIG_CLS_U32_MARK
@@ -965,7 +963,7 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
}
#endif
- if (tcf_exts_dump(skb, &n->exts) < 0)
+ if (tcf_act_dump(skb, tp, &n->actions) < 0)
goto nla_put_failure;
#ifdef CONFIG_NET_CLS_IND
@@ -1006,7 +1004,7 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
nla_nest_end(skb, nest);
if (TC_U32_KEY(n->handle))
- if (tcf_exts_dump_stats(skb, &n->exts) < 0)
+ if (tcf_act_dump_stats(skb, &n->actions) < 0)
goto nla_put_failure;
return skb->len;
@@ -1017,6 +1015,8 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
static struct tcf_proto_ops cls_u32_ops __read_mostly = {
.kind = "u32",
+ .action = TCA_U32_ACT,
+ .police = TCA_U32_POLICE,
.classify = u32_classify,
.init = u32_init,
.destroy = u32_destroy,
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCHv1] xen-netfront: always keep the Rx ring full of requests
From: David Miller @ 2014-10-03 22:54 UTC (permalink / raw)
To: david.vrabel; +Cc: netdev, xen-devel, konrad.wilk, boris.ostrovsky
In-Reply-To: <1412256826-18874-1-git-send-email-david.vrabel@citrix.com>
From: David Vrabel <david.vrabel@citrix.com>
Date: Thu, 2 Oct 2014 14:33:46 +0100
> A full Rx ring only requires 1 MiB of memory. This is not enough
> memory that it is useful to dynamically scale the number of Rx
> requests in the ring based on traffic rates.
>
> Keeping the ring full of Rx requests handles bursty traffic better
> than trying to converges on an optimal number of requests to keep
> filled.
>
> On a 4 core host, an iperf -P 64 -t 60 run from dom0 to a 4 VCPU guest
> improved from 5.1 Gbit/s to 5.6 Gbit/s. Gains with more bursty
> traffic are expected to be higher.
>
> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Can I get an ACK from someone else knowledgable about this area?
Thanks!
^ permalink raw reply
* Re: [PATCH net-next] net: do not export skb_gro_receive()
From: David Miller @ 2014-10-03 22:54 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1412260726.16704.99.camel@edumazet-glaptop2.roam.corp.google.com>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Thu, 02 Oct 2014 07:38:46 -0700
> From: Eric Dumazet <edumazet@google.com>
>
> skb_gro_receive() is only called from tcp_gro_receive() which is
> not in a module.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Applied, thanks Eric.
^ permalink raw reply
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