Initial commit. All spl source written up to this point wrapped

in an initial reasonable autoconf style build system.  This does
not yet build but the configure system does appear to work properly
and integrate with the kernel.  Hopefully the next commit gets
us back to a buildable version we can run the test suite against.



git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@1 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
This commit is contained in:
behlendo 2008-02-26 20:36:04 +00:00
commit f1ca4da6f7
43 changed files with 6322 additions and 0 deletions

5
ChangeLog Normal file
View File

@ -0,0 +1,5 @@
2008-02-26 Brian Behlendorf <behlendorf1@llnl.gov>
: Initial commit of the solaris porting layer (spl). Included
in addition to the source is an initial autoconf / configure
style build system.

9
Makefile.am Normal file
View File

@ -0,0 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-zip
SUBDIRS = src include scripts
CONFIG_CLEAN_FILES = aclocal.m4 config.guess config.sub
CONFIG_CLEAN_FILES += depcomp install-sh missing mkinstalldirs
EXTRA_DIST = autogen.sh
rpms: dist Makefile
rpmbuild -ta $(distdir).tar.gz

8
autogen.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
find . -type d -name .deps | xargs rm -rf
aclocal 2>/dev/null &&
autoheader &&
automake --add-missing --include-deps # 2>/dev/null &&
autoconf

133
configure.ac Normal file
View File

@ -0,0 +1,133 @@
AC_INIT
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(spl, 0.0.1)
AC_CONFIG_HEADERS([config.h])
AC_PROG_INSTALL
AC_PROG_CC
ver=`uname -r`
KERNELCFLAGS=
kernelsrc=
kernelbuild=
AC_ARG_WITH(kernel,
[ --with-linux=PATH Path to kernel source ],
[kernelsrc="$withval"; kernelbuild="$withval"])
AC_ARG_WITH(kernel-build,
[ --with-linux-obj=PATH Path to kernel build objects ],
[kernelbuild="$withval"])
AC_MSG_CHECKING([kernel source directory])
if test -z "$kernelsrc"; then
kernelbuild=
sourcelink=/lib/modules/${ver}/source
buildlink=/lib/modules/${ver}/build
if test -e $sourcelink; then
kernelsrc=`(cd $sourcelink; /bin/pwd)`
fi
if test -e $buildlink; then
kernelbuild=`(cd $buildlink; /bin/pwd)`
fi
if test -z "$kernelsrc"; then
kernelsrc=$kernelbuild
fi
if test -z "$kernelsrc" -o -z "$kernelbuild"; then
AC_MSG_RESULT([Not found])
AC_MSG_ERROR([
*** Please specify the location of the kernel source
*** with the '--with-kernel=PATH' option])
fi
fi
AC_MSG_RESULT([$kernelsrc])
AC_MSG_CHECKING([kernel build directory])
AC_MSG_RESULT([$kernelbuild])
AC_MSG_CHECKING([kernel source version])
if test -r $kernelbuild/include/linux/version.h &&
fgrep -q UTS_RELEASE $kernelbuild/include/linux/version.h; then
kernsrcver=`(echo "#include <linux/version.h>";
echo "kernsrcver=UTS_RELEASE") |
cpp -I $kernelbuild/include |
grep "^kernsrcver=" | cut -d \" -f 2`
elif test -r $kernelbuild/include/linux/utsrelease.h &&
fgrep -q UTS_RELEASE $kernelbuild/include/linux/utsrelease.h; then
kernsrcver=`(echo "#include <linux/utsrelease.h>";
echo "kernsrcver=UTS_RELEASE") |
cpp -I $kernelbuild/include |
grep "^kernsrcver=" | cut -d \" -f 2`
fi
if test -z "$kernsrcver"; then
AC_MSG_RESULT([Not found])
AC_MSG_ERROR([
*** Cannot determine the version of the linux kernel source.
*** Please prepare the kernel before running this script])
fi
AC_MSG_RESULT([$kernsrcver])
kmoduledir=${INSTALL_MOD_PATH}/lib/modules/$kernsrcver
AC_SUBST(kernelsrc)
AC_SUBST(kmoduledir)
#
# Each version of the kernel provides a slightly different
# ABI, so figure out what we have to work with and adapt.
#
AC_MSG_CHECKING([if kernel defines kzalloc function])
if egrep -qw "kzalloc" $kernelsrc/include/linux/slab.h; then
AC_DEFINE(HAVE_KZALLOC, 1, [kzalloc() is defined])
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
AC_MSG_CHECKING([if inode has i_private field])
if egrep -qw "i_private" $kernelsrc/include/linux/fs.h; then
AC_DEFINE(HAVE_I_PRIVATE, 1, [inode has i_private field])
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
AC_MSG_CHECKING([if inode has i_mutex field ])
if egrep -qw "i_mutex" $kernelsrc/include/linux/fs.h; then
AC_DEFINE(HAVE_I_MUTEX, 1, [inode has i_mutex field])
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
AC_MSG_CHECKING([if kernel has mutex.h ])
if test -f $kernelsrc/include/linux/mutex.h; then
AC_DEFINE(HAVE_MUTEX_H, 1, [kernel has mutex.h])
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
fi
if test "$kernelbuild" != "$kernelsrc"; then
KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$kernelbuild"
fi
AC_SUBST(KERNELMAKE_PARAMS)
AC_SUBST(KERNELCPPFLAGS)
AC_SUBST(KERNELCFLAGS)
AC_CONFIG_FILES([ Makefile
src/Makefile
src/cmd/Makefile
src/spl/Makefile
src/splat/Makefile
include/Makefile
scripts/Makefile
scripts/spl.spec
])
AC_OUTPUT

5
include/Makefile.am Normal file
View File

@ -0,0 +1,5 @@
EXTRA_DIST = spl.h splat.h splat-ctl.h
EXTRA_DIST += linux-condvar.h linux-kmem.h linux-random.h linux-thread.h
EXTRA_DIST += linux-types.h linux-cred.h linux-kstat.h linux-rwlock.h
EXTRA_DIST += linux-time.h linux-callb.h linux-generic.h linux-mutex.h
EXTRA_DIST += linux-taskq.h linux-timer.h

45
include/linux-callb.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _SYS_LINUX_CALLB_H
#define _SYS_LINUX_CALLB_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/linux-mutex.h>
#define DEBUG_CALLB
#ifndef DEBUG_CALLB
#define CALLB_CPR_ASSERT(cp) BUG_ON(!(MUTEX_HELD((cp)->cc_lockp)));
#else
#define CALLB_CPR_ASSERT(cp)
#endif
typedef struct callb_cpr {
kmutex_t *cc_lockp;
} callb_cpr_t;
#define CALLB_CPR_INIT(cp, lockp, func, name) { \
(cp)->cc_lockp = lockp; \
}
#define CALLB_CPR_SAFE_BEGIN(cp) { \
CALLB_CPR_ASSERT(cp); \
}
#define CALLB_CPR_SAFE_END(cp, lockp) { \
CALLB_CPR_ASSERT(cp); \
}
#define CALLB_CPR_EXIT(cp) { \
ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
mutex_exit((cp)->cc_lockp); \
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_CALLB_H */

201
include/linux-condvar.h Normal file
View File

@ -0,0 +1,201 @@
#ifndef _SYS_LINUX_CONDVAR_H
#define _SYS_LINUX_CONDVAR_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/wait.h>
/* The kcondvar_t struct is protected by mutex taken externally before
* calling any of the wait/signal funs, and passed into the wait funs.
*/
#define CV_MAGIC 0x346545f4
#define CV_POISON 0x95
typedef struct {
int cv_magic;
char *cv_name;
wait_queue_head_t cv_event;
atomic_t cv_waiters;
kmutex_t *cv_mutex; /* only for verification purposes */
} kcondvar_t;
typedef enum { CV_DEFAULT=0, CV_DRIVER } kcv_type_t;
static __inline__ void
cv_init(kcondvar_t *cvp, char *name, kcv_type_t type, void *arg)
{
BUG_ON(cvp == NULL);
BUG_ON(type != CV_DEFAULT);
BUG_ON(arg != NULL);
cvp->cv_magic = CV_MAGIC;
init_waitqueue_head(&cvp->cv_event);
atomic_set(&cvp->cv_waiters, 0);
cvp->cv_mutex = NULL;
cvp->cv_name = NULL;
if (name) {
cvp->cv_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
if (cvp->cv_name)
strcpy(cvp->cv_name, name);
}
}
static __inline__ void
cv_destroy(kcondvar_t *cvp)
{
BUG_ON(cvp == NULL);
BUG_ON(cvp->cv_magic != CV_MAGIC);
BUG_ON(atomic_read(&cvp->cv_waiters) != 0);
BUG_ON(waitqueue_active(&cvp->cv_event));
if (cvp->cv_name)
kfree(cvp->cv_name);
memset(cvp, CV_POISON, sizeof(*cvp));
}
static __inline__ void
cv_wait(kcondvar_t *cvp, kmutex_t *mtx)
{
DEFINE_WAIT(wait);
int flag = 1;
BUG_ON(cvp == NULL || mtx == NULL);
BUG_ON(cvp->cv_magic != CV_MAGIC);
BUG_ON(!mutex_owned(mtx));
if (cvp->cv_mutex == NULL)
cvp->cv_mutex = mtx;
/* Ensure the same mutex is used by all callers */
BUG_ON(cvp->cv_mutex != mtx);
for (;;) {
prepare_to_wait_exclusive(&cvp->cv_event, &wait,
TASK_INTERRUPTIBLE);
/* Must occur after we are added to the list but only once */
if (flag) {
atomic_inc(&cvp->cv_waiters);
flag = 0;
}
/* XXX - The correct thing to do here may be to wake up and
* force the caller to handle the signal. Spurious wakeups
* should already be safely handled by the caller. */
if (signal_pending(current))
flush_signals(current);
/* Mutex should be dropped after prepare_to_wait() this
* ensures we're linked in to the waiters list and avoids the
* race where 'cvp->cv_waiters > 0' but the list is empty. */
mutex_exit(mtx);
schedule();
mutex_enter(mtx);
/* XXX - The correct thing to do here may be to wake up and
* force the caller to handle the signal. Spurious wakeups
* should already be safely handled by the caller. */
if (signal_pending(current))
continue;
break;
}
atomic_dec(&cvp->cv_waiters);
finish_wait(&cvp->cv_event, &wait);
}
/* 'expire_time' argument is an absolute wall clock time in jiffies.
* Return value is time left (expire_time - now) or -1 if timeout occurred.
*/
static __inline__ clock_t
cv_timedwait(kcondvar_t *cvp, kmutex_t *mtx, clock_t expire_time)
{
DEFINE_WAIT(wait);
clock_t time_left;
int flag = 1;
BUG_ON(cvp == NULL || mtx == NULL);
BUG_ON(cvp->cv_magic != CV_MAGIC);
BUG_ON(!mutex_owned(mtx));
if (cvp->cv_mutex == NULL)
cvp->cv_mutex = mtx;
/* XXX - Does not handle jiffie wrap properly */
time_left = expire_time - jiffies;
if (time_left <= 0)
return -1;
/* Ensure the same mutex is used by all callers */
BUG_ON(cvp->cv_mutex != mtx);
for (;;) {
prepare_to_wait_exclusive(&cvp->cv_event, &wait,
TASK_INTERRUPTIBLE);
if (flag) {
atomic_inc(&cvp->cv_waiters);
flag = 0;
}
/* XXX - The correct thing to do here may be to wake up and
* force the caller to handle the signal. Spurious wakeups
* should already be safely handled by the caller. */
if (signal_pending(current))
flush_signals(current);
/* Mutex should be dropped after prepare_to_wait() this
* ensures we're linked in to the waiters list and avoids the
* race where 'cvp->cv_waiters > 0' but the list is empty. */
mutex_exit(mtx);
time_left = schedule_timeout(time_left);
mutex_enter(mtx);
/* XXX - The correct thing to do here may be to wake up and
* force the caller to handle the signal. Spurious wakeups
* should already be safely handled by the caller. */
if (signal_pending(current)) {
if (time_left > 0)
continue;
flush_signals(current);
}
break;
}
atomic_dec(&cvp->cv_waiters);
finish_wait(&cvp->cv_event, &wait);
return (time_left > 0 ? time_left : -1);
}
static __inline__ void
cv_signal(kcondvar_t *cvp)
{
BUG_ON(cvp == NULL);
BUG_ON(cvp->cv_magic != CV_MAGIC);
/* All waiters are added with WQ_FLAG_EXCLUSIVE so only one
* waiter will be set runable with each call to wake_up().
* Additionally wake_up() holds a spin_lock assoicated with
* the wait queue to ensure we don't race waking up processes. */
if (atomic_read(&cvp->cv_waiters) > 0)
wake_up(&cvp->cv_event);
}
static __inline__ void
cv_broadcast(kcondvar_t *cvp)
{
BUG_ON(cvp == NULL);
BUG_ON(cvp->cv_magic != CV_MAGIC);
/* Wake_up_all() will wake up all waiters even those which
* have the WQ_FLAG_EXCLUSIVE flag set. */
if (atomic_read(&cvp->cv_waiters) > 0)
wake_up_all(&cvp->cv_event);
}
#endif /* _SYS_LINUX_CONDVAR_H */

40
include/linux-cred.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef _SYS_LINUX_CRED_H
#define _SYS_LINUX_CRED_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/types.h>
/* XXX - Portions commented out because we really just want to have the type
* defined and the contents aren't nearly so important at the moment. */
typedef struct cred {
uint_t cr_ref; /* reference count */
uid_t cr_uid; /* effective user id */
gid_t cr_gid; /* effective group id */
uid_t cr_ruid; /* real user id */
gid_t cr_rgid; /* real group id */
uid_t cr_suid; /* "saved" user id (from exec) */
gid_t cr_sgid; /* "saved" group id (from exec) */
uint_t cr_ngroups; /* number of groups returned by */
/* crgroups() */
#if 0
cred_priv_t cr_priv; /* privileges */
projid_t cr_projid; /* project */
struct zone *cr_zone; /* pointer to per-zone structure */
struct ts_label_s *cr_label; /* pointer to the effective label */
credsid_t *cr_ksid; /* pointer to SIDs */
#endif
gid_t cr_groups[1]; /* cr_groups size not fixed */
/* audit info is defined dynamically */
/* and valid only when audit enabled */
/* auditinfo_addr_t cr_auinfo; audit info */
} cred_t;
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_CRED_H */

