Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jesse/openvswitch
Jesse Gross says: ==================== A number of significant new features and optimizations for net-next/3.12. Highlights are: * "Megaflows", an optimization that allows userspace to specify which flow fields were used to compute the results of the flow lookup. This allows for a major reduction in flow setups (the major performance bottleneck in Open vSwitch) without reducing flexibility. * Converting netlink dump operations to use RCU, allowing for additional parallelism in userspace. * Matching and modifying SCTP protocol fields. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
5b2941b18d
13 changed files with 1365 additions and 562 deletions
|
@ -91,6 +91,46 @@ Often we ellipsize arguments not important to the discussion, e.g.:
|
|||
in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
|
||||
|
||||
|
||||
Wildcarded flow key format
|
||||
--------------------------
|
||||
|
||||
A wildcarded flow is described with two sequences of Netlink attributes
|
||||
passed over the Netlink socket. A flow key, exactly as described above, and an
|
||||
optional corresponding flow mask.
|
||||
|
||||
A wildcarded flow can represent a group of exact match flows. Each '1' bit
|
||||
in the mask specifies a exact match with the corresponding bit in the flow key.
|
||||
A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit
|
||||
of a incoming packet. Using wildcarded flow can improve the flow set up rate
|
||||
by reduce the number of new flows need to be processed by the user space program.
|
||||
|
||||
Support for the mask Netlink attribute is optional for both the kernel and user
|
||||
space program. The kernel can ignore the mask attribute, installing an exact
|
||||
match flow, or reduce the number of don't care bits in the kernel to less than
|
||||
what was specified by the user space program. In this case, variations in bits
|
||||
that the kernel does not implement will simply result in additional flow setups.
|
||||
The kernel module will also work with user space programs that neither support
|
||||
nor supply flow mask attributes.
|
||||
|
||||
Since the kernel may ignore or modify wildcard bits, it can be difficult for
|
||||
the userspace program to know exactly what matches are installed. There are
|
||||
two possible approaches: reactively install flows as they miss the kernel
|
||||
flow table (and therefore not attempt to determine wildcard changes at all)
|
||||
or use the kernel's response messages to determine the installed wildcards.
|
||||
|
||||
When interacting with userspace, the kernel should maintain the match portion
|
||||
of the key exactly as originally installed. This will provides a handle to
|
||||
identify the flow for all future operations. However, when reporting the
|
||||
mask of an installed flow, the mask should include any restrictions imposed
|
||||
by the kernel.
|
||||
|
||||
The behavior when using overlapping wildcarded flows is undefined. It is the
|
||||
responsibility of the user space program to ensure that any incoming packet
|
||||
can match at most one flow, wildcarded or not. The current implementation
|
||||
performs best-effort detection of overlapping wildcarded flows and may reject
|
||||
some but not all of them. However, this behavior may change in future versions.
|
||||
|
||||
|
||||
Basic rule for evolving flow keys
|
||||
---------------------------------
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#define NEXTHDR_ICMP 58 /* ICMP for IPv6. */
|
||||
#define NEXTHDR_NONE 59 /* No next header */
|
||||
#define NEXTHDR_DEST 60 /* Destination options header. */
|
||||
#define NEXTHDR_SCTP 132 /* SCTP message. */
|
||||
#define NEXTHDR_MOBILITY 135 /* Mobility header. */
|
||||
|
||||
#define NEXTHDR_MAX 255
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
/*
|
||||
* Copyright (c) 2007-2011 Nicira Networks.
|
||||
* Copyright (c) 2007-2013 Nicira, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
|
@ -259,6 +259,7 @@ enum ovs_key_attr {
|
|||
OVS_KEY_ATTR_ND, /* struct ovs_key_nd */
|
||||
OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */
|
||||
OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */
|
||||
OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */
|
||||
|
@ -333,6 +334,11 @@ struct ovs_key_udp {
|
|||
__be16 udp_dst;
|
||||
};
|
||||
|
||||
struct ovs_key_sctp {
|
||||
__be16 sctp_src;
|
||||
__be16 sctp_dst;
|
||||
};
|
||||
|
||||
struct ovs_key_icmp {
|
||||
__u8 icmp_type;
|
||||
__u8 icmp_code;
|
||||
|
@ -379,6 +385,12 @@ struct ovs_key_nd {
|
|||
* @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the
|
||||
* last-used time, accumulated TCP flags, and statistics for this flow.
|
||||
* Otherwise ignored in requests. Never present in notifications.
|
||||
* @OVS_FLOW_ATTR_MASK: Nested %OVS_KEY_ATTR_* attributes specifying the
|
||||
* mask bits for wildcarded flow match. Mask bit value '1' specifies exact
|
||||
* match with corresponding flow key bit, while mask bit value '0' specifies
|
||||
* a wildcarded match. Omitting attribute is treated as wildcarding all
|
||||
* corresponding fields. Optional for all requests. If not present,
|
||||
* all flow key bits are exact match bits.
|
||||
*
|
||||
* These attributes follow the &struct ovs_header within the Generic Netlink
|
||||
* payload for %OVS_FLOW_* commands.
|
||||
|
@ -391,6 +403,7 @@ enum ovs_flow_attr {
|
|||
OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */
|
||||
OVS_FLOW_ATTR_USED, /* u64 msecs last used in monotonic time. */
|
||||
OVS_FLOW_ATTR_CLEAR, /* Flag to clear stats, tcp_flags, used. */
|
||||
OVS_FLOW_ATTR_MASK, /* Sequence of OVS_KEY_ATTR_* attributes. */
|
||||
__OVS_FLOW_ATTR_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
config OPENVSWITCH
|
||||
tristate "Open vSwitch"
|
||||
select LIBCRC32C
|
||||
---help---
|
||||
Open vSwitch is a multilayer Ethernet switch targeted at virtualized
|
||||
environments. In addition to supporting a variety of features
|
||||
|
|
|
@ -10,10 +10,13 @@ openvswitch-y := \
|
|||
dp_notify.o \
|
||||
flow.o \
|
||||
vport.o \
|
||||
vport-gre.o \
|
||||
vport-internal_dev.o \
|
||||
vport-netdev.o
|
||||
|
||||
ifneq ($(CONFIG_OPENVSWITCH_VXLAN),)
|
||||
openvswitch-y += vport-vxlan.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_OPENVSWITCH_GRE),)
|
||||
openvswitch-y += vport-gre.o
|
||||
endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2012 Nicira, Inc.
|
||||
* Copyright (c) 2007-2013 Nicira, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/openvswitch.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in6.h>
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include <net/ipv6.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/dsfield.h>
|
||||
#include <net/sctp/checksum.h>
|
||||
|
||||
#include "datapath.h"
|
||||
#include "vport.h"
|
||||
|
@ -352,6 +354,39 @@ static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int set_sctp(struct sk_buff *skb,
|
||||
const struct ovs_key_sctp *sctp_port_key)
|
||||
{
|
||||
struct sctphdr *sh;
|
||||
int err;
|
||||
unsigned int sctphoff = skb_transport_offset(skb);
|
||||
|
||||
err = make_writable(skb, sctphoff + sizeof(struct sctphdr));
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
|
||||
sh = sctp_hdr(skb);
|
||||
if (sctp_port_key->sctp_src != sh->source ||
|
||||
sctp_port_key->sctp_dst != sh->dest) {
|
||||
__le32 old_correct_csum, new_csum, old_csum;
|
||||
|
||||
old_csum = sh->checksum;
|
||||
old_correct_csum = sctp_compute_cksum(skb, sctphoff);
|
||||
|
||||
sh->source = sctp_port_key->sctp_src;
|
||||
sh->dest = sctp_port_key->sctp_dst;
|
||||
|
||||
new_csum = sctp_compute_cksum(skb, sctphoff);
|
||||
|
||||
/* Carry any checksum errors through. */
|
||||
sh->checksum = old_csum ^ old_correct_csum ^ new_csum;
|
||||
|
||||
skb->rxhash = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
|
||||
{
|
||||
struct vport *vport;
|
||||
|
@ -376,8 +411,10 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
|
|||
const struct nlattr *a;
|
||||
int rem;
|
||||
|
||||
BUG_ON(!OVS_CB(skb)->pkt_key);
|
||||
|
||||
upcall.cmd = OVS_PACKET_CMD_ACTION;
|
||||
upcall.key = &OVS_CB(skb)->flow->key;
|
||||
upcall.key = OVS_CB(skb)->pkt_key;
|
||||
upcall.userdata = NULL;
|
||||
upcall.portid = 0;
|
||||
|
||||
|
@ -459,6 +496,10 @@ static int execute_set_action(struct sk_buff *skb,
|
|||
case OVS_KEY_ATTR_UDP:
|
||||
err = set_udp(skb, nla_data(nested_attr));
|
||||
break;
|
||||
|
||||
case OVS_KEY_ATTR_SCTP:
|
||||
err = set_sctp(skb, nla_data(nested_attr));
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2012 Nicira, Inc.
|
||||
* Copyright (c) 2007-2013 Nicira, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
|
@ -165,7 +165,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
|
|||
{
|
||||
struct datapath *dp = container_of(rcu, struct datapath, rcu);
|
||||
|
||||
ovs_flow_tbl_destroy((__force struct flow_table *)dp->table);
|
||||
ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
|
||||
free_percpu(dp->stats_percpu);
|
||||
release_net(ovs_dp_get_net(dp));
|
||||
kfree(dp->ports);
|
||||
|
@ -226,19 +226,18 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
|
|||
struct sw_flow_key key;
|
||||
u64 *stats_counter;
|
||||
int error;
|
||||
int key_len;
|
||||
|
||||
stats = this_cpu_ptr(dp->stats_percpu);
|
||||
|
||||
/* Extract flow from 'skb' into 'key'. */
|
||||
error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
|
||||
error = ovs_flow_extract(skb, p->port_no, &key);
|
||||
if (unlikely(error)) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Look up flow. */
|
||||
flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len);
|
||||
flow = ovs_flow_lookup(rcu_dereference(dp->table), &key);
|
||||
if (unlikely(!flow)) {
|
||||
struct dp_upcall_info upcall;
|
||||
|
||||
|
@ -253,6 +252,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
OVS_CB(skb)->flow = flow;
|
||||
OVS_CB(skb)->pkt_key = &key;
|
||||
|
||||
stats_counter = &stats->n_hit;
|
||||
ovs_flow_used(OVS_CB(skb)->flow, skb);
|
||||
|
@ -435,7 +435,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
|
|||
upcall->dp_ifindex = dp_ifindex;
|
||||
|
||||
nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
|
||||
ovs_flow_to_nlattrs(upcall_info->key, user_skb);
|
||||
ovs_flow_to_nlattrs(upcall_info->key, upcall_info->key, user_skb);
|
||||
nla_nest_end(user_skb, nla);
|
||||
|
||||
if (upcall_info->userdata)
|
||||
|
@ -468,7 +468,7 @@ static int flush_flows(struct datapath *dp)
|
|||
|
||||
rcu_assign_pointer(dp->table, new_table);
|
||||
|
||||
ovs_flow_tbl_deferred_destroy(old_table);
|
||||
ovs_flow_tbl_destroy(old_table, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -611,10 +611,12 @@ static int validate_tp_port(const struct sw_flow_key *flow_key)
|
|||
static int validate_and_copy_set_tun(const struct nlattr *attr,
|
||||
struct sw_flow_actions **sfa)
|
||||
{
|
||||
struct ovs_key_ipv4_tunnel tun_key;
|
||||
struct sw_flow_match match;
|
||||
struct sw_flow_key key;
|
||||
int err, start;
|
||||
|
||||
err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &tun_key);
|
||||
ovs_match_init(&match, &key, NULL);
|
||||
err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -622,7 +624,8 @@ 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, &tun_key, sizeof(tun_key));
|
||||
err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key,
|
||||
sizeof(match.key->tun_key));
|
||||
add_nested_action_end(*sfa, start);
|
||||
|
||||
return err;
|
||||
|
@ -709,6 +712,12 @@ static int validate_set(const struct nlattr *a,
|
|||
|
||||
return validate_tp_port(flow_key);
|
||||
|
||||
case OVS_KEY_ATTR_SCTP:
|
||||
if (flow_key->ip.proto != IPPROTO_SCTP)
|
||||
return -EINVAL;
|
||||
|
||||
return validate_tp_port(flow_key);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -857,7 +866,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||
struct ethhdr *eth;
|
||||
int len;
|
||||
int err;
|
||||
int key_len;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] ||
|
||||
|
@ -890,11 +898,11 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||
if (IS_ERR(flow))
|
||||
goto err_kfree_skb;
|
||||
|
||||
err = ovs_flow_extract(packet, -1, &flow->key, &key_len);
|
||||
err = ovs_flow_extract(packet, -1, &flow->key);
|
||||
if (err)
|
||||
goto err_flow_free;
|
||||
|
||||
err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]);
|
||||
err = ovs_flow_metadata_from_nlattrs(flow, a[OVS_PACKET_ATTR_KEY]);
|
||||
if (err)
|
||||
goto err_flow_free;
|
||||
acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
|
||||
|
@ -908,6 +916,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||
goto err_flow_free;
|
||||
|
||||
OVS_CB(packet)->flow = flow;
|
||||
OVS_CB(packet)->pkt_key = &flow->key;
|
||||
packet->priority = flow->key.phy.priority;
|
||||
packet->mark = flow->key.phy.skb_mark;
|
||||
|
||||
|
@ -922,13 +931,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||
local_bh_enable();
|
||||
rcu_read_unlock();
|
||||
|
||||
ovs_flow_free(flow);
|
||||
ovs_flow_free(flow, false);
|
||||
return err;
|
||||
|
||||
err_unlock:
|
||||
rcu_read_unlock();
|
||||
err_flow_free:
|
||||
ovs_flow_free(flow);
|
||||
ovs_flow_free(flow, false);
|
||||
err_kfree_skb:
|
||||
kfree_skb(packet);
|
||||
err:
|
||||
|
@ -951,9 +960,10 @@ static struct genl_ops dp_packet_genl_ops[] = {
|
|||
|
||||
static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
|
||||
{
|
||||
struct flow_table *table;
|
||||
int i;
|
||||
struct flow_table *table = ovsl_dereference(dp->table);
|
||||
|
||||
table = rcu_dereference_check(dp->table, lockdep_ovsl_is_held());
|
||||
stats->n_flows = ovs_flow_tbl_count(table);
|
||||
|
||||
stats->n_hit = stats->n_missed = stats->n_lost = 0;
|
||||
|
@ -1044,7 +1054,8 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
|
|||
if (!start)
|
||||
return -EMSGSIZE;
|
||||
|
||||
err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key));
|
||||
err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
|
||||
nla_data(ovs_key));
|
||||
if (err)
|
||||
return err;
|
||||
nla_nest_end(skb, start);
|
||||
|
@ -1092,6 +1103,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
|
|||
{
|
||||
return NLMSG_ALIGN(sizeof(struct ovs_header))
|
||||
+ nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
|
||||
+ nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */
|
||||
+ nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
|
||||
+ nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
|
||||
+ nla_total_size(8) /* OVS_FLOW_ATTR_USED */
|
||||
|
@ -1104,7 +1116,6 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||
u32 seq, u32 flags, u8 cmd)
|
||||
{
|
||||
const int skb_orig_len = skb->len;
|
||||
const struct sw_flow_actions *sf_acts;
|
||||
struct nlattr *start;
|
||||
struct ovs_flow_stats stats;
|
||||
struct ovs_header *ovs_header;
|
||||
|
@ -1113,22 +1124,33 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||
u8 tcp_flags;
|
||||
int err;
|
||||
|
||||
sf_acts = ovsl_dereference(flow->sf_acts);
|
||||
|
||||
ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
|
||||
if (!ovs_header)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ovs_header->dp_ifindex = get_dpifindex(dp);
|
||||
|
||||
/* Fill flow key. */
|
||||
nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
|
||||
if (!nla)
|
||||
goto nla_put_failure;
|
||||
err = ovs_flow_to_nlattrs(&flow->key, skb);
|
||||
|
||||
err = ovs_flow_to_nlattrs(&flow->unmasked_key,
|
||||
&flow->unmasked_key, skb);
|
||||
if (err)
|
||||
goto error;
|
||||
nla_nest_end(skb, nla);
|
||||
|
||||
nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK);
|
||||
if (!nla)
|
||||
goto nla_put_failure;
|
||||
|
||||
err = ovs_flow_to_nlattrs(&flow->key, &flow->mask->key, skb);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
nla_nest_end(skb, nla);
|
||||
|
||||
spin_lock_bh(&flow->lock);
|
||||
used = flow->used;
|
||||
stats.n_packets = flow->packet_count;
|
||||
|
@ -1161,6 +1183,11 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
|
|||
*/
|
||||
start = nla_nest_start(skb, OVS_FLOW_ATTR_ACTIONS);
|
||||
if (start) {
|
||||
const struct sw_flow_actions *sf_acts;
|
||||
|
||||
sf_acts = rcu_dereference_check(flow->sf_acts,
|
||||
lockdep_ovsl_is_held());
|
||||
|
||||
err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb);
|
||||
if (!err)
|
||||
nla_nest_end(skb, start);
|
||||
|
@ -1211,20 +1238,24 @@ static int ovs_flow_cmd_new_or_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;
|
||||
struct sw_flow *flow;
|
||||
struct sw_flow_key key, masked_key;
|
||||
struct sw_flow *flow = NULL;
|
||||
struct sw_flow_mask mask;
|
||||
struct sk_buff *reply;
|
||||
struct datapath *dp;
|
||||
struct flow_table *table;
|
||||
struct sw_flow_actions *acts = NULL;
|
||||
struct sw_flow_match match;
|
||||
int error;
|
||||
int key_len;
|
||||
|
||||
/* Extract key. */
|
||||
error = -EINVAL;
|
||||
if (!a[OVS_FLOW_ATTR_KEY])
|
||||
goto error;
|
||||
error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
|
||||
|
||||
ovs_match_init(&match, &key, &mask);
|
||||
error = ovs_match_from_nlattrs(&match,
|
||||
a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
|
@ -1235,9 +1266,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
if (IS_ERR(acts))
|
||||
goto error;
|
||||
|
||||
error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0, &acts);
|
||||
if (error)
|
||||
ovs_flow_key_mask(&masked_key, &key, &mask);
|
||||
error = validate_and_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;
|
||||
}
|
||||
} else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
|
||||
error = -EINVAL;
|
||||
goto error;
|
||||
|
@ -1250,8 +1285,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
goto err_unlock_ovs;
|
||||
|
||||
table = ovsl_dereference(dp->table);
|
||||
flow = ovs_flow_tbl_lookup(table, &key, key_len);
|
||||
|
||||
/* Check if this is a duplicate flow */
|
||||
flow = ovs_flow_lookup(table, &key);
|
||||
if (!flow) {
|
||||
struct sw_flow_mask *mask_p;
|
||||
/* Bail out if we're not allowed to create a new flow. */
|
||||
error = -ENOENT;
|
||||
if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
|
||||
|
@ -1264,7 +1302,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
new_table = ovs_flow_tbl_expand(table);
|
||||
if (!IS_ERR(new_table)) {
|
||||
rcu_assign_pointer(dp->table, new_table);
|
||||
ovs_flow_tbl_deferred_destroy(table);
|
||||
ovs_flow_tbl_destroy(table, true);
|
||||
table = ovsl_dereference(dp->table);
|
||||
}
|
||||
}
|
||||
|
@ -1277,14 +1315,30 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
}
|
||||
clear_stats(flow);
|
||||
|
||||
flow->key = masked_key;
|
||||
flow->unmasked_key = key;
|
||||
|
||||
/* Make sure mask is unique in the system */
|
||||
mask_p = ovs_sw_flow_mask_find(table, &mask);
|
||||
if (!mask_p) {
|
||||
/* Allocate a new mask if none exsits. */
|
||||
mask_p = ovs_sw_flow_mask_alloc();
|
||||
if (!mask_p)
|
||||
goto err_flow_free;
|
||||
mask_p->key = mask.key;
|
||||
mask_p->range = mask.range;
|
||||
ovs_sw_flow_mask_insert(table, mask_p);
|
||||
}
|
||||
|
||||
ovs_sw_flow_mask_add_ref(mask_p);
|
||||
flow->mask = mask_p;
|
||||
rcu_assign_pointer(flow->sf_acts, acts);
|
||||
|
||||
/* Put flow in bucket. */
|
||||
ovs_flow_tbl_insert(table, flow, &key, key_len);
|
||||
ovs_flow_insert(table, flow);
|
||||
|
||||
reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
|
||||
info->snd_seq,
|
||||
OVS_FLOW_CMD_NEW);
|
||||
info->snd_seq, OVS_FLOW_CMD_NEW);
|
||||
} else {
|
||||
/* We found a matching flow. */
|
||||
struct sw_flow_actions *old_acts;
|
||||
|
@ -1300,6 +1354,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
|
||||
goto err_unlock_ovs;
|
||||
|
||||
/* The unmasked key has to be the same for flow updates. */
|
||||
error = -EINVAL;
|
||||
if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) {
|
||||
OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n");
|
||||
goto err_unlock_ovs;
|
||||
}
|
||||
|
||||
/* Update actions. */
|
||||
old_acts = ovsl_dereference(flow->sf_acts);
|
||||
rcu_assign_pointer(flow->sf_acts, acts);
|
||||
|
@ -1324,6 +1385,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
|
|||
ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
|
||||
return 0;
|
||||
|
||||
err_flow_free:
|
||||
ovs_flow_free(flow, false);
|
||||
err_unlock_ovs:
|
||||
ovs_unlock();
|
||||
err_kfree:
|
||||
|
@ -1341,12 +1404,16 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
|
|||
struct sw_flow *flow;
|
||||
struct datapath *dp;
|
||||
struct flow_table *table;
|
||||
struct sw_flow_match match;
|
||||
int err;
|
||||
int key_len;
|
||||
|
||||
if (!a[OVS_FLOW_ATTR_KEY])
|
||||
if (!a[OVS_FLOW_ATTR_KEY]) {
|
||||
OVS_NLERR("Flow get message rejected, Key attribute missing.\n");
|
||||
return -EINVAL;
|
||||
err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
|
||||
}
|
||||
|
||||
ovs_match_init(&match, &key, NULL);
|
||||
err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -1358,7 +1425,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
|
|||
}
|
||||
|
||||
table = ovsl_dereference(dp->table);
|
||||
flow = ovs_flow_tbl_lookup(table, &key, key_len);
|
||||
flow = ovs_flow_lookup_unmasked_key(table, &match);
|
||||
if (!flow) {
|
||||
err = -ENOENT;
|
||||
goto unlock;
|
||||
|
@ -1387,8 +1454,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
|||
struct sw_flow *flow;
|
||||
struct datapath *dp;
|
||||
struct flow_table *table;
|
||||
struct sw_flow_match match;
|
||||
int err;
|
||||
int key_len;
|
||||
|
||||
ovs_lock();
|
||||
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
|
||||
|
@ -1401,12 +1468,14 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
|||
err = flush_flows(dp);
|
||||
goto unlock;
|
||||
}
|
||||
err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
|
||||
|
||||
ovs_match_init(&match, &key, NULL);
|
||||
err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL);
|
||||
if (err)
|
||||
goto unlock;
|
||||
|
||||
table = ovsl_dereference(dp->table);
|
||||
flow = ovs_flow_tbl_lookup(table, &key, key_len);
|
||||
flow = ovs_flow_lookup_unmasked_key(table, &match);
|
||||
if (!flow) {
|
||||
err = -ENOENT;
|
||||
goto unlock;
|
||||
|
@ -1418,13 +1487,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
ovs_flow_tbl_remove(table, flow);
|
||||
ovs_flow_remove(table, flow);
|
||||
|
||||
err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid,
|
||||
info->snd_seq, 0, OVS_FLOW_CMD_DEL);
|
||||
BUG_ON(err < 0);
|
||||
|
||||
ovs_flow_deferred_free(flow);
|
||||
ovs_flow_free(flow, true);
|
||||
ovs_unlock();
|
||||
|
||||
ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
|
||||
|
@ -1440,22 +1509,21 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
struct datapath *dp;
|
||||
struct flow_table *table;
|
||||
|
||||
ovs_lock();
|
||||
rcu_read_lock();
|
||||
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
|
||||
if (!dp) {
|
||||
ovs_unlock();
|
||||
rcu_read_unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
table = ovsl_dereference(dp->table);
|
||||
|
||||
table = rcu_dereference(dp->table);
|
||||
for (;;) {
|
||||
struct sw_flow *flow;
|
||||
u32 bucket, obj;
|
||||
|
||||
bucket = cb->args[0];
|
||||
obj = cb->args[1];
|
||||
flow = ovs_flow_tbl_next(table, &bucket, &obj);
|
||||
flow = ovs_flow_dump_next(table, &bucket, &obj);
|
||||
if (!flow)
|
||||
break;
|
||||
|
||||
|
@ -1468,7 +1536,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
cb->args[0] = bucket;
|
||||
cb->args[1] = obj;
|
||||
}
|
||||
ovs_unlock();
|
||||
rcu_read_unlock();
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
|
@ -1664,7 +1732,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
|
|||
goto err_destroy_local_port;
|
||||
|
||||
ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
|
||||
list_add_tail(&dp->list_node, &ovs_net->dps);
|
||||
list_add_tail_rcu(&dp->list_node, &ovs_net->dps);
|
||||
|
||||
ovs_unlock();
|
||||
|
||||
|
@ -1678,7 +1746,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
|
|||
err_destroy_percpu:
|
||||
free_percpu(dp->stats_percpu);
|
||||
err_destroy_table:
|
||||
ovs_flow_tbl_destroy(ovsl_dereference(dp->table));
|
||||
ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false);
|
||||
err_free_dp:
|
||||
release_net(ovs_dp_get_net(dp));
|
||||
kfree(dp);
|
||||
|
@ -1702,7 +1770,7 @@ static void __dp_destroy(struct datapath *dp)
|
|||
ovs_dp_detach_port(vport);
|
||||
}
|
||||
|
||||
list_del(&dp->list_node);
|
||||
list_del_rcu(&dp->list_node);
|
||||
|
||||
/* OVSP_LOCAL is datapath internal port. We need to make sure that
|
||||
* all port in datapath are destroyed first before freeing datapath.
|
||||
|
@ -1807,8 +1875,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
int skip = cb->args[0];
|
||||
int i = 0;
|
||||
|
||||
ovs_lock();
|
||||
list_for_each_entry(dp, &ovs_net->dps, list_node) {
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) {
|
||||
if (i >= skip &&
|
||||
ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
|
@ -1816,7 +1884,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
break;
|
||||
i++;
|
||||
}
|
||||
ovs_unlock();
|
||||
rcu_read_unlock();
|
||||
|
||||
cb->args[0] = i;
|
||||
|
||||
|
@ -2285,7 +2353,7 @@ static void rehash_flow_table(struct work_struct *work)
|
|||
new_table = ovs_flow_tbl_rehash(old_table);
|
||||
if (!IS_ERR(new_table)) {
|
||||
rcu_assign_pointer(dp->table, new_table);
|
||||
ovs_flow_tbl_deferred_destroy(old_table);
|
||||
ovs_flow_tbl_destroy(old_table, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,11 +88,13 @@ struct datapath {
|
|||
/**
|
||||
* struct ovs_skb_cb - OVS data in skb CB
|
||||
* @flow: The flow associated with this packet. May be %NULL if no flow.
|
||||
* @pkt_key: The flow information extracted from the packet. Must be nonnull.
|
||||
* @tun_key: Key for the tunnel that encapsulated this packet. NULL if the
|
||||
* packet is not being tunneled.
|
||||
*/
|
||||
struct ovs_skb_cb {
|
||||
struct sw_flow *flow;
|
||||
struct sw_flow_key *pkt_key;
|
||||
struct ovs_key_ipv4_tunnel *tun_key;
|
||||
};
|
||||
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
|
||||
|
@ -183,4 +185,8 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
|
|||
|
||||
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
|
||||
void ovs_dp_notify_wq(struct work_struct *work);
|
||||
|
||||
#define OVS_NLERR(fmt, ...) \
|
||||
pr_info_once("netlink: " fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif /* datapath.h */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2007-2011 Nicira, Inc.
|
||||
* Copyright (c) 2007-2013 Nicira, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
|
@ -33,6 +33,8 @@
|
|||
#include <net/inet_ecn.h>
|
||||
|
||||
struct sk_buff;
|
||||
struct sw_flow_mask;
|
||||
struct flow_table;
|
||||
|
||||
struct sw_flow_actions {
|
||||
struct rcu_head rcu;
|
||||
|
@ -97,8 +99,8 @@ struct sw_flow_key {
|
|||
} addr;
|
||||
union {
|
||||
struct {
|
||||
__be16 src; /* TCP/UDP source port. */
|
||||
__be16 dst; /* TCP/UDP destination port. */
|
||||
__be16 src; /* TCP/UDP/SCTP source port. */
|
||||
__be16 dst; /* TCP/UDP/SCTP destination port. */
|
||||
} tp;
|
||||
struct {
|
||||
u8 sha[ETH_ALEN]; /* ARP source hardware address. */
|
||||
|
@ -113,8 +115,8 @@ struct sw_flow_key {
|
|||
} addr;
|
||||
__be32 label; /* IPv6 flow label. */
|
||||
struct {
|
||||
__be16 src; /* TCP/UDP source port. */
|
||||
__be16 dst; /* TCP/UDP destination port. */
|
||||
__be16 src; /* TCP/UDP/SCTP source port. */
|
||||
__be16 dst; /* TCP/UDP/SCTP destination port. */
|
||||
} tp;
|
||||
struct {
|
||||
struct in6_addr target; /* ND target address. */
|
||||
|
@ -123,7 +125,7 @@ struct sw_flow_key {
|
|||
} nd;
|
||||
} ipv6;
|
||||
};
|
||||
};
|
||||
} __aligned(__alignof__(long));
|
||||
|
||||
struct sw_flow {
|
||||
struct rcu_head rcu;
|
||||
|
@ -131,6 +133,8 @@ struct sw_flow {
|
|||
u32 hash;
|
||||
|
||||
struct sw_flow_key key;
|
||||
struct sw_flow_key unmasked_key;
|
||||
struct sw_flow_mask *mask;
|
||||
struct sw_flow_actions __rcu *sf_acts;
|
||||
|
||||
spinlock_t lock; /* Lock for values below. */
|
||||
|
@ -140,6 +144,20 @@ struct sw_flow {
|
|||
u8 tcp_flags; /* Union of seen TCP flags. */
|
||||
};
|
||||
|
||||
struct sw_flow_key_range {
|
||||
size_t start;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
struct sw_flow_match {
|
||||
struct sw_flow_key *key;
|
||||
struct sw_flow_key_range range;
|
||||
struct sw_flow_mask *mask;
|
||||
};
|
||||
|
||||
void ovs_match_init(struct sw_flow_match *match,
|
||||
struct sw_flow_key *key, struct sw_flow_mask *mask);
|
||||
|
||||
struct arp_eth_header {
|
||||
__be16 ar_hrd; /* format of hardware address */
|
||||
__be16 ar_pro; /* format of protocol address */
|
||||
|
@ -159,21 +177,21 @@ void ovs_flow_exit(void);
|
|||
|
||||
struct sw_flow *ovs_flow_alloc(void);
|
||||
void ovs_flow_deferred_free(struct sw_flow *);
|
||||
void ovs_flow_free(struct sw_flow *flow);
|
||||
void ovs_flow_free(struct sw_flow *, bool deferred);
|
||||
|
||||
struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
|
||||
void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
|
||||
|
||||
int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
|
||||
int *key_lenp);
|
||||
int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *);
|
||||
void ovs_flow_used(struct sw_flow *, struct sk_buff *);
|
||||
u64 ovs_flow_used_time(unsigned long flow_jiffies);
|
||||
|
||||
int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
|
||||
int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
|
||||
int ovs_flow_to_nlattrs(const struct sw_flow_key *,
|
||||
const struct sw_flow_key *, struct sk_buff *);
|
||||
int ovs_match_from_nlattrs(struct sw_flow_match *match,
|
||||
const struct nlattr *,
|
||||
const struct nlattr *);
|
||||
int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
|
||||
const struct nlattr *attr);
|
||||
int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow,
|
||||
const struct nlattr *attr);
|
||||
|
||||
#define MAX_ACTIONS_BUFSIZE (32 * 1024)
|
||||
#define TBL_MIN_BUCKETS 1024
|
||||
|
@ -182,6 +200,7 @@ struct flow_table {
|
|||
struct flex_array *buckets;
|
||||
unsigned int count, n_buckets;
|
||||
struct rcu_head rcu;
|
||||
struct list_head *mask_list;
|
||||
int node_ver;
|
||||
u32 hash_seed;
|
||||
bool keep_flows;
|
||||
|
@ -197,22 +216,44 @@ static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table)
|
|||
return (table->count > table->n_buckets);
|
||||
}
|
||||
|
||||
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table,
|
||||
struct sw_flow_key *key, int len);
|
||||
void ovs_flow_tbl_destroy(struct flow_table *table);
|
||||
void ovs_flow_tbl_deferred_destroy(struct flow_table *table);
|
||||
struct sw_flow *ovs_flow_lookup(struct flow_table *,
|
||||
const struct sw_flow_key *);
|
||||
struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table,
|
||||
struct sw_flow_match *match);
|
||||
|
||||
void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
|
||||
struct flow_table *ovs_flow_tbl_alloc(int new_size);
|
||||
struct flow_table *ovs_flow_tbl_expand(struct flow_table *table);
|
||||
struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table);
|
||||
void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
|
||||
struct sw_flow_key *key, int key_len);
|
||||
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
|
||||
|
||||
struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx);
|
||||
void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow);
|
||||
void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow);
|
||||
|
||||
struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx);
|
||||
extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
|
||||
int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr,
|
||||
struct ovs_key_ipv4_tunnel *tun_key);
|
||||
struct sw_flow_match *match, bool is_mask);
|
||||
int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb,
|
||||
const struct ovs_key_ipv4_tunnel *tun_key);
|
||||
const struct ovs_key_ipv4_tunnel *tun_key,
|
||||
const struct ovs_key_ipv4_tunnel *output);
|
||||
|
||||
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
|
||||
const struct sw_flow_key *key, int key_end);
|
||||
|
||||
struct sw_flow_mask {
|
||||
int ref_count;
|
||||
struct rcu_head rcu;
|
||||
struct list_head list;
|
||||
struct sw_flow_key_range range;
|
||||
struct sw_flow_key key;
|
||||
};
|
||||
|
||||
struct sw_flow_mask *ovs_sw_flow_mask_alloc(void);
|
||||
void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *);
|
||||
void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred);
|
||||
void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *);
|
||||
struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *,
|
||||
const struct sw_flow_mask *);
|
||||
void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src,
|
||||
const struct sw_flow_mask *mask);
|
||||
#endif /* flow.h */
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_OPENVSWITCH_GRE
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/if.h>
|
||||
|
@ -271,5 +270,3 @@ const struct vport_ops ovs_gre_vport_ops = {
|
|||
.get_name = gre_get_name,
|
||||
.send = gre_tnl_send,
|
||||
};
|
||||
|
||||
#endif /* OPENVSWITCH_GRE */
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/llc.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/openvswitch.h>
|
||||
|
||||
#include <net/llc.h>
|
||||
|
||||
|
@ -74,6 +75,15 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
|
|||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
static struct net_device *get_dpdev(struct datapath *dp)
|
||||
{
|
||||
struct vport *local;
|
||||
|
||||
local = ovs_vport_ovsl(dp, OVSP_LOCAL);
|
||||
BUG_ON(!local);
|
||||
return netdev_vport_priv(local)->dev;
|
||||
}
|
||||
|
||||
static struct vport *netdev_create(const struct vport_parms *parms)
|
||||
{
|
||||
struct vport *vport;
|
||||
|
@ -103,10 +113,15 @@ static struct vport *netdev_create(const struct vport_parms *parms)
|
|||
}
|
||||
|
||||
rtnl_lock();
|
||||
err = netdev_master_upper_dev_link(netdev_vport->dev,
|
||||
get_dpdev(vport->dp));
|
||||
if (err)
|
||||
goto error_unlock;
|
||||
|
||||
err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
|
||||
vport);
|
||||
if (err)
|
||||
goto error_unlock;
|
||||
goto error_master_upper_dev_unlink;
|
||||
|
||||
dev_set_promiscuity(netdev_vport->dev, 1);
|
||||
netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
|
||||
|
@ -114,6 +129,8 @@ static struct vport *netdev_create(const struct vport_parms *parms)
|
|||
|
||||
return vport;
|
||||
|
||||
error_master_upper_dev_unlink:
|
||||
netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
|
||||
error_unlock:
|
||||
rtnl_unlock();
|
||||
error_put:
|
||||
|
@ -140,6 +157,7 @@ static void netdev_destroy(struct vport *vport)
|
|||
rtnl_lock();
|
||||
netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
|
||||
netdev_rx_handler_unregister(netdev_vport->dev);
|
||||
netdev_upper_dev_unlink(netdev_vport->dev, get_dpdev(vport->dp));
|
||||
dev_set_promiscuity(netdev_vport->dev, -1);
|
||||
rtnl_unlock();
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
|
|||
* ovs_vport_set_options - modify existing vport device (for kernel callers)
|
||||
*
|
||||
* @vport: vport to modify.
|
||||
* @port: New configuration.
|
||||
* @options: New configuration.
|
||||
*
|
||||
* Modifies an existing device with the specified configuration (which is
|
||||
* dependent on device type). ovs_mutex must be held.
|
||||
|
@ -328,6 +328,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
|
|||
*
|
||||
* @vport: vport that received the packet
|
||||
* @skb: skb that was received
|
||||
* @tun_key: tunnel (if any) that carried packet
|
||||
*
|
||||
* Must be called with rcu_read_lock. The packet cannot be shared and
|
||||
* skb->data should point to the Ethernet header.
|
||||
|
|
Loading…
Reference in a new issue