📄 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/module.h>#include <linux/fs.h>#include <linux/kernel.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/delay.h>#include <linux/mutex.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/scsicam.h>#include <scsi/sd.h>#include "scsi_logging.h"MODULE_AUTHOR("Eric Youngdale");MODULE_DESCRIPTION("SCSI disk (sd) driver");MODULE_LICENSE("GPL");MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK0_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK1_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK2_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK3_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK4_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK5_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK6_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK7_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK8_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK9_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK10_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK11_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK12_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK13_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK14_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR);MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK);MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD);MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);static int sd_revalidate_disk(struct gendisk *);static int sd_probe(struct device *);static int sd_remove(struct device *);static void sd_shutdown(struct device *);static int sd_suspend(struct device *, pm_message_t state);static int sd_resume(struct device *);static void sd_rescan(struct device *);static int sd_done(struct scsi_cmnd *);static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);static void scsi_disk_release(struct class_device *cdev);static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);static void sd_print_result(struct scsi_disk *, int);static DEFINE_IDR(sd_index_idr);static DEFINE_SPINLOCK(sd_index_lock);/* 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 DEFINE_MUTEX(sd_ref_mutex);static const char *sd_cache_types[] = { "write through", "none", "write back", "write back, no read (daft)"};static ssize_t sd_store_cache_type(struct class_device *cdev, const char *buf, size_t count){ int i, ct = -1, rcd, wce, sp; struct scsi_disk *sdkp = to_scsi_disk(cdev); struct scsi_device *sdp = sdkp->device; char buffer[64]; char *buffer_data; struct scsi_mode_data data; struct scsi_sense_hdr sshdr; int len; if (sdp->type != TYPE_DISK) /* no cache control on RBC devices; theoretically they * can do it, but there's probably so many exceptions * it's not worth the risk */ return -EINVAL; for (i = 0; i < ARRAY_SIZE(sd_cache_types); i++) { const int len = strlen(sd_cache_types[i]); if (strncmp(sd_cache_types[i], buf, len) == 0 && buf[len] == '\n') { ct = i; break; } } if (ct < 0) return -EINVAL; rcd = ct & 0x01 ? 1 : 0; wce = ct & 0x02 ? 1 : 0; if (scsi_mode_sense(sdp, 0x08, 8, buffer, sizeof(buffer), SD_TIMEOUT, SD_MAX_RETRIES, &data, NULL)) return -EINVAL; len = min_t(size_t, sizeof(buffer), data.length - data.header_length - data.block_descriptor_length); buffer_data = buffer + data.header_length + data.block_descriptor_length; buffer_data[2] &= ~0x05; buffer_data[2] |= wce << 2 | rcd; sp = buffer_data[0] & 0x80 ? 1 : 0; if (scsi_mode_select(sdp, 1, sp, 8, buffer_data, len, SD_TIMEOUT, SD_MAX_RETRIES, &data, &sshdr)) { if (scsi_sense_valid(&sshdr)) sd_print_sense_hdr(sdkp, &sshdr); return -EINVAL; } sd_revalidate_disk(sdkp->disk); return count;}static ssize_t sd_store_manage_start_stop(struct class_device *cdev, const char *buf, size_t count){ struct scsi_disk *sdkp = to_scsi_disk(cdev); struct scsi_device *sdp = sdkp->device; if (!capable(CAP_SYS_ADMIN)) return -EACCES; sdp->manage_start_stop = simple_strtoul(buf, NULL, 10); return count;}static ssize_t sd_store_allow_restart(struct class_device *cdev, const char *buf, size_t count){ struct scsi_disk *sdkp = to_scsi_disk(cdev); struct scsi_device *sdp = sdkp->device; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (sdp->type != TYPE_DISK) return -EINVAL; sdp->allow_restart = simple_strtoul(buf, NULL, 10); return count;}static ssize_t sd_show_cache_type(struct class_device *cdev, char *buf){ struct scsi_disk *sdkp = to_scsi_disk(cdev); int ct = sdkp->RCD + 2*sdkp->WCE; return snprintf(buf, 40, "%s\n", sd_cache_types[ct]);}static ssize_t sd_show_fua(struct class_device *cdev, char *buf){ struct scsi_disk *sdkp = to_scsi_disk(cdev); return snprintf(buf, 20, "%u\n", sdkp->DPOFUA);}static ssize_t sd_show_manage_start_stop(struct class_device *cdev, char *buf){ struct scsi_disk *sdkp = to_scsi_disk(cdev); struct scsi_device *sdp = sdkp->device; return snprintf(buf, 20, "%u\n", sdp->manage_start_stop);}static ssize_t sd_show_allow_restart(struct class_device *cdev, char *buf){ struct scsi_disk *sdkp = to_scsi_disk(cdev); return snprintf(buf, 40, "%d\n", sdkp->device->allow_restart);}static struct class_device_attribute sd_disk_attrs[] = { __ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type, sd_store_cache_type), __ATTR(FUA, S_IRUGO, sd_show_fua, NULL), __ATTR(allow_restart, S_IRUGO|S_IWUSR, sd_show_allow_restart, sd_store_allow_restart), __ATTR(manage_start_stop, S_IRUGO|S_IWUSR, sd_show_manage_start_stop, sd_store_manage_start_stop), __ATTR_NULL,};static struct class sd_disk_class = { .name = "scsi_disk", .owner = THIS_MODULE, .release = scsi_disk_release, .class_dev_attrs = sd_disk_attrs,};static struct scsi_driver sd_template = { .owner = THIS_MODULE, .gendrv = { .name = "sd", .probe = sd_probe, .remove = sd_remove, .suspend = sd_suspend, .resume = sd_resume, .shutdown = sd_shutdown, }, .rescan = sd_rescan, .done = sd_done,};/* * 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 */ }}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; if (disk->private_data) { sdkp = scsi_disk(disk); if (scsi_device_get(sdkp->device) == 0) class_device_get(&sdkp->cdev); else sdkp = NULL; } return sdkp;}static struct scsi_disk *scsi_disk_get(struct gendisk *disk){ struct scsi_disk *sdkp; mutex_lock(&sd_ref_mutex); sdkp = __scsi_disk_get(disk); mutex_unlock(&sd_ref_mutex); return sdkp;}static struct scsi_disk *scsi_disk_get_from_dev(struct device *dev){ struct scsi_disk *sdkp; mutex_lock(&sd_ref_mutex); sdkp = dev_get_drvdata(dev); if (sdkp) sdkp = __scsi_disk_get(sdkp->disk); mutex_unlock(&sd_ref_mutex); return sdkp;}static void scsi_disk_put(struct scsi_disk *sdkp){ struct scsi_device *sdev = sdkp->device; mutex_lock(&sd_ref_mutex); class_device_put(&sdkp->cdev); scsi_device_put(sdev); mutex_unlock(&sd_ref_mutex);}/** * 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_prep_fn(struct request_queue *q, struct request *rq){ struct scsi_cmnd *SCpnt; struct scsi_device *sdp = q->queuedata; struct gendisk *disk = rq->rq_disk; sector_t block = rq->sector; unsigned int this_count = rq->nr_sectors; unsigned int timeout = sdp->timeout; int ret; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { ret = scsi_setup_blk_pc_cmnd(sdp, rq); goto out; } else if (rq->cmd_type != REQ_TYPE_FS) { ret = BLKPREP_KILL; goto out; } ret = scsi_setup_fs_cmnd(sdp, rq); if (ret != BLKPREP_OK) goto out; SCpnt = rq->special; /* from here on until we're complete, any goto out * is used for a killable error condition */ ret = BLKPREP_KILL; SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt, "sd_init_command: block=%llu, " "count=%d\n", (unsigned long long)block, this_count)); if (!sdp || !scsi_device_online(sdp) || block + rq->nr_sectors > get_capacity(disk)) { SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "Finishing %ld sectors\n", rq->nr_sectors)); SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "Retry with 0x%p\n", SCpnt)); goto out; } 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"); */ goto out; } SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n", (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) || (rq->nr_sectors & 1)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); goto out; } else { block = block >> 1; this_count = this_count >> 1; } } if (sdp->sector_size == 2048) { if ((block & 3) || (rq->nr_sectors & 3)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); goto out; } else { block = block >> 2; this_count = this_count >> 2; } } if (sdp->sector_size == 4096) { if ((block & 7) || (rq->nr_sectors & 7)) { scmd_printk(KERN_ERR, SCpnt, "Bad block number requested\n"); goto out; } else { block = block >> 3; this_count = this_count >> 3; } } if (rq_data_dir(rq) == WRITE) { if (!sdp->writeable) { goto out; } SCpnt->cmnd[0] = WRITE_6; SCpnt->sc_data_direction = DMA_TO_DEVICE; } else if (rq_data_dir(rq) == READ) { SCpnt->cmnd[0] = READ_6; SCpnt->sc_data_direction = DMA_FROM_DEVICE; } else { scmd_printk(KERN_ERR, SCpnt, "Unknown command %x\n", rq->cmd_flags); goto out; } SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "%s %d/%ld 512 byte blocks.\n", (rq_data_dir(rq) == WRITE) ? "writing" : "reading", this_count, rq->nr_sectors)); SCpnt->cmnd[1] = 0; if (block > 0xffffffff) { SCpnt->cmnd[0] += READ_16 - READ_6; SCpnt->cmnd[1] |= blk_fua_rq(rq) ? 0x8 : 0; 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) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -