#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux /sched.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include < linux/errno.h> #include <linux/timer.h> #include <linux/types.h>/ * size_t * /#include <linux/fcntl.h>/ * O_accmode * /#include <linux/hdreg.h>/ * Hdio_getgeo * /#include <linux/kdev_t.h> #include <linux/vmalloc.h> #include <linux/genhd.h> #include <linux/ Blkdev.h> #include <linux/buffer_head.h>/ * Invalidate_bdev * /#include <linux/bio.h>Static intsbull_major = 0;/ * block device number, 0 is automatically assigned * /Module_param (Sbull_major,int, 0);//modulus parameter, this main device number can specify a value when the module is loaded Static intHardsect_size = 512;/ * Hardware Sector size * /Module_param (Hardsect_size,int, 0);/ * ditto * / Static intNsectors = 1024;/ * Number of hardware sectors * /Module_param (Nsectors,int, 0);/ * ditto * / Static intNdevices = 4; Module_param (Ndevices,int, 0);/ * The different "request modes" we can use. */ enum{rm_simple = 0, Rm_full = 1, Rm_noqueue = 2,};Static intRequest_mode = Rm_simple; Module_param (Request_mode,int, 0);#defineSbull_minors 16#defineKernel_sector_size 512#defineInvalidate_delay 30*hz/*SBULL Device Structure * / structsbull_dev{intSize/ * In sector units, device size * /U8 *data;/ * Data array * / ShortUsers/ * Number of users * / ShortMedia_change;/ * Media Change flag * /spinlock_tLock;/ * for Mutex * / structRequest_queue *queue;/ * Device Request queue * / structGendisk *GD;/ * GENDISK structure * / structTimer_list timer;/ * Used to simulate media changes * /};Static structSbull_dev *devices = NULL;/* * Handle an I/O request. Functions for processing I/O copy data */ Static voidSbull_transfer (structSbull_dev *dev, unsignedLongsector, unsignedLongNsect,Char*buffer,intWrite) {unsignedLongoffset = sector * KERNEL_SECTOR_SIZE; UnsignedLongnbytes = Nsect * kernel_sector_size;if(offset + nbytes) > Dev->size) {PRINTK (Kern_notice"Beyond-end write (%ld%ld) \ n", offset, nbytes);return; }if(write) memcpy (dev->data + offset, buffer, nbytes);Elsememcpy (buffer, Dev->data + offset, nbytes); }/* * The simple form of the request function. */ Static voidSbull_request (structRequest_queue *q) {structRequest *req;//define Request structurereq = Blk_fetch_request (q); while(req! = NULL) {structSbull_dev *dev = req->rq_disk->private_data;//Get the first outstanding request in a queue if(Req->cmd_type! = req_type_fs) {//Determine if the file system requestPRINTK (Kern_notice"Skip non-fs request\n"); Blk_end_request_all (req,-eio);//Notification request processing failed, Eio for I/O error. Continue; } sbull_transfer (Dev, Blk_rq_pos (req),//Sector cursor whereBlk_rq_cur_sectors (req),//number of sectors that need to be transferredReq->buffer,//data buffers to be transmitted or acceptedRq_data_dir (req));//Get transmission direction, 0 means read, 1 means write if(!__blk_end_request_cur (req, 0)) {//Next requestreq = NULL; } } }/* * Transfer a single bio. Bio Processing function */ Static intSbull_xfer_bio (structSbull_dev *dev,structBio *bio) {intIstructBio_vec *bvec;//define the actual VEC listsector_t sector = bio->bi_sector;//Define the first sector to be transferredBio_for_each_segment (bvec,bio,i) {//The following macro traverses each of the bio's segments, obtaining a kernel virtual address to access the buffer Char*buffer = __bio_kmap_atomic (BIO,I,KM_USER0);//Through the kmap_atomic () function to get the virtual address of the I-buffer returning bioSbull_transfer (Dev, Sector,index number of the//start sectorBio_cur_bytes (bio)/kernel_sector_size,//number of sectors that need to be transferredBuffer//buffer pointer for data transferBio_data_dir (bio) = = WRITE);//transmission direction, 0 representations from device read, not 0 write from deviceSector + = Bio_cur_bytes (bio)/kernel_sector_size;//return sector number__bio_kunmap_atomic (BIO,KM_USER0);//Returns the kernel virtual address obtained by __bio_kmap_atomic ()}return0; }/* * Transfer a full request. Requests handler function */ Static intSbull_xfer_request (structSbull_dev *dev,structRequest *req) {structBio *bio;intNsect = 0; __rq_for_each_bio (bio, req) {//This macro traverses each bio in the request, passing a pointer for the Sbull_xfer_bio () transferSbull_xfer_bio (dev, bio);//Call bio processing functionNsect + = bio->bi_size/kernel_sector_size;//Number of bytes passed/sector size equals sector number}returnNsect; }/* * Smarter request function that "handles clustering". */ Static voidSbull_full_request (structRequest_queue *q) {structRequest *req;structSbull_dev *dev = q->queuedata; req = Blk_fetch_request (q);/ * Iterate through each request * / while(req! = NULL) {if(Req->cmd_type! = req_type_fs) {//Determine if the request is a file system requestPRINTK (Kern_notice"Skip non-fs request\n"); Blk_end_request_all (req,-eio);//The request is an I/O error Continue; } sbull_xfer_request (Dev, req);//Call Request handler function if(!blk_end_request_cur (req, 0)) {//req point to Next requestreq = NULL; } } }/* * The direct make request version. */ Static intSbull_make_request (structRequest_queue *q,structBio *bio) {structSbull_dev *dev = q->queuedata;intStatus Status = Sbull_xfer_bio (Dev,bio); Bio_endio (bio, status);//Bio_endio () function notification processing end return0; }/* * Open () function */ Static intSbull_open (structBlock_device *BD, fmode_t mode) {structSbull_dev *dev = bd->bd_disk->private_data; Del_timer_sync (&dev->timer);/* Media removal Timer: Destroy Timer */Spin_lock (&dev->Lock);/ * Lock * / if(!dev->users) Check_disk_change (BD); dev->users++; Spin_unlock (&dev->Lock);/ * unlock * / return0; }Static intSbull_release (structGendisk *GD, fmode_t mode) {structSbull_dev *dev = gd->private_data; Spin_lock (&dev->Lock); dev->users--;if(!dev->users) {dev->timer.expires = jiffies + invalidate_delay; Add_timer (&dev->timer);/* Media removal timer: Load timer, 30s */} spin_unlock (&dev->Lock);return0; }/* * Look for a (simulated) media change. */ intSbull_media_change (structGendisk *gd) {structSbull_dev *dev = gd->private_data;returndev->media_change; }/* * Revalidate.we do not take the lock here,for fear of deadlocking with open. * That's needs to be reevaluated. * Call this function kernel will try to re-read the partition table, here This function is simply reset the MEDIA_CHANGE flag bit, and * Clear memory space to simulate inserting a disk */ intSbull_revalidate (structGendisk *gd) {structSbull_dev *dev = gd->private_data;if(Dev->media_change) {dev->media_change = 0; memset (dev->data, 0, dev->size);}return0; }/* * the "Invalidte" function runs out of the device Timer;it sets a flag to * Simulate the removal of the media. */ voidSbull_invalidate (unsignedLongLdev) {structSbull_dev *dev = (structSbull_dev *) Ldev; Spin_lock (&dev->Lock);if(Dev->users | |!dev->data) PRINTK (kern_warning"Sbull:timer sanity check failed\n");ElseDev->media_change = 1; Spin_unlock (&dev->Lock); }/* *ioctl: * Temporary processing of a command: Query request for physical information about the device */ intSbull_ioctl (structBlock_device *BD, fmode_t mode, unsigned cmd, unsignedLongARG) {LongSizestructHd_geometry Geo;structSbull_dev *dev = bd->bd_disk->private_data;Switch(CMD) { CaseHdio_getgeo:size = dev->size* (hardsect_size/kernel_sector_size); Geo.cylinders = (Size & ~0x3f) >> 6; Geo.sectors = 16; Geo.heads = 4; Geo.start = 4;if(Copy_to_user (void__user*) arg, &geo,sizeof(GEO)))return-efault;return0; }return-enotty; }/ * Get geometry information * / Static intSbull_getgeo (structBlock_device *BD,structHd_geometry *geo) {LongSizestructSbull_dev *dev = bd->bd_disk->private_data; Size = Dev->size * (hardsect_size/kernel_sector_size); Geo->cylinders = (Size & ~0x3f) >> 6; Geo->heads = 4; Geo->sectors = 16; Geo->start = 4;return0; }/* * the device operations structure. */ Static structBlock_device_operations sbull_ops = {. Owner = This_module,. Open = Sbull_open,. Release = Sbull_release,. media_changed = Sbull_media_change,/* User checks whether the media has been changed (removed) */. Revalidate_disk = Sbull_revalidate,. IOCTL = Sbull_ioctl,. Getgeo = Sbull_getgeo};/ * Initialize the specific implementation of the SBULL_DEV data structure * / Static voidSbull_setup_device (structSbull_dev *dev) {PRINTK (kern_info"sbull:step into into setup_device\n"); memset (Dev, 0,sizeof(structSbull_dev)); Dev->size = nsectors * hardsect_size;//Whole block device size 1024 (sector number) * 512 (sector size)Dev->data = Vmalloc (dev->size);/ * Open up virtual storage space * / if(Dev->data = = NULL) {PRINTK (Kern_notice"Vmalloc failure\n");return; } spin_lock_init (&dev->Lock);/ * Initialize spin lock * /Init_timer (&dev->timer);/ * Initialize timer * /Dev->timer.data = (unsignedLong) Dev; Dev->timer.function = sbull_invalidate;/ * Timeout processing function * / /* * The I/O queue, depending on whether we is using our own * Make_request function or not. */ Switch(Request_mode) { CaseRm_noqueue:dev->queue = Blk_alloc_queue (Gfp_kernel);/ * Assign "request Queue" * / if(Dev->queue = = NULL)GotoOut_vfree; Blk_queue_make_request (Dev->queue, sbull_make_request);/ * Bind the "Manufacturing request" function * / Break; CaseRm_full:dev->queue = Blk_init_queue (Sbull_full_request, &dev->Lock);/ * Request Queue initialization * / if(Dev->queue = = NULL)GotoOut_vfree; Break; CaseRm_simple:dev->queue = Blk_init_queue (Sbull_request, &dev->Lock);/ * Request Queue initialization * / if(Dev->queue = = NULL)GotoOut_vfree; Break;default: PRINTK (Kern_notice"Bad Request mode%d, using simple\n", Request_mode); } blk_queue_logical_block_size (Dev->queue, hardsect_size);/ * Hardware sector size settings * /Dev->queue->queuedata = Dev; DEV->GD = Alloc_disk (sbull_minors);if(!DEV->GD) {/ * Dynamically Assign GENDISK structure * /PRINTK (Kern_notice"Alloc_disk failure\n");GotoOut_vfree; } dev->gd->major = Sbull_major;/ * main device number * /Dev->gd->first_minor = sbull_minors;/ * Secondary Device number * /Dev->gd->fops = &sbull_ops;/ * block device operation structure Body * /Dev->gd->queue = dev->queue;/ * Request queue * /Dev->gd->private_data = Dev;/ * Private Data * /snprintf (Dev->gd->disk_name, 32,"Sbull");/ * The name of the secondary device * / / * The size of each request is an integer multiple of the sector size, and the kernel always considers the sector size to be 512 bytes, so it must be converted/*Set_capacity (DEV->GD, Nsectors * (hardsect_size/kernel_sector_size)); Add_disk (DEV->GD);/ * After completing the above initialization, call the Add_disk function to register the disk device * / return; Out_vfree:if(dev->data) Vfree (dev->data); }Static int__init Sbull_init (void) {intI PRINTK (kern_warning"Sbull:start init\n");//Register a block device, the first parameter is the device number, 0 is the dynamic allocation, the second parameter is the device nameSbull_major = Register_blkdev (Sbull_major,"Sbull");if(sbull_major <= 0) {PRINTK (kern_warning"sbull:unable to get major number\n");return-ebusy; } PRINTK (Kern_info"Sbull:start kmalloc\n"); Devices = Kmalloc (ndevices *sizeof(structSbull_dev), Gfp_kernel);/ * Allocate space for block core data structure Sbull_dev * / if(Devices = = NULL)GotoOut_unregister; PRINTK (kern_warning"Sbull:start setup_device\n"); Sbull_setup_device (Devices);/ * Initialize the SBULL_DEV core data structure and add_disk*/ return0; Out_unregister:unregister_blkdev (Sbull_major,"Sbull");return-enomem; }Static voidSbull_exit (void) {structSbull_dev *dev = Devices; Del_timer_sync (&dev->timer);if(DEV->GD) {Del_gendisk (DEV->GD); Put_disk (DEV->GD);}if(Dev->queue) {if(Request_mode = = Rm_noqueue) kobject_put (& (Dev->queue)->kobj);ElseBlk_cleanup_queue (Dev->queue); }if(dev->data) Vfree (dev->data); Unregister_blkdev (Sbull_major,"Sbull"); Kfree (Devices); } module_license ("Dual BSD/GPL"); Module_init (Sbull_init); Module_exit (Sbull_exit);
Linux block device driver (iv)--Simple Sbull instance