#include <net/ipv6.h>
#include <net/ndisc.h>
-#include "datapath.h"
#include "vlan.h"
#define TBL_MIN_BUCKETS 1024
*d++ = *s++ & *m++;
}
-struct sw_flow *ovs_flow_alloc(void)
+struct sw_flow *ovs_flow_alloc(bool percpu_stats)
{
struct sw_flow *flow;
+ int cpu;
flow = kmem_cache_alloc(flow_cache, GFP_KERNEL);
if (!flow)
return ERR_PTR(-ENOMEM);
- spin_lock_init(&flow->lock);
flow->sf_acts = NULL;
flow->mask = NULL;
+ flow->stats.is_percpu = percpu_stats;
+
+ if (!percpu_stats) {
+ flow->stats.stat = kzalloc(sizeof(*flow->stats.stat), GFP_KERNEL);
+ if (!flow->stats.stat)
+ goto err;
+
+ spin_lock_init(&flow->stats.stat->lock);
+ } else {
+ flow->stats.cpu_stats = alloc_percpu(struct flow_stats);
+ if (!flow->stats.cpu_stats)
+ goto err;
+
+ for_each_possible_cpu(cpu) {
+ struct flow_stats *cpu_stats;
+
+ cpu_stats = per_cpu_ptr(flow->stats.cpu_stats, cpu);
+ spin_lock_init(&cpu_stats->lock);
+ }
+ }
return flow;
+err:
+ kfree(flow);
+ return ERR_PTR(-ENOMEM);
}
int ovs_flow_tbl_count(struct flow_table *table)
static void flow_free(struct sw_flow *flow)
{
kfree((struct sf_flow_acts __force *)flow->sf_acts);
+ if (flow->stats.is_percpu)
+ free_percpu(flow->stats.cpu_stats);
+ else
+ kfree(flow->stats.stat);
kmem_cache_free(flow_cache, flow);
}
flow_free(flow);
}
+static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
+{
+ struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu);
+
+ kfree(mask);
+}
+
+static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
+{
+ if (!mask)
+ return;
+
+ BUG_ON(!mask->ref_count);
+ mask->ref_count--;
+
+ if (!mask->ref_count) {
+ list_del_rcu(&mask->list);
+ if (deferred)
+ call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
+ else
+ kfree(mask);
+ }
+}
+
void ovs_flow_free(struct sw_flow *flow, bool deferred)
{
if (!flow)
return;
- ovs_sw_flow_mask_del_ref(flow->mask, deferred);
+ flow_mask_del_ref(flow->mask, deferred);
if (deferred)
call_rcu(&flow->rcu, rcu_free_flow_callback);
__table_instance_destroy(ti);
}
-void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred)
+void ovs_flow_tbl_destroy(struct flow_table *table)
{
struct table_instance *ti = ovsl_dereference(table->ti);
- table_instance_destroy(ti, deferred);
+ table_instance_destroy(ti, false);
}
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
new_ti = table_instance_alloc(n_buckets);
if (!new_ti)
- return ERR_PTR(-ENOMEM);
+ return NULL;
flow_table_copy_flows(ti, new_ti);
hash = flow_hash(&masked_key, key_start, key_end);
head = find_bucket(ti, hash);
hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) {
- if (flow->mask == mask &&
+ if (flow->mask == mask && flow->hash == hash &&
flow_cmp_masked_key(flow, &masked_key,
key_start, key_end))
return flow;
return NULL;
}
-struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
- const struct sw_flow_key *key)
+struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl,
+ const struct sw_flow_key *key,
+ u32 *n_mask_hit)
{
- struct table_instance *ti = rcu_dereference(tbl->ti);
+ struct table_instance *ti = rcu_dereference_ovsl(tbl->ti);
struct sw_flow_mask *mask;
struct sw_flow *flow;
+ *n_mask_hit = 0;
list_for_each_entry_rcu(mask, &tbl->mask_list, list) {
+ (*n_mask_hit)++;
flow = masked_flow_lookup(ti, key, mask);
if (flow) /* Found */
return flow;
return NULL;
}
-static struct table_instance *table_instance_expand(struct table_instance *ti)
+struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
+ const struct sw_flow_key *key)
{
- return table_instance_rehash(ti, ti->n_buckets * 2);
+ u32 __always_unused n_mask_hit;
+
+ return ovs_flow_tbl_lookup_stats(tbl, key, &n_mask_hit);
}
-void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow)
+int ovs_flow_tbl_num_masks(const struct flow_table *table)
{
- struct table_instance *ti = NULL;
- struct table_instance *new_ti = NULL;
-
- ti = ovsl_dereference(table->ti);
+ struct sw_flow_mask *mask;
+ int num = 0;
- /* Expand table, if necessary, to make room. */
- if (table->count > ti->n_buckets)
- new_ti = table_instance_expand(ti);
- else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL))
- new_ti = table_instance_rehash(ti, ti->n_buckets);
+ list_for_each_entry(mask, &table->mask_list, list)
+ num++;
- if (new_ti && !IS_ERR(new_ti)) {
- rcu_assign_pointer(table->ti, new_ti);
- ovs_flow_tbl_destroy(table, true);
- ti = ovsl_dereference(table->ti);
- table->last_rehash = jiffies;
- }
+ return num;
+}
- flow->hash = flow_hash(&flow->key, flow->mask->range.start,
- flow->mask->range.end);
- table_instance_insert(ti, flow);
- table->count++;
+static struct table_instance *table_instance_expand(struct table_instance *ti)
+{
+ return table_instance_rehash(ti, ti->n_buckets * 2);
}
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
table->count--;
}
-struct sw_flow_mask *ovs_sw_flow_mask_alloc(void)
+static struct sw_flow_mask *mask_alloc(void)
{
struct sw_flow_mask *mask;
return mask;
}
-void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *mask)
+static void mask_add_ref(struct sw_flow_mask *mask)
{
mask->ref_count++;
}
-static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu)
-{
- struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu);
-
- kfree(mask);
-}
-
-void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred)
-{
- if (!mask)
- return;
-
- BUG_ON(!mask->ref_count);
- mask->ref_count--;
-
- if (!mask->ref_count) {
- list_del_rcu(&mask->list);
- if (deferred)
- call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb);
- else
- kfree(mask);
- }
-}
-
static bool mask_equal(const struct sw_flow_mask *a,
const struct sw_flow_mask *b)
{
&& (memcmp(a_, b_, range_n_bytes(&a->range)) == 0);
}
-struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl,
+static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
const struct sw_flow_mask *mask)
{
struct list_head *ml;
return NULL;
}
-/**
- * add a new mask into the mask list.
- * The caller needs to make sure that 'mask' is not the same
- * as any masks that are already on the list.
- */
-void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask)
+/* Add 'mask' into the mask list, if it is not already there. */
+static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
+ struct sw_flow_mask *new)
{
- list_add_rcu(&mask->list, &tbl->mask_list);
+ struct sw_flow_mask *mask;
+ mask = flow_mask_find(tbl, new);
+ if (!mask) {
+ /* Allocate a new mask if none exsits. */
+ mask = mask_alloc();
+ if (!mask)
+ return -ENOMEM;
+ mask->key = new->key;
+ mask->range = new->range;
+ list_add_rcu(&mask->list, &tbl->mask_list);
+ }
+
+ mask_add_ref(mask);
+ flow->mask = mask;
+ return 0;
+}
+
+int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
+ struct sw_flow_mask *mask)
+{
+ struct table_instance *new_ti = NULL;
+ struct table_instance *ti;
+ int err;
+
+ err = flow_mask_insert(table, flow, mask);
+ if (err)
+ return err;
+
+ flow->hash = flow_hash(&flow->key, flow->mask->range.start,
+ flow->mask->range.end);
+ ti = ovsl_dereference(table->ti);
+ table_instance_insert(ti, flow);
+ table->count++;
+
+ /* Expand table, if necessary, to make room. */
+ if (table->count > ti->n_buckets)
+ new_ti = table_instance_expand(ti);
+ else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL))
+ new_ti = table_instance_rehash(ti, ti->n_buckets);
+
+ if (new_ti) {
+ rcu_assign_pointer(table->ti, new_ti);
+ table_instance_destroy(ti, true);
+ table->last_rehash = jiffies;
+ }
+ return 0;
}
/* Initializes the flow module.