mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
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:
@@ -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
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
/*
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() */
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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() */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() */
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user