netdev: Add more functions for manipulating device flags.
[sliver-openvswitch.git] / datapath / chain.c
index 458e9e4..c857e9a 100644 (file)
@@ -1,13 +1,20 @@
 /*
  * Distributed under the terms of the GNU GPL version 2.
- * Copyright (c) 2007 The Board of Trustees of The Leland Stanford Junior University
+ * Copyright (c) 2007, 2008 The Board of Trustees of The Leland 
+ * Stanford Junior University
  */
 
 #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
@@ -31,19 +38,25 @@ 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
@@ -95,7 +108,8 @@ int chain_insert(struct sw_chain *chain, struct sw_flow *flow)
  * 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)
+int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, 
+               uint16_t priority, int strict)
 {
        int count = 0;
        int i;
@@ -103,12 +117,11 @@ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, int stri
        for (i = 0; i < chain->n_tables; i++) {
                struct sw_table *t = chain->tables[i];
                rcu_read_lock();
-               count += t->delete(t, key, strict);
+               count += t->delete(t, key, priority, strict);
                rcu_read_unlock();
        }
 
        return count;
-
 }
 
 /* Performs timeout processing on all the tables in 'chain'.  Returns the
@@ -140,8 +153,10 @@ 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);
 }
 
@@ -159,3 +174,28 @@ void chain_print_stats(struct sw_chain *chain)
                                        stats.name, stats.n_flows, stats.max_flows);
        }
 }
+
+
+int chain_set_hw_hook(struct sw_table *(*create_hw_table)(void),
+                     struct module *owner)
+{
+       int retval = -EBUSY;
+
+       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);