Linux Device Drivers 2nd Edition (779877), страница 86
Текст из файла (страница 86)
Initialization required a line likeblk_dev[major].request_fn = sbull_request;36422 June 2001 16:41http://openlib.org.uaBackward CompatibilityBecause this method allows for only one queue per major number, the multiqueuecapability of 2.4 kernels is not present in earlier releases. Because there was onlyone queue, the request function did not need the queue as an argument, so it tooknone. Its prototype was as follows:void (*request) (void);Also, all queues had active heads, so blk_queue_headactive did not exist.There was no blk_ioctl function in 2.2 and prior releases. There was, however, amacro called RO_IOCTLS, which could be inserted in a switch statement toimplement BLKROSET and BLKROGET.
sysdep.h in the sample source includes animplementation of blk_ioctl that uses RO_IOCTLS and implements a few other ofthe standard ioctl commands as well:#ifdef RO_IOCTLSstatic inline int blk_ioctl(kdev_t dev, unsigned int cmd,unsigned long arg){int err;switch (cmd) {case BLKRAGET: /* return the read-ahead value */if (!arg) return -EINVAL;err = ! access_ok(VERIFY_WRITE, arg, sizeof(long));if (err) return -EFAULT;PUT_USER(read_ahead[MAJOR(dev)],(long *) arg);return 0;case BLKRASET: /* set the read-ahead value */if (!capable(CAP_SYS_ADMIN)) return -EACCES;if (arg > 0xff) return -EINVAL; /* limit it */read_ahead[MAJOR(dev)] = arg;return 0;case BLKFLSBUF: /* flush */if (! capable(CAP_SYS_ADMIN)) return -EACCES; /* only root */fsync_dev(dev);invalidate_buffers(dev);return 0;RO_IOCTLS(dev, arg);}return -ENOTTY;}#endif/* RO_IOCTLS */The BLKFRAGET, BLKFRASET, BLKSECTGET, BLKSECTSET, BLKELVGET, andBLKELVSET commands were added with Linux 2.2, and BLKPG was added in 2.4.36522 June 2001 16:41http://openlib.org.uaChapter 12: Loading Block DriversLinux 2.0 did not have the max_readahead array.
The max_segments array,instead, existed and was used in Linux 2.0 and 2.2, but device drivers did not normally need to set it.Finally, register_disk did not exist until Linux 2.4. There was, instead, a functioncalled resetup_one_dev, which performed a similar function:resetup_one_dev(struct gendisk *gd, int drive);register_disk is emulated in sysdep.h with the following code:static inline void register_disk(struct gendisk *gdev, kdev_t dev,unsigned minors, struct file_operations *ops, long size){if (! gdev)return;resetup_one_dev(gdev, MINOR(dev) >> gdev->minor_shift);}Linux 2.0 was different, of course, in not supporting any sort of fine-grained SMP.Thus, there was no io_request_lock and much less need to worry about concurrent access to the I/O request queue.One final thing worth keeping in mind: although nobody really knows what willhappen in the 2.5 development series, a major block device overhaul is almostcertain.
Many people are unhappy with the design of this layer, and there is a lotof pressure to redo it.Quick ReferenceThe most important functions and macros used in writing block drivers are summarized here. To save space, however, we do not list the fields of structrequest, struct buffer_head, or struct genhd, and we omit the predefined ioctl commands.#include <linux/fs.h>int register_blkdev(unsigned int major, const char *name,struct block_device_operations *bdops);int unregister_blkdev(unsigned int major, const char *name);These functions are in charge of device registration in the module’s initialization function and device removal in the cleanup function.#include <linux/blkdev.h>blk_init_queue(request_queue_t *queue, request_fn_proc*request);blk_cleanup_queue(request_queue_t *queue);The first function initializes a queue and establishes the request function; thesecond is used at cleanup time.36622 June 2001 16:41http://openlib.org.uaQuick ReferenceBLK_DEFAULT_QUEUE(major)This macro returns a default I/O request queue for a given major number.struct blk_dev_struct blk_dev[MAX_BLKDEV];This array is used by the kernel to find the proper queue for a given request.int read_ahead[];int max_readahead[][];read_ahead contains block-level read-ahead values for every major number.A value of 8 is reasonable for devices like hard disks; the value should begreater for slower media.
max_readahead contains filesystem-level readahead values for every major and minor number, and is not usually changedfrom the system default.int max_sectors[][];This array, indexed by both major and minor number, holds the maximumnumber of sectors that should be merged into a single I/O request.int blksize_size[][];int blk_size[][];int hardsect_size[][];These two-dimensional arrays are indexed by major and minor number. Thedriver is responsible for allocating and deallocating the row in the matrix associated with its major number. The arrays represent the size of device blocks inbytes (it usually is 1 KB), the size of each minor device in kilobytes (notblocks), and the size of the hardware sector in bytes.MAJOR_NRDEVICE_NAMEDEVICE_NR(kdev_t device)DEVICE_INTR#include <linux/blk.h>These macros must be defined by the driver befor e it includes<linux/blk.h>, because they are used within that file.
MAJOR_NR is themajor number for the device, DEVICE_NAME is the name of the device to beused in error messages, DEVICE_NR returns the minor number of the physicaldevice referred to by a device number, and DEVICE_INTR is a little-usedsymbol that points to the device’s bottom-half interrupt handler.spinlock_t io_request_lock;The spinlock that must be held whenever an I/O request queue is beingmanipulated.36722 June 2001 16:41http://openlib.org.uaChapter 12: Loading Block Driversstruct request *CURRENT;This macro points to the current request when the default queue is beingused.
The request structure describes a data chunk to be transferred and isused by the driver’s request function.INIT_REQUEST;end_request(int status);INIT_REQUEST checks the next request on the queue and returns if there areno more requests to execute. end_r equest is called at the completion of ablock request.spinlock_t io_request_lock;The I/O request lock must be held any time that the request queue is beingmanipulated.struct request *blkdev_entry_next_request(struct list_head*head);struct request *blkdev_next_request(struct request *req);struct request *blkdev_prev_request(struct request *req);blkdev_dequeue_request(struct request *req);blkdev_release_request(struct request *req);Various functions for working with the I/O request queue.blk_queue_headactive(request_queue_t *queue, int active);Indicates whether the first request in the queue is being actively processed bythe driver or not.void blk_queue_make_request(request_queue_t *queue,make_request_fn *func);Provides a function to handle block I/O requests directly out of the kernel.end_that_request_first(struct request *req, int status, char*name);end_that_request_last(struct request *req);Handle the stages of completing a block I/O request.
end_that_r equest_last isonly called when all buffers in the request have been processed — that is,when end_that_r equest_first returns 0.bh->b_end_io(struct buffer_head *bh, int status);Signals the completion of I/O on the given buffer.int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned longarg);A utility function that implements most of the standard block device ioctl commands.36822 June 2001 16:41http://openlib.org.uaQuick Referenceint check_disk_change(kdev_t dev);This function checks to see if a media change has occurred on the givendevice, and calls the driver’s revalidate method if a change is detected.#include<linux/gendisk.h>struct gendisk;struct gendisk *gendisk_head;The generic hard disk allows Linux to support partitionable devices easily. Thegendisk structure describes a generic disk; gendisk_head is the beginningof a linked list of structures describing all of the disks on the system.void register_disk(struct gendisk *gd, int drive, unsignedminors, struct block_device_operations *ops, longsize);This function scans the partition table of the disk and rewrites genhd->partto reflect the new partitioning.36922 June 2001 16:41http://openlib.org.uaCHAPTER THIRTEENMMAP ANDDMAThis chapter delves into the area of Linux memory management, with an emphasison techniques that are useful to the device driver writer.
The material in this chapter is somewhat advanced, and not everybody will need a grasp of it. Nonetheless,many tasks can only be done through digging more deeply into the memory management subsystem; it also provides an interesting look into how an important partof the kernel works.The material in this chapter is divided into three sections.