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> 24f2a5fec1SChris Wilson #include <linux/ww_mutex.h> 25f2a5fec1SChris Wilson 26f2a5fec1SChris Wilson static DEFINE_WW_CLASS(ww_class); 27f2a5fec1SChris Wilson 28f2a5fec1SChris Wilson struct test_mutex { 29f2a5fec1SChris Wilson struct work_struct work; 30f2a5fec1SChris Wilson struct ww_mutex mutex; 31f2a5fec1SChris Wilson struct completion ready, go, done; 32f2a5fec1SChris Wilson unsigned int flags; 33f2a5fec1SChris Wilson }; 34f2a5fec1SChris Wilson 35f2a5fec1SChris Wilson #define TEST_MTX_SPIN BIT(0) 36f2a5fec1SChris Wilson #define TEST_MTX_TRY BIT(1) 37f2a5fec1SChris Wilson #define TEST_MTX_CTX BIT(2) 38f2a5fec1SChris Wilson #define __TEST_MTX_LAST BIT(3) 39f2a5fec1SChris Wilson 40f2a5fec1SChris Wilson static void test_mutex_work(struct work_struct *work) 41f2a5fec1SChris Wilson { 42f2a5fec1SChris Wilson struct test_mutex *mtx = container_of(work, typeof(*mtx), work); 43f2a5fec1SChris Wilson 44f2a5fec1SChris Wilson complete(&mtx->ready); 45f2a5fec1SChris Wilson wait_for_completion(&mtx->go); 46f2a5fec1SChris Wilson 47f2a5fec1SChris Wilson if (mtx->flags & TEST_MTX_TRY) { 48f2a5fec1SChris Wilson while (!ww_mutex_trylock(&mtx->mutex)) 49f2a5fec1SChris Wilson cpu_relax(); 50f2a5fec1SChris Wilson } else { 51f2a5fec1SChris Wilson ww_mutex_lock(&mtx->mutex, NULL); 52f2a5fec1SChris Wilson } 53f2a5fec1SChris Wilson complete(&mtx->done); 54f2a5fec1SChris Wilson ww_mutex_unlock(&mtx->mutex); 55f2a5fec1SChris Wilson } 56f2a5fec1SChris Wilson 57f2a5fec1SChris Wilson static int __test_mutex(unsigned int flags) 58f2a5fec1SChris Wilson { 59f2a5fec1SChris Wilson #define TIMEOUT (HZ / 16) 60f2a5fec1SChris Wilson struct test_mutex mtx; 61f2a5fec1SChris Wilson struct ww_acquire_ctx ctx; 62f2a5fec1SChris Wilson int ret; 63f2a5fec1SChris Wilson 64f2a5fec1SChris Wilson ww_mutex_init(&mtx.mutex, &ww_class); 65f2a5fec1SChris Wilson ww_acquire_init(&ctx, &ww_class); 66f2a5fec1SChris Wilson 67f2a5fec1SChris Wilson INIT_WORK_ONSTACK(&mtx.work, test_mutex_work); 68f2a5fec1SChris Wilson init_completion(&mtx.ready); 69f2a5fec1SChris Wilson init_completion(&mtx.go); 70f2a5fec1SChris Wilson init_completion(&mtx.done); 71f2a5fec1SChris Wilson mtx.flags = flags; 72f2a5fec1SChris Wilson 73f2a5fec1SChris Wilson schedule_work(&mtx.work); 74f2a5fec1SChris Wilson 75f2a5fec1SChris Wilson wait_for_completion(&mtx.ready); 76f2a5fec1SChris Wilson ww_mutex_lock(&mtx.mutex, (flags & TEST_MTX_CTX) ? &ctx : NULL); 77f2a5fec1SChris Wilson complete(&mtx.go); 78f2a5fec1SChris Wilson if (flags & TEST_MTX_SPIN) { 79f2a5fec1SChris Wilson unsigned long timeout = jiffies + TIMEOUT; 80f2a5fec1SChris Wilson 81f2a5fec1SChris Wilson ret = 0; 82f2a5fec1SChris Wilson do { 83f2a5fec1SChris Wilson if (completion_done(&mtx.done)) { 84f2a5fec1SChris Wilson ret = -EINVAL; 85f2a5fec1SChris Wilson break; 86f2a5fec1SChris Wilson } 87f2a5fec1SChris Wilson cpu_relax(); 88f2a5fec1SChris Wilson } while (time_before(jiffies, timeout)); 89f2a5fec1SChris Wilson } else { 90f2a5fec1SChris Wilson ret = wait_for_completion_timeout(&mtx.done, TIMEOUT); 91f2a5fec1SChris Wilson } 92f2a5fec1SChris Wilson ww_mutex_unlock(&mtx.mutex); 93f2a5fec1SChris Wilson ww_acquire_fini(&ctx); 94f2a5fec1SChris Wilson 95f2a5fec1SChris Wilson if (ret) { 96f2a5fec1SChris Wilson pr_err("%s(flags=%x): mutual exclusion failure\n", 97f2a5fec1SChris Wilson __func__, flags); 98f2a5fec1SChris Wilson ret = -EINVAL; 99f2a5fec1SChris Wilson } 100f2a5fec1SChris Wilson 101f2a5fec1SChris Wilson flush_work(&mtx.work); 102f2a5fec1SChris Wilson destroy_work_on_stack(&mtx.work); 103f2a5fec1SChris Wilson return ret; 104f2a5fec1SChris Wilson #undef TIMEOUT 105f2a5fec1SChris Wilson } 106f2a5fec1SChris Wilson 107f2a5fec1SChris Wilson static int test_mutex(void) 108f2a5fec1SChris Wilson { 109f2a5fec1SChris Wilson int ret; 110f2a5fec1SChris Wilson int i; 111f2a5fec1SChris Wilson 112f2a5fec1SChris Wilson for (i = 0; i < __TEST_MTX_LAST; i++) { 113f2a5fec1SChris Wilson ret = __test_mutex(i); 114f2a5fec1SChris Wilson if (ret) 115f2a5fec1SChris Wilson return ret; 116f2a5fec1SChris Wilson } 117f2a5fec1SChris Wilson 118f2a5fec1SChris Wilson return 0; 119f2a5fec1SChris Wilson } 120f2a5fec1SChris Wilson 121c22fb380SChris Wilson static int test_aa(void) 122c22fb380SChris Wilson { 123c22fb380SChris Wilson struct ww_mutex mutex; 124c22fb380SChris Wilson struct ww_acquire_ctx ctx; 125c22fb380SChris Wilson int ret; 126c22fb380SChris Wilson 127c22fb380SChris Wilson ww_mutex_init(&mutex, &ww_class); 128c22fb380SChris Wilson ww_acquire_init(&ctx, &ww_class); 129c22fb380SChris Wilson 130c22fb380SChris Wilson ww_mutex_lock(&mutex, &ctx); 131c22fb380SChris Wilson 132c22fb380SChris Wilson if (ww_mutex_trylock(&mutex)) { 133c22fb380SChris Wilson pr_err("%s: trylocked itself!\n", __func__); 134c22fb380SChris Wilson ww_mutex_unlock(&mutex); 135c22fb380SChris Wilson ret = -EINVAL; 136c22fb380SChris Wilson goto out; 137c22fb380SChris Wilson } 138c22fb380SChris Wilson 139c22fb380SChris Wilson ret = ww_mutex_lock(&mutex, &ctx); 140c22fb380SChris Wilson if (ret != -EALREADY) { 141c22fb380SChris Wilson pr_err("%s: missed deadlock for recursing, ret=%d\n", 142c22fb380SChris Wilson __func__, ret); 143c22fb380SChris Wilson if (!ret) 144c22fb380SChris Wilson ww_mutex_unlock(&mutex); 145c22fb380SChris Wilson ret = -EINVAL; 146c22fb380SChris Wilson goto out; 147c22fb380SChris Wilson } 148c22fb380SChris Wilson 149c22fb380SChris Wilson ret = 0; 150c22fb380SChris Wilson out: 151c22fb380SChris Wilson ww_mutex_unlock(&mutex); 152c22fb380SChris Wilson ww_acquire_fini(&ctx); 153c22fb380SChris Wilson return ret; 154c22fb380SChris Wilson } 155c22fb380SChris Wilson 15670207686SChris Wilson struct test_abba { 15770207686SChris Wilson struct work_struct work; 15870207686SChris Wilson struct ww_mutex a_mutex; 15970207686SChris Wilson struct ww_mutex b_mutex; 16070207686SChris Wilson struct completion a_ready; 16170207686SChris Wilson struct completion b_ready; 16270207686SChris Wilson bool resolve; 16370207686SChris Wilson int result; 16470207686SChris Wilson }; 16570207686SChris Wilson 16670207686SChris Wilson static void test_abba_work(struct work_struct *work) 16770207686SChris Wilson { 16870207686SChris Wilson struct test_abba *abba = container_of(work, typeof(*abba), work); 16970207686SChris Wilson struct ww_acquire_ctx ctx; 17070207686SChris Wilson int err; 17170207686SChris Wilson 17270207686SChris Wilson ww_acquire_init(&ctx, &ww_class); 17370207686SChris Wilson ww_mutex_lock(&abba->b_mutex, &ctx); 17470207686SChris Wilson 17570207686SChris Wilson complete(&abba->b_ready); 17670207686SChris Wilson wait_for_completion(&abba->a_ready); 17770207686SChris Wilson 17870207686SChris Wilson err = ww_mutex_lock(&abba->a_mutex, &ctx); 17970207686SChris Wilson if (abba->resolve && err == -EDEADLK) { 18070207686SChris Wilson ww_mutex_unlock(&abba->b_mutex); 18170207686SChris Wilson ww_mutex_lock_slow(&abba->a_mutex, &ctx); 18270207686SChris Wilson err = ww_mutex_lock(&abba->b_mutex, &ctx); 18370207686SChris Wilson } 18470207686SChris Wilson 18570207686SChris Wilson if (!err) 18670207686SChris Wilson ww_mutex_unlock(&abba->a_mutex); 18770207686SChris Wilson ww_mutex_unlock(&abba->b_mutex); 18870207686SChris Wilson ww_acquire_fini(&ctx); 18970207686SChris Wilson 19070207686SChris Wilson abba->result = err; 19170207686SChris Wilson } 19270207686SChris Wilson 19370207686SChris Wilson static int test_abba(bool resolve) 19470207686SChris Wilson { 19570207686SChris Wilson struct test_abba abba; 19670207686SChris Wilson struct ww_acquire_ctx ctx; 19770207686SChris Wilson int err, ret; 19870207686SChris Wilson 19970207686SChris Wilson ww_mutex_init(&abba.a_mutex, &ww_class); 20070207686SChris Wilson ww_mutex_init(&abba.b_mutex, &ww_class); 20170207686SChris Wilson INIT_WORK_ONSTACK(&abba.work, test_abba_work); 20270207686SChris Wilson init_completion(&abba.a_ready); 20370207686SChris Wilson init_completion(&abba.b_ready); 20470207686SChris Wilson abba.resolve = resolve; 20570207686SChris Wilson 20670207686SChris Wilson schedule_work(&abba.work); 20770207686SChris Wilson 20870207686SChris Wilson ww_acquire_init(&ctx, &ww_class); 20970207686SChris Wilson ww_mutex_lock(&abba.a_mutex, &ctx); 21070207686SChris Wilson 21170207686SChris Wilson complete(&abba.a_ready); 21270207686SChris Wilson wait_for_completion(&abba.b_ready); 21370207686SChris Wilson 21470207686SChris Wilson err = ww_mutex_lock(&abba.b_mutex, &ctx); 21570207686SChris Wilson if (resolve && err == -EDEADLK) { 21670207686SChris Wilson ww_mutex_unlock(&abba.a_mutex); 21770207686SChris Wilson ww_mutex_lock_slow(&abba.b_mutex, &ctx); 21870207686SChris Wilson err = ww_mutex_lock(&abba.a_mutex, &ctx); 21970207686SChris Wilson } 22070207686SChris Wilson 22170207686SChris Wilson if (!err) 22270207686SChris Wilson ww_mutex_unlock(&abba.b_mutex); 22370207686SChris Wilson ww_mutex_unlock(&abba.a_mutex); 22470207686SChris Wilson ww_acquire_fini(&ctx); 22570207686SChris Wilson 22670207686SChris Wilson flush_work(&abba.work); 22770207686SChris Wilson destroy_work_on_stack(&abba.work); 22870207686SChris Wilson 22970207686SChris Wilson ret = 0; 23070207686SChris Wilson if (resolve) { 23170207686SChris Wilson if (err || abba.result) { 23270207686SChris Wilson pr_err("%s: failed to resolve ABBA deadlock, A err=%d, B err=%d\n", 23370207686SChris Wilson __func__, err, abba.result); 23470207686SChris Wilson ret = -EINVAL; 23570207686SChris Wilson } 23670207686SChris Wilson } else { 23770207686SChris Wilson if (err != -EDEADLK && abba.result != -EDEADLK) { 23870207686SChris Wilson pr_err("%s: missed ABBA deadlock, A err=%d, B err=%d\n", 23970207686SChris Wilson __func__, err, abba.result); 24070207686SChris Wilson ret = -EINVAL; 24170207686SChris Wilson } 24270207686SChris Wilson } 24370207686SChris Wilson return ret; 24470207686SChris Wilson } 24570207686SChris Wilson 246f2a5fec1SChris Wilson static int __init test_ww_mutex_init(void) 247f2a5fec1SChris Wilson { 248f2a5fec1SChris Wilson int ret; 249f2a5fec1SChris Wilson 250f2a5fec1SChris Wilson ret = test_mutex(); 251f2a5fec1SChris Wilson if (ret) 252f2a5fec1SChris Wilson return ret; 253f2a5fec1SChris Wilson 254c22fb380SChris Wilson ret = test_aa(); 255c22fb380SChris Wilson if (ret) 256c22fb380SChris Wilson return ret; 257c22fb380SChris Wilson 25870207686SChris Wilson ret = test_abba(false); 25970207686SChris Wilson if (ret) 26070207686SChris Wilson return ret; 26170207686SChris Wilson 26270207686SChris Wilson ret = test_abba(true); 26370207686SChris Wilson if (ret) 26470207686SChris Wilson return ret; 26570207686SChris Wilson 266f2a5fec1SChris Wilson return 0; 267f2a5fec1SChris Wilson } 268f2a5fec1SChris Wilson 269f2a5fec1SChris Wilson static void __exit test_ww_mutex_exit(void) 270f2a5fec1SChris Wilson { 271f2a5fec1SChris Wilson } 272f2a5fec1SChris Wilson 273f2a5fec1SChris Wilson module_init(test_ww_mutex_init); 274f2a5fec1SChris Wilson module_exit(test_ww_mutex_exit); 275f2a5fec1SChris Wilson 276f2a5fec1SChris Wilson MODULE_LICENSE("GPL"); 277f2a5fec1SChris Wilson MODULE_AUTHOR("Intel Corporation"); 278