162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Test module to generate lockups 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/sched/signal.h> 1262306a36Sopenharmony_ci#include <linux/sched/clock.h> 1362306a36Sopenharmony_ci#include <linux/cpu.h> 1462306a36Sopenharmony_ci#include <linux/nmi.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/file.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic unsigned int time_secs; 2062306a36Sopenharmony_cimodule_param(time_secs, uint, 0600); 2162306a36Sopenharmony_ciMODULE_PARM_DESC(time_secs, "lockup time in seconds, default 0"); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic unsigned int time_nsecs; 2462306a36Sopenharmony_cimodule_param(time_nsecs, uint, 0600); 2562306a36Sopenharmony_ciMODULE_PARM_DESC(time_nsecs, "nanoseconds part of lockup time, default 0"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic unsigned int cooldown_secs; 2862306a36Sopenharmony_cimodule_param(cooldown_secs, uint, 0600); 2962306a36Sopenharmony_ciMODULE_PARM_DESC(cooldown_secs, "cooldown time between iterations in seconds, default 0"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic unsigned int cooldown_nsecs; 3262306a36Sopenharmony_cimodule_param(cooldown_nsecs, uint, 0600); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(cooldown_nsecs, "nanoseconds part of cooldown, default 0"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic unsigned int iterations = 1; 3662306a36Sopenharmony_cimodule_param(iterations, uint, 0600); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(iterations, "lockup iterations, default 1"); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic bool all_cpus; 4062306a36Sopenharmony_cimodule_param(all_cpus, bool, 0400); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(all_cpus, "trigger lockup at all cpus at once"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int wait_state; 4462306a36Sopenharmony_cistatic char *state = "R"; 4562306a36Sopenharmony_cimodule_param(state, charp, 0400); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(state, "wait in 'R' running (default), 'D' uninterruptible, 'K' killable, 'S' interruptible state"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic bool use_hrtimer; 4962306a36Sopenharmony_cimodule_param(use_hrtimer, bool, 0400); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(use_hrtimer, "use high-resolution timer for sleeping"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic bool iowait; 5362306a36Sopenharmony_cimodule_param(iowait, bool, 0400); 5462306a36Sopenharmony_ciMODULE_PARM_DESC(iowait, "account sleep time as iowait"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic bool lock_read; 5762306a36Sopenharmony_cimodule_param(lock_read, bool, 0400); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(lock_read, "lock read-write locks for read"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic bool lock_single; 6162306a36Sopenharmony_cimodule_param(lock_single, bool, 0400); 6262306a36Sopenharmony_ciMODULE_PARM_DESC(lock_single, "acquire locks only at one cpu"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic bool reacquire_locks; 6562306a36Sopenharmony_cimodule_param(reacquire_locks, bool, 0400); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(reacquire_locks, "release and reacquire locks/irq/preempt between iterations"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic bool touch_softlockup; 6962306a36Sopenharmony_cimodule_param(touch_softlockup, bool, 0600); 7062306a36Sopenharmony_ciMODULE_PARM_DESC(touch_softlockup, "touch soft-lockup watchdog between iterations"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic bool touch_hardlockup; 7362306a36Sopenharmony_cimodule_param(touch_hardlockup, bool, 0600); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(touch_hardlockup, "touch hard-lockup watchdog between iterations"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic bool call_cond_resched; 7762306a36Sopenharmony_cimodule_param(call_cond_resched, bool, 0600); 7862306a36Sopenharmony_ciMODULE_PARM_DESC(call_cond_resched, "call cond_resched() between iterations"); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic bool measure_lock_wait; 8162306a36Sopenharmony_cimodule_param(measure_lock_wait, bool, 0400); 8262306a36Sopenharmony_ciMODULE_PARM_DESC(measure_lock_wait, "measure lock wait time"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic unsigned long lock_wait_threshold = ULONG_MAX; 8562306a36Sopenharmony_cimodule_param(lock_wait_threshold, ulong, 0400); 8662306a36Sopenharmony_ciMODULE_PARM_DESC(lock_wait_threshold, "print lock wait time longer than this in nanoseconds, default off"); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool test_disable_irq; 8962306a36Sopenharmony_cimodule_param_named(disable_irq, test_disable_irq, bool, 0400); 9062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_irq, "disable interrupts: generate hard-lockups"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic bool disable_softirq; 9362306a36Sopenharmony_cimodule_param(disable_softirq, bool, 0400); 9462306a36Sopenharmony_ciMODULE_PARM_DESC(disable_softirq, "disable bottom-half irq handlers"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic bool disable_preempt; 9762306a36Sopenharmony_cimodule_param(disable_preempt, bool, 0400); 9862306a36Sopenharmony_ciMODULE_PARM_DESC(disable_preempt, "disable preemption: generate soft-lockups"); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic bool lock_rcu; 10162306a36Sopenharmony_cimodule_param(lock_rcu, bool, 0400); 10262306a36Sopenharmony_ciMODULE_PARM_DESC(lock_rcu, "grab rcu_read_lock: generate rcu stalls"); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic bool lock_mmap_sem; 10562306a36Sopenharmony_cimodule_param(lock_mmap_sem, bool, 0400); 10662306a36Sopenharmony_ciMODULE_PARM_DESC(lock_mmap_sem, "lock mm->mmap_lock: block procfs interfaces"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic unsigned long lock_rwsem_ptr; 10962306a36Sopenharmony_cimodule_param_unsafe(lock_rwsem_ptr, ulong, 0400); 11062306a36Sopenharmony_ciMODULE_PARM_DESC(lock_rwsem_ptr, "lock rw_semaphore at address"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic unsigned long lock_mutex_ptr; 11362306a36Sopenharmony_cimodule_param_unsafe(lock_mutex_ptr, ulong, 0400); 11462306a36Sopenharmony_ciMODULE_PARM_DESC(lock_mutex_ptr, "lock mutex at address"); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic unsigned long lock_spinlock_ptr; 11762306a36Sopenharmony_cimodule_param_unsafe(lock_spinlock_ptr, ulong, 0400); 11862306a36Sopenharmony_ciMODULE_PARM_DESC(lock_spinlock_ptr, "lock spinlock at address"); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic unsigned long lock_rwlock_ptr; 12162306a36Sopenharmony_cimodule_param_unsafe(lock_rwlock_ptr, ulong, 0400); 12262306a36Sopenharmony_ciMODULE_PARM_DESC(lock_rwlock_ptr, "lock rwlock at address"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic unsigned int alloc_pages_nr; 12562306a36Sopenharmony_cimodule_param_unsafe(alloc_pages_nr, uint, 0600); 12662306a36Sopenharmony_ciMODULE_PARM_DESC(alloc_pages_nr, "allocate and free pages under locks"); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic unsigned int alloc_pages_order; 12962306a36Sopenharmony_cimodule_param(alloc_pages_order, uint, 0400); 13062306a36Sopenharmony_ciMODULE_PARM_DESC(alloc_pages_order, "page order to allocate"); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic gfp_t alloc_pages_gfp = GFP_KERNEL; 13362306a36Sopenharmony_cimodule_param_unsafe(alloc_pages_gfp, uint, 0400); 13462306a36Sopenharmony_ciMODULE_PARM_DESC(alloc_pages_gfp, "allocate pages with this gfp_mask, default GFP_KERNEL"); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic bool alloc_pages_atomic; 13762306a36Sopenharmony_cimodule_param(alloc_pages_atomic, bool, 0400); 13862306a36Sopenharmony_ciMODULE_PARM_DESC(alloc_pages_atomic, "allocate pages with GFP_ATOMIC"); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic bool reallocate_pages; 14162306a36Sopenharmony_cimodule_param(reallocate_pages, bool, 0400); 14262306a36Sopenharmony_ciMODULE_PARM_DESC(reallocate_pages, "free and allocate pages between iterations"); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct file *test_file; 14562306a36Sopenharmony_cistatic struct inode *test_inode; 14662306a36Sopenharmony_cistatic char test_file_path[256]; 14762306a36Sopenharmony_cimodule_param_string(file_path, test_file_path, sizeof(test_file_path), 0400); 14862306a36Sopenharmony_ciMODULE_PARM_DESC(file_path, "file path to test"); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic bool test_lock_inode; 15162306a36Sopenharmony_cimodule_param_named(lock_inode, test_lock_inode, bool, 0400); 15262306a36Sopenharmony_ciMODULE_PARM_DESC(lock_inode, "lock file -> inode -> i_rwsem"); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic bool test_lock_mapping; 15562306a36Sopenharmony_cimodule_param_named(lock_mapping, test_lock_mapping, bool, 0400); 15662306a36Sopenharmony_ciMODULE_PARM_DESC(lock_mapping, "lock file -> mapping -> i_mmap_rwsem"); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic bool test_lock_sb_umount; 15962306a36Sopenharmony_cimodule_param_named(lock_sb_umount, test_lock_sb_umount, bool, 0400); 16062306a36Sopenharmony_ciMODULE_PARM_DESC(lock_sb_umount, "lock file -> sb -> s_umount"); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic atomic_t alloc_pages_failed = ATOMIC_INIT(0); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic atomic64_t max_lock_wait = ATOMIC64_INIT(0); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct task_struct *main_task; 16762306a36Sopenharmony_cistatic int master_cpu; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void test_lock(bool master, bool verbose) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci u64 wait_start; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (measure_lock_wait) 17462306a36Sopenharmony_ci wait_start = local_clock(); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (lock_mutex_ptr && master) { 17762306a36Sopenharmony_ci if (verbose) 17862306a36Sopenharmony_ci pr_notice("lock mutex %ps\n", (void *)lock_mutex_ptr); 17962306a36Sopenharmony_ci mutex_lock((struct mutex *)lock_mutex_ptr); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (lock_rwsem_ptr && master) { 18362306a36Sopenharmony_ci if (verbose) 18462306a36Sopenharmony_ci pr_notice("lock rw_semaphore %ps\n", 18562306a36Sopenharmony_ci (void *)lock_rwsem_ptr); 18662306a36Sopenharmony_ci if (lock_read) 18762306a36Sopenharmony_ci down_read((struct rw_semaphore *)lock_rwsem_ptr); 18862306a36Sopenharmony_ci else 18962306a36Sopenharmony_ci down_write((struct rw_semaphore *)lock_rwsem_ptr); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (lock_mmap_sem && master) { 19362306a36Sopenharmony_ci if (verbose) 19462306a36Sopenharmony_ci pr_notice("lock mmap_lock pid=%d\n", main_task->pid); 19562306a36Sopenharmony_ci if (lock_read) 19662306a36Sopenharmony_ci mmap_read_lock(main_task->mm); 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci mmap_write_lock(main_task->mm); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (test_disable_irq) 20262306a36Sopenharmony_ci local_irq_disable(); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (disable_softirq) 20562306a36Sopenharmony_ci local_bh_disable(); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (disable_preempt) 20862306a36Sopenharmony_ci preempt_disable(); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (lock_rcu) 21162306a36Sopenharmony_ci rcu_read_lock(); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (lock_spinlock_ptr && master) { 21462306a36Sopenharmony_ci if (verbose) 21562306a36Sopenharmony_ci pr_notice("lock spinlock %ps\n", 21662306a36Sopenharmony_ci (void *)lock_spinlock_ptr); 21762306a36Sopenharmony_ci spin_lock((spinlock_t *)lock_spinlock_ptr); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (lock_rwlock_ptr && master) { 22162306a36Sopenharmony_ci if (verbose) 22262306a36Sopenharmony_ci pr_notice("lock rwlock %ps\n", 22362306a36Sopenharmony_ci (void *)lock_rwlock_ptr); 22462306a36Sopenharmony_ci if (lock_read) 22562306a36Sopenharmony_ci read_lock((rwlock_t *)lock_rwlock_ptr); 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci write_lock((rwlock_t *)lock_rwlock_ptr); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (measure_lock_wait) { 23162306a36Sopenharmony_ci s64 cur_wait = local_clock() - wait_start; 23262306a36Sopenharmony_ci s64 max_wait = atomic64_read(&max_lock_wait); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci do { 23562306a36Sopenharmony_ci if (cur_wait < max_wait) 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci max_wait = atomic64_cmpxchg(&max_lock_wait, 23862306a36Sopenharmony_ci max_wait, cur_wait); 23962306a36Sopenharmony_ci } while (max_wait != cur_wait); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (cur_wait > lock_wait_threshold) 24262306a36Sopenharmony_ci pr_notice_ratelimited("lock wait %lld ns\n", cur_wait); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void test_unlock(bool master, bool verbose) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci if (lock_rwlock_ptr && master) { 24962306a36Sopenharmony_ci if (lock_read) 25062306a36Sopenharmony_ci read_unlock((rwlock_t *)lock_rwlock_ptr); 25162306a36Sopenharmony_ci else 25262306a36Sopenharmony_ci write_unlock((rwlock_t *)lock_rwlock_ptr); 25362306a36Sopenharmony_ci if (verbose) 25462306a36Sopenharmony_ci pr_notice("unlock rwlock %ps\n", 25562306a36Sopenharmony_ci (void *)lock_rwlock_ptr); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (lock_spinlock_ptr && master) { 25962306a36Sopenharmony_ci spin_unlock((spinlock_t *)lock_spinlock_ptr); 26062306a36Sopenharmony_ci if (verbose) 26162306a36Sopenharmony_ci pr_notice("unlock spinlock %ps\n", 26262306a36Sopenharmony_ci (void *)lock_spinlock_ptr); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (lock_rcu) 26662306a36Sopenharmony_ci rcu_read_unlock(); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (disable_preempt) 26962306a36Sopenharmony_ci preempt_enable(); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (disable_softirq) 27262306a36Sopenharmony_ci local_bh_enable(); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (test_disable_irq) 27562306a36Sopenharmony_ci local_irq_enable(); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (lock_mmap_sem && master) { 27862306a36Sopenharmony_ci if (lock_read) 27962306a36Sopenharmony_ci mmap_read_unlock(main_task->mm); 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci mmap_write_unlock(main_task->mm); 28262306a36Sopenharmony_ci if (verbose) 28362306a36Sopenharmony_ci pr_notice("unlock mmap_lock pid=%d\n", main_task->pid); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (lock_rwsem_ptr && master) { 28762306a36Sopenharmony_ci if (lock_read) 28862306a36Sopenharmony_ci up_read((struct rw_semaphore *)lock_rwsem_ptr); 28962306a36Sopenharmony_ci else 29062306a36Sopenharmony_ci up_write((struct rw_semaphore *)lock_rwsem_ptr); 29162306a36Sopenharmony_ci if (verbose) 29262306a36Sopenharmony_ci pr_notice("unlock rw_semaphore %ps\n", 29362306a36Sopenharmony_ci (void *)lock_rwsem_ptr); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (lock_mutex_ptr && master) { 29762306a36Sopenharmony_ci mutex_unlock((struct mutex *)lock_mutex_ptr); 29862306a36Sopenharmony_ci if (verbose) 29962306a36Sopenharmony_ci pr_notice("unlock mutex %ps\n", 30062306a36Sopenharmony_ci (void *)lock_mutex_ptr); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void test_alloc_pages(struct list_head *pages) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct page *page; 30762306a36Sopenharmony_ci unsigned int i; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci for (i = 0; i < alloc_pages_nr; i++) { 31062306a36Sopenharmony_ci page = alloc_pages(alloc_pages_gfp, alloc_pages_order); 31162306a36Sopenharmony_ci if (!page) { 31262306a36Sopenharmony_ci atomic_inc(&alloc_pages_failed); 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci list_add(&page->lru, pages); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void test_free_pages(struct list_head *pages) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct page *page, *next; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci list_for_each_entry_safe(page, next, pages, lru) 32462306a36Sopenharmony_ci __free_pages(page, alloc_pages_order); 32562306a36Sopenharmony_ci INIT_LIST_HEAD(pages); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void test_wait(unsigned int secs, unsigned int nsecs) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci if (wait_state == TASK_RUNNING) { 33162306a36Sopenharmony_ci if (secs) 33262306a36Sopenharmony_ci mdelay(secs * MSEC_PER_SEC); 33362306a36Sopenharmony_ci if (nsecs) 33462306a36Sopenharmony_ci ndelay(nsecs); 33562306a36Sopenharmony_ci return; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci __set_current_state(wait_state); 33962306a36Sopenharmony_ci if (use_hrtimer) { 34062306a36Sopenharmony_ci ktime_t time; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci time = ns_to_ktime((u64)secs * NSEC_PER_SEC + nsecs); 34362306a36Sopenharmony_ci schedule_hrtimeout(&time, HRTIMER_MODE_REL); 34462306a36Sopenharmony_ci } else { 34562306a36Sopenharmony_ci schedule_timeout(secs * HZ + nsecs_to_jiffies(nsecs)); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void test_lockup(bool master) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci u64 lockup_start = local_clock(); 35262306a36Sopenharmony_ci unsigned int iter = 0; 35362306a36Sopenharmony_ci LIST_HEAD(pages); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci pr_notice("Start on CPU%d\n", raw_smp_processor_id()); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci test_lock(master, true); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci test_alloc_pages(&pages); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci while (iter++ < iterations && !signal_pending(main_task)) { 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (iowait) 36462306a36Sopenharmony_ci current->in_iowait = 1; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci test_wait(time_secs, time_nsecs); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (iowait) 36962306a36Sopenharmony_ci current->in_iowait = 0; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (reallocate_pages) 37262306a36Sopenharmony_ci test_free_pages(&pages); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (reacquire_locks) 37562306a36Sopenharmony_ci test_unlock(master, false); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (touch_softlockup) 37862306a36Sopenharmony_ci touch_softlockup_watchdog(); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (touch_hardlockup) 38162306a36Sopenharmony_ci touch_nmi_watchdog(); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (call_cond_resched) 38462306a36Sopenharmony_ci cond_resched(); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci test_wait(cooldown_secs, cooldown_nsecs); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (reacquire_locks) 38962306a36Sopenharmony_ci test_lock(master, false); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (reallocate_pages) 39262306a36Sopenharmony_ci test_alloc_pages(&pages); 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pr_notice("Finish on CPU%d in %lld ns\n", raw_smp_processor_id(), 39662306a36Sopenharmony_ci local_clock() - lockup_start); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci test_free_pages(&pages); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci test_unlock(master, true); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct work_struct, test_works); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void test_work_fn(struct work_struct *work) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci test_lockup(!lock_single || 40862306a36Sopenharmony_ci work == per_cpu_ptr(&test_works, master_cpu)); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic bool test_kernel_ptr(unsigned long addr, int size) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci void *ptr = (void *)addr; 41462306a36Sopenharmony_ci char buf; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (!addr) 41762306a36Sopenharmony_ci return false; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* should be at least readable kernel address */ 42062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_ALTERNATE_USER_ADDRESS_SPACE) && 42162306a36Sopenharmony_ci (access_ok((void __user *)ptr, 1) || 42262306a36Sopenharmony_ci access_ok((void __user *)ptr + size - 1, 1))) { 42362306a36Sopenharmony_ci pr_err("user space ptr invalid in kernel: %#lx\n", addr); 42462306a36Sopenharmony_ci return true; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (get_kernel_nofault(buf, ptr) || 42862306a36Sopenharmony_ci get_kernel_nofault(buf, ptr + size - 1)) { 42962306a36Sopenharmony_ci pr_err("invalid kernel ptr: %#lx\n", addr); 43062306a36Sopenharmony_ci return true; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return false; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic bool __maybe_unused test_magic(unsigned long addr, int offset, 43762306a36Sopenharmony_ci unsigned int expected) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci void *ptr = (void *)addr + offset; 44062306a36Sopenharmony_ci unsigned int magic = 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (!addr) 44362306a36Sopenharmony_ci return false; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (get_kernel_nofault(magic, ptr) || magic != expected) { 44662306a36Sopenharmony_ci pr_err("invalid magic at %#lx + %#x = %#x, expected %#x\n", 44762306a36Sopenharmony_ci addr, offset, magic, expected); 44862306a36Sopenharmony_ci return true; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return false; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int __init test_lockup_init(void) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci u64 test_start = local_clock(); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci main_task = current; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (state[0]) { 46162306a36Sopenharmony_ci case 'S': 46262306a36Sopenharmony_ci wait_state = TASK_INTERRUPTIBLE; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case 'D': 46562306a36Sopenharmony_ci wait_state = TASK_UNINTERRUPTIBLE; 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci case 'K': 46862306a36Sopenharmony_ci wait_state = TASK_KILLABLE; 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci case 'R': 47162306a36Sopenharmony_ci wait_state = TASK_RUNNING; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci default: 47462306a36Sopenharmony_ci pr_err("unknown state=%s\n", state); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (alloc_pages_atomic) 47962306a36Sopenharmony_ci alloc_pages_gfp = GFP_ATOMIC; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (test_kernel_ptr(lock_spinlock_ptr, sizeof(spinlock_t)) || 48262306a36Sopenharmony_ci test_kernel_ptr(lock_rwlock_ptr, sizeof(rwlock_t)) || 48362306a36Sopenharmony_ci test_kernel_ptr(lock_mutex_ptr, sizeof(struct mutex)) || 48462306a36Sopenharmony_ci test_kernel_ptr(lock_rwsem_ptr, sizeof(struct rw_semaphore))) 48562306a36Sopenharmony_ci return -EINVAL; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_SPINLOCK 48862306a36Sopenharmony_ci#ifdef CONFIG_PREEMPT_RT 48962306a36Sopenharmony_ci if (test_magic(lock_spinlock_ptr, 49062306a36Sopenharmony_ci offsetof(spinlock_t, lock.wait_lock.magic), 49162306a36Sopenharmony_ci SPINLOCK_MAGIC) || 49262306a36Sopenharmony_ci test_magic(lock_rwlock_ptr, 49362306a36Sopenharmony_ci offsetof(rwlock_t, rwbase.rtmutex.wait_lock.magic), 49462306a36Sopenharmony_ci SPINLOCK_MAGIC) || 49562306a36Sopenharmony_ci test_magic(lock_mutex_ptr, 49662306a36Sopenharmony_ci offsetof(struct mutex, rtmutex.wait_lock.magic), 49762306a36Sopenharmony_ci SPINLOCK_MAGIC) || 49862306a36Sopenharmony_ci test_magic(lock_rwsem_ptr, 49962306a36Sopenharmony_ci offsetof(struct rw_semaphore, rwbase.rtmutex.wait_lock.magic), 50062306a36Sopenharmony_ci SPINLOCK_MAGIC)) 50162306a36Sopenharmony_ci return -EINVAL; 50262306a36Sopenharmony_ci#else 50362306a36Sopenharmony_ci if (test_magic(lock_spinlock_ptr, 50462306a36Sopenharmony_ci offsetof(spinlock_t, rlock.magic), 50562306a36Sopenharmony_ci SPINLOCK_MAGIC) || 50662306a36Sopenharmony_ci test_magic(lock_rwlock_ptr, 50762306a36Sopenharmony_ci offsetof(rwlock_t, magic), 50862306a36Sopenharmony_ci RWLOCK_MAGIC) || 50962306a36Sopenharmony_ci test_magic(lock_mutex_ptr, 51062306a36Sopenharmony_ci offsetof(struct mutex, wait_lock.magic), 51162306a36Sopenharmony_ci SPINLOCK_MAGIC) || 51262306a36Sopenharmony_ci test_magic(lock_rwsem_ptr, 51362306a36Sopenharmony_ci offsetof(struct rw_semaphore, wait_lock.magic), 51462306a36Sopenharmony_ci SPINLOCK_MAGIC)) 51562306a36Sopenharmony_ci return -EINVAL; 51662306a36Sopenharmony_ci#endif 51762306a36Sopenharmony_ci#endif 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if ((wait_state != TASK_RUNNING || 52062306a36Sopenharmony_ci (call_cond_resched && !reacquire_locks) || 52162306a36Sopenharmony_ci (alloc_pages_nr && gfpflags_allow_blocking(alloc_pages_gfp))) && 52262306a36Sopenharmony_ci (test_disable_irq || disable_softirq || disable_preempt || 52362306a36Sopenharmony_ci lock_rcu || lock_spinlock_ptr || lock_rwlock_ptr)) { 52462306a36Sopenharmony_ci pr_err("refuse to sleep in atomic context\n"); 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (lock_mmap_sem && !main_task->mm) { 52962306a36Sopenharmony_ci pr_err("no mm to lock mmap_lock\n"); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (test_file_path[0]) { 53462306a36Sopenharmony_ci test_file = filp_open(test_file_path, O_RDONLY, 0); 53562306a36Sopenharmony_ci if (IS_ERR(test_file)) { 53662306a36Sopenharmony_ci pr_err("failed to open %s: %ld\n", test_file_path, PTR_ERR(test_file)); 53762306a36Sopenharmony_ci return PTR_ERR(test_file); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci test_inode = file_inode(test_file); 54062306a36Sopenharmony_ci } else if (test_lock_inode || 54162306a36Sopenharmony_ci test_lock_mapping || 54262306a36Sopenharmony_ci test_lock_sb_umount) { 54362306a36Sopenharmony_ci pr_err("no file to lock\n"); 54462306a36Sopenharmony_ci return -EINVAL; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (test_lock_inode && test_inode) 54862306a36Sopenharmony_ci lock_rwsem_ptr = (unsigned long)&test_inode->i_rwsem; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (test_lock_mapping && test_file && test_file->f_mapping) 55162306a36Sopenharmony_ci lock_rwsem_ptr = (unsigned long)&test_file->f_mapping->i_mmap_rwsem; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (test_lock_sb_umount && test_inode) 55462306a36Sopenharmony_ci lock_rwsem_ptr = (unsigned long)&test_inode->i_sb->s_umount; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci pr_notice("START pid=%d time=%u +%u ns cooldown=%u +%u ns iterations=%u state=%s %s%s%s%s%s%s%s%s%s%s%s\n", 55762306a36Sopenharmony_ci main_task->pid, time_secs, time_nsecs, 55862306a36Sopenharmony_ci cooldown_secs, cooldown_nsecs, iterations, state, 55962306a36Sopenharmony_ci all_cpus ? "all_cpus " : "", 56062306a36Sopenharmony_ci iowait ? "iowait " : "", 56162306a36Sopenharmony_ci test_disable_irq ? "disable_irq " : "", 56262306a36Sopenharmony_ci disable_softirq ? "disable_softirq " : "", 56362306a36Sopenharmony_ci disable_preempt ? "disable_preempt " : "", 56462306a36Sopenharmony_ci lock_rcu ? "lock_rcu " : "", 56562306a36Sopenharmony_ci lock_read ? "lock_read " : "", 56662306a36Sopenharmony_ci touch_softlockup ? "touch_softlockup " : "", 56762306a36Sopenharmony_ci touch_hardlockup ? "touch_hardlockup " : "", 56862306a36Sopenharmony_ci call_cond_resched ? "call_cond_resched " : "", 56962306a36Sopenharmony_ci reacquire_locks ? "reacquire_locks " : ""); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (alloc_pages_nr) 57262306a36Sopenharmony_ci pr_notice("ALLOCATE PAGES nr=%u order=%u gfp=%pGg %s\n", 57362306a36Sopenharmony_ci alloc_pages_nr, alloc_pages_order, &alloc_pages_gfp, 57462306a36Sopenharmony_ci reallocate_pages ? "reallocate_pages " : ""); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (all_cpus) { 57762306a36Sopenharmony_ci unsigned int cpu; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci cpus_read_lock(); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci preempt_disable(); 58262306a36Sopenharmony_ci master_cpu = smp_processor_id(); 58362306a36Sopenharmony_ci for_each_online_cpu(cpu) { 58462306a36Sopenharmony_ci INIT_WORK(per_cpu_ptr(&test_works, cpu), test_work_fn); 58562306a36Sopenharmony_ci queue_work_on(cpu, system_highpri_wq, 58662306a36Sopenharmony_ci per_cpu_ptr(&test_works, cpu)); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci preempt_enable(); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci for_each_online_cpu(cpu) 59162306a36Sopenharmony_ci flush_work(per_cpu_ptr(&test_works, cpu)); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci cpus_read_unlock(); 59462306a36Sopenharmony_ci } else { 59562306a36Sopenharmony_ci test_lockup(true); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (measure_lock_wait) 59962306a36Sopenharmony_ci pr_notice("Maximum lock wait: %lld ns\n", 60062306a36Sopenharmony_ci atomic64_read(&max_lock_wait)); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (alloc_pages_nr) 60362306a36Sopenharmony_ci pr_notice("Page allocation failed %u times\n", 60462306a36Sopenharmony_ci atomic_read(&alloc_pages_failed)); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci pr_notice("FINISH in %llu ns\n", local_clock() - test_start); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (test_file) 60962306a36Sopenharmony_ci fput(test_file); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (signal_pending(main_task)) 61262306a36Sopenharmony_ci return -EINTR; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return -EAGAIN; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_cimodule_init(test_lockup_init); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 61962306a36Sopenharmony_ciMODULE_AUTHOR("Konstantin Khlebnikov <khlebnikov@yandex-team.ru>"); 62062306a36Sopenharmony_ciMODULE_DESCRIPTION("Test module to generate lockups"); 621