//
// Virtual Disk
//
#define __KERNEL__
#define MODULE
//
// Include the necessary head files
//
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/blk.h>
#define VIRTUAL_DISK_DEV_MAJOR 42 // The major number of the virtual disk device
#define DEVICE_NAME "VirtualDisk" // The name of the virtual disk device
#define VIRTUAL_DISK_SECTOR_SIZE 512 // disk sector size
#define VIRTUAL_DISK_BLOCK_SIZE 1024 // block size in bytes
#define VIRTUAL_DISK_BLOCK_SIZE_IN_SECTOR 2 // block size in sector
#define VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK 10*1024 // size in blocks in blocks
/* the storage pool */
static char *g_pVirtualDiskPool; // The pointer pointing the buffer used by our virtual disk
static int g_iSectorSize = VIRTUAL_DISK_SECTOR_SIZE;
static int g_iBlockSizeInBytes = VIRTUAL_DISK_BLOCK_SIZE;
static int g_iDiskSizeInBlocks = VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK;
static int g_iReadAheadCount = 4;
//
// _fops function declarations
//
static int VirtualDiskOpen (struct inode *pINode, struct file *pFile);
static int VirtualDiskRelease (struct inode *pINode, struct file *pFile);
static int VirtualDiskIoctl (struct inode *pINode, struct file *pFile, unsigned int iCmd, unsigned long ulArg);
static struct block_device_operations g_fops =
{
open: VirtualDiskOpen,
release: VirtualDiskRelease,
ioctl: VirtualDiskIoctl,
};
//
// The function processing the read/write request
//
static int VirtualDiskRequest(request_queue_t *pRequestQueue, int rw, struct buffer_head *pBufferHead)
{
unsigned long ulOffset, ulTotal;
//printk ("%s sector rsector = %lu, blocknr = %lu\n", rw == READ ? "read" : "write", pBufferHead->b_rsector, pBufferHead->b_blocknr);
ulOffset = pBufferHead->b_rsector*VIRTUAL_DISK_SECTOR_SIZE;
ulTotal = (unsigned long)pBufferHead->b_size;
//
// Access beyond end of the device
//
if (ulTotal + ulOffset > VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK * VIRTUAL_DISK_BLOCK_SIZE)
{
printk ("Error: access beyond end of the device");
//
// Io error process
//
buffer_IO_error(pBufferHead);
return 0;
}
if (rw == READ)
{
//
// Copy from our memory pool to the buffer
//
memcpy(bh_kmap(pBufferHead), g_pVirtualDiskPool + ulOffset, ulTotal);
}
else if (rw == WRITE)
{
//
// Copy from the buffer to our memory pool
//
memcpy(g_pVirtualDiskPool + ulOffset, bh_kmap(pBufferHead), ulTotal);
}
else
{
printk ("Error: Invalid command!");
}
//
// End I/O process
//
pBufferHead->b_end_io(pBufferHead,1);
if (rw == READ)
printk ("Read end successfully!\n");
else
printk ("Write end successfully!\n");
return 0;
}
static int VirtualDiskRelease(struct inode *pINode, struct file *file)
{
//
// Decrement reference count
//
MOD_DEC_USE_COUNT;
return 0;
}
static int VirtualDiskOpen(struct inode *pINode, struct file *pFile)
{
//
// Increment reference count
//
MOD_INC_USE_COUNT;
return 0;
}
static int VirtualDiskIoctl (struct inode *pINode, struct file *pFile, unsigned int iCmd, unsigned long ulArg)
{
//
// We can define some special command by pINode->i_rdev
//
unsigned int iMinor;
int iTmp;
if (!pINode || !pINode->i_rdev)
return -EINVAL;
iMinor = MINOR(pINode->i_rdev);
switch (iCmd)
{
case BLKFLSBUF:
//
// Flush buffers content to storage device
//
printk ("ioctl: BLKFLSBUF\n");
//
// Deny all but root
//
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
fsync_dev(pINode->i_rdev);
invalidate_buffers(pINode->i_rdev);
break;
case BLKGETSIZE:
//
// Return device size
//
printk ("ioctl: BLKGETSIZE\n");
if (!ulArg)
return -EINVAL;
return put_user(VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK*VIRTUAL_DISK_BLOCK_SIZE_IN_SECTOR, (long *) ulArg);
case BLKRASET:
//
// Set read ahead value
//
printk ("ioctl: BLKRASET\n");
if (get_user(iTmp, (long *)ulArg))
return -EINVAL;
if (iTmp > 0xff)
return -EINVAL;
read_ahead[VIRTUAL_DISK_DEV_MAJOR] = iTmp;
return 0;
case BLKRAGET:
//
// Return read ahead value
//
printk ("ioctl: BLKRAGET\n");
if (!ulArg)
return -EINVAL;
return put_user(read_ahead[VIRTUAL_DISK_DEV_MAJOR], (long *)ulArg);
case BLKSSZGET:
//
// Return block size
//
printk ("ioctl: BLKSSZGET\n");
if (!ulArg)
return -EINVAL;
return put_user(VIRTUAL_DISK_BLOCK_SIZE, (long *)ulArg);
default:
printk ("ioctl wanted\n");
return -ENOTTY;
}
return 0;
}
int init_module(void)
{
int iResult = 0;
//
// Verify block size must be a multiple of sector size
//
if (g_iBlockSizeInBytes & (VIRTUAL_DISK_SECTOR_SIZE - 1))
{
printk ("Block size not a multiple of sector size\n");
return -EINVAL;
}
//
// Allocate the buffer used by our disk
//
g_pVirtualDiskPool = (char *) vmalloc (VIRTUAL_DISK_BLOCK_SIZE*VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK);
if (g_pVirtualDiskPool == NULL)
{
printk ("Not enough memory.\n");
return -ENOMEM;
}
memset(g_pVirtualDiskPool, 0, VIRTUAL_DISK_BLOCK_SIZE*VIRTUAL_DISK_TOTAL_SIZE_IN_BLOCK);
//
// Register our device to the devfs
//
if ((iResult = register_blkdev(VIRTUAL_DISK_DEV_MAJOR, DEVICE_NAME, &g_fops)) != 0)
{
printk ("couldn't register virtual disk device\n");
return iResult;
}
//
// Register our disk information
//
hardsect_size [VIRTUAL_DISK_DEV_MAJOR] = &g_iSectorSize;
blksize_size [VIRTUAL_DISK_DEV_MAJOR] = &g_iBlockSizeInBytes;
blk_size [VIRTUAL_DISK_DEV_MAJOR] = &g_iDiskSizeInBlocks;
//
// Register our read/write function
//建立自己的请求队列
blk_queue_make_request (BLK_DEFAULT_QUEUE(VIRTUAL_DISK_DEV_MAJOR), &VirtualDiskRequest);
read_ahead [VIRTUAL_DISK_DEV_MAJOR] = g_iReadAheadCount;
return 0;
}
void cleanup_module(void)
{
//
// Unregister our block device
//
unregister_blkdev(VIRTUAL_DISK_DEV_MAJOR, DEVICE_NAME);
invalidate_buffers (MKDEV(VIRTUAL_DISK_DEV_MAJOR, 0));
//
// Invalidate our read/write funtion
//
blk_dev[VIRTUAL_DISK_DEV_MAJOR].request_queue.request_fn = 0;
vfree(g_pVirtualDiskPool);
}
MODULE_LICENSE("GPL");