scsi_debug.c 59.3 KB
Newer Older
1
/*
Linus Torvalds's avatar
Linus Torvalds committed
2
 *  linux/kernel/scsi_debug.c
3
 * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
Linus Torvalds's avatar
Linus Torvalds committed
4 5
 *  Copyright (C) 1992  Eric Youngdale
 *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
Linus Torvalds's avatar
Linus Torvalds committed
6
 *  to make sure that we are not getting blocks mixed up, and PANIC if
Linus Torvalds's avatar
Linus Torvalds committed
7
 *  anything out of the ordinary is seen.
8
 * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Linus Torvalds's avatar
Linus Torvalds committed
9
 *
10 11 12
 *  This version is more generic, simulating a variable number of disk
 *  (or disk like devices) sharing a common amount of RAM
 *
Linus Torvalds's avatar
Linus Torvalds committed
13
 *
Douglas Gilbert's avatar
Douglas Gilbert committed
14
 *  For documentation see http://www.torque.net/sg/sdebug26.html
Linus Torvalds's avatar
Linus Torvalds committed
15
 *
16 17 18 19 20
 *   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]
21
 *   Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
22
 *   Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
23 24
 *   dpg: change style of boot options to "scsi_debug.num_tgts=2" and
 *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28 29 30 31 32 33 34 35 36 37
 */

#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>
Linus Torvalds's avatar
Linus Torvalds committed
38
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
#include <linux/proc_fs.h>
Linus Torvalds's avatar
Linus Torvalds committed
40
#include <linux/smp_lock.h>
41
#include <linux/vmalloc.h>
42
#include <linux/moduleparam.h>
Linus Torvalds's avatar
Linus Torvalds committed
43

44
#include <linux/blkdev.h>
Linus Torvalds's avatar
Linus Torvalds committed
45
#include "scsi.h"
46
#include <scsi/scsi_host.h>
47
#include <scsi/scsicam.h>
Linus Torvalds's avatar
Linus Torvalds committed
48

49
#include <linux/stat.h>
Linus Torvalds's avatar
Linus Torvalds committed
50

Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54
#ifndef LINUX_VERSION_CODE
#include <linux/version.h>
#endif

55
#include "scsi_logging.h"
56 57
#include "scsi_debug.h"

James Bottomley's avatar
James Bottomley committed
58
#define SCSI_DEBUG_VERSION "1.75"
Douglas Gilbert's avatar
Douglas Gilbert committed
59
static const char * scsi_debug_version_date = "20050113";
60 61 62 63 64 65 66 67 68 69

/* 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
70 71

#define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
Linus Torvalds's avatar
Linus Torvalds committed
72

73
/* Default values for driver parameters */
74 75 76 77 78 79
#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.
 */
80
#define DEF_DELAY   1
Linus Torvalds's avatar
Linus Torvalds committed
81
#define DEF_DEV_SIZE_MB   8
82
#define DEF_EVERY_NTH   0
83
#define DEF_NUM_PARTS   0
84
#define DEF_OPTS   0
James Bottomley's avatar
James Bottomley committed
85
#define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
86
#define DEF_PTYPE   0
87
#define DEF_D_SENSE   0
88 89

/* bit mask values for scsi_debug_opts */
90 91
#define SCSI_DEBUG_OPT_NOISE   1
#define SCSI_DEBUG_OPT_MEDIUM_ERR   2
92 93 94 95 96 97
#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.
James Bottomley's avatar
James Bottomley committed
98 99 100 101 102 103 104
 *
 * 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.
 * 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).
105
 */
106

107 108 109
/* 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 */
110

111 112 113 114
/* 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

115 116
static int scsi_debug_add_host = DEF_NUM_HOST;
static int scsi_debug_delay = DEF_DELAY;
117
static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
118
static int scsi_debug_every_nth = DEF_EVERY_NTH;
119
static int scsi_debug_max_luns = DEF_MAX_LUNS;
120 121 122
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;
123
static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
124
static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
125
static int scsi_debug_dsense = DEF_D_SENSE;
126 127

static int scsi_debug_cmnd_count = 0;
Linus Torvalds's avatar
Linus Torvalds committed
128

129 130
#define DEV_READONLY(TGT)      (0)
#define DEV_REMOVEABLE(TGT)    (0)
Linus Torvalds's avatar
Linus Torvalds committed
131

132 133 134 135 136 137 138 139
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 */
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143 144

/* 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
Linus Torvalds's avatar
Linus Torvalds committed
145

146 147
#define SDEBUG_MAX_PARTS 4

148
#define SDEBUG_SENSE_LEN 32
Linus Torvalds's avatar
Linus Torvalds committed
149

150
struct sdebug_dev_info {
151
	struct list_head dev_list;
152
	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
153 154 155
	unsigned int channel;
	unsigned int target;
	unsigned int lun;
156
	struct sdebug_host_info *sdbg_host;
Linus Torvalds's avatar
Linus Torvalds committed
157
	char reset;
158
	char used;
159
};
160 161 162 163

struct sdebug_host_info {
	struct list_head host_list;
	struct Scsi_Host *shost;
164
	struct device dev;
165 166 167
	struct list_head dev_info_list;
};

168 169 170
#define to_sdebug_host(d)	\
	container_of(d, struct sdebug_host_info, dev)

171
static LIST_HEAD(sdebug_host_list);
172
static DEFINE_SPINLOCK(sdebug_host_list_lock);
173

174
typedef void (* done_funct_t) (struct scsi_cmnd *);
175 176 177 178 179 180 181 182 183 184

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];

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
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,
James Bottomley's avatar
James Bottomley committed
205
	.use_clustering = 	DISABLE_CLUSTERING,
206 207 208
	.module =		THIS_MODULE,
};

209 210
static unsigned char * fake_storep;	/* ramdisk storage */

Linus Torvalds's avatar
Linus Torvalds committed
211 212 213 214 215
static int num_aborts = 0;
static int num_dev_resets = 0;
static int num_bus_resets = 0;
static int num_host_resets = 0;

216 217
static DEFINE_SPINLOCK(queued_arr_lock);
static DEFINE_RWLOCK(atomic_rw);
Linus Torvalds's avatar
Linus Torvalds committed
218

219
static char sdebug_proc_name[] = "scsi_debug";
220 221 222

static int sdebug_driver_probe(struct device *);
static int sdebug_driver_remove(struct device *);
223
static struct bus_type pseudo_lld_bus;
224

225
static struct device_driver sdebug_driverfs_driver = {
226
	.name 		= sdebug_proc_name,
227
	.bus		= &pseudo_lld_bus,
228 229
	.probe          = sdebug_driver_probe,
	.remove         = sdebug_driver_remove,
230
};
Linus Torvalds's avatar
Linus Torvalds committed
231

232
static const int check_condition_result =
233 234
		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;

235
/* function declarations */
James Bottomley's avatar
James Bottomley committed
236 237 238 239 240 241 242
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_readcap(struct scsi_cmnd * SCpnt,
			struct sdebug_dev_info * devip);
static int resp_mode_sense(struct scsi_cmnd * SCpnt, int target,
243
			   struct sdebug_dev_info * devip);
244
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
245
		     int num, struct sdebug_dev_info * devip);
246 247
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
		      int num, struct sdebug_dev_info * devip);
James Bottomley's avatar
James Bottomley committed
248 249 250 251 252 253
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);
254
static void timer_intr_handler(unsigned long);
255
static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
256
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
James Bottomley's avatar
James Bottomley committed
257
			    int asc, int asq);
258
static int check_reset(struct scsi_cmnd * SCpnt,
259
		       struct sdebug_dev_info * devip);
260 261
static int schedule_resp(struct scsi_cmnd * cmnd,
			 struct sdebug_dev_info * devip,
262
			 done_funct_t done, int scsi_result, int delta_jiff);
263 264
static void __init sdebug_build_parts(unsigned char * ramp);
static void __init init_all_queued(void);
265 266 267 268 269 270
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);
Linus Torvalds's avatar
Linus Torvalds committed
271

272 273
static int sdebug_add_adapter(void);
static void sdebug_remove_adapter(void);
274 275
static void sdebug_max_tgts_luns(void);

276 277
static struct device pseudo_primary;
static struct bus_type pseudo_lld_bus;
278

Linus Torvalds's avatar
Linus Torvalds committed
279

280
static
281
int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
Linus Torvalds's avatar
Linus Torvalds committed
282
{
283
	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
284
	int block, upper_blk, num, k;
285
	int errsts = 0;
286
	int target = SCpnt->device->id;
287
	struct sdebug_dev_info * devip = NULL;
288
	int inj_recovered = 0;
Linus Torvalds's avatar
Linus Torvalds committed
289

290 291
	if (done == NULL)
		return 0;	/* assume mid level reprocessing command */
Linus Torvalds's avatar
Linus Torvalds committed
292

293 294 295 296 297 298
	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
		printk(KERN_INFO "scsi_debug: cmd ");
		for (k = 0, num = SCpnt->cmd_len; k < num; ++k)
			printk("%02x ", (int)cmd[k]);
		printk("\n");
	}
299
        if(target == sdebug_driver_template.this_id) {
300 301 302 303
		printk(KERN_INFO "scsi_debug: initiator's id used as "
		       "target!\n");
		return schedule_resp(SCpnt, NULL, done,
				     DID_NO_CONNECT << 16, 0);
Linus Torvalds's avatar
Linus Torvalds committed
304 305
        }

306
	if (SCpnt->device->lun >= scsi_debug_max_luns)
307
		return schedule_resp(SCpnt, NULL, done,
308
				     DID_NO_CONNECT << 16, 0);
309
	devip = devInfoReg(SCpnt->device);
310
	if (NULL == devip)
311
		return schedule_resp(SCpnt, NULL, done,
312
				     DID_NO_CONNECT << 16, 0);
Linus Torvalds's avatar
Linus Torvalds committed
313

James Bottomley's avatar
James Bottomley committed
314 315 316 317 318
        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;
319 320 321 322
		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 */
323 324
        }

Linus Torvalds's avatar
Linus Torvalds committed
325
	switch (*cmd) {
326
	case INQUIRY:     /* mandatory, ignore unit attention */
James Bottomley's avatar
James Bottomley committed
327
		errsts = resp_inquiry(SCpnt, target, devip);
328
		break;
329
	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
James Bottomley's avatar
James Bottomley committed
330
		errsts = resp_requests(SCpnt, devip);
331
		break;
332
	case REZERO_UNIT:	/* actually this is REWIND for SSC */
Linus Torvalds's avatar
Linus Torvalds committed
333
	case START_STOP:
334
		errsts = check_reset(SCpnt, devip);
Linus Torvalds's avatar
Linus Torvalds committed
335 336
		break;
	case ALLOW_MEDIUM_REMOVAL:
337 338 339
		if ((errsts = check_reset(SCpnt, devip)))
			break;
		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
340 341
			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
			        cmd[4] ? "inhibited" : "enabled");
Linus Torvalds's avatar
Linus Torvalds committed
342
		break;
Linus Torvalds's avatar
Linus Torvalds committed
343
	case SEND_DIAGNOSTIC:     /* mandatory */
344
		errsts = check_reset(SCpnt, devip);
Linus Torvalds's avatar
Linus Torvalds committed
345
		break;
Linus Torvalds's avatar
Linus Torvalds committed
346
	case TEST_UNIT_READY:     /* mandatory */
347
		errsts = check_reset(SCpnt, devip);
Linus Torvalds's avatar
Linus Torvalds committed
348
		break;
349 350 351 352 353 354 355 356 357 358 359 360
        case RESERVE:
		errsts = check_reset(SCpnt, devip);
                break;
        case RESERVE_10:
		errsts = check_reset(SCpnt, devip);
                break;
        case RELEASE:
		errsts = check_reset(SCpnt, devip);
                break;
        case RELEASE_10:
		errsts = check_reset(SCpnt, devip);
                break;
Linus Torvalds's avatar
Linus Torvalds committed
361
	case READ_CAPACITY:
James Bottomley's avatar
James Bottomley committed
362
		errsts = resp_readcap(SCpnt, devip);
Linus Torvalds's avatar
Linus Torvalds committed
363
		break;
364
	case READ_16:
Linus Torvalds's avatar
Linus Torvalds committed
365
	case READ_12:
Linus Torvalds's avatar
Linus Torvalds committed
366 367
	case READ_10:
	case READ_6:
368 369
		if ((errsts = check_reset(SCpnt, devip)))
			break;
Linus Torvalds's avatar
Linus Torvalds committed
370
		upper_blk = 0;
371
		if ((*cmd) == READ_16) {
372
			upper_blk = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
373
				    (cmd[3] << 16) + (cmd[2] << 24);
374
			block = cmd[9] + (cmd[8] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
375
				(cmd[7] << 16) + (cmd[6] << 24);
376
			num = cmd[13] + (cmd[12] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
377
				(cmd[11] << 16) + (cmd[10] << 24);
378
		} else if ((*cmd) == READ_12) {
379
			block = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
380
				(cmd[3] << 16) + (cmd[2] << 24);
381
			num = cmd[9] + (cmd[8] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
382
				(cmd[7] << 16) + (cmd[6] << 24);
383
		} else if ((*cmd) == READ_10) {
384
			block = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
385 386
				(cmd[3] << 16) + (cmd[2] << 24);
			num = cmd[8] + (cmd[7] << 8);
387
		} else {
388
			block = cmd[3] + (cmd[2] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
389 390 391
				((cmd[1] & 0x1f) << 16);
			num = cmd[4];
		}
392
		errsts = resp_read(SCpnt, upper_blk, block, num, devip);
393
		if (inj_recovered && (0 == errsts)) {
394
			mk_sense_buffer(devip, RECOVERED_ERROR,
James Bottomley's avatar
James Bottomley committed
395
					THRESHHOLD_EXCEEDED, 0);
396 397
			errsts = check_condition_result;
		}
398
		break;
399
	case REPORT_LUNS:	/* mandatory, ignore unit attention */
James Bottomley's avatar
James Bottomley committed
400 401 402 403
		errsts = resp_report_luns(SCpnt, devip);
		break;
	case VERIFY:		/* 10 byte SBC-2 command */
		errsts = check_reset(SCpnt, devip);
404
		break;
405
	case WRITE_16:
Linus Torvalds's avatar
Linus Torvalds committed
406
	case WRITE_12:
Linus Torvalds's avatar
Linus Torvalds committed
407 408
	case WRITE_10:
	case WRITE_6:
409 410
		if ((errsts = check_reset(SCpnt, devip)))
			break;
Linus Torvalds's avatar
Linus Torvalds committed
411
		upper_blk = 0;
412
		if ((*cmd) == WRITE_16) {
413
			upper_blk = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
414
				    (cmd[3] << 16) + (cmd[2] << 24);
415
			block = cmd[9] + (cmd[8] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
416
				(cmd[7] << 16) + (cmd[6] << 24);
417
			num = cmd[13] + (cmd[12] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
418
				(cmd[11] << 16) + (cmd[10] << 24);
419
		} else if ((*cmd) == WRITE_12) {
420
			block = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
421
				(cmd[3] << 16) + (cmd[2] << 24);
422
			num = cmd[9] + (cmd[8] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
423
				(cmd[7] << 16) + (cmd[6] << 24);
424
		} else if ((*cmd) == WRITE_10) {
425
			block = cmd[5] + (cmd[4] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
426 427
				(cmd[3] << 16) + (cmd[2] << 24);
			num = cmd[8] + (cmd[7] << 8);
428
		} else {
429
			block = cmd[3] + (cmd[2] << 8) +
Linus Torvalds's avatar
Linus Torvalds committed
430 431 432
				((cmd[1] & 0x1f) << 16);
			num = cmd[4];
		}
433
		errsts = resp_write(SCpnt, upper_blk, block, num, devip);
434
		if (inj_recovered && (0 == errsts)) {
435
			mk_sense_buffer(devip, RECOVERED_ERROR,
James Bottomley's avatar
James Bottomley committed
436
					THRESHHOLD_EXCEEDED, 0);
437 438
			errsts = check_condition_result;
		}
439
		break;
Linus Torvalds's avatar
Linus Torvalds committed
440
	case MODE_SENSE:
441
	case MODE_SENSE_10:
James Bottomley's avatar
James Bottomley committed
442
		errsts = resp_mode_sense(SCpnt, target, devip);
Linus Torvalds's avatar
Linus Torvalds committed
443
		break;
444
	case SYNCHRONIZE_CACHE:
445
		errsts = check_reset(SCpnt, devip);
446
		break;
Linus Torvalds's avatar
Linus Torvalds committed
447
	default:
448 449 450
		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
			       "supported\n", *cmd);
451
		if ((errsts = check_reset(SCpnt, devip)))
452
			break;	/* Unit attention takes precedence */
James Bottomley's avatar
James Bottomley committed
453
		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
454
		errsts = check_condition_result;
455
		break;
Linus Torvalds's avatar
Linus Torvalds committed
456
	}
457
	return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay);
Linus Torvalds's avatar
Linus Torvalds committed
458 459
}

460
static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
461 462 463 464
{
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
	}
465 466
	return -EINVAL;
	/* return -ENOTTY; // correct return but upsets fdisk */
467 468
}

469
static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip)
Linus Torvalds's avatar
Linus Torvalds committed
470 471
{
	if (devip->reset) {
472 473 474
		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
			printk(KERN_INFO "scsi_debug: Reporting Unit "
			       "attention: power on reset\n");
Linus Torvalds's avatar
Linus Torvalds committed
475
		devip->reset = 0;
James Bottomley's avatar
James Bottomley committed
476
		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
477
		return check_condition_result;
Linus Torvalds's avatar
Linus Torvalds committed
478 479 480 481
	}
	return 0;
}

James Bottomley's avatar
James Bottomley committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
/* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
				int arr_len)
{
	int k, req_len, act_len, len, active;
	void * kaddr;
	void * kaddr_off;
	struct scatterlist * sgpnt;

	if (0 == scp->request_bufflen)
		return 0;
	if (NULL == scp->request_buffer)
		return (DID_ERROR << 16);
	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
		return (DID_ERROR << 16);
	if (0 == scp->use_sg) {
		req_len = scp->request_bufflen;
		act_len = (req_len < arr_len) ? req_len : arr_len;
		memcpy(scp->request_buffer, arr, act_len);
		scp->resid = req_len - act_len;
		return 0;
	}
	sgpnt = (struct scatterlist *)scp->request_buffer;
	active = 1;
	for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
		if (active) {
			kaddr = (unsigned char *)
				kmap_atomic(sgpnt->page, KM_USER0);
			if (NULL == kaddr)
				return (DID_ERROR << 16);
			kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
			len = sgpnt->length;
			if ((req_len + len) > arr_len) {
				active = 0;
				len = arr_len - req_len;
			}
			memcpy(kaddr_off, arr + req_len, len);
			kunmap_atomic(kaddr, KM_USER0);
			act_len += len;
		}
		req_len += sgpnt->length;
	}
	scp->resid = req_len - act_len;
	return 0;
}

/* Returns number of bytes fetched into 'arr' or -1 if error. */
static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
			       int max_arr_len)
{
	int k, req_len, len, fin;
	void * kaddr;
	void * kaddr_off;
	struct scatterlist * sgpnt;

	if (0 == scp->request_bufflen)
		return 0;
	if (NULL == scp->request_buffer)
		return -1;
	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
	      (scp->sc_data_direction == DMA_TO_DEVICE)))
		return -1;
	if (0 == scp->use_sg) {
		req_len = scp->request_bufflen;
		len = (req_len < max_arr_len) ? req_len : max_arr_len;
		memcpy(arr, scp->request_buffer, len);
		return len;
	}
	sgpnt = (struct scatterlist *)scp->request_buffer;
	for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
		kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
		if (NULL == kaddr)
			return -1;
		kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
		len = sgpnt->length;
		if ((req_len + len) > max_arr_len) {
			len = max_arr_len - req_len;
			fin = 1;
		}
		memcpy(arr + req_len, kaddr_off, len);
		kunmap_atomic(kaddr, KM_USER0);
		if (fin)
			return req_len + len;
		req_len += sgpnt->length;
	}
	return req_len;
}
570

