diff --git a/autoconf/spl-build.m4 b/autoconf/spl-build.m4 index 33465b6bb7be..e13c5b8752cc 100644 --- a/autoconf/spl-build.m4 +++ b/autoconf/spl-build.m4 @@ -1,685 +1,686 @@ AC_DEFUN([SPL_AC_KERNEL], [ ver=`uname -r` AC_ARG_WITH([linux], AS_HELP_STRING([--with-linux=PATH], [Path to kernel source]), [kernelsrc="$withval"; kernelbuild="$withval"]) AC_ARG_WITH([linux-obj], AS_HELP_STRING([--with-linux-obj=PATH], [Path to kernel build objects]), [kernelbuild="$withval"]) AC_MSG_CHECKING([kernel source directory]) if test -z "$kernelsrc"; then kernelbuild= sourcelink=/lib/modules/${ver}/source buildlink=/lib/modules/${ver}/build if test -e $sourcelink; then kernelsrc=`(cd $sourcelink; /bin/pwd)` fi if test -e $buildlink; then kernelbuild=`(cd $buildlink; /bin/pwd)` fi if test -z "$kernelsrc"; then kernelsrc=$kernelbuild fi if test -z "$kernelsrc" -o -z "$kernelbuild"; then AC_MSG_RESULT([Not found]) AC_MSG_ERROR([ *** Please specify the location of the kernel source *** with the '--with-kernel=PATH' option]) fi fi AC_MSG_RESULT([$kernelsrc]) AC_MSG_CHECKING([kernel build directory]) AC_MSG_RESULT([$kernelbuild]) AC_MSG_CHECKING([kernel source version]) if test -r $kernelbuild/include/linux/version.h && fgrep -q UTS_RELEASE $kernelbuild/include/linux/version.h; then kernsrcver=`(echo "#include "; echo "kernsrcver=UTS_RELEASE") | cpp -I $kernelbuild/include | grep "^kernsrcver=" | cut -d \" -f 2` elif test -r $kernelbuild/include/linux/utsrelease.h && fgrep -q UTS_RELEASE $kernelbuild/include/linux/utsrelease.h; then kernsrcver=`(echo "#include "; echo "kernsrcver=UTS_RELEASE") | cpp -I $kernelbuild/include | grep "^kernsrcver=" | cut -d \" -f 2` fi if test -z "$kernsrcver"; then AC_MSG_RESULT([Not found]) AC_MSG_ERROR([ *** Cannot determine the version of the linux kernel source. *** Please prepare the kernel before running this script]) fi AC_MSG_RESULT([$kernsrcver]) kmoduledir=${INSTALL_MOD_PATH}/lib/modules/$kernsrcver LINUX=${kernelsrc} LINUX_OBJ=${kernelbuild} AC_SUBST(LINUX) AC_SUBST(LINUX_OBJ) AC_SUBST(kmoduledir) ]) AC_DEFUN([SPL_AC_DEBUG], [ AC_MSG_CHECKING([whether debugging is enabled]) AC_ARG_ENABLE( [debug], AS_HELP_STRING([--enable-debug], [Enable generic debug support (default off)]), [ case "$enableval" in yes) spl_ac_debug=yes ;; no) spl_ac_debug=no ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$enableval" for --enable-debug]) ;; esac ] ) if test "$spl_ac_debug" = yes; then AC_MSG_RESULT([yes]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG" else AC_MSG_RESULT([no]) AC_DEFINE([NDEBUG], [1], [Define to 1 to disable debug tracing]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DNDEBUG" fi ]) AC_DEFUN([SPL_AC_DEBUG_KMEM], [ AC_MSG_CHECKING([whether kmem debugging is enabled]) AC_ARG_ENABLE( [debug-kmem], AS_HELP_STRING([--enable-debug-kmem], [Enable kmem debug support (default off)]), [ case "$enableval" in yes) spl_ac_debug_kmem=yes ;; no) spl_ac_debug_kmem=no ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$enableval" for --enable-debug-kmem]) ;; esac ] ) if test "$spl_ac_debug_kmem" = yes; then AC_MSG_RESULT([yes]) AC_DEFINE([DEBUG_KMEM], [1], [Define to 1 to enable kmem debugging]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG_KMEM" else AC_MSG_RESULT([no]) fi ]) AC_DEFUN([SPL_AC_DEBUG_MUTEX], [ AC_MSG_CHECKING([whether mutex debugging is enabled]) AC_ARG_ENABLE( [debug-mutex], AS_HELP_STRING([--enable-debug-mutex], [Enable mutex debug support (default off)]), [ case "$enableval" in yes) spl_ac_debug_mutex=yes ;; no) spl_ac_debug_mutex=no ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$enableval" for --enable-debug-mutex]) ;; esac ] ) if test "$spl_ac_debug_mutex" = yes; then AC_MSG_RESULT([yes]) AC_DEFINE([DEBUG_MUTEX], [1], [Define to 1 to enable mutex debugging]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG_MUTEX" else AC_MSG_RESULT([no]) fi ]) AC_DEFUN([SPL_AC_DEBUG_KSTAT], [ AC_MSG_CHECKING([whether kstat debugging is enabled]) AC_ARG_ENABLE( [debug-kstat], AS_HELP_STRING([--enable-debug-kstat], [Enable kstat debug support (default off)]), [ case "$enableval" in yes) spl_ac_debug_kstat=yes ;; no) spl_ac_debug_kstat=no ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$enableval" for --enable-debug-kstat]) ;; esac ] ) if test "$spl_ac_debug_kstat" = yes; then AC_MSG_RESULT([yes]) AC_DEFINE([DEBUG_KSTAT], [1], [Define to 1 to enable kstat debugging]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG_KSTAT" else AC_MSG_RESULT([no]) fi ]) AC_DEFUN([SPL_AC_DEBUG_CALLB], [ AC_MSG_CHECKING([whether callb debugging is enabled]) AC_ARG_ENABLE( [debug-callb], AS_HELP_STRING([--enable-debug-callb], [Enable callb debug support (default off)]), [ case "$enableval" in yes) spl_ac_debug_callb=yes ;; no) spl_ac_debug_callb=no ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$enableval" for --enable-debug-callb]) ;; esac ] ) if test "$spl_ac_debug_callb" = yes; then AC_MSG_RESULT([yes]) AC_DEFINE([DEBUG_CALLB], [1], [Define to 1 to enable callb debugging]) KERNELCPPFLAGS="${KERNELCPPFLAGS} -DDEBUG_CALLB" else AC_MSG_RESULT([no]) fi ]) dnl # dnl # SPL_LINUX_CONFTEST dnl # AC_DEFUN([SPL_LINUX_CONFTEST], [ cat >conftest.c <<_ACEOF $1 _ACEOF ]) dnl # dnl # SPL_LANG_PROGRAM(C)([PROLOGUE], [BODY]) dnl # m4_define([SPL_LANG_PROGRAM], [ $1 int main (void) { dnl Do *not* indent the following line: there may be CPP directives. dnl Don't move the `;' right after for the same reason. $2 ; return 0; } ]) dnl # dnl # SPL_LINUX_COMPILE_IFELSE / like AC_COMPILE_IFELSE dnl # AC_DEFUN([SPL_LINUX_COMPILE_IFELSE], [ m4_ifvaln([$1], [SPL_LINUX_CONFTEST([$1])])dnl rm -f build/conftest.o build/conftest.mod.c build/conftest.ko build/Makefile echo "obj-m := conftest.o" >build/Makefile dnl AS_IF([AC_TRY_COMMAND(cp conftest.c build && make [$2] CC="$CC" -f $PWD/build/Makefile LINUXINCLUDE="-Iinclude -include include/linux/autoconf.h" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM SUBDIRS=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], AS_IF([AC_TRY_COMMAND(cp conftest.c build && make [$2] CC="$CC" LINUXINCLUDE="-Iinclude -include include/linux/autoconf.h" -o tmp_include_depends -o scripts -o include/config/MARKER -C $LINUX_OBJ EXTRA_CFLAGS="-Werror-implicit-function-declaration $EXTRA_KCFLAGS" $ARCH_UM M=$PWD/build) >/dev/null && AC_TRY_COMMAND([$3])], [$4], [_AC_MSG_LOG_CONFTEST m4_ifvaln([$5],[$5])dnl])dnl rm -f build/conftest.o build/conftest.mod.c build/conftest.mod.o build/conftest.ko m4_ifval([$1], [build/conftest.c conftest.c])[]dnl ]) dnl # dnl # SPL_LINUX_TRY_COMPILE like AC_TRY_COMPILE dnl # AC_DEFUN([SPL_LINUX_TRY_COMPILE], [SPL_LINUX_COMPILE_IFELSE( [AC_LANG_SOURCE([SPL_LANG_PROGRAM([[$1]], [[$2]])])], [modules], [test -s build/conftest.o], [$3], [$4]) ]) dnl # dnl # SPL_LINUX_CONFIG dnl # AC_DEFUN([SPL_LINUX_CONFIG], [AC_MSG_CHECKING([whether Linux was built with CONFIG_$1]) SPL_LINUX_TRY_COMPILE([ #ifndef AUTOCONF_INCLUDED #include #endif ],[ #ifndef CONFIG_$1 #error CONFIG_$1 not #defined #endif ],[ AC_MSG_RESULT([yes]) $2 ],[ AC_MSG_RESULT([no]) $3 ]) ]) dnl # dnl # SPL_CHECK_SYMBOL_EXPORT dnl # check symbol exported or not dnl # AC_DEFUN([SPL_CHECK_SYMBOL_EXPORT], [AC_MSG_CHECKING([whether symbol $1 is exported]) grep -q -E '[[[:space:]]]$1[[[:space:]]]' $LINUX/Module.symvers 2>/dev/null rc=$? if test $rc -ne 0; then export=0 for file in $2; do grep -q -E "EXPORT_SYMBOL.*($1)" "$LINUX/$file" 2>/dev/null rc=$? if test $rc -eq 0; then export=1 break; fi done if test $export -eq 0; then AC_MSG_RESULT([no]) $4 else AC_MSG_RESULT([yes]) $3 fi else AC_MSG_RESULT([yes]) $3 fi ]) dnl # dnl # SPL_CHECK_HEADER dnl # check whether header exists and define HAVE_$2_HEADER dnl # AC_DEFUN([SPL_CHECK_HEADER], [AC_MSG_CHECKING([whether header $1 exists]) SPL_LINUX_TRY_COMPILE([ #include <$1> ],[ return 0; ],[ AC_DEFINE(HAVE_$2_HEADER, 1, [$1 exists]) AC_MSG_RESULT(yes) $3 ],[ AC_MSG_RESULT(no) $4 ]) ]) dnl # dnl # 2.6.24 API change, dnl # check if uintptr_t typedef is defined dnl # AC_DEFUN([SPL_AC_TYPE_UINTPTR_T], [AC_MSG_CHECKING([whether kernel defines uintptr_t]) SPL_LINUX_TRY_COMPILE([ #include ],[ uintptr_t *ptr; ],[ AC_MSG_RESULT([yes]) AC_DEFINE(HAVE_UINTPTR_T, 1, [kernel defines uintptr_t]) ],[ AC_MSG_RESULT([no]) ]) ]) dnl # dnl # 2.6.19 API change, dnl # panic_notifier_list use atomic_notifier operations dnl # AC_DEFUN([SPL_AC_ATOMIC_PANIC_NOTIFIER], [AC_MSG_CHECKING([whether panic_notifier_list is atomic]) SPL_LINUX_TRY_COMPILE([ #include #include ],[ struct atomic_notifier_head panic_notifier_list; ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_ATOMIC_PANIC_NOTIFIER, 1, [panic_notifier_list is atomic]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.20 API change, dnl # INIT_WORK use 2 args and not store data inside dnl # AC_DEFUN([SPL_AC_3ARGS_INIT_WORK], [AC_MSG_CHECKING([whether INIT_WORK wants 3 args]) SPL_LINUX_TRY_COMPILE([ #include ],[ struct work_struct work; INIT_WORK(&work, NULL, NULL); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_3ARGS_INIT_WORK, 1, [INIT_WORK wants 3 args]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.21 API change, dnl # 'register_sysctl_table' use only one argument instead of two dnl # AC_DEFUN([SPL_AC_2ARGS_REGISTER_SYSCTL], [AC_MSG_CHECKING([whether register_sysctl_table() wants 2 args]) SPL_LINUX_TRY_COMPILE([ #include ],[ return register_sysctl_table(NULL,0); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_2ARGS_REGISTER_SYSCTL, 1, [register_sysctl_table() wants 2 args]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.23 API change dnl # Old set_shrinker API replaced with register_shrinker dnl # AC_DEFUN([SPL_AC_SET_SHRINKER], [ AC_MSG_CHECKING([whether set_shrinker() available]) SPL_LINUX_TRY_COMPILE([ #include ],[ return set_shrinker(DEFAULT_SEEKS, NULL); ],[ AC_MSG_RESULT([yes]) AC_DEFINE(HAVE_SET_SHRINKER, 1, [set_shrinker() available]) ],[ AC_MSG_RESULT([no]) ]) ]) dnl # dnl # 2.6.25 API change, dnl # struct path entry added to struct nameidata dnl # AC_DEFUN([SPL_AC_PATH_IN_NAMEIDATA], [AC_MSG_CHECKING([whether struct path used in struct nameidata]) SPL_LINUX_TRY_COMPILE([ #include ],[ struct nameidata nd; nd.path.mnt = NULL; nd.path.dentry = NULL; ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_PATH_IN_NAMEIDATA, 1, [struct path used in struct nameidata]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # Custom SPL patch may export this system it is not required dnl # AC_DEFUN([SPL_AC_TASK_CURR], [ SPL_CHECK_SYMBOL_EXPORT([task_curr], [kernel/sched.c], [AC_DEFINE(HAVE_TASK_CURR, 1, [task_curr() exported])], []) ]) dnl # dnl # 2.6.19 API change, dnl # Use CTL_UNNUMBERED when binary sysctl is not required dnl # AC_DEFUN([SPL_AC_CTL_UNNUMBERED], [AC_MSG_CHECKING([whether unnumbered sysctl support exists]) SPL_LINUX_TRY_COMPILE([ #include ],[ #ifndef CTL_UNNUMBERED #error CTL_UNNUMBERED undefined #endif ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_CTL_UNNUMBERED, 1, [unnumbered sysctl support exists]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.16 API change. dnl # Check if 'fls64()' is available dnl # AC_DEFUN([SPL_AC_FLS64], [AC_MSG_CHECKING([whether fls64() is available]) SPL_LINUX_TRY_COMPILE([ #include ],[ return fls64(0); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_FLS64, 1, [fls64() is available]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.18 API change, check whether device_create() is available. dnl # Device_create() was introduced in 2.6.18 and depricated dnl # class_device_create() which was fully removed in 2.6.26. dnl # AC_DEFUN([SPL_AC_DEVICE_CREATE], [ SPL_CHECK_SYMBOL_EXPORT( [device_create], [drivers/base/core.c], [AC_DEFINE(HAVE_DEVICE_CREATE, 1, [device_create() is available])], []) ]) dnl # dnl # 2.6.13 API change, check whether class_device_create() is available. dnl # Class_device_create() was introduced in 2.6.13 and depricated dnl # class_simple_device_add() which was fully removed in 2.6.13. dnl # AC_DEFUN([SPL_AC_CLASS_DEVICE_CREATE], [ SPL_CHECK_SYMBOL_EXPORT( [class_device_create], [drivers/base/class.c], [AC_DEFINE(HAVE_CLASS_DEVICE_CREATE, 1, [class_device_create() is available])], []) ]) dnl # dnl # 2.6.26 API change, set_normalized_timespec() is exported. dnl # AC_DEFUN([SPL_AC_SET_NORMALIZED_TIMESPEC_EXPORT], [ SPL_CHECK_SYMBOL_EXPORT( [set_normalized_timespec], [kernel/time.c], [AC_DEFINE(HAVE_SET_NORMALIZED_TIMESPEC_EXPORT, 1, [set_normalized_timespec() is available as export])], []) ]) dnl # dnl # 2.6.16 API change, set_normalize_timespec() moved to time.c dnl # previously it was available in time.h as an inline. dnl # AC_DEFUN([SPL_AC_SET_NORMALIZED_TIMESPEC_INLINE], [ AC_MSG_CHECKING([whether set_normalized_timespec() is an inline]) SPL_LINUX_TRY_COMPILE([ #include - ],[ void set_normalized_timespec(struct timespec *ts, - time_t sec, long nsec); - ],[ + time_t sec, long nsec) { } + ], + [], + [ AC_MSG_RESULT(no) ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SET_NORMALIZED_TIMESPEC_INLINE, 1, [set_normalized_timespec() is available as inline]) ]) ]) dnl # dnl # 2.6.18 API change, dnl # timespec_sub() inline function available in linux/time.h dnl # AC_DEFUN([SPL_AC_TIMESPEC_SUB], [ AC_MSG_CHECKING([whether timespec_sub() is available]) SPL_LINUX_TRY_COMPILE([ #include ],[ struct timespec a, b, c = { 0 }; c = timespec_sub(a, b); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_TIMESPEC_SUB, 1, [timespec_sub() is available]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.19 API change, dnl # check if init_utsname() is available in linux/utsname.h dnl # AC_DEFUN([SPL_AC_INIT_UTSNAME], [ AC_MSG_CHECKING([whether init_utsname() is available]) SPL_LINUX_TRY_COMPILE([ #include ],[ struct new_utsname *a = init_utsname(); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_INIT_UTSNAME, 1, [init_utsname() is available]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.26 API change, dnl # definition of struct fdtable relocated to linux/fdtable.h dnl # AC_DEFUN([SPL_AC_FDTABLE_HEADER], [ SPL_CHECK_HEADER([linux/fdtable.h], [FDTABLE], [], []) ]) dnl # dnl # 2.6.14 API change, dnl # check whether 'files_fdtable()' exists dnl # AC_DEFUN([SPL_AC_FILES_FDTABLE], [ AC_MSG_CHECKING([whether files_fdtable() is available]) SPL_LINUX_TRY_COMPILE([ #include #include #ifdef HAVE_FDTABLE_HEADER #include #endif ],[ struct files_struct *files = current->files; struct fdtable *fdt = files_fdtable(files); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_FILES_FDTABLE, 1, [files_fdtable() is available]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.18 API change, dnl # added linux/uaccess.h dnl # AC_DEFUN([SPL_AC_UACCESS_HEADER], [ SPL_CHECK_HEADER([linux/uaccess.h], [UACCESS], [], []) ]) dnl # dnl # 2.6.12 API change, dnl # check whether 'kmalloc_node()' is available. dnl # AC_DEFUN([SPL_AC_KMALLOC_NODE], [ AC_MSG_CHECKING([whether kmalloc_node() is available]) SPL_LINUX_TRY_COMPILE([ #include ],[ void *a = kmalloc_node(1, GFP_KERNEL, 0); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_KMALLOC_NODE, 1, [kmalloc_node() is available]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.9 API change, dnl # check whether 'monotonic_clock()' is available it may dnl # be available for some archs but not others. dnl # AC_DEFUN([SPL_AC_MONOTONIC_CLOCK], [ SPL_CHECK_SYMBOL_EXPORT( [monotonic_clock], [], [AC_DEFINE(HAVE_MONOTONIC_CLOCK, 1, [monotonic_clock() is available])], []) ]) dnl # dnl # 2.6.16 API change, dnl # check whether 'struct inode' has i_mutex dnl # AC_DEFUN([SPL_AC_INODE_I_MUTEX], [ AC_MSG_CHECKING([whether struct inode has i_mutex]) SPL_LINUX_TRY_COMPILE([ #include #include ],[ struct inode i; mutex_init(&i.i_mutex); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_INODE_I_MUTEX, 1, [struct inode has i_mutex]) ],[ AC_MSG_RESULT(no) ]) ]) dnl # dnl # 2.6.14 API change, dnl # check whether 'div64_64()' is available dnl # AC_DEFUN([SPL_AC_DIV64_64], [ AC_MSG_CHECKING([whether div64_64() is available]) SPL_LINUX_TRY_COMPILE([ #include ],[ uint64_t i = div64_64(1ULL, 1ULL); ],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_DIV64_64, 1, [div64_64() is available]) ],[ AC_MSG_RESULT(no) ]) ]) diff --git a/include/spl-device.h b/include/spl-device.h index 2bbd299b8e57..d18aedf072b1 100644 --- a/include/spl-device.h +++ b/include/spl-device.h @@ -1,53 +1,59 @@ #ifndef _SPL_DEVICE_H #define _SPL_DEVICE_H #include /* * Preferred API from 2.6.18 to 2.6.26+ */ #ifdef HAVE_DEVICE_CREATE typedef struct class spl_class; +typedef struct device spl_device; #define spl_class_create(mod, name) class_create(mod, name) #define spl_class_destroy(cls) class_destroy(cls) -#define spl_device_create(cls, parent, devt, device, fmt, args...) \ +#define spl_device_create(cls, parent, devt, device, fmt, args...) \ device_create(cls, parent, devt, fmt, ## args) -#define spl_device_destroy(cls, devt) device_destroy(cls, devt) +#define spl_device_destroy(cls, cls_dev, devt) \ + device_destroy(cls, devt) /* * Preferred API from 2.6.13 to 2.6.17 * Depricated in 2.6.18 * Removed in 2.6.26 */ #else #ifdef HAVE_CLASS_DEVICE_CREATE typedef struct class spl_class; +typedef struct class_device spl_device; #define spl_class_create(mod, name) class_create(mod, name) #define spl_class_destroy(cls) class_destroy(cls) -#define spl_device_create(cls, parent, devt, device, fmt, args...) \ - class_device_create(cls, parent, devt, device, fmt, ## args) -#define spl_device_destroy(cls, devt) class_device_destroy(cls, devt) +#define spl_device_create(cls, parent, devt, device, fmt, args...) \ + class_device_create(cls, devt, device, fmt, ## args) +#define spl_device_destroy(cls, cls_dev, devt) \ + class_device_unregister(cls_dev) /* * Prefered API from 2.6.0 to 2.6.12 * Depricated in 2.6.13 * Removed in 2.6.13 */ #else /* Legacy API */ typedef struct class_simple spl_class; +typedef struct class_device spl_class_device; #define spl_class_create(mod, name) class_simple_create(mod, name) #define spl_class_destroy(cls) class_simple_destroy(cls) -#define spl_device_create(cls, parent, devt, device, fmt, args...) \ +#define spl_device_create(cls, parent, devt, device, fmt, args...) \ class_simple_device_add(cls, devt, device, fmt, ## args) -#define spl_device_destroy(cls, devt) class_simple_device_remove(devt) +#define spl_device_destroy(cls, cls_dev, devt) \ + class_simple_device_remove(devt) #endif #endif #endif /* _SPL_DEVICE_H */ diff --git a/include/sys/sunddi.h b/include/sys/sunddi.h index 764ae3820fe9..bbfd412daf29 100644 --- a/include/sys/sunddi.h +++ b/include/sys/sunddi.h @@ -1,215 +1,216 @@ /* * This file is part of the SPL: Solaris Porting Layer. * * Copyright (c) 2008 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * UCRL-CODE-235197 * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SPL_SUNDDI_H #define _SPL_SUNDDI_H #include #include #include #include #include #include #include #include #include typedef int ddi_devid_t; typedef enum { DDI_INFO_DEVT2DEVINFO = 0, DDI_INFO_DEVT2INSTANCE = 1 } ddi_info_cmd_t; typedef enum { DDI_ATTACH = 0, DDI_RESUME = 1, DDI_PM_RESUME = 2 } ddi_attach_cmd_t; typedef enum { DDI_DETACH = 0, DDI_SUSPEND = 1, DDI_PM_SUSPEND = 2, DDI_HOTPLUG_DETACH = 3 } ddi_detach_cmd_t; typedef enum { DDI_RESET_FORCE = 0 } ddi_reset_cmd_t; typedef enum { PROP_LEN = 0, PROP_LEN_AND_VAL_BUF = 1, PROP_LEN_AND_VAL_ALLOC = 2, PROP_EXISTS = 3 } ddi_prop_op_t; typedef void *devmap_cookie_t; typedef struct as { uchar_t a_flags; } as_t; typedef struct pollhead { struct polldat *ph_list; } pollhead_t; typedef struct dev_info { kmutex_t di_lock; struct dev_ops *di_ops; struct cdev *di_cdev; spl_class *di_class; + spl_device *di_device; major_t di_major; minor_t di_minor; dev_t di_dev; unsigned di_minors; struct list_head di_list; } dev_info_t; typedef struct cb_ops { int (*cb_open)(dev_t *devp, int flag, int otyp, cred_t *credp); int (*cb_close)(dev_t dev, int flag, int otyp, cred_t *credp); int (*cb_strategy)(void *bp); int (*cb_print)(dev_t dev, char *str); int (*cb_dump)(dev_t dev, caddr_t addr, daddr_t blkno, int nblk); int (*cb_read)(dev_t dev, struct uio *uiop, cred_t *credp); int (*cb_write)(dev_t dev, struct uio *uiop, cred_t *credp); int (*cb_ioctl)(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp); int (*cb_devmap)(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len, size_t *maplen, uint_t model); int (*cb_mmap)(dev_t dev, off_t off, int prot); int (*cb_segmap)(dev_t dev, off_t off, struct as *asp, caddr_t *addrp, off_t len, unsigned int prot, unsigned int maxprot, unsigned int flags, cred_t *credp); int (*cb_chpoll)(dev_t dev, short events, int anyyet, short *reventsp, struct pollhead **phpp); int (*cb_prop_op)(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags, char *name, caddr_t valuep, int *length); struct streamtab *cb_str; int cb_flag; int cb_rev; int (*cb_aread)(dev_t dev, struct aio_req *aio, cred_t *credp); int (*cb_awrite)(dev_t dev, struct aio_req *aio, cred_t *credp); } cb_ops_t; typedef struct dev_ops { int devo_rev; int devo_refcnt; int (*devo_getinfo)(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); int (*devo_identify)(dev_info_t *dip); int (*devo_probe)(dev_info_t *dip); int (*devo_attach)(dev_info_t *dip, ddi_attach_cmd_t cmd); int (*devo_detach)(dev_info_t *dip, ddi_detach_cmd_t cmd); int (*devo_reset)(dev_info_t *dip, ddi_reset_cmd_t cmd); struct cb_ops *devo_cb_ops; struct bus_ops *devo_bus_ops; int (*devo_power)(dev_info_t *dip, int component, int level); } dev_ops_t; typedef struct mod_ops { int (*modm_install)(void); int (*modm_remove)(void); int (*modm_info)(void); } mod_ops_t; typedef struct modldrv { struct mod_ops *drv_modops; char *drv_linkinfo; struct dev_ops *drv_dev_ops; struct dev_info *drv_dev_info; } modldrv_t; #define MODREV_1 1 #define D_NEW 0x000 #define D_MP 0x020 #define D_64BIT 0x200 #define DEVO_REV 3 #define CB_REV 1 #define DDI_SUCCESS 0 #define DDI_FAILURE -1 #define DDI_PSEUDO "ddi_pseudo" #define nodev NULL #define nochpoll NULL #define nulldev NULL #define mod_driverops NULL #define ddi_prop_op NULL #define getminor(x) (x) #define getmajor(x) (x) #define ddi_driver_major(di) getmajor(di->di_dev) #define DDI_DEV_T_NONE ((dev_t)-1) #define DDI_DEV_T_ANY ((dev_t)-2) #define DDI_MAJOR_T_UNKNOWN ((major_t)0) #define DDI_PROP_DONTPASS 0x0001 #define DDI_PROP_CANSLEEP 0x0002 #define ddi_prop_lookup_string(x1,x2,x3,x4,x5) (*x5 = NULL) #define ddi_prop_free(x) (void)0 #define ddi_root_node() (void)0 #define mod_install(x) 0 #define mod_remove(x) 0 extern int __ddi_create_minor_node(dev_info_t *dip, char *name, int spec_type, minor_t minor_num, char *node_type, int flag, struct module *mod); extern void __ddi_remove_minor_node(dev_info_t *dip, char *name); extern int __mod_install(struct modlinkage *modlp); extern int __mod_remove(struct modlinkage *modlp); static __inline__ void ddi_report_dev(dev_info_t *d) { } static __inline__ void ddi_prop_remove_all(dev_info_t *dip) { } static __inline__ int ddi_create_minor_node(dev_info_t *di, char *name, int spec_type, minor_t minor_num, char *node_type, int flag) { return __ddi_create_minor_node(di, name, spec_type, minor_num, node_type, flag, THIS_MODULE); } #undef mod_install #undef mod_remove #define ddi_remove_minor_node __ddi_remove_minor_node #define mod_install __mod_install #define mod_remove __mod_remove #endif /* SPL_SUNDDI_H */ diff --git a/modules/spl/spl-module.c b/modules/spl/spl-module.c index 19c5db2cd317..18f57fdf1bad 100644 --- a/modules/spl/spl-module.c +++ b/modules/spl/spl-module.c @@ -1,352 +1,354 @@ /* * This file is part of the SPL: Solaris Porting Layer. * * Copyright (c) 2008 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * UCRL-CODE-235197 * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #ifdef DEBUG_SUBSYSTEM #undef DEBUG_SUBSYSTEM #endif #define DEBUG_SUBSYSTEM S_MODULE static spinlock_t dev_info_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(dev_info_list); static struct dev_info * get_dev_info(dev_t dev) { struct dev_info *di; spin_lock(&dev_info_lock); list_for_each_entry(di, &dev_info_list, di_list) if (di->di_dev == dev) goto out; di = NULL; out: spin_unlock(&dev_info_lock); return di; } static int mod_generic_ioctl(struct inode *ino, struct file *filp, unsigned int cmd, unsigned long arg) { struct dev_info *di; int rc, flag = 0, rvalp = 0; cred_t *cr = NULL; di = get_dev_info(MKDEV(imajor(ino), iminor(ino))); if (di == NULL) return EINVAL; rc = di->di_ops->devo_cb_ops->cb_ioctl(di->di_dev, (int)cmd,(intptr_t)arg, flag, cr, &rvalp); return rc; } int __ddi_create_minor_node(dev_info_t *di, char *name, int spec_type, minor_t minor_num, char *node_type, int flag, struct module *mod) { struct cdev *cdev; struct dev_ops *dev_ops; struct cb_ops *cb_ops; struct file_operations *fops; int rc; ENTRY; ASSERT(spec_type == S_IFCHR); ASSERT(minor_num < di->di_minors); ASSERT(!strcmp(node_type, DDI_PSEUDO)); ASSERT(flag == 0); fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); if (fops == NULL) RETURN(DDI_FAILURE); cdev = cdev_alloc(); if (cdev == NULL) { kfree(fops); RETURN(DDI_FAILURE); } cdev->ops = fops; mutex_enter(&di->di_lock); dev_ops = di->di_ops; ASSERT(dev_ops); cb_ops = di->di_ops->devo_cb_ops; ASSERT(cb_ops); /* Setup the fops to cb_ops mapping */ fops->owner = mod; if (cb_ops->cb_ioctl) fops->ioctl = mod_generic_ioctl; #if 0 if (cb_ops->cb_open) fops->open = mod_generic_open; if (cb_ops->cb_close) fops->release = mod_generic_close; if (cb_ops->cb_read) fops->read = mod_generic_read; if (cb_ops->cb_write) fops->write = mod_generic_write; #endif /* XXX: Currently unsupported operations */ ASSERT(cb_ops->cb_open == NULL); ASSERT(cb_ops->cb_close == NULL); ASSERT(cb_ops->cb_read == NULL); ASSERT(cb_ops->cb_write == NULL); ASSERT(cb_ops->cb_strategy == NULL); ASSERT(cb_ops->cb_print == NULL); ASSERT(cb_ops->cb_dump == NULL); ASSERT(cb_ops->cb_devmap == NULL); ASSERT(cb_ops->cb_mmap == NULL); ASSERT(cb_ops->cb_segmap == NULL); ASSERT(cb_ops->cb_chpoll == NULL); ASSERT(cb_ops->cb_prop_op == NULL); ASSERT(cb_ops->cb_str == NULL); ASSERT(cb_ops->cb_aread == NULL); ASSERT(cb_ops->cb_awrite == NULL); di->di_minor = minor_num; di->di_dev = MKDEV(di->di_major, di->di_minor); rc = cdev_add(cdev, di->di_dev, 1); if (rc) { CERROR("Error adding cdev, %d\n", rc); kfree(fops); cdev_del(cdev); mutex_exit(&di->di_lock); RETURN(DDI_FAILURE); } di->di_class = spl_class_create(THIS_MODULE, name); if (IS_ERR(di->di_class)) { rc = PTR_ERR(di->di_class); CERROR("Error creating %s class, %d\n", name, rc); kfree(fops); cdev_del(di->di_cdev); mutex_exit(&di->di_lock); RETURN(DDI_FAILURE); } /* Do not append a 0 to devices with minor nums of 0 */ if (di->di_minor == 0) { - spl_device_create(di->di_class, NULL, di->di_dev, - NULL, "%s", name); + di->di_device = spl_device_create(di->di_class, NULL, + di->di_dev, NULL, + "%s", name); } else { - spl_device_create(di->di_class, NULL, di->di_dev, - NULL, "%s%d", name, di->di_minor); + di->di_device = spl_device_create(di->di_class, NULL, + di->di_dev, NULL, + "%s%d", name, di->di_minor); } di->di_cdev = cdev; spin_lock(&dev_info_lock); list_add(&di->di_list, &dev_info_list); spin_unlock(&dev_info_lock); mutex_exit(&di->di_lock); RETURN(DDI_SUCCESS); } EXPORT_SYMBOL(__ddi_create_minor_node); static void __ddi_remove_minor_node_locked(dev_info_t *di, char *name) { if (di->di_class) { - spl_device_destroy(di->di_class, di->di_dev); + spl_device_destroy(di->di_class, di->di_device, di->di_dev); spl_class_destroy(di->di_class); di->di_class = NULL; di->di_dev = 0; } if (di->di_cdev) { cdev_del(di->di_cdev); di->di_cdev = NULL; } spin_lock(&dev_info_lock); list_del_init(&di->di_list); spin_unlock(&dev_info_lock); } void __ddi_remove_minor_node(dev_info_t *di, char *name) { ENTRY; mutex_enter(&di->di_lock); __ddi_remove_minor_node_locked(di, name); mutex_exit(&di->di_lock); EXIT; } EXPORT_SYMBOL(ddi_remove_minor_node); #if 0 static int mod_generic_open(struct inode *, struct file *) { open(dev_t *devp, int flag, int otyp, cred_t *credp); } static int mod_generic_close(struct inode *, struct file *) { close(dev_t dev, int flag, int otyp, cred_t *credp); } static ssize_t mod_generic_read(struct file *, char __user *, size_t, loff_t *) { read(dev_t dev, struct uio *uiop, cred_t *credp); } static ssize_t mod_generic_write(struct file *, const char __user *, size_t, loff_t *) { write(dev_t dev, struct uio *uiop, cred_t *credp); } #endif static struct dev_info * dev_info_alloc(major_t major, minor_t minors, struct dev_ops *ops) { struct dev_info *di; di = kmalloc(sizeof(struct dev_info), GFP_KERNEL); if (di == NULL) return NULL; mutex_init(&di->di_lock, NULL, MUTEX_DEFAULT, NULL); INIT_LIST_HEAD(&di->di_list); di->di_ops = ops; di->di_class = NULL; di->di_cdev = NULL; di->di_major = major; di->di_minor = 0; di->di_minors = minors; di->di_dev = 0; return di; } static void dev_info_free(struct dev_info *di) { mutex_enter(&di->di_lock); __ddi_remove_minor_node_locked(di, NULL); mutex_exit(&di->di_lock); mutex_destroy(&di->di_lock); kfree(di); } int __mod_install(struct modlinkage *modlp) { struct modldrv *drv = modlp->ml_modldrv; struct dev_info *di; int rc; ENTRY; di = dev_info_alloc(modlp->ml_major, modlp->ml_minors, drv->drv_dev_ops); if (di == NULL) RETURN(ENOMEM); /* XXX: Really we need to be calling devo_probe if it's available * and then calling devo_attach for each device discovered. However * for now we just call it once and let the app sort it out. */ rc = drv->drv_dev_ops->devo_attach(di, DDI_ATTACH); if (rc != DDI_SUCCESS) { dev_info_free(di); RETURN(rc); } drv->drv_dev_info = di; RETURN(DDI_SUCCESS); } EXPORT_SYMBOL(__mod_install); int __mod_remove(struct modlinkage *modlp) { struct modldrv *drv = modlp->ml_modldrv; struct dev_info *di = drv->drv_dev_info; int rc; ENTRY; rc = drv->drv_dev_ops->devo_detach(di, DDI_DETACH); if (rc != DDI_SUCCESS) RETURN(rc); dev_info_free(di); drv->drv_dev_info = NULL; RETURN(DDI_SUCCESS); } EXPORT_SYMBOL(__mod_remove); int ldi_ident_from_mod(struct modlinkage *modlp, ldi_ident_t *lip) { ldi_ident_t li; ENTRY; ASSERT(modlp); ASSERT(lip); li = kmalloc(sizeof(struct ldi_ident), GFP_KERNEL); if (li == NULL) RETURN(ENOMEM); li->li_dev = MKDEV(modlp->ml_major, 0); *lip = li; RETURN(0); } EXPORT_SYMBOL(ldi_ident_from_mod); void ldi_ident_release(ldi_ident_t lip) { ENTRY; ASSERT(lip); kfree(lip); EXIT; } EXPORT_SYMBOL(ldi_ident_release); diff --git a/modules/splat/splat-ctl.c b/modules/splat/splat-ctl.c index 0c8b673d9606..b82c85f9c91d 100644 --- a/modules/splat/splat-ctl.c +++ b/modules/splat/splat-ctl.c @@ -1,676 +1,678 @@ /* * This file is part of the SPL: Solaris Porting Layer. * * Copyright (c) 2008 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * UCRL-CODE-235197 * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * My intent is to create a loadable 'splat' (Solaris Porting LAyer * Tests) module which can be used as an access point to run * in kernel Solaris ABI regression tests. This provides a * nice mechanism to validate the shim primates are working properly. * * The basic design is the splat module is that it is constructed of * various splat_* source files each of which contains regression tests. * For example the splat_linux_kmem.c file contains tests for validating * kmem correctness. When the splat module is loaded splat_*_init() * will be called for each subsystems tests, similarly splat_*_fini() is * called when the splat module is removed. Each test can then be * run by making an ioctl() call from a userspace control application * to pick the subsystem and test which should be run. */ #include "splat-internal.h" static spl_class *splat_class; +static spl_device *splat_device; static struct list_head splat_module_list; static spinlock_t splat_module_lock; static int splat_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); splat_info_t *info; if (minor >= SPLAT_MINORS) return -ENXIO; info = (splat_info_t *)kmalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) return -ENOMEM; spin_lock_init(&info->info_lock); info->info_size = SPLAT_INFO_BUFFER_SIZE; info->info_buffer = (char *)vmalloc(SPLAT_INFO_BUFFER_SIZE); if (info->info_buffer == NULL) { kfree(info); return -ENOMEM; } info->info_head = info->info_buffer; file->private_data = (void *)info; return 0; } static int splat_release(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); splat_info_t *info = (splat_info_t *)file->private_data; if (minor >= SPLAT_MINORS) return -ENXIO; ASSERT(info); ASSERT(info->info_buffer); vfree(info->info_buffer); kfree(info); return 0; } static int splat_buffer_clear(struct file *file, splat_cfg_t *kcfg, unsigned long arg) { splat_info_t *info = (splat_info_t *)file->private_data; ASSERT(info); ASSERT(info->info_buffer); spin_lock(&info->info_lock); memset(info->info_buffer, 0, info->info_size); info->info_head = info->info_buffer; spin_unlock(&info->info_lock); return 0; } static int splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg) { splat_info_t *info = (splat_info_t *)file->private_data; char *buf; int min, size, rc = 0; ASSERT(info); ASSERT(info->info_buffer); spin_lock(&info->info_lock); if (kcfg->cfg_arg1 > 0) { size = kcfg->cfg_arg1; buf = (char *)vmalloc(size); if (buf == NULL) { rc = -ENOMEM; goto out; } /* Zero fill and truncate contents when coping buffer */ min = ((size < info->info_size) ? size : info->info_size); memset(buf, 0, size); memcpy(buf, info->info_buffer, min); vfree(info->info_buffer); info->info_size = size; info->info_buffer = buf; info->info_head = info->info_buffer; } kcfg->cfg_rc1 = info->info_size; if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) rc = -EFAULT; out: spin_unlock(&info->info_lock); return rc; } static splat_subsystem_t * splat_subsystem_find(int id) { splat_subsystem_t *sub; spin_lock(&splat_module_lock); list_for_each_entry(sub, &splat_module_list, subsystem_list) { if (id == sub->desc.id) { spin_unlock(&splat_module_lock); return sub; } } spin_unlock(&splat_module_lock); return NULL; } static int splat_subsystem_count(splat_cfg_t *kcfg, unsigned long arg) { splat_subsystem_t *sub; int i = 0; spin_lock(&splat_module_lock); list_for_each_entry(sub, &splat_module_list, subsystem_list) i++; spin_unlock(&splat_module_lock); kcfg->cfg_rc1 = i; if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) return -EFAULT; return 0; } static int splat_subsystem_list(splat_cfg_t *kcfg, unsigned long arg) { splat_subsystem_t *sub; splat_cfg_t *tmp; int size, i = 0; /* Structure will be sized large enough for N subsystem entries * which is passed in by the caller. On exit the number of * entries filled in with valid subsystems will be stored in * cfg_rc1. If the caller does not provide enough entries * for all subsystems we will truncate the list to avoid overrun. */ size = sizeof(*tmp) + kcfg->cfg_data.splat_subsystems.size * sizeof(splat_user_t); tmp = kmalloc(size, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; /* Local 'tmp' is used as the structure copied back to user space */ memset(tmp, 0, size); memcpy(tmp, kcfg, sizeof(*kcfg)); spin_lock(&splat_module_lock); list_for_each_entry(sub, &splat_module_list, subsystem_list) { strncpy(tmp->cfg_data.splat_subsystems.descs[i].name, sub->desc.name, SPLAT_NAME_SIZE); strncpy(tmp->cfg_data.splat_subsystems.descs[i].desc, sub->desc.desc, SPLAT_DESC_SIZE); tmp->cfg_data.splat_subsystems.descs[i].id = sub->desc.id; /* Truncate list if we are about to overrun alloc'ed memory */ if ((i++) == kcfg->cfg_data.splat_subsystems.size) break; } spin_unlock(&splat_module_lock); tmp->cfg_rc1 = i; if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) { kfree(tmp); return -EFAULT; } kfree(tmp); return 0; } static int splat_test_count(splat_cfg_t *kcfg, unsigned long arg) { splat_subsystem_t *sub; splat_test_t *test; int i = 0; /* Subsystem ID passed as arg1 */ sub = splat_subsystem_find(kcfg->cfg_arg1); if (sub == NULL) return -EINVAL; spin_lock(&(sub->test_lock)); list_for_each_entry(test, &(sub->test_list), test_list) i++; spin_unlock(&(sub->test_lock)); kcfg->cfg_rc1 = i; if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg))) return -EFAULT; return 0; } static int splat_test_list(splat_cfg_t *kcfg, unsigned long arg) { splat_subsystem_t *sub; splat_test_t *test; splat_cfg_t *tmp; int size, i = 0; /* Subsystem ID passed as arg1 */ sub = splat_subsystem_find(kcfg->cfg_arg1); if (sub == NULL) return -EINVAL; /* Structure will be sized large enough for N test entries * which is passed in by the caller. On exit the number of * entries filled in with valid tests will be stored in * cfg_rc1. If the caller does not provide enough entries * for all tests we will truncate the list to avoid overrun. */ size = sizeof(*tmp)+kcfg->cfg_data.splat_tests.size*sizeof(splat_user_t); tmp = kmalloc(size, GFP_KERNEL); if (tmp == NULL) return -ENOMEM; /* Local 'tmp' is used as the structure copied back to user space */ memset(tmp, 0, size); memcpy(tmp, kcfg, sizeof(*kcfg)); spin_lock(&(sub->test_lock)); list_for_each_entry(test, &(sub->test_list), test_list) { strncpy(tmp->cfg_data.splat_tests.descs[i].name, test->desc.name, SPLAT_NAME_SIZE); strncpy(tmp->cfg_data.splat_tests.descs[i].desc, test->desc.desc, SPLAT_DESC_SIZE); tmp->cfg_data.splat_tests.descs[i].id = test->desc.id; /* Truncate list if we are about to overrun alloc'ed memory */ if ((i++) == kcfg->cfg_data.splat_tests.size) break; } spin_unlock(&(sub->test_lock)); tmp->cfg_rc1 = i; if (copy_to_user((struct splat_cfg_t __user *)arg, tmp, size)) { kfree(tmp); return -EFAULT; } kfree(tmp); return 0; } static int splat_validate(struct file *file, splat_subsystem_t *sub, int cmd, void *arg) { splat_test_t *test; spin_lock(&(sub->test_lock)); list_for_each_entry(test, &(sub->test_list), test_list) { if (test->desc.id == cmd) { spin_unlock(&(sub->test_lock)); return test->test(file, arg); } } spin_unlock(&(sub->test_lock)); return -EINVAL; } static int splat_ioctl_cfg(struct file *file, unsigned long arg) { splat_cfg_t kcfg; int rc = 0; if (copy_from_user(&kcfg, (splat_cfg_t *)arg, sizeof(kcfg))) return -EFAULT; if (kcfg.cfg_magic != SPLAT_CFG_MAGIC) { splat_print(file, "Bad config magic 0x%x != 0x%x\n", kcfg.cfg_magic, SPLAT_CFG_MAGIC); return -EINVAL; } switch (kcfg.cfg_cmd) { case SPLAT_CFG_BUFFER_CLEAR: /* cfg_arg1 - Unused * cfg_rc1 - Unused */ rc = splat_buffer_clear(file, &kcfg, arg); break; case SPLAT_CFG_BUFFER_SIZE: /* cfg_arg1 - 0 - query size; >0 resize * cfg_rc1 - Set to current buffer size */ rc = splat_buffer_size(file, &kcfg, arg); break; case SPLAT_CFG_SUBSYSTEM_COUNT: /* cfg_arg1 - Unused * cfg_rc1 - Set to number of subsystems */ rc = splat_subsystem_count(&kcfg, arg); break; case SPLAT_CFG_SUBSYSTEM_LIST: /* cfg_arg1 - Unused * cfg_rc1 - Set to number of subsystems * cfg_data.splat_subsystems - Populated with subsystems */ rc = splat_subsystem_list(&kcfg, arg); break; case SPLAT_CFG_TEST_COUNT: /* cfg_arg1 - Set to a target subsystem * cfg_rc1 - Set to number of tests */ rc = splat_test_count(&kcfg, arg); break; case SPLAT_CFG_TEST_LIST: /* cfg_arg1 - Set to a target subsystem * cfg_rc1 - Set to number of tests * cfg_data.splat_subsystems - Populated with tests */ rc = splat_test_list(&kcfg, arg); break; default: splat_print(file, "Bad config command %d\n", kcfg.cfg_cmd); rc = -EINVAL; break; } return rc; } static int splat_ioctl_cmd(struct file *file, unsigned long arg) { splat_subsystem_t *sub; splat_cmd_t kcmd; int rc = -EINVAL; void *data = NULL; if (copy_from_user(&kcmd, (splat_cfg_t *)arg, sizeof(kcmd))) return -EFAULT; if (kcmd.cmd_magic != SPLAT_CMD_MAGIC) { splat_print(file, "Bad command magic 0x%x != 0x%x\n", kcmd.cmd_magic, SPLAT_CFG_MAGIC); return -EINVAL; } /* Allocate memory for any opaque data the caller needed to pass on */ if (kcmd.cmd_data_size > 0) { data = (void *)kmalloc(kcmd.cmd_data_size, GFP_KERNEL); if (data == NULL) return -ENOMEM; if (copy_from_user(data, (void *)(arg + offsetof(splat_cmd_t, cmd_data_str)), kcmd.cmd_data_size)) { kfree(data); return -EFAULT; } } sub = splat_subsystem_find(kcmd.cmd_subsystem); if (sub != NULL) rc = splat_validate(file, sub, kcmd.cmd_test, data); else rc = -EINVAL; if (data != NULL) kfree(data); return rc; } static int splat_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = iminor(file->f_dentry->d_inode); int rc = 0; /* Ignore tty ioctls */ if ((cmd & 0xffffff00) == ((int)'T') << 8) return -ENOTTY; if (minor >= SPLAT_MINORS) return -ENXIO; switch (cmd) { case SPLAT_CFG: rc = splat_ioctl_cfg(file, arg); break; case SPLAT_CMD: rc = splat_ioctl_cmd(file, arg); break; default: splat_print(file, "Bad ioctl command %d\n", cmd); rc = -EINVAL; break; } return rc; } /* I'm not sure why you would want to write in to this buffer from * user space since its principle use is to pass test status info * back to the user space, but I don't see any reason to prevent it. */ static ssize_t splat_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned int minor = iminor(file->f_dentry->d_inode); splat_info_t *info = (splat_info_t *)file->private_data; int rc = 0; if (minor >= SPLAT_MINORS) return -ENXIO; ASSERT(info); ASSERT(info->info_buffer); spin_lock(&info->info_lock); /* Write beyond EOF */ if (*ppos >= info->info_size) { rc = -EFBIG; goto out; } /* Resize count if beyond EOF */ if (*ppos + count > info->info_size) count = info->info_size - *ppos; if (copy_from_user(info->info_buffer, buf, count)) { rc = -EFAULT; goto out; } *ppos += count; rc = count; out: spin_unlock(&info->info_lock); return rc; } static ssize_t splat_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned int minor = iminor(file->f_dentry->d_inode); splat_info_t *info = (splat_info_t *)file->private_data; int rc = 0; if (minor >= SPLAT_MINORS) return -ENXIO; ASSERT(info); ASSERT(info->info_buffer); spin_lock(&info->info_lock); /* Read beyond EOF */ if (*ppos >= info->info_size) goto out; /* Resize count if beyond EOF */ if (*ppos + count > info->info_size) count = info->info_size - *ppos; if (copy_to_user(buf, info->info_buffer + *ppos, count)) { rc = -EFAULT; goto out; } *ppos += count; rc = count; out: spin_unlock(&info->info_lock); return rc; } static loff_t splat_seek(struct file *file, loff_t offset, int origin) { unsigned int minor = iminor(file->f_dentry->d_inode); splat_info_t *info = (splat_info_t *)file->private_data; int rc = -EINVAL; if (minor >= SPLAT_MINORS) return -ENXIO; ASSERT(info); ASSERT(info->info_buffer); spin_lock(&info->info_lock); switch (origin) { case 0: /* SEEK_SET - No-op just do it */ break; case 1: /* SEEK_CUR - Seek from current */ offset = file->f_pos + offset; break; case 2: /* SEEK_END - Seek from end */ offset = info->info_size + offset; break; } if (offset >= 0) { file->f_pos = offset; file->f_version = 0; rc = offset; } spin_unlock(&info->info_lock); return rc; } static struct file_operations splat_fops = { .owner = THIS_MODULE, .open = splat_open, .release = splat_release, .ioctl = splat_ioctl, .read = splat_read, .write = splat_write, .llseek = splat_seek, }; static struct cdev splat_cdev = { .owner = THIS_MODULE, .kobj = { .name = SPLAT_NAME, }, }; static int __init splat_init(void) { dev_t dev; int rc; spin_lock_init(&splat_module_lock); INIT_LIST_HEAD(&splat_module_list); SPLAT_SUBSYSTEM_INIT(kmem); SPLAT_SUBSYSTEM_INIT(taskq); SPLAT_SUBSYSTEM_INIT(krng); SPLAT_SUBSYSTEM_INIT(mutex); SPLAT_SUBSYSTEM_INIT(condvar); SPLAT_SUBSYSTEM_INIT(thread); SPLAT_SUBSYSTEM_INIT(rwlock); SPLAT_SUBSYSTEM_INIT(time); SPLAT_SUBSYSTEM_INIT(vnode); SPLAT_SUBSYSTEM_INIT(kobj); SPLAT_SUBSYSTEM_INIT(atomic); dev = MKDEV(SPLAT_MAJOR, 0); if ((rc = register_chrdev_region(dev, SPLAT_MINORS, SPLAT_NAME))) goto error; /* Support for registering a character driver */ cdev_init(&splat_cdev, &splat_fops); if ((rc = cdev_add(&splat_cdev, dev, SPLAT_MINORS))) { printk(KERN_ERR "splat: Error adding cdev, %d\n", rc); kobject_put(&splat_cdev.kobj); unregister_chrdev_region(dev, SPLAT_MINORS); goto error; } /* Support for udev make driver info available in sysfs */ splat_class = spl_class_create(THIS_MODULE, "splat"); if (IS_ERR(splat_class)) { rc = PTR_ERR(splat_class); printk(KERN_ERR "splat: Error creating splat class, %d\n", rc); cdev_del(&splat_cdev); unregister_chrdev_region(dev, SPLAT_MINORS); goto error; } - spl_device_create(splat_class, NULL, MKDEV(SPLAT_MAJOR, 0), - NULL, SPLAT_NAME); + splat_device = spl_device_create(splat_class, NULL, + MKDEV(SPLAT_MAJOR, 0), + NULL, SPLAT_NAME); printk(KERN_INFO "splat: Loaded Solaris Porting LAyer " "Tests v%s\n", VERSION); return 0; error: printk(KERN_ERR "splat: Error registering splat device, %d\n", rc); return rc; } static void splat_fini(void) { dev_t dev = MKDEV(SPLAT_MAJOR, 0); - spl_device_destroy(splat_class, dev); + spl_device_destroy(splat_class, splat_device, dev); spl_class_destroy(splat_class); cdev_del(&splat_cdev); unregister_chrdev_region(dev, SPLAT_MINORS); SPLAT_SUBSYSTEM_FINI(atomic); SPLAT_SUBSYSTEM_FINI(kobj); SPLAT_SUBSYSTEM_FINI(vnode); SPLAT_SUBSYSTEM_FINI(time); SPLAT_SUBSYSTEM_FINI(rwlock); SPLAT_SUBSYSTEM_FINI(thread); SPLAT_SUBSYSTEM_FINI(condvar); SPLAT_SUBSYSTEM_FINI(mutex); SPLAT_SUBSYSTEM_FINI(krng); SPLAT_SUBSYSTEM_FINI(taskq); SPLAT_SUBSYSTEM_FINI(kmem); ASSERT(list_empty(&splat_module_list)); printk(KERN_INFO "splat: Unloaded Solaris Porting LAyer " "Tests v%s\n", VERSION); } module_init(splat_init); module_exit(splat_fini); MODULE_AUTHOR("Lawrence Livermore National Labs"); MODULE_DESCRIPTION("Solaris Porting LAyer Tests"); MODULE_LICENSE("GPL"); diff --git a/modules/splat/splat-vnode.c b/modules/splat/splat-vnode.c index c85b6165a8e7..413651dac1d9 100644 --- a/modules/splat/splat-vnode.c +++ b/modules/splat/splat-vnode.c @@ -1,531 +1,532 @@ /* * This file is part of the SPL: Solaris Porting Layer. * * Copyright (c) 2008 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * UCRL-CODE-235197 * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "splat-internal.h" #include #define SPLAT_SUBSYSTEM_VNODE 0x0900 #define SPLAT_VNODE_NAME "vnode" #define SPLAT_VNODE_DESC "Kernel Vnode Tests" #define SPLAT_VNODE_TEST1_ID 0x0901 #define SPLAT_VNODE_TEST1_NAME "vn_open" #define SPLAT_VNODE_TEST1_DESC "Vn_open Test" #define SPLAT_VNODE_TEST2_ID 0x0902 #define SPLAT_VNODE_TEST2_NAME "vn_openat" #define SPLAT_VNODE_TEST2_DESC "Vn_openat Test" #define SPLAT_VNODE_TEST3_ID 0x0903 #define SPLAT_VNODE_TEST3_NAME "vn_rdwr" #define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test" #define SPLAT_VNODE_TEST4_ID 0x0904 #define SPLAT_VNODE_TEST4_NAME "vn_rename" #define SPLAT_VNODE_TEST4_DESC "Vn_rename Test" #define SPLAT_VNODE_TEST5_ID 0x0905 #define SPLAT_VNODE_TEST5_NAME "vn_getattr" #define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test" #define SPLAT_VNODE_TEST6_ID 0x0906 #define SPLAT_VNODE_TEST6_NAME "vn_sync" #define SPLAT_VNODE_TEST6_DESC "Vn_sync Test" #define SPLAT_VNODE_TEST7_ID 0x0907 #define SPLAT_VNODE_TEST7_NAME "vn_getf" #define SPLAT_VNODE_TEST7_DESC "vn_getf/vn_releasef Test" #define SPLAT_VNODE_TEST_FILE "/etc/fstab" #define SPLAT_VNODE_TEST_FILE_AT "etc/fstab" #define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp" #define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1" #define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2" static int splat_vnode_test1(struct file *file, void *arg) { vnode_t *vp; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return rc; } rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Failed to vn_close test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return rc; } splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed " "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); return rc; } /* splat_vnode_test1() */ static int splat_vnode_test2(struct file *file, void *arg) { vnode_t *vp; int rc; if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0, rootdir, 0))) { splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Failed to vn_openat test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return rc; } rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Failed to vn_close test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return rc; } splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed " "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); return rc; } /* splat_vnode_test2() */ static int splat_vnode_test3(struct file *file, void *arg) { vnode_t *vp; char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return rc; } rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = -EINVAL; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " "read expected data pattern to test file: %s\n", SPLAT_VNODE_TEST_FILE_RW); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return rc; } /* splat_vnode_test3() */ static int splat_vnode_test4(struct file *file, void *arg) { vnode_t *vp; char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, rc); goto out; } rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, rc); goto out2; } VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); rc = vn_rename(SPLAT_VNODE_TEST_FILE_RW1,SPLAT_VNODE_TEST_FILE_RW2,0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rename " "%s -> %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2, rc); goto out; } if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, FREAD | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW2, rc); goto out; } rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW2, rc); goto out2; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out2; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Wrote to %s: %s", SPLAT_VNODE_TEST_FILE_RW1, buf1); splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Read from %s: %s", SPLAT_VNODE_TEST_FILE_RW2, buf2); splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Successfully renamed " "test file %s -> %s and verified data pattern\n", SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2); out2: VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); out: vn_remove(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, RMFILE); vn_remove(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, RMFILE); return rc; } /* splat_vnode_test4() */ static int splat_vnode_test5(struct file *file, void *arg) { vnode_t *vp; vattr_t vap; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return rc; } rc = VOP_GETATTR(vp, &vap, 0, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed to vn_getattr test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); goto out; } if (vap.va_type != VREG) { rc = -EINVAL; splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed expected regular file type " "(%d != VREG): %s (%d)\n", vap.va_type, SPLAT_VNODE_TEST_FILE, rc); goto out; } splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully " "vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); return rc; } /* splat_vnode_test5() */ static int splat_vnode_test6(struct file *file, void *arg) { vnode_t *vp; char buf[32] = "SPL VNode Interface Test File\n"; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, FWRITE | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return rc; } rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_fsync(vp, 0, 0, 0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed vn_fsync of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully " "fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); VN_RELE(vp); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return rc; } /* splat_vnode_test6() */ /* Basically a slightly modified version of sys_close() */ static int fd_uninstall(int fd) { struct file *fp; struct files_struct *files = current->files; +#ifdef HAVE_FILES_FDTABLE struct fdtable *fdt; spin_lock(&files->file_lock); -#ifdef HAVE_FILES_FDTABLE fdt = files_fdtable(files); if (fd >= fdt->max_fds) goto out_unlock; fp = fdt->fd[fd]; if (!fp) goto out_unlock; rcu_assign_pointer(fdt->fd[fd], NULL); FD_CLR(fd, fdt->close_on_exec); #else + spin_lock(&files->file_lock); if (fd >= files->max_fds) goto out_unlock; fp = files->fd[fd]; if (!fp) goto out_unlock; files->fd[fd] = NULL; FD_CLR(fd, files->close_on_exec); #endif /* Dropping the lock here exposes a minor race but it allows me * to use the existing kernel interfaces for this, and for a test * case I think that's reasonable. */ spin_unlock(&files->file_lock); put_unused_fd(fd); return 0; out_unlock: spin_unlock(&files->file_lock); return -EBADF; } /* fd_uninstall() */ static int splat_vnode_test7(struct file *file, void *arg) { char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; struct file *lfp; file_t *fp; int rc, fd; /* Prep work needed to test getf/releasef */ fd = get_unused_fd(); if (fd < 0) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to get unused fd (%d)\n", fd); return fd; } lfp = filp_open(SPLAT_VNODE_TEST_FILE_RW, O_RDWR|O_CREAT|O_EXCL, 0644); if (IS_ERR(lfp)) { put_unused_fd(fd); rc = PTR_ERR(lfp); splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to filp_open: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return rc; } /* Pair up the new fd and lfp in the current context, this allows * getf to lookup the file struct simply by the known open fd */ fd_install(fd, lfp); /* Actual getf()/releasef() test */ fp = vn_getf(fd); if (fp == NULL) { rc = -EINVAL; splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to getf fd %d: (%d)\n", fd, rc); goto out; } rc = vn_rdwr(UIO_WRITE, fp->f_vnode, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_rdwr(UIO_READ, fp->f_vnode, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc < 0) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = -EINVAL; splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " "read expected data pattern to test file: %s\n", SPLAT_VNODE_TEST_FILE_RW); out: vn_releasef(fd); fd_uninstall(fd); filp_close(lfp, 0); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return rc; } /* splat_vnode_test7() */ splat_subsystem_t * splat_vnode_init(void) { splat_subsystem_t *sub; sub = kmalloc(sizeof(*sub), GFP_KERNEL); if (sub == NULL) return NULL; memset(sub, 0, sizeof(*sub)); strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE); strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE); INIT_LIST_HEAD(&sub->subsystem_list); INIT_LIST_HEAD(&sub->test_list); spin_lock_init(&sub->test_lock); sub->desc.id = SPLAT_SUBSYSTEM_VNODE; SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC, SPLAT_VNODE_TEST1_ID, splat_vnode_test1); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC, SPLAT_VNODE_TEST2_ID, splat_vnode_test2); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC, SPLAT_VNODE_TEST3_ID, splat_vnode_test3); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST4_NAME, SPLAT_VNODE_TEST4_DESC, SPLAT_VNODE_TEST4_ID, splat_vnode_test4); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC, SPLAT_VNODE_TEST5_ID, splat_vnode_test5); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC, SPLAT_VNODE_TEST6_ID, splat_vnode_test6); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST7_NAME, SPLAT_VNODE_TEST7_DESC, SPLAT_VNODE_TEST7_ID, splat_vnode_test7); return sub; } /* splat_vnode_init() */ void splat_vnode_fini(splat_subsystem_t *sub) { ASSERT(sub); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST7_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST6_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST5_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST4_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST3_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST2_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST1_ID); kfree(sub); } /* splat_vnode_fini() */ int splat_vnode_id(void) { return SPLAT_SUBSYSTEM_VNODE; } /* splat_vnode_id() */