Merge branch 'locking'
This commit is contained in:
+8
-10
@@ -62,8 +62,7 @@ error:
|
||||
/* Searches 'chain' for a flow matching 'key', which must not have any wildcard
|
||||
* fields. Returns the flow if successful, otherwise a null pointer.
|
||||
*
|
||||
* Caller must hold rcu_read_lock, and not release it until it is done with the
|
||||
* returned flow. */
|
||||
* Caller must hold rcu_read_lock or dp_mutex. */
|
||||
struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
const struct sw_flow_key *key)
|
||||
{
|
||||
@@ -85,12 +84,12 @@ struct sw_flow *chain_lookup(struct sw_chain *chain,
|
||||
* If successful, 'flow' becomes owned by the chain, otherwise it is retained
|
||||
* by the caller.
|
||||
*
|
||||
* Caller must hold rcu_read_lock. If insertion is successful, it must not
|
||||
* release rcu_read_lock until it is done with the inserted flow. */
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
{
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
if (t->insert(t, flow))
|
||||
@@ -107,18 +106,17 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
|
||||
* iterating through the entire contents of each table for keys that contain
|
||||
* wildcards. Relatively cheap for fully specified keys.
|
||||
*
|
||||
* The caller need not hold any locks. */
|
||||
* Caller must hold dp_mutex. */
|
||||
int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
uint16_t priority, int strict)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
rcu_read_lock();
|
||||
count += t->delete(t, key, priority, strict);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -130,17 +128,17 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key,
|
||||
* Expensive as currently implemented, since it iterates through the entire
|
||||
* contents of each table.
|
||||
*
|
||||
* The caller need not hold any locks. */
|
||||
* Caller must not hold dp_mutex, because individual tables take and release it
|
||||
* as necessary. */
|
||||
int chain_timeout(struct sw_chain *chain)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
might_sleep();
|
||||
for (i = 0; i < chain->n_tables; i++) {
|
||||
struct sw_table *t = chain->tables[i];
|
||||
rcu_read_lock();
|
||||
count += t->timeout(chain->dp, t);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
+32
-61
@@ -64,15 +64,16 @@ static struct genl_multicast_group mc_group;
|
||||
/* It's hard to imagine wanting more than one datapath, but... */
|
||||
#define DP_MAX 32
|
||||
|
||||
/* datapaths. Protected on the read side by rcu_read_lock, on the write side
|
||||
* by dp_mutex.
|
||||
/* Datapaths. Protected on the read side by rcu_read_lock, on the write side
|
||||
* by dp_mutex. dp_mutex is almost completely redundant with genl_mutex
|
||||
* maintained by the Generic Netlink code, but the timeout path needs mutual
|
||||
* exclusion too.
|
||||
*
|
||||
* It is safe to access the datapath and net_bridge_port structures with just
|
||||
* the dp_mutex, but to access the chain you need to take the rcu_read_lock
|
||||
* also (because dp_mutex doesn't prevent flows from being destroyed).
|
||||
* dp_mutex.
|
||||
*/
|
||||
static struct datapath *dps[DP_MAX];
|
||||
static DEFINE_MUTEX(dp_mutex);
|
||||
DEFINE_MUTEX(dp_mutex);
|
||||
|
||||
static int dp_maint_func(void *data);
|
||||
static int send_port_status(struct net_bridge_port *p, uint8_t status);
|
||||
@@ -242,9 +243,7 @@ uint64_t gen_datapath_id(uint16_t dp_idx)
|
||||
}
|
||||
|
||||
/* Creates a new datapath numbered 'dp_idx'. Returns 0 for success or a
|
||||
* negative error code.
|
||||
*
|
||||
* Not called with any locks. */
|
||||
* negative error code. */
|
||||
static int new_dp(int dp_idx)
|
||||
{
|
||||
struct datapath *dp;
|
||||
@@ -256,9 +255,8 @@ static int new_dp(int dp_idx)
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = rcu_dereference(dps[dp_idx]);
|
||||
if (dp != NULL) {
|
||||
/* Exit early if a datapath with that number already exists. */
|
||||
if (dps[dp_idx]) {
|
||||
err = -EEXIST;
|
||||
goto err_unlock;
|
||||
}
|
||||
@@ -293,8 +291,7 @@ static int new_dp(int dp_idx)
|
||||
if (IS_ERR(dp->dp_task))
|
||||
goto err_destroy_chain;
|
||||
|
||||
rcu_assign_pointer(dps[dp_idx], dp);
|
||||
mutex_unlock(&dp_mutex);
|
||||
dps[dp_idx] = dp;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -307,12 +304,11 @@ err_destroy_dp_dev:
|
||||
err_free_dp:
|
||||
kfree(dp);
|
||||
err_unlock:
|
||||
mutex_unlock(&dp_mutex);
|
||||
module_put(THIS_MODULE);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Find and return a free port number under 'dp'. Called under dp_mutex. */
|
||||
/* Find and return a free port number under 'dp'. */
|
||||
static int find_portno(struct datapath *dp)
|
||||
{
|
||||
int i;
|
||||
@@ -350,7 +346,6 @@ static struct net_bridge_port *new_nbp(struct datapath *dp,
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Called with dp_mutex. */
|
||||
int add_switch_port(struct datapath *dp, struct net_device *dev)
|
||||
{
|
||||
struct net_bridge_port *p;
|
||||
@@ -374,8 +369,7 @@ int add_switch_port(struct datapath *dp, struct net_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete 'p' from switch.
|
||||
* Called with dp_mutex. */
|
||||
/* Delete 'p' from switch. */
|
||||
static int del_switch_port(struct net_bridge_port *p)
|
||||
{
|
||||
/* First drop references to device. */
|
||||
@@ -399,7 +393,6 @@ static int del_switch_port(struct net_bridge_port *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with dp_mutex. */
|
||||
static void del_dp(struct datapath *dp)
|
||||
{
|
||||
struct net_bridge_port *p, *n;
|
||||
@@ -467,8 +460,6 @@ static int dp_frame_hook(struct net_bridge_port *p, struct sk_buff **pskb)
|
||||
}
|
||||
#else
|
||||
/* NB: This has only been tested on 2.4.35 */
|
||||
|
||||
/* Called without any locks (?) */
|
||||
static void dp_frame_hook(struct sk_buff *skb)
|
||||
{
|
||||
struct net_bridge_port *p = skb->dev->br_port;
|
||||
@@ -882,7 +873,6 @@ static int dp_genl_del(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!info->attrs[DP_GENL_A_DP_IDX])
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = dp_get(nla_get_u32((info->attrs[DP_GENL_A_DP_IDX])));
|
||||
if (!dp)
|
||||
err = -ENOENT;
|
||||
@@ -890,7 +880,6 @@ static int dp_genl_del(struct sk_buff *skb, struct genl_info *info)
|
||||
del_dp(dp);
|
||||
err = 0;
|
||||
}
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -974,7 +963,6 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get datapath. */
|
||||
mutex_lock(&dp_mutex);
|
||||
dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
@@ -1003,7 +991,6 @@ static int dp_genl_add_del_port(struct sk_buff *skb, struct genl_info *info)
|
||||
out_put:
|
||||
dev_put(port);
|
||||
out:
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1034,26 +1021,22 @@ static int dp_genl_openflow(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!info->attrs[DP_GENL_A_DP_IDX] || !va)
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
dp = dp_get(nla_get_u32(info->attrs[DP_GENL_A_DP_IDX]));
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
|
||||
if (nla_len(va) < sizeof(struct ofp_header)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (nla_len(va) < sizeof(struct ofp_header))
|
||||
return -EINVAL;
|
||||
oh = nla_data(va);
|
||||
|
||||
sender.xid = oh->xid;
|
||||
sender.pid = info->snd_pid;
|
||||
sender.seq = info->snd_seq;
|
||||
err = fwd_control_input(dp->chain, &sender, nla_data(va), nla_len(va));
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
mutex_lock(&dp_mutex);
|
||||
err = fwd_control_input(dp->chain, &sender,
|
||||
nla_data(va), nla_len(va));
|
||||
mutex_unlock(&dp_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1369,7 +1352,6 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
* struct genl_ops. This kluge supports earlier versions also. */
|
||||
cb->done = dp_genl_openflow_done;
|
||||
|
||||
rcu_read_lock();
|
||||
if (!cb->args[0]) {
|
||||
struct nlattr *attrs[DP_GENL_A_MAX + 1];
|
||||
struct ofp_stats_request *rq;
|
||||
@@ -1382,21 +1364,17 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
if (!attrs[DP_GENL_A_DP_IDX])
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
dp_idx = nla_get_u16(attrs[DP_GENL_A_DP_IDX]);
|
||||
dp = dp_get(dp_idx);
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
|
||||
va = attrs[DP_GENL_A_OPENFLOW];
|
||||
len = nla_len(va);
|
||||
if (!va || len < sizeof *rq)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
rq = nla_data(va);
|
||||
type = ntohs(rq->type);
|
||||
@@ -1405,12 +1383,12 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
|| ntohs(rq->header.length) != len
|
||||
|| type >= ARRAY_SIZE(stats)
|
||||
|| !stats[type].dump)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
s = &stats[type];
|
||||
body_len = len - offsetof(struct ofp_stats_request, body);
|
||||
if (body_len < s->min_body || body_len > s->max_body)
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
|
||||
cb->args[0] = 1;
|
||||
cb->args[1] = dp_idx;
|
||||
@@ -1420,7 +1398,7 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
void *state;
|
||||
err = s->init(dp, rq->body, body_len, &state);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
cb->args[4] = (long) state;
|
||||
}
|
||||
} else if (cb->args[0] == 1) {
|
||||
@@ -1428,13 +1406,10 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
s = &stats[cb->args[2]];
|
||||
|
||||
dp = dp_get(dp_idx);
|
||||
if (!dp) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
if (!dp)
|
||||
return -ENOENT;
|
||||
} else {
|
||||
err = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sender.xid = cb->args[3];
|
||||
@@ -1443,10 +1418,8 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
|
||||
osr = put_openflow_headers(dp, skb, OFPT_STATS_REPLY, &sender,
|
||||
&max_openflow_len);
|
||||
if (IS_ERR(osr)) {
|
||||
err = PTR_ERR(osr);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(osr))
|
||||
return PTR_ERR(osr);
|
||||
osr->type = htons(s - stats);
|
||||
osr->flags = 0;
|
||||
resize_openflow_skb(skb, &osr->header, max_openflow_len);
|
||||
@@ -1465,8 +1438,6 @@ dp_genl_openflow_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
err = skb->len;
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef DATAPATH_H
|
||||
#define DATAPATH_H 1
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
@@ -67,6 +68,8 @@ struct sender {
|
||||
uint32_t seq; /* Netlink sequence ID of request. */
|
||||
};
|
||||
|
||||
extern struct mutex dp_mutex;
|
||||
|
||||
int dp_output_port(struct datapath *, struct sk_buff *, int out_port);
|
||||
int dp_output_control(struct datapath *, struct sk_buff *, uint32_t,
|
||||
size_t, int);
|
||||
|
||||
+9
-13
@@ -55,19 +55,16 @@ static int dp_dev_mac_addr(struct net_device *dev, void *p)
|
||||
static int dp_dev_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct dp_dev *dp_dev = dp_dev_priv(netdev);
|
||||
struct datapath *dp;
|
||||
struct datapath *dp = dp_dev->dp;
|
||||
|
||||
dp_dev->stats.tx_packets++;
|
||||
dp_dev->stats.tx_bytes += skb->len;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
rcu_read_lock();
|
||||
dp = dp_dev->dp;
|
||||
if (likely(dp != NULL)) {
|
||||
dp_dev->stats.tx_packets++;
|
||||
dp_dev->stats.tx_bytes += skb->len;
|
||||
skb_reset_mac_header(skb);
|
||||
fwd_port_input(dp->chain, skb, OFPP_LOCAL);
|
||||
} else {
|
||||
dp_dev->stats.tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
fwd_port_input(dp->chain, skb, OFPP_LOCAL);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -127,8 +124,7 @@ int dp_dev_setup(struct datapath *dp)
|
||||
|
||||
void dp_dev_destroy(struct datapath *dp)
|
||||
{
|
||||
struct dp_dev *dp_dev = dp_dev_priv(dp->netdev);
|
||||
dp_dev->dp = NULL;
|
||||
netif_tx_disable(dp->netdev);
|
||||
synchronize_net();
|
||||
unregister_netdev(dp->netdev);
|
||||
free_netdev(dp->netdev);
|
||||
|
||||
@@ -125,16 +125,6 @@ void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from)
|
||||
memset(to->pad, '\0', sizeof(to->pad));
|
||||
}
|
||||
|
||||
/* Returns true if 'flow' can be deleted and set up for a deferred free, false
|
||||
* if deletion has already been scheduled (by another thread).
|
||||
*
|
||||
* Caller must hold rcu_read_lock. */
|
||||
int flow_del(struct sw_flow *flow)
|
||||
{
|
||||
return !atomic_cmpxchg(&flow->deleted, 0, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(flow_del);
|
||||
|
||||
/* Allocates and returns a new flow with 'n_actions' action, using allocation
|
||||
* flags 'flags'. Returns the new flow or a null pointer on failure. */
|
||||
struct sw_flow *flow_alloc(int n_actions, gfp_t flags)
|
||||
|
||||
+2
-11
@@ -2,7 +2,6 @@
|
||||
#define FLOW_H 1
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/types.h>
|
||||
@@ -49,15 +48,9 @@ static inline void check_key_align(void)
|
||||
/* Locking:
|
||||
*
|
||||
* - Readers must take rcu_read_lock and hold it the entire time that the flow
|
||||
* must continue to exist. Readers need not take delete_lock. They *may*
|
||||
* examine 'deleted' *if* it is important not to read stale data.
|
||||
* must continue to exist.
|
||||
*
|
||||
* - Deleters must take rcu_read_lock and call flow_del to verify that another
|
||||
* thread has not already deleted the flow. If not, do a deferred free of
|
||||
* the flow with call_rcu, then rcu_assign_pointer or [h]list_del_rcu the
|
||||
* flow.
|
||||
*
|
||||
* - In-place update not yet contemplated.
|
||||
* - Writers must hold dp_mutex.
|
||||
*/
|
||||
struct sw_flow {
|
||||
struct sw_flow_key key;
|
||||
@@ -81,7 +74,6 @@ struct sw_flow {
|
||||
uint64_t packet_count; /* Number of packets associated with this entry */
|
||||
uint64_t byte_count; /* Number of bytes associated with this entry */
|
||||
|
||||
atomic_t deleted; /* 0 if not deleted, 1 if deleted. */
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
@@ -92,7 +84,6 @@ struct sw_flow *flow_alloc(int n_actions, gfp_t flags);
|
||||
void flow_free(struct sw_flow *);
|
||||
void flow_deferred_free(struct sw_flow *);
|
||||
void flow_extract(struct sk_buff *, uint16_t in_port, struct sw_flow_key *);
|
||||
int flow_del(struct sw_flow *);
|
||||
void flow_extract_match(struct sw_flow_key* to, const struct ofp_match* from);
|
||||
void flow_fill_match(struct ofp_match* to, const struct sw_flow_key* from);
|
||||
|
||||
|
||||
@@ -406,7 +406,6 @@ add_flow(struct sw_chain *chain, const struct ofp_flow_mod *ofm)
|
||||
flow->init_time = jiffies;
|
||||
flow->byte_count = 0;
|
||||
flow->packet_count = 0;
|
||||
atomic_set(&flow->deleted, 0);
|
||||
spin_lock_init(&flow->lock);
|
||||
memcpy(flow->actions, ofm->actions, n_acts * sizeof *flow->actions);
|
||||
|
||||
|
||||
@@ -49,10 +49,6 @@
|
||||
#define DUMMY_MAX_FLOW 8192
|
||||
|
||||
|
||||
/* xxx Explain need for this separate list because of RCU */
|
||||
static spinlock_t pending_free_lock;
|
||||
static struct list_head pending_free_list;
|
||||
|
||||
/* sw_flow private data for dummy table entries. */
|
||||
struct sw_flow_dummy {
|
||||
struct list_head node;
|
||||
@@ -63,43 +59,14 @@ struct sw_flow_dummy {
|
||||
struct sw_table_dummy {
|
||||
struct sw_table swt;
|
||||
|
||||
spinlock_t lock;
|
||||
unsigned int max_flows;
|
||||
atomic_t n_flows;
|
||||
unsigned int n_flows;
|
||||
struct list_head flows;
|
||||
struct list_head iter_flows;
|
||||
unsigned long int next_serial;
|
||||
};
|
||||
|
||||
|
||||
static void table_dummy_sfw_destroy(struct sw_flow_dummy *sfw)
|
||||
{
|
||||
/* xxx Remove the entry from hardware. If you need to do any other
|
||||
* xxx clean-up associated with the entry, do it here.
|
||||
*/
|
||||
|
||||
kfree(sfw);
|
||||
}
|
||||
|
||||
static void table_dummy_rcu_callback(struct rcu_head *rcu)
|
||||
{
|
||||
struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu);
|
||||
|
||||
spin_lock(&pending_free_lock);
|
||||
if (flow->private) {
|
||||
struct sw_flow_dummy *sfw = flow->private;
|
||||
list_add(&sfw->node, &pending_free_list);
|
||||
flow->private = NULL;
|
||||
}
|
||||
spin_unlock(&pending_free_lock);
|
||||
flow_free(flow);
|
||||
}
|
||||
|
||||
static void table_dummy_flow_deferred_free(struct sw_flow *flow)
|
||||
{
|
||||
call_rcu(&flow->rcu, table_dummy_rcu_callback);
|
||||
}
|
||||
|
||||
static struct sw_flow *table_dummy_lookup(struct sw_table *swt,
|
||||
const struct sw_flow_key *key)
|
||||
{
|
||||
@@ -123,7 +90,8 @@ static int table_dummy_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
/* xxx Do whatever needs to be done to insert an entry in hardware.
|
||||
* xxx If the entry can't be inserted, return 0. This stub code
|
||||
* xxx doesn't do anything yet, so we're going to return 0...you
|
||||
* xxx shouldn't.
|
||||
* xxx shouldn't (and you should update n_flows in struct
|
||||
* xxx sw_table_dummy, too).
|
||||
*/
|
||||
kfree(flow->private);
|
||||
return 0;
|
||||
@@ -132,13 +100,12 @@ static int table_dummy_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
|
||||
static int do_delete(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
if (flow_del(flow)) {
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
table_dummy_flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
/* xxx Remove the entry from hardware. If you need to do any other
|
||||
* xxx clean-up associated with the entry, do it here.
|
||||
*/
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int table_dummy_delete(struct sw_table *swt,
|
||||
@@ -148,13 +115,12 @@ static int table_dummy_delete(struct sw_table *swt,
|
||||
struct sw_flow *flow;
|
||||
unsigned int count = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &td->flows, node) {
|
||||
list_for_each_entry (flow, &td->flows, node) {
|
||||
if (flow_del_matches(&flow->key, key, strict)
|
||||
&& (!strict || (flow->priority == priority)))
|
||||
count += do_delete(swt, flow);
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &td->n_flows);
|
||||
td->n_flows -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -163,12 +129,12 @@ static int table_dummy_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
{
|
||||
struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
|
||||
struct sw_flow *flow;
|
||||
struct sw_flow_dummy *sfw, *n;
|
||||
int del_count = 0;
|
||||
uint64_t packet_count = 0;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &td->flows, node) {
|
||||
mutex_lock(&dp_mutex);
|
||||
list_for_each_entry (flow, &td->flows, node) {
|
||||
/* xxx Retrieve the packet count associated with this entry
|
||||
* xxx and store it in "packet_count".
|
||||
*/
|
||||
@@ -187,22 +153,11 @@ static int table_dummy_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
}
|
||||
del_count += do_delete(swt, flow);
|
||||
}
|
||||
if ((i % 50) == 0) {
|
||||
msleep_interruptible(1);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
mutex_unlock(&dp_mutex);
|
||||
|
||||
/* Remove any entries queued for removal */
|
||||
spin_lock_bh(&pending_free_lock);
|
||||
list_for_each_entry_safe (sfw, n, &pending_free_list, node) {
|
||||
list_del(&sfw->node);
|
||||
table_dummy_sfw_destroy(sfw);
|
||||
}
|
||||
spin_unlock_bh(&pending_free_lock);
|
||||
|
||||
if (del_count)
|
||||
atomic_sub(del_count, &td->n_flows);
|
||||
td->n_flows -= del_count;
|
||||
return del_count;
|
||||
}
|
||||
|
||||
@@ -239,7 +194,7 @@ static int table_dummy_iterate(struct sw_table *swt,
|
||||
unsigned long start;
|
||||
|
||||
start = ~position->private[0];
|
||||
list_for_each_entry_rcu (flow, &tl->iter_flows, iter_node) {
|
||||
list_for_each_entry (flow, &tl->iter_flows, iter_node) {
|
||||
if (flow->serial <= start && flow_matches(key, &flow->key)) {
|
||||
int error = callback(flow, private);
|
||||
if (error) {
|
||||
@@ -256,7 +211,7 @@ static void table_dummy_stats(struct sw_table *swt,
|
||||
{
|
||||
struct sw_table_dummy *td = (struct sw_table_dummy *) swt;
|
||||
stats->name = "dummy";
|
||||
stats->n_flows = atomic_read(&td->n_flows);
|
||||
stats->n_flows = td->n_flows;
|
||||
stats->max_flows = td->max_flows;
|
||||
}
|
||||
|
||||
@@ -280,15 +235,11 @@ static struct sw_table *table_dummy_create(void)
|
||||
swt->stats = table_dummy_stats;
|
||||
|
||||
td->max_flows = DUMMY_MAX_FLOW;
|
||||
atomic_set(&td->n_flows, 0);
|
||||
td->n_flows = 0;
|
||||
INIT_LIST_HEAD(&td->flows);
|
||||
INIT_LIST_HEAD(&td->iter_flows);
|
||||
spin_lock_init(&td->lock);
|
||||
td->next_serial = 0;
|
||||
|
||||
INIT_LIST_HEAD(&pending_free_list);
|
||||
spin_lock_init(&pending_free_lock);
|
||||
|
||||
return swt;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __LINUX_KERNEL_WRAPPER_H 1
|
||||
|
||||
#include_next <linux/kernel.h>
|
||||
#include <linux/config.h>
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
@@ -40,4 +41,26 @@ int vprintk(const char *msg, ...)
|
||||
/* Force a compilation error if condition is true */
|
||||
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
|
||||
|
||||
/**
|
||||
* might_sleep - annotation for functions that can sleep
|
||||
*
|
||||
* this macro will print a stack trace if it is executed in an atomic
|
||||
* context (spinlock, irq-handler, ...).
|
||||
*
|
||||
* This is a useful debugging help to be able to catch problems early and not
|
||||
* be bitten later when the calling function happens to sleep when it is not
|
||||
* supposed to.
|
||||
*/
|
||||
#define might_resched() do { } while (0)
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
void __might_sleep(char *file, int line);
|
||||
# define might_sleep() \
|
||||
do { __might_sleep(__FILE__, __LINE__); might_resched(); } while (0)
|
||||
#else
|
||||
# define might_sleep() do { might_resched(); } while (0)
|
||||
#endif
|
||||
|
||||
#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
* Distributed under the terms of the GNU GPL version 2.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/hardirq.h>
|
||||
|
||||
int vprintk(const char *msg, ...)
|
||||
{
|
||||
@@ -25,3 +27,20 @@ int vprintk(const char *msg, ...)
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(vprintk);
|
||||
|
||||
#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
void __might_sleep(char *file, int line)
|
||||
{
|
||||
static unsigned long prev_jiffy; /* ratelimiting */
|
||||
|
||||
if ((in_interrupt()) && !oops_in_progress) {
|
||||
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
|
||||
return;
|
||||
prev_jiffy = jiffies;
|
||||
printk(KERN_ERR "BUG: sleeping function called from invalid"
|
||||
" context at %s:%d\n", file, line);
|
||||
dump_stack();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(__might_sleep);
|
||||
#endif
|
||||
|
||||
+12
-21
@@ -21,9 +21,8 @@ static void kmem_free(void *, size_t);
|
||||
|
||||
struct sw_table_hash {
|
||||
struct sw_table swt;
|
||||
spinlock_t lock;
|
||||
struct crc32 crc32;
|
||||
atomic_t n_flows;
|
||||
unsigned int n_flows;
|
||||
unsigned int bucket_mask; /* Number of buckets minus 1. */
|
||||
struct sw_flow **buckets;
|
||||
};
|
||||
@@ -47,22 +46,19 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
struct sw_table_hash *th = (struct sw_table_hash *) swt;
|
||||
struct sw_flow **bucket;
|
||||
unsigned long int flags;
|
||||
int retval;
|
||||
|
||||
if (flow->key.wildcards != 0)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&th->lock, flags);
|
||||
bucket = find_bucket(swt, &flow->key);
|
||||
if (*bucket == NULL) {
|
||||
atomic_inc(&th->n_flows);
|
||||
th->n_flows++;
|
||||
rcu_assign_pointer(*bucket, flow);
|
||||
retval = 1;
|
||||
} else {
|
||||
struct sw_flow *old_flow = *bucket;
|
||||
if (!memcmp(&old_flow->key, &flow->key, sizeof flow->key)
|
||||
&& flow_del(old_flow)) {
|
||||
if (!memcmp(&old_flow->key, &flow->key, sizeof flow->key)) {
|
||||
rcu_assign_pointer(*bucket, flow);
|
||||
flow_deferred_free(old_flow);
|
||||
retval = 1;
|
||||
@@ -70,19 +66,15 @@ static int table_hash_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&th->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Caller must update n_flows. */
|
||||
static int do_delete(struct sw_flow **bucket, struct sw_flow *flow)
|
||||
{
|
||||
if (flow_del(flow)) {
|
||||
rcu_assign_pointer(*bucket, NULL);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
rcu_assign_pointer(*bucket, NULL);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns number of deleted flows. We can ignore the priority
|
||||
@@ -110,8 +102,7 @@ static int table_hash_delete(struct sw_table *swt,
|
||||
count += do_delete(bucket, flow);
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &th->n_flows);
|
||||
th->n_flows -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -121,6 +112,7 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
unsigned int i;
|
||||
int count = 0;
|
||||
|
||||
mutex_lock(&dp_mutex);
|
||||
for (i = 0; i <= th->bucket_mask; i++) {
|
||||
struct sw_flow **bucket = &th->buckets[i];
|
||||
struct sw_flow *flow = *bucket;
|
||||
@@ -130,9 +122,9 @@ static int table_hash_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
dp_send_flow_expired(dp, flow);
|
||||
}
|
||||
}
|
||||
th->n_flows -= count;
|
||||
mutex_unlock(&dp_mutex);
|
||||
|
||||
if (count)
|
||||
atomic_sub(count, &th->n_flows);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -191,7 +183,7 @@ static void table_hash_stats(struct sw_table *swt,
|
||||
{
|
||||
struct sw_table_hash *th = (struct sw_table_hash *) swt;
|
||||
stats->name = "hash";
|
||||
stats->n_flows = atomic_read(&th->n_flows);
|
||||
stats->n_flows = th->n_flows;
|
||||
stats->max_flows = th->bucket_mask + 1;
|
||||
}
|
||||
|
||||
@@ -223,9 +215,8 @@ struct sw_table *table_hash_create(unsigned int polynomial,
|
||||
swt->iterate = table_hash_iterate;
|
||||
swt->stats = table_hash_stats;
|
||||
|
||||
spin_lock_init(&th->lock);
|
||||
crc32_init(&th->crc32, polynomial);
|
||||
atomic_set(&th->n_flows, 0);
|
||||
th->n_flows = 0;
|
||||
|
||||
return swt;
|
||||
}
|
||||
|
||||
+18
-29
@@ -15,9 +15,8 @@
|
||||
struct sw_table_linear {
|
||||
struct sw_table swt;
|
||||
|
||||
spinlock_t lock;
|
||||
unsigned int max_flows;
|
||||
atomic_t n_flows;
|
||||
unsigned int n_flows;
|
||||
struct list_head flows;
|
||||
struct list_head iter_flows;
|
||||
unsigned long int next_serial;
|
||||
@@ -38,7 +37,6 @@ static struct sw_flow *table_linear_lookup(struct sw_table *swt,
|
||||
static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
|
||||
unsigned long int flags;
|
||||
struct sw_flow *f;
|
||||
|
||||
|
||||
@@ -46,16 +44,13 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
* always be placed behind those with equal priority. Just replace
|
||||
* any flows that match exactly.
|
||||
*/
|
||||
spin_lock_irqsave(&tl->lock, flags);
|
||||
list_for_each_entry_rcu (f, &tl->flows, node) {
|
||||
list_for_each_entry (f, &tl->flows, node) {
|
||||
if (f->priority == flow->priority
|
||||
&& f->key.wildcards == flow->key.wildcards
|
||||
&& flow_matches(&f->key, &flow->key)
|
||||
&& flow_del(f)) {
|
||||
&& flow_matches(&f->key, &flow->key)) {
|
||||
flow->serial = f->serial;
|
||||
list_replace_rcu(&f->node, &flow->node);
|
||||
list_replace_rcu(&f->iter_node, &flow->iter_node);
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
flow_deferred_free(f);
|
||||
return 1;
|
||||
}
|
||||
@@ -65,29 +60,24 @@ static int table_linear_insert(struct sw_table *swt, struct sw_flow *flow)
|
||||
}
|
||||
|
||||
/* Make sure there's room in the table. */
|
||||
if (atomic_read(&tl->n_flows) >= tl->max_flows) {
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
if (tl->n_flows >= tl->max_flows) {
|
||||
return 0;
|
||||
}
|
||||
atomic_inc(&tl->n_flows);
|
||||
tl->n_flows++;
|
||||
|
||||
/* Insert the entry immediately in front of where we're pointing. */
|
||||
flow->serial = tl->next_serial++;
|
||||
list_add_tail_rcu(&flow->node, &f->node);
|
||||
list_add_rcu(&flow->iter_node, &tl->iter_flows);
|
||||
spin_unlock_irqrestore(&tl->lock, flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_delete(struct sw_table *swt, struct sw_flow *flow)
|
||||
{
|
||||
if (flow_del(flow)) {
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
list_del_rcu(&flow->node);
|
||||
list_del_rcu(&flow->iter_node);
|
||||
flow_deferred_free(flow);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int table_linear_delete(struct sw_table *swt,
|
||||
@@ -97,13 +87,12 @@ static int table_linear_delete(struct sw_table *swt,
|
||||
struct sw_flow *flow;
|
||||
unsigned int count = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &tl->flows, node) {
|
||||
list_for_each_entry (flow, &tl->flows, node) {
|
||||
if (flow_del_matches(&flow->key, key, strict)
|
||||
&& (!strict || (flow->priority == priority)))
|
||||
count += do_delete(swt, flow);
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &tl->n_flows);
|
||||
tl->n_flows -= count;
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -113,15 +102,16 @@ static int table_linear_timeout(struct datapath *dp, struct sw_table *swt)
|
||||
struct sw_flow *flow;
|
||||
int count = 0;
|
||||
|
||||
list_for_each_entry_rcu (flow, &tl->flows, node) {
|
||||
mutex_lock(&dp_mutex);
|
||||
list_for_each_entry (flow, &tl->flows, node) {
|
||||
if (flow_timeout(flow)) {
|
||||
count += do_delete(swt, flow);
|
||||
if (dp->flags & OFPC_SEND_FLOW_EXP)
|
||||
dp_send_flow_expired(dp, flow);
|
||||
}
|
||||
}
|
||||
if (count)
|
||||
atomic_sub(count, &tl->n_flows);
|
||||
tl->n_flows -= count;
|
||||
mutex_unlock(&dp_mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -149,7 +139,7 @@ static int table_linear_iterate(struct sw_table *swt,
|
||||
unsigned long start;
|
||||
|
||||
start = position->private[0];
|
||||
list_for_each_entry_rcu (flow, &tl->iter_flows, iter_node) {
|
||||
list_for_each_entry (flow, &tl->iter_flows, iter_node) {
|
||||
if (flow->serial >= start && flow_matches(key, &flow->key)) {
|
||||
int error = callback(flow, private);
|
||||
if (error) {
|
||||
@@ -166,7 +156,7 @@ static void table_linear_stats(struct sw_table *swt,
|
||||
{
|
||||
struct sw_table_linear *tl = (struct sw_table_linear *) swt;
|
||||
stats->name = "linear";
|
||||
stats->n_flows = atomic_read(&tl->n_flows);
|
||||
stats->n_flows = tl->n_flows;
|
||||
stats->max_flows = tl->max_flows;
|
||||
}
|
||||
|
||||
@@ -190,10 +180,9 @@ struct sw_table *table_linear_create(unsigned int max_flows)
|
||||
swt->stats = table_linear_stats;
|
||||
|
||||
tl->max_flows = max_flows;
|
||||
atomic_set(&tl->n_flows, 0);
|
||||
tl->n_flows = 0;
|
||||
INIT_LIST_HEAD(&tl->flows);
|
||||
INIT_LIST_HEAD(&tl->iter_flows);
|
||||
spin_lock_init(&tl->lock);
|
||||
tl->next_serial = 0;
|
||||
|
||||
return swt;
|
||||
|
||||
Reference in New Issue
Block a user