📄 sbull.c
字号:
/* * sbull.c -- the Simple Block Utility * * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet * Copyright (C) 2001 O'Reilly & Associates * * The source code in this file can be freely used, adapted, * and redistributed in source or binary form, so long as an * acknowledgment appears in derived source files. The citation * should list that the code comes from the book "Linux Device * Drivers" by Alessandro Rubini and Jonathan Corbet, published * by O'Reilly & Associates. No warranty is attached; * we cannot take responsibility for errors or fitness for use. * * $Id: sbull.c,v 1.32 2001/07/18 22:28:16 rubini Exp $ */#ifndef __KERNEL__# define __KERNEL__#endif#ifndef MODULE# define MODULE#endif#include <linux/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h> /* printk() */#include <linux/malloc.h> /* kmalloc() */#include <linux/fs.h> /* everything... */#include <linux/errno.h> /* error codes */#include <linux/timer.h>#include <linux/types.h> /* size_t */#include <linux/fcntl.h> /* O_ACCMODE */#include <linux/hdreg.h> /* HDIO_GETGEO */#include <asm/system.h> /* cli(), *_flags */#define MAJOR_NR sbull_major /* force definitions on in blk.h */static int sbull_major; /* must be declared before including blk.h */#define DEVICE_NR(device) MINOR(device) /* sbull has no partition bits */#define DEVICE_NAME "sbull" /* name for messaging */#define DEVICE_INTR sbull_intrptr /* pointer to the bottom half */#define DEVICE_NO_RANDOM /* no entropy to contribute */#define DEVICE_REQUEST sbull_request#define DEVICE_OFF(d) /* do-nothing */#include <linux/blk.h>#include "sbull.h" /* local definitions */#ifdef HAVE_BLKPG_H#include <linux/blkpg.h> /* blk_ioctl() */#endif/* * Do the raw char interface in 2.4. */#ifdef LINUX_24# define DO_RAW_INTERFACE# include <linux/iobuf.h>static void sbullr_init();static void sbullr_release();# define SBULLR_SECTOR 512 /* insist on this */# define SBULLR_SECTOR_MASK (SBULLR_SECTOR - 1)# define SBULLR_SECTOR_SHIFT 9#endif/* * Non-prefixed symbols are static. They are meant to be assigned at * load time. Prefixed symbols are not static, so they can be used in * debugging. They are hidden anyways by register_symtab() unless * SBULL_DEBUG is defined. */static int major = SBULL_MAJOR;static int devs = SBULL_DEVS;static int rahead = SBULL_RAHEAD;static int size = SBULL_SIZE;static int blksize = SBULL_BLKSIZE;static int hardsect = SBULL_HARDSECT;MODULE_PARM(major, "i");MODULE_PARM(devs, "i");MODULE_PARM(rahead, "i");MODULE_PARM(size, "i");MODULE_PARM(blksize, "i");MODULE_PARM(hardsect, "i");MODULE_AUTHOR("Alessandro Rubini");int sbull_devs, sbull_rahead, sbull_size;int sbull_blksize, sbull_hardsect;/* The following items are obtained through kmalloc() in sbull_init() */Sbull_Dev *sbull_devices = NULL;int *sbull_blksizes = NULL;int *sbull_sizes = NULL;int *sbull_hardsects = NULL;/* * We can do without a request queue, but only in 2.4 */#if defined(LINUX_24) && !defined(SBULL_MULTIQUEUE)static int noqueue = 0; /* Use request queue by default */MODULE_PARM(noqueue, "i");#endif#ifdef DO_RAW_INTERFACEint sbullr_major = SBULLR_MAJOR;MODULE_PARM(sbullr_major, "i");#endifint sbull_revalidate(kdev_t i_rdev);/* * Open and close */int sbull_open (struct inode *inode, struct file *filp){ Sbull_Dev *dev; /* device information */ int num = MINOR(inode->i_rdev); if (num >= sbull_devs) return -ENODEV; dev = sbull_devices + num; /* kill the timer associated to the device: it might be active */ del_timer(&dev->timer); spin_lock(&dev->lock); /* revalidate on first open and fail if no data is there */ if (!dev->usage) { check_disk_change(inode->i_rdev); if (!dev->data) { spin_unlock (&dev->lock); return -ENOMEM; } } dev->usage++; spin_unlock(&dev->lock); MOD_INC_USE_COUNT; return 0; /* success */}int sbull_release (struct inode *inode, struct file *filp){ Sbull_Dev *dev = sbull_devices + MINOR(inode->i_rdev); spin_lock(&dev->lock); dev->usage--; /* * If the device is closed for the last time, start a timer * to release RAM in half a minute. The function and argument * for the timer have been setup in sbull_init() */ if (!dev->usage) { dev->timer.expires = jiffies + 30 * HZ; add_timer(&dev->timer); /* but flush it right now */ fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); } MOD_DEC_USE_COUNT; spin_unlock(&dev->lock); return 0;}/* * The timer function. As argument it receives the device */void sbull_expires(unsigned long data){ Sbull_Dev *dev = (Sbull_Dev *)data; spin_lock(&dev->lock); if (dev->usage || !dev->data) { spin_unlock(&dev->lock); printk(KERN_WARNING "sbull: timer mismatch for device %i\n", dev - sbull_devices); return; } PDEBUG("freeing device %i\n",dev - sbull_devices); vfree(dev->data); dev->data=0; spin_unlock(&dev->lock); return;} /* * The ioctl() implementation */int sbull_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ int err; long size; struct hd_geometry geo; PDEBUG("ioctl 0x%x 0x%lx\n", cmd, arg); switch(cmd) { case BLKGETSIZE: /* Return the device size, expressed in sectors */ if (!arg) return -EINVAL; /* NULL pointer: not valid */ err = ! access_ok (VERIFY_WRITE, arg, sizeof(long)); if (err) return -EFAULT; size = blksize*sbull_sizes[MINOR(inode->i_rdev)] / sbull_hardsects[MINOR(inode->i_rdev)]; if (copy_to_user((long *) arg, &size, sizeof (long))) return -EFAULT; return 0; case BLKRRPART: /* re-read partition table: can't do it */ return -ENOTTY; case HDIO_GETGEO: /* * Get geometry: since we are a virtual device, we have to make * up something plausible. So we claim 16 sectors, four heads, * and calculate the corresponding number of cylinders. We set the * start of data at sector four. */ err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo)); if (err) return -EFAULT; size = sbull_size * blksize / sbull_hardsect; geo.cylinders = (size & ~0x3f) >> 6; geo.heads = 4; geo.sectors = 16; geo.start = 4; if (copy_to_user((void *) arg, &geo, sizeof(geo))) return -EFAULT; return 0; default: /* * For ioctls we don't understand, let the block layer handle them. */ return blk_ioctl(inode->i_rdev, cmd, arg); } return -ENOTTY; /* unknown command */}/* * Support for removable devices */int sbull_check_change(kdev_t i_rdev){ int minor = MINOR(i_rdev); Sbull_Dev *dev = sbull_devices + minor; if (minor >= sbull_devs) /* paranoid */ return 0; PDEBUG("check_change for dev %i\n",minor); if (dev->data) return 0; /* still valid */ return 1; /* expired */}/* * Note no locks taken out here. In a worst case scenario, we could drop * a chunk of system memory. But that should never happen, since validation * happens at open or mount time, when locks are held. */int sbull_revalidate(kdev_t i_rdev){ Sbull_Dev *dev = sbull_devices + MINOR(i_rdev); PDEBUG("revalidate for dev %i\n",MINOR(i_rdev)); if (dev->data) return 0; dev->data = vmalloc(dev->size); if (!dev->data) return -ENOMEM; return 0;}/* * The file operations */#ifdef LINUX_24struct block_device_operations sbull_bdops = { open: sbull_open, release: sbull_release, ioctl: sbull_ioctl, check_media_change: sbull_check_change, revalidate: sbull_revalidate,};#else#ifdef LINUX_20void sbull_release_20 (struct inode *inode, struct file *filp){ (void) sbull_release (inode, filp);}#define sbull_release sbull_release_20#endifstruct file_operations sbull_bdops = { read: block_read, write: block_write, ioctl: sbull_ioctl, open: sbull_open, release: sbull_release, fsync: block_fsync, check_media_change: sbull_check_change, revalidate: sbull_revalidate};# endif /* LINUX_24 *//* * Block-driver specific functions *//* * Find the device for this request. */static Sbull_Dev *sbull_locate_device(const struct request *req){ int devno; Sbull_Dev *device; /* Check if the minor number is in range */ devno = DEVICE_NR(req->rq_dev); if (devno >= sbull_devs) { static int count = 0; if (count++ < 5) /* print the message at most five times */ printk(KERN_WARNING "sbull: request for unknown device\n"); return NULL; } device = sbull_devices + devno; /* Pick it out of our device array */ return device;}/* * Perform an actual transfer. */static int sbull_transfer(Sbull_Dev *device, const struct request *req){ int size; u8 *ptr; ptr = device->data + req->sector * sbull_hardsect; size = req->current_nr_sectors * sbull_hardsect; /* Make sure that the transfer fits within the device. */ if (ptr + size > device->data + sbull_blksize*sbull_size) { static int count = 0; if (count++ < 5) printk(KERN_WARNING "sbull: request past end of device\n"); return 0; } /* Looks good, do the transfer. */ switch(req->cmd) { case READ: memcpy(req->buffer, ptr, size); /* from sbull to buffer */ return 1; case WRITE: memcpy(ptr, req->buffer, size); /* from buffer to sbull */ return 1; default: /* can't happen */ return 0; }}#ifdef LINUX_24/* * Transfer a buffer directly, without going through the request queue. */int sbull_make_request(request_queue_t *queue, int rw, struct buffer_head *bh){ u8 *ptr; /* Figure out what we are doing */ Sbull_Dev *device = sbull_devices + MINOR(bh->b_rdev); ptr = device->data + bh->b_rsector * sbull_hardsect; /* Paranoid check, this apparently can really happen */ if (ptr + bh->b_size > device->data + sbull_blksize*sbull_size) { static int count = 0; if (count++ < 5) printk(KERN_WARNING "sbull: request past end of device\n"); bh->b_end_io(bh, 0); return 0; } /* This could be a high memory buffer, shift it down */#if CONFIG_HIGHMEM bh = create_bounce(rw, bh);#endif /* Do the transfer */ switch(rw) { case READ: case READA: /* Readahead */ memcpy(bh->b_data, ptr, bh->b_size); /* from sbull to buffer */ bh->b_end_io(bh, 1); break; case WRITE: refile_buffer(bh); memcpy(ptr, bh->b_data, bh->b_size); /* from buffer to sbull */ mark_buffer_uptodate(bh, 1); bh->b_end_io(bh, 1); break; default: /* can't happen */ bh->b_end_io(bh, 0); break; } /* Nonzero return means we're done */ return 0;}void sbull_unused_request(request_queue_t *q){ static int count = 0; if (count++ < 5) printk(KERN_WARNING "sbull: unused_request called\n");} #endif /* LINUX_24 */#if defined(SBULL_EMPTY_REQUEST)/* * This empty request function just prints the interesting items * of the current request. The sectors affected by the request * are printed as <first-sector>-<number-of-sectors>. */#ifdef LINUX_24void sbull_request(request_queue_t *q)#else void sbull_request()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -