]> git.proxmox.com Git - mirror_spl-debian.git/blobdiff - module/splat/splat-ctl.c
New upstream version 0.7.2
[mirror_spl-debian.git] / module / splat / splat-ctl.c
index 1591039807614620b003b70f74cbb1b31277d3d0..8452f1363904e9a6b661ae922a7fe6e3a3e3dec0 100644 (file)
@@ -1,91 +1,98 @@
-/*
- *  This file is part of the SPL: Solaris Porting Layer.
- *
- *  Copyright (c) 2008 Lawrence Livermore National Security, LLC.
- *  Produced at Lawrence Livermore National Laboratory
- *  Written by:
- *          Brian Behlendorf <behlendorf1@llnl.gov>,
- *          Herb Wartens <wartens2@llnl.gov>,
- *          Jim Garlick <garlick@llnl.gov>
+/*****************************************************************************\
+ *  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 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 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.
  *
- *  This is distributed in the hope that it will be useful, but WITHOUT
+ *  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 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.
+ *  with the SPL.  If not, see <http://www.gnu.org/licenses/>.
+ *****************************************************************************
+ *  Solaris Porting LAyer Tests (SPLAT) Test Control Interface.
  *
- * 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.
- */
-
+ *  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 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);
+       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;
 
-        return 0;
+       splat_print(file, "%s\n", spl_version);
+
+       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);
 
+       mutex_destroy(&info->info_lock);
        vfree(info->info_buffer);
        kfree(info);
 
@@ -100,10 +107,10 @@ splat_buffer_clear(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
        ASSERT(info);
        ASSERT(info->info_buffer);
 
-       spin_lock(&info->info_lock);
+       mutex_enter(&info->info_lock);
        memset(info->info_buffer, 0, info->info_size);
        info->info_head = info->info_buffer;
-       spin_unlock(&info->info_lock);
+       mutex_exit(&info->info_lock);
 
        return 0;
 }
@@ -118,7 +125,7 @@ splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
        ASSERT(info);
        ASSERT(info->info_buffer);
 
-       spin_lock(&info->info_lock);
+       mutex_enter(&info->info_lock);
        if (kcfg->cfg_arg1 > 0) {
 
                size = kcfg->cfg_arg1;
@@ -143,7 +150,7 @@ splat_buffer_size(struct file *file, splat_cfg_t *kcfg, unsigned long arg)
        if (copy_to_user((struct splat_cfg_t __user *)arg, kcfg, sizeof(*kcfg)))
                rc = -EFAULT;
 out:
-       spin_unlock(&info->info_lock);
+       mutex_exit(&info->info_lock);
 
        return rc;
 }
@@ -439,20 +446,15 @@ splat_ioctl_cmd(struct file *file, unsigned int cmd, unsigned long arg)
        return rc;
 }
 
-static int
-splat_ioctl(struct inode *inode, struct file *file,
-           unsigned int cmd, unsigned long arg)
+static long
+splat_unlocked_ioctl(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, cmd, arg);
@@ -474,7 +476,7 @@ splat_ioctl(struct inode *inode, struct file *file,
 static long
 splat_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       return splat_ioctl(NULL, file, cmd, arg);
+       return splat_unlocked_ioctl(file, cmd, arg);
 }
 #endif /* CONFIG_COMPAT */
 
