scsi_debug.c
来自「linux 内核源代码」· C语言 代码 · 共 2,078 行 · 第 1/5 页
C
2,078 行
/* * 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. To be more * realistic, the simulated devices have the transport attributes of * SAS disks. * * * For documentation see http://www.torque.net/sg/sdebug26.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/module.h>#include <linux/kernel.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/vmalloc.h>#include <linux/moduleparam.h>#include <linux/scatterlist.h>#include <linux/blkdev.h>#include "scsi.h"#include <scsi/scsi_host.h>#include <scsi/scsicam.h>#include <linux/stat.h>#include "scsi_logging.h"#include "scsi_debug.h"#define SCSI_DEBUG_VERSION "1.81"static const char * scsi_debug_version_date = "20070104";/* Additional Sense Code (ASC) */#define NO_ADDITIONAL_SENSE 0x0#define LOGICAL_UNIT_NOT_READY 0x4#define UNRECOVERED_READ_ERR 0x11#define PARAMETER_LIST_LENGTH_ERR 0x1a#define INVALID_OPCODE 0x20#define ADDR_OUT_OF_RANGE 0x21#define INVALID_FIELD_IN_CDB 0x24#define INVALID_FIELD_IN_PARAM_LIST 0x26#define POWERON_RESET 0x29#define SAVING_PARAMS_UNSUP 0x39#define TRANSPORT_PROBLEM 0x4b#define THRESHOLD_EXCEEDED 0x5d#define LOW_POWER_COND_ON 0x5e/* Additional Sense Code Qualifier (ASCQ) */#define ACK_NAK_TO 0x3#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 5 /* INQUIRY, byte2 [5->SPC-3] */#define DEF_PTYPE 0#define DEF_D_SENSE 0#define DEF_NO_LUN_0 0#define DEF_VIRTUAL_GB 0#define DEF_FAKE_RW 0#define DEF_VPD_USE_HOSTNO 1/* 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#define SCSI_DEBUG_OPT_TRANSPORT_ERR 16/* 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. * - a TRANSPORT_ERROR is simulated on successful read and write * commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set. * * When "every_nth" < 0 then after "- 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. * - a TRANSPORT_ERROR is simulated on successful read and write * commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set. * This will continue until some other action occurs (e.g. the user * writing a new value (other than -1 or 1) to every_nth via sysfs). *//* 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 0#define SAM2_WLUN_REPORT_LUNS 0xc101static 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_dsense = DEF_D_SENSE;static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;static int scsi_debug_fake_rw = DEF_FAKE_RW;static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;static int scsi_debug_cmnd_count = 0;#define DEV_READONLY(TGT) (0)#define DEV_REMOVEABLE(TGT) (0)static unsigned int sdebug_store_size; /* in bytes */static unsigned int sdebug_store_sectors;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; unsigned int wlun; char reset; char stopped; 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 DEFINE_SPINLOCK(sdebug_host_list_lock);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 struct 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 = 256, .cmd_per_lun = 16, .max_sectors = 0xffff, .unchecked_isa_dma = 0, .use_clustering = ENABLE_CLUSTERING, .module = THIS_MODULE,};static unsigned char * fake_storep; /* ramdisk storage */static int num_aborts = 0;static int num_dev_resets = 0;static int num_bus_resets = 0;static int num_host_resets = 0;static DEFINE_SPINLOCK(queued_arr_lock);static DEFINE_RWLOCK(atomic_rw);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,};static const int check_condition_result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b};static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0};/* function declarations */static int resp_inquiry(struct scsi_cmnd * SCpnt, int target, struct sdebug_dev_info * devip);static int resp_requests(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip);static int resp_start_stop(struct scsi_cmnd * scp, struct sdebug_dev_info * devip);static int resp_report_tgtpgs(struct scsi_cmnd * scp, struct sdebug_dev_info * devip);static int resp_readcap(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip);static int resp_readcap16(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip);static int resp_mode_sense(struct scsi_cmnd * scp, int target, struct sdebug_dev_info * devip);static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, struct sdebug_dev_info * devip);static int resp_log_sense(struct scsi_cmnd * scp, struct sdebug_dev_info * devip);static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba, unsigned int num, struct sdebug_dev_info * devip);static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba, unsigned int num, struct sdebug_dev_info * devip);static int resp_report_luns(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip);static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr, int arr_len);static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr, int max_arr_len);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);static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only, 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 port_group_id, int target_dev_id, int dev_id_num, const char * dev_id_str, int dev_id_str_len);static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);static int 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;staticint scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done){ unsigned char *cmd = (unsigned char *) SCpnt->cmnd; int len, k, j; unsigned int num; unsigned long long lba; int errsts = 0; int target = SCpnt->device->id; struct sdebug_dev_info * devip = NULL; int inj_recovered = 0; int inj_transport = 0; int delay_override = 0; if (done == NULL) return 0; /* assume mid level reprocessing command */ SCpnt->resid = 0; if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) { printk(KERN_INFO "scsi_debug: cmd "); for (k = 0, len = SCpnt->cmd_len; k < len; ++k) printk("%02x ", (int)cmd[k]); printk("\n"); } 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) && (SCpnt->device->lun != SAM2_WLUN_REPORT_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 >= abs(scsi_debug_every_nth))) { scsi_debug_cmnd_count = 0; if (scsi_debug_every_nth < -1) scsi_debug_every_nth = -1; 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 */ else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts) inj_transport = 1; /* to reads and writes below */ } if (devip->wlun) { switch (*cmd) { case INQUIRY: case REQUEST_SENSE: case TEST_UNIT_READY: case REPORT_LUNS: break; /* only allowable wlun commands */ default: if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Opcode: 0x%x " "not supported for wlun\n", *cmd); mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0); errsts = check_condition_result; return schedule_resp(SCpnt, devip, done, errsts, 0); } } switch (*cmd) { case INQUIRY: /* mandatory, ignore unit attention */ delay_override = 1; errsts = resp_inquiry(SCpnt, target, devip); break; case REQUEST_SENSE: /* mandatory, ignore unit attention */ delay_override = 1; errsts = resp_requests(SCpnt, devip); break; case REZERO_UNIT: /* actually this is REWIND for SSC */ case START_STOP: errsts = resp_start_stop(SCpnt, devip); break; case ALLOW_MEDIUM_REMOVAL: if ((errsts = check_readiness(SCpnt, 1, devip))) break; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Medium removal %s\n", cmd[4] ? "inhibited" : "enabled"); break; case SEND_DIAGNOSTIC: /* mandatory */ errsts = check_readiness(SCpnt, 1, devip); break; case TEST_UNIT_READY: /* mandatory */ delay_override = 1; errsts = check_readiness(SCpnt, 0, devip); break; case RESERVE: errsts = check_readiness(SCpnt, 1, devip); break;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?