📄 sd.c
字号:
/* * sd.c Copyright (C) 1992 Drew Eckhardt * Copyright (C) 1993, 1994, 1995 Eric Youngdale * * Linux scsi disk driver * Initial versions: Drew Eckhardt * Subsequent revisions: Eric Youngdale * * <drew@colorado.edu> * * Modified by Eric Youngdale ericy@cais.com to * add scatter-gather, multiple outstanding request, and other * enhancements. * * Modified by Eric Youngdale eric@aib.com to support loadable * low-level scsi drivers. */#include <linux/module.h>#ifdef MODULE/* * This is a variable in scsi.c that is set when we are processing something * after boot time. By definition, this is true when we are a loadable module * ourselves. */#define MODULE_FLAG 1#else#define MODULE_FLAG scsi_loadable_module_flag#endif /* MODULE */#include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <asm/system.h>#define MAJOR_NR SCSI_DISK_MAJOR#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#include "sd.h"#include <scsi/scsi_ioctl.h>#include "constants.h"#include <linux/genhd.h>/* * static const char RCSid[] = "$Header:"; */#define MAX_RETRIES 5/* * Time out in seconds for disks and Magneto-opticals (which are slower). */#define SD_TIMEOUT (20 * HZ)#define SD_MOD_TIMEOUT (25 * HZ)#define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \ SC->device->type != TYPE_MOD)struct hd_struct * sd;Scsi_Disk * rscsi_disks = NULL;static int * sd_sizes;static int * sd_blocksizes;static int * sd_hardsizes; /* Hardware sector size */extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);static int check_scsidisk_media_change(kdev_t);static int fop_revalidate_scsidisk(kdev_t);static int sd_init_onedisk(int);static void requeue_sd_request (Scsi_Cmnd * SCpnt);static int sd_init(void);static void sd_finish(void);static int sd_attach(Scsi_Device *);static int sd_detect(Scsi_Device *);static void sd_detach(Scsi_Device *);struct Scsi_Device_Template sd_template = { NULL, "disk", "sd", NULL, TYPE_DISK, SCSI_DISK_MAJOR, 0, 0, 0, 1, sd_detect, sd_init, sd_finish, sd_attach, sd_detach};static int sd_open(struct inode * inode, struct file * filp){ int target; target = DEVICE_NR(inode->i_rdev); if(target >= sd_template.dev_max || !rscsi_disks[target].device) return -ENXIO; /* No such device */ /* * Make sure that only one process can do a check_change_disk at one time. * This is also used to lock out further access when the partition table * is being re-read. */ while (rscsi_disks[target].device->busy) barrier(); if(rscsi_disks[target].device->removable) { check_disk_change(inode->i_rdev); /* * If the drive is empty, just let the open fail. */ if ( !rscsi_disks[target].ready ) return -ENXIO; /* * Similarly, 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. */ if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) ) return -EROFS; } /* * See if we are requesting a non-existent partition. Do this * after checking for disk change. */ if(sd_sizes[MINOR(inode->i_rdev)] == 0) return -ENXIO; if(rscsi_disks[target].device->removable) if(!rscsi_disks[target].device->access_count) sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0); rscsi_disks[target].device->access_count++; if (rscsi_disks[target].device->host->hostt->usage_count) (*rscsi_disks[target].device->host->hostt->usage_count)++; if(sd_template.usage_count) (*sd_template.usage_count)++; return 0;}static void sd_release(struct inode * inode, struct file * file){ int target; fsync_dev(inode->i_rdev); target = DEVICE_NR(inode->i_rdev); rscsi_disks[target].device->access_count--; if (rscsi_disks[target].device->host->hostt->usage_count) (*rscsi_disks[target].device->host->hostt->usage_count)--; if(sd_template.usage_count) (*sd_template.usage_count)--; if(rscsi_disks[target].device->removable) { if(!rscsi_disks[target].device->access_count) sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0); }}static void sd_geninit(struct gendisk *);static struct file_operations sd_fops = { NULL, /* lseek - default */ block_read, /* read - general block-dev read */ block_write, /* write - general block-dev write */ NULL, /* readdir - bad */ NULL, /* select */ sd_ioctl, /* ioctl */ NULL, /* mmap */ sd_open, /* open code */ sd_release, /* release */ block_fsync, /* fsync */ NULL, /* fasync */ check_scsidisk_media_change, /* Disk change */ fop_revalidate_scsidisk /* revalidate */};static struct gendisk sd_gendisk = { MAJOR_NR, /* Major number */ "sd", /* Major name */ 4, /* Bits to shift to get real from partition */ 1 << 4, /* Number of partitions per real */ 0, /* maximum number of real */ sd_geninit, /* init function */ NULL, /* hd struct */ NULL, /* block sizes */ 0, /* number */ NULL, /* internal */ NULL /* next */};static void sd_geninit (struct gendisk *ignored){ int i; for (i = 0; i < sd_template.dev_max; ++i) if(rscsi_disks[i].device) sd[i << 4].nr_sects = rscsi_disks[i].capacity;#if 0 /* No longer needed - we keep track of this as we attach/detach */ sd_gendisk.nr_real = sd_template.dev_max;#endif}/* * rw_intr is the interrupt routine for the device driver. * It will be notified on the end of a SCSI read / write, and * will take one of several actions based on success or failure. */static void rw_intr (Scsi_Cmnd *SCpnt){ int result = SCpnt->result; int this_count = SCpnt->bufflen >> 9; int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 1; #ifdef DEBUG printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev), SCpnt->host->host_no, result);#endif /* Handle MEDIUM ERRORs that indicate partial success. Since this is a relatively rare error condition, no care is taken to avoid unnecessary additional work such as memcpy's that could be avoided. */ if (driver_byte(result) != 0 && /* An error occurred */ SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */ SCpnt->sense_buffer[2] == MEDIUM_ERROR) { long error_sector = (SCpnt->sense_buffer[3] << 24) | (SCpnt->sense_buffer[4] << 16) | (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; int sector_size = rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].sector_size; if (SCpnt->request.bh != NULL) block_sectors = SCpnt->request.bh->b_size >> 9; if (sector_size == 1024) { error_sector <<= 1; if (block_sectors < 2) block_sectors = 2; } else if (sector_size == 256) error_sector >>= 1; error_sector -= sd[MINOR(SCpnt->request.rq_dev)].start_sect; error_sector &= ~ (block_sectors - 1); good_sectors = error_sector - SCpnt->request.sector; if (good_sectors < 0 || good_sectors >= this_count) good_sectors = 0; } /* * Handle RECOVERED ERRORs that indicate success after recovery action * by the target device. */ if (SCpnt->sense_buffer[0] == 0xF0 && /* Sense data is valid */ SCpnt->sense_buffer[2] == RECOVERED_ERROR) { printk("scsidisk recovered I/O error: dev %s, sector %lu, absolute sector %lu\n", kdevname(SCpnt->request.rq_dev), SCpnt->request.sector, SCpnt->request.sector + sd[MINOR(SCpnt->request.rq_dev)].start_sect); good_sectors = this_count; result = 0; } /* * First case : we assume that the command succeeded. One of two things * will happen here. Either we will be finished, or there will be more * sectors that we were unable to read last time. */ if (good_sectors > 0) { #ifdef DEBUG printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev), SCpnt->request.nr_sectors); printk("use_sg is %d\n ",SCpnt->use_sg);#endif if (SCpnt->use_sg) { struct scatterlist * sgpnt; int i; sgpnt = (struct scatterlist *) SCpnt->buffer; for(i=0; i<SCpnt->use_sg; i++) {#ifdef DEBUG printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);#endif if (sgpnt[i].alt_address) { if (SCpnt->request.cmd == READ) memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length); scsi_free(sgpnt[i].address, sgpnt[i].length); } } /* Free list of scatter-gather pointers */ scsi_free(SCpnt->buffer, SCpnt->sglist_len); } else { if (SCpnt->buffer != SCpnt->request.buffer) {#ifdef DEBUG printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen);#endif if (SCpnt->request.cmd == READ) memcpy(SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen); scsi_free(SCpnt->buffer, SCpnt->bufflen); } } /* * If multiple sectors are requested in one buffer, then * they will have been finished off by the first command. * If not, then we have a multi-buffer command. */ if (SCpnt->request.nr_sectors > this_count) { SCpnt->request.errors = 0; if (!SCpnt->request.bh) {#ifdef DEBUG printk("sd%c : handling page request, no buffer\n", 'a' + MINOR(SCpnt->request.rq_dev));#endif /* * The SCpnt->request.nr_sectors field is always done in * 512 byte sectors, even if this really isn't the case. */ panic("sd.c: linked page request (%lx %x)", SCpnt->request.sector, this_count); } } SCpnt = end_scsi_request(SCpnt, 1, good_sectors); if (result == 0) { requeue_sd_request(SCpnt); return; } } if (good_sectors == 0) { /* Free up any indirection buffers we allocated for DMA purposes. */ if (SCpnt->use_sg) { struct scatterlist * sgpnt; int i; sgpnt = (struct scatterlist *) SCpnt->buffer; for(i=0; i<SCpnt->use_sg; i++) {#ifdef DEBUG printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen);#endif if (sgpnt[i].alt_address) { scsi_free(sgpnt[i].address, sgpnt[i].length); } } scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */ } else {#ifdef DEBUG printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer, SCpnt->bufflen);#endif if (SCpnt->buffer != SCpnt->request.buffer) scsi_free(SCpnt->buffer, SCpnt->bufflen); } } /* * Now, if we were good little boys and girls, Santa left us a request * sense buffer. We can extract information from this, so we * can choose a block to remap, etc. */ if (driver_byte(result) != 0) { if (suggestion(result) == SUGGEST_REMAP) {#ifdef REMAP /* * Not yet implemented. A read will fail after being remapped, * a write will call the strategy routine again. */ if rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].remap { result = 0; } else#endif } if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) { if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) { if(rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->removable) { /* detected disc change. set a bit and quietly refuse * further access. */ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1; SCpnt = end_scsi_request(SCpnt, 0, this_count); requeue_sd_request(SCpnt); return; } else { /* * Must have been a power glitch, or a bus reset. * Could not have been a media change, so we just retry * the request and see what happens. */ requeue_sd_request(SCpnt); return; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -