📄 sd.c
字号:
/* * sd.c Copyright (C) 1992 Drew Eckhardt * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale * * Linux scsi disk driver * Initial versions: Drew Eckhardt * Subsequent revisions: Eric Youngdale * Modification history: * - Drew Eckhardt <drew@colorado.edu> original * - Eric Youngdale <eric@andante.org> add scatter-gather, multiple * outstanding request, and other enhancements. * Support loadable low-level scsi drivers. * - Jirka Hanika <geo@ff.cuni.cz> support more scsi disks using * eight major numbers. * - Richard Gooch <rgooch@atnf.csiro.au> support devfs. * - Torben Mathiasen <tmm@image.dk> Resource allocation fixes in * sd_init and cleanups. * - Alex Davis <letmein@erols.com> Fix problem where partition info * not being read in sd_open. Fix problem where removable media * could be ejected after sd_open. * - Douglas Gilbert <dgilbert@interlog.com> cleanup for lk 2.5.x * - Badari Pulavarty <pbadari@us.ibm.com>, Matthew Wilcox * <willy@debian.org>, Kurt Garloff <garloff@suse.de>: * Support 32k/1M disks. * * Logging policy (needs CONFIG_SCSI_LOGGING defined): * - setting up transfer: SCSI_LOG_HLQUEUE levels 1 and 2 * - end of transfer (bh + scsi_lib): SCSI_LOG_HLCOMPLETE level 1 * - entering sd_ioctl: SCSI_LOG_IOCTL level 1 * - entering other commands: SCSI_LOG_HLQUEUE level 3 * Note: when the logging level is set by the user, it must be greater * than the level indicated above to trigger output. */#include <linux/config.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/bio.h>#include <linux/genhd.h>#include <linux/hdreg.h>#include <linux/errno.h>#include <linux/idr.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/kref.h>#include <asm/uaccess.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_host.h>#include <scsi/scsi_ioctl.h>#include <scsi/scsi_request.h>#include <scsi/scsicam.h>#include "scsi_logging.h"/* * More than enough for everybody ;) The huge number of majors * is a leftover from 16bit dev_t days, we don't really need that * much numberspace. */#define SD_MAJORS 16/* * This is limited by the naming scheme enforced in sd_probe, * add another character to it if you really need more disks. */#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26)/* * Time out in seconds for disks and Magneto-opticals (which are slower). */#define SD_TIMEOUT (30 * HZ)#define SD_MOD_TIMEOUT (75 * HZ)/* * Number of allowed retries */#define SD_MAX_RETRIES 5static void scsi_disk_release(struct kref *kref);struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; struct kref kref; struct gendisk *disk; unsigned int openers; /* protected by BKL for now, yuck */ sector_t capacity; /* size in 512-byte sectors */ u32 index; u8 media_present; u8 write_prot; unsigned WCE : 1; /* state of disk WCE bit */ unsigned RCD : 1; /* state of disk RCD bit, unused */};static DEFINE_IDR(sd_index_idr);static spinlock_t sd_index_lock = SPIN_LOCK_UNLOCKED;/* This semaphore is used to mediate the 0->1 reference get in the * face of object destruction (i.e. we can't allow a get on an * object after last put) */static DECLARE_MUTEX(sd_ref_sem);static int sd_revalidate_disk(struct gendisk *disk);static void sd_rw_intr(struct scsi_cmnd * SCpnt);static int sd_probe(struct device *);static int sd_remove(struct device *);static void sd_shutdown(struct device *dev);static void sd_rescan(struct device *);static int sd_init_command(struct scsi_cmnd *);static int sd_issue_flush(struct device *, sector_t *);static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, struct scsi_request *SRpnt, unsigned char *buffer);static struct scsi_driver sd_template = { .owner = THIS_MODULE, .gendrv = { .name = "sd", .probe = sd_probe, .remove = sd_remove, .shutdown = sd_shutdown, }, .rescan = sd_rescan, .init_command = sd_init_command, .issue_flush = sd_issue_flush,};/* * Device no to disk mapping: * * major disc2 disc p1 * |............|.............|....|....| <- dev_t * 31 20 19 8 7 4 3 0 * * Inside a major, we have 16k disks, however mapped non- * contiguously. The first 16 disks are for major0, the next * ones with major1, ... Disk 256 is for major0 again, disk 272 * for major1, ... * As we stay compatible with our numbering scheme, we can reuse * the well-know SCSI majors 8, 65--71, 136--143. */static int sd_major(int major_idx){ switch (major_idx) { case 0: return SCSI_DISK0_MAJOR; case 1 ... 7: return SCSI_DISK1_MAJOR + major_idx - 1; case 8 ... 15: return SCSI_DISK8_MAJOR + major_idx - 8; default: BUG(); return 0; /* shut up gcc */ }}#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kref)static inline struct scsi_disk *scsi_disk(struct gendisk *disk){ return container_of(disk->private_data, struct scsi_disk, driver);}static struct scsi_disk *scsi_disk_get(struct gendisk *disk){ struct scsi_disk *sdkp = NULL; down(&sd_ref_sem); if (disk->private_data == NULL) goto out; sdkp = scsi_disk(disk); kref_get(&sdkp->kref); if (scsi_device_get(sdkp->device)) goto out_put; up(&sd_ref_sem); return sdkp; out_put: kref_put(&sdkp->kref, scsi_disk_release); sdkp = NULL; out: up(&sd_ref_sem); return sdkp;}static void scsi_disk_put(struct scsi_disk *sdkp){ down(&sd_ref_sem); scsi_device_put(sdkp->device); kref_put(&sdkp->kref, scsi_disk_release); up(&sd_ref_sem);}/** * sd_init_command - build a scsi (read or write) command from * information in the request structure. * @SCpnt: pointer to mid-level's per scsi command structure that * contains request and into which the scsi command is written * * Returns 1 if successful and 0 if error (or cannot be done now). **/static int sd_init_command(struct scsi_cmnd * SCpnt){ unsigned int this_count, timeout; struct gendisk *disk; sector_t block; struct scsi_device *sdp = SCpnt->device; timeout = sdp->timeout; /* * these are already setup, just copy cdb basically */ if (SCpnt->request->flags & REQ_BLOCK_PC) { struct request *rq = SCpnt->request; if (sizeof(rq->cmd) > sizeof(SCpnt->cmnd)) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; else if (rq->data_len) SCpnt->sc_data_direction = DMA_FROM_DEVICE; else SCpnt->sc_data_direction = DMA_NONE; this_count = rq->data_len; if (rq->timeout) timeout = rq->timeout; SCpnt->transfersize = rq->data_len; goto queue; } /* * we only do REQ_CMD and REQ_BLOCK_PC */ if (!(SCpnt->request->flags & REQ_CMD)) return 0; disk = SCpnt->request->rq_disk; block = SCpnt->request->sector; this_count = SCpnt->request_bufflen >> 9; SCSI_LOG_HLQUEUE(1, printk("sd_init_command: disk=%s, block=%llu, " "count=%d\n", disk->disk_name, (unsigned long long)block, this_count)); if (!sdp || !scsi_device_online(sdp) || block + SCpnt->request->nr_sectors > get_capacity(disk)) { SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", SCpnt->request->nr_sectors)); SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt)); return 0; } if (sdp->changed) { /* * quietly refuse to do anything to a changed disc until * the changed bit has been reset */ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */ return 0; } SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n", disk->disk_name, (unsigned long long)block)); /* * If we have a 1K hardware sectorsize, prevent access to single * 512 byte sectors. In theory we could handle this - in fact * the scsi cdrom driver must be able to handle this because * we typically use 1K blocksizes, and cdroms typically have * 2K hardware sectorsizes. Of course, things are simpler * with the cdrom, since it is read-only. For performance * reasons, the filesystems should be able to handle this * and not force the scsi disk driver to use bounce buffers * for this. */ if (sdp->sector_size == 1024) { if ((block & 1) || (SCpnt->request->nr_sectors & 1)) { printk(KERN_ERR "sd: Bad block number requested"); return 0; } else { block = block >> 1; this_count = this_count >> 1; } } if (sdp->sector_size == 2048) { if ((block & 3) || (SCpnt->request->nr_sectors & 3)) { printk(KERN_ERR "sd: Bad block number requested"); return 0; } else { block = block >> 2; this_count = this_count >> 2; } } if (sdp->sector_size == 4096) { if ((block & 7) || (SCpnt->request->nr_sectors & 7)) { printk(KERN_ERR "sd: Bad block number requested"); return 0; } else { block = block >> 3; this_count = this_count >> 3; } } if (rq_data_dir(SCpnt->request) == WRITE) { if (!sdp->writeable) { return 0; } SCpnt->cmnd[0] = WRITE_6; SCpnt->sc_data_direction = DMA_TO_DEVICE; } else if (rq_data_dir(SCpnt->request) == READ) { SCpnt->cmnd[0] = READ_6; SCpnt->sc_data_direction = DMA_FROM_DEVICE; } else { printk(KERN_ERR "sd: Unknown command %lx\n", SCpnt->request->flags);/* overkill panic("Unknown sd command %lx\n", SCpnt->request->flags); */ return 0; } SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n", disk->disk_name, (rq_data_dir(SCpnt->request) == WRITE) ? "writing" : "reading", this_count, SCpnt->request->nr_sectors)); SCpnt->cmnd[1] = 0; if (block > 0xffffffff) { SCpnt->cmnd[0] += READ_16 - READ_6; SCpnt->cmnd[2] = sizeof(block) > 4 ? (unsigned char) (block >> 56) & 0xff : 0; SCpnt->cmnd[3] = sizeof(block) > 4 ? (unsigned char) (block >> 48) & 0xff : 0; SCpnt->cmnd[4] = sizeof(block) > 4 ? (unsigned char) (block >> 40) & 0xff : 0; SCpnt->cmnd[5] = sizeof(block) > 4 ? (unsigned char) (block >> 32) & 0xff : 0; SCpnt->cmnd[6] = (unsigned char) (block >> 24) & 0xff; SCpnt->cmnd[7] = (unsigned char) (block >> 16) & 0xff; SCpnt->cmnd[8] = (unsigned char) (block >> 8) & 0xff; SCpnt->cmnd[9] = (unsigned char) block & 0xff; SCpnt->cmnd[10] = (unsigned char) (this_count >> 24) & 0xff; SCpnt->cmnd[11] = (unsigned char) (this_count >> 16) & 0xff; SCpnt->cmnd[12] = (unsigned char) (this_count >> 8) & 0xff; SCpnt->cmnd[13] = (unsigned char) this_count & 0xff; SCpnt->cmnd[14] = SCpnt->cmnd[15] = 0; } else if ((this_count > 0xff) || (block > 0x1fffff) || SCpnt->device->use_10_for_rw) { if (this_count > 0xffff) this_count = 0xffff; SCpnt->cmnd[0] += READ_10 - READ_6; SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; SCpnt->cmnd[5] = (unsigned char) block & 0xff; SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0; SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff; SCpnt->cmnd[8] = (unsigned char) this_count & 0xff; } else { if (this_count > 0xff) this_count = 0xff; SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f); SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff); SCpnt->cmnd[3] = (unsigned char) block & 0xff; SCpnt->cmnd[4] = (unsigned char) this_count; SCpnt->cmnd[5] = 0; } SCpnt->request_bufflen = SCpnt->bufflen = this_count * sdp->sector_size; /* * We shouldn't disconnect in the middle of a sector, so with a dumb * host adapter, it's safe to assume that we can at least transfer * this many bytes between each connect / disconnect. */ SCpnt->transfersize = sdp->sector_size; SCpnt->underflow = this_count << 9;queue: SCpnt->allowed = SD_MAX_RETRIES; SCpnt->timeout_per_command = timeout; /* * This is the completion routine we use. This is matched in terms * of capability to this function. */ SCpnt->done = sd_rw_intr; /* * This indicates that the command is ready from our end to be * queued. */ return 1;}/** * sd_open - open a scsi disk device * @inode: only i_rdev member may be used * @filp: only f_mode and f_flags may be used * * Returns 0 if successful. Returns a negated errno value in case * of error. * * Note: This can be called from a user context (e.g. fsck(1) ) * or from within the kernel (e.g. as a result of a mount(1) ). * In the latter case @inode and @filp carry an abridged amount * of information as noted above. **/static int sd_open(struct inode *inode, struct file *filp){ struct gendisk *disk = inode->i_bdev->bd_disk; struct scsi_disk *sdkp; struct scsi_device *sdev; int retval; if (!(sdkp = scsi_disk_get(disk))) return -ENXIO; SCSI_LOG_HLQUEUE(3, printk("sd_open: disk=%s\n", disk->disk_name)); sdev = sdkp->device; /* * If the device is in error recovery, wait until it is done. * If the device is offline, then disallow any access to it. */ retval = -ENXIO; if (!scsi_block_when_processing_errors(sdev)) goto error_out; if (sdev->removable || sdkp->write_prot) check_disk_change(inode->i_bdev); /* * If the drive is empty, just let the open fail. */ retval = -ENOMEDIUM; if (sdev->removable && !sdkp->media_present && !(filp->f_flags & O_NDELAY)) goto error_out; /* * If the device has the write protect tab set, have the open fail * if the user expects to be able to write to the thing. */ retval = -EROFS; if (sdkp->write_prot && (filp->f_mode & FMODE_WRITE)) goto error_out; /* * It is possible that the disk changing stuff resulted in * the device being taken offline. If this is the case, * report this to the user, and don't pretend that the * open actually succeeded. */ retval = -ENXIO; if (!scsi_device_online(sdev)) goto error_out; if (!sdkp->openers++ && sdev->removable) { if (scsi_block_when_processing_errors(sdev)) scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); } return 0;error_out: scsi_disk_put(sdkp); return retval; }/** * sd_release - invoked when the (last) close(2) is called on this * scsi disk. * @inode: only i_rdev member may be used * @filp: only f_mode and f_flags may be used * * Returns 0. * * Note: may block (uninterruptible) if error recovery is underway * on this disk. **/static int sd_release(struct inode *inode, struct file *filp){ struct gendisk *disk = inode->i_bdev->bd_disk; struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdev = sdkp->device; SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name)); if (!--sdkp->openers && sdev->removable) { if (scsi_block_when_processing_errors(sdev)) scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); } /* * XXX and what if there are packets in flight and this close() * XXX is followed by a "rmmod sd_mod"? */ scsi_disk_put(sdkp); return 0;}static int sd_hdio_getgeo(struct block_device *bdev, struct hd_geometry __user *loc){ struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk); struct scsi_device *sdp = sdkp->device; struct Scsi_Host *host = sdp->host; int diskinfo[4]; /* default to most commonly used values */ diskinfo[0] = 0x40; /* 1 << 6 */ diskinfo[1] = 0x20; /* 1 << 5 */ diskinfo[2] = sdkp->capacity >> 11; /* override with calculated, extended default, or driver values */ if (host->hostt->bios_param) host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo); else scsicam_bios_param(bdev, sdkp->capacity, diskinfo); if (put_user(diskinfo[0], &loc->heads)) return -EFAULT; if (put_user(diskinfo[1], &loc->sectors)) return -EFAULT; if (put_user(diskinfo[2], &loc->cylinders)) return -EFAULT; if (put_user((unsigned)get_start_sect(bdev),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -