/* * Distributed under the terms of the GNU GPL version 2. * Copyright (c) 2007, 2008, 2009 The Board of Trustees of The Leland * Stanford Junior University */ #include "chain.h" #include "datapath.h" #include "flow.h" #include "table.h" #include #include #include #include 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 * due to out-of-memory. */ static int add_table(struct sw_chain *chain, struct sw_table *table, int emerg) { if (table == NULL) return -ENOMEM; if (chain->n_tables >= CHAIN_MAX_TABLES) { printk(KERN_EMERG "%s: too many tables in chain\n", chain->dp->netdev->name); table->destroy(table); return -ENOBUFS; } if (emerg) chain->emerg_table = table; else chain->tables[chain->n_tables++] = table; return 0; } /* Creates and returns a new chain associated with 'dp'. Returns NULL if the * chain cannot be created. */ struct sw_chain *chain_create(struct datapath *dp) { struct sw_chain *chain = kzalloc(sizeof *chain, GFP_KERNEL); if (chain == NULL) goto error; chain->dp = dp; 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, 0)) goto error; } if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS, 0x741B8CD7, TABLE_HASH_MAX_FLOWS), 0) || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 0) || add_table(chain, table_linear_create(TABLE_LINEAR_MAX_FLOWS), 1)) 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 or dp_mutex. */ struct sw_flow *chain_lookup(struct sw_chain *chain, const struct sw_flow_key *key, int emerg) { int i; BUG_ON(key->wildcards); if (emerg) { struct sw_table *t = chain->emerg_table; struct sw_flow *flow = t->lookup(t, key); t->n_lookup++; if (flow) { t->n_matched++; return flow; } } else { for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; struct sw_flow *flow = t->lookup(t, key); t->n_lookup++; if (flow) { t->n_matched++; return flow; } } } return NULL; } /* Inserts 'flow' into 'chain', replacing any duplicate flow. Returns 0 if * successful or a negative error. * * If successful, 'flow' becomes owned by the chain, otherwise it is retained * by the caller. * * Caller must hold dp_mutex. */ int chain_insert(struct sw_chain *chain, struct sw_flow *flow, int emerg) { int i; might_sleep(); if (emerg) { struct sw_table *t = chain->emerg_table; if (t->insert(t, flow)) return 0; } else { for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; if (t->insert(t, flow)) return 0; } } return -ENOBUFS; } /* 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_header *actions, size_t actions_len, int emerg) { int count = 0; int i; if (emerg) { struct sw_table *t = chain->emerg_table; count += t->modify(t, key, priority, strict, actions, actions_len); } else { for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; count += t->modify(t, key, priority, strict, actions, actions_len); } } return count; } /* Checks whether the chain has an entry with the same priority which conflicts * with 'key'. If 'strict' set, wildcards should also match. If 'strict' is not * set, comparison is done 'module wildcards'. * * Returns 'true' if such an entry exists, 'false' otherwise. */ int chain_has_conflict(struct sw_chain *chain, const struct sw_flow_key *key, uint16_t priority, int strict) { int i; for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; if (t->has_conflict(t, key, priority, strict)) { return true; } } return false; } /* Deletes from 'chain' any and all flows that match 'key'. If 'out_port' * is not OFPP_NONE, then matching entries must have that port as an * argument for an output action. If 'strict" is set, then 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. * * Caller must hold dp_mutex. */ int chain_delete(struct sw_chain *chain, const struct sw_flow_key *key, uint16_t out_port, uint16_t priority, int strict, int emerg) { int count = 0; int i; might_sleep(); if (emerg) { struct sw_table *t = chain->emerg_table; count += t->delete(chain->dp, t, key, out_port, priority, strict); } else { for (i = 0; i < chain->n_tables; i++) { struct sw_table *t = chain->tables[i]; count += t->delete(chain->dp, t, key, out_port, priority, strict); } } return count; } /* Performs timeout processing on all the tables in 'chain'. Returns the * number of flow entries deleted through expiration. * * Expensive as currently implemented, since it iterates through the entire * contents of each table. * * 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]; count += t->timeout(chain->dp, t); } return count; } /* Destroys 'chain', which must not have any users. */ void chain_destroy(struct sw_chain *chain) { int i; struct sw_table *t = NULL; synchronize_rcu(); for (i = 0; i < chain->n_tables; i++) { t = chain->tables[i]; if (t->destroy) t->destroy(t); } t = chain->emerg_table; if (t->destroy) t->destroy(t); module_put(chain->owner); kfree(chain); } 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);