72
include/linux-generic.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef _SYS_LINUX_GENERIC_H
#define _SYS_LINUX_GENERIC_H
#ifdef __cplusplus
extern "C" {
#endif
/* Missing defines.
*/
#define INT32_MAX INT_MAX
#define UINT64_MAX (~0ULL)
#define NBBY 8
#define ENOTSUP ENOTSUPP
#define MAXNAMELEN 256
#define MAXPATHLEN PATH_MAX
#define __va_list va_list
#define _KERNEL 1
#define max_ncpus 64
/* 0..MAX_PRIO-1: Process priority
* 0..MAX_RT_PRIO-1: RT priority tasks
* MAX_RT_PRIO..MAX_PRIO-1: SCHED_NORMAL tasks
*
* Treat shim tasks as SCHED_NORMAL tasks
*/
#define minclsyspri (MAX_RT_PRIO)
#define maxclsyspri (MAX_PRIO-1)
#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20)
#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20)
#define kred NULL
#define FREAD 1
#define FWRITE 2
#define FCREAT O_CREAT
#define FTRUNC O_TRUNC
#define FOFFMAX O_LARGEFILE
#define FSYNC O_SYNC
#define FDSYNC O_DSYNC
#define FRSYNC O_RSYNC
#define FEXCL O_EXCL
#define FNODSYNC 0x10000 /* fsync pseudo flag */
#define FNOFOLLOW 0x20000 /* don't follow symlinks */
/* Missing macros
*/
#define PAGESIZE PAGE_SIZE
/* from Solaris sys/byteorder.h */
#define BSWAP_8(x) ((x) & 0xff)
#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8))
#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16))
#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32))
/* Map some simple functions.
*/
#define bzero(ptr,size) memset(ptr,0,size)
#define bcopy(src,dest,size) memcpy(dest,src,size)
#define ASSERT(x) BUG_ON(!(x))
#define ASSERT3U(left,OP,right) BUG_ON(!((left) OP (right)))
/* Missing globals
*/
static int p0 = 0;
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_GENERIC_H */

173
include/linux-kmem.h Normal file
View File

@ -0,0 +1,173 @@
#ifndef _SYS_LINUX_KMEM_H
#define _SYS_LINUX_KMEM_H
#ifdef __cplusplus
extern "C" {
#endif
#undef DEBUG_KMEM
#undef DEBUG_KMEM_UNIMPLEMENTED
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
/*
* Memory allocation interfaces
*/
#define KM_SLEEP GFP_KERNEL
#define KM_NOSLEEP GFP_ATOMIC
#undef KM_PANIC /* No linux analog */
#define KM_PUSHPAGE (GFP_KERNEL | GFP_HIGH)
#define KM_VMFLAGS GFP_LEVEL_MASK
#define KM_FLAGS __GFP_BITS_MASK
#ifdef DEBUG_KMEM
/* Shim layer memory accounting */
extern atomic_t kmem_alloc_used;
extern unsigned int kmem_alloc_max;
#endif
#ifdef DEBUG_KMEM
#define __kmem_alloc(size, flags, allocator) \
({ void *_ptr_; \
\
/* Marked unlikely because we should never be doing this */ \
if (unlikely((size) > (PAGE_SIZE * 2))) \
printk("Warning: kmem_alloc(%d, 0x%x) large alloc at %s:%d " \
"(%d/%d)\n", (int)(size), (int)(flags), \
__FILE__, __LINE__, \
atomic_read(&kmem_alloc_used), kmem_alloc_max); \
\
_ptr_ = (void *)allocator((size), (flags)); \
if (_ptr_ == NULL) { \
printk("Warning: kmem_alloc(%d, 0x%x) failed at %s:%d " \
"(%d/%d)\n", (int)(size), (int)(flags), \
__FILE__, __LINE__, \
atomic_read(&kmem_alloc_used), kmem_alloc_max); \
atomic_add((size), &kmem_alloc_used); \
if (unlikely(atomic_read(&kmem_alloc_used) > kmem_alloc_max)) \
kmem_alloc_max = atomic_read(&kmem_alloc_used); \
} \
\
_ptr_; \
})
#define kmem_alloc(size, flags) __kmem_alloc(size, flags, kmalloc)
#define kmem_zalloc(size, flags) __kmem_alloc(size, flags, kzalloc)
#define kmem_free(ptr, size) \
({ \
BUG_ON(!ptr); \
atomic_sub((size), &kmem_alloc_used); \
memset(ptr, 0x5a, (size)); /* Poison */ \
kfree(ptr); \
(ptr) = (void *)0xdeadbeef; \
})
#else
#define kmem_alloc(size, flags) kmalloc(size, flags)
#define kmem_zalloc(size, flags) kzalloc(size, flags)
#define kmem_free(ptr, size) kfree(ptr)
#endif /* DEBUG_KMEM */
#ifdef DEBUG_KMEM_UNIMPLEMENTED
static __inline__ void *
kmem_alloc_tryhard(size_t size, size_t *alloc_size, int kmflags)
{
#error "kmem_alloc_tryhard() not implemented"
}
#endif /* DEBUG_KMEM_UNIMPLEMENTED */
/*
* Slab allocation interfaces
*/
#undef KMC_NOTOUCH /* No linux analog */
#define KMC_NODEBUG 0x00000000 /* Default beahvior */
#define KMC_NOMAGAZINE /* No linux analog */
#define KMC_NOHASH /* No linux analog */
#define KMC_QCACHE /* No linux analog */
#define KMC_REAP_CHUNK 256
#define KMC_DEFAULT_SEEKS DEFAULT_SEEKS
/* Defined by linux slab.h
* typedef struct kmem_cache_s kmem_cache_t;
*/
/* No linux analog
* extern int kmem_ready;
* extern pgcnt_t kmem_reapahead;
*/
#ifdef DEBUG_KMEM_UNIMPLEMENTED
static __inline__ void kmem_init(void) {
#error "kmem_init() not implemented"
}
static __inline__ void kmem_thread_init(void) {
#error "kmem_thread_init() not implemented"
}
static __inline__ void kmem_mp_init(void) {
#error "kmem_mp_init() not implemented"
}
static __inline__ void kmem_reap_idspace(void) {
#error "kmem_reap_idspace() not implemented"
}
static __inline__ size_t kmem_avail(void) {
#error "kmem_avail() not implemented"
}
static __inline__ size_t kmem_maxavail(void) {
#error "kmem_maxavail() not implemented"
}
static __inline__ uint64_t kmem_cache_stat(kmem_cache_t *cache) {
#error "kmem_cache_stat() not implemented"
}
#endif /* DEBUG_KMEM_UNIMPLEMENTED */
/* XXX - Used by arc.c to adjust its memory footprint. We may want
* to use this hook in the future to adjust behavior based on
* debug levels. For now it's safe to always return 0.
*/
static __inline__ int
kmem_debugging(void)
{
return 0;
}
typedef int (*kmem_constructor_t)(void *, void *, int);
typedef void (*kmem_destructor_t)(void *, void *);
typedef void (*kmem_reclaim_t)(void *);
kmem_cache_t *
__kmem_cache_create(char *name, size_t size, size_t align,
int (*constructor)(void *, void *, int),
void (*destructor)(void *, void *),
void (*reclaim)(void *),
void *priv, void *vmp, int flags);
void
__kmem_cache_destroy(kmem_cache_t *cache);
#define kmem_cache_create(name,size,align,ctor,dtor,rclm,priv,vmp,flags) \
__kmem_cache_create(name,size,align,ctor,dtor,rclm,priv,vmp,flags)
#define kmem_cache_destroy(cache) __kmem_cache_destroy(cache)
#define kmem_cache_alloc(cache, flags) kmem_cache_alloc(cache, flags)
#define kmem_cache_free(cache, ptr) kmem_cache_free(cache, ptr)
#define kmem_cache_reap_now(cache) kmem_cache_shrink(cache)
#define kmem_reap() __kmem_reap()
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_KMEM_H */

136
include/linux-kstat.h Normal file
View File

@ -0,0 +1,136 @@
#ifndef _SYS_LINUX_KSTAT_H
#define _SYS_LINUX_KSTAT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/linux-types.h>
/* XXX - The minimum functionality here is stubbed out but nothing works. */
#define KSTAT_STRLEN 31 /* 30 chars + NULL; must be 16 * n - 1 */
#define KSTAT_TYPE_RAW 0 /* can be anything */
/* ks_ndata >= 1 */
#define KSTAT_TYPE_NAMED 1 /* name/value pair */
/* ks_ndata >= 1 */
#define KSTAT_TYPE_INTR 2 /* interrupt statistics */
/* ks_ndata == 1 */
#define KSTAT_TYPE_IO 3 /* I/O statistics */
/* ks_ndata == 1 */
#define KSTAT_TYPE_TIMER 4 /* event timer */
/* ks_ndata >= 1 */
#define KSTAT_NUM_TYPES 5
#define KSTAT_DATA_CHAR 0
#define KSTAT_DATA_INT32 1
#define KSTAT_DATA_UINT32 2
#define KSTAT_DATA_INT64 3
#define KSTAT_DATA_UINT64 4
#define KSTAT_FLAG_VIRTUAL 0x01
#define KSTAT_FLAG_VAR_SIZE 0x02
#define KSTAT_FLAG_WRITABLE 0x04
#define KSTAT_FLAG_PERSISTENT 0x08
#define KSTAT_FLAG_DORMANT 0x10
#define KSTAT_FLAG_INVALID 0x2
typedef int kid_t; /* unique kstat id */
typedef struct kstat_s {
/*
* Fields relevant to both kernel and user
*/
hrtime_t ks_crtime; /* creation time (from gethrtime()) */
struct kstat_s *ks_next; /* kstat chain linkage */
kid_t ks_kid; /* unique kstat ID */
char ks_module[KSTAT_STRLEN]; /* provider module name */
uchar_t ks_resv; /* reserved, currently just padding */
int ks_instance; /* provider module's instance */
char ks_name[KSTAT_STRLEN]; /* kstat name */
uchar_t ks_type; /* kstat data type */
char ks_class[KSTAT_STRLEN]; /* kstat class */
uchar_t ks_flags; /* kstat flags */
void *ks_data; /* kstat type-specific data */
uint_t ks_ndata; /* # of type-specific data records */
size_t ks_data_size; /* total size of kstat data section */
hrtime_t ks_snaptime; /* time of last data shapshot */
/*
* Fields relevant to kernel only
*/
int (*ks_update)(struct kstat *, int); /* dynamic update */
void *ks_private; /* arbitrary provider-private data */
int (*ks_snapshot)(struct kstat *, void *, int);
void *ks_lock; /* protects this kstat's data */
} kstat_t;
typedef struct kstat_named_s {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
char c[16]; /* enough for 128-bit ints */
int32_t i32;
uint32_t ui32;
struct {
union {
char *ptr; /* NULL-term string */
char __pad[8]; /* 64-bit padding */
} addr;
uint32_t len; /* # bytes for strlen + '\0' */
} str;
/*
* The int64_t and uint64_t types are not valid for a maximally conformant
* 32-bit compilation environment (cc -Xc) using compilers prior to the
* introduction of C99 conforming compiler (reference ISO/IEC 9899:1990).
* In these cases, the visibility of i64 and ui64 is only permitted for
* 64-bit compilation environments or 32-bit non-maximally conformant
* C89 or C90 ANSI C compilation environments (cc -Xt and cc -Xa). In the
* C99 ANSI C compilation environment, the long long type is supported.
* The _INT64_TYPE is defined by the implementation (see sys/int_types.h).
*/
int64_t i64;
uint64_t ui64;
long l;
ulong_t ul;
/* These structure members are obsolete */
longlong_t ll;
u_longlong_t ull;
float f;
double d;
} value; /* value of counter */
} kstat_named_t;
static __inline__ kstat_t *
kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
const char *ks_class, uchar_t ks_type, uint_t ks_ndata,
uchar_t ks_flags)
{
return NULL;
}
static __inline__ void
kstat_install(kstat_t *ksp)
{
return;
}
static __inline__ void
kstat_delete(kstat_t *ksp)
{
return;
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_KSTAT_H */

118
include/linux-mutex.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef _SYS_LINUX_MUTEX_H
#define _SYS_LINUX_MUTEX_H
#ifdef __cplusplus
extern "C" {
#endif
/* See the "Big Theory Statement" in solaris mutex.c.
*
* Spin mutexes apparently aren't needed by zfs so we assert
* if ibc is non-zero.
*
* Our impementation of adaptive mutexes aren't really adaptive.
* They go to sleep every time.
*/
#define MUTEX_DEFAULT 0
#define MUTEX_HELD(x) (mutex_owned(x))
#define KM_MAGIC 0x42424242
#define KM_POISON 0x84
typedef struct {
int km_magic;
char *km_name;
struct task_struct *km_owner;
struct semaphore km_sem;
} kmutex_t;
#undef mutex_init
static __inline__ void
mutex_init(kmutex_t *mp, char *name, int type, void *ibc)
{
BUG_ON(ibc != NULL); /* XXX - Spin mutexes not needed? */
BUG_ON(type != MUTEX_DEFAULT); /* XXX - Only default type supported? */
mp->km_magic = KM_MAGIC;
sema_init(&mp->km_sem, 1);
mp->km_owner = NULL;
mp->km_name = NULL;
if (name) {
mp->km_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
if (mp->km_name)
strcpy(mp->km_name, name);
}
}
#undef mutex_destroy
static __inline__ void
mutex_destroy(kmutex_t *mp)
{
BUG_ON(mp->km_magic != KM_MAGIC);
if (mp->km_name)
kfree(mp->km_name);
memset(mp, KM_POISON, sizeof(*mp));
}
static __inline__ void
mutex_enter(kmutex_t *mp)
{
BUG_ON(mp->km_magic != KM_MAGIC);
down(&mp->km_sem); /* Will check in_atomic() for us */
BUG_ON(mp->km_owner != NULL);
mp->km_owner = current;
}
/* Return 1 if we acquired the mutex, else zero.
*/
static __inline__ int
mutex_tryenter(kmutex_t *mp)
{
int result;
BUG_ON(mp->km_magic != KM_MAGIC);
result = down_trylock(&mp->km_sem); /* returns 0 if acquired */
if (result == 0) {
BUG_ON(mp->km_owner != NULL);
mp->km_owner = current;
return 1;
}
return 0;
}
static __inline__ void
mutex_exit(kmutex_t *mp)
{
BUG_ON(mp->km_magic != KM_MAGIC);
BUG_ON(mp->km_owner != current);
mp->km_owner = NULL;
up(&mp->km_sem);
}
/* Return 1 if mutex is held by current process, else zero.
*/
static __inline__ int
mutex_owned(kmutex_t *mp)
{
BUG_ON(mp->km_magic != KM_MAGIC);
return (mp->km_owner == current);
}
/* Return owner if mutex is owned, else NULL.
*/
static __inline__ kthread_t *
mutex_owner(kmutex_t *mp)
{
BUG_ON(mp->km_magic != KM_MAGIC);
return mp->km_owner;
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_MUTEX_H */

37
include/linux-random.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef _SYS_LINUX_RANDOM_H
#define _SYS_LINUX_RANDOM_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/random.h>
/* FIXME:
* Should add support for blocking in the future to
* ensure that proper entopy is collected. ZFS doesn't
* use it at the moment so this is good enough for now.
* Always will succeed by returning 0.
*/
static __inline__ int
random_get_bytes(uint8_t *ptr, size_t len)
{
BUG_ON(len < 0);
get_random_bytes((void *)ptr,(int)len);
return 0;
}
/* Always will succeed by returning 0. */
static __inline__ int
random_get_pseudo_bytes(uint8_t *ptr, size_t len)
{
BUG_ON(len < 0);
get_random_bytes((void *)ptr,(int)len);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_RANDOM_H */

223
include/linux-rwlock.h Normal file
View File

@ -0,0 +1,223 @@
#ifndef _SYS_LINUX_RWLOCK_H
#define _SYS_LINUX_RWLOCK_H
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <asm/current.h>
#include <sys/linux-types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
RW_DRIVER = 2, /* driver (DDI) rwlock */
RW_DEFAULT = 4 /* kernel default rwlock */
} krw_type_t;
typedef enum {
RW_WRITER,
RW_READER
} krw_t;
#define RW_READ_HELD(x) (rw_read_held((x)))
#define RW_WRITE_HELD(x) (rw_write_held((x)))
#define RW_LOCK_HELD(x) (rw_lock_held((x)))
#define RW_ISWRITER(x) (rw_iswriter(x))
#define RW_MAGIC 0x3423645a
#define RW_POISON 0xa6
typedef struct {
int rw_magic;
char *rw_name;
struct rw_semaphore rw_sem;
struct task_struct *rw_owner; /* holder of the write lock */
} krwlock_t;
static __inline__ void
rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg)
{
BUG_ON(type != RW_DEFAULT); /* XXX no irq handler use */
BUG_ON(arg != NULL); /* XXX no irq handler use */
rwlp->rw_magic = RW_MAGIC;
rwlp->rw_owner = NULL; /* no one holds the write lock yet */
init_rwsem(&rwlp->rw_sem);
rwlp->rw_name = NULL;
if (name) {
rwlp->rw_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
if (rwlp->rw_name)
strcpy(rwlp->rw_name, name);
}
}
static __inline__ void
rw_destroy(krwlock_t *rwlp)
{
BUG_ON(rwlp == NULL);
BUG_ON(rwlp->rw_magic != RW_MAGIC);
BUG_ON(rwlp->rw_owner != NULL);
spin_lock(&rwlp->rw_sem.wait_lock);
BUG_ON(!list_empty(&rwlp->rw_sem.wait_list));
spin_unlock(&rwlp->rw_sem.wait_lock);
if (rwlp->rw_name)
kfree(rwlp->rw_name);
memset(rwlp, RW_POISON, sizeof(krwlock_t));
}
/* Return 0 if the lock could not be obtained without blocking.
*/
static __inline__ int
rw_tryenter(krwlock_t *rwlp, krw_t rw)
{
int result;
BUG_ON(rwlp->rw_magic != RW_MAGIC);
switch (rw) {
/* these functions return 1 if success, 0 if contention */
case RW_READER:
/* Here the Solaris code would return 0
* if there were any write waiters. Specifically
* thinking about the case where readers may have
* the lock and we would also allow this thread
* to grab the read lock with a writer waiting in the
* queue. This doesn't seem like a correctness
* issue, so just call down_read_trylock()
* for the test. We may have to revisit this if
* it becomes an issue */
result = down_read_trylock(&rwlp->rw_sem);
break;
case RW_WRITER:
result = down_write_trylock(&rwlp->rw_sem);
if (result) {
/* there better not be anyone else
* holding the write lock here */
BUG_ON(rwlp->rw_owner != NULL);
rwlp->rw_owner = current;
}
break;
}
return result;
}
static __inline__ void
rw_enter(krwlock_t *rwlp, krw_t rw)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
switch (rw) {
case RW_READER:
/* Here the Solaris code would block
* if there were any write waiters. Specifically
* thinking about the case where readers may have
* the lock and we would also allow this thread
* to grab the read lock with a writer waiting in the
* queue. This doesn't seem like a correctness
* issue, so just call down_read()
* for the test. We may have to revisit this if
* it becomes an issue */
down_read(&rwlp->rw_sem);
break;
case RW_WRITER:
down_write(&rwlp->rw_sem);
/* there better not be anyone else
* holding the write lock here */
BUG_ON(rwlp->rw_owner != NULL);
rwlp->rw_owner = current;
break;
}
}
static __inline__ void
rw_exit(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
/* rw_owner is held by current
* thread iff it is a writer */
if (rwlp->rw_owner == current) {
rwlp->rw_owner = NULL;
up_write(&rwlp->rw_sem);
} else {
up_read(&rwlp->rw_sem);
}
}
static __inline__ void
rw_downgrade(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
BUG_ON(rwlp->rw_owner != current);
rwlp->rw_owner = NULL;
downgrade_write(&rwlp->rw_sem);
}
/* Return 0 if unable to perform the upgrade.
* Might be wise to fix the caller
* to acquire the write lock first?
*/
static __inline__ int
rw_tryupgrade(krwlock_t *rwlp)
{
int result;
BUG_ON(rwlp->rw_magic != RW_MAGIC);
spin_lock(&rwlp->rw_sem.wait_lock);
/* Check if there is anyone waiting for the
* lock. If there is, then we know we should
* not try to upgrade the lock */
if (!list_empty(&rwlp->rw_sem.wait_list)) {
printk(KERN_WARNING "There are threads waiting\n");
spin_unlock(&rwlp->rw_sem.wait_lock);
return 0;
}
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
/* Note that activity is protected by
* the wait_lock. Don't try to upgrade
* if there are multiple readers currently
* holding the lock */
if (rwlp->rw_sem.activity > 1) {
#else
/* Don't try to upgrade
* if there are multiple readers currently
* holding the lock */
if ((rwlp->rw_sem.count & RWSEM_ACTIVE_MASK) > 1) {
#endif
spin_unlock(&rwlp->rw_sem.wait_lock);
return 0;
}
/* Here it should be safe to drop the
* read lock and reacquire it for writing since
* we know there are no waiters */
up_read(&rwlp->rw_sem);
/* returns 1 if success, 0 if contention */
result = down_write_trylock(&rwlp->rw_sem);
/* Check if upgrade failed. Should not ever happen
* if we got to this point */
BUG_ON(!result);
BUG_ON(rwlp->rw_owner != NULL);
rwlp->rw_owner = current;
spin_unlock(&rwlp->rw_sem.wait_lock);
return 1;
}
static __inline__ kthread_t *
rw_owner(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
return rwlp->rw_owner;
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_RWLOCK_H */

86
include/linux-taskq.h Normal file
View File

@ -0,0 +1,86 @@
#ifndef _SYS_LINUX_TASKQ_H
#define _SYS_LINUX_TASKQ_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* Task Queues - As of linux 2.6.x task queues have been replaced by a
* similar construct called work queues. The big difference on the linux
* side is that functions called from work queues run in process context
* and not interrupt context.
*
* One nice feature of Solaris which does not exist in linux work
* queues in the notion of a dynamic work queue. Rather than implementing
* this in the shim layer I'm hardcoding one-thread per work queue.
*
* XXX - This may end up being a significant performance penalty which
* forces us to implement dynamic workqueues. Which is all very doable
* with a little effort.
*/
#include <linux/workqueue.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <sys/linux-types.h>
#undef DEBUG_TASKQ_UNIMPLEMENTED
#define TASKQ_NAMELEN 31
#define taskq_t workq_t
typedef struct workqueue_struct workq_t;
typedef unsigned long taskqid_t;
typedef void (*task_func_t)(void *);
/*
* Public flags for taskq_create(): bit range 0-15
*/
#define TASKQ_PREPOPULATE 0x0000 /* XXX - Workqueues fully populate */
#define TASKQ_CPR_SAFE 0x0000 /* XXX - No analog */
#define TASKQ_DYNAMIC 0x0000 /* XXX - Worksqueues not dynamic */
/*
* Flags for taskq_dispatch. TQ_SLEEP/TQ_NOSLEEP should be same as
* KM_SLEEP/KM_NOSLEEP.
*/
#define TQ_SLEEP 0x00 /* XXX - Workqueues don't support */
#define TQ_NOSLEEP 0x00 /* these sorts of flags. They */
#define TQ_NOQUEUE 0x00 /* always run in application */
#define TQ_NOALLOC 0x00 /* context and can sleep. */
#ifdef DEBUG_TASKQ_UNIMPLEMENTED
static __inline__ void taskq_init(void) {
#error "taskq_init() not implemented"
}
static __inline__ taskq_t *
taskq_create_instance(const char *, int, int, pri_t, int, int, uint_t) {
#error "taskq_create_instance() not implemented"
}
extern void nulltask(void *);
extern void taskq_suspend(taskq_t *);
extern int taskq_suspended(taskq_t *);
extern void taskq_resume(taskq_t *);
#endif /* DEBUG_TASKQ_UNIMPLEMENTED */
extern taskqid_t __taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
extern taskq_t *__taskq_create(const char *, int, pri_t, int, int, uint_t);
#define taskq_create(name, thr, pri, min, max, flags) \
__taskq_create(name, thr, pri, min, max, flags)
#define taskq_dispatch(tq, func, priv, flags) \
__taskq_dispatch(tq, func, priv, flags)
#define taskq_destory(tq) destroy_workqueue(tq)
#define taskq_wait(tq) flush_workqueue(tq)
#define taskq_member(tq, kthr) 1 /* XXX -Just be true */
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_TASKQ_H */

48
include/linux-thread.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef _SYS_LINUX_THREAD_H
#define _SYS_LINUX_THREAD_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <sys/linux-types.h>
#include <sys/linux-generic.h>
/*
* Thread interfaces
*/
#define TP_MAGIC 0x53535353
#define TS_SLEEP TASK_INTERRUPTIBLE
#define TS_RUN TASK_RUNNING
#define TS_ZOMB EXIT_ZOMBIE
#define TS_STOPPED TASK_STOPPED
#if 0
#define TS_FREE 0x00 /* No clean linux mapping */
#define TS_ONPROC 0x04 /* No clean linux mapping */
#define TS_WAIT 0x20 /* No clean linux mapping */
#endif
#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
__thread_create(stk, stksize, func, arg, len, pp, state, pri)
#define thread_exit() __thread_exit()
#define curthread get_current()
/* We just need a valid type to pass around, it's unused */
typedef struct proc_s {
int foo;
} proc_t;
kthread_t * __thread_create(caddr_t stk, size_t stksize,
void (*proc)(void *), void *args,
size_t len, proc_t *pp, int state,
pri_t pri);
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_THREAD_H */

64
include/linux-time.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef _SYS_TIME_H
#define _SYS_TIME_H
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.16 */
/*
* Structure returned by gettimeofday(2) system call,
* and used in other calls.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/time.h>
#include <sys/linux-types.h>
extern unsigned long long monotonic_clock(void);
typedef struct timespec timestruc_t; /* definition per SVr4 */
typedef longlong_t hrtime_t;
#define TIME32_MAX INT32_MAX
#define TIME32_MIN INT32_MIN
#define SEC 1
#define MILLISEC 1000
#define MICROSEC 1000000
#define NANOSEC 1000000000
#define hz \
({ \
BUG_ON(HZ < 100 || HZ > MICROSEC); \
HZ; \
})
#define gethrestime(ts) getnstimeofday((ts))
static __inline__ hrtime_t
gethrtime(void) {
/* BUG_ON(cur_timer == timer_none); */
/* Solaris expects a long long here but monotonic_clock() returns an
* unsigned long long. Note that monotonic_clock() returns the number
* of nanoseconds passed since kernel initialization. Even for a signed
* long long this will not "go negative" for ~292 years.
*/
return monotonic_clock();
}
static __inline__ time_t
gethrestime_sec(void)
{
timestruc_t now;
gethrestime(&now);
return (now.tv_sec);
}
#ifdef __cplusplus
}
#endif
#endif /* _SYS_TIME_H */

21
include/linux-timer.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _SYS_LINUX_TIMER_H
#define _SYS_LINUX_TIMER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <linux/sched.h>
#include <linux/timer.h>
#define lbolt ((clock_t)jiffies)
#define lbolt64 ((int64_t)get_jiffies_64())
#define delay(ticks) schedule_timeout((long timeout)(ticks))
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_TIMER_H */

27
include/linux-types.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef _SYS_LINUX_TYPES_H
#define _SYS_LINUX_TYPES_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum { B_FALSE=0, B_TRUE=1 } boolean_t;
typedef unsigned long uintptr_t;
typedef unsigned long intptr_t;
typedef unsigned long ulong_t;
typedef unsigned int uint_t;
typedef unsigned char uchar_t;
typedef unsigned long long u_longlong_t;
typedef unsigned long long u_offset_t;
typedef unsigned long long rlim64_t;
typedef long long longlong_t;
typedef long long offset_t;
typedef struct task_struct kthread_t;
typedef struct vmem { } vmem_t;
typedef short pri_t;
#ifdef __cplusplus
}
#endif
#endif /* _SYS_LINUX_TYPES_H */

4
include/spl.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef _SYS_SPL_H
#define _SYS_SPL_H
#endif /* _SYS_SPL_H */

211
include/splat-ctl.h Normal file
View File

@ -0,0 +1,211 @@
#ifndef _SYS_KZT_H
#define _SYS_KZT_H
#ifdef _KERNEL
#include <asm/ioctls.h>
#include <asm/uaccess.h>
#include <linux/list.h>
#endif /* _KERNEL */
#define KZT_VERSION "v1.0"
#define KZT_VERSION_SIZE 64
#define KZT_MAJOR 229 /* XXX - Arbitrary */
#define KZT_MINORS 1
#define KZT_DEV "/dev/kztctl"
#define KZT_NAME_SIZE 12
#define KZT_DESC_SIZE 60
typedef struct kzt_user {
char name[KZT_NAME_SIZE]; /* short name */
char desc[KZT_DESC_SIZE]; /* short description */
int id; /* unique numeric id */
} kzt_user_t;
#define KZT_CFG_MAGIC 0x15263748U
typedef struct kzt_cfg {
unsigned int cfg_magic; /* Unique magic */
int cfg_cmd; /* Config command */
int cfg_arg1; /* Config command arg 1 */
int cfg_rc1; /* Config response 1 */
union {
struct {
int size;
kzt_user_t descs[0];
} kzt_subsystems;
struct {
int size;
kzt_user_t descs[0];
} kzt_tests;
} cfg_data;
} kzt_cfg_t;
#define KZT_CMD_MAGIC 0x9daebfc0U
typedef struct kzt_cmd {
unsigned int cmd_magic; /* Unique magic */
int cmd_subsystem; /* Target subsystem */
int cmd_test; /* Subsystem test */
int cmd_data_size; /* Extra opaque data */
char cmd_data_str[0]; /* Opaque data region */
} kzt_cmd_t;
/* Valid ioctls */
#define KZT_CFG _IOWR('f', 101, long)
#define KZT_CMD _IOWR('f', 102, long)
/* Valid configuration commands */
#define KZT_CFG_BUFFER_CLEAR 0x001 /* Clear text buffer */
#define KZT_CFG_BUFFER_SIZE 0x002 /* Resize text buffer */
#define KZT_CFG_SUBSYSTEM_COUNT 0x101 /* Number of subsystem */
#define KZT_CFG_SUBSYSTEM_LIST 0x102 /* List of N subsystems */
#define KZT_CFG_TEST_COUNT 0x201 /* Number of tests */
#define KZT_CFG_TEST_LIST 0x202 /* List of N tests */
/* Valid subsystem and test commands defined in each subsystem, we do
* need to be careful to avoid colisions. That alone may argue to define
* them all here, for now we just define the global error codes.
*/
#define KZT_SUBSYSTEM_UNKNOWN 0xF00
#define KZT_TEST_UNKNOWN 0xFFF
#ifdef _KERNEL
#define KZT_SUBSYSTEM_INIT(type) \
({ kzt_subsystem_t *_sub_; \
\
_sub_ = (kzt_subsystem_t *)kzt_##type##_init(); \
if (_sub_ == NULL) { \
printk(KERN_ERR "Error initializing: " #type "\n"); \
} else { \
spin_lock(&kzt_module_lock); \
list_add_tail(&(_sub_->subsystem_list), \
&kzt_module_list); \
spin_unlock(&kzt_module_lock); \
} \
})
#define KZT_SUBSYSTEM_FINI(type) \
({ kzt_subsystem_t *_sub_, *_tmp_; \
int _id_, _flag_ = 0; \
\
_id_ = kzt_##type##_id(); \
spin_lock(&kzt_module_lock); \
list_for_each_entry_safe(_sub_, _tmp_, &kzt_module_list, \
subsystem_list) { \
if (_sub_->desc.id == _id_) { \
list_del_init(&(_sub_->subsystem_list)); \
spin_unlock(&kzt_module_lock); \
kzt_##type##_fini(_sub_); \
spin_lock(&kzt_module_lock); \
_flag_ = 1; \
} \
} \
spin_unlock(&kzt_module_lock); \
\
if (!_flag_) \
printk(KERN_ERR "Error finalizing: " #type "\n"); \
})
#define KZT_TEST_INIT(sub, n, d, tid, func) \
({ kzt_test_t *_test_; \
\
_test_ = (kzt_test_t *)kmalloc(sizeof(*_test_), GFP_KERNEL); \
if (_test_ == NULL) { \
printk(KERN_ERR "Error initializing: " n "/" #tid" \n");\
} else { \
memset(_test_, 0, sizeof(*_test_)); \
strncpy(_test_->desc.name, n, KZT_NAME_SIZE); \
strncpy(_test_->desc.desc, d, KZT_DESC_SIZE); \
_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 KZT_TEST_FINI(sub, tid) \
({ kzt_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 "Error finalizing: " #tid "\n"); \
})
typedef int (*kzt_test_func_t)(struct file *, void *);
typedef struct kzt_test {
struct list_head test_list;
kzt_user_t desc;
kzt_test_func_t test;
} kzt_test_t;
typedef struct kzt_subsystem {
struct list_head subsystem_list;/* List had to chain entries */
kzt_user_t desc;
spinlock_t test_lock;
struct list_head test_list;
} kzt_subsystem_t;
#define KZT_INFO_BUFFER_SIZE 65536
#define KZT_INFO_BUFFER_REDZONE 256
typedef struct kzt_info {
spinlock_t info_lock;
int info_size;
char *info_buffer;
char *info_head; /* Internal kernel use only */
} kzt_info_t;
#define sym2str(sym) (char *)(#sym)
#define kzt_print(file, format, args...) \
({ kzt_info_t *_info_ = (kzt_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) > \
(KZT_INFO_BUFFER_SIZE -KZT_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 kzt_vprint(file, test, format, args...) \
kzt_print(file, "%*s: " format, KZT_NAME_SIZE, test, args)
kzt_subsystem_t * kzt_condvar_init(void);
kzt_subsystem_t * kzt_kmem_init(void);
kzt_subsystem_t * kzt_mutex_init(void);
kzt_subsystem_t * kzt_krng_init(void);
kzt_subsystem_t * kzt_rwlock_init(void);
kzt_subsystem_t * kzt_taskq_init(void);
kzt_subsystem_t * kzt_thread_init(void);
kzt_subsystem_t * kzt_time_init(void);
#endif /* _KERNEL */
#endif /* _SYS_KZT_H */

47
include/splat.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef _KZT_H
#define _KZT_H
#include <sys/splat-ctl.h>
#define DEV_NAME "/dev/kztctl"
#define COLOR_BLACK "\033[0;30m"
#define COLOR_DK_GRAY "\033[1;30m"
#define COLOR_BLUE "\033[0;34m"
#define COLOR_LT_BLUE "\033[1;34m"
#define COLOR_GREEN "\033[0;32m"
#define COLOR_LT_GREEN "\033[1;32m"
#define COLOR_CYAN "\033[0;36m"
#define COLOR_LT_CYAN "\033[1;36m"
#define COLOR_RED "\033[0;31m"
#define COLOR_LT_RED "\033[1;31m"
#define COLOR_PURPLE "\033[0;35m"
#define COLOR_LT_PURPLE "\033[1;35m"
#define COLOR_BROWN "\033[0;33m"
#define COLOR_YELLOW "\033[1;33m"
#define COLOR_LT_GRAY "\033[0;37m"
#define COLOR_WHITE "\033[1;37m"
#define COLOR_RESET "\033[0m"
typedef struct subsystem {
uu_list_node_t sub_node; /* Linkage for global subsystem list */
kzt_user_t sub_desc; /* Subsystem description */
uu_list_t *sub_tests; /* Assocated subsystem tests list */
} subsystem_t;
typedef struct test {
uu_list_node_t test_node; /* Linkage for globals test list */
kzt_user_t test_desc; /* Test description */
subsystem_t *test_sub; /* Parent subsystem */
} test_t;
typedef struct cmd_args {
int args_verbose; /* Verbose flag */
int args_do_list; /* Display all tests flag */
int args_do_all; /* Run all tests flag */
int args_do_color; /* Colorize output */
int args_exit_on_error; /* Exit on first error flag */
uu_list_t *args_tests; /* Requested subsystems/tests */
} cmd_args_t;
#endif /* _KZT_H */

1
scripts/Makefile.am Normal file
View File

@ -0,0 +1 @@
EXTRA_DIST = spl.spec.in

28
scripts/spl.spec Normal file
View File

@ -0,0 +1,28 @@
# spl
%define name spl
%define version 0.0.1
Summary: Solaris Porting Layer
Name: %{name}
Version: %{version}
Release: 1
Copyright: GPL
Group: Utilities/System
BuildRoot: /tmp/%{name}-%{version}
Source: %{name}-%{version}.tar.gz
%description
Abstration layer to provide Solaris style primatives in the linux kernel.
%prep
%setup -q
./configure
%build
rm -rf $RPM_BUILD_ROOT
make
%install
make install "DESTDIR=$RPM_BUILD_ROOT"
%files

28
scripts/spl.spec.in Normal file
View File

@ -0,0 +1,28 @@
# spl
%define name @PACKAGE@
%define version @VERSION@
Summary: Solaris Porting Layer
Name: %{name}
Version: %{version}
Release: 1
Copyright: GPL
Group: Utilities/System
BuildRoot: /tmp/%{name}-%{version}
Source: %{name}-%{version}.tar.gz
%description
Abstration layer to provide Solaris style primatives in the linux kernel.
%prep
%setup -q
./configure
%build
rm -rf $RPM_BUILD_ROOT
make
%install
make install "DESTDIR=$RPM_BUILD_ROOT"
%files

1
src/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = cmd spl splat

3
src/cmd/Makefile.am Normal file
View File

@ -0,0 +1,3 @@
AM_CFLAGS = @EXTRA_CFLAGS@ -g -O2 -W -Wall -Wstrict-prototypes -Wshadow
sbin_PROGRAMS = splat
kzt_SOURCES = splat.c

821
src/cmd/splat.c Normal file
View File

@ -0,0 +1,821 @@
/* Kernel ZFS Test (KZT) user space command interface */
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <assert.h>
#include <fcntl.h>
#include <libuutil.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "splat.h"
#undef ioctl
static const char shortOpts[] = "hvlat:xc";
static const struct option longOpts[] = {
{ "help", no_argument, 0, 'h' },
{ "verbose", no_argument, 0, 'v' },
{ "list", no_argument, 0, 'l' },
{ "all", no_argument, 0, 'a' },
{ "test", required_argument, 0, 't' },
{ "exit", no_argument, 0, 'x' },
{ "nocolor", no_argument, 0, 'c' },
{ 0, 0, 0, 0 }
};
static uu_list_t *subsystems; /* Subsystem/tests */
static uu_list_pool_t *subsystem_pool; /* Subsystem pool */
static uu_list_pool_t *test_pool; /* Test pool */
static int kztctl_fd; /* Control file descriptor */
static char kzt_version[KZT_VERSION_SIZE]; /* Kernel version string */
static char *kzt_buffer = NULL; /* Scratch space area */
static int kzt_buffer_size = 0; /* Scratch space size */
static void test_list(uu_list_t *, int);
static int dev_clear(void);
static int usage(void) {
fprintf(stderr, "usage: kzt [hvla] [-t <subsystem:<tests>>]\n");
fprintf(stderr,
" --help -h This help\n"
" --verbose -v Increase verbosity\n"
" --list -l List all tests in all subsystems\n"
" --all -a Run all tests in all subsystems\n"
" --test -t <sub:test> Run 'test' in subsystem 'sub'\n"
" --exit -x Exit on first test error\n"
" --nocolor -c Do not colorize output\n");
fprintf(stderr, "\n"
"Examples:\n"
" kzt -t kmem:all # Runs all kmem tests\n"
" kzt -t taskq:0x201 # Run taskq test 0x201\n");
return 0;
}
static subsystem_t *subsystem_init(kzt_user_t *desc)
{
subsystem_t *sub;
sub = (subsystem_t *)malloc(sizeof(*sub));
if (sub == NULL)
return NULL;
memcpy(&sub->sub_desc, desc, sizeof(*desc));
uu_list_node_init(sub, &sub->sub_node, subsystem_pool);
sub->sub_tests = uu_list_create(test_pool, NULL, 0);
if (sub->sub_tests == NULL) {
free(sub);
return NULL;
}
return sub;
}
static void subsystem_fini(subsystem_t *sub)
{
assert(sub != NULL);
uu_list_node_fini(sub, &sub->sub_node, subsystem_pool);
free(sub);
}
static int subsystem_setup(void)
{
kzt_cfg_t *cfg;
int i, rc, size, cfg_size;
subsystem_t *sub;
kzt_user_t *desc;
/* Aquire the number of registered subsystems */
cfg_size = sizeof(*cfg);
cfg = (kzt_cfg_t *)malloc(cfg_size);
if (cfg == NULL)
return -ENOMEM;
memset(cfg, 0, cfg_size);
cfg->cfg_magic = KZT_CFG_MAGIC;
cfg->cfg_cmd = KZT_CFG_SUBSYSTEM_COUNT;
rc = ioctl(kztctl_fd, KZT_CFG, cfg);
if (rc) {
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg->cfg_cmd, errno);
free(cfg);
return rc;
}
size = cfg->cfg_rc1;
free(cfg);
/* Based on the newly aquired number of subsystems allocate enough
* memory to get the descriptive information for them all. */
cfg_size = sizeof(*cfg) + size * sizeof(kzt_user_t);
cfg = (kzt_cfg_t *)malloc(cfg_size);
if (cfg == NULL)
return -ENOMEM;
memset(cfg, 0, cfg_size);
cfg->cfg_magic = KZT_CFG_MAGIC;
cfg->cfg_cmd = KZT_CFG_SUBSYSTEM_LIST;
cfg->cfg_data.kzt_subsystems.size = size;
rc = ioctl(kztctl_fd, KZT_CFG, cfg);
if (rc) {
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg->cfg_cmd, errno);
free(cfg);
return rc;
}
/* Add the new subsystems in to the global list */
size = cfg->cfg_rc1;
for (i = 0; i < size; i++) {
desc = &(cfg->cfg_data.kzt_subsystems.descs[i]);
sub = subsystem_init(desc);
if (sub == NULL) {
fprintf(stderr, "Error initializing subsystem: %s\n",
desc->name);
free(cfg);
return -ENOMEM;
}
uu_list_insert(subsystems, sub, 0);
}
free(cfg);
return 0;
}
static int subsystem_compare(const void *l_arg, const void *r_arg, void *private)
{
const subsystem_t *l = l_arg;
const subsystem_t *r = r_arg;
if (l->sub_desc.id > r->sub_desc.id)
return 1;
if (l->sub_desc.id < r->sub_desc.id)
return -1;
return 0;
}
static void subsystem_list(uu_list_t *list, int indent)
{
subsystem_t *sub;
fprintf(stdout,
"------------------------------- "
"Available KZT Tests "
"-------------------------------\n");
for (sub = uu_list_first(list); sub != NULL;
sub = uu_list_next(list, sub)) {
fprintf(stdout, "%*s0x%0*x %-*s ---- %s ----\n",
indent, "",
4, sub->sub_desc.id,
KZT_NAME_SIZE + 7, sub->sub_desc.name,
sub->sub_desc.desc);
test_list(sub->sub_tests, indent + 7);
}
}
static test_t *test_init(subsystem_t *sub, kzt_user_t *desc)
{
test_t *test;
test = (test_t *)malloc(sizeof(*test));
if (test == NULL)
return NULL;
test->test_sub = sub;
memcpy(&test->test_desc, desc, sizeof(*desc));
uu_list_node_init(test, &test->test_node, test_pool);
return test;
}
static void test_fini(test_t *test)
{
assert(test != NULL);
uu_list_node_fini(test, &test->test_node, test_pool);
free(test);
}
static int test_setup(subsystem_t *sub)
{
kzt_cfg_t *cfg;
int i, rc, size;
test_t *test;
kzt_user_t *desc;
/* Aquire the number of registered tests for the give subsystem */
cfg = (kzt_cfg_t *)malloc(sizeof(*cfg));
if (cfg == NULL)
return -ENOMEM;
memset(cfg, 0, sizeof(*cfg));
cfg->cfg_magic = KZT_CFG_MAGIC;
cfg->cfg_cmd = KZT_CFG_TEST_COUNT;
cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */
rc = ioctl(kztctl_fd, KZT_CFG, cfg);
if (rc) {
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg->cfg_cmd, errno);
free(cfg);
return rc;
}
size = cfg->cfg_rc1;
free(cfg);
/* Based on the newly aquired number of tests allocate enough
* memory to get the descriptive information for them all. */
cfg = (kzt_cfg_t *)malloc(sizeof(*cfg) + size * sizeof(kzt_user_t));
if (cfg == NULL)
return -ENOMEM;
memset(cfg, 0, sizeof(*cfg) + size * sizeof(kzt_user_t));
cfg->cfg_magic = KZT_CFG_MAGIC;
cfg->cfg_cmd = KZT_CFG_TEST_LIST;
cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */
cfg->cfg_data.kzt_tests.size = size;
rc = ioctl(kztctl_fd, KZT_CFG, cfg);
if (rc) {
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg->cfg_cmd, errno);
free(cfg);
return rc;
}
/* Add the new tests in to the relevant subsystems */
size = cfg->cfg_rc1;
for (i = 0; i < size; i++) {
desc = &(cfg->cfg_data.kzt_tests.descs[i]);
test = test_init(sub, desc);
if (test == NULL) {
fprintf(stderr, "Error initializing test: %s\n",
desc->name);
free(cfg);
return -ENOMEM;
}
uu_list_insert(sub->sub_tests, test, 0);
}
free(cfg);
return 0;
}
static int test_compare(const void *l_arg, const void *r_arg, void *private)
{
const test_t *l = l_arg;
const test_t *r = r_arg;
if (l->test_desc.id > r->test_desc.id)
return 1;
if (l->test_desc.id < r->test_desc.id)
return -1;
return 0;
}
static test_t *test_copy(test_t *test)
{
return test_init(test->test_sub, &test->test_desc);
}
static void test_list(uu_list_t *list, int indent)
{
test_t *test;
for (test = uu_list_first(list); test != NULL;
test = uu_list_next(list, test))
fprintf(stdout, "%*s0x%0*x %-*s %-*s\n",
indent, "",
04, test->test_desc.id,
KZT_NAME_SIZE, test->test_desc.name,
KZT_DESC_SIZE, test->test_desc.desc);
}
static test_t *test_find(char *sub_str, char *test_str)
{
subsystem_t *sub;
test_t *test;
int sub_num, test_num;
/* No error checking here because it may not be a number, it's
* perfectly OK for it to be a string. Since we're just using
* it for comparison purposes this is all very safe.
*/
sub_num = strtol(sub_str, NULL, 0);
test_num = strtol(test_str, NULL, 0);
for (sub = uu_list_first(subsystems); sub != NULL;
sub = uu_list_next(subsystems, sub)) {
if (strncmp(sub->sub_desc.name, sub_str, KZT_NAME_SIZE) &&
sub->sub_desc.id != sub_num)
continue;
for (test = uu_list_first(sub->sub_tests); test != NULL;
test = uu_list_next(sub->sub_tests, test)) {
if (!strncmp(test->test_desc.name, test_str,
KZT_NAME_SIZE) || test->test_desc.id == test_num)
return test;
}
}
return NULL;
}
static int test_add(cmd_args_t *args, test_t *test)
{
test_t *tmp;
tmp = test_copy(test);
if (tmp == NULL)
return -ENOMEM;
uu_list_insert(args->args_tests, tmp, 0);
return 0;
}
static int test_add_all(cmd_args_t *args)
{
subsystem_t *sub;
test_t *test;
int rc;
for (sub = uu_list_first(subsystems); sub != NULL;
sub = uu_list_next(subsystems, sub)) {
for (test = uu_list_first(sub->sub_tests); test != NULL;
test = uu_list_next(sub->sub_tests, test)) {
if (rc = test_add(args, test))
return rc;
}
}
return 0;
}
static int test_run(cmd_args_t *args, test_t *test)
{
subsystem_t *sub = test->test_sub;
kzt_cmd_t *cmd;
int rc, cmd_size;
dev_clear();
cmd_size = sizeof(*cmd);
cmd = (kzt_cmd_t *)malloc(cmd_size);
if (cmd == NULL)
return -ENOMEM;
memset(cmd, 0, cmd_size);
cmd->cmd_magic = KZT_CMD_MAGIC;
cmd->cmd_subsystem = sub->sub_desc.id;
cmd->cmd_test = test->test_desc.id;
cmd->cmd_data_size = 0; /* Unused feature */
fprintf(stdout, "%*s:%-*s ",
KZT_NAME_SIZE, sub->sub_desc.name,
KZT_NAME_SIZE, test->test_desc.name);
fflush(stdout);
rc = ioctl(kztctl_fd, KZT_CMD, cmd);
if (args->args_do_color) {
fprintf(stdout, "%s %s\n", rc ?
COLOR_RED "Fail" COLOR_RESET :
COLOR_GREEN "Pass" COLOR_RESET,
rc ? strerror(errno) : "");
} else {
fprintf(stdout, "%s %s\n", rc ?
"Fail" : "Pass",
rc ? strerror(errno) : "");
}
fflush(stdout);
free(cmd);
if (args->args_verbose) {
if ((rc = read(kztctl_fd, kzt_buffer, kzt_buffer_size - 1)) < 0) {
fprintf(stdout, "Error reading results: %d\n", rc);
} else {
fprintf(stdout, "\n%s\n", kzt_buffer);
fflush(stdout);
}
}
return rc;
}
static int tests_run(cmd_args_t *args)
{
test_t *test;
int rc;
fprintf(stdout,
"------------------------------- "
"Running KZT Tests "
"-------------------------------\n");
for (test = uu_list_first(args->args_tests); test != NULL;
test = uu_list_next(args->args_tests, test)) {
rc = test_run(args, test);
if (rc && args->args_exit_on_error)
return rc;
}
return 0;
}
static int args_parse_test(cmd_args_t *args, char *str)
{
subsystem_t *s;
test_t *t;
char *sub_str, *test_str;
int sub_num, test_num;
int sub_all = 0, test_all = 0;
int rc, flag = 0;
test_str = strchr(str, ':');
if (test_str == NULL) {
fprintf(stderr, "Test must be of the "
"form <subsystem:test>\n");
return -EINVAL;
}
sub_str = str;
test_str[0] = '\0';
test_str = test_str + 1;
sub_num = strtol(sub_str, NULL, 0);
test_num = strtol(test_str, NULL, 0);
if (!strncasecmp(sub_str, "all", strlen(sub_str)) || (sub_num == -1))
sub_all = 1;
if (!strncasecmp(test_str, "all", strlen(test_str)) || (test_num == -1))
test_all = 1;
if (sub_all) {
if (test_all) {
/* Add all tests from all subsystems */
for (s = uu_list_first(subsystems); s != NULL;
s = uu_list_next(subsystems, s))
for (t = uu_list_first(s->sub_tests);t != NULL;
t = uu_list_next(s->sub_tests, t))
if (rc = test_add(args, t))
goto error_run;
} else {
/* Add a specific test from all subsystems */
for (s = uu_list_first(subsystems); s != NULL;
s = uu_list_next(subsystems, s)) {
if (t = test_find(s->sub_desc.name,test_str)) {
if (rc = test_add(args, t))
goto error_run;
flag = 1;
}
}
if (!flag)
fprintf(stderr, "No tests '%s:%s' could be "
"found\n", sub_str, test_str);
}
} else {
if (test_all) {
/* Add all tests from a specific subsystem */
for (s = uu_list_first(subsystems); s != NULL;
s = uu_list_next(subsystems, s)) {
if (strncasecmp(sub_str, s->sub_desc.name,
strlen(sub_str)))
continue;
for (t = uu_list_first(s->sub_tests);t != NULL;
t = uu_list_next(s->sub_tests, t))
if (rc = test_add(args, t))
goto error_run;
}
} else {
/* Add a specific test from a specific subsystem */
if (t = test_find(sub_str, test_str)) {
if (rc = test_add(args, t))
goto error_run;
} else {
fprintf(stderr, "Test '%s:%s' could not be "
"found\n", sub_str, test_str);
return -EINVAL;
}
}
}
return 0;
error_run:
fprintf(stderr, "Test '%s:%s' not added to run list: %d\n",
sub_str, test_str, rc);
return rc;
}
static void args_fini(cmd_args_t *args)
{
struct cmd_test *ptr1, *ptr2;
assert(args != NULL);
if (args->args_tests != NULL) {
uu_list_destroy(args->args_tests);
}
free(args);
}
static cmd_args_t *
args_init(int argc, char **argv)
{
cmd_args_t *args;
int c, rc;
if (argc == 1) {
usage();
return (cmd_args_t *) NULL;
}
/* Configure and populate the args structures */
args = malloc(sizeof(*args));
if (args == NULL)
return NULL;
memset(args, 0, sizeof(*args));
args->args_verbose = 0;
args->args_do_list = 0;
args->args_do_all = 0;
args->args_do_color = 1;
args->args_exit_on_error = 0;
args->args_tests = uu_list_create(test_pool, NULL, 0);
if (args->args_tests == NULL) {
args_fini(args);
return NULL;
}
while ((c = getopt_long(argc, argv, shortOpts, longOpts, NULL)) != -1){
switch (c) {
case 'v': args->args_verbose++; break;
case 'l': args->args_do_list = 1; break;
case 'a': args->args_do_all = 1; break;
case 'c': args->args_do_color = 0; break;
case 'x': args->args_exit_on_error = 1; break;
case 't':
if (args->args_do_all) {
fprintf(stderr, "Option -t <subsystem:test> is "
"useless when used with -a\n");
args_fini(args);
return NULL;
}
rc = args_parse_test(args, argv[optind - 1]);
if (rc) {
args_fini(args);
return NULL;
}
break;
case 'h':
case '?':
usage();
args_fini(args);
return NULL;
default:
fprintf(stderr, "Unknown option '%s'\n",
argv[optind - 1]);
break;
}
}
return args;
}
static int
dev_clear(void)
{
kzt_cfg_t cfg;
int rc;
memset(&cfg, 0, sizeof(cfg));
cfg.cfg_magic = KZT_CFG_MAGIC;
cfg.cfg_cmd = KZT_CFG_BUFFER_CLEAR;
cfg.cfg_arg1 = 0;
rc = ioctl(kztctl_fd, KZT_CFG, &cfg);
if (rc)
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg.cfg_cmd, errno);
lseek(kztctl_fd, 0, SEEK_SET);
return rc;
}
static int
dev_size(int size)
{
kzt_cfg_t cfg;
int rc;
memset(&cfg, 0, sizeof(cfg));
cfg.cfg_magic = KZT_CFG_MAGIC;
cfg.cfg_cmd = KZT_CFG_BUFFER_SIZE;
cfg.cfg_arg1 = size;
rc = ioctl(kztctl_fd, KZT_CFG, &cfg);
if (rc) {
fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
(unsigned long) KZT_CFG, cfg.cfg_cmd, errno);
return rc;
}
return cfg.cfg_rc1;
}
static void
dev_fini(void)
{
if (kzt_buffer)
free(kzt_buffer);
if (kztctl_fd != -1) {
if (close(kztctl_fd) == -1) {
fprintf(stderr, "Unable to close %s: %d\n",
KZT_DEV, errno);
}
}
}
static int
dev_init(void)
{
subsystem_t *sub;
int rc;
kztctl_fd = open(KZT_DEV, O_RDONLY);
if (kztctl_fd == -1) {
fprintf(stderr, "Unable to open %s: %d\n"
"Is the kzt module loaded?\n", KZT_DEV, errno);
rc = errno;
goto error;
}
/* Determine kernel module version string */
memset(kzt_version, 0, KZT_VERSION_SIZE);
if ((rc = read(kztctl_fd, kzt_version, KZT_VERSION_SIZE - 1)) == -1)
goto error;
if (rc = dev_clear())
goto error;
if ((rc = dev_size(0)) < 0)
goto error;
kzt_buffer_size = rc;
kzt_buffer = (char *)malloc(kzt_buffer_size);
if (kzt_buffer == NULL) {
rc = -ENOMEM;
goto error;
}
memset(kzt_buffer, 0, kzt_buffer_size);
/* Determine available subsystems */
if ((rc = subsystem_setup()) != 0)
goto error;
/* Determine available tests for all subsystems */
for (sub = uu_list_first(subsystems); sub != NULL;
sub = uu_list_next(subsystems, sub))
if ((rc = test_setup(sub)) != 0)
goto error;
return 0;
error:
if (kztctl_fd != -1) {
if (close(kztctl_fd) == -1) {
fprintf(stderr, "Unable to close %s: %d\n",
KZT_DEV, errno);
}
}
return rc;
}
int
init(void)
{
int rc;
/* Configure the subsystem pool */
subsystem_pool = uu_list_pool_create("sub_pool", sizeof(subsystem_t),
offsetof(subsystem_t, sub_node),
subsystem_compare, 0);
if (subsystem_pool == NULL)
return -ENOMEM;
/* Configure the test pool */
test_pool = uu_list_pool_create("test_pool", sizeof(test_t),
offsetof(test_t, test_node),
test_compare, 0);
if (test_pool == NULL) {
uu_list_pool_destroy(subsystem_pool);
return -ENOMEM;
}
/* Allocate the subsystem list */
subsystems = uu_list_create(subsystem_pool, NULL, 0);
if (subsystems == NULL) {
uu_list_pool_destroy(test_pool);
uu_list_pool_destroy(subsystem_pool);
return -ENOMEM;
}
return 0;
}
void
fini(void)
{
/* XXX - Cleanup destroy lists release memory */
/* XXX - Remove contents of list first */
uu_list_destroy(subsystems);
}
int
main(int argc, char **argv)
{
cmd_args_t *args = NULL;
int rc = 0;
/* General init */
if (rc = init())
return rc;
/* Device specific init */
if (rc = dev_init())
goto out;
/* Argument init and parsing */
if ((args = args_init(argc, argv)) == NULL) {
rc = -1;
goto out;
}
/* Generic kernel version string */
if (args->args_verbose)
fprintf(stdout, "%s", kzt_version);
/* Print the available test list and exit */
if (args->args_do_list) {
subsystem_list(subsystems, 0);
goto out;
}
/* Add all available test to the list of tests to run */
if (args->args_do_all) {
if (rc = test_add_all(args))
goto out;
}
/* Run all the requested tests */
if (rc = tests_run(args))
goto out;
out:
if (args != NULL)
args_fini(args);
dev_fini();
fini();
return rc;
}

50
src/spl/Makefile.in Normal file
View File

@ -0,0 +1,50 @@
# Makefile.in for spl kernel module
MODULES := spl
DISTFILES = Makefile.in \
linux-kmem.c linux-rwlock.c linux-taskq.c linux-thread.c
# Removed '-std=gnu99' does to compile issues with i386 SPIN_LOCK_UNLOCKED
# EXTRA_CFLAGS += -I$(src)
# EXTRA_CFLAGS += -Wall -Wno-unknown-pragmas -Wno-missing-braces \
# -Wno-sign-compare -Wno-parentheses -Wno-uninitialized \
# -Wno-implicit-function-declaration -Wno-unused -Wno-trigraphs \
# -Wno-char-subscripts -Wno-switch
# Solaris porting layer module
obj-m := spl.o
spl-objs += linux-kmem.o
spl-objs += linux-thread.o
spl-objs += linux-taskq.o
spl-objs += linux-rwlock.o
splmodule := spl.ko
splmoduledir := @kmoduledir@/kernel/lib/
all: all-spec
install: all
mkdir -p $(DESTDIR)$(splmoduledir)
$(INSTALL) -m 644 $(splmodule) $(DESTDIR)$(splmoduledir)/$(splmodule)
-/sbin/depmod -a
uninstall:
rm -f $(DESTDIR)$(splmoduledir)/$(splmodule)
-/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)
all-spec:
$(MAKE) -C @kernelsrc@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ modules

249
src/spl/linux-kmem.c Normal file
View File

@ -0,0 +1,249 @@
#include <sys/linux-kmem.h>
/*
* Memory allocation interfaces
*/
#ifdef DEBUG_KMEM
/* Shim layer memory accounting */
atomic_t kmem_alloc_used;
unsigned int kmem_alloc_max;
#endif
/*
* Slab allocation interfaces
*
* While the linux slab implementation was inspired by solaris they
* have made some changes to the API which complicates this shim
* layer. For one thing the same symbol names are used with different
* arguments for the prototypes. To deal with this we must use the
* preprocessor to re-order arguments. Happily for us standard C says,
* "Macro's appearing in their own expansion are not reexpanded" so
* this does not result in an infinite recursion. Additionally the
* function pointers registered by solarias differ from those used
* by linux so a lookup and mapping from linux style callback to a
* solaris style callback is needed. There is some overhead in this
* operation which isn't horibile but it needs to be kept in mind.
*/
typedef struct kmem_cache_cb {
struct list_head kcc_list;
kmem_cache_t * kcc_cache;
kmem_constructor_t kcc_constructor;
kmem_destructor_t kcc_destructor;
kmem_reclaim_t kcc_reclaim;
void * kcc_private;
void * kcc_vmp;
} kmem_cache_cb_t;
static spinlock_t kmem_cache_cb_lock = SPIN_LOCK_UNLOCKED;
//static spinlock_t kmem_cache_cb_lock = (spinlock_t) { 1 SPINLOCK_MAGIC_INIT };
static LIST_HEAD(kmem_cache_cb_list);
static struct shrinker *kmem_cache_shrinker;
/* Function must be called while holding the kmem_cache_cb_lock
* Because kmem_cache_t is an opaque datatype we're forced to
* match pointers to identify specific cache entires.
*/
static kmem_cache_cb_t *
kmem_cache_find_cache_cb(kmem_cache_t *cache)
{
kmem_cache_cb_t *kcc;
list_for_each_entry(kcc, &kmem_cache_cb_list, kcc_list)
if (cache == kcc->kcc_cache)
return kcc;
return NULL;
}
static kmem_cache_cb_t *
kmem_cache_add_cache_cb(kmem_cache_t *cache,
kmem_constructor_t constructor,
kmem_destructor_t destructor,
kmem_reclaim_t reclaim,
void *priv, void *vmp)
{
kmem_cache_cb_t *kcc;
kcc = (kmem_cache_cb_t *)kmalloc(sizeof(*kcc), GFP_KERNEL);
if (kcc) {
kcc->kcc_cache = cache;
kcc->kcc_constructor = constructor;
kcc->kcc_destructor = destructor;
kcc->kcc_reclaim = reclaim;
kcc->kcc_private = priv;
kcc->kcc_vmp = vmp;
spin_lock(&kmem_cache_cb_lock);
list_add(&kcc->kcc_list, &kmem_cache_cb_list);
spin_unlock(&kmem_cache_cb_lock);
}
return kcc;
}
static void
kmem_cache_remove_cache_cb(kmem_cache_cb_t *kcc)
{
spin_lock(&kmem_cache_cb_lock);
list_del(&kcc->kcc_list);
spin_unlock(&kmem_cache_cb_lock);
if (kcc)
kfree(kcc);
}
static void
kmem_cache_generic_constructor(void *ptr, kmem_cache_t *cache, unsigned long flags)
{
kmem_cache_cb_t *kcc;
spin_lock(&kmem_cache_cb_lock);
/* Callback list must be in sync with linux slab caches */
kcc = kmem_cache_find_cache_cb(cache);
BUG_ON(!kcc);
kcc->kcc_constructor(ptr, kcc->kcc_private, (int)flags);
spin_unlock(&kmem_cache_cb_lock);
/* Linux constructor has no return code, silently eat it */
}
static void
kmem_cache_generic_destructor(void *ptr, kmem_cache_t *cache, unsigned long flags)
{
kmem_cache_cb_t *kcc;
spin_lock(&kmem_cache_cb_lock);
/* Callback list must be in sync with linux slab caches */
kcc = kmem_cache_find_cache_cb(cache);
BUG_ON(!kcc);
/* Solaris destructor takes no flags, silently eat them */
kcc->kcc_destructor(ptr, kcc->kcc_private);
spin_unlock(&kmem_cache_cb_lock);
}
/* XXX - Arguments are ignored */
static int
kmem_cache_generic_shrinker(int nr_to_scan, unsigned int gfp_mask)
{
kmem_cache_cb_t *kcc;
int total = 0;
/* Under linux a shrinker is not tightly coupled with a slab
* cache. In fact linux always systematically trys calling all
* registered shrinker callbacks until its target reclamation level
* is reached. Because of this we only register one shrinker
* function in the shim layer for all slab caches. And we always
* attempt to shrink all caches when this generic shrinker is called.
*/
spin_lock(&kmem_cache_cb_lock);
list_for_each_entry(kcc, &kmem_cache_cb_list, kcc_list) {
/* Under linux the desired number and gfp type of objects
* is passed to the reclaiming function as a sugested reclaim
* target. I do not pass these args on because reclaim
* policy is entirely up to the owner under solaris. We only
* pass on the pre-registered private data.
*/
if (kcc->kcc_reclaim)
kcc->kcc_reclaim(kcc->kcc_private);
total += 1;
}
/* Under linux we should return the remaining number of entires in
* the cache. Unfortunately, I don't see an easy way to safely
* emulate this behavior so I'm returning one entry per cache which
* was registered with the generic shrinker. This should fake out
* the linux VM when it attempts to shrink caches.
*/
spin_unlock(&kmem_cache_cb_lock);
return total;
}
/* Ensure the __kmem_cache_create/__kmem_cache_destroy macros are
* removed here to prevent a recursive substitution, we want to call
* the native linux version.
*/
#undef kmem_cache_create
#undef kmem_cache_destroy
kmem_cache_t *
__kmem_cache_create(char *name, size_t size, size_t align,
int (*constructor)(void *, void *, int),
void (*destructor)(void *, void *),
void (*reclaim)(void *),
void *priv, void *vmp, int flags)
{
kmem_cache_t *cache;
kmem_cache_cb_t *kcc;
int shrinker_flag = 0;
/* FIXME: - Option currently unsupported by shim layer */
BUG_ON(vmp);
cache = kmem_cache_create(name, size, align, flags,
kmem_cache_generic_constructor,
kmem_cache_generic_destructor);
if (cache == NULL)
return NULL;
/* Register shared shrinker function on initial cache create */
spin_lock(&kmem_cache_cb_lock);
if (list_empty(&kmem_cache_cb_list)) {
kmem_cache_shrinker = set_shrinker(KMC_DEFAULT_SEEKS,
kmem_cache_generic_shrinker);
if (kmem_cache_shrinker == NULL) {
kmem_cache_destroy(cache);
spin_unlock(&kmem_cache_cb_lock);
return NULL;
}
}
spin_unlock(&kmem_cache_cb_lock);
kcc = kmem_cache_add_cache_cb(cache, constructor, destructor,
reclaim, priv, vmp);
if (kcc == NULL) {
if (shrinker_flag) /* New shrinker registered must be removed */
remove_shrinker(kmem_cache_shrinker);
kmem_cache_destroy(cache);
return NULL;
}
return cache;
}
/* Return codes discarded because Solaris implementation has void return */
void
__kmem_cache_destroy(kmem_cache_t *cache)
{
kmem_cache_cb_t *kcc;
spin_lock(&kmem_cache_cb_lock);
kcc = kmem_cache_find_cache_cb(cache);
spin_unlock(&kmem_cache_cb_lock);
if (kcc == NULL)
return;
kmem_cache_destroy(cache);
kmem_cache_remove_cache_cb(kcc);
/* Unregister generic shrinker on removal of all caches */
spin_lock(&kmem_cache_cb_lock);
if (list_empty(&kmem_cache_cb_list))
remove_shrinker(kmem_cache_shrinker);
spin_unlock(&kmem_cache_cb_lock);
}
void
__kmem_reap(void) {
/* Since there's no easy hook in to linux to force all the registered
* shrinkers to run we just run the ones registered for this shim */
kmem_cache_generic_shrinker(KMC_REAP_CHUNK, GFP_KERNEL);
}

41
src/spl/linux-rwlock.c Normal file
View File

@ -0,0 +1,41 @@
#include <sys/linux-rwlock.h>
int
rw_lock_held(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
if (rwlp->rw_sem.activity != 0) {
#else
if (rwlp->rw_sem.count != 0) {
#endif
return 1;
}
return 0;
}
int
rw_read_held(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
if (rw_lock_held(rwlp) && rwlp->rw_owner == NULL) {
return 1;
}
return 0;
}
int
rw_write_held(krwlock_t *rwlp)
{
BUG_ON(rwlp->rw_magic != RW_MAGIC);
if (rwlp->rw_owner == current) {
return 1;
}
return 0;
}

78
src/spl/linux-taskq.c Normal file
View File

@ -0,0 +1,78 @@
#include <sys/linux-taskq.h>
/*
* Task queue interface
*
* The taskq_work_wrapper functions are used to manage the work_structs
* which must be submitted to linux. The shim layer allocates a wrapper
* structure for all items which contains a pointer to itself as well as
* the real work to be performed. When the work item run the generic
* handle is called which calls the real work function and then using
* the self pointer frees the work_struct.
*/
typedef struct taskq_work_wrapper {
struct work_struct tww_work;
task_func_t tww_func;
void * tww_priv;
} taskq_work_wrapper_t;
static void
taskq_work_handler(void *priv)
{
taskq_work_wrapper_t *tww = priv;
BUG_ON(tww == NULL);
BUG_ON(tww->tww_func == NULL);
/* Call the real function and free the wrapper */
tww->tww_func(tww->tww_priv);
kfree(tww);
}
/* XXX - All flags currently ignored */
taskqid_t
__taskq_dispatch(taskq_t *tq, task_func_t func, void *priv, uint_t flags)
{
struct workqueue_struct *wq = tq;
taskq_work_wrapper_t *tww;
int rc;
BUG_ON(in_interrupt());
BUG_ON(tq == NULL);
BUG_ON(func == NULL);
tww = (taskq_work_wrapper_t *)kmalloc(sizeof(*tww), GFP_KERNEL);
if (!tww)
return (taskqid_t)0;
INIT_WORK(&(tww->tww_work), taskq_work_handler, tww);
tww->tww_func = func;
tww->tww_priv = priv;
rc = queue_work(wq, &(tww->tww_work));
if (!rc) {
kfree(tww);
return (taskqid_t)0;
}
return (taskqid_t)wq;
}
/* XXX - Most args ignored until we decide if it's worth the effort
* to emulate the solaris notion of dynamic thread pools. For
* now we simply serialize everything through one thread which
* may come back to bite us as a performance issue.
* pri - Ignore priority
* min - Ignored until this is a dynamic thread pool
* max - Ignored until this is a dynamic thread pool
* flags - Ignored until this is a dynamic thread_pool
*/
taskq_t *
__taskq_create(const char *name, int nthreads, pri_t pri,
int minalloc, int maxalloc, uint_t flags)
{
/* NOTE: Linux workqueue names are limited to 10 chars */
return create_singlethread_workqueue(name);
}

113
src/spl/linux-thread.c Normal file
View File

@ -0,0 +1,113 @@
#include <sys/linux-thread.h>
/*
* Thread interfaces
*/
typedef struct thread_priv_s {
unsigned long tp_magic; /* Magic */
void (*tp_func)(void *); /* Registered function */
void *tp_args; /* Args to be passed to function */
size_t tp_len; /* Len to be passed to function */
int tp_state; /* State to start thread at */
pri_t tp_pri; /* Priority to start threat at */
volatile kthread_t *tp_task; /* Task pointer for new thread */
spinlock_t tp_lock; /* Syncronization lock */
wait_queue_head_t tp_waitq; /* Syncronization wait queue */
} thread_priv_t;
int
thread_generic_wrapper(void *arg)
{
thread_priv_t *tp = (thread_priv_t *)arg;
void (*func)(void *);
void *args;
char name[16];
/* Use the truncated function name as thread name */
snprintf(name, sizeof(name), "%s", "kthread");
daemonize(name);
spin_lock(&tp->tp_lock);
BUG_ON(tp->tp_magic != TP_MAGIC);
func = tp->tp_func;
args = tp->tp_args;
tp->tp_task = get_current();
set_current_state(tp->tp_state);
set_user_nice((kthread_t *)tp->tp_task, PRIO_TO_NICE(tp->tp_pri));
spin_unlock(&tp->tp_lock);
wake_up(&tp->tp_waitq);
/* DO NOT USE 'ARG' AFTER THIS POINT, EVER, EVER, EVER!
* Local variables are used here because after the calling thread
* has been woken up it will exit and this memory will no longer
* be safe to access since it was declared on the callers stack. */
if (func)
func(args);
return 0;
}
void
__thread_exit(void)
{
return;
}
/* thread_create() may block forever if it cannot create a thread or
* allocate memory. This is preferable to returning a NULL which Solaris
* style callers likely never check for... since it can't fail. */
kthread_t *
__thread_create(caddr_t stk, size_t stksize, void (*proc)(void *),
void *args, size_t len, proc_t *pp, int state, pri_t pri)
{
thread_priv_t tp;
DEFINE_WAIT(wait);
kthread_t *task;
long pid;
/* Option pp is simply ignored */
/* Variable stack size unsupported */
BUG_ON(stk != NULL);
BUG_ON(stk != 0);
/* Variable tp is located on the stack and not the heap because I want
* to minimize any chance of a failure, since the Solaris code is designed
* such that this function cannot fail. This is a little dangerous since
* we're passing a stack address to a new thread but correct locking was
* added to ensure the callee can use the data safely until wake_up(). */
tp.tp_magic = TP_MAGIC;
tp.tp_func = proc;
tp.tp_args = args;
tp.tp_len = len;
tp.tp_state = state;
tp.tp_pri = pri;
tp.tp_task = NULL;
spin_lock_init(&tp.tp_lock);
init_waitqueue_head(&tp.tp_waitq);
spin_lock(&tp.tp_lock);
/* Solaris says this must never fail so we try forever */
while ((pid = kernel_thread(thread_generic_wrapper, (void *)&tp, 0)) < 0)
printk(KERN_ERR "linux-thread: Unable to create thread; "
"pid = %ld\n", pid);
/* All signals are ignored due to sleeping TASK_UNINTERRUPTIBLE */
for (;;) {
prepare_to_wait(&tp.tp_waitq, &wait, TASK_UNINTERRUPTIBLE);
if (tp.tp_task != NULL)
break;
spin_unlock(&tp.tp_lock);
schedule();
spin_lock(&tp.tp_lock);
}
/* Verify the pid retunred matches the pid in the task struct */
BUG_ON(pid != (tp.tp_task)->pid);
spin_unlock(&tp.tp_lock);
return (kthread_t *)tp.tp_task;
}

57
src/splat/Makefile.in Normal file
View File

@ -0,0 +1,57 @@
# Makefile.in for splat kernel module
MODULES := splat
DISTFILES = Makefile.in \
splat-kmem.c splat-random.c splat-taskq.c \
splat-time.c splat-condvar.c splat-mutex.c \
splat-rwlock.c splat-thread.c splat-ctl.c
# Removed '-std=gnu99' does to compile issues with i386 SPIN_LOCK_UNLOCKED
# EXTRA_CFLAGS += -I$(src)
# EXTRA_CFLAGS += -Wall -Wno-unknown-pragmas -Wno-missing-braces \
# -Wno-sign-compare -Wno-parentheses -Wno-uninitialized \
# -Wno-implicit-function-declaration -Wno-unused -Wno-trigraphs \
# -Wno-char-subscripts -Wno-switch
# Solaris porting layer aggressive 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
splatmodule := splat.ko
splatmoduledir := @kmoduledir@/kernel/lib/
all: all-spec
install: all
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)
all-spec:
$(MAKE) -C @kernelsrc@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ modules

454
src/splat/splat-condvar.c Normal file
View File

@ -0,0 +1,454 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_CONDVAR 0x0500
#define KZT_CONDVAR_NAME "condvar"
#define KZT_CONDVAR_DESC "Kernel Condition Variable Tests"
#define KZT_CONDVAR_TEST1_ID 0x0501
#define KZT_CONDVAR_TEST1_NAME "signal1"
#define KZT_CONDVAR_TEST1_DESC "Wake a single thread, cv_wait()/cv_signal()"
#define KZT_CONDVAR_TEST2_ID 0x0502
#define KZT_CONDVAR_TEST2_NAME "broadcast1"
#define KZT_CONDVAR_TEST2_DESC "Wake all threads, cv_wait()/cv_broadcast()"
#define KZT_CONDVAR_TEST3_ID 0x0503
#define KZT_CONDVAR_TEST3_NAME "signal2"
#define KZT_CONDVAR_TEST3_DESC "Wake a single thread, cv_wait_timeout()/cv_signal()"
#define KZT_CONDVAR_TEST4_ID 0x0504
#define KZT_CONDVAR_TEST4_NAME "broadcast2"
#define KZT_CONDVAR_TEST4_DESC "Wake all threads, cv_wait_timeout()/cv_broadcast()"
#define KZT_CONDVAR_TEST5_ID 0x0505
#define KZT_CONDVAR_TEST5_NAME "timeout"
#define KZT_CONDVAR_TEST5_DESC "Timeout thread, cv_wait_timeout()"
#define KZT_CONDVAR_TEST_MAGIC 0x115599DDUL
#define KZT_CONDVAR_TEST_NAME "condvar_test"
#define KZT_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
kzt_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 == KZT_CONDVAR_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d", KZT_CONDVAR_TEST_NAME, ct->ct_id);
daemonize(name);
mutex_enter(&cv->cv_mtx);
kzt_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);
kzt_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
kzt_condvar_test1(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_CONDVAR_TEST_COUNT];
condvar_thr_t ct[KZT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = KZT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, KZT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, KZT_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 < KZT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_id = i;
ct[i].ct_name = KZT_CONDVAR_TEST1_NAME;
ct[i].ct_rc = 0;
pids[i] = kernel_thread(kzt_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;
kzt_vprint(file, KZT_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)
kzt_vprint(file, KZT_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
kzt_condvar_test2(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_CONDVAR_TEST_COUNT];
condvar_thr_t ct[KZT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = KZT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, KZT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, KZT_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 < KZT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_id = i;
ct[i].ct_name = KZT_CONDVAR_TEST2_NAME;
ct[i].ct_rc = 0;
pids[i] = kernel_thread(kzt_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();
kzt_vprint(file, KZT_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
kzt_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 == KZT_CONDVAR_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d", KZT_CONDVAR_TEST_NAME, ct->ct_id);
daemonize(name);
mutex_enter(&cv->cv_mtx);
kzt_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;
kzt_vprint(cv->cv_file, ct->ct_name, "%s thread timed out, "
"should have been woken\n", name);
} else {
kzt_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
kzt_condvar_test3(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_CONDVAR_TEST_COUNT];
condvar_thr_t ct[KZT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = KZT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, KZT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, KZT_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 < KZT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_id = i;
ct[i].ct_name = KZT_CONDVAR_TEST3_NAME;
ct[i].ct_rc = 0;
pids[i] = kernel_thread(kzt_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;
kzt_vprint(file, KZT_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)
kzt_vprint(file, KZT_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
kzt_condvar_test4(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_CONDVAR_TEST_COUNT];
condvar_thr_t ct[KZT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = KZT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, KZT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, KZT_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 < KZT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_id = i;
ct[i].ct_name = KZT_CONDVAR_TEST3_NAME;
ct[i].ct_rc = 0;
pids[i] = kernel_thread(kzt_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;
kzt_vprint(file, KZT_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)
kzt_vprint(file, KZT_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
kzt_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, KZT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&condvar, KZT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_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 {
kzt_vprint(file, KZT_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 {
kzt_vprint(file, KZT_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;
}
kzt_subsystem_t *
kzt_condvar_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_CONDVAR_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_CONDVAR_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_CONDVAR;
KZT_TEST_INIT(sub, KZT_CONDVAR_TEST1_NAME, KZT_CONDVAR_TEST1_DESC,
KZT_CONDVAR_TEST1_ID, kzt_condvar_test1);
KZT_TEST_INIT(sub, KZT_CONDVAR_TEST2_NAME, KZT_CONDVAR_TEST2_DESC,
KZT_CONDVAR_TEST2_ID, kzt_condvar_test2);
KZT_TEST_INIT(sub, KZT_CONDVAR_TEST3_NAME, KZT_CONDVAR_TEST3_DESC,
KZT_CONDVAR_TEST3_ID, kzt_condvar_test3);
KZT_TEST_INIT(sub, KZT_CONDVAR_TEST4_NAME, KZT_CONDVAR_TEST4_DESC,
KZT_CONDVAR_TEST4_ID, kzt_condvar_test4);
KZT_TEST_INIT(sub, KZT_CONDVAR_TEST5_NAME, KZT_CONDVAR_TEST5_DESC,
KZT_CONDVAR_TEST5_ID, kzt_condvar_test5);
return sub;
}
void
kzt_condvar_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_CONDVAR_TEST5_ID);
KZT_TEST_FINI(sub, KZT_CONDVAR_TEST4_ID);
KZT_TEST_FINI(sub, KZT_CONDVAR_TEST3_ID);
KZT_TEST_FINI(sub, KZT_CONDVAR_TEST2_ID);
KZT_TEST_FINI(sub, KZT_CONDVAR_TEST1_ID);
kfree(sub);
}
int
kzt_condvar_id(void) {
return KZT_SUBSYSTEM_CONDVAR;
}

684
src/splat/splat-ctl.c Normal file
View File

@ -0,0 +1,684 @@
/*
* My intent is the create a loadable kzt (kernel ZFS test) module
* which can be used as an access point to run in kernel ZFS regression
* tests. Why do we need this when we have ztest? Well ztest.c only
* excersises the ZFS code proper, it cannot be used to validate the
* linux kernel shim primatives. This also provides a nice hook for
* any other in kernel regression tests we wish to run such as direct
* in-kernel tests against the DMU.
*
* The basic design is the kzt module is that it is constructed of
* various kzt_* source files each of which contains regression tests.
* For example the kzt_linux_kmem.c file contains tests for validating
* kmem correctness. When the kzt module is loaded kzt_*_init()
* will be called for each subsystems tests, similarly kzt_*_fini() is
* called when the kzt 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.
*
* Author: Brian Behlendorf
*/
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/device.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
#include <linux/devfs_fs_kernel.h>
#endif
#include <linux/cdev.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
static struct class_simple *kzt_class;
#else
static struct class *kzt_class;
#endif
static struct list_head kzt_module_list;
static spinlock_t kzt_module_lock;
static int
kzt_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
kzt_info_t *info;
if (minor >= KZT_MINORS)
return -ENXIO;
info = (kzt_info_t *)kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
spin_lock_init(&info->info_lock);
info->info_size = KZT_INFO_BUFFER_SIZE;
info->info_buffer = (char *)vmalloc(KZT_INFO_BUFFER_SIZE);
if (info->info_buffer == NULL) {
kfree(info);
return -ENOMEM;
}
info->info_head = info->info_buffer;
file->private_data = (void *)info;
kzt_print(file, "Kernel ZFS Tests %s\n", KZT_VERSION);
return 0;
}
static int
kzt_release(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
kzt_info_t *info = (kzt_info_t *)file->private_data;
if (minor >= KZT_MINORS)
return -ENXIO;
ASSERT(info);
ASSERT(info->info_buffer);
vfree(info->info_buffer);
kfree(info);
return 0;
}
static int
kzt_buffer_clear(struct file *file, kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_info_t *info = (kzt_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
kzt_buffer_size(struct file *file, kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_info_t *info = (kzt_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 kzt_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
rc = -EFAULT;
out:
spin_unlock(&info->info_lock);
return rc;
}
static kzt_subsystem_t *
kzt_subsystem_find(int id) {
kzt_subsystem_t *sub;
spin_lock(&kzt_module_lock);
list_for_each_entry(sub, &kzt_module_list, subsystem_list) {
if (id == sub->desc.id) {
spin_unlock(&kzt_module_lock);
return sub;
}
}
spin_unlock(&kzt_module_lock);
return NULL;
}
static int
kzt_subsystem_count(kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_subsystem_t *sub;
int i = 0;
spin_lock(&kzt_module_lock);
list_for_each_entry(sub, &kzt_module_list, subsystem_list)
i++;
spin_unlock(&kzt_module_lock);
kcfg->cfg_rc1 = i;
if (copy_to_user((struct kzt_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
return -EFAULT;
return 0;
}
static int
kzt_subsystem_list(kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_subsystem_t *sub;
kzt_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.kzt_subsystems.size *
sizeof(kzt_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(&kzt_module_lock);
list_for_each_entry(sub, &kzt_module_list, subsystem_list) {
strncpy(tmp->cfg_data.kzt_subsystems.descs[i].name,
sub->desc.name, KZT_NAME_SIZE);
strncpy(tmp->cfg_data.kzt_subsystems.descs[i].desc,
sub->desc.desc, KZT_DESC_SIZE);
tmp->cfg_data.kzt_subsystems.descs[i].id = sub->desc.id;
/* Truncate list if we are about to overrun alloc'ed memory */
if ((i++) == kcfg->cfg_data.kzt_subsystems.size)
break;
}
spin_unlock(&kzt_module_lock);
tmp->cfg_rc1 = i;
if (copy_to_user((struct kzt_cfg_t __user *)arg, tmp, size)) {
kfree(tmp);
return -EFAULT;
}
kfree(tmp);
return 0;
}
static int
kzt_test_count(kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_subsystem_t *sub;
kzt_test_t *test;
int rc, i = 0;
/* Subsystem ID passed as arg1 */
sub = kzt_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 kzt_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
return -EFAULT;
return 0;
}
static int
kzt_test_list(kzt_cfg_t *kcfg, unsigned long arg)
{
kzt_subsystem_t *sub;
kzt_test_t *test;
kzt_cfg_t *tmp;
int size, rc, i = 0;
/* Subsystem ID passed as arg1 */
sub = kzt_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.kzt_tests.size*sizeof(kzt_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.kzt_tests.descs[i].name,
test->desc.name, KZT_NAME_SIZE);
strncpy(tmp->cfg_data.kzt_tests.descs[i].desc,
test->desc.desc, KZT_DESC_SIZE);
tmp->cfg_data.kzt_tests.descs[i].id = test->desc.id;
/* Truncate list if we are about to overrun alloc'ed memory */
if ((i++) == kcfg->cfg_data.kzt_tests.size)
break;
}
spin_unlock(&(sub->test_lock));
tmp->cfg_rc1 = i;
if (copy_to_user((struct kzt_cfg_t __user *)arg, tmp, size)) {
kfree(tmp);
return -EFAULT;
}
kfree(tmp);
return 0;
}
static int
kzt_validate(struct file *file, kzt_subsystem_t *sub, int cmd, void *arg)
{
kzt_test_t *test;
int rc = 0;
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
kzt_ioctl_cfg(struct file *file, unsigned long arg)
{
kzt_cfg_t kcfg;
int rc = 0;
if (copy_from_user(&kcfg, (kzt_cfg_t *)arg, sizeof(kcfg)))
return -EFAULT;
if (kcfg.cfg_magic != KZT_CFG_MAGIC) {
kzt_print(file, "Bad config magic 0x%x != 0x%x\n",
kcfg.cfg_magic, KZT_CFG_MAGIC);
return -EINVAL;
}
switch (kcfg.cfg_cmd) {
case KZT_CFG_BUFFER_CLEAR:
/* cfg_arg1 - Unused
* cfg_rc1 - Unused
*/
rc = kzt_buffer_clear(file, &kcfg, arg);
break;
case KZT_CFG_BUFFER_SIZE:
/* cfg_arg1 - 0 - query size; >0 resize
* cfg_rc1 - Set to current buffer size
*/
rc = kzt_buffer_size(file, &kcfg, arg);
break;
case KZT_CFG_SUBSYSTEM_COUNT:
/* cfg_arg1 - Unused
* cfg_rc1 - Set to number of subsystems
*/
rc = kzt_subsystem_count(&kcfg, arg);
break;
case KZT_CFG_SUBSYSTEM_LIST:
/* cfg_arg1 - Unused
* cfg_rc1 - Set to number of subsystems
* cfg_data.kzt_subsystems - Populated with subsystems
*/
rc = kzt_subsystem_list(&kcfg, arg);
break;
case KZT_CFG_TEST_COUNT:
/* cfg_arg1 - Set to a target subsystem
* cfg_rc1 - Set to number of tests
*/
rc = kzt_test_count(&kcfg, arg);
break;
case KZT_CFG_TEST_LIST:
/* cfg_arg1 - Set to a target subsystem
* cfg_rc1 - Set to number of tests
* cfg_data.kzt_subsystems - Populated with tests
*/
rc = kzt_test_list(&kcfg, arg);
break;
default:
kzt_print(file, "Bad config command %d\n", kcfg.cfg_cmd);
rc = -EINVAL;
break;
}
return rc;
}
static int
kzt_ioctl_cmd(struct file *file, unsigned long arg)
{
kzt_subsystem_t *sub;
kzt_cmd_t kcmd;
int rc = -EINVAL;
void *data = NULL;
if (copy_from_user(&kcmd, (kzt_cfg_t *)arg, sizeof(kcmd)))
return -EFAULT;
if (kcmd.cmd_magic != KZT_CMD_MAGIC) {
kzt_print(file, "Bad command magic 0x%x != 0x%x\n",
kcmd.cmd_magic, KZT_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(kzt_cmd_t,
cmd_data_str)), kcmd.cmd_data_size)) {
kfree(data);
return -EFAULT;
}
}
sub = kzt_subsystem_find(kcmd.cmd_subsystem);
if (sub != NULL)
rc = kzt_validate(file, sub, kcmd.cmd_test, data);
else
rc = -EINVAL;
if (data != NULL)
kfree(data);
return rc;
}
static int
kzt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int minor, rc = 0;
/* Ignore tty ioctls */
if ((cmd & 0xffffff00) == ((int)'T') << 8)
return -ENOTTY;
if (minor >= KZT_MINORS)
return -ENXIO;
switch (cmd) {
case KZT_CFG:
rc = kzt_ioctl_cfg(file, arg);
break;
case KZT_CMD:
rc = kzt_ioctl_cmd(file, arg);
break;
default:
kzt_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 kzt_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
kzt_info_t *info = (kzt_info_t *)file->private_data;
int rc = 0;
if (minor >= KZT_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 kzt_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
kzt_info_t *info = (kzt_info_t *)file->private_data;
int rc = 0;
if (minor >= KZT_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 kzt_seek(struct file *file, loff_t offset, int origin)
{
unsigned int minor = iminor(file->f_dentry->d_inode);
kzt_info_t *info = (kzt_info_t *)file->private_data;
int rc = -EINVAL;
if (minor >= KZT_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 kzt_fops = {
.owner = THIS_MODULE,
.open = kzt_open,
.release = kzt_release,
.ioctl = kzt_ioctl,
.read = kzt_read,
.write = kzt_write,
.llseek = kzt_seek,
};
static struct cdev kzt_cdev = {
.owner = THIS_MODULE,
.kobj = { .name = "kztctl", },
};
static int __init
kzt_init(void)
{
dev_t dev;
int i, rc;
spin_lock_init(&kzt_module_lock);
INIT_LIST_HEAD(&kzt_module_list);
KZT_SUBSYSTEM_INIT(kmem);
KZT_SUBSYSTEM_INIT(taskq);
KZT_SUBSYSTEM_INIT(krng);
KZT_SUBSYSTEM_INIT(mutex);
KZT_SUBSYSTEM_INIT(condvar);
KZT_SUBSYSTEM_INIT(thread);
KZT_SUBSYSTEM_INIT(rwlock);
KZT_SUBSYSTEM_INIT(time);
dev = MKDEV(KZT_MAJOR, 0);
if (rc = register_chrdev_region(dev, KZT_MINORS, "kztctl"))
goto error;
/* Support for registering a character driver */
cdev_init(&kzt_cdev, &kzt_fops);
if ((rc = cdev_add(&kzt_cdev, dev, KZT_MINORS))) {
printk(KERN_ERR "kzt: Error adding cdev, %d\n", rc);
kobject_put(&kzt_cdev.kobj);
unregister_chrdev_region(dev, KZT_MINORS);
goto error;
}
/* Support for udev make driver info available in sysfs */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
kzt_class = class_simple_create(THIS_MODULE, "kzt");
#else
kzt_class = class_create(THIS_MODULE, "kzt");
#endif
if (IS_ERR(kzt_class)) {
rc = PTR_ERR(kzt_class);
printk(KERN_ERR "kzt: Error creating kzt class, %d\n", rc);
cdev_del(&kzt_cdev);
unregister_chrdev_region(dev, KZT_MINORS);
goto error;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
class_simple_device_add(kzt_class, MKDEV(KZT_MAJOR, 0),
NULL, "kztctl");
#else
class_device_create(kzt_class, NULL, MKDEV(KZT_MAJOR, 0),
NULL, "kztctl");
#endif
printk(KERN_INFO "kzt: Kernel ZFS Tests %s Loaded\n", KZT_VERSION);
return 0;
error:
printk(KERN_ERR "kzt: Error registering kzt device, %d\n", rc);
return rc;
}
static void
kzt_fini(void)
{
dev_t dev = MKDEV(KZT_MAJOR, 0);
int i;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
class_simple_device_remove(dev);
class_simple_destroy(kzt_class);
devfs_remove("kzt/kztctl");
devfs_remove("kzt");
#else
class_device_destroy(kzt_class, dev);
class_destroy(kzt_class);
#endif
cdev_del(&kzt_cdev);
unregister_chrdev_region(dev, KZT_MINORS);
KZT_SUBSYSTEM_FINI(time);
KZT_SUBSYSTEM_FINI(rwlock);
KZT_SUBSYSTEM_FINI(thread);
KZT_SUBSYSTEM_FINI(condvar);
KZT_SUBSYSTEM_FINI(mutex);
KZT_SUBSYSTEM_FINI(krng);
KZT_SUBSYSTEM_FINI(taskq);
KZT_SUBSYSTEM_FINI(kmem);
ASSERT(list_empty(&kzt_module_list));
printk(KERN_INFO "kzt: Kernel ZFS Tests %s Unloaded\n", KZT_VERSION);
}
module_init(kzt_init);
module_exit(kzt_fini);
MODULE_AUTHOR("Lawrence Livermore National Labs");
MODULE_DESCRIPTION("Kernel ZFS Test");
MODULE_LICENSE("GPL");

365
src/splat/splat-kmem.c Normal file
View File

@ -0,0 +1,365 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_KMEM 0x0100
#define KZT_KMEM_NAME "kmem"
#define KZT_KMEM_DESC "Kernel Malloc/Slab Tests"
#define KZT_KMEM_TEST1_ID 0x0101
#define KZT_KMEM_TEST1_NAME "kmem_alloc"
#define KZT_KMEM_TEST1_DESC "Memory allocation test (kmem_alloc)"
#define KZT_KMEM_TEST2_ID 0x0102
#define KZT_KMEM_TEST2_NAME "kmem_zalloc"
#define KZT_KMEM_TEST2_DESC "Memory allocation test (kmem_zalloc)"
#define KZT_KMEM_TEST3_ID 0x0103
#define KZT_KMEM_TEST3_NAME "slab_alloc"
#define KZT_KMEM_TEST3_DESC "Slab constructor/destructor test"
#define KZT_KMEM_TEST4_ID 0x0104
#define KZT_KMEM_TEST4_NAME "slab_reap"
#define KZT_KMEM_TEST4_DESC "Slab reaping test"
#define KZT_KMEM_ALLOC_COUNT 10
/* XXX - This test may fail under tight memory conditions */
static int
kzt_kmem_test1(struct file *file, void *arg)
{
void *ptr[KZT_KMEM_ALLOC_COUNT];
int size = PAGE_SIZE;
int i, count, rc = 0;
while ((!rc) && (size < (PAGE_SIZE * 16))) {
count = 0;
for (i = 0; i < KZT_KMEM_ALLOC_COUNT; i++) {
ptr[i] = kmem_alloc(size, KM_SLEEP);
if (ptr[i])
count++;
}
for (i = 0; i < KZT_KMEM_ALLOC_COUNT; i++)
if (ptr[i])
kmem_free(ptr[i], size);
kzt_vprint(file, KZT_KMEM_TEST1_NAME,
"%d byte allocations, %d/%d successful\n",
size, count, KZT_KMEM_ALLOC_COUNT);
if (count != KZT_KMEM_ALLOC_COUNT)
rc = -ENOMEM;
size *= 2;
}
return rc;
}
static int
kzt_kmem_test2(struct file *file, void *arg)
{
void *ptr[KZT_KMEM_ALLOC_COUNT];
int size = PAGE_SIZE;
int i, j, count, rc = 0;
while ((!rc) && (size < (PAGE_SIZE * 16))) {
count = 0;
for (i = 0; i < KZT_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 < KZT_KMEM_ALLOC_COUNT; i++) {
for (j = 0; j < size; j++) {
if (((char *)ptr[i])[j] != '\0') {
kzt_vprint(file, KZT_KMEM_TEST2_NAME,
"%d-byte allocation was "
"not zeroed\n", size);
rc = -EFAULT;
}
}
}
for (i = 0; i < KZT_KMEM_ALLOC_COUNT; i++)
if (ptr[i])
kmem_free(ptr[i], size);
kzt_vprint(file, KZT_KMEM_TEST2_NAME,
"%d byte allocations, %d/%d successful\n",
size, count, KZT_KMEM_ALLOC_COUNT);
if (count != KZT_KMEM_ALLOC_COUNT)
rc = -ENOMEM;
size *= 2;
}
return rc;
}
#define KZT_KMEM_TEST_MAGIC 0x004488CCUL
#define KZT_KMEM_CACHE_NAME "kmem_test"
#define KZT_KMEM_CACHE_SIZE 256
#define KZT_KMEM_OBJ_COUNT 128
#define KZT_KMEM_OBJ_RECLAIM 64
typedef struct kmem_cache_data {
char kcd_buf[KZT_KMEM_CACHE_SIZE];
unsigned long kcd_magic;
int kcd_flag;
} 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[KZT_KMEM_OBJ_COUNT];
int kcp_count;
int kcp_rc;
} kmem_cache_priv_t;
static int
kzt_kmem_test34_constructor(void *ptr, void *priv, int flags)
{
kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr;
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
if (kcd) {
memset(kcd->kcd_buf, 0xaa, KZT_KMEM_CACHE_SIZE);
kcd->kcd_flag = 1;
if (kcp) {
kcd->kcd_magic = kcp->kcp_magic;
kcp->kcp_count++;
}
}
return 0;
}
static void
kzt_kmem_test34_destructor(void *ptr, void *priv)
{
kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr;
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
if (kcd) {
memset(kcd->kcd_buf, 0xbb, KZT_KMEM_CACHE_SIZE);
kcd->kcd_flag = 0;
if (kcp)
kcp->kcp_count--;
}
return;
}
static int
kzt_kmem_test3(struct file *file, void *arg)
{
kmem_cache_t *cache = NULL;
kmem_cache_data_t *kcd = NULL;
kmem_cache_priv_t kcp;
int rc = 0, max;
kcp.kcp_magic = KZT_KMEM_TEST_MAGIC;
kcp.kcp_file = file;
kcp.kcp_count = 0;
kcp.kcp_rc = 0;
cache = kmem_cache_create(KZT_KMEM_CACHE_NAME, sizeof(*kcd), 0,
kzt_kmem_test34_constructor,
kzt_kmem_test34_destructor,
NULL, &kcp, NULL, 0);
if (!cache) {
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"Unable to create '%s'\n", KZT_KMEM_CACHE_NAME);
return -ENOMEM;
}
kcd = kmem_cache_alloc(cache, 0);
if (!kcd) {
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"Unable to allocate from '%s'\n",
KZT_KMEM_CACHE_NAME);
rc = -EINVAL;
goto out_free;
}
if (!kcd->kcd_flag) {
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"Failed to run contructor for '%s'\n",
KZT_KMEM_CACHE_NAME);
rc = -EINVAL;
goto out_free;
}
if (kcd->kcd_magic != kcp.kcp_magic) {
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"Failed to pass private data to constructor "
"for '%s'\n", KZT_KMEM_CACHE_NAME);
rc = -EINVAL;
goto out_free;
}
max = kcp.kcp_count;
/* Destructor's run lazily so it hard to check correctness here.
* We assume if it doesn't crash the free worked properly */
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) {
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"Failed to run destructor on all slab objects "
"for '%s'\n", KZT_KMEM_CACHE_NAME);
rc = -EINVAL;
}
kzt_vprint(file, KZT_KMEM_TEST3_NAME,
"%d allocated/destroyed objects for '%s'\n",
max, KZT_KMEM_CACHE_NAME);
return rc;
out_free:
if (kcd)
kmem_cache_free(cache, kcd);
out_destroy:
kmem_cache_destroy(cache);
return rc;
}
static void
kzt_kmem_test4_reclaim(void *priv)
{
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
int i;
kzt_vprint(kcp->kcp_file, KZT_KMEM_TEST4_NAME,
"Reaping %d objects from '%s'\n",
KZT_KMEM_OBJ_RECLAIM, KZT_KMEM_CACHE_NAME);
for (i = 0; i < KZT_KMEM_OBJ_RECLAIM; i++) {
if (kcp->kcp_kcd[i]) {
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
kcp->kcp_kcd[i] = NULL;
}
}
return;
}
static int
kzt_kmem_test4(struct file *file, void *arg)
{
kmem_cache_t *cache;
kmem_cache_priv_t kcp;
int i, rc = 0, max, reclaim_percent, target_percent;
kcp.kcp_magic = KZT_KMEM_TEST_MAGIC;
kcp.kcp_file = file;
kcp.kcp_count = 0;
kcp.kcp_rc = 0;
cache = kmem_cache_create(KZT_KMEM_CACHE_NAME,
sizeof(kmem_cache_data_t), 0,
kzt_kmem_test34_constructor,
kzt_kmem_test34_destructor,
kzt_kmem_test4_reclaim, &kcp, NULL, 0);
if (!cache) {
kzt_vprint(file, KZT_KMEM_TEST4_NAME,
"Unable to create '%s'\n", KZT_KMEM_CACHE_NAME);
return -ENOMEM;
}
kcp.kcp_cache = cache;
for (i = 0; i < KZT_KMEM_OBJ_COUNT; i++) {
/* All allocations need not succeed */
kcp.kcp_kcd[i] = kmem_cache_alloc(cache, 0);
if (!kcp.kcp_kcd[i]) {
kzt_vprint(file, KZT_KMEM_TEST4_NAME,
"Unable to allocate from '%s'\n",
KZT_KMEM_CACHE_NAME);
}
}
max = kcp.kcp_count;
/* Force shrinker to run */
kmem_reap();
/* Reclaim reclaimed objects, this ensure the destructors are run */
kmem_cache_reap_now(cache);
reclaim_percent = ((kcp.kcp_count * 100) / max);
target_percent = (((KZT_KMEM_OBJ_COUNT - KZT_KMEM_OBJ_RECLAIM) * 100) /
KZT_KMEM_OBJ_COUNT);
kzt_vprint(file, KZT_KMEM_TEST4_NAME,
"%d%% (%d/%d) of previous size, target of "
"%d%%-%d%% for '%s'\n", reclaim_percent, kcp.kcp_count,
max, target_percent - 10, target_percent + 10,
KZT_KMEM_CACHE_NAME);
if ((reclaim_percent < target_percent - 10) ||
(reclaim_percent > target_percent + 10))
rc = -EINVAL;
/* Cleanup our mess */
for (i = 0; i < KZT_KMEM_OBJ_COUNT; i++)
if (kcp.kcp_kcd[i])
kmem_cache_free(cache, kcp.kcp_kcd[i]);
kmem_cache_destroy(cache);
return rc;
}
kzt_subsystem_t *
kzt_kmem_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_KMEM_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_KMEM_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_KMEM;
KZT_TEST_INIT(sub, KZT_KMEM_TEST1_NAME, KZT_KMEM_TEST1_DESC,
KZT_KMEM_TEST1_ID, kzt_kmem_test1);
KZT_TEST_INIT(sub, KZT_KMEM_TEST2_NAME, KZT_KMEM_TEST2_DESC,
KZT_KMEM_TEST2_ID, kzt_kmem_test2);
KZT_TEST_INIT(sub, KZT_KMEM_TEST3_NAME, KZT_KMEM_TEST3_DESC,
KZT_KMEM_TEST3_ID, kzt_kmem_test3);
KZT_TEST_INIT(sub, KZT_KMEM_TEST4_NAME, KZT_KMEM_TEST4_DESC,
KZT_KMEM_TEST4_ID, kzt_kmem_test4);
return sub;
}
void
kzt_kmem_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_KMEM_TEST4_ID);
KZT_TEST_FINI(sub, KZT_KMEM_TEST3_ID);
KZT_TEST_FINI(sub, KZT_KMEM_TEST2_ID);
KZT_TEST_FINI(sub, KZT_KMEM_TEST1_ID);
kfree(sub);
}
int
kzt_kmem_id(void) {
return KZT_SUBSYSTEM_KMEM;
}

324
src/splat/splat-mutex.c Normal file
View File

@ -0,0 +1,324 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_MUTEX 0x0400
#define KZT_MUTEX_NAME "mutex"
#define KZT_MUTEX_DESC "Kernel Mutex Tests"
#define KZT_MUTEX_TEST1_ID 0x0401
#define KZT_MUTEX_TEST1_NAME "tryenter"
#define KZT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness"
#define KZT_MUTEX_TEST2_ID 0x0402
#define KZT_MUTEX_TEST2_NAME "race"
#define KZT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex"
#define KZT_MUTEX_TEST3_ID 0x0403
#define KZT_MUTEX_TEST3_NAME "owned"
#define KZT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness"
#define KZT_MUTEX_TEST4_ID 0x0404
#define KZT_MUTEX_TEST4_NAME "owner"
#define KZT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness"
#define KZT_MUTEX_TEST_MAGIC 0x115599DDUL
#define KZT_MUTEX_TEST_NAME "mutex_test"
#define KZT_MUTEX_TEST_WORKQ "mutex_wq"
#define KZT_MUTEX_TEST_COUNT 128
typedef struct mutex_priv {
unsigned long mp_magic;
struct file *mp_file;
struct work_struct mp_work[KZT_MUTEX_TEST_COUNT];
kmutex_t mp_mtx;
int mp_rc;
} mutex_priv_t;
static void
kzt_mutex_test1_work(void *priv)
{
mutex_priv_t *mp = (mutex_priv_t *)priv;
ASSERT(mp->mp_magic == KZT_MUTEX_TEST_MAGIC);
mp->mp_rc = 0;
if (!mutex_tryenter(&mp->mp_mtx))
mp->mp_rc = -EBUSY;
}
static int
kzt_mutex_test1(struct file *file, void *arg)
{
struct workqueue_struct *wq;
struct work_struct work;
mutex_priv_t *mp;
int rc = 0;
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
if (mp == NULL)
return -ENOMEM;
wq = create_singlethread_workqueue(KZT_MUTEX_TEST_WORKQ);
if (wq == NULL) {
rc = -ENOMEM;
goto out2;
}
mutex_init(&(mp->mp_mtx), KZT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mutex_enter(&(mp->mp_mtx));
mp->mp_magic = KZT_MUTEX_TEST_MAGIC;
mp->mp_file = file;
INIT_WORK(&work, kzt_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;
}
kzt_vprint(file, KZT_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;
}
kzt_vprint(file, KZT_MUTEX_TEST1_NAME, "%s",
"mutex_trylock() correctly succeeded when mutex unheld\n");
out:
mutex_destroy(&(mp->mp_mtx));
destroy_workqueue(wq);
out2:
kfree(mp);
return rc;
}
static void
kzt_mutex_test2_work(void *priv)
{
mutex_priv_t *mp = (mutex_priv_t *)priv;
int rc;
ASSERT(mp->mp_magic == KZT_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);
}
static int
kzt_mutex_test2(struct file *file, void *arg)
{
struct workqueue_struct *wq;
mutex_priv_t *mp;
int i, rc = 0;
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(KZT_MUTEX_TEST_WORKQ);
if (wq == NULL) {
rc = -ENOMEM;
goto out;
}
mutex_init(&(mp->mp_mtx), KZT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mp->mp_magic = KZT_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 < KZT_MUTEX_TEST_COUNT; i++) {
INIT_WORK(&(mp->mp_work[i]), kzt_mutex_test2_work, mp);
if (!queue_work(wq, &(mp->mp_work[i]))) {
kzt_vprint(file, KZT_MUTEX_TEST2_NAME,
"Failed to queue work id %d\n", i);
rc = -EINVAL;
}
}
flush_workqueue(wq);
if (mp->mp_rc == KZT_MUTEX_TEST_COUNT) {
kzt_vprint(file, KZT_MUTEX_TEST2_NAME, "%d racing threads "
"correctly entered/exited the mutex %d times\n",
num_online_cpus(), mp->mp_rc);
} else {
kzt_vprint(file, KZT_MUTEX_TEST2_NAME, "%d racing threads "
"only processed %d/%d mutex work items\n",
num_online_cpus(), mp->mp_rc, KZT_MUTEX_TEST_COUNT);
rc = -EINVAL;
}
mutex_destroy(&(mp->mp_mtx));
destroy_workqueue(wq);
out:
kfree(mp);
return rc;
}
static int
kzt_mutex_test3(struct file *file, void *arg)
{
kmutex_t mtx;
int rc = 0;
mutex_init(&mtx, KZT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mutex_enter(&mtx);
/* Mutex should be owned by current */
if (!mutex_owned(&mtx)) {
kzt_vprint(file, KZT_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)) {
kzt_vprint(file, KZT_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;
}
kzt_vprint(file, KZT_MUTEX_TEST3_NAME, "%s",
"Correct mutex_owned() behavior\n");
out:
mutex_destroy(&mtx);
return rc;
}
static int
kzt_mutex_test4(struct file *file, void *arg)
{
kmutex_t mtx;
kthread_t *owner;
int rc = 0;
mutex_init(&mtx, KZT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mutex_enter(&mtx);
/* Mutex should be owned by current */
owner = mutex_owner(&mtx);
if (current != owner) {
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_MUTEX_TEST3_NAME, "Mutex should not "
"be owned but is owned by pid %d\n", owner->pid);
rc = -EINVAL;
goto out;
}
kzt_vprint(file, KZT_MUTEX_TEST3_NAME, "%s",
"Correct mutex_owner() behavior\n");
out:
mutex_destroy(&mtx);
return rc;
}
kzt_subsystem_t *
kzt_mutex_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_MUTEX_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_MUTEX_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_MUTEX;
KZT_TEST_INIT(sub, KZT_MUTEX_TEST1_NAME, KZT_MUTEX_TEST1_DESC,
KZT_MUTEX_TEST1_ID, kzt_mutex_test1);
KZT_TEST_INIT(sub, KZT_MUTEX_TEST2_NAME, KZT_MUTEX_TEST2_DESC,
KZT_MUTEX_TEST2_ID, kzt_mutex_test2);
KZT_TEST_INIT(sub, KZT_MUTEX_TEST3_NAME, KZT_MUTEX_TEST3_DESC,
KZT_MUTEX_TEST3_ID, kzt_mutex_test3);
KZT_TEST_INIT(sub, KZT_MUTEX_TEST4_NAME, KZT_MUTEX_TEST4_DESC,
KZT_MUTEX_TEST4_ID, kzt_mutex_test4);
return sub;
}
void
kzt_mutex_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_MUTEX_TEST4_ID);
KZT_TEST_FINI(sub, KZT_MUTEX_TEST3_ID);
KZT_TEST_FINI(sub, KZT_MUTEX_TEST2_ID);
KZT_TEST_FINI(sub, KZT_MUTEX_TEST1_ID);
kfree(sub);
}
int
kzt_mutex_id(void) {
return KZT_SUBSYSTEM_MUTEX;
}

104
src/splat/splat-random.c Normal file
View File

@ -0,0 +1,104 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_KRNG 0x0300
#define KZT_KRNG_NAME "krng"
#define KZT_KRNG_DESC "Kernel Random Number Generator Tests"
#define KZT_KRNG_TEST1_ID 0x0301
#define KZT_KRNG_TEST1_NAME "freq"
#define KZT_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
kzt_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;
kzt_print(file, "Test 1 Number of ones: %d\n", num);
kzt_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;
}
kzt_subsystem_t *
kzt_krng_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_KRNG_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_KRNG_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_KRNG;
KZT_TEST_INIT(sub, KZT_KRNG_TEST1_NAME, KZT_KRNG_TEST1_DESC,
KZT_KRNG_TEST1_ID, kzt_krng_test1);
return sub;
}
void
kzt_krng_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_KRNG_TEST1_ID);
kfree(sub);
}
int
kzt_krng_id(void) {
return KZT_SUBSYSTEM_KRNG;
}

764
src/splat/splat-rwlock.c Normal file
View File

@ -0,0 +1,764 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_RWLOCK 0x0700
#define KZT_RWLOCK_NAME "rwlock"
#define KZT_RWLOCK_DESC "Kernel RW Lock Tests"
#define KZT_RWLOCK_TEST1_ID 0x0701
#define KZT_RWLOCK_TEST1_NAME "rwtest1"
#define KZT_RWLOCK_TEST1_DESC "Multiple Readers One Writer"
#define KZT_RWLOCK_TEST2_ID 0x0702
#define KZT_RWLOCK_TEST2_NAME "rwtest2"
#define KZT_RWLOCK_TEST2_DESC "Multiple Writers"
#define KZT_RWLOCK_TEST3_ID 0x0703
#define KZT_RWLOCK_TEST3_NAME "rwtest3"
#define KZT_RWLOCK_TEST3_DESC "Owner Verification"
#define KZT_RWLOCK_TEST4_ID 0x0704
#define KZT_RWLOCK_TEST4_NAME "rwtest4"
#define KZT_RWLOCK_TEST4_DESC "Trylock Test"
#define KZT_RWLOCK_TEST5_ID 0x0705
#define KZT_RWLOCK_TEST5_NAME "rwtest5"
#define KZT_RWLOCK_TEST5_DESC "Write Downgrade Test"
#define KZT_RWLOCK_TEST6_ID 0x0706
#define KZT_RWLOCK_TEST6_NAME "rwtest6"
#define KZT_RWLOCK_TEST6_DESC "Read Upgrade Test"
#define KZT_RWLOCK_TEST_MAGIC 0x115599DDUL
#define KZT_RWLOCK_TEST_NAME "rwlock_test"
#define KZT_RWLOCK_TEST_COUNT 8
#define KZT_RWLOCK_RELEASE_INIT 0
#define KZT_RWLOCK_RELEASE_WRITERS 1
#define KZT_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
kzt_rwlock_sleep(signed long delay)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(delay);
}
#define kzt_rwlock_lock_and_test(lock,test) \
({ \
int ret = 0; \
\
spin_lock(lock); \
ret = (test) ? 1 : 0; \
spin_unlock(lock); \
ret; \
})
void kzt_init_rw_priv(rw_priv_t *rwv, struct file *file)
{
rwv->rw_magic = KZT_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, KZT_RWLOCK_RELEASE_INIT);
/* Initialize the read/write lock */
rw_init(&rwv->rwl, KZT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL);
}
int
kzt_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 == KZT_RWLOCK_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d",
KZT_RWLOCK_TEST_NAME, rwt->rwt_id);
daemonize(name);
get_random_bytes((void *)&rnd, 1);
kzt_rwlock_sleep(rnd * HZ / 1000);
spin_lock(&rwv->rw_priv_lock);
kzt_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);
kzt_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,
kzt_rwlock_lock_and_test(&rwv->rw_priv_lock,
atomic_read(&rwv->rw_release) ==
KZT_RWLOCK_RELEASE_WRITERS));
spin_lock(&rwv->rw_priv_lock);
atomic_inc(&rwv->rw_completed);
atomic_dec(&rwv->rw_acquired);
kzt_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
kzt_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 == KZT_RWLOCK_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d",
KZT_RWLOCK_TEST_NAME, rwt->rwt_id);
daemonize(name);
get_random_bytes((void *)&rnd, 1);
kzt_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,
kzt_rwlock_lock_and_test(&rwv->rw_priv_lock,
atomic_read(&rwv->rw_acquired) > 0));
spin_lock(&rwv->rw_priv_lock);
kzt_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);
kzt_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,
kzt_rwlock_lock_and_test(&rwv->rw_priv_lock,
atomic_read(&rwv->rw_release) ==
KZT_RWLOCK_RELEASE_READERS));
spin_lock(&rwv->rw_priv_lock);
atomic_inc(&rwv->rw_completed);
atomic_dec(&rwv->rw_acquired);
kzt_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
kzt_rwlock_test1(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_RWLOCK_TEST_COUNT];
rw_thr_t rwt[KZT_RWLOCK_TEST_COUNT];
rw_priv_t rwv;
/* Initialize private data
* including the rwlock */
kzt_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 < KZT_RWLOCK_TEST_COUNT; i++) {
rwt[i].rwt_rwp = &rwv;
rwt[i].rwt_id = i;
rwt[i].rwt_name = KZT_RWLOCK_TEST1_NAME;
rwt[i].rwt_rc = 0;
/* The first thread will be a writer */
if (i == 0) {
pids[i] = kernel_thread(kzt_rwlock_test1_writer_thread,
&rwt[i], 0);
} else {
pids[i] = kernel_thread(kzt_rwlock_test1_reader_thread,
&rwt[i], 0);
}
if (pids[i] >= 0) {
count++;
}
}
/* Once the writer has the lock, release the readers */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock, atomic_read(&rwv.rw_acquired) <= 0)) {
kzt_rwlock_sleep(1 * HZ);
}
wake_up_interruptible(&rwv.rw_waitq);
/* Ensure that there is only 1 writer and all readers are waiting */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) != 1 ||
atomic_read(&rwv.rw_waiters) !=
KZT_RWLOCK_TEST_COUNT - 1)) {
kzt_rwlock_sleep(1 * HZ);
}
/* Relase the writer */
spin_lock(&rwv.rw_priv_lock);
atomic_set(&rwv.rw_release, KZT_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 (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) <= 1)) {
kzt_rwlock_sleep(1 * HZ);
}
/* Release the readers */
spin_lock(&rwv.rw_priv_lock);
atomic_set(&rwv.rw_release, KZT_RWLOCK_RELEASE_READERS);
spin_unlock(&rwv.rw_priv_lock);
wake_up_interruptible(&rwv.rw_waitq);
/* Wait for the test to complete */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) != 0 ||
atomic_read(&rwv.rw_waiters) != 0)) {
kzt_rwlock_sleep(1 * HZ);
}
rw_destroy(&rwv.rwl);
return rc;
}
int
kzt_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 == KZT_RWLOCK_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d",
KZT_RWLOCK_TEST_NAME, rwt->rwt_id);
daemonize(name);
get_random_bytes((void *)&rnd, 1);
kzt_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);
kzt_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,
kzt_rwlock_lock_and_test(&rwv->rw_priv_lock,
atomic_read(&rwv->rw_release) ==
KZT_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);
kzt_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 */
kzt_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);
kzt_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
kzt_rwlock_test2(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_RWLOCK_TEST_COUNT];
rw_thr_t rwt[KZT_RWLOCK_TEST_COUNT];
rw_priv_t rwv;
/* Initialize private data
* including the rwlock */
kzt_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 < KZT_RWLOCK_TEST_COUNT; i++) {
rwt[i].rwt_rwp = &rwv;
rwt[i].rwt_id = i;
rwt[i].rwt_name = KZT_RWLOCK_TEST2_NAME;
rwt[i].rwt_rc = 0;
/* The first thread will be a writer */
pids[i] = kernel_thread(kzt_rwlock_test2_writer_thread,
&rwt[i], 0);
if (pids[i] >= 0) {
count++;
}
}
/* Wait for writers to get queued up */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_waiters) < KZT_RWLOCK_TEST_COUNT)) {
kzt_rwlock_sleep(1 * HZ);
}
/* Relase the writers */
spin_lock(&rwv.rw_priv_lock);
atomic_set(&rwv.rw_release, KZT_RWLOCK_RELEASE_WRITERS);
spin_unlock(&rwv.rw_priv_lock);
wake_up_interruptible(&rwv.rw_waitq);
/* Wait for the test to complete */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) != 0 ||
atomic_read(&rwv.rw_waiters) != 0)) {
kzt_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 < KZT_RWLOCK_TEST_COUNT; i++) {
if (rwt[i].rwt_rc) {
rc++;
}
}
rw_destroy(&rwv.rwl);
return rc;
}
static int
kzt_rwlock_test3(struct file *file, void *arg)
{
kthread_t *owner;
rw_priv_t rwv;
int rc = 0;
/* Initialize private data
* including the rwlock */
kzt_init_rw_priv(&rwv, file);
/* Take the rwlock for writing */
rw_enter(&rwv.rwl, RW_WRITER);
owner = rw_owner(&rwv.rwl);
if (current != owner) {
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_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
kzt_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 == KZT_RWLOCK_TEST_MAGIC);
snprintf(name, sizeof(name), "%s%d",
KZT_RWLOCK_TEST_NAME, rwt->rwt_id);
daemonize(name);
get_random_bytes((void *)&rnd, 1);
kzt_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,
kzt_rwlock_lock_and_test(&rwv->rw_priv_lock,
atomic_read(&rwv->rw_acquired) > 0));
spin_lock(&rwv->rw_priv_lock);
kzt_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);
kzt_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);
kzt_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);
kzt_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
kzt_rwlock_test4(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
long pids[KZT_RWLOCK_TEST_COUNT];
rw_thr_t rwt[KZT_RWLOCK_TEST_COUNT];
rw_priv_t rwv;
/* Initialize private data
* including the rwlock */
kzt_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 < KZT_RWLOCK_TEST_COUNT; i++) {
rwt[i].rwt_rwp = &rwv;
rwt[i].rwt_id = i;
rwt[i].rwt_name = KZT_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(kzt_rwlock_test1_writer_thread,
&rwt[i], 0);
} else {
pids[i] = kernel_thread(kzt_rwlock_test4_reader_thread,
&rwt[i], 0);
}
if (pids[i] >= 0) {
count++;
}
}
/* Once the writer has the lock, release the readers */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) <= 0)) {
kzt_rwlock_sleep(1 * HZ);
}
wake_up_interruptible(&rwv.rw_waitq);
/* Make sure that the reader threads complete */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_completed) != KZT_RWLOCK_TEST_COUNT - 1)) {
kzt_rwlock_sleep(1 * HZ);
}
/* Release the writer */
spin_lock(&rwv.rw_priv_lock);
atomic_set(&rwv.rw_release, KZT_RWLOCK_RELEASE_WRITERS);
spin_unlock(&rwv.rw_priv_lock);
wake_up_interruptible(&rwv.rw_waitq);
/* Wait for the test to complete */
while (kzt_rwlock_lock_and_test(&rwv.rw_priv_lock,
atomic_read(&rwv.rw_acquired) != 0 ||
atomic_read(&rwv.rw_waiters) != 0)) {
kzt_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 < KZT_RWLOCK_TEST_COUNT; i++) {
if (rwt[i].rwt_rc) {
rc++;
}
}
rw_destroy(&rwv.rwl);
return rc;
}
static int
kzt_rwlock_test5(struct file *file, void *arg)
{
kthread_t *owner;
rw_priv_t rwv;
int rc = 0;
/* Initialize private data
* including the rwlock */
kzt_init_rw_priv(&rwv, file);
/* Take the rwlock for writing */
rw_enter(&rwv.rwl, RW_WRITER);
owner = rw_owner(&rwv.rwl);
if (current != owner) {
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_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
kzt_rwlock_test6(struct file *file, void *arg)
{
kthread_t *owner;
rw_priv_t rwv;
int rc = 0;
/* Initialize private data
* including the rwlock */
kzt_init_rw_priv(&rwv, file);
/* Take the rwlock for reading */
rw_enter(&rwv.rwl, RW_READER);
owner = rw_owner(&rwv.rwl);
if (owner) {
kzt_vprint(file, KZT_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) {
kzt_vprint(file, KZT_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;
}
kzt_subsystem_t *
kzt_rwlock_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_RWLOCK_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_RWLOCK_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_RWLOCK;
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST1_NAME, KZT_RWLOCK_TEST1_DESC,
KZT_RWLOCK_TEST1_ID, kzt_rwlock_test1);
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST2_NAME, KZT_RWLOCK_TEST2_DESC,
KZT_RWLOCK_TEST2_ID, kzt_rwlock_test2);
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST3_NAME, KZT_RWLOCK_TEST3_DESC,
KZT_RWLOCK_TEST3_ID, kzt_rwlock_test3);
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST4_NAME, KZT_RWLOCK_TEST4_DESC,
KZT_RWLOCK_TEST4_ID, kzt_rwlock_test4);
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST5_NAME, KZT_RWLOCK_TEST5_DESC,
KZT_RWLOCK_TEST5_ID, kzt_rwlock_test5);
KZT_TEST_INIT(sub, KZT_RWLOCK_TEST6_NAME, KZT_RWLOCK_TEST6_DESC,
KZT_RWLOCK_TEST6_ID, kzt_rwlock_test6);
return sub;
}
void
kzt_rwlock_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST6_ID);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST5_ID);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST4_ID);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST3_ID);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST2_ID);
KZT_TEST_FINI(sub, KZT_RWLOCK_TEST1_ID);
kfree(sub);
}
int
kzt_rwlock_id(void) {
return KZT_SUBSYSTEM_RWLOCK;
}

