{"id":2227797,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2227797/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260424-force_rcu-v4-3-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp/","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.1/projects/14/?format=json","name":"QEMU Development","link_name":"qemu-devel","list_id":"qemu-devel.nongnu.org","list_email":"qemu-devel@nongnu.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260424-force_rcu-v4-3-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp>","date":"2026-04-24T09:27:08","name":"[v4,3/6] rcu: Use call_rcu() in synchronize_rcu()","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"48409700d6969c321a86c29792555a792f92ce5a","submitter":{"id":90980,"url":"http://patchwork.ozlabs.org/api/1.1/people/90980/?format=json","name":"Akihiko Odaki","email":"odaki@rsg.ci.i.u-tokyo.ac.jp"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260424-force_rcu-v4-3-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp/mbox/","series":[{"id":501326,"url":"http://patchwork.ozlabs.org/api/1.1/series/501326/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/list/?series=501326","date":"2026-04-24T09:27:07","name":"virtio-gpu: Force RCU when unmapping blob","version":4,"mbox":"http://patchwork.ozlabs.org/series/501326/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2227797/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2227797/checks/","tags":{},"headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"key not found in DNS\" header.d=rsg.ci.i.u-tokyo.ac.jp\n header.i=@rsg.ci.i.u-tokyo.ac.jp header.a=rsa-sha256 header.s=rs20250326\n header.b=r1WyzKCf;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g272V3tTsz1xvV\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 19:29:22 +1000 (AEST)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wGCqS-00010r-CE; Fri, 24 Apr 2026 05:29:08 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <odaki@rsg.ci.i.u-tokyo.ac.jp>)\n id 1wGCp3-0007MT-3h\n for qemu-devel@nongnu.org; Fri, 24 Apr 2026 05:27:41 -0400","from www3579.sakura.ne.jp ([49.212.243.89])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <odaki@rsg.ci.i.u-tokyo.ac.jp>)\n id 1wGCoz-0007aK-NY\n for qemu-devel@nongnu.org; Fri, 24 Apr 2026 05:27:40 -0400","from h205.csg.ci.i.u-tokyo.ac.jp (h205.csg.ci.i.u-tokyo.ac.jp\n [133.11.54.205]) (authenticated bits=0)\n by www3579.sakura.ne.jp (8.16.1/8.16.1) with ESMTPSA id 63O9RHIf004358\n (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO);\n Fri, 24 Apr 2026 18:27:26 +0900 (JST)\n (envelope-from odaki@rsg.ci.i.u-tokyo.ac.jp)"],"DKIM-Signature":"a=rsa-sha256; bh=7SYbmYKvDcRKPumrWJKsajKHA9ZFczVdAT+icArERIs=;\n c=relaxed/relaxed; d=rsg.ci.i.u-tokyo.ac.jp;\n h=From:Message-Id:To:Subject:Date;\n s=rs20250326; t=1777022846; v=1;\n b=r1WyzKCfsBIfLiK5TP5o5l70FP47lnYtx4hapwEniSOjN9Yfc2nODW+ZQPB7Pupv\n wSsfa1N9pO6HEaAw3JZr3rVpa42rCHJaqSMAO0+gtrdWHp1G8crqjLPd9Qn01RV4\n Gj+6tHF5TiYDpuLT1c4giYY67m8Ih+SJGCam37+dqNq3CV1oQ/LMj356dbtYr8pX\n UdpzdCgXgLOm2KV9T7I8CLQB3laiUwrVEy0fufUMRsmB6RE3ThjgWTHS+ykQTHTL\n RnLIyqjlSiYcwAjXglBpSIct0OtEkTS2tghvuPjQ1t//+tzmhsMPCcV1V1EjsLhh\n jBiZERECneUh8BYFAKmEjg==","From":"Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>","Date":"Fri, 24 Apr 2026 18:27:08 +0900","Subject":"[PATCH v4 3/6] rcu: Use call_rcu() in synchronize_rcu()","MIME-Version":"1.0","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"7bit","Message-Id":"<20260424-force_rcu-v4-3-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp>","References":"<20260424-force_rcu-v4-0-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp>","In-Reply-To":"<20260424-force_rcu-v4-0-feccfaca0568@rsg.ci.i.u-tokyo.ac.jp>","To":"qemu-devel@nongnu.org, Dmitry Osipenko <dmitry.osipenko@collabora.com>","Cc":"Paolo Bonzini <pbonzini@redhat.com>,\n \"Michael S. Tsirkin\" <mst@redhat.com>,\n =?utf-8?q?Alex_Benn=C3=A9e?= <alex.bennee@linaro.org>,\n Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>","X-Mailer":"b4 0.16-dev-16047","Received-SPF":"pass client-ip=49.212.243.89;\n envelope-from=odaki@rsg.ci.i.u-tokyo.ac.jp; helo=www3579.sakura.ne.jp","X-Spam_score_int":"-16","X-Spam_score":"-1.7","X-Spam_bar":"-","X-Spam_report":"(-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1,\n DKIM_SIGNED=0.1, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=no autolearn_force=no","X-Spam_action":"no action","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"qemu development <qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<https://lists.nongnu.org/archive/html/qemu-devel>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"},"content":"Previously, synchronize_rcu() was a single-threaded implementation that\nis protected with a mutex. It was used only in the RCU thread and tests,\nand real users instead use call_rcu(), which relies on the RCU thread.\n\nThe usage of synchronize_rcu() in tests did not accurately represent\nreal use cases because it caused locking with the mutex, which never\nhappened in real use cases, and it did not exercise the logic in the\nRCU thread.\n\nAdd a new implementation of synchronize_rcu() which uses call_rcu() to\nrepresent real use cases in tests. The old synchronize_rcu() is now\nrenamed to enter_qs() and only used in the RCU thread, making the mutex\nunnecessary.\n\nSigned-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>\nTested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>\n---\n util/rcu.c | 51 +++++++++++++++++++++++++++------------------------\n 1 file changed, 27 insertions(+), 24 deletions(-)","diff":"diff --git a/util/rcu.c b/util/rcu.c\nindex acac9446ea98..3c4af9d213c8 100644\n--- a/util/rcu.c\n+++ b/util/rcu.c\n@@ -38,7 +38,7 @@\n \n /*\n  * Global grace period counter.  Bit 0 is always one in rcu_gp_ctr.\n- * Bits 1 and above are defined in synchronize_rcu.\n+ * Bits 1 and above are defined in enter_qs().\n  */\n #define RCU_GP_LOCKED           (1UL << 0)\n #define RCU_GP_CTR              (1UL << 1)\n@@ -52,7 +52,6 @@ QemuEvent rcu_gp_event;\n static int in_drain_call_rcu;\n static int rcu_call_count;\n static QemuMutex rcu_registry_lock;\n-static QemuMutex rcu_sync_lock;\n \n /*\n  * Check whether a quiescent state was crossed between the beginning of\n@@ -111,7 +110,7 @@ static void wait_for_readers(void)\n          *\n          * If this is the last iteration, this barrier also prevents\n          * frees from seeping upwards, and orders the two wait phases\n-         * on architectures with 32-bit longs; see synchronize_rcu().\n+         * on architectures with 32-bit longs; see enter_qs().\n          */\n         smp_mb_global();\n \n@@ -137,9 +136,9 @@ static void wait_for_readers(void)\n          * wait too much time.\n          *\n          * rcu_register_thread() may add nodes to &registry; it will not\n-         * wake up synchronize_rcu, but that is okay because at least another\n+         * wake up enter_qs(), but that is okay because at least another\n          * thread must exit its RCU read-side critical section before\n-         * synchronize_rcu is done.  The next iteration of the loop will\n+         * enter_qs() is done.  The next iteration of the loop will\n          * move the new thread's rcu_reader from &registry to &qsreaders,\n          * because rcu_gp_ongoing() will return false.\n          *\n@@ -171,10 +170,8 @@ static void wait_for_readers(void)\n     QLIST_SWAP(&registry, &qsreaders, node);\n }\n \n-void synchronize_rcu(void)\n+static void enter_qs(void)\n {\n-    QEMU_LOCK_GUARD(&rcu_sync_lock);\n-\n     /* Write RCU-protected pointers before reading p_rcu_reader->ctr.\n      * Pairs with smp_mb_placeholder() in rcu_read_lock().\n      *\n@@ -289,7 +286,7 @@ static void *call_rcu_thread(void *opaque)\n \n         /*\n          * Fetch rcu_call_count now, we only must process elements that were\n-         * added before synchronize_rcu() starts.\n+         * added before enter_qs() starts.\n          */\n         for (;;) {\n             qemu_event_reset(&rcu_call_ready_event);\n@@ -304,7 +301,7 @@ static void *call_rcu_thread(void *opaque)\n             qemu_event_wait(&rcu_call_ready_event);\n         }\n \n-        synchronize_rcu();\n+        enter_qs();\n         qatomic_sub(&rcu_call_count, n);\n         bql_lock();\n         while (n > 0) {\n@@ -337,15 +334,24 @@ void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node))\n }\n \n \n-struct rcu_drain {\n+typedef struct Sync {\n     struct rcu_head rcu;\n-    QemuEvent drain_complete_event;\n-};\n+    QemuEvent complete_event;\n+} Sync;\n \n-static void drain_rcu_callback(struct rcu_head *node)\n+static void sync_rcu_callback(Sync *sync)\n {\n-    struct rcu_drain *event = (struct rcu_drain *)node;\n-    qemu_event_set(&event->drain_complete_event);\n+    qemu_event_set(&sync->complete_event);\n+}\n+\n+void synchronize_rcu(void)\n+{\n+    Sync sync;\n+\n+    qemu_event_init(&sync.complete_event, false);\n+    call_rcu(&sync, sync_rcu_callback, rcu);\n+    qemu_event_wait(&sync.complete_event);\n+    qemu_event_destroy(&sync.complete_event);\n }\n \n /*\n@@ -359,11 +365,11 @@ static void drain_rcu_callback(struct rcu_head *node)\n \n void drain_call_rcu(void)\n {\n-    struct rcu_drain rcu_drain;\n+    Sync sync;\n     bool locked = bql_locked();\n \n-    memset(&rcu_drain, 0, sizeof(struct rcu_drain));\n-    qemu_event_init(&rcu_drain.drain_complete_event, false);\n+    memset(&sync, 0, sizeof(sync));\n+    qemu_event_init(&sync.complete_event, false);\n \n     if (locked) {\n         bql_unlock();\n@@ -383,8 +389,8 @@ void drain_call_rcu(void)\n      */\n \n     qatomic_inc(&in_drain_call_rcu);\n-    call_rcu1(&rcu_drain.rcu, drain_rcu_callback);\n-    qemu_event_wait(&rcu_drain.drain_complete_event);\n+    call_rcu(&sync, sync_rcu_callback, rcu);\n+    qemu_event_wait(&sync.complete_event);\n     qatomic_dec(&in_drain_call_rcu);\n \n     if (locked) {\n@@ -427,7 +433,6 @@ static void rcu_init_complete(void)\n     QemuThread thread;\n \n     qemu_mutex_init(&rcu_registry_lock);\n-    qemu_mutex_init(&rcu_sync_lock);\n     qemu_event_init(&rcu_gp_event, true);\n \n     qemu_event_init(&rcu_call_ready_event, false);\n@@ -460,7 +465,6 @@ static void rcu_init_lock(void)\n         return;\n     }\n \n-    qemu_mutex_lock(&rcu_sync_lock);\n     qemu_mutex_lock(&rcu_registry_lock);\n }\n \n@@ -471,7 +475,6 @@ static void rcu_init_unlock(void)\n     }\n \n     qemu_mutex_unlock(&rcu_registry_lock);\n-    qemu_mutex_unlock(&rcu_sync_lock);\n }\n \n static void rcu_init_child(void)\n","prefixes":["v4","3/6"]}