Prepare SPL repo to merge with ZFS repo

This commit removes everything from the repository except the core
SPL implementation for Linux.  Those files which remain have been
moved to non-conflicting locations to facilitate the merge.
The README.md and associated files have been updated accordingly.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
Brian Behlendorf
2018-02-14 17:01:15 -08:00
parent 1149b62d20
commit a91258913f
222 changed files with 777 additions and 16870 deletions
-13
View File
@@ -1,13 +0,0 @@
*.ko
*.ko.unsigned
*.ko.out
*.ko.out.sig
*.mod.c
.*.cmd
modules.order
/.tmp_versions
/Module.markers
/Module.symvers
!Makefile.in
-54
View File
@@ -1,54 +0,0 @@
subdir-m += spl
subdir-m += splat
INSTALL_MOD_DIR ?= extra
SPL_MODULE_CFLAGS = -I@abs_top_srcdir@/include
SPL_MODULE_CFLAGS += -include @abs_top_builddir@/spl_config.h
export SPL_MODULE_CFLAGS
modules:
$(MAKE) -C @LINUX_OBJ@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ CONFIG_SPL=m $@
clean:
@# Only cleanup the kernel build directories when CONFIG_KERNEL
@# is defined. This indicates that kernel modules should be built.
@CONFIG_KERNEL_TRUE@ $(MAKE) -C @LINUX_OBJ@ SUBDIRS=`pwd` @KERNELMAKE_PARAMS@ $@
if [ -f @LINUX_SYMBOLS@ ]; then $(RM) @LINUX_SYMBOLS@; fi
if [ -f Module.markers ]; then $(RM) Module.markers; fi
modules_install:
@# Install the kernel modules
$(MAKE) -C @LINUX_OBJ@ SUBDIRS=`pwd` $@ \
INSTALL_MOD_PATH=$(DESTDIR)$(INSTALL_MOD_PATH) \
INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) \
KERNELRELEASE=@LINUX_VERSION@
@# Remove extraneous build products when packaging
kmoddir=$(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/@LINUX_VERSION@; \
if [ -n "$(DESTDIR)" ]; then \
find $$kmoddir -name 'modules.*' | xargs $(RM); \
fi
sysmap=$(DESTDIR)$(INSTALL_MOD_PATH)/boot/System.map-@LINUX_VERSION@; \
if [ -f $$sysmap ]; then \
depmod -ae -F $$sysmap @LINUX_VERSION@; \
fi
modules_uninstall:
@# Uninstall the kernel modules
kmoddir=$(DESTDIR)$(INSTALL_MOD_PATH)/lib/modules/@LINUX_VERSION@
list='$(subdir-m)'; for subdir in $$list; do \
$(RM) -R $$kmoddir/$(INSTALL_MOD_DIR)/$$subdir; \
done
distdir:
list='$(subdir-m)'; for subdir in $$list; do \
(find @top_srcdir@/module/$$subdir -name '*.c' -o -name '*.h' |\
xargs /bin/cp -t $$distdir/$$subdir); \
done
distclean maintainer-clean: clean
install: modules_install
uninstall: modules_uninstall
all: modules
check:
-30
View File
@@ -1,30 +0,0 @@
# Makefile.in for spl kernel module
src = @abs_top_srcdir@/module/spl
obj = @abs_builddir@
MODULE := spl
EXTRA_CFLAGS = $(SPL_MODULE_CFLAGS) @KERNELCPPFLAGS@
# Solaris porting layer module
obj-$(CONFIG_SPL) := $(MODULE).o
$(MODULE)-objs += spl-proc.o
$(MODULE)-objs += spl-kmem.o
$(MODULE)-objs += spl-kmem-cache.o
$(MODULE)-objs += spl-vmem.o
$(MODULE)-objs += spl-thread.o
$(MODULE)-objs += spl-taskq.o
$(MODULE)-objs += spl-rwlock.o
$(MODULE)-objs += spl-vnode.o
$(MODULE)-objs += spl-err.o
$(MODULE)-objs += spl-kobj.o
$(MODULE)-objs += spl-generic.o
$(MODULE)-objs += spl-atomic.o
$(MODULE)-objs += spl-mutex.o
$(MODULE)-objs += spl-kstat.o
$(MODULE)-objs += spl-condvar.o
$(MODULE)-objs += spl-xdr.o
$(MODULE)-objs += spl-cred.o
$(MODULE)-objs += spl-tsd.o
$(MODULE)-objs += spl-zlib.o
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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 program 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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
@@ -0,0 +1 @@
COMPATIBILITY LAYER FOR OPENZFS ON LINUX
+7 -2
View File
@@ -43,9 +43,8 @@
#include <linux/ctype.h>
#include <sys/disp.h>
#include <sys/random.h>
#include <sys/strings.h>
#include <linux/kmod.h>
#include <linux/math64_compat.h>
#include <linux/proc_compat.h>
char spl_version[32] = "SPL v" SPL_META_VERSION "-" SPL_META_RELEASE;
EXPORT_SYMBOL(spl_version);
@@ -260,6 +259,12 @@ __udivdi3(uint64_t u, uint64_t v)
}
EXPORT_SYMBOL(__udivdi3);
/* BEGIN CSTYLED */
#ifndef abs64
#define abs64(x) ({ uint64_t t = (x) >> 63; ((x) ^ t) - t; })
#endif
/* END CSTYLED */
/*
* Implementation of 64-bit signed division for 32-bit machines.
*/
+2 -2
View File
@@ -24,13 +24,13 @@
#include <sys/kmem.h>
#include <sys/kmem_cache.h>
#include <sys/shrinker.h>
#include <sys/taskq.h>
#include <sys/timer.h>
#include <sys/vmem.h>
#include <sys/wait.h>
#include <linux/slab.h>
#include <linux/swap.h>
#include <linux/mm_compat.h>
#include <linux/wait_compat.h>
#include <linux/prefetch.h>
/*
+1
View File
@@ -28,6 +28,7 @@
#include <sys/kstat.h>
#include <sys/vmem.h>
#include <sys/cmn_err.h>
#include <sys/sysmacros.h>
#ifndef HAVE_PDE_DATA
#define PDE_DATA(x) (PDE(x)->data)
+1 -1
View File
@@ -30,10 +30,10 @@
#include <sys/kmem_cache.h>
#include <sys/vmem.h>
#include <sys/taskq.h>
#include <sys/proc.h>
#include <linux/ctype.h>
#include <linux/kmod.h>
#include <linux/seq_file.h>
#include <linux/proc_compat.h>
#include <linux/uaccess.h>
#include <linux/version.h>
+1 -1
View File
@@ -25,7 +25,7 @@
#include <sys/debug.h>
#include <sys/vmem.h>
#include <sys/kmem_cache.h>
#include <linux/mm_compat.h>
#include <sys/shrinker.h>
#include <linux/module.h>
vmem_t *heap_arena = NULL;
+75 -1
View File
@@ -28,7 +28,11 @@
#include <sys/vnode.h>
#include <sys/kmem_cache.h>
#include <linux/falloc.h>
#include <linux/file_compat.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#ifdef HAVE_FDTABLE_HEADER
#include <linux/fdtable.h>
#endif
vnode_t *rootdir = (vnode_t *)0xabcd1234;
EXPORT_SYMBOL(rootdir);
@@ -39,6 +43,76 @@ static spl_kmem_cache_t *vn_file_cache;
static DEFINE_SPINLOCK(vn_file_lock);
static LIST_HEAD(vn_file_list);
static int
spl_filp_fallocate(struct file *fp, int mode, loff_t offset, loff_t len)
{
int error = -EOPNOTSUPP;
#ifdef HAVE_FILE_FALLOCATE
if (fp->f_op->fallocate)
error = fp->f_op->fallocate(fp, mode, offset, len);
#else
#ifdef HAVE_INODE_FALLOCATE
if (fp->f_dentry && fp->f_dentry->d_inode &&
fp->f_dentry->d_inode->i_op->fallocate)
error = fp->f_dentry->d_inode->i_op->fallocate(
fp->f_dentry->d_inode, mode, offset, len);
#endif /* HAVE_INODE_FALLOCATE */
#endif /* HAVE_FILE_FALLOCATE */
return (error);
}
static int
spl_filp_fsync(struct file *fp, int sync)
{
#ifdef HAVE_2ARGS_VFS_FSYNC
return (vfs_fsync(fp, sync));
#else
return (vfs_fsync(fp, (fp)->f_dentry, sync));
#endif /* HAVE_2ARGS_VFS_FSYNC */
}
static ssize_t
spl_kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
{
#if defined(HAVE_KERNEL_WRITE_PPOS)
return (kernel_write(file, buf, count, pos));
#else
mm_segment_t saved_fs;
ssize_t ret;
saved_fs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, (__force const char __user *)buf, count, pos);
set_fs(saved_fs);
return (ret);
#endif
}
static ssize_t
spl_kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
{
#if defined(HAVE_KERNEL_READ_PPOS)
return (kernel_read(file, buf, count, pos));
#else
mm_segment_t saved_fs;
ssize_t ret;
saved_fs = get_fs();
set_fs(get_ds());
ret = vfs_read(file, (void __user *)buf, count, pos);
set_fs(saved_fs);
return (ret);
#endif
}
vtype_t
vn_mode_to_vtype(mode_t mode)
{
+3 -1
View File
@@ -25,7 +25,7 @@
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/types.h>
#include <rpc/types.h>
#include <sys/sysmacros.h>
#include <rpc/xdr.h>
/*
@@ -131,6 +131,8 @@
static struct xdr_ops xdrmem_encode_ops;
static struct xdr_ops xdrmem_decode_ops;
typedef int bool_t;
void
xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size,
const enum xdr_op op)
-1
View File
@@ -57,7 +57,6 @@
#include <sys/kmem.h>
#include <sys/kmem_cache.h>
#include <sys/zmod.h>
#include <linux/zlib_compat.h>
static spl_kmem_cache_t *zlib_workspace_cache;
-28
View File
@@ -1,28 +0,0 @@
# Makefile.in for splat kernel module
src = @abs_top_srcdir@/module/splat
obj = @abs_builddir@
MODULE := splat
EXTRA_CFLAGS = $(SPL_MODULE_CFLAGS) @KERNELCPPFLAGS@
# Solaris Porting LAyer Tests
obj-$(CONFIG_SPL) := $(MODULE).o
$(MODULE)-objs += splat-ctl.o
$(MODULE)-objs += splat-kmem.o
$(MODULE)-objs += splat-taskq.o
$(MODULE)-objs += splat-random.o
$(MODULE)-objs += splat-mutex.o
$(MODULE)-objs += splat-condvar.o
$(MODULE)-objs += splat-thread.o
$(MODULE)-objs += splat-rwlock.o
$(MODULE)-objs += splat-time.o
$(MODULE)-objs += splat-vnode.o
$(MODULE)-objs += splat-kobj.o
$(MODULE)-objs += splat-atomic.o
$(MODULE)-objs += splat-list.o
$(MODULE)-objs += splat-generic.o
$(MODULE)-objs += splat-cred.o
$(MODULE)-objs += splat-zlib.o
$(MODULE)-objs += splat-linux.o
-233
View File
@@ -1,233 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Atomic Tests.
*/
#include <sys/atomic.h>
#include <sys/thread.h>
#include <sys/mutex.h>
#include <linux/mm_compat.h>
#include <linux/wait_compat.h>
#include <linux/slab.h>
#include "splat-internal.h"
#define SPLAT_ATOMIC_NAME "atomic"
#define SPLAT_ATOMIC_DESC "Kernel Atomic Tests"
#define SPLAT_ATOMIC_TEST1_ID 0x0b01
#define SPLAT_ATOMIC_TEST1_NAME "64-bit"
#define SPLAT_ATOMIC_TEST1_DESC "Validate 64-bit atomic ops"
#define SPLAT_ATOMIC_TEST_MAGIC 0x43435454UL
#define SPLAT_ATOMIC_INIT_VALUE 10000000UL
typedef enum {
SPLAT_ATOMIC_INC_64 = 0,
SPLAT_ATOMIC_DEC_64 = 1,
SPLAT_ATOMIC_ADD_64 = 2,
SPLAT_ATOMIC_SUB_64 = 3,
SPLAT_ATOMIC_ADD_64_NV = 4,
SPLAT_ATOMIC_SUB_64_NV = 5,
SPLAT_ATOMIC_COUNT_64 = 6
} atomic_op_t;
typedef struct atomic_priv {
unsigned long ap_magic;
struct file *ap_file;
kmutex_t ap_lock;
spl_wait_queue_head_t ap_waitq;
volatile uint64_t ap_atomic;
volatile uint64_t ap_atomic_exited;
atomic_op_t ap_op;
} atomic_priv_t;
static void
splat_atomic_work(void *priv)
{
atomic_priv_t *ap;
atomic_op_t op;
int i;
ap = (atomic_priv_t *)priv;
ASSERT(ap->ap_magic == SPLAT_ATOMIC_TEST_MAGIC);
mutex_enter(&ap->ap_lock);
op = ap->ap_op;
wake_up(&ap->ap_waitq);
mutex_exit(&ap->ap_lock);
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
"Thread %d successfully started: %lu/%lu\n", op,
(long unsigned)ap->ap_atomic,
(long unsigned)ap->ap_atomic_exited);
for (i = 0; i < SPLAT_ATOMIC_INIT_VALUE / 10; i++) {
/* Periodically sleep to mix up the ordering */
if ((i % (SPLAT_ATOMIC_INIT_VALUE / 100)) == 0) {
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
"Thread %d sleeping: %lu/%lu\n", op,
(long unsigned)ap->ap_atomic,
(long unsigned)ap->ap_atomic_exited);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 100);
}
switch (op) {
case SPLAT_ATOMIC_INC_64:
atomic_inc_64(&ap->ap_atomic);
break;
case SPLAT_ATOMIC_DEC_64:
atomic_dec_64(&ap->ap_atomic);
break;
case SPLAT_ATOMIC_ADD_64:
atomic_add_64(&ap->ap_atomic, 3);
break;
case SPLAT_ATOMIC_SUB_64:
atomic_sub_64(&ap->ap_atomic, 3);
break;
case SPLAT_ATOMIC_ADD_64_NV:
atomic_add_64_nv(&ap->ap_atomic, 5);
break;
case SPLAT_ATOMIC_SUB_64_NV:
atomic_sub_64_nv(&ap->ap_atomic, 5);
break;
default:
PANIC("Undefined op %d\n", op);
}
}
atomic_inc_64(&ap->ap_atomic_exited);
splat_vprint(ap->ap_file, SPLAT_ATOMIC_TEST1_NAME,
"Thread %d successfully exited: %lu/%lu\n", op,
(long unsigned)ap->ap_atomic,
(long unsigned)ap->ap_atomic_exited);
wake_up(&ap->ap_waitq);
thread_exit();
}
static int
splat_atomic_test1_cond(atomic_priv_t *ap, int started)
{
return (ap->ap_atomic_exited == started);
}
static int
splat_atomic_test1(struct file *file, void *arg)
{
atomic_priv_t ap;
DEFINE_WAIT(wait);
kthread_t *thr;
int i, rc = 0;
ap.ap_magic = SPLAT_ATOMIC_TEST_MAGIC;
ap.ap_file = file;
mutex_init(&ap.ap_lock, SPLAT_ATOMIC_TEST1_NAME, MUTEX_DEFAULT, NULL);
init_waitqueue_head(&ap.ap_waitq);
ap.ap_atomic = SPLAT_ATOMIC_INIT_VALUE;
ap.ap_atomic_exited = 0;
for (i = 0; i < SPLAT_ATOMIC_COUNT_64; i++) {
mutex_enter(&ap.ap_lock);
ap.ap_op = i;
thr = (kthread_t *)thread_create(NULL, 0, splat_atomic_work,
&ap, 0, &p0, TS_RUN,
defclsyspri);
if (thr == NULL) {
rc = -ESRCH;
mutex_exit(&ap.ap_lock);
break;
}
/* Prepare to wait, the new thread will wake us once it
* has made a copy of the unique private passed data */
prepare_to_wait(&ap.ap_waitq, &wait, TASK_UNINTERRUPTIBLE);
mutex_exit(&ap.ap_lock);
schedule();
}
wait_event(ap.ap_waitq, splat_atomic_test1_cond(&ap, i));
if (rc) {
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME, "Only started "
"%d/%d test threads\n", i, SPLAT_ATOMIC_COUNT_64);
return rc;
}
if (ap.ap_atomic != SPLAT_ATOMIC_INIT_VALUE) {
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME,
"Final value %lu does not match initial value %lu\n",
(long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE);
return -EINVAL;
}
splat_vprint(file, SPLAT_ATOMIC_TEST1_NAME,
"Success initial and final values match, %lu == %lu\n",
(long unsigned)ap.ap_atomic, SPLAT_ATOMIC_INIT_VALUE);
mutex_destroy(&ap.ap_lock);
return 0;
}
splat_subsystem_t *
splat_atomic_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_ATOMIC_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_ATOMIC_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_ATOMIC;
splat_test_init(sub, SPLAT_ATOMIC_TEST1_NAME, SPLAT_ATOMIC_TEST1_DESC,
SPLAT_ATOMIC_TEST1_ID, splat_atomic_test1);
return sub;
}
void
splat_atomic_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_ATOMIC_TEST1_ID);
kfree(sub);
}
int
splat_atomic_id(void) {
return SPLAT_SUBSYSTEM_ATOMIC;
}
-511
View File
@@ -1,511 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Condition Variable Tests.
*/
#include <sys/condvar.h>
#include <sys/timer.h>
#include <sys/thread.h>
#include "splat-internal.h"
#define SPLAT_CONDVAR_NAME "condvar"
#define SPLAT_CONDVAR_DESC "Kernel Condition Variable Tests"
#define SPLAT_CONDVAR_TEST1_ID 0x0501
#define SPLAT_CONDVAR_TEST1_NAME "signal1"
#define SPLAT_CONDVAR_TEST1_DESC "Wake a single thread, cv_wait()/cv_signal()"
#define SPLAT_CONDVAR_TEST2_ID 0x0502
#define SPLAT_CONDVAR_TEST2_NAME "broadcast1"
#define SPLAT_CONDVAR_TEST2_DESC "Wake all threads, cv_wait()/cv_broadcast()"
#define SPLAT_CONDVAR_TEST3_ID 0x0503
#define SPLAT_CONDVAR_TEST3_NAME "signal2"
#define SPLAT_CONDVAR_TEST3_DESC "Wake a single thread, cv_wait_timeout()/cv_signal()"
#define SPLAT_CONDVAR_TEST4_ID 0x0504
#define SPLAT_CONDVAR_TEST4_NAME "broadcast2"
#define SPLAT_CONDVAR_TEST4_DESC "Wake all threads, cv_wait_timeout()/cv_broadcast()"
#define SPLAT_CONDVAR_TEST5_ID 0x0505
#define SPLAT_CONDVAR_TEST5_NAME "timeout"
#define SPLAT_CONDVAR_TEST5_DESC "Timeout thread, cv_wait_timeout()"
#define SPLAT_CONDVAR_TEST_MAGIC 0x115599DDUL
#define SPLAT_CONDVAR_TEST_NAME "condvar"
#define SPLAT_CONDVAR_TEST_COUNT 8
typedef struct condvar_priv {
unsigned long cv_magic;
struct file *cv_file;
kcondvar_t cv_condvar;
kmutex_t cv_mtx;
} condvar_priv_t;
typedef struct condvar_thr {
const char *ct_name;
condvar_priv_t *ct_cvp;
struct task_struct *ct_thread;
int ct_rc;
} condvar_thr_t;
int
splat_condvar_test12_thread(void *arg)
{
condvar_thr_t *ct = (condvar_thr_t *)arg;
condvar_priv_t *cv = ct->ct_cvp;
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
mutex_enter(&cv->cv_mtx);
splat_vprint(cv->cv_file, ct->ct_name,
"%s thread sleeping with %d waiters\n",
ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters));
cv_wait(&cv->cv_condvar, &cv->cv_mtx);
splat_vprint(cv->cv_file, ct->ct_name,
"%s thread woken %d waiters remain\n",
ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters));
mutex_exit(&cv->cv_mtx);
/* wait for main thread reap us */
while (!kthread_should_stop())
schedule();
return 0;
}
static int
splat_condvar_test1(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL);
/* Create some threads, the exact number isn't important just as
* long as we know how many we managed to create and should expect. */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_name = SPLAT_CONDVAR_TEST1_NAME;
ct[i].ct_rc = 0;
ct[i].ct_thread = spl_kthread_create(splat_condvar_test12_thread,
&ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i);
if (!IS_ERR(ct[i].ct_thread)) {
wake_up_process(ct[i].ct_thread);
count++;
}
}
/* Wait until all threads are waiting on the condition variable */
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
schedule();
/* Wake a single thread at a time, wait until it exits */
for (i = 1; i <= count; i++) {
cv_signal(&cv.cv_condvar);
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
schedule();
/* Correct behavior 1 thread woken */
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
continue;
splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Attempted to "
"wake %d thread but work %d threads woke\n",
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
rc = -EINVAL;
break;
}
if (!rc)
splat_vprint(file, SPLAT_CONDVAR_TEST1_NAME, "Correctly woke "
"%d sleeping threads %d at a time\n", count, 1);
/* Wait until that last nutex is dropped */
while (mutex_owner(&cv.cv_mtx))
schedule();
/* Wake everything for the failure case */
cv_broadcast(&cv.cv_condvar);
cv_destroy(&cv.cv_condvar);
/* wait for threads to exit */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
if (!IS_ERR(ct[i].ct_thread))
kthread_stop(ct[i].ct_thread);
}
mutex_destroy(&cv.cv_mtx);
return rc;
}
static int
splat_condvar_test2(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL);
/* Create some threads, the exact number isn't important just as
* long as we know how many we managed to create and should expect. */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_name = SPLAT_CONDVAR_TEST2_NAME;
ct[i].ct_rc = 0;
ct[i].ct_thread = spl_kthread_create(splat_condvar_test12_thread,
&ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i);
if (!IS_ERR(ct[i].ct_thread)) {
wake_up_process(ct[i].ct_thread);
count++;
}
}
/* Wait until all threads are waiting on the condition variable */
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
schedule();
/* Wake all threads waiting on the condition variable */
cv_broadcast(&cv.cv_condvar);
/* Wait until all threads have exited */
while ((atomic_read(&cv.cv_condvar.cv_waiters) > 0) || mutex_owner(&cv.cv_mtx))
schedule();
splat_vprint(file, SPLAT_CONDVAR_TEST2_NAME, "Correctly woke all "
"%d sleeping threads at once\n", count);
/* Wake everything for the failure case */
cv_destroy(&cv.cv_condvar);
/* wait for threads to exit */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
if (!IS_ERR(ct[i].ct_thread))
kthread_stop(ct[i].ct_thread);
}
mutex_destroy(&cv.cv_mtx);
return rc;
}
int
splat_condvar_test34_thread(void *arg)
{
condvar_thr_t *ct = (condvar_thr_t *)arg;
condvar_priv_t *cv = ct->ct_cvp;
clock_t rc;
ASSERT(cv->cv_magic == SPLAT_CONDVAR_TEST_MAGIC);
mutex_enter(&cv->cv_mtx);
splat_vprint(cv->cv_file, ct->ct_name,
"%s thread sleeping with %d waiters\n",
ct->ct_thread->comm, atomic_read(&cv->cv_condvar.cv_waiters));
/* Sleep no longer than 3 seconds, for this test we should
* actually never sleep that long without being woken up. */
rc = cv_timedwait(&cv->cv_condvar, &cv->cv_mtx, lbolt + HZ * 3);
if (rc == -1) {
ct->ct_rc = -ETIMEDOUT;
splat_vprint(cv->cv_file, ct->ct_name, "%s thread timed out, "
"should have been woken\n", ct->ct_thread->comm);
} else {
splat_vprint(cv->cv_file, ct->ct_name,
"%s thread woken %d waiters remain\n",
ct->ct_thread->comm,
atomic_read(&cv->cv_condvar.cv_waiters));
}
mutex_exit(&cv->cv_mtx);
/* wait for main thread reap us */
while (!kthread_should_stop())
schedule();
return 0;
}
static int
splat_condvar_test3(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL);
/* Create some threads, the exact number isn't important just as
* long as we know how many we managed to create and should expect. */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
ct[i].ct_rc = 0;
ct[i].ct_thread = spl_kthread_create(splat_condvar_test34_thread,
&ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i);
if (!IS_ERR(ct[i].ct_thread)) {
wake_up_process(ct[i].ct_thread);
count++;
}
}
/* Wait until all threads are waiting on the condition variable */
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
schedule();
/* Wake a single thread at a time, wait until it exits */
for (i = 1; i <= count; i++) {
cv_signal(&cv.cv_condvar);
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
schedule();
/* Correct behavior 1 thread woken */
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
continue;
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to "
"wake %d thread but work %d threads woke\n",
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
rc = -EINVAL;
break;
}
/* Validate no waiting thread timed out early */
for (i = 0; i < count; i++)
if (ct[i].ct_rc)
rc = ct[i].ct_rc;
if (!rc)
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke "
"%d sleeping threads %d at a time\n", count, 1);
/* Wait until that last nutex is dropped */
while (mutex_owner(&cv.cv_mtx))
schedule();
/* Wake everything for the failure case */
cv_broadcast(&cv.cv_condvar);
cv_destroy(&cv.cv_condvar);
/* wait for threads to exit */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
if (!IS_ERR(ct[i].ct_thread))
kthread_stop(ct[i].ct_thread);
}
mutex_destroy(&cv.cv_mtx);
return rc;
}
static int
splat_condvar_test4(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
condvar_thr_t ct[SPLAT_CONDVAR_TEST_COUNT];
condvar_priv_t cv;
cv.cv_magic = SPLAT_CONDVAR_TEST_MAGIC;
cv.cv_file = file;
mutex_init(&cv.cv_mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&cv.cv_condvar, NULL, CV_DEFAULT, NULL);
/* Create some threads, the exact number isn't important just as
* long as we know how many we managed to create and should expect. */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
ct[i].ct_cvp = &cv;
ct[i].ct_name = SPLAT_CONDVAR_TEST3_NAME;
ct[i].ct_rc = 0;
ct[i].ct_thread = spl_kthread_create(splat_condvar_test34_thread,
&ct[i], "%s/%d", SPLAT_CONDVAR_TEST_NAME, i);
if (!IS_ERR(ct[i].ct_thread)) {
wake_up_process(ct[i].ct_thread);
count++;
}
}
/* Wait until all threads are waiting on the condition variable */
while (atomic_read(&cv.cv_condvar.cv_waiters) != count)
schedule();
/* Wake a single thread at a time, wait until it exits */
for (i = 1; i <= count; i++) {
cv_signal(&cv.cv_condvar);
while (atomic_read(&cv.cv_condvar.cv_waiters) > (count - i))
schedule();
/* Correct behavior 1 thread woken */
if (atomic_read(&cv.cv_condvar.cv_waiters) == (count - i))
continue;
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Attempted to "
"wake %d thread but work %d threads woke\n",
1, count - atomic_read(&cv.cv_condvar.cv_waiters));
rc = -EINVAL;
break;
}
/* Validate no waiting thread timed out early */
for (i = 0; i < count; i++)
if (ct[i].ct_rc)
rc = ct[i].ct_rc;
if (!rc)
splat_vprint(file, SPLAT_CONDVAR_TEST3_NAME, "Correctly woke "
"%d sleeping threads %d at a time\n", count, 1);
/* Wait until that last nutex is dropped */
while (mutex_owner(&cv.cv_mtx))
schedule();
/* Wake everything for the failure case */
cv_broadcast(&cv.cv_condvar);
cv_destroy(&cv.cv_condvar);
/* wait for threads to exit */
for (i = 0; i < SPLAT_CONDVAR_TEST_COUNT; i++) {
if (!IS_ERR(ct[i].ct_thread))
kthread_stop(ct[i].ct_thread);
}
mutex_destroy(&cv.cv_mtx);
return rc;
}
static int
splat_condvar_test5(struct file *file, void *arg)
{
kcondvar_t condvar;
kmutex_t mtx;
clock_t time_left, time_before, time_after, time_delta;
uint64_t whole_delta;
uint32_t remain_delta;
int rc = 0;
mutex_init(&mtx, SPLAT_CONDVAR_TEST_NAME, MUTEX_DEFAULT, NULL);
cv_init(&condvar, NULL, CV_DEFAULT, NULL);
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME, "Thread going to sleep for "
"%d second and expecting to be woken by timeout\n", 1);
/* Allow a 1 second timeout, plenty long to validate correctness. */
time_before = lbolt;
mutex_enter(&mtx);
time_left = cv_timedwait(&condvar, &mtx, lbolt + HZ);
mutex_exit(&mtx);
time_after = lbolt;
time_delta = time_after - time_before; /* XXX - Handle jiffie wrap */
whole_delta = time_delta;
remain_delta = do_div(whole_delta, HZ);
if (time_left == -1) {
if (time_delta >= HZ) {
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
"Thread correctly timed out and was asleep "
"for %d.%d seconds (%d second min)\n",
(int)whole_delta, (int)remain_delta, 1);
} else {
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
"Thread correctly timed out but was only "
"asleep for %d.%d seconds (%d second "
"min)\n", (int)whole_delta,
(int)remain_delta, 1);
rc = -ETIMEDOUT;
}
} else {
splat_vprint(file, SPLAT_CONDVAR_TEST5_NAME,
"Thread exited after only %d.%d seconds, it "
"did not hit the %d second timeout\n",
(int)whole_delta, (int)remain_delta, 1);
rc = -ETIMEDOUT;
}
cv_destroy(&condvar);
mutex_destroy(&mtx);
return rc;
}
splat_subsystem_t *
splat_condvar_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_CONDVAR_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_CONDVAR_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_CONDVAR;
splat_test_init(sub, SPLAT_CONDVAR_TEST1_NAME, SPLAT_CONDVAR_TEST1_DESC,
SPLAT_CONDVAR_TEST1_ID, splat_condvar_test1);
splat_test_init(sub, SPLAT_CONDVAR_TEST2_NAME, SPLAT_CONDVAR_TEST2_DESC,
SPLAT_CONDVAR_TEST2_ID, splat_condvar_test2);
splat_test_init(sub, SPLAT_CONDVAR_TEST3_NAME, SPLAT_CONDVAR_TEST3_DESC,
SPLAT_CONDVAR_TEST3_ID, splat_condvar_test3);
splat_test_init(sub, SPLAT_CONDVAR_TEST4_NAME, SPLAT_CONDVAR_TEST4_DESC,
SPLAT_CONDVAR_TEST4_ID, splat_condvar_test4);
splat_test_init(sub, SPLAT_CONDVAR_TEST5_NAME, SPLAT_CONDVAR_TEST5_DESC,
SPLAT_CONDVAR_TEST5_ID, splat_condvar_test5);
return sub;
}
void
splat_condvar_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_CONDVAR_TEST5_ID);
splat_test_fini(sub, SPLAT_CONDVAR_TEST4_ID);
splat_test_fini(sub, SPLAT_CONDVAR_TEST3_ID);
splat_test_fini(sub, SPLAT_CONDVAR_TEST2_ID);
splat_test_fini(sub, SPLAT_CONDVAR_TEST1_ID);
kfree(sub);
}
int
splat_condvar_id(void) {
return SPLAT_SUBSYSTEM_CONDVAR;
}
-299
View File
@@ -1,299 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Credential Tests.
*/
#include <sys/cred.h>
#include <sys/random.h>
#include "splat-internal.h"
#define SPLAT_CRED_NAME "cred"
#define SPLAT_CRED_DESC "Kernel Cred Tests"
#define SPLAT_CRED_TEST1_ID 0x0e01
#define SPLAT_CRED_TEST1_NAME "cred"
#define SPLAT_CRED_TEST1_DESC "Task Credential Test"
#define SPLAT_CRED_TEST2_ID 0x0e02
#define SPLAT_CRED_TEST2_NAME "kcred"
#define SPLAT_CRED_TEST2_DESC "Kernel Credential Test"
#define SPLAT_CRED_TEST3_ID 0x0e03
#define SPLAT_CRED_TEST3_NAME "groupmember"
#define SPLAT_CRED_TEST3_DESC "Group Member Test"
#define GROUP_STR_SIZE 128
#define GROUP_STR_REDZONE 16
static int
splat_cred_test1(struct file *file, void *arg)
{
char str[GROUP_STR_SIZE];
uid_t uid, ruid, suid;
gid_t gid, rgid, sgid, *groups;
int ngroups, i, count = 0;
cred_t *cr = CRED();
uid = crgetuid(cr);
ruid = crgetruid(cr);
suid = crgetsuid(cr);
gid = crgetgid(cr);
rgid = crgetrgid(cr);
sgid = crgetsgid(cr);
ngroups = crgetngroups(cr);
groups = crgetgroups(cr);
memset(str, 0, GROUP_STR_SIZE);
for (i = 0; i < ngroups; i++) {
count += sprintf(str + count, "%d ", groups[i]);
if (count > (GROUP_STR_SIZE - GROUP_STR_REDZONE)) {
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"Failed too many group entries for temp "
"buffer: %d, %s\n", ngroups, str);
return -ENOSPC;
}
}
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"uid: %d ruid: %d suid: %d "
"gid: %d rgid: %d sgid: %d\n",
uid, ruid, suid, gid, rgid, sgid);
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"ngroups: %d groups: %s\n", ngroups, str);
if (uid || ruid || suid || gid || rgid || sgid) {
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"Failed expected all uids+gids to be %d\n", 0);
return -EIDRM;
}
if (ngroups > NGROUPS_MAX) {
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"Failed ngroups must not exceed NGROUPS_MAX: "
"%d > %d\n", ngroups, NGROUPS_MAX);
return -EIDRM;
}
splat_vprint(file, SPLAT_CRED_TEST1_NAME,
"Success sane CRED(): %d\n", 0);
return 0;
} /* splat_cred_test1() */
static int
splat_cred_test2(struct file *file, void *arg)
{
char str[GROUP_STR_SIZE];
uid_t uid, ruid, suid;
gid_t gid, rgid, sgid, *groups;
int ngroups, i, count = 0;
crhold(kcred);
uid = crgetuid(kcred);
ruid = crgetruid(kcred);
suid = crgetsuid(kcred);
gid = crgetgid(kcred);
rgid = crgetrgid(kcred);
sgid = crgetsgid(kcred);
ngroups = crgetngroups(kcred);
groups = crgetgroups(kcred);
memset(str, 0, GROUP_STR_SIZE);
for (i = 0; i < ngroups; i++) {
count += sprintf(str + count, "%d ", groups[i]);
if (count > (GROUP_STR_SIZE - GROUP_STR_REDZONE)) {
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"Failed too many group entries for temp "
"buffer: %d, %s\n", ngroups, str);
crfree(kcred);
return -ENOSPC;
}
}
crfree(kcred);
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"uid: %d ruid: %d suid: %d "
"gid: %d rgid: %d sgid: %d\n",
uid, ruid, suid, gid, rgid, sgid);
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"ngroups: %d groups: %s\n", ngroups, str);
if (uid || ruid || suid || gid || rgid || sgid) {
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"Failed expected all uids+gids to be %d\n", 0);
return -EIDRM;
}
if (ngroups > NGROUPS_MAX) {
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"Failed ngroups must not exceed NGROUPS_MAX: "
"%d > %d\n", ngroups, NGROUPS_MAX);
return -EIDRM;
}
splat_vprint(file, SPLAT_CRED_TEST2_NAME,
"Success sane kcred: %d\n", 0);
return 0;
} /* splat_cred_test2() */
#define SPLAT_NGROUPS 32
/*
* Verify the groupmember() works correctly by constructing an interesting
* CRED() and checking that the expected gids are part of it.
*/
static int
splat_cred_test3(struct file *file, void *arg)
{
gid_t known_gid, missing_gid, tmp_gid;
unsigned char rnd;
struct group_info *gi;
int i, rc;
get_random_bytes((void *)&rnd, 1);
known_gid = (rnd > 0) ? rnd : 1;
missing_gid = 0;
/*
* Create an interesting known set of gids for test purposes. The
* gids are pseudo randomly selected are will be in the range of
* 1:(NGROUPS_MAX-1). Gid 0 is explicitly avoided so we can reliably
* test for its absence in the test cases.
*/
gi = groups_alloc(SPLAT_NGROUPS);
if (gi == NULL) {
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed create "
"group_info for known gids: %d\n", -ENOMEM);
rc = -ENOMEM;
goto show_groups;
}
for (i = 0, tmp_gid = known_gid; i < SPLAT_NGROUPS; i++) {
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Adding gid %d "
"to current CRED() (%d/%d)\n", tmp_gid, i, gi->ngroups);
#ifdef HAVE_KUIDGID_T
GROUP_AT(gi, i) = make_kgid(current_user_ns(), tmp_gid);
#else
GROUP_AT(gi, i) = tmp_gid;
#endif /* HAVE_KUIDGID_T */
tmp_gid = ((tmp_gid * 17) % (NGROUPS_MAX - 1)) + 1;
}
/* Set the new groups in the CRED() and release our reference. */
rc = set_current_groups(gi);
put_group_info(gi);
if (rc) {
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed to add "
"gid %d to current group: %d\n", known_gid, rc);
goto show_groups;
}
/* Verify groupmember() finds the known_gid in the CRED() */
rc = groupmember(known_gid, CRED());
if (!rc) {
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed to find "
"known gid %d in CRED()'s groups.\n", known_gid);
rc = -EIDRM;
goto show_groups;
}
/* Verify groupmember() does NOT finds the missing gid in the CRED() */
rc = groupmember(missing_gid, CRED());
if (rc) {
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Failed missing "
"gid %d was found in CRED()'s groups.\n", missing_gid);
rc = -EIDRM;
goto show_groups;
}
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "Success groupmember() "
"correctly detects expected gids in CRED(): %d\n", rc);
show_groups:
if (rc) {
int i, grps = crgetngroups(CRED());
splat_vprint(file, SPLAT_CRED_TEST3_NAME, "%d groups: ", grps);
for (i = 0; i < grps; i++)
splat_print(file, "%d ", crgetgroups(CRED())[i]);
splat_print(file, "%s", "\n");
}
return (rc);
} /* splat_cred_test3() */
splat_subsystem_t *
splat_cred_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_CRED_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_CRED_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_CRED;
splat_test_init(sub, SPLAT_CRED_TEST1_NAME, SPLAT_CRED_TEST1_DESC,
SPLAT_CRED_TEST1_ID, splat_cred_test1);
splat_test_init(sub, SPLAT_CRED_TEST2_NAME, SPLAT_CRED_TEST2_DESC,
SPLAT_CRED_TEST2_ID, splat_cred_test2);
splat_test_init(sub, SPLAT_CRED_TEST3_NAME, SPLAT_CRED_TEST3_DESC,
SPLAT_CRED_TEST3_ID, splat_cred_test3);
return sub;
} /* splat_cred_init() */
void
splat_cred_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_CRED_TEST3_ID);
splat_test_fini(sub, SPLAT_CRED_TEST2_ID);
splat_test_fini(sub, SPLAT_CRED_TEST1_ID);
kfree(sub);
} /* splat_cred_fini() */
int
splat_cred_id(void)
{
return SPLAT_SUBSYSTEM_CRED;
} /* splat_cred_id() */
-753
View File
@@ -1,753 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Test Control Interface.
*
* The 'splat' (Solaris Porting LAyer Tests) module is designed as a
* framework which runs various in kernel regression tests to validate
* the SPL primitives honor the Solaris ABI.
*
* The splat module is constructed of various splat_* source files each
* of which contain regression tests for a particular subsystem. For
* example, the splat_kmem.c file contains all the tests for validating
* the kmem interfaces have been implemented correctly. When the splat
* module is loaded splat_*_init() will be called for each subsystems
* tests. It is the responsibility of splat_*_init() to register all
* the tests for this subsystem using the splat_test_init().
* Similarly splat_*_fini() is called when the splat module is removed
* and is responsible for unregistering its tests via the splat_test_fini.
* Once a test is registered it can then be run with an ioctl()
* call which specifies the subsystem and test to be run. The provided
* splat command line tool can be used to display all available
* subsystems and tests. It can also be used to run the full suite
* of regression tests or particular tests.
*/
#include <sys/debug.h>
#include <sys/mutex.h>
#include <sys/types.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "splat-internal.h"
static struct list_head splat_module_list;
static spinlock_t splat_module_lock;
static int
splat_open(struct inode *inode, struct file *file)
{
splat_info_t *info;
info = (splat_info_t *)kmalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
mutex_init(&info->info_lock, SPLAT_NAME, MUTEX_DEFAULT, NULL);
info->info_size = SPLAT_INFO_BUFFER_SIZE;
info->info_buffer = (char *)vmalloc(SPLAT_INFO_BUFFER_SIZE);
if (info->info_buffer == NULL) {
kfree(info);
return -ENOMEM;
}
memset(info->info_buffer, 0, info->info_size);
info->info_head = info->info_buffer;
file->private_data = (void *)info;
splat_print(file, "%s\n", spl_version);
return 0;
}
static int
splat_release(struct inode *inode, struct file *file)
{
splat_info_t *info = (splat_info_t *)file->private_data;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_destroy(&info->info_lock);
vfree(info->info_buffer);
kfree(info);
return 0;
}
static int
splat_buffer_clear(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
{
splat_info_t *info = (splat_info_t *)file->private_data;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_enter(&info->info_lock);
memset(info->info_buffer, 0, info->info_size);
info->info_head = info->info_buffer;
mutex_exit(&info->info_lock);
return 0;
}
static int
splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
{
splat_info_t *info = (splat_info_t *)file->private_data;
char *buf;
int min, size, rc = 0;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_enter(&info->info_lock);
if (kcfg->cfg_arg1 > 0) {
size = kcfg->cfg_arg1;
buf = (char *)vmalloc(size);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
/* Zero fill and truncate contents when coping buffer */
min = ((size < info->info_size) ? size : info->info_size);
memset(buf, 0, size);
memcpy(buf, info->info_buffer, min);
vfree(info->info_buffer);
info->info_size = size;
info->info_buffer = buf;
info->info_head = info->info_buffer;
}
kcfg->cfg_rc1 = info->info_size;
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
rc = -EFAULT;
out:
mutex_exit(&info->info_lock);
return rc;
}
static splat_subsystem_t *
splat_subsystem_find(int id) {
splat_subsystem_t *sub;
spin_lock(&splat_module_lock);
list_for_each_entry(sub, &splat_module_list, subsystem_list) {
if (id == sub->desc.id) {
spin_unlock(&splat_module_lock);
return sub;
}
}
spin_unlock(&splat_module_lock);
return NULL;
}
static int
splat_subsystem_count(splat_cfg_t *kcfg, unsigned long arg)
{
splat_subsystem_t *sub;
int i = 0;
spin_lock(&splat_module_lock);
list_for_each_entry(sub, &splat_module_list, subsystem_list)
i++;
spin_unlock(&splat_module_lock);
kcfg->cfg_rc1 = i;
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
return -EFAULT;
return 0;
}
static int
splat_subsystem_list(splat_cfg_t *kcfg, unsigned long arg)
{
splat_subsystem_t *sub;
splat_cfg_t *tmp;
int size, i = 0;
/* Structure will be sized large enough for N subsystem entries
* which is passed in by the caller. On exit the number of
* entries filled in with valid subsystems will be stored in
* cfg_rc1. If the caller does not provide enough entries
* for all subsystems we will truncate the list to avoid overrun.
*/
size = sizeof(*tmp) + kcfg->cfg_data.splat_subsystems.size *
sizeof(splat_user_t);
tmp = kmalloc(size, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
/* Local 'tmp' is used as the structure copied back to user space */
memset(tmp, 0, size);
memcpy(tmp, kcfg, sizeof(*kcfg));
spin_lock(&splat_module_lock);
list_for_each_entry(sub, &splat_module_list, subsystem_list) {
strncpy(tmp->cfg_data.splat_subsystems.descs[i].name,
sub->desc.name, SPLAT_NAME_SIZE);
strncpy(tmp->cfg_data.splat_subsystems.descs[i].desc,
sub->desc.desc, SPLAT_DESC_SIZE);
tmp->cfg_data.splat_subsystems.descs[i].id = sub->desc.id;
/* Truncate list if we are about to overrun alloc'ed memory */
if ((i++) == kcfg->cfg_data.splat_subsystems.size)
break;
}
spin_unlock(&splat_module_lock);
tmp->cfg_rc1 = i;
if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) {
kfree(tmp);
return -EFAULT;
}
kfree(tmp);
return 0;
}
static int
splat_test_count(splat_cfg_t *kcfg, unsigned long arg)
{
splat_subsystem_t *sub;
splat_test_t *test;
int i = 0;
/* Subsystem ID passed as arg1 */
sub = splat_subsystem_find(kcfg->cfg_arg1);
if (sub == NULL)
return -EINVAL;
spin_lock(&(sub->test_lock));
list_for_each_entry(test, &(sub->test_list), test_list)
i++;
spin_unlock(&(sub->test_lock));
kcfg->cfg_rc1 = i;
if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
return -EFAULT;
return 0;
}
static int
splat_test_list(splat_cfg_t *kcfg, unsigned long arg)
{
splat_subsystem_t *sub;
splat_test_t *test;
splat_cfg_t *tmp;
int size, i = 0;
/* Subsystem ID passed as arg1 */
sub = splat_subsystem_find(kcfg->cfg_arg1);
if (sub == NULL)
return -EINVAL;
/* Structure will be sized large enough for N test entries
* which is passed in by the caller. On exit the number of
* entries filled in with valid tests will be stored in
* cfg_rc1. If the caller does not provide enough entries
* for all tests we will truncate the list to avoid overrun.
*/
size = sizeof(*tmp)+kcfg->cfg_data.splat_tests.size*sizeof(splat_user_t);
tmp = kmalloc(size, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
/* Local 'tmp' is used as the structure copied back to user space */
memset(tmp, 0, size);
memcpy(tmp, kcfg, sizeof(*kcfg));
spin_lock(&(sub->test_lock));
list_for_each_entry(test, &(sub->test_list), test_list) {
strncpy(tmp->cfg_data.splat_tests.descs[i].name,
test->desc.name, SPLAT_NAME_SIZE);
strncpy(tmp->cfg_data.splat_tests.descs[i].desc,
test->desc.desc, SPLAT_DESC_SIZE);
tmp->cfg_data.splat_tests.descs[i].id = test->desc.id;
/* Truncate list if we are about to overrun alloc'ed memory */
if ((i++) == kcfg->cfg_data.splat_tests.size)
break;
}
spin_unlock(&(sub->test_lock));
tmp->cfg_rc1 = i;
if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) {
kfree(tmp);
return -EFAULT;
}
kfree(tmp);
return 0;
}
static int
splat_validate(struct file *file, splat_subsystem_t *sub, int cmd, void *arg)
{
splat_test_t *test;
spin_lock(&(sub->test_lock));
list_for_each_entry(test, &(sub->test_list), test_list) {
if (test->desc.id == cmd) {
spin_unlock(&(sub->test_lock));
return test->test(file, arg);
}
}
spin_unlock(&(sub->test_lock));
return -EINVAL;
}
static int
splat_ioctl_cfg(struct file *file, unsigned int cmd, unsigned long arg)
{
splat_cfg_t kcfg;
int rc = 0;
/* User and kernel space agree about arg size */
if (_IOC_SIZE(cmd) != sizeof(kcfg))
return -EBADMSG;
if (copy_from_user(&kcfg, (splat_cfg_t *)arg, sizeof(kcfg)))
return -EFAULT;
if (kcfg.cfg_magic != SPLAT_CFG_MAGIC) {
splat_print(file, "Bad config magic 0x%x != 0x%x\n",
kcfg.cfg_magic, SPLAT_CFG_MAGIC);
return -EINVAL;
}
switch (kcfg.cfg_cmd) {
case SPLAT_CFG_BUFFER_CLEAR:
/* cfg_arg1 - Unused
* cfg_rc1 - Unused
*/
rc = splat_buffer_clear(file, &kcfg, arg);
break;
case SPLAT_CFG_BUFFER_SIZE:
/* cfg_arg1 - 0 - query size; >0 resize
* cfg_rc1 - Set to current buffer size
*/
rc = splat_buffer_size(file, &kcfg, arg);
break;
case SPLAT_CFG_SUBSYSTEM_COUNT:
/* cfg_arg1 - Unused
* cfg_rc1 - Set to number of subsystems
*/
rc = splat_subsystem_count(&kcfg, arg);
break;
case SPLAT_CFG_SUBSYSTEM_LIST:
/* cfg_arg1 - Unused
* cfg_rc1 - Set to number of subsystems
* cfg_data.splat_subsystems - Set with subsystems
*/
rc = splat_subsystem_list(&kcfg, arg);
break;
case SPLAT_CFG_TEST_COUNT:
/* cfg_arg1 - Set to a target subsystem
* cfg_rc1 - Set to number of tests
*/
rc = splat_test_count(&kcfg, arg);
break;
case SPLAT_CFG_TEST_LIST:
/* cfg_arg1 - Set to a target subsystem
* cfg_rc1 - Set to number of tests
* cfg_data.splat_subsystems - Populated with tests
*/
rc = splat_test_list(&kcfg, arg);
break;
default:
splat_print(file, "Bad config command %d\n",
kcfg.cfg_cmd);
rc = -EINVAL;
break;
}
return rc;
}
static int
splat_ioctl_cmd(struct file *file, unsigned int cmd, unsigned long arg)
{
splat_subsystem_t *sub;
splat_cmd_t kcmd;
int rc = -EINVAL;
void *data = NULL;
/* User and kernel space agree about arg size */
if (_IOC_SIZE(cmd) != sizeof(kcmd))
return -EBADMSG;
if (copy_from_user(&kcmd, (splat_cfg_t *)arg, sizeof(kcmd)))
return -EFAULT;
if (kcmd.cmd_magic != SPLAT_CMD_MAGIC) {
splat_print(file, "Bad command magic 0x%x != 0x%x\n",
kcmd.cmd_magic, SPLAT_CFG_MAGIC);
return -EINVAL;
}
/* Allocate memory for any opaque data the caller needed to pass on */
if (kcmd.cmd_data_size > 0) {
data = (void *)kmalloc(kcmd.cmd_data_size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
if (copy_from_user(data, (void *)(arg + offsetof(splat_cmd_t,
cmd_data_str)), kcmd.cmd_data_size)) {
kfree(data);
return -EFAULT;
}
}
sub = splat_subsystem_find(kcmd.cmd_subsystem);
if (sub != NULL)
rc = splat_validate(file, sub, kcmd.cmd_test, data);
else
rc = -EINVAL;
if (data != NULL)
kfree(data);
return rc;
}
static long
splat_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc = 0;
/* Ignore tty ioctls */
if ((cmd & 0xffffff00) == ((int)'T') << 8)
return -ENOTTY;
switch (cmd) {
case SPLAT_CFG:
rc = splat_ioctl_cfg(file, cmd, arg);
break;
case SPLAT_CMD:
rc = splat_ioctl_cmd(file, cmd, arg);
break;
default:
splat_print(file, "Bad ioctl command %d\n", cmd);
rc = -EINVAL;
break;
}
return rc;
}
#ifdef CONFIG_COMPAT
/* Compatibility handler for ioctls from 32-bit ELF binaries */
static long
splat_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return splat_unlocked_ioctl(file, cmd, arg);
}
#endif /* CONFIG_COMPAT */
/* I'm not sure why you would want to write in to this buffer from
* user space since its principle use is to pass test status info
* back to the user space, but I don't see any reason to prevent it.
*/
static ssize_t splat_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
splat_info_t *info = (splat_info_t *)file->private_data;
int rc = 0;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_enter(&info->info_lock);
/* Write beyond EOF */
if (*ppos >= info->info_size) {
rc = -EFBIG;
goto out;
}
/* Resize count if beyond EOF */
if (*ppos + count > info->info_size)
count = info->info_size - *ppos;
if (copy_from_user(info->info_buffer, buf, count)) {
rc = -EFAULT;
goto out;
}
*ppos += count;
rc = count;
out:
mutex_exit(&info->info_lock);
return rc;
}
static ssize_t splat_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
splat_info_t *info = (splat_info_t *)file->private_data;
int rc = 0;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_enter(&info->info_lock);
/* Read beyond EOF */
if (*ppos >= info->info_size)
goto out;
/* Resize count if beyond EOF */
if (*ppos + count > info->info_size)
count = info->info_size - *ppos;
if (copy_to_user(buf, info->info_buffer + *ppos, count)) {
rc = -EFAULT;
goto out;
}
*ppos += count;
rc = count;
out:
mutex_exit(&info->info_lock);
return rc;
}
static loff_t splat_seek(struct file *file, loff_t offset, int origin)
{
splat_info_t *info = (splat_info_t *)file->private_data;
int rc = -EINVAL;
ASSERT(info);
ASSERT(info->info_buffer);
mutex_enter(&info->info_lock);
switch (origin) {
case 0: /* SEEK_SET - No-op just do it */
break;
case 1: /* SEEK_CUR - Seek from current */
offset = file->f_pos + offset;
break;
case 2: /* SEEK_END - Seek from end */
offset = info->info_size + offset;
break;
}
if (offset >= 0) {
file->f_pos = offset;
file->f_version = 0;
rc = offset;
}
mutex_exit(&info->info_lock);
return rc;
}
static struct file_operations splat_fops = {
.owner = THIS_MODULE,
.open = splat_open,
.release = splat_release,
.unlocked_ioctl = splat_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = splat_compat_ioctl,
#endif
.read = splat_read,
.write = splat_write,
.llseek = splat_seek,
};
static struct miscdevice splat_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = SPLAT_NAME,
.fops = &splat_fops,
};
static void splat_subsystem_init(const char *name,
splat_subsystem_t *(*init)(void))
{
splat_subsystem_t *sub;
sub = init();
if (sub == NULL) {
printk(KERN_ERR "splat: Error initializing: %s\n", name);
return;
}
spin_lock(&splat_module_lock);
list_add_tail(&sub->subsystem_list, &splat_module_list);
spin_unlock(&splat_module_lock);
}
static void splat_subsystem_fini(const char *name,
int (*id_func)(void), void (*fini)(splat_subsystem_t *))
{
splat_subsystem_t *sub, *tmp;
int id, flag = 0;
id = id_func();
spin_lock(&splat_module_lock);
list_for_each_entry_safe(sub, tmp, &splat_module_list, subsystem_list) {
if (sub->desc.id == id) {
list_del_init(&sub->subsystem_list);
flag = 1;
break;
}
}
spin_unlock(&splat_module_lock);
if (flag == 0)
printk(KERN_ERR "splat: Error finalizing: %s\n", name);
else
fini(sub);
}
#define SPLAT_SUBSYSTEM_INIT(type) \
splat_subsystem_init(#type, splat_##type##_init)
#define SPLAT_SUBSYSTEM_FINI(type) \
splat_subsystem_fini(#type, splat_##type##_id, splat_##type##_fini)
void splat_test_init(splat_subsystem_t *sub, const char *name,
const char *desc, unsigned int tid, splat_test_func_t func)
{
splat_test_t *test;
test = kmalloc(sizeof (splat_test_t), GFP_KERNEL);
if (test == NULL) {
printk(KERN_ERR "splat: Error initializing: %s/%u\n",
name, tid);
return;
}
memset(test, 0, sizeof (splat_test_t));
strncpy(test->desc.name, name, SPLAT_NAME_SIZE-1);
strncpy(test->desc.desc, desc, SPLAT_DESC_SIZE-1);
test->desc.id = tid;
test->test = func;
INIT_LIST_HEAD(&test->test_list);
spin_lock(&sub->test_lock);
list_add_tail(&test->test_list, &sub->test_list);
spin_unlock(&sub->test_lock);
}
void splat_test_fini(splat_subsystem_t *sub, unsigned int tid)
{
splat_test_t *test, *tmp;
int flag = 0;
spin_lock(&sub->test_lock);
list_for_each_entry_safe(test, tmp, &sub->test_list, test_list) {
if (test->desc.id == tid) {
list_del_init(&test->test_list);
kfree(test);
flag = 1;
break;
}
}
spin_unlock(&sub->test_lock);
if (flag == 0)
printk(KERN_ERR "splat: Error finalizing: %u\n", tid);
}
static int __init
splat_init(void)
{
int error;
spin_lock_init(&splat_module_lock);
INIT_LIST_HEAD(&splat_module_list);
SPLAT_SUBSYSTEM_INIT(kmem);
SPLAT_SUBSYSTEM_INIT(taskq);
SPLAT_SUBSYSTEM_INIT(krng);
SPLAT_SUBSYSTEM_INIT(mutex);
SPLAT_SUBSYSTEM_INIT(condvar);
SPLAT_SUBSYSTEM_INIT(thread);
SPLAT_SUBSYSTEM_INIT(rwlock);
SPLAT_SUBSYSTEM_INIT(time);
SPLAT_SUBSYSTEM_INIT(vnode);
SPLAT_SUBSYSTEM_INIT(kobj);
SPLAT_SUBSYSTEM_INIT(atomic);
SPLAT_SUBSYSTEM_INIT(list);
SPLAT_SUBSYSTEM_INIT(generic);
SPLAT_SUBSYSTEM_INIT(cred);
SPLAT_SUBSYSTEM_INIT(zlib);
SPLAT_SUBSYSTEM_INIT(linux);
error = misc_register(&splat_misc);
if (error) {
printk(KERN_INFO "SPLAT: misc_register() failed %d\n", error);
} else {
printk(KERN_INFO "SPLAT: Loaded module v%s-%s%s\n",
SPL_META_VERSION, SPL_META_RELEASE, SPL_DEBUG_STR);
}
return (error);
}
static void __exit
splat_fini(void)
{
misc_deregister(&splat_misc);
SPLAT_SUBSYSTEM_FINI(linux);
SPLAT_SUBSYSTEM_FINI(zlib);
SPLAT_SUBSYSTEM_FINI(cred);
SPLAT_SUBSYSTEM_FINI(generic);
SPLAT_SUBSYSTEM_FINI(list);
SPLAT_SUBSYSTEM_FINI(atomic);
SPLAT_SUBSYSTEM_FINI(kobj);
SPLAT_SUBSYSTEM_FINI(vnode);
SPLAT_SUBSYSTEM_FINI(time);
SPLAT_SUBSYSTEM_FINI(rwlock);
SPLAT_SUBSYSTEM_FINI(thread);
SPLAT_SUBSYSTEM_FINI(condvar);
SPLAT_SUBSYSTEM_FINI(mutex);
SPLAT_SUBSYSTEM_FINI(krng);
SPLAT_SUBSYSTEM_FINI(taskq);
SPLAT_SUBSYSTEM_FINI(kmem);
ASSERT(list_empty(&splat_module_list));
printk(KERN_INFO "SPLAT: Unloaded module v%s-%s%s\n",
SPL_META_VERSION, SPL_META_RELEASE, SPL_DEBUG_STR);
}
module_init(splat_init);
module_exit(splat_fini);
MODULE_DESCRIPTION("Solaris Porting LAyer Tests");
MODULE_AUTHOR(SPL_META_AUTHOR);
MODULE_LICENSE(SPL_META_LICENSE);
MODULE_VERSION(SPL_META_VERSION "-" SPL_META_RELEASE);
-367
View File
@@ -1,367 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Generic Tests.
*/
#include <sys/sunddi.h>
#include <linux/math64_compat.h>
#include "splat-internal.h"
#define SPLAT_GENERIC_NAME "generic"
#define SPLAT_GENERIC_DESC "Kernel Generic Tests"
#define SPLAT_GENERIC_TEST1_ID 0x0d01
#define SPLAT_GENERIC_TEST1_NAME "ddi_strtoul"
#define SPLAT_GENERIC_TEST1_DESC "ddi_strtoul Test"
#define SPLAT_GENERIC_TEST2_ID 0x0d02
#define SPLAT_GENERIC_TEST2_NAME "ddi_strtol"
#define SPLAT_GENERIC_TEST2_DESC "ddi_strtol Test"
#define SPLAT_GENERIC_TEST3_ID 0x0d03
#define SPLAT_GENERIC_TEST3_NAME "ddi_strtoull"
#define SPLAT_GENERIC_TEST3_DESC "ddi_strtoull Test"
#define SPLAT_GENERIC_TEST4_ID 0x0d04
#define SPLAT_GENERIC_TEST4_NAME "ddi_strtoll"
#define SPLAT_GENERIC_TEST4_DESC "ddi_strtoll Test"
# define SPLAT_GENERIC_TEST5_ID 0x0d05
# define SPLAT_GENERIC_TEST5_NAME "udivdi3"
# define SPLAT_GENERIC_TEST5_DESC "Unsigned Div-64 Test"
# define SPLAT_GENERIC_TEST6_ID 0x0d06
# define SPLAT_GENERIC_TEST6_NAME "divdi3"
# define SPLAT_GENERIC_TEST6_DESC "Signed Div-64 Test"
#define STR_POS "123456789"
#define STR_NEG "-123456789"
#define STR_BASE "0xabcdef"
#define STR_RANGE_MAX "10000000000000000"
#define STR_RANGE_MIN "-10000000000000000"
#define STR_INVAL1 "12345U"
#define STR_INVAL2 "invald"
#define VAL_POS 123456789
#define VAL_NEG -123456789
#define VAL_BASE 0xabcdef
#define VAL_INVAL1 12345U
#define define_generic_msg_strtox(type, valtype) \
static void \
generic_msg_strto##type(struct file *file, char *msg, int rc, int *err, \
const char *s, valtype d, char *endptr) \
{ \
splat_vprint(file, SPLAT_GENERIC_TEST1_NAME, \
"%s (%d) %s: %s == %lld, 0x%p\n", \
rc ? "Fail" : "Pass", *err, msg, s, \
(unsigned long long)d, endptr); \
*err = rc; \
}
define_generic_msg_strtox(ul, unsigned long);
define_generic_msg_strtox(l, long);
define_generic_msg_strtox(ull, unsigned long long);
define_generic_msg_strtox(ll, long long);
#define define_splat_generic_test_strtox(type, valtype) \
static int \
splat_generic_test_strto##type(struct file *file, void *arg) \
{ \
int rc, rc1, rc2, rc3, rc4, rc5, rc6, rc7; \
char str[20], *endptr; \
valtype r; \
\
/* Positive value: expect success */ \
r = 0; \
rc = 1; \
endptr = NULL; \
rc1 = ddi_strto##type(STR_POS, &endptr, 10, &r); \
if (rc1 == 0 && r == VAL_POS && endptr && *endptr == '\0') \
rc = 0; \
\
generic_msg_strto##type(file, "positive", rc , &rc1, \
STR_POS, r, endptr); \
\
/* Negative value: expect success */ \
r = 0; \
rc = 1; \
endptr = NULL; \
strcpy(str, STR_NEG); \
rc2 = ddi_strto##type(str, &endptr, 10, &r); \
if (#type[0] == 'u') { \
if (rc2 == 0 && r == 0 && endptr == str) \
rc = 0; \
} else { \
if (rc2 == 0 && r == VAL_NEG && \
endptr && *endptr == '\0') \
rc = 0; \
} \
\
generic_msg_strto##type(file, "negative", rc, &rc2, \
STR_NEG, r, endptr); \
\
/* Non decimal base: expect sucess */ \
r = 0; \
rc = 1; \
endptr = NULL; \
rc3 = ddi_strto##type(STR_BASE, &endptr, 0, &r); \
if (rc3 == 0 && r == VAL_BASE && endptr && *endptr == '\0') \
rc = 0; \
\
generic_msg_strto##type(file, "base", rc, &rc3, \
STR_BASE, r, endptr); \
\
/* Max out of range: failure expected, r unchanged */ \
r = 0; \
rc = 1; \
endptr = NULL; \
rc4 = ddi_strto##type(STR_RANGE_MAX, &endptr, 16, &r); \
if (rc4 == ERANGE && r == 0 && endptr == NULL) \
rc = 0; \
\
generic_msg_strto##type(file, "max", rc, &rc4, \
STR_RANGE_MAX, r, endptr); \
\
/* Min out of range: failure expected, r unchanged */ \
r = 0; \
rc = 1; \
endptr = NULL; \
strcpy(str, STR_RANGE_MIN); \
rc5 = ddi_strto##type(str, &endptr, 16, &r); \
if (#type[0] == 'u') { \
if (rc5 == 0 && r == 0 && endptr == str) \
rc = 0; \
} else { \
if (rc5 == ERANGE && r == 0 && endptr == NULL) \
rc = 0; \
} \
\
generic_msg_strto##type(file, "min", rc, &rc5, \
STR_RANGE_MIN, r, endptr); \
\
/* Invalid string: success expected, endptr == 'U' */ \
r = 0; \
rc = 1; \
endptr = NULL; \
rc6 = ddi_strto##type(STR_INVAL1, &endptr, 10, &r); \
if (rc6 == 0 && r == VAL_INVAL1 && endptr && *endptr == 'U') \
rc = 0; \
\
generic_msg_strto##type(file, "invalid", rc, &rc6, \
STR_INVAL1, r, endptr); \
\
/* Invalid string: failure expected, endptr == str */ \
r = 0; \
rc = 1; \
endptr = NULL; \
strcpy(str, STR_INVAL2); \
rc7 = ddi_strto##type(str, &endptr, 10, &r); \
if (rc7 == 0 && r == 0 && endptr == str) \
rc = 0; \
\
generic_msg_strto##type(file, "invalid", rc, &rc7, \
STR_INVAL2, r, endptr); \
\
return (rc1 || rc2 || rc3 || rc4 || rc5 || rc6 || rc7) ? \
-EINVAL : 0; \
}
define_splat_generic_test_strtox(ul, unsigned long);
define_splat_generic_test_strtox(l, long);
define_splat_generic_test_strtox(ull, unsigned long long);
define_splat_generic_test_strtox(ll, long long);
/*
* The entries in the table are used in all combinations and the
* return value is checked to ensure it is range. On 32-bit
* systems __udivdi3 will be invoked for the 64-bit division.
* On 64-bit system the native 64-bit divide will be used so
* __udivdi3 isn't used but we might as well stil run the test.
*/
static int
splat_generic_test_udivdi3(struct file *file, void *arg)
{
const uint64_t tabu[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 1000, 2003,
32765, 32766, 32767, 32768, 32769, 32760,
65533, 65534, 65535, 65536, 65537, 65538,
0x7ffffffeULL, 0x7fffffffULL, 0x80000000ULL, 0x80000001ULL,
0x7000000000000000ULL, 0x7000000080000000ULL, 0x7000000080000001ULL,
0x7fffffffffffffffULL, 0x7fffffff8fffffffULL, 0x7fffffff8ffffff1ULL,
0x7fffffff00000000ULL, 0x7fffffff80000000ULL, 0x7fffffff00000001ULL,
0x8000000000000000ULL, 0x8000000080000000ULL, 0x8000000080000001ULL,
0xc000000000000000ULL, 0xc000000080000000ULL, 0xc000000080000001ULL,
0xfffffffffffffffdULL, 0xfffffffffffffffeULL, 0xffffffffffffffffULL,
};
uint64_t uu, vu, qu, ru;
int n, i, j, errors = 0;
splat_vprint(file, SPLAT_GENERIC_TEST5_NAME, "%s",
"Testing unsigned 64-bit division.\n");
n = sizeof(tabu) / sizeof(tabu[0]);
for (i = 0; i < n; i++) {
for (j = 1; j < n; j++) {
uu = tabu[i];
vu = tabu[j];
qu = uu / vu; /* __udivdi3 */
ru = uu - qu * vu;
if (qu > uu || ru >= vu) {
splat_vprint(file, SPLAT_GENERIC_TEST5_NAME,
"%016llx/%016llx != %016llx rem %016llx\n",
uu, vu, qu, ru);
errors++;
}
}
}
if (errors) {
splat_vprint(file, SPLAT_GENERIC_TEST5_NAME,
"Failed %d/%d tests\n", errors, n * (n - 1));
return -ERANGE;
}
splat_vprint(file, SPLAT_GENERIC_TEST5_NAME,
"Passed all %d tests\n", n * (n - 1));
return 0;
}
/*
* The entries the table are used in all combinations, with + and - signs
* preceding them. The return value is checked to ensure it is range.
* On 32-bit systems __divdi3 will be invoked for the 64-bit division.
* On 64-bit system the native 64-bit divide will be used so __divdi3
* isn't used but we might as well stil run the test.
*/
static int
splat_generic_test_divdi3(struct file *file, void *arg)
{
const int64_t tabs[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 1000, 2003,
32765, 32766, 32767, 32768, 32769, 32760,
65533, 65534, 65535, 65536, 65537, 65538,
0x7ffffffeLL, 0x7fffffffLL, 0x80000000LL, 0x80000001LL,
0x7000000000000000LL, 0x7000000080000000LL, 0x7000000080000001LL,
0x7fffffffffffffffLL, 0x7fffffff8fffffffLL, 0x7fffffff8ffffff1LL,
0x7fffffff00000000LL, 0x7fffffff80000000LL, 0x7fffffff00000001LL,
0x0123456789abcdefLL, 0x00000000abcdef01LL, 0x0000000012345678LL,
#if BITS_PER_LONG == 32
0x8000000000000000LL, 0x8000000080000000LL, 0x8000000080000001LL,
#endif
};
int64_t u, v, q, r;
int n, i, j, k, errors = 0;
splat_vprint(file, SPLAT_GENERIC_TEST6_NAME, "%s",
"Testing signed 64-bit division.\n");
n = sizeof(tabs) / sizeof(tabs[0]);
for (i = 0; i < n; i++) {
for (j = 1; j < n; j++) {
for (k = 0; k <= 3; k++) {
u = (k & 1) ? -tabs[i] : tabs[i];
v = (k >= 2) ? -tabs[j] : tabs[j];
q = u / v; /* __divdi3 */
r = u - q * v;
if (abs64(q) > abs64(u) ||
abs64(r) >= abs64(v) ||
(r != 0 && (r ^ u) < 0)) {
splat_vprint(file,
SPLAT_GENERIC_TEST6_NAME,
"%016llx/%016llx != %016llx "
"rem %016llx\n", u, v, q, r);
errors++;
}
}
}
}
if (errors) {
splat_vprint(file, SPLAT_GENERIC_TEST6_NAME,
"Failed %d/%d tests\n", errors, n * (n - 1));
return -ERANGE;
}
splat_vprint(file, SPLAT_GENERIC_TEST6_NAME,
"Passed all %d tests\n", n * (n - 1));
return 0;
}
splat_subsystem_t *
splat_generic_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_GENERIC_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_GENERIC_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_GENERIC;
splat_test_init(sub, SPLAT_GENERIC_TEST1_NAME, SPLAT_GENERIC_TEST1_DESC,
SPLAT_GENERIC_TEST1_ID, splat_generic_test_strtoul);
splat_test_init(sub, SPLAT_GENERIC_TEST2_NAME, SPLAT_GENERIC_TEST2_DESC,
SPLAT_GENERIC_TEST2_ID, splat_generic_test_strtol);
splat_test_init(sub, SPLAT_GENERIC_TEST3_NAME, SPLAT_GENERIC_TEST3_DESC,
SPLAT_GENERIC_TEST3_ID, splat_generic_test_strtoull);
splat_test_init(sub, SPLAT_GENERIC_TEST4_NAME, SPLAT_GENERIC_TEST4_DESC,
SPLAT_GENERIC_TEST4_ID, splat_generic_test_strtoll);
splat_test_init(sub, SPLAT_GENERIC_TEST5_NAME, SPLAT_GENERIC_TEST5_DESC,
SPLAT_GENERIC_TEST5_ID, splat_generic_test_udivdi3);
splat_test_init(sub, SPLAT_GENERIC_TEST6_NAME, SPLAT_GENERIC_TEST6_DESC,
SPLAT_GENERIC_TEST6_ID, splat_generic_test_divdi3);
return sub;
}
void
splat_generic_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_GENERIC_TEST6_ID);
splat_test_fini(sub, SPLAT_GENERIC_TEST5_ID);
splat_test_fini(sub, SPLAT_GENERIC_TEST4_ID);
splat_test_fini(sub, SPLAT_GENERIC_TEST3_ID);
splat_test_fini(sub, SPLAT_GENERIC_TEST2_ID);
splat_test_fini(sub, SPLAT_GENERIC_TEST1_ID);
kfree(sub);
}
int
splat_generic_id(void)
{
return SPLAT_SUBSYSTEM_GENERIC;
}
-150
View File
@@ -1,150 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SPLAT_INTERNAL_H
#define _SPLAT_INTERNAL_H
#include "splat-ctl.h"
#include <sys/mutex.h>
#include <linux/file_compat.h>
#include <linux/version.h>
typedef int (*splat_test_func_t)(struct file *, void *);
typedef struct splat_test {
struct list_head test_list;
splat_user_t desc;
splat_test_func_t test;
} splat_test_t;
typedef struct splat_subsystem {
struct list_head subsystem_list;/* List had to chain entries */
splat_user_t desc;
spinlock_t test_lock;
struct list_head test_list;
} splat_subsystem_t;
void splat_test_init(splat_subsystem_t *sub, const char *name,
const char *desc, unsigned int tid, splat_test_func_t func);
void splat_test_fini(splat_subsystem_t *sub, unsigned int tid);
#define SPLAT_INFO_BUFFER_SIZE 65536
#define SPLAT_INFO_BUFFER_REDZONE 256
typedef struct splat_info {
kmutex_t info_lock;
int info_size;
char *info_buffer;
char *info_head; /* Internal kernel use only */
} splat_info_t;
#define sym2str(sym) (char *)(#sym)
#define splat_print(file, format, args...) \
({ splat_info_t *_info_ = (splat_info_t *)file->private_data; \
int _rc_; \
\
ASSERT(_info_); \
ASSERT(_info_->info_buffer); \
\
mutex_enter(&_info_->info_lock); \
\
/* Don't allow the kernel to start a write in the red zone */ \
if ((int)(_info_->info_head - _info_->info_buffer) > \
(SPLAT_INFO_BUFFER_SIZE - SPLAT_INFO_BUFFER_REDZONE)) { \
_rc_ = -EOVERFLOW; \
} else { \
_rc_ = sprintf(_info_->info_head, format, args); \
if (_rc_ >= 0) \
_info_->info_head += _rc_; \
} \
\
mutex_exit(&_info_->info_lock); \
_rc_; \
})
#define splat_vprint(file, test, format, args...) \
splat_print(file, "%*s: " format, SPLAT_NAME_SIZE, test, args)
#define splat_locked_test(lock, test) \
({ \
int _rc_; \
spin_lock(lock); \
_rc_ = (test) ? 1 : 0; \
spin_unlock(lock); \
_rc_; \
})
splat_subsystem_t *splat_condvar_init(void);
splat_subsystem_t *splat_kmem_init(void);
splat_subsystem_t *splat_mutex_init(void);
splat_subsystem_t *splat_krng_init(void);
splat_subsystem_t *splat_rwlock_init(void);
splat_subsystem_t *splat_taskq_init(void);
splat_subsystem_t *splat_thread_init(void);
splat_subsystem_t *splat_time_init(void);
splat_subsystem_t *splat_vnode_init(void);
splat_subsystem_t *splat_kobj_init(void);
splat_subsystem_t *splat_atomic_init(void);
splat_subsystem_t *splat_list_init(void);
splat_subsystem_t *splat_generic_init(void);
splat_subsystem_t *splat_cred_init(void);
splat_subsystem_t *splat_zlib_init(void);
splat_subsystem_t *splat_linux_init(void);
void splat_condvar_fini(splat_subsystem_t *);
void splat_kmem_fini(splat_subsystem_t *);
void splat_mutex_fini(splat_subsystem_t *);
void splat_krng_fini(splat_subsystem_t *);
void splat_rwlock_fini(splat_subsystem_t *);
void splat_taskq_fini(splat_subsystem_t *);
void splat_thread_fini(splat_subsystem_t *);
void splat_time_fini(splat_subsystem_t *);
void splat_vnode_fini(splat_subsystem_t *);
void splat_kobj_fini(splat_subsystem_t *);
void splat_atomic_fini(splat_subsystem_t *);
void splat_list_fini(splat_subsystem_t *);
void splat_generic_fini(splat_subsystem_t *);
void splat_cred_fini(splat_subsystem_t *);
void splat_zlib_fini(splat_subsystem_t *);
void splat_linux_fini(splat_subsystem_t *);
int splat_condvar_id(void);
int splat_kmem_id(void);
int splat_mutex_id(void);
int splat_krng_id(void);
int splat_rwlock_id(void);
int splat_taskq_id(void);
int splat_thread_id(void);
int splat_time_id(void);
int splat_vnode_id(void);
int splat_kobj_id(void);
int splat_atomic_id(void);
int splat_list_id(void);
int splat_generic_id(void);
int splat_cred_id(void);
int splat_zlib_id(void);
int splat_linux_id(void);
#endif /* _SPLAT_INTERNAL_H */
File diff suppressed because it is too large Load Diff
-166
View File
@@ -1,166 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Kobj Tests.
*/
#include <sys/kobj.h>
#include "splat-internal.h"
#define SPLAT_KOBJ_NAME "kobj"
#define SPLAT_KOBJ_DESC "Kernel Kobj Tests"
#define SPLAT_KOBJ_TEST1_ID 0x0a01
#define SPLAT_KOBJ_TEST1_NAME "open"
#define SPLAT_KOBJ_TEST1_DESC "Kobj Open/Close Test"
#define SPLAT_KOBJ_TEST2_ID 0x0a02
#define SPLAT_KOBJ_TEST2_NAME "size/read"
#define SPLAT_KOBJ_TEST2_DESC "Kobj Size/Read Test"
#define SPLAT_KOBJ_TEST_FILE "/etc/fstab"
static int
splat_kobj_test1(struct file *file, void *arg)
{
struct _buf *f;
f = kobj_open_file(SPLAT_KOBJ_TEST_FILE);
if (f == (struct _buf *)-1) {
splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Failed to open "
"test file: %s\n", SPLAT_KOBJ_TEST_FILE);
return -ENOENT;
}
kobj_close_file(f);
splat_vprint(file, SPLAT_KOBJ_TEST1_NAME, "Successfully opened and "
"closed test file: %s\n", SPLAT_KOBJ_TEST_FILE);
return 0;
} /* splat_kobj_test1() */
static int
splat_kobj_test2(struct file *file, void *arg)
{
struct _buf *f;
char *buf;
uint64_t size;
int rc;
f = kobj_open_file(SPLAT_KOBJ_TEST_FILE);
if (f == (struct _buf *)-1) {
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to open "
"test file: %s\n", SPLAT_KOBJ_TEST_FILE);
return -ENOENT;
}
rc = kobj_get_filesize(f, &size);
if (rc) {
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed stat of "
"test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc);
goto out;
}
buf = kmalloc(size + 1, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed to alloc "
"%lld bytes for tmp buffer (%d)\n",
(long long)size, rc);
goto out;
}
memset(buf, 0, size + 1);
rc = kobj_read_file(f, buf, size, 0);
if (rc < 0) {
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Failed read of "
"test file: %s (%d)\n", SPLAT_KOBJ_TEST_FILE, rc);
goto out2;
}
/* Validate we read as many bytes as expected based on the stat. This
* isn't a perfect test since we didn't create the file however it is
* pretty unlikely there are garbage characters in your /etc/fstab */
if (size != (uint64_t)strlen(buf)) {
rc = -EFBIG;
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Stat'ed size "
"(%lld) does not match number of bytes read "
"(%lld)\n", (long long)size,
(long long)strlen(buf));
goto out2;
}
rc = 0;
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "\n%s\n", buf);
splat_vprint(file, SPLAT_KOBJ_TEST2_NAME, "Successfully stat'ed "
"and read expected number of bytes (%lld) from test "
"file: %s\n", (long long)size, SPLAT_KOBJ_TEST_FILE);
out2:
kfree(buf);
out:
kobj_close_file(f);
return rc;
} /* splat_kobj_test2() */
splat_subsystem_t *
splat_kobj_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_KOBJ_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_KOBJ_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_KOBJ;
splat_test_init(sub, SPLAT_KOBJ_TEST1_NAME, SPLAT_KOBJ_TEST1_DESC,
SPLAT_KOBJ_TEST1_ID, splat_kobj_test1);
splat_test_init(sub, SPLAT_KOBJ_TEST2_NAME, SPLAT_KOBJ_TEST2_DESC,
SPLAT_KOBJ_TEST2_ID, splat_kobj_test2);
return sub;
} /* splat_kobj_init() */
void
splat_kobj_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_KOBJ_TEST2_ID);
splat_test_fini(sub, SPLAT_KOBJ_TEST1_ID);
kfree(sub);
} /* splat_kobj_fini() */
int
splat_kobj_id(void)
{
return SPLAT_SUBSYSTEM_KOBJ;
} /* splat_kobj_id() */
-237
View File
@@ -1,237 +0,0 @@
/*
* Copyright (C) 2011 Lawrence Livermore National Security, LLC.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Kernel Compatibility Tests.
*/
#include <sys/kmem.h>
#include <linux/mm_compat.h>
#include "splat-internal.h"
#define SPLAT_LINUX_NAME "linux"
#define SPLAT_LINUX_DESC "Kernel Compatibility Tests"
#define SPLAT_LINUX_TEST1_ID 0x1001
#define SPLAT_LINUX_TEST1_NAME "shrinker"
#define SPLAT_LINUX_TEST1_DESC "Shrinker test"
/*
* Wait queue used to eliminate race between dropping of slab
* and execution of the shrinker callback
*/
DECLARE_WAIT_QUEUE_HEAD(shrinker_wait);
SPL_SHRINKER_CALLBACK_FWD_DECLARE(splat_linux_shrinker_fn);
SPL_SHRINKER_DECLARE(splat_linux_shrinker, splat_linux_shrinker_fn, 1);
static unsigned long splat_linux_shrinker_size = 0;
static struct file *splat_linux_shrinker_file = NULL;
static spl_shrinker_t
__splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc)
{
static int failsafe = 0;
static unsigned long last_splat_linux_shrinker_size = 0;
unsigned long size;
spl_shrinker_t count;
/*
* shrinker_size can only decrease or stay the same between callbacks
* in the same run, so Reset failsafe whenever shrinker increases
* as this indicates a new run.
*/
if (last_splat_linux_shrinker_size < splat_linux_shrinker_size)
failsafe = 0;
last_splat_linux_shrinker_size = splat_linux_shrinker_size;
if (sc->nr_to_scan) {
size = MIN(sc->nr_to_scan, splat_linux_shrinker_size);
splat_linux_shrinker_size -= size;
splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
"Reclaimed %lu objects, size now %lu\n",
size, splat_linux_shrinker_size);
#ifdef HAVE_SPLIT_SHRINKER_CALLBACK
count = size;
#else
count = splat_linux_shrinker_size;
#endif /* HAVE_SPLIT_SHRINKER_CALLBACK */
} else {
count = splat_linux_shrinker_size;
splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
"Cache size is %lu\n", splat_linux_shrinker_size);
}
/* Far more calls than expected abort drop_slab as a failsafe */
if (failsafe > 100) {
splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
"Far more calls than expected (%d), size now %lu\n",
failsafe, splat_linux_shrinker_size);
return (SHRINK_STOP);
} else {
/*
* We only increment failsafe if it doesn't trigger. This
* makes any failsafe failure persistent until the next test.
*/
failsafe++;
}
/* Shrinker has run, so signal back to test. */
wake_up(&shrinker_wait);
return (count);
}
SPL_SHRINKER_CALLBACK_WRAPPER(splat_linux_shrinker_fn);
#define DROP_SLAB_CMD \
"exec 0</dev/null " \
" 1>/proc/sys/vm/drop_caches " \
" 2>/dev/null; " \
"echo 2"
static int
splat_linux_drop_slab(struct file *file)
{
char *argv[] = { "/bin/sh",
"-c",
DROP_SLAB_CMD,
NULL };
char *envp[] = { "HOME=/",
"TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
int rc;
rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
if (rc)
splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
"Failed user helper '%s %s %s', rc = %d\n",
argv[0], argv[1], argv[2], rc);
return rc;
}
/*
* Verify correct shrinker functionality by registering a shrinker
* with the required compatibility macros. We then use a simulated
* cache and force the systems caches to be dropped. The shrinker
* should be repeatedly called until it reports that the cache is
* empty. It is then cleanly unregistered and correct behavior is
* verified. There are now four slightly different supported shrinker
* API and this test ensures the compatibility code is correct.
*/
static int
splat_linux_test1(struct file *file, void *arg)
{
int rc = -EINVAL;
/*
* Globals used by the shrinker, it is not safe to run this
* test concurrently this is a safe assumption for SPLAT tests.
* Regardless we do some minimal checking a bail if concurrent
* use is detected.
*/
if (splat_linux_shrinker_size || splat_linux_shrinker_file) {
splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
"Failed due to concurrent shrinker test, rc = %d\n", rc);
return (rc);
}
splat_linux_shrinker_size = 1024;
splat_linux_shrinker_file = file;
spl_register_shrinker(&splat_linux_shrinker);
rc = splat_linux_drop_slab(file);
if (rc)
goto out;
/*
* By the time we get here, it is possible that the shrinker has not
* yet run. splat_linux_drop_slab sends a signal for it to run, but
* there is no guarantee of when it will actually run. We wait for it
* to run here, terminating when either the shrinker size is now 0 or
* we timeout after 1 second, which should be an eternity (error).
*/
rc = wait_event_timeout(shrinker_wait, !splat_linux_shrinker_size, HZ);
if (!rc) {
splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
"Failed cache shrinking timed out, size now %lu",
splat_linux_shrinker_size);
rc = -ETIMEDOUT;
} else {
rc = 0;
}
if (!rc && splat_linux_shrinker_size != 0) {
splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
"Failed cache was not shrunk to 0, size now %lu",
splat_linux_shrinker_size);
rc = -EDOM;
}
out:
spl_unregister_shrinker(&splat_linux_shrinker);
splat_linux_shrinker_size = 0;
splat_linux_shrinker_file = NULL;
return rc;
}
splat_subsystem_t *
splat_linux_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_LINUX_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_LINUX_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_LINUX;
splat_test_init(sub, SPLAT_LINUX_TEST1_NAME, SPLAT_LINUX_TEST1_DESC,
SPLAT_LINUX_TEST1_ID, splat_linux_test1);
return sub;
}
void
splat_linux_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_LINUX_TEST1_ID);
kfree(sub);
}
int
splat_linux_id(void) {
return SPLAT_SUBSYSTEM_LINUX;
}
-475
View File
@@ -1,475 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) List Tests.
*/
#include <sys/list.h>
#include <sys/kmem.h>
#include "splat-internal.h"
#define SPLAT_LIST_NAME "list"
#define SPLAT_LIST_DESC "Kernel List Tests"
#define SPLAT_LIST_TEST1_ID 0x0c01
#define SPLAT_LIST_TEST1_NAME "create/destroy"
#define SPLAT_LIST_TEST1_DESC "Create/destroy Test"
#define SPLAT_LIST_TEST2_ID 0x0c02
#define SPLAT_LIST_TEST2_NAME "ins/rm head"
#define SPLAT_LIST_TEST2_DESC "Insert/remove head Test"
#define SPLAT_LIST_TEST3_ID 0x0c03
#define SPLAT_LIST_TEST3_NAME "ins/rm tail"
#define SPLAT_LIST_TEST3_DESC "Insert/remove tail Test"
#define SPLAT_LIST_TEST4_ID 0x0c04
#define SPLAT_LIST_TEST4_NAME "insert_after"
#define SPLAT_LIST_TEST4_DESC "Insert_after Test"
#define SPLAT_LIST_TEST5_ID 0x0c05
#define SPLAT_LIST_TEST5_NAME "insert_before"
#define SPLAT_LIST_TEST5_DESC "Insert_before Test"
#define SPLAT_LIST_TEST6_ID 0x0c06
#define SPLAT_LIST_TEST6_NAME "remove"
#define SPLAT_LIST_TEST6_DESC "Remove Test"
#define SPLAT_LIST_TEST7_ID 0x0c7
#define SPLAT_LIST_TEST7_NAME "active"
#define SPLAT_LIST_TEST7_DESC "Active Test"
/* It is important that li_node is not the first element, this
* ensures the list_d2l/list_object macros are working correctly. */
typedef struct list_item {
int li_data;
list_node_t li_node;
} list_item_t;
#define LIST_ORDER_STACK 0
#define LIST_ORDER_QUEUE 1
static int
splat_list_test1(struct file *file, void *arg)
{
list_t list;
splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
if (!list_is_empty(&list)) {
splat_vprint(file, SPLAT_LIST_TEST1_NAME,
"New list NOT empty%s\n", "");
/* list_destroy() intentionally skipped to avoid assert */
return -EEXIST;
}
splat_vprint(file, SPLAT_LIST_TEST1_NAME, "Destroying list\n%s", "");
list_destroy(&list);
/* Validate the list has been destroyed */
if (list_link_active(&list.list_head)) {
splat_vprint(file, SPLAT_LIST_TEST1_NAME,
"Destroyed list still active%s", "");
return -EIO;
}
return 0;
}
static int
splat_list_validate(list_t *list, int size, int order, int mult)
{
list_item_t *li;
int i;
/* Walk all items in list from head to verify stack or queue
* ordering. We bound the for loop by size+1 to ensure that
* we still terminate if there is list corruption. We also
* intentionally make things a little more complex than they
* need to be by using list_head/list_next for queues, and
* list_tail/list_prev for stacks. This is simply done for
* coverage and to ensure these function are working right.
*/
for (i = 0, li = (order ? list_head(list) : list_tail(list));
i < size + 1 && li != NULL;
i++, li = (order ? list_next(list, li) : list_prev(list, li)))
if (li->li_data != i * mult)
return -EIDRM;
if (i != size)
return -E2BIG;
return 0;
}
static int
splat_list_test2(struct file *file, void *arg)
{
list_t list;
list_item_t *li;
int i, list_size = 8, rc = 0;
splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
/* Insert all items at the list head to form a stack */
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
"Adding %d items to list head\n", list_size);
for (i = 0; i < list_size; i++) {
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li == NULL) {
rc = -ENOMEM;
goto out;
}
list_link_init(&li->li_node);
li->li_data = i;
list_insert_head(&list, li);
}
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
"Validating %d item list is a stack\n", list_size);
rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1);
if (rc)
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
"List validation failed, %d\n", rc);
out:
/* Remove all items */
splat_vprint(file, SPLAT_LIST_TEST2_NAME,
"Removing %d items from list head\n", list_size);
while ((li = list_remove_head(&list)))
kmem_free(li, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST2_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
static int
splat_list_test3(struct file *file, void *arg)
{
list_t list;
list_item_t *li;
int i, list_size = 8, rc = 0;
splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
/* Insert all items at the list tail to form a queue */
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
"Adding %d items to list tail\n", list_size);
for (i = 0; i < list_size; i++) {
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li == NULL) {
rc = -ENOMEM;
goto out;
}
list_link_init(&li->li_node);
li->li_data = i;
list_insert_tail(&list, li);
}
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
"Validating %d item list is a queue\n", list_size);
rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1);
if (rc)
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
"List validation failed, %d\n", rc);
out:
/* Remove all items */
splat_vprint(file, SPLAT_LIST_TEST3_NAME,
"Removing %d items from list tail\n", list_size);
while ((li = list_remove_tail(&list)))
kmem_free(li, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST3_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
static int
splat_list_test4(struct file *file, void *arg)
{
list_t list;
list_item_t *li_new, *li_last = NULL;
int i, list_size = 8, rc = 0;
splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
/* Insert all items after the last item to form a queue */
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
"Adding %d items each after the last item\n", list_size);
for (i = 0; i < list_size; i++) {
li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li_new == NULL) {
rc = -ENOMEM;
goto out;
}
list_link_init(&li_new->li_node);
li_new->li_data = i;
list_insert_after(&list, li_last, li_new);
li_last = li_new;
}
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
"Validating %d item list is a queue\n", list_size);
rc = splat_list_validate(&list, list_size, LIST_ORDER_QUEUE, 1);
if (rc)
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
"List validation failed, %d\n", rc);
out:
/* Remove all items */
splat_vprint(file, SPLAT_LIST_TEST4_NAME,
"Removing %d items from list tail\n", list_size);
while ((li_new = list_remove_head(&list)))
kmem_free(li_new, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST4_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
static int
splat_list_test5(struct file *file, void *arg)
{
list_t list;
list_item_t *li_new, *li_last = NULL;
int i, list_size = 8, rc = 0;
splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
/* Insert all items before the last item to form a stack */
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
"Adding %d items each before the last item\n", list_size);
for (i = 0; i < list_size; i++) {
li_new = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li_new == NULL) {
rc = -ENOMEM;
goto out;
}
list_link_init(&li_new->li_node);
li_new->li_data = i;
list_insert_before(&list, li_last, li_new);
li_last = li_new;
}
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
"Validating %d item list is a queue\n", list_size);
rc = splat_list_validate(&list, list_size, LIST_ORDER_STACK, 1);
if (rc)
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
"List validation failed, %d\n", rc);
out:
/* Remove all items */
splat_vprint(file, SPLAT_LIST_TEST5_NAME,
"Removing %d items from list tail\n", list_size);
while ((li_new = list_remove_tail(&list)))
kmem_free(li_new, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST5_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
static int
splat_list_test6(struct file *file, void *arg)
{
list_t list;
list_item_t *li, *li_prev;
int i, list_size = 8, rc = 0;
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
/* Insert all items at the list tail to form a queue */
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
"Adding %d items to list tail\n", list_size);
for (i = 0; i < list_size; i++) {
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li == NULL) {
rc = -ENOMEM;
goto out;
}
list_link_init(&li->li_node);
li->li_data = i;
list_insert_tail(&list, li);
}
/* Remove all odd items from the queue */
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
"Removing %d odd items from the list\n", list_size >> 1);
for (li = list_head(&list); li != NULL; li = list_next(&list, li)) {
if (li->li_data % 2 == 1) {
li_prev = list_prev(&list, li);
list_remove(&list, li);
kmem_free(li, sizeof(list_item_t));
li = li_prev;
}
}
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Validating %d item "
"list is a queue of only even elements\n", list_size / 2);
rc = splat_list_validate(&list, list_size / 2, LIST_ORDER_QUEUE, 2);
if (rc)
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
"List validation failed, %d\n", rc);
out:
/* Remove all items */
splat_vprint(file, SPLAT_LIST_TEST6_NAME,
"Removing %d items from list tail\n", list_size / 2);
while ((li = list_remove_tail(&list)))
kmem_free(li, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST6_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
static int
splat_list_test7(struct file *file, void *arg)
{
list_t list;
list_item_t *li;
int rc = 0;
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Creating list\n%s", "");
list_create(&list, sizeof(list_item_t), offsetof(list_item_t, li_node));
li = kmem_alloc(sizeof(list_item_t), KM_SLEEP);
if (li == NULL) {
rc = -ENOMEM;
goto out;
}
/* Validate newly initialized node is inactive */
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Init list node\n%s", "");
list_link_init(&li->li_node);
if (list_link_active(&li->li_node)) {
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Newly initialized "
"list node should inactive %p/%p\n",
li->li_node.prev, li->li_node.next);
rc = -EINVAL;
goto out_li;
}
/* Validate node is active when linked in to a list */
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Insert list node\n%s", "");
list_insert_head(&list, li);
if (!list_link_active(&li->li_node)) {
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node "
"inserted in list should be active %p/%p\n",
li->li_node.prev, li->li_node.next);
rc = -EINVAL;
goto out;
}
/* Validate node is inactive when removed from list */
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Remove list node\n%s", "");
list_remove(&list, li);
if (list_link_active(&li->li_node)) {
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "List node "
"removed from list should be inactive %p/%p\n",
li->li_node.prev, li->li_node.next);
rc = -EINVAL;
}
out_li:
kmem_free(li, sizeof(list_item_t));
out:
/* Remove all items */
while ((li = list_remove_head(&list)))
kmem_free(li, sizeof(list_item_t));
splat_vprint(file, SPLAT_LIST_TEST7_NAME, "Destroying list\n%s", "");
list_destroy(&list);
return rc;
}
splat_subsystem_t *
splat_list_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_LIST_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_LIST_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_LIST;
splat_test_init(sub, SPLAT_LIST_TEST1_NAME, SPLAT_LIST_TEST1_DESC,
SPLAT_LIST_TEST1_ID, splat_list_test1);
splat_test_init(sub, SPLAT_LIST_TEST2_NAME, SPLAT_LIST_TEST2_DESC,
SPLAT_LIST_TEST2_ID, splat_list_test2);
splat_test_init(sub, SPLAT_LIST_TEST3_NAME, SPLAT_LIST_TEST3_DESC,
SPLAT_LIST_TEST3_ID, splat_list_test3);
splat_test_init(sub, SPLAT_LIST_TEST4_NAME, SPLAT_LIST_TEST4_DESC,
SPLAT_LIST_TEST4_ID, splat_list_test4);
splat_test_init(sub, SPLAT_LIST_TEST5_NAME, SPLAT_LIST_TEST5_DESC,
SPLAT_LIST_TEST5_ID, splat_list_test5);
splat_test_init(sub, SPLAT_LIST_TEST6_NAME, SPLAT_LIST_TEST6_DESC,
SPLAT_LIST_TEST6_ID, splat_list_test6);
splat_test_init(sub, SPLAT_LIST_TEST7_NAME, SPLAT_LIST_TEST7_DESC,
SPLAT_LIST_TEST7_ID, splat_list_test7);
return sub;
}
void
splat_list_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_LIST_TEST7_ID);
splat_test_fini(sub, SPLAT_LIST_TEST6_ID);
splat_test_fini(sub, SPLAT_LIST_TEST5_ID);
splat_test_fini(sub, SPLAT_LIST_TEST4_ID);
splat_test_fini(sub, SPLAT_LIST_TEST3_ID);
splat_test_fini(sub, SPLAT_LIST_TEST2_ID);
splat_test_fini(sub, SPLAT_LIST_TEST1_ID);
kfree(sub);
}
int
splat_list_id(void)
{
return SPLAT_SUBSYSTEM_LIST;
}
-447
View File
@@ -1,447 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Mutex Tests.
*/
#include <sys/mutex.h>
#include <sys/taskq.h>
#include <linux/delay.h>
#include <linux/mm_compat.h>
#include "splat-internal.h"
#define SPLAT_MUTEX_NAME "mutex"
#define SPLAT_MUTEX_DESC "Kernel Mutex Tests"
#define SPLAT_MUTEX_TEST1_ID 0x0401
#define SPLAT_MUTEX_TEST1_NAME "tryenter"
#define SPLAT_MUTEX_TEST1_DESC "Validate mutex_tryenter() correctness"
#define SPLAT_MUTEX_TEST2_ID 0x0402
#define SPLAT_MUTEX_TEST2_NAME "race"
#define SPLAT_MUTEX_TEST2_DESC "Many threads entering/exiting the mutex"
#define SPLAT_MUTEX_TEST3_ID 0x0403
#define SPLAT_MUTEX_TEST3_NAME "owned"
#define SPLAT_MUTEX_TEST3_DESC "Validate mutex_owned() correctness"
#define SPLAT_MUTEX_TEST4_ID 0x0404
#define SPLAT_MUTEX_TEST4_NAME "owner"
#define SPLAT_MUTEX_TEST4_DESC "Validate mutex_owner() correctness"
#define SPLAT_MUTEX_TEST_MAGIC 0x115599DDUL
#define SPLAT_MUTEX_TEST_NAME "mutex_test"
#define SPLAT_MUTEX_TEST_TASKQ "mutex_taskq"
#define SPLAT_MUTEX_TEST_COUNT 128
typedef struct mutex_priv {
unsigned long mp_magic;
struct file *mp_file;
kmutex_t mp_mtx;
int mp_rc;
int mp_rc2;
} mutex_priv_t;
static void
splat_mutex_test1_func(void *arg)
{
mutex_priv_t *mp = (mutex_priv_t *)arg;
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
if (mutex_tryenter(&mp->mp_mtx)) {
mp->mp_rc = 0;
mutex_exit(&mp->mp_mtx);
} else {
mp->mp_rc = -EBUSY;
}
}
static int
splat_mutex_test1(struct file *file, void *arg)
{
mutex_priv_t *mp;
taskq_t *tq;
taskqid_t id;
int rc = 0;
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
if (mp == NULL)
return -ENOMEM;
tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, 1, defclsyspri,
50, INT_MAX, TASKQ_PREPOPULATE);
if (tq == NULL) {
rc = -ENOMEM;
goto out2;
}
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
mp->mp_file = file;
mutex_init(&mp->mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mutex_enter(&mp->mp_mtx);
/*
* Schedule a task function which will try and acquire the mutex via
* mutex_tryenter() while it's held. This should fail and the task
* function will indicate this status in the passed private data.
*/
mp->mp_rc = -EINVAL;
id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
mutex_exit(&mp->mp_mtx);
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
"taskq_dispatch() failed\n");
rc = -EINVAL;
goto out;
}
taskq_wait_id(tq, id);
mutex_exit(&mp->mp_mtx);
/* Task function successfully acquired mutex, very bad! */
if (mp->mp_rc != -EBUSY) {
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME,
"mutex_trylock() incorrectly succeeded when "
"the mutex was held, %d/%d\n", (int)id, mp->mp_rc);
rc = -EINVAL;
goto out;
} else {
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
"mutex_trylock() correctly failed when "
"the mutex was held\n");
}
/*
* Schedule a task function which will try and acquire the mutex via
* mutex_tryenter() while it is not held. This should succeed and
* can be verified by checking the private data.
*/
mp->mp_rc = -EINVAL;
id = taskq_dispatch(tq, splat_mutex_test1_func, mp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
"taskq_dispatch() failed\n");
rc = -EINVAL;
goto out;
}
taskq_wait_id(tq, id);
/* Task function failed to acquire mutex, very bad! */
if (mp->mp_rc != 0) {
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME,
"mutex_trylock() incorrectly failed when the mutex "
"was not held, %d/%d\n", (int)id, mp->mp_rc);
rc = -EINVAL;
} else {
splat_vprint(file, SPLAT_MUTEX_TEST1_NAME, "%s",
"mutex_trylock() correctly succeeded "
"when the mutex was not held\n");
}
out:
taskq_destroy(tq);
mutex_destroy(&(mp->mp_mtx));
out2:
kfree(mp);
return rc;
}
static void
splat_mutex_test2_func(void *arg)
{
mutex_priv_t *mp = (mutex_priv_t *)arg;
int rc;
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
/* Read the value before sleeping and write it after we wake up to
* maximize the chance of a race if mutexs are not working properly */
mutex_enter(&mp->mp_mtx);
rc = mp->mp_rc;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 100); /* 1/100 of a second */
VERIFY(mp->mp_rc == rc);
mp->mp_rc = rc + 1;
mutex_exit(&mp->mp_mtx);
}
static int
splat_mutex_test2(struct file *file, void *arg)
{
mutex_priv_t *mp;
taskq_t *tq;
taskqid_t id;
int i, rc = 0;
mp = (mutex_priv_t *)kmalloc(sizeof(*mp), GFP_KERNEL);
if (mp == NULL)
return -ENOMEM;
/* Create several threads allowing tasks to race with each other */
tq = taskq_create(SPLAT_MUTEX_TEST_TASKQ, num_online_cpus(),
defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
if (tq == NULL) {
rc = -ENOMEM;
goto out;
}
mp->mp_magic = SPLAT_MUTEX_TEST_MAGIC;
mp->mp_file = file;
mutex_init(&(mp->mp_mtx), SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
mp->mp_rc = 0;
/*
* Schedule N work items to the work queue each of which enters the
* mutex, sleeps briefly, then exits the mutex. On a multiprocessor
* box these work items will be handled by all available CPUs. The
* task function checks to ensure the tracked shared variable is
* always only incremented by one. Additionally, the mutex itself
* is instrumented such that if any two processors are in the
* critical region at the same time the system will panic. If the
* mutex is implemented right this will never happy, that's a pass.
*/
for (i = 0; i < SPLAT_MUTEX_TEST_COUNT; i++) {
id = taskq_dispatch(tq, splat_mutex_test2_func, mp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME,
"Failed to queue task %d\n", i);
rc = -EINVAL;
}
}
taskq_wait(tq);
if (mp->mp_rc == SPLAT_MUTEX_TEST_COUNT) {
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
"correctly entered/exited the mutex %d times\n",
num_online_cpus(), mp->mp_rc);
} else {
splat_vprint(file, SPLAT_MUTEX_TEST2_NAME, "%d racing threads "
"only processed %d/%d mutex work items\n",
num_online_cpus(),mp->mp_rc,SPLAT_MUTEX_TEST_COUNT);
rc = -EINVAL;
}
taskq_destroy(tq);
mutex_destroy(&(mp->mp_mtx));
out:
kfree(mp);
return rc;
}
static void
splat_mutex_owned(void *priv)
{
mutex_priv_t *mp = (mutex_priv_t *)priv;
ASSERT(mp->mp_magic == SPLAT_MUTEX_TEST_MAGIC);
mp->mp_rc = mutex_owned(&mp->mp_mtx);
mp->mp_rc2 = MUTEX_HELD(&mp->mp_mtx);
}
static int
splat_mutex_test3(struct file *file, void *arg)
{
mutex_priv_t mp;
taskq_t *tq;
taskqid_t id;
int rc = 0;
mp.mp_magic = SPLAT_MUTEX_TEST_MAGIC;
mp.mp_file = file;
mutex_init(&mp.mp_mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
if ((tq = taskq_create(SPLAT_MUTEX_TEST_NAME, 1, defclsyspri,
50, INT_MAX, TASKQ_PREPOPULATE)) == NULL) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Taskq '%s' "
"create failed\n", SPLAT_MUTEX_TEST3_NAME);
return -EINVAL;
}
mutex_enter(&mp.mp_mtx);
/* Mutex should be owned by current */
if (!mutex_owned(&mp.mp_mtx)) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Unowned mutex "
"should be owned by pid %d\n", current->pid);
rc = -EINVAL;
goto out_exit;
}
id = taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
"dispatch function '%s' to taskq\n",
sym2str(splat_mutex_owned));
rc = -EINVAL;
goto out_exit;
}
taskq_wait(tq);
/* Mutex should not be owned which checked from a different thread */
if (mp.mp_rc || mp.mp_rc2) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
"pid %d not by taskq\n", current->pid);
rc = -EINVAL;
goto out_exit;
}
mutex_exit(&mp.mp_mtx);
/* Mutex should not be owned by current */
if (mutex_owned(&mp.mp_mtx)) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
"pid %d it should be unowned\b", current->pid);
rc = -EINVAL;
goto out;
}
id = taskq_dispatch(tq, splat_mutex_owned, &mp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Failed to "
"dispatch function '%s' to taskq\n",
sym2str(splat_mutex_owned));
rc = -EINVAL;
goto out;
}
taskq_wait(tq);
/* Mutex should be owned by no one */
if (mp.mp_rc || mp.mp_rc2) {
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "Mutex owned by "
"no one, %d/%d disagrees\n", mp.mp_rc, mp.mp_rc2);
rc = -EINVAL;
goto out;
}
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
"Correct mutex_owned() behavior\n");
goto out;
out_exit:
mutex_exit(&mp.mp_mtx);
out:
mutex_destroy(&mp.mp_mtx);
taskq_destroy(tq);
return rc;
}
static int
splat_mutex_test4(struct file *file, void *arg)
{
kmutex_t mtx;
kthread_t *owner;
int rc = 0;
mutex_init(&mtx, SPLAT_MUTEX_TEST_NAME, MUTEX_DEFAULT, NULL);
/*
* Verify mutex owner is cleared after being dropped. Depending
* on how you build your kernel this behavior changes, ensure the
* SPL mutex implementation is properly detecting this.
*/
mutex_enter(&mtx);
msleep(100);
mutex_exit(&mtx);
if (MUTEX_HELD(&mtx)) {
splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should "
"not be held, bit is by %p\n", mutex_owner(&mtx));
rc = -EINVAL;
goto out;
}
mutex_enter(&mtx);
/* Mutex should be owned by current */
owner = mutex_owner(&mtx);
if (current != owner) {
splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should "
"be owned by pid %d but is owned by pid %d\n",
current->pid, owner ? owner->pid : -1);
rc = -EINVAL;
goto out;
}
mutex_exit(&mtx);
/* Mutex should not be owned by any task */
owner = mutex_owner(&mtx);
if (owner) {
splat_vprint(file, SPLAT_MUTEX_TEST4_NAME, "Mutex should not "
"be owned but is owned by pid %d\n", owner->pid);
rc = -EINVAL;
goto out;
}
splat_vprint(file, SPLAT_MUTEX_TEST3_NAME, "%s",
"Correct mutex_owner() behavior\n");
out:
mutex_destroy(&mtx);
return rc;
}
splat_subsystem_t *
splat_mutex_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_MUTEX_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_MUTEX_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_MUTEX;
splat_test_init(sub, SPLAT_MUTEX_TEST1_NAME, SPLAT_MUTEX_TEST1_DESC,
SPLAT_MUTEX_TEST1_ID, splat_mutex_test1);
splat_test_init(sub, SPLAT_MUTEX_TEST2_NAME, SPLAT_MUTEX_TEST2_DESC,
SPLAT_MUTEX_TEST2_ID, splat_mutex_test2);
splat_test_init(sub, SPLAT_MUTEX_TEST3_NAME, SPLAT_MUTEX_TEST3_DESC,
SPLAT_MUTEX_TEST3_ID, splat_mutex_test3);
splat_test_init(sub, SPLAT_MUTEX_TEST4_NAME, SPLAT_MUTEX_TEST4_DESC,
SPLAT_MUTEX_TEST4_ID, splat_mutex_test4);
return sub;
}
void
splat_mutex_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_MUTEX_TEST4_ID);
splat_test_fini(sub, SPLAT_MUTEX_TEST3_ID);
splat_test_fini(sub, SPLAT_MUTEX_TEST2_ID);
splat_test_fini(sub, SPLAT_MUTEX_TEST1_ID);
kfree(sub);
}
int
splat_mutex_id(void) {
return SPLAT_SUBSYSTEM_MUTEX;
}
-130
View File
@@ -1,130 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Random Number Generator Tests.
*/
#include <sys/random.h>
#include <sys/kmem.h>
#include "splat-internal.h"
#define SPLAT_KRNG_NAME "krng"
#define SPLAT_KRNG_DESC "Kernel Random Number Generator Tests"
#define SPLAT_KRNG_TEST1_ID 0x0301
#define SPLAT_KRNG_TEST1_NAME "freq"
#define SPLAT_KRNG_TEST1_DESC "Frequency Test"
#define KRNG_NUM_BITS 1048576
#define KRNG_NUM_BYTES (KRNG_NUM_BITS >> 3)
#define KRNG_NUM_BITS_DIV2 (KRNG_NUM_BITS >> 1)
#define KRNG_ERROR_RANGE 2097
/* Random Number Generator Tests
There can be meny more tests on quality of the
random number generator. For now we are only
testing the frequency of particular bits.
We could also test consecutive sequences,
randomness within a particular block, etc.
but is probably not necessary for our purposes */
static int
splat_krng_test1(struct file *file, void *arg)
{
uint8_t *buf;
int i, j, diff, num = 0, rc = 0;
buf = kmalloc(sizeof(*buf) * KRNG_NUM_BYTES, GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
memset(buf, 0, sizeof(*buf) * KRNG_NUM_BYTES);
/* Always succeeds */
random_get_pseudo_bytes(buf, sizeof(uint8_t) * KRNG_NUM_BYTES);
for (i = 0; i < KRNG_NUM_BYTES; i++) {
uint8_t tmp = buf[i];
for (j = 0; j < 8; j++) {
uint8_t tmp2 = ((tmp >> j) & 0x01);
if (tmp2 == 1) {
num++;
}
}
}
kfree(buf);
diff = KRNG_NUM_BITS_DIV2 - num;
if (diff < 0)
diff *= -1;
splat_print(file, "Test 1 Number of ones: %d\n", num);
splat_print(file, "Test 1 Difference from expected: %d Allowed: %d\n",
diff, KRNG_ERROR_RANGE);
if (diff > KRNG_ERROR_RANGE)
rc = -ERANGE;
out:
return rc;
}
splat_subsystem_t *
splat_krng_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_KRNG_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_KRNG_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_KRNG;
splat_test_init(sub, SPLAT_KRNG_TEST1_NAME, SPLAT_KRNG_TEST1_DESC,
SPLAT_KRNG_TEST1_ID, splat_krng_test1);
return sub;
}
void
splat_krng_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_KRNG_TEST1_ID);
kfree(sub);
}
int
splat_krng_id(void) {
return SPLAT_SUBSYSTEM_KRNG;
}
-747
View File
@@ -1,747 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Read/Writer Lock Tests.
*/
#include <sys/random.h>
#include <sys/rwlock.h>
#include <sys/taskq.h>
#include <linux/delay.h>
#include <linux/mm_compat.h>
#include "splat-internal.h"
#define SPLAT_RWLOCK_NAME "rwlock"
#define SPLAT_RWLOCK_DESC "Kernel RW Lock Tests"
#define SPLAT_RWLOCK_TEST1_ID 0x0701
#define SPLAT_RWLOCK_TEST1_NAME "N-rd/1-wr"
#define SPLAT_RWLOCK_TEST1_DESC "Multiple readers one writer"
#define SPLAT_RWLOCK_TEST2_ID 0x0702
#define SPLAT_RWLOCK_TEST2_NAME "0-rd/N-wr"
#define SPLAT_RWLOCK_TEST2_DESC "Multiple writers"
#define SPLAT_RWLOCK_TEST3_ID 0x0703
#define SPLAT_RWLOCK_TEST3_NAME "held"
#define SPLAT_RWLOCK_TEST3_DESC "RW_{LOCK|READ|WRITE}_HELD"
#define SPLAT_RWLOCK_TEST4_ID 0x0704
#define SPLAT_RWLOCK_TEST4_NAME "tryenter"
#define SPLAT_RWLOCK_TEST4_DESC "Tryenter"
#define SPLAT_RWLOCK_TEST5_ID 0x0705
#define SPLAT_RWLOCK_TEST5_NAME "rw_downgrade"
#define SPLAT_RWLOCK_TEST5_DESC "Write downgrade"
#define SPLAT_RWLOCK_TEST6_ID 0x0706
#define SPLAT_RWLOCK_TEST6_NAME "rw_tryupgrade-1"
#define SPLAT_RWLOCK_TEST6_DESC "rwsem->count value"
#define SPLAT_RWLOCK_TEST7_ID 0x0707
#define SPLAT_RWLOCK_TEST7_NAME "rw_tryupgrade-2"
#define SPLAT_RWLOCK_TEST7_DESC "Read upgrade"
#define SPLAT_RWLOCK_TEST_MAGIC 0x115599DDUL
#define SPLAT_RWLOCK_TEST_NAME "rwlock_test"
#define SPLAT_RWLOCK_TEST_TASKQ "rwlock_taskq"
#define SPLAT_RWLOCK_TEST_COUNT 8
#define SPLAT_RWLOCK_RELEASE_INIT 0
#define SPLAT_RWLOCK_RELEASE_WR 1
#define SPLAT_RWLOCK_RELEASE_RD 2
typedef struct rw_priv {
unsigned long rw_magic;
struct file *rw_file;
krwlock_t rw_rwlock;
spinlock_t rw_lock;
spl_wait_queue_head_t rw_waitq;
int rw_completed;
int rw_holders;
int rw_waiters;
int rw_release;
int rw_rc;
krw_t rw_type;
} rw_priv_t;
typedef struct rw_thr {
const char *rwt_name;
rw_priv_t *rwt_rwp;
struct task_struct *rwt_thread;
} rw_thr_t;
void splat_init_rw_priv(rw_priv_t *rwp, struct file *file)
{
rwp->rw_magic = SPLAT_RWLOCK_TEST_MAGIC;
rwp->rw_file = file;
rw_init(&rwp->rw_rwlock, SPLAT_RWLOCK_TEST_NAME, RW_DEFAULT, NULL);
spin_lock_init(&rwp->rw_lock);
init_waitqueue_head(&rwp->rw_waitq);
rwp->rw_completed = 0;
rwp->rw_holders = 0;
rwp->rw_waiters = 0;
rwp->rw_release = SPLAT_RWLOCK_RELEASE_INIT;
rwp->rw_rc = 0;
rwp->rw_type = 0;
}
#if defined(CONFIG_PREEMPT_RT_FULL)
static int
splat_rwlock_test1(struct file *file, void *arg)
{
/*
* This test will never succeed on PREEMPT_RT_FULL because these
* kernels only allow a single thread to hold the lock.
*/
return 0;
}
#else
static int
splat_rwlock_wr_thr(void *arg)
{
rw_thr_t *rwt = (rw_thr_t *)arg;
rw_priv_t *rwp = rwt->rwt_rwp;
uint8_t rnd;
ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
get_random_bytes((void *)&rnd, 1);
msleep((unsigned int)rnd);
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s trying to acquire rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
spin_lock(&rwp->rw_lock);
rwp->rw_waiters++;
spin_unlock(&rwp->rw_lock);
rw_enter(&rwp->rw_rwlock, RW_WRITER);
spin_lock(&rwp->rw_lock);
rwp->rw_waiters--;
rwp->rw_holders++;
spin_unlock(&rwp->rw_lock);
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s acquired rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
/* Wait for control thread to signal we can release the write lock */
wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock,
rwp->rw_release == SPLAT_RWLOCK_RELEASE_WR));
spin_lock(&rwp->rw_lock);
rwp->rw_completed++;
rwp->rw_holders--;
spin_unlock(&rwp->rw_lock);
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s dropped rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
rw_exit(&rwp->rw_rwlock);
return 0;
}
static int
splat_rwlock_rd_thr(void *arg)
{
rw_thr_t *rwt = (rw_thr_t *)arg;
rw_priv_t *rwp = rwt->rwt_rwp;
uint8_t rnd;
ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
get_random_bytes((void *)&rnd, 1);
msleep((unsigned int)rnd);
/* Don't try and take the semaphore until after someone has it */
wait_event_interruptible(rwp->rw_waitq,
splat_locked_test(&rwp->rw_lock, rwp->rw_holders > 0));
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s trying to acquire rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
spin_lock(&rwp->rw_lock);
rwp->rw_waiters++;
spin_unlock(&rwp->rw_lock);
rw_enter(&rwp->rw_rwlock, RW_READER);
spin_lock(&rwp->rw_lock);
rwp->rw_waiters--;
rwp->rw_holders++;
spin_unlock(&rwp->rw_lock);
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s acquired rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
/* Wait for control thread to signal we can release the read lock */
wait_event_interruptible(rwp->rw_waitq, splat_locked_test(&rwp->rw_lock,
rwp->rw_release == SPLAT_RWLOCK_RELEASE_RD));
spin_lock(&rwp->rw_lock);
rwp->rw_completed++;
rwp->rw_holders--;
spin_unlock(&rwp->rw_lock);
splat_vprint(rwp->rw_file, rwt->rwt_name,
"%s dropped rwlock (%d holding/%d waiting)\n",
rwt->rwt_thread->comm, rwp->rw_holders, rwp->rw_waiters);
rw_exit(&rwp->rw_rwlock);
return 0;
}
static int
splat_rwlock_test1(struct file *file, void *arg)
{
int i, count = 0, rc = 0;
rw_thr_t rwt[SPLAT_RWLOCK_TEST_COUNT];
rw_priv_t *rwp;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
/* Create some threads, the exact number isn't important just as
* long as we know how many we managed to create and should expect. */
for (i = 0; i < SPLAT_RWLOCK_TEST_COUNT; i++) {
rwt[i].rwt_rwp = rwp;
rwt[i].rwt_name = SPLAT_RWLOCK_TEST1_NAME;
/* The first thread will be the writer */
if (i == 0)
rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_wr_thr,
&rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i);
else
rwt[i].rwt_thread = spl_kthread_create(splat_rwlock_rd_thr,
&rwt[i], "%s/%d", SPLAT_RWLOCK_TEST_NAME, i);
if (!IS_ERR(rwt[i].rwt_thread)) {
wake_up_process(rwt[i].rwt_thread);
count++;
}
}
/* Wait for the writer */
while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders == 0)) {
wake_up_interruptible(&rwp->rw_waitq);
msleep(100);
}
/* Wait for 'count-1' readers */
while (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters < count - 1)) {
wake_up_interruptible(&rwp->rw_waitq);
msleep(100);
}
/* Verify there is only one lock holder */
if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders) != 1) {
splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only 1 holder "
"expected for rwlock (%d holding/%d waiting)\n",
rwp->rw_holders, rwp->rw_waiters);
rc = -EINVAL;
}
/* Verify 'count-1' readers */
if (splat_locked_test(&rwp->rw_lock, rwp->rw_waiters != count - 1)) {
splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d waiters "
"expected for rwlock (%d holding/%d waiting)\n",
count - 1, rwp->rw_holders, rwp->rw_waiters);
rc = -EINVAL;
}
/* Signal the writer to release, allows readers to acquire */
spin_lock(&rwp->rw_lock);
rwp->rw_release = SPLAT_RWLOCK_RELEASE_WR;
wake_up_interruptible(&rwp->rw_waitq);
spin_unlock(&rwp->rw_lock);
/* Wait for 'count-1' readers to hold the lock */
while (splat_locked_test(&rwp->rw_lock, rwp->rw_holders < count - 1)) {
wake_up_interruptible(&rwp->rw_waitq);
msleep(100);
}
/* Verify there are 'count-1' readers */
if (splat_locked_test(&rwp->rw_lock, rwp->rw_holders != count - 1)) {
splat_vprint(file, SPLAT_RWLOCK_TEST1_NAME, "Only %d holders "
"expected for rwlock (%d holding/%d waiting)\n",
count - 1, rwp->rw_holders, rwp->rw_waiters);
rc = -EINVAL;
}
/* Release 'count-1' readers */
spin_lock(&rwp->rw_lock);
rwp->rw_release = SPLAT_RWLOCK_RELEASE_RD;
wake_up_interruptible(&rwp->rw_waitq);
spin_unlock(&rwp->rw_lock);
/* Wait for the test to complete */
while (splat_locked_test(&rwp->rw_lock,
rwp->rw_holders>0 || rwp->rw_waiters>0))
msleep(100);
rw_destroy(&(rwp->rw_rwlock));
kfree(rwp);
return rc;
}
#endif
static void
splat_rwlock_test2_func(void *arg)
{
rw_priv_t *rwp = (rw_priv_t *)arg;
int rc;
ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
/* Read the value before sleeping and write it after we wake up to
* maximize the chance of a race if rwlocks are not working properly */
rw_enter(&rwp->rw_rwlock, RW_WRITER);
rc = rwp->rw_rc;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 100); /* 1/100 of a second */
VERIFY(rwp->rw_rc == rc);
rwp->rw_rc = rc + 1;
rw_exit(&rwp->rw_rwlock);
}
static int
splat_rwlock_test2(struct file *file, void *arg)
{
rw_priv_t *rwp;
taskq_t *tq;
int i, rc = 0, tq_count = 256;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
/* Create several threads allowing tasks to race with each other */
tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, num_online_cpus(),
defclsyspri, 50, INT_MAX, TASKQ_PREPOPULATE);
if (tq == NULL) {
rc = -ENOMEM;
goto out;
}
/*
* Schedule N work items to the work queue each of which enters the
* writer rwlock, sleeps briefly, then exits the writer rwlock. On a
* multiprocessor box these work items will be handled by all available
* CPUs. The task function checks to ensure the tracked shared variable
* is always only incremented by one. Additionally, the rwlock itself
* is instrumented such that if any two processors are in the
* critical region at the same time the system will panic. If the
* rwlock is implemented right this will never happy, that's a pass.
*/
for (i = 0; i < tq_count; i++) {
if (taskq_dispatch(tq, splat_rwlock_test2_func, rwp,
TQ_SLEEP) == TASKQID_INVALID) {
splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME,
"Failed to queue task %d\n", i);
rc = -EINVAL;
}
}
taskq_wait(tq);
if (rwp->rw_rc == tq_count) {
splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
"correctly entered/exited the rwlock %d times\n",
num_online_cpus(), rwp->rw_rc);
} else {
splat_vprint(file, SPLAT_RWLOCK_TEST2_NAME, "%d racing threads "
"only processed %d/%d w rwlock work items\n",
num_online_cpus(), rwp->rw_rc, tq_count);
rc = -EINVAL;
}
taskq_destroy(tq);
rw_destroy(&(rwp->rw_rwlock));
out:
kfree(rwp);
return rc;
}
#define splat_rwlock_test3_helper(rwp,rex1,rex2,wex1,wex2,held_func,rc) \
do { \
int result, _rc1_, _rc2_, _rc3_, _rc4_; \
\
rc = 0; \
rw_enter(&(rwp)->rw_rwlock, RW_READER); \
_rc1_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex1); \
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
" returned %d (expected %d) when RW_READER\n", \
_rc1_ ? "Fail " : "", result, rex1); \
rw_exit(&(rwp)->rw_rwlock); \
_rc2_ = ((result = held_func(&(rwp)->rw_rwlock)) != rex2); \
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
" returned %d (expected %d) when !RW_READER\n", \
_rc2_ ? "Fail " : "", result, rex2); \
\
rw_enter(&(rwp)->rw_rwlock, RW_WRITER); \
_rc3_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex1); \
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
" returned %d (expected %d) when RW_WRITER\n", \
_rc3_ ? "Fail " : "", result, wex1); \
rw_exit(&(rwp)->rw_rwlock); \
_rc4_ = ((result = held_func(&(rwp)->rw_rwlock)) != wex2); \
splat_vprint(file, SPLAT_RWLOCK_TEST3_NAME, "%s" #held_func \
" returned %d (expected %d) when !RW_WRITER\n", \
_rc4_ ? "Fail " : "", result, wex2); \
\
rc = ((_rc1_ || _rc2_ || _rc3_ || _rc4_) ? -EINVAL : 0); \
} while(0);
static int
splat_rwlock_test3(struct file *file, void *arg)
{
rw_priv_t *rwp;
int rc1, rc2, rc3;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
splat_rwlock_test3_helper(rwp, 1, 0, 1, 0, RW_LOCK_HELD, rc1);
splat_rwlock_test3_helper(rwp, 1, 0, 0, 0, RW_READ_HELD, rc2);
splat_rwlock_test3_helper(rwp, 0, 0, 1, 0, RW_WRITE_HELD, rc3);
rw_destroy(&rwp->rw_rwlock);
kfree(rwp);
return ((rc1 || rc2 || rc3) ? -EINVAL : 0);
}
static void
splat_rwlock_test4_func(void *arg)
{
rw_priv_t *rwp = (rw_priv_t *)arg;
ASSERT(rwp->rw_magic == SPLAT_RWLOCK_TEST_MAGIC);
if (rw_tryenter(&rwp->rw_rwlock, rwp->rw_type)) {
rwp->rw_rc = 0;
rw_exit(&rwp->rw_rwlock);
} else {
rwp->rw_rc = -EBUSY;
}
}
static char *
splat_rwlock_test4_name(krw_t type)
{
switch (type) {
case RW_NONE: return "RW_NONE";
case RW_WRITER: return "RW_WRITER";
case RW_READER: return "RW_READER";
}
return NULL;
}
static int
splat_rwlock_test4_type(taskq_t *tq, rw_priv_t *rwp, int expected_rc,
krw_t holder_type, krw_t try_type)
{
int id, rc = 0;
/* Schedule a task function which will try and acquire the rwlock
* using type try_type while the rwlock is being held as holder_type.
* The result must match expected_rc for the test to pass */
rwp->rw_rc = -EINVAL;
rwp->rw_type = try_type;
if (holder_type == RW_WRITER || holder_type == RW_READER)
rw_enter(&rwp->rw_rwlock, holder_type);
id = taskq_dispatch(tq, splat_rwlock_test4_func, rwp, TQ_SLEEP);
if (id == TASKQID_INVALID) {
splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME, "%s",
"taskq_dispatch() failed\n");
rc = -EINVAL;
goto out;
}
taskq_wait_id(tq, id);
if (rwp->rw_rc != expected_rc)
rc = -EINVAL;
splat_vprint(rwp->rw_file, SPLAT_RWLOCK_TEST4_NAME,
"%srw_tryenter(%s) returned %d (expected %d) when %s\n",
rc ? "Fail " : "", splat_rwlock_test4_name(try_type),
rwp->rw_rc, expected_rc,
splat_rwlock_test4_name(holder_type));
out:
if (holder_type == RW_WRITER || holder_type == RW_READER)
rw_exit(&rwp->rw_rwlock);
return rc;
}
static int
splat_rwlock_test4(struct file *file, void *arg)
{
rw_priv_t *rwp;
taskq_t *tq;
int rc = 0, rc1, rc2, rc3, rc4, rc5, rc6;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
tq = taskq_create(SPLAT_RWLOCK_TEST_TASKQ, 1, defclsyspri,
50, INT_MAX, TASKQ_PREPOPULATE);
if (tq == NULL) {
rc = -ENOMEM;
goto out;
}
splat_init_rw_priv(rwp, file);
/*
* Validate all combinations of rw_tryenter() contention.
*
* The concurrent reader test is modified for PREEMPT_RT_FULL
* kernels which do not permit concurrent read locks to be taken
* from different threads. The same thread is allowed to take
* the read lock multiple times.
*/
rc1 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_WRITER);
rc2 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_WRITER, RW_READER);
rc3 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_WRITER);
#if defined(CONFIG_PREEMPT_RT_FULL)
rc4 = splat_rwlock_test4_type(tq, rwp, -EBUSY, RW_READER, RW_READER);
#else
rc4 = splat_rwlock_test4_type(tq, rwp, 0, RW_READER, RW_READER);
#endif
rc5 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_WRITER);
rc6 = splat_rwlock_test4_type(tq, rwp, 0, RW_NONE, RW_READER);
if (rc1 || rc2 || rc3 || rc4 || rc5 || rc6)
rc = -EINVAL;
taskq_destroy(tq);
out:
rw_destroy(&(rwp->rw_rwlock));
kfree(rwp);
return rc;
}
static int
splat_rwlock_test5(struct file *file, void *arg)
{
rw_priv_t *rwp;
int rc = -EINVAL;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
rw_enter(&rwp->rw_rwlock, RW_WRITER);
if (!RW_WRITE_HELD(&rwp->rw_rwlock)) {
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME,
"rwlock should be write lock: %d\n",
RW_WRITE_HELD(&rwp->rw_rwlock));
goto out;
}
rw_downgrade(&rwp->rw_rwlock);
if (!RW_READ_HELD(&rwp->rw_rwlock)) {
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME,
"rwlock should be read lock: %d\n",
RW_READ_HELD(&rwp->rw_rwlock));
goto out;
}
rc = 0;
splat_vprint(file, SPLAT_RWLOCK_TEST5_NAME, "%s",
"rwlock properly downgraded\n");
out:
rw_exit(&rwp->rw_rwlock);
rw_destroy(&rwp->rw_rwlock);
kfree(rwp);
return rc;
}
static int
splat_rwlock_test6(struct file *file, void *arg)
{
rw_priv_t *rwp;
int rc;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
rw_enter(&rwp->rw_rwlock, RW_READER);
if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) !=
SPL_RWSEM_SINGLE_READER_VALUE) {
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME,
"We assumed single reader rwsem->count "
"should be %ld, but is %ld\n",
(long int)SPL_RWSEM_SINGLE_READER_VALUE,
(long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock)));
rc = -ENOLCK;
goto out;
}
rw_exit(&rwp->rw_rwlock);
rw_enter(&rwp->rw_rwlock, RW_WRITER);
if (RWSEM_COUNT(SEM(&rwp->rw_rwlock)) !=
SPL_RWSEM_SINGLE_WRITER_VALUE) {
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME,
"We assumed single writer rwsem->count "
"should be %ld, but is %ld\n",
(long int)SPL_RWSEM_SINGLE_WRITER_VALUE,
(long int)RWSEM_COUNT(SEM(&rwp->rw_rwlock)));
rc = -ENOLCK;
goto out;
}
rc = 0;
splat_vprint(file, SPLAT_RWLOCK_TEST6_NAME, "%s",
"rwsem->count same as we assumed\n");
out:
rw_exit(&rwp->rw_rwlock);
rw_destroy(&rwp->rw_rwlock);
kfree(rwp);
return rc;
}
static int
splat_rwlock_test7(struct file *file, void *arg)
{
rw_priv_t *rwp;
int rc;
rwp = (rw_priv_t *)kmalloc(sizeof(*rwp), GFP_KERNEL);
if (rwp == NULL)
return -ENOMEM;
splat_init_rw_priv(rwp, file);
rw_enter(&rwp->rw_rwlock, RW_READER);
if (!RW_READ_HELD(&rwp->rw_rwlock)) {
splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME,
"rwlock should be read lock: %d\n",
RW_READ_HELD(&rwp->rw_rwlock));
rc = -ENOLCK;
goto out;
}
/* With one reader upgrade should never fail. */
rc = rw_tryupgrade(&rwp->rw_rwlock);
if (!rc) {
splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME,
"rwlock failed upgrade from reader: %d\n",
RW_READ_HELD(&rwp->rw_rwlock));
rc = -ENOLCK;
goto out;
}
if (RW_READ_HELD(&rwp->rw_rwlock) || !RW_WRITE_HELD(&rwp->rw_rwlock)) {
splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "rwlock should "
"have 0 (not %d) reader and 1 (not %d) writer\n",
RW_READ_HELD(&rwp->rw_rwlock),
RW_WRITE_HELD(&rwp->rw_rwlock));
goto out;
}
rc = 0;
splat_vprint(file, SPLAT_RWLOCK_TEST7_NAME, "%s",
"rwlock properly upgraded\n");
out:
rw_exit(&rwp->rw_rwlock);
rw_destroy(&rwp->rw_rwlock);
kfree(rwp);
return rc;
}
splat_subsystem_t *
splat_rwlock_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_RWLOCK_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_RWLOCK_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_RWLOCK;
splat_test_init(sub, SPLAT_RWLOCK_TEST1_NAME, SPLAT_RWLOCK_TEST1_DESC,
SPLAT_RWLOCK_TEST1_ID, splat_rwlock_test1);
splat_test_init(sub, SPLAT_RWLOCK_TEST2_NAME, SPLAT_RWLOCK_TEST2_DESC,
SPLAT_RWLOCK_TEST2_ID, splat_rwlock_test2);
splat_test_init(sub, SPLAT_RWLOCK_TEST3_NAME, SPLAT_RWLOCK_TEST3_DESC,
SPLAT_RWLOCK_TEST3_ID, splat_rwlock_test3);
splat_test_init(sub, SPLAT_RWLOCK_TEST4_NAME, SPLAT_RWLOCK_TEST4_DESC,
SPLAT_RWLOCK_TEST4_ID, splat_rwlock_test4);
splat_test_init(sub, SPLAT_RWLOCK_TEST5_NAME, SPLAT_RWLOCK_TEST5_DESC,
SPLAT_RWLOCK_TEST5_ID, splat_rwlock_test5);
splat_test_init(sub, SPLAT_RWLOCK_TEST6_NAME, SPLAT_RWLOCK_TEST6_DESC,
SPLAT_RWLOCK_TEST6_ID, splat_rwlock_test6);
splat_test_init(sub, SPLAT_RWLOCK_TEST7_NAME, SPLAT_RWLOCK_TEST7_DESC,
SPLAT_RWLOCK_TEST7_ID, splat_rwlock_test7);
return sub;
}
void
splat_rwlock_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_RWLOCK_TEST7_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST6_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST5_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST4_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST3_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST2_ID);
splat_test_fini(sub, SPLAT_RWLOCK_TEST1_ID);
kfree(sub);
}
int
splat_rwlock_id(void) {
return SPLAT_SUBSYSTEM_RWLOCK;
}
File diff suppressed because it is too large Load Diff
-390
View File
@@ -1,390 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Thread Tests.
*/
#include <sys/thread.h>
#include <sys/random.h>
#include <linux/delay.h>
#include <linux/mm_compat.h>
#include <linux/wait_compat.h>
#include <linux/slab.h>
#include "splat-internal.h"
#define SPLAT_THREAD_NAME "thread"
#define SPLAT_THREAD_DESC "Kernel Thread Tests"
#define SPLAT_THREAD_TEST1_ID 0x0601
#define SPLAT_THREAD_TEST1_NAME "create"
#define SPLAT_THREAD_TEST1_DESC "Validate thread creation"
#define SPLAT_THREAD_TEST2_ID 0x0602
#define SPLAT_THREAD_TEST2_NAME "exit"
#define SPLAT_THREAD_TEST2_DESC "Validate thread exit"
#define SPLAT_THREAD_TEST3_ID 0x6003
#define SPLAT_THREAD_TEST3_NAME "tsd"
#define SPLAT_THREAD_TEST3_DESC "Validate thread specific data"
#define SPLAT_THREAD_TEST_MAGIC 0x4488CC00UL
#define SPLAT_THREAD_TEST_KEYS 32
#define SPLAT_THREAD_TEST_THREADS 16
typedef struct thread_priv {
unsigned long tp_magic;
struct file *tp_file;
spinlock_t tp_lock;
spl_wait_queue_head_t tp_waitq;
uint_t tp_keys[SPLAT_THREAD_TEST_KEYS];
int tp_rc;
int tp_count;
int tp_dtor_count;
} thread_priv_t;
static int
splat_thread_rc(thread_priv_t *tp, int rc)
{
int ret;
spin_lock(&tp->tp_lock);
ret = (tp->tp_rc == rc);
spin_unlock(&tp->tp_lock);
return ret;
}
static int
splat_thread_count(thread_priv_t *tp, int count)
{
int ret;
spin_lock(&tp->tp_lock);
ret = (tp->tp_count == count);
spin_unlock(&tp->tp_lock);
return ret;
}
static void
splat_thread_work1(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
spin_lock(&tp->tp_lock);
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
tp->tp_rc = 1;
wake_up(&tp->tp_waitq);
spin_unlock(&tp->tp_lock);
thread_exit();
}
static int
splat_thread_test1(struct file *file, void *arg)
{
thread_priv_t tp;
kthread_t *thr;
tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
tp.tp_file = file;
spin_lock_init(&tp.tp_lock);
init_waitqueue_head(&tp.tp_waitq);
tp.tp_rc = 0;
thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work1, &tp, 0,
&p0, TS_RUN, defclsyspri);
/* Must never fail under Solaris, but we check anyway since this
* can happen in the linux SPL, we may want to change this behavior */
if (thr == NULL)
return -ESRCH;
/* Sleep until the thread sets tp.tp_rc == 1 */
wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
splat_vprint(file, SPLAT_THREAD_TEST1_NAME, "%s",
"Thread successfully started properly\n");
return 0;
}
static void
splat_thread_work2(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
spin_lock(&tp->tp_lock);
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
tp->tp_rc = 1;
wake_up(&tp->tp_waitq);
spin_unlock(&tp->tp_lock);
thread_exit();
/* The following code is unreachable when thread_exit() is
* working properly, which is exactly what we're testing */
spin_lock(&tp->tp_lock);
tp->tp_rc = 2;
wake_up(&tp->tp_waitq);
spin_unlock(&tp->tp_lock);
}
static int
splat_thread_test2(struct file *file, void *arg)
{
thread_priv_t tp;
kthread_t *thr;
int rc = 0;
tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
tp.tp_file = file;
spin_lock_init(&tp.tp_lock);
init_waitqueue_head(&tp.tp_waitq);
tp.tp_rc = 0;
thr = (kthread_t *)thread_create(NULL, 0, splat_thread_work2, &tp, 0,
&p0, TS_RUN, defclsyspri);
/* Must never fail under Solaris, but we check anyway since this
* can happen in the linux SPL, we may want to change this behavior */
if (thr == NULL)
return -ESRCH;
/* Sleep until the thread sets tp.tp_rc == 1 */
wait_event(tp.tp_waitq, splat_thread_rc(&tp, 1));
/* Sleep until the thread sets tp.tp_rc == 2, or until we hit
* the timeout. If thread exit is working properly we should
* hit the timeout and never see to.tp_rc == 2. */
rc = wait_event_timeout(tp.tp_waitq, splat_thread_rc(&tp, 2), HZ / 10);
if (rc > 0) {
rc = -EINVAL;
splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
"Thread did not exit properly at thread_exit()\n");
} else {
splat_vprint(file, SPLAT_THREAD_TEST2_NAME, "%s",
"Thread successfully exited at thread_exit()\n");
}
return rc;
}
static void
splat_thread_work3_common(thread_priv_t *tp)
{
ulong_t rnd;
int i, rc = 0;
/* set a unique value for each key using a random value */
get_random_bytes((void *)&rnd, 4);
for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
tsd_set(tp->tp_keys[i], (void *)(i + rnd));
/* verify the unique value for each key */
for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
if (tsd_get(tp->tp_keys[i]) != (void *)(i + rnd))
rc = -EINVAL;
/* set the value to thread_priv_t for use by the destructor */
for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
tsd_set(tp->tp_keys[i], (void *)tp);
spin_lock(&tp->tp_lock);
if (rc && !tp->tp_rc)
tp->tp_rc = rc;
tp->tp_count++;
wake_up_all(&tp->tp_waitq);
spin_unlock(&tp->tp_lock);
}
static void
splat_thread_work3_wait(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
splat_thread_work3_common(tp);
wait_event(tp->tp_waitq, splat_thread_count(tp, 0));
thread_exit();
}
static void
splat_thread_work3_exit(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
splat_thread_work3_common(tp);
thread_exit();
}
static void
splat_thread_dtor3(void *priv)
{
thread_priv_t *tp = (thread_priv_t *)priv;
ASSERT(tp->tp_magic == SPLAT_THREAD_TEST_MAGIC);
spin_lock(&tp->tp_lock);
tp->tp_dtor_count++;
spin_unlock(&tp->tp_lock);
}
/*
* Create threads which set and verify SPLAT_THREAD_TEST_KEYS number of
* keys. These threads may then exit by calling thread_exit() which calls
* tsd_exit() resulting in all their thread specific data being reclaimed.
* Alternately, the thread may block in which case the thread specific
* data will be reclaimed as part of tsd_destroy(). In either case all
* thread specific data must be reclaimed, this is verified by ensuring
* the registered destructor is called the correct number of times.
*/
static int
splat_thread_test3(struct file *file, void *arg)
{
int i, rc = 0, expected, wait_count = 0, exit_count = 0;
thread_priv_t tp;
tp.tp_magic = SPLAT_THREAD_TEST_MAGIC;
tp.tp_file = file;
spin_lock_init(&tp.tp_lock);
init_waitqueue_head(&tp.tp_waitq);
tp.tp_rc = 0;
tp.tp_count = 0;
tp.tp_dtor_count = 0;
for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++) {
tp.tp_keys[i] = 0;
tsd_create(&tp.tp_keys[i], splat_thread_dtor3);
}
/* Start tsd wait threads */
for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) {
if (thread_create(NULL, 0, splat_thread_work3_wait,
&tp, 0, &p0, TS_RUN, defclsyspri))
wait_count++;
}
/* All wait threads have setup their tsd and are blocking. */
wait_event(tp.tp_waitq, splat_thread_count(&tp, wait_count));
if (tp.tp_dtor_count != 0) {
splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
"Prematurely ran %d tsd destructors\n", tp.tp_dtor_count);
if (!rc)
rc = -ERANGE;
}
/* Start tsd exit threads */
for (i = 0; i < SPLAT_THREAD_TEST_THREADS; i++) {
if (thread_create(NULL, 0, splat_thread_work3_exit,
&tp, 0, &p0, TS_RUN, defclsyspri))
exit_count++;
}
/* All exit threads verified tsd and are in the process of exiting */
wait_event(tp.tp_waitq,splat_thread_count(&tp, wait_count+exit_count));
msleep(500);
expected = (SPLAT_THREAD_TEST_KEYS * exit_count);
if (tp.tp_dtor_count != expected) {
splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
"Expected %d exit tsd destructors but saw %d\n",
expected, tp.tp_dtor_count);
if (!rc)
rc = -ERANGE;
}
/* Destroy all keys and associated tsd in blocked threads */
for (i = 0; i < SPLAT_THREAD_TEST_KEYS; i++)
tsd_destroy(&tp.tp_keys[i]);
expected = (SPLAT_THREAD_TEST_KEYS * (exit_count + wait_count));
if (tp.tp_dtor_count != expected) {
splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
"Expected %d wait+exit tsd destructors but saw %d\n",
expected, tp.tp_dtor_count);
if (!rc)
rc = -ERANGE;
}
/* Release the remaining wait threads, sleep briefly while they exit */
spin_lock(&tp.tp_lock);
tp.tp_count = 0;
wake_up_all(&tp.tp_waitq);
spin_unlock(&tp.tp_lock);
msleep(500);
if (tp.tp_rc) {
splat_vprint(file, SPLAT_THREAD_TEST3_NAME,
"Thread tsd_get()/tsd_set() error %d\n", tp.tp_rc);
if (!rc)
rc = tp.tp_rc;
} else if (!rc) {
splat_vprint(file, SPLAT_THREAD_TEST3_NAME, "%s",
"Thread specific data verified\n");
}
return rc;
}
splat_subsystem_t *
splat_thread_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_THREAD_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_THREAD_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_THREAD;
splat_test_init(sub, SPLAT_THREAD_TEST1_NAME, SPLAT_THREAD_TEST1_DESC,
SPLAT_THREAD_TEST1_ID, splat_thread_test1);
splat_test_init(sub, SPLAT_THREAD_TEST2_NAME, SPLAT_THREAD_TEST2_DESC,
SPLAT_THREAD_TEST2_ID, splat_thread_test2);
splat_test_init(sub, SPLAT_THREAD_TEST3_NAME, SPLAT_THREAD_TEST3_DESC,
SPLAT_THREAD_TEST3_ID, splat_thread_test3);
return sub;
}
void
splat_thread_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_THREAD_TEST3_ID);
splat_test_fini(sub, SPLAT_THREAD_TEST2_ID);
splat_test_fini(sub, SPLAT_THREAD_TEST1_ID);
kfree(sub);
}
int
splat_thread_id(void) {
return SPLAT_SUBSYSTEM_THREAD;
}
-119
View File
@@ -1,119 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Time Tests.
*/
#include <sys/time.h>
#include <linux/mm_compat.h>
#include <linux/slab.h>
#include "splat-internal.h"
#define SPLAT_TIME_NAME "time"
#define SPLAT_TIME_DESC "Kernel Time Tests"
#define SPLAT_TIME_TEST1_ID 0x0801
#define SPLAT_TIME_TEST1_NAME "time1"
#define SPLAT_TIME_TEST1_DESC "HZ Test"
#define SPLAT_TIME_TEST2_ID 0x0802
#define SPLAT_TIME_TEST2_NAME "time2"
#define SPLAT_TIME_TEST2_DESC "Monotonic Test"
static int
splat_time_test1(struct file *file, void *arg)
{
int myhz = hz;
splat_vprint(file, SPLAT_TIME_TEST1_NAME, "hz is %d\n", myhz);
return 0;
}
static int
splat_time_test2(struct file *file, void *arg)
{
hrtime_t tm1, tm2;
int i;
tm1 = gethrtime();
splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm1);
for(i = 0; i < 100; i++) {
tm2 = gethrtime();
splat_vprint(file, SPLAT_TIME_TEST2_NAME, "time is %lld\n", tm2);
if(tm1 > tm2) {
splat_print(file, "%s: gethrtime() is not giving "
"monotonically increasing values\n",
SPLAT_TIME_TEST2_NAME);
return 1;
}
tm1 = tm2;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(10);
}
return 0;
}
splat_subsystem_t *
splat_time_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_TIME_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_TIME_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_TIME;
splat_test_init(sub, SPLAT_TIME_TEST1_NAME, SPLAT_TIME_TEST1_DESC,
SPLAT_TIME_TEST1_ID, splat_time_test1);
splat_test_init(sub, SPLAT_TIME_TEST2_NAME, SPLAT_TIME_TEST2_DESC,
SPLAT_TIME_TEST2_ID, splat_time_test2);
return sub;
}
void
splat_time_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_TIME_TEST2_ID);
splat_test_fini(sub, SPLAT_TIME_TEST1_ID);
kfree(sub);
}
int
splat_time_id(void)
{
return SPLAT_SUBSYSTEM_TIME;
}
-355
View File
@@ -1,355 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Vnode Tests.
*/
#include <sys/vnode.h>
#include "splat-internal.h"
#define SPLAT_VNODE_NAME "vnode"
#define SPLAT_VNODE_DESC "Kernel Vnode Tests"
#define SPLAT_VNODE_TEST1_ID 0x0901
#define SPLAT_VNODE_TEST1_NAME "vn_open"
#define SPLAT_VNODE_TEST1_DESC "Vn_open Test"
#define SPLAT_VNODE_TEST2_ID 0x0902
#define SPLAT_VNODE_TEST2_NAME "vn_openat"
#define SPLAT_VNODE_TEST2_DESC "Vn_openat Test"
#define SPLAT_VNODE_TEST3_ID 0x0903
#define SPLAT_VNODE_TEST3_NAME "vn_rdwr"
#define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test"
#define SPLAT_VNODE_TEST5_ID 0x0905
#define SPLAT_VNODE_TEST5_NAME "vn_getattr"
#define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test"
#define SPLAT_VNODE_TEST6_ID 0x0906
#define SPLAT_VNODE_TEST6_NAME "vn_sync"
#define SPLAT_VNODE_TEST6_DESC "Vn_sync Test"
#define SPLAT_VNODE_TEST_FILE "/etc/fstab"
#define SPLAT_VNODE_TEST_FILE_AT "etc/fstab"
#define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp"
#define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1"
#define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2"
static int
splat_vnode_user_cmd(struct file *file, void *arg,
char *name, char *cmd)
{
char sh_path[] = "/bin/sh";
char *argv[] = { sh_path,
"-c",
cmd,
NULL };
char *envp[] = { "HOME=/",
"TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
int rc;
rc = call_usermodehelper(sh_path, argv, envp, UMH_WAIT_PROC);
if (rc) {
splat_vprint(file, name,
"Failed command: %s %s %s (%d)\n",
argv[0], argv[1], cmd, rc);
return -EPERM;
}
return 0;
}
static int
splat_vnode_unlink_all(struct file *file, void *arg, char *name)
{
char *cmds[] = { "rm -f " SPLAT_VNODE_TEST_FILE_RW,
"rm -f " SPLAT_VNODE_TEST_FILE_RW1,
"rm -f " SPLAT_VNODE_TEST_FILE_RW2,
NULL };
int i = 0, rc = 0;
while (cmds[i] != NULL) {
if ((rc = splat_vnode_user_cmd(file, arg, name, cmds[i])))
return rc;
i++;
}
return rc;
}
static int
splat_vnode_test1(struct file *file, void *arg)
{
vnode_t *vp;
int rc;
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE,
FREAD, 0644, &vp, 0, 0))) {
splat_vprint(file, SPLAT_VNODE_TEST1_NAME,
"Failed to vn_open test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
return -rc;
}
rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST1_NAME,
"Failed to vn_close test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
return -rc;
}
splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed "
"and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE);
return -rc;
} /* splat_vnode_test1() */
static int
splat_vnode_test2(struct file *file, void *arg)
{
vnode_t *vp;
int rc;
if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE,
FREAD, 0644, &vp, 0, 0, rootdir, 0))) {
splat_vprint(file, SPLAT_VNODE_TEST2_NAME,
"Failed to vn_openat test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
return -rc;
}
rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST2_NAME,
"Failed to vn_close test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
return -rc;
}
splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed "
"and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE);
return -rc;
} /* splat_vnode_test2() */
static int
splat_vnode_test3(struct file *file, void *arg)
{
vnode_t *vp;
char buf1[32] = "SPL VNode Interface Test File\n";
char buf2[32] = "";
int rc;
if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST3_NAME)))
return rc;
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE,
FWRITE | FREAD | FCREAT | FEXCL,
0644, &vp, 0, 0))) {
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
"Failed to vn_open test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
return -rc;
}
rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0,
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
"Failed vn_rdwr write of test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
goto out;
}
rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0,
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
"Failed vn_rdwr read of test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
goto out;
}
if (strncmp(buf1, buf2, strlen(buf1))) {
rc = EINVAL;
splat_vprint(file, SPLAT_VNODE_TEST3_NAME,
"Failed strncmp data written does not match "
"data read\nWrote: %sRead: %s\n", buf1, buf2);
goto out;
}
rc = 0;
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1);
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2);
splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and "
"read expected data pattern to test file: %s\n",
SPLAT_VNODE_TEST_FILE_RW);
out:
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
return -rc;
} /* splat_vnode_test3() */
static int
splat_vnode_test5(struct file *file, void *arg)
{
vnode_t *vp;
vattr_t vap;
int rc;
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE,
FREAD, 0644, &vp, 0, 0))) {
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
"Failed to vn_open test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
return -rc;
}
rc = VOP_GETATTR(vp, &vap, 0, 0, NULL);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
"Failed to vn_getattr test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE, rc);
goto out;
}
if (vap.va_type != VREG) {
rc = EINVAL;
splat_vprint(file, SPLAT_VNODE_TEST5_NAME,
"Failed expected regular file type "
"(%d != VREG): %s (%d)\n", vap.va_type,
SPLAT_VNODE_TEST_FILE, rc);
goto out;
}
splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully "
"vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE);
out:
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
return -rc;
} /* splat_vnode_test5() */
static int
splat_vnode_test6(struct file *file, void *arg)
{
vnode_t *vp;
char buf[32] = "SPL VNode Interface Test File\n";
int rc;
if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST6_NAME)))
return rc;
if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE,
FWRITE | FCREAT | FEXCL, 0644, &vp, 0, 0))) {
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
"Failed to vn_open test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
return -rc;
}
rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0,
UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
"Failed vn_rdwr write of test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
goto out;
}
rc = vn_fsync(vp, 0, 0, 0);
if (rc) {
splat_vprint(file, SPLAT_VNODE_TEST6_NAME,
"Failed vn_fsync of test file: %s (%d)\n",
SPLAT_VNODE_TEST_FILE_RW, rc);
goto out;
}
rc = 0;
splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully "
"fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW);
out:
VOP_CLOSE(vp, 0, 0, 0, 0, 0);
return -rc;
} /* splat_vnode_test6() */
splat_subsystem_t *
splat_vnode_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_VNODE;
splat_test_init(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC,
SPLAT_VNODE_TEST1_ID, splat_vnode_test1);
splat_test_init(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC,
SPLAT_VNODE_TEST2_ID, splat_vnode_test2);
splat_test_init(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC,
SPLAT_VNODE_TEST3_ID, splat_vnode_test3);
splat_test_init(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC,
SPLAT_VNODE_TEST5_ID, splat_vnode_test5);
splat_test_init(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC,
SPLAT_VNODE_TEST6_ID, splat_vnode_test6);
return sub;
} /* splat_vnode_init() */
void
splat_vnode_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_VNODE_TEST6_ID);
splat_test_fini(sub, SPLAT_VNODE_TEST5_ID);
splat_test_fini(sub, SPLAT_VNODE_TEST3_ID);
splat_test_fini(sub, SPLAT_VNODE_TEST2_ID);
splat_test_fini(sub, SPLAT_VNODE_TEST1_ID);
kfree(sub);
} /* splat_vnode_fini() */
int
splat_vnode_id(void)
{
return SPLAT_SUBSYSTEM_VNODE;
} /* splat_vnode_id() */
-166
View File
@@ -1,166 +0,0 @@
/*
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
* Copyright (C) 2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
* For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* The SPL is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************
* Solaris Porting LAyer Tests (SPLAT) Zlib Compression Tests.
*/
#include <sys/zmod.h>
#include <sys/random.h>
#include <sys/kmem.h>
#include <sys/vmem.h>
#include "splat-internal.h"
#define SPLAT_ZLIB_NAME "zlib"
#define SPLAT_ZLIB_DESC "Zlib Compression Tests"
#define SPLAT_ZLIB_TEST1_ID 0x0f01
#define SPLAT_ZLIB_TEST1_NAME "compress/uncompress"
#define SPLAT_ZLIB_TEST1_DESC "Compress/Uncompress Test"
#define BUFFER_SIZE (128 * 1024)
static int
splat_zlib_test1_check(struct file *file, void *src, void *dst, void *chk,
int level)
{
size_t dst_len = BUFFER_SIZE;
size_t chk_len = BUFFER_SIZE;
int rc;
memset(dst, 0, BUFFER_SIZE);
memset(chk, 0, BUFFER_SIZE);
rc = z_compress_level(dst, &dst_len, src, BUFFER_SIZE, level);
if (rc != Z_OK) {
splat_vprint(file, SPLAT_ZLIB_TEST1_NAME,
"Failed level %d z_compress_level(), %d\n", level, rc);
return -EINVAL;
}
rc = z_uncompress(chk, &chk_len, dst, dst_len);
if (rc != Z_OK) {
splat_vprint(file, SPLAT_ZLIB_TEST1_NAME,
"Failed level %d z_uncompress(), %d\n", level, rc);
return -EINVAL;
}
rc = memcmp(src, chk, BUFFER_SIZE);
if (rc) {
splat_vprint(file, SPLAT_ZLIB_TEST1_NAME,
"Failed level %d memcmp()), %d\n", level, rc);
return -EINVAL;
}
splat_vprint(file, SPLAT_ZLIB_TEST1_NAME,
"Passed level %d, compressed %d bytes to %d bytes\n",
level, BUFFER_SIZE, (int)dst_len);
return 0;
}
/*
* Compress a buffer, uncompress the newly compressed buffer, then
* compare it to the original. Do this for all 9 compression levels.
*/
static int
splat_zlib_test1(struct file *file, void *arg)
{
void *src = NULL, *dst = NULL, *chk = NULL;
int i, rc, level;
src = vmalloc(BUFFER_SIZE);
if (src == NULL) {
rc = -ENOMEM;
goto out;
}
dst = vmalloc(BUFFER_SIZE);
if (dst == NULL) {
rc = -ENOMEM;
goto out;
}
chk = vmalloc(BUFFER_SIZE);
if (chk == NULL) {
rc = -ENOMEM;
goto out;
}
/* Source buffer is a repeating 1024 byte random pattern. */
random_get_pseudo_bytes(src, sizeof(uint8_t) * 1024);
for (i = 1; i < 128; i++)
memcpy(src + (i * 1024), src, 1024);
for (level = 1; level <= 9; level++)
if ((rc = splat_zlib_test1_check(file, src, dst, chk, level)))
break;
out:
if (src)
vfree(src);
if (dst)
vfree(dst);
if (chk)
vfree(chk);
return rc;
}
splat_subsystem_t *
splat_zlib_init(void)
{
splat_subsystem_t *sub;
sub = kmalloc(sizeof(*sub), GFP_KERNEL);
if (sub == NULL)
return NULL;
memset(sub, 0, sizeof(*sub));
strncpy(sub->desc.name, SPLAT_ZLIB_NAME, SPLAT_NAME_SIZE);
strncpy(sub->desc.desc, SPLAT_ZLIB_DESC, SPLAT_DESC_SIZE);
INIT_LIST_HEAD(&sub->subsystem_list);
INIT_LIST_HEAD(&sub->test_list);
spin_lock_init(&sub->test_lock);
sub->desc.id = SPLAT_SUBSYSTEM_ZLIB;
splat_test_init(sub, SPLAT_ZLIB_TEST1_NAME, SPLAT_ZLIB_TEST1_DESC,
SPLAT_ZLIB_TEST1_ID, splat_zlib_test1);
return sub;
}
void
splat_zlib_fini(splat_subsystem_t *sub)
{
ASSERT(sub);
splat_test_fini(sub, SPLAT_ZLIB_TEST1_ID);
kfree(sub);
}
int
splat_zlib_id(void) {
return SPLAT_SUBSYSTEM_ZLIB;
}