mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 19:04:45 +03:00
Rename modules to module and update references
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
subdir-m += spl
|
||||
subdir-m += splat
|
||||
|
||||
all:
|
||||
$(MAKE) -C @LINUX@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ modules
|
||||
|
||||
install uninstall clean distclean maintainer-clean distdir:
|
||||
$(MAKE) -C @LINUX@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ $@
|
||||
|
||||
check:
|
||||
@@ -0,0 +1,51 @@
|
||||
# Makefile.in for spl kernel module
|
||||
|
||||
MODULES := spl
|
||||
DISTFILES = Makefile.in \
|
||||
spl-kmem.c spl-rwlock.c spl-taskq.c \
|
||||
spl-thread.c spl-generic.c
|
||||
EXTRA_CFLAGS = @KERNELCPPFLAGS@
|
||||
|
||||
# Solaris porting layer module
|
||||
obj-m := spl.o
|
||||
|
||||
spl-objs += spl-debug.o
|
||||
spl-objs += spl-proc.o
|
||||
spl-objs += spl-kmem.o
|
||||
spl-objs += spl-thread.o
|
||||
spl-objs += spl-taskq.o
|
||||
spl-objs += spl-rwlock.o
|
||||
spl-objs += spl-vnode.o
|
||||
spl-objs += spl-err.o
|
||||
spl-objs += spl-time.o
|
||||
spl-objs += spl-kobj.o
|
||||
spl-objs += spl-module.o
|
||||
spl-objs += spl-generic.o
|
||||
spl-objs += spl-atomic.o
|
||||
spl-objs += spl-mutex.o
|
||||
spl-objs += spl-kstat.o
|
||||
spl-objs += spl-condvar.o
|
||||
|
||||
splmodule := spl.ko
|
||||
splmoduledir := @kmoduledir@/kernel/lib/
|
||||
|
||||
install:
|
||||
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)
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/atomic.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_ATOMIC
|
||||
|
||||
/* Global atomic lock declarations */
|
||||
spinlock_t atomic64_lock = SPIN_LOCK_UNLOCKED;
|
||||
spinlock_t atomic32_lock = SPIN_LOCK_UNLOCKED;
|
||||
|
||||
EXPORT_SYMBOL(atomic64_lock);
|
||||
EXPORT_SYMBOL(atomic32_lock);
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/condvar.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_CONDVAR
|
||||
|
||||
void
|
||||
__cv_init(kcondvar_t *cvp, char *name, kcv_type_t type, void *arg)
|
||||
{
|
||||
int flags = KM_SLEEP;
|
||||
|
||||
ENTRY;
|
||||
ASSERT(cvp);
|
||||
ASSERT(name);
|
||||
ASSERT(type == CV_DEFAULT);
|
||||
ASSERT(arg == NULL);
|
||||
|
||||
cvp->cv_magic = CV_MAGIC;
|
||||
init_waitqueue_head(&cvp->cv_event);
|
||||
spin_lock_init(&cvp->cv_lock);
|
||||
atomic_set(&cvp->cv_waiters, 0);
|
||||
cvp->cv_mutex = NULL;
|
||||
cvp->cv_name = NULL;
|
||||
cvp->cv_name_size = strlen(name) + 1;
|
||||
|
||||
/* We may be called when there is a non-zero preempt_count or
|
||||
* interrupts are disabled is which case we must not sleep.
|
||||
*/
|
||||
if (current_thread_info()->preempt_count || irqs_disabled())
|
||||
flags = KM_NOSLEEP;
|
||||
|
||||
cvp->cv_name = kmem_alloc(cvp->cv_name_size, flags);
|
||||
if (cvp->cv_name)
|
||||
strcpy(cvp->cv_name, name);
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_init);
|
||||
|
||||
void
|
||||
__cv_destroy(kcondvar_t *cvp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(cvp);
|
||||
ASSERT(cvp->cv_magic == CV_MAGIC);
|
||||
spin_lock(&cvp->cv_lock);
|
||||
ASSERT(atomic_read(&cvp->cv_waiters) == 0);
|
||||
ASSERT(!waitqueue_active(&cvp->cv_event));
|
||||
|
||||
if (cvp->cv_name)
|
||||
kmem_free(cvp->cv_name, cvp->cv_name_size);
|
||||
|
||||
spin_unlock(&cvp->cv_lock);
|
||||
memset(cvp, CV_POISON, sizeof(*cvp));
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_destroy);
|
||||
|
||||
void
|
||||
__cv_wait(kcondvar_t *cvp, kmutex_t *mp)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
ENTRY;
|
||||
|
||||
ASSERT(cvp);
|
||||
ASSERT(mp);
|
||||
ASSERT(cvp->cv_magic == CV_MAGIC);
|
||||
spin_lock(&cvp->cv_lock);
|
||||
ASSERT(mutex_owned(mp));
|
||||
|
||||
if (cvp->cv_mutex == NULL)
|
||||
cvp->cv_mutex = mp;
|
||||
|
||||
/* Ensure the same mutex is used by all callers */
|
||||
ASSERT(cvp->cv_mutex == mp);
|
||||
spin_unlock(&cvp->cv_lock);
|
||||
|
||||
prepare_to_wait_exclusive(&cvp->cv_event, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
atomic_inc(&cvp->cv_waiters);
|
||||
|
||||
/* 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(mp);
|
||||
schedule();
|
||||
mutex_enter(mp);
|
||||
|
||||
atomic_dec(&cvp->cv_waiters);
|
||||
finish_wait(&cvp->cv_event, &wait);
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_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.
|
||||
*/
|
||||
clock_t
|
||||
__cv_timedwait(kcondvar_t *cvp, kmutex_t *mp, clock_t expire_time)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
clock_t time_left;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(cvp);
|
||||
ASSERT(mp);
|
||||
ASSERT(cvp->cv_magic == CV_MAGIC);
|
||||
spin_lock(&cvp->cv_lock);
|
||||
ASSERT(mutex_owned(mp));
|
||||
|
||||
if (cvp->cv_mutex == NULL)
|
||||
cvp->cv_mutex = mp;
|
||||
|
||||
/* Ensure the same mutex is used by all callers */
|
||||
ASSERT(cvp->cv_mutex == mp);
|
||||
spin_unlock(&cvp->cv_lock);
|
||||
|
||||
/* XXX - Does not handle jiffie wrap properly */
|
||||
time_left = expire_time - jiffies;
|
||||
if (time_left <= 0)
|
||||
RETURN(-1);
|
||||
|
||||
prepare_to_wait_exclusive(&cvp->cv_event, &wait,
|
||||
TASK_UNINTERRUPTIBLE);
|
||||
atomic_inc(&cvp->cv_waiters);
|
||||
|
||||
/* 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(mp);
|
||||
time_left = schedule_timeout(time_left);
|
||||
mutex_enter(mp);
|
||||
|
||||
atomic_dec(&cvp->cv_waiters);
|
||||
finish_wait(&cvp->cv_event, &wait);
|
||||
|
||||
RETURN(time_left > 0 ? time_left : -1);
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_timedwait);
|
||||
|
||||
void
|
||||
__cv_signal(kcondvar_t *cvp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(cvp);
|
||||
ASSERT(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);
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_signal);
|
||||
|
||||
void
|
||||
__cv_broadcast(kcondvar_t *cvp)
|
||||
{
|
||||
ASSERT(cvp);
|
||||
ASSERT(cvp->cv_magic == CV_MAGIC);
|
||||
ENTRY;
|
||||
|
||||
/* 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);
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__cv_broadcast);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/cmn_err.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_GENERIC
|
||||
|
||||
#ifndef NDEBUG
|
||||
static char ce_prefix[CE_IGNORE][10] = { "", "NOTICE: ", "WARNING: ", "" };
|
||||
static char ce_suffix[CE_IGNORE][2] = { "", "\n", "\n", "" };
|
||||
#endif
|
||||
|
||||
void
|
||||
vpanic(const char *fmt, va_list ap)
|
||||
{
|
||||
char msg[MAXMSGLEN];
|
||||
|
||||
vsnprintf(msg, MAXMSGLEN - 1, fmt, ap);
|
||||
panic(msg);
|
||||
} /* vpanic() */
|
||||
EXPORT_SYMBOL(vpanic);
|
||||
|
||||
void
|
||||
cmn_err(int ce, const char *fmt, ...)
|
||||
{
|
||||
char msg[MAXMSGLEN];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, MAXMSGLEN - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
CERROR("%s", msg);
|
||||
} /* cmn_err() */
|
||||
EXPORT_SYMBOL(cmn_err);
|
||||
|
||||
void
|
||||
vcmn_err(int ce, const char *fmt, va_list ap)
|
||||
{
|
||||
char msg[MAXMSGLEN];
|
||||
|
||||
if (ce == CE_PANIC)
|
||||
vpanic(fmt, ap);
|
||||
|
||||
if (ce != CE_NOTE) { /* suppress noise in stress testing */
|
||||
vsnprintf(msg, MAXMSGLEN - 1, fmt, ap);
|
||||
CERROR("%s%s%s", ce_prefix[ce], msg, ce_suffix[ce]);
|
||||
}
|
||||
} /* vcmn_err() */
|
||||
EXPORT_SYMBOL(vcmn_err);
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/vmsystm.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/taskq.h>
|
||||
#include <sys/debug.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/kstat.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_GENERIC
|
||||
|
||||
char spl_version[16] = "SPL v" VERSION;
|
||||
|
||||
long spl_hostid = 0;
|
||||
EXPORT_SYMBOL(spl_hostid);
|
||||
|
||||
char hw_serial[11] = "<none>";
|
||||
EXPORT_SYMBOL(hw_serial);
|
||||
|
||||
int p0 = 0;
|
||||
EXPORT_SYMBOL(p0);
|
||||
|
||||
vmem_t *zio_alloc_arena = NULL;
|
||||
EXPORT_SYMBOL(zio_alloc_arena);
|
||||
|
||||
int
|
||||
highbit(unsigned long i)
|
||||
{
|
||||
register int h = 1;
|
||||
ENTRY;
|
||||
|
||||
if (i == 0)
|
||||
RETURN(0);
|
||||
#if BITS_PER_LONG == 64
|
||||
if (i & 0xffffffff00000000ul) {
|
||||
h += 32; i >>= 32;
|
||||
}
|
||||
#endif
|
||||
if (i & 0xffff0000) {
|
||||
h += 16; i >>= 16;
|
||||
}
|
||||
if (i & 0xff00) {
|
||||
h += 8; i >>= 8;
|
||||
}
|
||||
if (i & 0xf0) {
|
||||
h += 4; i >>= 4;
|
||||
}
|
||||
if (i & 0xc) {
|
||||
h += 2; i >>= 2;
|
||||
}
|
||||
if (i & 0x2) {
|
||||
h += 1;
|
||||
}
|
||||
RETURN(h);
|
||||
}
|
||||
EXPORT_SYMBOL(highbit);
|
||||
|
||||
/*
|
||||
* Implementation of 64 bit division for 32-bit machines.
|
||||
*/
|
||||
#if BITS_PER_LONG == 32
|
||||
uint64_t __udivdi3(uint64_t dividend, uint64_t divisor)
|
||||
{
|
||||
#ifdef HAVE_DIV64_64
|
||||
return div64_64(dividend, divisor);
|
||||
#else
|
||||
/* Taken from a 2.6.24 kernel. */
|
||||
uint32_t high, d;
|
||||
|
||||
high = divisor >> 32;
|
||||
if (high) {
|
||||
unsigned int shift = fls(high);
|
||||
|
||||
d = divisor >> shift;
|
||||
dividend >>= shift;
|
||||
} else
|
||||
d = divisor;
|
||||
|
||||
do_div(dividend, d);
|
||||
|
||||
return dividend;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(__udivdi3);
|
||||
|
||||
/*
|
||||
* Implementation of 64 bit modulo for 32-bit machines.
|
||||
*/
|
||||
uint64_t __umoddi3(uint64_t dividend, uint64_t divisor)
|
||||
{
|
||||
return dividend - divisor * (dividend / divisor);
|
||||
}
|
||||
EXPORT_SYMBOL(__umoddi3);
|
||||
#endif
|
||||
|
||||
/* NOTE: The strtoxx behavior is solely based on my reading of the Solaris
|
||||
* ddi_strtol(9F) man page. I have not verified the behavior of these
|
||||
* functions against their Solaris counterparts. It is possible that I
|
||||
* may have misinterpretted the man page or the man page is incorrect.
|
||||
*/
|
||||
int ddi_strtoul(const char *, char **, int, unsigned long *);
|
||||
int ddi_strtol(const char *, char **, int, long *);
|
||||
int ddi_strtoull(const char *, char **, int, unsigned long long *);
|
||||
int ddi_strtoll(const char *, char **, int, long long *);
|
||||
|
||||
#define define_ddi_strtoux(type, valtype) \
|
||||
int ddi_strtou##type(const char *str, char **endptr, \
|
||||
int base, valtype *result) \
|
||||
{ \
|
||||
valtype last_value, value = 0; \
|
||||
char *ptr = (char *)str; \
|
||||
int flag = 1, digit; \
|
||||
\
|
||||
if (strlen(ptr) == 0) \
|
||||
return EINVAL; \
|
||||
\
|
||||
/* Auto-detect base based on prefix */ \
|
||||
if (!base) { \
|
||||
if (str[0] == '0') { \
|
||||
if (tolower(str[1])=='x' && isxdigit(str[2])) { \
|
||||
base = 16; /* hex */ \
|
||||
ptr += 2; \
|
||||
} else if (str[1] >= '0' && str[1] < 8) { \
|
||||
base = 8; /* octal */ \
|
||||
ptr += 1; \
|
||||
} else { \
|
||||
return EINVAL; \
|
||||
} \
|
||||
} else { \
|
||||
base = 10; /* decimal */ \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
while (1) { \
|
||||
if (isdigit(*ptr)) \
|
||||
digit = *ptr - '0'; \
|
||||
else if (isalpha(*ptr)) \
|
||||
digit = tolower(*ptr) - 'a' + 10; \
|
||||
else \
|
||||
break; \
|
||||
\
|
||||
if (digit >= base) \
|
||||
break; \
|
||||
\
|
||||
last_value = value; \
|
||||
value = value * base + digit; \
|
||||
if (last_value > value) /* Overflow */ \
|
||||
return ERANGE; \
|
||||
\
|
||||
flag = 1; \
|
||||
ptr++; \
|
||||
} \
|
||||
\
|
||||
if (flag) \
|
||||
*result = value; \
|
||||
\
|
||||
if (endptr) \
|
||||
*endptr = (char *)(flag ? ptr : str); \
|
||||
\
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define define_ddi_strtox(type, valtype) \
|
||||
int ddi_strto##type(const char *str, char **endptr, \
|
||||
int base, valtype *result) \
|
||||
{ \
|
||||
int rc; \
|
||||
\
|
||||
if (*str == '-') { \
|
||||
rc = ddi_strtou##type(str + 1, endptr, base, result); \
|
||||
if (!rc) { \
|
||||
if (*endptr == str + 1) \
|
||||
*endptr = (char *)str; \
|
||||
else \
|
||||
*result = -*result; \
|
||||
} \
|
||||
} else { \
|
||||
rc = ddi_strtou##type(str, endptr, base, result); \
|
||||
} \
|
||||
\
|
||||
return rc; \
|
||||
}
|
||||
|
||||
define_ddi_strtoux(l, unsigned long)
|
||||
define_ddi_strtox(l, long)
|
||||
define_ddi_strtoux(ll, unsigned long long)
|
||||
define_ddi_strtox(ll, long long)
|
||||
|
||||
EXPORT_SYMBOL(ddi_strtoul);
|
||||
EXPORT_SYMBOL(ddi_strtol);
|
||||
EXPORT_SYMBOL(ddi_strtoll);
|
||||
EXPORT_SYMBOL(ddi_strtoull);
|
||||
|
||||
struct new_utsname *__utsname(void)
|
||||
{
|
||||
#ifdef HAVE_INIT_UTSNAME
|
||||
return init_utsname();
|
||||
#else
|
||||
return &system_utsname;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(__utsname);
|
||||
|
||||
static int
|
||||
set_hostid(void)
|
||||
{
|
||||
char sh_path[] = "/bin/sh";
|
||||
char *argv[] = { sh_path,
|
||||
"-c",
|
||||
"/usr/bin/hostid >/proc/sys/kernel/spl/hostid",
|
||||
NULL };
|
||||
char *envp[] = { "HOME=/",
|
||||
"TERM=linux",
|
||||
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
||||
NULL };
|
||||
|
||||
/* Doing address resolution in the kernel is tricky and just
|
||||
* not a good idea in general. So to set the proper 'hw_serial'
|
||||
* use the usermodehelper support to ask '/bin/sh' to run
|
||||
* '/usr/bin/hostid' and redirect the result to /proc/sys/spl/hostid
|
||||
* for us to use. It's a horific solution but it will do for now.
|
||||
*/
|
||||
return call_usermodehelper(sh_path, argv, envp, 1);
|
||||
}
|
||||
|
||||
static int __init spl_init(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if ((rc = debug_init()))
|
||||
return rc;
|
||||
|
||||
if ((rc = spl_kmem_init()))
|
||||
GOTO(out , rc);
|
||||
|
||||
if ((rc = spl_mutex_init()))
|
||||
GOTO(out2 , rc);
|
||||
|
||||
if ((rc = spl_taskq_init()))
|
||||
GOTO(out3, rc);
|
||||
|
||||
if ((rc = vn_init()))
|
||||
GOTO(out4, rc);
|
||||
|
||||
if ((rc = proc_init()))
|
||||
GOTO(out5, rc);
|
||||
|
||||
if ((rc = kstat_init()))
|
||||
GOTO(out6, rc);
|
||||
|
||||
if ((rc = set_hostid()))
|
||||
GOTO(out7, rc = -EADDRNOTAVAIL);
|
||||
|
||||
printk("SPL: Loaded Solaris Porting Layer v%s\n", VERSION);
|
||||
RETURN(rc);
|
||||
out7:
|
||||
kstat_fini();
|
||||
out6:
|
||||
proc_fini();
|
||||
out5:
|
||||
vn_fini();
|
||||
out4:
|
||||
spl_taskq_fini();
|
||||
out3:
|
||||
spl_mutex_fini();
|
||||
out2:
|
||||
spl_kmem_fini();
|
||||
out:
|
||||
debug_fini();
|
||||
|
||||
printk("SPL: Failed to Load Solaris Porting Layer v%s, "
|
||||
"rc = %d\n", VERSION, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void spl_fini(void)
|
||||
{
|
||||
ENTRY;
|
||||
|
||||
printk("SPL: Unloaded Solaris Porting Layer v%s\n", VERSION);
|
||||
kstat_fini();
|
||||
proc_fini();
|
||||
vn_fini();
|
||||
spl_taskq_fini();
|
||||
spl_mutex_fini();
|
||||
spl_kmem_fini();
|
||||
debug_fini();
|
||||
}
|
||||
|
||||
module_init(spl_init);
|
||||
module_exit(spl_fini);
|
||||
|
||||
MODULE_AUTHOR("Lawrence Livermore National Labs");
|
||||
MODULE_DESCRIPTION("Solaris Porting Layer");
|
||||
MODULE_LICENSE("GPL");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/kobj.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_KOBJ
|
||||
|
||||
struct _buf *
|
||||
kobj_open_file(const char *name)
|
||||
{
|
||||
struct _buf *file;
|
||||
vnode_t *vp;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
file = kmalloc(sizeof(_buf_t), GFP_KERNEL);
|
||||
if (file == NULL)
|
||||
RETURN((_buf_t *)-1UL);
|
||||
|
||||
if ((rc = vn_open(name, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) {
|
||||
kfree(file);
|
||||
RETURN((_buf_t *)-1UL);
|
||||
}
|
||||
|
||||
file->vp = vp;
|
||||
|
||||
RETURN(file);
|
||||
} /* kobj_open_file() */
|
||||
EXPORT_SYMBOL(kobj_open_file);
|
||||
|
||||
void
|
||||
kobj_close_file(struct _buf *file)
|
||||
{
|
||||
ENTRY;
|
||||
VOP_CLOSE(file->vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(file->vp);
|
||||
kfree(file);
|
||||
EXIT;
|
||||
} /* kobj_close_file() */
|
||||
EXPORT_SYMBOL(kobj_close_file);
|
||||
|
||||
int
|
||||
kobj_read_file(struct _buf *file, char *buf, ssize_t size, offset_t off)
|
||||
{
|
||||
ENTRY;
|
||||
RETURN(vn_rdwr(UIO_READ, file->vp, buf, size, off,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL));
|
||||
} /* kobj_read_file() */
|
||||
EXPORT_SYMBOL(kobj_read_file);
|
||||
|
||||
int
|
||||
kobj_get_filesize(struct _buf *file, uint64_t *size)
|
||||
{
|
||||
vattr_t vap;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
rc = VOP_GETATTR(file->vp, &vap, 0, 0, NULL);
|
||||
if (rc)
|
||||
RETURN(rc);
|
||||
|
||||
*size = vap.va_size;
|
||||
|
||||
RETURN(rc);
|
||||
} /* kobj_get_filesize() */
|
||||
EXPORT_SYMBOL(kobj_get_filesize);
|
||||
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/kstat.h>
|
||||
|
||||
#ifdef DEBUG_KSTAT
|
||||
|
||||
static spinlock_t kstat_lock;
|
||||
static struct list_head kstat_list;
|
||||
static kid_t kstat_id;
|
||||
|
||||
static void
|
||||
kstat_seq_show_headers(struct seq_file *f)
|
||||
{
|
||||
kstat_t *ksp = (kstat_t *)f->private;
|
||||
ASSERT(ksp->ks_magic == KS_MAGIC);
|
||||
|
||||
seq_printf(f, "%d %d 0x%02x %d %d %lld %lld\n",
|
||||
ksp->ks_kid, ksp->ks_type, ksp->ks_flags,
|
||||
ksp->ks_ndata, (int)ksp->ks_data_size,
|
||||
ksp->ks_crtime, ksp->ks_snaptime);
|
||||
|
||||
switch (ksp->ks_type) {
|
||||
case KSTAT_TYPE_RAW:
|
||||
seq_printf(f, "raw data");
|
||||
break;
|
||||
case KSTAT_TYPE_NAMED:
|
||||
seq_printf(f, "%-31s %-4s %s\n",
|
||||
"name", "type", "data");
|
||||
break;
|
||||
case KSTAT_TYPE_INTR:
|
||||
seq_printf(f, "%-8s %-8s %-8s %-8s %-8s\n",
|
||||
"hard", "soft", "watchdog",
|
||||
"spurious", "multsvc");
|
||||
break;
|
||||
case KSTAT_TYPE_IO:
|
||||
seq_printf(f,
|
||||
"%-8s %-8s %-8s %-8s %-8s %-8s "
|
||||
"%-8s %-8s %-8s %-8s %-8s %-8s\n",
|
||||
"nread", "nwritten", "reads", "writes",
|
||||
"wtime", "wlentime", "wupdate",
|
||||
"rtime", "rlentime", "rupdate",
|
||||
"wcnt", "rcnt");
|
||||
break;
|
||||
case KSTAT_TYPE_TIMER:
|
||||
seq_printf(f,
|
||||
"%-31s %-8s "
|
||||
"%-8s %-8s %-8s %-8s %-8s\n",
|
||||
"name", "events", "elapsed",
|
||||
"min", "max", "start", "stop");
|
||||
break;
|
||||
default:
|
||||
SBUG(); /* Unreachable */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show_raw(struct seq_file *f, unsigned char *p, int l)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
seq_printf(f, "%03x:", i);
|
||||
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (i * 16 + j >= l) {
|
||||
seq_printf(f, "\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
seq_printf(f, " %02x", (unsigned char)p[i * 16 + j]);
|
||||
}
|
||||
seq_printf(f, "\n");
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show_named(struct seq_file *f, kstat_named_t *knp)
|
||||
{
|
||||
seq_printf(f, "%-31s %-4d ", knp->name, knp->data_type);
|
||||
|
||||
switch (knp->data_type) {
|
||||
case KSTAT_DATA_CHAR:
|
||||
knp->value.c[15] = '\0'; /* NULL terminate */
|
||||
seq_printf(f, "%-16s", knp->value.c);
|
||||
break;
|
||||
/* XXX - We need to be more careful able what tokens are
|
||||
* used for each arch, for now this is correct for x86_64.
|
||||
*/
|
||||
case KSTAT_DATA_INT32:
|
||||
seq_printf(f, "%d", knp->value.i32);
|
||||
break;
|
||||
case KSTAT_DATA_UINT32:
|
||||
seq_printf(f, "%u", knp->value.ui32);
|
||||
break;
|
||||
case KSTAT_DATA_INT64:
|
||||
seq_printf(f, "%lld", (signed long long)knp->value.i64);
|
||||
break;
|
||||
case KSTAT_DATA_UINT64:
|
||||
seq_printf(f, "%llu", (unsigned long long)knp->value.ui64);
|
||||
break;
|
||||
case KSTAT_DATA_LONG:
|
||||
seq_printf(f, "%ld", knp->value.l);
|
||||
break;
|
||||
case KSTAT_DATA_ULONG:
|
||||
seq_printf(f, "%lu", knp->value.ul);
|
||||
break;
|
||||
case KSTAT_DATA_STRING:
|
||||
KSTAT_NAMED_STR_PTR(knp)
|
||||
[KSTAT_NAMED_STR_BUFLEN(knp)-1] = '\0';
|
||||
seq_printf(f, "%s", KSTAT_NAMED_STR_PTR(knp));
|
||||
break;
|
||||
default:
|
||||
SBUG(); /* Unreachable */
|
||||
}
|
||||
|
||||
seq_printf(f, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show_intr(struct seq_file *f, kstat_intr_t *kip)
|
||||
{
|
||||
seq_printf(f, "%-8u %-8u %-8u %-8u %-8u\n",
|
||||
kip->intrs[KSTAT_INTR_HARD],
|
||||
kip->intrs[KSTAT_INTR_SOFT],
|
||||
kip->intrs[KSTAT_INTR_WATCHDOG],
|
||||
kip->intrs[KSTAT_INTR_SPURIOUS],
|
||||
kip->intrs[KSTAT_INTR_MULTSVC]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show_io(struct seq_file *f, kstat_io_t *kip)
|
||||
{
|
||||
seq_printf(f,
|
||||
"%-8llu %-8llu %-8u %-8u %-8lld %-8lld "
|
||||
"%-8lld %-8lld %-8lld %-8lld %-8u %-8u\n",
|
||||
kip->nread, kip->nwritten,
|
||||
kip->reads, kip->writes,
|
||||
kip->wtime, kip->wlentime, kip->wlastupdate,
|
||||
kip->rtime, kip->wlentime, kip->rlastupdate,
|
||||
kip->wcnt, kip->rcnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show_timer(struct seq_file *f, kstat_timer_t *ktp)
|
||||
{
|
||||
seq_printf(f,
|
||||
"%-31s %-8llu %-8lld %-8lld %-8lld %-8lld %-8lld\n",
|
||||
ktp->name, ktp->num_events, ktp->elapsed_time,
|
||||
ktp->min_time, ktp->max_time,
|
||||
ktp->start_time, ktp->stop_time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kstat_seq_show(struct seq_file *f, void *p)
|
||||
{
|
||||
kstat_t *ksp = (kstat_t *)f->private;
|
||||
int rc = 0;
|
||||
|
||||
ASSERT(ksp->ks_magic == KS_MAGIC);
|
||||
|
||||
switch (ksp->ks_type) {
|
||||
case KSTAT_TYPE_RAW:
|
||||
ASSERT(ksp->ks_ndata == 1);
|
||||
rc = kstat_seq_show_raw(f, ksp->ks_data,
|
||||
ksp->ks_data_size);
|
||||
break;
|
||||
case KSTAT_TYPE_NAMED:
|
||||
rc = kstat_seq_show_named(f, (kstat_named_t *)p);
|
||||
break;
|
||||
case KSTAT_TYPE_INTR:
|
||||
rc = kstat_seq_show_intr(f, (kstat_intr_t *)p);
|
||||
break;
|
||||
case KSTAT_TYPE_IO:
|
||||
rc = kstat_seq_show_io(f, (kstat_io_t *)p);
|
||||
break;
|
||||
case KSTAT_TYPE_TIMER:
|
||||
rc = kstat_seq_show_timer(f, (kstat_timer_t *)p);
|
||||
break;
|
||||
default:
|
||||
SBUG(); /* Unreachable */
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void *
|
||||
kstat_seq_data_addr(kstat_t *ksp, loff_t n)
|
||||
{
|
||||
void *rc = NULL;
|
||||
ENTRY;
|
||||
|
||||
switch (ksp->ks_type) {
|
||||
case KSTAT_TYPE_RAW:
|
||||
rc = ksp->ks_data;
|
||||
break;
|
||||
case KSTAT_TYPE_NAMED:
|
||||
rc = ksp->ks_data + n * sizeof(kstat_named_t);
|
||||
break;
|
||||
case KSTAT_TYPE_INTR:
|
||||
rc = ksp->ks_data + n * sizeof(kstat_intr_t);
|
||||
break;
|
||||
case KSTAT_TYPE_IO:
|
||||
rc = ksp->ks_data + n * sizeof(kstat_io_t);
|
||||
break;
|
||||
case KSTAT_TYPE_TIMER:
|
||||
rc = ksp->ks_data + n * sizeof(kstat_timer_t);
|
||||
break;
|
||||
default:
|
||||
SBUG(); /* Unreachable */
|
||||
}
|
||||
|
||||
RETURN(rc);
|
||||
}
|
||||
|
||||
static void *
|
||||
kstat_seq_start(struct seq_file *f, loff_t *pos)
|
||||
{
|
||||
loff_t n = *pos;
|
||||
kstat_t *ksp = (kstat_t *)f->private;
|
||||
ASSERT(ksp->ks_magic == KS_MAGIC);
|
||||
ENTRY;
|
||||
|
||||
spin_lock(&ksp->ks_lock);
|
||||
ksp->ks_snaptime = gethrtime();
|
||||
|
||||
if (!n)
|
||||
kstat_seq_show_headers(f);
|
||||
|
||||
if (n >= ksp->ks_ndata)
|
||||
RETURN(NULL);
|
||||
|
||||
RETURN(kstat_seq_data_addr(ksp, n));
|
||||
}
|
||||
|
||||
static void *
|
||||
kstat_seq_next(struct seq_file *f, void *p, loff_t *pos)
|
||||
{
|
||||
kstat_t *ksp = (kstat_t *)f->private;
|
||||
ASSERT(ksp->ks_magic == KS_MAGIC);
|
||||
ENTRY;
|
||||
|
||||
++*pos;
|
||||
if (*pos >= ksp->ks_ndata)
|
||||
RETURN(NULL);
|
||||
|
||||
RETURN(kstat_seq_data_addr(ksp, *pos));
|
||||
}
|
||||
|
||||
static void
|
||||
kstat_seq_stop(struct seq_file *f, void *v)
|
||||
{
|
||||
kstat_t *ksp = (kstat_t *)f->private;
|
||||
ASSERT(ksp->ks_magic == KS_MAGIC);
|
||||
|
||||
spin_unlock(&ksp->ks_lock);
|
||||
}
|
||||
|
||||
static struct seq_operations kstat_seq_ops = {
|
||||
.show = kstat_seq_show,
|
||||
.start = kstat_seq_start,
|
||||
.next = kstat_seq_next,
|
||||
.stop = kstat_seq_stop,
|
||||
};
|
||||
|
||||
static int
|
||||
proc_kstat_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct seq_file *f;
|
||||
int rc;
|
||||
|
||||
rc = seq_open(filp, &kstat_seq_ops);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
f = filp->private_data;
|
||||
f->private = PDE(inode)->data;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations proc_kstat_operations = {
|
||||
.open = proc_kstat_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
kstat_t *ksp;
|
||||
|
||||
ASSERT(ks_module);
|
||||
ASSERT(ks_instance == 0);
|
||||
ASSERT(ks_name);
|
||||
ASSERT(!(ks_flags & KSTAT_FLAG_UNSUPPORTED));
|
||||
|
||||
if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
|
||||
ASSERT(ks_ndata == 1);
|
||||
|
||||
ksp = kmem_zalloc(sizeof(*ksp), KM_SLEEP);
|
||||
if (ksp == NULL)
|
||||
return ksp;
|
||||
|
||||
spin_lock(&kstat_lock);
|
||||
ksp->ks_kid = kstat_id;
|
||||
kstat_id++;
|
||||
spin_unlock(&kstat_lock);
|
||||
|
||||
ksp->ks_magic = KS_MAGIC;
|
||||
spin_lock_init(&ksp->ks_lock);
|
||||
INIT_LIST_HEAD(&ksp->ks_list);
|
||||
|
||||
ksp->ks_crtime = gethrtime();
|
||||
ksp->ks_snaptime = ksp->ks_crtime;
|
||||
strncpy(ksp->ks_module, ks_module, KSTAT_STRLEN);
|
||||
ksp->ks_instance = ks_instance;
|
||||
strncpy(ksp->ks_name, ks_name, KSTAT_STRLEN);
|
||||
strncpy(ksp->ks_class, ks_class, KSTAT_STRLEN);
|
||||
ksp->ks_type = ks_type;
|
||||
ksp->ks_flags = ks_flags;
|
||||
|
||||
switch (ksp->ks_type) {
|
||||
case KSTAT_TYPE_RAW:
|
||||
ksp->ks_ndata = 1;
|
||||
ksp->ks_data_size = ks_ndata;
|
||||
break;
|
||||
case KSTAT_TYPE_NAMED:
|
||||
ksp->ks_ndata = ks_ndata;
|
||||
ksp->ks_data_size = ks_ndata * sizeof(kstat_named_t);
|
||||
break;
|
||||
case KSTAT_TYPE_INTR:
|
||||
ksp->ks_ndata = ks_ndata;
|
||||
ksp->ks_data_size = ks_ndata * sizeof(kstat_intr_t);
|
||||
break;
|
||||
case KSTAT_TYPE_IO:
|
||||
ksp->ks_ndata = ks_ndata;
|
||||
ksp->ks_data_size = ks_ndata * sizeof(kstat_io_t);
|
||||
break;
|
||||
case KSTAT_TYPE_TIMER:
|
||||
ksp->ks_ndata = ks_ndata;
|
||||
ksp->ks_data_size = ks_ndata * sizeof(kstat_timer_t);
|
||||
break;
|
||||
default:
|
||||
SBUG(); /* Unreachable */
|
||||
}
|
||||
|
||||
if (ksp->ks_flags & KSTAT_FLAG_VIRTUAL) {
|
||||
ksp->ks_data = NULL;
|
||||
} else {
|
||||
ksp->ks_data = kmem_alloc(ksp->ks_data_size, KM_SLEEP);
|
||||
if (ksp->ks_data == NULL) {
|
||||
kmem_free(ksp, sizeof(*ksp));
|
||||
ksp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ksp;
|
||||
}
|
||||
EXPORT_SYMBOL(__kstat_create);
|
||||
|
||||
void
|
||||
__kstat_install(kstat_t *ksp)
|
||||
{
|
||||
struct proc_dir_entry *de_module, *de_name;
|
||||
kstat_t *tmp;
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
spin_lock(&kstat_lock);
|
||||
|
||||
/* Item may only be added to the list once */
|
||||
list_for_each_entry(tmp, &kstat_list, ks_list) {
|
||||
if (tmp == ksp) {
|
||||
spin_unlock(&kstat_lock);
|
||||
GOTO(out, rc = -EEXIST);
|
||||
}
|
||||
}
|
||||
|
||||
list_add_tail(&ksp->ks_list, &kstat_list);
|
||||
spin_unlock(&kstat_lock);
|
||||
|
||||
de_module = proc_dir_entry_find(proc_spl_kstat, ksp->ks_module);
|
||||
if (de_module == NULL) {
|
||||
de_module = proc_mkdir(ksp->ks_module, proc_spl_kstat);
|
||||
if (de_module == NULL)
|
||||
GOTO(out, rc = -EUNATCH);
|
||||
}
|
||||
|
||||
de_name = create_proc_entry(ksp->ks_name, 0444, de_module);
|
||||
if (de_name == NULL)
|
||||
GOTO(out, rc = -EUNATCH);
|
||||
|
||||
spin_lock(&ksp->ks_lock);
|
||||
ksp->ks_proc = de_name;
|
||||
de_name->proc_fops = &proc_kstat_operations;
|
||||
de_name->data = (void *)ksp;
|
||||
spin_unlock(&ksp->ks_lock);
|
||||
out:
|
||||
if (rc) {
|
||||
spin_lock(&kstat_lock);
|
||||
list_del_init(&ksp->ks_list);
|
||||
spin_unlock(&kstat_lock);
|
||||
}
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__kstat_install);
|
||||
|
||||
void
|
||||
__kstat_delete(kstat_t *ksp)
|
||||
{
|
||||
struct proc_dir_entry *de_module;
|
||||
|
||||
spin_lock(&kstat_lock);
|
||||
list_del_init(&ksp->ks_list);
|
||||
spin_unlock(&kstat_lock);
|
||||
|
||||
if (ksp->ks_proc) {
|
||||
de_module = ksp->ks_proc->parent;
|
||||
remove_proc_entry(ksp->ks_name, de_module);
|
||||
|
||||
/* Remove top level module directory if it's empty */
|
||||
if (proc_dir_entries(de_module) == 0)
|
||||
remove_proc_entry(de_module->name, de_module->parent);
|
||||
}
|
||||
|
||||
if (!(ksp->ks_flags & KSTAT_FLAG_VIRTUAL))
|
||||
kmem_free(ksp->ks_data, ksp->ks_data_size);
|
||||
|
||||
kmem_free(ksp, sizeof(*ksp));
|
||||
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(__kstat_delete);
|
||||
|
||||
#endif /* DEBUG_KSTAT */
|
||||
|
||||
int
|
||||
kstat_init(void)
|
||||
{
|
||||
ENTRY;
|
||||
#ifdef DEBUG_KSTAT
|
||||
spin_lock_init(&kstat_lock);
|
||||
INIT_LIST_HEAD(&kstat_list);
|
||||
kstat_id = 0;
|
||||
#endif /* DEBUG_KSTAT */
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
void
|
||||
kstat_fini(void)
|
||||
{
|
||||
ENTRY;
|
||||
#ifdef DEBUG_KSTAT
|
||||
ASSERT(list_empty(&kstat_list));
|
||||
#endif /* DEBUG_KSTAT */
|
||||
EXIT;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/sunddi.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_MODULE
|
||||
|
||||
static spinlock_t dev_info_lock = SPIN_LOCK_UNLOCKED;
|
||||
static LIST_HEAD(dev_info_list);
|
||||
|
||||
static struct dev_info *
|
||||
get_dev_info(dev_t dev)
|
||||
{
|
||||
struct dev_info *di;
|
||||
|
||||
spin_lock(&dev_info_lock);
|
||||
|
||||
list_for_each_entry(di, &dev_info_list, di_list)
|
||||
if (di->di_dev == dev)
|
||||
goto out;
|
||||
|
||||
di = NULL;
|
||||
out:
|
||||
spin_unlock(&dev_info_lock);
|
||||
return di;
|
||||
}
|
||||
|
||||
static int
|
||||
mod_generic_ioctl(struct inode *ino, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct dev_info *di;
|
||||
int rc, flags = 0, rvalp = 0;
|
||||
cred_t *cr = NULL;
|
||||
|
||||
di = get_dev_info(MKDEV(imajor(ino), iminor(ino)));
|
||||
if (di == NULL)
|
||||
return EINVAL;
|
||||
|
||||
rc = di->di_ops->devo_cb_ops->cb_ioctl(di->di_dev,
|
||||
(int)cmd,(intptr_t)arg,
|
||||
flags, cr, &rvalp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
__ddi_create_minor_node(dev_info_t *di, char *name, int spec_type,
|
||||
minor_t minor_num, char *node_type,
|
||||
int flags, struct module *mod)
|
||||
{
|
||||
struct cdev *cdev;
|
||||
struct dev_ops *dev_ops;
|
||||
struct cb_ops *cb_ops;
|
||||
struct file_operations *fops;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(spec_type == S_IFCHR);
|
||||
ASSERT(minor_num < di->di_minors);
|
||||
ASSERT(!strcmp(node_type, DDI_PSEUDO));
|
||||
|
||||
fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
|
||||
if (fops == NULL)
|
||||
RETURN(DDI_FAILURE);
|
||||
|
||||
cdev = cdev_alloc();
|
||||
if (cdev == NULL) {
|
||||
kfree(fops);
|
||||
RETURN(DDI_FAILURE);
|
||||
}
|
||||
|
||||
cdev->ops = fops;
|
||||
|
||||
mutex_enter(&di->di_lock);
|
||||
dev_ops = di->di_ops;
|
||||
ASSERT(dev_ops);
|
||||
cb_ops = di->di_ops->devo_cb_ops;
|
||||
ASSERT(cb_ops);
|
||||
|
||||
/* Setup the fops to cb_ops mapping */
|
||||
fops->owner = mod;
|
||||
if (cb_ops->cb_ioctl)
|
||||
fops->ioctl = mod_generic_ioctl;
|
||||
|
||||
#if 0
|
||||
if (cb_ops->cb_open)
|
||||
fops->open = mod_generic_open;
|
||||
|
||||
if (cb_ops->cb_close)
|
||||
fops->release = mod_generic_close;
|
||||
|
||||
if (cb_ops->cb_read)
|
||||
fops->read = mod_generic_read;
|
||||
|
||||
if (cb_ops->cb_write)
|
||||
fops->write = mod_generic_write;
|
||||
#endif
|
||||
/* XXX: Currently unsupported operations */
|
||||
ASSERT(cb_ops->cb_open == NULL);
|
||||
ASSERT(cb_ops->cb_close == NULL);
|
||||
ASSERT(cb_ops->cb_read == NULL);
|
||||
ASSERT(cb_ops->cb_write == NULL);
|
||||
ASSERT(cb_ops->cb_strategy == NULL);
|
||||
ASSERT(cb_ops->cb_print == NULL);
|
||||
ASSERT(cb_ops->cb_dump == NULL);
|
||||
ASSERT(cb_ops->cb_devmap == NULL);
|
||||
ASSERT(cb_ops->cb_mmap == NULL);
|
||||
ASSERT(cb_ops->cb_segmap == NULL);
|
||||
ASSERT(cb_ops->cb_chpoll == NULL);
|
||||
ASSERT(cb_ops->cb_prop_op == NULL);
|
||||
ASSERT(cb_ops->cb_str == NULL);
|
||||
ASSERT(cb_ops->cb_aread == NULL);
|
||||
ASSERT(cb_ops->cb_awrite == NULL);
|
||||
|
||||
di->di_cdev = cdev;
|
||||
di->di_flags = flags;
|
||||
di->di_minor = minor_num;
|
||||
di->di_dev = MKDEV(di->di_major, di->di_minor);
|
||||
|
||||
rc = cdev_add(cdev, di->di_dev, 1);
|
||||
if (rc) {
|
||||
CERROR("Error adding cdev, %d\n", rc);
|
||||
kfree(fops);
|
||||
cdev_del(cdev);
|
||||
mutex_exit(&di->di_lock);
|
||||
RETURN(DDI_FAILURE);
|
||||
}
|
||||
|
||||
spin_lock(&dev_info_lock);
|
||||
list_add(&di->di_list, &dev_info_list);
|
||||
spin_unlock(&dev_info_lock);
|
||||
|
||||
mutex_exit(&di->di_lock);
|
||||
|
||||
RETURN(DDI_SUCCESS);
|
||||
}
|
||||
EXPORT_SYMBOL(__ddi_create_minor_node);
|
||||
|
||||
static void
|
||||
__ddi_remove_minor_node_locked(dev_info_t *di, char *name)
|
||||
{
|
||||
if (di->di_cdev) {
|
||||
cdev_del(di->di_cdev);
|
||||
di->di_cdev = NULL;
|
||||
}
|
||||
|
||||
spin_lock(&dev_info_lock);
|
||||
list_del_init(&di->di_list);
|
||||
spin_unlock(&dev_info_lock);
|
||||
}
|
||||
|
||||
void
|
||||
__ddi_remove_minor_node(dev_info_t *di, char *name)
|
||||
{
|
||||
ENTRY;
|
||||
mutex_enter(&di->di_lock);
|
||||
__ddi_remove_minor_node_locked(di, name);
|
||||
mutex_exit(&di->di_lock);
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__ddi_remove_minor_node);
|
||||
|
||||
int
|
||||
ddi_quiesce_not_needed(dev_info_t *dip)
|
||||
{
|
||||
RETURN(DDI_SUCCESS);
|
||||
}
|
||||
EXPORT_SYMBOL(ddi_quiesce_not_needed);
|
||||
|
||||
#if 0
|
||||
static int
|
||||
mod_generic_open(struct inode *, struct file *)
|
||||
{
|
||||
open(dev_t *devp, int flags, int otyp, cred_t *credp);
|
||||
}
|
||||
|
||||
static int
|
||||
mod_generic_close(struct inode *, struct file *)
|
||||
{
|
||||
close(dev_t dev, int flags, int otyp, cred_t *credp);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mod_generic_read(struct file *, char __user *, size_t, loff_t *)
|
||||
{
|
||||
read(dev_t dev, struct uio *uiop, cred_t *credp);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mod_generic_write(struct file *, const char __user *, size_t, loff_t *)
|
||||
{
|
||||
write(dev_t dev, struct uio *uiop, cred_t *credp);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct dev_info *
|
||||
dev_info_alloc(major_t major, minor_t minors, struct dev_ops *ops) {
|
||||
struct dev_info *di;
|
||||
|
||||
di = kmalloc(sizeof(struct dev_info), GFP_KERNEL);
|
||||
if (di == NULL)
|
||||
return NULL;
|
||||
|
||||
mutex_init(&di->di_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
INIT_LIST_HEAD(&di->di_list);
|
||||
di->di_ops = ops;
|
||||
di->di_class = NULL;
|
||||
di->di_cdev = NULL;
|
||||
di->di_major = major;
|
||||
di->di_minor = 0;
|
||||
di->di_minors = minors;
|
||||
di->di_dev = 0;
|
||||
|
||||
return di;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_info_free(struct dev_info *di)
|
||||
{
|
||||
mutex_enter(&di->di_lock);
|
||||
__ddi_remove_minor_node_locked(di, NULL);
|
||||
mutex_exit(&di->di_lock);
|
||||
mutex_destroy(&di->di_lock);
|
||||
kfree(di);
|
||||
}
|
||||
|
||||
int
|
||||
__mod_install(struct modlinkage *modlp)
|
||||
{
|
||||
struct modldrv *drv = modlp->ml_modldrv;
|
||||
struct dev_info *di;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
di = dev_info_alloc(modlp->ml_major, modlp->ml_minors,
|
||||
drv->drv_dev_ops);
|
||||
if (di == NULL)
|
||||
RETURN(ENOMEM);
|
||||
|
||||
/* XXX: Really we need to be calling devo_probe if it's available
|
||||
* and then calling devo_attach for each device discovered. However
|
||||
* for now we just call it once and let the app sort it out.
|
||||
*/
|
||||
rc = drv->drv_dev_ops->devo_attach(di, DDI_ATTACH);
|
||||
if (rc != DDI_SUCCESS) {
|
||||
dev_info_free(di);
|
||||
RETURN(rc);
|
||||
}
|
||||
|
||||
drv->drv_dev_info = di;
|
||||
|
||||
RETURN(DDI_SUCCESS);
|
||||
}
|
||||
EXPORT_SYMBOL(__mod_install);
|
||||
|
||||
int
|
||||
__mod_remove(struct modlinkage *modlp)
|
||||
{
|
||||
struct modldrv *drv = modlp->ml_modldrv;
|
||||
struct dev_info *di = drv->drv_dev_info;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
rc = drv->drv_dev_ops->devo_detach(di, DDI_DETACH);
|
||||
if (rc != DDI_SUCCESS)
|
||||
RETURN(rc);
|
||||
|
||||
dev_info_free(di);
|
||||
drv->drv_dev_info = NULL;
|
||||
|
||||
RETURN(DDI_SUCCESS);
|
||||
}
|
||||
EXPORT_SYMBOL(__mod_remove);
|
||||
|
||||
int
|
||||
ldi_ident_from_mod(struct modlinkage *modlp, ldi_ident_t *lip)
|
||||
{
|
||||
ldi_ident_t li;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(modlp);
|
||||
ASSERT(lip);
|
||||
|
||||
li = kmalloc(sizeof(struct ldi_ident), GFP_KERNEL);
|
||||
if (li == NULL)
|
||||
RETURN(ENOMEM);
|
||||
|
||||
li->li_dev = MKDEV(modlp->ml_major, 0);
|
||||
*lip = li;
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
EXPORT_SYMBOL(ldi_ident_from_mod);
|
||||
|
||||
void
|
||||
ldi_ident_release(ldi_ident_t lip)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(lip);
|
||||
kfree(lip);
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(ldi_ident_release);
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/mutex.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_MUTEX
|
||||
|
||||
/* Mutex implementation based on those found in Solaris. This means
|
||||
* they the MUTEX_DEFAULT type is an adaptive mutex. When calling
|
||||
* mutex_enter() your process will spin waiting for the lock if it's
|
||||
* likely the lock will be free'd shortly. If it looks like the
|
||||
* lock will be held for a longer time we schedule and sleep waiting
|
||||
* for it. This determination is made by checking if the holder of
|
||||
* the lock is currently running on cpu or sleeping waiting to be
|
||||
* scheduled. If the holder is currently running it's likely the
|
||||
* lock will be shortly dropped.
|
||||
*
|
||||
* XXX: This is basically a rough implementation to see if this
|
||||
* helps our performance. If it does a more careful implementation
|
||||
* should be done, perhaps in assembly.
|
||||
*/
|
||||
|
||||
/* 0: Never spin when trying to aquire lock
|
||||
* -1: Spin until aquired or holder yeilds without dropping lock
|
||||
* 1-MAX_INT: Spin for N attempts before sleeping for lock
|
||||
*/
|
||||
int mutex_spin_max = 0;
|
||||
|
||||
#ifdef DEBUG_MUTEX
|
||||
int mutex_stats[MUTEX_STATS_SIZE] = { 0 };
|
||||
spinlock_t mutex_stats_lock;
|
||||
struct list_head mutex_stats_list;
|
||||
#endif
|
||||
|
||||
int
|
||||
__spl_mutex_init(kmutex_t *mp, char *name, int type, void *ibc)
|
||||
{
|
||||
int flags = KM_SLEEP;
|
||||
|
||||
ASSERT(mp);
|
||||
ASSERT(name);
|
||||
ASSERT(ibc == NULL);
|
||||
ASSERT(mp->km_magic != KM_MAGIC); /* Never double init */
|
||||
|
||||
mp->km_name = NULL;
|
||||
mp->km_name_size = strlen(name) + 1;
|
||||
|
||||
switch (type) {
|
||||
case MUTEX_DEFAULT:
|
||||
mp->km_type = MUTEX_ADAPTIVE;
|
||||
break;
|
||||
case MUTEX_SPIN:
|
||||
case MUTEX_ADAPTIVE:
|
||||
mp->km_type = type;
|
||||
break;
|
||||
default:
|
||||
SBUG();
|
||||
}
|
||||
|
||||
/* We may be called when there is a non-zero preempt_count or
|
||||
* interrupts are disabled is which case we must not sleep.
|
||||
*/
|
||||
if (current_thread_info()->preempt_count || irqs_disabled())
|
||||
flags = KM_NOSLEEP;
|
||||
|
||||
/* Semaphore kmem_alloc'ed to keep struct size down (<64b) */
|
||||
mp->km_sem = kmem_alloc(sizeof(struct semaphore), flags);
|
||||
if (mp->km_sem == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
mp->km_name = kmem_alloc(mp->km_name_size, flags);
|
||||
if (mp->km_name == NULL) {
|
||||
kmem_free(mp->km_sem, sizeof(struct semaphore));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sema_init(mp->km_sem, 1);
|
||||
strncpy(mp->km_name, name, mp->km_name_size);
|
||||
|
||||
#ifdef DEBUG_MUTEX
|
||||
mp->km_stats = kmem_zalloc(sizeof(int) * MUTEX_STATS_SIZE, flags);
|
||||
if (mp->km_stats == NULL) {
|
||||
kmem_free(mp->km_name, mp->km_name_size);
|
||||
kmem_free(mp->km_sem, sizeof(struct semaphore));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* XXX - This appears to be a much more contended lock than I
|
||||
* would have expected. To run with this debugging enabled and
|
||||
* get reasonable performance we may need to be more clever and
|
||||
* do something like hash the mutex ptr on to one of several
|
||||
* lists to ease this single point of contention.
|
||||
*/
|
||||
spin_lock(&mutex_stats_lock);
|
||||
list_add_tail(&mp->km_list, &mutex_stats_list);
|
||||
spin_unlock(&mutex_stats_lock);
|
||||
#endif
|
||||
mp->km_magic = KM_MAGIC;
|
||||
mp->km_owner = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(__spl_mutex_init);
|
||||
|
||||
void
|
||||
__spl_mutex_destroy(kmutex_t *mp)
|
||||
{
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
|
||||
#ifdef DEBUG_MUTEX
|
||||
spin_lock(&mutex_stats_lock);
|
||||
list_del_init(&mp->km_list);
|
||||
spin_unlock(&mutex_stats_lock);
|
||||
|
||||
kmem_free(mp->km_stats, sizeof(int) * MUTEX_STATS_SIZE);
|
||||
#endif
|
||||
kmem_free(mp->km_name, mp->km_name_size);
|
||||
kmem_free(mp->km_sem, sizeof(struct semaphore));
|
||||
|
||||
memset(mp, KM_POISON, sizeof(*mp));
|
||||
}
|
||||
EXPORT_SYMBOL(__spl_mutex_destroy);
|
||||
|
||||
/* Return 1 if we acquired the mutex, else zero. */
|
||||
int
|
||||
__mutex_tryenter(kmutex_t *mp)
|
||||
{
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_TRYENTER_TOTAL);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_TRYENTER_TOTAL);
|
||||
|
||||
rc = down_trylock(mp->km_sem);
|
||||
if (rc == 0) {
|
||||
ASSERT(mp->km_owner == NULL);
|
||||
mp->km_owner = current;
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_TRYENTER_NOT_HELD);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_TRYENTER_NOT_HELD);
|
||||
}
|
||||
|
||||
RETURN(!rc);
|
||||
}
|
||||
EXPORT_SYMBOL(__mutex_tryenter);
|
||||
|
||||
#ifndef HAVE_TASK_CURR
|
||||
#define task_curr(owner) 0
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
mutex_enter_adaptive(kmutex_t *mp)
|
||||
{
|
||||
struct task_struct *owner;
|
||||
int count = 0;
|
||||
|
||||
/* Lock is not held so we expect to aquire the lock */
|
||||
if ((owner = mp->km_owner) == NULL) {
|
||||
down(mp->km_sem);
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_ENTER_NOT_HELD);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_ENTER_NOT_HELD);
|
||||
} else {
|
||||
/* The lock is held by a currently running task which
|
||||
* we expect will drop the lock before leaving the
|
||||
* head of the runqueue. So the ideal thing to do
|
||||
* is spin until we aquire the lock and avoid a
|
||||
* context switch. However it is also possible the
|
||||
* task holding the lock yields the processor with
|
||||
* out dropping lock. In which case, we know it's
|
||||
* going to be a while so we stop spinning and go
|
||||
* to sleep waiting for the lock to be available.
|
||||
* This should strike the optimum balance between
|
||||
* spinning and sleeping waiting for a lock.
|
||||
*/
|
||||
while (task_curr(owner) && (count <= mutex_spin_max)) {
|
||||
if (down_trylock(mp->km_sem) == 0) {
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_ENTER_SPIN);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_ENTER_SPIN);
|
||||
GOTO(out, count);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
/* The lock is held by a sleeping task so it's going to
|
||||
* cost us minimally one context switch. We might as
|
||||
* well sleep and yield the processor to other tasks.
|
||||
*/
|
||||
down(mp->km_sem);
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_ENTER_SLEEP);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_ENTER_SLEEP);
|
||||
}
|
||||
out:
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_ENTER_TOTAL);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_ENTER_TOTAL);
|
||||
}
|
||||
|
||||
void
|
||||
__mutex_enter(kmutex_t *mp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
|
||||
switch (mp->km_type) {
|
||||
case MUTEX_SPIN:
|
||||
while (down_trylock(mp->km_sem));
|
||||
MUTEX_STAT_INC(mutex_stats, MUTEX_ENTER_SPIN);
|
||||
MUTEX_STAT_INC(mp->km_stats, MUTEX_ENTER_SPIN);
|
||||
break;
|
||||
case MUTEX_ADAPTIVE:
|
||||
mutex_enter_adaptive(mp);
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(mp->km_owner == NULL);
|
||||
mp->km_owner = current;
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__mutex_enter);
|
||||
|
||||
void
|
||||
__mutex_exit(kmutex_t *mp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
ASSERT(mp->km_owner == current);
|
||||
mp->km_owner = NULL;
|
||||
up(mp->km_sem);
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__mutex_exit);
|
||||
|
||||
/* Return 1 if mutex is held by current process, else zero. */
|
||||
int
|
||||
__mutex_owned(kmutex_t *mp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
RETURN(mp->km_owner == current);
|
||||
}
|
||||
EXPORT_SYMBOL(__mutex_owned);
|
||||
|
||||
/* Return owner if mutex is owned, else NULL. */
|
||||
kthread_t *
|
||||
__spl_mutex_owner(kmutex_t *mp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(mp);
|
||||
ASSERT(mp->km_magic == KM_MAGIC);
|
||||
RETURN(mp->km_owner);
|
||||
}
|
||||
EXPORT_SYMBOL(__spl_mutex_owner);
|
||||
|
||||
int
|
||||
spl_mutex_init(void)
|
||||
{
|
||||
ENTRY;
|
||||
#ifdef DEBUG_MUTEX
|
||||
spin_lock_init(&mutex_stats_lock);
|
||||
INIT_LIST_HEAD(&mutex_stats_list);
|
||||
#endif
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
void
|
||||
spl_mutex_fini(void)
|
||||
{
|
||||
ENTRY;
|
||||
#ifdef DEBUG_MUTEX
|
||||
ASSERT(list_empty(&mutex_stats_list));
|
||||
#endif
|
||||
EXIT;
|
||||
}
|
||||
|
||||
module_param(mutex_spin_max, int, 0644);
|
||||
MODULE_PARM_DESC(mutex_spin_max, "Spin a maximum of N times to aquire lock");
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/rwlock.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_RWLOCK
|
||||
|
||||
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
|
||||
struct rwsem_waiter {
|
||||
struct list_head list;
|
||||
struct task_struct *task;
|
||||
unsigned int flags;
|
||||
#define RWSEM_WAITING_FOR_READ 0x00000001
|
||||
#define RWSEM_WAITING_FOR_WRITE 0x00000002
|
||||
};
|
||||
/* wake a single writer */
|
||||
static struct rw_semaphore *
|
||||
__rwsem_wake_one_writer_locked(struct rw_semaphore *sem)
|
||||
{
|
||||
struct rwsem_waiter *waiter;
|
||||
struct task_struct *tsk;
|
||||
|
||||
sem->activity = -1;
|
||||
|
||||
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
|
||||
list_del(&waiter->list);
|
||||
|
||||
tsk = waiter->task;
|
||||
smp_mb();
|
||||
waiter->task = NULL;
|
||||
wake_up_process(tsk);
|
||||
put_task_struct(tsk);
|
||||
return sem;
|
||||
}
|
||||
|
||||
/* release a read lock on the semaphore */
|
||||
static void
|
||||
__up_read_locked(struct rw_semaphore *sem)
|
||||
{
|
||||
if (--sem->activity == 0 && !list_empty(&sem->wait_list))
|
||||
(void)__rwsem_wake_one_writer_locked(sem);
|
||||
}
|
||||
|
||||
/* trylock for writing -- returns 1 if successful, 0 if contention */
|
||||
static int
|
||||
__down_write_trylock_locked(struct rw_semaphore *sem)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (sem->activity == 0 && list_empty(&sem->wait_list)) {
|
||||
/* granted */
|
||||
sem->activity = -1;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
__rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg)
|
||||
{
|
||||
int flags = KM_SLEEP;
|
||||
|
||||
ASSERT(rwlp);
|
||||
ASSERT(name);
|
||||
ASSERT(type == RW_DEFAULT); /* XXX no irq handler use */
|
||||
ASSERT(arg == NULL); /* XXX no irq handler use */
|
||||
|
||||
rwlp->rw_magic = RW_MAGIC;
|
||||
rwlp->rw_owner = NULL;
|
||||
rwlp->rw_name = NULL;
|
||||
rwlp->rw_name_size = strlen(name) + 1;
|
||||
|
||||
/* We may be called when there is a non-zero preempt_count or
|
||||
* interrupts are disabled is which case we must not sleep.
|
||||
*/
|
||||
if (current_thread_info()->preempt_count || irqs_disabled())
|
||||
flags = KM_NOSLEEP;
|
||||
|
||||
rwlp->rw_name = kmem_alloc(rwlp->rw_name_size, flags);
|
||||
if (rwlp->rw_name == NULL)
|
||||
return;
|
||||
|
||||
init_rwsem(&rwlp->rw_sem);
|
||||
strcpy(rwlp->rw_name, name);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_init);
|
||||
|
||||
void
|
||||
__rw_destroy(krwlock_t *rwlp)
|
||||
{
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
ASSERT(rwlp->rw_owner == NULL);
|
||||
spin_lock(&rwlp->rw_sem.wait_lock);
|
||||
ASSERT(list_empty(&rwlp->rw_sem.wait_list));
|
||||
spin_unlock(&rwlp->rw_sem.wait_lock);
|
||||
|
||||
kmem_free(rwlp->rw_name, rwlp->rw_name_size);
|
||||
|
||||
memset(rwlp, RW_POISON, sizeof(krwlock_t));
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_destroy);
|
||||
|
||||
/* Return 0 if the lock could not be obtained without blocking. */
|
||||
int
|
||||
__rw_tryenter(krwlock_t *rwlp, krw_t rw)
|
||||
{
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(rwlp);
|
||||
ASSERT(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 */
|
||||
rc = down_read_trylock(&rwlp->rw_sem);
|
||||
break;
|
||||
case RW_WRITER:
|
||||
rc = down_write_trylock(&rwlp->rw_sem);
|
||||
if (rc) {
|
||||
/* there better not be anyone else
|
||||
* holding the write lock here */
|
||||
ASSERT(rwlp->rw_owner == NULL);
|
||||
rwlp->rw_owner = current;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SBUG();
|
||||
}
|
||||
|
||||
RETURN(rc);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_tryenter);
|
||||
|
||||
void
|
||||
__rw_enter(krwlock_t *rwlp, krw_t rw)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(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 */
|
||||
ASSERT(rwlp->rw_owner == NULL);
|
||||
rwlp->rw_owner = current;
|
||||
break;
|
||||
default:
|
||||
SBUG();
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_enter);
|
||||
|
||||
void
|
||||
__rw_exit(krwlock_t *rwlp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(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);
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_exit);
|
||||
|
||||
void
|
||||
__rw_downgrade(krwlock_t *rwlp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
ASSERT(rwlp->rw_owner == current);
|
||||
|
||||
rwlp->rw_owner = NULL;
|
||||
downgrade_write(&rwlp->rw_sem);
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_downgrade);
|
||||
|
||||
/* Return 0 if unable to perform the upgrade.
|
||||
* Might be wise to fix the caller
|
||||
* to acquire the write lock first?
|
||||
*/
|
||||
int
|
||||
__rw_tryupgrade(krwlock_t *rwlp)
|
||||
{
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(rwlp);
|
||||
ASSERT(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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
|
||||
/* Here it should be safe to drop the
|
||||
* read lock and reacquire it for writing since
|
||||
* we know there are no waiters */
|
||||
__up_read_locked(&rwlp->rw_sem);
|
||||
|
||||
/* returns 1 if success, 0 if contention */
|
||||
rc = __down_write_trylock_locked(&rwlp->rw_sem);
|
||||
#else
|
||||
/* 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 */
|
||||
rc = down_write_trylock(&rwlp->rw_sem);
|
||||
#endif
|
||||
|
||||
/* Check if upgrade failed. Should not ever happen
|
||||
* if we got to this point */
|
||||
ASSERT(rc);
|
||||
ASSERT(rwlp->rw_owner == NULL);
|
||||
rwlp->rw_owner = current;
|
||||
spin_unlock(&rwlp->rw_sem.wait_lock);
|
||||
|
||||
RETURN(1);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_tryupgrade);
|
||||
|
||||
kthread_t *
|
||||
__rw_owner(krwlock_t *rwlp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
RETURN(rwlp->rw_owner);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_owner);
|
||||
|
||||
int
|
||||
__rw_read_held(krwlock_t *rwlp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
RETURN(__rw_lock_held(rwlp) && rwlp->rw_owner == NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_read_held);
|
||||
|
||||
int
|
||||
__rw_write_held(krwlock_t *rwlp)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
RETURN(rwlp->rw_owner == current);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_write_held);
|
||||
|
||||
int
|
||||
__rw_lock_held(krwlock_t *rwlp)
|
||||
{
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(rwlp);
|
||||
ASSERT(rwlp->rw_magic == RW_MAGIC);
|
||||
|
||||
spin_lock_irq(&(rwlp->rw_sem.wait_lock));
|
||||
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
|
||||
if (rwlp->rw_sem.activity != 0) {
|
||||
#else
|
||||
if (rwlp->rw_sem.count != 0) {
|
||||
#endif
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&(rwlp->rw_sem.wait_lock));
|
||||
|
||||
RETURN(rc);
|
||||
}
|
||||
EXPORT_SYMBOL(__rw_lock_held);
|
||||
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/taskq.h>
|
||||
#include <sys/kmem.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_TASKQ
|
||||
|
||||
/* Global system-wide dynamic task queue available for all consumers */
|
||||
taskq_t *system_taskq;
|
||||
EXPORT_SYMBOL(system_taskq);
|
||||
|
||||
typedef struct spl_task {
|
||||
spinlock_t t_lock;
|
||||
struct list_head t_list;
|
||||
taskqid_t t_id;
|
||||
task_func_t *t_func;
|
||||
void *t_arg;
|
||||
} spl_task_t;
|
||||
|
||||
/* NOTE: Must be called with tq->tq_lock held, returns a list_t which
|
||||
* is not attached to the free, work, or pending taskq lists.
|
||||
*/
|
||||
static spl_task_t *
|
||||
task_alloc(taskq_t *tq, uint_t flags)
|
||||
{
|
||||
spl_task_t *t;
|
||||
int count = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
ASSERT(flags & (TQ_SLEEP | TQ_NOSLEEP)); /* One set */
|
||||
ASSERT(!((flags & TQ_SLEEP) && (flags & TQ_NOSLEEP))); /* Not both */
|
||||
ASSERT(spin_is_locked(&tq->tq_lock));
|
||||
retry:
|
||||
/* Aquire spl_task_t's from free list if available */
|
||||
if (!list_empty(&tq->tq_free_list) && !(flags & TQ_NEW)) {
|
||||
t = list_entry(tq->tq_free_list.next, spl_task_t, t_list);
|
||||
list_del_init(&t->t_list);
|
||||
RETURN(t);
|
||||
}
|
||||
|
||||
/* Free list is empty and memory allocs are prohibited */
|
||||
if (flags & TQ_NOALLOC)
|
||||
RETURN(NULL);
|
||||
|
||||
/* Hit maximum spl_task_t pool size */
|
||||
if (tq->tq_nalloc >= tq->tq_maxalloc) {
|
||||
if (flags & TQ_NOSLEEP)
|
||||
RETURN(NULL);
|
||||
|
||||
/* Sleep periodically polling the free list for an available
|
||||
* spl_task_t. If a full second passes and we have not found
|
||||
* one gives up and return a NULL to the caller. */
|
||||
if (flags & TQ_SLEEP) {
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
schedule_timeout(HZ / 100);
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
if (count < 100)
|
||||
GOTO(retry, count++);
|
||||
|
||||
RETURN(NULL);
|
||||
}
|
||||
|
||||
/* Unreachable, TQ_SLEEP xor TQ_NOSLEEP */
|
||||
SBUG();
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
t = kmem_alloc(sizeof(spl_task_t), flags & (TQ_SLEEP | TQ_NOSLEEP));
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
if (t) {
|
||||
spin_lock_init(&t->t_lock);
|
||||
INIT_LIST_HEAD(&t->t_list);
|
||||
t->t_id = 0;
|
||||
t->t_func = NULL;
|
||||
t->t_arg = NULL;
|
||||
tq->tq_nalloc++;
|
||||
}
|
||||
|
||||
RETURN(t);
|
||||
}
|
||||
|
||||
/* NOTE: Must be called with tq->tq_lock held, expectes the spl_task_t
|
||||
* to already be removed from the free, work, or pending taskq lists.
|
||||
*/
|
||||
static void
|
||||
task_free(taskq_t *tq, spl_task_t *t)
|
||||
{
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
ASSERT(t);
|
||||
ASSERT(spin_is_locked(&tq->tq_lock));
|
||||
ASSERT(list_empty(&t->t_list));
|
||||
|
||||
kmem_free(t, sizeof(spl_task_t));
|
||||
tq->tq_nalloc--;
|
||||
|
||||
EXIT;
|
||||
}
|
||||
|
||||
/* NOTE: Must be called with tq->tq_lock held, either destroyes the
|
||||
* spl_task_t if too many exist or moves it to the free list for later use.
|
||||
*/
|
||||
static void
|
||||
task_done(taskq_t *tq, spl_task_t *t)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(tq);
|
||||
ASSERT(t);
|
||||
ASSERT(spin_is_locked(&tq->tq_lock));
|
||||
|
||||
list_del_init(&t->t_list);
|
||||
|
||||
if (tq->tq_nalloc <= tq->tq_minalloc) {
|
||||
t->t_id = 0;
|
||||
t->t_func = NULL;
|
||||
t->t_arg = NULL;
|
||||
list_add_tail(&t->t_list, &tq->tq_free_list);
|
||||
} else {
|
||||
task_free(tq, t);
|
||||
}
|
||||
|
||||
EXIT;
|
||||
}
|
||||
|
||||
/* Taskqid's are handed out in a monotonically increasing fashion per
|
||||
* taskq_t. We don't handle taskqid wrapping yet, but fortuntely it isi
|
||||
* a 64-bit value so this is probably never going to happen. The lowest
|
||||
* pending taskqid is stored in the taskq_t to make it easy for any
|
||||
* taskq_wait()'ers to know if the tasks they're waiting for have
|
||||
* completed. Unfortunately, tq_task_lowest is kept up to date is
|
||||
* a pretty brain dead way, something more clever should be done.
|
||||
*/
|
||||
static int
|
||||
taskq_wait_check(taskq_t *tq, taskqid_t id)
|
||||
{
|
||||
RETURN(tq->tq_lowest_id >= id);
|
||||
}
|
||||
|
||||
/* Expected to wait for all previously scheduled tasks to complete. We do
|
||||
* not need to wait for tasked scheduled after this call to complete. In
|
||||
* otherwords we do not need to drain the entire taskq. */
|
||||
void
|
||||
__taskq_wait_id(taskq_t *tq, taskqid_t id)
|
||||
{
|
||||
ENTRY;
|
||||
ASSERT(tq);
|
||||
|
||||
wait_event(tq->tq_wait_waitq, taskq_wait_check(tq, id));
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_wait_id);
|
||||
|
||||
void
|
||||
__taskq_wait(taskq_t *tq)
|
||||
{
|
||||
taskqid_t id;
|
||||
ENTRY;
|
||||
ASSERT(tq);
|
||||
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
id = tq->tq_next_id;
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
__taskq_wait_id(tq, id);
|
||||
|
||||
EXIT;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_wait);
|
||||
|
||||
int
|
||||
__taskq_member(taskq_t *tq, void *t)
|
||||
{
|
||||
int i;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
ASSERT(t);
|
||||
|
||||
for (i = 0; i < tq->tq_nthreads; i++)
|
||||
if (tq->tq_threads[i] == (struct task_struct *)t)
|
||||
RETURN(1);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_member);
|
||||
|
||||
taskqid_t
|
||||
__taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t flags)
|
||||
{
|
||||
spl_task_t *t;
|
||||
taskqid_t rc = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
ASSERT(func);
|
||||
if (unlikely(in_atomic() && (flags & TQ_SLEEP))) {
|
||||
CERROR("May schedule while atomic: %s/0x%08x/%d\n",
|
||||
current->comm, preempt_count(), current->pid);
|
||||
SBUG();
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
/* Taskq being destroyed and all tasks drained */
|
||||
if (!(tq->tq_flags & TQ_ACTIVE))
|
||||
GOTO(out, rc = 0);
|
||||
|
||||
/* Do not queue the task unless there is idle thread for it */
|
||||
ASSERT(tq->tq_nactive <= tq->tq_nthreads);
|
||||
if ((flags & TQ_NOQUEUE) && (tq->tq_nactive == tq->tq_nthreads))
|
||||
GOTO(out, rc = 0);
|
||||
|
||||
if ((t = task_alloc(tq, flags)) == NULL)
|
||||
GOTO(out, rc = 0);
|
||||
|
||||
spin_lock(&t->t_lock);
|
||||
list_add_tail(&t->t_list, &tq->tq_pend_list);
|
||||
t->t_id = rc = tq->tq_next_id;
|
||||
tq->tq_next_id++;
|
||||
t->t_func = func;
|
||||
t->t_arg = arg;
|
||||
spin_unlock(&t->t_lock);
|
||||
|
||||
wake_up(&tq->tq_work_waitq);
|
||||
out:
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
RETURN(rc);
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_dispatch);
|
||||
|
||||
/* NOTE: Must be called with tq->tq_lock held */
|
||||
static taskqid_t
|
||||
taskq_lowest_id(taskq_t *tq)
|
||||
{
|
||||
taskqid_t lowest_id = ~0;
|
||||
spl_task_t *t;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
ASSERT(spin_is_locked(&tq->tq_lock));
|
||||
|
||||
list_for_each_entry(t, &tq->tq_pend_list, t_list)
|
||||
if (t->t_id < lowest_id)
|
||||
lowest_id = t->t_id;
|
||||
|
||||
list_for_each_entry(t, &tq->tq_work_list, t_list)
|
||||
if (t->t_id < lowest_id)
|
||||
lowest_id = t->t_id;
|
||||
|
||||
RETURN(lowest_id);
|
||||
}
|
||||
|
||||
static int
|
||||
taskq_thread(void *args)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
sigset_t blocked;
|
||||
taskqid_t id;
|
||||
taskq_t *tq = args;
|
||||
spl_task_t *t;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
sigfillset(&blocked);
|
||||
sigprocmask(SIG_BLOCK, &blocked, NULL);
|
||||
flush_signals(current);
|
||||
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
tq->tq_nthreads++;
|
||||
wake_up(&tq->tq_wait_waitq);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
|
||||
add_wait_queue(&tq->tq_work_waitq, &wait);
|
||||
if (list_empty(&tq->tq_pend_list)) {
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
schedule();
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
} else {
|
||||
__set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
remove_wait_queue(&tq->tq_work_waitq, &wait);
|
||||
if (!list_empty(&tq->tq_pend_list)) {
|
||||
t = list_entry(tq->tq_pend_list.next, spl_task_t, t_list);
|
||||
list_del_init(&t->t_list);
|
||||
list_add_tail(&t->t_list, &tq->tq_work_list);
|
||||
tq->tq_nactive++;
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
/* Perform the requested task */
|
||||
t->t_func(t->t_arg);
|
||||
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
tq->tq_nactive--;
|
||||
id = t->t_id;
|
||||
task_done(tq, t);
|
||||
|
||||
/* Update the lowest remaining taskqid yet to run */
|
||||
if (tq->tq_lowest_id == id) {
|
||||
tq->tq_lowest_id = taskq_lowest_id(tq);
|
||||
ASSERT(tq->tq_lowest_id > id);
|
||||
}
|
||||
|
||||
wake_up_all(&tq->tq_wait_waitq);
|
||||
}
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
}
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
tq->tq_nthreads--;
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
taskq_t *
|
||||
__taskq_create(const char *name, int nthreads, pri_t pri,
|
||||
int minalloc, int maxalloc, uint_t flags)
|
||||
{
|
||||
taskq_t *tq;
|
||||
struct task_struct *t;
|
||||
int rc = 0, i, j = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(pri <= maxclsyspri);
|
||||
ASSERT(minalloc >= 0);
|
||||
ASSERT(maxalloc <= INT_MAX);
|
||||
ASSERT(!(flags & (TASKQ_CPR_SAFE | TASKQ_DYNAMIC))); /* Unsupported */
|
||||
|
||||
tq = kmem_alloc(sizeof(*tq), KM_SLEEP);
|
||||
if (tq == NULL)
|
||||
RETURN(NULL);
|
||||
|
||||
tq->tq_threads = kmem_alloc(nthreads * sizeof(t), KM_SLEEP);
|
||||
if (tq->tq_threads == NULL) {
|
||||
kmem_free(tq, sizeof(*tq));
|
||||
RETURN(NULL);
|
||||
}
|
||||
|
||||
spin_lock_init(&tq->tq_lock);
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
tq->tq_name = name;
|
||||
tq->tq_nactive = 0;
|
||||
tq->tq_nthreads = 0;
|
||||
tq->tq_pri = pri;
|
||||
tq->tq_minalloc = minalloc;
|
||||
tq->tq_maxalloc = maxalloc;
|
||||
tq->tq_nalloc = 0;
|
||||
tq->tq_flags = (flags | TQ_ACTIVE);
|
||||
tq->tq_next_id = 1;
|
||||
tq->tq_lowest_id = 1;
|
||||
INIT_LIST_HEAD(&tq->tq_free_list);
|
||||
INIT_LIST_HEAD(&tq->tq_work_list);
|
||||
INIT_LIST_HEAD(&tq->tq_pend_list);
|
||||
init_waitqueue_head(&tq->tq_work_waitq);
|
||||
init_waitqueue_head(&tq->tq_wait_waitq);
|
||||
|
||||
if (flags & TASKQ_PREPOPULATE)
|
||||
for (i = 0; i < minalloc; i++)
|
||||
task_done(tq, task_alloc(tq, TQ_SLEEP | TQ_NEW));
|
||||
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
t = kthread_create(taskq_thread, tq, "%s/%d", name, i);
|
||||
if (t) {
|
||||
tq->tq_threads[i] = t;
|
||||
kthread_bind(t, i % num_online_cpus());
|
||||
set_user_nice(t, PRIO_TO_NICE(pri));
|
||||
wake_up_process(t);
|
||||
j++;
|
||||
} else {
|
||||
tq->tq_threads[i] = NULL;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for all threads to be started before potential destroy */
|
||||
wait_event(tq->tq_wait_waitq, tq->tq_nthreads == j);
|
||||
|
||||
if (rc) {
|
||||
__taskq_destroy(tq);
|
||||
tq = NULL;
|
||||
}
|
||||
|
||||
RETURN(tq);
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_create);
|
||||
|
||||
void
|
||||
__taskq_destroy(taskq_t *tq)
|
||||
{
|
||||
spl_task_t *t;
|
||||
int i, nthreads;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(tq);
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
tq->tq_flags &= ~TQ_ACTIVE;
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
/* TQ_ACTIVE cleared prevents new tasks being added to pending */
|
||||
__taskq_wait(tq);
|
||||
|
||||
nthreads = tq->tq_nthreads;
|
||||
for (i = 0; i < nthreads; i++)
|
||||
if (tq->tq_threads[i])
|
||||
kthread_stop(tq->tq_threads[i]);
|
||||
|
||||
spin_lock_irqsave(&tq->tq_lock, tq->tq_lock_flags);
|
||||
|
||||
while (!list_empty(&tq->tq_free_list)) {
|
||||
t = list_entry(tq->tq_free_list.next, spl_task_t, t_list);
|
||||
list_del_init(&t->t_list);
|
||||
task_free(tq, t);
|
||||
}
|
||||
|
||||
ASSERT(tq->tq_nthreads == 0);
|
||||
ASSERT(tq->tq_nalloc == 0);
|
||||
ASSERT(list_empty(&tq->tq_free_list));
|
||||
ASSERT(list_empty(&tq->tq_work_list));
|
||||
ASSERT(list_empty(&tq->tq_pend_list));
|
||||
|
||||
spin_unlock_irqrestore(&tq->tq_lock, tq->tq_lock_flags);
|
||||
kmem_free(tq->tq_threads, nthreads * sizeof(spl_task_t *));
|
||||
kmem_free(tq, sizeof(taskq_t));
|
||||
|
||||
EXIT;
|
||||
}
|
||||
EXPORT_SYMBOL(__taskq_destroy);
|
||||
|
||||
int
|
||||
spl_taskq_init(void)
|
||||
{
|
||||
ENTRY;
|
||||
|
||||
system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512,
|
||||
TASKQ_PREPOPULATE);
|
||||
if (system_taskq == NULL)
|
||||
RETURN(1);
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
void
|
||||
spl_taskq_fini(void)
|
||||
{
|
||||
ENTRY;
|
||||
taskq_destroy(system_taskq);
|
||||
EXIT;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/thread.h>
|
||||
#include <sys/kmem.h>
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_THREAD
|
||||
|
||||
/*
|
||||
* Thread interfaces
|
||||
*/
|
||||
typedef struct thread_priv_s {
|
||||
unsigned long tp_magic; /* Magic */
|
||||
int tp_name_size; /* Name size */
|
||||
char *tp_name; /* Name (without _thread suffix) */
|
||||
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 */
|
||||
} thread_priv_t;
|
||||
|
||||
static int
|
||||
thread_generic_wrapper(void *arg)
|
||||
{
|
||||
thread_priv_t *tp = (thread_priv_t *)arg;
|
||||
void (*func)(void *);
|
||||
void *args;
|
||||
|
||||
ASSERT(tp->tp_magic == TP_MAGIC);
|
||||
func = tp->tp_func;
|
||||
args = tp->tp_args;
|
||||
set_current_state(tp->tp_state);
|
||||
set_user_nice((kthread_t *)get_current(), PRIO_TO_NICE(tp->tp_pri));
|
||||
kmem_free(tp->tp_name, tp->tp_name_size);
|
||||
kmem_free(tp, sizeof(thread_priv_t));
|
||||
|
||||
if (func)
|
||||
func(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
__thread_exit(void)
|
||||
{
|
||||
ENTRY;
|
||||
EXIT;
|
||||
complete_and_exit(NULL, 0);
|
||||
/* Unreachable */
|
||||
}
|
||||
EXPORT_SYMBOL(__thread_exit);
|
||||
|
||||
/* 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, thread_func_t func,
|
||||
const char *name, void *args, size_t len, int *pp,
|
||||
int state, pri_t pri)
|
||||
{
|
||||
thread_priv_t *tp;
|
||||
struct task_struct *tsk;
|
||||
char *p;
|
||||
ENTRY;
|
||||
|
||||
/* Option pp is simply ignored */
|
||||
/* Variable stack size unsupported */
|
||||
ASSERT(stk == NULL);
|
||||
|
||||
tp = kmem_alloc(sizeof(thread_priv_t), KM_SLEEP);
|
||||
if (tp == NULL)
|
||||
RETURN(NULL);
|
||||
|
||||
tp->tp_magic = TP_MAGIC;
|
||||
tp->tp_name_size = strlen(name) + 1;
|
||||
|
||||
tp->tp_name = kmem_alloc(tp->tp_name_size, KM_SLEEP);
|
||||
if (tp->tp_name == NULL) {
|
||||
kmem_free(tp, sizeof(thread_priv_t));
|
||||
RETURN(NULL);
|
||||
}
|
||||
|
||||
strncpy(tp->tp_name, name, tp->tp_name_size);
|
||||
|
||||
/* Strip trailing "_thread" from passed name which will be the func
|
||||
* name since the exposed API has no parameter for passing a name.
|
||||
*/
|
||||
p = strstr(tp->tp_name, "_thread");
|
||||
if (p)
|
||||
p[0] = '\0';
|
||||
|
||||
tp->tp_func = func;
|
||||
tp->tp_args = args;
|
||||
tp->tp_len = len;
|
||||
tp->tp_state = state;
|
||||
tp->tp_pri = pri;
|
||||
|
||||
tsk = kthread_create(thread_generic_wrapper, (void *)tp, tp->tp_name);
|
||||
if (IS_ERR(tsk)) {
|
||||
CERROR("Failed to create thread: %ld\n", PTR_ERR(tsk));
|
||||
RETURN(NULL);
|
||||
}
|
||||
|
||||
wake_up_process(tsk);
|
||||
RETURN((kthread_t *)tsk);
|
||||
}
|
||||
EXPORT_SYMBOL(__thread_create);
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef HAVE_MONOTONIC_CLOCK
|
||||
extern unsigned long long monotonic_clock(void);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_TIME
|
||||
|
||||
void
|
||||
__gethrestime(timestruc_t *ts)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
do_gettimeofday(&tv);
|
||||
ts->tv_sec = tv.tv_sec;
|
||||
ts->tv_nsec = tv.tv_usec * NSEC_PER_USEC;
|
||||
}
|
||||
EXPORT_SYMBOL(__gethrestime);
|
||||
|
||||
/* Use monotonic_clock() by default. It's faster and is available on older
|
||||
* kernels, but few architectures have them, so we must fallback to
|
||||
* do_posix_clock_monotonic_gettime().
|
||||
*/
|
||||
hrtime_t
|
||||
__gethrtime(void) {
|
||||
#ifdef HAVE_MONOTONIC_CLOCK
|
||||
unsigned long long res = monotonic_clock();
|
||||
|
||||
/* Deal with signed/unsigned mismatch */
|
||||
return (hrtime_t)(res & ~(1ULL << 63));
|
||||
#else
|
||||
int64_t j = get_jiffies_64();
|
||||
|
||||
return j * NSEC_PER_SEC / HZ;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(__gethrtime);
|
||||
|
||||
/* set_normalized_timespec() API changes
|
||||
* 2.6.0 - 2.6.15: Inline function provided by linux/time.h
|
||||
* 2.6.16 - 2.6.25: Function prototype defined but not exported
|
||||
* 2.6.26 - 2.6.x: Function defined and exported
|
||||
*/
|
||||
#if !defined(HAVE_SET_NORMALIZED_TIMESPEC_INLINE) && \
|
||||
!defined(HAVE_SET_NORMALIZED_TIMESPEC_EXPORT)
|
||||
void
|
||||
set_normalized_timespec(struct timespec *ts, time_t sec, long nsec)
|
||||
{
|
||||
while (nsec >= NSEC_PER_SEC) {
|
||||
nsec -= NSEC_PER_SEC;
|
||||
++sec;
|
||||
}
|
||||
while (nsec < 0) {
|
||||
nsec += NSEC_PER_SEC;
|
||||
--sec;
|
||||
}
|
||||
ts->tv_sec = sec;
|
||||
ts->tv_nsec = nsec;
|
||||
}
|
||||
EXPORT_SYMBOL(set_normalized_timespec);
|
||||
#endif
|
||||
@@ -0,0 +1,678 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
|
||||
#ifdef DEBUG_SUBSYSTEM
|
||||
#undef DEBUG_SUBSYSTEM
|
||||
#endif
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_VNODE
|
||||
|
||||
void *rootdir = NULL;
|
||||
EXPORT_SYMBOL(rootdir);
|
||||
|
||||
static spl_kmem_cache_t *vn_cache;
|
||||
static spl_kmem_cache_t *vn_file_cache;
|
||||
|
||||
static spinlock_t vn_file_lock = SPIN_LOCK_UNLOCKED;
|
||||
static LIST_HEAD(vn_file_list);
|
||||
|
||||
static vtype_t
|
||||
vn_get_sol_type(umode_t mode)
|
||||
{
|
||||
if (S_ISREG(mode))
|
||||
return VREG;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
return VDIR;
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
return VCHR;
|
||||
|
||||
if (S_ISBLK(mode))
|
||||
return VBLK;
|
||||
|
||||
if (S_ISFIFO(mode))
|
||||
return VFIFO;
|
||||
|
||||
if (S_ISLNK(mode))
|
||||
return VLNK;
|
||||
|
||||
if (S_ISSOCK(mode))
|
||||
return VSOCK;
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
return VCHR;
|
||||
|
||||
return VNON;
|
||||
} /* vn_get_sol_type() */
|
||||
|
||||
vnode_t *
|
||||
vn_alloc(int flag)
|
||||
{
|
||||
vnode_t *vp;
|
||||
ENTRY;
|
||||
|
||||
vp = kmem_cache_alloc(vn_cache, flag);
|
||||
if (vp != NULL) {
|
||||
vp->v_file = NULL;
|
||||
vp->v_type = 0;
|
||||
}
|
||||
|
||||
RETURN(vp);
|
||||
} /* vn_alloc() */
|
||||
EXPORT_SYMBOL(vn_alloc);
|
||||
|
||||
void
|
||||
vn_free(vnode_t *vp)
|
||||
{
|
||||
ENTRY;
|
||||
kmem_cache_free(vn_cache, vp);
|
||||
EXIT;
|
||||
} /* vn_free() */
|
||||
EXPORT_SYMBOL(vn_free);
|
||||
|
||||
int
|
||||
vn_open(const char *path, uio_seg_t seg, int flags, int mode,
|
||||
vnode_t **vpp, int x1, void *x2)
|
||||
{
|
||||
struct file *fp;
|
||||
struct kstat stat;
|
||||
int rc, saved_umask = 0;
|
||||
vnode_t *vp;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(flags & (FWRITE | FREAD));
|
||||
ASSERT(seg == UIO_SYSSPACE);
|
||||
ASSERT(vpp);
|
||||
*vpp = NULL;
|
||||
|
||||
if (!(flags & FCREAT) && (flags & FWRITE))
|
||||
flags |= FEXCL;
|
||||
|
||||
/* Note for filp_open() the two low bits must be remapped to mean:
|
||||
* 01 - read-only -> 00 read-only
|
||||
* 10 - write-only -> 01 write-only
|
||||
* 11 - read-write -> 10 read-write
|
||||
*/
|
||||
flags--;
|
||||
|
||||
if (flags & FCREAT)
|
||||
saved_umask = xchg(¤t->fs->umask, 0);
|
||||
|
||||
fp = filp_open(path, flags, mode);
|
||||
|
||||
if (flags & FCREAT)
|
||||
(void)xchg(¤t->fs->umask, saved_umask);
|
||||
|
||||
if (IS_ERR(fp))
|
||||
RETURN(-PTR_ERR(fp));
|
||||
|
||||
rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
|
||||
if (rc) {
|
||||
filp_close(fp, 0);
|
||||
RETURN(-rc);
|
||||
}
|
||||
|
||||
vp = vn_alloc(KM_SLEEP);
|
||||
if (!vp) {
|
||||
filp_close(fp, 0);
|
||||
RETURN(ENOMEM);
|
||||
}
|
||||
|
||||
mutex_enter(&vp->v_lock);
|
||||
vp->v_type = vn_get_sol_type(stat.mode);
|
||||
vp->v_file = fp;
|
||||
*vpp = vp;
|
||||
mutex_exit(&vp->v_lock);
|
||||
|
||||
RETURN(0);
|
||||
} /* vn_open() */
|
||||
EXPORT_SYMBOL(vn_open);
|
||||
|
||||
int
|
||||
vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
|
||||
vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
|
||||
{
|
||||
char *realpath;
|
||||
int len, rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(vp == rootdir);
|
||||
|
||||
len = strlen(path) + 2;
|
||||
realpath = kmalloc(len, GFP_KERNEL);
|
||||
if (!realpath)
|
||||
RETURN(ENOMEM);
|
||||
|
||||
(void)snprintf(realpath, len, "/%s", path);
|
||||
rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
|
||||
kfree(realpath);
|
||||
|
||||
RETURN(rc);
|
||||
} /* vn_openat() */
|
||||
EXPORT_SYMBOL(vn_openat);
|
||||
|
||||
int
|
||||
vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
|
||||
uio_seg_t seg, int x1, rlim64_t x2, void *x3, ssize_t *residp)
|
||||
{
|
||||
loff_t offset;
|
||||
mm_segment_t saved_fs;
|
||||
struct file *fp;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(uio == UIO_WRITE || uio == UIO_READ);
|
||||
ASSERT(vp);
|
||||
ASSERT(vp->v_file);
|
||||
ASSERT(seg == UIO_SYSSPACE);
|
||||
ASSERT(x1 == 0);
|
||||
ASSERT(x2 == RLIM64_INFINITY);
|
||||
|
||||
offset = off;
|
||||
fp = vp->v_file;
|
||||
|
||||
/* Writable user data segment must be briefly increased for this
|
||||
* process so we can use the user space read call paths to write
|
||||
* in to memory allocated by the kernel. */
|
||||
saved_fs = get_fs();
|
||||
set_fs(get_ds());
|
||||
|
||||
if (uio & UIO_WRITE)
|
||||
rc = vfs_write(fp, addr, len, &offset);
|
||||
else
|
||||
rc = vfs_read(fp, addr, len, &offset);
|
||||
|
||||
set_fs(saved_fs);
|
||||
|
||||
if (rc < 0)
|
||||
RETURN(-rc);
|
||||
|
||||
if (residp) {
|
||||
*residp = len - rc;
|
||||
} else {
|
||||
if (rc != len)
|
||||
RETURN(EIO);
|
||||
}
|
||||
|
||||
RETURN(0);
|
||||
} /* vn_rdwr() */
|
||||
EXPORT_SYMBOL(vn_rdwr);
|
||||
|
||||
int
|
||||
vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
|
||||
{
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(vp);
|
||||
ASSERT(vp->v_file);
|
||||
|
||||
rc = filp_close(vp->v_file, 0);
|
||||
vn_free(vp);
|
||||
|
||||
RETURN(-rc);
|
||||
} /* vn_close() */
|
||||
EXPORT_SYMBOL(vn_close);
|
||||
|
||||
/* vn_seek() does not actually seek it only performs bounds checking on the
|
||||
* proposed seek. We perform minimal checking and allow vn_rdwr() to catch
|
||||
* anything more serious. */
|
||||
int
|
||||
vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
|
||||
{
|
||||
return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
|
||||
}
|
||||
EXPORT_SYMBOL(vn_seek);
|
||||
|
||||
static struct dentry *
|
||||
vn_lookup_hash(struct nameidata *nd)
|
||||
{
|
||||
return lookup_one_len(nd->last.name, nd->nd_dentry, nd->last.len);
|
||||
} /* lookup_hash() */
|
||||
|
||||
static void
|
||||
vn_path_release(struct nameidata *nd)
|
||||
{
|
||||
dput(nd->nd_dentry);
|
||||
mntput(nd->nd_mnt);
|
||||
}
|
||||
|
||||
/* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */
|
||||
int
|
||||
vn_remove(const char *path, uio_seg_t seg, int flags)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct nameidata nd;
|
||||
struct inode *inode = NULL;
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(seg == UIO_SYSSPACE);
|
||||
ASSERT(flags == RMFILE);
|
||||
|
||||
rc = path_lookup(path, LOOKUP_PARENT, &nd);
|
||||
if (rc)
|
||||
GOTO(exit, rc);
|
||||
|
||||
rc = -EISDIR;
|
||||
if (nd.last_type != LAST_NORM)
|
||||
GOTO(exit1, rc);
|
||||
|
||||
#ifdef HAVE_INODE_I_MUTEX
|
||||
mutex_lock_nested(&nd.nd_dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
#else
|
||||
down(&nd.nd_dentry->d_inode->i_sem);
|
||||
#endif
|
||||
dentry = vn_lookup_hash(&nd);
|
||||
rc = PTR_ERR(dentry);
|
||||
if (!IS_ERR(dentry)) {
|
||||
/* Why not before? Because we want correct rc value */
|
||||
if (nd.last.name[nd.last.len])
|
||||
GOTO(slashes, rc);
|
||||
|
||||
inode = dentry->d_inode;
|
||||
if (inode)
|
||||
atomic_inc(&inode->i_count);
|
||||
rc = vfs_unlink(nd.nd_dentry->d_inode, dentry);
|
||||
exit2:
|
||||
dput(dentry);
|
||||
}
|
||||
#ifdef HAVE_INODE_I_MUTEX
|
||||
mutex_unlock(&nd.nd_dentry->d_inode->i_mutex);
|
||||
#else
|
||||
up(&nd.nd_dentry->d_inode->i_sem);
|
||||
#endif
|
||||
if (inode)
|
||||
iput(inode); /* truncate the inode here */
|
||||
exit1:
|
||||
vn_path_release(&nd);
|
||||
exit:
|
||||
RETURN(-rc);
|
||||
|
||||
slashes:
|
||||
rc = !dentry->d_inode ? -ENOENT :
|
||||
S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
|
||||
GOTO(exit2, rc);
|
||||
} /* vn_remove() */
|
||||
EXPORT_SYMBOL(vn_remove);
|
||||
|
||||
/* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */
|
||||
int
|
||||
vn_rename(const char *oldname, const char *newname, int x1)
|
||||
{
|
||||
struct dentry * old_dir, * new_dir;
|
||||
struct dentry * old_dentry, *new_dentry;
|
||||
struct dentry * trap;
|
||||
struct nameidata oldnd, newnd;
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
|
||||
if (rc)
|
||||
GOTO(exit, rc);
|
||||
|
||||
rc = path_lookup(newname, LOOKUP_PARENT, &newnd);
|
||||
if (rc)
|
||||
GOTO(exit1, rc);
|
||||
|
||||
rc = -EXDEV;
|
||||
if (oldnd.nd_mnt != newnd.nd_mnt)
|
||||
GOTO(exit2, rc);
|
||||
|
||||
old_dir = oldnd.nd_dentry;
|
||||
rc = -EBUSY;
|
||||
if (oldnd.last_type != LAST_NORM)
|
||||
GOTO(exit2, rc);
|
||||
|
||||
new_dir = newnd.nd_dentry;
|
||||
if (newnd.last_type != LAST_NORM)
|
||||
GOTO(exit2, rc);
|
||||
|
||||
trap = lock_rename(new_dir, old_dir);
|
||||
|
||||
old_dentry = vn_lookup_hash(&oldnd);
|
||||
|
||||
rc = PTR_ERR(old_dentry);
|
||||
if (IS_ERR(old_dentry))
|
||||
GOTO(exit3, rc);
|
||||
|
||||
/* source must exist */
|
||||
rc = -ENOENT;
|
||||
if (!old_dentry->d_inode)
|
||||
GOTO(exit4, rc);
|
||||
|
||||
/* unless the source is a directory trailing slashes give -ENOTDIR */
|
||||
if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
|
||||
rc = -ENOTDIR;
|
||||
if (oldnd.last.name[oldnd.last.len])
|
||||
GOTO(exit4, rc);
|
||||
if (newnd.last.name[newnd.last.len])
|
||||
GOTO(exit4, rc);
|
||||
}
|
||||
|
||||
/* source should not be ancestor of target */
|
||||
rc = -EINVAL;
|
||||
if (old_dentry == trap)
|
||||
GOTO(exit4, rc);
|
||||
|
||||
new_dentry = vn_lookup_hash(&newnd);
|
||||
rc = PTR_ERR(new_dentry);
|
||||
if (IS_ERR(new_dentry))
|
||||
GOTO(exit4, rc);
|
||||
|
||||
/* target should not be an ancestor of source */
|
||||
rc = -ENOTEMPTY;
|
||||
if (new_dentry == trap)
|
||||
GOTO(exit5, rc);
|
||||
|
||||
rc = vfs_rename(old_dir->d_inode, old_dentry,
|
||||
new_dir->d_inode, new_dentry);
|
||||
exit5:
|
||||
dput(new_dentry);
|
||||
exit4:
|
||||
dput(old_dentry);
|
||||
exit3:
|
||||
unlock_rename(new_dir, old_dir);
|
||||
exit2:
|
||||
vn_path_release(&newnd);
|
||||
exit1:
|
||||
vn_path_release(&oldnd);
|
||||
exit:
|
||||
RETURN(-rc);
|
||||
}
|
||||
EXPORT_SYMBOL(vn_rename);
|
||||
|
||||
int
|
||||
vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
|
||||
{
|
||||
struct file *fp;
|
||||
struct kstat stat;
|
||||
int rc;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(vp);
|
||||
ASSERT(vp->v_file);
|
||||
ASSERT(vap);
|
||||
|
||||
fp = vp->v_file;
|
||||
|
||||
rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
|
||||
if (rc)
|
||||
RETURN(-rc);
|
||||
|
||||
vap->va_type = vn_get_sol_type(stat.mode);
|
||||
vap->va_mode = stat.mode;
|
||||
vap->va_uid = stat.uid;
|
||||
vap->va_gid = stat.gid;
|
||||
vap->va_fsid = 0;
|
||||
vap->va_nodeid = stat.ino;
|
||||
vap->va_nlink = stat.nlink;
|
||||
vap->va_size = stat.size;
|
||||
vap->va_blocksize = stat.blksize;
|
||||
vap->va_atime.tv_sec = stat.atime.tv_sec;
|
||||
vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC;
|
||||
vap->va_mtime.tv_sec = stat.mtime.tv_sec;
|
||||
vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC;
|
||||
vap->va_ctime.tv_sec = stat.ctime.tv_sec;
|
||||
vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC;
|
||||
vap->va_rdev = stat.rdev;
|
||||
vap->va_blocks = stat.blocks;
|
||||
|
||||
RETURN(0);
|
||||
}
|
||||
EXPORT_SYMBOL(vn_getattr);
|
||||
|
||||
int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
|
||||
{
|
||||
int datasync = 0;
|
||||
ENTRY;
|
||||
|
||||
ASSERT(vp);
|
||||
ASSERT(vp->v_file);
|
||||
|
||||
if (flags & FDSYNC)
|
||||
datasync = 1;
|
||||
|
||||
RETURN(-file_fsync(vp->v_file, vp->v_file->f_dentry, datasync));
|
||||
} /* vn_fsync() */
|
||||
EXPORT_SYMBOL(vn_fsync);
|
||||
|
||||
/* Function must be called while holding the vn_file_lock */
|
||||
static file_t *
|
||||
file_find(int fd)
|
||||
{
|
||||
file_t *fp;
|
||||
|
||||
ASSERT(spin_is_locked(&vn_file_lock));
|
||||
|
||||
list_for_each_entry(fp, &vn_file_list, f_list) {
|
||||
if (fd == fp->f_fd) {
|
||||
ASSERT(atomic_read(&fp->f_ref) != 0);
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
} /* file_find() */
|
||||
|
||||
file_t *
|
||||
vn_getf(int fd)
|
||||
{
|
||||
struct kstat stat;
|
||||
struct file *lfp;
|
||||
file_t *fp;
|
||||
vnode_t *vp;
|
||||
int rc = 0;
|
||||
ENTRY;
|
||||
|
||||
/* Already open just take an extra reference */
|
||||
spin_lock(&vn_file_lock);
|
||||
|
||||
fp = file_find(fd);
|
||||
if (fp) {
|
||||
atomic_inc(&fp->f_ref);
|
||||
spin_unlock(&vn_file_lock);
|
||||
RETURN(fp);
|
||||
}
|
||||
|
||||
spin_unlock(&vn_file_lock);
|
||||
|
||||
/* File was not yet opened create the object and setup */
|
||||
fp = kmem_cache_alloc(vn_file_cache, KM_SLEEP);
|
||||
if (fp == NULL)
|
||||
GOTO(out, rc);
|
||||
|
||||
mutex_enter(&fp->f_lock);
|
||||
|
||||
fp->f_fd = fd;
|
||||
fp->f_offset = 0;
|
||||
atomic_inc(&fp->f_ref);
|
||||
|
||||
lfp = fget(fd);
|
||||
if (lfp == NULL)
|
||||
GOTO(out_mutex, rc);
|
||||
|
||||
vp = vn_alloc(KM_SLEEP);
|
||||
if (vp == NULL)
|
||||
GOTO(out_fget, rc);
|
||||
|
||||
if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat))
|
||||
GOTO(out_vnode, rc);
|
||||
|
||||
mutex_enter(&vp->v_lock);
|
||||
vp->v_type = vn_get_sol_type(stat.mode);
|
||||
vp->v_file = lfp;
|
||||
mutex_exit(&vp->v_lock);
|
||||
|
||||
fp->f_vnode = vp;
|
||||
fp->f_file = lfp;
|
||||
|
||||
/* Put it on the tracking list */
|
||||
spin_lock(&vn_file_lock);
|
||||
list_add(&fp->f_list, &vn_file_list);
|
||||
spin_unlock(&vn_file_lock);
|
||||
|
||||
mutex_exit(&fp->f_lock);
|
||||
RETURN(fp);
|
||||
|
||||
out_vnode:
|
||||
vn_free(vp);
|
||||
out_fget:
|
||||
fput(lfp);
|
||||
out_mutex:
|
||||
mutex_exit(&fp->f_lock);
|
||||
kmem_cache_free(vn_file_cache, fp);
|
||||
out:
|
||||
RETURN(NULL);
|
||||
} /* getf() */
|
||||
EXPORT_SYMBOL(getf);
|
||||
|
||||
static void releasef_locked(file_t *fp)
|
||||
{
|
||||
ASSERT(fp->f_file);
|
||||
ASSERT(fp->f_vnode);
|
||||
|
||||
/* Unlinked from list, no refs, safe to free outside mutex */
|
||||
fput(fp->f_file);
|
||||
vn_free(fp->f_vnode);
|
||||
|
||||
kmem_cache_free(vn_file_cache, fp);
|
||||
}
|
||||
|
||||
void
|
||||
vn_releasef(int fd)
|
||||
{
|
||||
file_t *fp;
|
||||
ENTRY;
|
||||
|
||||
spin_lock(&vn_file_lock);
|
||||
fp = file_find(fd);
|
||||
if (fp) {
|
||||
atomic_dec(&fp->f_ref);
|
||||
if (atomic_read(&fp->f_ref) > 0) {
|
||||
spin_unlock(&vn_file_lock);
|
||||
EXIT;
|
||||
return;
|
||||
}
|
||||
|
||||
list_del(&fp->f_list);
|
||||
releasef_locked(fp);
|
||||
}
|
||||
spin_unlock(&vn_file_lock);
|
||||
|
||||
EXIT;
|
||||
return;
|
||||
} /* releasef() */
|
||||
EXPORT_SYMBOL(releasef);
|
||||
|
||||
static int
|
||||
vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
|
||||
{
|
||||
struct vnode *vp = buf;
|
||||
|
||||
mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
|
||||
return (0);
|
||||
} /* vn_cache_constructor() */
|
||||
|
||||
static void
|
||||
vn_cache_destructor(void *buf, void *cdrarg)
|
||||
{
|
||||
struct vnode *vp = buf;
|
||||
|
||||
mutex_destroy(&vp->v_lock);
|
||||
} /* vn_cache_destructor() */
|
||||
|
||||
static int
|
||||
vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
|
||||
{
|
||||
file_t *fp = buf;
|
||||
|
||||
atomic_set(&fp->f_ref, 0);
|
||||
mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||
INIT_LIST_HEAD(&fp->f_list);
|
||||
|
||||
return (0);
|
||||
} /* file_cache_constructor() */
|
||||
|
||||
static void
|
||||
vn_file_cache_destructor(void *buf, void *cdrarg)
|
||||
{
|
||||
file_t *fp = buf;
|
||||
|
||||
mutex_destroy(&fp->f_lock);
|
||||
} /* vn_file_cache_destructor() */
|
||||
|
||||
int
|
||||
vn_init(void)
|
||||
{
|
||||
ENTRY;
|
||||
vn_cache = kmem_cache_create("spl_vn_cache",
|
||||
sizeof(struct vnode), 64,
|
||||
vn_cache_constructor,
|
||||
vn_cache_destructor,
|
||||
NULL, NULL, NULL, 0);
|
||||
|
||||
vn_file_cache = kmem_cache_create("spl_vn_file_cache",
|
||||
sizeof(file_t), 64,
|
||||
vn_file_cache_constructor,
|
||||
vn_file_cache_destructor,
|
||||
NULL, NULL, NULL, 0);
|
||||
RETURN(0);
|
||||
} /* vn_init() */
|
||||
|
||||
void
|
||||
vn_fini(void)
|
||||
{
|
||||
file_t *fp, *next_fp;
|
||||
int leaked = 0;
|
||||
ENTRY;
|
||||
|
||||
spin_lock(&vn_file_lock);
|
||||
|
||||
list_for_each_entry_safe(fp, next_fp, &vn_file_list, f_list) {
|
||||
list_del(&fp->f_list);
|
||||
releasef_locked(fp);
|
||||
leaked++;
|
||||
}
|
||||
|
||||
kmem_cache_destroy(vn_file_cache);
|
||||
vn_file_cache = NULL;
|
||||
spin_unlock(&vn_file_lock);
|
||||
|
||||
if (leaked > 0)
|
||||
CWARN("Warning %d files leaked\n", leaked);
|
||||
|
||||
kmem_cache_destroy(vn_cache);
|
||||
|
||||
EXIT;
|
||||
return;
|
||||
} /* vn_fini() */
|
||||
@@ -0,0 +1,47 @@
|
||||
# Makefile.in for splat kernel module
|
||||
|
||||
MODULES := splat
|
||||
DISTFILES = Makefile.in *.c *.h
|
||||
EXTRA_CFLAGS = @KERNELCPPFLAGS@
|
||||
|
||||
# Solaris Porting LAyer Tests
|
||||
obj-m := splat.o
|
||||
|
||||
splat-objs += splat-ctl.o
|
||||
splat-objs += splat-kmem.o
|
||||
splat-objs += splat-taskq.o
|
||||
splat-objs += splat-random.o
|
||||
splat-objs += splat-mutex.o
|
||||
splat-objs += splat-condvar.o
|
||||
splat-objs += splat-thread.o
|
||||
splat-objs += splat-rwlock.o
|
||||
splat-objs += splat-time.o
|
||||
splat-objs += splat-vnode.o
|
||||
splat-objs += splat-kobj.o
|
||||
splat-objs += splat-atomic.o
|
||||
splat-objs += splat-list.o
|
||||
splat-objs += splat-generic.o
|
||||
|
||||
splatmodule := splat.ko
|
||||
splatmoduledir := @kmoduledir@/kernel/lib/
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)$(splatmoduledir)
|
||||
$(INSTALL) -m 644 $(splatmodule) $(DESTDIR)$(splatmoduledir)/$(splatmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(splatmoduledir)/$(splatmodule)
|
||||
-/sbin/depmod -a
|
||||
|
||||
clean:
|
||||
-rm -f $(splmodule) *.o .*.cmd *.mod.c *.ko *.s */*.o
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile
|
||||
rm -rf .tmp_versions
|
||||
|
||||
maintainer-clean: distclean
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
cp -p $(DISTFILES) $(distdir)
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_ATOMIC 0x0b00
|
||||
#define SPLAT_ATOMIC_NAME "atomic"
|
||||
#define SPLAT_ATOMIC_DESC "Kernel Atomic Tests"
|
||||
|
||||
#define SPLAT_ATOMIC_TEST1_ID 0x0b01
|
||||
#define SPLAT_ATOMIC_TEST1_NAME "64-bit"
|
||||
#define SPLAT_ATOMIC_TEST1_DESC "Validate 64-bit atomic ops"
|
||||
|
||||
#define SPLAT_ATOMIC_TEST_MAGIC 0x43435454UL
|
||||
#define SPLAT_ATOMIC_INIT_VALUE 10000000UL
|
||||
|
||||
typedef enum {
|
||||
SPLAT_ATOMIC_INC_64 = 0,
|
||||
SPLAT_ATOMIC_DEC_64 = 1,
|
||||
SPLAT_ATOMIC_ADD_64 = 2,
|
||||
SPLAT_ATOMIC_SUB_64 = 3,
|
||||
SPLAT_ATOMIC_ADD_64_NV = 4,
|
||||
SPLAT_ATOMIC_SUB_64_NV = 5,
|
||||
SPLAT_ATOMIC_COUNT_64 = 6
|
||||
} atomic_op_t;
|
||||
|
||||
typedef struct atomic_priv {
|
||||
unsigned long ap_magic;
|
||||
struct file *ap_file;
|
||||
spinlock_t ap_lock;
|
||||
wait_queue_head_t ap_waitq;
|
||||
volatile uint64_t ap_atomic;
|
||||
volatile uint64_t ap_atomic_exited;
|
||||
atomic_op_t ap_op;
|
||||
|
||||
} atomic_priv_t;
|
||||
|
||||
static void
|
||||
splat_atomic_work(void *priv)
|
||||
{
|
||||
atomic_priv_t *ap;
|
||||
atomic_op_t op;
|
||||
int i;
|
||||
|
||||
ap = (atomic_priv_t *)priv;
|
||||
ASSERT(ap->ap_magic == SPLAT_ATOMIC_TEST_MAGIC);
|
||||
|
||||
spin_lock(&ap->ap_lock);
|
||||
op = ap->ap_op;
|
||||
wake_up(&ap->ap_waitq);
|
||||
spin_unlock(&ap->ap_lock);
|
||||
|
||||
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
|
||||
"Thread %d successfully started: %lu/%lu\n", op,
|
||||
(long unsigned)ap->ap_atomic,
|
||||
(long unsigned)ap->ap_atomic_exited);
|
||||
|
||||
for (i = 0; i < SPLAT_ATOMIC_INIT_VALUE / 10; i++) {
|
||||
|
||||
/* Periodically sleep to mix up the ordering */
|
||||
if ((i % (SPLAT_ATOMIC_INIT_VALUE / 100)) == 0) {
|
||||
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
|
||||
"Thread %d sleeping: %lu/%lu\n", op,
|
||||
(long unsigned)ap->ap_atomic,
|
||||
(long unsigned)ap->ap_atomic_exited);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ / 100);
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case SPLAT_ATOMIC_INC_64:
|
||||
atomic_inc_64(&ap->ap_atomic);
|
||||
break;
|
||||
case SPLAT_ATOMIC_DEC_64:
|
||||
atomic_dec_64(&ap->ap_atomic);
|
||||
break;
|
||||
case SPLAT_ATOMIC_ADD_64:
|
||||
atomic_add_64(&ap->ap_atomic, 3);
|
||||
break;
|
||||
case SPLAT_ATOMIC_SUB_64:
|
||||
atomic_sub_64(&ap->ap_atomic, 3);
|
||||
break;
|
||||
case SPLAT_ATOMIC_ADD_64_NV:
|
||||
atomic_add_64_nv(&ap->ap_atomic, 5);
|
||||
break;
|
||||
case SPLAT_ATOMIC_SUB_64_NV:
|
||||
atomic_sub_64_nv(&ap->ap_atomic, 5);
|
||||
break;
|
||||
default:
|
||||
SBUG();
|
||||
}
|
||||
}
|
||||
|
||||
atomic_inc_64(&ap->ap_atomic_exited);
|
||||
|
||||
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
|
||||
"Thread %d successfully exited: %lu/%lu\n", op,
|
||||
(long unsigned)ap->ap_atomic,
|
||||
(long unsigned)ap->ap_atomic_exited);
|
||||
|
||||
wake_up(&ap->ap_waitq);
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
static int
|
||||
splat_atomic_test1_cond(atomic_priv_t *ap, int started)
|
||||
{
|
||||
return (ap->ap_atomic_exited == started);
|
||||
}
|
||||
|
||||
static int
|
||||
splat_atomic_test1(struct file *file, void *arg)
|
||||
{
|
||||
atomic_priv_t ap;
|
||||
DEFINE_WAIT(wait);
|
||||
kthread_t *thr;
|
||||
int i, rc = 0;
|
||||
|
||||
ap.ap_magic = SPLAT_ATOMIC_TEST_MAGIC;
|
||||
ap.ap_file = file;
|
||||
spin_lock_init(&ap.ap_lock);
|
||||
init_waitqueue_head(&ap.ap_waitq);
|
||||
ap.ap_atomic = SPLAT_ATOMIC_INIT_VALUE;
|
||||
ap.ap_atomic_exited = 0;
|
||||
|
||||
for (i = 0; i < SPLAT_ATOMIC_COUNT_64; i++) {
|
||||
spin_lock(&ap.ap_lock);
|
||||
ap.ap_op = i;
|
||||
|
||||
thr = (kthread_t *)thread_create(NULL, 0, splat_atomic_work,
|
||||
&ap, 0, &p0, TS_RUN,
|
||||
minclsyspri);
|
||||
if (thr == NULL) {
|
||||
rc = -ESRCH;
|
||||
spin_unlock(&ap.ap_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepare to wait, the new thread will wake us once it
|
||||
* has made a copy of the unique private passed data */
|
||||
prepare_to_wait(&ap.ap_waitq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
spin_unlock(&ap.ap_lock);
|
||||
schedule();
|
||||
}
|
||||
|
||||
wait_event_interruptible(ap.ap_waitq, splat_atomic_test1_cond(&ap, i));
|
||||
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME, "Only started "
|
||||
"%d/%d test threads\n", i, SPLAT_ATOMIC_COUNT_64);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ap.ap_atomic != SPLAT_ATOMIC_INIT_VALUE) {
|
||||
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME,
|
||||
"Final value %lu does not match initial value %lu\n",
|
||||
(long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME,
|
||||
"Success initial and final values match, %lu == %lu\n",
|
||||
(long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_atomic_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_ATOMIC_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_ATOMIC_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_ATOMIC;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_ATOMIC_TEST1_NAME, SPLAT_ATOMIC_TEST1_DESC,
|
||||
SPLAT_ATOMIC_TEST1_ID, splat_atomic_test1);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_atomic_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_ATOMIC_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_atomic_id(void) {
|
||||
return SPLAT_SUBSYSTEM_ATOMIC;
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_CONDVAR 0x0500
|
||||
#define SPLAT_CONDVAR_NAME "condvar"
|
||||
#define SPLAT_CONDVAR_DESC "Kernel Condition Variable Tests"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST1_ID 0x0501
|
||||
#define SPLAT_CONDVAR_TEST1_NAME "signal1"
|
||||
#define SPLAT_CONDVAR_TEST1_DESC "Wake a single thread, cv_wait()/cv_signal()"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST2_ID 0x0502
|
||||
#define SPLAT_CONDVAR_TEST2_NAME "broadcast1"
|
||||
#define SPLAT_CONDVAR_TEST2_DESC "Wake all threads, cv_wait()/cv_broadcast()"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST3_ID 0x0503
|
||||
#define SPLAT_CONDVAR_TEST3_NAME "signal2"
|
||||
#define SPLAT_CONDVAR_TEST3_DESC "Wake a single thread, cv_wait_timeout()/cv_signal()"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST4_ID 0x0504
|
||||
#define SPLAT_CONDVAR_TEST4_NAME "broadcast2"
|
||||
#define SPLAT_CONDVAR_TEST4_DESC "Wake all threads, cv_wait_timeout()/cv_broadcast()"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST5_ID 0x0505
|
||||
#define SPLAT_CONDVAR_TEST5_NAME "timeout"
|
||||
#define SPLAT_CONDVAR_TEST5_DESC "Timeout thread, cv_wait_timeout()"
|
||||
|
||||
#define SPLAT_CONDVAR_TEST_MAGIC 0x115599DDUL
|
||||
#define SPLAT_CONDVAR_TEST_NAME "condvar_test"
|
||||
#define SPLAT_CONDVAR_TEST_COUNT 8
|
||||
|
||||
typedef struct condvar_priv {
|
||||
unsigned long cv_magic;
|
||||
struct file *cv_file;
|
||||
kcondvar_t cv_condvar;
|
||||
kmutex_t cv_mtx;
|
||||
} condvar_priv_t;
|
||||
|
||||
typedef struct condvar_thr {
|
||||
int ct_id;
|
||||
const char *ct_name;
|
||||
condvar_priv_t *ct_cvp;
|
||||
int ct_rc;
|
||||
} condvar_thr_t;
|
||||
|
||||
int
|
||||
splat_condvar_test12_thread(void *arg)
|
||||
{
|
||||
condvar_thr_t *ct = (condvar_thr_t *)arg;
|
||||
condvar_priv_t *cv = ct->ct_cvp;
|
||||
char name[16];
|
||||
|
||||
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name),"%s%d",SPLAT_CONDVAR_TEST_NAME,ct->ct_id);
|
||||
daemonize(name);
|
||||
|
||||
mutex_enter(&cv->cv_mtx);
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread sleeping with %d waiters\n",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
cv_wait(&cv->cv_condvar, &cv->cv_mtx);
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread woken %d waiters remain\n",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
mutex_exit(&cv->cv_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test1(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_priv_t cv;
|
||||
|
||||
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
|
||||
cv.cv_file = file;
|
||||
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&cv.cv_condvar, SPLAT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
|
||||
ct[i].ct_cvp = &cv;
|
||||
ct[i].ct_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST1_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test12_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Wait until all threads are waiting on the condition variable */
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
|
||||
schedule();
|
||||
|
||||
/* Wake a single thread at a time, wait until it exits */
|
||||
for (i = 1; i <= count; i++) {
|
||||
cv_signal(&cv.cv_condvar);
|
||||
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
|
||||
schedule();
|
||||
|
||||
/* Correct behavior 1 thread woken */
|
||||
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
|
||||
continue;
|
||||
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Attempted to "
|
||||
"wake %d thread but work %d threads woke\n",
|
||||
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rc)
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Correctly woke "
|
||||
"%d sleeping threads %d at a time\n", count, 1);
|
||||
|
||||
/* Wait until that last nutex is dropped */
|
||||
while (mutex_owner(&cv.cv_mtx))
|
||||
schedule();
|
||||
|
||||
/* Wake everything for the failure case */
|
||||
cv_broadcast(&cv.cv_condvar);
|
||||
cv_destroy(&cv.cv_condvar);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test2(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_priv_t cv;
|
||||
|
||||
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
|
||||
cv.cv_file = file;
|
||||
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&cv.cv_condvar, SPLAT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
|
||||
ct[i].ct_cvp = &cv;
|
||||
ct[i].ct_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST2_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test12_thread, &ct[i], 0);
|
||||
if (pids[i] > 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Wait until all threads are waiting on the condition variable */
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
|
||||
schedule();
|
||||
|
||||
/* Wake all threads waiting on the condition variable */
|
||||
cv_broadcast(&cv.cv_condvar);
|
||||
|
||||
/* Wait until all threads have exited */
|
||||
while ((atomic_read(&cv.cv_condvar.cv_waiters) > 0) || mutex_owner(&cv.cv_mtx))
|
||||
schedule();
|
||||
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST2_NAME, "Correctly woke all "
|
||||
"%d sleeping threads at once\n", count);
|
||||
|
||||
/* Wake everything for the failure case */
|
||||
cv_destroy(&cv.cv_condvar);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
splat_condvar_test34_thread(void *arg)
|
||||
{
|
||||
condvar_thr_t *ct = (condvar_thr_t *)arg;
|
||||
condvar_priv_t *cv = ct->ct_cvp;
|
||||
char name[16];
|
||||
clock_t rc;
|
||||
|
||||
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d", SPLAT_CONDVAR_TEST_NAME, ct->ct_id);
|
||||
daemonize(name);
|
||||
|
||||
mutex_enter(&cv->cv_mtx);
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread sleeping with %d waiters\n",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
|
||||
/* Sleep no longer than 3 seconds, for this test we should
|
||||
* actually never sleep that long without being woken up. */
|
||||
rc = cv_timedwait(&cv->cv_condvar, &cv->cv_mtx, lbolt + HZ * 3);
|
||||
if (rc == -1) {
|
||||
ct->ct_rc = -ETIMEDOUT;
|
||||
splat_vprint(cv->cv_file, ct->ct_name, "%s thread timed out, "
|
||||
"should have been woken\n", name);
|
||||
} else {
|
||||
splat_vprint(cv->cv_file, ct->ct_name,
|
||||
"%s thread woken %d waiters remain\n",
|
||||
name, atomic_read(&cv->cv_condvar.cv_waiters));
|
||||
}
|
||||
|
||||
mutex_exit(&cv->cv_mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test3(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_priv_t cv;
|
||||
|
||||
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
|
||||
cv.cv_file = file;
|
||||
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&cv.cv_condvar, SPLAT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
|
||||
ct[i].ct_cvp = &cv;
|
||||
ct[i].ct_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test34_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Wait until all threads are waiting on the condition variable */
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
|
||||
schedule();
|
||||
|
||||
/* Wake a single thread at a time, wait until it exits */
|
||||
for (i = 1; i <= count; i++) {
|
||||
cv_signal(&cv.cv_condvar);
|
||||
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
|
||||
schedule();
|
||||
|
||||
/* Correct behavior 1 thread woken */
|
||||
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
|
||||
continue;
|
||||
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to "
|
||||
"wake %d thread but work %d threads woke\n",
|
||||
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Validate no waiting thread timed out early */
|
||||
for (i = 0; i < count; i++)
|
||||
if (ct[i].ct_rc)
|
||||
rc = ct[i].ct_rc;
|
||||
|
||||
if (!rc)
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke "
|
||||
"%d sleeping threads %d at a time\n", count, 1);
|
||||
|
||||
/* Wait until that last nutex is dropped */
|
||||
while (mutex_owner(&cv.cv_mtx))
|
||||
schedule();
|
||||
|
||||
/* Wake everything for the failure case */
|
||||
cv_broadcast(&cv.cv_condvar);
|
||||
cv_destroy(&cv.cv_condvar);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test4(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
|
||||
condvar_priv_t cv;
|
||||
|
||||
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
|
||||
cv.cv_file = file;
|
||||
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&cv.cv_condvar, SPLAT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
|
||||
ct[i].ct_cvp = &cv;
|
||||
ct[i].ct_id = i;
|
||||
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
|
||||
ct[i].ct_rc = 0;
|
||||
|
||||
pids[i] = kernel_thread(splat_condvar_test34_thread, &ct[i], 0);
|
||||
if (pids[i] >= 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Wait until all threads are waiting on the condition variable */
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
|
||||
schedule();
|
||||
|
||||
/* Wake a single thread at a time, wait until it exits */
|
||||
for (i = 1; i <= count; i++) {
|
||||
cv_signal(&cv.cv_condvar);
|
||||
|
||||
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
|
||||
schedule();
|
||||
|
||||
/* Correct behavior 1 thread woken */
|
||||
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
|
||||
continue;
|
||||
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to "
|
||||
"wake %d thread but work %d threads woke\n",
|
||||
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Validate no waiting thread timed out early */
|
||||
for (i = 0; i < count; i++)
|
||||
if (ct[i].ct_rc)
|
||||
rc = ct[i].ct_rc;
|
||||
|
||||
if (!rc)
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke "
|
||||
"%d sleeping threads %d at a time\n", count, 1);
|
||||
|
||||
/* Wait until that last nutex is dropped */
|
||||
while (mutex_owner(&cv.cv_mtx))
|
||||
schedule();
|
||||
|
||||
/* Wake everything for the failure case */
|
||||
cv_broadcast(&cv.cv_condvar);
|
||||
cv_destroy(&cv.cv_condvar);
|
||||
mutex_destroy(&cv.cv_mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_condvar_test5(struct file *file, void *arg)
|
||||
{
|
||||
kcondvar_t condvar;
|
||||
kmutex_t mtx;
|
||||
clock_t time_left, time_before, time_after, time_delta;
|
||||
int64_t whole_delta;
|
||||
int32_t remain_delta;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
cv_init(&condvar, SPLAT_CONDVAR_TEST_NAME, CV_DEFAULT, NULL);
|
||||
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, "Thread going to sleep for "
|
||||
"%d second and expecting to be woken by timeout\n", 1);
|
||||
|
||||
/* Allow a 1 second timeout, plenty long to validate correctness. */
|
||||
time_before = lbolt;
|
||||
mutex_enter(&mtx);
|
||||
time_left = cv_timedwait(&condvar, &mtx, lbolt + HZ);
|
||||
mutex_exit(&mtx);
|
||||
time_after = lbolt;
|
||||
time_delta = time_after - time_before; /* XXX - Handle jiffie wrap */
|
||||
whole_delta = time_delta;
|
||||
remain_delta = do_div(whole_delta, HZ);
|
||||
|
||||
if (time_left == -1) {
|
||||
if (time_delta >= HZ) {
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
|
||||
"Thread correctly timed out and was asleep "
|
||||
"for %d.%d seconds (%d second min)\n",
|
||||
(int)whole_delta, remain_delta, 1);
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
|
||||
"Thread correctly timed out but was only "
|
||||
"asleep for %d.%d seconds (%d second "
|
||||
"min)\n", (int)whole_delta, remain_delta, 1);
|
||||
rc = -ETIMEDOUT;
|
||||
}
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
|
||||
"Thread exited after only %d.%d seconds, it "
|
||||
"did not hit the %d second timeout\n",
|
||||
(int)whole_delta, remain_delta, 1);
|
||||
rc = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
cv_destroy(&condvar);
|
||||
mutex_destroy(&mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_condvar_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_CONDVAR_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_CONDVAR_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_CONDVAR;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_CONDVAR_TEST1_NAME, SPLAT_CONDVAR_TEST1_DESC,
|
||||
SPLAT_CONDVAR_TEST1_ID, splat_condvar_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_CONDVAR_TEST2_NAME, SPLAT_CONDVAR_TEST2_DESC,
|
||||
SPLAT_CONDVAR_TEST2_ID, splat_condvar_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_CONDVAR_TEST3_NAME, SPLAT_CONDVAR_TEST3_DESC,
|
||||
SPLAT_CONDVAR_TEST3_ID, splat_condvar_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_CONDVAR_TEST4_NAME, SPLAT_CONDVAR_TEST4_DESC,
|
||||
SPLAT_CONDVAR_TEST4_ID, splat_condvar_test4);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_CONDVAR_TEST5_NAME, SPLAT_CONDVAR_TEST5_DESC,
|
||||
SPLAT_CONDVAR_TEST5_ID, splat_condvar_test5);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_condvar_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_CONDVAR_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_CONDVAR_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_CONDVAR_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_CONDVAR_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_CONDVAR_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_condvar_id(void) {
|
||||
return SPLAT_SUBSYSTEM_CONDVAR;
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* My intent is to create a loadable 'splat' (Solaris Porting LAyer
|
||||
* Tests) module which can be used as an access point to run
|
||||
* in kernel Solaris ABI regression tests. This provides a
|
||||
* nice mechanism to validate the shim primates are working properly.
|
||||
*
|
||||
* The basic design is the splat module is that it is constructed of
|
||||
* various splat_* source files each of which contains regression tests.
|
||||
* For example the splat_linux_kmem.c file contains tests for validating
|
||||
* kmem correctness. When the splat module is loaded splat_*_init()
|
||||
* will be called for each subsystems tests, similarly splat_*_fini() is
|
||||
* called when the splat module is removed. Each test can then be
|
||||
* run by making an ioctl() call from a userspace control application
|
||||
* to pick the subsystem and test which should be run.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
static spl_class *splat_class;
|
||||
static spl_device *splat_device;
|
||||
static struct list_head splat_module_list;
|
||||
static spinlock_t splat_module_lock;
|
||||
|
||||
static int
|
||||
splat_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
splat_info_t *info;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
info = (splat_info_t *)kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&info->info_lock);
|
||||
info->info_size = SPLAT_INFO_BUFFER_SIZE;
|
||||
info->info_buffer = (char *)vmalloc(SPLAT_INFO_BUFFER_SIZE);
|
||||
if (info->info_buffer == NULL) {
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->info_head = info->info_buffer;
|
||||
file->private_data = (void *)info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
vfree(info->info_buffer);
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_buffer_clear(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
memset(info->info_buffer, 0, info->info_size);
|
||||
info->info_head = info->info_buffer;
|
||||
spin_unlock(&info->info_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
char *buf;
|
||||
int min, size, rc = 0;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
if (kcfg->cfg_arg1 > 0) {
|
||||
|
||||
size = kcfg->cfg_arg1;
|
||||
buf = (char *)vmalloc(size);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Zero fill and truncate contents when coping buffer */
|
||||
min = ((size < info->info_size) ? size : info->info_size);
|
||||
memset(buf, 0, size);
|
||||
memcpy(buf, info->info_buffer, min);
|
||||
vfree(info->info_buffer);
|
||||
info->info_size = size;
|
||||
info->info_buffer = buf;
|
||||
info->info_head = info->info_buffer;
|
||||
}
|
||||
|
||||
kcfg->cfg_rc1 = info->info_size;
|
||||
|
||||
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
|
||||
rc = -EFAULT;
|
||||
out:
|
||||
spin_unlock(&info->info_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static splat_subsystem_t *
|
||||
splat_subsystem_find(int id) {
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
spin_lock(&splat_module_lock);
|
||||
list_for_each_entry(sub, &splat_module_list, subsystem_list) {
|
||||
if (id == sub->desc.id) {
|
||||
spin_unlock(&splat_module_lock);
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
spin_unlock(&splat_module_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_subsystem_count(splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
int i = 0;
|
||||
|
||||
spin_lock(&splat_module_lock);
|
||||
list_for_each_entry(sub, &splat_module_list, subsystem_list)
|
||||
i++;
|
||||
|
||||
spin_unlock(&splat_module_lock);
|
||||
kcfg->cfg_rc1 = i;
|
||||
|
||||
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_subsystem_list(splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
splat_cfg_t *tmp;
|
||||
int size, i = 0;
|
||||
|
||||
/* Structure will be sized large enough for N subsystem entries
|
||||
* which is passed in by the caller. On exit the number of
|
||||
* entries filled in with valid subsystems will be stored in
|
||||
* cfg_rc1. If the caller does not provide enough entries
|
||||
* for all subsystems we will truncate the list to avoid overrun.
|
||||
*/
|
||||
size = sizeof(*tmp) + kcfg->cfg_data.splat_subsystems.size *
|
||||
sizeof(splat_user_t);
|
||||
tmp = kmalloc(size, GFP_KERNEL);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Local 'tmp' is used as the structure copied back to user space */
|
||||
memset(tmp, 0, size);
|
||||
memcpy(tmp, kcfg, sizeof(*kcfg));
|
||||
|
||||
spin_lock(&splat_module_lock);
|
||||
list_for_each_entry(sub, &splat_module_list, subsystem_list) {
|
||||
strncpy(tmp->cfg_data.splat_subsystems.descs[i].name,
|
||||
sub->desc.name, SPLAT_NAME_SIZE);
|
||||
strncpy(tmp->cfg_data.splat_subsystems.descs[i].desc,
|
||||
sub->desc.desc, SPLAT_DESC_SIZE);
|
||||
tmp->cfg_data.splat_subsystems.descs[i].id = sub->desc.id;
|
||||
|
||||
/* Truncate list if we are about to overrun alloc'ed memory */
|
||||
if ((i++) == kcfg->cfg_data.splat_subsystems.size)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&splat_module_lock);
|
||||
tmp->cfg_rc1 = i;
|
||||
|
||||
if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) {
|
||||
kfree(tmp);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kfree(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_test_count(splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
splat_test_t *test;
|
||||
int i = 0;
|
||||
|
||||
/* Subsystem ID passed as arg1 */
|
||||
sub = splat_subsystem_find(kcfg->cfg_arg1);
|
||||
if (sub == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&(sub->test_lock));
|
||||
list_for_each_entry(test, &(sub->test_list), test_list)
|
||||
i++;
|
||||
|
||||
spin_unlock(&(sub->test_lock));
|
||||
kcfg->cfg_rc1 = i;
|
||||
|
||||
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_test_list(splat_cfg_t *kcfg, unsigned long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
splat_test_t *test;
|
||||
splat_cfg_t *tmp;
|
||||
int size, i = 0;
|
||||
|
||||
/* Subsystem ID passed as arg1 */
|
||||
sub = splat_subsystem_find(kcfg->cfg_arg1);
|
||||
if (sub == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
/* Structure will be sized large enough for N test entries
|
||||
* which is passed in by the caller. On exit the number of
|
||||
* entries filled in with valid tests will be stored in
|
||||
* cfg_rc1. If the caller does not provide enough entries
|
||||
* for all tests we will truncate the list to avoid overrun.
|
||||
*/
|
||||
size = sizeof(*tmp)+kcfg->cfg_data.splat_tests.size*sizeof(splat_user_t);
|
||||
tmp = kmalloc(size, GFP_KERNEL);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Local 'tmp' is used as the structure copied back to user space */
|
||||
memset(tmp, 0, size);
|
||||
memcpy(tmp, kcfg, sizeof(*kcfg));
|
||||
|
||||
spin_lock(&(sub->test_lock));
|
||||
list_for_each_entry(test, &(sub->test_list), test_list) {
|
||||
strncpy(tmp->cfg_data.splat_tests.descs[i].name,
|
||||
test->desc.name, SPLAT_NAME_SIZE);
|
||||
strncpy(tmp->cfg_data.splat_tests.descs[i].desc,
|
||||
test->desc.desc, SPLAT_DESC_SIZE);
|
||||
tmp->cfg_data.splat_tests.descs[i].id = test->desc.id;
|
||||
|
||||
/* Truncate list if we are about to overrun alloc'ed memory */
|
||||
if ((i++) == kcfg->cfg_data.splat_tests.size)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&(sub->test_lock));
|
||||
tmp->cfg_rc1 = i;
|
||||
|
||||
if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) {
|
||||
kfree(tmp);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kfree(tmp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_validate(struct file *file, splat_subsystem_t *sub, int cmd, void *arg)
|
||||
{
|
||||
splat_test_t *test;
|
||||
|
||||
spin_lock(&(sub->test_lock));
|
||||
list_for_each_entry(test, &(sub->test_list), test_list) {
|
||||
if (test->desc.id == cmd) {
|
||||
spin_unlock(&(sub->test_lock));
|
||||
return test->test(file, arg);
|
||||
}
|
||||
}
|
||||
spin_unlock(&(sub->test_lock));
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_ioctl_cfg(struct file *file, unsigned long arg)
|
||||
{
|
||||
splat_cfg_t kcfg;
|
||||
int rc = 0;
|
||||
|
||||
if (copy_from_user(&kcfg, (splat_cfg_t *)arg, sizeof(kcfg)))
|
||||
return -EFAULT;
|
||||
|
||||
if (kcfg.cfg_magic != SPLAT_CFG_MAGIC) {
|
||||
splat_print(file, "Bad config magic 0x%x != 0x%x\n",
|
||||
kcfg.cfg_magic, SPLAT_CFG_MAGIC);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (kcfg.cfg_cmd) {
|
||||
case SPLAT_CFG_BUFFER_CLEAR:
|
||||
/* cfg_arg1 - Unused
|
||||
* cfg_rc1 - Unused
|
||||
*/
|
||||
rc = splat_buffer_clear(file, &kcfg, arg);
|
||||
break;
|
||||
case SPLAT_CFG_BUFFER_SIZE:
|
||||
/* cfg_arg1 - 0 - query size; >0 resize
|
||||
* cfg_rc1 - Set to current buffer size
|
||||
*/
|
||||
rc = splat_buffer_size(file, &kcfg, arg);
|
||||
break;
|
||||
case SPLAT_CFG_SUBSYSTEM_COUNT:
|
||||
/* cfg_arg1 - Unused
|
||||
* cfg_rc1 - Set to number of subsystems
|
||||
*/
|
||||
rc = splat_subsystem_count(&kcfg, arg);
|
||||
break;
|
||||
case SPLAT_CFG_SUBSYSTEM_LIST:
|
||||
/* cfg_arg1 - Unused
|
||||
* cfg_rc1 - Set to number of subsystems
|
||||
* cfg_data.splat_subsystems - Populated with subsystems
|
||||
*/
|
||||
rc = splat_subsystem_list(&kcfg, arg);
|
||||
break;
|
||||
case SPLAT_CFG_TEST_COUNT:
|
||||
/* cfg_arg1 - Set to a target subsystem
|
||||
* cfg_rc1 - Set to number of tests
|
||||
*/
|
||||
rc = splat_test_count(&kcfg, arg);
|
||||
break;
|
||||
case SPLAT_CFG_TEST_LIST:
|
||||
/* cfg_arg1 - Set to a target subsystem
|
||||
* cfg_rc1 - Set to number of tests
|
||||
* cfg_data.splat_subsystems - Populated with tests
|
||||
*/
|
||||
rc = splat_test_list(&kcfg, arg);
|
||||
break;
|
||||
default:
|
||||
splat_print(file, "Bad config command %d\n", kcfg.cfg_cmd);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_ioctl_cmd(struct file *file, unsigned long arg)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
splat_cmd_t kcmd;
|
||||
int rc = -EINVAL;
|
||||
void *data = NULL;
|
||||
|
||||
if (copy_from_user(&kcmd, (splat_cfg_t *)arg, sizeof(kcmd)))
|
||||
return -EFAULT;
|
||||
|
||||
if (kcmd.cmd_magic != SPLAT_CMD_MAGIC) {
|
||||
splat_print(file, "Bad command magic 0x%x != 0x%x\n",
|
||||
kcmd.cmd_magic, SPLAT_CFG_MAGIC);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Allocate memory for any opaque data the caller needed to pass on */
|
||||
if (kcmd.cmd_data_size > 0) {
|
||||
data = (void *)kmalloc(kcmd.cmd_data_size, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, (void *)(arg + offsetof(splat_cmd_t,
|
||||
cmd_data_str)), kcmd.cmd_data_size)) {
|
||||
kfree(data);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
sub = splat_subsystem_find(kcmd.cmd_subsystem);
|
||||
if (sub != NULL)
|
||||
rc = splat_validate(file, sub, kcmd.cmd_test, data);
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
if (data != NULL)
|
||||
kfree(data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
int rc = 0;
|
||||
|
||||
/* Ignore tty ioctls */
|
||||
if ((cmd & 0xffffff00) == ((int)'T') << 8)
|
||||
return -ENOTTY;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
case SPLAT_CFG:
|
||||
rc = splat_ioctl_cfg(file, arg);
|
||||
break;
|
||||
case SPLAT_CMD:
|
||||
rc = splat_ioctl_cmd(file, arg);
|
||||
break;
|
||||
default:
|
||||
splat_print(file, "Bad ioctl command %d\n", cmd);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* I'm not sure why you would want to write in to this buffer from
|
||||
* user space since its principle use is to pass test status info
|
||||
* back to the user space, but I don't see any reason to prevent it.
|
||||
*/
|
||||
static ssize_t splat_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
|
||||
/* Write beyond EOF */
|
||||
if (*ppos >= info->info_size) {
|
||||
rc = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Resize count if beyond EOF */
|
||||
if (*ppos + count > info->info_size)
|
||||
count = info->info_size - *ppos;
|
||||
|
||||
if (copy_from_user(info->info_buffer, buf, count)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*ppos += count;
|
||||
rc = count;
|
||||
out:
|
||||
spin_unlock(&info->info_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t splat_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = 0;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
|
||||
/* Read beyond EOF */
|
||||
if (*ppos >= info->info_size)
|
||||
goto out;
|
||||
|
||||
/* Resize count if beyond EOF */
|
||||
if (*ppos + count > info->info_size)
|
||||
count = info->info_size - *ppos;
|
||||
|
||||
if (copy_to_user(buf, info->info_buffer + *ppos, count)) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*ppos += count;
|
||||
rc = count;
|
||||
out:
|
||||
spin_unlock(&info->info_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static loff_t splat_seek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_dentry->d_inode);
|
||||
splat_info_t *info = (splat_info_t *)file->private_data;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (minor >= SPLAT_MINORS)
|
||||
return -ENXIO;
|
||||
|
||||
ASSERT(info);
|
||||
ASSERT(info->info_buffer);
|
||||
|
||||
spin_lock(&info->info_lock);
|
||||
|
||||
switch (origin) {
|
||||
case 0: /* SEEK_SET - No-op just do it */
|
||||
break;
|
||||
case 1: /* SEEK_CUR - Seek from current */
|
||||
offset = file->f_pos + offset;
|
||||
break;
|
||||
case 2: /* SEEK_END - Seek from end */
|
||||
offset = info->info_size + offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset >= 0) {
|
||||
file->f_pos = offset;
|
||||
file->f_version = 0;
|
||||
rc = offset;
|
||||
}
|
||||
|
||||
spin_unlock(&info->info_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct file_operations splat_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = splat_open,
|
||||
.release = splat_release,
|
||||
.ioctl = splat_ioctl,
|
||||
.read = splat_read,
|
||||
.write = splat_write,
|
||||
.llseek = splat_seek,
|
||||
};
|
||||
|
||||
static struct cdev splat_cdev = {
|
||||
.owner = THIS_MODULE,
|
||||
.kobj = { .name = SPLAT_NAME, },
|
||||
};
|
||||
|
||||
static int __init
|
||||
splat_init(void)
|
||||
{
|
||||
dev_t dev;
|
||||
int rc;
|
||||
|
||||
spin_lock_init(&splat_module_lock);
|
||||
INIT_LIST_HEAD(&splat_module_list);
|
||||
|
||||
SPLAT_SUBSYSTEM_INIT(kmem);
|
||||
SPLAT_SUBSYSTEM_INIT(taskq);
|
||||
SPLAT_SUBSYSTEM_INIT(krng);
|
||||
SPLAT_SUBSYSTEM_INIT(mutex);
|
||||
SPLAT_SUBSYSTEM_INIT(condvar);
|
||||
SPLAT_SUBSYSTEM_INIT(thread);
|
||||
SPLAT_SUBSYSTEM_INIT(rwlock);
|
||||
SPLAT_SUBSYSTEM_INIT(time);
|
||||
SPLAT_SUBSYSTEM_INIT(vnode);
|
||||
SPLAT_SUBSYSTEM_INIT(kobj);
|
||||
SPLAT_SUBSYSTEM_INIT(atomic);
|
||||
SPLAT_SUBSYSTEM_INIT(list);
|
||||
SPLAT_SUBSYSTEM_INIT(generic);
|
||||
|
||||
dev = MKDEV(SPLAT_MAJOR, 0);
|
||||
if ((rc = register_chrdev_region(dev, SPLAT_MINORS, SPLAT_NAME)))
|
||||
goto error;
|
||||
|
||||
/* Support for registering a character driver */
|
||||
cdev_init(&splat_cdev, &splat_fops);
|
||||
if ((rc = cdev_add(&splat_cdev, dev, SPLAT_MINORS))) {
|
||||
printk(KERN_ERR "SPLAT: Error adding cdev, %d\n", rc);
|
||||
kobject_put(&splat_cdev.kobj);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Support for udev make driver info available in sysfs */
|
||||
splat_class = spl_class_create(THIS_MODULE, "splat");
|
||||
if (IS_ERR(splat_class)) {
|
||||
rc = PTR_ERR(splat_class);
|
||||
printk(KERN_ERR "SPLAT: Error creating splat class, %d\n", rc);
|
||||
cdev_del(&splat_cdev);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
splat_device = spl_device_create(splat_class, NULL,
|
||||
MKDEV(SPLAT_MAJOR, 0),
|
||||
NULL, SPLAT_NAME);
|
||||
|
||||
printk(KERN_INFO "SPLAT: Loaded Solaris Porting LAyer "
|
||||
"Tests v%s\n", VERSION);
|
||||
return 0;
|
||||
error:
|
||||
printk(KERN_ERR "SPLAT: Error registering splat device, %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_fini(void)
|
||||
{
|
||||
dev_t dev = MKDEV(SPLAT_MAJOR, 0);
|
||||
|
||||
spl_device_destroy(splat_class, splat_device, dev);
|
||||
spl_class_destroy(splat_class);
|
||||
cdev_del(&splat_cdev);
|
||||
unregister_chrdev_region(dev, SPLAT_MINORS);
|
||||
|
||||
SPLAT_SUBSYSTEM_FINI(generic);
|
||||
SPLAT_SUBSYSTEM_FINI(list);
|
||||
SPLAT_SUBSYSTEM_FINI(atomic);
|
||||
SPLAT_SUBSYSTEM_FINI(kobj);
|
||||
SPLAT_SUBSYSTEM_FINI(vnode);
|
||||
SPLAT_SUBSYSTEM_FINI(time);
|
||||
SPLAT_SUBSYSTEM_FINI(rwlock);
|
||||
SPLAT_SUBSYSTEM_FINI(thread);
|
||||
SPLAT_SUBSYSTEM_FINI(condvar);
|
||||
SPLAT_SUBSYSTEM_FINI(mutex);
|
||||
SPLAT_SUBSYSTEM_FINI(krng);
|
||||
SPLAT_SUBSYSTEM_FINI(taskq);
|
||||
SPLAT_SUBSYSTEM_FINI(kmem);
|
||||
|
||||
ASSERT(list_empty(&splat_module_list));
|
||||
printk(KERN_INFO "SPLAT: Unloaded Solaris Porting LAyer "
|
||||
"Tests v%s\n", VERSION);
|
||||
}
|
||||
|
||||
module_init(splat_init);
|
||||
module_exit(splat_fini);
|
||||
|
||||
MODULE_AUTHOR("Lawrence Livermore National Labs");
|
||||
MODULE_DESCRIPTION("Solaris Porting LAyer Tests");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_GENERIC 0x0d00
|
||||
#define SPLAT_GENERIC_NAME "generic"
|
||||
#define SPLAT_GENERIC_DESC "Kernel Generic Tests"
|
||||
|
||||
#define SPLAT_GENERIC_TEST1_ID 0x0d01
|
||||
#define SPLAT_GENERIC_TEST1_NAME "ddi_strtoul"
|
||||
#define SPLAT_GENERIC_TEST1_DESC "ddi_strtoul Test"
|
||||
|
||||
#define SPLAT_GENERIC_TEST2_ID 0x0d02
|
||||
#define SPLAT_GENERIC_TEST2_NAME "ddi_strtol"
|
||||
#define SPLAT_GENERIC_TEST2_DESC "ddi_strtol Test"
|
||||
|
||||
#define SPLAT_GENERIC_TEST3_ID 0x0d03
|
||||
#define SPLAT_GENERIC_TEST3_NAME "ddi_strtoull"
|
||||
#define SPLAT_GENERIC_TEST3_DESC "ddi_strtoull Test"
|
||||
|
||||
#define SPLAT_GENERIC_TEST4_ID 0x0d04
|
||||
#define SPLAT_GENERIC_TEST4_NAME "ddi_strtoll"
|
||||
#define SPLAT_GENERIC_TEST4_DESC "ddi_strtoll Test"
|
||||
|
||||
#define STR_POS "123456789"
|
||||
#define STR_NEG "-123456789"
|
||||
#define STR_BASE "0xabcdef"
|
||||
#define STR_RANGE_MAX "10000000000000000"
|
||||
#define STR_RANGE_MIN "-10000000000000000"
|
||||
#define STR_INVAL1 "12345U"
|
||||
#define STR_INVAL2 "invald"
|
||||
|
||||
#define VAL_POS 123456789
|
||||
#define VAL_NEG -123456789
|
||||
#define VAL_BASE 0xabcdef
|
||||
#define VAL_INVAL1 12345U
|
||||
|
||||
#define define_generic_msg_strtox(type, valtype) \
|
||||
static void \
|
||||
generic_msg_strto##type(struct file *file, char *msg, int rc, int *err, \
|
||||
const char *s, valtype d, char *endptr) \
|
||||
{ \
|
||||
splat_vprint(file, SPLAT_GENERIC_TEST1_NAME, \
|
||||
"%s (%d) %s: %s == %lld, 0x%p\n", \
|
||||
rc ? "Fail" : "Pass", *err, msg, s, \
|
||||
(unsigned long long)d, endptr); \
|
||||
*err = rc; \
|
||||
}
|
||||
|
||||
define_generic_msg_strtox(ul, unsigned long);
|
||||
define_generic_msg_strtox(l, long);
|
||||
define_generic_msg_strtox(ull, unsigned long long);
|
||||
define_generic_msg_strtox(ll, long long);
|
||||
|
||||
#define define_splat_generic_test_strtox(type, valtype) \
|
||||
static int \
|
||||
splat_generic_test_strto##type(struct file *file, void *arg) \
|
||||
{ \
|
||||
int rc, rc1, rc2, rc3, rc4, rc5, rc6, rc7; \
|
||||
char str[20], *endptr; \
|
||||
valtype r; \
|
||||
\
|
||||
/* Positive value: expect success */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
rc1 = ddi_strto##type(STR_POS, &endptr, 10, &r); \
|
||||
if (rc1 == 0 && r == VAL_POS && endptr && *endptr == '\0') \
|
||||
rc = 0; \
|
||||
\
|
||||
generic_msg_strto##type(file, "positive", rc , &rc1, \
|
||||
STR_POS, r, endptr); \
|
||||
\
|
||||
/* Negative value: expect success */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
strcpy(str, STR_NEG); \
|
||||
rc2 = ddi_strto##type(str, &endptr, 10, &r); \
|
||||
if (#type[0] == 'u') { \
|
||||
if (rc2 == 0 && r == 0 && endptr == str) \
|
||||
rc = 0; \
|
||||
} else { \
|
||||
if (rc2 == 0 && r == VAL_NEG && \
|
||||
endptr && *endptr == '\0') \
|
||||
rc = 0; \
|
||||
} \
|
||||
\
|
||||
generic_msg_strto##type(file, "negative", rc, &rc2, \
|
||||
STR_NEG, r, endptr); \
|
||||
\
|
||||
/* Non decimal base: expect sucess */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
rc3 = ddi_strto##type(STR_BASE, &endptr, 0, &r); \
|
||||
if (rc3 == 0 && r == VAL_BASE && endptr && *endptr == '\0') \
|
||||
rc = 0; \
|
||||
\
|
||||
generic_msg_strto##type(file, "base", rc, &rc3, \
|
||||
STR_BASE, r, endptr); \
|
||||
\
|
||||
/* Max out of range: failure expected, r unchanged */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
rc4 = ddi_strto##type(STR_RANGE_MAX, &endptr, 16, &r); \
|
||||
if (rc4 == ERANGE && r == 0 && endptr == NULL) \
|
||||
rc = 0; \
|
||||
\
|
||||
generic_msg_strto##type(file, "max", rc, &rc4, \
|
||||
STR_RANGE_MAX, r, endptr); \
|
||||
\
|
||||
/* Min out of range: failure expected, r unchanged */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
strcpy(str, STR_RANGE_MIN); \
|
||||
rc5 = ddi_strto##type(str, &endptr, 16, &r); \
|
||||
if (#type[0] == 'u') { \
|
||||
if (rc5 == 0 && r == 0 && endptr == str) \
|
||||
rc = 0; \
|
||||
} else { \
|
||||
if (rc5 == ERANGE && r == 0 && endptr == NULL) \
|
||||
rc = 0; \
|
||||
} \
|
||||
\
|
||||
generic_msg_strto##type(file, "min", rc, &rc5, \
|
||||
STR_RANGE_MIN, r, endptr); \
|
||||
\
|
||||
/* Invalid string: success expected, endptr == 'U' */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
rc6 = ddi_strto##type(STR_INVAL1, &endptr, 10, &r); \
|
||||
if (rc6 == 0 && r == VAL_INVAL1 && endptr && *endptr == 'U') \
|
||||
rc = 0; \
|
||||
\
|
||||
generic_msg_strto##type(file, "invalid", rc, &rc6, \
|
||||
STR_INVAL1, r, endptr); \
|
||||
\
|
||||
/* Invalid string: failure expected, endptr == str */ \
|
||||
r = 0; \
|
||||
rc = 1; \
|
||||
endptr = NULL; \
|
||||
strcpy(str, STR_INVAL2); \
|
||||
rc7 = ddi_strto##type(str, &endptr, 10, &r); \
|
||||
if (rc7 == 0 && r == 0 && endptr == str) \
|
||||
rc = 0; \
|
||||
\
|
||||
generic_msg_strto##type(file, "invalid", rc, &rc7, \
|
||||
STR_INVAL2, r, endptr); \
|
||||
\
|
||||
return (rc1 || rc2 || rc3 || rc4 || rc5 || rc6 || rc7) ? \
|
||||
-EINVAL : 0; \
|
||||
}
|
||||
|
||||
define_splat_generic_test_strtox(ul, unsigned long);
|
||||
define_splat_generic_test_strtox(l, long);
|
||||
define_splat_generic_test_strtox(ull, unsigned long long);
|
||||
define_splat_generic_test_strtox(ll, long long);
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_generic_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_GENERIC_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_GENERIC_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_GENERIC;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_GENERIC_TEST1_NAME, SPLAT_GENERIC_TEST1_DESC,
|
||||
SPLAT_GENERIC_TEST1_ID, splat_generic_test_strtoul);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_GENERIC_TEST2_NAME, SPLAT_GENERIC_TEST2_DESC,
|
||||
SPLAT_GENERIC_TEST2_ID, splat_generic_test_strtol);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_GENERIC_TEST3_NAME, SPLAT_GENERIC_TEST3_DESC,
|
||||
SPLAT_GENERIC_TEST3_ID, splat_generic_test_strtoull);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_GENERIC_TEST4_NAME, SPLAT_GENERIC_TEST4_DESC,
|
||||
SPLAT_GENERIC_TEST4_ID, splat_generic_test_strtoll);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_generic_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_GENERIC_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_GENERIC_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_GENERIC_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_GENERIC_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_generic_id(void)
|
||||
{
|
||||
return SPLAT_SUBSYSTEM_GENERIC;
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _SPLAT_INTERNAL_H
|
||||
#define _SPLAT_INTERNAL_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/ioctls.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <sys/callb.h>
|
||||
#include <sys/condvar.h>
|
||||
#include <sys/cred.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/kstat.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/taskq.h>
|
||||
#include <sys/thread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timer.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/kobj.h>
|
||||
#include <sys/atomic.h>
|
||||
#include <sys/list.h>
|
||||
#include <sys/sunddi.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
#include "spl-device.h"
|
||||
#include "splat-ctl.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_INIT(type) \
|
||||
({ splat_subsystem_t *_sub_; \
|
||||
\
|
||||
_sub_ = (splat_subsystem_t *)splat_##type##_init(); \
|
||||
if (_sub_ == NULL) { \
|
||||
printk(KERN_ERR "splat: Error initializing: " #type "\n"); \
|
||||
} else { \
|
||||
spin_lock(&splat_module_lock); \
|
||||
list_add_tail(&(_sub_->subsystem_list), \
|
||||
&splat_module_list); \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define SPLAT_SUBSYSTEM_FINI(type) \
|
||||
({ splat_subsystem_t *_sub_, *_tmp_; \
|
||||
int _id_, _flag_ = 0; \
|
||||
\
|
||||
_id_ = splat_##type##_id(); \
|
||||
spin_lock(&splat_module_lock); \
|
||||
list_for_each_entry_safe(_sub_, _tmp_, &splat_module_list, \
|
||||
subsystem_list) { \
|
||||
if (_sub_->desc.id == _id_) { \
|
||||
list_del_init(&(_sub_->subsystem_list)); \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
splat_##type##_fini(_sub_); \
|
||||
spin_lock(&splat_module_lock); \
|
||||
_flag_ = 1; \
|
||||
} \
|
||||
} \
|
||||
spin_unlock(&splat_module_lock); \
|
||||
\
|
||||
if (!_flag_) \
|
||||
printk(KERN_ERR "splat: Error finalizing: " #type "\n"); \
|
||||
})
|
||||
|
||||
#define SPLAT_TEST_INIT(sub, n, d, tid, func) \
|
||||
({ splat_test_t *_test_; \
|
||||
\
|
||||
_test_ = (splat_test_t *)kmalloc(sizeof(*_test_), GFP_KERNEL); \
|
||||
if (_test_ == NULL) { \
|
||||
printk(KERN_ERR "splat: Error initializing: " n "/" #tid" \n");\
|
||||
} else { \
|
||||
memset(_test_, 0, sizeof(*_test_)); \
|
||||
strncpy(_test_->desc.name, n, SPLAT_NAME_SIZE-1); \
|
||||
strncpy(_test_->desc.desc, d, SPLAT_DESC_SIZE-1); \
|
||||
_test_->desc.id = tid; \
|
||||
_test_->test = func; \
|
||||
INIT_LIST_HEAD(&(_test_->test_list)); \
|
||||
spin_lock(&((sub)->test_lock)); \
|
||||
list_add_tail(&(_test_->test_list),&((sub)->test_list));\
|
||||
spin_unlock(&((sub)->test_lock)); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define SPLAT_TEST_FINI(sub, tid) \
|
||||
({ splat_test_t *_test_, *_tmp_; \
|
||||
int _flag_ = 0; \
|
||||
\
|
||||
spin_lock(&((sub)->test_lock)); \
|
||||
list_for_each_entry_safe(_test_, _tmp_, \
|
||||
&((sub)->test_list), test_list) { \
|
||||
if (_test_->desc.id == tid) { \
|
||||
list_del_init(&(_test_->test_list)); \
|
||||
_flag_ = 1; \
|
||||
} \
|
||||
} \
|
||||
spin_unlock(&((sub)->test_lock)); \
|
||||
\
|
||||
if (!_flag_) \
|
||||
printk(KERN_ERR "splat: Error finalizing: " #tid "\n"); \
|
||||
})
|
||||
|
||||
typedef int (*splat_test_func_t)(struct file *, void *);
|
||||
|
||||
typedef struct splat_test {
|
||||
struct list_head test_list;
|
||||
splat_user_t desc;
|
||||
splat_test_func_t test;
|
||||
} splat_test_t;
|
||||
|
||||
typedef struct splat_subsystem {
|
||||
struct list_head subsystem_list;/* List had to chain entries */
|
||||
splat_user_t desc;
|
||||
spinlock_t test_lock;
|
||||
struct list_head test_list;
|
||||
} splat_subsystem_t;
|
||||
|
||||
#define SPLAT_INFO_BUFFER_SIZE 65536
|
||||
#define SPLAT_INFO_BUFFER_REDZONE 256
|
||||
|
||||
typedef struct splat_info {
|
||||
spinlock_t info_lock;
|
||||
int info_size;
|
||||
char *info_buffer;
|
||||
char *info_head; /* Internal kernel use only */
|
||||
} splat_info_t;
|
||||
|
||||
#define sym2str(sym) (char *)(#sym)
|
||||
|
||||
#define splat_print(file, format, args...) \
|
||||
({ splat_info_t *_info_ = (splat_info_t *)file->private_data; \
|
||||
int _rc_; \
|
||||
\
|
||||
ASSERT(_info_); \
|
||||
ASSERT(_info_->info_buffer); \
|
||||
\
|
||||
spin_lock(&_info_->info_lock); \
|
||||
\
|
||||
/* Don't allow the kernel to start a write in the red zone */ \
|
||||
if ((int)(_info_->info_head - _info_->info_buffer) > \
|
||||
(SPLAT_INFO_BUFFER_SIZE - SPLAT_INFO_BUFFER_REDZONE)) { \
|
||||
_rc_ = -EOVERFLOW; \
|
||||
} else { \
|
||||
_rc_ = sprintf(_info_->info_head, format, args); \
|
||||
if (_rc_ >= 0) \
|
||||
_info_->info_head += _rc_; \
|
||||
} \
|
||||
\
|
||||
spin_unlock(&_info_->info_lock); \
|
||||
_rc_; \
|
||||
})
|
||||
|
||||
#define splat_vprint(file, test, format, args...) \
|
||||
splat_print(file, "%*s: " format, SPLAT_NAME_SIZE, test, args)
|
||||
|
||||
splat_subsystem_t *splat_condvar_init(void);
|
||||
splat_subsystem_t *splat_kmem_init(void);
|
||||
splat_subsystem_t *splat_mutex_init(void);
|
||||
splat_subsystem_t *splat_krng_init(void);
|
||||
splat_subsystem_t *splat_rwlock_init(void);
|
||||
splat_subsystem_t *splat_taskq_init(void);
|
||||
splat_subsystem_t *splat_thread_init(void);
|
||||
splat_subsystem_t *splat_time_init(void);
|
||||
splat_subsystem_t *splat_vnode_init(void);
|
||||
splat_subsystem_t *splat_kobj_init(void);
|
||||
splat_subsystem_t *splat_atomic_init(void);
|
||||
splat_subsystem_t *splat_list_init(void);
|
||||
splat_subsystem_t *splat_generic_init(void);
|
||||
|
||||
void splat_condvar_fini(splat_subsystem_t *);
|
||||
void splat_kmem_fini(splat_subsystem_t *);
|
||||
void splat_mutex_fini(splat_subsystem_t *);
|
||||
void splat_krng_fini(splat_subsystem_t *);
|
||||
void splat_rwlock_fini(splat_subsystem_t *);
|
||||
void splat_taskq_fini(splat_subsystem_t *);
|
||||
void splat_thread_fini(splat_subsystem_t *);
|
||||
void splat_time_fini(splat_subsystem_t *);
|
||||
void splat_vnode_fini(splat_subsystem_t *);
|
||||
void splat_kobj_fini(splat_subsystem_t *);
|
||||
void splat_atomic_fini(splat_subsystem_t *);
|
||||
void splat_list_fini(splat_subsystem_t *);
|
||||
void splat_generic_fini(splat_subsystem_t *);
|
||||
|
||||
int splat_condvar_id(void);
|
||||
int splat_kmem_id(void);
|
||||
int splat_mutex_id(void);
|
||||
int splat_krng_id(void);
|
||||
int splat_rwlock_id(void);
|
||||
int splat_taskq_id(void);
|
||||
int splat_thread_id(void);
|
||||
int splat_time_id(void);
|
||||
int splat_vnode_id(void);
|
||||
int splat_kobj_id(void);
|
||||
int splat_atomic_id(void);
|
||||
int splat_list_id(void);
|
||||
int splat_generic_id(void);
|
||||
|
||||
#endif /* _SPLAT_INTERNAL_H */
|
||||
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KMEM 0x0100
|
||||
#define SPLAT_KMEM_NAME "kmem"
|
||||
#define SPLAT_KMEM_DESC "Kernel Malloc/Slab Tests"
|
||||
|
||||
#define SPLAT_KMEM_TEST1_ID 0x0101
|
||||
#define SPLAT_KMEM_TEST1_NAME "kmem_alloc"
|
||||
#define SPLAT_KMEM_TEST1_DESC "Memory allocation test (kmem_alloc)"
|
||||
|
||||
#define SPLAT_KMEM_TEST2_ID 0x0102
|
||||
#define SPLAT_KMEM_TEST2_NAME "kmem_zalloc"
|
||||
#define SPLAT_KMEM_TEST2_DESC "Memory allocation test (kmem_zalloc)"
|
||||
|
||||
#define SPLAT_KMEM_TEST3_ID 0x0103
|
||||
#define SPLAT_KMEM_TEST3_NAME "vmem_alloc"
|
||||
#define SPLAT_KMEM_TEST3_DESC "Memory allocation test (vmem_alloc)"
|
||||
|
||||
#define SPLAT_KMEM_TEST4_ID 0x0104
|
||||
#define SPLAT_KMEM_TEST4_NAME "vmem_zalloc"
|
||||
#define SPLAT_KMEM_TEST4_DESC "Memory allocation test (vmem_zalloc)"
|
||||
|
||||
#define SPLAT_KMEM_TEST5_ID 0x0105
|
||||
#define SPLAT_KMEM_TEST5_NAME "kmem_cache1"
|
||||
#define SPLAT_KMEM_TEST5_DESC "Slab ctor/dtor test (small)"
|
||||
|
||||
#define SPLAT_KMEM_TEST6_ID 0x0106
|
||||
#define SPLAT_KMEM_TEST6_NAME "kmem_cache2"
|
||||
#define SPLAT_KMEM_TEST6_DESC "Slab ctor/dtor test (large)"
|
||||
|
||||
#define SPLAT_KMEM_TEST7_ID 0x0107
|
||||
#define SPLAT_KMEM_TEST7_NAME "kmem_reap"
|
||||
#define SPLAT_KMEM_TEST7_DESC "Slab reaping test"
|
||||
|
||||
#define SPLAT_KMEM_TEST8_ID 0x0108
|
||||
#define SPLAT_KMEM_TEST8_NAME "kmem_lock"
|
||||
#define SPLAT_KMEM_TEST8_DESC "Slab locking test"
|
||||
|
||||
#define SPLAT_KMEM_ALLOC_COUNT 10
|
||||
#define SPLAT_VMEM_ALLOC_COUNT 10
|
||||
|
||||
|
||||
/* XXX - This test may fail under tight memory conditions */
|
||||
static int
|
||||
splat_kmem_test1(struct file *file, void *arg)
|
||||
{
|
||||
void *ptr[SPLAT_KMEM_ALLOC_COUNT];
|
||||
int size = PAGE_SIZE;
|
||||
int i, count, rc = 0;
|
||||
|
||||
/* We are intentionally going to push kmem_alloc to its max
|
||||
* allocation size, so suppress the console warnings for now */
|
||||
kmem_set_warning(0);
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 32))) {
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) {
|
||||
ptr[i] = kmem_alloc(size, KM_SLEEP);
|
||||
if (ptr[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++)
|
||||
if (ptr[i])
|
||||
kmem_free(ptr[i], size);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST1_NAME,
|
||||
"%d byte allocations, %d/%d successful\n",
|
||||
size, count, SPLAT_KMEM_ALLOC_COUNT);
|
||||
if (count != SPLAT_KMEM_ALLOC_COUNT)
|
||||
rc = -ENOMEM;
|
||||
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
kmem_set_warning(1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test2(struct file *file, void *arg)
|
||||
{
|
||||
void *ptr[SPLAT_KMEM_ALLOC_COUNT];
|
||||
int size = PAGE_SIZE;
|
||||
int i, j, count, rc = 0;
|
||||
|
||||
/* We are intentionally going to push kmem_alloc to its max
|
||||
* allocation size, so suppress the console warnings for now */
|
||||
kmem_set_warning(0);
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 32))) {
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) {
|
||||
ptr[i] = kmem_zalloc(size, KM_SLEEP);
|
||||
if (ptr[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Ensure buffer has been zero filled */
|
||||
for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) {
|
||||
for (j = 0; j < size; j++) {
|
||||
if (((char *)ptr[i])[j] != '\0') {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST2_NAME,
|
||||
"%d-byte allocation was "
|
||||
"not zeroed\n", size);
|
||||
rc = -EFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++)
|
||||
if (ptr[i])
|
||||
kmem_free(ptr[i], size);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST2_NAME,
|
||||
"%d byte allocations, %d/%d successful\n",
|
||||
size, count, SPLAT_KMEM_ALLOC_COUNT);
|
||||
if (count != SPLAT_KMEM_ALLOC_COUNT)
|
||||
rc = -ENOMEM;
|
||||
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
kmem_set_warning(1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test3(struct file *file, void *arg)
|
||||
{
|
||||
void *ptr[SPLAT_VMEM_ALLOC_COUNT];
|
||||
int size = PAGE_SIZE;
|
||||
int i, count, rc = 0;
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 1024))) {
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) {
|
||||
ptr[i] = vmem_alloc(size, KM_SLEEP);
|
||||
if (ptr[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++)
|
||||
if (ptr[i])
|
||||
vmem_free(ptr[i], size);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST3_NAME,
|
||||
"%d byte allocations, %d/%d successful\n",
|
||||
size, count, SPLAT_VMEM_ALLOC_COUNT);
|
||||
if (count != SPLAT_VMEM_ALLOC_COUNT)
|
||||
rc = -ENOMEM;
|
||||
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test4(struct file *file, void *arg)
|
||||
{
|
||||
void *ptr[SPLAT_VMEM_ALLOC_COUNT];
|
||||
int size = PAGE_SIZE;
|
||||
int i, j, count, rc = 0;
|
||||
|
||||
while ((!rc) && (size <= (PAGE_SIZE * 1024))) {
|
||||
count = 0;
|
||||
|
||||
for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) {
|
||||
ptr[i] = vmem_zalloc(size, KM_SLEEP);
|
||||
if (ptr[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Ensure buffer has been zero filled */
|
||||
for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) {
|
||||
for (j = 0; j < size; j++) {
|
||||
if (((char *)ptr[i])[j] != '\0') {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST4_NAME,
|
||||
"%d-byte allocation was "
|
||||
"not zeroed\n", size);
|
||||
rc = -EFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++)
|
||||
if (ptr[i])
|
||||
vmem_free(ptr[i], size);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST4_NAME,
|
||||
"%d byte allocations, %d/%d successful\n",
|
||||
size, count, SPLAT_VMEM_ALLOC_COUNT);
|
||||
if (count != SPLAT_VMEM_ALLOC_COUNT)
|
||||
rc = -ENOMEM;
|
||||
|
||||
size *= 2;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define SPLAT_KMEM_TEST_MAGIC 0x004488CCUL
|
||||
#define SPLAT_KMEM_CACHE_NAME "kmem_test"
|
||||
#define SPLAT_KMEM_OBJ_COUNT 128
|
||||
#define SPLAT_KMEM_OBJ_RECLAIM 16
|
||||
|
||||
typedef struct kmem_cache_data {
|
||||
unsigned long kcd_magic;
|
||||
int kcd_flag;
|
||||
char kcd_buf[0];
|
||||
} kmem_cache_data_t;
|
||||
|
||||
typedef struct kmem_cache_priv {
|
||||
unsigned long kcp_magic;
|
||||
struct file *kcp_file;
|
||||
kmem_cache_t *kcp_cache;
|
||||
kmem_cache_data_t *kcp_kcd[SPLAT_KMEM_OBJ_COUNT];
|
||||
spinlock_t kcp_lock;
|
||||
wait_queue_head_t kcp_waitq;
|
||||
int kcp_size;
|
||||
int kcp_count;
|
||||
int kcp_threads;
|
||||
int kcp_alloc;
|
||||
int kcp_rc;
|
||||
} kmem_cache_priv_t;
|
||||
|
||||
static int
|
||||
splat_kmem_cache_test_constructor(void *ptr, void *priv, int flags)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
||||
kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr;
|
||||
|
||||
if (kcd && kcp) {
|
||||
kcd->kcd_magic = kcp->kcp_magic;
|
||||
kcd->kcd_flag = 1;
|
||||
memset(kcd->kcd_buf, 0xaa, kcp->kcp_size - (sizeof *kcd));
|
||||
kcp->kcp_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_kmem_cache_test_destructor(void *ptr, void *priv)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
||||
kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr;
|
||||
|
||||
if (kcd && kcp) {
|
||||
kcd->kcd_magic = 0;
|
||||
kcd->kcd_flag = 0;
|
||||
memset(kcd->kcd_buf, 0xbb, kcp->kcp_size - (sizeof *kcd));
|
||||
kcp->kcp_count--;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_cache_size_test(struct file *file, void *arg,
|
||||
char *name, int size, int flags)
|
||||
{
|
||||
kmem_cache_t *cache = NULL;
|
||||
kmem_cache_data_t *kcd = NULL;
|
||||
kmem_cache_priv_t kcp;
|
||||
int rc = 0, max;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
kcp.kcp_size = size;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_rc = 0;
|
||||
|
||||
cache = kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp.kcp_size, 0,
|
||||
splat_kmem_cache_test_constructor,
|
||||
splat_kmem_cache_test_destructor,
|
||||
NULL, &kcp, NULL, flags);
|
||||
if (!cache) {
|
||||
splat_vprint(file, name,
|
||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kcd = kmem_cache_alloc(cache, KM_SLEEP);
|
||||
if (!kcd) {
|
||||
splat_vprint(file, name,
|
||||
"Unable to allocate from '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!kcd->kcd_flag) {
|
||||
splat_vprint(file, name,
|
||||
"Failed to run contructor for '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (kcd->kcd_magic != kcp.kcp_magic) {
|
||||
splat_vprint(file, name,
|
||||
"Failed to pass private data to constructor "
|
||||
"for '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
max = kcp.kcp_count;
|
||||
kmem_cache_free(cache, kcd);
|
||||
|
||||
/* Destroy the entire cache which will force destructors to
|
||||
* run and we can verify one was called for every object */
|
||||
kmem_cache_destroy(cache);
|
||||
if (kcp.kcp_count) {
|
||||
splat_vprint(file, name,
|
||||
"Failed to run destructor on all slab objects "
|
||||
"for '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
splat_vprint(file, name,
|
||||
"Successfully ran ctors/dtors for %d elements in '%s'\n",
|
||||
max, SPLAT_KMEM_CACHE_NAME);
|
||||
|
||||
return rc;
|
||||
|
||||
out_free:
|
||||
if (kcd)
|
||||
kmem_cache_free(cache, kcd);
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validate small object cache behavior for dynamic/kmem/vmem caches */
|
||||
static int
|
||||
splat_kmem_test5(struct file *file, void *arg)
|
||||
{
|
||||
char *name = SPLAT_KMEM_TEST5_NAME;
|
||||
int rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128, KMC_KMEM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return splat_kmem_cache_size_test(file, arg, name, 128, KMC_VMEM);
|
||||
}
|
||||
|
||||
/* Validate large object cache behavior for dynamic/kmem/vmem caches */
|
||||
static int
|
||||
splat_kmem_test6(struct file *file, void *arg)
|
||||
{
|
||||
char *name = SPLAT_KMEM_TEST6_NAME;
|
||||
int rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128 * 1024, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = splat_kmem_cache_size_test(file, arg, name, 128 * 1024, KMC_KMEM);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return splat_kmem_cache_size_test(file, arg, name, 128 * 1028, KMC_VMEM);
|
||||
}
|
||||
|
||||
static void
|
||||
splat_kmem_cache_test_reclaim(void *priv)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
||||
int i, count;
|
||||
|
||||
count = min(SPLAT_KMEM_OBJ_RECLAIM, kcp->kcp_count);
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Reaping %d objects from '%s'\n", count,
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) {
|
||||
if (kcp->kcp_kcd[i]) {
|
||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
||||
kcp->kcp_kcd[i] = NULL;
|
||||
|
||||
if (--count == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test7(struct file *file, void *arg)
|
||||
{
|
||||
kmem_cache_t *cache;
|
||||
kmem_cache_priv_t kcp;
|
||||
int i, rc = 0;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
kcp.kcp_size = 256;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_rc = 0;
|
||||
|
||||
cache = kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp.kcp_size, 0,
|
||||
splat_kmem_cache_test_constructor,
|
||||
splat_kmem_cache_test_destructor,
|
||||
splat_kmem_cache_test_reclaim,
|
||||
&kcp, NULL, 0);
|
||||
if (!cache) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
kcp.kcp_cache = cache;
|
||||
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) {
|
||||
/* All allocations need not succeed */
|
||||
kcp.kcp_kcd[i] = kmem_cache_alloc(cache, KM_SLEEP);
|
||||
if (!kcp.kcp_kcd[i]) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Unable to allocate from '%s'\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(kcp.kcp_count > 0);
|
||||
|
||||
/* Request the slab cache free any objects it can. For a few reasons
|
||||
* this may not immediately result in more free memory even if objects
|
||||
* are freed. First off, due to fragmentation we may not be able to
|
||||
* reclaim any slabs. Secondly, even if we do we fully clear some
|
||||
* slabs we will not want to immedately reclaim all of them because
|
||||
* we may contend with cache allocs and thrash. What we want to see
|
||||
* is slab size decrease more gradually as it becomes clear they
|
||||
* will not be needed. This should be acheivable in less than minute
|
||||
* if it takes longer than this something has gone wrong.
|
||||
*/
|
||||
for (i = 0; i < 60; i++) {
|
||||
kmem_cache_reap_now(cache);
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"%s cache objects %d, slabs %u/%u objs %u/%u\n",
|
||||
SPLAT_KMEM_CACHE_NAME, kcp.kcp_count,
|
||||
(unsigned)cache->skc_slab_alloc,
|
||||
(unsigned)cache->skc_slab_total,
|
||||
(unsigned)cache->skc_obj_alloc,
|
||||
(unsigned)cache->skc_obj_total);
|
||||
|
||||
if (cache->skc_obj_total == 0)
|
||||
break;
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ);
|
||||
}
|
||||
|
||||
if (cache->skc_obj_total == 0) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Successfully created %d objects "
|
||||
"in cache %s and reclaimed them\n",
|
||||
SPLAT_KMEM_OBJ_COUNT, SPLAT_KMEM_CACHE_NAME);
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST7_NAME,
|
||||
"Failed to reclaim %u/%d objects from cache %s\n",
|
||||
(unsigned)cache->skc_obj_total, SPLAT_KMEM_OBJ_COUNT,
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -ENOMEM;
|
||||
}
|
||||
|
||||
/* Cleanup our mess (for failure case of time expiring) */
|
||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++)
|
||||
if (kcp.kcp_kcd[i])
|
||||
kmem_cache_free(cache, kcp.kcp_kcd[i]);
|
||||
|
||||
kmem_cache_destroy(cache);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_kmem_test8_thread(void *arg)
|
||||
{
|
||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg;
|
||||
int count = kcp->kcp_alloc, rc = 0, i;
|
||||
void **objs;
|
||||
|
||||
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
||||
|
||||
objs = vmem_zalloc(count * sizeof(void *), KM_SLEEP);
|
||||
if (!objs) {
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME,
|
||||
"Unable to alloc objp array for cache '%s'\n",
|
||||
kcp->kcp_cache->skc_name);
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
objs[i] = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
||||
if (!objs[i]) {
|
||||
splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME,
|
||||
"Unable to allocate from cache '%s'\n",
|
||||
kcp->kcp_cache->skc_name);
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (objs[i])
|
||||
kmem_cache_free(kcp->kcp_cache, objs[i]);
|
||||
|
||||
vmem_free(objs, count * sizeof(void *));
|
||||
out:
|
||||
spin_lock(&kcp->kcp_lock);
|
||||
if (!kcp->kcp_rc)
|
||||
kcp->kcp_rc = rc;
|
||||
|
||||
if (--kcp->kcp_threads == 0)
|
||||
wake_up(&kcp->kcp_waitq);
|
||||
|
||||
spin_unlock(&kcp->kcp_lock);
|
||||
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test8_count(kmem_cache_priv_t *kcp, int threads)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&kcp->kcp_lock);
|
||||
ret = (kcp->kcp_threads == threads);
|
||||
spin_unlock(&kcp->kcp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This test will always pass and is simply here so I can easily
|
||||
* eyeball the slab cache locking overhead to ensure it is reasonable.
|
||||
*/
|
||||
static int
|
||||
splat_kmem_test8_sc(struct file *file, void *arg, int size, int count)
|
||||
{
|
||||
kmem_cache_priv_t kcp;
|
||||
kthread_t *thr;
|
||||
struct timespec start, stop, delta;
|
||||
char cache_name[32];
|
||||
int i, j, rc = 0, threads = 32;
|
||||
|
||||
kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC;
|
||||
kcp.kcp_file = file;
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "name",
|
||||
"time (sec)\tslabs \tobjs \thash\n");
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "",
|
||||
" \ttot/max/calc\ttot/max/calc\n");
|
||||
|
||||
for (i = 1; i <= count; i *= 2) {
|
||||
kcp.kcp_size = size;
|
||||
kcp.kcp_count = 0;
|
||||
kcp.kcp_threads = 0;
|
||||
kcp.kcp_alloc = i;
|
||||
kcp.kcp_rc = 0;
|
||||
spin_lock_init(&kcp.kcp_lock);
|
||||
init_waitqueue_head(&kcp.kcp_waitq);
|
||||
|
||||
(void)snprintf(cache_name, 32, "%s-%d-%d",
|
||||
SPLAT_KMEM_CACHE_NAME, size, i);
|
||||
kcp.kcp_cache = kmem_cache_create(cache_name, kcp.kcp_size, 0,
|
||||
splat_kmem_cache_test_constructor,
|
||||
splat_kmem_cache_test_destructor,
|
||||
NULL, &kcp, NULL, 0);
|
||||
if (!kcp.kcp_cache) {
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
||||
"Unable to create '%s' cache\n",
|
||||
SPLAT_KMEM_CACHE_NAME);
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
start = current_kernel_time();
|
||||
|
||||
for (j = 0; j < threads; j++) {
|
||||
thr = thread_create(NULL, 0, splat_kmem_test8_thread,
|
||||
&kcp, 0, &p0, TS_RUN, minclsyspri);
|
||||
if (thr == NULL) {
|
||||
rc = -ESRCH;
|
||||
break;
|
||||
}
|
||||
spin_lock(&kcp.kcp_lock);
|
||||
kcp.kcp_threads++;
|
||||
spin_unlock(&kcp.kcp_lock);
|
||||
}
|
||||
|
||||
/* Sleep until the thread sets kcp.kcp_threads == 0 */
|
||||
wait_event(kcp.kcp_waitq, splat_kmem_test8_count(&kcp, 0));
|
||||
stop = current_kernel_time();
|
||||
delta = timespec_sub(stop, start);
|
||||
|
||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %2ld.%09ld\t"
|
||||
"%lu/%lu/%lu\t%lu/%lu/%lu\n",
|
||||
kcp.kcp_cache->skc_name,
|
||||
delta.tv_sec, delta.tv_nsec,
|
||||
(unsigned long)kcp.kcp_cache->skc_slab_total,
|
||||
(unsigned long)kcp.kcp_cache->skc_slab_max,
|
||||
(unsigned long)(kcp.kcp_alloc * threads /
|
||||
SPL_KMEM_CACHE_OBJ_PER_SLAB),
|
||||
(unsigned long)kcp.kcp_cache->skc_obj_total,
|
||||
(unsigned long)kcp.kcp_cache->skc_obj_max,
|
||||
(unsigned long)(kcp.kcp_alloc * threads));
|
||||
|
||||
kmem_cache_destroy(kcp.kcp_cache);
|
||||
|
||||
if (!rc && kcp.kcp_rc)
|
||||
rc = kcp.kcp_rc;
|
||||
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_kmem_test8(struct file *file, void *arg)
|
||||
{
|
||||
int i, rc = 0;
|
||||
|
||||
/* Run through slab cache with objects size from
|
||||
* 16-1Mb in 4x multiples with 1024 objects each */
|
||||
for (i = 16; i <= 1024*1024; i *= 4) {
|
||||
rc = splat_kmem_test8_sc(file, arg, i, 256);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_kmem_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_KMEM_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_KMEM_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_KMEM;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST1_NAME, SPLAT_KMEM_TEST1_DESC,
|
||||
SPLAT_KMEM_TEST1_ID, splat_kmem_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST2_NAME, SPLAT_KMEM_TEST2_DESC,
|
||||
SPLAT_KMEM_TEST2_ID, splat_kmem_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST3_NAME, SPLAT_KMEM_TEST3_DESC,
|
||||
SPLAT_KMEM_TEST3_ID, splat_kmem_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST4_NAME, SPLAT_KMEM_TEST4_DESC,
|
||||
SPLAT_KMEM_TEST4_ID, splat_kmem_test4);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST5_NAME, SPLAT_KMEM_TEST5_DESC,
|
||||
SPLAT_KMEM_TEST5_ID, splat_kmem_test5);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST6_NAME, SPLAT_KMEM_TEST6_DESC,
|
||||
SPLAT_KMEM_TEST6_ID, splat_kmem_test6);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST7_NAME, SPLAT_KMEM_TEST7_DESC,
|
||||
SPLAT_KMEM_TEST7_ID, splat_kmem_test7);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST8_NAME, SPLAT_KMEM_TEST8_DESC,
|
||||
SPLAT_KMEM_TEST8_ID, splat_kmem_test8);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_kmem_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST8_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST7_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST6_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_kmem_id(void) {
|
||||
return SPLAT_SUBSYSTEM_KMEM;
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KOBJ 0x0a00
|
||||
#define SPLAT_KOBJ_NAME "kobj"
|
||||
#define SPLAT_KOBJ_DESC "Kernel Kobj Tests"
|
||||
|
||||
#define SPLAT_KOBJ_TEST1_ID 0x0a01
|
||||
#define SPLAT_KOBJ_TEST1_NAME "open"
|
||||
#define SPLAT_KOBJ_TEST1_DESC "Kobj Open/Close Test"
|
||||
|
||||
#define SPLAT_KOBJ_TEST2_ID 0x0a02
|
||||
#define SPLAT_KOBJ_TEST2_NAME "size/read"
|
||||
#define SPLAT_KOBJ_TEST2_DESC "Kobj Size/Read Test"
|
||||
|
||||
#define SPLAT_KOBJ_TEST_FILE "/etc/fstab"
|
||||
|
||||
static int
|
||||
splat_kobj_test1(struct file *file, void *arg)
|
||||
{
|
||||
struct _buf *f;
|
||||
|
||||
f = kobj_open_file(SPLAT_KOBJ_TEST_FILE);
|
||||
if (f == (struct _buf *)-1) {
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Failed to open "
|
||||
"test file: %s\n", SPLAT_KOBJ_TEST_FILE);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
kobj_close_file(f);
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Successfully opened and "
|
||||
"closed test file: %s\n", SPLAT_KOBJ_TEST_FILE);
|
||||
|
||||
return 0;
|
||||
} /* splat_kobj_test1() */
|
||||
|
||||
static int
|
||||
splat_kobj_test2(struct file *file, void *arg)
|
||||
{
|
||||
struct _buf *f;
|
||||
char *buf;
|
||||
uint64_t size;
|
||||
int rc;
|
||||
|
||||
f = kobj_open_file(SPLAT_KOBJ_TEST_FILE);
|
||||
if (f == (struct _buf *)-1) {
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to open "
|
||||
"test file: %s\n", SPLAT_KOBJ_TEST_FILE);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
rc = kobj_get_filesize(f, &size);
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed stat of "
|
||||
"test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = kmalloc(size + 1, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
rc = -ENOMEM;
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to alloc "
|
||||
"%lld bytes for tmp buffer (%d)\n", size, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(buf, 0, size + 1);
|
||||
rc = kobj_read_file(f, buf, size, 0);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed read of "
|
||||
"test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Validate we read as many bytes as expected based on the stat. This
|
||||
* isn't a perfect test since we didn't create the file however it is
|
||||
* pretty unlikely there are garbage characters in your /etc/fstab */
|
||||
if (size != (uint64_t)strlen(buf)) {
|
||||
rc = -EFBIG;
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Stat'ed size "
|
||||
"(%lld) does not match number of bytes read "
|
||||
"(%lld)\n", size, (uint64_t)strlen(buf));
|
||||
goto out2;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "\n%s\n", buf);
|
||||
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Successfully stat'ed "
|
||||
"and read expected number of bytes (%lld) from test "
|
||||
"file: %s\n", size, SPLAT_KOBJ_TEST_FILE);
|
||||
out2:
|
||||
kfree(buf);
|
||||
out:
|
||||
kobj_close_file(f);
|
||||
|
||||
return rc;
|
||||
} /* splat_kobj_test2() */
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_kobj_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_KOBJ_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_KOBJ_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_KOBJ;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KOBJ_TEST1_NAME, SPLAT_KOBJ_TEST1_DESC,
|
||||
SPLAT_KOBJ_TEST1_ID, splat_kobj_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KOBJ_TEST2_NAME, SPLAT_KOBJ_TEST2_DESC,
|
||||
SPLAT_KOBJ_TEST2_ID, splat_kobj_test2);
|
||||
|
||||
return sub;
|
||||
} /* splat_kobj_init() */
|
||||
|
||||
void
|
||||
splat_kobj_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KOBJ_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KOBJ_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
} /* splat_kobj_fini() */
|
||||
|
||||
int
|
||||
splat_kobj_id(void)
|
||||
{
|
||||
return SPLAT_SUBSYSTEM_KOBJ;
|
||||
} /* splat_kobj_id() */
|
||||
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_LIST 0x0c00
|
||||
#define SPLAT_LIST_NAME "list"
|
||||
#define SPLAT_LIST_DESC "Kernel List Tests"
|
||||
|
||||
#define SPLAT_LIST_TEST1_ID 0x0c01
|
||||
#define SPLAT_LIST_TEST1_NAME "create/destroy"
|
||||
#define SPLAT_LIST_TEST1_DESC "Create/destroy Test"
|
||||
|
||||
#define SPLAT_LIST_TEST2_ID 0x0c02
|
||||
#define SPLAT_LIST_TEST2_NAME "ins/rm head"
|
||||
#define SPLAT_LIST_TEST2_DESC "Insert/remove head Test"
|
||||
|
||||
#define SPLAT_LIST_TEST3_ID 0x0c03
|
||||
#define SPLAT_LIST_TEST3_NAME "ins/rm tail"
|
||||
#define SPLAT_LIST_TEST3_DESC "Insert/remove tail Test"
|
||||
|
||||
#define SPLAT_LIST_TEST4_ID 0x0c04
|
||||
#define SPLAT_LIST_TEST4_NAME "insert_after"
|
||||
#define SPLAT_LIST_TEST4_DESC "Insert_after Test"
|
||||
|
||||
#define SPLAT_LIST_TEST5_ID 0x0c05
|
||||
#define SPLAT_LIST_TEST5_NAME "insert_before"
|
||||
#define SPLAT_LIST_TEST5_DESC "Insert_before Test"
|
||||
|
||||
#define SPLAT_LIST_TEST6_ID 0x0c06
|
||||
#define SPLAT_LIST_TEST6_NAME "remove"
|
||||
#define SPLAT_LIST_TEST6_DESC "Remove Test"
|
||||
|
||||
#define SPLAT_LIST_TEST7_ID 0x0c7
|
||||
#define SPLAT_LIST_TEST7_NAME "active"
|
||||
#define SPLAT_LIST_TEST7_DESC "Active Test"
|
||||
|
||||
/* It is important that li_node is not the first element, this
|
||||
* ensures the list_d2l/list_object macros are working correctly. */
|
||||
typedef struct list_item {
|
||||
int li_data;
|
||||
list_node_t li_node;
|
||||
} list_item_t;
|
||||
|
||||
#define LIST_ORDER_STACK 0
|
||||
#define LIST_ORDER_QUEUE 1
|
||||
|
||||
static int
|
||||
splat_list_test1(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
if (!list_is_empty(&list)) {
|
||||
splat_vprint(file, SPLAT_LIST_TEST1_NAME,
|
||||
"New list NOT empty%s\n", "");
|
||||
/* list_destroy() intentionally skipped to avoid assert */
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
/* Validate the list has been destroyed */
|
||||
if (list_link_active(&list.list_head)) {
|
||||
splat_vprint(file, SPLAT_LIST_TEST1_NAME,
|
||||
"Destroyed list still active%s", "");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_validate(list_t *list, int size, int order, int mult)
|
||||
{
|
||||
list_item_t *li;
|
||||
int i;
|
||||
|
||||
/* Walk all items in list from head to verify stack or queue
|
||||
* ordering. We bound the for loop by size+1 to ensure that
|
||||
* we still terminate if there is list corruption. We also
|
||||
* intentionally make things a little more complex than they
|
||||
* need to be by using list_head/list_next for queues, and
|
||||
* list_tail/list_prev for stacks. This is simply done for
|
||||
* coverage and to ensure these function are working right.
|
||||
*/
|
||||
for (i = 0, li = (order ? list_head(list) : list_tail(list));
|
||||
i < size + 1 && li != NULL;
|
||||
i++, li = (order ? list_next(list, li) : list_prev(list, li)))
|
||||
if (li->li_data != i * mult)
|
||||
return -EIDRM;
|
||||
|
||||
if (i != size)
|
||||
return -E2BIG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test2(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li;
|
||||
int i, list_size = 8, rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
/* Insert all items at the list head to form a stack */
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
|
||||
"Adding %d items to list head\n", list_size);
|
||||
for (i = 0; i < list_size; i++) {
|
||||
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_link_init(&li->li_node);
|
||||
li->li_data = i;
|
||||
list_insert_head(&list, li);
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
|
||||
"Validating %d item list is a stack\n", list_size);
|
||||
rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1);
|
||||
if (rc)
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
|
||||
"List validation failed, %d\n", rc);
|
||||
out:
|
||||
/* Remove all items */
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
|
||||
"Removing %d items from list head\n", list_size);
|
||||
while ((li = list_remove_head(&list)))
|
||||
kmem_free(li, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test3(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li;
|
||||
int i, list_size = 8, rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
/* Insert all items at the list tail to form a queue */
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
|
||||
"Adding %d items to list tail\n", list_size);
|
||||
for (i = 0; i < list_size; i++) {
|
||||
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_link_init(&li->li_node);
|
||||
li->li_data = i;
|
||||
list_insert_tail(&list, li);
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
|
||||
"Validating %d item list is a queue\n", list_size);
|
||||
rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1);
|
||||
if (rc)
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
|
||||
"List validation failed, %d\n", rc);
|
||||
out:
|
||||
/* Remove all items */
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
|
||||
"Removing %d items from list tail\n", list_size);
|
||||
while ((li = list_remove_tail(&list)))
|
||||
kmem_free(li, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test4(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li_new, *li_last = NULL;
|
||||
int i, list_size = 8, rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
/* Insert all items after the last item to form a queue */
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
|
||||
"Adding %d items each after the last item\n", list_size);
|
||||
for (i = 0; i < list_size; i++) {
|
||||
li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li_new == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_link_init(&li_new->li_node);
|
||||
li_new->li_data = i;
|
||||
list_insert_after(&list, li_last, li_new);
|
||||
li_last = li_new;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
|
||||
"Validating %d item list is a queue\n", list_size);
|
||||
rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1);
|
||||
if (rc)
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
|
||||
"List validation failed, %d\n", rc);
|
||||
out:
|
||||
/* Remove all items */
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
|
||||
"Removing %d items from list tail\n", list_size);
|
||||
while ((li_new = list_remove_head(&list)))
|
||||
kmem_free(li_new, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test5(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li_new, *li_last = NULL;
|
||||
int i, list_size = 8, rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
/* Insert all items before the last item to form a stack */
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
|
||||
"Adding %d items each before the last item\n", list_size);
|
||||
for (i = 0; i < list_size; i++) {
|
||||
li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li_new == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_link_init(&li_new->li_node);
|
||||
li_new->li_data = i;
|
||||
list_insert_before(&list, li_last, li_new);
|
||||
li_last = li_new;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
|
||||
"Validating %d item list is a queue\n", list_size);
|
||||
rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1);
|
||||
if (rc)
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
|
||||
"List validation failed, %d\n", rc);
|
||||
out:
|
||||
/* Remove all items */
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
|
||||
"Removing %d items from list tail\n", list_size);
|
||||
while ((li_new = list_remove_tail(&list)))
|
||||
kmem_free(li_new, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test6(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li, *li_prev;
|
||||
int i, list_size = 8, rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
/* Insert all items at the list tail to form a queue */
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
|
||||
"Adding %d items to list tail\n", list_size);
|
||||
for (i = 0; i < list_size; i++) {
|
||||
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_link_init(&li->li_node);
|
||||
li->li_data = i;
|
||||
list_insert_tail(&list, li);
|
||||
}
|
||||
|
||||
/* Remove all odd items from the queue */
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
|
||||
"Removing %d odd items from the list\n", list_size / 2);
|
||||
for (li = list_head(&list); li != NULL; li = list_next(&list, li)) {
|
||||
if (li->li_data % 2 == 1) {
|
||||
li_prev = list_prev(&list, li);
|
||||
list_remove(&list, li);
|
||||
li = li_prev;
|
||||
}
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Validating %d item "
|
||||
"list is a queue of only even elements\n", list_size / 2);
|
||||
rc = splat_list_validate(&list, list_size / 2, LIST_ORDER_QUEUE, 2);
|
||||
if (rc)
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
|
||||
"List validation failed, %d\n", rc);
|
||||
out:
|
||||
/* Remove all items */
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
|
||||
"Removing %d items from list tail\n", list_size / 2);
|
||||
while ((li = list_remove_tail(&list)))
|
||||
kmem_free(li, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_list_test7(struct file *file, void *arg)
|
||||
{
|
||||
list_t list;
|
||||
list_item_t *li;
|
||||
int rc = 0;
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Creating list\n%s", "");
|
||||
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
|
||||
|
||||
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
|
||||
if (li == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Validate newly initialized node is inactive */
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Init list node\n%s", "");
|
||||
list_link_init(&li->li_node);
|
||||
if (list_link_active(&li->li_node)) {
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Newly initialized "
|
||||
"list node should inactive %p/%p\n",
|
||||
li->li_node.prev, li->li_node.next);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Validate node is active when linked in to a list */
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Insert list node\n%s", "");
|
||||
list_insert_head(&list, li);
|
||||
if (!list_link_active(&li->li_node)) {
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node "
|
||||
"inserted in list should be active %p/%p\n",
|
||||
li->li_node.prev, li->li_node.next);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Validate node is inactive when removed from list */
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Remove list node\n%s", "");
|
||||
list_remove(&list, li);
|
||||
if (list_link_active(&li->li_node)) {
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node "
|
||||
"removed from list should be inactive %p/%p\n",
|
||||
li->li_node.prev, li->li_node.next);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
kmem_free(li, sizeof(list_item_t));
|
||||
out:
|
||||
/* Remove all items */
|
||||
while ((li = list_remove_head(&list)))
|
||||
kmem_free(li, sizeof(list_item_t));
|
||||
|
||||
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Destroying list\n%s", "");
|
||||
list_destroy(&list);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_list_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_LIST_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_LIST_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_LIST;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST1_NAME, SPLAT_LIST_TEST1_DESC,
|
||||
SPLAT_LIST_TEST1_ID, splat_list_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST2_NAME, SPLAT_LIST_TEST2_DESC,
|
||||
SPLAT_LIST_TEST2_ID, splat_list_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST3_NAME, SPLAT_LIST_TEST3_DESC,
|
||||
SPLAT_LIST_TEST3_ID, splat_list_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST4_NAME, SPLAT_LIST_TEST4_DESC,
|
||||
SPLAT_LIST_TEST4_ID, splat_list_test4);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST5_NAME, SPLAT_LIST_TEST5_DESC,
|
||||
SPLAT_LIST_TEST5_ID, splat_list_test5);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST6_NAME, SPLAT_LIST_TEST6_DESC,
|
||||
SPLAT_LIST_TEST6_ID, splat_list_test6);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_LIST_TEST7_NAME, SPLAT_LIST_TEST7_DESC,
|
||||
SPLAT_LIST_TEST7_ID, splat_list_test7);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_list_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST7_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST6_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_LIST_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_list_id(void)
|
||||
{
|
||||
return SPLAT_SUBSYSTEM_LIST;
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_MUTEX 0x0400
|
||||
#define SPLAT_MUTEX_NAME "mutex"
|
||||
#define SPLAT_MUTEX_DESC "Kernel Mutex Tests"
|
||||
|
||||
#define SPLAT_MUTEX_TEST1_ID 0x0401
|
||||
#define SPLAT_MUTEX_TEST1_NAME "tryenter"
|
||||
#define SPLAT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness"
|
||||
|
||||
#define SPLAT_MUTEX_TEST2_ID 0x0402
|
||||
#define SPLAT_MUTEX_TEST2_NAME "race"
|
||||
#define SPLAT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex"
|
||||
|
||||
#define SPLAT_MUTEX_TEST3_ID 0x0403
|
||||
#define SPLAT_MUTEX_TEST3_NAME "owned"
|
||||
#define SPLAT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness"
|
||||
|
||||
#define SPLAT_MUTEX_TEST4_ID 0x0404
|
||||
#define SPLAT_MUTEX_TEST4_NAME "owner"
|
||||
#define SPLAT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness"
|
||||
|
||||
#define SPLAT_MUTEX_TEST_MAGIC 0x115599DDUL
|
||||
#define SPLAT_MUTEX_TEST_NAME "mutex_test"
|
||||
#define SPLAT_MUTEX_TEST_WORKQ "mutex_wq"
|
||||
#define SPLAT_MUTEX_TEST_COUNT 128
|
||||
|
||||
typedef struct mutex_priv {
|
||||
unsigned long mp_magic;
|
||||
struct file *mp_file;
|
||||
struct work_struct mp_work[SPLAT_MUTEX_TEST_COUNT];
|
||||
kmutex_t mp_mtx;
|
||||
int mp_rc;
|
||||
} mutex_priv_t;
|
||||
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
static void
|
||||
splat_mutex_test1_work(void *priv)
|
||||
{
|
||||
mutex_priv_t *mp = (mutex_priv_t *)priv;
|
||||
|
||||
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
|
||||
mp->mp_rc = 0;
|
||||
|
||||
if (!mutex_tryenter(&mp->mp_mtx))
|
||||
mp->mp_rc = -EBUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
splat_mutex_test1(struct file *file, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct work;
|
||||
mutex_priv_t *mp;
|
||||
|
||||
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
|
||||
if (mp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
wq = create_singlethread_workqueue(SPLAT_MUTEX_TEST_WORKQ);
|
||||
if (wq == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
mutex_enter(&(mp->mp_mtx));
|
||||
|
||||
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
|
||||
mp->mp_file = file;
|
||||
INIT_WORK(&work, splat_mutex_test1_work, mp);
|
||||
|
||||
/* Schedule a work item which will try and aquire the mutex via
|
||||
* mutex_tryenter() while its held. This should fail and the work
|
||||
* item will indicte this status in the passed private data. */
|
||||
if (!queue_work(wq, &work)) {
|
||||
mutex_exit(&(mp->mp_mtx));
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
mutex_exit(&(mp->mp_mtx));
|
||||
|
||||
/* Work item successfully aquired mutex, very bad! */
|
||||
if (mp->mp_rc != -EBUSY) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
|
||||
"mutex_trylock() correctly failed when mutex held\n");
|
||||
|
||||
/* Schedule a work item which will try and aquire the mutex via
|
||||
* mutex_tryenter() while it is not held. This should work and
|
||||
* the item will indicte this status in the passed private data. */
|
||||
if (!queue_work(wq, &work)) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
|
||||
/* Work item failed to aquire mutex, very bad! */
|
||||
if (mp->mp_rc != 0) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
|
||||
"mutex_trylock() correctly succeeded when mutex unheld\n");
|
||||
out:
|
||||
mutex_destroy(&(mp->mp_mtx));
|
||||
destroy_workqueue(wq);
|
||||
out2:
|
||||
kfree(mp);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
static void
|
||||
splat_mutex_test2_work(void *priv)
|
||||
{
|
||||
mutex_priv_t *mp = (mutex_priv_t *)priv;
|
||||
int rc;
|
||||
|
||||
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
|
||||
|
||||
/* Read the value before sleeping and write it after we wake up to
|
||||
* maximize the chance of a race if mutexs are not working properly */
|
||||
mutex_enter(&mp->mp_mtx);
|
||||
rc = mp->mp_rc;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(HZ / 100); /* 1/100 of a second */
|
||||
mp->mp_rc = rc + 1;
|
||||
mutex_exit(&mp->mp_mtx);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
splat_mutex_test2(struct file *file, void *arg)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef HAVE_3ARGS_INIT_WORK
|
||||
struct workqueue_struct *wq;
|
||||
mutex_priv_t *mp;
|
||||
int i;
|
||||
|
||||
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
|
||||
if (mp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create a thread per CPU items on queue will race */
|
||||
wq = create_workqueue(SPLAT_MUTEX_TEST_WORKQ);
|
||||
if (wq == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
|
||||
mp->mp_file = file;
|
||||
mp->mp_rc = 0;
|
||||
|
||||
/* Schedule N work items to the work queue each of which enters the
|
||||
* mutex, sleeps briefly, then exits the mutex. On a multiprocessor
|
||||
* box these work items will be handled by all available CPUs. The
|
||||
* mutex is instrumented such that if any two processors are in the
|
||||
* critical region at the same time the system will panic. If the
|
||||
* mutex is implemented right this will never happy, that's a pass. */
|
||||
for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) {
|
||||
INIT_WORK(&(mp->mp_work[i]), splat_mutex_test2_work, mp);
|
||||
|
||||
if (!queue_work(wq, &(mp->mp_work[i]))) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME,
|
||||
"Failed to queue work id %d\n", i);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
flush_workqueue(wq);
|
||||
|
||||
if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
|
||||
"correctly entered/exited the mutex %d times\n",
|
||||
num_online_cpus(), mp->mp_rc);
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
|
||||
"only processed %d/%d mutex work items\n",
|
||||
num_online_cpus(), mp->mp_rc, SPLAT_MUTEX_TEST_COUNT);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_destroy(&(mp->mp_mtx));
|
||||
destroy_workqueue(wq);
|
||||
out:
|
||||
kfree(mp);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_mutex_test3(struct file *file, void *arg)
|
||||
{
|
||||
kmutex_t mtx;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mutex_enter(&mtx);
|
||||
|
||||
/* Mutex should be owned by current */
|
||||
if (!mutex_owned(&mtx)) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, mtx.km_owner ? mtx.km_owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_exit(&mtx);
|
||||
|
||||
/* Mutex should not be owned by any task */
|
||||
if (mutex_owned(&mtx)) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should "
|
||||
"not be owned but is owned by pid %d\n",
|
||||
mtx.km_owner ? mtx.km_owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
|
||||
"Correct mutex_owned() behavior\n");
|
||||
out:
|
||||
mutex_destroy(&mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_mutex_test4(struct file *file, void *arg)
|
||||
{
|
||||
kmutex_t mtx;
|
||||
kthread_t *owner;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
|
||||
|
||||
mutex_enter(&mtx);
|
||||
|
||||
/* Mutex should be owned by current */
|
||||
owner = mutex_owner(&mtx);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, owner ? owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_exit(&mtx);
|
||||
|
||||
/* Mutex should not be owned by any task */
|
||||
owner = mutex_owner(&mtx);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
|
||||
"Correct mutex_owner() behavior\n");
|
||||
out:
|
||||
mutex_destroy(&mtx);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_mutex_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_MUTEX_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_MUTEX_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_MUTEX;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST1_NAME, SPLAT_MUTEX_TEST1_DESC,
|
||||
SPLAT_MUTEX_TEST1_ID, splat_mutex_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST2_NAME, SPLAT_MUTEX_TEST2_DESC,
|
||||
SPLAT_MUTEX_TEST2_ID, splat_mutex_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST3_NAME, SPLAT_MUTEX_TEST3_DESC,
|
||||
SPLAT_MUTEX_TEST3_ID, splat_mutex_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_MUTEX_TEST4_NAME, SPLAT_MUTEX_TEST4_DESC,
|
||||
SPLAT_MUTEX_TEST4_ID, splat_mutex_test4);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_mutex_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_MUTEX_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_mutex_id(void) {
|
||||
return SPLAT_SUBSYSTEM_MUTEX;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_KRNG 0x0300
|
||||
#define SPLAT_KRNG_NAME "krng"
|
||||
#define SPLAT_KRNG_DESC "Kernel Random Number Generator Tests"
|
||||
|
||||
#define SPLAT_KRNG_TEST1_ID 0x0301
|
||||
#define SPLAT_KRNG_TEST1_NAME "freq"
|
||||
#define SPLAT_KRNG_TEST1_DESC "Frequency Test"
|
||||
|
||||
#define KRNG_NUM_BITS 1048576
|
||||
#define KRNG_NUM_BYTES (KRNG_NUM_BITS >> 3)
|
||||
#define KRNG_NUM_BITS_DIV2 (KRNG_NUM_BITS >> 1)
|
||||
#define KRNG_ERROR_RANGE 2097
|
||||
|
||||
/* Random Number Generator Tests
|
||||
There can be meny more tests on quality of the
|
||||
random number generator. For now we are only
|
||||
testing the frequency of particular bits.
|
||||
We could also test consecutive sequences,
|
||||
randomness within a particular block, etc.
|
||||
but is probably not necessary for our purposes */
|
||||
|
||||
static int
|
||||
splat_krng_test1(struct file *file, void *arg)
|
||||
{
|
||||
uint8_t *buf;
|
||||
int i, j, diff, num = 0, rc = 0;
|
||||
|
||||
buf = kmalloc(sizeof(*buf) * KRNG_NUM_BYTES, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(*buf) * KRNG_NUM_BYTES);
|
||||
|
||||
/* Always succeeds */
|
||||
random_get_pseudo_bytes(buf, sizeof(uint8_t) * KRNG_NUM_BYTES);
|
||||
|
||||
for (i = 0; i < KRNG_NUM_BYTES; i++) {
|
||||
uint8_t tmp = buf[i];
|
||||
for (j = 0; j < 8; j++) {
|
||||
uint8_t tmp2 = ((tmp >> j) & 0x01);
|
||||
if (tmp2 == 1) {
|
||||
num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
diff = KRNG_NUM_BITS_DIV2 - num;
|
||||
if (diff < 0)
|
||||
diff *= -1;
|
||||
|
||||
splat_print(file, "Test 1 Number of ones: %d\n", num);
|
||||
splat_print(file, "Test 1 Difference from expected: %d Allowed: %d\n",
|
||||
diff, KRNG_ERROR_RANGE);
|
||||
|
||||
if (diff > KRNG_ERROR_RANGE)
|
||||
rc = -ERANGE;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_krng_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_KRNG_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_KRNG_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_KRNG;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_KRNG_TEST1_NAME, SPLAT_KRNG_TEST1_DESC,
|
||||
SPLAT_KRNG_TEST1_ID, splat_krng_test1);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_krng_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_KRNG_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_krng_id(void) {
|
||||
return SPLAT_SUBSYSTEM_KRNG;
|
||||
}
|
||||
@@ -0,0 +1,786 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_RWLOCK 0x0700
|
||||
#define SPLAT_RWLOCK_NAME "rwlock"
|
||||
#define SPLAT_RWLOCK_DESC "Kernel RW Lock Tests"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST1_ID 0x0701
|
||||
#define SPLAT_RWLOCK_TEST1_NAME "rwtest1"
|
||||
#define SPLAT_RWLOCK_TEST1_DESC "Multiple Readers One Writer"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST2_ID 0x0702
|
||||
#define SPLAT_RWLOCK_TEST2_NAME "rwtest2"
|
||||
#define SPLAT_RWLOCK_TEST2_DESC "Multiple Writers"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST3_ID 0x0703
|
||||
#define SPLAT_RWLOCK_TEST3_NAME "rwtest3"
|
||||
#define SPLAT_RWLOCK_TEST3_DESC "Owner Verification"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST4_ID 0x0704
|
||||
#define SPLAT_RWLOCK_TEST4_NAME "rwtest4"
|
||||
#define SPLAT_RWLOCK_TEST4_DESC "Trylock Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST5_ID 0x0705
|
||||
#define SPLAT_RWLOCK_TEST5_NAME "rwtest5"
|
||||
#define SPLAT_RWLOCK_TEST5_DESC "Write Downgrade Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST6_ID 0x0706
|
||||
#define SPLAT_RWLOCK_TEST6_NAME "rwtest6"
|
||||
#define SPLAT_RWLOCK_TEST6_DESC "Read Upgrade Test"
|
||||
|
||||
#define SPLAT_RWLOCK_TEST_MAGIC 0x115599DDUL
|
||||
#define SPLAT_RWLOCK_TEST_NAME "rwlock_test"
|
||||
#define SPLAT_RWLOCK_TEST_COUNT 8
|
||||
|
||||
#define SPLAT_RWLOCK_RELEASE_INIT 0
|
||||
#define SPLAT_RWLOCK_RELEASE_WRITERS 1
|
||||
#define SPLAT_RWLOCK_RELEASE_READERS 2
|
||||
|
||||
typedef struct rw_priv {
|
||||
unsigned long rw_magic;
|
||||
struct file *rw_file;
|
||||
krwlock_t rwl;
|
||||
spinlock_t rw_priv_lock;
|
||||
wait_queue_head_t rw_waitq;
|
||||
atomic_t rw_completed;
|
||||
atomic_t rw_acquired;
|
||||
atomic_t rw_waiters;
|
||||
atomic_t rw_release;
|
||||
} rw_priv_t;
|
||||
|
||||
typedef struct rw_thr {
|
||||
int rwt_id;
|
||||
const char *rwt_name;
|
||||
rw_priv_t *rwt_rwp;
|
||||
int rwt_rc;
|
||||
} rw_thr_t;
|
||||
|
||||
static inline void
|
||||
splat_rwlock_sleep(signed long delay)
|
||||
{
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(delay);
|
||||
}
|
||||
|
||||
#define splat_rwlock_lock_and_test(lock,test) \
|
||||
({ \
|
||||
int ret = 0; \
|
||||
\
|
||||
spin_lock(lock); \
|
||||
ret = (test) ? 1 : 0; \
|
||||
spin_unlock(lock); \
|
||||
ret; \
|
||||
})
|
||||
|
||||
void splat_init_rw_priv(rw_priv_t *rwv, struct file *file)
|
||||
{
|
||||
rwv->rw_magic = SPLAT_RWLOCK_TEST_MAGIC;
|
||||
rwv->rw_file = file;
|
||||
spin_lock_init(&rwv->rw_priv_lock);
|
||||
init_waitqueue_head(&rwv->rw_waitq);
|
||||
atomic_set(&rwv->rw_completed, 0);
|
||||
atomic_set(&rwv->rw_acquired, 0);
|
||||
atomic_set(&rwv->rw_waiters, 0);
|
||||
atomic_set(&rwv->rw_release, SPLAT_RWLOCK_RELEASE_INIT);
|
||||
|
||||
/* Initialize the read/write lock */
|
||||
rw_init(&rwv->rwl, SPLAT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test1_writer_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for writing
|
||||
* release it when we are told to */
|
||||
rw_enter(&rwv->rwl, RW_WRITER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can release the write lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_WRITERS));
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test1_reader_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Don't try and and take the semaphore until
|
||||
* someone else has already acquired it */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_acquired) > 0));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for reading
|
||||
* release it when we are told to */
|
||||
rw_enter(&rwv->rwl, RW_READER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can release the read lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_READERS));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test1(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
rwt[i].rwt_rwp = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST1_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
if (i == 0) {
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_writer_thread,
|
||||
&rwt[i], 0);
|
||||
} else {
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_reader_thread,
|
||||
&rwt[i], 0);
|
||||
}
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the writer has the lock, release the readers */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock, atomic_read(&rwv.rw_acquired) <= 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Ensure that there is only 1 writer and all readers are waiting */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 1 ||
|
||||
atomic_read(&rwv.rw_waiters) !=
|
||||
SPLAT_RWLOCK_TEST_COUNT - 1)) {
|
||||
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Relase the writer */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Now ensure that there are multiple reader threads holding the lock */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) <= 1)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Release the readers */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_READERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test2_writer_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Here just increment the waiters count even if we are not
|
||||
* exactly about to call rw_enter(). Not really a big deal
|
||||
* since more than likely will be true when we simulate work
|
||||
* later on */
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
atomic_inc(&rwv->rw_waiters);
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Wait here until the control thread
|
||||
* says we can acquire the write lock */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_release) ==
|
||||
SPLAT_RWLOCK_RELEASE_WRITERS));
|
||||
|
||||
/* Take the semaphore for writing */
|
||||
rw_enter(&rwv->rwl, RW_WRITER);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_waiters);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Give up the processor for a bit to simulate
|
||||
* doing some work while taking the write lock */
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Ensure that we are the only one writing */
|
||||
if (atomic_read(&rwv->rw_acquired) > 1) {
|
||||
rwt->rwt_rc = 1;
|
||||
} else {
|
||||
rwt->rwt_rc = 0;
|
||||
}
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s writer thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
rw_exit(&rwv->rwl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test2(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
rwt[i].rwt_rwp = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST2_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
pids[i] = kernel_thread(splat_rwlock_test2_writer_thread,
|
||||
&rwt[i], 0);
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for writers to get queued up */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_waiters) < SPLAT_RWLOCK_TEST_COUNT)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Relase the writers */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
|
||||
/* If any of the write threads ever acquired the lock
|
||||
* while another thread had it, make sure we return
|
||||
* an error */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
if (rwt[i].rwt_rc) {
|
||||
rc++;
|
||||
}
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test3(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for writing */
|
||||
rw_enter(&rwv.rwl, RW_WRITER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, owner ? owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Take the rwlock for reading.
|
||||
* Should not have an owner */
|
||||
rw_enter(&rwv.rwl, RW_READER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_test4_reader_thread(void *arg)
|
||||
{
|
||||
rw_thr_t *rwt = (rw_thr_t *)arg;
|
||||
rw_priv_t *rwv = rwt->rwt_rwp;
|
||||
uint8_t rnd = 0;
|
||||
char name[16];
|
||||
|
||||
ASSERT(rwv->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
|
||||
snprintf(name, sizeof(name), "%s%d",
|
||||
SPLAT_RWLOCK_TEST_NAME, rwt->rwt_id);
|
||||
daemonize(name);
|
||||
get_random_bytes((void *)&rnd, 1);
|
||||
splat_rwlock_sleep(rnd * HZ / 1000);
|
||||
|
||||
/* Don't try and and take the semaphore until
|
||||
* someone else has already acquired it */
|
||||
wait_event_interruptible(rwv->rw_waitq,
|
||||
splat_rwlock_lock_and_test(&rwv->rw_priv_lock,
|
||||
atomic_read(&rwv->rw_acquired) > 0));
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread trying to acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Take the semaphore for reading
|
||||
* release it when we are told to */
|
||||
rwt->rwt_rc = rw_tryenter(&rwv->rwl, RW_READER);
|
||||
|
||||
/* Here we acquired the lock this is a
|
||||
* failure since the writer should be
|
||||
* holding the lock */
|
||||
if (rwt->rwt_rc == 1) {
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread acquired rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_dec(&rwv->rw_acquired);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread dropped rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
|
||||
/* Release the semaphore */
|
||||
rw_exit(&rwv->rwl);
|
||||
}
|
||||
/* Here we know we didn't block and didn't
|
||||
* acquire the rwlock for reading */
|
||||
else {
|
||||
spin_lock(&rwv->rw_priv_lock);
|
||||
atomic_inc(&rwv->rw_completed);
|
||||
splat_vprint(rwv->rw_file, rwt->rwt_name,
|
||||
"%s reader thread could not acquire rwlock with "
|
||||
"%d holding lock and %d waiting\n",
|
||||
name, atomic_read(&rwv->rw_acquired),
|
||||
atomic_read(&rwv->rw_waiters));
|
||||
spin_unlock(&rwv->rw_priv_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test4(struct file *file, void *arg)
|
||||
{
|
||||
int i, count = 0, rc = 0;
|
||||
long pids[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
|
||||
rw_priv_t rwv;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Create some threads, the exact number isn't important just as
|
||||
* long as we know how many we managed to create and should expect. */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
rwt[i].rwt_rwp = &rwv;
|
||||
rwt[i].rwt_id = i;
|
||||
rwt[i].rwt_name = SPLAT_RWLOCK_TEST4_NAME;
|
||||
rwt[i].rwt_rc = 0;
|
||||
|
||||
/* The first thread will be a writer */
|
||||
if (i == 0) {
|
||||
/* We can reuse the test1 writer thread here */
|
||||
pids[i] = kernel_thread(splat_rwlock_test1_writer_thread,
|
||||
&rwt[i], 0);
|
||||
} else {
|
||||
pids[i] = kernel_thread(splat_rwlock_test4_reader_thread,
|
||||
&rwt[i], 0);
|
||||
}
|
||||
|
||||
if (pids[i] >= 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the writer has the lock, release the readers */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) <= 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Make sure that the reader threads complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_completed) != SPLAT_RWLOCK_TEST_COUNT - 1)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
/* Release the writer */
|
||||
spin_lock(&rwv.rw_priv_lock);
|
||||
atomic_set(&rwv.rw_release, SPLAT_RWLOCK_RELEASE_WRITERS);
|
||||
spin_unlock(&rwv.rw_priv_lock);
|
||||
wake_up_interruptible(&rwv.rw_waitq);
|
||||
|
||||
/* Wait for the test to complete */
|
||||
while (splat_rwlock_lock_and_test(&rwv.rw_priv_lock,
|
||||
atomic_read(&rwv.rw_acquired) != 0 ||
|
||||
atomic_read(&rwv.rw_waiters) != 0)) {
|
||||
splat_rwlock_sleep(1 * HZ);
|
||||
}
|
||||
|
||||
/* If any of the reader threads ever acquired the lock
|
||||
* while another thread had it, make sure we return
|
||||
* an error since the rw_tryenter() should have failed */
|
||||
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
|
||||
if (rwt[i].rwt_rc) {
|
||||
rc++;
|
||||
}
|
||||
}
|
||||
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test5(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for writing */
|
||||
rw_enter(&rwv.rwl, RW_WRITER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d\n",
|
||||
current->pid, owner ? owner->pid : -1);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure that the downgrade
|
||||
* worked properly */
|
||||
rw_downgrade(&rwv.rwl);
|
||||
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_rwlock_test6(struct file *file, void *arg)
|
||||
{
|
||||
kthread_t *owner;
|
||||
rw_priv_t rwv;
|
||||
int rc = 0;
|
||||
|
||||
/* Initialize private data
|
||||
* including the rwlock */
|
||||
splat_init_rw_priv(&rwv, file);
|
||||
|
||||
/* Take the rwlock for reading */
|
||||
rw_enter(&rwv.rwl, RW_READER);
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "rwlock should not "
|
||||
"be owned but is owned by pid %d\n", owner->pid);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make sure that the upgrade
|
||||
* worked properly */
|
||||
rc = !rw_tryupgrade(&rwv.rwl);
|
||||
|
||||
owner = rw_owner(&rwv.rwl);
|
||||
if (rc || current != owner) {
|
||||
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "rwlock should "
|
||||
"be owned by pid %d but is owned by pid %d "
|
||||
"trylock rc %d\n",
|
||||
current->pid, owner ? owner->pid : -1, rc);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Release the rwlock */
|
||||
rw_exit(&rwv.rwl);
|
||||
|
||||
out:
|
||||
rw_destroy(&rwv.rwl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_rwlock_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_RWLOCK_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_RWLOCK_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_RWLOCK;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST1_NAME, SPLAT_RWLOCK_TEST1_DESC,
|
||||
SPLAT_RWLOCK_TEST1_ID, splat_rwlock_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST2_NAME, SPLAT_RWLOCK_TEST2_DESC,
|
||||
SPLAT_RWLOCK_TEST2_ID, splat_rwlock_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST3_NAME, SPLAT_RWLOCK_TEST3_DESC,
|
||||
SPLAT_RWLOCK_TEST3_ID, splat_rwlock_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST4_NAME, SPLAT_RWLOCK_TEST4_DESC,
|
||||
SPLAT_RWLOCK_TEST4_ID, splat_rwlock_test4);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST5_NAME, SPLAT_RWLOCK_TEST5_DESC,
|
||||
SPLAT_RWLOCK_TEST5_ID, splat_rwlock_test5);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_RWLOCK_TEST6_NAME, SPLAT_RWLOCK_TEST6_DESC,
|
||||
SPLAT_RWLOCK_TEST6_ID, splat_rwlock_test6);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_rwlock_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST6_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_RWLOCK_TEST1_ID);
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_rwlock_id(void) {
|
||||
return SPLAT_SUBSYSTEM_RWLOCK;
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_TASKQ 0x0200
|
||||
#define SPLAT_TASKQ_NAME "taskq"
|
||||
#define SPLAT_TASKQ_DESC "Kernel Task Queue Tests"
|
||||
|
||||
#define SPLAT_TASKQ_TEST1_ID 0x0201
|
||||
#define SPLAT_TASKQ_TEST1_NAME "single"
|
||||
#define SPLAT_TASKQ_TEST1_DESC "Single task queue, single task"
|
||||
|
||||
#define SPLAT_TASKQ_TEST2_ID 0x0202
|
||||
#define SPLAT_TASKQ_TEST2_NAME "multiple"
|
||||
#define SPLAT_TASKQ_TEST2_DESC "Multiple task queues, multiple tasks"
|
||||
|
||||
#define SPLAT_TASKQ_TEST3_ID 0x0203
|
||||
#define SPLAT_TASKQ_TEST3_NAME "system"
|
||||
#define SPLAT_TASKQ_TEST3_DESC "System task queue, multiple tasks"
|
||||
|
||||
typedef struct splat_taskq_arg {
|
||||
int flag;
|
||||
int id;
|
||||
struct file *file;
|
||||
const char *name;
|
||||
} splat_taskq_arg_t;
|
||||
|
||||
/* Validation Test 1 - Create a taskq, queue a task, wait until
|
||||
* task completes, ensure task ran properly, cleanup taskq,
|
||||
*/
|
||||
static void
|
||||
splat_taskq_test13_func(void *arg)
|
||||
{
|
||||
splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
|
||||
|
||||
ASSERT(tq_arg);
|
||||
splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' function '%s' setting flag\n",
|
||||
tq_arg->name, sym2str(splat_taskq_test13_func));
|
||||
tq_arg->flag = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_taskq_test1(struct file *file, void *arg)
|
||||
{
|
||||
taskq_t *tq;
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_arg;
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' creating\n",
|
||||
SPLAT_TASKQ_TEST1_NAME);
|
||||
if ((tq = taskq_create(SPLAT_TASKQ_TEST1_NAME, 1, maxclsyspri,
|
||||
50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' create failed\n",
|
||||
SPLAT_TASKQ_TEST1_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tq_arg.flag = 0;
|
||||
tq_arg.id = 0;
|
||||
tq_arg.file = file;
|
||||
tq_arg.name = SPLAT_TASKQ_TEST1_NAME;
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' function '%s' dispatching\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
if ((id = taskq_dispatch(tq, splat_taskq_test13_func,
|
||||
&tq_arg, TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME,
|
||||
"Taskq '%s' function '%s' dispatch failed\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
taskq_destroy(tq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' waiting\n",
|
||||
tq_arg.name);
|
||||
taskq_wait(tq);
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST1_NAME, "Taskq '%s' destroying\n",
|
||||
tq_arg.name);
|
||||
taskq_destroy(tq);
|
||||
|
||||
return (tq_arg.flag) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/* Validation Test 2 - Create multiple taskq's, each with multiple tasks,
|
||||
* wait until all tasks complete, ensure all tasks ran properly and in the
|
||||
* the correct order, cleanup taskq's
|
||||
*/
|
||||
static void
|
||||
splat_taskq_test2_func1(void *arg)
|
||||
{
|
||||
splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
|
||||
|
||||
ASSERT(tq_arg);
|
||||
splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' flag = %d = %d * 2\n",
|
||||
tq_arg->name, tq_arg->id,
|
||||
sym2str(splat_taskq_test2_func1),
|
||||
tq_arg->flag * 2, tq_arg->flag);
|
||||
tq_arg->flag *= 2;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_taskq_test2_func2(void *arg)
|
||||
{
|
||||
splat_taskq_arg_t *tq_arg = (splat_taskq_arg_t *)arg;
|
||||
|
||||
ASSERT(tq_arg);
|
||||
splat_vprint(tq_arg->file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' flag = %d = %d + 1\n",
|
||||
tq_arg->name, tq_arg->id,
|
||||
sym2str(splat_taskq_test2_func2),
|
||||
tq_arg->flag + 1, tq_arg->flag);
|
||||
tq_arg->flag += 1;
|
||||
}
|
||||
|
||||
#define TEST2_TASKQS 8
|
||||
#define TEST2_THREADS_PER_TASKQ 4
|
||||
|
||||
static int
|
||||
splat_taskq_test2(struct file *file, void *arg) {
|
||||
taskq_t *tq[TEST2_TASKQS] = { NULL };
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_args[TEST2_TASKQS];
|
||||
int i, rc = 0;
|
||||
|
||||
for (i = 0; i < TEST2_TASKQS; i++) {
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME, "Taskq '%s/%d' "
|
||||
"creating\n", SPLAT_TASKQ_TEST2_NAME, i);
|
||||
if ((tq[i] = taskq_create(SPLAT_TASKQ_TEST2_NAME,
|
||||
TEST2_THREADS_PER_TASKQ,
|
||||
maxclsyspri, 50, INT_MAX,
|
||||
TASKQ_PREPOPULATE)) == NULL) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' create failed\n",
|
||||
SPLAT_TASKQ_TEST2_NAME, i);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
tq_args[i].flag = i;
|
||||
tq_args[i].id = i;
|
||||
tq_args[i].file = file;
|
||||
tq_args[i].name = SPLAT_TASKQ_TEST2_NAME;
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' dispatching\n",
|
||||
tq_args[i].name, tq_args[i].id,
|
||||
sym2str(splat_taskq_test2_func1));
|
||||
if ((id = taskq_dispatch(
|
||||
tq[i], splat_taskq_test2_func1,
|
||||
&tq_args[i], TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' dispatch "
|
||||
"failed\n", tq_args[i].name, tq_args[i].id,
|
||||
sym2str(splat_taskq_test2_func1));
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' dispatching\n",
|
||||
tq_args[i].name, tq_args[i].id,
|
||||
sym2str(splat_taskq_test2_func2));
|
||||
if ((id = taskq_dispatch(
|
||||
tq[i], splat_taskq_test2_func2,
|
||||
&tq_args[i], TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' function '%s' dispatch failed\n",
|
||||
tq_args[i].name, tq_args[i].id,
|
||||
sym2str(splat_taskq_test2_func2));
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* When rc is set we're effectively just doing cleanup here, so
|
||||
* ignore new errors in that case. They just cause noise. */
|
||||
for (i = 0; i < TEST2_TASKQS; i++) {
|
||||
if (tq[i] != NULL) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' waiting\n",
|
||||
tq_args[i].name, tq_args[i].id);
|
||||
taskq_wait(tq[i]);
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d; destroying\n",
|
||||
tq_args[i].name, tq_args[i].id);
|
||||
taskq_destroy(tq[i]);
|
||||
|
||||
if (!rc && tq_args[i].flag != ((i * 2) + 1)) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' processed tasks "
|
||||
"out of order; %d != %d\n",
|
||||
tq_args[i].name, tq_args[i].id,
|
||||
tq_args[i].flag, i * 2 + 1);
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST2_NAME,
|
||||
"Taskq '%s/%d' processed tasks "
|
||||
"in the correct order; %d == %d\n",
|
||||
tq_args[i].name, tq_args[i].id,
|
||||
tq_args[i].flag, i * 2 + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validation Test 3 - Use the global system task queue with a single
|
||||
* task, * wait until task completes, ensure task ran properly.
|
||||
*/
|
||||
static int
|
||||
splat_taskq_test3(struct file *file, void *arg)
|
||||
{
|
||||
taskqid_t id;
|
||||
splat_taskq_arg_t tq_arg;
|
||||
|
||||
tq_arg.flag = 0;
|
||||
tq_arg.id = 0;
|
||||
tq_arg.file = file;
|
||||
tq_arg.name = SPLAT_TASKQ_TEST3_NAME;
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
|
||||
"Taskq '%s' function '%s' dispatching\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
if ((id = taskq_dispatch(system_taskq, splat_taskq_test13_func,
|
||||
&tq_arg, TQ_SLEEP)) == 0) {
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST3_NAME,
|
||||
"Taskq '%s' function '%s' dispatch failed\n",
|
||||
tq_arg.name, sym2str(splat_taskq_test13_func));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_TASKQ_TEST3_NAME, "Taskq '%s' waiting\n",
|
||||
tq_arg.name);
|
||||
taskq_wait(system_taskq);
|
||||
|
||||
return (tq_arg.flag) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_taskq_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_TASKQ_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_TASKQ_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_TASKQ;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST1_NAME, SPLAT_TASKQ_TEST1_DESC,
|
||||
SPLAT_TASKQ_TEST1_ID, splat_taskq_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST2_NAME, SPLAT_TASKQ_TEST2_DESC,
|
||||
SPLAT_TASKQ_TEST2_ID, splat_taskq_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_TASKQ_TEST3_NAME, SPLAT_TASKQ_TEST3_DESC,
|
||||
SPLAT_TASKQ_TEST3_ID, splat_taskq_test3);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_taskq_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_TASKQ_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_taskq_id(void) {
|
||||
return SPLAT_SUBSYSTEM_TASKQ;
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_THREAD 0x0600
|
||||
#define SPLAT_THREAD_NAME "thread"
|
||||
#define SPLAT_THREAD_DESC "Kernel Thread Tests"
|
||||
|
||||
#define SPLAT_THREAD_TEST1_ID 0x0601
|
||||
#define SPLAT_THREAD_TEST1_NAME "create"
|
||||
#define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
|
||||
|
||||
#define SPLAT_THREAD_TEST2_ID 0x0602
|
||||
#define SPLAT_THREAD_TEST2_NAME "exit"
|
||||
#define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
|
||||
|
||||
#define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
|
||||
|
||||
typedef struct thread_priv {
|
||||
unsigned long tp_magic;
|
||||
struct file *tp_file;
|
||||
spinlock_t tp_lock;
|
||||
wait_queue_head_t tp_waitq;
|
||||
int tp_rc;
|
||||
} thread_priv_t;
|
||||
|
||||
static int
|
||||
splat_thread_rc(thread_priv_t *tp, int rc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&tp->tp_lock);
|
||||
ret = (tp->tp_rc == rc);
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_thread_work1(void *priv)
|
||||
{
|
||||
thread_priv_t *tp = (thread_priv_t *)priv;
|
||||
|
||||
spin_lock(&tp->tp_lock);
|
||||
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
|
||||
tp->tp_rc = 1;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
thread_exit();
|
||||
}
|
||||
|
||||
static int
|
||||
splat_thread_test1(struct file *file, void *arg)
|
||||
{
|
||||
thread_priv_t tp;
|
||||
kthread_t *thr;
|
||||
|
||||
tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
|
||||
tp.tp_file = file;
|
||||
spin_lock_init(&tp.tp_lock);
|
||||
init_waitqueue_head(&tp.tp_waitq);
|
||||
tp.tp_rc = 0;
|
||||
|
||||
thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work1, &tp, 0,
|
||||
&p0, TS_RUN, minclsyspri);
|
||||
/* Must never fail under Solaris, but we check anyway since this
|
||||
* can happen in the linux SPL, we may want to change this behavior */
|
||||
if (thr == NULL)
|
||||
return -ESRCH;
|
||||
|
||||
/* Sleep until the thread sets tp.tp_rc == 1 */
|
||||
wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
|
||||
|
||||
splat_vprint(file, SPLAT_THREAD_TEST1_NAME, "%s",
|
||||
"Thread successfully started properly\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
splat_thread_work2(void *priv)
|
||||
{
|
||||
thread_priv_t *tp = (thread_priv_t *)priv;
|
||||
|
||||
spin_lock(&tp->tp_lock);
|
||||
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
|
||||
tp->tp_rc = 1;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
thread_exit();
|
||||
|
||||
/* The following code is unreachable when thread_exit() is
|
||||
* working properly, which is exactly what we're testing */
|
||||
spin_lock(&tp->tp_lock);
|
||||
tp->tp_rc = 2;
|
||||
spin_unlock(&tp->tp_lock);
|
||||
|
||||
wake_up(&tp->tp_waitq);
|
||||
}
|
||||
|
||||
static int
|
||||
splat_thread_test2(struct file *file, void *arg)
|
||||
{
|
||||
thread_priv_t tp;
|
||||
kthread_t *thr;
|
||||
int rc = 0;
|
||||
|
||||
tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
|
||||
tp.tp_file = file;
|
||||
spin_lock_init(&tp.tp_lock);
|
||||
init_waitqueue_head(&tp.tp_waitq);
|
||||
tp.tp_rc = 0;
|
||||
|
||||
thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work2, &tp, 0,
|
||||
&p0, TS_RUN, minclsyspri);
|
||||
/* Must never fail under Solaris, but we check anyway since this
|
||||
* can happen in the linux SPL, we may want to change this behavior */
|
||||
if (thr == NULL)
|
||||
return -ESRCH;
|
||||
|
||||
/* Sleep until the thread sets tp.tp_rc == 1 */
|
||||
wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
|
||||
|
||||
/* Sleep until the thread sets tp.tp_rc == 2, or until we hit
|
||||
* the timeout. If thread exit is working properly we should
|
||||
* hit the timeout and never see to.tp_rc == 2. */
|
||||
rc = wait_event_timeout(tp.tp_waitq, splat_thread_rc(&tp, 2), HZ / 10);
|
||||
if (rc > 0) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
|
||||
"Thread did not exit properly at thread_exit()\n");
|
||||
} else {
|
||||
splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
|
||||
"Thread successfully exited at thread_exit()\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_thread_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_THREAD_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_THREAD_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_THREAD;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST1_NAME, SPLAT_THREAD_TEST1_DESC,
|
||||
SPLAT_THREAD_TEST1_ID, splat_thread_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_THREAD_TEST2_NAME, SPLAT_THREAD_TEST2_DESC,
|
||||
SPLAT_THREAD_TEST2_ID, splat_thread_test2);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_thread_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_THREAD_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_thread_id(void) {
|
||||
return SPLAT_SUBSYSTEM_THREAD;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
|
||||
#define SPLAT_SUBSYSTEM_TIME 0x0800
|
||||
#define SPLAT_TIME_NAME "time"
|
||||
#define SPLAT_TIME_DESC "Kernel Time Tests"
|
||||
|
||||
#define SPLAT_TIME_TEST1_ID 0x0801
|
||||
#define SPLAT_TIME_TEST1_NAME "time1"
|
||||
#define SPLAT_TIME_TEST1_DESC "HZ Test"
|
||||
|
||||
#define SPLAT_TIME_TEST2_ID 0x0802
|
||||
#define SPLAT_TIME_TEST2_NAME "time2"
|
||||
#define SPLAT_TIME_TEST2_DESC "Monotonic Test"
|
||||
|
||||
static int
|
||||
splat_time_test1(struct file *file, void *arg)
|
||||
{
|
||||
int myhz = hz;
|
||||
splat_vprint(file, SPLAT_TIME_TEST1_NAME, "hz is %d\n", myhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
splat_time_test2(struct file *file, void *arg)
|
||||
{
|
||||
hrtime_t tm1, tm2;
|
||||
int i;
|
||||
|
||||
tm1 = gethrtime();
|
||||
splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm1);
|
||||
|
||||
for(i = 0; i < 100; i++) {
|
||||
tm2 = gethrtime();
|
||||
splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm2);
|
||||
|
||||
if(tm1 > tm2) {
|
||||
splat_print(file, "%s: gethrtime() is not giving "
|
||||
"monotonically increasing values\n",
|
||||
SPLAT_TIME_TEST2_NAME);
|
||||
return 1;
|
||||
}
|
||||
tm1 = tm2;
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_time_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_TIME_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_TIME_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_TIME;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_TIME_TEST1_NAME, SPLAT_TIME_TEST1_DESC,
|
||||
SPLAT_TIME_TEST1_ID, splat_time_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_TIME_TEST2_NAME, SPLAT_TIME_TEST2_DESC,
|
||||
SPLAT_TIME_TEST2_ID, splat_time_test2);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
void
|
||||
splat_time_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_TIME_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_TIME_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
}
|
||||
|
||||
int
|
||||
splat_time_id(void)
|
||||
{
|
||||
return SPLAT_SUBSYSTEM_TIME;
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* This file is part of the SPL: Solaris Porting Layer.
|
||||
*
|
||||
* Copyright (c) 2008 Lawrence Livermore National Security, LLC.
|
||||
* Produced at Lawrence Livermore National Laboratory
|
||||
* Written by:
|
||||
* Brian Behlendorf <behlendorf1@llnl.gov>,
|
||||
* Herb Wartens <wartens2@llnl.gov>,
|
||||
* Jim Garlick <garlick@llnl.gov>
|
||||
* UCRL-CODE-235197
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "splat-internal.h"
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
#define SPLAT_SUBSYSTEM_VNODE 0x0900
|
||||
#define SPLAT_VNODE_NAME "vnode"
|
||||
#define SPLAT_VNODE_DESC "Kernel Vnode Tests"
|
||||
|
||||
#define SPLAT_VNODE_TEST1_ID 0x0901
|
||||
#define SPLAT_VNODE_TEST1_NAME "vn_open"
|
||||
#define SPLAT_VNODE_TEST1_DESC "Vn_open Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST2_ID 0x0902
|
||||
#define SPLAT_VNODE_TEST2_NAME "vn_openat"
|
||||
#define SPLAT_VNODE_TEST2_DESC "Vn_openat Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST3_ID 0x0903
|
||||
#define SPLAT_VNODE_TEST3_NAME "vn_rdwr"
|
||||
#define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST4_ID 0x0904
|
||||
#define SPLAT_VNODE_TEST4_NAME "vn_rename"
|
||||
#define SPLAT_VNODE_TEST4_DESC "Vn_rename Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST5_ID 0x0905
|
||||
#define SPLAT_VNODE_TEST5_NAME "vn_getattr"
|
||||
#define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST6_ID 0x0906
|
||||
#define SPLAT_VNODE_TEST6_NAME "vn_sync"
|
||||
#define SPLAT_VNODE_TEST6_DESC "Vn_sync Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST7_ID 0x0907
|
||||
#define SPLAT_VNODE_TEST7_NAME "vn_getf"
|
||||
#define SPLAT_VNODE_TEST7_DESC "vn_getf/vn_releasef Test"
|
||||
|
||||
#define SPLAT_VNODE_TEST_FILE "/etc/fstab"
|
||||
#define SPLAT_VNODE_TEST_FILE_AT "etc/fstab"
|
||||
#define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp"
|
||||
#define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1"
|
||||
#define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2"
|
||||
|
||||
static int
|
||||
splat_vnode_test1(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE,
|
||||
FREAD, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST1_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST1_NAME,
|
||||
"Failed to vn_close test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed "
|
||||
"and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test1() */
|
||||
|
||||
static int
|
||||
splat_vnode_test2(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE,
|
||||
FREAD, 0644, &vp, 0, 0, rootdir, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST2_NAME,
|
||||
"Failed to vn_openat test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST2_NAME,
|
||||
"Failed to vn_close test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed "
|
||||
"and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test2() */
|
||||
|
||||
static int
|
||||
splat_vnode_test3(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
char buf1[32] = "SPL VNode Interface Test File\n";
|
||||
char buf2[32] = "";
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE,
|
||||
FWRITE | FREAD | FCREAT | FEXCL,
|
||||
0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
|
||||
"Failed vn_rdwr read of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strncmp(buf1, buf2, strlen(buf1))) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
|
||||
"Failed strncmp data written does not match "
|
||||
"data read\nWrote: %sRead: %s\n", buf1, buf2);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and "
|
||||
"read expected data pattern to test file: %s\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW);
|
||||
|
||||
out:
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test3() */
|
||||
|
||||
static int
|
||||
splat_vnode_test4(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
char buf1[32] = "SPL VNode Interface Test File\n";
|
||||
char buf2[32] = "";
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE,
|
||||
FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, rc);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
|
||||
rc = vn_rename(SPLAT_VNODE_TEST_FILE_RW1,SPLAT_VNODE_TEST_FILE_RW2,0);
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rename "
|
||||
"%s -> %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1,
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE,
|
||||
FREAD | FEXCL, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed vn_rdwr read of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, rc);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (strncmp(buf1, buf2, strlen(buf1))) {
|
||||
rc = EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME,
|
||||
"Failed strncmp data written does not match "
|
||||
"data read\nWrote: %sRead: %s\n", buf1, buf2);
|
||||
goto out2;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Wrote to %s: %s",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, buf1);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Read from %s: %s",
|
||||
SPLAT_VNODE_TEST_FILE_RW2, buf2);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Successfully renamed "
|
||||
"test file %s -> %s and verified data pattern\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2);
|
||||
out2:
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
out:
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, RMFILE);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test4() */
|
||||
|
||||
static int
|
||||
splat_vnode_test5(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
vattr_t vap;
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE,
|
||||
FREAD, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = VOP_GETATTR(vp, &vap, 0, 0, NULL);
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
|
||||
"Failed to vn_getattr test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vap.va_type != VREG) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
|
||||
"Failed expected regular file type "
|
||||
"(%d != VREG): %s (%d)\n", vap.va_type,
|
||||
SPLAT_VNODE_TEST_FILE, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully "
|
||||
"vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE);
|
||||
|
||||
out:
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test5() */
|
||||
|
||||
static int
|
||||
splat_vnode_test6(struct file *file, void *arg)
|
||||
{
|
||||
vnode_t *vp;
|
||||
char buf[32] = "SPL VNode Interface Test File\n";
|
||||
int rc;
|
||||
|
||||
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE,
|
||||
FWRITE | FCREAT | FEXCL, 0644, &vp, 0, 0))) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
|
||||
"Failed to vn_open test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_fsync(vp, 0, 0, 0);
|
||||
if (rc) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
|
||||
"Failed vn_fsync of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully "
|
||||
"fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW);
|
||||
out:
|
||||
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
|
||||
VN_RELE(vp);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test6() */
|
||||
|
||||
/* Basically a slightly modified version of sys_close() */
|
||||
static int
|
||||
fd_uninstall(int fd)
|
||||
{
|
||||
struct file *fp;
|
||||
struct files_struct *files = current->files;
|
||||
#ifdef HAVE_FILES_FDTABLE
|
||||
struct fdtable *fdt;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
|
||||
if (fd >= fdt->max_fds)
|
||||
goto out_unlock;
|
||||
|
||||
fp = fdt->fd[fd];
|
||||
if (!fp)
|
||||
goto out_unlock;
|
||||
|
||||
rcu_assign_pointer(fdt->fd[fd], NULL);
|
||||
FD_CLR(fd, fdt->close_on_exec);
|
||||
#else
|
||||
spin_lock(&files->file_lock);
|
||||
if (fd >= files->max_fds)
|
||||
goto out_unlock;
|
||||
|
||||
fp = files->fd[fd];
|
||||
if (!fp)
|
||||
goto out_unlock;
|
||||
|
||||
files->fd[fd] = NULL;
|
||||
FD_CLR(fd, files->close_on_exec);
|
||||
#endif
|
||||
/* Dropping the lock here exposes a minor race but it allows me
|
||||
* to use the existing kernel interfaces for this, and for a test
|
||||
* case I think that's reasonable. */
|
||||
spin_unlock(&files->file_lock);
|
||||
put_unused_fd(fd);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&files->file_lock);
|
||||
return -EBADF;
|
||||
} /* fd_uninstall() */
|
||||
|
||||
static int
|
||||
splat_vnode_test7(struct file *file, void *arg)
|
||||
{
|
||||
char buf1[32] = "SPL VNode Interface Test File\n";
|
||||
char buf2[32] = "";
|
||||
struct file *lfp;
|
||||
file_t *fp;
|
||||
int rc, fd;
|
||||
|
||||
/* Prep work needed to test getf/releasef */
|
||||
fd = get_unused_fd();
|
||||
if (fd < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to get unused fd (%d)\n", fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
lfp = filp_open(SPLAT_VNODE_TEST_FILE_RW, O_RDWR|O_CREAT|O_EXCL, 0644);
|
||||
if (IS_ERR(lfp)) {
|
||||
put_unused_fd(fd);
|
||||
rc = PTR_ERR(lfp);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to filp_open: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pair up the new fd and lfp in the current context, this allows
|
||||
* getf to lookup the file struct simply by the known open fd */
|
||||
fd_install(fd, lfp);
|
||||
|
||||
/* Actual getf()/releasef() test */
|
||||
fp = vn_getf(fd);
|
||||
if (fp == NULL) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed to getf fd %d: (%d)\n", fd, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_WRITE, fp->f_vnode, buf1, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed vn_rdwr write of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = vn_rdwr(UIO_READ, fp->f_vnode, buf2, strlen(buf1), 0,
|
||||
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
|
||||
if (rc < 0) {
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed vn_rdwr read of test file: %s (%d)\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW, rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strncmp(buf1, buf2, strlen(buf1))) {
|
||||
rc = -EINVAL;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST7_NAME,
|
||||
"Failed strncmp data written does not match "
|
||||
"data read\nWrote: %sRead: %s\n", buf1, buf2);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2);
|
||||
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and "
|
||||
"read expected data pattern to test file: %s\n",
|
||||
SPLAT_VNODE_TEST_FILE_RW);
|
||||
out:
|
||||
vn_releasef(fd);
|
||||
fd_uninstall(fd);
|
||||
filp_close(lfp, 0);
|
||||
vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE);
|
||||
|
||||
return rc;
|
||||
} /* splat_vnode_test7() */
|
||||
|
||||
splat_subsystem_t *
|
||||
splat_vnode_init(void)
|
||||
{
|
||||
splat_subsystem_t *sub;
|
||||
|
||||
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
|
||||
if (sub == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(sub, 0, sizeof(*sub));
|
||||
strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE);
|
||||
strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE);
|
||||
INIT_LIST_HEAD(&sub->subsystem_list);
|
||||
INIT_LIST_HEAD(&sub->test_list);
|
||||
spin_lock_init(&sub->test_lock);
|
||||
sub->desc.id = SPLAT_SUBSYSTEM_VNODE;
|
||||
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC,
|
||||
SPLAT_VNODE_TEST1_ID, splat_vnode_test1);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC,
|
||||
SPLAT_VNODE_TEST2_ID, splat_vnode_test2);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC,
|
||||
SPLAT_VNODE_TEST3_ID, splat_vnode_test3);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST4_NAME, SPLAT_VNODE_TEST4_DESC,
|
||||
SPLAT_VNODE_TEST4_ID, splat_vnode_test4);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC,
|
||||
SPLAT_VNODE_TEST5_ID, splat_vnode_test5);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC,
|
||||
SPLAT_VNODE_TEST6_ID, splat_vnode_test6);
|
||||
SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST7_NAME, SPLAT_VNODE_TEST7_DESC,
|
||||
SPLAT_VNODE_TEST7_ID, splat_vnode_test7);
|
||||
|
||||
return sub;
|
||||
} /* splat_vnode_init() */
|
||||
|
||||
void
|
||||
splat_vnode_fini(splat_subsystem_t *sub)
|
||||
{
|
||||
ASSERT(sub);
|
||||
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST7_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST6_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST5_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST4_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST3_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST2_ID);
|
||||
SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST1_ID);
|
||||
|
||||
kfree(sub);
|
||||
} /* splat_vnode_fini() */
|
||||
|
||||
int
|
||||
splat_vnode_id(void)
|
||||
{
|
||||
return SPLAT_SUBSYSTEM_VNODE;
|
||||
} /* splat_vnode_id() */
|
||||
Reference in New Issue
Block a user