aboutsummaryrefslogtreecommitdiff
path: root/datapath/table.c
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2010-12-28 10:39:52 -0800
committerBen Pfaff <blp@nicira.com>2011-01-27 21:08:35 -0800
commit704a1e09e9b31ea39ca41c028c7c6aaf2482283a (patch)
treed270596243806665073b1773c2ee5f81ddc8abd0 /datapath/table.c
parentc662c789ed30a1e122967d5d183558e1bfe755a8 (diff)
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace software, so that either one can be upgraded or rolled back independent of the other. To do this in full generality, it must be possible to change the kernel's idea of the flow key separately from the userspace version. In turn, that means that flow keys must become variable-length. This does not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form, because that would require userspace to know how much space to allocate for each flow's key in advance, or to allocate as much space as could possibly be needed. Neither choice is very attractive. This commit prepares for a different solution, by replacing ODP_FLOW_LIST by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath on each call. It is much cleaner to allocate the maximum amount of space for a single flow key than to do so for possibly a very large number of flow keys. As a side effect, this patch also fixes a race condition that sometimes made "ovs-dpctl dump-flows" print an error: previously, flows were listed and then their actions were retrieved, which left a window in which ovs-vswitchd could delete the flow. Now dumping a flow and its actions is a single step, closing that window. Dumping all of the flows in a datapath is no longer an atomic step, so now it is possible to miss some flows or see a single flow twice during iteration, if the flow table is modified by another process. It doesn't look like this should be a problem for ovs-vswitchd. It would be faster to retrieve a number of flows in batch instead of just one at a time, but that will naturally happen later when the kernel datapath interface is changed to use Netlink, so this patch does not bother with it. Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'datapath/table.c')
-rw-r--r--datapath/table.c47
1 files changed, 47 insertions, 0 deletions
diff --git a/datapath/table.c b/datapath/table.c
index 79b9bc1b..35a532e8 100644
--- a/datapath/table.c
+++ b/datapath/table.c
@@ -251,6 +251,53 @@ int tbl_foreach(struct tbl *table,
return 0;
}
+/**
+ * tbl_next - find next node in hash table
+ * @table: table to iterate
+ * @bucketp: On entry, hash value of bucket to start from. On exit, updated
+ * to bucket to start from on next call.
+ * @objp: On entry, index to start from within first bucket. On exit, updated
+ * to index to start from on next call.
+ *
+ * Returns the next node in @table in hash order, or %NULL when no nodes remain
+ * in the hash table.
+ *
+ * On entry, uses the values that @bucketp and @objp reference to determine
+ * where to begin iteration. Use 0 for both values to begin a new iteration.
+ * On exit, stores the values to pass on the next iteration into @bucketp and
+ * @objp's referents.
+ */
+struct tbl_node *tbl_next(struct tbl *table, u32 *bucketp, u32 *objp)
+{
+ unsigned int n_l1 = table->n_buckets >> TBL_L1_SHIFT;
+ u32 s_l1_idx = *bucketp >> TBL_L1_SHIFT;
+ u32 s_l2_idx = *bucketp & (TBL_L2_SIZE - 1);
+ u32 s_obj = *objp;
+ unsigned int l1_idx;
+
+ for (l1_idx = s_l1_idx; l1_idx < n_l1; l1_idx++) {
+ struct tbl_bucket __rcu **l2 = table->buckets[l1_idx];
+ unsigned int l2_idx;
+
+ for (l2_idx = s_l2_idx; l2_idx < TBL_L2_SIZE; l2_idx++) {
+ struct tbl_bucket *bucket;
+
+ bucket = rcu_dereference(l2[l2_idx]);
+ if (bucket && s_obj < bucket->n_objs) {
+ *bucketp = (l1_idx << TBL_L1_SHIFT) + l2_idx;
+ *objp = s_obj + 1;
+ return bucket->objs[s_obj];
+ }
+
+ s_obj = 0;
+ }
+ s_l2_idx = 0;
+ }
+ *bucketp = 0;
+ *objp = 0;
+ return NULL;
+}
+
static int insert_table_flow(struct tbl_node *node, void *new_table_)
{
struct tbl *new_table = new_table_;