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 + -
显示快捷键?