Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

revocable: Add KUnit test for concurrent access

Add a test case to verify correct synchronization between concurrent
readers and a revocation.

The test setup involves:
1. Consumer 1 enters the critical section (SRCU read lock) and verifies
access to the resource.
2. Provider attempts to revoke the resource. This should block until
Consumer 1 releases the lock.
3. Consumer 2 attempts to enter the critical section while revocation
is pending. It should see the resource as revoked (NULL).
4. Consumer 1 exits, allowing the revocation to complete.

This ensures that the SRCU mechanism correctly enforces grace periods
and that new readers are properly prevented from accessing the resource
once revocation has begun.

A way to run the test:
$ ./tools/testing/kunit/kunit.py run \
--kconfig_add CONFIG_REVOCABLE_KUNIT_TEST=y \
--kconfig_add CONFIG_PROVE_LOCKING=y \
--kconfig_add CONFIG_DEBUG_KERNEL=y \
--kconfig_add CONFIG_DEBUG_INFO=y \
--kconfig_add CONFIG_DEBUG_INFO_DWARF5=y \
--kconfig_add CONFIG_KASAN=y \
--kconfig_add CONFIG_DETECT_HUNG_TASK=y \
--kconfig_add CONFIG_DEFAULT_HUNG_TASK_TIMEOUT="10" \
--arch=x86_64 --raw_output=all \
revocable_test

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Link: https://patch.msgid.link/20260129143733.45618-5-tzungbi@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tzung-Bi Shih and committed by
Greg Kroah-Hartman
98835762 377563ce

+104
+104
drivers/base/revocable_test.c
··· 17 17 * 18 18 * - Provider Use-after-free: Verifies revocable_init() correctly handles 19 19 * race conditions where the provider is being released. 20 + * 21 + * - Concurrent Access: Verifies multiple threads can access the resource. 20 22 */ 21 23 22 24 #include <kunit/test.h> 25 + #include <linux/completion.h> 26 + #include <linux/delay.h> 27 + #include <linux/kthread.h> 23 28 #include <linux/refcount.h> 24 29 #include <linux/revocable.h> 25 30 ··· 165 160 KUNIT_EXPECT_NE(test, ret, 0); 166 161 } 167 162 163 + struct test_concurrent_access_context { 164 + struct kunit *test; 165 + struct revocable_provider __rcu *rp; 166 + struct revocable rev; 167 + struct completion started, enter, exit; 168 + struct task_struct *thread; 169 + void *expected_res; 170 + }; 171 + 172 + static int test_concurrent_access_provider(void *data) 173 + { 174 + struct test_concurrent_access_context *ctx = data; 175 + 176 + complete(&ctx->started); 177 + 178 + wait_for_completion(&ctx->enter); 179 + revocable_provider_revoke(&ctx->rp); 180 + KUNIT_EXPECT_PTR_EQ(ctx->test, unrcu_pointer(ctx->rp), NULL); 181 + 182 + return 0; 183 + } 184 + 185 + static int test_concurrent_access_consumer(void *data) 186 + { 187 + struct test_concurrent_access_context *ctx = data; 188 + void *res; 189 + 190 + complete(&ctx->started); 191 + 192 + wait_for_completion(&ctx->enter); 193 + res = revocable_try_access(&ctx->rev); 194 + KUNIT_EXPECT_PTR_EQ(ctx->test, res, ctx->expected_res); 195 + 196 + wait_for_completion(&ctx->exit); 197 + revocable_withdraw_access(&ctx->rev); 198 + 199 + return 0; 200 + } 201 + 202 + static void revocable_test_concurrent_access(struct kunit *test) 203 + { 204 + struct revocable_provider __rcu *rp; 205 + void *real_res = (void *)0x12345678; 206 + struct test_concurrent_access_context *ctx; 207 + int ret, i; 208 + 209 + rp = revocable_provider_alloc(real_res); 210 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, rp); 211 + 212 + ctx = kunit_kmalloc_array(test, 3, sizeof(*ctx), GFP_KERNEL); 213 + KUNIT_ASSERT_NOT_NULL(test, ctx); 214 + 215 + for (i = 0; i < 3; ++i) { 216 + ctx[i].test = test; 217 + init_completion(&ctx[i].started); 218 + init_completion(&ctx[i].enter); 219 + init_completion(&ctx[i].exit); 220 + 221 + if (i == 0) { 222 + ctx[i].rp = rp; 223 + ctx[i].thread = kthread_run( 224 + test_concurrent_access_provider, ctx + i, 225 + "revocable_provider_%d", i); 226 + } else { 227 + ret = revocable_init(rp, &ctx[i].rev); 228 + KUNIT_ASSERT_EQ(test, ret, 0); 229 + 230 + ctx[i].thread = kthread_run( 231 + test_concurrent_access_consumer, ctx + i, 232 + "revocable_consumer_%d", i); 233 + } 234 + KUNIT_ASSERT_FALSE(test, IS_ERR(ctx[i].thread)); 235 + 236 + wait_for_completion(&ctx[i].started); 237 + } 238 + ctx[1].expected_res = real_res; 239 + ctx[2].expected_res = NULL; 240 + 241 + /* consumer1 enters read-side critical section */ 242 + complete(&ctx[1].enter); 243 + msleep(100); 244 + /* provider0 revokes the resource */ 245 + complete(&ctx[0].enter); 246 + msleep(100); 247 + /* consumer2 enters read-side critical section */ 248 + complete(&ctx[2].enter); 249 + msleep(100); 250 + 251 + /* consumer{1,2} exit read-side critical section */ 252 + complete(&ctx[1].exit); 253 + complete(&ctx[2].exit); 254 + 255 + for (i = 0; i < 3; ++i) 256 + kthread_stop(ctx[i].thread); 257 + for (i = 1; i < 3; ++i) 258 + revocable_deinit(&ctx[i].rev); 259 + } 260 + 168 261 static struct kunit_case revocable_test_cases[] = { 169 262 KUNIT_CASE(revocable_test_basic), 170 263 KUNIT_CASE(revocable_test_revocation), 171 264 KUNIT_CASE(revocable_test_try_access_macro), 172 265 KUNIT_CASE(revocable_test_try_access_macro2), 173 266 KUNIT_CASE(revocable_test_provider_use_after_free), 267 + KUNIT_CASE(revocable_test_concurrent_access), 174 268 {} 175 269 }; 176 270