2 * This file is part of the SPL: Solaris Porting Layer.
4 * Copyright (c) 2008 Lawrence Livermore National Security, LLC.
5 * Produced at Lawrence Livermore National Laboratory
7 * Brian Behlendorf <behlendorf1@llnl.gov>,
8 * Herb Wartens <wartens2@llnl.gov>,
9 * Jim Garlick <garlick@llnl.gov>
12 * This is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 * My intent is to create a loadable 'splat' (Solaris Porting LAyer
29 * Tests) module which can be used as an access point to run
30 * in kernel Solaris ABI regression tests. This provides a
31 * nice mechanism to validate the shim primates are working properly.
33 * The basic design is the splat module is that it is constructed of
34 * various splat_* source files each of which contains regression tests.
35 * For example the splat_linux_kmem.c file contains tests for validating
36 * kmem correctness. When the splat module is loaded splat_*_init()
37 * will be called for each subsystems tests, similarly splat_*_fini() is
38 * called when the splat module is removed. Each test can then be
39 * run by making an ioctl() call from a userspace control application
40 * to pick the subsystem and test which should be run.
43 #include "splat-internal.h"
45 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
46 #include <linux/devfs_fs_kernel.h>
49 #include <linux/cdev.h>
52 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
53 static struct class_simple
*splat_class
;
55 static struct class *splat_class
;
57 static struct list_head splat_module_list
;
58 static spinlock_t splat_module_lock
;
61 splat_open(struct inode
*inode
, struct file
*file
)
63 unsigned int minor
= iminor(inode
);
66 if (minor
>= SPLAT_MINORS
)
69 info
= (splat_info_t
*)kmalloc(sizeof(*info
), GFP_KERNEL
);
73 spin_lock_init(&info
->info_lock
);
74 info
->info_size
= SPLAT_INFO_BUFFER_SIZE
;
75 info
->info_buffer
= (char *)vmalloc(SPLAT_INFO_BUFFER_SIZE
);
76 if (info
->info_buffer
== NULL
) {
81 info
->info_head
= info
->info_buffer
;
82 file
->private_data
= (void *)info
;
88 splat_release(struct inode
*inode
, struct file
*file
)
90 unsigned int minor
= iminor(inode
);
91 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
93 if (minor
>= SPLAT_MINORS
)
97 ASSERT(info
->info_buffer
);
99 vfree(info
->info_buffer
);
106 splat_buffer_clear(struct file
*file
, splat_cfg_t
*kcfg
, unsigned long arg
)
108 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
111 ASSERT(info
->info_buffer
);
113 spin_lock(&info
->info_lock
);
114 memset(info
->info_buffer
, 0, info
->info_size
);
115 info
->info_head
= info
->info_buffer
;
116 spin_unlock(&info
->info_lock
);
122 splat_buffer_size(struct file
*file
, splat_cfg_t
*kcfg
, unsigned long arg
)
124 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
126 int min
, size
, rc
= 0;
129 ASSERT(info
->info_buffer
);
131 spin_lock(&info
->info_lock
);
132 if (kcfg
->cfg_arg1
> 0) {
134 size
= kcfg
->cfg_arg1
;
135 buf
= (char *)vmalloc(size
);
141 /* Zero fill and truncate contents when coping buffer */
142 min
= ((size
< info
->info_size
) ? size
: info
->info_size
);
143 memset(buf
, 0, size
);
144 memcpy(buf
, info
->info_buffer
, min
);
145 vfree(info
->info_buffer
);
146 info
->info_size
= size
;
147 info
->info_buffer
= buf
;
148 info
->info_head
= info
->info_buffer
;
151 kcfg
->cfg_rc1
= info
->info_size
;
153 if (copy_to_user((struct splat_cfg_t __user
*)arg
, kcfg
, sizeof(*kcfg
)))
156 spin_unlock(&info
->info_lock
);
162 static splat_subsystem_t
*
163 splat_subsystem_find(int id
) {
164 splat_subsystem_t
*sub
;
166 spin_lock(&splat_module_lock
);
167 list_for_each_entry(sub
, &splat_module_list
, subsystem_list
) {
168 if (id
== sub
->desc
.id
) {
169 spin_unlock(&splat_module_lock
);
173 spin_unlock(&splat_module_lock
);
179 splat_subsystem_count(splat_cfg_t
*kcfg
, unsigned long arg
)
181 splat_subsystem_t
*sub
;
184 spin_lock(&splat_module_lock
);
185 list_for_each_entry(sub
, &splat_module_list
, subsystem_list
)
188 spin_unlock(&splat_module_lock
);
191 if (copy_to_user((struct splat_cfg_t __user
*)arg
, kcfg
, sizeof(*kcfg
)))
198 splat_subsystem_list(splat_cfg_t
*kcfg
, unsigned long arg
)
200 splat_subsystem_t
*sub
;
204 /* Structure will be sized large enough for N subsystem entries
205 * which is passed in by the caller. On exit the number of
206 * entries filled in with valid subsystems will be stored in
207 * cfg_rc1. If the caller does not provide enough entries
208 * for all subsystems we will truncate the list to avoid overrun.
210 size
= sizeof(*tmp
) + kcfg
->cfg_data
.splat_subsystems
.size
*
211 sizeof(splat_user_t
);
212 tmp
= kmalloc(size
, GFP_KERNEL
);
216 /* Local 'tmp' is used as the structure copied back to user space */
217 memset(tmp
, 0, size
);
218 memcpy(tmp
, kcfg
, sizeof(*kcfg
));
220 spin_lock(&splat_module_lock
);
221 list_for_each_entry(sub
, &splat_module_list
, subsystem_list
) {
222 strncpy(tmp
->cfg_data
.splat_subsystems
.descs
[i
].name
,
223 sub
->desc
.name
, SPLAT_NAME_SIZE
);
224 strncpy(tmp
->cfg_data
.splat_subsystems
.descs
[i
].desc
,
225 sub
->desc
.desc
, SPLAT_DESC_SIZE
);
226 tmp
->cfg_data
.splat_subsystems
.descs
[i
].id
= sub
->desc
.id
;
228 /* Truncate list if we are about to overrun alloc'ed memory */
229 if ((i
++) == kcfg
->cfg_data
.splat_subsystems
.size
)
232 spin_unlock(&splat_module_lock
);
235 if (copy_to_user((struct splat_cfg_t __user
*)arg
, tmp
, size
)) {
245 splat_test_count(splat_cfg_t
*kcfg
, unsigned long arg
)
247 splat_subsystem_t
*sub
;
251 /* Subsystem ID passed as arg1 */
252 sub
= splat_subsystem_find(kcfg
->cfg_arg1
);
256 spin_lock(&(sub
->test_lock
));
257 list_for_each_entry(test
, &(sub
->test_list
), test_list
)
260 spin_unlock(&(sub
->test_lock
));
263 if (copy_to_user((struct splat_cfg_t __user
*)arg
, kcfg
, sizeof(*kcfg
)))
270 splat_test_list(splat_cfg_t
*kcfg
, unsigned long arg
)
272 splat_subsystem_t
*sub
;
277 /* Subsystem ID passed as arg1 */
278 sub
= splat_subsystem_find(kcfg
->cfg_arg1
);
282 /* Structure will be sized large enough for N test entries
283 * which is passed in by the caller. On exit the number of
284 * entries filled in with valid tests will be stored in
285 * cfg_rc1. If the caller does not provide enough entries
286 * for all tests we will truncate the list to avoid overrun.
288 size
= sizeof(*tmp
)+kcfg
->cfg_data
.splat_tests
.size
*sizeof(splat_user_t
);
289 tmp
= kmalloc(size
, GFP_KERNEL
);
293 /* Local 'tmp' is used as the structure copied back to user space */
294 memset(tmp
, 0, size
);
295 memcpy(tmp
, kcfg
, sizeof(*kcfg
));
297 spin_lock(&(sub
->test_lock
));
298 list_for_each_entry(test
, &(sub
->test_list
), test_list
) {
299 strncpy(tmp
->cfg_data
.splat_tests
.descs
[i
].name
,
300 test
->desc
.name
, SPLAT_NAME_SIZE
);
301 strncpy(tmp
->cfg_data
.splat_tests
.descs
[i
].desc
,
302 test
->desc
.desc
, SPLAT_DESC_SIZE
);
303 tmp
->cfg_data
.splat_tests
.descs
[i
].id
= test
->desc
.id
;
305 /* Truncate list if we are about to overrun alloc'ed memory */
306 if ((i
++) == kcfg
->cfg_data
.splat_tests
.size
)
309 spin_unlock(&(sub
->test_lock
));
312 if (copy_to_user((struct splat_cfg_t __user
*)arg
, tmp
, size
)) {
322 splat_validate(struct file
*file
, splat_subsystem_t
*sub
, int cmd
, void *arg
)
326 spin_lock(&(sub
->test_lock
));
327 list_for_each_entry(test
, &(sub
->test_list
), test_list
) {
328 if (test
->desc
.id
== cmd
) {
329 spin_unlock(&(sub
->test_lock
));
330 return test
->test(file
, arg
);
333 spin_unlock(&(sub
->test_lock
));
339 splat_ioctl_cfg(struct file
*file
, unsigned long arg
)
344 if (copy_from_user(&kcfg
, (splat_cfg_t
*)arg
, sizeof(kcfg
)))
347 if (kcfg
.cfg_magic
!= SPLAT_CFG_MAGIC
) {
348 splat_print(file
, "Bad config magic 0x%x != 0x%x\n",
349 kcfg
.cfg_magic
, SPLAT_CFG_MAGIC
);
353 switch (kcfg
.cfg_cmd
) {
354 case SPLAT_CFG_BUFFER_CLEAR
:
358 rc
= splat_buffer_clear(file
, &kcfg
, arg
);
360 case SPLAT_CFG_BUFFER_SIZE
:
361 /* cfg_arg1 - 0 - query size; >0 resize
362 * cfg_rc1 - Set to current buffer size
364 rc
= splat_buffer_size(file
, &kcfg
, arg
);
366 case SPLAT_CFG_SUBSYSTEM_COUNT
:
368 * cfg_rc1 - Set to number of subsystems
370 rc
= splat_subsystem_count(&kcfg
, arg
);
372 case SPLAT_CFG_SUBSYSTEM_LIST
:
374 * cfg_rc1 - Set to number of subsystems
375 * cfg_data.splat_subsystems - Populated with subsystems
377 rc
= splat_subsystem_list(&kcfg
, arg
);
379 case SPLAT_CFG_TEST_COUNT
:
380 /* cfg_arg1 - Set to a target subsystem
381 * cfg_rc1 - Set to number of tests
383 rc
= splat_test_count(&kcfg
, arg
);
385 case SPLAT_CFG_TEST_LIST
:
386 /* cfg_arg1 - Set to a target subsystem
387 * cfg_rc1 - Set to number of tests
388 * cfg_data.splat_subsystems - Populated with tests
390 rc
= splat_test_list(&kcfg
, arg
);
393 splat_print(file
, "Bad config command %d\n", kcfg
.cfg_cmd
);
402 splat_ioctl_cmd(struct file
*file
, unsigned long arg
)
404 splat_subsystem_t
*sub
;
409 if (copy_from_user(&kcmd
, (splat_cfg_t
*)arg
, sizeof(kcmd
)))
412 if (kcmd
.cmd_magic
!= SPLAT_CMD_MAGIC
) {
413 splat_print(file
, "Bad command magic 0x%x != 0x%x\n",
414 kcmd
.cmd_magic
, SPLAT_CFG_MAGIC
);
418 /* Allocate memory for any opaque data the caller needed to pass on */
419 if (kcmd
.cmd_data_size
> 0) {
420 data
= (void *)kmalloc(kcmd
.cmd_data_size
, GFP_KERNEL
);
424 if (copy_from_user(data
, (void *)(arg
+ offsetof(splat_cmd_t
,
425 cmd_data_str
)), kcmd
.cmd_data_size
)) {
431 sub
= splat_subsystem_find(kcmd
.cmd_subsystem
);
433 rc
= splat_validate(file
, sub
, kcmd
.cmd_test
, data
);
444 splat_ioctl(struct inode
*inode
, struct file
*file
,
445 unsigned int cmd
, unsigned long arg
)
447 unsigned int minor
= iminor(file
->f_dentry
->d_inode
);
450 /* Ignore tty ioctls */
451 if ((cmd
& 0xffffff00) == ((int)'T') << 8)
454 if (minor
>= SPLAT_MINORS
)
459 rc
= splat_ioctl_cfg(file
, arg
);
462 rc
= splat_ioctl_cmd(file
, arg
);
465 splat_print(file
, "Bad ioctl command %d\n", cmd
);
473 /* I'm not sure why you would want to write in to this buffer from
474 * user space since its principle use is to pass test status info
475 * back to the user space, but I don't see any reason to prevent it.
477 static ssize_t
splat_write(struct file
*file
, const char __user
*buf
,
478 size_t count
, loff_t
*ppos
)
480 unsigned int minor
= iminor(file
->f_dentry
->d_inode
);
481 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
484 if (minor
>= SPLAT_MINORS
)
488 ASSERT(info
->info_buffer
);
490 spin_lock(&info
->info_lock
);
492 /* Write beyond EOF */
493 if (*ppos
>= info
->info_size
) {
498 /* Resize count if beyond EOF */
499 if (*ppos
+ count
> info
->info_size
)
500 count
= info
->info_size
- *ppos
;
502 if (copy_from_user(info
->info_buffer
, buf
, count
)) {
510 spin_unlock(&info
->info_lock
);
514 static ssize_t
splat_read(struct file
*file
, char __user
*buf
,
515 size_t count
, loff_t
*ppos
)
517 unsigned int minor
= iminor(file
->f_dentry
->d_inode
);
518 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
521 if (minor
>= SPLAT_MINORS
)
525 ASSERT(info
->info_buffer
);
527 spin_lock(&info
->info_lock
);
529 /* Read beyond EOF */
530 if (*ppos
>= info
->info_size
)
533 /* Resize count if beyond EOF */
534 if (*ppos
+ count
> info
->info_size
)
535 count
= info
->info_size
- *ppos
;
537 if (copy_to_user(buf
, info
->info_buffer
+ *ppos
, count
)) {
545 spin_unlock(&info
->info_lock
);
549 static loff_t
splat_seek(struct file
*file
, loff_t offset
, int origin
)
551 unsigned int minor
= iminor(file
->f_dentry
->d_inode
);
552 splat_info_t
*info
= (splat_info_t
*)file
->private_data
;
555 if (minor
>= SPLAT_MINORS
)
559 ASSERT(info
->info_buffer
);
561 spin_lock(&info
->info_lock
);
564 case 0: /* SEEK_SET - No-op just do it */
566 case 1: /* SEEK_CUR - Seek from current */
567 offset
= file
->f_pos
+ offset
;
569 case 2: /* SEEK_END - Seek from end */
570 offset
= info
->info_size
+ offset
;
575 file
->f_pos
= offset
;
580 spin_unlock(&info
->info_lock
);
585 static struct file_operations splat_fops
= {
586 .owner
= THIS_MODULE
,
588 .release
= splat_release
,
589 .ioctl
= splat_ioctl
,
591 .write
= splat_write
,
592 .llseek
= splat_seek
,
595 static struct cdev splat_cdev
= {
596 .owner
= THIS_MODULE
,
597 .kobj
= { .name
= "splatctl", },
606 spin_lock_init(&splat_module_lock
);
607 INIT_LIST_HEAD(&splat_module_list
);
609 SPLAT_SUBSYSTEM_INIT(kmem
);
610 SPLAT_SUBSYSTEM_INIT(taskq
);
611 SPLAT_SUBSYSTEM_INIT(krng
);
612 SPLAT_SUBSYSTEM_INIT(mutex
);
613 SPLAT_SUBSYSTEM_INIT(condvar
);
614 SPLAT_SUBSYSTEM_INIT(thread
);
615 SPLAT_SUBSYSTEM_INIT(rwlock
);
616 SPLAT_SUBSYSTEM_INIT(time
);
617 SPLAT_SUBSYSTEM_INIT(vnode
);
618 SPLAT_SUBSYSTEM_INIT(kobj
);
619 SPLAT_SUBSYSTEM_INIT(atomic
);
621 dev
= MKDEV(SPLAT_MAJOR
, 0);
622 if ((rc
= register_chrdev_region(dev
, SPLAT_MINORS
, "splatctl")))
625 /* Support for registering a character driver */
626 cdev_init(&splat_cdev
, &splat_fops
);
627 if ((rc
= cdev_add(&splat_cdev
, dev
, SPLAT_MINORS
))) {
628 printk(KERN_ERR
"splat: Error adding cdev, %d\n", rc
);
629 kobject_put(&splat_cdev
.kobj
);
630 unregister_chrdev_region(dev
, SPLAT_MINORS
);
634 /* Support for udev make driver info available in sysfs */
635 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
636 splat_class
= class_simple_create(THIS_MODULE
, "splat");
638 splat_class
= class_create(THIS_MODULE
, "splat");
640 if (IS_ERR(splat_class
)) {
641 rc
= PTR_ERR(splat_class
);
642 printk(KERN_ERR
"splat: Error creating splat class, %d\n", rc
);
643 cdev_del(&splat_cdev
);
644 unregister_chrdev_region(dev
, SPLAT_MINORS
);
648 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
649 class_simple_device_add(splat_class
, MKDEV(SPLAT_MAJOR
, 0),
652 class_device_create(splat_class
, NULL
, MKDEV(SPLAT_MAJOR
, 0),
656 printk(KERN_INFO
"splat: Loaded Solaris Porting LAyer "
657 "Tests v%s\n", VERSION
);
660 printk(KERN_ERR
"splat: Error registering splat device, %d\n", rc
);
667 dev_t dev
= MKDEV(SPLAT_MAJOR
, 0);
669 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
670 class_simple_device_remove(dev
);
671 class_simple_destroy(splat_class
);
672 devfs_remove("splat/splatctl");
673 devfs_remove("splat");
675 class_device_destroy(splat_class
, dev
);
676 class_destroy(splat_class
);
678 cdev_del(&splat_cdev
);
679 unregister_chrdev_region(dev
, SPLAT_MINORS
);
681 SPLAT_SUBSYSTEM_FINI(atomic
);
682 SPLAT_SUBSYSTEM_FINI(kobj
);
683 SPLAT_SUBSYSTEM_FINI(vnode
);
684 SPLAT_SUBSYSTEM_FINI(time
);
685 SPLAT_SUBSYSTEM_FINI(rwlock
);
686 SPLAT_SUBSYSTEM_FINI(thread
);
687 SPLAT_SUBSYSTEM_FINI(condvar
);
688 SPLAT_SUBSYSTEM_FINI(mutex
);
689 SPLAT_SUBSYSTEM_FINI(krng
);
690 SPLAT_SUBSYSTEM_FINI(taskq
);
691 SPLAT_SUBSYSTEM_FINI(kmem
);
693 ASSERT(list_empty(&splat_module_list
));
694 printk(KERN_INFO
"splat: Unloaded Solaris Porting LAyer "
695 "Tests v%s\n", VERSION
);
698 module_init(splat_init
);
699 module_exit(splat_fini
);
701 MODULE_AUTHOR("Lawrence Livermore National Labs");
702 MODULE_DESCRIPTION("Solaris Porting LAyer Tests");
703 MODULE_LICENSE("GPL");