mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-11-18 10:21:01 +03:00
e4f1d29f89
stuff which only inclused the getf()/releasef() in to the vnode area where it will only really be used. These calls allow a user to grab an open file struct given only the known open fd for a particular user context. ZFS makes use of these, but they're a bit tricky to test from within the kernel since you already need the file open and know the fd. So we basically spook the system calls to setup the environment we need for the splat test case and verify given just the know fd we can get the file, create the needed vnode, and then use the vnode interface as usual to read and write from it. While I was hacking away I also noticed a NULL termination issue in the second kobj test case so I fixed that too. In fact, I fixed a few other things as well but all for the best! git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@51 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
679 lines
16 KiB
C
679 lines
16 KiB
C
/*
|
|
* My intent is to create a loadable 'splat' (solaris porting layer
|
|
* aggressive test) 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.
|
|
*
|
|
* Author: Brian Behlendorf
|
|
*/
|
|
|
|
#include "splat-internal.h"
|
|
#include <config.h>
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
#include <linux/devfs_fs_kernel.h>
|
|
#endif
|
|
|
|
#include <linux/cdev.h>
|
|
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
static struct class_simple *splat_class;
|
|
#else
|
|
static struct class *splat_class;
|
|
#endif
|
|
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 = "splatctl", },
|
|
};
|
|
|
|
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);
|
|
|
|
dev = MKDEV(SPLAT_MAJOR, 0);
|
|
if ((rc = register_chrdev_region(dev, SPLAT_MINORS, "splatctl")))
|
|
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 */
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
splat_class = class_simple_create(THIS_MODULE, "splat");
|
|
#else
|
|
splat_class = class_create(THIS_MODULE, "splat");
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
class_simple_device_add(splat_class, MKDEV(SPLAT_MAJOR, 0),
|
|
NULL, "splatctl");
|
|
#else
|
|
class_device_create(splat_class, NULL, MKDEV(SPLAT_MAJOR, 0),
|
|
NULL, "splatctl");
|
|
#endif
|
|
|
|
printk(KERN_INFO "splat: Loaded Solaris Porting Layer "
|
|
"Aggressive 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);
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
|
class_simple_device_remove(dev);
|
|
class_simple_destroy(splat_class);
|
|
devfs_remove("splat/splatctl");
|
|
devfs_remove("splat");
|
|
#else
|
|
class_device_destroy(splat_class, dev);
|
|
class_destroy(splat_class);
|
|
#endif
|
|
cdev_del(&splat_cdev);
|
|
unregister_chrdev_region(dev, SPLAT_MINORS);
|
|
|
|
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 "
|
|
"Aggressive Tests v%s\n", VERSION);
|
|
}
|
|
|
|
module_init(splat_init);
|
|
module_exit(splat_fini);
|
|
|
|
MODULE_AUTHOR("Lawrence Livermore National Labs");
|
|
MODULE_DESCRIPTION("Solaris Porting Layer Aggresive Tests");
|
|
MODULE_LICENSE("GPL");
|