@@ -485,17 +487,13 @@ splat_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 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);
+       mutex_enter(&info->info_lock);
 
        /* Write beyond EOF */
        if (*ppos >= info->info_size) {
@@ -515,24 +513,20 @@ static ssize_t splat_write(struct file *file, const char __user *buf,
        *ppos += count;
        rc = count;
 out:
-       spin_unlock(&info->info_lock);
+       mutex_exit(&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);
+       mutex_enter(&info->info_lock);
 
        /* Read beyond EOF */
        if (*ppos >= info->info_size)
@@ -550,23 +544,19 @@ static ssize_t splat_read(struct file *file, char __user *buf,
        *ppos += count;
        rc = count;
 out:
-       spin_unlock(&info->info_lock);
+       mutex_exit(&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);
+       mutex_enter(&info->info_lock);
 
        switch (origin) {
        case 0: /* SEEK_SET - No-op just do it */
@@ -585,7 +575,7 @@ static loff_t splat_seek(struct file *file, loff_t offset, int origin)
                rc = offset;
        }
 
-       spin_unlock(&info->info_lock);
+       mutex_exit(&info->info_lock);
 
        return rc;
 }
@@ -594,7 +584,7 @@ static struct file_operations splat_fops = {
        .owner          = THIS_MODULE,
        .open           = splat_open,
        .release        = splat_release,
-       .ioctl          = splat_ioctl,
+       .unlocked_ioctl = splat_unlocked_ioctl,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = splat_compat_ioctl,
 #endif
@@ -603,16 +593,98 @@ static struct file_operations splat_fops = {
        .llseek         = splat_seek,
 };
 
-static struct cdev splat_cdev = {
-       .owner  =       THIS_MODULE,
-       .kobj   =       { .name = SPLAT_NAME, },
+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)
 {
-       dev_t dev;
-       int rc;
+       int error;
 
        spin_lock_init(&splat_module_lock);
        INIT_LIST_HEAD(&splat_module_list);
@@ -631,52 +703,27 @@ splat_init(void)
        SPLAT_SUBSYSTEM_INIT(list);
        SPLAT_SUBSYSTEM_INIT(generic);
        SPLAT_SUBSYSTEM_INIT(cred);
-
-       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;
+       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);
        }
 
-       /* Support for udev make driver info available in sysfs */
-        splat_class = spl_class_create(THIS_MODULE, "splat");
-       if (IS_ERR(splat_class)) {
-               rc = PTR_ERR(splat_class);
-               printk(KERN_ERR "SPLAT: Error creating splat class, %d\n", rc);
-               cdev_del(&splat_cdev);
-               unregister_chrdev_region(dev, SPLAT_MINORS);
-               goto error;
-       }
-
-       splat_device = spl_device_create(splat_class, NULL,
-                                        MKDEV(SPLAT_MAJOR, 0),
-                                        NULL, SPLAT_NAME);
-
-       printk(KERN_INFO "SPLAT: Loaded Solaris Porting LAyer "
-              "Tests v%s\n", SPL_META_VERSION);
-       return 0;
-error:
-       printk(KERN_ERR "SPLAT: Error registering splat device, %d\n", rc);
-       return rc;
+       return (error);
 }
 
-static void
+static void __exit
 splat_fini(void)
 {
-       dev_t dev = MKDEV(SPLAT_MAJOR, 0);
-
-        spl_device_destroy(splat_class, splat_device, dev);
-        spl_class_destroy(splat_class);
-        cdev_del(&splat_cdev);
-        unregister_chrdev_region(dev, SPLAT_MINORS);
+       misc_deregister(&splat_misc);
 
+       SPLAT_SUBSYSTEM_FINI(linux);
+       SPLAT_SUBSYSTEM_FINI(zlib);
        SPLAT_SUBSYSTEM_FINI(cred);
        SPLAT_SUBSYSTEM_FINI(generic);
        SPLAT_SUBSYSTEM_FINI(list);
@@ -693,13 +740,14 @@ splat_fini(void)
        SPLAT_SUBSYSTEM_FINI(kmem);
 
        ASSERT(list_empty(&splat_module_list));
-       printk(KERN_INFO "SPLAT: Unloaded Solaris Porting LAyer "
-              "Tests v%s\n", SPL_META_VERSION);
+       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_AUTHOR("Lawrence Livermore National Labs");
 MODULE_DESCRIPTION("Solaris Porting LAyer Tests");
-MODULE_LICENSE("GPL");
+MODULE_AUTHOR(SPL_META_AUTHOR);
+MODULE_LICENSE(SPL_META_LICENSE);
+MODULE_VERSION(SPL_META_VERSION "-" SPL_META_RELEASE);