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.0...@bytedance.com>
Co-authored-by: Eelco Chaudron <echau...@redhat.com>
Signed-off-by: Eelco Chaudron <echau...@redhat.com>
Reviewed-by: David Marchand <david.march...@redhat.com>
---
 lib/ovs-rcu.c    | 37 +++++++++++++++++++++++++++++++++++++
 lib/ovs-rcu.h    | 15 +++++++++++++++
 tests/library.at |  2 +-
 tests/test-rcu.c | 29 +++++++++++++++++++++++++++--
 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 db4997d8f..6489be2c1 100644
--- a/tests/library.at
+++ b/tests/library.at
@@ -252,7 +252,7 @@ AT_CHECK([ovstest test-barrier], [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);
-- 
2.25.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to