James Bottomley's avatar
James Bottomley committed
571 572 573 574

static const char * inq_vendor_id = "Linux   ";
static const char * inq_product_id = "scsi_debug      ";
static const char * inq_product_rev = "0004";
575

576
static int inquiry_evpd_83(unsigned char * arr, int dev_id_num,
577 578 579 580 581 582 583 584 585
			   const char * dev_id_str, int dev_id_str_len)
{
	int num;

	/* Two identification descriptors: */
	/* T10 vendor identifier field format (faked) */
	arr[0] = 0x2;	/* ASCII */
	arr[1] = 0x1;
	arr[2] = 0x0;
James Bottomley's avatar
James Bottomley committed
586 587
	memcpy(&arr[4], inq_vendor_id, 8);
	memcpy(&arr[12], inq_product_id, 16);
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	memcpy(&arr[28], dev_id_str, dev_id_str_len);
	num = 8 + 16 + dev_id_str_len;
	arr[3] = num;
	num += 4;
	/* NAA IEEE registered identifier (faked) */
	arr[num] = 0x1;	/* binary */
	arr[num + 1] = 0x3;
	arr[num + 2] = 0x0;
	arr[num + 3] = 0x8;
	arr[num + 4] = 0x51;	/* ieee company id=0x123456 (faked) */
	arr[num + 5] = 0x23;
	arr[num + 6] = 0x45;
	arr[num + 7] = 0x60;
	arr[num + 8] = (dev_id_num >> 24);
	arr[num + 9] = (dev_id_num >> 16) & 0xff;
	arr[num + 10] = (dev_id_num >> 8) & 0xff;
	arr[num + 11] = dev_id_num & 0xff;
	return num + 12;
}
607

James Bottomley's avatar
James Bottomley committed
608 609 610 611 612 613

#define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 128

static int resp_inquiry(struct scsi_cmnd * scp, int target,
			struct sdebug_dev_info * devip)
Linus Torvalds's avatar
Linus Torvalds committed
614
{
615
	unsigned char pq_pdt;
616
	unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
James Bottomley's avatar
James Bottomley committed
617 618
	unsigned char *cmd = (unsigned char *)scp->cmnd;
	int alloc_len;
619

James Bottomley's avatar
James Bottomley committed
620
	alloc_len = (cmd[3] << 8) + cmd[4];
621
	memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
622
	pq_pdt = (scsi_debug_ptype & 0x1f);
623 624
	arr[0] = pq_pdt;
	if (0x2 & cmd[1]) {  /* CMDDT bit set */
625
		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
James Bottomley's avatar
James Bottomley committed
626
			       	0);
627
		return check_condition_result;
628 629 630 631
	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
		int dev_id_num, len;
		char dev_id_str[6];
		
632
		dev_id_num = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
633
			     (devip->target * 1000) + devip->lun;
Andrew Morton's avatar
Andrew Morton committed
634
		len = scnprintf(dev_id_str, 6, "%d", dev_id_num);
635
		if (0 == cmd[2]) { /* supported vital product data pages */
636 637 638 639 640
			arr[3] = 3;
			arr[4] = 0x0; /* this page */
			arr[5] = 0x80; /* unit serial number */
			arr[6] = 0x83; /* device identification */
		} else if (0x80 == cmd[2]) { /* unit serial number */
641
			arr[1] = 0x80;
642 643 644 645 646 647 648
			arr[3] = len;
			memcpy(&arr[4], dev_id_str, len);
		} else if (0x83 == cmd[2]) { /* device identification */
			arr[1] = 0x83;
			arr[3] = inquiry_evpd_83(&arr[4], dev_id_num,
						 dev_id_str, len);
		} else {
649
			/* Illegal request, invalid field in cdb */
650
			mk_sense_buffer(devip, ILLEGAL_REQUEST,
James Bottomley's avatar
James Bottomley committed
651
					INVALID_FIELD_IN_CDB, 0);
652
			return check_condition_result;
653
		}
James Bottomley's avatar
James Bottomley committed
654 655
		return fill_from_dev_buffer(scp, arr,
			    min(alloc_len, SDEBUG_MAX_INQ_ARR_SZ));
656 657 658
	}
	/* drops through here for a standard inquiry */
	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
659
	arr[2] = scsi_debug_scsi_level;
660
	arr[3] = 2;    /* response_data_format==2 */
661
	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
662
	arr[6] = 0x1; /* claim: ADDR16 */
James Bottomley's avatar
James Bottomley committed
663
	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
664
	arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */
James Bottomley's avatar
James Bottomley committed
665 666 667
	memcpy(&arr[8], inq_vendor_id, 8);
	memcpy(&arr[16], inq_product_id, 16);
	memcpy(&arr[32], inq_product_rev, 4);
668 669
	/* version descriptors (2 bytes each) follow */
	arr[58] = 0x0; arr[59] = 0x40; /* SAM-2 */
James Bottomley's avatar
James Bottomley committed
670
	arr[60] = 0x3; arr[61] = 0x0;  /* SPC-3 */
671
	if (scsi_debug_ptype == 0) {
James Bottomley's avatar
James Bottomley committed
672
		arr[62] = 0x1; arr[63] = 0x80; /* SBC */
673
	} else if (scsi_debug_ptype == 1) {
James Bottomley's avatar
James Bottomley committed
674
		arr[62] = 0x2; arr[63] = 0x00; /* SSC */
675
	}
James Bottomley's avatar
James Bottomley committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
	return fill_from_dev_buffer(scp, arr,
			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
}

static int resp_requests(struct scsi_cmnd * scp,
			 struct sdebug_dev_info * devip)
{
	unsigned char * sbuff;
	unsigned char *cmd = (unsigned char *)scp->cmnd;
	unsigned char arr[SDEBUG_SENSE_LEN];
	int len = 18;

	memset(arr, 0, SDEBUG_SENSE_LEN);
	if (devip->reset == 1)
		mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
	sbuff = devip->sense_buff;
	if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
		/* DESC bit set and sense_buff in fixed format */
		arr[0] = 0x72;
		arr[1] = sbuff[2];     /* sense key */
		arr[2] = sbuff[12];    /* asc */
		arr[3] = sbuff[13];    /* ascq */
		len = 8;
	} else
		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
	mk_sense_buffer(devip, 0, NO_ADDED_SENSE, 0);
	return fill_from_dev_buffer(scp, arr, len);
}

