Added lookup count to ofp_table_stats.
[sliver-openvswitch.git] / datapath / chain.c
index 2f8708e..7d98056 100644 (file)
@@ -7,8 +7,14 @@
 #include "chain.h"
 #include "flow.h"
 #include "table.h"
+#include <linux/module.h>
 #include <linux/rcupdate.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static struct sw_table *(*create_hw_table_hook)(void);
+static struct module *hw_table_owner;
+static DEFINE_SPINLOCK(hook_lock);
 
 /* Attempts to append 'table' to the set of tables in 'chain'.  Returns 0 or
  * negative error.  If 'table' is null it is assumed that table creation failed
@@ -32,26 +38,31 @@ struct sw_chain *chain_create(struct datapath *dp)
 {
        struct sw_chain *chain = kzalloc(sizeof *chain, GFP_KERNEL);
        if (chain == NULL)
-               return NULL;
+               goto error;
        chain->dp = dp;
-
-       if (add_table(chain, table_mac_create(TABLE_MAC_NUM_BUCKETS, 
-                                               TABLE_MAC_MAX_FLOWS))
-               || add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
-                                               0x741B8CD7, TABLE_HASH_MAX_FLOWS))
-               || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS))) {
-               chain_destroy(chain);
-               return NULL;
+       chain->owner = try_module_get(hw_table_owner) ? hw_table_owner : NULL;
+       if (chain->owner && create_hw_table_hook) {
+               struct sw_table *hwtable = create_hw_table_hook();
+               if (!hwtable || add_table(chain, hwtable))
+                       goto error;
        }
 
+       if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
+                                               0x741B8CD7, TABLE_HASH_MAX_FLOWS))
+           || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS)))
+               goto error;
        return chain;
+
+error:
+       if (chain)
+               chain_destroy(chain);
+       return NULL;
 }
 
 /* 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)
 {
@@ -61,8 +72,11 @@ struct sw_flow *chain_lookup(struct sw_chain *chain,
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
                struct sw_flow *flow = t->lookup(t, key);
-               if (flow)
+               t->n_lookup++;
+               if (flow) {
+                       t->n_matched++;
                        return flow;
+               }
        }
        return NULL;
 }
@@ -73,12 +87,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))
@@ -88,28 +102,50 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
        return -ENOBUFS;
 }
 
-/* Deletes from 'chain' any and all flows that match 'key'.  Returns the number
- * of flows that were deleted.
+/* Modifies actions in 'chain' that match 'key'.  If 'strict' set, wildcards 
+ * and priority must match.  Returns the number of flows that were modified.
+ *
+ * Expensive in the general case as currently implemented, since it requires
+ * iterating through the entire contents of each table for keys that contain
+ * wildcards.  Relatively cheap for fully specified keys. */
+int
+chain_modify(struct sw_chain *chain, const struct sw_flow_key *key, 
+               uint16_t priority, int strict,
+               const struct ofp_action *actions, int n_actions)
+{
+       int count = 0;
+       int i;
+
+       for (i = 0; i < chain->n_tables; i++) {
+               struct sw_table *t = chain->tables[i];
+               count += t->modify(t, key, priority, strict, actions, n_actions);
+       }
+
+       return count;
+}
+
+/* Deletes from 'chain' any and all flows that match 'key'.  If 'strict' set, 
+ * wildcards and priority must match.  Returns the number of flows that were 
+ * deleted.
  *
  * Expensive in the general case as currently implemented, since it requires
  * 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. */
-int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, int strict)
+ * 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, strict);
-               rcu_read_unlock();
+               count += t->delete(t, key, priority, strict);
        }
 
        return count;
-
 }
 
 /* Performs timeout processing on all the tables in 'chain'.  Returns the
@@ -118,17 +154,17 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, int stri
  * 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;
 }
@@ -141,22 +177,33 @@ void chain_destroy(struct sw_chain *chain)
        synchronize_rcu();
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
-               t->destroy(t);
+               if (t->destroy)
+                       t->destroy(t);
        }
+       module_put(chain->owner);
        kfree(chain);
 }
 
-/* Prints statistics for each of the tables in 'chain'. */
-void chain_print_stats(struct sw_chain *chain)
+int chain_set_hw_hook(struct sw_table *(*create_hw_table)(void),
+                     struct module *owner)
 {
-       int i;
+       int retval = -EBUSY;
 
-       printk("\n");
-       for (i = 0; i < chain->n_tables; i++) {
-               struct sw_table *t = chain->tables[i];
-               struct sw_table_stats stats;
-               t->stats(t, &stats);
-               printk("%s: %lu/%lu flows\n",
-                                       stats.name, stats.n_flows, stats.max_flows);
+       spin_lock(&hook_lock);
+       if (!create_hw_table_hook) {
+               create_hw_table_hook = create_hw_table;
+               hw_table_owner = owner;
+               retval = 0;
        }
+       spin_unlock(&hook_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(chain_set_hw_hook);
+
+void chain_clear_hw_hook(void)
+{
+       create_hw_table_hook = NULL;
+       hw_table_owner = NULL;
 }
+EXPORT_SYMBOL(chain_clear_hw_hook);