316 lines
6.9 KiB
C
316 lines
6.9 KiB
C
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/genhd.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/blk.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mempool.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/quotaops.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/dnotify.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/security.h>
|
|
#include <linux/buffer_head.h>
|
|
#include <asm/namei.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
|
|
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
|
|
#define TEST_MEM_SIZE 4096
|
|
#define FALSE 0
|
|
#include "Ltpfs.h"
|
|
|
|
static int ltpdev_open(struct inode *inode, struct file *pfile);
|
|
static int ltpdev_release(struct inode *inode, struct file *pfile);
|
|
static int ltpdev_ioctl(struct inode *pinode, struct file *pfile,
|
|
unsigned int cmd, unsigned long arg);
|
|
static int do_buffer_c_tests(void);
|
|
|
|
static struct block_device_operations blkops = {
|
|
open: ltpdev_open,
|
|
release:ltpdev_release,
|
|
ioctl: ltpdev_ioctl,
|
|
};
|
|
|
|
int ltp_fs_major = LTPMAJOR;
|
|
int test_iteration = 0;
|
|
|
|
static char genhd_flags = 0;
|
|
static struct gendisk *gd_ptr;
|
|
static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
|
|
|
|
MODULE_AUTHOR("Martin Ridgeway <mridge@us.ibm.com>");
|
|
MODULE_DESCRIPTION(FS_LTP_TEST_DRIVER_NAME);
|
|
MODULE_LICENSE("GPL");
|
|
|
|
/*
|
|
* Device operations for the virtual FS devices
|
|
*/
|
|
|
|
static struct pm_dev *ltp_pm_dev = NULL;
|
|
struct block_device *ltplookup_bdev(const char *path);
|
|
int path_lookup(const char *name, unsigned int flags, struct nameidata *nd);
|
|
//static int __emul_lookup_dentry(const char *name, struct nameidata *nd);
|
|
void path_release(struct nameidata *nd);
|
|
|
|
static int ltpdev_open(struct inode *pinode, struct file *pfile)
|
|
{
|
|
printk(KERN_ALERT "ltpdev_open \n");
|
|
return 0;
|
|
}
|
|
|
|
static int ltpdev_release(struct inode *pinode, struct file *pfile)
|
|
{
|
|
|
|
printk(KERN_ALERT "ltpdev_release \n");
|
|
return 0;
|
|
}
|
|
|
|
static int ltpdev_ioctl(struct inode *pinode, struct file *pfile,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
|
|
struct bio *my_bio = NULL;
|
|
struct bio *my_bio_copy = NULL;
|
|
request_queue_t *q = NULL;
|
|
struct block_device *bdev = NULL;
|
|
unsigned long uaddr;
|
|
|
|
unsigned int bytes_done = 100;
|
|
|
|
int error = 0;
|
|
int rc = 0;
|
|
|
|
/*****************************************************************************/
|
|
|
|
printk(KERN_ALERT "ltpdev_ioctl fs tests\n");
|
|
|
|
switch (cmd) {
|
|
|
|
case LTPAIODEV_CMD:
|
|
printk(KERN_ALERT "Running AIO FS tests \n");
|
|
printk(KERN_ALERT "AIO FS tests complete\n");
|
|
break;
|
|
|
|
case LTPBIODEV_CMD:
|
|
|
|
printk(KERN_ALERT "Running BIO FS tests \n");
|
|
|
|
my_bio = bio_alloc(GFP_KERNEL, 0);
|
|
if (!my_bio) {
|
|
printk(KERN_ALERT
|
|
"Error getting kernel slab memory !!\n");
|
|
} else {
|
|
printk(KERN_ALERT "kernel slab memory alloc OK\n");
|
|
}
|
|
|
|
bio_endio(my_bio, bytes_done, error);
|
|
|
|
printk(KERN_ALERT "Return from bio_endio = %d \n", error);
|
|
|
|
my_bio_copy = bio_clone(my_bio, GFP_ATOMIC);
|
|
|
|
if (!my_bio_copy) {
|
|
printk(KERN_ALERT
|
|
"Error getting kernel bio clone !!\n");
|
|
} else {
|
|
printk(KERN_ALERT "kernel bio clone OK\n");
|
|
}
|
|
|
|
my_bio_copy = bio_clone(my_bio, GFP_NOIO);
|
|
|
|
if (!my_bio_copy) {
|
|
printk(KERN_ALERT
|
|
"Error getting kernel bio clone !!\n");
|
|
} else {
|
|
printk(KERN_ALERT "kernel bio clone OK\n");
|
|
}
|
|
|
|
// q = bdev_get_queue(my_bio->bi_bdev);
|
|
|
|
// rc = bio_phys_segments(q, my_bio);
|
|
|
|
// rc = bio_hw_segments(q, my_bio);
|
|
|
|
bdev = lookup_bdev(LTP_FS_DEVICE_NAME);
|
|
|
|
printk(KERN_ALERT "return from bdev size %d\n",
|
|
bdev->bd_block_size);
|
|
|
|
printk(KERN_ALERT "Return from phys_segments = %d \n", rc);
|
|
|
|
// Don't use this API, causes system to hang and corrupts FS
|
|
// bio_put(my_bio);
|
|
|
|
(char *)uaddr = kmalloc(TEST_MEM_SIZE, GFP_KERNEL);
|
|
|
|
my_bio_copy = bio_map_user(bdev, uaddr, TEST_MEM_SIZE, FALSE);
|
|
|
|
printk(KERN_ALERT "Return from bio_map_user %p\n", my_bio_copy);
|
|
|
|
do_buffer_c_tests();
|
|
|
|
printk(KERN_ALERT "BIO FS tests complete\n");
|
|
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ltp_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int init_module(void)
|
|
{
|
|
int result;
|
|
|
|
printk(KERN_ALERT "ltpdev_init_module \n");
|
|
|
|
ltp_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, ltp_pm_callback);
|
|
|
|
result = register_blkdev(ltp_fs_major, LTP_FS_DEV_NAME);
|
|
|
|
printk(KERN_ALERT "LTP FS: register_blkdev result=%d major %d\n",
|
|
result, ltp_fs_major);
|
|
|
|
if (result < 0) {
|
|
printk(KERN_ALERT "LTP FS: can't get major %d\n", ltp_fs_major);
|
|
return result;
|
|
}
|
|
|
|
gd_ptr = kmalloc(sizeof(struct gendisk *), GFP_KERNEL);
|
|
|
|
if (!gd_ptr) {
|
|
printk(KERN_ALERT "ERROR getting memory !!!\n");
|
|
return 0;
|
|
}
|
|
|
|
gd_ptr = alloc_disk(1);
|
|
|
|
printk(KERN_ALERT "gd_ptr after alloc = %p \n", gd_ptr);
|
|
|
|
gd_ptr->major = ltp_fs_major;
|
|
gd_ptr->first_minor = 0;
|
|
gd_ptr->fops = &blkops;
|
|
gd_ptr->driverfs_dev = NULL;
|
|
gd_ptr->capacity = MAX_NUM_DISKS;
|
|
gd_ptr->flags = genhd_flags;
|
|
|
|
sprintf(gd_ptr->disk_name, LTP_FS_DEV_NAME);
|
|
|
|
add_disk(gd_ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup_module(void)
|
|
{
|
|
|
|
printk(KERN_ALERT "Exiting module and cleaning up \n");
|
|
|
|
pm_unregister(ltp_pm_dev);
|
|
|
|
put_disk(gd_ptr);
|
|
|
|
del_gendisk(gd_ptr);
|
|
|
|
unregister_blkdev(ltp_fs_major, LTP_FS_DEV_NAME);
|
|
|
|
}
|
|
|
|
static int do_buffer_c_tests()
|
|
{
|
|
int line_no = 0;
|
|
|
|
printk(KERN_ALERT "Starting buffer.c coverage tests... \n");
|
|
|
|
__buffer_error("Test file", line_no);
|
|
|
|
printk(KERN_ALERT "buffer.c coverage tests complete...\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lookup_bdev - lookup a struct block_device by name
|
|
*
|
|
* @path: special file representing the block device
|
|
*
|
|
* Get a reference to the blockdevice at @path in the current
|
|
* namespace if possible and return it. Return ERR_PTR(error)
|
|
* otherwise.
|
|
*/
|
|
struct block_device *lookup_bdev(const char *path)
|
|
{
|
|
struct block_device *bdev;
|
|
struct inode *inode;
|
|
struct nameidata nd;
|
|
int error;
|
|
|
|
if (!path || !*path)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
error = path_lookup(path, LOOKUP_FOLLOW, &nd);
|
|
if (error)
|
|
return ERR_PTR(error);
|
|
|
|
inode = nd.dentry->d_inode;
|
|
error = -ENOTBLK;
|
|
if (!S_ISBLK(inode->i_mode))
|
|
goto fail;
|
|
error = -EACCES;
|
|
if (nd.mnt->mnt_flags & MNT_NODEV)
|
|
goto fail;
|
|
error = bd_acquire(inode);
|
|
if (error)
|
|
goto fail;
|
|
bdev = inode->i_bdev;
|
|
|
|
out:
|
|
path_release(&nd);
|
|
return bdev;
|
|
fail:
|
|
bdev = ERR_PTR(error);
|
|
goto out;
|
|
}
|
|
|
|
int bd_acquire(struct inode *inode)
|
|
{
|
|
struct block_device *bdev;
|
|
spin_lock(&bdev_lock);
|
|
if (inode->i_bdev) {
|
|
atomic_inc(&inode->i_bdev->bd_count);
|
|
spin_unlock(&bdev_lock);
|
|
return 0;
|
|
}
|
|
spin_unlock(&bdev_lock);
|
|
bdev = bdget(kdev_t_to_nr(inode->i_rdev));
|
|
if (!bdev)
|
|
return -ENOMEM;
|
|
spin_lock(&bdev_lock);
|
|
if (!inode->i_bdev) {
|
|
inode->i_bdev = bdev;
|
|
inode->i_mapping = bdev->bd_inode->i_mapping;
|
|
list_add(&inode->i_devices, &bdev->bd_inodes);
|
|
} else if (inode->i_bdev != bdev)
|
|
BUG();
|
|
spin_unlock(&bdev_lock);
|
|
return 0;
|
|
}
|