#define SDEBUG_READCAP_ARR_SZ 8
static int resp_readcap(struct scsi_cmnd * scp,
			struct sdebug_dev_info * devip)
{
	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
	unsigned long capac;
	int errsts;

	if ((errsts = check_reset(scp, devip)))
		return errsts;
	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
	capac = (unsigned long)sdebug_capacity - 1;
	arr[0] = (capac >> 24);
	arr[1] = (capac >> 16) & 0xff;
	arr[2] = (capac >> 8) & 0xff;
	arr[3] = capac & 0xff;
	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
	arr[7] = SECT_SIZE_PER(target) & 0xff;
	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
724 725
}

726
/* <<Following mode page info copied from ST318451LW>> */
727

728
static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
729
{	/* Read-Write Error Recovery page for mode_sense */
730
	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
731 732 733 734 735 736 737 738
					5, 0, 0xff, 0xff};

	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
	if (1 == pcontrol)
		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
	return sizeof(err_recov_pg);
}

739
static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
740
{ 	/* Disconnect-Reconnect page for mode_sense */
741
	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
742 743 744 745 746 747 748 749
					 0, 0, 0, 0, 0, 0, 0, 0};

	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
	if (1 == pcontrol)
		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
	return sizeof(disconnect_pg);
}

750 751 752 753 754 755 756
static int resp_format_pg(unsigned char * p, int pcontrol, int target)
{       /* Format device page for mode_sense */
        unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
                                     0, 0, 0, 0, 0, 0, 0, 0,
                                     0, 0, 0, 0, 0x40, 0, 0, 0};

        memcpy(p, format_pg, sizeof(format_pg));
757 758
        p[10] = (sdebug_sectors_per >> 8) & 0xff;
        p[11] = sdebug_sectors_per & 0xff;
759 760 761 762 763 764 765 766 767 768
        p[12] = (SECT_SIZE >> 8) & 0xff;
        p[13] = SECT_SIZE & 0xff;
        if (DEV_REMOVEABLE(target))
                p[20] |= 0x20; /* should agree with INQUIRY */
        if (1 == pcontrol)
                memset(p + 2, 0, sizeof(format_pg) - 2);
        return sizeof(format_pg);
}

static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
769
{ 	/* Caching page for mode_sense */
770
	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
771 772 773 774 775 776 777 778
		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};

	memcpy(p, caching_pg, sizeof(caching_pg));
	if (1 == pcontrol)
		memset(p + 2, 0, sizeof(caching_pg) - 2);
	return sizeof(caching_pg);
}

779
static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
780 781 782 783
{ 	/* Control mode page for mode_sense */
	unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
				     0, 0, 0x2, 0x4b};

784 785
	if (scsi_debug_dsense)
		ctrl_m_pg[2] |= 0x4;
786 787 788 789 790 791
	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
	if (1 == pcontrol)
		memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
	return sizeof(ctrl_m_pg);
}

792 793 794 795 796 797 798 799 800
static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
{	/* Informational Exceptions control mode page for mode_sense */
	unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
				    0, 0, 0x0, 0x0};
	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
	if (1 == pcontrol)
		memset(p + 2, 0, sizeof(iec_m_pg) - 2);
	return sizeof(iec_m_pg);
}
801 802 803

#define SDEBUG_MAX_MSENSE_SZ 256

James Bottomley's avatar
James Bottomley committed
804
static int resp_mode_sense(struct scsi_cmnd * scp, int target,
805
			   struct sdebug_dev_info * devip)
806 807
{
	unsigned char dbd;
808
	int pcontrol, pcode, subpcode;
809
	unsigned char dev_spec;
James Bottomley's avatar
James Bottomley committed
810
	int alloc_len, msense_6, offset, len, errsts;
811 812
	unsigned char * ap;
	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
James Bottomley's avatar
James Bottomley committed
813
	unsigned char *cmd = (unsigned char *)scp->cmnd;
814

James Bottomley's avatar
James Bottomley committed
815 816
	if ((errsts = check_reset(scp, devip)))
		return errsts;
817 818 819
	dbd = cmd[1] & 0x8;
	pcontrol = (cmd[2] & 0xc0) >> 6;
	pcode = cmd[2] & 0x3f;
820
	subpcode = cmd[3];
821
	msense_6 = (MODE_SENSE == cmd[0]);
822
	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
823 824
	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
	if (0x3 == pcontrol) {  /* Saving values not supported */
825
		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
James Bottomley's avatar
James Bottomley committed
826
			       	0);
827
		return check_condition_result;
828 829 830 831 832
	}
	dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
	if (msense_6) {
		arr[2] = dev_spec;
		offset = 4;
833
	} else {
834 835
		arr[3] = dev_spec;
		offset = 8;
Linus Torvalds's avatar
Linus Torvalds committed
836
	}
837 838
	ap = arr + offset;

839 840
	if (0 != subpcode) { /* TODO: Control Extension page */
		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
James Bottomley's avatar
James Bottomley committed
841
			       	0);
842 843
		return check_condition_result;
	}
844 845
	switch (pcode) {
	case 0x1:	/* Read-Write error recovery page, direct access */
846
		len = resp_err_recov_pg(ap, pcontrol, target);
847 848 849
		offset += len;
		break;
	case 0x2:	/* Disconnect-Reconnect page, all devices */
850
		len = resp_disconnect_pg(ap, pcontrol, target);
851 852
		offset += len;
		break;
853 854 855 856
        case 0x3:       /* Format device page, direct access */
                len = resp_format_pg(ap, pcontrol, target);
                offset += len;
                break;
857
	case 0x8:	/* Caching page, direct access */
858
		len = resp_caching_pg(ap, pcontrol, target);
859 860 861
		offset += len;
		break;
	case 0xa:	/* Control Mode page, all devices */
862
		len = resp_ctrl_m_pg(ap, pcontrol, target);
863 864
		offset += len;
		break;
865 866 867 868
	case 0x1c:	/* Informational Exceptions Mode page, all devices */
		len = resp_iec_m_pg(ap, pcontrol, target);
		offset += len;
		break;
869
	case 0x3f:	/* Read all Mode pages */
870 871
		len = resp_err_recov_pg(ap, pcontrol, target);
		len += resp_disconnect_pg(ap + len, pcontrol, target);
872
		len += resp_format_pg(ap + len, pcontrol, target);
873 874
		len += resp_caching_pg(ap + len, pcontrol, target);
		len += resp_ctrl_m_pg(ap + len, pcontrol, target);
875
		len += resp_iec_m_pg(ap + len, pcontrol, target);
876 877 878
		offset += len;
		break;
	default:
879
		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
James Bottomley's avatar
James Bottomley committed
880
			       	0);
881
		return check_condition_result;
882 883 884 885
	}
	if (msense_6)
		arr[0] = offset - 1;
	else {
James Bottomley's avatar
James Bottomley committed
886 887
		arr[0] = ((offset - 2) >> 8) & 0xff;
		arr[1] = (offset - 2) & 0xff;
888
	}
James Bottomley's avatar
James Bottomley committed
889
	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
Linus Torvalds's avatar
Linus Torvalds committed
890 891
}

892
static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block,
893
		     int num, struct sdebug_dev_info * devip)
Linus Torvalds's avatar
Linus Torvalds committed
894 895
{
	unsigned long iflags;
James Bottomley's avatar
James Bottomley committed
896
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
897

898
	if (upper_blk || (block + num > sdebug_capacity)) {
899
		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
James Bottomley's avatar
James Bottomley committed
900
				0);
901
		return check_condition_result;
Linus Torvalds's avatar
Linus Torvalds committed
902
	}
903
	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
904
	    (block <= OPT_MEDIUM_ERR_ADDR) &&
905
	    ((block + num) > OPT_MEDIUM_ERR_ADDR)) {
906
		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
James Bottomley's avatar
James Bottomley committed
907
				0);
908
		/* claim unrecoverable read error */
909
		return check_condition_result;
910
	}
911
	read_lock_irqsave(&atomic_rw, iflags);
James Bottomley's avatar
James Bottomley committed
912 913
	ret = fill_from_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
			   	   num * SECT_SIZE);
914
	read_unlock_irqrestore(&atomic_rw, iflags);
James Bottomley's avatar
James Bottomley committed
915
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
916 917
}

918 919
static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block,
		      int num, struct sdebug_dev_info * devip)
Linus Torvalds's avatar
Linus Torvalds committed
920 921
{
	unsigned long iflags;
James Bottomley's avatar
James Bottomley committed
922
	int res;
Linus Torvalds's avatar
Linus Torvalds committed
923

924
	if (upper_blk || (block + num > sdebug_capacity)) {
925
		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
James Bottomley's avatar
James Bottomley committed
926
			       	0);
927
		return check_condition_result;
Linus Torvalds's avatar
Linus Torvalds committed
928 929
	}

930
	write_lock_irqsave(&atomic_rw, iflags);
James Bottomley's avatar
James Bottomley committed
931 932
	res = fetch_to_dev_buffer(SCpnt, fake_storep + (block * SECT_SIZE),
			   	  num * SECT_SIZE);
933
	write_unlock_irqrestore(&atomic_rw, iflags);
James Bottomley's avatar
James Bottomley committed
934 935 936 937 938 939
	if (-1 == res)
		return (DID_ERROR << 16);
	else if ((res < (num * SECT_SIZE)) &&
		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
		printk(KERN_INFO "scsi_debug: write: cdb indicated=%d, "
		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
Linus Torvalds's avatar
Linus Torvalds committed
940 941 942
	return 0;
}

James Bottomley's avatar
James Bottomley committed
943 944 945 946
#define SDEBUG_RLUN_ARR_SZ 128

static int resp_report_luns(struct scsi_cmnd * scp,
			    struct sdebug_dev_info * devip)
Linus Torvalds's avatar
Linus Torvalds committed
947
{
948
	unsigned int alloc_len;
949
	int lun_cnt, i, upper;
James Bottomley's avatar
James Bottomley committed
950
	unsigned char *cmd = (unsigned char *)scp->cmnd;
951
	int select_report = (int)cmd[2];
952
	struct scsi_lun *one_lun;
James Bottomley's avatar
James Bottomley committed
953
	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
Linus Torvalds's avatar
Linus Torvalds committed
954

955 956
	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
	if ((alloc_len < 16) || (select_report > 2)) {
957
		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
James Bottomley's avatar
James Bottomley committed
958
			       	0);
959
		return check_condition_result;
960
	}
James Bottomley's avatar
James Bottomley committed
961 962 963 964 965 966 967 968 969 970 971 972 973 974
	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
	lun_cnt = scsi_debug_max_luns;
	arr[2] = ((sizeof(struct scsi_lun) * lun_cnt) >> 8) & 0xff;
	arr[3] = (sizeof(struct scsi_lun) * lun_cnt) & 0xff;
	lun_cnt = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
			    sizeof(struct scsi_lun)), lun_cnt);
	one_lun = (struct scsi_lun *) &arr[8];
	for (i = 0; i < lun_cnt; i++) {
		upper = (i >> 8) & 0x3f;
		if (upper)
			one_lun[i].scsi_lun[0] =
			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
		one_lun[i].scsi_lun[1] = i & 0xff;
Linus Torvalds's avatar
Linus Torvalds committed
975
	}
James Bottomley's avatar
James Bottomley committed
976 977
	return fill_from_dev_buffer(scp, arr,
				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
Linus Torvalds's avatar
Linus Torvalds committed
978 979
}

980 981
/* When timer goes off this function is called. */
static void timer_intr_handler(unsigned long indx)
Linus Torvalds's avatar
Linus Torvalds committed
982
{
983
	struct sdebug_queued_cmd * sqcp;
984
	unsigned long iflags;
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999

	if (indx >= SCSI_DEBUG_CANQUEUE) {
		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
		       "large\n");
		return;
	}
	spin_lock_irqsave(&queued_arr_lock, iflags);
	sqcp = &queued_arr[(int)indx];
	if (! sqcp->in_use) {
		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
		       "interrupt\n");
		spin_unlock_irqrestore(&queued_arr_lock, iflags);
		return;
	}
	sqcp->in_use = 0;
1000 1001
	if (sqcp->done_funct) {
		sqcp->a_cmnd->result = sqcp->scsi_result;
1002
		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
1003
	}
1004 1005
	sqcp->done_funct = NULL;
	spin_unlock_irqrestore(&queued_arr_lock, iflags);
Linus Torvalds's avatar
Linus Torvalds committed
1006 1007
}

1008 1009 1010 1011 1012 1013 1014 1015
static int scsi_debug_slave_alloc(struct scsi_device * sdp)
{
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
	return 0;
}

1016
static int scsi_debug_slave_configure(struct scsi_device * sdp)
Linus Torvalds's avatar
Linus Torvalds committed
1017
{
1018
	struct sdebug_dev_info * devip;
Linus Torvalds's avatar
Linus Torvalds committed
1019

1020
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1021
		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1022 1023 1024
		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
1025 1026
	devip = devInfoReg(sdp);
	sdp->hostdata = devip;
1027
	if (sdp->host->cmd_per_lun)
1028
		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
1029 1030 1031 1032
					sdp->host->cmd_per_lun);
	return 0;
}

1033
static void scsi_debug_slave_destroy(struct scsi_device * sdp)
1034
{
1035
	struct sdebug_dev_info * devip =
1036 1037 1038
				(struct sdebug_dev_info *)sdp->hostdata;

	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1039
		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
1040 1041 1042
		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
	if (devip) {
		/* make this slot avaliable for re-use */
1043
		devip->used = 0;
1044 1045 1046 1047
		sdp->hostdata = NULL;
	}
}

1048
static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
1049
{
1050 1051
	struct sdebug_host_info * sdbg_host;
	struct sdebug_dev_info * open_devip = NULL;
1052
	struct sdebug_dev_info * devip =
1053
			(struct sdebug_dev_info *)sdev->hostdata;
1054 1055 1056

	if (devip)
		return devip;
1057
	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
1058
        if(! sdbg_host) {
1059
                printk(KERN_ERR "Host info NULL\n");
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
		return NULL;
        }
	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
		if ((devip->used) && (devip->channel == sdev->channel) &&
                    (devip->target == sdev->id) &&
                    (devip->lun == sdev->lun))
                        return devip;
		else {
			if ((!devip->used) && (!open_devip))
				open_devip = devip;
Linus Torvalds's avatar
Linus Torvalds committed
1070 1071
		}
	}
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
	if (NULL == open_devip) { /* try and make a new one */
		open_devip = kmalloc(sizeof(*open_devip),GFP_KERNEL);
		if (NULL == open_devip) {
			printk(KERN_ERR "%s: out of memory at line %d\n",
				__FUNCTION__, __LINE__);
			return NULL;
		}
		memset(open_devip, 0, sizeof(*open_devip));
		open_devip->sdbg_host = sdbg_host;
		list_add_tail(&open_devip->dev_list,
		&sdbg_host->dev_info_list);
	}
1084 1085 1086 1087 1088 1089 1090 1091
        if (open_devip) {
		open_devip->channel = sdev->channel;
		open_devip->target = sdev->id;
		open_devip->lun = sdev->lun;
		open_devip->sdbg_host = sdbg_host;
		open_devip->reset = 1;
		open_devip->used = 1;
		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
James Bottomley's avatar
James Bottomley committed
1092 1093 1094 1095 1096 1097
		if (scsi_debug_dsense)
			open_devip->sense_buff[0] = 0x72;
		else {
			open_devip->sense_buff[0] = 0x70;
			open_devip->sense_buff[7] = 0xa;
		}
1098 1099 1100
		return open_devip;
        }
        return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1101 1102
}

1103
static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
James Bottomley's avatar
James Bottomley committed
1104
			    int asc, int asq)
Linus Torvalds's avatar
Linus Torvalds committed
1105
{
1106 1107 1108 1109
	unsigned char * sbuff;

	sbuff = devip->sense_buff;
	memset(sbuff, 0, SDEBUG_SENSE_LEN);
1110 1111 1112 1113 1114 1115 1116 1117
	if (scsi_debug_dsense) {
		sbuff[0] = 0x72;  /* descriptor, current */
		sbuff[1] = key;
		sbuff[2] = asc;
		sbuff[3] = asq;
	} else {
		sbuff[0] = 0x70;  /* fixed, current */
		sbuff[2] = key;
James Bottomley's avatar
James Bottomley committed
1118
		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
1119 1120 1121 1122 1123 1124
		sbuff[12] = asc;
		sbuff[13] = asq;
	}
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
Linus Torvalds's avatar
Linus Torvalds committed
1125 1126
}

1127
static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
1128
{
1129 1130
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: abort\n");
Linus Torvalds's avatar
Linus Torvalds committed
1131
	++num_aborts;
1132
	stop_queued_cmnd(SCpnt);
Linus Torvalds's avatar
Linus Torvalds committed
1133
	return SUCCESS;
Linus Torvalds's avatar
Linus Torvalds committed
1134 1135
}

1136 1137
static int scsi_debug_biosparam(struct scsi_device *sdev,
		struct block_device * bdev, sector_t capacity, int *info)
Linus Torvalds's avatar
Linus Torvalds committed
1138
{
1139 1140 1141
	int res;
	unsigned char *buf;

1142 1143
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: biosparam\n");
1144 1145
	buf = scsi_bios_ptable(bdev);
	if (buf) {
1146
		res = scsi_partsize(buf, capacity,
1147 1148 1149 1150 1151 1152 1153 1154
				    &info[2], &info[0], &info[1]);
		kfree(buf);
		if (! res)
			return res;
	}
	info[0] = sdebug_heads;
	info[1] = sdebug_sectors_per;
	info[2] = sdebug_cylinders_per;
Linus Torvalds's avatar
Linus Torvalds committed
1155 1156 1157
	return 0;
}

1158
static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
1159
{
1160
	struct sdebug_dev_info * devip;
Linus Torvalds's avatar
Linus Torvalds committed
1161

1162 1163
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: device_reset\n");
Linus Torvalds's avatar
Linus Torvalds committed
1164
	++num_dev_resets;
1165
	if (SCpnt) {
1166
		devip = devInfoReg(SCpnt->device);
1167 1168
		if (devip)
			devip->reset = 1;
Linus Torvalds's avatar
Linus Torvalds committed
1169 1170 1171 1172
	}
	return SUCCESS;
}

1173
static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
1174
{
1175 1176 1177 1178
	struct sdebug_host_info *sdbg_host;
        struct sdebug_dev_info * dev_info;
        struct scsi_device * sdp;
        struct Scsi_Host * hp;
Linus Torvalds's avatar
Linus Torvalds committed
1179

1180 1181
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: bus_reset\n");
Linus Torvalds's avatar
Linus Torvalds committed
1182
	++num_bus_resets;
1183
	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
1184
		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
1185 1186 1187 1188 1189
		if (sdbg_host) {
			list_for_each_entry(dev_info,
                                            &sdbg_host->dev_info_list,
                                            dev_list)
				dev_info->reset = 1;
Linus Torvalds's avatar
Linus Torvalds committed
1190 1191 1192 1193 1194
		}
	}
	return SUCCESS;
}

1195
static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
1196
{
1197 1198
	struct sdebug_host_info * sdbg_host;
        struct sdebug_dev_info * dev_info;
Linus Torvalds's avatar
Linus Torvalds committed
1199

1200 1201
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: host_reset\n");
Linus Torvalds's avatar
Linus Torvalds committed
1202
	++num_host_resets;
1203 1204 1205 1206 1207 1208 1209
        spin_lock(&sdebug_host_list_lock);
        list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
                list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
                                    dev_list)
                        dev_info->reset = 1;
        }
        spin_unlock(&sdebug_host_list_lock);
1210
	stop_all_queued();
Linus Torvalds's avatar
Linus Torvalds committed
1211 1212 1213
	return SUCCESS;
}

1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
/* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
{
	unsigned long iflags;
	int k;
	struct sdebug_queued_cmd * sqcp;

	spin_lock_irqsave(&queued_arr_lock, iflags);
	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
		sqcp = &queued_arr[k];
		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
			del_timer_sync(&sqcp->cmnd_timer);
			sqcp->in_use = 0;
			sqcp->a_cmnd = NULL;
			break;
		}
	}
	spin_unlock_irqrestore(&queued_arr_lock, iflags);
	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
}

/* Deletes (stops) timers of all queued commands */
static void stop_all_queued(void)
{
	unsigned long iflags;
	int k;
	struct sdebug_queued_cmd * sqcp;

	spin_lock_irqsave(&queued_arr_lock, iflags);
	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
		sqcp = &queued_arr[k];
		if (sqcp->in_use && sqcp->a_cmnd) {
			del_timer_sync(&sqcp->cmnd_timer);
			sqcp->in_use = 0;
			sqcp->a_cmnd = NULL;
		}
	}
	spin_unlock_irqrestore(&queued_arr_lock, iflags);
}

/* Initializes timers in queued array */
1255
static void __init init_all_queued(void)
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
{
	unsigned long iflags;
	int k;
	struct sdebug_queued_cmd * sqcp;

	spin_lock_irqsave(&queued_arr_lock, iflags);
	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
		sqcp = &queued_arr[k];
		init_timer(&sqcp->cmnd_timer);
		sqcp->in_use = 0;
		sqcp->a_cmnd = NULL;
	}
	spin_unlock_irqrestore(&queued_arr_lock, iflags);
}

1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
static void __init sdebug_build_parts(unsigned char * ramp)
{
	struct partition * pp;
	int starts[SDEBUG_MAX_PARTS + 2];
	int sectors_per_part, num_sectors, k;
	int heads_by_sects, start_sec, end_sec;

	/* assume partition table already zeroed */
	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
		return;
	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
				    "partitions to %d\n", SDEBUG_MAX_PARTS);
	}
	num_sectors = (int)(sdebug_store_size / SECT_SIZE);
	sectors_per_part = (num_sectors - sdebug_sectors_per)
			   / scsi_debug_num_parts;
	heads_by_sects = sdebug_heads * sdebug_sectors_per;
        starts[0] = sdebug_sectors_per;
	for (k = 1; k < scsi_debug_num_parts; ++k)
		starts[k] = ((k * sectors_per_part) / heads_by_sects)
			    * heads_by_sects;
	starts[scsi_debug_num_parts] = num_sectors;
	starts[scsi_debug_num_parts + 1] = 0;

	ramp[510] = 0x55;	/* magic partition markings */
	ramp[511] = 0xAA;
	pp = (struct partition *)(ramp + 0x1be);
	for (k = 0; starts[k + 1]; ++k, ++pp) {
		start_sec = starts[k];
		end_sec = starts[k + 1] - 1;
		pp->boot_ind = 0;

		pp->cyl = start_sec / heads_by_sects;
		pp->head = (start_sec - (pp->cyl * heads_by_sects))
			   / sdebug_sectors_per;
		pp->sector = (start_sec % sdebug_sectors_per) + 1;

		pp->end_cyl = end_sec / heads_by_sects;
		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
			       / sdebug_sectors_per;
		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;

		pp->start_sect = start_sec;
		pp->nr_sects = end_sec - start_sec + 1;
		pp->sys_ind = 0x83;	/* plain Linux partition */
	}
}

static int schedule_resp(struct scsi_cmnd * cmnd,
1322 1323 1324
			 struct sdebug_dev_info * devip,
			 done_funct_t done, int scsi_result, int delta_jiff)
{
1325 1326 1327 1328
	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
		if (scsi_result) {
			struct scsi_device * sdp = cmnd->device;

1329
			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
1330 1331 1332
			       "non-zero result=0x%x\n", sdp->host->host_no,
			       sdp->channel, sdp->id, sdp->lun, scsi_result);
		}
1333 1334 1335
	}
	if (cmnd && devip) {
		/* simulate autosense by this driver */
1336
		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
1337
			memcpy(cmnd->sense_buffer, devip->sense_buff,
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
	}
	if (delta_jiff <= 0) {
		if (cmnd)
			cmnd->result = scsi_result;
		if (done)
			done(cmnd);
		return 0;
	} else {
		unsigned long iflags;
		int k;
		struct sdebug_queued_cmd * sqcp = NULL;

		spin_lock_irqsave(&queued_arr_lock, iflags);
		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
			sqcp = &queued_arr[k];
			if (! sqcp->in_use)
				break;
		}
		if (k >= SCSI_DEBUG_CANQUEUE) {
			spin_unlock_irqrestore(&queued_arr_lock, iflags);
			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
			return 1;	/* report busy to mid level */
		}
		sqcp->in_use = 1;
		sqcp->a_cmnd = cmnd;
		sqcp->scsi_result = scsi_result;
		sqcp->done_funct = done;
		sqcp->cmnd_timer.function = timer_intr_handler;
		sqcp->cmnd_timer.data = k;
		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
		add_timer(&sqcp->cmnd_timer);
		spin_unlock_irqrestore(&queued_arr_lock, iflags);
1372 1373
		if (cmnd)
			cmnd->result = 0;
1374 1375 1376 1377
		return 0;
	}
}

1378 1379
/* Set 'perm' (4th argument) to 0 to disable module_param's definition
 * of sysfs parameters (which module_param doesn't yet support).
1380
 * Sysfs parameters defined explicitly below.
1381
 */
1382 1383
module_param_named(add_host, scsi_debug_add_host, int, 0); /* perm=0644 */
module_param_named(delay, scsi_debug_delay, int, 0); /* perm=0644 */
1384
module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, 0);
1385
module_param_named(dsense, scsi_debug_dsense, int, 0);
1386
module_param_named(every_nth, scsi_debug_every_nth, int, 0);
1387 1388 1389 1390 1391 1392
module_param_named(max_luns, scsi_debug_max_luns, int, 0);
module_param_named(num_parts, scsi_debug_num_parts, int, 0);
module_param_named(num_tgts, scsi_debug_num_tgts, int, 0);
module_param_named(opts, scsi_debug_opts, int, 0); /* perm=0644 */
module_param_named(ptype, scsi_debug_ptype, int, 0);
module_param_named(scsi_level, scsi_debug_scsi_level, int, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1393 1394 1395 1396

MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
MODULE_LICENSE("GPL");
1397
MODULE_VERSION(SCSI_DEBUG_VERSION);
Linus Torvalds's avatar
Linus Torvalds committed
1398

1399 1400
MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
1401
MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs");
1402
MODULE_PARM_DESC(dsense, "use descriptor sense format(def: fixed)");
1403
MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
1404 1405 1406 1407 1408
MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate");
MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
MODULE_PARM_DESC(num_tgts, "number of SCSI targets per host to simulate");
MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->...");
MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
James Bottomley's avatar
James Bottomley committed
1409
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
1410 1411


Linus Torvalds's avatar
Linus Torvalds committed
1412 1413
static char sdebug_info[256];

1414
static const char * scsi_debug_info(struct Scsi_Host * shp)
Linus Torvalds's avatar
Linus Torvalds committed
1415
{
1416 1417 1418
	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
		scsi_debug_version_date, scsi_debug_dev_size_mb,
1419
		scsi_debug_opts);
Linus Torvalds's avatar
Linus Torvalds committed
1420
	return sdebug_info;
Linus Torvalds's avatar
Linus Torvalds committed
1421 1422 1423 1424 1425
}

/* scsi_debug_proc_info
 * Used if the driver currently has no own support for /proc/scsi
 */
1426 1427
static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
				int length, int inout)
Linus Torvalds's avatar
Linus Torvalds committed
1428 1429 1430 1431 1432 1433 1434
{
	int len, pos, begin;
	int orig_length;

	orig_length = length;

	if (inout == 1) {
1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
		char arr[16];
		int minLen = length > 15 ? 15 : length;

		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
			return -EACCES;
		memcpy(arr, buffer, minLen);
		arr[minLen] = '\0';
		if (1 != sscanf(arr, "%d", &pos))
			return -EINVAL;
		scsi_debug_opts = pos;
James Bottomley's avatar
James Bottomley committed
1445
		if (scsi_debug_every_nth != 0)
1446
                        scsi_debug_cmnd_count = 0;
1447
		return length;
Linus Torvalds's avatar
Linus Torvalds committed
1448 1449
	}
	begin = 0;
1450 1451
	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
	    "%s [%s]\n"
1452
	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
1453
	    "every_nth=%d(curr:%d)\n"
1454 1455
	    "delay=%d, max_luns=%d, scsi_level=%d\n"
	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
1456
	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
Linus Torvalds's avatar
Linus Torvalds committed
1457
	    "host_resets=%d\n",
1458
	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
1459
	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
1460 1461
	    scsi_debug_cmnd_count, scsi_debug_delay,
	    scsi_debug_max_luns, scsi_debug_scsi_level,
1462
	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
Linus Torvalds's avatar
Linus Torvalds committed
1463
	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
Linus Torvalds's avatar
Linus Torvalds committed
1464 1465 1466 1467 1468 1469 1470 1471
	if (pos < offset) {
		len = 0;
		begin = pos;
	}
	*start = buffer + (offset - begin);	/* Start of wanted data */
	len -= (offset - begin);
	if (len > length)
		len = length;
1472
	return len;
Linus Torvalds's avatar
Linus Torvalds committed
1473 1474
}

1475
static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
1476
{
Andrew Morton's avatar
Andrew Morton committed
1477
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
1478
}
Linus Torvalds's avatar
Linus Torvalds committed
1479

1480
static ssize_t sdebug_delay_store(struct device_driver * ddp,
1481
				  const char * buf, size_t count)
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493
{
        int delay;
	char work[20];

        if (1 == sscanf(buf, "%10s", work)) {
		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
			scsi_debug_delay = delay;
			return count;
		}
	}
	return -EINVAL;
}
1494
DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
1495
	    sdebug_delay_store);
1496

1497
static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
1498
{
Andrew Morton's avatar
Andrew Morton committed
1499
        return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
1500 1501
}

1502
static ssize_t sdebug_opts_store(struct device_driver * ddp,
1503
				 const char * buf, size_t count)
1504 1505 1506
{
        int opts;
	char work[20];
Linus Torvalds's avatar
Linus Torvalds committed
1507

1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
        if (1 == sscanf(buf, "%10s", work)) {
		if (0 == strnicmp(work,"0x", 2)) {
			if (1 == sscanf(&work[2], "%x", &opts))
				goto opts_done;
		} else {
			if (1 == sscanf(work, "%d", &opts))
				goto opts_done;
		}
	}
	return -EINVAL;
opts_done:
	scsi_debug_opts = opts;
1520
	scsi_debug_cmnd_count = 0;
1521 1522
	return count;
}
1523
DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
1524
	    sdebug_opts_store);
1525

1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
{
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
}
static ssize_t sdebug_ptype_store(struct device_driver * ddp,
				  const char * buf, size_t count)
{
        int n;

	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
		scsi_debug_ptype = n;
		return count;
	}
	return -EINVAL;
}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
1541
DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
1542

1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560
static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
{
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
}
static ssize_t sdebug_dsense_store(struct device_driver * ddp,
				  const char * buf, size_t count)
{
        int n;

	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
		scsi_debug_dsense = n;
		return count;
	}
	return -EINVAL;
}
DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
	    sdebug_dsense_store);

1561
static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
1562
{
Andrew Morton's avatar
Andrew Morton committed
1563
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
1564
}
1565
static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
1566 1567 1568 1569 1570
				     const char * buf, size_t count)
{
        int n;

	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
1571
		scsi_debug_num_tgts = n;
1572
		sdebug_max_tgts_luns();
1573 1574 1575 1576
		return count;
	}
	return -EINVAL;
}
1577
DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
1578
	    sdebug_num_tgts_store);
1579

1580
static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
1581
{
Andrew Morton's avatar
Andrew Morton committed
1582
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
1583
}
1584
DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
1585 1586 1587 1588 1589

static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
{
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
}
Greg Kroah-Hartman's avatar
Greg Kroah-Hartman committed
1590
DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
1591

1592
static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
1593
{
Andrew Morton's avatar
Andrew Morton committed
1594
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
1595
}
1596
static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
1597 1598 1599 1600
				      const char * buf, size_t count)
{
        int nth;

James Bottomley's avatar
James Bottomley committed
1601
	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
1602 1603 1604 1605 1606 1607 1608
		scsi_debug_every_nth = nth;
		scsi_debug_cmnd_count = 0;
		return count;
	}
	return -EINVAL;
}
DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
1609
	    sdebug_every_nth_store);
1610

1611
static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
1612
{
Andrew Morton's avatar
Andrew Morton committed
1613
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
1614
}
1615
static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
1616 1617 1618 1619 1620 1621
				     const char * buf, size_t count)
{
        int n;

	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
		scsi_debug_max_luns = n;
1622
		sdebug_max_tgts_luns();
1623 1624 1625 1626
		return count;
	}
	return -EINVAL;
}
1627
DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
1628
	    sdebug_max_luns_store);
1629

1630
static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
1631
{
Andrew Morton's avatar
Andrew Morton committed
1632
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
1633
}
1634
DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
1635

1636
static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
1637
{
Andrew Morton's avatar
Andrew Morton committed
1638
        return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
1639 1640
}

1641
static ssize_t sdebug_add_host_store(struct device_driver * ddp,
1642
				     const char * buf, size_t count)
1643
{
1644
        int delta_hosts;
1645 1646 1647 1648
	char work[20];

        if (1 != sscanf(buf, "%10s", work))
		return -EINVAL;
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658
	{	/* temporary hack around sscanf() problem with -ve nums */
		int neg = 0;

		if ('-' == *work)
			neg = 1;
		if (1 != sscanf(work + neg, "%d", &delta_hosts))
			return -EINVAL;
		if (neg)
			delta_hosts = -delta_hosts;
	}
1659
	if (delta_hosts > 0) {
1660
		do {
1661
			sdebug_add_adapter();
1662 1663
		} while (--delta_hosts);
	} else if (delta_hosts < 0) {
1664
		do {
1665
			sdebug_remove_adapter();
1666
		} while (++delta_hosts);
1667 1668 1669
	}
	return count;
}
1670
DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show, 
1671
	    sdebug_add_host_store);
1672

1673
static void do_create_driverfs_files(void)
1674
{
1675
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
1676
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
1677
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
Douglas Gilbert's avatar
Douglas Gilbert committed
1678
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
1679 1680
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
1681 1682 1683 1684
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
1685
	driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
1686 1687
}

1688
static void do_remove_driverfs_files(void)
1689
{
1690
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
1691 1692 1693 1694
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
1695 1696
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
Douglas Gilbert's avatar
Douglas Gilbert committed
1697
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
1698
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
1699
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
1700
	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
1701 1702
}

1703 1704
static int __init scsi_debug_init(void)
{
1705
	unsigned long sz;
1706
	int host_to_add;
1707 1708
	int k;

1709 1710
	if (scsi_debug_dev_size_mb < 1)
		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
1711 1712 1713 1714 1715 1716
	sdebug_store_size = (unsigned long)scsi_debug_dev_size_mb * 1048576;
	sdebug_capacity = sdebug_store_size / SECT_SIZE;

	/* play around with geometry, don't waste too much on track 0 */
	sdebug_heads = 8;
	sdebug_sectors_per = 32;
1717
	if (scsi_debug_dev_size_mb >= 16)
1718 1719 1720
		sdebug_heads = 32;
	else if (scsi_debug_dev_size_mb >= 256)
		sdebug_heads = 64;
1721
	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
1722
			       (sdebug_sectors_per * sdebug_heads);
1723
	if (sdebug_cylinders_per >= 1024) {
1724 1725 1726
		/* other LLDs do this; implies >= 1GB ram disk ... */
		sdebug_heads = 255;
		sdebug_sectors_per = 63;
1727
		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
1728 1729
			       (sdebug_sectors_per * sdebug_heads);
	}
1730

1731
	sz = sdebug_store_size;
1732 1733 1734 1735 1736 1737
	fake_storep = vmalloc(sz);
	if (NULL == fake_storep) {
		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
		return -ENOMEM;
	}
	memset(fake_storep, 0, sz);
1738 1739
	if (scsi_debug_num_parts > 0)
		sdebug_build_parts(fake_storep);
1740 1741 1742

	init_all_queued();

1743 1744
	device_register(&pseudo_primary);
	bus_register(&pseudo_lld_bus);
1745
	driver_register(&sdebug_driverfs_driver);
1746 1747 1748 1749
	do_create_driverfs_files();

	sdebug_driver_template.proc_name = (char *)sdebug_proc_name;

1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
	host_to_add = scsi_debug_add_host;
        scsi_debug_add_host = 0;

        for (k = 0; k < host_to_add; k++) {
                if (sdebug_add_adapter()) {
                        printk(KERN_ERR "scsi_debug_init: "
                               "sdebug_add_adapter failed k=%d\n", k);
                        break;
                }
        }
1760 1761

	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
1762
		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
1763
		       scsi_debug_add_host);
1764 1765 1766 1767 1768 1769
	}
	return 0;
}

static void __exit scsi_debug_exit(void)
{
1770 1771
	int k = scsi_debug_add_host;

1772
	stop_all_queued();
1773 1774
	for (; k; k--)
		sdebug_remove_adapter();
1775
	do_remove_driverfs_files();
1776
	driver_unregister(&sdebug_driverfs_driver);
1777 1778
	bus_unregister(&pseudo_lld_bus);
	device_unregister(&pseudo_primary);
1779 1780 1781 1782

	vfree(fake_storep);
}

1783
device_initcall(scsi_debug_init);
1784
module_exit(scsi_debug_exit);
1785

1786 1787 1788 1789 1790 1791
void pseudo_0_release(struct device * dev)
{
	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}

1792 1793
static struct device pseudo_primary = {
	.bus_id		= "pseudo_0",
1794
	.release	= pseudo_0_release,
1795 1796
};

1797
static int pseudo_lld_bus_match(struct device *dev,
1798 1799 1800 1801 1802 1803
                          struct device_driver *dev_driver)
{
        return 1;
}

static struct bus_type pseudo_lld_bus = {
1804 1805
        .name = "pseudo",
        .match = pseudo_lld_bus_match,
1806 1807
};

1808
static void sdebug_release_adapter(struct device * dev)
1809
{
1810 1811 1812 1813
        struct sdebug_host_info *sdbg_host;

	sdbg_host = to_sdebug_host(dev);
        kfree(sdbg_host);
1814
}
1815

1816
static int sdebug_add_adapter(void)
1817
{
1818 1819 1820 1821 1822 1823 1824
	int k, devs_per_host;
        int error = 0;
        struct sdebug_host_info *sdbg_host;
        struct sdebug_dev_info *sdbg_devinfo;
        struct list_head *lh, *lh_sf;

        sdbg_host = kmalloc(sizeof(*sdbg_host),GFP_KERNEL);
1825

1826
        if (NULL == sdbg_host) {
1827 1828 1829 1830
                printk(KERN_ERR "%s: out of memory at line %d\n",
                       __FUNCTION__, __LINE__);
                return -ENOMEM;
        }
1831

1832 1833 1834 1835 1836 1837 1838 1839 1840 1841
        memset(sdbg_host, 0, sizeof(*sdbg_host));
        INIT_LIST_HEAD(&sdbg_host->dev_info_list);

	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
        for (k = 0; k < devs_per_host; k++) {
                sdbg_devinfo = kmalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
                if (NULL == sdbg_devinfo) {
                        printk(KERN_ERR "%s: out of memory at line %d\n",
                               __FUNCTION__, __LINE__);
                        error = -ENOMEM;
1842
			goto clean;
1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
                }
                memset(sdbg_devinfo, 0, sizeof(*sdbg_devinfo));
                sdbg_devinfo->sdbg_host = sdbg_host;
                list_add_tail(&sdbg_devinfo->dev_list,
                              &sdbg_host->dev_info_list);
        }

        spin_lock(&sdebug_host_list_lock);
        list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
        spin_unlock(&sdebug_host_list_lock);

        sdbg_host->dev.bus = &pseudo_lld_bus;
        sdbg_host->dev.parent = &pseudo_primary;
        sdbg_host->dev.release = &sdebug_release_adapter;
        sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
1858

1859
        error = device_register(&sdbg_host->dev);
1860

1861
        if (error)
1862
		goto clean;
1863

1864 1865 1866
	++scsi_debug_add_host;
        return error;

1867
clean:
1868 1869 1870 1871 1872 1873 1874 1875
	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
					  dev_list);
		list_del(&sdbg_devinfo->dev_list);
		kfree(sdbg_devinfo);
	}

	kfree(sdbg_host);
1876 1877 1878
        return error;
}

1879
static void sdebug_remove_adapter(void)
1880 1881 1882 1883
{
        struct sdebug_host_info * sdbg_host = NULL;

        spin_lock(&sdebug_host_list_lock);
1884
        if (!list_empty(&sdebug_host_list)) {
1885 1886
                sdbg_host = list_entry(sdebug_host_list.prev,
                                       struct sdebug_host_info, host_list);
1887 1888
		list_del(&sdbg_host->host_list);
	}
1889 1890
        spin_unlock(&sdebug_host_list_lock);

1891 1892 1893 1894
	if (!sdbg_host)
		return;

        device_unregister(&sdbg_host->dev);
1895
        --scsi_debug_add_host;
1896 1897
}

1898
static int sdebug_driver_probe(struct device * dev)
1899
{
1900 1901 1902 1903
        int error = 0;
        struct sdebug_host_info *sdbg_host;
        struct Scsi_Host *hpnt;

1904
	sdbg_host = to_sdebug_host(dev);
1905

1906
        hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
1907 1908 1909
        if (NULL == hpnt) {
                printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
                error = -ENODEV;
1910
		return error;
1911 1912 1913
        }

        sdbg_host->shost = hpnt;
1914
	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
1915 1916 1917 1918 1919
	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
		hpnt->max_id = scsi_debug_num_tgts + 1;
	else
		hpnt->max_id = scsi_debug_num_tgts;
	hpnt->max_lun = scsi_debug_max_luns;
1920

1921
        error = scsi_add_host(hpnt, &sdbg_host->dev);
1922 1923 1924
        if (error) {
                printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
                error = -ENODEV;
1925
		scsi_host_put(hpnt);
1926 1927
        } else
		scsi_scan_host(hpnt);
1928 1929 1930 1931 1932


        return error;
}

1933
static int sdebug_driver_remove(struct device * dev)
1934 1935
{
        struct list_head *lh, *lh_sf;
1936
        struct sdebug_host_info *sdbg_host;
1937
        struct sdebug_dev_info *sdbg_devinfo;
1938

1939
	sdbg_host = to_sdebug_host(dev);
1940

1941
	if (!sdbg_host) {
1942 1943 1944
		printk(KERN_ERR "%s: Unable to locate host info\n",
		       __FUNCTION__);
		return -ENODEV;
1945
	}
1946

1947
        scsi_remove_host(sdbg_host->shost);
1948

1949 1950 1951 1952 1953 1954 1955
        list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
                sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
                                          dev_list);
                list_del(&sdbg_devinfo->dev_list);
                kfree(sdbg_devinfo);
        }

1956
        scsi_host_put(sdbg_host->shost);
1957
        return 0;
1958
}
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976

static void sdebug_max_tgts_luns(void)
{
	struct sdebug_host_info * sdbg_host;
	struct Scsi_Host *hpnt;

	spin_lock(&sdebug_host_list_lock);
	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
		hpnt = sdbg_host->shost;
		if ((hpnt->this_id >= 0) &&
		    (scsi_debug_num_tgts > hpnt->this_id))
			hpnt->max_id = scsi_debug_num_tgts + 1;
		else
			hpnt->max_id = scsi_debug_num_tgts;
		hpnt->max_lun = scsi_debug_max_luns;
	}
	spin_unlock(&sdebug_host_list_lock);
}