Message ID | 20231117052210.26396-1-shahuang@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9910:0:b0:403:3b70:6f57 with SMTP id i16csp311010vqn; Thu, 16 Nov 2023 21:23:15 -0800 (PST) X-Google-Smtp-Source: AGHT+IE+OTCZiiJrdaPFvwVpm6fa+MJXKBGb07vq34QhNnJueWbLJTP3UheIf0YUdrr0JOc47IuY X-Received: by 2002:a17:903:191:b0:1cc:511d:eb43 with SMTP id z17-20020a170903019100b001cc511deb43mr13709348plg.61.1700198595152; Thu, 16 Nov 2023 21:23:15 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1700198595; cv=none; d=google.com; s=arc-20160816; b=wcF6I81ZJWBmDcb1TUD8mlKifb1F2tK3pDjpc6MzIf9aQlt+h2wATTy76FYDv0fApe eJosTg/pJYpCBmYlwyPw7cOpSmEdUf4yN6bH8UYt2NiEuAwSmy9rDIuoehdAc7NCr9e7 WE9ofZipVXhV9d63tE4LfHXQxHP+NMMXapULkxQFxXQB5eYJu/J2pYu2NeqkIrfTZC+z Q0jcm2tA+K3u8BwFNcgsPNaOlm0dy8tM1bpVbB/dXtS8DjTHNoSoZPsu3jtMV/JaLkVh N4s+3JBgkFReiAB10ca1TrxzEu591mJOw6NfKFblNyJGF40/rOsG5SRFX7a+9y0X4wm0 1a+g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=GP1qDRaGGYNkvm0KV8/GWSP2DF/3SQkemClyjrO5cH0=; fh=X9WnwKkDfPYglX9NYtmkULqN2hpGKtAxg+ZpXZqA9NA=; b=IB9fCOmSqT/2e2gceV0KoGR5HmuaM1oXJJNBD5XGAS9mNtU//EmHp44V4GM3/hWV2J GKqa9Wt91ZBcnAM2h5Trc5pv1Z5T0cEDRL6jhWr2FGvjEIppO0x1UsKpMncZXux7Rp2T gwkfcI/A4d94W2h8OUMH5JkjCWmWitHEIK+Z4WsJaRqm3+bQPiFFBZFz82DDg/uDUdvc xxMxDUzoX/FGVvcV6uKx1JDsLlADtoc/3/GB/ks5dVJuqRUR+Qo4hCbm08uI++W52ZcF xkEkZiPtfmY62IEIx3Z4b18mhV0JsnqR2RrUTz0o3/aB7K/hdDto0wV4KZ8wjoZLZNvk Sarw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=X0lYoHpQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:8 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from fry.vger.email (fry.vger.email. [2620:137:e000::3:8]) by mx.google.com with ESMTPS id y17-20020a1709027c9100b001cc5f995bccsi1021641pll.182.2023.11.16.21.23.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Nov 2023 21:23:15 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:8 as permitted sender) client-ip=2620:137:e000::3:8; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=X0lYoHpQ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:8 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by fry.vger.email (Postfix) with ESMTP id 554F082162DB; Thu, 16 Nov 2023 21:23:07 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at fry.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229926AbjKQFWf (ORCPT <rfc822;b08248@gmail.com> + 31 others); Fri, 17 Nov 2023 00:22:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229436AbjKQFWe (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Fri, 17 Nov 2023 00:22:34 -0500 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 43440D4A for <linux-kernel@vger.kernel.org>; Thu, 16 Nov 2023 21:22:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700198550; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=GP1qDRaGGYNkvm0KV8/GWSP2DF/3SQkemClyjrO5cH0=; b=X0lYoHpQVuEQla8IbFNODQ2Zltb1pvDmjXs+lNeNNt9zlTdqf4HLoDuusM9pbWqvSZXwU9 /oJ08uvZbURyPAVfwUSq9wswZ2XgAsHIonAP92eRfapFQphSf1bgxTvw+4qO57gqTAyij5 /S9lEyjI9cEUnDeI0IJMw5dgc4fdCUU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-385-FAvH0CbkOg-51Q-zciuaDQ-1; Fri, 17 Nov 2023 00:22:26 -0500 X-MC-Unique: FAvH0CbkOg-51Q-zciuaDQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 173C6101A529; Fri, 17 Nov 2023 05:22:26 +0000 (UTC) Received: from virt-mtcollins-01.lab.eng.rdu2.redhat.com (virt-mtcollins-01.lab.eng.rdu2.redhat.com [10.8.1.196]) by smtp.corp.redhat.com (Postfix) with ESMTP id 064E5C15881; Fri, 17 Nov 2023 05:22:26 +0000 (UTC) From: Shaoqin Huang <shahuang@redhat.com> To: kvm@vger.kernel.org, kvmarm@lists.linux.dev Cc: Shaoqin Huang <shahuang@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, Shuah Khan <shuah@kernel.org>, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2] KVM: selftests: Fix the dirty_log_test semaphore imbalance Date: Fri, 17 Nov 2023 00:22:09 -0500 Message-Id: <20231117052210.26396-1-shahuang@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.8 X-Spam-Status: No, score=-1.0 required=5.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on fry.vger.email Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (fry.vger.email [0.0.0.0]); Thu, 16 Nov 2023 21:23:07 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1782787442180046681 X-GMAIL-MSGID: 1782787442180046681 |
Series |
[v2] KVM: selftests: Fix the dirty_log_test semaphore imbalance
|
|
Commit Message
Shaoqin Huang
Nov. 17, 2023, 5:22 a.m. UTC
When execute the dirty_log_test on some aarch64 machine, it sometimes
trigger the ASSERT:
==== Test Assertion Failure ====
dirty_log_test.c:384: dirty_ring_vcpu_ring_full
pid=14854 tid=14854 errno=22 - Invalid argument
1 0x00000000004033eb: dirty_ring_collect_dirty_pages at dirty_log_test.c:384
2 0x0000000000402d27: log_mode_collect_dirty_pages at dirty_log_test.c:505
3 (inlined by) run_test at dirty_log_test.c:802
4 0x0000000000403dc7: for_each_guest_mode at guest_modes.c:100
5 0x0000000000401dff: main at dirty_log_test.c:941 (discriminator 3)
6 0x0000ffff9be173c7: ?? ??:0
7 0x0000ffff9be1749f: ?? ??:0
8 0x000000000040206f: _start at ??:?
Didn't continue vcpu even without ring full
The dirty_log_test fails when execute the dirty-ring test, this is
because the sem_vcpu_cont and the sem_vcpu_stop is non-zero value when
execute the dirty_ring_collect_dirty_pages() function. When those two
sem_t variables are non-zero, the dirty_ring_wait_vcpu() at the
beginning of the dirty_ring_collect_dirty_pages() will not wait for the
vcpu to stop, but continue to execute the following code. In this case,
before vcpu stop, if the dirty_ring_vcpu_ring_full is true, and the
dirty_ring_collect_dirty_pages() has passed the check for the
dirty_ring_vcpu_ring_full but hasn't execute the check for the
continued_vcpu, the vcpu stop, and set the dirty_ring_vcpu_ring_full to
false. Then dirty_ring_collect_dirty_pages() will trigger the ASSERT.
Why sem_vcpu_cont and sem_vcpu_stop can be non-zero value? It's because
the dirty_ring_before_vcpu_join() execute the sem_post(&sem_vcpu_cont)
at the end of each dirty-ring test. It can cause two cases:
1. sem_vcpu_cont be non-zero. When we set the host_quit to be true,
the vcpu_worker directly see the host_quit to be true, it quit. So
the log_mode_before_vcpu_join() function will set the sem_vcpu_cont
to 1, since the vcpu_worker has quit, it won't consume it.
2. sem_vcpu_stop be non-zero. When we set the host_quit to be true,
the vcpu_worker has entered the guest state, the next time it exit
from guest state, it will set the sem_vcpu_stop to 1, and then see
the host_quit, no one will consume the sem_vcpu_stop.
When execute more and more dirty-ring tests, the sem_vcpu_cont and
sem_vcpu_stop can be larger and larger, which makes many code paths
don't wait for the sem_t. Thus finally cause the problem.
To fix this problem, we can wait a while before set the host_quit to
true, which gives the vcpu time to enter the guest state, so it will
exit again. Then we can wait the vcpu to exit, and let it continue
again, then the vcpu will see the host_quit. Thus the sem_vcpu_cont and
sem_vcpu_stop will be both zero when test finished.
Signed-off-by: Shaoqin Huang <shahuang@redhat.com>
---
v1->v2:
- Fix the real logic bug, not just fresh the context.
v1: https://lore.kernel.org/all/20231116093536.22256-1-shahuang@redhat.com/
---
tools/testing/selftests/kvm/dirty_log_test.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
Comments
Kindly ping.. On 11/17/23 13:22, Shaoqin Huang wrote: > When execute the dirty_log_test on some aarch64 machine, it sometimes > trigger the ASSERT: > > ==== Test Assertion Failure ==== > dirty_log_test.c:384: dirty_ring_vcpu_ring_full > pid=14854 tid=14854 errno=22 - Invalid argument > 1 0x00000000004033eb: dirty_ring_collect_dirty_pages at dirty_log_test.c:384 > 2 0x0000000000402d27: log_mode_collect_dirty_pages at dirty_log_test.c:505 > 3 (inlined by) run_test at dirty_log_test.c:802 > 4 0x0000000000403dc7: for_each_guest_mode at guest_modes.c:100 > 5 0x0000000000401dff: main at dirty_log_test.c:941 (discriminator 3) > 6 0x0000ffff9be173c7: ?? ??:0 > 7 0x0000ffff9be1749f: ?? ??:0 > 8 0x000000000040206f: _start at ??:? > Didn't continue vcpu even without ring full > > The dirty_log_test fails when execute the dirty-ring test, this is > because the sem_vcpu_cont and the sem_vcpu_stop is non-zero value when > execute the dirty_ring_collect_dirty_pages() function. When those two > sem_t variables are non-zero, the dirty_ring_wait_vcpu() at the > beginning of the dirty_ring_collect_dirty_pages() will not wait for the > vcpu to stop, but continue to execute the following code. In this case, > before vcpu stop, if the dirty_ring_vcpu_ring_full is true, and the > dirty_ring_collect_dirty_pages() has passed the check for the > dirty_ring_vcpu_ring_full but hasn't execute the check for the > continued_vcpu, the vcpu stop, and set the dirty_ring_vcpu_ring_full to > false. Then dirty_ring_collect_dirty_pages() will trigger the ASSERT. > > Why sem_vcpu_cont and sem_vcpu_stop can be non-zero value? It's because > the dirty_ring_before_vcpu_join() execute the sem_post(&sem_vcpu_cont) > at the end of each dirty-ring test. It can cause two cases: > > 1. sem_vcpu_cont be non-zero. When we set the host_quit to be true, > the vcpu_worker directly see the host_quit to be true, it quit. So > the log_mode_before_vcpu_join() function will set the sem_vcpu_cont > to 1, since the vcpu_worker has quit, it won't consume it. > 2. sem_vcpu_stop be non-zero. When we set the host_quit to be true, > the vcpu_worker has entered the guest state, the next time it exit > from guest state, it will set the sem_vcpu_stop to 1, and then see > the host_quit, no one will consume the sem_vcpu_stop. > > When execute more and more dirty-ring tests, the sem_vcpu_cont and > sem_vcpu_stop can be larger and larger, which makes many code paths > don't wait for the sem_t. Thus finally cause the problem. > > To fix this problem, we can wait a while before set the host_quit to > true, which gives the vcpu time to enter the guest state, so it will > exit again. Then we can wait the vcpu to exit, and let it continue > again, then the vcpu will see the host_quit. Thus the sem_vcpu_cont and > sem_vcpu_stop will be both zero when test finished. > > Signed-off-by: Shaoqin Huang <shahuang@redhat.com> > --- > v1->v2: > - Fix the real logic bug, not just fresh the context. > > v1: https://lore.kernel.org/all/20231116093536.22256-1-shahuang@redhat.com/ > --- > tools/testing/selftests/kvm/dirty_log_test.c | 16 +++++++++++++++- > 1 file changed, 15 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c > index 936f3a8d1b83..a6e0ff46a07c 100644 > --- a/tools/testing/selftests/kvm/dirty_log_test.c > +++ b/tools/testing/selftests/kvm/dirty_log_test.c > @@ -417,7 +417,8 @@ static void dirty_ring_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) > > static void dirty_ring_before_vcpu_join(void) > { > - /* Kick another round of vcpu just to make sure it will quit */ > + /* Wait vcpu exit, and let it continue to see the host_quit. */ > + dirty_ring_wait_vcpu(); > sem_post(&sem_vcpu_cont); > } > > @@ -719,6 +720,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) > struct kvm_vm *vm; > unsigned long *bmap; > uint32_t ring_buf_idx = 0; > + int sem_val; > > if (!log_mode_supported()) { > print_skip("Log mode '%s' not supported", > @@ -726,6 +728,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) > return; > } > > + sem_getvalue(&sem_vcpu_stop, &sem_val); > + assert(sem_val == 0); > + sem_getvalue(&sem_vcpu_cont, &sem_val); > + assert(sem_val == 0); > + > /* > * We reserve page table for 2 times of extra dirty mem which > * will definitely cover the original (1G+) test range. Here > @@ -825,6 +832,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) > sync_global_to_guest(vm, iteration); > } > > + /* > + * > + * Before we set the host_quit, let the vcpu has time to run, to make > + * sure we consume the sem_vcpu_stop and the vcpu consume the > + * sem_vcpu_cont, to keep the semaphore balance. > + */ > + usleep(p->interval * 1000); > /* Tell the vcpu thread to quit */ > host_quit = true; > log_mode_before_vcpu_join();
+Peter On Fri, Nov 17, 2023, Shaoqin Huang wrote: > When execute the dirty_log_test on some aarch64 machine, it sometimes > trigger the ASSERT: > > ==== Test Assertion Failure ==== > dirty_log_test.c:384: dirty_ring_vcpu_ring_full > pid=14854 tid=14854 errno=22 - Invalid argument > 1 0x00000000004033eb: dirty_ring_collect_dirty_pages at dirty_log_test.c:384 > 2 0x0000000000402d27: log_mode_collect_dirty_pages at dirty_log_test.c:505 > 3 (inlined by) run_test at dirty_log_test.c:802 > 4 0x0000000000403dc7: for_each_guest_mode at guest_modes.c:100 > 5 0x0000000000401dff: main at dirty_log_test.c:941 (discriminator 3) > 6 0x0000ffff9be173c7: ?? ??:0 > 7 0x0000ffff9be1749f: ?? ??:0 > 8 0x000000000040206f: _start at ??:? > Didn't continue vcpu even without ring full > > The dirty_log_test fails when execute the dirty-ring test, this is > because the sem_vcpu_cont and the sem_vcpu_stop is non-zero value when > execute the dirty_ring_collect_dirty_pages() function. When those two > sem_t variables are non-zero, the dirty_ring_wait_vcpu() at the > beginning of the dirty_ring_collect_dirty_pages() will not wait for the > vcpu to stop, but continue to execute the following code. In this case, > before vcpu stop, if the dirty_ring_vcpu_ring_full is true, and the > dirty_ring_collect_dirty_pages() has passed the check for the > dirty_ring_vcpu_ring_full but hasn't execute the check for the > continued_vcpu, the vcpu stop, and set the dirty_ring_vcpu_ring_full to > false. Then dirty_ring_collect_dirty_pages() will trigger the ASSERT. > > Why sem_vcpu_cont and sem_vcpu_stop can be non-zero value? It's because > the dirty_ring_before_vcpu_join() execute the sem_post(&sem_vcpu_cont) > at the end of each dirty-ring test. It can cause two cases: > > 1. sem_vcpu_cont be non-zero. When we set the host_quit to be true, > the vcpu_worker directly see the host_quit to be true, it quit. So > the log_mode_before_vcpu_join() function will set the sem_vcpu_cont > to 1, since the vcpu_worker has quit, it won't consume it. > 2. sem_vcpu_stop be non-zero. When we set the host_quit to be true, > the vcpu_worker has entered the guest state, the next time it exit > from guest state, it will set the sem_vcpu_stop to 1, and then see > the host_quit, no one will consume the sem_vcpu_stop. > > When execute more and more dirty-ring tests, the sem_vcpu_cont and > sem_vcpu_stop can be larger and larger, which makes many code paths > don't wait for the sem_t. Thus finally cause the problem. > > To fix this problem, we can wait a while before set the host_quit to > true, which gives the vcpu time to enter the guest state, so it will > exit again. Then we can wait the vcpu to exit, and let it continue > again, then the vcpu will see the host_quit. Thus the sem_vcpu_cont and > sem_vcpu_stop will be both zero when test finished. > > Signed-off-by: Shaoqin Huang <shahuang@redhat.com> > --- > v1->v2: > - Fix the real logic bug, not just fresh the context. > > v1: https://lore.kernel.org/all/20231116093536.22256-1-shahuang@redhat.com/ > --- > tools/testing/selftests/kvm/dirty_log_test.c | 16 +++++++++++++++- > 1 file changed, 15 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c > index 936f3a8d1b83..a6e0ff46a07c 100644 > --- a/tools/testing/selftests/kvm/dirty_log_test.c > +++ b/tools/testing/selftests/kvm/dirty_log_test.c > @@ -417,7 +417,8 @@ static void dirty_ring_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) > > static void dirty_ring_before_vcpu_join(void) > { > - /* Kick another round of vcpu just to make sure it will quit */ > + /* Wait vcpu exit, and let it continue to see the host_quit. */ > + dirty_ring_wait_vcpu(); > sem_post(&sem_vcpu_cont); > } > > @@ -719,6 +720,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) > struct kvm_vm *vm; > unsigned long *bmap; > uint32_t ring_buf_idx = 0; > + int sem_val; > > if (!log_mode_supported()) { > print_skip("Log mode '%s' not supported", > @@ -726,6 +728,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) > return; > } > > + sem_getvalue(&sem_vcpu_stop, &sem_val); > + assert(sem_val == 0); > + sem_getvalue(&sem_vcpu_cont, &sem_val); > + assert(sem_val == 0); Never use bare assert() in selftests, they'll either get compiled out or IIRC, will cause the test to silently exit if they fail. Either do nothing, or use TEST_ASSERT(). > + > /* > * We reserve page table for 2 times of extra dirty mem which > * will definitely cover the original (1G+) test range. Here > @@ -825,6 +832,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) > sync_global_to_guest(vm, iteration); > } > > + /* > + * > + * Before we set the host_quit, let the vcpu has time to run, to make > + * sure we consume the sem_vcpu_stop and the vcpu consume the > + * sem_vcpu_cont, to keep the semaphore balance. > + */ > + usleep(p->interval * 1000); Please no. "Wait for a while" is never a complete solution for fixing races. In rare cases, adding a delay might be the only sane workaround, but I doubt that's the case here.
Hi Sean, Thanks for your reviewing. On 1/27/24 04:25, Sean Christopherson wrote: > +Peter > > On Fri, Nov 17, 2023, Shaoqin Huang wrote: >> When execute the dirty_log_test on some aarch64 machine, it sometimes >> trigger the ASSERT: >> >> ==== Test Assertion Failure ==== >> dirty_log_test.c:384: dirty_ring_vcpu_ring_full >> pid=14854 tid=14854 errno=22 - Invalid argument >> 1 0x00000000004033eb: dirty_ring_collect_dirty_pages at dirty_log_test.c:384 >> 2 0x0000000000402d27: log_mode_collect_dirty_pages at dirty_log_test.c:505 >> 3 (inlined by) run_test at dirty_log_test.c:802 >> 4 0x0000000000403dc7: for_each_guest_mode at guest_modes.c:100 >> 5 0x0000000000401dff: main at dirty_log_test.c:941 (discriminator 3) >> 6 0x0000ffff9be173c7: ?? ??:0 >> 7 0x0000ffff9be1749f: ?? ??:0 >> 8 0x000000000040206f: _start at ??:? >> Didn't continue vcpu even without ring full >> >> The dirty_log_test fails when execute the dirty-ring test, this is >> because the sem_vcpu_cont and the sem_vcpu_stop is non-zero value when >> execute the dirty_ring_collect_dirty_pages() function. When those two >> sem_t variables are non-zero, the dirty_ring_wait_vcpu() at the >> beginning of the dirty_ring_collect_dirty_pages() will not wait for the >> vcpu to stop, but continue to execute the following code. In this case, >> before vcpu stop, if the dirty_ring_vcpu_ring_full is true, and the >> dirty_ring_collect_dirty_pages() has passed the check for the >> dirty_ring_vcpu_ring_full but hasn't execute the check for the >> continued_vcpu, the vcpu stop, and set the dirty_ring_vcpu_ring_full to >> false. Then dirty_ring_collect_dirty_pages() will trigger the ASSERT. >> >> Why sem_vcpu_cont and sem_vcpu_stop can be non-zero value? It's because >> the dirty_ring_before_vcpu_join() execute the sem_post(&sem_vcpu_cont) >> at the end of each dirty-ring test. It can cause two cases: >> >> 1. sem_vcpu_cont be non-zero. When we set the host_quit to be true, >> the vcpu_worker directly see the host_quit to be true, it quit. So >> the log_mode_before_vcpu_join() function will set the sem_vcpu_cont >> to 1, since the vcpu_worker has quit, it won't consume it. >> 2. sem_vcpu_stop be non-zero. When we set the host_quit to be true, >> the vcpu_worker has entered the guest state, the next time it exit >> from guest state, it will set the sem_vcpu_stop to 1, and then see >> the host_quit, no one will consume the sem_vcpu_stop. >> >> When execute more and more dirty-ring tests, the sem_vcpu_cont and >> sem_vcpu_stop can be larger and larger, which makes many code paths >> don't wait for the sem_t. Thus finally cause the problem. >> >> To fix this problem, we can wait a while before set the host_quit to >> true, which gives the vcpu time to enter the guest state, so it will >> exit again. Then we can wait the vcpu to exit, and let it continue >> again, then the vcpu will see the host_quit. Thus the sem_vcpu_cont and >> sem_vcpu_stop will be both zero when test finished. >> >> Signed-off-by: Shaoqin Huang <shahuang@redhat.com> >> --- >> v1->v2: >> - Fix the real logic bug, not just fresh the context. >> >> v1: https://lore.kernel.org/all/20231116093536.22256-1-shahuang@redhat.com/ >> --- >> tools/testing/selftests/kvm/dirty_log_test.c | 16 +++++++++++++++- >> 1 file changed, 15 insertions(+), 1 deletion(-) >> >> diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c >> index 936f3a8d1b83..a6e0ff46a07c 100644 >> --- a/tools/testing/selftests/kvm/dirty_log_test.c >> +++ b/tools/testing/selftests/kvm/dirty_log_test.c >> @@ -417,7 +417,8 @@ static void dirty_ring_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) >> >> static void dirty_ring_before_vcpu_join(void) >> { >> - /* Kick another round of vcpu just to make sure it will quit */ >> + /* Wait vcpu exit, and let it continue to see the host_quit. */ >> + dirty_ring_wait_vcpu(); >> sem_post(&sem_vcpu_cont); >> } >> >> @@ -719,6 +720,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) >> struct kvm_vm *vm; >> unsigned long *bmap; >> uint32_t ring_buf_idx = 0; >> + int sem_val; >> >> if (!log_mode_supported()) { >> print_skip("Log mode '%s' not supported", >> @@ -726,6 +728,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) >> return; >> } >> >> + sem_getvalue(&sem_vcpu_stop, &sem_val); >> + assert(sem_val == 0); >> + sem_getvalue(&sem_vcpu_cont, &sem_val); >> + assert(sem_val == 0); > > Never use bare assert() in selftests, they'll either get compiled out or IIRC, > will cause the test to silently exit if they fail. Either do nothing, or use > TEST_ASSERT(). > Sure. I will replace them with TEST_ASSERT(). >> + >> /* >> * We reserve page table for 2 times of extra dirty mem which >> * will definitely cover the original (1G+) test range. Here >> @@ -825,6 +832,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) >> sync_global_to_guest(vm, iteration); >> } >> >> + /* >> + * >> + * Before we set the host_quit, let the vcpu has time to run, to make >> + * sure we consume the sem_vcpu_stop and the vcpu consume the >> + * sem_vcpu_cont, to keep the semaphore balance. >> + */ >> + usleep(p->interval * 1000); > > Please no. "Wait for a while" is never a complete solution for fixing races. > In rare cases, adding a delay might be the only sane workaround, but I doubt that's > the case here. If that's the case. I guess I should keep the current solution. Except you have any better solution, please let me know. Thanks, Shaoqin >
On Thu, Feb 01, 2024, Shaoqin Huang wrote: > > > /* > > > * We reserve page table for 2 times of extra dirty mem which > > > * will definitely cover the original (1G+) test range. Here > > > @@ -825,6 +832,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) > > > sync_global_to_guest(vm, iteration); > > > } > > > + /* > > > + * > > > + * Before we set the host_quit, let the vcpu has time to run, to make > > > + * sure we consume the sem_vcpu_stop and the vcpu consume the > > > + * sem_vcpu_cont, to keep the semaphore balance. > > > + */ > > > + usleep(p->interval * 1000); > > > > Please no. "Wait for a while" is never a complete solution for fixing races. > > In rare cases, adding a delay might be the only sane workaround, but I doubt that's > > the case here. > > If that's the case. I guess I should keep the current solution. Except you > have any better solution, please let me know. Unfortunately I don't have a better solution, and I don't have cycles to stare at this deeply to figure out what how to make the synchronization rock solid. Sorry :-/
diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c index 936f3a8d1b83..a6e0ff46a07c 100644 --- a/tools/testing/selftests/kvm/dirty_log_test.c +++ b/tools/testing/selftests/kvm/dirty_log_test.c @@ -417,7 +417,8 @@ static void dirty_ring_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) static void dirty_ring_before_vcpu_join(void) { - /* Kick another round of vcpu just to make sure it will quit */ + /* Wait vcpu exit, and let it continue to see the host_quit. */ + dirty_ring_wait_vcpu(); sem_post(&sem_vcpu_cont); } @@ -719,6 +720,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) struct kvm_vm *vm; unsigned long *bmap; uint32_t ring_buf_idx = 0; + int sem_val; if (!log_mode_supported()) { print_skip("Log mode '%s' not supported", @@ -726,6 +728,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) return; } + sem_getvalue(&sem_vcpu_stop, &sem_val); + assert(sem_val == 0); + sem_getvalue(&sem_vcpu_cont, &sem_val); + assert(sem_val == 0); + /* * We reserve page table for 2 times of extra dirty mem which * will definitely cover the original (1G+) test range. Here @@ -825,6 +832,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) sync_global_to_guest(vm, iteration); } + /* + * + * Before we set the host_quit, let the vcpu has time to run, to make + * sure we consume the sem_vcpu_stop and the vcpu consume the + * sem_vcpu_cont, to keep the semaphore balance. + */ + usleep(p->interval * 1000); /* Tell the vcpu thread to quit */ host_quit = true; log_mode_before_vcpu_join();