scsi_debug.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,869 行 · 第 1/4 页
C
1,869 行
/* * linux/kernel/scsi_debug.c * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv * Copyright (C) 1992 Eric Youngdale * Simulate a host adapter with 2 disks attached. Do a lot of checking * to make sure that we are not getting blocks mixed up, and PANIC if * anything out of the ordinary is seen. * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * * This version is more generic, simulating a variable number of disk * (or disk like devices) sharing a common amount of RAM * * * For documentation see http://www.torque.net/sg/sdebug25.html * * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] * dpg: work for devfs large number of disks [20010809] * forked for lk 2.5 series [20011216, 20020101] * use vmalloc() more inquiry+mode_sense [20020302] * add timers for delayed responses [20020721] * Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031] * Mike Anderson <andmike@us.ibm.com> sysfs work [20021118] * dpg: change style of boot options to "scsi_debug.num_tgts=2" and * module options to "modprobe scsi_debug num_tgts=2" [20021221] */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/types.h>#include <linux/string.h>#include <linux/genhd.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/smp_lock.h>#include <linux/vmalloc.h>#include <linux/moduleparam.h>#include <linux/blkdev.h>#include "scsi.h"#include <scsi/scsi_host.h>#include <scsi/scsicam.h>#include <linux/stat.h>#ifndef LINUX_VERSION_CODE#include <linux/version.h>#endif#include "scsi_logging.h"#include "scsi_debug.h"#define SCSI_DEBUG_VERSION "1.73"static const char * scsi_debug_version_date = "20040518";/* Additional Sense Code (ASC) used */#define NO_ADDED_SENSE 0x0#define UNRECOVERED_READ_ERR 0x11#define INVALID_OPCODE 0x20#define ADDR_OUT_OF_RANGE 0x21#define INVALID_FIELD_IN_CDB 0x24#define POWERON_RESET 0x29#define SAVING_PARAMS_UNSUP 0x39#define THRESHHOLD_EXCEEDED 0x5d#define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG *//* Default values for driver parameters */#define DEF_NUM_HOST 1#define DEF_NUM_TGTS 1#define DEF_MAX_LUNS 1/* With these defaults, this driver will make 1 host with 1 target * (id 0) containing 1 logical unit (lun 0). That is 1 device. */#define DEF_DELAY 1#define DEF_DEV_SIZE_MB 8#define DEF_EVERY_NTH 0#define DEF_NUM_PARTS 0#define DEF_OPTS 0#define DEF_SCSI_LEVEL 3#define DEF_PTYPE 0/* bit mask values for scsi_debug_opts */#define SCSI_DEBUG_OPT_NOISE 1#define SCSI_DEBUG_OPT_MEDIUM_ERR 2#define SCSI_DEBUG_OPT_TIMEOUT 4#define SCSI_DEBUG_OPT_RECOVERED_ERR 8/* When "every_nth" > 0 then modulo "every_nth" commands: * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set * - a RECOVERED_ERROR is simulated on successful read and write * commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set. *//* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this * sector on read commands: */#define OPT_MEDIUM_ERR_ADDR 0x1234 /* that's sector 4660 in decimal *//* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1) * or "peripheral device" addressing (value 0) */#define SAM2_LUN_ADDRESS_METHOD 0static int scsi_debug_add_host = DEF_NUM_HOST;static int scsi_debug_delay = DEF_DELAY;static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;static int scsi_debug_every_nth = DEF_EVERY_NTH;static int scsi_debug_max_luns = DEF_MAX_LUNS;static int scsi_debug_num_parts = DEF_NUM_PARTS;static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */static int scsi_debug_opts = DEF_OPTS;static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */static int scsi_debug_cmnd_count = 0;#define DEV_READONLY(TGT) (0)#define DEV_REMOVEABLE(TGT) (0)static unsigned long sdebug_store_size; /* in bytes */static sector_t sdebug_capacity; /* in sectors *//* old BIOS stuff, kernel may get rid of them but some mode sense pages may still need them */static int sdebug_heads; /* heads per disk */static int sdebug_cylinders_per; /* cylinders per surface */static int sdebug_sectors_per; /* sectors per cylinder *//* default sector size is 512 bytes, 2**9 bytes */#define POW2_SECT_SIZE 9#define SECT_SIZE (1 << POW2_SECT_SIZE)#define SECT_SIZE_PER(TGT) SECT_SIZE#define SDEBUG_MAX_PARTS 4#define SDEBUG_SENSE_LEN 32struct sdebug_dev_info { struct list_head dev_list; unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */ unsigned int channel; unsigned int target; unsigned int lun; struct sdebug_host_info *sdbg_host; char reset; char used;};struct sdebug_host_info { struct list_head host_list; struct Scsi_Host *shost; struct device dev; struct list_head dev_info_list;};#define to_sdebug_host(d) \ container_of(d, struct sdebug_host_info, dev)static LIST_HEAD(sdebug_host_list);static spinlock_t sdebug_host_list_lock = SPIN_LOCK_UNLOCKED;typedef void (* done_funct_t) (struct scsi_cmnd *);struct sdebug_queued_cmd { int in_use; struct timer_list cmnd_timer; done_funct_t done_funct; struct scsi_cmnd * a_cmnd; int scsi_result;};static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];static Scsi_Host_Template sdebug_driver_template = { .proc_info = scsi_debug_proc_info, .name = "SCSI DEBUG", .info = scsi_debug_info, .slave_alloc = scsi_debug_slave_alloc, .slave_configure = scsi_debug_slave_configure, .slave_destroy = scsi_debug_slave_destroy, .ioctl = scsi_debug_ioctl, .queuecommand = scsi_debug_queuecommand, .eh_abort_handler = scsi_debug_abort, .eh_bus_reset_handler = scsi_debug_bus_reset, .eh_device_reset_handler = scsi_debug_device_reset, .eh_host_reset_handler = scsi_debug_host_reset, .bios_param = scsi_debug_biosparam, .can_queue = SCSI_DEBUG_CANQUEUE, .this_id = 7, .sg_tablesize = 64, .cmd_per_lun = 3, .max_sectors = 4096, .unchecked_isa_dma = 0, .use_clustering = ENABLE_CLUSTERING, .module = THIS_MODULE,};static unsigned char * fake_storep; /* ramdisk storage */static unsigned char spare_buff[SDEBUG_SENSE_LEN];static int num_aborts = 0;static int num_dev_resets = 0;static int num_bus_resets = 0;static int num_host_resets = 0;static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED;static rwlock_t atomic_rw = RW_LOCK_UNLOCKED;static char sdebug_proc_name[] = "scsi_debug";static int sdebug_driver_probe(struct device *);static int sdebug_driver_remove(struct device *);static struct bus_type pseudo_lld_bus;static struct device_driver sdebug_driverfs_driver = { .name = sdebug_proc_name, .bus = &pseudo_lld_bus, .probe = sdebug_driver_probe, .remove = sdebug_driver_remove,};static const int check_condition_result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;/* function declarations */static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip);static int resp_mode_sense(unsigned char * cmd, int target, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip);static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block, int num, struct sdebug_dev_info * devip);static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, int num, struct sdebug_dev_info * devip);static int resp_report_luns(unsigned char * cmd, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip);static void timer_intr_handler(unsigned long);static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, int asc, int asq, int inbandLen);static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip);static int schedule_resp(struct scsi_cmnd * cmnd, struct sdebug_dev_info * devip, done_funct_t done, int scsi_result, int delta_jiff);static void __init sdebug_build_parts(unsigned char * ramp);static void __init init_all_queued(void);static void stop_all_queued(void);static int stop_queued_cmnd(struct scsi_cmnd * cmnd);static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, const char * dev_id_str, int dev_id_str_len);static void do_create_driverfs_files(void);static void do_remove_driverfs_files(void);static int sdebug_add_adapter(void);static void sdebug_remove_adapter(void);static void sdebug_max_tgts_luns(void);static struct device pseudo_primary;static struct bus_type pseudo_lld_bus;static unsigned char * scatg2virt(const struct scatterlist * sclp){ if (NULL == sclp) return NULL; else if (sclp->page) return (unsigned char *)page_address(sclp->page) + sclp->offset; else return NULL;}staticint scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done){ unsigned char *cmd = (unsigned char *) SCpnt->cmnd; int block, upper_blk, num; unsigned char *buff; int errsts = 0; int target = SCpnt->device->id; int bufflen = SCpnt->request_bufflen; unsigned long capac; struct sdebug_dev_info * devip = NULL; unsigned char * sbuff; int inj_recovered = 0; if (done == NULL) return 0; /* assume mid level reprocessing command */ if (SCpnt->use_sg) { /* just use first element */ struct scatterlist *sgpnt = (struct scatterlist *) SCpnt->request_buffer; buff = scatg2virt(&sgpnt[0]); bufflen = sgpnt[0].length; /* READ and WRITE process scatterlist themselves */ } else buff = (unsigned char *) SCpnt->request_buffer; if (NULL == buff) { buff = spare_buff; /* assume cmd moves no data */ bufflen = SDEBUG_SENSE_LEN; } if(target == sdebug_driver_template.this_id) { printk(KERN_INFO "scsi_debug: initiator's id used as " "target!\n"); return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); } if (SCpnt->device->lun >= scsi_debug_max_luns) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); devip = devInfoReg(SCpnt->device); if (NULL == devip) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); if ((scsi_debug_every_nth > 0) && (++scsi_debug_cmnd_count >= scsi_debug_every_nth)) { scsi_debug_cmnd_count =0; if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) return 0; /* ignore command causing timeout */ else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) inj_recovered = 1; /* to reads and writes below */ } switch (*cmd) { case INQUIRY: /* mandatory */ errsts = resp_inquiry(cmd, target, buff, bufflen, devip); break; case REQUEST_SENSE: /* mandatory */ /* Since this driver indicates autosense by placing the * sense buffer in the scsi_cmnd structure in the response * (when SAM_STAT_CHECK_CONDITION is set), the mid level * shouldn't need to call REQUEST_SENSE */ if (devip) { sbuff = devip->sense_buff; memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ? bufflen : SDEBUG_SENSE_LEN); mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0, 7); } else { memset(buff, 0, bufflen); buff[0] = 0x70; } break; case START_STOP: errsts = check_reset(SCpnt, devip); break; case ALLOW_MEDIUM_REMOVAL: if ((errsts = check_reset(SCpnt, devip))) break; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk("\tMedium removal %s\n", cmd[4] ? "inhibited" : "enabled"); break; case SEND_DIAGNOSTIC: /* mandatory */ memset(buff, 0, bufflen); break; case TEST_UNIT_READY: /* mandatory */ memset(buff, 0, bufflen); break; case RESERVE: errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); break; case RESERVE_10: errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); break; case RELEASE: errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); break; case RELEASE_10: errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); break; case READ_CAPACITY: errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); if (bufflen > 7) { capac = (unsigned long)sdebug_capacity - 1; buff[0] = (capac >> 24); buff[1] = (capac >> 16) & 0xff; buff[2] = (capac >> 8) & 0xff; buff[3] = capac & 0xff; buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; buff[7] = SECT_SIZE_PER(target) & 0xff; } break; case READ_16: case READ_12: case READ_10: case READ_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; if ((*cmd) == READ_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); num = cmd[13] + (cmd[12] << 8) + (cmd[11] << 16) + (cmd[10] << 24); } else if ((*cmd) == READ_12) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); } else if ((*cmd) == READ_10) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[8] + (cmd[7] << 8); } else { block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16); num = cmd[4]; } errsts = resp_read(SCpnt, upper_blk, block, num, devip); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHHOLD_EXCEEDED, 0, 18); errsts = check_condition_result; } break; case REPORT_LUNS: errsts = resp_report_luns(cmd, buff, bufflen, devip); break; case WRITE_16: case WRITE_12: case WRITE_10: case WRITE_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; if ((*cmd) == WRITE_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); num = cmd[13] + (cmd[12] << 8) + (cmd[11] << 16) + (cmd[10] << 24); } else if ((*cmd) == WRITE_12) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); } else if ((*cmd) == WRITE_10) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[8] + (cmd[7] << 8); } else { block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16); num = cmd[4]; } errsts = resp_write(SCpnt, upper_blk, block, num, devip); if (inj_recovered && (0 == errsts)) { mk_sense_buffer(devip, RECOVERED_ERROR, THRESHHOLD_EXCEEDED, 0, 18); errsts = check_condition_result; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?