mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Rename modules to module and update references
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Makefile.in for splat kernel module
|
||||
|
||||
MODULES := splat
|
||||
DISTFILES = Makefile.in *.c *.h
|
||||
EXTRA_CFLAGS = @KERNELCPPFLAGS@
|
||||
|
||||
# Solaris Porting LAyer Tests
|
||||
obj-m := splat.o
|
||||
|
||||
splat-objs += splat-ctl.o
|
||||
splat-objs += splat-kmem.o
|
||||
splat-objs += splat-taskq.o
|
||||
splat-objs += splat-random.o
|
||||
splat-objs += splat-mutex.o
|
||||
splat-objs += splat-condvar.o
|
||||
splat-objs += splat-thread.o
|
||||
splat-objs += splat-rwlock.o
|
||||
splat-objs += splat-time.o
|
||||
splat-objs += splat-vnode.o
|
||||
splat-objs += splat-kobj.o
|
||||
splat-objs += splat-atomic.o
|
||||
splat-objs += splat-list.o
|
||||
splat-objs += splat-generic.o
|
||||
|
||||
splatmodule := splat.ko
|
||||
splatmoduledir := @kmoduledir@/kernel/lib/
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(splatmoduledir)
|
||||
$(INSTALL) -m 644 $(splatmodule) $(DESTDIR)$(splatmoduledir)/$(splatmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(splatmoduledir)/$(splatmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
clean:
|
||||
-rm -f $(splmodule) *.o .*.cmd *.mod.c *.ko *.s */*.o
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
rm -rf .tmp_versions
|
||||
|
||||
maintainer-clean: distclean
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
cp -p $(DISTFILES) $(distdir)
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_ATOMIC 0x0b00
|
||||
#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;
|
||||
spinlock_t ap_lock;
|
||||
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);
|
||||
|
||||
spin_lock(&ap->ap_lock);
|
||||
op = ap->ap_op;
|
||||
wake_up(&ap->ap_waitq);
|
||||
spin_unlock(&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:
|
||||
SBUG();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
spin_lock_init(&ap.ap_lock);
|
||||
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++) {
|
||||
spin_lock(&ap.ap_lock);
|
||||
ap.ap_op = i;
|
||||
|
||||
thr = (kthread_t *)thread_create(NULL, 0, splat_atomic_work,
|
||||
&ap, 0, &p0, TS_RUN,
|
||||
minclsyspri);
|
||||
if (thr == NULL) {
|
||||
rc = -ESRCH;
|
||||
spin_unlock(&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);
|
||||
spin_unlock(&ap.ap_lock);
|
||||
schedule();
|
||||
}
|
||||
|
||||
wait_event_interruptible(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);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_CONDVAR 0x0500
|
||||
#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_test"
|
||||
#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 {
|
||||
int ct_id;
|
||||
const char *ct_name;
|
||||
condvar_priv_t *ct_cvp;
|
||||
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;
|
||||
char name[16];
|
||||
|
||||
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name),"%s%d",SPLAT_CONDVAR_TEST_NAME,ct->ct_id);
|
||||
daemonize(name);
|
||||
|
||||
mutex_enter(&cv->cv_mtx);
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread sleeping with %d waiters\n",
|
||||
name, 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",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
mutex_exit(&cv->cv_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test1(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
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, SPLAT_CONDVAR_TEST_NAME, 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_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST1_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test12_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
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);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test2(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
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, SPLAT_CONDVAR_TEST_NAME, 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_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST2_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test12_thread, &ct[i], 0);
|
||||
if (pids[i] > 0)
|
||||
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);
|
||||
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;
|
||||
char name[16];
|
||||
clock_t rc;
|
||||
|
||||
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d", SPLAT_CONDVAR_TEST_NAME, ct->ct_id);
|
||||
daemonize(name);
|
||||
|
||||
mutex_enter(&cv->cv_mtx);
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread sleeping with %d waiters\n",
|
||||
name, 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", name);
|
||||
} else {
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread woken %d waiters remain\n",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
}
|
||||
|
||||
mutex_exit(&cv->cv_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test3(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
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, SPLAT_CONDVAR_TEST_NAME, 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_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test34_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
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);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test4(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
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, SPLAT_CONDVAR_TEST_NAME, 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_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test34_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
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);
|
||||
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;
|
||||
int64_t whole_delta;
|
||||
int32_t remain_delta;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&condvar, SPLAT_CONDVAR_TEST_NAME, 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, 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, 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, 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;
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* My intent is to create a loadable 'splat' (Solaris Porting LAyer
|
||||
* Tests) module which can be used as an access point to run
|
||||
* in kernel Solaris ABI regression tests. This provides a
|
||||
* nice mechanism to validate the shim primates are working properly.
|
||||
*
|
||||
* The basic design is the splat module is that it is constructed of
|
||||
* various splat_* source files each of which contains regression tests.
|
||||
* For example the splat_linux_kmem.c file contains tests for validating
|
||||
* kmem correctness. When the splat module is loaded splat_*_init()
|
||||
* will be called for each subsystems tests, similarly splat_*_fini() is
|
||||
* called when the splat module is removed. Each test can then be
|
||||
* run by making an ioctl() call from a userspace control application
|
||||
* to pick the subsystem and test which should be run.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
static spl_class *splat_class;
|
||||
static spl_device *splat_device;
|
||||
static struct list_head splat_module_list;
|
||||
static spinlock_t splat_module_lock;
|
||||
|
||||
static int
|
||||
splat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
splat_info_t *info;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
info = (splat_info_t *)kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&info->info_lock);
|
||||
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;
|
||||
}
|
||||
|
||||
info->info_head = info->info_buffer;
|
||||
file->private_data = (void *)info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
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);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
memset(info->info_buffer, 0, info->info_size);
|
||||
info->info_head = info->info_buffer;
|
||||
spin_unlock(&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);
|
||||
|
||||
spin_lock(&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:
|
||||
spin_unlock(&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 long arg)
|
||||
{
|
||||
splat_cfg_t kcfg;
|
||||
int rc = 0;
|
||||
|
||||
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 - Populated 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 long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
splat_cmd_t kcmd;
|
||||
int rc = -EINVAL;
|
||||
void *data = NULL;
|
||||
|
||||
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 int
|
||||
splat_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
int rc = 0;
|
||||
|
||||
/* Ignore tty ioctls */
|
||||
if ((cmd & 0xffffff00) == ((int)'T') << 8)
|
||||
return -ENOTTY;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
case SPLAT_CFG:
|
||||
rc = splat_ioctl_cfg(file, arg);
|
||||
break;
|
||||
case SPLAT_CMD:
|
||||
rc = splat_ioctl_cmd(file, arg);
|
||||
break;
|
||||
default:
|
||||
splat_print(file, "Bad ioctl command %d\n", cmd);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&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:
|
||||
spin_unlock(&info->info_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t splat_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&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:
|
||||
spin_unlock(&info->info_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static loff_t splat_seek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&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;
|
||||
}
|
||||
|
||||
spin_unlock(&info->info_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations splat_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = splat_open,
|
||||
.release = splat_release,
|
||||
.ioctl = splat_ioctl,
|
||||
.read = splat_read,
|
||||
.write = splat_write,
|
||||
.llseek = splat_seek,
|
||||
};
|
||||
|
||||
static struct cdev splat_cdev = {
|
||||
.owner = THIS_MODULE,
|
||||
.kobj = { .name = SPLAT_NAME, },
|
||||
};
|
||||
|
||||
static int __init
|
||||
splat_init(void)
|
||||
{
|
||||
dev_t dev;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
|
||||
dev = MKDEV(SPLAT_MAJOR, 0);
|
||||
if ((rc = register_chrdev_region(dev, SPLAT_MINORS, SPLAT_NAME)))
|
||||
goto error;
|
||||
|
||||
/* Support for registering a character driver */
|
||||
cdev_init(&splat_cdev, &splat_fops);
|
||||
if ((rc = cdev_add(&splat_cdev, dev, SPLAT_MINORS))) {
|
||||
printk(KERN_ERR "SPLAT: Error adding cdev, %d\n", rc);
|
||||
kobject_put(&splat_cdev.kobj);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Support for udev make driver info available in sysfs */
|
||||
splat_class = spl_class_create(THIS_MODULE, "splat");
|
||||
if (IS_ERR(splat_class)) {
|
||||
rc = PTR_ERR(splat_class);
|
||||
printk(KERN_ERR "SPLAT: Error creating splat class, %d\n", rc);
|
||||
cdev_del(&splat_cdev);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
splat_device = spl_device_create(splat_class, NULL,
|
||||
MKDEV(SPLAT_MAJOR, 0),
|
||||
NULL, SPLAT_NAME);
|
||||
|
||||
printk(KERN_INFO "SPLAT: Loaded Solaris Porting LAyer "
|
||||
"Tests v%s\n", VERSION);
|
||||
return 0;
|
||||
error:
|
||||
printk(KERN_ERR "SPLAT: Error registering splat device, %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_fini(void)
|
||||
{
|
||||
dev_t dev = MKDEV(SPLAT_MAJOR, 0);
|
||||
|
||||
spl_device_destroy(splat_class, splat_device, dev);
|
||||
spl_class_destroy(splat_class);
|
||||
cdev_del(&splat_cdev);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
|
||||
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 Solaris Porting LAyer "
|
||||
"Tests v%s\n", VERSION);
|
||||
}
|
||||
|
||||
module_init(splat_init);
|
||||
module_exit(splat_fini);
|
||||
|
||||
MODULE_AUTHOR("Lawrence Livermore National Labs");
|
||||
MODULE_DESCRIPTION("Solaris Porting LAyer Tests");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_GENERIC 0x0d00
|
||||
#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 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);
|
||||
|
||||
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);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_generic_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _SPLAT_INTERNAL_H
|
||||
#define _SPLAT_INTERNAL_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/ioctls.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <sys/callb.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/cred.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/kstat.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/taskq.h>
|
||||
#include <sys/thread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timer.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/kobj.h>
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/list.h>
|
||||
#include <sys/sunddi.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
#include "spl-device.h"
|
||||
#include "splat-ctl.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_INIT(type) \
|
||||
({ splat_subsystem_t *_sub_; \
|
||||
\
|
||||
_sub_ = (splat_subsystem_t *)splat_##type##_init(); \
|
||||
if (_sub_ == NULL) { \
|
||||
printk(KERN_ERR "splat: Error initializing: " #type "\n"); \
|
||||
} else { \
|
||||
spin_lock(&splat_module_lock); \
|
||||
list_add_tail(&(_sub_->subsystem_list), \
|
||||
&splat_module_list); \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define SPLAT_SUBSYSTEM_FINI(type) \
|
||||
({ splat_subsystem_t *_sub_, *_tmp_; \
|
||||
int _id_, _flag_ = 0; \
|
||||
\
|
||||
_id_ = splat_##type##_id(); \
|
||||
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)); \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
splat_##type##_fini(_sub_); \
|
||||
spin_lock(&splat_module_lock); \
|
||||
_flag_ = 1; \
|
||||
} \
|
||||
} \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
\
|
||||
if (!_flag_) \
|
||||
printk(KERN_ERR "splat: Error finalizing: " #type "\n"); \
|
||||
})
|
||||
|
||||
#define SPLAT_TEST_INIT(sub, n, d, tid, func) \
|
||||
({ splat_test_t *_test_; \
|
||||
\
|
||||
_test_ = (splat_test_t *)kmalloc(sizeof(*_test_), GFP_KERNEL); \
|
||||
if (_test_ == NULL) { \
|
||||
printk(KERN_ERR "splat: Error initializing: " n "/" #tid" \n");\
|
||||
} else { \
|
||||
memset(_test_, 0, sizeof(*_test_)); \
|
||||
strncpy(_test_->desc.name, n, SPLAT_NAME_SIZE-1); \
|
||||
strncpy(_test_->desc.desc, d, 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)); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define SPLAT_TEST_FINI(sub, 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)); \
|
||||
_flag_ = 1; \
|
||||
} \
|
||||
} \
|
||||
spin_unlock(&((sub)->test_lock)); \
|
||||
\
|
||||
if (!_flag_) \
|
||||
printk(KERN_ERR "splat: Error finalizing: " #tid "\n"); \
|
||||
})
|
||||
|
||||
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;
|
||||
|
||||
#define SPLAT_INFO_BUFFER_SIZE 65536
|
||||
#define SPLAT_INFO_BUFFER_REDZONE 256
|
||||
|
||||
typedef struct splat_info {
|
||||
spinlock_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); \
|
||||
\
|
||||
spin_lock(&_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_; \
|
||||
} \
|
||||
\
|
||||
spin_unlock(&_info_->info_lock); \
|
||||
_rc_; \
|
||||
})
|
||||
|
||||
#define splat_vprint(file, test, format, args...) \
|
||||
splat_print(file, "%*s: " format, SPLAT_NAME_SIZE, test, args)
|
||||
|
||||
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);
|
||||
|
||||
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 *);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* _SPLAT_INTERNAL_H */
|
||||
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KMEM 0x0100
|
||||
#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 "kmem_cache1"
|
||||
#define SPLAT_KMEM_TEST5_DESC "Slab ctor/dtor test (small)"
|
||||
|
||||
#define SPLAT_KMEM_TEST6_ID 0x0106
|
||||
#define SPLAT_KMEM_TEST6_NAME "kmem_cache2"
|
||||
#define SPLAT_KMEM_TEST6_DESC "Slab ctor/dtor test (large)"
|
||||
|
||||
#define SPLAT_KMEM_TEST7_ID 0x0107
|
||||
#define SPLAT_KMEM_TEST7_NAME "kmem_reap"
|
||||
#define SPLAT_KMEM_TEST7_DESC "Slab reaping test"
|
||||
|
||||
#define SPLAT_KMEM_TEST8_ID 0x0108
|
||||
#define SPLAT_KMEM_TEST8_NAME "kmem_lock"
|
||||
#define SPLAT_KMEM_TEST8_DESC "Slab locking test"
|
||||
|
||||
#define SPLAT_KMEM_ALLOC_COUNT 10
|
||||
#define SPLAT_VMEM_ALLOC_COUNT 10
|
||||
|
||||
|
||||
/* XXX - This test may fail under tight memory conditions */
|
||||
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;
|
||||
|
||||
/* We are intentionally going to push kmem_alloc to its max
|
||||
* allocation size, so suppress the console warnings for now */
|
||||
kmem_set_warning(0);
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 32))) {
|
||||
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;
|
||||
}
|
||||
|
||||
kmem_set_warning(1);
|
||||
|
||||
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;
|
||||
|
||||
/* We are intentionally going to push kmem_alloc to its max
|
||||
* allocation size, so suppress the console warnings for now */
|
||||
kmem_set_warning(0);
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 32))) {
|
||||
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;
|
||||
}
|
||||
|
||||
kmem_set_warning(1);
|
||||
|
||||
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;
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 1024))) {
|
||||
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;
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 1024))) {
|
||||
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 128
|
||||
#define SPLAT_KMEM_OBJ_RECLAIM 16
|
||||
|
||||
typedef struct kmem_cache_data {
|
||||
unsigned long kcd_magic;
|
||||
int kcd_flag;
|
||||
char kcd_buf[0];
|
||||
} kmem_cache_data_t;
|
||||
|
||||
typedef struct kmem_cache_priv {
|
||||
unsigned long kcp_magic;
|
||||
struct file *kcp_file;
|
||||
kmem_cache_t *kcp_cache;
|
||||
kmem_cache_data_t *kcp_kcd[SPLAT_KMEM_OBJ_COUNT];
|
||||
spinlock_t kcp_lock;
|
||||
wait_queue_head_t kcp_waitq;
|
||||
int kcp_size;
|
||||
int kcp_count;
|
||||
int kcp_threads;
|
||||
int kcp_alloc;
|
||||
int kcp_rc;
|
||||
} kmem_cache_priv_t;
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_cache_size_test(struct file *file, void *arg,
|
||||
char *name, int size, int flags)
|
||||
{
|
||||
kmem_cache_t *cache = NULL;
|
||||
kmem_cache_data_t *kcd = NULL;
|
||||
kmem_cache_priv_t kcp;
|
||||
int rc = 0, max;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
kcp.kcp_size = size;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_rc = 0;
|
||||
|
||||
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, flags);
|
||||
if (!cache) {
|
||||
splat_vprint(file, name,
|
||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kcd = kmem_cache_alloc(cache, KM_SLEEP);
|
||||
if (!kcd) {
|
||||
splat_vprint(file, name,
|
||||
"Unable to allocate from '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!kcd->kcd_flag) {
|
||||
splat_vprint(file, name,
|
||||
"Failed to run contructor for '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (kcd->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;
|
||||
}
|
||||
|
||||
max = kcp.kcp_count;
|
||||
kmem_cache_free(cache, kcd);
|
||||
|
||||
/* Destroy the entire cache which will force destructors to
|
||||
* run and we can verify one was called for every object */
|
||||
kmem_cache_destroy(cache);
|
||||
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;
|
||||
}
|
||||
|
||||
splat_vprint(file, name,
|
||||
"Successfully ran ctors/dtors for %d elements in '%s'\n",
|
||||
max, SPLAT_KMEM_CACHE_NAME);
|
||||
|
||||
return rc;
|
||||
|
||||
out_free:
|
||||
if (kcd)
|
||||
kmem_cache_free(cache, kcd);
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
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 rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128, KMC_KMEM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return splat_kmem_cache_size_test(file, arg, name, 128, KMC_VMEM);
|
||||
}
|
||||
|
||||
/* 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 rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128 * 1024, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128 * 1024, KMC_KMEM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return splat_kmem_cache_size_test(file, arg, name, 128 * 1028, KMC_VMEM);
|
||||
}
|
||||
|
||||
static void
|
||||
splat_kmem_cache_test_reclaim(void *priv)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
||||
int i, count;
|
||||
|
||||
count = min(SPLAT_KMEM_OBJ_RECLAIM, kcp->kcp_count);
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Reaping %d objects from '%s'\n", count,
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) {
|
||||
if (kcp->kcp_kcd[i]) {
|
||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
||||
kcp->kcp_kcd[i] = NULL;
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test7(struct file *file, void *arg)
|
||||
{
|
||||
kmem_cache_t *cache;
|
||||
kmem_cache_priv_t kcp;
|
||||
int i, rc = 0;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
kcp.kcp_size = 256;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_rc = 0;
|
||||
|
||||
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 (!cache) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kcp.kcp_cache = cache;
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) {
|
||||
/* All allocations need not succeed */
|
||||
kcp.kcp_kcd[i] = kmem_cache_alloc(cache, KM_SLEEP);
|
||||
if (!kcp.kcp_kcd[i]) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Unable to allocate from '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(kcp.kcp_count > 0);
|
||||
|
||||
/* Request 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 immedately reclaim all of them because
|
||||
* we may contend with cache allocs and thrash. What we want to see
|
||||
* is slab size decrease more gradually as it becomes clear they
|
||||
* will not be needed. This should be acheivable in less than minute
|
||||
* if it takes longer than this something has gone wrong.
|
||||
*/
|
||||
for (i = 0; i < 60; i++) {
|
||||
kmem_cache_reap_now(cache);
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"%s cache objects %d, slabs %u/%u objs %u/%u\n",
|
||||
SPLAT_KMEM_CACHE_NAME, kcp.kcp_count,
|
||||
(unsigned)cache->skc_slab_alloc,
|
||||
(unsigned)cache->skc_slab_total,
|
||||
(unsigned)cache->skc_obj_alloc,
|
||||
(unsigned)cache->skc_obj_total);
|
||||
|
||||
if (cache->skc_obj_total == 0)
|
||||
break;
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ);
|
||||
}
|
||||
|
||||
if (cache->skc_obj_total == 0) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_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_TEST7_NAME,
|
||||
"Failed to reclaim %u/%d objects from cache %s\n",
|
||||
(unsigned)cache->skc_obj_total, SPLAT_KMEM_OBJ_COUNT,
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
|
||||
/* Cleanup our mess (for failure case of time expiring) */
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++)
|
||||
if (kcp.kcp_kcd[i])
|
||||
kmem_cache_free(cache, kcp.kcp_kcd[i]);
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_kmem_test8_thread(void *arg)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg;
|
||||
int count = kcp->kcp_alloc, rc = 0, i;
|
||||
void **objs;
|
||||
|
||||
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
||||
|
||||
objs = vmem_zalloc(count * sizeof(void *), KM_SLEEP);
|
||||
if (!objs) {
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME,
|
||||
"Unable to alloc objp array for cache '%s'\n",
|
||||
kcp->kcp_cache->skc_name);
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
objs[i] = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
||||
if (!objs[i]) {
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME,
|
||||
"Unable to allocate from cache '%s'\n",
|
||||
kcp->kcp_cache->skc_name);
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (objs[i])
|
||||
kmem_cache_free(kcp->kcp_cache, objs[i]);
|
||||
|
||||
vmem_free(objs, count * sizeof(void *));
|
||||
out:
|
||||
spin_lock(&kcp->kcp_lock);
|
||||
if (!kcp->kcp_rc)
|
||||
kcp->kcp_rc = rc;
|
||||
|
||||
if (--kcp->kcp_threads == 0)
|
||||
wake_up(&kcp->kcp_waitq);
|
||||
|
||||
spin_unlock(&kcp->kcp_lock);
|
||||
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test8_count(kmem_cache_priv_t *kcp, int threads)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&kcp->kcp_lock);
|
||||
ret = (kcp->kcp_threads == threads);
|
||||
spin_unlock(&kcp->kcp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This test will always pass and is simply here so I can easily
|
||||
* eyeball the slab cache locking overhead to ensure it is reasonable.
|
||||
*/
|
||||
static int
|
||||
splat_kmem_test8_sc(struct file *file, void *arg, int size, int count)
|
||||
{
|
||||
kmem_cache_priv_t kcp;
|
||||
kthread_t *thr;
|
||||
struct timespec start, stop, delta;
|
||||
char cache_name[32];
|
||||
int i, j, rc = 0, threads = 32;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "name",
|
||||
"time (sec)\tslabs \tobjs \thash\n");
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "",
|
||||
" \ttot/max/calc\ttot/max/calc\n");
|
||||
|
||||
for (i = 1; i <= count; i *= 2) {
|
||||
kcp.kcp_size = size;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_threads = 0;
|
||||
kcp.kcp_alloc = i;
|
||||
kcp.kcp_rc = 0;
|
||||
spin_lock_init(&kcp.kcp_lock);
|
||||
init_waitqueue_head(&kcp.kcp_waitq);
|
||||
|
||||
(void)snprintf(cache_name, 32, "%s-%d-%d",
|
||||
SPLAT_KMEM_CACHE_NAME, size, i);
|
||||
kcp.kcp_cache = kmem_cache_create(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_TEST8_NAME,
|
||||
"Unable to create '%s' cache\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
start = current_kernel_time();
|
||||
|
||||
for (j = 0; j < threads; j++) {
|
||||
thr = thread_create(NULL, 0, splat_kmem_test8_thread,
|
||||
&kcp, 0, &p0, TS_RUN, minclsyspri);
|
||||
if (thr == NULL) {
|
||||
rc = -ESRCH;
|
||||
break;
|
||||
}
|
||||
spin_lock(&kcp.kcp_lock);
|
||||
kcp.kcp_threads++;
|
||||
spin_unlock(&kcp.kcp_lock);
|
||||
}
|
||||
|
||||
/* Sleep until the thread sets kcp.kcp_threads == 0 */
|
||||
wait_event(kcp.kcp_waitq, splat_kmem_test8_count(&kcp, 0));
|
||||
stop = current_kernel_time();
|
||||
delta = timespec_sub(stop, start);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_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 * 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 * threads));
|
||||
|
||||
kmem_cache_destroy(kcp.kcp_cache);
|
||||
|
||||
if (!rc && kcp.kcp_rc)
|
||||
rc = kcp.kcp_rc;
|
||||
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test8(struct file *file, void *arg)
|
||||
{
|
||||
int i, rc = 0;
|
||||
|
||||
/* Run through slab cache with objects size from
|
||||
* 16-1Mb in 4x multiples with 1024 objects each */
|
||||
for (i = 16; i <= 1024*1024; i *= 4) {
|
||||
rc = splat_kmem_test8_sc(file, arg, i, 256);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_kmem_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KOBJ 0x0a00
|
||||
#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", 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", size, (uint64_t)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", 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() */
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_LIST 0x0c00
|
||||
#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 / 2);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_MUTEX 0x0400
|
||||
#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_WORKQ "mutex_wq"
|
||||
#define SPLAT_MUTEX_TEST_COUNT 128
|
||||
|
||||
typedef struct mutex_priv {
|
||||
unsigned long mp_magic;
|
||||
struct file *mp_file;
|
||||
struct work_struct mp_work[SPLAT_MUTEX_TEST_COUNT];
|
||||
kmutex_t mp_mtx;
|
||||
int mp_rc;
|
||||
} mutex_priv_t;
|
||||
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
static void
|
||||
splat_mutex_test1_work(void *priv)
|
||||
{
|
||||
mutex_priv_t *mp = (mutex_priv_t *)priv;
|
||||
|
||||
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
|
||||
mp->mp_rc = 0;
|
||||
|
||||
if (!mutex_tryenter(&mp->mp_mtx))
|
||||
mp->mp_rc = -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
splat_mutex_test1(struct file *file, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct work;
|
||||
mutex_priv_t *mp;
|
||||
|
||||
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
|
||||
if (mp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
wq = create_singlethread_workqueue(SPLAT_MUTEX_TEST_WORKQ);
|
||||
if (wq == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
mutex_enter(&(mp->mp_mtx));
|
||||
|
||||
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
|
||||
mp->mp_file = file;
|
||||
INIT_WORK(&work, splat_mutex_test1_work, mp);
|
||||
|
||||
/* Schedule a work item which will try and aquire the mutex via
|
||||
* mutex_tryenter() while its held. This should fail and the work
|
||||
* item will indicte this status in the passed private data. */
|
||||
if (!queue_work(wq, &work)) {
|
||||
mutex_exit(&(mp->mp_mtx));
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
mutex_exit(&(mp->mp_mtx));
|
||||
|
||||
/* Work item successfully aquired mutex, very bad! */
|
||||
if (mp->mp_rc != -EBUSY) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
|
||||
"mutex_trylock() correctly failed when mutex held\n");
|
||||
|
||||
/* Schedule a work item which will try and aquire the mutex via
|
||||
* mutex_tryenter() while it is not held. This should work and
|
||||
* the item will indicte this status in the passed private data. */
|
||||
if (!queue_work(wq, &work)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
|
||||
/* Work item failed to aquire mutex, very bad! */
|
||||
if (mp->mp_rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
|
||||
"mutex_trylock() correctly succeeded when mutex unheld\n");
|
||||
out:
|
||||
mutex_destroy(&(mp->mp_mtx));
|
||||
destroy_workqueue(wq);
|
||||
out2:
|
||||
kfree(mp);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
static void
|
||||
splat_mutex_test2_work(void *priv)
|
||||
{
|
||||
mutex_priv_t *mp = (mutex_priv_t *)priv;
|
||||
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 */
|
||||
mp->mp_rc = rc + 1;
|
||||
mutex_exit(&mp->mp_mtx);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
splat_mutex_test2(struct file *file, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
struct workqueue_struct *wq;
|
||||
mutex_priv_t *mp;
|
||||
int i;
|
||||
|
||||
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
|
||||
if (mp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create a thread per CPU items on queue will race */
|
||||
wq = create_workqueue(SPLAT_MUTEX_TEST_WORKQ);
|
||||
if (wq == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
|
||||
mp->mp_file = file;
|
||||
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
|
||||
* mutex 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++) {
|
||||
INIT_WORK(&(mp->mp_work[i]), splat_mutex_test2_work, mp);
|
||||
|
||||
if (!queue_work(wq, &(mp->mp_work[i]))) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME,
|
||||
"Failed to queue work id %d\n", i);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
mutex_destroy(&(mp->mp_mtx));
|
||||
destroy_workqueue(wq);
|
||||
out:
|
||||
kfree(mp);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_mutex_test3(struct file *file, void *arg)
|
||||
{
|
||||
kmutex_t mtx;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mutex_enter(&mtx);
|
||||
|
||||
/* Mutex should be owned by current */
|
||||
if (!mutex_owned(&mtx)) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, mtx.km_owner ? mtx.km_owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_exit(&mtx);
|
||||
|
||||
/* Mutex should not be owned by any task */
|
||||
if (mutex_owned(&mtx)) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should "
|
||||
"not be owned but is owned by pid %d\n",
|
||||
mtx.km_owner ? mtx.km_owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
|
||||
"Correct mutex_owned() behavior\n");
|
||||
out:
|
||||
mutex_destroy(&mtx);
|
||||
|
||||
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);
|
||||
|
||||
mutex_enter(&mtx);
|
||||
|
||||
/* Mutex should be owned by current */
|
||||
owner = mutex_owner(&mtx);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_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_TEST3_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;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KRNG 0x0300
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_RWLOCK 0x0700
|
||||
#define SPLAT_RWLOCK_NAME "rwlock"
|
||||
#define SPLAT_RWLOCK_DESC "Kernel RW Lock Tests"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST1_ID 0x0701
|
||||
#define SPLAT_RWLOCK_TEST1_NAME "rwtest1"
|
||||
#define SPLAT_RWLOCK_TEST1_DESC "Multiple Readers One Writer"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST2_ID 0x0702
|
||||
#define SPLAT_RWLOCK_TEST2_NAME "rwtest2"
|
||||
#define SPLAT_RWLOCK_TEST2_DESC "Multiple Writers"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST3_ID 0x0703
|
||||
#define SPLAT_RWLOCK_TEST3_NAME "rwtest3"
|
||||
#define SPLAT_RWLOCK_TEST3_DESC "Owner Verification"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST4_ID 0x0704
|
||||
#define SPLAT_RWLOCK_TEST4_NAME "rwtest4"
|
||||
#define SPLAT_RWLOCK_TEST4_DESC "Trylock Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST5_ID 0x0705
|
||||
#define SPLAT_RWLOCK_TEST5_NAME "rwtest5"
|
||||
#define SPLAT_RWLOCK_TEST5_DESC "Write Downgrade Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST6_ID 0x0706
|
||||
#define SPLAT_RWLOCK_TEST6_NAME "rwtest6"
|
||||
#define SPLAT_RWLOCK_TEST6_DESC "Read Upgrade Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST_MAGIC 0x115599DDUL
|
||||
#define SPLAT_RWLOCK_TEST_NAME "rwlock_test"
|
||||
#define SPLAT_RWLOCK_TEST_COUNT 8
|
||||
|
||||
#define SPLAT_RWLOCK_RELEASE_INIT 0
|
||||
#define SPLAT_RWLOCK_RELEASE_WRITERS 1
|
||||
#define SPLAT_RWLOCK_RELEASE_READERS 2
|
||||
|
||||
typedef struct rw_priv {
|
||||
unsigned long rw_magic;
|
||||
struct file *rw_file;
|
||||
krwlock_t rwl;
|
||||
spinlock_t rw_priv_lock;
|
||||
wait_queue_head_t rw_waitq;
|
||||
atomic_t rw_completed;
|
||||
atomic_t rw_acquired;
|
||||
atomic_t rw_waiters;
|
||||
atomic_t rw_release;
|
||||
} rw_priv_t;
|
||||
|
||||
typedef struct rw_thr {
|
||||
int rwt_id;
|
||||
const char *rwt_name;
|
||||
rw_priv_t *rwt_rwp;
|
||||
int rwt_rc;
|
||||
} rw_thr_t;
|
||||
|
||||
static inline void
|
||||
splat_rwlock_sleep(signed long delay)
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(delay);
|
||||
}
|
||||
|
||||
#define splat_rwlock_lock_and_test(lock,test) \
|
||||
({ \
|
||||
int ret = 0; \
|
||||
\
|
||||
spin_lock(lock); \
|
||||
ret = (test) ? 1 : 0; \
|
||||
spin_unlock(lock); \
|
||||
ret; \
|
||||
})
|
||||
|
||||
void splat_init_rw_priv(rw_priv_t *rwv, struct file *file)
|
||||
{
|
||||
rwv->rw_magic = SPLAT_RWLOCK_TEST_MAGIC;
|
||||
rwv->rw_file = file;
|
||||
spin_lock_init(&rwv->rw_priv_lock);
|
||||
init_waitqueue_head(&rwv->rw_waitq);
|
||||
atomic_set(&rwv->rw_completed, 0);
|
||||
atomic_set(&rwv->rw_acquired, 0);
|
||||
atomic_set(&rwv->rw_waiters, 0);
|
||||
atomic_set(&rwv->rw_release, SPLAT_RWLOCK_RELEASE_INIT);
|
||||
|
||||
/* Initialize the read/write lock */
|
||||
rw_init(&rwv->rwl, SPLAT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test1_writer_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for writing
|
||||
* release it when we are told to */
|
||||
rw_enter(&rwv->rwl, RW_WRITER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can release the write lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_WRITERS));
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test1_reader_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Don't try and and take the semaphore until
|
||||
* someone else has already acquired it */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_acquired) > 0));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for reading
|
||||
* release it when we are told to */
|
||||
rw_enter(&rwv->rwl, RW_READER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can release the read lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_READERS));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test1(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data including the rwlock */
|
||||
splat_init_rw_priv(&rwv, 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 = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST1_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
if (i == 0) {
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_writer_thread,
|
||||
&rwt[i], 0);
|
||||
} else {
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_reader_thread,
|
||||
&rwt[i], 0);
|
||||
}
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the writer has the lock, release the readers */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock, atomic_read(&rwv.rw_acquired) <= 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Ensure that there is only 1 writer and all readers are waiting */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 1 ||
|
||||
atomic_read(&rwv.rw_waiters) !=
|
||||
SPLAT_RWLOCK_TEST_COUNT - 1)) {
|
||||
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Relase the writer */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Now ensure that there are multiple reader threads holding the lock */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) <= 1)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Release the readers */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_READERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test2_writer_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Here just increment the waiters count even if we are not
|
||||
* exactly about to call rw_enter(). Not really a big deal
|
||||
* since more than likely will be true when we simulate work
|
||||
* later on */
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can acquire the write lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_WRITERS));
|
||||
|
||||
/* Take the semaphore for writing */
|
||||
rw_enter(&rwv->rwl, RW_WRITER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Give up the processor for a bit to simulate
|
||||
* doing some work while taking the write lock */
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Ensure that we are the only one writing */
|
||||
if (atomic_read(&rwv->rw_acquired) > 1) {
|
||||
rwt->rwt_rc = 1;
|
||||
} else {
|
||||
rwt->rwt_rc = 0;
|
||||
}
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
rw_exit(&rwv->rwl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test2(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data including the rwlock */
|
||||
splat_init_rw_priv(&rwv, 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 = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST2_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
pids[i] = kernel_thread(splat_rwlock_test2_writer_thread,
|
||||
&rwt[i], 0);
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for writers to get queued up */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_waiters) < SPLAT_RWLOCK_TEST_COUNT)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Relase the writers */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
|
||||
/* If any of the write threads ever acquired the lock
|
||||
* while another thread had it, make sure we return
|
||||
* an error */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
if (rwt[i].rwt_rc) {
|
||||
rc++;
|
||||
}
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test3(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for writing */
|
||||
rw_enter(&rwv.rwl, RW_WRITER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, owner ? owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Take the rwlock for reading.
|
||||
* Should not have an owner */
|
||||
rw_enter(&rwv.rwl, RW_READER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test4_reader_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Don't try and and take the semaphore until
|
||||
* someone else has already acquired it */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_acquired) > 0));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for reading
|
||||
* release it when we are told to */
|
||||
rwt->rwt_rc = rw_tryenter(&rwv->rwl, RW_READER);
|
||||
|
||||
/* Here we acquired the lock this is a
|
||||
* failure since the writer should be
|
||||
* holding the lock */
|
||||
if (rwt->rwt_rc == 1) {
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
}
|
||||
/* Here we know we didn't block and didn't
|
||||
* acquire the rwlock for reading */
|
||||
else {
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread could not acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test4(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, 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 = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST4_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
if (i == 0) {
|
||||
/* We can reuse the test1 writer thread here */
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_writer_thread,
|
||||
&rwt[i], 0);
|
||||
} else {
|
||||
pids[i] = kernel_thread(splat_rwlock_test4_reader_thread,
|
||||
&rwt[i], 0);
|
||||
}
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the writer has the lock, release the readers */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) <= 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Make sure that the reader threads complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_completed) != SPLAT_RWLOCK_TEST_COUNT - 1)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Release the writer */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
|
||||
/* If any of the reader threads ever acquired the lock
|
||||
* while another thread had it, make sure we return
|
||||
* an error since the rw_tryenter() should have failed */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
if (rwt[i].rwt_rc) {
|
||||
rc++;
|
||||
}
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test5(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for writing */
|
||||
rw_enter(&rwv.rwl, RW_WRITER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, owner ? owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure that the downgrade
|
||||
* worked properly */
|
||||
rw_downgrade(&rwv.rwl);
|
||||
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test6(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for reading */
|
||||
rw_enter(&rwv.rwl, RW_READER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure that the upgrade
|
||||
* worked properly */
|
||||
rc = !rw_tryupgrade(&rwv.rwl);
|
||||
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (rc || current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d "
|
||||
"trylock rc %d\n",
|
||||
current->pid, owner ? owner->pid : -1, rc);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
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);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_rwlock_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_TASKQ 0x0200
|
||||
#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"
|
||||
|
||||
typedef struct splat_taskq_arg {
|
||||
int flag;
|
||||
int id;
|
||||
struct file *file;
|
||||
const char *name;
|
||||
} splat_taskq_arg_t;
|
||||
|
||||
/* Validation Test 1 - 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(struct file *file, void *arg)
|
||||
{
|
||||
taskq_t *tq;
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_arg;
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
|
||||
SPLAT_TASKQ_TEST1_NAME);
|
||||
if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
|
||||
50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' create failed\n",
|
||||
SPLAT_TASKQ_TEST1_NAME);
|
||||
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 ((id = taskq_dispatch(tq, splat_taskq_test13_func,
|
||||
&tq_arg, TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' function '%s' dispatch failed\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
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);
|
||||
taskq_destroy(tq);
|
||||
|
||||
return (tq_arg.flag) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/* Validation Test 2 - Create multiple taskq's, each with multiple tasks,
|
||||
* wait until all tasks complete, ensure all tasks ran properly and in the
|
||||
* the correct order, cleanup taskq's
|
||||
*/
|
||||
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 4
|
||||
|
||||
static int
|
||||
splat_taskq_test2(struct file *file, void *arg) {
|
||||
taskq_t *tq[TEST2_TASKQS] = { NULL };
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_args[TEST2_TASKQS];
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < TEST2_TASKQS; i++) {
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
|
||||
"creating\n", SPLAT_TASKQ_TEST2_NAME, i);
|
||||
if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
|
||||
TEST2_THREADS_PER_TASKQ,
|
||||
maxclsyspri, 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 ((id = taskq_dispatch(
|
||||
tq[i], splat_taskq_test2_func1,
|
||||
&tq_args[i], TQ_SLEEP)) == 0) {
|
||||
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 ((id = taskq_dispatch(
|
||||
tq[i], splat_taskq_test2_func2,
|
||||
&tq_args[i], TQ_SLEEP)) == 0) {
|
||||
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[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validation Test 3 - Use the global system task queue with a single
|
||||
* task, * wait until task completes, ensure task ran properly.
|
||||
*/
|
||||
static int
|
||||
splat_taskq_test3(struct file *file, void *arg)
|
||||
{
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_arg;
|
||||
|
||||
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' dispatching\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
if ((id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
|
||||
&tq_arg, TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
|
||||
"Taskq '%s' function '%s' dispatch failed\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
|
||||
tq_arg.name);
|
||||
taskq_wait(system_taskq);
|
||||
|
||||
return (tq_arg.flag) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_taskq_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_THREAD 0x0600
|
||||
#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_TEST_MAGIC 0x4488CC00UL
|
||||
|
||||
typedef struct thread_priv {
|
||||
unsigned long tp_magic;
|
||||
struct file *tp_file;
|
||||
spinlock_t tp_lock;
|
||||
wait_queue_head_t tp_waitq;
|
||||
int tp_rc;
|
||||
} 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 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;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
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, minclsyspri);
|
||||
/* 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;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
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;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
}
|
||||
|
||||
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, minclsyspri);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_thread_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_TIME 0x0800
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This 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.
|
||||
*
|
||||
* This 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 this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#define SPLAT_SUBSYSTEM_VNODE 0x0900
|
||||
#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_TEST4_ID 0x0904
|
||||
#define SPLAT_VNODE_TEST4_NAME "vn_rename"
|
||||
#define SPLAT_VNODE_TEST4_DESC "Vn_rename 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_TEST7_ID 0x0907
|
||||
#define SPLAT_VNODE_TEST7_NAME "vn_getf"
|
||||
#define SPLAT_VNODE_TEST7_DESC "vn_getf/vn_releasef 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_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);
|
||||
VN_RELE(vp);
|
||||
|
||||
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);
|
||||
VN_RELE(vp);
|
||||
|
||||
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 = 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 < 0) {
|
||||
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 < 0) {
|
||||
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);
|
||||
VN_RELE(vp);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test3() */
|
||||
|
||||
static int
|
||||
splat_vnode_test4(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
char buf1[32] = "SPL VNode Interface Test File\n";
|
||||
char buf2[32] = "";
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE,
|
||||
FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, rc);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
|
||||
rc = vn_rename(SPLAT_VNODE_TEST_FILE_RW1,SPLAT_VNODE_TEST_FILE_RW2,0);
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rename "
|
||||
"%s -> %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1,
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE,
|
||||
FREAD | FEXCL, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed vn_rdwr read of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (strncmp(buf1, buf2, strlen(buf1))) {
|
||||
rc = EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed strncmp data written does not match "
|
||||
"data read\nWrote: %sRead: %s\n", buf1, buf2);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Wrote to %s: %s",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, buf1);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Read from %s: %s",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, buf2);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Successfully renamed "
|
||||
"test file %s -> %s and verified data pattern\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2);
|
||||
out2:
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
out:
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, RMFILE);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test4() */
|
||||
|
||||
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);
|
||||
VN_RELE(vp);
|
||||
|
||||
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 = 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 < 0) {
|
||||
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);
|
||||
VN_RELE(vp);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test6() */
|
||||
|
||||
/* Basically a slightly modified version of sys_close() */
|
||||
static int
|
||||
fd_uninstall(int fd)
|
||||
{
|
||||
struct file *fp;
|
||||
struct files_struct *files = current->files;
|
||||
#ifdef HAVE_FILES_FDTABLE
|
||||
struct fdtable *fdt;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
|
||||
if (fd >= fdt->max_fds)
|
||||
goto out_unlock;
|
||||
|
||||
fp = fdt->fd[fd];
|
||||
if (!fp)
|
||||
goto out_unlock;
|
||||
|
||||
rcu_assign_pointer(fdt->fd[fd], NULL);
|
||||
FD_CLR(fd, fdt->close_on_exec);
|
||||
#else
|
||||
spin_lock(&files->file_lock);
|
||||
if (fd >= files->max_fds)
|
||||
goto out_unlock;
|
||||
|
||||
fp = files->fd[fd];
|
||||
if (!fp)
|
||||
goto out_unlock;
|
||||
|
||||
files->fd[fd] = NULL;
|
||||
FD_CLR(fd, files->close_on_exec);
|
||||
#endif
|
||||
/* Dropping the lock here exposes a minor race but it allows me
|
||||
* to use the existing kernel interfaces for this, and for a test
|
||||
* case I think that's reasonable. */
|
||||
spin_unlock(&files->file_lock);
|
||||
put_unused_fd(fd);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&files->file_lock);
|
||||
return -EBADF;
|
||||
} /* fd_uninstall() */
|
||||
|
||||
static int
|
||||
splat_vnode_test7(struct file *file, void *arg)
|
||||
{
|
||||
char buf1[32] = "SPL VNode Interface Test File\n";
|
||||
char buf2[32] = "";
|
||||
struct file *lfp;
|
||||
file_t *fp;
|
||||
int rc, fd;
|
||||
|
||||
/* Prep work needed to test getf/releasef */
|
||||
fd = get_unused_fd();
|
||||
if (fd < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to get unused fd (%d)\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
lfp = filp_open(SPLAT_VNODE_TEST_FILE_RW, O_RDWR|O_CREAT|O_EXCL, 0644);
|
||||
if (IS_ERR(lfp)) {
|
||||
put_unused_fd(fd);
|
||||
rc = PTR_ERR(lfp);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to filp_open: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pair up the new fd and lfp in the current context, this allows
|
||||
* getf to lookup the file struct simply by the known open fd */
|
||||
fd_install(fd, lfp);
|
||||
|
||||
/* Actual getf()/releasef() test */
|
||||
fp = vn_getf(fd);
|
||||
if (fp == NULL) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to getf fd %d: (%d)\n", fd, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, fp->f_vnode, buf1, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_READ, fp->f_vnode, buf2, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_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_TEST7_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:
|
||||
vn_releasef(fd);
|
||||
fd_uninstall(fd);
|
||||
filp_close(lfp, 0);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test7() */
|
||||
|
||||
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_TEST4_NAME, SPLAT_VNODE_TEST4_DESC,
|
||||
SPLAT_VNODE_TEST4_ID, splat_vnode_test4);
|
||||
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);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST7_NAME, SPLAT_VNODE_TEST7_DESC,
|
||||
SPLAT_VNODE_TEST7_ID, splat_vnode_test7);
|
||||
|
||||
return sub;
|
||||
} /* splat_vnode_init() */
|
||||
|
||||
void
|
||||
splat_vnode_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST7_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST6_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST4_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() */
|
||||
Reference in New Issue
Block a user