238
src/splat/splat-taskq.c Normal file
View File

@ -0,0 +1,238 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_TASKQ 0x0200
#define KZT_TASKQ_NAME "taskq"
#define KZT_TASKQ_DESC "Kernel Task Queue Tests"
#define KZT_TASKQ_TEST1_ID 0x0201
#define KZT_TASKQ_TEST1_NAME "single"
#define KZT_TASKQ_TEST1_DESC "Single task queue, single task"
#define KZT_TASKQ_TEST2_ID 0x0202
#define KZT_TASKQ_TEST2_NAME "multiple"
#define KZT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
typedef struct kzt_taskq_arg {
int flag;
int id;
struct file *file;
const char *name;
} kzt_taskq_arg_t;
/* Validation Test 1 - Create a taskq, queue a task, wait until
* task completes, ensure task ran properly, cleanup taskq,
*/
static void
kzt_taskq_test1_func(void *arg)
{
kzt_taskq_arg_t *tq_arg = (kzt_taskq_arg_t *)arg;
ASSERT(tq_arg);
kzt_vprint(tq_arg->file, KZT_TASKQ_TEST1_NAME,
"Taskq '%s' function '%s' setting flag\n",
tq_arg->name, sym2str(kzt_taskq_test1_func));
tq_arg->flag = 1;
}
static int
kzt_taskq_test1(struct file *file, void *arg)
{
taskq_t *tq;
taskqid_t id;
kzt_taskq_arg_t tq_arg;
kzt_vprint(file, KZT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
KZT_TASKQ_TEST1_NAME);
if ((tq = taskq_create(KZT_TASKQ_TEST1_NAME, 1, 0, 0, 0, 0)) == NULL) {
kzt_vprint(file, KZT_TASKQ_TEST1_NAME,
"Taskq '%s' create failed\n",
KZT_TASKQ_TEST1_NAME);
return -EINVAL;
}
tq_arg.flag = 0;
tq_arg.id = 0;
tq_arg.file = file;
tq_arg.name = KZT_TASKQ_TEST1_NAME;
kzt_vprint(file, KZT_TASKQ_TEST1_NAME,
"Taskq '%s' function '%s' dispatching\n",
tq_arg.name, sym2str(kzt_taskq_test1_func));
if ((id = taskq_dispatch(tq, kzt_taskq_test1_func, &tq_arg, 0)) == 0) {
kzt_vprint(file, KZT_TASKQ_TEST1_NAME,
"Taskq '%s' function '%s' dispatch failed\n",
tq_arg.name, sym2str(kzt_taskq_test1_func));
taskq_destory(tq);
return -EINVAL;
}
kzt_vprint(file, KZT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
tq_arg.name);
taskq_wait(tq);
kzt_vprint(file, KZT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
tq_arg.name);
taskq_destory(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
kzt_taskq_test2_func1(void *arg)
{
kzt_taskq_arg_t *tq_arg = (kzt_taskq_arg_t *)arg;
ASSERT(tq_arg);
kzt_vprint(tq_arg->file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
tq_arg->name, tq_arg->id,
sym2str(kzt_taskq_test2_func1),
tq_arg->flag * 2, tq_arg->flag);
tq_arg->flag *= 2;
}
static void
kzt_taskq_test2_func2(void *arg)
{
kzt_taskq_arg_t *tq_arg = (kzt_taskq_arg_t *)arg;
ASSERT(tq_arg);
kzt_vprint(tq_arg->file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
tq_arg->name, tq_arg->id,
sym2str(kzt_taskq_test2_func2),
tq_arg->flag + 1, tq_arg->flag);
tq_arg->flag += 1;
}
#define TEST2_TASKQS 8
static int
kzt_taskq_test2(struct file *file, void *arg) {
taskq_t *tq[TEST2_TASKQS] = { NULL };
taskqid_t id;
kzt_taskq_arg_t tq_args[TEST2_TASKQS];
int i, rc = 0;
for (i = 0; i < TEST2_TASKQS; i++) {
kzt_vprint(file, KZT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
"creating\n", KZT_TASKQ_TEST2_NAME, i);
if ((tq[i] = taskq_create(KZT_TASKQ_TEST2_NAME,
1, 0, 0, 0, 0)) == NULL) {
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' create failed\n",
KZT_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 = KZT_TASKQ_TEST2_NAME;
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' dispatching\n",
tq_args[i].name, tq_args[i].id,
sym2str(kzt_taskq_test2_func1));
if ((id = taskq_dispatch(
tq[i], kzt_taskq_test2_func1, &tq_args[i], 0)) == 0) {
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' dispatch "
"failed\n", tq_args[i].name, tq_args[i].id,
sym2str(kzt_taskq_test2_func1));
rc = -EINVAL;
break;
}
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' dispatching\n",
tq_args[i].name, tq_args[i].id,
sym2str(kzt_taskq_test2_func2));
if ((id = taskq_dispatch(
tq[i], kzt_taskq_test2_func2, &tq_args[i], 0)) == 0) {
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' function '%s' dispatch failed\n",
tq_args[i].name, tq_args[i].id,
sym2str(kzt_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) {
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d' waiting\n",
tq_args[i].name, tq_args[i].id);
taskq_wait(tq[i]);
kzt_vprint(file, KZT_TASKQ_TEST2_NAME,
"Taskq '%s/%d; destroying\n",
tq_args[i].name, tq_args[i].id);
taskq_destory(tq[i]);
if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
kzt_vprint(file, KZT_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 {
kzt_vprint(file, KZT_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;
}
kzt_subsystem_t *
kzt_taskq_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_TASKQ_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_TASKQ_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_TASKQ;
KZT_TEST_INIT(sub, KZT_TASKQ_TEST1_NAME, KZT_TASKQ_TEST1_DESC,
KZT_TASKQ_TEST1_ID, kzt_taskq_test1);
KZT_TEST_INIT(sub, KZT_TASKQ_TEST2_NAME, KZT_TASKQ_TEST2_DESC,
KZT_TASKQ_TEST2_ID, kzt_taskq_test2);
return sub;
}
void
kzt_taskq_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_TASKQ_TEST2_ID);
KZT_TEST_FINI(sub, KZT_TASKQ_TEST1_ID);
kfree(sub);
}
int
kzt_taskq_id(void) {
return KZT_SUBSYSTEM_TASKQ;
}

116
src/splat/splat-thread.c Normal file
View File

@ -0,0 +1,116 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_THREAD 0x0600
#define KZT_THREAD_NAME "thread"
#define KZT_THREAD_DESC "Kernel Thread Tests"
#define KZT_THREAD_TEST1_ID 0x0601
#define KZT_THREAD_TEST1_NAME "create"
#define KZT_THREAD_TEST1_DESC "Validate thread creation and destruction"
#define KZT_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 void
kzt_thread_work(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
spin_lock(&tp->tp_lock);
ASSERT(tp->tp_magic == KZT_THREAD_TEST_MAGIC);
tp->tp_rc = 1;
spin_unlock(&tp->tp_lock);
wake_up(&tp->tp_waitq);
thread_exit();
}
static int
kzt_thread_test1(struct file *file, void *arg)
{
thread_priv_t tp;
DEFINE_WAIT(wait);
kthread_t *thr;
int rc = 0;
tp.tp_magic = KZT_THREAD_TEST_MAGIC;
tp.tp_file = file;
spin_lock_init(&tp.tp_lock);
init_waitqueue_head(&tp.tp_waitq);
tp.tp_rc = 0;
spin_lock(&tp.tp_lock);
thr = (kthread_t *)thread_create(NULL, 0, kzt_thread_work, &tp, 0,
(proc_t *) &p0, TS_RUN, minclsyspri);
/* Must never fail under Solaris, but we check anyway so we can
* report an error when this impossible thing happens */
if (thr == NULL) {
rc = -ESRCH;
goto out;
}
for (;;) {
prepare_to_wait(&tp.tp_waitq, &wait, TASK_UNINTERRUPTIBLE);
if (tp.tp_rc)
break;
spin_unlock(&tp.tp_lock);
schedule();
spin_lock(&tp.tp_lock);
}
kzt_vprint(file, KZT_THREAD_TEST1_NAME, "%s",
"Thread successfully started and exited cleanly\n");
out:
spin_unlock(&tp.tp_lock);
return rc;
}
kzt_subsystem_t *
kzt_thread_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_THREAD_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_THREAD_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_THREAD;
KZT_TEST_INIT(sub, KZT_THREAD_TEST1_NAME, KZT_THREAD_TEST1_DESC,
KZT_THREAD_TEST1_ID, kzt_thread_test1);
return sub;
}
void
kzt_thread_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_THREAD_TEST1_ID);
kfree(sub);
}
int
kzt_thread_id(void) {
return KZT_SUBSYSTEM_THREAD;
}

