1f2a5fec1SChris Wilson /* 2f2a5fec1SChris Wilson * Module-based API test facility for ww_mutexes 3f2a5fec1SChris Wilson * 4f2a5fec1SChris Wilson * This program is free software; you can redistribute it and/or modify 5f2a5fec1SChris Wilson * it under the terms of the GNU General Public License as published by 6f2a5fec1SChris Wilson * the Free Software Foundation; either version 2 of the License, or 7f2a5fec1SChris Wilson * (at your option) any later version. 8f2a5fec1SChris Wilson * 9f2a5fec1SChris Wilson * This program is distributed in the hope that it will be useful, 10f2a5fec1SChris Wilson * but WITHOUT ANY WARRANTY; without even the implied warranty of 11f2a5fec1SChris Wilson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12f2a5fec1SChris Wilson * GNU General Public License for more details. 13f2a5fec1SChris Wilson * 14f2a5fec1SChris Wilson * You should have received a copy of the GNU General Public License 15f2a5fec1SChris Wilson * along with this program; if not, you can access it online at 16f2a5fec1SChris Wilson * http://www.gnu.org/licenses/gpl-2.0.html. 17f2a5fec1SChris Wilson */ 18f2a5fec1SChris Wilson 19f2a5fec1SChris Wilson #include <linux/kernel.h> 20f2a5fec1SChris Wilson 21f2a5fec1SChris Wilson #include <linux/completion.h> 22f2a5fec1SChris Wilson #include <linux/kthread.h> 23f2a5fec1SChris Wilson #include <linux/module.h> 24d1b42b80SChris Wilson #include <linux/slab.h> 25f2a5fec1SChris Wilson #include <linux/ww_mutex.h> 26f2a5fec1SChris Wilson 27f2a5fec1SChris Wilson static DEFINE_WW_CLASS(ww_class); 28d1b42b80SChris Wilson struct workqueue_struct *wq; 29f2a5fec1SChris Wilson 30f2a5fec1SChris Wilson struct test_mutex { 31f2a5fec1SChris Wilson struct work_struct work; 32f2a5fec1SChris Wilson struct ww_mutex mutex; 33f2a5fec1SChris Wilson struct completion ready, go, done; 34f2a5fec1SChris Wilson unsigned int flags; 35f2a5fec1SChris Wilson }; 36f2a5fec1SChris Wilson 37f2a5fec1SChris Wilson #define TEST_MTX_SPIN BIT(0) 38f2a5fec1SChris Wilson #define TEST_MTX_TRY BIT(1) 39f2a5fec1SChris Wilson #define TEST_MTX_CTX BIT(2) 40f2a5fec1SChris Wilson #define __TEST_MTX_LAST BIT(3) 41f2a5fec1SChris Wilson 42f2a5fec1SChris Wilson static void test_mutex_work(struct work_struct *work) 43f2a5fec1SChris Wilson { 44f2a5fec1SChris Wilson struct test_mutex *mtx = container_of(work, typeof(*mtx), work); 45f2a5fec1SChris Wilson 46f2a5fec1SChris Wilson complete(&mtx->ready); 47f2a5fec1SChris Wilson wait_for_completion(&mtx->go); 48f2a5fec1SChris Wilson 49f2a5fec1SChris Wilson if (mtx->flags & TEST_MTX_TRY) { 50f2a5fec1SChris Wilson while (!ww_mutex_trylock(&mtx->mutex)) 51f2a5fec1SChris Wilson cpu_relax(); 52f2a5fec1SChris Wilson } else { 53f2a5fec1SChris Wilson ww_mutex_lock(&mtx->mutex, NULL); 54f2a5fec1SChris Wilson } 55f2a5fec1SChris Wilson complete(&mtx->done); 56f2a5fec1SChris Wilson ww_mutex_unlock(&mtx->mutex); 57f2a5fec1SChris Wilson } 58f2a5fec1SChris Wilson 59f2a5fec1SChris Wilson static int __test_mutex(unsigned int flags) 60f2a5fec1SChris Wilson { 61f2a5fec1SChris Wilson #define TIMEOUT (HZ / 16) 62f2a5fec1SChris Wilson struct test_mutex mtx; 63f2a5fec1SChris Wilson struct ww_acquire_ctx ctx; 64f2a5fec1SChris Wilson int ret; 65f2a5fec1SChris Wilson 66f2a5fec1SChris Wilson ww_mutex_init(&mtx.mutex, &ww_class); 67f2a5fec1SChris Wilson ww_acquire_init(&ctx, &ww_class); 68f2a5fec1SChris Wilson 69f2a5fec1SChris Wilson INIT_WORK_ONSTACK(&mtx.work, test_mutex_work); 70f2a5fec1SChris Wilson init_completion(&mtx.ready); 71f2a5fec1SChris Wilson init_completion(&mtx.go); 72f2a5fec1SChris Wilson init_completion(&mtx.done); 73f2a5fec1SChris Wilson mtx.flags = flags; 74f2a5fec1SChris Wilson 75f2a5fec1SChris Wilson schedule_work(&mtx.work); 76f2a5fec1SChris Wilson 77f2a5fec1SChris Wilson wait_for_completion(&mtx.ready); 78f2a5fec1SChris Wilson ww_mutex_lock(&mtx.mutex, (flags & TEST_MTX_CTX) ? &ctx : NULL); 79f2a5fec1SChris Wilson complete(&mtx.go); 80f2a5fec1SChris Wilson if (flags & TEST_MTX_SPIN) { 81f2a5fec1SChris Wilson unsigned long timeout = jiffies + TIMEOUT; 82f2a5fec1SChris Wilson 83f2a5fec1SChris Wilson ret = 0; 84f2a5fec1SChris Wilson do { 85f2a5fec1SChris Wilson if (completion_done(&mtx.done)) { 86f2a5fec1SChris Wilson ret = -EINVAL; 87f2a5fec1SChris Wilson break; 88f2a5fec1SChris Wilson } 89f2a5fec1SChris Wilson cpu_relax(); 90f2a5fec1SChris Wilson } while (time_before(jiffies, timeout)); 91f2a5fec1SChris Wilson } else { 92f2a5fec1SChris Wilson ret = wait_for_completion_timeout(&mtx.done, TIMEOUT); 93f2a5fec1SChris Wilson } 94f2a5fec1SChris Wilson ww_mutex_unlock(&mtx.mutex); 95f2a5fec1SChris Wilson ww_acquire_fini(&ctx); 96f2a5fec1SChris Wilson 97f2a5fec1SChris Wilson if (ret) { 98f2a5fec1SChris Wilson pr_err("%s(flags=%x): mutual exclusion failure\n", 99f2a5fec1SChris Wilson __func__, flags); 100f2a5fec1SChris Wilson ret = -EINVAL; 101f2a5fec1SChris Wilson } 102f2a5fec1SChris Wilson 103f2a5fec1SChris Wilson flush_work(&mtx.work); 104f2a5fec1SChris Wilson destroy_work_on_stack(&mtx.work); 105f2a5fec1SChris Wilson return ret; 106f2a5fec1SChris Wilson #undef TIMEOUT 107f2a5fec1SChris Wilson } 108f2a5fec1SChris Wilson 109f2a5fec1SChris Wilson static int test_mutex(void) 110f2a5fec1SChris Wilson { 111f2a5fec1SChris Wilson int ret; 112f2a5fec1SChris Wilson int i; 113f2a5fec1SChris Wilson 114f2a5fec1SChris Wilson for (i = 0; i < __TEST_MTX_LAST; i++) { 115f2a5fec1SChris Wilson ret = __test_mutex(i); 116f2a5fec1SChris Wilson if (ret) 117f2a5fec1SChris Wilson return ret; 118f2a5fec1SChris Wilson } 119f2a5fec1SChris Wilson 120f2a5fec1SChris Wilson return 0; 121f2a5fec1SChris Wilson } 122f2a5fec1SChris Wilson 123c22fb380SChris Wilson static int test_aa(void) 124c22fb380SChris Wilson { 125c22fb380SChris Wilson struct ww_mutex mutex; 126c22fb380SChris Wilson struct ww_acquire_ctx ctx; 127c22fb380SChris Wilson int ret; 128c22fb380SChris Wilson 129c22fb380SChris Wilson ww_mutex_init(&mutex, &ww_class); 130c22fb380SChris Wilson ww_acquire_init(&ctx, &ww_class); 131c22fb380SChris Wilson 132c22fb380SChris Wilson ww_mutex_lock(&mutex, &ctx); 133c22fb380SChris Wilson 134c22fb380SChris Wilson if (ww_mutex_trylock(&mutex)) { 135c22fb380SChris Wilson pr_err("%s: trylocked itself!\n", __func__); 136c22fb380SChris Wilson ww_mutex_unlock(&mutex); 137c22fb380SChris Wilson ret = -EINVAL; 138c22fb380SChris Wilson goto out; 139c22fb380SChris Wilson } 140c22fb380SChris Wilson 141c22fb380SChris Wilson ret = ww_mutex_lock(&mutex, &ctx); 142c22fb380SChris Wilson if (ret != -EALREADY) { 143c22fb380SChris Wilson pr_err("%s: missed deadlock for recursing, ret=%d\n", 144c22fb380SChris Wilson __func__, ret); 145c22fb380SChris Wilson if (!ret) 146c22fb380SChris Wilson ww_mutex_unlock(&mutex); 147c22fb380SChris Wilson ret = -EINVAL; 148c22fb380SChris Wilson goto out; 149c22fb380SChris Wilson } 150c22fb380SChris Wilson 151c22fb380SChris Wilson ret = 0; 152c22fb380SChris Wilson out: 153c22fb380SChris Wilson ww_mutex_unlock(&mutex); 154c22fb380SChris Wilson ww_acquire_fini(&ctx); 155c22fb380SChris Wilson return ret; 156c22fb380SChris Wilson } 157c22fb380SChris Wilson 15870207686SChris Wilson struct test_abba { 15970207686SChris Wilson struct work_struct work; 16070207686SChris Wilson struct ww_mutex a_mutex; 16170207686SChris Wilson struct ww_mutex b_mutex; 16270207686SChris Wilson struct completion a_ready; 16370207686SChris Wilson struct completion b_ready; 16470207686SChris Wilson bool resolve; 16570207686SChris Wilson int result; 16670207686SChris Wilson }; 16770207686SChris Wilson 16870207686SChris Wilson static void test_abba_work(struct work_struct *work) 16970207686SChris Wilson { 17070207686SChris Wilson struct test_abba *abba = container_of(work, typeof(*abba), work); 17170207686SChris Wilson struct ww_acquire_ctx ctx; 17270207686SChris Wilson int err; 17370207686SChris Wilson 17470207686SChris Wilson ww_acquire_init(&ctx, &ww_class); 17570207686SChris Wilson ww_mutex_lock(&abba->b_mutex, &ctx); 17670207686SChris Wilson 17770207686SChris Wilson complete(&abba->b_ready); 17870207686SChris Wilson wait_for_completion(&abba->a_ready); 17970207686SChris Wilson 18070207686SChris Wilson err = ww_mutex_lock(&abba->a_mutex, &ctx); 18170207686SChris Wilson if (abba->resolve && err == -EDEADLK) { 18270207686SChris Wilson ww_mutex_unlock(&abba->b_mutex); 18370207686SChris Wilson ww_mutex_lock_slow(&abba->a_mutex, &ctx); 18470207686SChris Wilson err = ww_mutex_lock(&abba->b_mutex, &ctx); 18570207686SChris Wilson } 18670207686SChris Wilson 18770207686SChris Wilson if (!err) 18870207686SChris Wilson ww_mutex_unlock(&abba->a_mutex); 18970207686SChris Wilson ww_mutex_unlock(&abba->b_mutex); 19070207686SChris Wilson ww_acquire_fini(&ctx); 19170207686SChris Wilson 19270207686SChris Wilson abba->result = err; 19370207686SChris Wilson } 19470207686SChris Wilson 19570207686SChris Wilson static int test_abba(bool resolve) 19670207686SChris Wilson { 19770207686SChris Wilson struct test_abba abba; 19870207686SChris Wilson struct ww_acquire_ctx ctx; 19970207686SChris Wilson int err, ret; 20070207686SChris Wilson 20170207686SChris Wilson ww_mutex_init(&abba.a_mutex, &ww_class); 20270207686SChris Wilson ww_mutex_init(&abba.b_mutex, &ww_class); 20370207686SChris Wilson INIT_WORK_ONSTACK(&abba.work, test_abba_work); 20470207686SChris Wilson init_completion(&abba.a_ready); 20570207686SChris Wilson init_completion(&abba.b_ready); 20670207686SChris Wilson abba.resolve = resolve; 20770207686SChris Wilson 20870207686SChris Wilson schedule_work(&abba.work); 20970207686SChris Wilson 21070207686SChris Wilson ww_acquire_init(&ctx, &ww_class); 21170207686SChris Wilson ww_mutex_lock(&abba.a_mutex, &ctx); 21270207686SChris Wilson 21370207686SChris Wilson complete(&abba.a_ready); 21470207686SChris Wilson wait_for_completion(&abba.b_ready); 21570207686SChris Wilson 21670207686SChris Wilson err = ww_mutex_lock(&abba.b_mutex, &ctx); 21770207686SChris Wilson if (resolve && err == -EDEADLK) { 21870207686SChris Wilson ww_mutex_unlock(&abba.a_mutex); 21970207686SChris Wilson ww_mutex_lock_slow(&abba.b_mutex, &ctx); 22070207686SChris Wilson err = ww_mutex_lock(&abba.a_mutex, &ctx); 22170207686SChris Wilson } 22270207686SChris Wilson 22370207686SChris Wilson if (!err) 22470207686SChris Wilson ww_mutex_unlock(&abba.b_mutex); 22570207686SChris Wilson ww_mutex_unlock(&abba.a_mutex); 22670207686SChris Wilson ww_acquire_fini(&ctx); 22770207686SChris Wilson 22870207686SChris Wilson flush_work(&abba.work); 22970207686SChris Wilson destroy_work_on_stack(&abba.work); 23070207686SChris Wilson 23170207686SChris Wilson ret = 0; 23270207686SChris Wilson if (resolve) { 23370207686SChris Wilson if (err || abba.result) { 23470207686SChris Wilson pr_err("%s: failed to resolve ABBA deadlock, A err=%d, B err=%d\n", 23570207686SChris Wilson __func__, err, abba.result); 23670207686SChris Wilson ret = -EINVAL; 23770207686SChris Wilson } 23870207686SChris Wilson } else { 23970207686SChris Wilson if (err != -EDEADLK && abba.result != -EDEADLK) { 24070207686SChris Wilson pr_err("%s: missed ABBA deadlock, A err=%d, B err=%d\n", 24170207686SChris Wilson __func__, err, abba.result); 24270207686SChris Wilson ret = -EINVAL; 24370207686SChris Wilson } 24470207686SChris Wilson } 24570207686SChris Wilson return ret; 24670207686SChris Wilson } 24770207686SChris Wilson 248d1b42b80SChris Wilson struct test_cycle { 249d1b42b80SChris Wilson struct work_struct work; 250d1b42b80SChris Wilson struct ww_mutex a_mutex; 251d1b42b80SChris Wilson struct ww_mutex *b_mutex; 252d1b42b80SChris Wilson struct completion *a_signal; 253d1b42b80SChris Wilson struct completion b_signal; 254d1b42b80SChris Wilson int result; 255d1b42b80SChris Wilson }; 256d1b42b80SChris Wilson 257d1b42b80SChris Wilson static void test_cycle_work(struct work_struct *work) 258d1b42b80SChris Wilson { 259d1b42b80SChris Wilson struct test_cycle *cycle = container_of(work, typeof(*cycle), work); 260d1b42b80SChris Wilson struct ww_acquire_ctx ctx; 261d1b42b80SChris Wilson int err; 262d1b42b80SChris Wilson 263d1b42b80SChris Wilson ww_acquire_init(&ctx, &ww_class); 264d1b42b80SChris Wilson ww_mutex_lock(&cycle->a_mutex, &ctx); 265d1b42b80SChris Wilson 266d1b42b80SChris Wilson complete(cycle->a_signal); 267d1b42b80SChris Wilson wait_for_completion(&cycle->b_signal); 268d1b42b80SChris Wilson 269d1b42b80SChris Wilson err = ww_mutex_lock(cycle->b_mutex, &ctx); 270d1b42b80SChris Wilson if (err == -EDEADLK) { 271d1b42b80SChris Wilson ww_mutex_unlock(&cycle->a_mutex); 272d1b42b80SChris Wilson ww_mutex_lock_slow(cycle->b_mutex, &ctx); 273d1b42b80SChris Wilson err = ww_mutex_lock(&cycle->a_mutex, &ctx); 274d1b42b80SChris Wilson } 275d1b42b80SChris Wilson 276d1b42b80SChris Wilson if (!err) 277d1b42b80SChris Wilson ww_mutex_unlock(cycle->b_mutex); 278d1b42b80SChris Wilson ww_mutex_unlock(&cycle->a_mutex); 279d1b42b80SChris Wilson ww_acquire_fini(&ctx); 280d1b42b80SChris Wilson 281d1b42b80SChris Wilson cycle->result = err; 282d1b42b80SChris Wilson } 283d1b42b80SChris Wilson 284d1b42b80SChris Wilson static int __test_cycle(unsigned int nthreads) 285d1b42b80SChris Wilson { 286d1b42b80SChris Wilson struct test_cycle *cycles; 287d1b42b80SChris Wilson unsigned int n, last = nthreads - 1; 288d1b42b80SChris Wilson int ret; 289d1b42b80SChris Wilson 290d1b42b80SChris Wilson cycles = kmalloc_array(nthreads, sizeof(*cycles), GFP_KERNEL); 291d1b42b80SChris Wilson if (!cycles) 292d1b42b80SChris Wilson return -ENOMEM; 293d1b42b80SChris Wilson 294d1b42b80SChris Wilson for (n = 0; n < nthreads; n++) { 295d1b42b80SChris Wilson struct test_cycle *cycle = &cycles[n]; 296d1b42b80SChris Wilson 297d1b42b80SChris Wilson ww_mutex_init(&cycle->a_mutex, &ww_class); 298d1b42b80SChris Wilson if (n == last) 299d1b42b80SChris Wilson cycle->b_mutex = &cycles[0].a_mutex; 300d1b42b80SChris Wilson else 301d1b42b80SChris Wilson cycle->b_mutex = &cycles[n + 1].a_mutex; 302d1b42b80SChris Wilson 303d1b42b80SChris Wilson if (n == 0) 304d1b42b80SChris Wilson cycle->a_signal = &cycles[last].b_signal; 305d1b42b80SChris Wilson else 306d1b42b80SChris Wilson cycle->a_signal = &cycles[n - 1].b_signal; 307d1b42b80SChris Wilson init_completion(&cycle->b_signal); 308d1b42b80SChris Wilson 309d1b42b80SChris Wilson INIT_WORK(&cycle->work, test_cycle_work); 310d1b42b80SChris Wilson cycle->result = 0; 311d1b42b80SChris Wilson } 312d1b42b80SChris Wilson 313d1b42b80SChris Wilson for (n = 0; n < nthreads; n++) 314d1b42b80SChris Wilson queue_work(wq, &cycles[n].work); 315d1b42b80SChris Wilson 316d1b42b80SChris Wilson flush_workqueue(wq); 317d1b42b80SChris Wilson 318d1b42b80SChris Wilson ret = 0; 319d1b42b80SChris Wilson for (n = 0; n < nthreads; n++) { 320d1b42b80SChris Wilson struct test_cycle *cycle = &cycles[n]; 321d1b42b80SChris Wilson 322d1b42b80SChris Wilson if (!cycle->result) 323d1b42b80SChris Wilson continue; 324d1b42b80SChris Wilson 325d1b42b80SChris Wilson pr_err("cylic deadlock not resolved, ret[%d/%d] = %d\n", 326d1b42b80SChris Wilson n, nthreads, cycle->result); 327d1b42b80SChris Wilson ret = -EINVAL; 328d1b42b80SChris Wilson break; 329d1b42b80SChris Wilson } 330d1b42b80SChris Wilson 331d1b42b80SChris Wilson for (n = 0; n < nthreads; n++) 332d1b42b80SChris Wilson ww_mutex_destroy(&cycles[n].a_mutex); 333d1b42b80SChris Wilson kfree(cycles); 334d1b42b80SChris Wilson return ret; 335d1b42b80SChris Wilson } 336d1b42b80SChris Wilson 337d1b42b80SChris Wilson static int test_cycle(unsigned int ncpus) 338d1b42b80SChris Wilson { 339d1b42b80SChris Wilson unsigned int n; 340d1b42b80SChris Wilson int ret; 341d1b42b80SChris Wilson 342d1b42b80SChris Wilson for (n = 2; n <= ncpus + 1; n++) { 343d1b42b80SChris Wilson ret = __test_cycle(n); 344d1b42b80SChris Wilson if (ret) 345d1b42b80SChris Wilson return ret; 346d1b42b80SChris Wilson } 347d1b42b80SChris Wilson 348d1b42b80SChris Wilson return 0; 349d1b42b80SChris Wilson } 350d1b42b80SChris Wilson 351f2a5fec1SChris Wilson static int __init test_ww_mutex_init(void) 352f2a5fec1SChris Wilson { 353d1b42b80SChris Wilson int ncpus = num_online_cpus(); 354f2a5fec1SChris Wilson int ret; 355f2a5fec1SChris Wilson 356d1b42b80SChris Wilson wq = alloc_workqueue("test-ww_mutex", WQ_UNBOUND, 0); 357d1b42b80SChris Wilson if (!wq) 358d1b42b80SChris Wilson return -ENOMEM; 359d1b42b80SChris Wilson 360f2a5fec1SChris Wilson ret = test_mutex(); 361f2a5fec1SChris Wilson if (ret) 362f2a5fec1SChris Wilson return ret; 363f2a5fec1SChris Wilson 364c22fb380SChris Wilson ret = test_aa(); 365c22fb380SChris Wilson if (ret) 366c22fb380SChris Wilson return ret; 367c22fb380SChris Wilson 36870207686SChris Wilson ret = test_abba(false); 36970207686SChris Wilson if (ret) 37070207686SChris Wilson return ret; 37170207686SChris Wilson 37270207686SChris Wilson ret = test_abba(true); 37370207686SChris Wilson if (ret) 37470207686SChris Wilson return ret; 37570207686SChris Wilson 376d1b42b80SChris Wilson ret = test_cycle(ncpus); 377d1b42b80SChris Wilson if (ret) 378d1b42b80SChris Wilson return ret; 379d1b42b80SChris Wilson 380f2a5fec1SChris Wilson return 0; 381f2a5fec1SChris Wilson } 382f2a5fec1SChris Wilson 383f2a5fec1SChris Wilson static void __exit test_ww_mutex_exit(void) 384f2a5fec1SChris Wilson { 385d1b42b80SChris Wilson destroy_workqueue(wq); 386f2a5fec1SChris Wilson } 387f2a5fec1SChris Wilson 388f2a5fec1SChris Wilson module_init(test_ww_mutex_init); 389f2a5fec1SChris Wilson module_exit(test_ww_mutex_exit); 390f2a5fec1SChris Wilson 391f2a5fec1SChris Wilson MODULE_LICENSE("GPL"); 392f2a5fec1SChris Wilson MODULE_AUTHOR("Intel Corporation"); 393