diff options
Diffstat (limited to 'module/splat')
-rw-r--r-- | module/splat/Makefile.in | 28 | ||||
-rw-r--r-- | module/splat/splat-atomic.c | 233 | ||||
-rw-r--r-- | module/splat/splat-condvar.c | 511 | ||||
-rw-r--r-- | module/splat/splat-cred.c | 299 | ||||
-rw-r--r-- | module/splat/splat-ctl.c | 753 | ||||
-rw-r--r-- | module/splat/splat-generic.c | 367 | ||||
-rw-r--r-- | module/splat/splat-internal.h | 150 | ||||
-rw-r--r-- | module/splat/splat-kmem.c | 1410 | ||||
-rw-r--r-- | module/splat/splat-kobj.c | 166 | ||||
-rw-r--r-- | module/splat/splat-linux.c | 237 | ||||
-rw-r--r-- | module/splat/splat-list.c | 475 | ||||
-rw-r--r-- | module/splat/splat-mutex.c | 447 | ||||
-rw-r--r-- | module/splat/splat-random.c | 130 | ||||
-rw-r--r-- | module/splat/splat-rwlock.c | 747 | ||||
-rw-r--r-- | module/splat/splat-taskq.c | 1548 | ||||
-rw-r--r-- | module/splat/splat-thread.c | 390 | ||||
-rw-r--r-- | module/splat/splat-time.c | 119 | ||||
-rw-r--r-- | module/splat/splat-vnode.c | 355 | ||||
-rw-r--r-- | module/splat/splat-zlib.c | 166 |
19 files changed, 0 insertions, 8531 deletions
diff --git a/module/splat/Makefile.in b/module/splat/Makefile.in deleted file mode 100644 index 680f28492..000000000 --- a/module/splat/Makefile.in +++ /dev/null @@ -1,28 +0,0 @@ -# Makefile.in for splat kernel module - -src = @abs_top_srcdir@/module/splat -obj = @abs_builddir@ - -MODULE := splat -EXTRA_CFLAGS = $(SPL_MODULE_CFLAGS) @KERNELCPPFLAGS@ - -# Solaris Porting LAyer Tests -obj-$(CONFIG_SPL) := $(MODULE).o - -$(MODULE)-objs += splat-ctl.o -$(MODULE)-objs += splat-kmem.o -$(MODULE)-objs += splat-taskq.o -$(MODULE)-objs += splat-random.o -$(MODULE)-objs += splat-mutex.o -$(MODULE)-objs += splat-condvar.o -$(MODULE)-objs += splat-thread.o -$(MODULE)-objs += splat-rwlock.o -$(MODULE)-objs += splat-time.o -$(MODULE)-objs += splat-vnode.o -$(MODULE)-objs += splat-kobj.o -$(MODULE)-objs += splat-atomic.o -$(MODULE)-objs += splat-list.o -$(MODULE)-objs += splat-generic.o -$(MODULE)-objs += splat-cred.o -$(MODULE)-objs += splat-zlib.o -$(MODULE)-objs += splat-linux.o diff --git a/module/splat/splat-atomic.c b/module/splat/splat-atomic.c deleted file mode 100644 index 8aaa0835d..000000000 --- a/module/splat/splat-atomic.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Atomic Tests. - */ - -#include <sys/atomic.h> -#include <sys/thread.h> -#include <sys/mutex.h> -#include <linux/mm_compat.h> -#include <linux/wait_compat.h> -#include <linux/slab.h> -#include "splat-internal.h" - -#define SPLAT_ATOMIC_NAME "atomic" -#define SPLAT_ATOMIC_DESC "Kernel Atomic Tests" - -#define SPLAT_ATOMIC_TEST1_ID 0x0b01 -#define SPLAT_ATOMIC_TEST1_NAME "64-bit" -#define SPLAT_ATOMIC_TEST1_DESC "Validate 64-bit atomic ops" - -#define SPLAT_ATOMIC_TEST_MAGIC 0x43435454UL -#define SPLAT_ATOMIC_INIT_VALUE 10000000UL - -typedef enum { - SPLAT_ATOMIC_INC_64 = 0, - SPLAT_ATOMIC_DEC_64 = 1, - SPLAT_ATOMIC_ADD_64 = 2, - SPLAT_ATOMIC_SUB_64 = 3, - SPLAT_ATOMIC_ADD_64_NV = 4, - SPLAT_ATOMIC_SUB_64_NV = 5, - SPLAT_ATOMIC_COUNT_64 = 6 -} atomic_op_t; - -typedef struct atomic_priv { - unsigned long ap_magic; - struct file *ap_file; - kmutex_t ap_lock; - spl_wait_queue_head_t ap_waitq; - volatile uint64_t ap_atomic; - volatile uint64_t ap_atomic_exited; - atomic_op_t ap_op; - -} atomic_priv_t; - -static void -splat_atomic_work(void *priv) -{ - atomic_priv_t *ap; - atomic_op_t op; - int i; - - ap = (atomic_priv_t *)priv; - ASSERT(ap->ap_magic == SPLAT_ATOMIC_TEST_MAGIC); - - mutex_enter(&ap->ap_lock); - op = ap->ap_op; - wake_up(&ap->ap_waitq); - mutex_exit(&ap->ap_lock); - - splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME, - "Thread %d successfully started: %lu/%lu\n", op, - (long unsigned)ap->ap_atomic, - (long unsigned)ap->ap_atomic_exited); - - for (i = 0; i < SPLAT_ATOMIC_INIT_VALUE / 10; i++) { - - /* Periodically sleep to mix up the ordering */ - if ((i % (SPLAT_ATOMIC_INIT_VALUE / 100)) == 0) { - splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME, - "Thread %d sleeping: %lu/%lu\n", op, - (long unsigned)ap->ap_atomic, - (long unsigned)ap->ap_atomic_exited); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 100); - } - - switch (op) { - case SPLAT_ATOMIC_INC_64: - atomic_inc_64(&ap->ap_atomic); - break; - case SPLAT_ATOMIC_DEC_64: - atomic_dec_64(&ap->ap_atomic); - break; - case SPLAT_ATOMIC_ADD_64: - atomic_add_64(&ap->ap_atomic, 3); - break; - case SPLAT_ATOMIC_SUB_64: - atomic_sub_64(&ap->ap_atomic, 3); - break; - case SPLAT_ATOMIC_ADD_64_NV: - atomic_add_64_nv(&ap->ap_atomic, 5); - break; - case SPLAT_ATOMIC_SUB_64_NV: - atomic_sub_64_nv(&ap->ap_atomic, 5); - break; - default: - PANIC("Undefined op %d\n", op); - } - } - - atomic_inc_64(&ap->ap_atomic_exited); - - splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME, - "Thread %d successfully exited: %lu/%lu\n", op, - (long unsigned)ap->ap_atomic, - (long unsigned)ap->ap_atomic_exited); - - wake_up(&ap->ap_waitq); - thread_exit(); -} - -static int -splat_atomic_test1_cond(atomic_priv_t *ap, int started) -{ - return (ap->ap_atomic_exited == started); -} - -static int -splat_atomic_test1(struct file *file, void *arg) -{ - atomic_priv_t ap; - DEFINE_WAIT(wait); - kthread_t *thr; - int i, rc = 0; - - ap.ap_magic = SPLAT_ATOMIC_TEST_MAGIC; - ap.ap_file = file; - mutex_init(&ap.ap_lock, SPLAT_ATOMIC_TEST1_NAME, MUTEX_DEFAULT, NULL); - init_waitqueue_head(&ap.ap_waitq); - ap.ap_atomic = SPLAT_ATOMIC_INIT_VALUE; - ap.ap_atomic_exited = 0; - - for (i = 0; i < SPLAT_ATOMIC_COUNT_64; i++) { - mutex_enter(&ap.ap_lock); - ap.ap_op = i; - - thr = (kthread_t *)thread_create(NULL, 0, splat_atomic_work, - &ap, 0, &p0, TS_RUN, - defclsyspri); - if (thr == NULL) { - rc = -ESRCH; - mutex_exit(&ap.ap_lock); - break; - } - - /* Prepare to wait, the new thread will wake us once it - * has made a copy of the unique private passed data */ - prepare_to_wait(&ap.ap_waitq, &wait, TASK_UNINTERRUPTIBLE); - mutex_exit(&ap.ap_lock); - schedule(); - } - - wait_event(ap.ap_waitq, splat_atomic_test1_cond(&ap, i)); - - if (rc) { - splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME, "Only started " - "%d/%d test threads\n", i, SPLAT_ATOMIC_COUNT_64); - return rc; - } - - if (ap.ap_atomic != SPLAT_ATOMIC_INIT_VALUE) { - splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME, - "Final value %lu does not match initial value %lu\n", - (long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE); - return -EINVAL; - } - - splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME, - "Success initial and final values match, %lu == %lu\n", - (long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE); - - mutex_destroy(&ap.ap_lock); - - return 0; -} - -splat_subsystem_t * -splat_atomic_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_ATOMIC_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_ATOMIC_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_ATOMIC; - - splat_test_init(sub, SPLAT_ATOMIC_TEST1_NAME, SPLAT_ATOMIC_TEST1_DESC, - SPLAT_ATOMIC_TEST1_ID, splat_atomic_test1); - - return sub; -} - -void -splat_atomic_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_ATOMIC_TEST1_ID); - - kfree(sub); -} - -int -splat_atomic_id(void) { - return SPLAT_SUBSYSTEM_ATOMIC; -} diff --git a/module/splat/splat-condvar.c b/module/splat/splat-condvar.c deleted file mode 100644 index 0804baf50..000000000 --- a/module/splat/splat-condvar.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Condition Variable Tests. - */ - -#include <sys/condvar.h> -#include <sys/timer.h> -#include <sys/thread.h> -#include "splat-internal.h" - -#define SPLAT_CONDVAR_NAME "condvar" -#define SPLAT_CONDVAR_DESC "Kernel Condition Variable Tests" - -#define SPLAT_CONDVAR_TEST1_ID 0x0501 -#define SPLAT_CONDVAR_TEST1_NAME "signal1" -#define SPLAT_CONDVAR_TEST1_DESC "Wake a single thread, cv_wait()/cv_signal()" - -#define SPLAT_CONDVAR_TEST2_ID 0x0502 -#define SPLAT_CONDVAR_TEST2_NAME "broadcast1" -#define SPLAT_CONDVAR_TEST2_DESC "Wake all threads, cv_wait()/cv_broadcast()" - -#define SPLAT_CONDVAR_TEST3_ID 0x0503 -#define SPLAT_CONDVAR_TEST3_NAME "signal2" -#define SPLAT_CONDVAR_TEST3_DESC "Wake a single thread, cv_wait_timeout()/cv_signal()" - -#define SPLAT_CONDVAR_TEST4_ID 0x0504 -#define SPLAT_CONDVAR_TEST4_NAME "broadcast2" -#define SPLAT_CONDVAR_TEST4_DESC "Wake all threads, cv_wait_timeout()/cv_broadcast()" - -#define SPLAT_CONDVAR_TEST5_ID 0x0505 -#define SPLAT_CONDVAR_TEST5_NAME "timeout" -#define SPLAT_CONDVAR_TEST5_DESC "Timeout thread, cv_wait_timeout()" - -#define SPLAT_CONDVAR_TEST_MAGIC 0x115599DDUL -#define SPLAT_CONDVAR_TEST_NAME "condvar" -#define SPLAT_CONDVAR_TEST_COUNT 8 - -typedef struct condvar_priv { - unsigned long cv_magic; - struct file *cv_file; - kcondvar_t cv_condvar; - kmutex_t cv_mtx; -} condvar_priv_t; - -typedef struct condvar_thr { - const char *ct_name; - condvar_priv_t *ct_cvp; - struct task_struct *ct_thread; - int ct_rc; -} condvar_thr_t; - -int -splat_condvar_test12_thread(void *arg) -{ - condvar_thr_t *ct = (condvar_thr_t *)arg; - condvar_priv_t *cv = ct->ct_cvp; - - ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC); - - mutex_enter(&cv->cv_mtx); - splat_vprint(cv->cv_file, ct->ct_name, - "%s thread sleeping with %d waiters\n", - ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters)); - cv_wait(&cv->cv_condvar, &cv->cv_mtx); - splat_vprint(cv->cv_file, ct->ct_name, - "%s thread woken %d waiters remain\n", - ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters)); - mutex_exit(&cv->cv_mtx); - - /* wait for main thread reap us */ - while (!kthread_should_stop()) - schedule(); - return 0; -} - -static int -splat_condvar_test1(struct file *file, void *arg) -{ - int i, count = 0, rc = 0; - condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT]; - condvar_priv_t cv; - - cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC; - cv.cv_file = file; - mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL); - cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL); - - /* Create some threads, the exact number isn't important just as - * long as we know how many we managed to create and should expect. */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - ct[i].ct_cvp = &cv; - ct[i].ct_name = SPLAT_CONDVAR_TEST1_NAME; - ct[i].ct_rc = 0; - ct[i].ct_thread = spl_kthread_create(splat_condvar_test12_thread, - &ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i); - - if (!IS_ERR(ct[i].ct_thread)) { - wake_up_process(ct[i].ct_thread); - count++; - } - } - - /* Wait until all threads are waiting on the condition variable */ - while (atomic_read(&cv.cv_condvar.cv_waiters) != count) - schedule(); - - /* Wake a single thread at a time, wait until it exits */ - for (i = 1; i <= count; i++) { - cv_signal(&cv.cv_condvar); - - while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i)) - schedule(); - - /* Correct behavior 1 thread woken */ - if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i)) - continue; - - splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Attempted to " - "wake %d thread but work %d threads woke\n", - 1, count - atomic_read(&cv.cv_condvar.cv_waiters)); - rc = -EINVAL; - break; - } - - if (!rc) - splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Correctly woke " - "%d sleeping threads %d at a time\n", count, 1); - - /* Wait until that last nutex is dropped */ - while (mutex_owner(&cv.cv_mtx)) - schedule(); - - /* Wake everything for the failure case */ - cv_broadcast(&cv.cv_condvar); - cv_destroy(&cv.cv_condvar); - - /* wait for threads to exit */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - if (!IS_ERR(ct[i].ct_thread)) - kthread_stop(ct[i].ct_thread); - } - mutex_destroy(&cv.cv_mtx); - - return rc; -} - -static int -splat_condvar_test2(struct file *file, void *arg) -{ - int i, count = 0, rc = 0; - condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT]; - condvar_priv_t cv; - - cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC; - cv.cv_file = file; - mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL); - cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL); - - /* Create some threads, the exact number isn't important just as - * long as we know how many we managed to create and should expect. */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - ct[i].ct_cvp = &cv; - ct[i].ct_name = SPLAT_CONDVAR_TEST2_NAME; - ct[i].ct_rc = 0; - ct[i].ct_thread = spl_kthread_create(splat_condvar_test12_thread, - &ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i); - - if (!IS_ERR(ct[i].ct_thread)) { - wake_up_process(ct[i].ct_thread); - count++; - } - } - - /* Wait until all threads are waiting on the condition variable */ - while (atomic_read(&cv.cv_condvar.cv_waiters) != count) - schedule(); - - /* Wake all threads waiting on the condition variable */ - cv_broadcast(&cv.cv_condvar); - - /* Wait until all threads have exited */ - while ((atomic_read(&cv.cv_condvar.cv_waiters) > 0) || mutex_owner(&cv.cv_mtx)) - schedule(); - - splat_vprint(file, SPLAT_CONDVAR_TEST2_NAME, "Correctly woke all " - "%d sleeping threads at once\n", count); - - /* Wake everything for the failure case */ - cv_destroy(&cv.cv_condvar); - - /* wait for threads to exit */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - if (!IS_ERR(ct[i].ct_thread)) - kthread_stop(ct[i].ct_thread); - } - mutex_destroy(&cv.cv_mtx); - - return rc; -} - -int -splat_condvar_test34_thread(void *arg) -{ - condvar_thr_t *ct = (condvar_thr_t *)arg; - condvar_priv_t *cv = ct->ct_cvp; - clock_t rc; - - ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC); - - mutex_enter(&cv->cv_mtx); - splat_vprint(cv->cv_file, ct->ct_name, - "%s thread sleeping with %d waiters\n", - ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters)); - - /* Sleep no longer than 3 seconds, for this test we should - * actually never sleep that long without being woken up. */ - rc = cv_timedwait(&cv->cv_condvar, &cv->cv_mtx, lbolt + HZ * 3); - if (rc == -1) { - ct->ct_rc = -ETIMEDOUT; - splat_vprint(cv->cv_file, ct->ct_name, "%s thread timed out, " - "should have been woken\n", ct->ct_thread->comm); - } else { - splat_vprint(cv->cv_file, ct->ct_name, - "%s thread woken %d waiters remain\n", - ct->ct_thread->comm, - atomic_read(&cv->cv_condvar.cv_waiters)); - } - - mutex_exit(&cv->cv_mtx); - - /* wait for main thread reap us */ - while (!kthread_should_stop()) - schedule(); - return 0; -} - -static int -splat_condvar_test3(struct file *file, void *arg) -{ - int i, count = 0, rc = 0; - condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT]; - condvar_priv_t cv; - - cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC; - cv.cv_file = file; - mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL); - cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL); - - /* Create some threads, the exact number isn't important just as - * long as we know how many we managed to create and should expect. */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - ct[i].ct_cvp = &cv; - ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME; - ct[i].ct_rc = 0; - ct[i].ct_thread = spl_kthread_create(splat_condvar_test34_thread, - &ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i); - - if (!IS_ERR(ct[i].ct_thread)) { - wake_up_process(ct[i].ct_thread); - count++; - } - } - - /* Wait until all threads are waiting on the condition variable */ - while (atomic_read(&cv.cv_condvar.cv_waiters) != count) - schedule(); - - /* Wake a single thread at a time, wait until it exits */ - for (i = 1; i <= count; i++) { - cv_signal(&cv.cv_condvar); - - while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i)) - schedule(); - - /* Correct behavior 1 thread woken */ - if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i)) - continue; - - splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to " - "wake %d thread but work %d threads woke\n", - 1, count - atomic_read(&cv.cv_condvar.cv_waiters)); - rc = -EINVAL; - break; - } - - /* Validate no waiting thread timed out early */ - for (i = 0; i < count; i++) - if (ct[i].ct_rc) - rc = ct[i].ct_rc; - - if (!rc) - splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke " - "%d sleeping threads %d at a time\n", count, 1); - - /* Wait until that last nutex is dropped */ - while (mutex_owner(&cv.cv_mtx)) - schedule(); - - /* Wake everything for the failure case */ - cv_broadcast(&cv.cv_condvar); - cv_destroy(&cv.cv_condvar); - - /* wait for threads to exit */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - if (!IS_ERR(ct[i].ct_thread)) - kthread_stop(ct[i].ct_thread); - } - mutex_destroy(&cv.cv_mtx); - - return rc; -} - -static int -splat_condvar_test4(struct file *file, void *arg) -{ - int i, count = 0, rc = 0; - condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT]; - condvar_priv_t cv; - - cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC; - cv.cv_file = file; - mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL); - cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL); - - /* Create some threads, the exact number isn't important just as - * long as we know how many we managed to create and should expect. */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - ct[i].ct_cvp = &cv; - ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME; - ct[i].ct_rc = 0; - ct[i].ct_thread = spl_kthread_create(splat_condvar_test34_thread, - &ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i); - - if (!IS_ERR(ct[i].ct_thread)) { - wake_up_process(ct[i].ct_thread); - count++; - } - } - - /* Wait until all threads are waiting on the condition variable */ - while (atomic_read(&cv.cv_condvar.cv_waiters) != count) - schedule(); - - /* Wake a single thread at a time, wait until it exits */ - for (i = 1; i <= count; i++) { - cv_signal(&cv.cv_condvar); - - while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i)) - schedule(); - - /* Correct behavior 1 thread woken */ - if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i)) - continue; - - splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to " - "wake %d thread but work %d threads woke\n", - 1, count - atomic_read(&cv.cv_condvar.cv_waiters)); - rc = -EINVAL; - break; - } - - /* Validate no waiting thread timed out early */ - for (i = 0; i < count; i++) - if (ct[i].ct_rc) - rc = ct[i].ct_rc; - - if (!rc) - splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke " - "%d sleeping threads %d at a time\n", count, 1); - - /* Wait until that last nutex is dropped */ - while (mutex_owner(&cv.cv_mtx)) - schedule(); - - /* Wake everything for the failure case */ - cv_broadcast(&cv.cv_condvar); - cv_destroy(&cv.cv_condvar); - - /* wait for threads to exit */ - for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) { - if (!IS_ERR(ct[i].ct_thread)) - kthread_stop(ct[i].ct_thread); - } - mutex_destroy(&cv.cv_mtx); - - return rc; -} - -static int -splat_condvar_test5(struct file *file, void *arg) -{ - kcondvar_t condvar; - kmutex_t mtx; - clock_t time_left, time_before, time_after, time_delta; - uint64_t whole_delta; - uint32_t remain_delta; - int rc = 0; - - mutex_init(&mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL); - cv_init(&condvar, NULL, CV_DEFAULT, NULL); - - splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, "Thread going to sleep for " - "%d second and expecting to be woken by timeout\n", 1); - - /* Allow a 1 second timeout, plenty long to validate correctness. */ - time_before = lbolt; - mutex_enter(&mtx); - time_left = cv_timedwait(&condvar, &mtx, lbolt + HZ); - mutex_exit(&mtx); - time_after = lbolt; - time_delta = time_after - time_before; /* XXX - Handle jiffie wrap */ - whole_delta = time_delta; - remain_delta = do_div(whole_delta, HZ); - - if (time_left == -1) { - if (time_delta >= HZ) { - splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, - "Thread correctly timed out and was asleep " - "for %d.%d seconds (%d second min)\n", - (int)whole_delta, (int)remain_delta, 1); - } else { - splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, - "Thread correctly timed out but was only " - "asleep for %d.%d seconds (%d second " - "min)\n", (int)whole_delta, - (int)remain_delta, 1); - rc = -ETIMEDOUT; - } - } else { - splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, - "Thread exited after only %d.%d seconds, it " - "did not hit the %d second timeout\n", - (int)whole_delta, (int)remain_delta, 1); - rc = -ETIMEDOUT; - } - - cv_destroy(&condvar); - mutex_destroy(&mtx); - - return rc; -} - -splat_subsystem_t * -splat_condvar_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_CONDVAR_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_CONDVAR_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_CONDVAR; - - splat_test_init(sub, SPLAT_CONDVAR_TEST1_NAME, SPLAT_CONDVAR_TEST1_DESC, - SPLAT_CONDVAR_TEST1_ID, splat_condvar_test1); - splat_test_init(sub, SPLAT_CONDVAR_TEST2_NAME, SPLAT_CONDVAR_TEST2_DESC, - SPLAT_CONDVAR_TEST2_ID, splat_condvar_test2); - splat_test_init(sub, SPLAT_CONDVAR_TEST3_NAME, SPLAT_CONDVAR_TEST3_DESC, - SPLAT_CONDVAR_TEST3_ID, splat_condvar_test3); - splat_test_init(sub, SPLAT_CONDVAR_TEST4_NAME, SPLAT_CONDVAR_TEST4_DESC, - SPLAT_CONDVAR_TEST4_ID, splat_condvar_test4); - splat_test_init(sub, SPLAT_CONDVAR_TEST5_NAME, SPLAT_CONDVAR_TEST5_DESC, - SPLAT_CONDVAR_TEST5_ID, splat_condvar_test5); - - return sub; -} - -void -splat_condvar_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_CONDVAR_TEST5_ID); - splat_test_fini(sub, SPLAT_CONDVAR_TEST4_ID); - splat_test_fini(sub, SPLAT_CONDVAR_TEST3_ID); - splat_test_fini(sub, SPLAT_CONDVAR_TEST2_ID); - splat_test_fini(sub, SPLAT_CONDVAR_TEST1_ID); - - kfree(sub); -} - -int -splat_condvar_id(void) { - return SPLAT_SUBSYSTEM_CONDVAR; -} diff --git a/module/splat/splat-cred.c b/module/splat/splat-cred.c deleted file mode 100644 index f13c0f752..000000000 --- a/module/splat/splat-cred.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Credential Tests. - */ - -#include <sys/cred.h> -#include <sys/random.h> -#include "splat-internal.h" - -#define SPLAT_CRED_NAME "cred" -#define SPLAT_CRED_DESC "Kernel Cred Tests" - -#define SPLAT_CRED_TEST1_ID 0x0e01 -#define SPLAT_CRED_TEST1_NAME "cred" -#define SPLAT_CRED_TEST1_DESC "Task Credential Test" - -#define SPLAT_CRED_TEST2_ID 0x0e02 -#define SPLAT_CRED_TEST2_NAME "kcred" -#define SPLAT_CRED_TEST2_DESC "Kernel Credential Test" - -#define SPLAT_CRED_TEST3_ID 0x0e03 -#define SPLAT_CRED_TEST3_NAME "groupmember" -#define SPLAT_CRED_TEST3_DESC "Group Member Test" - -#define GROUP_STR_SIZE 128 -#define GROUP_STR_REDZONE 16 - -static int -splat_cred_test1(struct file *file, void *arg) -{ - char str[GROUP_STR_SIZE]; - uid_t uid, ruid, suid; - gid_t gid, rgid, sgid, *groups; - int ngroups, i, count = 0; - cred_t *cr = CRED(); - - uid = crgetuid(cr); - ruid = crgetruid(cr); - suid = crgetsuid(cr); - - gid = crgetgid(cr); - rgid = crgetrgid(cr); - sgid = crgetsgid(cr); - - ngroups = crgetngroups(cr); - groups = crgetgroups(cr); - - memset(str, 0, GROUP_STR_SIZE); - for (i = 0; i < ngroups; i++) { - count += sprintf(str + count, "%d ", groups[i]); - - if (count > (GROUP_STR_SIZE - GROUP_STR_REDZONE)) { - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "Failed too many group entries for temp " - "buffer: %d, %s\n", ngroups, str); - return -ENOSPC; - } - } - - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "uid: %d ruid: %d suid: %d " - "gid: %d rgid: %d sgid: %d\n", - uid, ruid, suid, gid, rgid, sgid); - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "ngroups: %d groups: %s\n", ngroups, str); - - if (uid || ruid || suid || gid || rgid || sgid) { - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "Failed expected all uids+gids to be %d\n", 0); - return -EIDRM; - } - - if (ngroups > NGROUPS_MAX) { - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "Failed ngroups must not exceed NGROUPS_MAX: " - "%d > %d\n", ngroups, NGROUPS_MAX); - return -EIDRM; - } - - splat_vprint(file, SPLAT_CRED_TEST1_NAME, - "Success sane CRED(): %d\n", 0); - - return 0; -} /* splat_cred_test1() */ - -static int -splat_cred_test2(struct file *file, void *arg) -{ - char str[GROUP_STR_SIZE]; - uid_t uid, ruid, suid; - gid_t gid, rgid, sgid, *groups; - int ngroups, i, count = 0; - - crhold(kcred); - - uid = crgetuid(kcred); - ruid = crgetruid(kcred); - suid = crgetsuid(kcred); - - gid = crgetgid(kcred); - rgid = crgetrgid(kcred); - sgid = crgetsgid(kcred); - - ngroups = crgetngroups(kcred); - groups = crgetgroups(kcred); - - memset(str, 0, GROUP_STR_SIZE); - for (i = 0; i < ngroups; i++) { - count += sprintf(str + count, "%d ", groups[i]); - - if (count > (GROUP_STR_SIZE - GROUP_STR_REDZONE)) { - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "Failed too many group entries for temp " - "buffer: %d, %s\n", ngroups, str); - crfree(kcred); - return -ENOSPC; - } - } - - crfree(kcred); - - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "uid: %d ruid: %d suid: %d " - "gid: %d rgid: %d sgid: %d\n", - uid, ruid, suid, gid, rgid, sgid); - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "ngroups: %d groups: %s\n", ngroups, str); - - if (uid || ruid || suid || gid || rgid || sgid) { - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "Failed expected all uids+gids to be %d\n", 0); - return -EIDRM; - } - - if (ngroups > NGROUPS_MAX) { - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "Failed ngroups must not exceed NGROUPS_MAX: " - "%d > %d\n", ngroups, NGROUPS_MAX); - return -EIDRM; - } - - splat_vprint(file, SPLAT_CRED_TEST2_NAME, - "Success sane kcred: %d\n", 0); - - return 0; -} /* splat_cred_test2() */ - -#define SPLAT_NGROUPS 32 -/* - * Verify the groupmember() works correctly by constructing an interesting - * CRED() and checking that the expected gids are part of it. - */ -static int -splat_cred_test3(struct file *file, void *arg) -{ - gid_t known_gid, missing_gid, tmp_gid; - unsigned char rnd; - struct group_info *gi; - int i, rc; - - get_random_bytes((void *)&rnd, 1); - known_gid = (rnd > 0) ? rnd : 1; - missing_gid = 0; - - /* - * Create an interesting known set of gids for test purposes. The - * gids are pseudo randomly selected are will be in the range of - * 1:(NGROUPS_MAX-1). Gid 0 is explicitly avoided so we can reliably - * test for its absence in the test cases. - */ - gi = groups_alloc(SPLAT_NGROUPS); - if (gi == NULL) { - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed create " - "group_info for known gids: %d\n", -ENOMEM); - rc = -ENOMEM; - goto show_groups; - } - - for (i = 0, tmp_gid = known_gid; i < SPLAT_NGROUPS; i++) { - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Adding gid %d " - "to current CRED() (%d/%d)\n", tmp_gid, i, gi->ngroups); -#ifdef HAVE_KUIDGID_T - GROUP_AT(gi, i) = make_kgid(current_user_ns(), tmp_gid); -#else - GROUP_AT(gi, i) = tmp_gid; -#endif /* HAVE_KUIDGID_T */ - tmp_gid = ((tmp_gid * 17) % (NGROUPS_MAX - 1)) + 1; - } - - /* Set the new groups in the CRED() and release our reference. */ - rc = set_current_groups(gi); - put_group_info(gi); - - if (rc) { - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed to add " - "gid %d to current group: %d\n", known_gid, rc); - goto show_groups; - } - - /* Verify groupmember() finds the known_gid in the CRED() */ - rc = groupmember(known_gid, CRED()); - if (!rc) { - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed to find " - "known gid %d in CRED()'s groups.\n", known_gid); - rc = -EIDRM; - goto show_groups; - } - - /* Verify groupmember() does NOT finds the missing gid in the CRED() */ - rc = groupmember(missing_gid, CRED()); - if (rc) { - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed missing " - "gid %d was found in CRED()'s groups.\n", missing_gid); - rc = -EIDRM; - goto show_groups; - } - - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Success groupmember() " - "correctly detects expected gids in CRED(): %d\n", rc); - -show_groups: - if (rc) { - int i, grps = crgetngroups(CRED()); - - splat_vprint(file, SPLAT_CRED_TEST3_NAME, "%d groups: ", grps); - for (i = 0; i < grps; i++) - splat_print(file, "%d ", crgetgroups(CRED())[i]); - splat_print(file, "%s", "\n"); - } - - - return (rc); -} /* splat_cred_test3() */ - -splat_subsystem_t * -splat_cred_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_CRED_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_CRED_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_CRED; - - splat_test_init(sub, SPLAT_CRED_TEST1_NAME, SPLAT_CRED_TEST1_DESC, - SPLAT_CRED_TEST1_ID, splat_cred_test1); - splat_test_init(sub, SPLAT_CRED_TEST2_NAME, SPLAT_CRED_TEST2_DESC, - SPLAT_CRED_TEST2_ID, splat_cred_test2); - splat_test_init(sub, SPLAT_CRED_TEST3_NAME, SPLAT_CRED_TEST3_DESC, - SPLAT_CRED_TEST3_ID, splat_cred_test3); - - return sub; -} /* splat_cred_init() */ - -void -splat_cred_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_CRED_TEST3_ID); - splat_test_fini(sub, SPLAT_CRED_TEST2_ID); - splat_test_fini(sub, SPLAT_CRED_TEST1_ID); - - kfree(sub); -} /* splat_cred_fini() */ - -int -splat_cred_id(void) -{ - return SPLAT_SUBSYSTEM_CRED; -} /* splat_cred_id() */ diff --git a/module/splat/splat-ctl.c b/module/splat/splat-ctl.c deleted file mode 100644 index cf6850100..000000000 --- a/module/splat/splat-ctl.c +++ /dev/null @@ -1,753 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Test Control Interface. - * - * The 'splat' (Solaris Porting LAyer Tests) module is designed as a - * framework which runs various in kernel regression tests to validate - * the SPL primitives honor the Solaris ABI. - * - * The splat module is constructed of various splat_* source files each - * of which contain regression tests for a particular subsystem. For - * example, the splat_kmem.c file contains all the tests for validating - * the kmem interfaces have been implemented correctly. When the splat - * module is loaded splat_*_init() will be called for each subsystems - * tests. It is the responsibility of splat_*_init() to register all - * the tests for this subsystem using the splat_test_init(). - * Similarly splat_*_fini() is called when the splat module is removed - * and is responsible for unregistering its tests via the splat_test_fini. - * Once a test is registered it can then be run with an ioctl() - * call which specifies the subsystem and test to be run. The provided - * splat command line tool can be used to display all available - * subsystems and tests. It can also be used to run the full suite - * of regression tests or particular tests. - */ - -#include <sys/debug.h> -#include <sys/mutex.h> -#include <sys/types.h> -#include <linux/cdev.h> -#include <linux/fs.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/vmalloc.h> -#include "splat-internal.h" - -static struct list_head splat_module_list; -static spinlock_t splat_module_lock; - -static int -splat_open(struct inode *inode, struct file *file) -{ - splat_info_t *info; - - info = (splat_info_t *)kmalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) - return -ENOMEM; - - mutex_init(&info->info_lock, SPLAT_NAME, MUTEX_DEFAULT, NULL); - info->info_size = SPLAT_INFO_BUFFER_SIZE; - info->info_buffer = (char *)vmalloc(SPLAT_INFO_BUFFER_SIZE); - if (info->info_buffer == NULL) { - kfree(info); - return -ENOMEM; - } - memset(info->info_buffer, 0, info->info_size); - - info->info_head = info->info_buffer; - file->private_data = (void *)info; - - splat_print(file, "%s\n", spl_version); - - return 0; -} - -static int -splat_release(struct inode *inode, struct file *file) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_destroy(&info->info_lock); - vfree(info->info_buffer); - kfree(info); - - return 0; -} - -static int -splat_buffer_clear(struct file *file, splat_cfg_t *kcfg, unsigned long arg) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_enter(&info->info_lock); - memset(info->info_buffer, 0, info->info_size); - info->info_head = info->info_buffer; - mutex_exit(&info->info_lock); - - return 0; -} - -static int -splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - char *buf; - int min, size, rc = 0; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_enter(&info->info_lock); - if (kcfg->cfg_arg1 > 0) { - - size = kcfg->cfg_arg1; - buf = (char *)vmalloc(size); - if (buf == NULL) { - rc = -ENOMEM; - goto out; - } - - /* Zero fill and truncate contents when coping buffer */ - min = ((size < info->info_size) ? size : info->info_size); - memset(buf, 0, size); - memcpy(buf, info->info_buffer, min); - vfree(info->info_buffer); - info->info_size = size; - info->info_buffer = buf; - info->info_head = info->info_buffer; - } - - kcfg->cfg_rc1 = info->info_size; - - if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) - rc = -EFAULT; -out: - mutex_exit(&info->info_lock); - - return rc; -} - - -static splat_subsystem_t * -splat_subsystem_find(int id) { - splat_subsystem_t *sub; - - spin_lock(&splat_module_lock); - list_for_each_entry(sub, &splat_module_list, subsystem_list) { - if (id == sub->desc.id) { - spin_unlock(&splat_module_lock); - return sub; - } - } - spin_unlock(&splat_module_lock); - - return NULL; -} - -static int -splat_subsystem_count(splat_cfg_t *kcfg, unsigned long arg) -{ - splat_subsystem_t *sub; - int i = 0; - - spin_lock(&splat_module_lock); - list_for_each_entry(sub, &splat_module_list, subsystem_list) - i++; - - spin_unlock(&splat_module_lock); - kcfg->cfg_rc1 = i; - - if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) - return -EFAULT; - - return 0; -} - -static int -splat_subsystem_list(splat_cfg_t *kcfg, unsigned long arg) -{ - splat_subsystem_t *sub; - splat_cfg_t *tmp; - int size, i = 0; - - /* Structure will be sized large enough for N subsystem entries - * which is passed in by the caller. On exit the number of - * entries filled in with valid subsystems will be stored in - * cfg_rc1. If the caller does not provide enough entries - * for all subsystems we will truncate the list to avoid overrun. - */ - size = sizeof(*tmp) + kcfg->cfg_data.splat_subsystems.size * - sizeof(splat_user_t); - tmp = kmalloc(size, GFP_KERNEL); - if (tmp == NULL) - return -ENOMEM; - - /* Local 'tmp' is used as the structure copied back to user space */ - memset(tmp, 0, size); - memcpy(tmp, kcfg, sizeof(*kcfg)); - - spin_lock(&splat_module_lock); - list_for_each_entry(sub, &splat_module_list, subsystem_list) { - strncpy(tmp->cfg_data.splat_subsystems.descs[i].name, - sub->desc.name, SPLAT_NAME_SIZE); - strncpy(tmp->cfg_data.splat_subsystems.descs[i].desc, - sub->desc.desc, SPLAT_DESC_SIZE); - tmp->cfg_data.splat_subsystems.descs[i].id = sub->desc.id; - - /* Truncate list if we are about to overrun alloc'ed memory */ - if ((i++) == kcfg->cfg_data.splat_subsystems.size) - break; - } - spin_unlock(&splat_module_lock); - tmp->cfg_rc1 = i; - - if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) { - kfree(tmp); - return -EFAULT; - } - - kfree(tmp); - return 0; -} - -static int -splat_test_count(splat_cfg_t *kcfg, unsigned long arg) -{ - splat_subsystem_t *sub; - splat_test_t *test; - int i = 0; - - /* Subsystem ID passed as arg1 */ - sub = splat_subsystem_find(kcfg->cfg_arg1); - if (sub == NULL) - return -EINVAL; - - spin_lock(&(sub->test_lock)); - list_for_each_entry(test, &(sub->test_list), test_list) - i++; - - spin_unlock(&(sub->test_lock)); - kcfg->cfg_rc1 = i; - - if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) - return -EFAULT; - - return 0; -} - -static int -splat_test_list(splat_cfg_t *kcfg, unsigned long arg) -{ - splat_subsystem_t *sub; - splat_test_t *test; - splat_cfg_t *tmp; - int size, i = 0; - - /* Subsystem ID passed as arg1 */ - sub = splat_subsystem_find(kcfg->cfg_arg1); - if (sub == NULL) - return -EINVAL; - - /* Structure will be sized large enough for N test entries - * which is passed in by the caller. On exit the number of - * entries filled in with valid tests will be stored in - * cfg_rc1. If the caller does not provide enough entries - * for all tests we will truncate the list to avoid overrun. - */ - size = sizeof(*tmp)+kcfg->cfg_data.splat_tests.size*sizeof(splat_user_t); - tmp = kmalloc(size, GFP_KERNEL); - if (tmp == NULL) - return -ENOMEM; - - /* Local 'tmp' is used as the structure copied back to user space */ - memset(tmp, 0, size); - memcpy(tmp, kcfg, sizeof(*kcfg)); - - spin_lock(&(sub->test_lock)); - list_for_each_entry(test, &(sub->test_list), test_list) { - strncpy(tmp->cfg_data.splat_tests.descs[i].name, - test->desc.name, SPLAT_NAME_SIZE); - strncpy(tmp->cfg_data.splat_tests.descs[i].desc, - test->desc.desc, SPLAT_DESC_SIZE); - tmp->cfg_data.splat_tests.descs[i].id = test->desc.id; - - /* Truncate list if we are about to overrun alloc'ed memory */ - if ((i++) == kcfg->cfg_data.splat_tests.size) - break; - } - spin_unlock(&(sub->test_lock)); - tmp->cfg_rc1 = i; - - if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) { - kfree(tmp); - return -EFAULT; - } - - kfree(tmp); - return 0; -} - -static int -splat_validate(struct file *file, splat_subsystem_t *sub, int cmd, void *arg) -{ - splat_test_t *test; - - spin_lock(&(sub->test_lock)); - list_for_each_entry(test, &(sub->test_list), test_list) { - if (test->desc.id == cmd) { - spin_unlock(&(sub->test_lock)); - return test->test(file, arg); - } - } - spin_unlock(&(sub->test_lock)); - - return -EINVAL; -} - -static int -splat_ioctl_cfg(struct file *file, unsigned int cmd, unsigned long arg) -{ - splat_cfg_t kcfg; - int rc = 0; - - /* User and kernel space agree about arg size */ - if (_IOC_SIZE(cmd) != sizeof(kcfg)) - return -EBADMSG; - - if (copy_from_user(&kcfg, (splat_cfg_t *)arg, sizeof(kcfg))) - return -EFAULT; - - if (kcfg.cfg_magic != SPLAT_CFG_MAGIC) { - splat_print(file, "Bad config magic 0x%x != 0x%x\n", - kcfg.cfg_magic, SPLAT_CFG_MAGIC); - return -EINVAL; - } - - switch (kcfg.cfg_cmd) { - case SPLAT_CFG_BUFFER_CLEAR: - /* cfg_arg1 - Unused - * cfg_rc1 - Unused - */ - rc = splat_buffer_clear(file, &kcfg, arg); - break; - case SPLAT_CFG_BUFFER_SIZE: - /* cfg_arg1 - 0 - query size; >0 resize - * cfg_rc1 - Set to current buffer size - */ - rc = splat_buffer_size(file, &kcfg, arg); - break; - case SPLAT_CFG_SUBSYSTEM_COUNT: - /* cfg_arg1 - Unused - * cfg_rc1 - Set to number of subsystems - */ - rc = splat_subsystem_count(&kcfg, arg); - break; - case SPLAT_CFG_SUBSYSTEM_LIST: - /* cfg_arg1 - Unused - * cfg_rc1 - Set to number of subsystems - * cfg_data.splat_subsystems - Set with subsystems - */ - rc = splat_subsystem_list(&kcfg, arg); - break; - case SPLAT_CFG_TEST_COUNT: - /* cfg_arg1 - Set to a target subsystem - * cfg_rc1 - Set to number of tests - */ - rc = splat_test_count(&kcfg, arg); - break; - case SPLAT_CFG_TEST_LIST: - /* cfg_arg1 - Set to a target subsystem - * cfg_rc1 - Set to number of tests - * cfg_data.splat_subsystems - Populated with tests - */ - rc = splat_test_list(&kcfg, arg); - break; - default: - splat_print(file, "Bad config command %d\n", - kcfg.cfg_cmd); - rc = -EINVAL; - break; - } - - return rc; -} - -static int -splat_ioctl_cmd(struct file *file, unsigned int cmd, unsigned long arg) -{ - splat_subsystem_t *sub; - splat_cmd_t kcmd; - int rc = -EINVAL; - void *data = NULL; - - /* User and kernel space agree about arg size */ - if (_IOC_SIZE(cmd) != sizeof(kcmd)) - return -EBADMSG; - - if (copy_from_user(&kcmd, (splat_cfg_t *)arg, sizeof(kcmd))) - return -EFAULT; - - if (kcmd.cmd_magic != SPLAT_CMD_MAGIC) { - splat_print(file, "Bad command magic 0x%x != 0x%x\n", - kcmd.cmd_magic, SPLAT_CFG_MAGIC); - return -EINVAL; - } - - /* Allocate memory for any opaque data the caller needed to pass on */ - if (kcmd.cmd_data_size > 0) { - data = (void *)kmalloc(kcmd.cmd_data_size, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - - if (copy_from_user(data, (void *)(arg + offsetof(splat_cmd_t, - cmd_data_str)), kcmd.cmd_data_size)) { - kfree(data); - return -EFAULT; - } - } - - sub = splat_subsystem_find(kcmd.cmd_subsystem); - if (sub != NULL) - rc = splat_validate(file, sub, kcmd.cmd_test, data); - else - rc = -EINVAL; - - if (data != NULL) - kfree(data); - - return rc; -} - -static long -splat_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int rc = 0; - - /* Ignore tty ioctls */ - if ((cmd & 0xffffff00) == ((int)'T') << 8) - return -ENOTTY; - - switch (cmd) { - case SPLAT_CFG: - rc = splat_ioctl_cfg(file, cmd, arg); - break; - case SPLAT_CMD: - rc = splat_ioctl_cmd(file, cmd, arg); - break; - default: - splat_print(file, "Bad ioctl command %d\n", cmd); - rc = -EINVAL; - break; - } - - return rc; -} - -#ifdef CONFIG_COMPAT -/* Compatibility handler for ioctls from 32-bit ELF binaries */ -static long -splat_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return splat_unlocked_ioctl(file, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - -/* I'm not sure why you would want to write in to this buffer from - * user space since its principle use is to pass test status info - * back to the user space, but I don't see any reason to prevent it. - */ -static ssize_t splat_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - int rc = 0; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_enter(&info->info_lock); - - /* Write beyond EOF */ - if (*ppos >= info->info_size) { - rc = -EFBIG; - goto out; - } - - /* Resize count if beyond EOF */ - if (*ppos + count > info->info_size) - count = info->info_size - *ppos; - - if (copy_from_user(info->info_buffer, buf, count)) { - rc = -EFAULT; - goto out; - } - - *ppos += count; - rc = count; -out: - mutex_exit(&info->info_lock); - return rc; -} - -static ssize_t splat_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - int rc = 0; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_enter(&info->info_lock); - - /* Read beyond EOF */ - if (*ppos >= info->info_size) - goto out; - - /* Resize count if beyond EOF */ - if (*ppos + count > info->info_size) - count = info->info_size - *ppos; - - if (copy_to_user(buf, info->info_buffer + *ppos, count)) { - rc = -EFAULT; - goto out; - } - - *ppos += count; - rc = count; -out: - mutex_exit(&info->info_lock); - return rc; -} - -static loff_t splat_seek(struct file *file, loff_t offset, int origin) -{ - splat_info_t *info = (splat_info_t *)file->private_data; - int rc = -EINVAL; - - ASSERT(info); - ASSERT(info->info_buffer); - - mutex_enter(&info->info_lock); - - switch (origin) { - case 0: /* SEEK_SET - No-op just do it */ - break; - case 1: /* SEEK_CUR - Seek from current */ - offset = file->f_pos + offset; - break; - case 2: /* SEEK_END - Seek from end */ - offset = info->info_size + offset; - break; - } - - if (offset >= 0) { - file->f_pos = offset; - file->f_version = 0; - rc = offset; - } - - mutex_exit(&info->info_lock); - - return rc; -} - -static struct file_operations splat_fops = { - .owner = THIS_MODULE, - .open = splat_open, - .release = splat_release, - .unlocked_ioctl = splat_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = splat_compat_ioctl, -#endif - .read = splat_read, - .write = splat_write, - .llseek = splat_seek, -}; - -static struct miscdevice splat_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = SPLAT_NAME, - .fops = &splat_fops, -}; - -static void splat_subsystem_init(const char *name, - splat_subsystem_t *(*init)(void)) -{ - splat_subsystem_t *sub; - sub = init(); - if (sub == NULL) { - printk(KERN_ERR "splat: Error initializing: %s\n", name); - return; - } - spin_lock(&splat_module_lock); - list_add_tail(&sub->subsystem_list, &splat_module_list); - spin_unlock(&splat_module_lock); -} - -static void splat_subsystem_fini(const char *name, - int (*id_func)(void), void (*fini)(splat_subsystem_t *)) -{ - splat_subsystem_t *sub, *tmp; - int id, flag = 0; - - id = id_func(); - spin_lock(&splat_module_lock); - list_for_each_entry_safe(sub, tmp, &splat_module_list, subsystem_list) { - if (sub->desc.id == id) { - list_del_init(&sub->subsystem_list); - flag = 1; - break; - } - } - spin_unlock(&splat_module_lock); - if (flag == 0) - printk(KERN_ERR "splat: Error finalizing: %s\n", name); - else - fini(sub); -} - -#define SPLAT_SUBSYSTEM_INIT(type) \ - splat_subsystem_init(#type, splat_##type##_init) -#define SPLAT_SUBSYSTEM_FINI(type) \ - splat_subsystem_fini(#type, splat_##type##_id, splat_##type##_fini) - -void splat_test_init(splat_subsystem_t *sub, const char *name, - const char *desc, unsigned int tid, splat_test_func_t func) -{ - splat_test_t *test; - test = kmalloc(sizeof (splat_test_t), GFP_KERNEL); - if (test == NULL) { - printk(KERN_ERR "splat: Error initializing: %s/%u\n", - name, tid); - return; - } - memset(test, 0, sizeof (splat_test_t)); - strncpy(test->desc.name, name, SPLAT_NAME_SIZE-1); - strncpy(test->desc.desc, desc, SPLAT_DESC_SIZE-1); - test->desc.id = tid; - test->test = func; - INIT_LIST_HEAD(&test->test_list); - spin_lock(&sub->test_lock); - list_add_tail(&test->test_list, &sub->test_list); - spin_unlock(&sub->test_lock); -} - -void splat_test_fini(splat_subsystem_t *sub, unsigned int tid) -{ - splat_test_t *test, *tmp; - int flag = 0; - - spin_lock(&sub->test_lock); - list_for_each_entry_safe(test, tmp, &sub->test_list, test_list) { - if (test->desc.id == tid) { - list_del_init(&test->test_list); - kfree(test); - flag = 1; - break; - } - } - spin_unlock(&sub->test_lock); - - if (flag == 0) - printk(KERN_ERR "splat: Error finalizing: %u\n", tid); -} - -static int __init -splat_init(void) -{ - int error; - - spin_lock_init(&splat_module_lock); - INIT_LIST_HEAD(&splat_module_list); - - SPLAT_SUBSYSTEM_INIT(kmem); - SPLAT_SUBSYSTEM_INIT(taskq); - SPLAT_SUBSYSTEM_INIT(krng); - SPLAT_SUBSYSTEM_INIT(mutex); - SPLAT_SUBSYSTEM_INIT(condvar); - SPLAT_SUBSYSTEM_INIT(thread); - SPLAT_SUBSYSTEM_INIT(rwlock); - SPLAT_SUBSYSTEM_INIT(time); - SPLAT_SUBSYSTEM_INIT(vnode); - SPLAT_SUBSYSTEM_INIT(kobj); - SPLAT_SUBSYSTEM_INIT(atomic); - SPLAT_SUBSYSTEM_INIT(list); - SPLAT_SUBSYSTEM_INIT(generic); - SPLAT_SUBSYSTEM_INIT(cred); - SPLAT_SUBSYSTEM_INIT(zlib); - SPLAT_SUBSYSTEM_INIT(linux); - - error = misc_register(&splat_misc); - if (error) { - printk(KERN_INFO "SPLAT: misc_register() failed %d\n", error); - } else { - printk(KERN_INFO "SPLAT: Loaded module v%s-%s%s\n", - SPL_META_VERSION, SPL_META_RELEASE, SPL_DEBUG_STR); - } - - return (error); -} - -static void __exit -splat_fini(void) -{ - misc_deregister(&splat_misc); - - SPLAT_SUBSYSTEM_FINI(linux); - SPLAT_SUBSYSTEM_FINI(zlib); - SPLAT_SUBSYSTEM_FINI(cred); - SPLAT_SUBSYSTEM_FINI(generic); - SPLAT_SUBSYSTEM_FINI(list); - SPLAT_SUBSYSTEM_FINI(atomic); - SPLAT_SUBSYSTEM_FINI(kobj); - SPLAT_SUBSYSTEM_FINI(vnode); - SPLAT_SUBSYSTEM_FINI(time); - SPLAT_SUBSYSTEM_FINI(rwlock); - SPLAT_SUBSYSTEM_FINI(thread); - SPLAT_SUBSYSTEM_FINI(condvar); - SPLAT_SUBSYSTEM_FINI(mutex); - SPLAT_SUBSYSTEM_FINI(krng); - SPLAT_SUBSYSTEM_FINI(taskq); - SPLAT_SUBSYSTEM_FINI(kmem); - - ASSERT(list_empty(&splat_module_list)); - printk(KERN_INFO "SPLAT: Unloaded module v%s-%s%s\n", - SPL_META_VERSION, SPL_META_RELEASE, SPL_DEBUG_STR); -} - -module_init(splat_init); -module_exit(splat_fini); - -MODULE_DESCRIPTION("Solaris Porting LAyer Tests"); -MODULE_AUTHOR(SPL_META_AUTHOR); -MODULE_LICENSE(SPL_META_LICENSE); -MODULE_VERSION(SPL_META_VERSION "-" SPL_META_RELEASE); diff --git a/module/splat/splat-generic.c b/module/splat/splat-generic.c deleted file mode 100644 index e256c83f0..000000000 --- a/module/splat/splat-generic.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Generic Tests. - */ - -#include <sys/sunddi.h> -#include <linux/math64_compat.h> -#include "splat-internal.h" - -#define SPLAT_GENERIC_NAME "generic" -#define SPLAT_GENERIC_DESC "Kernel Generic Tests" - -#define SPLAT_GENERIC_TEST1_ID 0x0d01 -#define SPLAT_GENERIC_TEST1_NAME "ddi_strtoul" -#define SPLAT_GENERIC_TEST1_DESC "ddi_strtoul Test" - -#define SPLAT_GENERIC_TEST2_ID 0x0d02 -#define SPLAT_GENERIC_TEST2_NAME "ddi_strtol" -#define SPLAT_GENERIC_TEST2_DESC "ddi_strtol Test" - -#define SPLAT_GENERIC_TEST3_ID 0x0d03 -#define SPLAT_GENERIC_TEST3_NAME "ddi_strtoull" -#define SPLAT_GENERIC_TEST3_DESC "ddi_strtoull Test" - -#define SPLAT_GENERIC_TEST4_ID 0x0d04 -#define SPLAT_GENERIC_TEST4_NAME "ddi_strtoll" -#define SPLAT_GENERIC_TEST4_DESC "ddi_strtoll Test" - -# define SPLAT_GENERIC_TEST5_ID 0x0d05 -# define SPLAT_GENERIC_TEST5_NAME "udivdi3" -# define SPLAT_GENERIC_TEST5_DESC "Unsigned Div-64 Test" - -# define SPLAT_GENERIC_TEST6_ID 0x0d06 -# define SPLAT_GENERIC_TEST6_NAME "divdi3" -# define SPLAT_GENERIC_TEST6_DESC "Signed Div-64 Test" - -#define STR_POS "123456789" -#define STR_NEG "-123456789" -#define STR_BASE "0xabcdef" -#define STR_RANGE_MAX "10000000000000000" -#define STR_RANGE_MIN "-10000000000000000" -#define STR_INVAL1 "12345U" -#define STR_INVAL2 "invald" - -#define VAL_POS 123456789 -#define VAL_NEG -123456789 -#define VAL_BASE 0xabcdef -#define VAL_INVAL1 12345U - -#define define_generic_msg_strtox(type, valtype) \ -static void \ -generic_msg_strto##type(struct file *file, char *msg, int rc, int *err, \ - const char *s, valtype d, char *endptr) \ -{ \ - splat_vprint(file, SPLAT_GENERIC_TEST1_NAME, \ - "%s (%d) %s: %s == %lld, 0x%p\n", \ - rc ? "Fail" : "Pass", *err, msg, s, \ - (unsigned long long)d, endptr); \ - *err = rc; \ -} - -define_generic_msg_strtox(ul, unsigned long); -define_generic_msg_strtox(l, long); -define_generic_msg_strtox(ull, unsigned long long); -define_generic_msg_strtox(ll, long long); - -#define define_splat_generic_test_strtox(type, valtype) \ -static int \ -splat_generic_test_strto##type(struct file *file, void *arg) \ -{ \ - int rc, rc1, rc2, rc3, rc4, rc5, rc6, rc7; \ - char str[20], *endptr; \ - valtype r; \ - \ - /* Positive value: expect success */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - rc1 = ddi_strto##type(STR_POS, &endptr, 10, &r); \ - if (rc1 == 0 && r == VAL_POS && endptr && *endptr == '\0') \ - rc = 0; \ - \ - generic_msg_strto##type(file, "positive", rc , &rc1, \ - STR_POS, r, endptr); \ - \ - /* Negative value: expect success */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - strcpy(str, STR_NEG); \ - rc2 = ddi_strto##type(str, &endptr, 10, &r); \ - if (#type[0] == 'u') { \ - if (rc2 == 0 && r == 0 && endptr == str) \ - rc = 0; \ - } else { \ - if (rc2 == 0 && r == VAL_NEG && \ - endptr && *endptr == '\0') \ - rc = 0; \ - } \ - \ - generic_msg_strto##type(file, "negative", rc, &rc2, \ - STR_NEG, r, endptr); \ - \ - /* Non decimal base: expect sucess */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - rc3 = ddi_strto##type(STR_BASE, &endptr, 0, &r); \ - if (rc3 == 0 && r == VAL_BASE && endptr && *endptr == '\0') \ - rc = 0; \ - \ - generic_msg_strto##type(file, "base", rc, &rc3, \ - STR_BASE, r, endptr); \ - \ - /* Max out of range: failure expected, r unchanged */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - rc4 = ddi_strto##type(STR_RANGE_MAX, &endptr, 16, &r); \ - if (rc4 == ERANGE && r == 0 && endptr == NULL) \ - rc = 0; \ - \ - generic_msg_strto##type(file, "max", rc, &rc4, \ - STR_RANGE_MAX, r, endptr); \ - \ - /* Min out of range: failure expected, r unchanged */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - strcpy(str, STR_RANGE_MIN); \ - rc5 = ddi_strto##type(str, &endptr, 16, &r); \ - if (#type[0] == 'u') { \ - if (rc5 == 0 && r == 0 && endptr == str) \ - rc = 0; \ - } else { \ - if (rc5 == ERANGE && r == 0 && endptr == NULL) \ - rc = 0; \ - } \ - \ - generic_msg_strto##type(file, "min", rc, &rc5, \ - STR_RANGE_MIN, r, endptr); \ - \ - /* Invalid string: success expected, endptr == 'U' */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - rc6 = ddi_strto##type(STR_INVAL1, &endptr, 10, &r); \ - if (rc6 == 0 && r == VAL_INVAL1 && endptr && *endptr == 'U') \ - rc = 0; \ - \ - generic_msg_strto##type(file, "invalid", rc, &rc6, \ - STR_INVAL1, r, endptr); \ - \ - /* Invalid string: failure expected, endptr == str */ \ - r = 0; \ - rc = 1; \ - endptr = NULL; \ - strcpy(str, STR_INVAL2); \ - rc7 = ddi_strto##type(str, &endptr, 10, &r); \ - if (rc7 == 0 && r == 0 && endptr == str) \ - rc = 0; \ - \ - generic_msg_strto##type(file, "invalid", rc, &rc7, \ - STR_INVAL2, r, endptr); \ - \ - return (rc1 || rc2 || rc3 || rc4 || rc5 || rc6 || rc7) ? \ - -EINVAL : 0; \ -} - -define_splat_generic_test_strtox(ul, unsigned long); -define_splat_generic_test_strtox(l, long); -define_splat_generic_test_strtox(ull, unsigned long long); -define_splat_generic_test_strtox(ll, long long); - -/* - * The entries in the table are used in all combinations and the - * return value is checked to ensure it is range. On 32-bit - * systems __udivdi3 will be invoked for the 64-bit division. - * On 64-bit system the native 64-bit divide will be used so - * __udivdi3 isn't used but we might as well stil run the test. - */ -static int -splat_generic_test_udivdi3(struct file *file, void *arg) -{ - const uint64_t tabu[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 1000, 2003, - 32765, 32766, 32767, 32768, 32769, 32760, - 65533, 65534, 65535, 65536, 65537, 65538, - 0x7ffffffeULL, 0x7fffffffULL, 0x80000000ULL, 0x80000001ULL, - 0x7000000000000000ULL, 0x7000000080000000ULL, 0x7000000080000001ULL, - 0x7fffffffffffffffULL, 0x7fffffff8fffffffULL, 0x7fffffff8ffffff1ULL, - 0x7fffffff00000000ULL, 0x7fffffff80000000ULL, 0x7fffffff00000001ULL, - 0x8000000000000000ULL, 0x8000000080000000ULL, 0x8000000080000001ULL, - 0xc000000000000000ULL, 0xc000000080000000ULL, 0xc000000080000001ULL, - 0xfffffffffffffffdULL, 0xfffffffffffffffeULL, 0xffffffffffffffffULL, - }; - uint64_t uu, vu, qu, ru; - int n, i, j, errors = 0; - - splat_vprint(file, SPLAT_GENERIC_TEST5_NAME, "%s", - "Testing unsigned 64-bit division.\n"); - n = sizeof(tabu) / sizeof(tabu[0]); - for (i = 0; i < n; i++) { - for (j = 1; j < n; j++) { - uu = tabu[i]; - vu = tabu[j]; - qu = uu / vu; /* __udivdi3 */ - ru = uu - qu * vu; - if (qu > uu || ru >= vu) { - splat_vprint(file, SPLAT_GENERIC_TEST5_NAME, - "%016llx/%016llx != %016llx rem %016llx\n", - uu, vu, qu, ru); - errors++; - } - } - } - - if (errors) { - splat_vprint(file, SPLAT_GENERIC_TEST5_NAME, - "Failed %d/%d tests\n", errors, n * (n - 1)); - return -ERANGE; - } - - splat_vprint(file, SPLAT_GENERIC_TEST5_NAME, - "Passed all %d tests\n", n * (n - 1)); - - return 0; -} - -/* - * The entries the table are used in all combinations, with + and - signs - * preceding them. The return value is checked to ensure it is range. - * On 32-bit systems __divdi3 will be invoked for the 64-bit division. - * On 64-bit system the native 64-bit divide will be used so __divdi3 - * isn't used but we might as well stil run the test. - */ -static int -splat_generic_test_divdi3(struct file *file, void *arg) -{ - const int64_t tabs[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 1000, 2003, - 32765, 32766, 32767, 32768, 32769, 32760, - 65533, 65534, 65535, 65536, 65537, 65538, - 0x7ffffffeLL, 0x7fffffffLL, 0x80000000LL, 0x80000001LL, - 0x7000000000000000LL, 0x7000000080000000LL, 0x7000000080000001LL, - 0x7fffffffffffffffLL, 0x7fffffff8fffffffLL, 0x7fffffff8ffffff1LL, - 0x7fffffff00000000LL, 0x7fffffff80000000LL, 0x7fffffff00000001LL, - 0x0123456789abcdefLL, 0x00000000abcdef01LL, 0x0000000012345678LL, -#if BITS_PER_LONG == 32 - 0x8000000000000000LL, 0x8000000080000000LL, 0x8000000080000001LL, -#endif - }; - int64_t u, v, q, r; - int n, i, j, k, errors = 0; - - splat_vprint(file, SPLAT_GENERIC_TEST6_NAME, "%s", - "Testing signed 64-bit division.\n"); - n = sizeof(tabs) / sizeof(tabs[0]); - for (i = 0; i < n; i++) { - for (j = 1; j < n; j++) { - for (k = 0; k <= 3; k++) { - u = (k & 1) ? -tabs[i] : tabs[i]; - v = (k >= 2) ? -tabs[j] : tabs[j]; - - q = u / v; /* __divdi3 */ - r = u - q * v; - if (abs64(q) > abs64(u) || - abs64(r) >= abs64(v) || - (r != 0 && (r ^ u) < 0)) { - splat_vprint(file, - SPLAT_GENERIC_TEST6_NAME, - "%016llx/%016llx != %016llx " - "rem %016llx\n", u, v, q, r); - errors++; - } - } - } - } - - if (errors) { - splat_vprint(file, SPLAT_GENERIC_TEST6_NAME, - "Failed %d/%d tests\n", errors, n * (n - 1)); - return -ERANGE; - } - - splat_vprint(file, SPLAT_GENERIC_TEST6_NAME, - "Passed all %d tests\n", n * (n - 1)); - - return 0; -} - -splat_subsystem_t * -splat_generic_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_GENERIC_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_GENERIC_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_GENERIC; - - splat_test_init(sub, SPLAT_GENERIC_TEST1_NAME, SPLAT_GENERIC_TEST1_DESC, - SPLAT_GENERIC_TEST1_ID, splat_generic_test_strtoul); - splat_test_init(sub, SPLAT_GENERIC_TEST2_NAME, SPLAT_GENERIC_TEST2_DESC, - SPLAT_GENERIC_TEST2_ID, splat_generic_test_strtol); - splat_test_init(sub, SPLAT_GENERIC_TEST3_NAME, SPLAT_GENERIC_TEST3_DESC, - SPLAT_GENERIC_TEST3_ID, splat_generic_test_strtoull); - splat_test_init(sub, SPLAT_GENERIC_TEST4_NAME, SPLAT_GENERIC_TEST4_DESC, - SPLAT_GENERIC_TEST4_ID, splat_generic_test_strtoll); - splat_test_init(sub, SPLAT_GENERIC_TEST5_NAME, SPLAT_GENERIC_TEST5_DESC, - SPLAT_GENERIC_TEST5_ID, splat_generic_test_udivdi3); - splat_test_init(sub, SPLAT_GENERIC_TEST6_NAME, SPLAT_GENERIC_TEST6_DESC, - SPLAT_GENERIC_TEST6_ID, splat_generic_test_divdi3); - - return sub; -} - -void -splat_generic_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_GENERIC_TEST6_ID); - splat_test_fini(sub, SPLAT_GENERIC_TEST5_ID); - splat_test_fini(sub, SPLAT_GENERIC_TEST4_ID); - splat_test_fini(sub, SPLAT_GENERIC_TEST3_ID); - splat_test_fini(sub, SPLAT_GENERIC_TEST2_ID); - splat_test_fini(sub, SPLAT_GENERIC_TEST1_ID); - - kfree(sub); -} - -int -splat_generic_id(void) -{ - return SPLAT_SUBSYSTEM_GENERIC; -} diff --git a/module/splat/splat-internal.h b/module/splat/splat-internal.h deleted file mode 100644 index 97c10acfd..000000000 --- a/module/splat/splat-internal.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef _SPLAT_INTERNAL_H -#define _SPLAT_INTERNAL_H - -#include "splat-ctl.h" -#include <sys/mutex.h> -#include <linux/file_compat.h> -#include <linux/version.h> - -typedef int (*splat_test_func_t)(struct file *, void *); - -typedef struct splat_test { - struct list_head test_list; - splat_user_t desc; - splat_test_func_t test; -} splat_test_t; - -typedef struct splat_subsystem { - struct list_head subsystem_list;/* List had to chain entries */ - splat_user_t desc; - spinlock_t test_lock; - struct list_head test_list; -} splat_subsystem_t; - -void splat_test_init(splat_subsystem_t *sub, const char *name, - const char *desc, unsigned int tid, splat_test_func_t func); -void splat_test_fini(splat_subsystem_t *sub, unsigned int tid); - -#define SPLAT_INFO_BUFFER_SIZE 65536 -#define SPLAT_INFO_BUFFER_REDZONE 256 - -typedef struct splat_info { - kmutex_t info_lock; - int info_size; - char *info_buffer; - char *info_head; /* Internal kernel use only */ -} splat_info_t; - -#define sym2str(sym) (char *)(#sym) - -#define splat_print(file, format, args...) \ -({ splat_info_t *_info_ = (splat_info_t *)file->private_data; \ - int _rc_; \ - \ - ASSERT(_info_); \ - ASSERT(_info_->info_buffer); \ - \ - mutex_enter(&_info_->info_lock); \ - \ - /* Don't allow the kernel to start a write in the red zone */ \ - if ((int)(_info_->info_head - _info_->info_buffer) > \ - (SPLAT_INFO_BUFFER_SIZE - SPLAT_INFO_BUFFER_REDZONE)) { \ - _rc_ = -EOVERFLOW; \ - } else { \ - _rc_ = sprintf(_info_->info_head, format, args); \ - if (_rc_ >= 0) \ - _info_->info_head += _rc_; \ - } \ - \ - mutex_exit(&_info_->info_lock); \ - _rc_; \ -}) - -#define splat_vprint(file, test, format, args...) \ - splat_print(file, "%*s: " format, SPLAT_NAME_SIZE, test, args) - -#define splat_locked_test(lock, test) \ -({ \ - int _rc_; \ - spin_lock(lock); \ - _rc_ = (test) ? 1 : 0; \ - spin_unlock(lock); \ - _rc_; \ -}) - -splat_subsystem_t *splat_condvar_init(void); -splat_subsystem_t *splat_kmem_init(void); -splat_subsystem_t *splat_mutex_init(void); -splat_subsystem_t *splat_krng_init(void); -splat_subsystem_t *splat_rwlock_init(void); -splat_subsystem_t *splat_taskq_init(void); -splat_subsystem_t *splat_thread_init(void); -splat_subsystem_t *splat_time_init(void); -splat_subsystem_t *splat_vnode_init(void); -splat_subsystem_t *splat_kobj_init(void); -splat_subsystem_t *splat_atomic_init(void); -splat_subsystem_t *splat_list_init(void); -splat_subsystem_t *splat_generic_init(void); -splat_subsystem_t *splat_cred_init(void); -splat_subsystem_t *splat_zlib_init(void); -splat_subsystem_t *splat_linux_init(void); - -void splat_condvar_fini(splat_subsystem_t *); -void splat_kmem_fini(splat_subsystem_t *); -void splat_mutex_fini(splat_subsystem_t *); -void splat_krng_fini(splat_subsystem_t *); -void splat_rwlock_fini(splat_subsystem_t *); -void splat_taskq_fini(splat_subsystem_t *); -void splat_thread_fini(splat_subsystem_t *); -void splat_time_fini(splat_subsystem_t *); -void splat_vnode_fini(splat_subsystem_t *); -void splat_kobj_fini(splat_subsystem_t *); -void splat_atomic_fini(splat_subsystem_t *); -void splat_list_fini(splat_subsystem_t *); -void splat_generic_fini(splat_subsystem_t *); -void splat_cred_fini(splat_subsystem_t *); -void splat_zlib_fini(splat_subsystem_t *); -void splat_linux_fini(splat_subsystem_t *); - -int splat_condvar_id(void); -int splat_kmem_id(void); -int splat_mutex_id(void); -int splat_krng_id(void); -int splat_rwlock_id(void); -int splat_taskq_id(void); -int splat_thread_id(void); -int splat_time_id(void); -int splat_vnode_id(void); -int splat_kobj_id(void); -int splat_atomic_id(void); -int splat_list_id(void); -int splat_generic_id(void); -int splat_cred_id(void); -int splat_zlib_id(void); -int splat_linux_id(void); - -#endif /* _SPLAT_INTERNAL_H */ diff --git a/module/splat/splat-kmem.c b/module/splat/splat-kmem.c deleted file mode 100644 index 282f42d77..000000000 --- a/module/splat/splat-kmem.c +++ /dev/null @@ -1,1410 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Kmem Tests. - */ - -#include <sys/kmem.h> -#include <sys/kmem_cache.h> -#include <sys/vmem.h> -#include <sys/random.h> -#include <sys/thread.h> -#include <sys/vmsystm.h> -#include "splat-internal.h" - -#define SPLAT_KMEM_NAME "kmem" -#define SPLAT_KMEM_DESC "Kernel Malloc/Slab Tests" - -#define SPLAT_KMEM_TEST1_ID 0x0101 -#define SPLAT_KMEM_TEST1_NAME "kmem_alloc" -#define SPLAT_KMEM_TEST1_DESC "Memory allocation test (kmem_alloc)" - -#define SPLAT_KMEM_TEST2_ID 0x0102 -#define SPLAT_KMEM_TEST2_NAME "kmem_zalloc" -#define SPLAT_KMEM_TEST2_DESC "Memory allocation test (kmem_zalloc)" - -#define SPLAT_KMEM_TEST3_ID 0x0103 -#define SPLAT_KMEM_TEST3_NAME "vmem_alloc" -#define SPLAT_KMEM_TEST3_DESC "Memory allocation test (vmem_alloc)" - -#define SPLAT_KMEM_TEST4_ID 0x0104 -#define SPLAT_KMEM_TEST4_NAME "vmem_zalloc" -#define SPLAT_KMEM_TEST4_DESC "Memory allocation test (vmem_zalloc)" - -#define SPLAT_KMEM_TEST5_ID 0x0105 -#define SPLAT_KMEM_TEST5_NAME "slab_small" -#define SPLAT_KMEM_TEST5_DESC "Slab ctor/dtor test (small)" - -#define SPLAT_KMEM_TEST6_ID 0x0106 -#define SPLAT_KMEM_TEST6_NAME "slab_large" -#define SPLAT_KMEM_TEST6_DESC "Slab ctor/dtor test (large)" - -#define SPLAT_KMEM_TEST7_ID 0x0107 -#define SPLAT_KMEM_TEST7_NAME "slab_align" -#define SPLAT_KMEM_TEST7_DESC "Slab alignment test" - -#define SPLAT_KMEM_TEST8_ID 0x0108 -#define SPLAT_KMEM_TEST8_NAME "slab_reap" -#define SPLAT_KMEM_TEST8_DESC "Slab reaping test" - -#define SPLAT_KMEM_TEST9_ID 0x0109 -#define SPLAT_KMEM_TEST9_NAME "slab_age" -#define SPLAT_KMEM_TEST9_DESC "Slab aging test" - -#define SPLAT_KMEM_TEST10_ID 0x010a -#define SPLAT_KMEM_TEST10_NAME "slab_lock" -#define SPLAT_KMEM_TEST10_DESC "Slab locking test" - -#if 0 -#define SPLAT_KMEM_TEST11_ID 0x010b -#define SPLAT_KMEM_TEST11_NAME "slab_overcommit" -#define SPLAT_KMEM_TEST11_DESC "Slab memory overcommit test" -#endif - -#define SPLAT_KMEM_TEST13_ID 0x010d -#define SPLAT_KMEM_TEST13_NAME "slab_reclaim" -#define SPLAT_KMEM_TEST13_DESC "Slab direct memory reclaim test" - -#define SPLAT_KMEM_ALLOC_COUNT 10 -#define SPLAT_VMEM_ALLOC_COUNT 10 - - -static int -splat_kmem_test1(struct file *file, void *arg) -{ - void *ptr[SPLAT_KMEM_ALLOC_COUNT]; - int size = PAGE_SIZE; - int i, count, rc = 0; - - while ((!rc) && (size <= spl_kmem_alloc_warn)) { - count = 0; - - for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { - ptr[i] = kmem_alloc(size, KM_SLEEP); - if (ptr[i]) - count++; - } - - for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) - if (ptr[i]) - kmem_free(ptr[i], size); - - splat_vprint(file, SPLAT_KMEM_TEST1_NAME, - "%d byte allocations, %d/%d successful\n", - size, count, SPLAT_KMEM_ALLOC_COUNT); - if (count != SPLAT_KMEM_ALLOC_COUNT) - rc = -ENOMEM; - - size *= 2; - } - - return rc; -} - -static int -splat_kmem_test2(struct file *file, void *arg) -{ - void *ptr[SPLAT_KMEM_ALLOC_COUNT]; - int size = PAGE_SIZE; - int i, j, count, rc = 0; - - while ((!rc) && (size <= spl_kmem_alloc_warn)) { - count = 0; - - for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { - ptr[i] = kmem_zalloc(size, KM_SLEEP); - if (ptr[i]) - count++; - } - - /* Ensure buffer has been zero filled */ - for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { - for (j = 0; j < size; j++) { - if (((char *)ptr[i])[j] != '\0') { - splat_vprint(file,SPLAT_KMEM_TEST2_NAME, - "%d-byte allocation was " - "not zeroed\n", size); - rc = -EFAULT; - } - } - } - - for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) - if (ptr[i]) - kmem_free(ptr[i], size); - - splat_vprint(file, SPLAT_KMEM_TEST2_NAME, - "%d byte allocations, %d/%d successful\n", - size, count, SPLAT_KMEM_ALLOC_COUNT); - if (count != SPLAT_KMEM_ALLOC_COUNT) - rc = -ENOMEM; - - size *= 2; - } - - return rc; -} - -static int -splat_kmem_test3(struct file *file, void *arg) -{ - void *ptr[SPLAT_VMEM_ALLOC_COUNT]; - int size = PAGE_SIZE; - int i, count, rc = 0; - - /* - * Test up to 4x the maximum kmem_alloc() size to ensure both - * the kmem_alloc() and vmem_alloc() call paths are used. - */ - while ((!rc) && (size <= (4 * spl_kmem_alloc_max))) { - count = 0; - - for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { - ptr[i] = vmem_alloc(size, KM_SLEEP); - if (ptr[i]) - count++; - } - - for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) - if (ptr[i]) - vmem_free(ptr[i], size); - - splat_vprint(file, SPLAT_KMEM_TEST3_NAME, - "%d byte allocations, %d/%d successful\n", - size, count, SPLAT_VMEM_ALLOC_COUNT); - if (count != SPLAT_VMEM_ALLOC_COUNT) - rc = -ENOMEM; - - size *= 2; - } - - return rc; -} - -static int -splat_kmem_test4(struct file *file, void *arg) -{ - void *ptr[SPLAT_VMEM_ALLOC_COUNT]; - int size = PAGE_SIZE; - int i, j, count, rc = 0; - - /* - * Test up to 4x the maximum kmem_zalloc() size to ensure both - * the kmem_zalloc() and vmem_zalloc() call paths are used. - */ - while ((!rc) && (size <= (4 * spl_kmem_alloc_max))) { - count = 0; - - for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { - ptr[i] = vmem_zalloc(size, KM_SLEEP); - if (ptr[i]) - count++; - } - - /* Ensure buffer has been zero filled */ - for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { - for (j = 0; j < size; j++) { - if (((char *)ptr[i])[j] != '\0') { - splat_vprint(file, SPLAT_KMEM_TEST4_NAME, - "%d-byte allocation was " - "not zeroed\n", size); - rc = -EFAULT; - } - } - } - - for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) - if (ptr[i]) - vmem_free(ptr[i], size); - - splat_vprint(file, SPLAT_KMEM_TEST4_NAME, - "%d byte allocations, %d/%d successful\n", - size, count, SPLAT_VMEM_ALLOC_COUNT); - if (count != SPLAT_VMEM_ALLOC_COUNT) - rc = -ENOMEM; - - size *= 2; - } - - return rc; -} - -#define SPLAT_KMEM_TEST_MAGIC 0x004488CCUL -#define SPLAT_KMEM_CACHE_NAME "kmem_test" -#define SPLAT_KMEM_OBJ_COUNT 1024 -#define SPLAT_KMEM_OBJ_RECLAIM 32 /* objects */ -#define SPLAT_KMEM_THREADS 32 - -#define KCP_FLAG_READY 0x01 - -typedef struct kmem_cache_data { - unsigned long kcd_magic; - struct list_head kcd_node; - int kcd_flag; - char kcd_buf[0]; -} kmem_cache_data_t; - -typedef struct kmem_cache_thread { - spinlock_t kct_lock; - int kct_id; - struct list_head kct_list; -} kmem_cache_thread_t; - -typedef struct kmem_cache_priv { - unsigned long kcp_magic; - struct file *kcp_file; - kmem_cache_t *kcp_cache; - spinlock_t kcp_lock; - spl_wait_queue_head_t kcp_ctl_waitq; - spl_wait_queue_head_t kcp_thr_waitq; - int kcp_flags; - int kcp_kct_count; - kmem_cache_thread_t *kcp_kct[SPLAT_KMEM_THREADS]; - int kcp_size; - int kcp_align; - int kcp_count; - int kcp_alloc; - int kcp_rc; -} kmem_cache_priv_t; - -static kmem_cache_priv_t * -splat_kmem_cache_test_kcp_alloc(struct file *file, char *name, - int size, int align, int alloc) -{ - kmem_cache_priv_t *kcp; - - kcp = kmem_zalloc(sizeof(kmem_cache_priv_t), KM_SLEEP); - if (!kcp) - return NULL; - - kcp->kcp_magic = SPLAT_KMEM_TEST_MAGIC; - kcp->kcp_file = file; - kcp->kcp_cache = NULL; - spin_lock_init(&kcp->kcp_lock); - init_waitqueue_head(&kcp->kcp_ctl_waitq); - init_waitqueue_head(&kcp->kcp_thr_waitq); - kcp->kcp_flags = 0; - kcp->kcp_kct_count = -1; - kcp->kcp_size = size; - kcp->kcp_align = align; - kcp->kcp_count = 0; - kcp->kcp_alloc = alloc; - kcp->kcp_rc = 0; - - return kcp; -} - -static void -splat_kmem_cache_test_kcp_free(kmem_cache_priv_t *kcp) -{ - kmem_free(kcp, sizeof(kmem_cache_priv_t)); -} - -static kmem_cache_thread_t * -splat_kmem_cache_test_kct_alloc(kmem_cache_priv_t *kcp, int id) -{ - kmem_cache_thread_t *kct; - - ASSERT3S(id, <, SPLAT_KMEM_THREADS); - ASSERT(kcp->kcp_kct[id] == NULL); - - kct = kmem_zalloc(sizeof(kmem_cache_thread_t), KM_SLEEP); - if (!kct) - return NULL; - - spin_lock_init(&kct->kct_lock); - kct->kct_id = id; - INIT_LIST_HEAD(&kct->kct_list); - - spin_lock(&kcp->kcp_lock); - kcp->kcp_kct[id] = kct; - spin_unlock(&kcp->kcp_lock); - - return kct; -} - -static void -splat_kmem_cache_test_kct_free(kmem_cache_priv_t *kcp, - kmem_cache_thread_t *kct) -{ - spin_lock(&kcp->kcp_lock); - kcp->kcp_kct[kct->kct_id] = NULL; - spin_unlock(&kcp->kcp_lock); - - kmem_free(kct, sizeof(kmem_cache_thread_t)); -} - -static void -splat_kmem_cache_test_kcd_free(kmem_cache_priv_t *kcp, - kmem_cache_thread_t *kct) -{ - kmem_cache_data_t *kcd; - - spin_lock(&kct->kct_lock); - while (!list_empty(&kct->kct_list)) { - kcd = list_entry(kct->kct_list.next, - kmem_cache_data_t, kcd_node); - list_del(&kcd->kcd_node); - spin_unlock(&kct->kct_lock); - - kmem_cache_free(kcp->kcp_cache, kcd); - - spin_lock(&kct->kct_lock); - } - spin_unlock(&kct->kct_lock); -} - -static int -splat_kmem_cache_test_kcd_alloc(kmem_cache_priv_t *kcp, - kmem_cache_thread_t *kct, int count) -{ - kmem_cache_data_t *kcd; - int i; - - for (i = 0; i < count; i++) { - kcd = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP); - if (kcd == NULL) { - splat_kmem_cache_test_kcd_free(kcp, kct); - return -ENOMEM; - } - - spin_lock(&kct->kct_lock); - list_add_tail(&kcd->kcd_node, &kct->kct_list); - spin_unlock(&kct->kct_lock); - } - - return 0; -} - -static void -splat_kmem_cache_test_debug(struct file *file, char *name, - kmem_cache_priv_t *kcp) -{ - int j; - - splat_vprint(file, name, "%s cache objects %d", - kcp->kcp_cache->skc_name, kcp->kcp_count); - - if (kcp->kcp_cache->skc_flags & (KMC_KMEM | KMC_VMEM)) { - splat_vprint(file, name, ", slabs %u/%u objs %u/%u", - (unsigned)kcp->kcp_cache->skc_slab_alloc, - (unsigned)kcp->kcp_cache->skc_slab_total, - (unsigned)kcp->kcp_cache->skc_obj_alloc, - (unsigned)kcp->kcp_cache->skc_obj_total); - - if (!(kcp->kcp_cache->skc_flags & KMC_NOMAGAZINE)) { - splat_vprint(file, name, "%s", "mags"); - - for_each_online_cpu(j) - splat_print(file, "%u/%u ", - kcp->kcp_cache->skc_mag[j]->skm_avail, - kcp->kcp_cache->skc_mag[j]->skm_size); - } - } - - splat_print(file, "%s\n", ""); -} - -static int -splat_kmem_cache_test_constructor(void *ptr, void *priv, int flags) -{ - kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; - kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr; - - if (kcd && kcp) { - kcd->kcd_magic = kcp->kcp_magic; - INIT_LIST_HEAD(&kcd->kcd_node); - kcd->kcd_flag = 1; - memset(kcd->kcd_buf, 0xaa, kcp->kcp_size - (sizeof *kcd)); - kcp->kcp_count++; - } - - return 0; -} - -static void -splat_kmem_cache_test_destructor(void *ptr, void *priv) -{ - kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; - kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr; - - if (kcd && kcp) { - kcd->kcd_magic = 0; - kcd->kcd_flag = 0; - memset(kcd->kcd_buf, 0xbb, kcp->kcp_size - (sizeof *kcd)); - kcp->kcp_count--; - } - - return; -} - -/* - * Generic reclaim function which assumes that all objects may - * be reclaimed at any time. We free a small percentage of the - * objects linked off the kcp or kct[] every time we are called. - */ -static void -splat_kmem_cache_test_reclaim(void *priv) -{ - kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; - kmem_cache_thread_t *kct; - kmem_cache_data_t *kcd; - LIST_HEAD(reclaim); - int i, count; - - ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC); - - /* For each kct thread reclaim some objects */ - spin_lock(&kcp->kcp_lock); - for (i = 0; i < SPLAT_KMEM_THREADS; i++) { - kct = kcp->kcp_kct[i]; - if (!kct) - continue; - - spin_unlock(&kcp->kcp_lock); - spin_lock(&kct->kct_lock); - - count = SPLAT_KMEM_OBJ_RECLAIM; - while (count > 0 && !list_empty(&kct->kct_list)) { - kcd = list_entry(kct->kct_list.next, - kmem_cache_data_t, kcd_node); - list_del(&kcd->kcd_node); - list_add(&kcd->kcd_node, &reclaim); - count--; - } - - spin_unlock(&kct->kct_lock); - spin_lock(&kcp->kcp_lock); - } - spin_unlock(&kcp->kcp_lock); - - /* Freed outside the spin lock */ - while (!list_empty(&reclaim)) { - kcd = list_entry(reclaim.next, kmem_cache_data_t, kcd_node); - list_del(&kcd->kcd_node); - kmem_cache_free(kcp->kcp_cache, kcd); - } - - return; -} - -static int -splat_kmem_cache_test_threads(kmem_cache_priv_t *kcp, int threads) -{ - int rc; - - spin_lock(&kcp->kcp_lock); - rc = (kcp->kcp_kct_count == threads); - spin_unlock(&kcp->kcp_lock); - - return rc; -} - -static int -splat_kmem_cache_test_flags(kmem_cache_priv_t *kcp, int flags) -{ - int rc; - - spin_lock(&kcp->kcp_lock); - rc = (kcp->kcp_flags & flags); - spin_unlock(&kcp->kcp_lock); - - return rc; -} - -static void -splat_kmem_cache_test_thread(void *arg) -{ - kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg; - kmem_cache_thread_t *kct; - int rc = 0, id; - - ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC); - - /* Assign thread ids */ - spin_lock(&kcp->kcp_lock); - if (kcp->kcp_kct_count == -1) - kcp->kcp_kct_count = 0; - - id = kcp->kcp_kct_count; - kcp->kcp_kct_count++; - spin_unlock(&kcp->kcp_lock); - - kct = splat_kmem_cache_test_kct_alloc(kcp, id); - if (!kct) { - rc = -ENOMEM; - goto out; - } - - /* Wait for all threads to have started and report they are ready */ - if (kcp->kcp_kct_count == SPLAT_KMEM_THREADS) - wake_up(&kcp->kcp_ctl_waitq); - - wait_event(kcp->kcp_thr_waitq, - splat_kmem_cache_test_flags(kcp, KCP_FLAG_READY)); - - /* Create and destroy objects */ - rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, kcp->kcp_alloc); - splat_kmem_cache_test_kcd_free(kcp, kct); -out: - if (kct) - splat_kmem_cache_test_kct_free(kcp, kct); - - spin_lock(&kcp->kcp_lock); - if (!kcp->kcp_rc) - kcp->kcp_rc = rc; - - if ((--kcp->kcp_kct_count) == 0) - wake_up(&kcp->kcp_ctl_waitq); - - spin_unlock(&kcp->kcp_lock); - - thread_exit(); -} - -static int -splat_kmem_cache_test(struct file *file, void *arg, char *name, - int size, int align, int flags) -{ - kmem_cache_priv_t *kcp = NULL; - kmem_cache_data_t **kcd = NULL; - int i, rc = 0, objs = 0; - - /* Limit size for low memory machines (1/128 of memory) */ - size = MIN(size, (physmem * PAGE_SIZE) >> 7); - - splat_vprint(file, name, - "Testing size=%d, align=%d, flags=0x%04x\n", - size, align, flags); - - kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, align, 0); - if (!kcp) { - splat_vprint(file, name, "Unable to create '%s'\n", "kcp"); - return (-ENOMEM); - } - - kcp->kcp_cache = kmem_cache_create(SPLAT_KMEM_CACHE_NAME, - kcp->kcp_size, kcp->kcp_align, - splat_kmem_cache_test_constructor, - splat_kmem_cache_test_destructor, - NULL, kcp, NULL, flags); - if (kcp->kcp_cache == NULL) { - splat_vprint(file, name, "Unable to create " - "name='%s', size=%d, align=%d, flags=0x%x\n", - SPLAT_KMEM_CACHE_NAME, size, align, flags); - rc = -ENOMEM; - goto out_free; - } - - /* - * Allocate several slabs worth of objects to verify functionality. - * However, on 32-bit systems with limited address space constrain - * it to a single slab for the purposes of this test. - */ -#ifdef _LP64 - objs = kcp->kcp_cache->skc_slab_objs * 4; -#else - objs = 1; -#endif - kcd = kmem_zalloc(sizeof (kmem_cache_data_t *) * objs, KM_SLEEP); - if (kcd == NULL) { - splat_vprint(file, name, "Unable to allocate pointers " - "for %d objects\n", objs); - rc = -ENOMEM; - goto out_free; - } - - for (i = 0; i < objs; i++) { - kcd[i] = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP); - if (kcd[i] == NULL) { - splat_vprint(file, name, "Unable to allocate " - "from '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -EINVAL; - goto out_free; - } - - if (!kcd[i]->kcd_flag) { - splat_vprint(file, name, "Failed to run constructor " - "for '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -EINVAL; - goto out_free; - } - - if (kcd[i]->kcd_magic != kcp->kcp_magic) { - splat_vprint(file, name, - "Failed to pass private data to constructor " - "for '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -EINVAL; - goto out_free; - } - } - - for (i = 0; i < objs; i++) { - kmem_cache_free(kcp->kcp_cache, kcd[i]); - - /* Destructors are run for every kmem_cache_free() */ - if (kcd[i]->kcd_flag) { - splat_vprint(file, name, - "Failed to run destructor for '%s'\n", - SPLAT_KMEM_CACHE_NAME); - rc = -EINVAL; - goto out_free; - } - } - - if (kcp->kcp_count) { - splat_vprint(file, name, - "Failed to run destructor on all slab objects for '%s'\n", - SPLAT_KMEM_CACHE_NAME); - rc = -EINVAL; - } - - kmem_free(kcd, sizeof (kmem_cache_data_t *) * objs); - kmem_cache_destroy(kcp->kcp_cache); - - splat_kmem_cache_test_kcp_free(kcp); - splat_vprint(file, name, - "Success ran alloc'd/free'd %d objects of size %d\n", - objs, size); - - return (rc); - -out_free: - if (kcd) { - for (i = 0; i < objs; i++) { - if (kcd[i] != NULL) - kmem_cache_free(kcp->kcp_cache, kcd[i]); - } - - kmem_free(kcd, sizeof (kmem_cache_data_t *) * objs); - } - - if (kcp->kcp_cache) - kmem_cache_destroy(kcp->kcp_cache); - - splat_kmem_cache_test_kcp_free(kcp); - - return (rc); -} - -static int -splat_kmem_cache_thread_test(struct file *file, void *arg, char *name, - int size, int alloc, int max_time) -{ - kmem_cache_priv_t *kcp; - kthread_t *thr; - struct timespec start, stop, delta; - char cache_name[32]; - int i, rc = 0; - - kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, 0, alloc); - if (!kcp) { - splat_vprint(file, name, "Unable to create '%s'\n", "kcp"); - return -ENOMEM; - } - - (void)snprintf(cache_name, 32, "%s-%d-%d", - SPLAT_KMEM_CACHE_NAME, size, alloc); - kcp->kcp_cache = - kmem_cache_create(cache_name, kcp->kcp_size, 0, - splat_kmem_cache_test_constructor, - splat_kmem_cache_test_destructor, - splat_kmem_cache_test_reclaim, - kcp, NULL, 0); - if (!kcp->kcp_cache) { - splat_vprint(file, name, "Unable to create '%s'\n", cache_name); - rc = -ENOMEM; - goto out_kcp; - } - - getnstimeofday(&start); - - for (i = 0; i < SPLAT_KMEM_THREADS; i++) { - thr = thread_create(NULL, 0, - splat_kmem_cache_test_thread, - kcp, 0, &p0, TS_RUN, defclsyspri); - if (thr == NULL) { - rc = -ESRCH; - goto out_cache; - } - } - - /* Sleep until all threads have started, then set the ready - * flag and wake them all up for maximum concurrency. */ - wait_event(kcp->kcp_ctl_waitq, - splat_kmem_cache_test_threads(kcp, SPLAT_KMEM_THREADS)); - - spin_lock(&kcp->kcp_lock); - kcp->kcp_flags |= KCP_FLAG_READY; - spin_unlock(&kcp->kcp_lock); - wake_up_all(&kcp->kcp_thr_waitq); - - /* Sleep until all thread have finished */ - wait_event(kcp->kcp_ctl_waitq, splat_kmem_cache_test_threads(kcp, 0)); - - getnstimeofday(&stop); - delta = timespec_sub(stop, start); - - splat_vprint(file, name, - "%-22s %2ld.%09ld\t" - "%lu/%lu/%lu\t%lu/%lu/%lu\n", - kcp->kcp_cache->skc_name, - delta.tv_sec, delta.tv_nsec, - (unsigned long)kcp->kcp_cache->skc_slab_total, - (unsigned long)kcp->kcp_cache->skc_slab_max, - (unsigned long)(kcp->kcp_alloc * - SPLAT_KMEM_THREADS / - SPL_KMEM_CACHE_OBJ_PER_SLAB), - (unsigned long)kcp->kcp_cache->skc_obj_total, - (unsigned long)kcp->kcp_cache->skc_obj_max, - (unsigned long)(kcp->kcp_alloc * - SPLAT_KMEM_THREADS)); - - if (delta.tv_sec >= max_time) - rc = -ETIME; - - if (!rc && kcp->kcp_rc) - rc = kcp->kcp_rc; - -out_cache: - kmem_cache_destroy(kcp->kcp_cache); -out_kcp: - splat_kmem_cache_test_kcp_free(kcp); - return rc; -} - -/* Validate small object cache behavior for dynamic/kmem/vmem caches */ -static int -splat_kmem_test5(struct file *file, void *arg) -{ - char *name = SPLAT_KMEM_TEST5_NAME; - int i, rc = 0; - - /* Randomly pick small object sizes and alignments. */ - for (i = 0; i < 100; i++) { - int size, align, flags = 0; - uint32_t rnd; - - /* Evenly distribute tests over all value cache types */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - switch (rnd & 0x03) { - default: - case 0x00: - flags = 0; - break; - case 0x01: - flags = KMC_KMEM; - break; - case 0x02: - flags = KMC_VMEM; - break; - case 0x03: - flags = KMC_SLAB; - break; - } - - /* The following flags are set with a 1/10 chance */ - flags |= ((((rnd >> 8) % 10) == 0) ? KMC_OFFSLAB : 0); - flags |= ((((rnd >> 16) % 10) == 0) ? KMC_NOEMERGENCY : 0); - - /* 32b - PAGE_SIZE */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - size = MAX(rnd % (PAGE_SIZE + 1), 32); - - /* 2^N where (3 <= N <= PAGE_SHIFT) */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - align = (1 << MAX(3, rnd % (PAGE_SHIFT + 1))); - - rc = splat_kmem_cache_test(file, arg, name, size, align, flags); - if (rc) - return (rc); - } - - return (rc); -} - -/* - * Validate large object cache behavior for dynamic/kmem/vmem caches - */ -static int -splat_kmem_test6(struct file *file, void *arg) -{ - char *name = SPLAT_KMEM_TEST6_NAME; - int i, max_size, rc = 0; - - /* Randomly pick large object sizes and alignments. */ - for (i = 0; i < 100; i++) { - int size, align, flags = 0; - uint32_t rnd; - - /* Evenly distribute tests over all value cache types */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - switch (rnd & 0x03) { - default: - case 0x00: - flags = 0; - max_size = (SPL_KMEM_CACHE_MAX_SIZE * 1024 * 1024) / 2; - break; - case 0x01: - flags = KMC_KMEM; - max_size = (SPL_MAX_ORDER_NR_PAGES - 2) * PAGE_SIZE; - break; - case 0x02: - flags = KMC_VMEM; - max_size = (SPL_KMEM_CACHE_MAX_SIZE * 1024 * 1024) / 2; - break; - case 0x03: - flags = KMC_SLAB; - max_size = SPL_MAX_KMEM_ORDER_NR_PAGES * PAGE_SIZE; - break; - } - - /* The following flags are set with a 1/10 chance */ - flags |= ((((rnd >> 8) % 10) == 0) ? KMC_OFFSLAB : 0); - flags |= ((((rnd >> 16) % 10) == 0) ? KMC_NOEMERGENCY : 0); - - /* PAGE_SIZE - max_size */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - size = MAX(rnd % (max_size + 1), PAGE_SIZE), - - /* 2^N where (3 <= N <= PAGE_SHIFT) */ - get_random_bytes((void *)&rnd, sizeof (uint32_t)); - align = (1 << MAX(3, rnd % (PAGE_SHIFT + 1))); - - rc = splat_kmem_cache_test(file, arg, name, size, align, flags); - if (rc) - return (rc); - } - - return (rc); -} - -/* - * Validate object alignment cache behavior for caches - */ -static int -splat_kmem_test7(struct file *file, void *arg) -{ - char *name = SPLAT_KMEM_TEST7_NAME; - int max_size = (SPL_KMEM_CACHE_MAX_SIZE * 1024 * 1024) / 2; - int i, rc; - - for (i = SPL_KMEM_CACHE_ALIGN; i <= PAGE_SIZE; i *= 2) { - uint32_t size; - - get_random_bytes((void *)&size, sizeof (uint32_t)); - size = MAX(size % (max_size + 1), 32); - - rc = splat_kmem_cache_test(file, arg, name, size, i, 0); - if (rc) - return rc; - - rc = splat_kmem_cache_test(file, arg, name, size, i, - KMC_OFFSLAB); - if (rc) - return rc; - } - - return rc; -} - -/* - * Validate kmem_cache_reap() by requesting the slab cache free any objects - * it can. For a few reasons this may not immediately result in more free - * memory even if objects are freed. First off, due to fragmentation we - * may not be able to reclaim any slabs. Secondly, even if we do we fully - * clear some slabs we will not want to immediately reclaim all of them - * because we may contend with cache allocations and thrash. What we want - * to see is the slab size decrease more gradually as it becomes clear they - * will not be needed. This should be achievable in less than a minute. - * If it takes longer than this something has gone wrong. - */ -static int -splat_kmem_test8(struct file *file, void *arg) -{ - kmem_cache_priv_t *kcp; - kmem_cache_thread_t *kct; - unsigned int spl_kmem_cache_expire_old; - int i, rc = 0; - - /* Enable cache aging just for this test if it is disabled */ - spl_kmem_cache_expire_old = spl_kmem_cache_expire; - spl_kmem_cache_expire = KMC_EXPIRE_AGE; - - kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST8_NAME, - 256, 0, 0); - if (!kcp) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Unable to create '%s'\n", "kcp"); - rc = -ENOMEM; - goto out; - } - - kcp->kcp_cache = - kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp->kcp_size, 0, - splat_kmem_cache_test_constructor, - splat_kmem_cache_test_destructor, - splat_kmem_cache_test_reclaim, - kcp, NULL, 0); - if (!kcp->kcp_cache) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -ENOMEM; - goto out_kcp; - } - - kct = splat_kmem_cache_test_kct_alloc(kcp, 0); - if (!kct) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Unable to create '%s'\n", "kct"); - rc = -ENOMEM; - goto out_cache; - } - - rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, SPLAT_KMEM_OBJ_COUNT); - if (rc) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "Unable to " - "allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME); - goto out_kct; - } - - /* Force reclaim every 1/10 a second for 60 seconds. */ - for (i = 0; i < 600; i++) { - kmem_cache_reap_now(kcp->kcp_cache); - splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST8_NAME, kcp); - - if (kcp->kcp_count == 0) - break; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 10); - } - - if (kcp->kcp_count == 0) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Successfully created %d objects " - "in cache %s and reclaimed them\n", - SPLAT_KMEM_OBJ_COUNT, SPLAT_KMEM_CACHE_NAME); - } else { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Failed to reclaim %u/%d objects from cache %s\n", - (unsigned)kcp->kcp_count, - SPLAT_KMEM_OBJ_COUNT, SPLAT_KMEM_CACHE_NAME); - rc = -ENOMEM; - } - - /* Cleanup our mess (for failure case of time expiring) */ - splat_kmem_cache_test_kcd_free(kcp, kct); -out_kct: - splat_kmem_cache_test_kct_free(kcp, kct); -out_cache: - kmem_cache_destroy(kcp->kcp_cache); -out_kcp: - splat_kmem_cache_test_kcp_free(kcp); -out: - spl_kmem_cache_expire = spl_kmem_cache_expire_old; - - return rc; -} - -/* Test cache aging, we have allocated a large number of objects thus - * creating a large number of slabs and then free'd them all. However, - * since there should be little memory pressure at the moment those - * slabs have not been freed. What we want to see is the slab size - * decrease gradually as it becomes clear they will not be be needed. - * This should be achievable in less than minute. If it takes longer - * than this something has gone wrong. - */ -static int -splat_kmem_test9(struct file *file, void *arg) -{ - kmem_cache_priv_t *kcp; - kmem_cache_thread_t *kct; - unsigned int spl_kmem_cache_expire_old; - int i, rc = 0, count = SPLAT_KMEM_OBJ_COUNT * 128; - - /* Enable cache aging just for this test if it is disabled */ - spl_kmem_cache_expire_old = spl_kmem_cache_expire; - spl_kmem_cache_expire = KMC_EXPIRE_AGE; - - kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST9_NAME, - 256, 0, 0); - if (!kcp) { - splat_vprint(file, SPLAT_KMEM_TEST9_NAME, - "Unable to create '%s'\n", "kcp"); - rc = -ENOMEM; - goto out; - } - - kcp->kcp_cache = - kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp->kcp_size, 0, - splat_kmem_cache_test_constructor, - splat_kmem_cache_test_destructor, - NULL, kcp, NULL, 0); - if (!kcp->kcp_cache) { - splat_vprint(file, SPLAT_KMEM_TEST9_NAME, - "Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -ENOMEM; - goto out_kcp; - } - - kct = splat_kmem_cache_test_kct_alloc(kcp, 0); - if (!kct) { - splat_vprint(file, SPLAT_KMEM_TEST8_NAME, - "Unable to create '%s'\n", "kct"); - rc = -ENOMEM; - goto out_cache; - } - - rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, count); - if (rc) { - splat_vprint(file, SPLAT_KMEM_TEST9_NAME, "Unable to " - "allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME); - goto out_kct; - } - - splat_kmem_cache_test_kcd_free(kcp, kct); - - for (i = 0; i < 60; i++) { - splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST9_NAME, kcp); - - if (kcp->kcp_count == 0) - break; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - } - - if (kcp->kcp_count == 0) { - splat_vprint(file, SPLAT_KMEM_TEST9_NAME, - "Successfully created %d objects " - "in cache %s and reclaimed them\n", - count, SPLAT_KMEM_CACHE_NAME); - } else { - splat_vprint(file, SPLAT_KMEM_TEST9_NAME, - "Failed to reclaim %u/%d objects from cache %s\n", - (unsigned)kcp->kcp_count, count, - SPLAT_KMEM_CACHE_NAME); - rc = -ENOMEM; - } - -out_kct: - splat_kmem_cache_test_kct_free(kcp, kct); -out_cache: - kmem_cache_destroy(kcp->kcp_cache); -out_kcp: - splat_kmem_cache_test_kcp_free(kcp); -out: - spl_kmem_cache_expire = spl_kmem_cache_expire_old; - - return rc; -} - -/* - * This test creates N threads with a shared kmem cache. They then all - * concurrently allocate and free from the cache to stress the locking and - * concurrent cache performance. If any one test takes longer than 5 - * seconds to complete it is treated as a failure and may indicate a - * performance regression. On my test system no one test takes more - * than 1 second to complete so a 5x slowdown likely a problem. - */ -static int -splat_kmem_test10(struct file *file, void *arg) -{ - uint64_t size, alloc, maxsize, limit, rc = 0; - -#if defined(CONFIG_64BIT) - maxsize = (1024 * 1024); -#else - maxsize = (128 * 1024); -#endif - - for (size = 32; size <= maxsize; size *= 2) { - - splat_vprint(file, SPLAT_KMEM_TEST10_NAME, "%-22s %s", "name", - "time (sec)\tslabs \tobjs \thash\n"); - splat_vprint(file, SPLAT_KMEM_TEST10_NAME, "%-22s %s", "", - " \ttot/max/calc\ttot/max/calc\n"); - - for (alloc = 1; alloc <= 1024; alloc *= 2) { - - /* Skip tests which exceed 1/2 of memory. */ - limit = MIN(physmem * PAGE_SIZE, - vmem_size(NULL, VMEM_ALLOC | VMEM_FREE)) / 2; - if (size * alloc * SPLAT_KMEM_THREADS > limit) - continue; - - rc = splat_kmem_cache_thread_test(file, arg, - SPLAT_KMEM_TEST10_NAME, size, alloc, 5); - if (rc) - break; - } - } - - return rc; -} - -#if 0 -/* - * This test creates N threads with a shared kmem cache which overcommits - * memory by 4x. This makes it impossible for the slab to satify the - * thread requirements without having its reclaim hook run which will - * free objects back for use. This behavior is triggered by the linum VM - * detecting a low memory condition on the node and invoking the shrinkers. - * This should allow all the threads to complete while avoiding deadlock - * and for the most part out of memory events. This is very tough on the - * system so it is possible the test app may get oom'ed. This particular - * test has proven troublesome on 32-bit archs with limited virtual - * address space so it only run on 64-bit systems. - */ -static int -splat_kmem_test11(struct file *file, void *arg) -{ - uint64_t size, alloc, rc; - - size = 8 * 1024; - alloc = ((4 * physmem * PAGE_SIZE) / size) / SPLAT_KMEM_THREADS; - - splat_vprint(file, SPLAT_KMEM_TEST11_NAME, "%-22s %s", "name", - "time (sec)\tslabs \tobjs \thash\n"); - splat_vprint(file, SPLAT_KMEM_TEST11_NAME, "%-22s %s", "", - " \ttot/max/calc\ttot/max/calc\n"); - - rc = splat_kmem_cache_thread_test(file, arg, - SPLAT_KMEM_TEST11_NAME, size, alloc, 60); - - return rc; -} -#endif - -typedef struct dummy_page { - struct list_head dp_list; - char dp_pad[PAGE_SIZE - sizeof(struct list_head)]; -} dummy_page_t; - -/* - * This test is designed to verify that direct reclaim is functioning as - * expected. We allocate a large number of objects thus creating a large - * number of slabs. We then apply memory pressure and expect that the - * direct reclaim path can easily recover those slabs. The registered - * reclaim function will free the objects and the slab shrinker will call - * it repeatedly until at least a single slab can be freed. - * - * Note it may not be possible to reclaim every last slab via direct reclaim - * without a failure because the shrinker_rwsem may be contended. For this - * reason, quickly reclaiming 3/4 of the slabs is considered a success. - * - * This should all be possible within 10 seconds. For reference, on a - * system with 2G of memory this test takes roughly 0.2 seconds to run. - * It may take longer on larger memory systems but should still easily - * complete in the alloted 10 seconds. - */ -static int -splat_kmem_test13(struct file *file, void *arg) -{ - kmem_cache_priv_t *kcp; - kmem_cache_thread_t *kct; - dummy_page_t *dp; - struct list_head list; - struct timespec start, stop, delta = { 0, 0 }; - int size, count, slabs, fails = 0; - int i, rc = 0, max_time = 10; - - size = 128 * 1024; - count = MIN(physmem * PAGE_SIZE, vmem_size(NULL, - VMEM_ALLOC | VMEM_FREE)) / 4 / size; - - kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST13_NAME, - size, 0, 0); - if (!kcp) { - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Unable to create '%s'\n", "kcp"); - rc = -ENOMEM; - goto out; - } - - kcp->kcp_cache = - kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp->kcp_size, 0, - splat_kmem_cache_test_constructor, - splat_kmem_cache_test_destructor, - splat_kmem_cache_test_reclaim, - kcp, NULL, 0); - if (!kcp->kcp_cache) { - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME); - rc = -ENOMEM; - goto out_kcp; - } - - kct = splat_kmem_cache_test_kct_alloc(kcp, 0); - if (!kct) { - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Unable to create '%s'\n", "kct"); - rc = -ENOMEM; - goto out_cache; - } - - rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, count); - if (rc) { - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, "Unable to " - "allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME); - goto out_kct; - } - - i = 0; - slabs = kcp->kcp_cache->skc_slab_total; - INIT_LIST_HEAD(&list); - getnstimeofday(&start); - - /* Apply memory pressure */ - while (kcp->kcp_cache->skc_slab_total > (slabs >> 2)) { - - if ((i % 10000) == 0) - splat_kmem_cache_test_debug( - file, SPLAT_KMEM_TEST13_NAME, kcp); - - getnstimeofday(&stop); - delta = timespec_sub(stop, start); - if (delta.tv_sec >= max_time) { - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Failed to reclaim 3/4 of cache in %ds, " - "%u/%u slabs remain\n", max_time, - (unsigned)kcp->kcp_cache->skc_slab_total, - slabs); - rc = -ETIME; - break; - } - - dp = (dummy_page_t *)__get_free_page(GFP_KERNEL); - if (!dp) { - fails++; - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Failed (%d) to allocate page with %u " - "slabs still in the cache\n", fails, - (unsigned)kcp->kcp_cache->skc_slab_total); - continue; - } - - list_add(&dp->dp_list, &list); - i++; - } - - if (rc == 0) - splat_vprint(file, SPLAT_KMEM_TEST13_NAME, - "Successfully created %u slabs and with %d alloc " - "failures reclaimed 3/4 of them in %d.%03ds\n", - slabs, fails, - (int)delta.tv_sec, (int)delta.tv_nsec / 1000000); - - /* Release memory pressure pages */ - while (!list_empty(&list)) { - dp = list_entry(list.next, dummy_page_t, dp_list); - list_del_init(&dp->dp_list); - free_page((unsigned long)dp); - } - - /* Release remaining kmem cache objects */ - splat_kmem_cache_test_kcd_free(kcp, kct); -out_kct: - splat_kmem_cache_test_kct_free(kcp, kct); -out_cache: - kmem_cache_destroy(kcp->kcp_cache); -out_kcp: - splat_kmem_cache_test_kcp_free(kcp); -out: - return rc; -} - -splat_subsystem_t * -splat_kmem_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_KMEM_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_KMEM_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_KMEM; - - splat_test_init(sub, SPLAT_KMEM_TEST1_NAME, SPLAT_KMEM_TEST1_DESC, - SPLAT_KMEM_TEST1_ID, splat_kmem_test1); - splat_test_init(sub, SPLAT_KMEM_TEST2_NAME, SPLAT_KMEM_TEST2_DESC, - SPLAT_KMEM_TEST2_ID, splat_kmem_test2); - splat_test_init(sub, SPLAT_KMEM_TEST3_NAME, SPLAT_KMEM_TEST3_DESC, - SPLAT_KMEM_TEST3_ID, splat_kmem_test3); - splat_test_init(sub, SPLAT_KMEM_TEST4_NAME, SPLAT_KMEM_TEST4_DESC, - SPLAT_KMEM_TEST4_ID, splat_kmem_test4); - splat_test_init(sub, SPLAT_KMEM_TEST5_NAME, SPLAT_KMEM_TEST5_DESC, - SPLAT_KMEM_TEST5_ID, splat_kmem_test5); - splat_test_init(sub, SPLAT_KMEM_TEST6_NAME, SPLAT_KMEM_TEST6_DESC, - SPLAT_KMEM_TEST6_ID, splat_kmem_test6); - splat_test_init(sub, SPLAT_KMEM_TEST7_NAME, SPLAT_KMEM_TEST7_DESC, - SPLAT_KMEM_TEST7_ID, splat_kmem_test7); - splat_test_init(sub, SPLAT_KMEM_TEST8_NAME, SPLAT_KMEM_TEST8_DESC, - SPLAT_KMEM_TEST8_ID, splat_kmem_test8); - splat_test_init(sub, SPLAT_KMEM_TEST9_NAME, SPLAT_KMEM_TEST9_DESC, - SPLAT_KMEM_TEST9_ID, splat_kmem_test9); - splat_test_init(sub, SPLAT_KMEM_TEST10_NAME, SPLAT_KMEM_TEST10_DESC, - SPLAT_KMEM_TEST10_ID, splat_kmem_test10); -#if 0 - splat_test_init(sub, SPLAT_KMEM_TEST11_NAME, SPLAT_KMEM_TEST11_DESC, - SPLAT_KMEM_TEST11_ID, splat_kmem_test11); -#endif - splat_test_init(sub, SPLAT_KMEM_TEST13_NAME, SPLAT_KMEM_TEST13_DESC, - SPLAT_KMEM_TEST13_ID, splat_kmem_test13); - - return sub; -} - -void -splat_kmem_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_KMEM_TEST13_ID); -#if 0 - splat_test_fini(sub, SPLAT_KMEM_TEST11_ID); -#endif - splat_test_fini(sub, SPLAT_KMEM_TEST10_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST9_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST8_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST7_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST6_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST5_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST4_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST3_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST2_ID); - splat_test_fini(sub, SPLAT_KMEM_TEST1_ID); - - kfree(sub); -} - -int -splat_kmem_id(void) { - return SPLAT_SUBSYSTEM_KMEM; -} diff --git a/module/splat/splat-kobj.c b/module/splat/splat-kobj.c deleted file mode 100644 index bd44de395..000000000 --- a/module/splat/splat-kobj.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Kobj Tests. - */ - -#include <sys/kobj.h> -#include "splat-internal.h" - -#define SPLAT_KOBJ_NAME "kobj" -#define SPLAT_KOBJ_DESC "Kernel Kobj Tests" - -#define SPLAT_KOBJ_TEST1_ID 0x0a01 -#define SPLAT_KOBJ_TEST1_NAME "open" -#define SPLAT_KOBJ_TEST1_DESC "Kobj Open/Close Test" - -#define SPLAT_KOBJ_TEST2_ID 0x0a02 -#define SPLAT_KOBJ_TEST2_NAME "size/read" -#define SPLAT_KOBJ_TEST2_DESC "Kobj Size/Read Test" - -#define SPLAT_KOBJ_TEST_FILE "/etc/fstab" - -static int -splat_kobj_test1(struct file *file, void *arg) -{ - struct _buf *f; - - f = kobj_open_file(SPLAT_KOBJ_TEST_FILE); - if (f == (struct _buf *)-1) { - splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Failed to open " - "test file: %s\n", SPLAT_KOBJ_TEST_FILE); - return -ENOENT; - } - - kobj_close_file(f); - splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Successfully opened and " - "closed test file: %s\n", SPLAT_KOBJ_TEST_FILE); - - return 0; -} /* splat_kobj_test1() */ - -static int -splat_kobj_test2(struct file *file, void *arg) -{ - struct _buf *f; - char *buf; - uint64_t size; - int rc; - - f = kobj_open_file(SPLAT_KOBJ_TEST_FILE); - if (f == (struct _buf *)-1) { - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to open " - "test file: %s\n", SPLAT_KOBJ_TEST_FILE); - return -ENOENT; - } - - rc = kobj_get_filesize(f, &size); - if (rc) { - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed stat of " - "test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc); - goto out; - } - - buf = kmalloc(size + 1, GFP_KERNEL); - if (!buf) { - rc = -ENOMEM; - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to alloc " - "%lld bytes for tmp buffer (%d)\n", - (long long)size, rc); - goto out; - } - - memset(buf, 0, size + 1); - rc = kobj_read_file(f, buf, size, 0); - if (rc < 0) { - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed read of " - "test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc); - goto out2; - } - - /* Validate we read as many bytes as expected based on the stat. This - * isn't a perfect test since we didn't create the file however it is - * pretty unlikely there are garbage characters in your /etc/fstab */ - if (size != (uint64_t)strlen(buf)) { - rc = -EFBIG; - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Stat'ed size " - "(%lld) does not match number of bytes read " - "(%lld)\n", (long long)size, - (long long)strlen(buf)); - goto out2; - } - - rc = 0; - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "\n%s\n", buf); - splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Successfully stat'ed " - "and read expected number of bytes (%lld) from test " - "file: %s\n", (long long)size, SPLAT_KOBJ_TEST_FILE); -out2: - kfree(buf); -out: - kobj_close_file(f); - - return rc; -} /* splat_kobj_test2() */ - -splat_subsystem_t * -splat_kobj_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_KOBJ_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_KOBJ_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_KOBJ; - - splat_test_init(sub, SPLAT_KOBJ_TEST1_NAME, SPLAT_KOBJ_TEST1_DESC, - SPLAT_KOBJ_TEST1_ID, splat_kobj_test1); - splat_test_init(sub, SPLAT_KOBJ_TEST2_NAME, SPLAT_KOBJ_TEST2_DESC, - SPLAT_KOBJ_TEST2_ID, splat_kobj_test2); - - return sub; -} /* splat_kobj_init() */ - -void -splat_kobj_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_KOBJ_TEST2_ID); - splat_test_fini(sub, SPLAT_KOBJ_TEST1_ID); - - kfree(sub); -} /* splat_kobj_fini() */ - -int -splat_kobj_id(void) -{ - return SPLAT_SUBSYSTEM_KOBJ; -} /* splat_kobj_id() */ diff --git a/module/splat/splat-linux.c b/module/splat/splat-linux.c deleted file mode 100644 index 1565d74a3..000000000 --- a/module/splat/splat-linux.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2011 Lawrence Livermore National Security, LLC. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Kernel Compatibility Tests. - */ - -#include <sys/kmem.h> -#include <linux/mm_compat.h> -#include "splat-internal.h" - -#define SPLAT_LINUX_NAME "linux" -#define SPLAT_LINUX_DESC "Kernel Compatibility Tests" - -#define SPLAT_LINUX_TEST1_ID 0x1001 -#define SPLAT_LINUX_TEST1_NAME "shrinker" -#define SPLAT_LINUX_TEST1_DESC "Shrinker test" - -/* - * Wait queue used to eliminate race between dropping of slab - * and execution of the shrinker callback - */ -DECLARE_WAIT_QUEUE_HEAD(shrinker_wait); - -SPL_SHRINKER_CALLBACK_FWD_DECLARE(splat_linux_shrinker_fn); -SPL_SHRINKER_DECLARE(splat_linux_shrinker, splat_linux_shrinker_fn, 1); -static unsigned long splat_linux_shrinker_size = 0; -static struct file *splat_linux_shrinker_file = NULL; - -static spl_shrinker_t -__splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc) -{ - static int failsafe = 0; - static unsigned long last_splat_linux_shrinker_size = 0; - unsigned long size; - spl_shrinker_t count; - - /* - * shrinker_size can only decrease or stay the same between callbacks - * in the same run, so Reset failsafe whenever shrinker increases - * as this indicates a new run. - */ - if (last_splat_linux_shrinker_size < splat_linux_shrinker_size) - failsafe = 0; - - last_splat_linux_shrinker_size = splat_linux_shrinker_size; - - if (sc->nr_to_scan) { - size = MIN(sc->nr_to_scan, splat_linux_shrinker_size); - splat_linux_shrinker_size -= size; - - splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME, - "Reclaimed %lu objects, size now %lu\n", - size, splat_linux_shrinker_size); - -#ifdef HAVE_SPLIT_SHRINKER_CALLBACK - count = size; -#else - count = splat_linux_shrinker_size; -#endif /* HAVE_SPLIT_SHRINKER_CALLBACK */ - - } else { - count = splat_linux_shrinker_size; - splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME, - "Cache size is %lu\n", splat_linux_shrinker_size); - } - - /* Far more calls than expected abort drop_slab as a failsafe */ - if (failsafe > 100) { - splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME, - "Far more calls than expected (%d), size now %lu\n", - failsafe, splat_linux_shrinker_size); - return (SHRINK_STOP); - } else { - /* - * We only increment failsafe if it doesn't trigger. This - * makes any failsafe failure persistent until the next test. - */ - failsafe++; - } - - /* Shrinker has run, so signal back to test. */ - wake_up(&shrinker_wait); - - return (count); -} - -SPL_SHRINKER_CALLBACK_WRAPPER(splat_linux_shrinker_fn); - -#define DROP_SLAB_CMD \ - "exec 0</dev/null " \ - " 1>/proc/sys/vm/drop_caches " \ - " 2>/dev/null; " \ - "echo 2" - -static int -splat_linux_drop_slab(struct file *file) -{ - char *argv[] = { "/bin/sh", - "-c", - DROP_SLAB_CMD, - NULL }; - char *envp[] = { "HOME=/", - "TERM=linux", - "PATH=/sbin:/usr/sbin:/bin:/usr/bin", - NULL }; - int rc; - - rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); - if (rc) - splat_vprint(file, SPLAT_LINUX_TEST1_NAME, - "Failed user helper '%s %s %s', rc = %d\n", - argv[0], argv[1], argv[2], rc); - - return rc; -} - -/* - * Verify correct shrinker functionality by registering a shrinker - * with the required compatibility macros. We then use a simulated - * cache and force the systems caches to be dropped. The shrinker - * should be repeatedly called until it reports that the cache is - * empty. It is then cleanly unregistered and correct behavior is - * verified. There are now four slightly different supported shrinker - * API and this test ensures the compatibility code is correct. - */ -static int -splat_linux_test1(struct file *file, void *arg) -{ - int rc = -EINVAL; - - /* - * Globals used by the shrinker, it is not safe to run this - * test concurrently this is a safe assumption for SPLAT tests. - * Regardless we do some minimal checking a bail if concurrent - * use is detected. - */ - if (splat_linux_shrinker_size || splat_linux_shrinker_file) { - splat_vprint(file, SPLAT_LINUX_TEST1_NAME, - "Failed due to concurrent shrinker test, rc = %d\n", rc); - return (rc); - } - - splat_linux_shrinker_size = 1024; - splat_linux_shrinker_file = file; - - spl_register_shrinker(&splat_linux_shrinker); - rc = splat_linux_drop_slab(file); - if (rc) - goto out; - - /* - * By the time we get here, it is possible that the shrinker has not - * yet run. splat_linux_drop_slab sends a signal for it to run, but - * there is no guarantee of when it will actually run. We wait for it - * to run here, terminating when either the shrinker size is now 0 or - * we timeout after 1 second, which should be an eternity (error). - */ - rc = wait_event_timeout(shrinker_wait, !splat_linux_shrinker_size, HZ); - if (!rc) { - splat_vprint(file, SPLAT_LINUX_TEST1_NAME, - "Failed cache shrinking timed out, size now %lu", - splat_linux_shrinker_size); - rc = -ETIMEDOUT; - } else { - rc = 0; - } - - if (!rc && splat_linux_shrinker_size != 0) { - splat_vprint(file, SPLAT_LINUX_TEST1_NAME, - "Failed cache was not shrunk to 0, size now %lu", - splat_linux_shrinker_size); - rc = -EDOM; - } -out: - spl_unregister_shrinker(&splat_linux_shrinker); - - splat_linux_shrinker_size = 0; - splat_linux_shrinker_file = NULL; - - return rc; -} - -splat_subsystem_t * -splat_linux_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_LINUX_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_LINUX_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_LINUX; - - splat_test_init(sub, SPLAT_LINUX_TEST1_NAME, SPLAT_LINUX_TEST1_DESC, - SPLAT_LINUX_TEST1_ID, splat_linux_test1); - - return sub; -} - -void -splat_linux_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_LINUX_TEST1_ID); - - kfree(sub); -} - -int -splat_linux_id(void) { - return SPLAT_SUBSYSTEM_LINUX; -} diff --git a/module/splat/splat-list.c b/module/splat/splat-list.c deleted file mode 100644 index 8a5f3c937..000000000 --- a/module/splat/splat-list.c +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) List Tests. - */ - -#include <sys/list.h> -#include <sys/kmem.h> -#include "splat-internal.h" - -#define SPLAT_LIST_NAME "list" -#define SPLAT_LIST_DESC "Kernel List Tests" - -#define SPLAT_LIST_TEST1_ID 0x0c01 -#define SPLAT_LIST_TEST1_NAME "create/destroy" -#define SPLAT_LIST_TEST1_DESC "Create/destroy Test" - -#define SPLAT_LIST_TEST2_ID 0x0c02 -#define SPLAT_LIST_TEST2_NAME "ins/rm head" -#define SPLAT_LIST_TEST2_DESC "Insert/remove head Test" - -#define SPLAT_LIST_TEST3_ID 0x0c03 -#define SPLAT_LIST_TEST3_NAME "ins/rm tail" -#define SPLAT_LIST_TEST3_DESC "Insert/remove tail Test" - -#define SPLAT_LIST_TEST4_ID 0x0c04 -#define SPLAT_LIST_TEST4_NAME "insert_after" -#define SPLAT_LIST_TEST4_DESC "Insert_after Test" - -#define SPLAT_LIST_TEST5_ID 0x0c05 -#define SPLAT_LIST_TEST5_NAME "insert_before" -#define SPLAT_LIST_TEST5_DESC "Insert_before Test" - -#define SPLAT_LIST_TEST6_ID 0x0c06 -#define SPLAT_LIST_TEST6_NAME "remove" -#define SPLAT_LIST_TEST6_DESC "Remove Test" - -#define SPLAT_LIST_TEST7_ID 0x0c7 -#define SPLAT_LIST_TEST7_NAME "active" -#define SPLAT_LIST_TEST7_DESC "Active Test" - -/* It is important that li_node is not the first element, this - * ensures the list_d2l/list_object macros are working correctly. */ -typedef struct list_item { - int li_data; - list_node_t li_node; -} list_item_t; - -#define LIST_ORDER_STACK 0 -#define LIST_ORDER_QUEUE 1 - -static int -splat_list_test1(struct file *file, void *arg) -{ - list_t list; - - splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - if (!list_is_empty(&list)) { - splat_vprint(file, SPLAT_LIST_TEST1_NAME, - "New list NOT empty%s\n", ""); - /* list_destroy() intentionally skipped to avoid assert */ - return -EEXIST; - } - - splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - /* Validate the list has been destroyed */ - if (list_link_active(&list.list_head)) { - splat_vprint(file, SPLAT_LIST_TEST1_NAME, - "Destroyed list still active%s", ""); - return -EIO; - } - - return 0; -} - -static int -splat_list_validate(list_t *list, int size, int order, int mult) -{ - list_item_t *li; - int i; - - /* Walk all items in list from head to verify stack or queue - * ordering. We bound the for loop by size+1 to ensure that - * we still terminate if there is list corruption. We also - * intentionally make things a little more complex than they - * need to be by using list_head/list_next for queues, and - * list_tail/list_prev for stacks. This is simply done for - * coverage and to ensure these function are working right. - */ - for (i = 0, li = (order ? list_head(list) : list_tail(list)); - i < size + 1 && li != NULL; - i++, li = (order ? list_next(list, li) : list_prev(list, li))) - if (li->li_data != i * mult) - return -EIDRM; - - if (i != size) - return -E2BIG; - - return 0; -} - -static int -splat_list_test2(struct file *file, void *arg) -{ - list_t list; - list_item_t *li; - int i, list_size = 8, rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - /* Insert all items at the list head to form a stack */ - splat_vprint(file, SPLAT_LIST_TEST2_NAME, - "Adding %d items to list head\n", list_size); - for (i = 0; i < list_size; i++) { - li = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li == NULL) { - rc = -ENOMEM; - goto out; - } - - list_link_init(&li->li_node); - li->li_data = i; - list_insert_head(&list, li); - } - - splat_vprint(file, SPLAT_LIST_TEST2_NAME, - "Validating %d item list is a stack\n", list_size); - rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1); - if (rc) - splat_vprint(file, SPLAT_LIST_TEST2_NAME, - "List validation failed, %d\n", rc); -out: - /* Remove all items */ - splat_vprint(file, SPLAT_LIST_TEST2_NAME, - "Removing %d items from list head\n", list_size); - while ((li = list_remove_head(&list))) - kmem_free(li, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -static int -splat_list_test3(struct file *file, void *arg) -{ - list_t list; - list_item_t *li; - int i, list_size = 8, rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - /* Insert all items at the list tail to form a queue */ - splat_vprint(file, SPLAT_LIST_TEST3_NAME, - "Adding %d items to list tail\n", list_size); - for (i = 0; i < list_size; i++) { - li = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li == NULL) { - rc = -ENOMEM; - goto out; - } - - list_link_init(&li->li_node); - li->li_data = i; - list_insert_tail(&list, li); - } - - splat_vprint(file, SPLAT_LIST_TEST3_NAME, - "Validating %d item list is a queue\n", list_size); - rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1); - if (rc) - splat_vprint(file, SPLAT_LIST_TEST3_NAME, - "List validation failed, %d\n", rc); -out: - /* Remove all items */ - splat_vprint(file, SPLAT_LIST_TEST3_NAME, - "Removing %d items from list tail\n", list_size); - while ((li = list_remove_tail(&list))) - kmem_free(li, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -static int -splat_list_test4(struct file *file, void *arg) -{ - list_t list; - list_item_t *li_new, *li_last = NULL; - int i, list_size = 8, rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - /* Insert all items after the last item to form a queue */ - splat_vprint(file, SPLAT_LIST_TEST4_NAME, - "Adding %d items each after the last item\n", list_size); - for (i = 0; i < list_size; i++) { - li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li_new == NULL) { - rc = -ENOMEM; - goto out; - } - - list_link_init(&li_new->li_node); - li_new->li_data = i; - list_insert_after(&list, li_last, li_new); - li_last = li_new; - } - - splat_vprint(file, SPLAT_LIST_TEST4_NAME, - "Validating %d item list is a queue\n", list_size); - rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1); - if (rc) - splat_vprint(file, SPLAT_LIST_TEST4_NAME, - "List validation failed, %d\n", rc); -out: - /* Remove all items */ - splat_vprint(file, SPLAT_LIST_TEST4_NAME, - "Removing %d items from list tail\n", list_size); - while ((li_new = list_remove_head(&list))) - kmem_free(li_new, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -static int -splat_list_test5(struct file *file, void *arg) -{ - list_t list; - list_item_t *li_new, *li_last = NULL; - int i, list_size = 8, rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - /* Insert all items before the last item to form a stack */ - splat_vprint(file, SPLAT_LIST_TEST5_NAME, - "Adding %d items each before the last item\n", list_size); - for (i = 0; i < list_size; i++) { - li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li_new == NULL) { - rc = -ENOMEM; - goto out; - } - - list_link_init(&li_new->li_node); - li_new->li_data = i; - list_insert_before(&list, li_last, li_new); - li_last = li_new; - } - - splat_vprint(file, SPLAT_LIST_TEST5_NAME, - "Validating %d item list is a queue\n", list_size); - rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1); - if (rc) - splat_vprint(file, SPLAT_LIST_TEST5_NAME, - "List validation failed, %d\n", rc); -out: - /* Remove all items */ - splat_vprint(file, SPLAT_LIST_TEST5_NAME, - "Removing %d items from list tail\n", list_size); - while ((li_new = list_remove_tail(&list))) - kmem_free(li_new, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -static int -splat_list_test6(struct file *file, void *arg) -{ - list_t list; - list_item_t *li, *li_prev; - int i, list_size = 8, rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - /* Insert all items at the list tail to form a queue */ - splat_vprint(file, SPLAT_LIST_TEST6_NAME, - "Adding %d items to list tail\n", list_size); - for (i = 0; i < list_size; i++) { - li = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li == NULL) { - rc = -ENOMEM; - goto out; - } - - list_link_init(&li->li_node); - li->li_data = i; - list_insert_tail(&list, li); - } - - /* Remove all odd items from the queue */ - splat_vprint(file, SPLAT_LIST_TEST6_NAME, - "Removing %d odd items from the list\n", list_size >> 1); - for (li = list_head(&list); li != NULL; li = list_next(&list, li)) { - if (li->li_data % 2 == 1) { - li_prev = list_prev(&list, li); - list_remove(&list, li); - kmem_free(li, sizeof(list_item_t)); - li = li_prev; - } - } - - splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Validating %d item " - "list is a queue of only even elements\n", list_size / 2); - rc = splat_list_validate(&list, list_size / 2, LIST_ORDER_QUEUE, 2); - if (rc) - splat_vprint(file, SPLAT_LIST_TEST6_NAME, - "List validation failed, %d\n", rc); -out: - /* Remove all items */ - splat_vprint(file, SPLAT_LIST_TEST6_NAME, - "Removing %d items from list tail\n", list_size / 2); - while ((li = list_remove_tail(&list))) - kmem_free(li, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -static int -splat_list_test7(struct file *file, void *arg) -{ - list_t list; - list_item_t *li; - int rc = 0; - - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Creating list\n%s", ""); - list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node)); - - li = kmem_alloc(sizeof(list_item_t), KM_SLEEP); - if (li == NULL) { - rc = -ENOMEM; - goto out; - } - - /* Validate newly initialized node is inactive */ - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Init list node\n%s", ""); - list_link_init(&li->li_node); - if (list_link_active(&li->li_node)) { - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Newly initialized " - "list node should inactive %p/%p\n", - li->li_node.prev, li->li_node.next); - rc = -EINVAL; - goto out_li; - } - - /* Validate node is active when linked in to a list */ - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Insert list node\n%s", ""); - list_insert_head(&list, li); - if (!list_link_active(&li->li_node)) { - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node " - "inserted in list should be active %p/%p\n", - li->li_node.prev, li->li_node.next); - rc = -EINVAL; - goto out; - } - - /* Validate node is inactive when removed from list */ - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Remove list node\n%s", ""); - list_remove(&list, li); - if (list_link_active(&li->li_node)) { - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node " - "removed from list should be inactive %p/%p\n", - li->li_node.prev, li->li_node.next); - rc = -EINVAL; - } -out_li: - kmem_free(li, sizeof(list_item_t)); -out: - /* Remove all items */ - while ((li = list_remove_head(&list))) - kmem_free(li, sizeof(list_item_t)); - - splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Destroying list\n%s", ""); - list_destroy(&list); - - return rc; -} - -splat_subsystem_t * -splat_list_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_LIST_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_LIST_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_LIST; - - splat_test_init(sub, SPLAT_LIST_TEST1_NAME, SPLAT_LIST_TEST1_DESC, - SPLAT_LIST_TEST1_ID, splat_list_test1); - splat_test_init(sub, SPLAT_LIST_TEST2_NAME, SPLAT_LIST_TEST2_DESC, - SPLAT_LIST_TEST2_ID, splat_list_test2); - splat_test_init(sub, SPLAT_LIST_TEST3_NAME, SPLAT_LIST_TEST3_DESC, - SPLAT_LIST_TEST3_ID, splat_list_test3); - splat_test_init(sub, SPLAT_LIST_TEST4_NAME, SPLAT_LIST_TEST4_DESC, - SPLAT_LIST_TEST4_ID, splat_list_test4); - splat_test_init(sub, SPLAT_LIST_TEST5_NAME, SPLAT_LIST_TEST5_DESC, - SPLAT_LIST_TEST5_ID, splat_list_test5); - splat_test_init(sub, SPLAT_LIST_TEST6_NAME, SPLAT_LIST_TEST6_DESC, - SPLAT_LIST_TEST6_ID, splat_list_test6); - splat_test_init(sub, SPLAT_LIST_TEST7_NAME, SPLAT_LIST_TEST7_DESC, - SPLAT_LIST_TEST7_ID, splat_list_test7); - - return sub; -} - -void -splat_list_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_LIST_TEST7_ID); - splat_test_fini(sub, SPLAT_LIST_TEST6_ID); - splat_test_fini(sub, SPLAT_LIST_TEST5_ID); - splat_test_fini(sub, SPLAT_LIST_TEST4_ID); - splat_test_fini(sub, SPLAT_LIST_TEST3_ID); - splat_test_fini(sub, SPLAT_LIST_TEST2_ID); - splat_test_fini(sub, SPLAT_LIST_TEST1_ID); - - kfree(sub); -} - -int -splat_list_id(void) -{ - return SPLAT_SUBSYSTEM_LIST; -} diff --git a/module/splat/splat-mutex.c b/module/splat/splat-mutex.c deleted file mode 100644 index 202e6c0f6..000000000 --- a/module/splat/splat-mutex.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Mutex Tests. - */ - -#include <sys/mutex.h> -#include <sys/taskq.h> -#include <linux/delay.h> -#include <linux/mm_compat.h> -#include "splat-internal.h" - -#define SPLAT_MUTEX_NAME "mutex" -#define SPLAT_MUTEX_DESC "Kernel Mutex Tests" - -#define SPLAT_MUTEX_TEST1_ID 0x0401 -#define SPLAT_MUTEX_TEST1_NAME "tryenter" -#define SPLAT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness" - -#define SPLAT_MUTEX_TEST2_ID 0x0402 -#define SPLAT_MUTEX_TEST2_NAME "race" -#define SPLAT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex" - -#define SPLAT_MUTEX_TEST3_ID 0x0403 -#define SPLAT_MUTEX_TEST3_NAME "owned" -#define SPLAT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness" - -#define SPLAT_MUTEX_TEST4_ID 0x0404 -#define SPLAT_MUTEX_TEST4_NAME "owner" -#define SPLAT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness" - -#define SPLAT_MUTEX_TEST_MAGIC 0x115599DDUL -#define SPLAT_MUTEX_TEST_NAME "mutex_test" -#define SPLAT_MUTEX_TEST_TASKQ "mutex_taskq" -#define SPLAT_MUTEX_TEST_COUNT 128 - -typedef struct mutex_priv { - unsigned long mp_magic; - struct file *mp_file; - kmutex_t mp_mtx; - int mp_rc; - int mp_rc2; -} mutex_priv_t; - -static void -splat_mutex_test1_func(void *arg) -{ - mutex_priv_t *mp = (mutex_priv_t *)arg; - ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC); - - if (mutex_tryenter(&mp->mp_mtx)) { - mp->mp_rc = 0; - mutex_exit(&mp->mp_mtx); - } else { - mp->mp_rc = -EBUSY; - } -} - -static int -splat_mutex_test1(struct file *file, void *arg) -{ - mutex_priv_t *mp; - taskq_t *tq; - taskqid_t id; - int rc = 0; - - mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL); - if (mp == NULL) - return -ENOMEM; - - tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, 1, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE); - if (tq == NULL) { - rc = -ENOMEM; - goto out2; - } - - mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC; - mp->mp_file = file; - mutex_init(&mp->mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); - mutex_enter(&mp->mp_mtx); - - /* - * Schedule a task function which will try and acquire the mutex via - * mutex_tryenter() while it's held. This should fail and the task - * function will indicate this status in the passed private data. - */ - mp->mp_rc = -EINVAL; - id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - mutex_exit(&mp->mp_mtx); - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", - "taskq_dispatch() failed\n"); - rc = -EINVAL; - goto out; - } - - taskq_wait_id(tq, id); - mutex_exit(&mp->mp_mtx); - - /* Task function successfully acquired mutex, very bad! */ - if (mp->mp_rc != -EBUSY) { - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, - "mutex_trylock() incorrectly succeeded when " - "the mutex was held, %d/%d\n", (int)id, mp->mp_rc); - rc = -EINVAL; - goto out; - } else { - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", - "mutex_trylock() correctly failed when " - "the mutex was held\n"); - } - - /* - * Schedule a task function which will try and acquire the mutex via - * mutex_tryenter() while it is not held. This should succeed and - * can be verified by checking the private data. - */ - mp->mp_rc = -EINVAL; - id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", - "taskq_dispatch() failed\n"); - rc = -EINVAL; - goto out; - } - - taskq_wait_id(tq, id); - - /* Task function failed to acquire mutex, very bad! */ - if (mp->mp_rc != 0) { - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, - "mutex_trylock() incorrectly failed when the mutex " - "was not held, %d/%d\n", (int)id, mp->mp_rc); - rc = -EINVAL; - } else { - splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s", - "mutex_trylock() correctly succeeded " - "when the mutex was not held\n"); - } -out: - taskq_destroy(tq); - mutex_destroy(&(mp->mp_mtx)); -out2: - kfree(mp); - return rc; -} - -static void -splat_mutex_test2_func(void *arg) -{ - mutex_priv_t *mp = (mutex_priv_t *)arg; - int rc; - ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC); - - /* Read the value before sleeping and write it after we wake up to - * maximize the chance of a race if mutexs are not working properly */ - mutex_enter(&mp->mp_mtx); - rc = mp->mp_rc; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 100); /* 1/100 of a second */ - VERIFY(mp->mp_rc == rc); - mp->mp_rc = rc + 1; - mutex_exit(&mp->mp_mtx); -} - -static int -splat_mutex_test2(struct file *file, void *arg) -{ - mutex_priv_t *mp; - taskq_t *tq; - taskqid_t id; - int i, rc = 0; - - mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL); - if (mp == NULL) - return -ENOMEM; - - /* Create several threads allowing tasks to race with each other */ - tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, num_online_cpus(), - defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE); - if (tq == NULL) { - rc = -ENOMEM; - goto out; - } - - mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC; - mp->mp_file = file; - mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); - mp->mp_rc = 0; - - /* - * Schedule N work items to the work queue each of which enters the - * mutex, sleeps briefly, then exits the mutex. On a multiprocessor - * box these work items will be handled by all available CPUs. The - * task function checks to ensure the tracked shared variable is - * always only incremented by one. Additionally, the mutex itself - * is instrumented such that if any two processors are in the - * critical region at the same time the system will panic. If the - * mutex is implemented right this will never happy, that's a pass. - */ - for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) { - id = taskq_dispatch(tq, splat_mutex_test2_func, mp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, - "Failed to queue task %d\n", i); - rc = -EINVAL; - } - } - - taskq_wait(tq); - - if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) { - splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads " - "correctly entered/exited the mutex %d times\n", - num_online_cpus(), mp->mp_rc); - } else { - splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads " - "only processed %d/%d mutex work items\n", - num_online_cpus(),mp->mp_rc,SPLAT_MUTEX_TEST_COUNT); - rc = -EINVAL; - } - - taskq_destroy(tq); - mutex_destroy(&(mp->mp_mtx)); -out: - kfree(mp); - return rc; -} - -static void -splat_mutex_owned(void *priv) -{ - mutex_priv_t *mp = (mutex_priv_t *)priv; - - ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC); - mp->mp_rc = mutex_owned(&mp->mp_mtx); - mp->mp_rc2 = MUTEX_HELD(&mp->mp_mtx); -} - -static int -splat_mutex_test3(struct file *file, void *arg) -{ - mutex_priv_t mp; - taskq_t *tq; - taskqid_t id; - int rc = 0; - - mp.mp_magic = SPLAT_MUTEX_TEST_MAGIC; - mp.mp_file = file; - mutex_init(&mp.mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); - - if ((tq = taskq_create(SPLAT_MUTEX_TEST_NAME, 1, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Taskq '%s' " - "create failed\n", SPLAT_MUTEX_TEST3_NAME); - return -EINVAL; - } - - mutex_enter(&mp.mp_mtx); - - /* Mutex should be owned by current */ - if (!mutex_owned(&mp.mp_mtx)) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Unowned mutex " - "should be owned by pid %d\n", current->pid); - rc = -EINVAL; - goto out_exit; - } - - id = taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to " - "dispatch function '%s' to taskq\n", - sym2str(splat_mutex_owned)); - rc = -EINVAL; - goto out_exit; - } - taskq_wait(tq); - - /* Mutex should not be owned which checked from a different thread */ - if (mp.mp_rc || mp.mp_rc2) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by " - "pid %d not by taskq\n", current->pid); - rc = -EINVAL; - goto out_exit; - } - - mutex_exit(&mp.mp_mtx); - - /* Mutex should not be owned by current */ - if (mutex_owned(&mp.mp_mtx)) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by " - "pid %d it should be unowned\b", current->pid); - rc = -EINVAL; - goto out; - } - - id = taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to " - "dispatch function '%s' to taskq\n", - sym2str(splat_mutex_owned)); - rc = -EINVAL; - goto out; - } - taskq_wait(tq); - - /* Mutex should be owned by no one */ - if (mp.mp_rc || mp.mp_rc2) { - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by " - "no one, %d/%d disagrees\n", mp.mp_rc, mp.mp_rc2); - rc = -EINVAL; - goto out; - } - - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s", - "Correct mutex_owned() behavior\n"); - goto out; -out_exit: - mutex_exit(&mp.mp_mtx); -out: - mutex_destroy(&mp.mp_mtx); - taskq_destroy(tq); - - return rc; -} - -static int -splat_mutex_test4(struct file *file, void *arg) -{ - kmutex_t mtx; - kthread_t *owner; - int rc = 0; - - mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL); - - /* - * Verify mutex owner is cleared after being dropped. Depending - * on how you build your kernel this behavior changes, ensure the - * SPL mutex implementation is properly detecting this. - */ - mutex_enter(&mtx); - msleep(100); - mutex_exit(&mtx); - if (MUTEX_HELD(&mtx)) { - splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should " - "not be held, bit is by %p\n", mutex_owner(&mtx)); - rc = -EINVAL; - goto out; - } - - mutex_enter(&mtx); - - /* Mutex should be owned by current */ - owner = mutex_owner(&mtx); - if (current != owner) { - splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should " - "be owned by pid %d but is owned by pid %d\n", - current->pid, owner ? owner->pid : -1); - rc = -EINVAL; - goto out; - } - - mutex_exit(&mtx); - - /* Mutex should not be owned by any task */ - owner = mutex_owner(&mtx); - if (owner) { - splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should not " - "be owned but is owned by pid %d\n", owner->pid); - rc = -EINVAL; - goto out; - } - - splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s", - "Correct mutex_owner() behavior\n"); -out: - mutex_destroy(&mtx); - - return rc; -} - -splat_subsystem_t * -splat_mutex_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_MUTEX_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_MUTEX_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_MUTEX; - - splat_test_init(sub, SPLAT_MUTEX_TEST1_NAME, SPLAT_MUTEX_TEST1_DESC, - SPLAT_MUTEX_TEST1_ID, splat_mutex_test1); - splat_test_init(sub, SPLAT_MUTEX_TEST2_NAME, SPLAT_MUTEX_TEST2_DESC, - SPLAT_MUTEX_TEST2_ID, splat_mutex_test2); - splat_test_init(sub, SPLAT_MUTEX_TEST3_NAME, SPLAT_MUTEX_TEST3_DESC, - SPLAT_MUTEX_TEST3_ID, splat_mutex_test3); - splat_test_init(sub, SPLAT_MUTEX_TEST4_NAME, SPLAT_MUTEX_TEST4_DESC, - SPLAT_MUTEX_TEST4_ID, splat_mutex_test4); - - return sub; -} - -void -splat_mutex_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_MUTEX_TEST4_ID); - splat_test_fini(sub, SPLAT_MUTEX_TEST3_ID); - splat_test_fini(sub, SPLAT_MUTEX_TEST2_ID); - splat_test_fini(sub, SPLAT_MUTEX_TEST1_ID); - - kfree(sub); -} - -int -splat_mutex_id(void) { - return SPLAT_SUBSYSTEM_MUTEX; -} diff --git a/module/splat/splat-random.c b/module/splat/splat-random.c deleted file mode 100644 index 2ddb823fc..000000000 --- a/module/splat/splat-random.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Random Number Generator Tests. - */ - -#include <sys/random.h> -#include <sys/kmem.h> -#include "splat-internal.h" - -#define SPLAT_KRNG_NAME "krng" -#define SPLAT_KRNG_DESC "Kernel Random Number Generator Tests" - -#define SPLAT_KRNG_TEST1_ID 0x0301 -#define SPLAT_KRNG_TEST1_NAME "freq" -#define SPLAT_KRNG_TEST1_DESC "Frequency Test" - -#define KRNG_NUM_BITS 1048576 -#define KRNG_NUM_BYTES (KRNG_NUM_BITS >> 3) -#define KRNG_NUM_BITS_DIV2 (KRNG_NUM_BITS >> 1) -#define KRNG_ERROR_RANGE 2097 - -/* Random Number Generator Tests - There can be meny more tests on quality of the - random number generator. For now we are only - testing the frequency of particular bits. - We could also test consecutive sequences, - randomness within a particular block, etc. - but is probably not necessary for our purposes */ - -static int -splat_krng_test1(struct file *file, void *arg) -{ - uint8_t *buf; - int i, j, diff, num = 0, rc = 0; - - buf = kmalloc(sizeof(*buf) * KRNG_NUM_BYTES, GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto out; - } - - memset(buf, 0, sizeof(*buf) * KRNG_NUM_BYTES); - - /* Always succeeds */ - random_get_pseudo_bytes(buf, sizeof(uint8_t) * KRNG_NUM_BYTES); - - for (i = 0; i < KRNG_NUM_BYTES; i++) { - uint8_t tmp = buf[i]; - for (j = 0; j < 8; j++) { - uint8_t tmp2 = ((tmp >> j) & 0x01); - if (tmp2 == 1) { - num++; - } - } - } - - kfree(buf); - - diff = KRNG_NUM_BITS_DIV2 - num; - if (diff < 0) - diff *= -1; - - splat_print(file, "Test 1 Number of ones: %d\n", num); - splat_print(file, "Test 1 Difference from expected: %d Allowed: %d\n", - diff, KRNG_ERROR_RANGE); - - if (diff > KRNG_ERROR_RANGE) - rc = -ERANGE; -out: - return rc; -} - -splat_subsystem_t * -splat_krng_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_KRNG_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_KRNG_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_KRNG; - - splat_test_init(sub, SPLAT_KRNG_TEST1_NAME, SPLAT_KRNG_TEST1_DESC, - SPLAT_KRNG_TEST1_ID, splat_krng_test1); - - return sub; -} - -void -splat_krng_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_KRNG_TEST1_ID); - - kfree(sub); -} - -int -splat_krng_id(void) { - return SPLAT_SUBSYSTEM_KRNG; -} diff --git a/module/splat/splat-rwlock.c b/module/splat/splat-rwlock.c deleted file mode 100644 index 562a5f047..000000000 --- a/module/splat/splat-rwlock.c +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Read/Writer Lock Tests. - */ - -#include <sys/random.h> -#include <sys/rwlock.h> -#include <sys/taskq.h> -#include <linux/delay.h> -#include <linux/mm_compat.h> -#include "splat-internal.h" - -#define SPLAT_RWLOCK_NAME "rwlock" -#define SPLAT_RWLOCK_DESC "Kernel RW Lock Tests" - -#define SPLAT_RWLOCK_TEST1_ID 0x0701 -#define SPLAT_RWLOCK_TEST1_NAME "N-rd/1-wr" -#define SPLAT_RWLOCK_TEST1_DESC "Multiple readers one writer" - -#define SPLAT_RWLOCK_TEST2_ID 0x0702 -#define SPLAT_RWLOCK_TEST2_NAME "0-rd/N-wr" -#define SPLAT_RWLOCK_TEST2_DESC "Multiple writers" - -#define SPLAT_RWLOCK_TEST3_ID 0x0703 -#define SPLAT_RWLOCK_TEST3_NAME "held" -#define SPLAT_RWLOCK_TEST3_DESC "RW_{LOCK|READ|WRITE}_HELD" - -#define SPLAT_RWLOCK_TEST4_ID 0x0704 -#define SPLAT_RWLOCK_TEST4_NAME "tryenter" -#define SPLAT_RWLOCK_TEST4_DESC "Tryenter" - -#define SPLAT_RWLOCK_TEST5_ID 0x0705 -#define SPLAT_RWLOCK_TEST5_NAME "rw_downgrade" -#define SPLAT_RWLOCK_TEST5_DESC "Write downgrade" - -#define SPLAT_RWLOCK_TEST6_ID 0x0706 -#define SPLAT_RWLOCK_TEST6_NAME "rw_tryupgrade-1" -#define SPLAT_RWLOCK_TEST6_DESC "rwsem->count value" - -#define SPLAT_RWLOCK_TEST7_ID 0x0707 -#define SPLAT_RWLOCK_TEST7_NAME "rw_tryupgrade-2" -#define SPLAT_RWLOCK_TEST7_DESC "Read upgrade" - -#define SPLAT_RWLOCK_TEST_MAGIC 0x115599DDUL -#define SPLAT_RWLOCK_TEST_NAME "rwlock_test" -#define SPLAT_RWLOCK_TEST_TASKQ "rwlock_taskq" -#define SPLAT_RWLOCK_TEST_COUNT 8 - -#define SPLAT_RWLOCK_RELEASE_INIT 0 -#define SPLAT_RWLOCK_RELEASE_WR 1 -#define SPLAT_RWLOCK_RELEASE_RD 2 - -typedef struct rw_priv { - unsigned long rw_magic; - struct file *rw_file; - krwlock_t rw_rwlock; - spinlock_t rw_lock; - spl_wait_queue_head_t rw_waitq; - int rw_completed; - int rw_holders; - int rw_waiters; - int rw_release; - int rw_rc; - krw_t rw_type; -} rw_priv_t; - -typedef struct rw_thr { - const char *rwt_name; - rw_priv_t *rwt_rwp; - struct task_struct *rwt_thread; -} rw_thr_t; - -void splat_init_rw_priv(rw_priv_t *rwp, struct file *file) -{ - rwp->rw_magic = SPLAT_RWLOCK_TEST_MAGIC; - rwp->rw_file = file; - rw_init(&rwp->rw_rwlock, SPLAT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL); - spin_lock_init(&rwp->rw_lock); - init_waitqueue_head(&rwp->rw_waitq); - rwp->rw_completed = 0; - rwp->rw_holders = 0; - rwp->rw_waiters = 0; - rwp->rw_release = SPLAT_RWLOCK_RELEASE_INIT; - rwp->rw_rc = 0; - rwp->rw_type = 0; -} - -#if defined(CONFIG_PREEMPT_RT_FULL) -static int -splat_rwlock_test1(struct file *file, void *arg) -{ - /* - * This test will never succeed on PREEMPT_RT_FULL because these - * kernels only allow a single thread to hold the lock. - */ - return 0; -} -#else -static int -splat_rwlock_wr_thr(void *arg) -{ - rw_thr_t *rwt = (rw_thr_t *)arg; - rw_priv_t *rwp = rwt->rwt_rwp; - uint8_t rnd; - - ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC); - - get_random_bytes((void *)&rnd, 1); - msleep((unsigned int)rnd); - - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s trying to acquire rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - spin_lock(&rwp->rw_lock); - rwp->rw_waiters++; - spin_unlock(&rwp->rw_lock); - rw_enter(&rwp->rw_rwlock, RW_WRITER); - - spin_lock(&rwp->rw_lock); - rwp->rw_waiters--; - rwp->rw_holders++; - spin_unlock(&rwp->rw_lock); - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s acquired rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - - /* Wait for control thread to signal we can release the write lock */ - wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock, - rwp->rw_release == SPLAT_RWLOCK_RELEASE_WR)); - - spin_lock(&rwp->rw_lock); - rwp->rw_completed++; - rwp->rw_holders--; - spin_unlock(&rwp->rw_lock); - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s dropped rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - - rw_exit(&rwp->rw_rwlock); - - return 0; -} - -static int -splat_rwlock_rd_thr(void *arg) -{ - rw_thr_t *rwt = (rw_thr_t *)arg; - rw_priv_t *rwp = rwt->rwt_rwp; - uint8_t rnd; - - ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC); - - get_random_bytes((void *)&rnd, 1); - msleep((unsigned int)rnd); - - /* Don't try and take the semaphore until after someone has it */ - wait_event_interruptible(rwp->rw_waitq, - splat_locked_test(&rwp->rw_lock, rwp->rw_holders > 0)); - - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s trying to acquire rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - spin_lock(&rwp->rw_lock); - rwp->rw_waiters++; - spin_unlock(&rwp->rw_lock); - rw_enter(&rwp->rw_rwlock, RW_READER); - - spin_lock(&rwp->rw_lock); - rwp->rw_waiters--; - rwp->rw_holders++; - spin_unlock(&rwp->rw_lock); - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s acquired rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - - /* Wait for control thread to signal we can release the read lock */ - wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock, - rwp->rw_release == SPLAT_RWLOCK_RELEASE_RD)); - - spin_lock(&rwp->rw_lock); - rwp->rw_completed++; - rwp->rw_holders--; - spin_unlock(&rwp->rw_lock); - splat_vprint(rwp->rw_file, rwt->rwt_name, - "%s dropped rwlock (%d holding/%d waiting)\n", - rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters); - - rw_exit(&rwp->rw_rwlock); - - return 0; -} - -static int -splat_rwlock_test1(struct file *file, void *arg) -{ - int i, count = 0, rc = 0; - rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT]; - rw_priv_t *rwp; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - /* Create some threads, the exact number isn't important just as - * long as we know how many we managed to create and should expect. */ - for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) { - rwt[i].rwt_rwp = rwp; - rwt[i].rwt_name = SPLAT_RWLOCK_TEST1_NAME; - - /* The first thread will be the writer */ - if (i == 0) - rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_wr_thr, - &rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i); - else - rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_rd_thr, - &rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i); - - if (!IS_ERR(rwt[i].rwt_thread)) { - wake_up_process(rwt[i].rwt_thread); - count++; - } - } - - /* Wait for the writer */ - while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders == 0)) { - wake_up_interruptible(&rwp->rw_waitq); - msleep(100); - } - - /* Wait for 'count-1' readers */ - while (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters < count - 1)) { - wake_up_interruptible(&rwp->rw_waitq); - msleep(100); - } - - /* Verify there is only one lock holder */ - if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders) != 1) { - splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only 1 holder " - "expected for rwlock (%d holding/%d waiting)\n", - rwp->rw_holders, rwp->rw_waiters); - rc = -EINVAL; - } - - /* Verify 'count-1' readers */ - if (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters != count - 1)) { - splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d waiters " - "expected for rwlock (%d holding/%d waiting)\n", - count - 1, rwp->rw_holders, rwp->rw_waiters); - rc = -EINVAL; - } - - /* Signal the writer to release, allows readers to acquire */ - spin_lock(&rwp->rw_lock); - rwp->rw_release = SPLAT_RWLOCK_RELEASE_WR; - wake_up_interruptible(&rwp->rw_waitq); - spin_unlock(&rwp->rw_lock); - - /* Wait for 'count-1' readers to hold the lock */ - while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders < count - 1)) { - wake_up_interruptible(&rwp->rw_waitq); - msleep(100); - } - - /* Verify there are 'count-1' readers */ - if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders != count - 1)) { - splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d holders " - "expected for rwlock (%d holding/%d waiting)\n", - count - 1, rwp->rw_holders, rwp->rw_waiters); - rc = -EINVAL; - } - - /* Release 'count-1' readers */ - spin_lock(&rwp->rw_lock); - rwp->rw_release = SPLAT_RWLOCK_RELEASE_RD; - wake_up_interruptible(&rwp->rw_waitq); - spin_unlock(&rwp->rw_lock); - - /* Wait for the test to complete */ - while (splat_locked_test(&rwp->rw_lock, - rwp->rw_holders>0 || rwp->rw_waiters>0)) - msleep(100); - - rw_destroy(&(rwp->rw_rwlock)); - kfree(rwp); - - return rc; -} -#endif - -static void -splat_rwlock_test2_func(void *arg) -{ - rw_priv_t *rwp = (rw_priv_t *)arg; - int rc; - ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC); - - /* Read the value before sleeping and write it after we wake up to - * maximize the chance of a race if rwlocks are not working properly */ - rw_enter(&rwp->rw_rwlock, RW_WRITER); - rc = rwp->rw_rc; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 100); /* 1/100 of a second */ - VERIFY(rwp->rw_rc == rc); - rwp->rw_rc = rc + 1; - rw_exit(&rwp->rw_rwlock); -} - -static int -splat_rwlock_test2(struct file *file, void *arg) -{ - rw_priv_t *rwp; - taskq_t *tq; - int i, rc = 0, tq_count = 256; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - /* Create several threads allowing tasks to race with each other */ - tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, num_online_cpus(), - defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE); - if (tq == NULL) { - rc = -ENOMEM; - goto out; - } - - /* - * Schedule N work items to the work queue each of which enters the - * writer rwlock, sleeps briefly, then exits the writer rwlock. On a - * multiprocessor box these work items will be handled by all available - * CPUs. The task function checks to ensure the tracked shared variable - * is always only incremented by one. Additionally, the rwlock itself - * is instrumented such that if any two processors are in the - * critical region at the same time the system will panic. If the - * rwlock is implemented right this will never happy, that's a pass. - */ - for (i = 0; i < tq_count; i++) { - if (taskq_dispatch(tq, splat_rwlock_test2_func, rwp, - TQ_SLEEP) == TASKQID_INVALID) { - splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, - "Failed to queue task %d\n", i); - rc = -EINVAL; - } - } - - taskq_wait(tq); - - if (rwp->rw_rc == tq_count) { - splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads " - "correctly entered/exited the rwlock %d times\n", - num_online_cpus(), rwp->rw_rc); - } else { - splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads " - "only processed %d/%d w rwlock work items\n", - num_online_cpus(), rwp->rw_rc, tq_count); - rc = -EINVAL; - } - - taskq_destroy(tq); - rw_destroy(&(rwp->rw_rwlock)); -out: - kfree(rwp); - return rc; -} - -#define splat_rwlock_test3_helper(rwp,rex1,rex2,wex1,wex2,held_func,rc) \ -do { \ - int result, _rc1_, _rc2_, _rc3_, _rc4_; \ - \ - rc = 0; \ - rw_enter(&(rwp)->rw_rwlock, RW_READER); \ - _rc1_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex1); \ - splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ - " returned %d (expected %d) when RW_READER\n", \ - _rc1_ ? "Fail " : "", result, rex1); \ - rw_exit(&(rwp)->rw_rwlock); \ - _rc2_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex2); \ - splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ - " returned %d (expected %d) when !RW_READER\n", \ - _rc2_ ? "Fail " : "", result, rex2); \ - \ - rw_enter(&(rwp)->rw_rwlock, RW_WRITER); \ - _rc3_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex1); \ - splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ - " returned %d (expected %d) when RW_WRITER\n", \ - _rc3_ ? "Fail " : "", result, wex1); \ - rw_exit(&(rwp)->rw_rwlock); \ - _rc4_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex2); \ - splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \ - " returned %d (expected %d) when !RW_WRITER\n", \ - _rc4_ ? "Fail " : "", result, wex2); \ - \ - rc = ((_rc1_ || _rc2_ || _rc3_ || _rc4_) ? -EINVAL : 0); \ -} while(0); - -static int -splat_rwlock_test3(struct file *file, void *arg) -{ - rw_priv_t *rwp; - int rc1, rc2, rc3; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - splat_rwlock_test3_helper(rwp, 1, 0, 1, 0, RW_LOCK_HELD, rc1); - splat_rwlock_test3_helper(rwp, 1, 0, 0, 0, RW_READ_HELD, rc2); - splat_rwlock_test3_helper(rwp, 0, 0, 1, 0, RW_WRITE_HELD, rc3); - - rw_destroy(&rwp->rw_rwlock); - kfree(rwp); - - return ((rc1 || rc2 || rc3) ? -EINVAL : 0); -} - -static void -splat_rwlock_test4_func(void *arg) -{ - rw_priv_t *rwp = (rw_priv_t *)arg; - ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC); - - if (rw_tryenter(&rwp->rw_rwlock, rwp->rw_type)) { - rwp->rw_rc = 0; - rw_exit(&rwp->rw_rwlock); - } else { - rwp->rw_rc = -EBUSY; - } -} - -static char * -splat_rwlock_test4_name(krw_t type) -{ - switch (type) { - case RW_NONE: return "RW_NONE"; - case RW_WRITER: return "RW_WRITER"; - case RW_READER: return "RW_READER"; - } - - return NULL; -} - -static int -splat_rwlock_test4_type(taskq_t *tq, rw_priv_t *rwp, int expected_rc, - krw_t holder_type, krw_t try_type) -{ - int id, rc = 0; - - /* Schedule a task function which will try and acquire the rwlock - * using type try_type while the rwlock is being held as holder_type. - * The result must match expected_rc for the test to pass */ - rwp->rw_rc = -EINVAL; - rwp->rw_type = try_type; - - if (holder_type == RW_WRITER || holder_type == RW_READER) - rw_enter(&rwp->rw_rwlock, holder_type); - - id = taskq_dispatch(tq, splat_rwlock_test4_func, rwp, TQ_SLEEP); - if (id == TASKQID_INVALID) { - splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, "%s", - "taskq_dispatch() failed\n"); - rc = -EINVAL; - goto out; - } - - taskq_wait_id(tq, id); - - if (rwp->rw_rc != expected_rc) - rc = -EINVAL; - - splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, - "%srw_tryenter(%s) returned %d (expected %d) when %s\n", - rc ? "Fail " : "", splat_rwlock_test4_name(try_type), - rwp->rw_rc, expected_rc, - splat_rwlock_test4_name(holder_type)); -out: - if (holder_type == RW_WRITER || holder_type == RW_READER) - rw_exit(&rwp->rw_rwlock); - - return rc; -} - -static int -splat_rwlock_test4(struct file *file, void *arg) -{ - rw_priv_t *rwp; - taskq_t *tq; - int rc = 0, rc1, rc2, rc3, rc4, rc5, rc6; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, 1, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE); - if (tq == NULL) { - rc = -ENOMEM; - goto out; - } - - splat_init_rw_priv(rwp, file); - - /* - * Validate all combinations of rw_tryenter() contention. - * - * The concurrent reader test is modified for PREEMPT_RT_FULL - * kernels which do not permit concurrent read locks to be taken - * from different threads. The same thread is allowed to take - * the read lock multiple times. - */ - rc1 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_WRITER); - rc2 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_READER); - rc3 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_WRITER); -#if defined(CONFIG_PREEMPT_RT_FULL) - rc4 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_READER); -#else - rc4 = splat_rwlock_test4_type(tq, rwp, 0, RW_READER, RW_READER); -#endif - rc5 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_WRITER); - rc6 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_READER); - - if (rc1 || rc2 || rc3 || rc4 || rc5 || rc6) - rc = -EINVAL; - - taskq_destroy(tq); -out: - rw_destroy(&(rwp->rw_rwlock)); - kfree(rwp); - - return rc; -} - -static int -splat_rwlock_test5(struct file *file, void *arg) -{ - rw_priv_t *rwp; - int rc = -EINVAL; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - rw_enter(&rwp->rw_rwlock, RW_WRITER); - if (!RW_WRITE_HELD(&rwp->rw_rwlock)) { - splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, - "rwlock should be write lock: %d\n", - RW_WRITE_HELD(&rwp->rw_rwlock)); - goto out; - } - - rw_downgrade(&rwp->rw_rwlock); - if (!RW_READ_HELD(&rwp->rw_rwlock)) { - splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, - "rwlock should be read lock: %d\n", - RW_READ_HELD(&rwp->rw_rwlock)); - goto out; - } - - rc = 0; - splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "%s", - "rwlock properly downgraded\n"); -out: - rw_exit(&rwp->rw_rwlock); - rw_destroy(&rwp->rw_rwlock); - kfree(rwp); - - return rc; -} - -static int -splat_rwlock_test6(struct file *file, void *arg) -{ - rw_priv_t *rwp; - int rc; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - rw_enter(&rwp->rw_rwlock, RW_READER); - if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) != - SPL_RWSEM_SINGLE_READER_VALUE) { - splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, - "We assumed single reader rwsem->count " - "should be %ld, but is %ld\n", - (long int)SPL_RWSEM_SINGLE_READER_VALUE, - (long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock))); - rc = -ENOLCK; - goto out; - } - rw_exit(&rwp->rw_rwlock); - - rw_enter(&rwp->rw_rwlock, RW_WRITER); - if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) != - SPL_RWSEM_SINGLE_WRITER_VALUE) { - splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, - "We assumed single writer rwsem->count " - "should be %ld, but is %ld\n", - (long int)SPL_RWSEM_SINGLE_WRITER_VALUE, - (long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock))); - rc = -ENOLCK; - goto out; - } - rc = 0; - splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "%s", - "rwsem->count same as we assumed\n"); -out: - rw_exit(&rwp->rw_rwlock); - rw_destroy(&rwp->rw_rwlock); - kfree(rwp); - - return rc; -} - -static int -splat_rwlock_test7(struct file *file, void *arg) -{ - rw_priv_t *rwp; - int rc; - - rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL); - if (rwp == NULL) - return -ENOMEM; - - splat_init_rw_priv(rwp, file); - - rw_enter(&rwp->rw_rwlock, RW_READER); - if (!RW_READ_HELD(&rwp->rw_rwlock)) { - splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, - "rwlock should be read lock: %d\n", - RW_READ_HELD(&rwp->rw_rwlock)); - rc = -ENOLCK; - goto out; - } - - /* With one reader upgrade should never fail. */ - rc = rw_tryupgrade(&rwp->rw_rwlock); - if (!rc) { - splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, - "rwlock failed upgrade from reader: %d\n", - RW_READ_HELD(&rwp->rw_rwlock)); - rc = -ENOLCK; - goto out; - } - - if (RW_READ_HELD(&rwp->rw_rwlock) || !RW_WRITE_HELD(&rwp->rw_rwlock)) { - splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "rwlock should " - "have 0 (not %d) reader and 1 (not %d) writer\n", - RW_READ_HELD(&rwp->rw_rwlock), - RW_WRITE_HELD(&rwp->rw_rwlock)); - goto out; - } - - rc = 0; - splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "%s", - "rwlock properly upgraded\n"); -out: - rw_exit(&rwp->rw_rwlock); - rw_destroy(&rwp->rw_rwlock); - kfree(rwp); - - return rc; -} - -splat_subsystem_t * -splat_rwlock_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_RWLOCK_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_RWLOCK_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_RWLOCK; - - splat_test_init(sub, SPLAT_RWLOCK_TEST1_NAME, SPLAT_RWLOCK_TEST1_DESC, - SPLAT_RWLOCK_TEST1_ID, splat_rwlock_test1); - splat_test_init(sub, SPLAT_RWLOCK_TEST2_NAME, SPLAT_RWLOCK_TEST2_DESC, - SPLAT_RWLOCK_TEST2_ID, splat_rwlock_test2); - splat_test_init(sub, SPLAT_RWLOCK_TEST3_NAME, SPLAT_RWLOCK_TEST3_DESC, - SPLAT_RWLOCK_TEST3_ID, splat_rwlock_test3); - splat_test_init(sub, SPLAT_RWLOCK_TEST4_NAME, SPLAT_RWLOCK_TEST4_DESC, - SPLAT_RWLOCK_TEST4_ID, splat_rwlock_test4); - splat_test_init(sub, SPLAT_RWLOCK_TEST5_NAME, SPLAT_RWLOCK_TEST5_DESC, - SPLAT_RWLOCK_TEST5_ID, splat_rwlock_test5); - splat_test_init(sub, SPLAT_RWLOCK_TEST6_NAME, SPLAT_RWLOCK_TEST6_DESC, - SPLAT_RWLOCK_TEST6_ID, splat_rwlock_test6); - splat_test_init(sub, SPLAT_RWLOCK_TEST7_NAME, SPLAT_RWLOCK_TEST7_DESC, - SPLAT_RWLOCK_TEST7_ID, splat_rwlock_test7); - - return sub; -} - -void -splat_rwlock_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_RWLOCK_TEST7_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST6_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST5_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST4_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST3_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST2_ID); - splat_test_fini(sub, SPLAT_RWLOCK_TEST1_ID); - kfree(sub); -} - -int -splat_rwlock_id(void) { - return SPLAT_SUBSYSTEM_RWLOCK; -} diff --git a/module/splat/splat-taskq.c b/module/splat/splat-taskq.c deleted file mode 100644 index ff73e103a..000000000 --- a/module/splat/splat-taskq.c +++ /dev/null @@ -1,1548 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Task Queue Tests. - */ - -#include <sys/kmem.h> -#include <sys/vmem.h> -#include <sys/random.h> -#include <sys/taskq.h> -#include <sys/time.h> -#include <sys/timer.h> -#include <linux/delay.h> -#include "splat-internal.h" - -#define SPLAT_TASKQ_NAME "taskq" -#define SPLAT_TASKQ_DESC "Kernel Task Queue Tests" - -#define SPLAT_TASKQ_TEST1_ID 0x0201 -#define SPLAT_TASKQ_TEST1_NAME "single" -#define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task" - -#define SPLAT_TASKQ_TEST2_ID 0x0202 -#define SPLAT_TASKQ_TEST2_NAME "multiple" -#define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks" - -#define SPLAT_TASKQ_TEST3_ID 0x0203 -#define SPLAT_TASKQ_TEST3_NAME "system" -#define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks" - -#define SPLAT_TASKQ_TEST4_ID 0x0204 -#define SPLAT_TASKQ_TEST4_NAME "wait" -#define SPLAT_TASKQ_TEST4_DESC "Multiple task waiting" - -#define SPLAT_TASKQ_TEST5_ID 0x0205 -#define SPLAT_TASKQ_TEST5_NAME "order" -#define SPLAT_TASKQ_TEST5_DESC "Correct task ordering" - -#define SPLAT_TASKQ_TEST6_ID 0x0206 -#define SPLAT_TASKQ_TEST6_NAME "front" -#define SPLAT_TASKQ_TEST6_DESC "Correct ordering with TQ_FRONT flag" - -#define SPLAT_TASKQ_TEST7_ID 0x0207 -#define SPLAT_TASKQ_TEST7_NAME "recurse" -#define SPLAT_TASKQ_TEST7_DESC "Single task queue, recursive dispatch" - -#define SPLAT_TASKQ_TEST8_ID 0x0208 -#define SPLAT_TASKQ_TEST8_NAME "contention" -#define SPLAT_TASKQ_TEST8_DESC "1 queue, 100 threads, 131072 tasks" - -#define SPLAT_TASKQ_TEST9_ID 0x0209 -#define SPLAT_TASKQ_TEST9_NAME "delay" -#define SPLAT_TASKQ_TEST9_DESC "Delayed task execution" - -#define SPLAT_TASKQ_TEST10_ID 0x020a -#define SPLAT_TASKQ_TEST10_NAME "cancel" -#define SPLAT_TASKQ_TEST10_DESC "Cancel task execution" - -#define SPLAT_TASKQ_TEST11_ID 0x020b -#define SPLAT_TASKQ_TEST11_NAME "dynamic" -#define SPLAT_TASKQ_TEST11_DESC "Dynamic task queue thread creation" - -#define SPLAT_TASKQ_ORDER_MAX 8 -#define SPLAT_TASKQ_DEPTH_MAX 16 - - -typedef struct splat_taskq_arg { - int flag; - int id; - atomic_t *count; - int order[SPLAT_TASKQ_ORDER_MAX]; - unsigned int depth; - clock_t expire; - taskq_t *tq; - taskq_ent_t *tqe; - spinlock_t lock; - struct file *file; - const char *name; -} splat_taskq_arg_t; - -typedef struct splat_taskq_id { - int id; - splat_taskq_arg_t *arg; -} splat_taskq_id_t; - -/* - * Create a taskq, queue a task, wait until task completes, ensure - * task ran properly, cleanup taskq. - */ -static void -splat_taskq_test13_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - - ASSERT(tq_arg); - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME, - "Taskq '%s' function '%s' setting flag\n", - tq_arg->name, sym2str(splat_taskq_test13_func)); - tq_arg->flag = 1; -} - -static int -splat_taskq_test1_impl(struct file *file, void *arg, boolean_t prealloc) -{ - taskq_t *tq; - taskqid_t id; - splat_taskq_arg_t tq_arg; - taskq_ent_t *tqe; - - tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP); - taskq_init_ent(tqe); - - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, - "Taskq '%s' creating (%s dispatch)\n", - SPLAT_TASKQ_TEST1_NAME, - prealloc ? "prealloc" : "dynamic"); - if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, - "Taskq '%s' create failed\n", - SPLAT_TASKQ_TEST1_NAME); - kmem_free(tqe, sizeof (taskq_ent_t)); - return -EINVAL; - } - - tq_arg.flag = 0; - tq_arg.id = 0; - tq_arg.file = file; - tq_arg.name = SPLAT_TASKQ_TEST1_NAME; - - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, - "Taskq '%s' function '%s' dispatching\n", - tq_arg.name, sym2str(splat_taskq_test13_func)); - if (prealloc) { - taskq_dispatch_ent(tq, splat_taskq_test13_func, - &tq_arg, TQ_SLEEP, tqe); - id = tqe->tqent_id; - } else { - id = taskq_dispatch(tq, splat_taskq_test13_func, - &tq_arg, TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, - "Taskq '%s' function '%s' dispatch failed\n", - tq_arg.name, sym2str(splat_taskq_test13_func)); - kmem_free(tqe, sizeof (taskq_ent_t)); - taskq_destroy(tq); - return -EINVAL; - } - - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n", - tq_arg.name); - taskq_wait(tq); - splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n", - tq_arg.name); - - kmem_free(tqe, sizeof (taskq_ent_t)); - taskq_destroy(tq); - - return (tq_arg.flag) ? 0 : -EINVAL; -} - -static int -splat_taskq_test1(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test1_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test1_impl(file, arg, B_TRUE); - - return rc; -} - -/* - * Create multiple taskq's, each with multiple tasks, wait until - * all tasks complete, ensure all tasks ran properly and in the - * correct order. Run order must be the same as the order submitted - * because we only have 1 thread per taskq. Finally cleanup the taskq. - */ -static void -splat_taskq_test2_func1(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - - ASSERT(tq_arg); - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' function '%s' flag = %d = %d * 2\n", - tq_arg->name, tq_arg->id, - sym2str(splat_taskq_test2_func1), - tq_arg->flag * 2, tq_arg->flag); - tq_arg->flag *= 2; -} - -static void -splat_taskq_test2_func2(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - - ASSERT(tq_arg); - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' function '%s' flag = %d = %d + 1\n", - tq_arg->name, tq_arg->id, - sym2str(splat_taskq_test2_func2), - tq_arg->flag + 1, tq_arg->flag); - tq_arg->flag += 1; -} - -#define TEST2_TASKQS 8 -#define TEST2_THREADS_PER_TASKQ 1 - -static int -splat_taskq_test2_impl(struct file *file, void *arg, boolean_t prealloc) { - taskq_t *tq[TEST2_TASKQS] = { NULL }; - taskqid_t id; - splat_taskq_arg_t *tq_args[TEST2_TASKQS] = { NULL }; - taskq_ent_t *func1_tqes = NULL; - taskq_ent_t *func2_tqes = NULL; - int i, rc = 0; - - func1_tqes = kmalloc(sizeof(*func1_tqes) * TEST2_TASKQS, GFP_KERNEL); - if (func1_tqes == NULL) { - rc = -ENOMEM; - goto out; - } - - func2_tqes = kmalloc(sizeof(*func2_tqes) * TEST2_TASKQS, GFP_KERNEL); - if (func2_tqes == NULL) { - rc = -ENOMEM; - goto out; - } - - for (i = 0; i < TEST2_TASKQS; i++) { - taskq_init_ent(&func1_tqes[i]); - taskq_init_ent(&func2_tqes[i]); - - tq_args[i] = kmalloc(sizeof (splat_taskq_arg_t), GFP_KERNEL); - if (tq_args[i] == NULL) { - rc = -ENOMEM; - break; - } - - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' creating (%s dispatch)\n", - SPLAT_TASKQ_TEST2_NAME, i, - prealloc ? "prealloc" : "dynamic"); - if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME, - TEST2_THREADS_PER_TASKQ, - defclsyspri, 50, INT_MAX, - TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' create failed\n", - SPLAT_TASKQ_TEST2_NAME, i); - rc = -EINVAL; - break; - } - - tq_args[i]->flag = i; - tq_args[i]->id = i; - tq_args[i]->file = file; - tq_args[i]->name = SPLAT_TASKQ_TEST2_NAME; - - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' function '%s' dispatching\n", - tq_args[i]->name, tq_args[i]->id, - sym2str(splat_taskq_test2_func1)); - if (prealloc) { - taskq_dispatch_ent(tq[i], splat_taskq_test2_func1, - tq_args[i], TQ_SLEEP, &func1_tqes[i]); - id = func1_tqes[i].tqent_id; - } else { - id = taskq_dispatch(tq[i], splat_taskq_test2_func1, - tq_args[i], TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' function '%s' dispatch " - "failed\n", tq_args[i]->name, tq_args[i]->id, - sym2str(splat_taskq_test2_func1)); - rc = -EINVAL; - break; - } - - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' function '%s' dispatching\n", - tq_args[i]->name, tq_args[i]->id, - sym2str(splat_taskq_test2_func2)); - if (prealloc) { - taskq_dispatch_ent(tq[i], splat_taskq_test2_func2, - tq_args[i], TQ_SLEEP, &func2_tqes[i]); - id = func2_tqes[i].tqent_id; - } else { - id = taskq_dispatch(tq[i], splat_taskq_test2_func2, - tq_args[i], TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq " - "'%s/%d' function '%s' dispatch failed\n", - tq_args[i]->name, tq_args[i]->id, - sym2str(splat_taskq_test2_func2)); - rc = -EINVAL; - break; - } - } - - /* When rc is set we're effectively just doing cleanup here, so - * ignore new errors in that case. They just cause noise. */ - for (i = 0; i < TEST2_TASKQS; i++) { - if (tq_args[i] == NULL) - continue; - - if (tq[i] != NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' waiting\n", - tq_args[i]->name, tq_args[i]->id); - taskq_wait(tq[i]); - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d; destroying\n", - tq_args[i]->name, tq_args[i]->id); - - taskq_destroy(tq[i]); - - if (!rc && tq_args[i]->flag != ((i * 2) + 1)) { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' processed tasks " - "out of order; %d != %d\n", - tq_args[i]->name, tq_args[i]->id, - tq_args[i]->flag, i * 2 + 1); - rc = -EINVAL; - } else { - splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, - "Taskq '%s/%d' processed tasks " - "in the correct order; %d == %d\n", - tq_args[i]->name, tq_args[i]->id, - tq_args[i]->flag, i * 2 + 1); - } - - kfree(tq_args[i]); - } - } -out: - if (func1_tqes) - kfree(func1_tqes); - - if (func2_tqes) - kfree(func2_tqes); - - return rc; -} - -static int -splat_taskq_test2(struct file *file, void *arg) { - int rc; - - rc = splat_taskq_test2_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test2_impl(file, arg, B_TRUE); - - return rc; -} - -/* - * Use the global system task queue with a single task, wait until task - * completes, ensure task ran properly. - */ -static int -splat_taskq_test3_impl(struct file *file, void *arg, boolean_t prealloc) -{ - taskqid_t id; - splat_taskq_arg_t *tq_arg; - taskq_ent_t *tqe; - int error; - - tq_arg = kmem_alloc(sizeof (splat_taskq_arg_t), KM_SLEEP); - tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP); - taskq_init_ent(tqe); - - tq_arg->flag = 0; - tq_arg->id = 0; - tq_arg->file = file; - tq_arg->name = SPLAT_TASKQ_TEST3_NAME; - - splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, - "Taskq '%s' function '%s' %s dispatch\n", - tq_arg->name, sym2str(splat_taskq_test13_func), - prealloc ? "prealloc" : "dynamic"); - if (prealloc) { - taskq_dispatch_ent(system_taskq, splat_taskq_test13_func, - tq_arg, TQ_SLEEP, tqe); - id = tqe->tqent_id; - } else { - id = taskq_dispatch(system_taskq, splat_taskq_test13_func, - tq_arg, TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, - "Taskq '%s' function '%s' dispatch failed\n", - tq_arg->name, sym2str(splat_taskq_test13_func)); - kmem_free(tqe, sizeof (taskq_ent_t)); - kmem_free(tq_arg, sizeof (splat_taskq_arg_t)); - return -EINVAL; - } - - splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n", - tq_arg->name); - taskq_wait(system_taskq); - - error = (tq_arg->flag) ? 0 : -EINVAL; - - kmem_free(tqe, sizeof (taskq_ent_t)); - kmem_free(tq_arg, sizeof (splat_taskq_arg_t)); - - return (error); -} - -static int -splat_taskq_test3(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test3_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test3_impl(file, arg, B_TRUE); - - return rc; -} - -/* - * Create a taskq and dispatch a large number of tasks to the queue. - * Then use taskq_wait() to block until all the tasks complete, then - * cross check that all the tasks ran by checking the shared atomic - * counter which is incremented in the task function. - * - * First we try with a large 'maxalloc' value, then we try with a small one. - * We should not drop tasks when TQ_SLEEP is used in taskq_dispatch(), even - * if the number of pending tasks is above maxalloc. - */ -static void -splat_taskq_test4_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - ASSERT(tq_arg); - - atomic_inc(tq_arg->count); -} - -static int -splat_taskq_test4_common(struct file *file, void *arg, int minalloc, - int maxalloc, int nr_tasks, boolean_t prealloc) -{ - taskq_t *tq; - taskqid_t id; - splat_taskq_arg_t tq_arg; - taskq_ent_t *tqes; - atomic_t count; - int i, j, rc = 0; - - tqes = kmalloc(sizeof(*tqes) * nr_tasks, GFP_KERNEL); - if (tqes == NULL) - return -ENOMEM; - - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, - "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n", - SPLAT_TASKQ_TEST4_NAME, - prealloc ? "prealloc" : "dynamic", - minalloc, maxalloc, nr_tasks); - if ((tq = taskq_create(SPLAT_TASKQ_TEST4_NAME, 1, defclsyspri, - minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, - "Taskq '%s' create failed\n", - SPLAT_TASKQ_TEST4_NAME); - rc = -EINVAL; - goto out_free; - } - - tq_arg.file = file; - tq_arg.name = SPLAT_TASKQ_TEST4_NAME; - tq_arg.count = &count; - - for (i = 1; i <= nr_tasks; i *= 2) { - atomic_set(tq_arg.count, 0); - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, - "Taskq '%s' function '%s' dispatched %d times\n", - tq_arg.name, sym2str(splat_taskq_test4_func), i); - - for (j = 0; j < i; j++) { - taskq_init_ent(&tqes[j]); - - if (prealloc) { - taskq_dispatch_ent(tq, splat_taskq_test4_func, - &tq_arg, TQ_SLEEP, &tqes[j]); - id = tqes[j].tqent_id; - } else { - id = taskq_dispatch(tq, splat_taskq_test4_func, - &tq_arg, TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, - "Taskq '%s' function '%s' dispatch " - "%d failed\n", tq_arg.name, - sym2str(splat_taskq_test4_func), j); - rc = -EINVAL; - goto out; - } - } - - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' " - "waiting for %d dispatches\n", tq_arg.name, i); - taskq_wait(tq); - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' " - "%d/%d dispatches finished\n", tq_arg.name, - atomic_read(&count), i); - if (atomic_read(&count) != i) { - rc = -ERANGE; - goto out; - - } - } -out: - splat_vprint(file, SPLAT_TASKQ_TEST4_NAME, "Taskq '%s' destroying\n", - tq_arg.name); - taskq_destroy(tq); - -out_free: - kfree(tqes); - - return rc; -} - -static int -splat_taskq_test4_impl(struct file *file, void *arg, boolean_t prealloc) -{ - int rc; - - rc = splat_taskq_test4_common(file, arg, 50, INT_MAX, 1024, prealloc); - if (rc) - return rc; - - rc = splat_taskq_test4_common(file, arg, 1, 1, 32, prealloc); - - return rc; -} - -static int -splat_taskq_test4(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test4_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test4_impl(file, arg, B_TRUE); - - return rc; -} - -/* - * Create a taskq and dispatch a specific sequence of tasks carefully - * crafted to validate the order in which tasks are processed. When - * there are multiple worker threads each thread will process the - * next pending task as soon as it completes its current task. This - * means that tasks do not strictly complete in order in which they - * were dispatched (increasing task id). This is fine but we need to - * verify taskq_wait_outstanding() blocks until the passed task id and - * all lower task ids complete. We do this by dispatching the following - * specific sequence of tasks each of which block for N time units. - * We then use taskq_wait_outstanding() to unblock at specific task id and - * verify the only the expected task ids have completed and in the - * correct order. The two cases of interest are: - * - * 1) Task ids larger than the waited for task id can run and - * complete as long as there is an available worker thread. - * 2) All task ids lower than the waited one must complete before - * unblocking even if the waited task id itself has completed. - * - * The following table shows each task id and how they will be - * scheduled. Each rows represent one time unit and each column - * one of the three worker threads. The places taskq_wait_outstanding() - * must unblock for a specific id are identified as well as the - * task ids which must have completed and their order. - * - * +-----+ <--- taskq_wait_outstanding(tq, 8) unblocks - * | | Required Completion Order: 1,2,4,5,3,8,6,7 - * +-----+ | - * | | | - * | | +-----+ - * | | | 8 | - * | | +-----+ <--- taskq_wait_outstanding(tq, 3) unblocks - * | | 7 | | Required Completion Order: 1,2,4,5,3 - * | +-----+ | - * | 6 | | | - * +-----+ | | - * | | 5 | | - * | +-----+ | - * | 4 | | | - * +-----+ | | - * | 1 | 2 | 3 | - * +-----+-----+-----+ - * - */ -static void -splat_taskq_test5_func(void *arg) -{ - splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg; - splat_taskq_arg_t *tq_arg = tq_id->arg; - int factor; - - /* Delays determined by above table */ - switch (tq_id->id) { - default: factor = 0; break; - case 1: case 8: factor = 1; break; - case 2: case 4: case 5: factor = 2; break; - case 6: case 7: factor = 4; break; - case 3: factor = 5; break; - } - - msleep(factor * 100); - splat_vprint(tq_arg->file, tq_arg->name, - "Taskqid %d complete for taskq '%s'\n", - tq_id->id, tq_arg->name); - - spin_lock(&tq_arg->lock); - tq_arg->order[tq_arg->flag] = tq_id->id; - tq_arg->flag++; - spin_unlock(&tq_arg->lock); -} - -static int -splat_taskq_test_order(splat_taskq_arg_t *tq_arg, int *order) -{ - int i, j; - - for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) { - if (tq_arg->order[i] != order[i]) { - splat_vprint(tq_arg->file, tq_arg->name, - "Taskq '%s' incorrect completion " - "order\n", tq_arg->name); - splat_vprint(tq_arg->file, tq_arg->name, - "%s", "Expected { "); - - for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++) - splat_print(tq_arg->file, "%d ", order[j]); - - splat_print(tq_arg->file, "%s", "}\n"); - splat_vprint(tq_arg->file, tq_arg->name, - "%s", "Got { "); - - for (j = 0; j < SPLAT_TASKQ_ORDER_MAX; j++) - splat_print(tq_arg->file, "%d ", - tq_arg->order[j]); - - splat_print(tq_arg->file, "%s", "}\n"); - return -EILSEQ; - } - } - - splat_vprint(tq_arg->file, tq_arg->name, - "Taskq '%s' validated correct completion order\n", - tq_arg->name); - - return 0; -} - -static int -splat_taskq_test5_impl(struct file *file, void *arg, boolean_t prealloc) -{ - taskq_t *tq; - taskqid_t id; - splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX]; - splat_taskq_arg_t tq_arg; - int order1[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,0,0,0 }; - int order2[SPLAT_TASKQ_ORDER_MAX] = { 1,2,4,5,3,8,6,7 }; - taskq_ent_t *tqes; - int i, rc = 0; - - tqes = kmem_alloc(sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX, KM_SLEEP); - memset(tqes, 0, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX); - - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, - "Taskq '%s' creating (%s dispatch)\n", - SPLAT_TASKQ_TEST5_NAME, - prealloc ? "prealloc" : "dynamic"); - if ((tq = taskq_create(SPLAT_TASKQ_TEST5_NAME, 3, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, - "Taskq '%s' create failed\n", - SPLAT_TASKQ_TEST5_NAME); - return -EINVAL; - } - - tq_arg.flag = 0; - memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX); - spin_lock_init(&tq_arg.lock); - tq_arg.file = file; - tq_arg.name = SPLAT_TASKQ_TEST5_NAME; - - for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) { - taskq_init_ent(&tqes[i]); - - tq_id[i].id = i + 1; - tq_id[i].arg = &tq_arg; - - if (prealloc) { - taskq_dispatch_ent(tq, splat_taskq_test5_func, - &tq_id[i], TQ_SLEEP, &tqes[i]); - id = tqes[i].tqent_id; - } else { - id = taskq_dispatch(tq, splat_taskq_test5_func, - &tq_id[i], TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, - "Taskq '%s' function '%s' dispatch failed\n", - tq_arg.name, sym2str(splat_taskq_test5_func)); - rc = -EINVAL; - goto out; - } - - if (tq_id[i].id != id) { - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, - "Taskq '%s' expected taskqid %d got %d\n", - tq_arg.name, (int)tq_id[i].id, (int)id); - rc = -EINVAL; - goto out; - } - } - - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' " - "waiting for taskqid %d completion\n", tq_arg.name, 3); - taskq_wait_outstanding(tq, 3); - if ((rc = splat_taskq_test_order(&tq_arg, order1))) - goto out; - - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, "Taskq '%s' " - "waiting for taskqid %d completion\n", tq_arg.name, 8); - taskq_wait_outstanding(tq, 8); - rc = splat_taskq_test_order(&tq_arg, order2); - -out: - splat_vprint(file, SPLAT_TASKQ_TEST5_NAME, - "Taskq '%s' destroying\n", tq_arg.name); - taskq_destroy(tq); - - kmem_free(tqes, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX); - - return rc; -} - -static int -splat_taskq_test5(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test5_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test5_impl(file, arg, B_TRUE); - - return rc; -} - -/* - * Create a single task queue with three threads. Dispatch 8 tasks, - * setting TQ_FRONT on only the last three. Sleep after - * dispatching tasks 1-3 to ensure they will run and hold the threads - * busy while we dispatch the remaining tasks. Verify that tasks 6-8 - * run before task 4-5. - * - * The following table shows each task id and how they will be - * scheduled. Each rows represent one time unit and each column - * one of the three worker threads. - * - * NB: The Horizontal Line is the LAST Time unit consumed by the Task, - * and must be included in the factor calculation. - * T - * 17-> +-----+ - * 16 | T6 | - * 15-> +-----+ | - * 14 | T6 | | - * 13-> | | 5 +-----+ - * 12 | | | T6 | - * 11-> | +-----| | - * 10 | 4 | T6 | | - * 9-> +-----+ | 8 | - * 8 | T5 | | | - * 7-> | | 7 +-----+ - * 6 | | | T7 | - * 5-> | +-----+ | - * 4 | 6 | T5 | | - * 3-> +-----+ | | - * 2 | T3 | | | - * 1 | 1 | 2 | 3 | - * 0 +-----+-----+-----+ - * - */ -static void -splat_taskq_test6_func(void *arg) -{ - /* Delays determined by above table */ - static const int factor[SPLAT_TASKQ_ORDER_MAX+1] = {0,3,5,7,6,6,5,6,6}; - - splat_taskq_id_t *tq_id = (splat_taskq_id_t *)arg; - splat_taskq_arg_t *tq_arg = tq_id->arg; - - splat_vprint(tq_arg->file, tq_arg->name, - "Taskqid %d starting for taskq '%s'\n", - tq_id->id, tq_arg->name); - - if (tq_id->id < SPLAT_TASKQ_ORDER_MAX+1) { - msleep(factor[tq_id->id] * 50); - } - - spin_lock(&tq_arg->lock); - tq_arg->order[tq_arg->flag] = tq_id->id; - tq_arg->flag++; - spin_unlock(&tq_arg->lock); - - splat_vprint(tq_arg->file, tq_arg->name, - "Taskqid %d complete for taskq '%s'\n", - tq_id->id, tq_arg->name); -} - -static int -splat_taskq_test6_impl(struct file *file, void *arg, boolean_t prealloc) -{ - taskq_t *tq; - taskqid_t id; - splat_taskq_id_t tq_id[SPLAT_TASKQ_ORDER_MAX]; - splat_taskq_arg_t tq_arg; - int order[SPLAT_TASKQ_ORDER_MAX] = { 1,2,3,6,7,8,4,5 }; - taskq_ent_t *tqes; - int i, rc = 0; - uint_t tflags; - - tqes = kmem_alloc(sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX, KM_SLEEP); - memset(tqes, 0, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX); - - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, - "Taskq '%s' creating (%s dispatch)\n", - SPLAT_TASKQ_TEST6_NAME, - prealloc ? "prealloc" : "dynamic"); - if ((tq = taskq_create(SPLAT_TASKQ_TEST6_NAME, 3, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, - "Taskq '%s' create failed\n", - SPLAT_TASKQ_TEST6_NAME); - return -EINVAL; - } - - tq_arg.flag = 0; - memset(&tq_arg.order, 0, sizeof(int) * SPLAT_TASKQ_ORDER_MAX); - spin_lock_init(&tq_arg.lock); - tq_arg.file = file; - tq_arg.name = SPLAT_TASKQ_TEST6_NAME; - - for (i = 0; i < SPLAT_TASKQ_ORDER_MAX; i++) { - taskq_init_ent(&tqes[i]); - - tq_id[i].id = i + 1; - tq_id[i].arg = &tq_arg; - tflags = TQ_SLEEP; - if (i > 4) - tflags |= TQ_FRONT; - - if (prealloc) { - taskq_dispatch_ent(tq, splat_taskq_test6_func, - &tq_id[i], tflags, &tqes[i]); - id = tqes[i].tqent_id; - } else { - id = taskq_dispatch(tq, splat_taskq_test6_func, - &tq_id[i], tflags); - } - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, - "Taskq '%s' function '%s' dispatch failed\n", - tq_arg.name, sym2str(splat_taskq_test6_func)); - rc = -EINVAL; - goto out; - } - - if (tq_id[i].id != id) { - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, - "Taskq '%s' expected taskqid %d got %d\n", - tq_arg.name, (int)tq_id[i].id, (int)id); - rc = -EINVAL; - goto out; - } - /* Sleep to let tasks 1-3 start executing. */ - if ( i == 2 ) - msleep(100); - } - - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, "Taskq '%s' " - "waiting for taskqid %d completion\n", tq_arg.name, - SPLAT_TASKQ_ORDER_MAX); - taskq_wait_outstanding(tq, SPLAT_TASKQ_ORDER_MAX); - rc = splat_taskq_test_order(&tq_arg, order); - -out: - splat_vprint(file, SPLAT_TASKQ_TEST6_NAME, - "Taskq '%s' destroying\n", tq_arg.name); - taskq_destroy(tq); - - kmem_free(tqes, sizeof(*tqes) * SPLAT_TASKQ_ORDER_MAX); - - return rc; -} - -static int -splat_taskq_test6(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test6_impl(file, arg, B_FALSE); - if (rc) - return rc; - - rc = splat_taskq_test6_impl(file, arg, B_TRUE); - - return rc; -} - -static void -splat_taskq_test7_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - taskqid_t id; - - ASSERT(tq_arg); - - if (tq_arg->depth >= SPLAT_TASKQ_DEPTH_MAX) - return; - - tq_arg->depth++; - - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' function '%s' dispatching (depth = %u)\n", - tq_arg->name, sym2str(splat_taskq_test7_func), - tq_arg->depth); - - if (tq_arg->tqe) { - VERIFY(taskq_empty_ent(tq_arg->tqe)); - taskq_dispatch_ent(tq_arg->tq, splat_taskq_test7_func, - tq_arg, TQ_SLEEP, tq_arg->tqe); - id = tq_arg->tqe->tqent_id; - } else { - id = taskq_dispatch(tq_arg->tq, splat_taskq_test7_func, - tq_arg, TQ_SLEEP); - } - - if (id == TASKQID_INVALID) { - splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' function '%s' dispatch failed " - "(depth = %u)\n", tq_arg->name, - sym2str(splat_taskq_test7_func), tq_arg->depth); - tq_arg->flag = -EINVAL; - return; - } -} - -static int -splat_taskq_test7_impl(struct file *file, void *arg, boolean_t prealloc) -{ - taskq_t *tq; - splat_taskq_arg_t *tq_arg; - taskq_ent_t *tqe; - int error; - - splat_vprint(file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' creating (%s dispatch)\n", - SPLAT_TASKQ_TEST7_NAME, - prealloc ? "prealloc" : "dynamic"); - if ((tq = taskq_create(SPLAT_TASKQ_TEST7_NAME, 1, defclsyspri, - 50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' create failed\n", - SPLAT_TASKQ_TEST7_NAME); - return -EINVAL; - } - - tq_arg = kmem_alloc(sizeof (splat_taskq_arg_t), KM_SLEEP); - tqe = kmem_alloc(sizeof (taskq_ent_t), KM_SLEEP); - - tq_arg->depth = 0; - tq_arg->flag = 0; - tq_arg->id = 0; - tq_arg->file = file; - tq_arg->name = SPLAT_TASKQ_TEST7_NAME; - tq_arg->tq = tq; - - if (prealloc) { - taskq_init_ent(tqe); - tq_arg->tqe = tqe; - } else { - tq_arg->tqe = NULL; - } - - splat_taskq_test7_func(tq_arg); - - if (tq_arg->flag == 0) { - splat_vprint(file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' waiting\n", tq_arg->name); - taskq_wait_outstanding(tq, SPLAT_TASKQ_DEPTH_MAX); - } - - error = (tq_arg->depth == SPLAT_TASKQ_DEPTH_MAX ? 0 : -EINVAL); - - splat_vprint(file, SPLAT_TASKQ_TEST7_NAME, - "Taskq '%s' destroying\n", tq_arg->name); - - kmem_free(tqe, sizeof (taskq_ent_t)); - kmem_free(tq_arg, sizeof (splat_taskq_arg_t)); - - taskq_destroy(tq); - - return (error); -} - -static int -splat_taskq_test7(struct file *file, void *arg) -{ - int rc; - - rc = splat_taskq_test7_impl(file, arg, B_FALSE); - if (rc) - return (rc); - - rc = splat_taskq_test7_impl(file, arg, B_TRUE); - - return (rc); -} - -static void -splat_taskq_throughput_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - ASSERT(tq_arg); - - atomic_inc(tq_arg->count); -} - -static int -splat_taskq_throughput(struct file *file, void *arg, const char *name, - int nthreads, int minalloc, int maxalloc, int flags, int tasks, - struct timespec *delta) -{ - taskq_t *tq; - taskqid_t id; - splat_taskq_arg_t tq_arg; - taskq_ent_t **tqes; - atomic_t count; - struct timespec start, stop; - int i, j, rc = 0; - - tqes = vmalloc(sizeof (*tqes) * tasks); - if (tqes == NULL) - return (-ENOMEM); - - memset(tqes, 0, sizeof (*tqes) * tasks); - - splat_vprint(file, name, "Taskq '%s' creating (%d/%d/%d/%d)\n", - name, nthreads, minalloc, maxalloc, tasks); - if ((tq = taskq_create(name, nthreads, defclsyspri, - minalloc, maxalloc, flags)) == NULL) { - splat_vprint(file, name, "Taskq '%s' create failed\n", name); - rc = -EINVAL; - goto out_free; - } - - tq_arg.file = file; - tq_arg.name = name; - tq_arg.count = &count; - atomic_set(tq_arg.count, 0); - - getnstimeofday(&start); - - for (i = 0; i < tasks; i++) { - tqes[i] = kmalloc(sizeof (taskq_ent_t), GFP_KERNEL); - if (tqes[i] == NULL) { - rc = -ENOMEM; - goto out; - } - - taskq_init_ent(tqes[i]); - taskq_dispatch_ent(tq, splat_taskq_throughput_func, - &tq_arg, TQ_SLEEP, tqes[i]); - id = tqes[i]->tqent_id; - - if (id == TASKQID_INVALID) { - splat_vprint(file, name, "Taskq '%s' function '%s' " - "dispatch %d failed\n", tq_arg.name, - sym2str(splat_taskq_throughput_func), i); - rc = -EINVAL; - goto out; - } - } - - splat_vprint(file, name, "Taskq '%s' waiting for %d dispatches\n", - tq_arg.name, tasks); - - taskq_wait(tq); - - if (delta != NULL) { - getnstimeofday(&stop); - *delta = timespec_sub(stop, start); - } - - splat_vprint(file, name, "Taskq '%s' %d/%d dispatches finished\n", - tq_arg.name, atomic_read(tq_arg.count), tasks); - - if (atomic_read(tq_arg.count) != tasks) - rc = -ERANGE; - -out: - splat_vprint(file, name, "Taskq '%s' destroying\n", tq_arg.name); - taskq_destroy(tq); -out_free: - for (j = 0; j < tasks && tqes[j] != NULL; j++) - kfree(tqes[j]); - - vfree(tqes); - - return (rc); -} - -/* - * Create a taskq with 100 threads and dispatch a huge number of trivial - * tasks to generate contention on tq->tq_lock. This test should always - * pass. The purpose is to provide a benchmark for measuring the - * effectiveness of taskq optimizations. - */ -#define TEST8_NUM_TASKS 0x20000 -#define TEST8_THREADS_PER_TASKQ 100 - -static int -splat_taskq_test8(struct file *file, void *arg) -{ - return (splat_taskq_throughput(file, arg, - SPLAT_TASKQ_TEST8_NAME, TEST8_THREADS_PER_TASKQ, - 1, INT_MAX, TASKQ_PREPOPULATE, TEST8_NUM_TASKS, NULL)); -} - -/* - * Create a taskq and dispatch a number of delayed tasks to the queue. - * For each task verify that it was run no early than requested. - */ -static void -splat_taskq_test9_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - ASSERT(tq_arg); - - if (ddi_time_after_eq(ddi_get_lbolt(), tq_arg->expire)) - atomic_inc(tq_arg->count); - - kmem_free(tq_arg, sizeof(splat_taskq_arg_t)); -} - -static int -splat_taskq_test9(struct file *file, void *arg) -{ - taskq_t *tq; - atomic_t count; - int i, rc = 0; - int minalloc = 1; - int maxalloc = 10; - int nr_tasks = 100; - - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, - "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n", - SPLAT_TASKQ_TEST9_NAME, "delay", minalloc, maxalloc, nr_tasks); - if ((tq = taskq_create(SPLAT_TASKQ_TEST9_NAME, 3, defclsyspri, - minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, - "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST9_NAME); - return -EINVAL; - } - - atomic_set(&count, 0); - - for (i = 1; i <= nr_tasks; i++) { - splat_taskq_arg_t *tq_arg; - taskqid_t id; - uint32_t rnd; - - /* A random timeout in jiffies of at most 5 seconds */ - get_random_bytes((void *)&rnd, 4); - rnd = rnd % (5 * HZ); - - tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP); - tq_arg->file = file; - tq_arg->name = SPLAT_TASKQ_TEST9_NAME; - tq_arg->expire = ddi_get_lbolt() + rnd; - tq_arg->count = &count; - - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, - "Taskq '%s' delay dispatch %u jiffies\n", - SPLAT_TASKQ_TEST9_NAME, rnd); - - id = taskq_dispatch_delay(tq, splat_taskq_test9_func, - tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd); - - if (id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, - "Taskq '%s' delay dispatch failed\n", - SPLAT_TASKQ_TEST9_NAME); - kmem_free(tq_arg, sizeof(splat_taskq_arg_t)); - taskq_wait(tq); - rc = -EINVAL; - goto out; - } - } - - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' waiting for " - "%d delay dispatches\n", SPLAT_TASKQ_TEST9_NAME, nr_tasks); - - taskq_wait(tq); - if (atomic_read(&count) != nr_tasks) - rc = -ERANGE; - - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' %d/%d delay " - "dispatches finished on time\n", SPLAT_TASKQ_TEST9_NAME, - atomic_read(&count), nr_tasks); - splat_vprint(file, SPLAT_TASKQ_TEST9_NAME, "Taskq '%s' destroying\n", - SPLAT_TASKQ_TEST9_NAME); -out: - taskq_destroy(tq); - - return rc; -} - -/* - * Create a taskq and dispatch then cancel tasks in the queue. - */ -static void -splat_taskq_test10_func(void *arg) -{ - splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg; - uint8_t rnd; - - if (ddi_time_after_eq(ddi_get_lbolt(), tq_arg->expire)) - atomic_inc(tq_arg->count); - - /* Randomly sleep to further perturb the system */ - get_random_bytes((void *)&rnd, 1); - msleep(1 + (rnd % 9)); -} - -static int -splat_taskq_test10(struct file *file, void *arg) -{ - taskq_t *tq; - splat_taskq_arg_t **tqas; - atomic_t count; - int i, j, rc = 0; - int minalloc = 1; - int maxalloc = 10; - int nr_tasks = 100; - int canceled = 0; - int completed = 0; - int blocked = 0; - clock_t start, cancel; - - tqas = vmalloc(sizeof(*tqas) * nr_tasks); - if (tqas == NULL) - return -ENOMEM; - memset(tqas, 0, sizeof(*tqas) * nr_tasks); - - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, - "Taskq '%s' creating (%s dispatch) (%d/%d/%d)\n", - SPLAT_TASKQ_TEST10_NAME, "delay", minalloc, maxalloc, nr_tasks); - if ((tq = taskq_create(SPLAT_TASKQ_TEST10_NAME, 3, defclsyspri, - minalloc, maxalloc, TASKQ_PREPOPULATE)) == NULL) { - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, - "Taskq '%s' create failed\n", SPLAT_TASKQ_TEST10_NAME); - rc = -EINVAL; - goto out_free; - } - - atomic_set(&count, 0); - - for (i = 0; i < nr_tasks; i++) { - splat_taskq_arg_t *tq_arg; - uint32_t rnd; - - /* A random timeout in jiffies of at most 5 seconds */ - get_random_bytes((void *)&rnd, 4); - rnd = rnd % (5 * HZ); - - tq_arg = kmem_alloc(sizeof(splat_taskq_arg_t), KM_SLEEP); - tq_arg->file = file; - tq_arg->name = SPLAT_TASKQ_TEST10_NAME; - tq_arg->count = &count; - tqas[i] = tq_arg; - - /* - * Dispatch every 1/3 one immediately to mix it up, the cancel - * code is inherently racy and we want to try and provoke any - * subtle concurrently issues. - */ - if ((i % 3) == 0) { - tq_arg->expire = ddi_get_lbolt(); - tq_arg->id = taskq_dispatch(tq, splat_taskq_test10_func, - tq_arg, TQ_SLEEP); - } else { - tq_arg->expire = ddi_get_lbolt() + rnd; - tq_arg->id = taskq_dispatch_delay(tq, - splat_taskq_test10_func, - tq_arg, TQ_SLEEP, ddi_get_lbolt() + rnd); - } - - if (tq_arg->id == TASKQID_INVALID) { - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, - "Taskq '%s' dispatch failed\n", - SPLAT_TASKQ_TEST10_NAME); - kmem_free(tq_arg, sizeof(splat_taskq_arg_t)); - taskq_wait(tq); - rc = -EINVAL; - goto out; - } else { - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, - "Taskq '%s' dispatch %lu in %lu jiffies\n", - SPLAT_TASKQ_TEST10_NAME, (unsigned long)tq_arg->id, - !(i % 3) ? 0 : tq_arg->expire - ddi_get_lbolt()); - } - } - - /* - * Start randomly canceling tasks for the duration of the test. We - * happen to know the valid task id's will be in the range 1..nr_tasks - * because the taskq is private and was just created. However, we - * have no idea of a particular task has already executed or not. - */ - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' randomly " - "canceling task ids\n", SPLAT_TASKQ_TEST10_NAME); - - start = ddi_get_lbolt(); - i = 0; - - while (ddi_time_before(ddi_get_lbolt(), start + 5 * HZ)) { - taskqid_t id; - uint32_t rnd; - - i++; - cancel = ddi_get_lbolt(); - get_random_bytes((void *)&rnd, 4); - id = 1 + (rnd % nr_tasks); - rc = taskq_cancel_id(tq, id); - - /* - * Keep track of the results of the random cancels. - */ - if (rc == 0) { - canceled++; - } else if (rc == ENOENT) { - completed++; - } else if (rc == EBUSY) { - blocked++; - } else { - rc = -EINVAL; - break; - } - - /* - * Verify we never get blocked to long in taskq_cancel_id(). - * The worst case is 10ms if we happen to cancel the task - * which is currently executing. We allow a factor of 2x. - */ - if (ddi_get_lbolt() - cancel > HZ / 50) { - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, - "Taskq '%s' cancel for %lu took %lu\n", - SPLAT_TASKQ_TEST10_NAME, (unsigned long)id, - ddi_get_lbolt() - cancel); - rc = -ETIMEDOUT; - break; - } - - get_random_bytes((void *)&rnd, 4); - msleep(1 + (rnd % 100)); - rc = 0; - } - - taskq_wait(tq); - - /* - * Cross check the results of taskq_cancel_id() with the number of - * times the dispatched function actually ran successfully. - */ - if ((rc == 0) && (nr_tasks - canceled != atomic_read(&count))) - rc = -EDOM; - - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' %d attempts, " - "%d canceled, %d completed, %d blocked, %d/%d tasks run\n", - SPLAT_TASKQ_TEST10_NAME, i, canceled, completed, blocked, - atomic_read(&count), nr_tasks); - splat_vprint(file, SPLAT_TASKQ_TEST10_NAME, "Taskq '%s' destroying %d\n", - SPLAT_TASKQ_TEST10_NAME, rc); -out: - taskq_destroy(tq); -out_free: - for (j = 0; j < nr_tasks && tqas[j] != NULL; j++) - kmem_free(tqas[j], sizeof(splat_taskq_arg_t)); - vfree(tqas); - - return rc; -} - -/* - * Create a dynamic taskq with 100 threads and dispatch a huge number of - * trivial tasks. This will cause the taskq to grow quickly to its max - * thread count. This test should always pass. The purpose is to provide - * a benchmark for measuring the performance of dynamic taskqs. - */ -#define TEST11_NUM_TASKS 100000 -#define TEST11_THREADS_PER_TASKQ 100 - -static int -splat_taskq_test11(struct file *file, void *arg) -{ - struct timespec normal, dynamic; - int error; - - error = splat_taskq_throughput(file, arg, SPLAT_TASKQ_TEST11_NAME, - TEST11_THREADS_PER_TASKQ, 1, INT_MAX, - TASKQ_PREPOPULATE, TEST11_NUM_TASKS, &normal); - if (error) - return (error); - - error = splat_taskq_throughput(file, arg, SPLAT_TASKQ_TEST11_NAME, - TEST11_THREADS_PER_TASKQ, 1, INT_MAX, - TASKQ_PREPOPULATE | TASKQ_DYNAMIC, TEST11_NUM_TASKS, &dynamic); - if (error) - return (error); - - splat_vprint(file, SPLAT_TASKQ_TEST11_NAME, - "Timing taskq_wait(): normal=%ld.%09lds, dynamic=%ld.%09lds\n", - normal.tv_sec, normal.tv_nsec, - dynamic.tv_sec, dynamic.tv_nsec); - - /* A 10x increase in runtime is used to indicate a core problem. */ - if (((int64_t)dynamic.tv_sec * NANOSEC + (int64_t)dynamic.tv_nsec) > - (((int64_t)normal.tv_sec * NANOSEC + (int64_t)normal.tv_nsec) * 10)) - error = -ETIME; - - return (error); -} - -splat_subsystem_t * -splat_taskq_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_TASKQ; - - splat_test_init(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC, - SPLAT_TASKQ_TEST1_ID, splat_taskq_test1); - splat_test_init(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC, - SPLAT_TASKQ_TEST2_ID, splat_taskq_test2); - splat_test_init(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC, - SPLAT_TASKQ_TEST3_ID, splat_taskq_test3); - splat_test_init(sub, SPLAT_TASKQ_TEST4_NAME, SPLAT_TASKQ_TEST4_DESC, - SPLAT_TASKQ_TEST4_ID, splat_taskq_test4); - splat_test_init(sub, SPLAT_TASKQ_TEST5_NAME, SPLAT_TASKQ_TEST5_DESC, - SPLAT_TASKQ_TEST5_ID, splat_taskq_test5); - splat_test_init(sub, SPLAT_TASKQ_TEST6_NAME, SPLAT_TASKQ_TEST6_DESC, - SPLAT_TASKQ_TEST6_ID, splat_taskq_test6); - splat_test_init(sub, SPLAT_TASKQ_TEST7_NAME, SPLAT_TASKQ_TEST7_DESC, - SPLAT_TASKQ_TEST7_ID, splat_taskq_test7); - splat_test_init(sub, SPLAT_TASKQ_TEST8_NAME, SPLAT_TASKQ_TEST8_DESC, - SPLAT_TASKQ_TEST8_ID, splat_taskq_test8); - splat_test_init(sub, SPLAT_TASKQ_TEST9_NAME, SPLAT_TASKQ_TEST9_DESC, - SPLAT_TASKQ_TEST9_ID, splat_taskq_test9); - splat_test_init(sub, SPLAT_TASKQ_TEST10_NAME, SPLAT_TASKQ_TEST10_DESC, - SPLAT_TASKQ_TEST10_ID, splat_taskq_test10); - splat_test_init(sub, SPLAT_TASKQ_TEST11_NAME, SPLAT_TASKQ_TEST11_DESC, - SPLAT_TASKQ_TEST11_ID, splat_taskq_test11); - - return sub; -} - -void -splat_taskq_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_TASKQ_TEST11_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST10_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST9_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST8_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST7_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST6_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST5_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST4_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST3_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST2_ID); - splat_test_fini(sub, SPLAT_TASKQ_TEST1_ID); - - kfree(sub); -} - -int -splat_taskq_id(void) { - return SPLAT_SUBSYSTEM_TASKQ; -} diff --git a/module/splat/splat-thread.c b/module/splat/splat-thread.c deleted file mode 100644 index f2e6bf15e..000000000 --- a/module/splat/splat-thread.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Thread Tests. - */ - -#include <sys/thread.h> -#include <sys/random.h> -#include <linux/delay.h> -#include <linux/mm_compat.h> -#include <linux/wait_compat.h> -#include <linux/slab.h> -#include "splat-internal.h" - -#define SPLAT_THREAD_NAME "thread" -#define SPLAT_THREAD_DESC "Kernel Thread Tests" - -#define SPLAT_THREAD_TEST1_ID 0x0601 -#define SPLAT_THREAD_TEST1_NAME "create" -#define SPLAT_THREAD_TEST1_DESC "Validate thread creation" - -#define SPLAT_THREAD_TEST2_ID 0x0602 -#define SPLAT_THREAD_TEST2_NAME "exit" -#define SPLAT_THREAD_TEST2_DESC "Validate thread exit" - -#define SPLAT_THREAD_TEST3_ID 0x6003 -#define SPLAT_THREAD_TEST3_NAME "tsd" -#define SPLAT_THREAD_TEST3_DESC "Validate thread specific data" - -#define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL -#define SPLAT_THREAD_TEST_KEYS 32 -#define SPLAT_THREAD_TEST_THREADS 16 - -typedef struct thread_priv { - unsigned long tp_magic; - struct file *tp_file; - spinlock_t tp_lock; - spl_wait_queue_head_t tp_waitq; - uint_t tp_keys[SPLAT_THREAD_TEST_KEYS]; - int tp_rc; - int tp_count; - int tp_dtor_count; -} thread_priv_t; - -static int -splat_thread_rc(thread_priv_t *tp, int rc) -{ - int ret; - - spin_lock(&tp->tp_lock); - ret = (tp->tp_rc == rc); - spin_unlock(&tp->tp_lock); - - return ret; -} - -static int -splat_thread_count(thread_priv_t *tp, int count) -{ - int ret; - - spin_lock(&tp->tp_lock); - ret = (tp->tp_count == count); - spin_unlock(&tp->tp_lock); - - return ret; -} - -static void -splat_thread_work1(void *priv) -{ - thread_priv_t *tp = (thread_priv_t *)priv; - - spin_lock(&tp->tp_lock); - ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC); - tp->tp_rc = 1; - wake_up(&tp->tp_waitq); - spin_unlock(&tp->tp_lock); - - thread_exit(); -} - -static int -splat_thread_test1(struct file *file, void *arg) -{ - thread_priv_t tp; - kthread_t *thr; - - tp.tp_magic = SPLAT_THREAD_TEST_MAGIC; - tp.tp_file = file; - spin_lock_init(&tp.tp_lock); - init_waitqueue_head(&tp.tp_waitq); - tp.tp_rc = 0; - - thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work1, &tp, 0, - &p0, TS_RUN, defclsyspri); - /* Must never fail under Solaris, but we check anyway since this - * can happen in the linux SPL, we may want to change this behavior */ - if (thr == NULL) - return -ESRCH; - - /* Sleep until the thread sets tp.tp_rc == 1 */ - wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1)); - - splat_vprint(file, SPLAT_THREAD_TEST1_NAME, "%s", - "Thread successfully started properly\n"); - return 0; -} - -static void -splat_thread_work2(void *priv) -{ - thread_priv_t *tp = (thread_priv_t *)priv; - - spin_lock(&tp->tp_lock); - ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC); - tp->tp_rc = 1; - wake_up(&tp->tp_waitq); - spin_unlock(&tp->tp_lock); - - thread_exit(); - - /* The following code is unreachable when thread_exit() is - * working properly, which is exactly what we're testing */ - spin_lock(&tp->tp_lock); - tp->tp_rc = 2; - wake_up(&tp->tp_waitq); - spin_unlock(&tp->tp_lock); -} - -static int -splat_thread_test2(struct file *file, void *arg) -{ - thread_priv_t tp; - kthread_t *thr; - int rc = 0; - - tp.tp_magic = SPLAT_THREAD_TEST_MAGIC; - tp.tp_file = file; - spin_lock_init(&tp.tp_lock); - init_waitqueue_head(&tp.tp_waitq); - tp.tp_rc = 0; - - thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work2, &tp, 0, - &p0, TS_RUN, defclsyspri); - /* Must never fail under Solaris, but we check anyway since this - * can happen in the linux SPL, we may want to change this behavior */ - if (thr == NULL) - return -ESRCH; - - /* Sleep until the thread sets tp.tp_rc == 1 */ - wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1)); - - /* Sleep until the thread sets tp.tp_rc == 2, or until we hit - * the timeout. If thread exit is working properly we should - * hit the timeout and never see to.tp_rc == 2. */ - rc = wait_event_timeout(tp.tp_waitq, splat_thread_rc(&tp, 2), HZ / 10); - if (rc > 0) { - rc = -EINVAL; - splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s", - "Thread did not exit properly at thread_exit()\n"); - } else { - splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s", - "Thread successfully exited at thread_exit()\n"); - } - - return rc; -} - -static void -splat_thread_work3_common(thread_priv_t *tp) -{ - ulong_t rnd; - int i, rc = 0; - - /* set a unique value for each key using a random value */ - get_random_bytes((void *)&rnd, 4); - for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) - tsd_set(tp->tp_keys[i], (void *)(i + rnd)); - - /* verify the unique value for each key */ - for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) - if (tsd_get(tp->tp_keys[i]) != (void *)(i + rnd)) - rc = -EINVAL; - - /* set the value to thread_priv_t for use by the destructor */ - for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) - tsd_set(tp->tp_keys[i], (void *)tp); - - spin_lock(&tp->tp_lock); - if (rc && !tp->tp_rc) - tp->tp_rc = rc; - - tp->tp_count++; - wake_up_all(&tp->tp_waitq); - spin_unlock(&tp->tp_lock); -} - -static void -splat_thread_work3_wait(void *priv) -{ - thread_priv_t *tp = (thread_priv_t *)priv; - - ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC); - splat_thread_work3_common(tp); - wait_event(tp->tp_waitq, splat_thread_count(tp, 0)); - thread_exit(); -} - -static void -splat_thread_work3_exit(void *priv) -{ - thread_priv_t *tp = (thread_priv_t *)priv; - - ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC); - splat_thread_work3_common(tp); - thread_exit(); -} - -static void -splat_thread_dtor3(void *priv) -{ - thread_priv_t *tp = (thread_priv_t *)priv; - - ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC); - spin_lock(&tp->tp_lock); - tp->tp_dtor_count++; - spin_unlock(&tp->tp_lock); -} - -/* - * Create threads which set and verify SPLAT_THREAD_TEST_KEYS number of - * keys. These threads may then exit by calling thread_exit() which calls - * tsd_exit() resulting in all their thread specific data being reclaimed. - * Alternately, the thread may block in which case the thread specific - * data will be reclaimed as part of tsd_destroy(). In either case all - * thread specific data must be reclaimed, this is verified by ensuring - * the registered destructor is called the correct number of times. - */ -static int -splat_thread_test3(struct file *file, void *arg) -{ - int i, rc = 0, expected, wait_count = 0, exit_count = 0; - thread_priv_t tp; - - tp.tp_magic = SPLAT_THREAD_TEST_MAGIC; - tp.tp_file = file; - spin_lock_init(&tp.tp_lock); - init_waitqueue_head(&tp.tp_waitq); - tp.tp_rc = 0; - tp.tp_count = 0; - tp.tp_dtor_count = 0; - - for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) { - tp.tp_keys[i] = 0; - tsd_create(&tp.tp_keys[i], splat_thread_dtor3); - } - - /* Start tsd wait threads */ - for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) { - if (thread_create(NULL, 0, splat_thread_work3_wait, - &tp, 0, &p0, TS_RUN, defclsyspri)) - wait_count++; - } - - /* All wait threads have setup their tsd and are blocking. */ - wait_event(tp.tp_waitq, splat_thread_count(&tp, wait_count)); - - if (tp.tp_dtor_count != 0) { - splat_vprint(file, SPLAT_THREAD_TEST3_NAME, - "Prematurely ran %d tsd destructors\n", tp.tp_dtor_count); - if (!rc) - rc = -ERANGE; - } - - /* Start tsd exit threads */ - for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) { - if (thread_create(NULL, 0, splat_thread_work3_exit, - &tp, 0, &p0, TS_RUN, defclsyspri)) - exit_count++; - } - - /* All exit threads verified tsd and are in the process of exiting */ - wait_event(tp.tp_waitq,splat_thread_count(&tp, wait_count+exit_count)); - msleep(500); - - expected = (SPLAT_THREAD_TEST_KEYS * exit_count); - if (tp.tp_dtor_count != expected) { - splat_vprint(file, SPLAT_THREAD_TEST3_NAME, - "Expected %d exit tsd destructors but saw %d\n", - expected, tp.tp_dtor_count); - if (!rc) - rc = -ERANGE; - } - - /* Destroy all keys and associated tsd in blocked threads */ - for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) - tsd_destroy(&tp.tp_keys[i]); - - expected = (SPLAT_THREAD_TEST_KEYS * (exit_count + wait_count)); - if (tp.tp_dtor_count != expected) { - splat_vprint(file, SPLAT_THREAD_TEST3_NAME, - "Expected %d wait+exit tsd destructors but saw %d\n", - expected, tp.tp_dtor_count); - if (!rc) - rc = -ERANGE; - } - - /* Release the remaining wait threads, sleep briefly while they exit */ - spin_lock(&tp.tp_lock); - tp.tp_count = 0; - wake_up_all(&tp.tp_waitq); - spin_unlock(&tp.tp_lock); - msleep(500); - - if (tp.tp_rc) { - splat_vprint(file, SPLAT_THREAD_TEST3_NAME, - "Thread tsd_get()/tsd_set() error %d\n", tp.tp_rc); - if (!rc) - rc = tp.tp_rc; - } else if (!rc) { - splat_vprint(file, SPLAT_THREAD_TEST3_NAME, "%s", - "Thread specific data verified\n"); - } - - return rc; -} - -splat_subsystem_t * -splat_thread_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_THREAD_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_THREAD_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_THREAD; - - splat_test_init(sub, SPLAT_THREAD_TEST1_NAME, SPLAT_THREAD_TEST1_DESC, - SPLAT_THREAD_TEST1_ID, splat_thread_test1); - splat_test_init(sub, SPLAT_THREAD_TEST2_NAME, SPLAT_THREAD_TEST2_DESC, - SPLAT_THREAD_TEST2_ID, splat_thread_test2); - splat_test_init(sub, SPLAT_THREAD_TEST3_NAME, SPLAT_THREAD_TEST3_DESC, - SPLAT_THREAD_TEST3_ID, splat_thread_test3); - - return sub; -} - -void -splat_thread_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - splat_test_fini(sub, SPLAT_THREAD_TEST3_ID); - splat_test_fini(sub, SPLAT_THREAD_TEST2_ID); - splat_test_fini(sub, SPLAT_THREAD_TEST1_ID); - - kfree(sub); -} - -int -splat_thread_id(void) { - return SPLAT_SUBSYSTEM_THREAD; -} diff --git a/module/splat/splat-time.c b/module/splat/splat-time.c deleted file mode 100644 index a0e261956..000000000 --- a/module/splat/splat-time.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Time Tests. - */ - -#include <sys/time.h> -#include <linux/mm_compat.h> -#include <linux/slab.h> -#include "splat-internal.h" - -#define SPLAT_TIME_NAME "time" -#define SPLAT_TIME_DESC "Kernel Time Tests" - -#define SPLAT_TIME_TEST1_ID 0x0801 -#define SPLAT_TIME_TEST1_NAME "time1" -#define SPLAT_TIME_TEST1_DESC "HZ Test" - -#define SPLAT_TIME_TEST2_ID 0x0802 -#define SPLAT_TIME_TEST2_NAME "time2" -#define SPLAT_TIME_TEST2_DESC "Monotonic Test" - -static int -splat_time_test1(struct file *file, void *arg) -{ - int myhz = hz; - splat_vprint(file, SPLAT_TIME_TEST1_NAME, "hz is %d\n", myhz); - return 0; -} - -static int -splat_time_test2(struct file *file, void *arg) -{ - hrtime_t tm1, tm2; - int i; - - tm1 = gethrtime(); - splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm1); - - for(i = 0; i < 100; i++) { - tm2 = gethrtime(); - splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm2); - - if(tm1 > tm2) { - splat_print(file, "%s: gethrtime() is not giving " - "monotonically increasing values\n", - SPLAT_TIME_TEST2_NAME); - return 1; - } - tm1 = tm2; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(10); - } - - return 0; -} - -splat_subsystem_t * -splat_time_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_TIME_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_TIME_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_TIME; - - splat_test_init(sub, SPLAT_TIME_TEST1_NAME, SPLAT_TIME_TEST1_DESC, - SPLAT_TIME_TEST1_ID, splat_time_test1); - splat_test_init(sub, SPLAT_TIME_TEST2_NAME, SPLAT_TIME_TEST2_DESC, - SPLAT_TIME_TEST2_ID, splat_time_test2); - - return sub; -} - -void -splat_time_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_TIME_TEST2_ID); - splat_test_fini(sub, SPLAT_TIME_TEST1_ID); - - kfree(sub); -} - -int -splat_time_id(void) -{ - return SPLAT_SUBSYSTEM_TIME; -} diff --git a/module/splat/splat-vnode.c b/module/splat/splat-vnode.c deleted file mode 100644 index 4ccf24f1e..000000000 --- a/module/splat/splat-vnode.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Vnode Tests. - */ - -#include <sys/vnode.h> -#include "splat-internal.h" - -#define SPLAT_VNODE_NAME "vnode" -#define SPLAT_VNODE_DESC "Kernel Vnode Tests" - -#define SPLAT_VNODE_TEST1_ID 0x0901 -#define SPLAT_VNODE_TEST1_NAME "vn_open" -#define SPLAT_VNODE_TEST1_DESC "Vn_open Test" - -#define SPLAT_VNODE_TEST2_ID 0x0902 -#define SPLAT_VNODE_TEST2_NAME "vn_openat" -#define SPLAT_VNODE_TEST2_DESC "Vn_openat Test" - -#define SPLAT_VNODE_TEST3_ID 0x0903 -#define SPLAT_VNODE_TEST3_NAME "vn_rdwr" -#define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test" - -#define SPLAT_VNODE_TEST5_ID 0x0905 -#define SPLAT_VNODE_TEST5_NAME "vn_getattr" -#define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test" - -#define SPLAT_VNODE_TEST6_ID 0x0906 -#define SPLAT_VNODE_TEST6_NAME "vn_sync" -#define SPLAT_VNODE_TEST6_DESC "Vn_sync Test" - -#define SPLAT_VNODE_TEST_FILE "/etc/fstab" -#define SPLAT_VNODE_TEST_FILE_AT "etc/fstab" -#define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp" -#define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1" -#define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2" - -static int -splat_vnode_user_cmd(struct file *file, void *arg, - char *name, char *cmd) -{ - char sh_path[] = "/bin/sh"; - char *argv[] = { sh_path, - "-c", - cmd, - NULL }; - char *envp[] = { "HOME=/", - "TERM=linux", - "PATH=/sbin:/usr/sbin:/bin:/usr/bin", - NULL }; - int rc; - - rc = call_usermodehelper(sh_path, argv, envp, UMH_WAIT_PROC); - if (rc) { - splat_vprint(file, name, - "Failed command: %s %s %s (%d)\n", - argv[0], argv[1], cmd, rc); - return -EPERM; - } - - return 0; -} - -static int -splat_vnode_unlink_all(struct file *file, void *arg, char *name) -{ - char *cmds[] = { "rm -f " SPLAT_VNODE_TEST_FILE_RW, - "rm -f " SPLAT_VNODE_TEST_FILE_RW1, - "rm -f " SPLAT_VNODE_TEST_FILE_RW2, - NULL }; - int i = 0, rc = 0; - - while (cmds[i] != NULL) { - if ((rc = splat_vnode_user_cmd(file, arg, name, cmds[i]))) - return rc; - - i++; - } - - return rc; -} - -static int -splat_vnode_test1(struct file *file, void *arg) -{ - vnode_t *vp; - int rc; - - if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, - FREAD, 0644, &vp, 0, 0))) { - splat_vprint(file, SPLAT_VNODE_TEST1_NAME, - "Failed to vn_open test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - return -rc; - } - - rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); - - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST1_NAME, - "Failed to vn_close test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - return -rc; - } - - splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed " - "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); - - return -rc; -} /* splat_vnode_test1() */ - -static int -splat_vnode_test2(struct file *file, void *arg) -{ - vnode_t *vp; - int rc; - - if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE, - FREAD, 0644, &vp, 0, 0, rootdir, 0))) { - splat_vprint(file, SPLAT_VNODE_TEST2_NAME, - "Failed to vn_openat test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - return -rc; - } - - rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); - - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST2_NAME, - "Failed to vn_close test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - return -rc; - } - - splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed " - "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); - - return -rc; -} /* splat_vnode_test2() */ - -static int -splat_vnode_test3(struct file *file, void *arg) -{ - vnode_t *vp; - char buf1[32] = "SPL VNode Interface Test File\n"; - char buf2[32] = ""; - int rc; - - if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST3_NAME))) - return rc; - - if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, - FWRITE | FREAD | FCREAT | FEXCL, - 0644, &vp, 0, 0))) { - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, - "Failed to vn_open test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - return -rc; - } - - rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, - UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, - "Failed vn_rdwr write of test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - goto out; - } - - rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, - UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, - "Failed vn_rdwr read of test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - goto out; - } - - if (strncmp(buf1, buf2, strlen(buf1))) { - rc = EINVAL; - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, - "Failed strncmp data written does not match " - "data read\nWrote: %sRead: %s\n", buf1, buf2); - goto out; - } - - rc = 0; - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); - splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " - "read expected data pattern to test file: %s\n", - SPLAT_VNODE_TEST_FILE_RW); - -out: - VOP_CLOSE(vp, 0, 0, 0, 0, 0); - - return -rc; -} /* splat_vnode_test3() */ - -static int -splat_vnode_test5(struct file *file, void *arg) -{ - vnode_t *vp; - vattr_t vap; - int rc; - - if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, - FREAD, 0644, &vp, 0, 0))) { - splat_vprint(file, SPLAT_VNODE_TEST5_NAME, - "Failed to vn_open test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - return -rc; - } - - rc = VOP_GETATTR(vp, &vap, 0, 0, NULL); - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST5_NAME, - "Failed to vn_getattr test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE, rc); - goto out; - } - - if (vap.va_type != VREG) { - rc = EINVAL; - splat_vprint(file, SPLAT_VNODE_TEST5_NAME, - "Failed expected regular file type " - "(%d != VREG): %s (%d)\n", vap.va_type, - SPLAT_VNODE_TEST_FILE, rc); - goto out; - } - - splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully " - "vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE); - -out: - VOP_CLOSE(vp, 0, 0, 0, 0, 0); - - return -rc; -} /* splat_vnode_test5() */ - -static int -splat_vnode_test6(struct file *file, void *arg) -{ - vnode_t *vp; - char buf[32] = "SPL VNode Interface Test File\n"; - int rc; - - if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST6_NAME))) - return rc; - - if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, - FWRITE | FCREAT | FEXCL, 0644, &vp, 0, 0))) { - splat_vprint(file, SPLAT_VNODE_TEST6_NAME, - "Failed to vn_open test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - return -rc; - } - - rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0, - UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST6_NAME, - "Failed vn_rdwr write of test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - goto out; - } - - rc = vn_fsync(vp, 0, 0, 0); - if (rc) { - splat_vprint(file, SPLAT_VNODE_TEST6_NAME, - "Failed vn_fsync of test file: %s (%d)\n", - SPLAT_VNODE_TEST_FILE_RW, rc); - goto out; - } - - rc = 0; - splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully " - "fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW); -out: - VOP_CLOSE(vp, 0, 0, 0, 0, 0); - - return -rc; -} /* splat_vnode_test6() */ - -splat_subsystem_t * -splat_vnode_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_VNODE; - - splat_test_init(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC, - SPLAT_VNODE_TEST1_ID, splat_vnode_test1); - splat_test_init(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC, - SPLAT_VNODE_TEST2_ID, splat_vnode_test2); - splat_test_init(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC, - SPLAT_VNODE_TEST3_ID, splat_vnode_test3); - splat_test_init(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC, - SPLAT_VNODE_TEST5_ID, splat_vnode_test5); - splat_test_init(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC, - SPLAT_VNODE_TEST6_ID, splat_vnode_test6); - - return sub; -} /* splat_vnode_init() */ - -void -splat_vnode_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_VNODE_TEST6_ID); - splat_test_fini(sub, SPLAT_VNODE_TEST5_ID); - splat_test_fini(sub, SPLAT_VNODE_TEST3_ID); - splat_test_fini(sub, SPLAT_VNODE_TEST2_ID); - splat_test_fini(sub, SPLAT_VNODE_TEST1_ID); - - kfree(sub); -} /* splat_vnode_fini() */ - -int -splat_vnode_id(void) -{ - return SPLAT_SUBSYSTEM_VNODE; -} /* splat_vnode_id() */ diff --git a/module/splat/splat-zlib.c b/module/splat/splat-zlib.c deleted file mode 100644 index 28e521c82..000000000 --- a/module/splat/splat-zlib.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf <[email protected]>. - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see <http://zfsonlinux.org/>. - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see <http://www.gnu.org/licenses/>. - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) Zlib Compression Tests. - */ - -#include <sys/zmod.h> -#include <sys/random.h> -#include <sys/kmem.h> -#include <sys/vmem.h> -#include "splat-internal.h" - -#define SPLAT_ZLIB_NAME "zlib" -#define SPLAT_ZLIB_DESC "Zlib Compression Tests" - -#define SPLAT_ZLIB_TEST1_ID 0x0f01 -#define SPLAT_ZLIB_TEST1_NAME "compress/uncompress" -#define SPLAT_ZLIB_TEST1_DESC "Compress/Uncompress Test" - -#define BUFFER_SIZE (128 * 1024) - -static int -splat_zlib_test1_check(struct file *file, void *src, void *dst, void *chk, - int level) -{ - size_t dst_len = BUFFER_SIZE; - size_t chk_len = BUFFER_SIZE; - int rc; - - memset(dst, 0, BUFFER_SIZE); - memset(chk, 0, BUFFER_SIZE); - - rc = z_compress_level(dst, &dst_len, src, BUFFER_SIZE, level); - if (rc != Z_OK) { - splat_vprint(file, SPLAT_ZLIB_TEST1_NAME, - "Failed level %d z_compress_level(), %d\n", level, rc); - return -EINVAL; - } - - rc = z_uncompress(chk, &chk_len, dst, dst_len); - if (rc != Z_OK) { - splat_vprint(file, SPLAT_ZLIB_TEST1_NAME, - "Failed level %d z_uncompress(), %d\n", level, rc); - return -EINVAL; - } - - rc = memcmp(src, chk, BUFFER_SIZE); - if (rc) { - splat_vprint(file, SPLAT_ZLIB_TEST1_NAME, - "Failed level %d memcmp()), %d\n", level, rc); - return -EINVAL; - } - - splat_vprint(file, SPLAT_ZLIB_TEST1_NAME, - "Passed level %d, compressed %d bytes to %d bytes\n", - level, BUFFER_SIZE, (int)dst_len); - - return 0; -} - -/* - * Compress a buffer, uncompress the newly compressed buffer, then - * compare it to the original. Do this for all 9 compression levels. - */ -static int -splat_zlib_test1(struct file *file, void *arg) -{ - void *src = NULL, *dst = NULL, *chk = NULL; - int i, rc, level; - - src = vmalloc(BUFFER_SIZE); - if (src == NULL) { - rc = -ENOMEM; - goto out; - } - - dst = vmalloc(BUFFER_SIZE); - if (dst == NULL) { - rc = -ENOMEM; - goto out; - } - - chk = vmalloc(BUFFER_SIZE); - if (chk == NULL) { - rc = -ENOMEM; - goto out; - } - - /* Source buffer is a repeating 1024 byte random pattern. */ - random_get_pseudo_bytes(src, sizeof(uint8_t) * 1024); - for (i = 1; i < 128; i++) - memcpy(src + (i * 1024), src, 1024); - - for (level = 1; level <= 9; level++) - if ((rc = splat_zlib_test1_check(file, src, dst, chk, level))) - break; -out: - if (src) - vfree(src); - - if (dst) - vfree(dst); - - if (chk) - vfree(chk); - - return rc; -} - -splat_subsystem_t * -splat_zlib_init(void) -{ - splat_subsystem_t *sub; - - sub = kmalloc(sizeof(*sub), GFP_KERNEL); - if (sub == NULL) - return NULL; - - memset(sub, 0, sizeof(*sub)); - strncpy(sub->desc.name, SPLAT_ZLIB_NAME, SPLAT_NAME_SIZE); - strncpy(sub->desc.desc, SPLAT_ZLIB_DESC, SPLAT_DESC_SIZE); - INIT_LIST_HEAD(&sub->subsystem_list); - INIT_LIST_HEAD(&sub->test_list); - spin_lock_init(&sub->test_lock); - sub->desc.id = SPLAT_SUBSYSTEM_ZLIB; - - splat_test_init(sub, SPLAT_ZLIB_TEST1_NAME, SPLAT_ZLIB_TEST1_DESC, - SPLAT_ZLIB_TEST1_ID, splat_zlib_test1); - - return sub; -} - -void -splat_zlib_fini(splat_subsystem_t *sub) -{ - ASSERT(sub); - - splat_test_fini(sub, SPLAT_ZLIB_TEST1_ID); - - kfree(sub); -} - -int -splat_zlib_id(void) { - return SPLAT_SUBSYSTEM_ZLIB; -} |