[ovs-dev] [PATCHv3 4/7] dpif: Make dpif_flow_dump_next() thread-safe.
Joe Stringer
joestringer at nicira.com
Wed Feb 12 18:37:07 UTC 2014
This patch makes it the caller's responsibility to initialize a
per-thread 'state' object and pass it down to the dpif_flow_dump_next()
implementation. The implementation can expect to be called from multiple
threads with the same 'iter' and different 'state' objects.
When flow_dump_next() returns non-zero, the implementation must ensure
that subsequent calls with the same arguments also return non-zero.
Subsequent calls with the same 'iter' and different 'state' may return
zero, but should make progress towards returning non-zero.
Signed-off-by: Joe Stringer <joestringer at nicira.com>
---
v3: Rebase.
v2: Replace 'buffer' with opaque per-thread 'state'.
Update dpif.[ch] documentation.
Track status in dpif-linux.
dpif_flow_stats is no longer modified by this patch.
---
lib/dpif-linux.c | 19 +++++++++++--------
lib/dpif-netdev.c | 40 ++++++++++++++++++++++++++--------------
lib/dpif-provider.h | 27 +++++++++++++++++----------
lib/dpif.c | 23 ++++++++++++++---------
lib/dpif.h | 20 ++++++++++++++------
ofproto/ofproto-dpif-upcall.c | 7 +++++--
ofproto/ofproto-dpif.c | 8 ++++++--
utilities/ovs-dpctl.c | 9 ++++++---
8 files changed, 99 insertions(+), 54 deletions(-)
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 351b125..c77eb26 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -997,7 +997,7 @@ struct dpif_linux_flow_state {
struct dpif_linux_flow_iter {
struct nl_dump dump;
- void *state;
+ atomic_int status;
};
static void
@@ -1038,21 +1038,20 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **iterp)
dpif_linux_flow_to_ofpbuf(&request, buf);
nl_dump_start(&iter->dump, NETLINK_GENERIC, buf);
ofpbuf_delete(buf);
-
- dpif_linux_flow_dump_state_init(&iter->state);
+ atomic_init(&iter->status, 0);
return 0;
}
static int
-dpif_linux_flow_dump_next(const struct dpif *dpif_, void *iter_,
+dpif_linux_flow_dump_next(const struct dpif *dpif_, void *iter_, void *state_,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
struct dpif_linux_flow_iter *iter = iter_;
- struct dpif_linux_flow_state *state = iter->state;
+ struct dpif_linux_flow_state *state = state_;
struct ofpbuf buf;
int error;
@@ -1066,6 +1065,7 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_, void *iter_,
error = dpif_linux_flow_from_ofpbuf(&state->flow, &buf);
if (error) {
+ atomic_store(&iter->status, error);
return error;
}
@@ -1105,10 +1105,13 @@ static int
dpif_linux_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
{
struct dpif_linux_flow_iter *iter = iter_;
- int error = nl_dump_done(&iter->dump);
- dpif_linux_flow_dump_state_uninit(iter->state);
+ int dump_status;
+ unsigned int nl_status = nl_dump_done(&iter->dump);
+
+ atomic_read(&iter->status, &dump_status);
+ atomic_destroy(iter->status);
free(iter);
- return error;
+ return dump_status ? dump_status : nl_status;
}
static void
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 0ba1e46..0646067 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1313,7 +1313,8 @@ struct dp_netdev_flow_state {
struct dp_netdev_flow_iter {
uint32_t bucket;
uint32_t offset;
- void *state;
+ int status;
+ struct ovs_mutex mutex;
};
static void
@@ -1342,32 +1343,43 @@ dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **iterp)
*iterp = iter = xmalloc(sizeof *iter);
iter->bucket = 0;
iter->offset = 0;
- dpif_netdev_flow_dump_state_init(&iter->state);
+ iter->status = 0;
+ ovs_mutex_init(&iter->mutex);
return 0;
}
static int
-dpif_netdev_flow_dump_next(const struct dpif *dpif, void *iter_,
+dpif_netdev_flow_dump_next(const struct dpif *dpif, void *iter_, void *state_,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats)
{
struct dp_netdev_flow_iter *iter = iter_;
- struct dp_netdev_flow_state *state = iter->state;
+ struct dp_netdev_flow_state *state = state_;
struct dp_netdev *dp = get_dp_netdev(dpif);
struct dp_netdev_flow *netdev_flow;
- struct hmap_node *node;
+ int error;
- fat_rwlock_rdlock(&dp->cls.rwlock);
- node = hmap_at_position(&dp->flow_table, &iter->bucket, &iter->offset);
- if (node) {
- netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
- dp_netdev_flow_ref(netdev_flow);
+ ovs_mutex_lock(&iter->mutex);
+ error = iter->status;
+ if (!error) {
+ struct hmap_node *node;
+
+ fat_rwlock_rdlock(&dp->cls.rwlock);
+ node = hmap_at_position(&dp->flow_table, &iter->bucket, &iter->offset);
+ if (node) {
+ netdev_flow = CONTAINER_OF(node, struct dp_netdev_flow, node);
+ dp_netdev_flow_ref(netdev_flow);
+ }
+ fat_rwlock_unlock(&dp->cls.rwlock);
+ if (!node) {
+ iter->status = error = EOF;
+ }
}
- fat_rwlock_unlock(&dp->cls.rwlock);
- if (!node) {
- return EOF;
+ ovs_mutex_unlock(&iter->mutex);
+ if (error) {
+ return error;
}
if (key) {
@@ -1422,7 +1434,7 @@ dpif_netdev_flow_dump_done(const struct dpif *dpif OVS_UNUSED, void *iter_)
{
struct dp_netdev_flow_iter *iter = iter_;
- dpif_netdev_flow_dump_state_uninit(iter->state);
+ ovs_mutex_destroy(&iter->mutex);
free(iter);
return 0;
}
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 57b37b0..c85de5f 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -272,13 +272,18 @@ struct dpif_class {
* On failure, returns a positive errno value. */
int (*flow_dump_start)(const struct dpif *dpif, void **iterp);
- /* Attempts to retrieve another flow from 'dpif' for 'iter', which was
- * initialized by a successful call to the 'flow_dump_start' function for
- * 'dpif'. On success, updates the output parameters as described below
- * and returns 0. Returns EOF if the end of the flow table has been
- * reached, or a positive errno value on error. This function will not be
- * called again once it returns nonzero within a given iteration (but the
- * 'flow_dump_done' function will be called afterward).
+ /* Attempts to retrieve another flow from 'dpif' for 'iter', using
+ * 'state' for storage. 'iter' must have been initialized by a successful
+ * call to the 'flow_dump_start' function for 'dpif'. 'state' must have
+ * been initialised with a call to the 'flow_dump_state_init' function for
+ * 'dpif.
+ *
+ * On success, updates the output parameters as described below and returns
+ * 0. Returns EOF if the end of the flow table has been reached, or a
+ * positive errno value on error. Multiple threads may use the same 'dpif'
+ * and 'iter' with this function, but all other parameters must be
+ * different for each thread. If this function returns non-zero,
+ * subsequent calls with the same arguments will also return non-zero.
*
* On success:
*
@@ -300,15 +305,17 @@ struct dpif_class {
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' must guarantee that it
* remains accessible and unchanging until at least the next call to
- * 'flow_dump_next' or 'flow_dump_done' for 'iter'. */
- int (*flow_dump_next)(const struct dpif *dpif, void *iter,
+ * 'flow_dump_next' or 'flow_dump_done' for 'iter' and 'state'. */
+ int (*flow_dump_next)(const struct dpif *dpif, void *iter, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
const struct dpif_flow_stats **stats);
/* Releases resources from 'dpif' for 'iter', which was initialized by a
- * successful call to the 'flow_dump_start' function for 'dpif'. */
+ * successful call to the 'flow_dump_start' function for 'dpif'. Callers
+ * must ensure that this function is called once within a given iteration,
+ * as the final flow dump operation. */
int (*flow_dump_done)(const struct dpif *dpif, void *iter);
/* Releases 'state' which was initialized by a call to the
diff --git a/lib/dpif.c b/lib/dpif.c
index 7126571..f972011 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -992,12 +992,17 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
log_operation(dpif, "flow_dump_start", dump->error);
}
-/* Attempts to retrieve another flow from 'dump', which must have been
- * initialized with dpif_flow_dump_start(). On success, updates the output
- * parameters as described below and returns true. Otherwise, returns false.
- * Failure might indicate an actual error or merely the end of the flow table.
- * An error status for the entire dump operation is provided when it is
- * completed by calling dpif_flow_dump_done().
+/* Attempts to retrieve another flow from 'dump', using 'state' for
+ * thread-local storage. 'dump' must have been initialized with
+ * dpif_flow_dump_start(), and 'state' must have been initialized with
+ * dpif_flow_state_init().
+ *
+ * On success, updates the output parameters as described below and returns
+ * true. Otherwise, returns false. Failure might indicate an actual error or
+ * merely the end of the flow table. An error status for the entire dump
+ * operation is provided when it is completed by calling dpif_flow_dump_done().
+ * Multiple threads may use the same 'dump' with this function, but all other
+ * parameters must not be shared.
*
* On success, if 'key' and 'key_len' are nonnull then '*key' and '*key_len'
* will be set to Netlink attributes with types OVS_KEY_ATTR_* representing the
@@ -1009,9 +1014,9 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif)
* All of the returned data is owned by 'dpif', not by the caller, and the
* caller must not modify or free it. 'dpif' guarantees that it remains
* accessible and unchanging until at least the next call to 'flow_dump_next'
- * or 'flow_dump_done' for 'dump'. */
+ * or 'flow_dump_done' for 'dump' and 'state'. */
bool
-dpif_flow_dump_next(struct dpif_flow_dump *dump,
+dpif_flow_dump_next(struct dpif_flow_dump *dump, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
@@ -1021,7 +1026,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump,
int error = dump->error;
if (!error) {
- error = dpif->dpif_class->flow_dump_next(dpif, dump->iter,
+ error = dpif->dpif_class->flow_dump_next(dpif, dump->iter, state,
key, key_len,
mask, mask_len,
actions, actions_len,
diff --git a/lib/dpif.h b/lib/dpif.h
index e4f75b1..65de686 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -356,11 +356,19 @@
* thread-safe: they may be called from different threads only on
* different dpif objects.
*
- * - Functions that operate on struct dpif_port_dump or struct
- * dpif_flow_dump are conditionally thread-safe with respect to those
- * objects. That is, one may dump ports or flows from any number of
- * threads at once, but each thread must use its own struct dpif_port_dump
- * or dpif_flow_dump.
+ * - dpif_flow_dump_next() is conditionally thread-safe: It may be called
+ * from different threads with the same 'struct dpif_flow_dump', but all
+ * other parameters must be different for each thread.
+ *
+ * - dpif_flow_dump_done() is conditionally thread-safe: All threads that
+ * share the same 'struct dpif_flow_dump' must have finished using it.
+ * This function must then be called exactly once for a particular
+ * dpif_flow_dump to finish the corresponding flow dump operation.
+ *
+ * - Functions that operate on 'struct dpif_port_dump' are conditionally
+ * thread-safe with respect to those objects. That is, one may dump ports
+ * from any number of threads at once, but each thread must use its own
+ * struct dpif_port_dump.
*/
#ifndef DPIF_H
#define DPIF_H 1
@@ -511,7 +519,7 @@ struct dpif_flow_dump {
};
void dpif_flow_dump_state_init(const struct dpif *, void **statep);;
void dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *);
-bool dpif_flow_dump_next(struct dpif_flow_dump *,
+bool dpif_flow_dump_next(struct dpif_flow_dump *, void *state,
const struct nlattr **key, size_t *key_len,
const struct nlattr **mask, size_t *mask_len,
const struct nlattr **actions, size_t *actions_len,
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 11f76be..a0e77a8 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -536,6 +536,7 @@ udpif_flow_dumper(void *arg)
bool need_revalidate;
uint64_t reval_seq;
size_t n_flows, i;
+ void *state = NULL;
reval_seq = seq_read(udpif->reval_seq);
need_revalidate = udpif->last_reval_seq != reval_seq;
@@ -547,8 +548,9 @@ udpif_flow_dumper(void *arg)
start_time = time_msec();
dpif_flow_dump_start(&dump, udpif->dpif);
- while (dpif_flow_dump_next(&dump, &key, &key_len, &mask, &mask_len,
- NULL, NULL, &stats)
+ dpif_flow_dump_state_init(udpif->dpif, &state);
+ while (dpif_flow_dump_next(&dump, state, &key, &key_len,
+ &mask, &mask_len, NULL, NULL, &stats)
&& !latch_is_set(&udpif->exit_latch)) {
struct udpif_flow_dump *udump = xmalloc(sizeof *udump);
struct revalidator *revalidator;
@@ -579,6 +581,7 @@ udpif_flow_dumper(void *arg)
xpthread_cond_signal(&revalidator->wake_cond);
ovs_mutex_unlock(&revalidator->mutex);
}
+ dpif_flow_dump_state_uninit(udpif->dpif, state);
dpif_flow_dump_done(&dump);
/* Let all the revalidators finish and garbage collect. */
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 7b3e1eb..842fb26 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4127,6 +4127,7 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct hmap portno_names;
+ void *state = NULL;
ofproto = ofproto_dpif_lookup(argv[argc - 1]);
if (!ofproto) {
@@ -4145,8 +4146,10 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
ds_init(&ds);
dpif_flow_dump_start(&flow_dump, ofproto->backer->dpif);
- while (dpif_flow_dump_next(&flow_dump, &key, &key_len, &mask, &mask_len,
- &actions, &actions_len, &stats)) {
+ dpif_flow_dump_state_init(ofproto->backer->dpif, &state);
+ while (dpif_flow_dump_next(&flow_dump, state, &key, &key_len,
+ &mask, &mask_len, &actions, &actions_len,
+ &stats)) {
if (!ofproto_dpif_contains_flow(ofproto, key, key_len)) {
continue;
}
@@ -4159,6 +4162,7 @@ ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn,
format_odp_actions(&ds, actions, actions_len);
ds_put_char(&ds, '\n');
}
+ dpif_flow_dump_state_uninit(ofproto->backer->dpif, state);
if (dpif_flow_dump_done(&flow_dump)) {
ds_clear(&ds);
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 3b1dff1..04b3f73 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -763,6 +763,7 @@ dpctl_dump_flows(int argc, char *argv[])
char *name, *error, *filter = NULL;
struct flow flow_filter;
struct flow_wildcards wc_filter;
+ void *state = NULL;
if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) {
filter = xstrdup(argv[--argc] + 7);
@@ -790,9 +791,10 @@ dpctl_dump_flows(int argc, char *argv[])
ds_init(&ds);
dpif_flow_dump_start(&flow_dump, dpif);
- while (dpif_flow_dump_next(&flow_dump, &key, &key_len,
- &mask, &mask_len,
- &actions, &actions_len, &stats)) {
+ dpif_flow_dump_state_init(dpif, &state);
+ while (dpif_flow_dump_next(&flow_dump, state, &key, &key_len,
+ &mask, &mask_len, &actions, &actions_len,
+ &stats)) {
if (filter) {
struct flow flow;
struct flow_wildcards wc;
@@ -823,6 +825,7 @@ dpctl_dump_flows(int argc, char *argv[])
format_odp_actions(&ds, actions, actions_len);
printf("%s\n", ds_cstr(&ds));
}
+ dpif_flow_dump_state_uninit(dpif, state);
dpif_flow_dump_done(&flow_dump);
free(filter);
--
1.7.9.5
More information about the dev
mailing list