Commit 8e4e1713 authored by Pravin B Shelar's avatar Pravin B Shelar Committed by Jesse Gross

openvswitch: Simplify datapath locking.

Currently OVS uses combination of genl and rtnl lock to protect
datapath state.  This was done due to networking stack locking.
But this has complicated locking and there are few lock ordering
issues with new tunneling protocols.
Following patch simplifies locking by introducing new ovs mutex
and now this lock is used to protect entire ovs state.
Signed-off-by: default avatarPravin B Shelar <pshelar@nicira.com>
Signed-off-by: default avatarJesse Gross <jesse@nicira.com>
parent b4f9e8cd
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/lockdep.h>
#include <linux/openvswitch.h> #include <linux/openvswitch.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/dmi.h> #include <linux/dmi.h>
...@@ -56,21 +57,13 @@ ...@@ -56,21 +57,13 @@
#include "flow.h" #include "flow.h"
#include "vport-internal_dev.h" #include "vport-internal_dev.h"
/**
* struct ovs_net - Per net-namespace data for ovs.
* @dps: List of datapaths to enable dumping them all out.
* Protected by genl_mutex.
*/
struct ovs_net {
struct list_head dps;
};
static int ovs_net_id __read_mostly;
#define REHASH_FLOW_INTERVAL (10 * 60 * HZ) #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
static void rehash_flow_table(struct work_struct *work); static void rehash_flow_table(struct work_struct *work);
static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table); static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
int ovs_net_id __read_mostly;
static void ovs_notify(struct sk_buff *skb, struct genl_info *info, static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
struct genl_multicast_group *grp) struct genl_multicast_group *grp)
{ {
...@@ -81,20 +74,42 @@ static void ovs_notify(struct sk_buff *skb, struct genl_info *info, ...@@ -81,20 +74,42 @@ static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
/** /**
* DOC: Locking: * DOC: Locking:
* *
* Writes to device state (add/remove datapath, port, set operations on vports, * All writes e.g. Writes to device state (add/remove datapath, port, set
* etc.) are protected by RTNL. * operations on vports, etc.), Writes to other state (flow table
* * modifications, set miscellaneous datapath parameters, etc.) are protected
* Writes to other state (flow table modifications, set miscellaneous datapath * by ovs_lock.
* parameters, etc.) are protected by genl_mutex. The RTNL lock nests inside
* genl_mutex.
* *
* Reads are protected by RCU. * Reads are protected by RCU.
* *
* There are a few special cases (mostly stats) that have their own * There are a few special cases (mostly stats) that have their own
* synchronization but they nest under all of above and don't interact with * synchronization but they nest under all of above and don't interact with
* each other. * each other.
*
* The RTNL lock nests inside ovs_mutex.
*/ */
static DEFINE_MUTEX(ovs_mutex);
void ovs_lock(void)
{
mutex_lock(&ovs_mutex);
}
void ovs_unlock(void)
{
mutex_unlock(&ovs_mutex);
}
#ifdef CONFIG_LOCKDEP
int lockdep_ovsl_is_held(void)
{
if (debug_locks)
return lockdep_is_held(&ovs_mutex);
else
return 1;
}
#endif
static struct vport *new_vport(const struct vport_parms *); static struct vport *new_vport(const struct vport_parms *);
static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *, static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *,
const struct dp_upcall_info *); const struct dp_upcall_info *);
...@@ -102,7 +117,7 @@ static int queue_userspace_packet(struct net *, int dp_ifindex, ...@@ -102,7 +117,7 @@ static int queue_userspace_packet(struct net *, int dp_ifindex,
struct sk_buff *, struct sk_buff *,
const struct dp_upcall_info *); const struct dp_upcall_info *);
/* Must be called with rcu_read_lock, genl_mutex, or RTNL lock. */ /* Must be called with rcu_read_lock or ovs_mutex. */
static struct datapath *get_dp(struct net *net, int dp_ifindex) static struct datapath *get_dp(struct net *net, int dp_ifindex)
{ {
struct datapath *dp = NULL; struct datapath *dp = NULL;
...@@ -120,10 +135,10 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex) ...@@ -120,10 +135,10 @@ static struct datapath *get_dp(struct net *net, int dp_ifindex)
return dp; return dp;
} }
/* Must be called with rcu_read_lock or RTNL lock. */ /* Must be called with rcu_read_lock or ovs_mutex. */
const char *ovs_dp_name(const struct datapath *dp) const char *ovs_dp_name(const struct datapath *dp)
{ {
struct vport *vport = ovs_vport_rtnl_rcu(dp, OVSP_LOCAL); struct vport *vport = ovs_vport_ovsl_rcu(dp, OVSP_LOCAL);
return vport->ops->get_name(vport); return vport->ops->get_name(vport);
} }
...@@ -175,7 +190,7 @@ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no) ...@@ -175,7 +190,7 @@ struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
return NULL; return NULL;
} }
/* Called with RTNL lock and genl_lock. */ /* Called with ovs_mutex. */
static struct vport *new_vport(const struct vport_parms *parms) static struct vport *new_vport(const struct vport_parms *parms)
{ {
struct vport *vport; struct vport *vport;
...@@ -187,14 +202,12 @@ static struct vport *new_vport(const struct vport_parms *parms) ...@@ -187,14 +202,12 @@ static struct vport *new_vport(const struct vport_parms *parms)
hlist_add_head_rcu(&vport->dp_hash_node, head); hlist_add_head_rcu(&vport->dp_hash_node, head);
} }
return vport; return vport;
} }
/* Called with RTNL lock. */
void ovs_dp_detach_port(struct vport *p) void ovs_dp_detach_port(struct vport *p)
{ {
ASSERT_RTNL(); ASSERT_OVSL();
/* First drop references to device. */ /* First drop references to device. */
hlist_del_rcu(&p->dp_hash_node); hlist_del_rcu(&p->dp_hash_node);
...@@ -432,13 +445,13 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, ...@@ -432,13 +445,13 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
return err; return err;
} }
/* Called with genl_mutex. */ /* Called with ovs_mutex. */
static int flush_flows(struct datapath *dp) static int flush_flows(struct datapath *dp)
{ {
struct flow_table *old_table; struct flow_table *old_table;
struct flow_table *new_table; struct flow_table *new_table;
old_table = genl_dereference(dp->table); old_table = ovsl_dereference(dp->table);
new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS); new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS);
if (!new_table) if (!new_table)
return -ENOMEM; return -ENOMEM;
...@@ -788,7 +801,7 @@ static struct genl_ops dp_packet_genl_ops[] = { ...@@ -788,7 +801,7 @@ static struct genl_ops dp_packet_genl_ops[] = {
static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats) static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats)
{ {
int i; int i;
struct flow_table *table = genl_dereference(dp->table); struct flow_table *table = ovsl_dereference(dp->table);
stats->n_flows = ovs_flow_tbl_count(table); stats->n_flows = ovs_flow_tbl_count(table);
...@@ -840,7 +853,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) ...@@ -840,7 +853,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
+ nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */ + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
} }
/* Called with genl_lock. */ /* Called with ovs_mutex. */
static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
struct sk_buff *skb, u32 portid, struct sk_buff *skb, u32 portid,
u32 seq, u32 flags, u8 cmd) u32 seq, u32 flags, u8 cmd)
...@@ -854,8 +867,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ...@@ -854,8 +867,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
u8 tcp_flags; u8 tcp_flags;
int err; int err;
sf_acts = rcu_dereference_protected(flow->sf_acts, sf_acts = ovsl_dereference(flow->sf_acts);
lockdep_genl_is_held());
ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd); ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
if (!ovs_header) if (!ovs_header)
...@@ -919,8 +931,7 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow) ...@@ -919,8 +931,7 @@ static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow)
{ {
const struct sw_flow_actions *sf_acts; const struct sw_flow_actions *sf_acts;
sf_acts = rcu_dereference_protected(flow->sf_acts, sf_acts = ovsl_dereference(flow->sf_acts);
lockdep_genl_is_held());
return genlmsg_new(ovs_flow_cmd_msg_size(sf_acts), GFP_KERNEL); return genlmsg_new(ovs_flow_cmd_msg_size(sf_acts), GFP_KERNEL);
} }
...@@ -971,12 +982,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -971,12 +982,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
goto error; goto error;
} }
ovs_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
error = -ENODEV; error = -ENODEV;
if (!dp) if (!dp)
goto error; goto err_unlock_ovs;
table = genl_dereference(dp->table); table = ovsl_dereference(dp->table);
flow = ovs_flow_tbl_lookup(table, &key, key_len); flow = ovs_flow_tbl_lookup(table, &key, key_len);
if (!flow) { if (!flow) {
struct sw_flow_actions *acts; struct sw_flow_actions *acts;
...@@ -984,7 +996,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -984,7 +996,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
/* Bail out if we're not allowed to create a new flow. */ /* Bail out if we're not allowed to create a new flow. */
error = -ENOENT; error = -ENOENT;
if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) if (info->genlhdr->cmd == OVS_FLOW_CMD_SET)
goto error; goto err_unlock_ovs;
/* Expand table, if necessary, to make room. */ /* Expand table, if necessary, to make room. */
if (ovs_flow_tbl_need_to_expand(table)) { if (ovs_flow_tbl_need_to_expand(table)) {
...@@ -994,7 +1006,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -994,7 +1006,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
if (!IS_ERR(new_table)) { if (!IS_ERR(new_table)) {
rcu_assign_pointer(dp->table, new_table); rcu_assign_pointer(dp->table, new_table);
ovs_flow_tbl_deferred_destroy(table); ovs_flow_tbl_deferred_destroy(table);
table = genl_dereference(dp->table); table = ovsl_dereference(dp->table);
} }
} }
...@@ -1002,7 +1014,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1002,7 +1014,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
flow = ovs_flow_alloc(); flow = ovs_flow_alloc();
if (IS_ERR(flow)) { if (IS_ERR(flow)) {
error = PTR_ERR(flow); error = PTR_ERR(flow);
goto error; goto err_unlock_ovs;
} }
flow->key = key; flow->key = key;
clear_stats(flow); clear_stats(flow);
...@@ -1035,11 +1047,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1035,11 +1047,10 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
error = -EEXIST; error = -EEXIST;
if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW && if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW &&
info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL))
goto error; goto err_unlock_ovs;
/* Update actions. */ /* Update actions. */
old_acts = rcu_dereference_protected(flow->sf_acts, old_acts = ovsl_dereference(flow->sf_acts);
lockdep_genl_is_held());
acts_attrs = a[OVS_FLOW_ATTR_ACTIONS]; acts_attrs = a[OVS_FLOW_ATTR_ACTIONS];
if (acts_attrs && if (acts_attrs &&
(old_acts->actions_len != nla_len(acts_attrs) || (old_acts->actions_len != nla_len(acts_attrs) ||
...@@ -1050,7 +1061,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1050,7 +1061,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
new_acts = ovs_flow_actions_alloc(acts_attrs); new_acts = ovs_flow_actions_alloc(acts_attrs);
error = PTR_ERR(new_acts); error = PTR_ERR(new_acts);
if (IS_ERR(new_acts)) if (IS_ERR(new_acts))
goto error; goto err_unlock_ovs;
rcu_assign_pointer(flow->sf_acts, new_acts); rcu_assign_pointer(flow->sf_acts, new_acts);
ovs_flow_deferred_free_acts(old_acts); ovs_flow_deferred_free_acts(old_acts);
...@@ -1066,6 +1077,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1066,6 +1077,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
spin_unlock_bh(&flow->lock); spin_unlock_bh(&flow->lock);
} }
} }
ovs_unlock();
if (!IS_ERR(reply)) if (!IS_ERR(reply))
ovs_notify(reply, info, &ovs_dp_flow_multicast_group); ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
...@@ -1076,6 +1088,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1076,6 +1088,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
error_free_flow: error_free_flow:
ovs_flow_free(flow); ovs_flow_free(flow);
err_unlock_ovs:
ovs_unlock();
error: error:
return error; return error;
} }
...@@ -1098,21 +1112,32 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) ...@@ -1098,21 +1112,32 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
if (err) if (err)
return err; return err;
ovs_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) if (!dp) {
return -ENODEV; err = -ENODEV;
goto unlock;
}
table = genl_dereference(dp->table); table = ovsl_dereference(dp->table);
flow = ovs_flow_tbl_lookup(table, &key, key_len); flow = ovs_flow_tbl_lookup(table, &key, key_len);
if (!flow) if (!flow) {
return -ENOENT; err = -ENOENT;
goto unlock;
}
reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, 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);
if (IS_ERR(reply)) if (IS_ERR(reply)) {
return PTR_ERR(reply); err = PTR_ERR(reply);
goto unlock;
}
ovs_unlock();
return genlmsg_reply(reply, info); return genlmsg_reply(reply, info);
unlock:
ovs_unlock();
return err;
} }
static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
...@@ -1127,25 +1152,33 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1127,25 +1152,33 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
int err; int err;
int key_len; int key_len;
ovs_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) if (!dp) {
return -ENODEV; err = -ENODEV;
goto unlock;
if (!a[OVS_FLOW_ATTR_KEY]) }
return flush_flows(dp);
if (!a[OVS_FLOW_ATTR_KEY]) {
err = flush_flows(dp);
goto unlock;
}
err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]); err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
if (err) if (err)
return err; goto unlock;
table = genl_dereference(dp->table); table = ovsl_dereference(dp->table);
flow = ovs_flow_tbl_lookup(table, &key, key_len); flow = ovs_flow_tbl_lookup(table, &key, key_len);
if (!flow) if (!flow) {
return -ENOENT; err = -ENOENT;
goto unlock;
}
reply = ovs_flow_cmd_alloc_info(flow); reply = ovs_flow_cmd_alloc_info(flow);
if (!reply) if (!reply) {
return -ENOMEM; err = -ENOMEM;
goto unlock;
}
ovs_flow_tbl_remove(table, flow); ovs_flow_tbl_remove(table, flow);
...@@ -1154,9 +1187,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1154,9 +1187,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
BUG_ON(err < 0); BUG_ON(err < 0);
ovs_flow_deferred_free(flow); ovs_flow_deferred_free(flow);
ovs_unlock();
ovs_notify(reply, info, &ovs_dp_flow_multicast_group); ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
return 0; return 0;
unlock:
ovs_unlock();
return err;
} }
static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
...@@ -1165,11 +1202,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1165,11 +1202,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
struct datapath *dp; struct datapath *dp;
struct flow_table *table; struct flow_table *table;
ovs_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) if (!dp) {
ovs_unlock();
return -ENODEV; return -ENODEV;
}
table = genl_dereference(dp->table); table = ovsl_dereference(dp->table);
for (;;) { for (;;) {
struct sw_flow *flow; struct sw_flow *flow;
...@@ -1190,6 +1230,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1190,6 +1230,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[0] = bucket; cb->args[0] = bucket;
cb->args[1] = obj; cb->args[1] = obj;
} }
ovs_unlock();
return skb->len; return skb->len;
} }
...@@ -1295,7 +1336,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid, ...@@ -1295,7 +1336,7 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid,
return skb; return skb;
} }
/* Called with genl_mutex and optionally with RTNL lock also. */ /* Called with ovs_mutex. */
static struct datapath *lookup_datapath(struct net *net, static struct datapath *lookup_datapath(struct net *net,
struct ovs_header *ovs_header, struct ovs_header *ovs_header,
struct nlattr *a[OVS_DP_ATTR_MAX + 1]) struct nlattr *a[OVS_DP_ATTR_MAX + 1])
...@@ -1329,12 +1370,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1329,12 +1370,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID]) if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
goto err; goto err;
rtnl_lock(); ovs_lock();
err = -ENOMEM; err = -ENOMEM;
dp = kzalloc(sizeof(*dp), GFP_KERNEL); dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (dp == NULL) if (dp == NULL)
goto err_unlock_rtnl; goto err_unlock_ovs;
ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
...@@ -1385,35 +1426,34 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1385,35 +1426,34 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id); ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
list_add_tail(&dp->list_node, &ovs_net->dps); list_add_tail(&dp->list_node, &ovs_net->dps);
rtnl_unlock();
ovs_unlock();
ovs_notify(reply, info, &ovs_dp_datapath_multicast_group); ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0; return 0;
err_destroy_local_port: err_destroy_local_port:
ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL)); ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
err_destroy_ports_array: err_destroy_ports_array:
kfree(dp->ports); kfree(dp->ports);
err_destroy_percpu: err_destroy_percpu:
free_percpu(dp->stats_percpu); free_percpu(dp->stats_percpu);
err_destroy_table: err_destroy_table:
ovs_flow_tbl_destroy(genl_dereference(dp->table)); ovs_flow_tbl_destroy(ovsl_dereference(dp->table));
err_free_dp: err_free_dp:
release_net(ovs_dp_get_net(dp)); release_net(ovs_dp_get_net(dp));
kfree(dp); kfree(dp);
err_unlock_rtnl: err_unlock_ovs:
rtnl_unlock(); ovs_unlock();
err: err:
return err; return err;
} }
/* Called with genl_mutex. */ /* Called with ovs_mutex. */
static void __dp_destroy(struct datapath *dp) static void __dp_destroy(struct datapath *dp)
{ {
int i; int i;
rtnl_lock();
for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) { for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
struct vport *vport; struct vport *vport;
struct hlist_node *n; struct hlist_node *n;
...@@ -1424,14 +1464,11 @@ static void __dp_destroy(struct datapath *dp) ...@@ -1424,14 +1464,11 @@ static void __dp_destroy(struct datapath *dp)
} }
list_del(&dp->list_node); list_del(&dp->list_node);
ovs_dp_detach_port(ovs_vport_rtnl(dp, OVSP_LOCAL));
/* rtnl_unlock() will wait until all the references to devices that /* OVSP_LOCAL is datapath internal port. We need to make sure that
* are pending unregistration have been dropped. We do it here to * all port in datapath are destroyed first before freeing datapath.
* ensure that any internal devices (which contain DP pointers) are
* fully destroyed before freeing the datapath.
*/ */
rtnl_unlock(); ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
call_rcu(&dp->rcu, destroy_dp_rcu); call_rcu(&dp->rcu, destroy_dp_rcu);
} }
...@@ -1442,22 +1479,27 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1442,22 +1479,27 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp; struct datapath *dp;
int err; int err;
ovs_lock();
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
err = PTR_ERR(dp); err = PTR_ERR(dp);
if (IS_ERR(dp)) if (IS_ERR(dp))
return err; goto unlock;
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
info->snd_seq, OVS_DP_CMD_DEL); info->snd_seq, OVS_DP_CMD_DEL);
err = PTR_ERR(reply); err = PTR_ERR(reply);
if (IS_ERR(reply)) if (IS_ERR(reply))
return err; goto unlock;
__dp_destroy(dp); __dp_destroy(dp);
ovs_unlock();
ovs_notify(reply, info, &ovs_dp_datapath_multicast_group); ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0; return 0;
unlock:
ovs_unlock();
return err;
} }
static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
...@@ -1466,9 +1508,11 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1466,9 +1508,11 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp; struct datapath *dp;
int err; int err;
ovs_lock();
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
err = PTR_ERR(dp);
if (IS_ERR(dp)) if (IS_ERR(dp))
return PTR_ERR(dp); goto unlock;
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
info->snd_seq, OVS_DP_CMD_NEW); info->snd_seq, OVS_DP_CMD_NEW);
...@@ -1476,29 +1520,45 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1476,29 +1520,45 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
err = PTR_ERR(reply); err = PTR_ERR(reply);
netlink_set_err(sock_net(skb->sk)->genl_sock, 0, netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
ovs_dp_datapath_multicast_group.id, err); ovs_dp_datapath_multicast_group.id, err);
return 0; err = 0;
goto unlock;
} }
ovs_unlock();
ovs_notify(reply, info, &ovs_dp_datapath_multicast_group); ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0; return 0;
unlock:
ovs_unlock();
return err;
} }
static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info) static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
{ {
struct sk_buff *reply; struct sk_buff *reply;
struct datapath *dp; struct datapath *dp;
int err;
ovs_lock();
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs); dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
if (IS_ERR(dp)) if (IS_ERR(dp)) {
return PTR_ERR(dp); err = PTR_ERR(dp);
goto unlock;
}
reply = ovs_dp_cmd_build_info(dp, info->snd_portid, reply = ovs_dp_cmd_build_info(dp, info->snd_portid,
info->snd_seq, OVS_DP_CMD_NEW); info->snd_seq, OVS_DP_CMD_NEW);
if (IS_ERR(reply)) if (IS_ERR(reply)) {
return PTR_ERR(reply); err = PTR_ERR(reply);
goto unlock;
}
ovs_unlock();
return genlmsg_reply(reply, info); return genlmsg_reply(reply, info);
unlock:
ovs_unlock();
return err;
} }
static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
...@@ -1508,6 +1568,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1508,6 +1568,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
int skip = cb->args[0]; int skip = cb->args[0];
int i = 0; int i = 0;
ovs_lock();
list_for_each_entry(dp, &ovs_net->dps, list_node) { list_for_each_entry(dp, &ovs_net->dps, list_node) {
if (i >= skip && if (i >= skip &&
ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid, ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
...@@ -1516,6 +1577,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1516,6 +1577,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
break; break;
i++; i++;
} }
ovs_unlock();
cb->args[0] = i; cb->args[0] = i;
...@@ -1568,7 +1630,7 @@ struct genl_multicast_group ovs_dp_vport_multicast_group = { ...@@ -1568,7 +1630,7 @@ struct genl_multicast_group ovs_dp_vport_multicast_group = {
.name = OVS_VPORT_MCGROUP .name = OVS_VPORT_MCGROUP
}; };
/* Called with RTNL lock or RCU read lock. */ /* Called with ovs_mutex or RCU read lock. */
static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
u32 portid, u32 seq, u32 flags, u8 cmd) u32 portid, u32 seq, u32 flags, u8 cmd)
{ {
...@@ -1607,7 +1669,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, ...@@ -1607,7 +1669,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
return err; return err;
} }
/* Called with RTNL lock or RCU read lock. */ /* Called with ovs_mutex or RCU read lock. */
struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
u32 seq, u8 cmd) u32 seq, u8 cmd)
{ {
...@@ -1626,7 +1688,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, ...@@ -1626,7 +1688,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
return skb; return skb;
} }
/* Called with RTNL lock or RCU read lock. */ /* Called with ovs_mutex or RCU read lock. */
static struct vport *lookup_vport(struct net *net, static struct vport *lookup_vport(struct net *net,
struct ovs_header *ovs_header, struct ovs_header *ovs_header,
struct nlattr *a[OVS_VPORT_ATTR_MAX + 1]) struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
...@@ -1652,7 +1714,7 @@ static struct vport *lookup_vport(struct net *net, ...@@ -1652,7 +1714,7 @@ static struct vport *lookup_vport(struct net *net,
if (!dp) if (!dp)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
vport = ovs_vport_rtnl_rcu(dp, port_no); vport = ovs_vport_ovsl_rcu(dp, port_no);
if (!vport) if (!vport)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
return vport; return vport;
...@@ -1676,7 +1738,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1676,7 +1738,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
!a[OVS_VPORT_ATTR_UPCALL_PID]) !a[OVS_VPORT_ATTR_UPCALL_PID])
goto exit; goto exit;
rtnl_lock(); ovs_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
err = -ENODEV; err = -ENODEV;
if (!dp) if (!dp)
...@@ -1689,7 +1751,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1689,7 +1751,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
if (port_no >= DP_MAX_PORTS) if (port_no >= DP_MAX_PORTS)
goto exit_unlock; goto exit_unlock;
vport = ovs_vport_rtnl_rcu(dp, port_no); vport = ovs_vport_ovsl(dp, port_no);
err = -EBUSY; err = -EBUSY;
if (vport) if (vport)
goto exit_unlock; goto exit_unlock;
...@@ -1699,7 +1761,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1699,7 +1761,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
err = -EFBIG; err = -EFBIG;
goto exit_unlock; goto exit_unlock;
} }
vport = ovs_vport_rtnl(dp, port_no); vport = ovs_vport_ovsl(dp, port_no);
if (!vport) if (!vport)
break; break;
} }
...@@ -1729,7 +1791,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) ...@@ -1729,7 +1791,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
ovs_notify(reply, info, &ovs_dp_vport_multicast_group); ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
exit_unlock: exit_unlock:
rtnl_unlock(); ovs_unlock();
exit: exit:
return err; return err;
} }
...@@ -1741,7 +1803,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1741,7 +1803,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
struct vport *vport; struct vport *vport;
int err; int err;
rtnl_lock(); ovs_lock();
vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
err = PTR_ERR(vport); err = PTR_ERR(vport);
if (IS_ERR(vport)) if (IS_ERR(vport))
...@@ -1767,10 +1829,12 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) ...@@ -1767,10 +1829,12 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
goto exit_unlock; goto exit_unlock;
} }
ovs_unlock();
ovs_notify(reply, info, &ovs_dp_vport_multicast_group); ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
return 0;
exit_unlock: exit_unlock:
rtnl_unlock(); ovs_unlock();
return err; return err;
} }
...@@ -1781,7 +1845,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1781,7 +1845,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
struct vport *vport; struct vport *vport;
int err; int err;
rtnl_lock(); ovs_lock();
vport = lookup_vport(sock_net(skb->sk), info->userhdr, a); vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
err = PTR_ERR(vport); err = PTR_ERR(vport);
if (IS_ERR(vport)) if (IS_ERR(vport))
...@@ -1804,7 +1868,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) ...@@ -1804,7 +1868,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
ovs_notify(reply, info, &ovs_dp_vport_multicast_group); ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
exit_unlock: exit_unlock:
rtnl_unlock(); ovs_unlock();
return err; return err;
} }
...@@ -1964,13 +2028,13 @@ static void rehash_flow_table(struct work_struct *work) ...@@ -1964,13 +2028,13 @@ static void rehash_flow_table(struct work_struct *work)
struct datapath *dp; struct datapath *dp;
struct net *net; struct net *net;
genl_lock(); ovs_lock();
rtnl_lock(); rtnl_lock();
for_each_net(net) { for_each_net(net) {
struct ovs_net *ovs_net = net_generic(net, ovs_net_id); struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
list_for_each_entry(dp, &ovs_net->dps, list_node) { list_for_each_entry(dp, &ovs_net->dps, list_node) {
struct flow_table *old_table = genl_dereference(dp->table); struct flow_table *old_table = ovsl_dereference(dp->table);
struct flow_table *new_table; struct flow_table *new_table;
new_table = ovs_flow_tbl_rehash(old_table); new_table = ovs_flow_tbl_rehash(old_table);
...@@ -1981,8 +2045,7 @@ static void rehash_flow_table(struct work_struct *work) ...@@ -1981,8 +2045,7 @@ static void rehash_flow_table(struct work_struct *work)
} }
} }
rtnl_unlock(); rtnl_unlock();
genl_unlock(); ovs_unlock();
schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL); schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL);
} }
...@@ -1991,18 +2054,21 @@ static int __net_init ovs_init_net(struct net *net) ...@@ -1991,18 +2054,21 @@ static int __net_init ovs_init_net(struct net *net)
struct ovs_net *ovs_net = net_generic(net, ovs_net_id); struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
INIT_LIST_HEAD(&ovs_net->dps); INIT_LIST_HEAD(&ovs_net->dps);
INIT_WORK(&ovs_net->dp_notify_work, ovs_dp_notify_wq);
return 0; return 0;
} }
static void __net_exit ovs_exit_net(struct net *net) static void __net_exit ovs_exit_net(struct net *net)
{ {
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
struct datapath *dp, *dp_next; struct datapath *dp, *dp_next;
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
genl_lock(); ovs_lock();
list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node) list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
__dp_destroy(dp); __dp_destroy(dp);
genl_unlock(); ovs_unlock();
cancel_work_sync(&ovs_net->dp_notify_work);
} }
static struct pernet_operations ovs_net_ops = { static struct pernet_operations ovs_net_ops = {
......
...@@ -57,9 +57,9 @@ struct dp_stats_percpu { ...@@ -57,9 +57,9 @@ struct dp_stats_percpu {
* struct datapath - datapath for flow-based packet switching * struct datapath - datapath for flow-based packet switching
* @rcu: RCU callback head for deferred destruction. * @rcu: RCU callback head for deferred destruction.
* @list_node: Element in global 'dps' list. * @list_node: Element in global 'dps' list.
* @table: Current flow table. Protected by genl_lock and RCU. * @table: Current flow table. Protected by ovs_mutex and RCU.
* @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by * @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by
* RTNL and RCU. * ovs_mutex and RCU.
* @stats_percpu: Per-CPU datapath statistics. * @stats_percpu: Per-CPU datapath statistics.
* @net: Reference to net namespace. * @net: Reference to net namespace.
* *
...@@ -85,26 +85,6 @@ struct datapath { ...@@ -85,26 +85,6 @@ struct datapath {
#endif #endif
}; };
struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no);
static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no)
{
WARN_ON_ONCE(!rcu_read_lock_held());
return ovs_lookup_vport(dp, port_no);
}
static inline struct vport *ovs_vport_rtnl_rcu(const struct datapath *dp, int port_no)
{
WARN_ON_ONCE(!rcu_read_lock_held() && !rtnl_is_locked());
return ovs_lookup_vport(dp, port_no);
}
static inline struct vport *ovs_vport_rtnl(const struct datapath *dp, int port_no)
{
ASSERT_RTNL();
return ovs_lookup_vport(dp, port_no);
}
/** /**
* struct ovs_skb_cb - OVS data in skb CB * struct ovs_skb_cb - OVS data in skb CB
* @flow: The flow associated with this packet. May be %NULL if no flow. * @flow: The flow associated with this packet. May be %NULL if no flow.
...@@ -131,6 +111,30 @@ struct dp_upcall_info { ...@@ -131,6 +111,30 @@ struct dp_upcall_info {
u32 portid; u32 portid;
}; };
/**
* struct ovs_net - Per net-namespace data for ovs.
* @dps: List of datapaths to enable dumping them all out.
* Protected by genl_mutex.
*/
struct ovs_net {
struct list_head dps;
struct work_struct dp_notify_work;
};
extern int ovs_net_id;
void ovs_lock(void);
void ovs_unlock(void);
#ifdef CONFIG_LOCKDEP
int lockdep_ovsl_is_held(void);
#else
#define lockdep_ovsl_is_held() 1
#endif
#define ASSERT_OVSL() WARN_ON(unlikely(!lockdep_ovsl_is_held()))
#define ovsl_dereference(p) \
rcu_dereference_protected(p, lockdep_ovsl_is_held())
static inline struct net *ovs_dp_get_net(struct datapath *dp) static inline struct net *ovs_dp_get_net(struct datapath *dp)
{ {
return read_pnet(&dp->net); return read_pnet(&dp->net);
...@@ -141,6 +145,26 @@ static inline void ovs_dp_set_net(struct datapath *dp, struct net *net) ...@@ -141,6 +145,26 @@ static inline void ovs_dp_set_net(struct datapath *dp, struct net *net)
write_pnet(&dp->net, net); write_pnet(&dp->net, net);
} }
struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no);
static inline struct vport *ovs_vport_rcu(const struct datapath *dp, int port_no)
{
WARN_ON_ONCE(!rcu_read_lock_held());
return ovs_lookup_vport(dp, port_no);
}
static inline struct vport *ovs_vport_ovsl_rcu(const struct datapath *dp, int port_no)
{
WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held());
return ovs_lookup_vport(dp, port_no);
}
static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_no)
{
ASSERT_OVSL();
return ovs_lookup_vport(dp, port_no);
}
extern struct notifier_block ovs_dp_device_notifier; extern struct notifier_block ovs_dp_device_notifier;
extern struct genl_multicast_group ovs_dp_vport_multicast_group; extern struct genl_multicast_group ovs_dp_vport_multicast_group;
...@@ -154,4 +178,5 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, ...@@ -154,4 +178,5 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
u8 cmd); u8 cmd);
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb); int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb);
void ovs_dp_notify_wq(struct work_struct *work);
#endif /* datapath.h */ #endif /* datapath.h */
...@@ -18,31 +18,18 @@ ...@@ -18,31 +18,18 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/netns/generic.h>
#include "datapath.h" #include "datapath.h"
#include "vport-internal_dev.h" #include "vport-internal_dev.h"
#include "vport-netdev.h" #include "vport-netdev.h"
static int dp_device_event(struct notifier_block *unused, unsigned long event, static void dp_detach_port_notify(struct vport *vport)
void *ptr)
{ {
struct net_device *dev = ptr;
struct vport *vport;
if (ovs_is_internal_dev(dev))
vport = ovs_internal_dev_get_vport(dev);
else
vport = ovs_netdev_get_vport(dev);
if (!vport)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
if (!ovs_is_internal_dev(dev)) {
struct sk_buff *notify; struct sk_buff *notify;
struct datapath *dp = vport->dp; struct datapath *dp;
dp = vport->dp;
notify = ovs_vport_cmd_build_info(vport, 0, 0, notify = ovs_vport_cmd_build_info(vport, 0, 0,
OVS_VPORT_CMD_DEL); OVS_VPORT_CMD_DEL);
ovs_dp_detach_port(vport); ovs_dp_detach_port(vport);
...@@ -50,14 +37,59 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event, ...@@ -50,14 +37,59 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0, netlink_set_err(ovs_dp_get_net(dp)->genl_sock, 0,
ovs_dp_vport_multicast_group.id, ovs_dp_vport_multicast_group.id,
PTR_ERR(notify)); PTR_ERR(notify));
break; return;
} }
genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0, genlmsg_multicast_netns(ovs_dp_get_net(dp), notify, 0,
ovs_dp_vport_multicast_group.id, ovs_dp_vport_multicast_group.id,
GFP_KERNEL); GFP_KERNEL);
}
void ovs_dp_notify_wq(struct work_struct *work)
{
struct ovs_net *ovs_net = container_of(work, struct ovs_net, dp_notify_work);
struct datapath *dp;
ovs_lock();
list_for_each_entry(dp, &ovs_net->dps, list_node) {
int i;
for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
struct vport *vport;
struct hlist_node *n;
hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node) {
struct netdev_vport *netdev_vport;
if (vport->ops->type != OVS_VPORT_TYPE_NETDEV)
continue;
netdev_vport = netdev_vport_priv(vport);
if (netdev_vport->dev->reg_state == NETREG_UNREGISTERED ||
netdev_vport->dev->reg_state == NETREG_UNREGISTERING)
dp_detach_port_notify(vport);
}
}
} }
break; ovs_unlock();
}
static int dp_device_event(struct notifier_block *unused, unsigned long event,
void *ptr)
{
struct ovs_net *ovs_net;
struct net_device *dev = ptr;
struct vport *vport = NULL;
if (!ovs_is_internal_dev(dev))
vport = ovs_netdev_get_vport(dev);
if (!vport)
return NOTIFY_DONE;
if (event == NETDEV_UNREGISTER) {
ovs_net = net_generic(dev_net(dev), ovs_net_id);
queue_work(system_wq, &ovs_net->dp_notify_work);
} }
return NOTIFY_DONE; return NOTIFY_DONE;
......
...@@ -173,16 +173,19 @@ static struct vport *internal_dev_create(const struct vport_parms *parms) ...@@ -173,16 +173,19 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
if (vport->port_no == OVSP_LOCAL) if (vport->port_no == OVSP_LOCAL)
netdev_vport->dev->features |= NETIF_F_NETNS_LOCAL; netdev_vport->dev->features |= NETIF_F_NETNS_LOCAL;
rtnl_lock();
err = register_netdevice(netdev_vport->dev); err = register_netdevice(netdev_vport->dev);
if (err) if (err)
goto error_free_netdev; goto error_free_netdev;
dev_set_promiscuity(netdev_vport->dev, 1); dev_set_promiscuity(netdev_vport->dev, 1);
rtnl_unlock();
netif_start_queue(netdev_vport->dev); netif_start_queue(netdev_vport->dev);
return vport; return vport;
error_free_netdev: error_free_netdev:
rtnl_unlock();
free_netdev(netdev_vport->dev); free_netdev(netdev_vport->dev);
error_free_vport: error_free_vport:
ovs_vport_free(vport); ovs_vport_free(vport);
...@@ -195,10 +198,13 @@ static void internal_dev_destroy(struct vport *vport) ...@@ -195,10 +198,13 @@ static void internal_dev_destroy(struct vport *vport)
struct netdev_vport *netdev_vport = netdev_vport_priv(vport); struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
netif_stop_queue(netdev_vport->dev); netif_stop_queue(netdev_vport->dev);
rtnl_lock();
dev_set_promiscuity(netdev_vport->dev, -1); dev_set_promiscuity(netdev_vport->dev, -1);
/* unregister_netdevice() waits for an RCU grace period. */ /* unregister_netdevice() waits for an RCU grace period. */
unregister_netdevice(netdev_vport->dev); unregister_netdevice(netdev_vport->dev);
rtnl_unlock();
} }
static int internal_dev_recv(struct vport *vport, struct sk_buff *skb) static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
......
...@@ -100,16 +100,20 @@ static struct vport *netdev_create(const struct vport_parms *parms) ...@@ -100,16 +100,20 @@ static struct vport *netdev_create(const struct vport_parms *parms)
goto error_put; goto error_put;
} }
rtnl_lock();
err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook,
vport); vport);
if (err) if (err)
goto error_put; goto error_unlock;
dev_set_promiscuity(netdev_vport->dev, 1); dev_set_promiscuity(netdev_vport->dev, 1);
netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH; netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
rtnl_unlock();
return vport; return vport;
error_unlock:
rtnl_unlock();
error_put: error_put:
dev_put(netdev_vport->dev); dev_put(netdev_vport->dev);
error_free_vport: error_free_vport:
...@@ -131,9 +135,11 @@ static void netdev_destroy(struct vport *vport) ...@@ -131,9 +135,11 @@ static void netdev_destroy(struct vport *vport)
{ {
struct netdev_vport *netdev_vport = netdev_vport_priv(vport); struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
rtnl_lock();
netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH; netdev_vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
netdev_rx_handler_unregister(netdev_vport->dev); netdev_rx_handler_unregister(netdev_vport->dev);
dev_set_promiscuity(netdev_vport->dev, -1); dev_set_promiscuity(netdev_vport->dev, -1);
rtnl_unlock();
call_rcu(&netdev_vport->rcu, free_port_rcu); call_rcu(&netdev_vport->rcu, free_port_rcu);
} }
......
...@@ -40,7 +40,7 @@ static const struct vport_ops *vport_ops_list[] = { ...@@ -40,7 +40,7 @@ static const struct vport_ops *vport_ops_list[] = {
&ovs_internal_vport_ops, &ovs_internal_vport_ops,
}; };
/* Protected by RCU read lock for reading, RTNL lock for writing. */ /* Protected by RCU read lock for reading, ovs_mutex for writing. */
static struct hlist_head *dev_table; static struct hlist_head *dev_table;
#define VPORT_HASH_BUCKETS 1024 #define VPORT_HASH_BUCKETS 1024
...@@ -80,7 +80,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name) ...@@ -80,7 +80,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name)
* *
* @name: name of port to find * @name: name of port to find
* *
* Must be called with RTNL or RCU read lock. * Must be called with ovs or RCU read lock.
*/ */
struct vport *ovs_vport_locate(struct net *net, const char *name) struct vport *ovs_vport_locate(struct net *net, const char *name)
{ {
...@@ -161,7 +161,7 @@ void ovs_vport_free(struct vport *vport) ...@@ -161,7 +161,7 @@ void ovs_vport_free(struct vport *vport)
* @parms: Information about new vport. * @parms: Information about new vport.
* *
* Creates a new vport with the specified configuration (which is dependent on * Creates a new vport with the specified configuration (which is dependent on
* device type). RTNL lock must be held. * device type). ovs_mutex must be held.
*/ */
struct vport *ovs_vport_add(const struct vport_parms *parms) struct vport *ovs_vport_add(const struct vport_parms *parms)
{ {
...@@ -169,8 +169,6 @@ struct vport *ovs_vport_add(const struct vport_parms *parms) ...@@ -169,8 +169,6 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
int err = 0; int err = 0;
int i; int i;
ASSERT_RTNL();
for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) { for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) {
if (vport_ops_list[i]->type == parms->type) { if (vport_ops_list[i]->type == parms->type) {
struct hlist_head *bucket; struct hlist_head *bucket;
...@@ -201,12 +199,10 @@ struct vport *ovs_vport_add(const struct vport_parms *parms) ...@@ -201,12 +199,10 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
* @port: New configuration. * @port: New configuration.
* *
* Modifies an existing device with the specified configuration (which is * Modifies an existing device with the specified configuration (which is
* dependent on device type). RTNL lock must be held. * dependent on device type). ovs_mutex must be held.
*/ */
int ovs_vport_set_options(struct vport *vport, struct nlattr *options) int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
{ {
ASSERT_RTNL();
if (!vport->ops->set_options) if (!vport->ops->set_options)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return vport->ops->set_options(vport, options); return vport->ops->set_options(vport, options);
...@@ -218,11 +214,11 @@ int ovs_vport_set_options(struct vport *vport, struct nlattr *options) ...@@ -218,11 +214,11 @@ int ovs_vport_set_options(struct vport *vport, struct nlattr *options)
* @vport: vport to delete. * @vport: vport to delete.
* *
* Detaches @vport from its datapath and destroys it. It is possible to fail * Detaches @vport from its datapath and destroys it. It is possible to fail
* for reasons such as lack of memory. RTNL lock must be held. * for reasons such as lack of memory. ovs_mutex must be held.
*/ */
void ovs_vport_del(struct vport *vport) void ovs_vport_del(struct vport *vport)
{ {
ASSERT_RTNL(); ASSERT_OVSL();
hlist_del_rcu(&vport->hash_node); hlist_del_rcu(&vport->hash_node);
...@@ -237,7 +233,7 @@ void ovs_vport_del(struct vport *vport) ...@@ -237,7 +233,7 @@ void ovs_vport_del(struct vport *vport)
* *
* Retrieves transmit, receive, and error stats for the given device. * Retrieves transmit, receive, and error stats for the given device.
* *
* Must be called with RTNL lock or rcu_read_lock. * Must be called with ovs_mutex or rcu_read_lock.
*/ */
void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
{ {
...@@ -296,7 +292,7 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats) ...@@ -296,7 +292,7 @@ void ovs_vport_get_stats(struct vport *vport, struct ovs_vport_stats *stats)
* negative error code if a real error occurred. If an error occurs, @skb is * negative error code if a real error occurred. If an error occurs, @skb is
* left unmodified. * left unmodified.
* *
* Must be called with RTNL lock or rcu_read_lock. * Must be called with ovs_mutex or rcu_read_lock.
*/ */
int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb) int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
{ {
...@@ -348,7 +344,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb) ...@@ -348,7 +344,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
* @vport: vport on which to send the packet * @vport: vport on which to send the packet
* @skb: skb to send * @skb: skb to send
* *
* Sends the given packet and returns the length of data sent. Either RTNL * Sends the given packet and returns the length of data sent. Either ovs
* lock or rcu_read_lock must be held. * lock or rcu_read_lock must be held.
*/ */
int ovs_vport_send(struct vport *vport, struct sk_buff *skb) int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
......
...@@ -138,14 +138,14 @@ struct vport_parms { ...@@ -138,14 +138,14 @@ struct vport_parms {
struct vport_ops { struct vport_ops {
enum ovs_vport_type type; enum ovs_vport_type type;
/* Called with RTNL lock. */ /* Called with ovs_mutex. */
struct vport *(*create)(const struct vport_parms *); struct vport *(*create)(const struct vport_parms *);
void (*destroy)(struct vport *); void (*destroy)(struct vport *);
int (*set_options)(struct vport *, struct nlattr *); int (*set_options)(struct vport *, struct nlattr *);
int (*get_options)(const struct vport *, struct sk_buff *); int (*get_options)(const struct vport *, struct sk_buff *);
/* Called with rcu_read_lock or RTNL lock. */ /* Called with rcu_read_lock or ovs_mutex. */
const char *(*get_name)(const struct vport *); const char *(*get_name)(const struct vport *);
void (*get_config)(const struct vport *, void *); void (*get_config)(const struct vport *, void *);
int (*get_ifindex)(const struct vport *); int (*get_ifindex)(const struct vport *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment