summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeng He <hepeng.0320@bytedance.com>2022-05-26 02:47:45 +0000
committerIlya Maximets <i.maximets@ovn.org>2022-05-30 23:40:57 +0200
commit77c89b0d25e24acf2476aae5b7407fc5901be858 (patch)
treec363c657d55cd41b28c6e28dd113f6e6b4b95f24
parentc8c78a76e540b2c3542c655c72fe581247919e26 (diff)
downloadopenvswitch-77c89b0d25e24acf2476aae5b7407fc5901be858.tar.gz
ovs-rcu: Add ovsrcu_barrier.
ovsrcu_barrier will block the current thread until all the postponed rcu job has been finished. it's like a OVS version of the Linux kernel rcu_barrier(). Signed-off-by: Peng He <hepeng.0320@bytedance.com> Co-authored-by: Eelco Chaudron <echaudro@redhat.com> Signed-off-by: Eelco Chaudron <echaudro@redhat.com> Reviewed-by: David Marchand <david.marchand@redhat.com> Acked-by: Eelco Chaudron <echaudro@redhat.com> Acked-by: Aaron Conole <aconole@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
-rw-r--r--lib/ovs-rcu.c37
-rw-r--r--lib/ovs-rcu.h15
-rw-r--r--tests/library.at2
-rw-r--r--tests/test-rcu.c29
4 files changed, 80 insertions, 3 deletions
diff --git a/lib/ovs-rcu.c b/lib/ovs-rcu.c
index 1866bd308..946aa04d1 100644
--- a/lib/ovs-rcu.c
+++ b/lib/ovs-rcu.c
@@ -444,3 +444,40 @@ ovsrcu_init_module(void)
ovsthread_once_done(&once);
}
}
+
+static void
+ovsrcu_barrier_func(void *seq_)
+{
+ struct seq *seq = (struct seq *) seq_;
+ seq_change(seq);
+}
+
+/* Similar to the kernel rcu_barrier, ovsrcu_barrier waits for all outstanding
+ * RCU callbacks to complete. However, unlike the kernel rcu_barrier, which
+ * might return immediately if there are no outstanding RCU callbacks,
+ * this API will at least wait for a grace period.
+ *
+ * Another issue the caller might need to know is that the barrier is just
+ * for "one-shot", i.e. if inside some RCU callbacks, another RCU callback is
+ * registered, this API only guarantees the first round of RCU callbacks have
+ * been executed after it returns.
+ */
+void
+ovsrcu_barrier(void)
+{
+ struct seq *seq = seq_create();
+ /* First let all threads flush their cbsets. */
+ ovsrcu_synchronize();
+
+ /* Then register a new cbset, ensure this cbset
+ * is at the tail of the global list. */
+ uint64_t seqno = seq_read(seq);
+ ovsrcu_postpone__(ovsrcu_barrier_func, (void *) seq);
+
+ do {
+ seq_wait(seq, seqno);
+ poll_block();
+ } while (seqno == seq_read(seq));
+
+ seq_destroy(seq);
+}
diff --git a/lib/ovs-rcu.h b/lib/ovs-rcu.h
index ecc4c9201..8b397b7fb 100644
--- a/lib/ovs-rcu.h
+++ b/lib/ovs-rcu.h
@@ -155,6 +155,19 @@
* port_delete(id);
* }
*
+ * Use ovsrcu_barrier() to wait for all the outstanding RCU callbacks to
+ * finish. This is useful when you have to destroy some resources however
+ * these resources are referenced in the outstanding RCU callbacks.
+ *
+ * void rcu_cb(void *A) {
+ * do_something(A);
+ * }
+ *
+ * void destroy_A() {
+ * ovsrcu_postpone(rcu_cb, A); // will use A later
+ * ovsrcu_barrier(); // wait for rcu_cb done
+ * do_destroy_A(); // free A
+ * }
*/
#include "compiler.h"
@@ -310,4 +323,6 @@ void ovsrcu_synchronize(void);
void ovsrcu_exit(void);
+void ovsrcu_barrier(void);
+
#endif /* ovs-rcu.h */
diff --git a/tests/library.at b/tests/library.at
index 1702b7556..e27d9e8bc 100644
--- a/tests/library.at
+++ b/tests/library.at
@@ -247,7 +247,7 @@ AT_CHECK([ovstest test-ofpbuf], [0], [])
AT_CLEANUP
AT_SETUP([rcu])
-AT_CHECK([ovstest test-rcu-quiesce], [0], [])
+AT_CHECK([ovstest test-rcu], [0], [])
AT_CLEANUP
AT_SETUP([stopwatch module])
diff --git a/tests/test-rcu.c b/tests/test-rcu.c
index 965f3c49f..bb17092bf 100644
--- a/tests/test-rcu.c
+++ b/tests/test-rcu.c
@@ -35,7 +35,7 @@ quiescer_main(void *aux OVS_UNUSED)
}
static void
-test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+test_rcu_quiesce(void)
{
pthread_t quiescer;
@@ -48,4 +48,29 @@ test_rcu_quiesce(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
xpthread_join(quiescer, NULL);
}
-OVSTEST_REGISTER("test-rcu-quiesce", test_rcu_quiesce);
+static void
+add_count(void *_count)
+{
+ unsigned *count = (unsigned *)_count;
+ (*count) ++;
+}
+
+static void
+test_rcu_barrier(void)
+{
+ unsigned count = 0;
+ for (int i = 0; i < 10; i ++) {
+ ovsrcu_postpone(add_count, &count);
+ }
+
+ ovsrcu_barrier();
+ ovs_assert(count == 10);
+}
+
+static void
+test_rcu(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) {
+ test_rcu_quiesce();
+ test_rcu_barrier();
+}
+
+OVSTEST_REGISTER("test-rcu", test_rcu);