90
src/splat/splat-time.c Normal file
View File

@ -0,0 +1,90 @@
#include <sys/zfs_context.h>
#include <sys/splat-ctl.h>
#define KZT_SUBSYSTEM_TIME 0x0800
#define KZT_TIME_NAME "time"
#define KZT_TIME_DESC "Kernel Time Tests"
#define KZT_TIME_TEST1_ID 0x0801
#define KZT_TIME_TEST1_NAME "time1"
#define KZT_TIME_TEST1_DESC "HZ Test"
#define KZT_TIME_TEST2_ID 0x0802
#define KZT_TIME_TEST2_NAME "time2"
#define KZT_TIME_TEST2_DESC "Monotonic Test"
static int
kzt_time_test1(struct file *file, void *arg)
{
int myhz = hz;
kzt_vprint(file, KZT_TIME_TEST1_NAME, "hz is %d\n", myhz);
return 0;
}
static int
kzt_time_test2(struct file *file, void *arg)
{
hrtime_t tm1, tm2;
int i;
tm1 = gethrtime();
kzt_vprint(file, KZT_TIME_TEST2_NAME, "time is %lld\n", tm1);
for(i = 0; i < 100; i++) {
tm2 = gethrtime();
kzt_vprint(file, KZT_TIME_TEST2_NAME, "time is %lld\n", tm2);
if(tm1 > tm2) {
kzt_print(file, "%s: gethrtime() is not giving monotonically increasing values\n", KZT_TIME_TEST2_NAME);
return 1;
}
tm1 = tm2;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
}
return 0;
}
kzt_subsystem_t *
kzt_time_init(void)
{
kzt_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, KZT_TIME_NAME, KZT_NAME_SIZE);
strncpy(sub->desc.desc, KZT_TIME_DESC, KZT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = KZT_SUBSYSTEM_TIME;
KZT_TEST_INIT(sub, KZT_TIME_TEST1_NAME, KZT_TIME_TEST1_DESC,
KZT_TIME_TEST1_ID, kzt_time_test1);
KZT_TEST_INIT(sub, KZT_TIME_TEST2_NAME, KZT_TIME_TEST2_DESC,
KZT_TIME_TEST2_ID, kzt_time_test2);
return sub;
}
void
kzt_time_fini(kzt_subsystem_t *sub)
{
ASSERT(sub);
KZT_TEST_FINI(sub, KZT_TIME_TEST2_ID);
KZT_TEST_FINI(sub, KZT_TIME_TEST1_ID);
kfree(sub);
}
int
kzt_time_id(void)
{
return KZT_SUBSYSTEM_TIME;
}