Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 273 kB image not shown  

Quelle  scsi_debug.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 *
 * Copyright (C) 2001 - 2021 Douglas Gilbert
 *
 *  For documentation see http://sg.danny.cz/sg/scsi_debug.html
 */



#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__

#include <linux/module.h>
#include <linux/align.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <linux/scatterlist.h>
#include <linux/blkdev.h>
#include <linux/crc-t10dif.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>
#include <linux/hrtimer.h>
#include <linux/uuid.h>
#include <linux/t10-pi.h>
#include <linux/msdos_partition.h>
#include <linux/random.h>
#include <linux/xarray.h>
#include <linux/prefetch.h>
#include <linux/debugfs.h>
#include <linux/async.h>
#include <linux/cleanup.h>

#include <net/checksum.h>

#include <linux/unaligned.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsicam.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>

#include "sd.h"
#include "scsi_logging.h"

/* make sure inq_product_rev string corresponds to this version */
#define SDEBUG_VERSION "0191" /* format to fit INQUIRY revision field */
static const char *sdebug_version_date = "20210520";

#define MY_NAME "scsi_debug"

/* Additional Sense Code (ASC) */
#define NO_ADDITIONAL_SENSE 0x0
#define OVERLAP_ATOMIC_COMMAND_ASC 0x0
#define OVERLAP_ATOMIC_COMMAND_ASCQ 0x23
#define FILEMARK_DETECTED_ASCQ 0x1
#define EOP_EOM_DETECTED_ASCQ 0x2
#define BEGINNING_OF_P_M_DETECTED_ASCQ 0x4
#define EOD_DETECTED_ASCQ 0x5
#define LOGICAL_UNIT_NOT_READY 0x4
#define LOGICAL_UNIT_COMMUNICATION_FAILURE 0x8
#define UNRECOVERED_READ_ERR 0x11
#define PARAMETER_LIST_LENGTH_ERR 0x1a
#define INVALID_OPCODE 0x20
#define LBA_OUT_OF_RANGE 0x21
#define INVALID_FIELD_IN_CDB 0x24
#define INVALID_FIELD_IN_PARAM_LIST 0x26
#define WRITE_PROTECTED 0x27
#define UA_READY_ASC 0x28
#define UA_RESET_ASC 0x29
#define UA_CHANGED_ASC 0x2a
#define TOO_MANY_IN_PARTITION_ASC 0x3b
#define TARGET_CHANGED_ASC 0x3f
#define LUNS_CHANGED_ASCQ 0x0e
#define INSUFF_RES_ASC 0x55
#define INSUFF_RES_ASCQ 0x3
#define POWER_ON_RESET_ASCQ 0x0
#define POWER_ON_OCCURRED_ASCQ 0x1
#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */
#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */
#define CAPACITY_CHANGED_ASCQ 0x9
#define SAVING_PARAMS_UNSUP 0x39
#define TRANSPORT_PROBLEM 0x4b
#define THRESHOLD_EXCEEDED 0x5d
#define LOW_POWER_COND_ON 0x5e
#define MISCOMPARE_VERIFY_ASC 0x1d
#define MICROCODE_CHANGED_ASCQ 0x1 /* with TARGET_CHANGED_ASC */
#define MICROCODE_CHANGED_WO_RESET_ASCQ 0x16
#define WRITE_ERROR_ASC 0xc
#define UNALIGNED_WRITE_ASCQ 0x4
#define WRITE_BOUNDARY_ASCQ 0x5
#define READ_INVDATA_ASCQ 0x6
#define READ_BOUNDARY_ASCQ 0x7
#define ATTEMPT_ACCESS_GAP 0x9
#define INSUFF_ZONE_ASCQ 0xe
/* see drivers/scsi/sense_codes.h */

/* Additional Sense Code Qualifier (ASCQ) */
#define ACK_NAK_TO 0x3

/* 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_ATO 1
#define DEF_CDB_LEN 10
#define DEF_JDELAY   1  /* if > 0 unit is a jiffy */
#define DEF_DEV_SIZE_PRE_INIT   0
#define DEF_DEV_SIZE_MB   8
#define DEF_ZBC_DEV_SIZE_MB   128
#define DEF_DIF 0
#define DEF_DIX 0
#define DEF_PER_HOST_STORE false
#define DEF_D_SENSE   0
#define DEF_EVERY_NTH   0
#define DEF_FAKE_RW 0
#define DEF_GUARD 0
#define DEF_HOST_LOCK 0
#define DEF_LBPU 0
#define DEF_LBPWS 0
#define DEF_LBPWS10 0
#define DEF_LBPRZ 1
#define DEF_LOWEST_ALIGNED 0
#define DEF_NDELAY   0  /* if > 0 unit is a nanosecond */
#define DEF_NO_LUN_0   0
#define DEF_NUM_PARTS   0
#define DEF_OPTS   0
#define DEF_OPT_BLKS 1024
#define DEF_PHYSBLK_EXP 0
#define DEF_OPT_XFERLEN_EXP 0
#define DEF_PTYPE   TYPE_DISK
#define DEF_RANDOM false
#define DEF_REMOVABLE false
#define DEF_SCSI_LEVEL   7    /* INQUIRY, byte2 [6->SPC-4; 7->SPC-5] */
#define DEF_SECTOR_SIZE 512
#define DEF_UNMAP_ALIGNMENT 0
#define DEF_UNMAP_GRANULARITY 1
#define DEF_UNMAP_MAX_BLOCKS 0xFFFFFFFF
#define DEF_UNMAP_MAX_DESC 256
#define DEF_VIRTUAL_GB   0
#define DEF_VPD_USE_HOSTNO 1
#define DEF_WRITESAME_LENGTH 0xFFFF
#define DEF_ATOMIC_WR 0
#define DEF_ATOMIC_WR_MAX_LENGTH 128
#define DEF_ATOMIC_WR_ALIGN 2
#define DEF_ATOMIC_WR_GRAN 2
#define DEF_ATOMIC_WR_MAX_LENGTH_BNDRY (DEF_ATOMIC_WR_MAX_LENGTH)
#define DEF_ATOMIC_WR_MAX_BNDRY 128
#define DEF_STRICT 0
#define DEF_STATISTICS false
#define DEF_SUBMIT_QUEUES 1
#define DEF_TUR_MS_TO_READY 0
#define DEF_UUID_CTL 0
#define JDELAY_OVERRIDDEN -9999

/* Default parameters for ZBC drives */
#define DEF_ZBC_ZONE_SIZE_MB 128
#define DEF_ZBC_MAX_OPEN_ZONES 8
#define DEF_ZBC_NR_CONV_ZONES 1

/* Default parameters for tape drives */
#define TAPE_DEF_DENSITY  0x0
#define TAPE_BAD_DENSITY  0x65
#define TAPE_DEF_BLKSIZE  0
#define TAPE_MIN_BLKSIZE  512
#define TAPE_MAX_BLKSIZE  1048576
#define TAPE_EW 20
#define TAPE_MAX_PARTITIONS 2
#define TAPE_UNITS 10000
#define TAPE_PARTITION_1_UNITS 1000

/* The tape block data definitions */
#define TAPE_BLOCK_FM_FLAG   ((u32)0x1 << 30)
#define TAPE_BLOCK_EOD_FLAG  ((u32)0x2 << 30)
#define TAPE_BLOCK_MARK_MASK ((u32)0x3 << 30)
#define TAPE_BLOCK_SIZE_MASK (~TAPE_BLOCK_MARK_MASK)
#define TAPE_BLOCK_MARK(a) (a & TAPE_BLOCK_MARK_MASK)
#define TAPE_BLOCK_SIZE(a) (a & TAPE_BLOCK_SIZE_MASK)
#define IS_TAPE_BLOCK_FM(a)   ((a & TAPE_BLOCK_FM_FLAG) != 0)
#define IS_TAPE_BLOCK_EOD(a)  ((a & TAPE_BLOCK_EOD_FLAG) != 0)

struct tape_block {
 u32 fl_size;
 unsigned char data[4];
};

/* Flags for sense data */
#define SENSE_FLAG_FILEMARK  0x80
#define SENSE_FLAG_EOM 0x40
#define SENSE_FLAG_ILI 0x20

#define SDEBUG_LUN_0_VAL 0

/* bit mask values for sdebug_opts */
#define SDEBUG_OPT_NOISE  1
#define SDEBUG_OPT_MEDIUM_ERR  2
#define SDEBUG_OPT_TIMEOUT  4
#define SDEBUG_OPT_RECOVERED_ERR 8
#define SDEBUG_OPT_TRANSPORT_ERR 16
#define SDEBUG_OPT_DIF_ERR  32
#define SDEBUG_OPT_DIX_ERR  64
#define SDEBUG_OPT_MAC_TIMEOUT  128
#define SDEBUG_OPT_SHORT_TRANSFER 0x100
#define SDEBUG_OPT_Q_NOISE  0x200
#define SDEBUG_OPT_ALL_TSF  0x400 /* ignore */
#define SDEBUG_OPT_RARE_TSF  0x800
#define SDEBUG_OPT_N_WCE  0x1000
#define SDEBUG_OPT_RESET_NOISE  0x2000
#define SDEBUG_OPT_NO_CDB_NOISE  0x4000
#define SDEBUG_OPT_HOST_BUSY  0x8000
#define SDEBUG_OPT_CMD_ABORT  0x10000
#define SDEBUG_OPT_ALL_NOISE (SDEBUG_OPT_NOISE | SDEBUG_OPT_Q_NOISE | \
         SDEBUG_OPT_RESET_NOISE)
#define SDEBUG_OPT_ALL_INJECTING (SDEBUG_OPT_RECOVERED_ERR | \
      SDEBUG_OPT_TRANSPORT_ERR | \
      SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR | \
      SDEBUG_OPT_SHORT_TRANSFER | \
      SDEBUG_OPT_HOST_BUSY | \
      SDEBUG_OPT_CMD_ABORT)
#define SDEBUG_OPT_RECOV_DIF_DIX (SDEBUG_OPT_RECOVERED_ERR | \
      SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR)

/* As indicated in SAM-5 and SPC-4 Unit Attentions (UAs) are returned in
 * priority order. In the subset implemented here lower numbers have higher
 * priority. The UA numbers should be a sequence starting from 0 with
 * SDEBUG_NUM_UAS being 1 higher than the highest numbered UA. */

#define SDEBUG_UA_POR 0  /* Power on, reset, or bus device reset */
#define SDEBUG_UA_POOCCUR 1 /* Power on occurred */
#define SDEBUG_UA_BUS_RESET 2
#define SDEBUG_UA_MODE_CHANGED 3
#define SDEBUG_UA_CAPACITY_CHANGED 4
#define SDEBUG_UA_LUNS_CHANGED 5
#define SDEBUG_UA_MICROCODE_CHANGED 6 /* simulate firmware change */
#define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 7
#define SDEBUG_UA_NOT_READY_TO_READY 8
#define SDEBUG_NUM_UAS 9

/* when 1==SDEBUG_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 */
#define OPT_MEDIUM_ERR_NUM    10     /* number of consecutive medium errs */

/* SDEBUG_CANQUEUE is the maximum number of commands that can be queued
 * (for response) per submit queue at one time. Can be reduced by max_queue
 * option. Command responses are not queued when jdelay=0 and ndelay=0. The
 * per-device DEF_CMD_PER_LUN can be changed via sysfs:
 * /sys/class/scsi_device/<h:c:t:l>/device/queue_depth
 * but cannot exceed SDEBUG_CANQUEUE .
 */

#define SDEBUG_CANQUEUE_WORDS  3 /* a WORD is bits in a long */
#define SDEBUG_CANQUEUE  (SDEBUG_CANQUEUE_WORDS * BITS_PER_LONG)
#define DEF_CMD_PER_LUN  SDEBUG_CANQUEUE

/* UA - Unit Attention; SA - Service Action; SSU - Start Stop Unit */
#define F_D_IN   1 /* Data-in command (e.g. READ) */
#define F_D_OUT   2 /* Data-out command (e.g. WRITE) */
#define F_D_OUT_MAYBE  4 /* WRITE SAME, NDOB bit */
#define F_D_UNKN  8
#define F_RL_WLUN_OK  0x10 /* allowed with REPORT LUNS W-LUN */
#define F_SKIP_UA  0x20 /* bypass UAs (e.g. INQUIRY command) */
#define F_DELAY_OVERR  0x40 /* for commands like INQUIRY */
#define F_SA_LOW  0x80 /* SA is in cdb byte 1, bits 4 to 0 */
#define F_SA_HIGH  0x100 /* SA is in cdb bytes 8 and 9 */
#define F_INV_OP  0x200 /* invalid opcode (not supported) */
#define F_FAKE_RW  0x400 /* bypass resp_*() when fake_rw set */
#define F_M_ACCESS  0x800 /* media access, reacts to SSU state */
#define F_SSU_DELAY  0x1000 /* SSU command delay (long-ish) */
#define F_SYNC_DELAY  0x2000 /* SYNCHRONIZE CACHE delay */

/* Useful combinations of the above flags */
#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR)
#define FF_MEDIA_IO (F_M_ACCESS | F_FAKE_RW)
#define FF_SA (F_SA_HIGH | F_SA_LOW)
#define F_LONG_DELAY  (F_SSU_DELAY | F_SYNC_DELAY)

/* Device selection bit mask */
#define DS_ALL     0xffffffff
#define DS_SBC     (1 << TYPE_DISK)
#define DS_SSC     (1 << TYPE_TAPE)
#define DS_ZBC     (1 << TYPE_ZBC)

#define DS_NO_SSC  (DS_ALL & ~DS_SSC)

#define SDEBUG_MAX_PARTS 4

#define SDEBUG_MAX_CMD_LEN 32

#define SDEB_XA_NOT_IN_USE XA_MARK_1

/* Zone types (zbcr05 table 25) */
enum sdebug_z_type {
 ZBC_ZTYPE_CNV = 0x1,
 ZBC_ZTYPE_SWR = 0x2,
 ZBC_ZTYPE_SWP = 0x3,
 /* ZBC_ZTYPE_SOBR = 0x4, */
 ZBC_ZTYPE_GAP = 0x5,
};

/* enumeration names taken from table 26, zbcr05 */
enum sdebug_z_cond {
 ZBC_NOT_WRITE_POINTER = 0x0,
 ZC1_EMPTY  = 0x1,
 ZC2_IMPLICIT_OPEN = 0x2,
 ZC3_EXPLICIT_OPEN = 0x3,
 ZC4_CLOSED  = 0x4,
 ZC6_READ_ONLY  = 0xd,
 ZC5_FULL  = 0xe,
 ZC7_OFFLINE  = 0xf,
};

struct sdeb_zone_state { /* ZBC: per zone state */
 enum sdebug_z_type z_type;
 enum sdebug_z_cond z_cond;
 bool z_non_seq_resource;
 unsigned int z_size;
 sector_t z_start;
 sector_t z_wp;
};

enum sdebug_err_type {
 ERR_TMOUT_CMD  = 0, /* make specific scsi command timeout */
 ERR_FAIL_QUEUE_CMD = 1, /* make specific scsi command's */
     /* queuecmd return failed */
 ERR_FAIL_CMD  = 2, /* make specific scsi command's */
     /* queuecmd return succeed but */
     /* with errors set in scsi_cmnd */
 ERR_ABORT_CMD_FAILED = 3, /* control return FAILED from */
     /* scsi_debug_abort() */
 ERR_LUN_RESET_FAILED = 4, /* control return FAILED from */
     /* scsi_debug_device_reseLUN_RESET_FAILEDt() */
};

struct sdebug_err_inject {
 int type;
 struct list_head list;
 int cnt;
 unsigned char cmd;
 struct rcu_head rcu;

 union {
  /*
 * For ERR_FAIL_QUEUE_CMD
 */

  int queuecmd_ret;

  /*
 * For ERR_FAIL_CMD
 */

  struct {
   unsigned char host_byte;
   unsigned char driver_byte;
   unsigned char status_byte;
   unsigned char sense_key;
   unsigned char asc;
   unsigned char asq;
  };
 };
};

struct sdebug_dev_info {
 struct list_head dev_list;
 unsigned int channel;
 unsigned int target;
 u64 lun;
 uuid_t lu_name;
 struct sdebug_host_info *sdbg_host;
 unsigned long uas_bm[1];
 atomic_t stopped; /* 1: by SSU, 2: device start */
 bool used;

 /* For ZBC devices */
 bool zoned;
 unsigned int zcap;
 unsigned int zsize;
 unsigned int zsize_shift;
 unsigned int nr_zones;
 unsigned int nr_conv_zones;
 unsigned int nr_seq_zones;
 unsigned int nr_imp_open;
 unsigned int nr_exp_open;
 unsigned int nr_closed;
 unsigned int max_open;
 ktime_t create_ts; /* time since bootup that this device was created */
 struct sdeb_zone_state *zstate;

 /* For tapes */
 unsigned int tape_blksize;
 unsigned int tape_density;
 unsigned char tape_partition;
 unsigned char tape_nbr_partitions;
 unsigned char tape_pending_nbr_partitions;
 unsigned int tape_pending_part_0_size;
 unsigned int tape_pending_part_1_size;
 unsigned char tape_dce;
 unsigned int tape_location[TAPE_MAX_PARTITIONS];
 unsigned int tape_eop[TAPE_MAX_PARTITIONS];
 struct tape_block *tape_blocks[TAPE_MAX_PARTITIONS];

 struct dentry *debugfs_entry;
 struct spinlock list_lock;
 struct list_head inject_err_list;
};

struct sdebug_target_info {
 bool reset_fail;
 struct dentry *debugfs_entry;
};

struct sdebug_host_info {
 struct list_head host_list;
 int si_idx; /* sdeb_store_info (per host) xarray index */
 struct Scsi_Host *shost;
 struct device dev;
 struct list_head dev_info_list;
};

/* There is an xarray of pointers to this struct's objects, one per host */
struct sdeb_store_info {
 rwlock_t macc_data_lck; /* for media data access on this store */
 rwlock_t macc_meta_lck; /* for atomic media meta access on this store */
 rwlock_t macc_sector_lck; /* per-sector media data access on this store */
 u8 *storep;  /* user data storage (ram) */
 struct t10_pi_tuple *dif_storep; /* protection info */
 void *map_storep; /* provisioning map */
};

#define dev_to_sdebug_host(d) \
 container_of(d, struct sdebug_host_info, dev)

#define shost_to_sdebug_host(shost) \
 dev_to_sdebug_host(shost->dma_dev)

enum sdeb_defer_type {SDEB_DEFER_NONE = 0, SDEB_DEFER_HRT = 1,
        SDEB_DEFER_WQ = 2, SDEB_DEFER_POLL = 3};

struct sdebug_defer {
 struct hrtimer hrt;
 struct execute_work ew;
 ktime_t cmpl_ts;/* time since boot to complete this cmd */
 int issuing_cpu;
 bool aborted; /* true when blk_abort_request() already called */
 enum sdeb_defer_type defer_t;
};

struct sdebug_scsi_cmd {
 spinlock_t   lock;
 struct sdebug_defer sd_dp;
};

static atomic_t sdebug_cmnd_count;   /* number of incoming commands */
static atomic_t sdebug_completions;  /* count of deferred completions */
static atomic_t sdebug_miss_cpus;    /* submission + completion cpus differ */
static atomic_t sdebug_a_tsf;      /* 'almost task set full' counter */
static atomic_t sdeb_inject_pending;
static atomic_t sdeb_mq_poll_count;  /* bumped when mq_poll returns > 0 */

struct opcode_info_t {
 u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff */
    /* for terminating element */
 u8 opcode;  /* if num_attached > 0, preferred */
 u16 sa;   /* service action */
 u32 devsel;  /* device type mask for this definition */
 u32 flags;  /* OR-ed set of SDEB_F_* */
 int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *);
 const struct opcode_info_t *arrp;  /* num_attached elements or NULL */
 u8 len_mask[16]; /* len_mask[0]-->cdb_len, then mask for cdb */
    /* 1 to min(cdb_len, 15); ignore cdb[15...] */
};

/* SCSI opcodes (first byte of cdb) of interest mapped onto these indexes */
enum sdeb_opcode_index {
 SDEB_I_INVALID_OPCODE = 0,
 SDEB_I_INQUIRY = 1,
 SDEB_I_REPORT_LUNS = 2,
 SDEB_I_REQUEST_SENSE = 3,
 SDEB_I_TEST_UNIT_READY = 4,
 SDEB_I_MODE_SENSE = 5,  /* 6, 10 */
 SDEB_I_MODE_SELECT = 6,  /* 6, 10 */
 SDEB_I_LOG_SENSE = 7,
 SDEB_I_READ_CAPACITY = 8, /* 10; 16 is in SA_IN(16) */
 SDEB_I_READ = 9,  /* 6, 10, 12, 16 */
 SDEB_I_WRITE = 10,  /* 6, 10, 12, 16 */
 SDEB_I_START_STOP = 11,
 SDEB_I_SERV_ACT_IN_16 = 12, /* add ...SERV_ACT_IN_12 if needed */
 SDEB_I_SERV_ACT_OUT_16 = 13, /* add ...SERV_ACT_OUT_12 if needed */
 SDEB_I_MAINT_IN = 14,
 SDEB_I_MAINT_OUT = 15,
 SDEB_I_VERIFY = 16,  /* VERIFY(10), VERIFY(16) */
 SDEB_I_VARIABLE_LEN = 17, /* READ(32), WRITE(32), WR_SCAT(32) */
 SDEB_I_RESERVE = 18,  /* 6, 10 */
 SDEB_I_RELEASE = 19,  /* 6, 10 */
 SDEB_I_ALLOW_REMOVAL = 20, /* PREVENT ALLOW MEDIUM REMOVAL */
 SDEB_I_REZERO_UNIT = 21, /* REWIND in SSC */
 SDEB_I_ATA_PT = 22,  /* 12, 16 */
 SDEB_I_SEND_DIAG = 23,
 SDEB_I_UNMAP = 24,
 SDEB_I_WRITE_BUFFER = 25,
 SDEB_I_WRITE_SAME = 26,  /* 10, 16 */
 SDEB_I_SYNC_CACHE = 27,  /* 10, 16 */
 SDEB_I_COMP_WRITE = 28,
 SDEB_I_PRE_FETCH = 29,  /* 10, 16 */
 SDEB_I_ZONE_OUT = 30,  /* 0x94+SA; includes no data xfer */
 SDEB_I_ZONE_IN = 31,  /* 0x95+SA; all have data-in */
 SDEB_I_ATOMIC_WRITE_16 = 32,
 SDEB_I_READ_BLOCK_LIMITS = 33,
 SDEB_I_LOCATE = 34,
 SDEB_I_WRITE_FILEMARKS = 35,
 SDEB_I_SPACE = 36,
 SDEB_I_FORMAT_MEDIUM = 37,
 SDEB_I_ERASE = 38,
 SDEB_I_LAST_ELEM_P1 = 39, /* keep this last (previous + 1) */
};


static const unsigned char opcode_ind_arr[256] = {
/* 0x0; 0x0->0x1f: 6 byte cdbs */
 SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
     SDEB_I_FORMAT_MEDIUM, SDEB_I_READ_BLOCK_LIMITS, 0, 0,
 SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
 SDEB_I_WRITE_FILEMARKS, SDEB_I_SPACE, SDEB_I_INQUIRY, 0, 0,
     SDEB_I_MODE_SELECT, SDEB_I_RESERVE, SDEB_I_RELEASE,
 0, SDEB_I_ERASE, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
     SDEB_I_ALLOW_REMOVAL, 0,
/* 0x20; 0x20->0x3f: 10 byte cdbs */
 0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0,
 SDEB_I_READ, 0, SDEB_I_WRITE, SDEB_I_LOCATE, 0, 0, 0, SDEB_I_VERIFY,
 0, 0, 0, 0, SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, 0,
 0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0,
/* 0x40; 0x40->0x5f: 10 byte cdbs */
 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0,
 0, 0, 0, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
     SDEB_I_RELEASE,
 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0,
/* 0x60; 0x60->0x7d are reserved, 0x7e is "extended cdb" */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, SDEB_I_VARIABLE_LEN,
/* 0x80; 0x80->0x9f: 16 byte cdbs */
 0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
 SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0,
 0, 0, 0, SDEB_I_VERIFY,
 SDEB_I_PRE_FETCH, SDEB_I_SYNC_CACHE, 0, SDEB_I_WRITE_SAME,
 SDEB_I_ZONE_OUT, SDEB_I_ZONE_IN, 0, 0,
 0, 0, 0, 0,
 SDEB_I_ATOMIC_WRITE_16, 0, SDEB_I_SERV_ACT_IN_16, SDEB_I_SERV_ACT_OUT_16,
/* 0xa0; 0xa0->0xbf: 12 byte cdbs */
 SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
      SDEB_I_MAINT_OUT, 0, 0, 0,
 SDEB_I_READ, 0 /* SDEB_I_SERV_ACT_OUT_12 */, SDEB_I_WRITE,
      0 /* SDEB_I_SERV_ACT_IN_12 */, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xc0; 0xc0->0xff: vendor specific */
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

/*
 * The following "response" functions return the SCSI mid-level's 4 byte
 * tuple-in-an-int. To handle commands with an IMMED bit, for a faster
 * command completion, they can mask their return value with
 * SDEG_RES_IMMED_MASK .
 */

#define SDEG_RES_IMMED_MASK 0x40000000

static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_mode_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_mode_select(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_log_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_readcap(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_read_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_read_tape(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_tape(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_scat(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_start_stop(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_readcap16(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_get_stream_status(struct scsi_cmnd *scp,
      struct sdebug_dev_info *devip);
static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_verify(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_buffer(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_sync_cache(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_pre_fetch(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_report_zones(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_atomic_write(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_open_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_close_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_finish_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rwp_zone(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_read_blklimits(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_locate(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_write_filemarks(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_space(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_read_position(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_rewind(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_format_medium(struct scsi_cmnd *, struct sdebug_dev_info *);
static int resp_erase(struct scsi_cmnd *, struct sdebug_dev_info *);

static int sdebug_do_add_host(bool mk_new_store);
static int sdebug_add_host_helper(int per_host_idx);
static void sdebug_do_remove_host(bool the_end);
static int sdebug_add_store(void);
static void sdebug_erase_store(int idx, struct sdeb_store_info *sip);
static void sdebug_erase_all_stores(bool apart_from_first);

/*
 * The following are overflow arrays for cdbs that "hit" the same index in
 * the opcode_info_arr array. The most time sensitive (or commonly used) cdb
 * should be placed in opcode_info_arr[], the others should be placed here.
 */

static const struct opcode_info_t msense_iarr[] = {
 {0, 0x1a, 0, DS_ALL, F_D_IN, NULL, NULL,
     {6,  0xe8, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};

static const struct opcode_info_t mselect_iarr[] = {
 {0, 0x15, 0, DS_ALL, F_D_OUT, NULL, NULL,
     {6,  0xf1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};

static const struct opcode_info_t read_iarr[] = {
 {0, 0x28, 0, DS_NO_SSC, F_D_IN | FF_MEDIA_IO, resp_read_dt0, NULL,/* READ(10) */
     {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },
 {0, 0x8, 0, DS_NO_SSC, F_D_IN | FF_MEDIA_IO, resp_read_dt0, NULL, /* READ(6) disk */
     {6,  0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x8, 0, DS_SSC, F_D_IN | FF_MEDIA_IO, resp_read_tape, NULL, /* READ(6) tape */
     {6,  0x03, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0xa8, 0, DS_NO_SSC, F_D_IN | FF_MEDIA_IO, resp_read_dt0, NULL,/* READ(12) */
     {12,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf,
      0xc7, 0, 0, 0, 0} },
};

static const struct opcode_info_t write_iarr[] = {
 {0, 0x2a, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO, resp_write_dt0,  /* WRITE(10) */
     NULL, {10,  0xfb, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7,
     0, 0, 0, 0, 0, 0} },
 {0, 0xa, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO, resp_write_dt0, /* WRITE(6) disk */
     NULL, {6,  0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0} },
 {0, 0xa, 0, DS_SSC, F_D_OUT | FF_MEDIA_IO, resp_write_tape, /* WRITE(6) tape */
     NULL, {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0} },
 {0, 0xaa, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO, resp_write_dt0,  /* WRITE(12) */
     NULL, {12,  0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xbf, 0xc7, 0, 0, 0, 0} },
};

static const struct opcode_info_t verify_iarr[] = {
 {0, 0x2f, 0, DS_NO_SSC, F_D_OUT_MAYBE | FF_MEDIA_IO, resp_verify,/* VERIFY(10) */
     NULL, {10,  0xf7, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xc7,
     0, 0, 0, 0, 0, 0} },
};

static const struct opcode_info_t sa_in_16_iarr[] = {
 {0, 0x9e, 0x12, DS_NO_SSC, F_SA_LOW | F_D_IN, resp_get_lba_status, NULL,
     {16,  0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0, 0xc7} }, /* GET LBA STATUS(16) */
 {0, 0x9e, 0x16, DS_NO_SSC, F_SA_LOW | F_D_IN, resp_get_stream_status, NULL,
     {16, 0x16, 0, 0, 0xff, 0xff, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff,
      0, 0} }, /* GET STREAM STATUS */
};

static const struct opcode_info_t vl_iarr[] = { /* VARIABLE LENGTH */
 {0, 0x7f, 0xb, DS_NO_SSC, F_SA_HIGH | F_D_OUT | FF_MEDIA_IO, resp_write_dt0,
     NULL, {32,  0xc7, 0, 0, 0, 0, 0x3f, 0x18, 0x0, 0xb, 0xfa,
     0, 0xff, 0xff, 0xff, 0xff} }, /* WRITE(32) */
 {0, 0x7f, 0x11, DS_NO_SSC, F_SA_HIGH | F_D_OUT | FF_MEDIA_IO, resp_write_scat,
     NULL, {32,  0xc7, 0, 0, 0, 0, 0x3f, 0x18, 0x0, 0x11, 0xf8,
     0, 0xff, 0xff, 0x0, 0x0} }, /* WRITE SCATTERED(32) */
};

static const struct opcode_info_t maint_in_iarr[] = { /* MAINT IN */
 {0, 0xa3, 0xc, DS_ALL, F_SA_LOW | F_D_IN, resp_rsup_opcodes, NULL,
     {12,  0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0,
      0xc7, 0, 0, 0, 0} }, /* REPORT SUPPORTED OPERATION CODES */
 {0, 0xa3, 0xd, DS_ALL, F_SA_LOW | F_D_IN, resp_rsup_tmfs, NULL,
     {12,  0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
      0, 0} }, /* REPORTED SUPPORTED TASK MANAGEMENT FUNCTIONS */
};

static const struct opcode_info_t write_same_iarr[] = {
 {0, 0x93, 0, DS_NO_SSC, F_D_OUT_MAYBE | FF_MEDIA_IO, resp_write_same_16, NULL,
     {16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0x3f, 0xc7} },  /* WRITE SAME(16) */
};

static const struct opcode_info_t reserve_iarr[] = {
 {0, 0x16, 0, DS_ALL, F_D_OUT, NULL, NULL, /* RESERVE(6) */
     {6,  0x1f, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};

static const struct opcode_info_t release_iarr[] = {
 {0, 0x17, 0, DS_ALL, F_D_OUT, NULL, NULL, /* RELEASE(6) */
     {6,  0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};

static const struct opcode_info_t sync_cache_iarr[] = {
 {0, 0x91, 0, DS_NO_SSC, F_SYNC_DELAY | F_M_ACCESS, resp_sync_cache, NULL,
     {16,  0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* SYNC_CACHE (16) */
};

static const struct opcode_info_t pre_fetch_iarr[] = {
 {0, 0x90, 0, DS_NO_SSC, F_SYNC_DELAY | FF_MEDIA_IO, resp_pre_fetch, NULL,
     {16,  0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* PRE-FETCH (16) */
 {0, 0x34, 0, DS_SSC, F_SYNC_DELAY | FF_MEDIA_IO, resp_read_position, NULL,
     {10,  0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },    /* READ POSITION (10) */
};

static const struct opcode_info_t zone_out_iarr[] = { /* ZONE OUT(16) */
 {0, 0x94, 0x1, DS_NO_SSC, F_SA_LOW | F_M_ACCESS, resp_close_zone, NULL,
     {16, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} }, /* CLOSE ZONE */
 {0, 0x94, 0x2, DS_NO_SSC, F_SA_LOW | F_M_ACCESS, resp_finish_zone, NULL,
     {16, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} }, /* FINISH ZONE */
 {0, 0x94, 0x4, DS_NO_SSC, F_SA_LOW | F_M_ACCESS, resp_rwp_zone, NULL,
     {16, 0x4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0, 0, 0xff, 0xff, 0x1, 0xc7} },  /* RESET WRITE POINTER */
};

static const struct opcode_info_t zone_in_iarr[] = { /* ZONE IN(16) */
 {0, 0x95, 0x6, DS_NO_SSC, F_SA_LOW | F_D_IN | F_M_ACCESS, NULL, NULL,
     {16, 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} }, /* REPORT ZONES */
};


/* This array is accessed via SDEB_I_* values. Make sure all are mapped,
 * plus the terminating elements for logic that scans this table such as
 * REPORT SUPPORTED OPERATION CODES. */

static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEM_P1 + 1] = {
/* 0 */
 {0, 0, 0, DS_ALL, F_INV_OP | FF_RESPOND, NULL, NULL, /* unknown opcodes */
     {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x12, 0, DS_ALL, FF_RESPOND | F_D_IN, resp_inquiry, NULL, /* INQUIRY */
     {6,  0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0xa0, 0, DS_ALL, FF_RESPOND | F_D_IN, resp_report_luns, NULL,
     {12,  0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
      0, 0} },     /* REPORT LUNS */
 {0, 0x3, 0, DS_ALL, FF_RESPOND | F_D_IN, resp_requests, NULL,
     {6,  0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x0, 0, DS_ALL, F_M_ACCESS | F_RL_WLUN_OK, NULL, NULL,/* TEST UNIT READY */
     {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
/* 5 */
 {ARRAY_SIZE(msense_iarr), 0x5a, 0, DS_ALL, F_D_IN, /* MODE SENSE(10) */
     resp_mode_sense, msense_iarr, {10,  0xf8, 0xff, 0xff, 0, 0, 0,
  0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
 {ARRAY_SIZE(mselect_iarr), 0x55, 0, DS_ALL, F_D_OUT, /* MODE SELECT(10) */
     resp_mode_select, mselect_iarr, {10,  0xf1, 0, 0, 0, 0, 0, 0xff,
  0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
 {0, 0x4d, 0, DS_NO_SSC, F_D_IN, resp_log_sense, NULL, /* LOG SENSE */
     {10,  0xe3, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0,
      0, 0, 0} },
 {0, 0x25, 0, DS_NO_SSC, F_D_IN, resp_readcap, NULL,    /* READ CAPACITY(10) */
     {10,  0xe1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0,
      0, 0} },
 {ARRAY_SIZE(read_iarr), 0x88, 0, DS_NO_SSC, F_D_IN | FF_MEDIA_IO, /* READ(16) */
     resp_read_dt0, read_iarr, {16,  0xfe, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} },
/* 10 */
 {ARRAY_SIZE(write_iarr), 0x8a, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO,
     resp_write_dt0, write_iarr,   /* WRITE(16) */
  {16,  0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xc7} },
 {0, 0x1b, 0, DS_ALL, F_SSU_DELAY, resp_start_stop, NULL,/* START STOP UNIT */
     {6,  0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {ARRAY_SIZE(sa_in_16_iarr), 0x9e, 0x10, DS_NO_SSC, F_SA_LOW | F_D_IN,
     resp_readcap16, sa_in_16_iarr, /* SA_IN(16), READ CAPACITY(16) */
  {16,  0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0x1, 0xc7} },
 {0, 0x9f, 0x12, DS_NO_SSC, F_SA_LOW | F_D_OUT | FF_MEDIA_IO, resp_write_scat,
     NULL, {16,  0x12, 0xf9, 0x0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xc7} },  /* SA_OUT(16), WRITE SCAT(16) */
 {ARRAY_SIZE(maint_in_iarr), 0xa3, 0xa, DS_ALL, F_SA_LOW | F_D_IN,
     resp_report_tgtpgs, /* MAINT IN, REPORT TARGET PORT GROUPS */
  maint_in_iarr, {12,  0xea, 0, 0, 0, 0, 0xff, 0xff, 0xff,
    0xff, 0, 0xc7, 0, 0, 0, 0} },
/* 15 */
 {0, 0, 0, DS_ALL, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */
     {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {ARRAY_SIZE(verify_iarr), 0x8f, 0, DS_NO_SSC,
     F_D_OUT_MAYBE | FF_MEDIA_IO, resp_verify, /* VERIFY(16) */
     verify_iarr, {16,  0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xc7} },
 {ARRAY_SIZE(vl_iarr), 0x7f, 0x9, DS_NO_SSC, F_SA_HIGH | F_D_IN | FF_MEDIA_IO,
     resp_read_dt0, vl_iarr, /* VARIABLE LENGTH, READ(32) */
     {32,  0xc7, 0, 0, 0, 0, 0x3f, 0x18, 0x0, 0x9, 0xfe, 0, 0xff, 0xff,
      0xff, 0xff} },
 {ARRAY_SIZE(reserve_iarr), 0x56, 0, DS_ALL, F_D_OUT,
     NULL, reserve_iarr, /* RESERVE(10) <no response function> */
     {10,  0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
      0} },
 {ARRAY_SIZE(release_iarr), 0x57, 0, DS_ALL, F_D_OUT,
     NULL, release_iarr, /* RELEASE(10) <no response function> */
     {10,  0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
      0} },
/* 20 */
 {0, 0x1e, 0, DS_ALL, 0, NULL, NULL, /* ALLOW REMOVAL */
     {6,  0, 0, 0, 0x3, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x1, 0, DS_SSC, 0, resp_rewind, NULL,
     {6,  0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0, 0, DS_NO_SSC, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
     {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x1d, 0, DS_ALL, F_D_OUT, NULL, NULL,      /* SEND DIAGNOSTIC */
     {6,  0xf7, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x42, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO, resp_unmap, NULL, /* UNMAP */
     {10,  0x1, 0, 0, 0, 0, 0x3f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
/* 25 */
 {0, 0x3b, 0, DS_NO_SSC, F_D_OUT_MAYBE, resp_write_buffer, NULL,
     {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },   /* WRITE_BUFFER */
 {ARRAY_SIZE(write_same_iarr), 0x41, 0, DS_NO_SSC, F_D_OUT_MAYBE | FF_MEDIA_IO,
     resp_write_same_10, write_same_iarr, /* WRITE SAME(10) */
  {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0,
   0, 0, 0, 0, 0} },
 {ARRAY_SIZE(sync_cache_iarr), 0x35, 0, DS_NO_SSC, F_SYNC_DELAY | F_M_ACCESS,
     resp_sync_cache, sync_cache_iarr,
     {10,  0x7, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },   /* SYNC_CACHE (10) */
 {0, 0x89, 0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO, resp_comp_write, NULL,
     {16,  0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0,
      0, 0xff, 0x3f, 0xc7} },  /* COMPARE AND WRITE */
 {ARRAY_SIZE(pre_fetch_iarr), 0x34, 0, DS_NO_SSC, F_SYNC_DELAY | FF_MEDIA_IO,
     resp_pre_fetch, pre_fetch_iarr,
     {10,  0x2, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },   /* PRE-FETCH (10) */
      /* READ POSITION (10) */

/* 30 */
 {ARRAY_SIZE(zone_out_iarr), 0x94, 0x3, DS_NO_SSC, F_SA_LOW | F_M_ACCESS,
     resp_open_zone, zone_out_iarr, /* ZONE_OUT(16), OPEN ZONE) */
  {16,  0x3 /* SA */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x1, 0xc7} },
 {ARRAY_SIZE(zone_in_iarr), 0x95, 0x0, DS_NO_SSC, F_SA_LOW | F_M_ACCESS,
     resp_report_zones, zone_in_iarr, /* ZONE_IN(16), REPORT ZONES) */
  {16,  0x0 /* SA */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xc7} },
/* 32 */
 {0, 0x9c, 0x0, DS_NO_SSC, F_D_OUT | FF_MEDIA_IO,
     resp_atomic_write, NULL, /* ATOMIC WRITE 16 */
  {16,  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} },
 {0, 0x05, 0, DS_SSC, F_D_IN, resp_read_blklimits, NULL,    /* READ BLOCK LIMITS (6) */
     {6,  0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x2b, 0, DS_SSC, F_D_UNKN, resp_locate, NULL,    /* LOCATE (10) */
     {10,  0x07, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xff, 0xc7, 0, 0,
      0, 0, 0, 0} },
 {0, 0x10, 0, DS_SSC, F_D_IN, resp_write_filemarks, NULL,   /* WRITE FILEMARKS (6) */
     {6,  0x01, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x11, 0, DS_SSC, F_D_IN, resp_space, NULL,    /* SPACE (6) */
     {6,  0x07, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x4, 0, DS_SSC, 0, resp_format_medium, NULL,  /* FORMAT MEDIUM (6) */
     {6,  0x3, 0x7, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
 {0, 0x19, 0, DS_SSC, F_D_IN, resp_erase, NULL,    /* ERASE (6) */
     {6,  0x03, 0x33, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
/* 39 */
/* sentinel */
 {0xff, 0, 0, 0, 0, NULL, NULL,  /* terminating element */
     {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};

static int sdebug_num_hosts;
static int sdebug_add_host = DEF_NUM_HOST;  /* in sysfs this is relative */
static int sdebug_ato = DEF_ATO;
static int sdebug_cdb_len = DEF_CDB_LEN;
static int sdebug_jdelay = DEF_JDELAY; /* if > 0 then unit is jiffies */
static int sdebug_dev_size_mb = DEF_DEV_SIZE_PRE_INIT;
static int sdebug_dif = DEF_DIF;
static int sdebug_dix = DEF_DIX;
static int sdebug_dsense = DEF_D_SENSE;
static int sdebug_every_nth = DEF_EVERY_NTH;
static int sdebug_fake_rw = DEF_FAKE_RW;
static unsigned int sdebug_guard = DEF_GUARD;
static int sdebug_host_max_queue; /* per host */
static int sdebug_lowest_aligned = DEF_LOWEST_ALIGNED;
static int sdebug_max_luns = DEF_MAX_LUNS;
static int sdebug_max_queue = SDEBUG_CANQUEUE; /* per submit queue */
static unsigned int sdebug_medium_error_start = OPT_MEDIUM_ERR_ADDR;
static int sdebug_medium_error_count = OPT_MEDIUM_ERR_NUM;
static int sdebug_ndelay = DEF_NDELAY; /* if > 0 then unit is nanoseconds */
static int sdebug_no_lun_0 = DEF_NO_LUN_0;
static int sdebug_no_uld;
static int sdebug_num_parts = DEF_NUM_PARTS;
static int sdebug_num_tgts = DEF_NUM_TGTS; /* targets per host */
static int sdebug_opt_blks = DEF_OPT_BLKS;
static int sdebug_opts = DEF_OPTS;
static int sdebug_physblk_exp = DEF_PHYSBLK_EXP;
static int sdebug_opt_xferlen_exp = DEF_OPT_XFERLEN_EXP;
static int sdebug_ptype = DEF_PTYPE; /* SCSI peripheral device type */
static int sdebug_scsi_level = DEF_SCSI_LEVEL;
static int sdebug_sector_size = DEF_SECTOR_SIZE;
static int sdeb_tur_ms_to_ready = DEF_TUR_MS_TO_READY;
static int sdebug_virtual_gb = DEF_VIRTUAL_GB;
static int sdebug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
static unsigned int sdebug_lbpu = DEF_LBPU;
static unsigned int sdebug_lbpws = DEF_LBPWS;
static unsigned int sdebug_lbpws10 = DEF_LBPWS10;
static unsigned int sdebug_lbprz = DEF_LBPRZ;
static unsigned int sdebug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
static unsigned int sdebug_unmap_granularity = DEF_UNMAP_GRANULARITY;
static unsigned int sdebug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
static unsigned int sdebug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
static unsigned int sdebug_write_same_length = DEF_WRITESAME_LENGTH;
static unsigned int sdebug_atomic_wr = DEF_ATOMIC_WR;
static unsigned int sdebug_atomic_wr_max_length = DEF_ATOMIC_WR_MAX_LENGTH;
static unsigned int sdebug_atomic_wr_align = DEF_ATOMIC_WR_ALIGN;
static unsigned int sdebug_atomic_wr_gran = DEF_ATOMIC_WR_GRAN;
static unsigned int sdebug_atomic_wr_max_length_bndry =
   DEF_ATOMIC_WR_MAX_LENGTH_BNDRY;
static unsigned int sdebug_atomic_wr_max_bndry = DEF_ATOMIC_WR_MAX_BNDRY;
static int sdebug_uuid_ctl = DEF_UUID_CTL;
static bool sdebug_random = DEF_RANDOM;
static bool sdebug_per_host_store = DEF_PER_HOST_STORE;
static bool sdebug_removable = DEF_REMOVABLE;
static bool sdebug_clustering;
static bool sdebug_host_lock = DEF_HOST_LOCK;
static bool sdebug_strict = DEF_STRICT;
static bool sdebug_any_injecting_opt;
static bool sdebug_no_rwlock;
static bool sdebug_verbose;
static bool have_dif_prot;
static bool write_since_sync;
static bool sdebug_statistics = DEF_STATISTICS;
static bool sdebug_wp;
static bool sdebug_allow_restart;
static enum {
 BLK_ZONED_NONE = 0,
 BLK_ZONED_HA = 1,
 BLK_ZONED_HM = 2,
} sdeb_zbc_model = BLK_ZONED_NONE;
static char *sdeb_zbc_model_s;

enum sam_lun_addr_method {SAM_LUN_AM_PERIPHERAL = 0x0,
     SAM_LUN_AM_FLAT = 0x1,
     SAM_LUN_AM_LOGICAL_UNIT = 0x2,
     SAM_LUN_AM_EXTENDED = 0x3};
static enum sam_lun_addr_method sdebug_lun_am = SAM_LUN_AM_PERIPHERAL;
static int sdebug_lun_am_i = (int)SAM_LUN_AM_PERIPHERAL;

static unsigned int sdebug_store_sectors;
static sector_t sdebug_capacity; /* in sectors */

/* old BIOS stuff, kernel may get rid of them but some mode sense pages
   may still need them */

static int sdebug_heads;  /* heads per disk */
static int sdebug_cylinders_per; /* cylinders per surface */
static int sdebug_sectors_per;  /* sectors per cylinder */

static LIST_HEAD(sdebug_host_list);
static DEFINE_MUTEX(sdebug_host_list_mutex);

static struct xarray per_store_arr;
static struct xarray *per_store_ap = &per_store_arr;
static int sdeb_first_idx = -1;  /* invalid index ==> none created */
static int sdeb_most_recent_idx = -1;
static DEFINE_RWLOCK(sdeb_fake_rw_lck); /* need a RW lock when fake_rw=1 */

static unsigned long map_size;
static int num_aborts;
static int num_dev_resets;
static int num_target_resets;
static int num_bus_resets;
static int num_host_resets;
static int dix_writes;
static int dix_reads;
static int dif_errors;

/* ZBC global data */
static bool sdeb_zbc_in_use; /* true for host-aware and host-managed disks */
static int sdeb_zbc_zone_cap_mb;
static int sdeb_zbc_zone_size_mb;
static int sdeb_zbc_max_open = DEF_ZBC_MAX_OPEN_ZONES;
static int sdeb_zbc_nr_conv = DEF_ZBC_NR_CONV_ZONES;

static int submit_queues = DEF_SUBMIT_QUEUES;  /* > 1 for multi-queue (mq) */
static int poll_queues; /* iouring iopoll interface.*/

static atomic_long_t writes_by_group_number[64];

static char sdebug_proc_name[] = MY_NAME;
static const char *my_name = MY_NAME;

static const struct bus_type pseudo_lld_bus;

static struct device_driver sdebug_driverfs_driver = {
 .name   = sdebug_proc_name,
 .bus  = &pseudo_lld_bus,
};

static const int check_condition_result =
 SAM_STAT_CHECK_CONDITION;

static const int illegal_condition_result =
 (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION;

static const int device_qfull_result =
 (DID_ABORT << 16) | SAM_STAT_TASK_SET_FULL;

static const int condition_met_result = SAM_STAT_CONDITION_MET;

static struct dentry *sdebug_debugfs_root;
static ASYNC_DOMAIN_EXCLUSIVE(sdebug_async_domain);

static u32 sdebug_get_devsel(struct scsi_device *sdp)
{
 unsigned char devtype = sdp->type;
 u32 devsel;

 if (devtype < 32)
  devsel = (1 << devtype);
 else
  devsel = DS_ALL;

 return devsel;
}

static void sdebug_err_free(struct rcu_head *head)
{
 struct sdebug_err_inject *inject =
  container_of(head, typeof(*inject), rcu);

 kfree(inject);
}

static void sdebug_err_add(struct scsi_device *sdev, struct sdebug_err_inject *new)
{
 struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
 struct sdebug_err_inject *err;

 spin_lock(&devip->list_lock);
 list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
  if (err->type == new->type && err->cmd == new->cmd) {
   list_del_rcu(&err->list);
   call_rcu(&err->rcu, sdebug_err_free);
  }
 }

 list_add_tail_rcu(&new->list, &devip->inject_err_list);
 spin_unlock(&devip->list_lock);
}

static int sdebug_err_remove(struct scsi_device *sdev, const char *buf, size_t count)
{
 struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
 struct sdebug_err_inject *err;
 int type;
 unsigned char cmd;

 if (sscanf(buf, "- %d %hhx", &type, &cmd) != 2) {
  kfree(buf);
  return -EINVAL;
 }

 spin_lock(&devip->list_lock);
 list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
  if (err->type == type && err->cmd == cmd) {
   list_del_rcu(&err->list);
   call_rcu(&err->rcu, sdebug_err_free);
   spin_unlock(&devip->list_lock);
   kfree(buf);
   return count;
  }
 }
 spin_unlock(&devip->list_lock);

 kfree(buf);
 return -EINVAL;
}

static int sdebug_error_show(struct seq_file *m, void *p)
{
 struct scsi_device *sdev = (struct scsi_device *)m->private;
 struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
 struct sdebug_err_inject *err;

 seq_puts(m, "Type\tCount\tCommand\n");

 rcu_read_lock();
 list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
  switch (err->type) {
  case ERR_TMOUT_CMD:
  case ERR_ABORT_CMD_FAILED:
  case ERR_LUN_RESET_FAILED:
   seq_printf(m, "%d\t%d\t0x%x\n", err->type, err->cnt,
    err->cmd);
  break;

  case ERR_FAIL_QUEUE_CMD:
   seq_printf(m, "%d\t%d\t0x%x\t0x%x\n", err->type,
    err->cnt, err->cmd, err->queuecmd_ret);
  break;

  case ERR_FAIL_CMD:
   seq_printf(m, "%d\t%d\t0x%x\t0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
    err->type, err->cnt, err->cmd,
    err->host_byte, err->driver_byte,
    err->status_byte, err->sense_key,
    err->asc, err->asq);
  break;
  }
 }
 rcu_read_unlock();

 return 0;
}

static int sdebug_error_open(struct inode *inode, struct file *file)
{
 return single_open(file, sdebug_error_show, inode->i_private);
}

static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf,
  size_t count, loff_t *ppos)
{
 char *buf;
 unsigned int inject_type;
 struct sdebug_err_inject *inject;
 struct scsi_device *sdev = (struct scsi_device *)file->f_inode->i_private;

 buf = kzalloc(count + 1, GFP_KERNEL);
 if (!buf)
  return -ENOMEM;

 if (copy_from_user(buf, ubuf, count)) {
  kfree(buf);
  return -EFAULT;
 }

 if (buf[0] == '-')
  return sdebug_err_remove(sdev, buf, count);

 if (sscanf(buf, "%d", &inject_type) != 1) {
  kfree(buf);
  return -EINVAL;
 }

 inject = kzalloc(sizeof(struct sdebug_err_inject), GFP_KERNEL);
 if (!inject) {
  kfree(buf);
  return -ENOMEM;
 }

 switch (inject_type) {
 case ERR_TMOUT_CMD:
 case ERR_ABORT_CMD_FAILED:
 case ERR_LUN_RESET_FAILED:
  if (sscanf(buf, "%d %d %hhx", &inject->type, &inject->cnt,
      &inject->cmd) != 3)
   goto out_error;
 break;

 case ERR_FAIL_QUEUE_CMD:
  if (sscanf(buf, "%d %d %hhx %x", &inject->type, &inject->cnt,
      &inject->cmd, &inject->queuecmd_ret) != 4)
   goto out_error;
 break;

 case ERR_FAIL_CMD:
  if (sscanf(buf, "%d %d %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
      &inject->type, &inject->cnt, &inject->cmd,
      &inject->host_byte, &inject->driver_byte,
      &inject->status_byte, &inject->sense_key,
      &inject->asc, &inject->asq) != 9)
   goto out_error;
 break;

 default:
  goto out_error;
 break;
 }

 kfree(buf);
 sdebug_err_add(sdev, inject);

 return count;

out_error:
 kfree(buf);
 kfree(inject);
 return -EINVAL;
}

static const struct file_operations sdebug_error_fops = {
 .open = sdebug_error_open,
 .read = seq_read,
 .write = sdebug_error_write,
 .release = single_release,
};

static int sdebug_target_reset_fail_show(struct seq_file *m, void *p)
{
 struct scsi_target *starget = (struct scsi_target *)m->private;
 struct sdebug_target_info *targetip =
  (struct sdebug_target_info *)starget->hostdata;

 if (targetip)
  seq_printf(m, "%c\n", targetip->reset_fail ? 'Y' : 'N');

 return 0;
}

static int sdebug_target_reset_fail_open(struct inode *inode, struct file *file)
{
 return single_open(file, sdebug_target_reset_fail_show, inode->i_private);
}

static ssize_t sdebug_target_reset_fail_write(struct file *file,
  const char __user *ubuf, size_t count, loff_t *ppos)
{
 int ret;
 struct scsi_target *starget =
  (struct scsi_target *)file->f_inode->i_private;
 struct sdebug_target_info *targetip =
  (struct sdebug_target_info *)starget->hostdata;

 if (targetip) {
  ret = kstrtobool_from_user(ubuf, count, &targetip->reset_fail);
  return ret < 0 ? ret : count;
 }
 return -ENODEV;
}

static const struct file_operations sdebug_target_reset_fail_fops = {
 .open = sdebug_target_reset_fail_open,
 .read = seq_read,
 .write = sdebug_target_reset_fail_write,
 .release = single_release,
};

static int sdebug_target_alloc(struct scsi_target *starget)
{
 struct sdebug_target_info *targetip;

 targetip = kzalloc(sizeof(struct sdebug_target_info), GFP_KERNEL);
 if (!targetip)
  return -ENOMEM;

 async_synchronize_full_domain(&sdebug_async_domain);

 targetip->debugfs_entry = debugfs_create_dir(dev_name(&starget->dev),
    sdebug_debugfs_root);

 debugfs_create_file("fail_reset", 0600, targetip->debugfs_entry, starget,
    &sdebug_target_reset_fail_fops);

 starget->hostdata = targetip;

 return 0;
}

static void sdebug_tartget_cleanup_async(void *data, async_cookie_t cookie)
{
 struct sdebug_target_info *targetip = data;

 debugfs_remove(targetip->debugfs_entry);
 kfree(targetip);
}

static void sdebug_target_destroy(struct scsi_target *starget)
{
 struct sdebug_target_info *targetip;

 targetip = (struct sdebug_target_info *)starget->hostdata;
 if (targetip) {
  starget->hostdata = NULL;
  async_schedule_domain(sdebug_tartget_cleanup_async, targetip,
    &sdebug_async_domain);
 }
}

/* Only do the extra work involved in logical block provisioning if one or
 * more of the lbpu, lbpws or lbpws10 parameters are given and we are doing
 * real reads and writes (i.e. not skipping them for speed).
 */

static inline bool scsi_debug_lbp(void)
{
 return 0 == sdebug_fake_rw &&
  (sdebug_lbpu || sdebug_lbpws || sdebug_lbpws10);
}

static inline bool scsi_debug_atomic_write(void)
{
 return sdebug_fake_rw == 0 && sdebug_atomic_wr;
}

static void *lba2fake_store(struct sdeb_store_info *sip,
       unsigned long long lba)
{
 struct sdeb_store_info *lsip = sip;

 lba = do_div(lba, sdebug_store_sectors);
 if (!sip || !sip->storep) {
  WARN_ON_ONCE(true);
  lsip = xa_load(per_store_ap, 0);  /* should never be NULL */
 }
 return lsip->storep + lba * sdebug_sector_size;
}

static struct t10_pi_tuple *dif_store(struct sdeb_store_info *sip,
          sector_t sector)
{
 sector = sector_div(sector, sdebug_store_sectors);

 return sip->dif_storep + sector;
}

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

 mutex_lock(&sdebug_host_list_mutex);
 list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
  hpnt = sdbg_host->shost;
  if ((hpnt->this_id >= 0) &&
      (sdebug_num_tgts > hpnt->this_id))
   hpnt->max_id = sdebug_num_tgts + 1;
  else
   hpnt->max_id = sdebug_num_tgts;
  /* sdebug_max_luns; */
  hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1;
 }
 mutex_unlock(&sdebug_host_list_mutex);
}

enum sdeb_cmd_data {SDEB_IN_DATA = 0, SDEB_IN_CDB = 1};

/* Set in_bit to -1 to indicate no bit position of invalid field */
static void mk_sense_invalid_fld(struct scsi_cmnd *scp,
     enum sdeb_cmd_data c_d,
     int in_byte, int in_bit)
{
 unsigned char *sbuff;
 u8 sks[4];
 int sl, asc;

 sbuff = scp->sense_buffer;
 if (!sbuff) {
  sdev_printk(KERN_ERR, scp->device,
       "%s: sense_buffer is NULL\n", __func__);
  return;
 }
 asc = c_d ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST;
 memset(sbuff, 0, SCSI_SENSE_BUFFERSIZE);
 scsi_build_sense(scp, sdebug_dsense, ILLEGAL_REQUEST, asc, 0);
 memset(sks, 0, sizeof(sks));
 sks[0] = 0x80;
 if (c_d)
  sks[0] |= 0x40;
 if (in_bit >= 0) {
  sks[0] |= 0x8;
  sks[0] |= 0x7 & in_bit;
 }
 put_unaligned_be16(in_byte, sks + 1);
 if (sdebug_dsense) {
  sl = sbuff[7] + 8;
  sbuff[7] = sl;
  sbuff[sl] = 0x2;
  sbuff[sl + 1] = 0x6;
  memcpy(sbuff + sl + 4, sks, 3);
 } else
  memcpy(sbuff + 15, sks, 3);
 if (sdebug_verbose)
  sdev_printk(KERN_INFO, scp->device, "%s: [sense_key,asc,ascq"
       "]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n",
       my_name, asc, c_d ? 'C' : 'D', in_byte, in_bit);
}

static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq)
{
 if (!scp->sense_buffer) {
  sdev_printk(KERN_ERR, scp->device,
       "%s: sense_buffer is NULL\n", __func__);
  return;
 }
 memset(scp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);

 scsi_build_sense(scp, sdebug_dsense, key, asc, asq);

 if (sdebug_verbose)
  sdev_printk(KERN_INFO, scp->device,
       "%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n",
       my_name, key, asc, asq);
}

/* Sense data that has information fields for tapes */
static void mk_sense_info_tape(struct scsi_cmnd *scp, int key, int asc, int asq,
   unsigned int information, unsigned char tape_flags)
{
 if (!scp->sense_buffer) {
  sdev_printk(KERN_ERR, scp->device,
       "%s: sense_buffer is NULL\n", __func__);
  return;
 }
 memset(scp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);

 scsi_build_sense(scp, /* sdebug_dsense */ 0, key, asc, asq);
 /* only fixed format so far */

 scp->sense_buffer[0] |= 0x80; /* valid */
 scp->sense_buffer[2] |= tape_flags;
 put_unaligned_be32(information, &scp->sense_buffer[3]);

 if (sdebug_verbose)
  sdev_printk(KERN_INFO, scp->device,
       "%s: [sense_key,asc,ascq]: [0x%x,0x%x,0x%x]\n",
       my_name, key, asc, asq);
}

static void mk_sense_invalid_opcode(struct scsi_cmnd *scp)
{
 mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
}

static int scsi_debug_ioctl(struct scsi_device *dev, unsigned int cmd,
       void __user *arg)
{
 if (sdebug_verbose) {
  if (0x1261 == cmd)
   sdev_printk(KERN_INFO, dev,
        "%s: BLKFLSBUF [0x1261]\n", __func__);
  else if (0x5331 == cmd)
   sdev_printk(KERN_INFO, dev,
        "%s: CDROM_GET_CAPABILITY [0x5331]\n",
        __func__);
  else
   sdev_printk(KERN_INFO, dev, "%s: cmd=0x%x\n",
        __func__, cmd);
 }
 return -EINVAL;
 /* return -ENOTTY; // correct return but upsets fdisk */
}

static void config_cdb_len(struct scsi_device *sdev)
{
 switch (sdebug_cdb_len) {
 case 6: /* suggest 6 byte READ, WRITE and MODE SENSE/SELECT */
  sdev->use_10_for_rw = false;
  sdev->use_16_for_rw = false;
  sdev->use_10_for_ms = false;
  break;
 case 10: /* suggest 10 byte RWs and 6 byte MODE SENSE/SELECT */
  sdev->use_10_for_rw = true;
  sdev->use_16_for_rw = false;
  sdev->use_10_for_ms = false;
  break;
 case 12: /* suggest 10 byte RWs and 10 byte MODE SENSE/SELECT */
  sdev->use_10_for_rw = true;
  sdev->use_16_for_rw = false;
  sdev->use_10_for_ms = true;
  break;
 case 16:
  sdev->use_10_for_rw = false;
  sdev->use_16_for_rw = true;
  sdev->use_10_for_ms = true;
  break;
 case 32: /* No knobs to suggest this so same as 16 for now */
  sdev->use_10_for_rw = false;
  sdev->use_16_for_rw = true;
  sdev->use_10_for_ms = true;
  break;
 default:
  pr_warn("unexpected cdb_len=%d, force to 10\n",
   sdebug_cdb_len);
  sdev->use_10_for_rw = true;
  sdev->use_16_for_rw = false;
  sdev->use_10_for_ms = false;
  sdebug_cdb_len = 10;
  break;
 }
}

static void all_config_cdb_len(void)
{
 struct sdebug_host_info *sdbg_host;
 struct Scsi_Host *shost;
 struct scsi_device *sdev;

 mutex_lock(&sdebug_host_list_mutex);
 list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
  shost = sdbg_host->shost;
  shost_for_each_device(sdev, shost) {
   config_cdb_len(sdev);
  }
 }
 mutex_unlock(&sdebug_host_list_mutex);
}

static void clear_luns_changed_on_target(struct sdebug_dev_info *devip)
{
 struct sdebug_host_info *sdhp = devip->sdbg_host;
 struct sdebug_dev_info *dp;

 list_for_each_entry(dp, &sdhp->dev_info_list, dev_list) {
  if ((devip->sdbg_host == dp->sdbg_host) &&
      (devip->target == dp->target)) {
   clear_bit(SDEBUG_UA_LUNS_CHANGED, dp->uas_bm);
  }
 }
}

static int make_ua(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
 int k;

 k = find_first_bit(devip->uas_bm, SDEBUG_NUM_UAS);
 if (k != SDEBUG_NUM_UAS) {
  const char *cp = NULL;

  switch (k) {
  case SDEBUG_UA_POR:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_RESET_ASC,
     POWER_ON_RESET_ASCQ);
   if (sdebug_verbose)
    cp = "power on reset";
   break;
  case SDEBUG_UA_POOCCUR:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_RESET_ASC,
     POWER_ON_OCCURRED_ASCQ);
   if (sdebug_verbose)
    cp = "power on occurred";
   break;
  case SDEBUG_UA_BUS_RESET:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_RESET_ASC,
     BUS_RESET_ASCQ);
   if (sdebug_verbose)
    cp = "bus reset";
   break;
  case SDEBUG_UA_MODE_CHANGED:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_CHANGED_ASC,
     MODE_CHANGED_ASCQ);
   if (sdebug_verbose)
    cp = "mode parameters changed";
   break;
  case SDEBUG_UA_CAPACITY_CHANGED:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_CHANGED_ASC,
     CAPACITY_CHANGED_ASCQ);
   if (sdebug_verbose)
    cp = "capacity data changed";
   break;
  case SDEBUG_UA_MICROCODE_CHANGED:
   mk_sense_buffer(scp, UNIT_ATTENTION,
     TARGET_CHANGED_ASC,
     MICROCODE_CHANGED_ASCQ);
   if (sdebug_verbose)
    cp = "microcode has been changed";
   break;
  case SDEBUG_UA_MICROCODE_CHANGED_WO_RESET:
   mk_sense_buffer(scp, UNIT_ATTENTION,
     TARGET_CHANGED_ASC,
     MICROCODE_CHANGED_WO_RESET_ASCQ);
   if (sdebug_verbose)
    cp = "microcode has been changed without reset";
   break;
  case SDEBUG_UA_LUNS_CHANGED:
   /*
 * SPC-3 behavior is to report a UNIT ATTENTION with
 * ASC/ASCQ REPORTED LUNS DATA HAS CHANGED on every LUN
 * on the target, until a REPORT LUNS command is
 * received.  SPC-4 behavior is to report it only once.
 * NOTE:  sdebug_scsi_level does not use the same
 * values as struct scsi_device->scsi_level.
 */

   if (sdebug_scsi_level >= 6) /* SPC-4 and above */
    clear_luns_changed_on_target(devip);
   mk_sense_buffer(scp, UNIT_ATTENTION,
     TARGET_CHANGED_ASC,
     LUNS_CHANGED_ASCQ);
   if (sdebug_verbose)
    cp = "reported luns data has changed";
   break;
  case SDEBUG_UA_NOT_READY_TO_READY:
   mk_sense_buffer(scp, UNIT_ATTENTION, UA_READY_ASC,
     0);
   if (sdebug_verbose)
    cp = "not ready to ready transition/media change";
   break;
  default:
   pr_warn("unexpected unit attention code=%d\n", k);
   if (sdebug_verbose)
    cp = "unknown";
   break;
  }
  clear_bit(k, devip->uas_bm);
  if (sdebug_verbose)
   sdev_printk(KERN_INFO, scp->device,
       "%s reports: Unit attention: %s\n",
       my_name, cp);
  return check_condition_result;
 }
 return 0;
}

/* Build SCSI "data-in" buffer. Returns 0 if ok else (DID_ERROR << 16). */
static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
    int arr_len)
{
 int act_len;
 struct scsi_data_buffer *sdb = &scp->sdb;

 if (!sdb->length)
  return 0;
 if (scp->sc_data_direction != DMA_FROM_DEVICE)
  return DID_ERROR << 16;

 act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
          arr, arr_len);
 scsi_set_resid(scp, scsi_bufflen(scp) - act_len);

 return 0;
}

/* Partial build of SCSI "data-in" buffer. Returns 0 if ok else
 * (DID_ERROR << 16). Can write to offset in data-in buffer. If multiple
 * calls, not required to write in ascending offset order. Assumes resid
 * set to scsi_bufflen() prior to any calls.
 */

static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr,
      int arr_len, unsigned int off_dst)
{
 unsigned int act_len, n;
 struct scsi_data_buffer *sdb = &scp->sdb;
 off_t skip = off_dst;

 if (sdb->length <= off_dst)
  return 0;
 if (scp->sc_data_direction != DMA_FROM_DEVICE)
  return DID_ERROR << 16;

 act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents,
           arr, arr_len, skip);
 pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n",
   __func__, off_dst, scsi_bufflen(scp), act_len,
   scsi_get_resid(scp));
 n = scsi_bufflen(scp) - (off_dst + act_len);
 scsi_set_resid(scp, min_t(u32, scsi_get_resid(scp), n));
 return 0;
}

/* Fetches from SCSI "data-out" buffer. 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 arr_len)
{
 if (!scsi_bufflen(scp))
  return 0;
 if (scp->sc_data_direction != DMA_TO_DEVICE)
  return -1;

 return scsi_sg_copy_to_buffer(scp, arr, arr_len);
}


static char sdebug_inq_vendor_id[9] = "Linux ";
static char sdebug_inq_product_id[17] = "scsi_debug ";
static char sdebug_inq_product_rev[5] = SDEBUG_VERSION;
/* Use some locally assigned NAAs for SAS addresses. */
static const u64 naa3_comp_a = 0x3222222000000000ULL;
static const u64 naa3_comp_b = 0x3333333000000000ULL;
static const u64 naa3_comp_c = 0x3111111000000000ULL;

/* Device identification VPD page. Returns number of bytes placed in arr */
static int inquiry_vpd_83(unsigned char *arr, int port_group_id,
     int target_dev_id, int dev_id_num,
     const char *dev_id_str, int dev_id_str_len,
     const uuid_t *lu_name)
{
 int num, port_a;
 char b[32];

 port_a = target_dev_id + 1;
 /* T10 vendor identifier field format (faked) */
 arr[0] = 0x2; /* ASCII */
 arr[1] = 0x1;
 arr[2] = 0x0;
 memcpy(&arr[4], sdebug_inq_vendor_id, 8);
 memcpy(&arr[12], sdebug_inq_product_id, 16);
 memcpy(&arr[28], dev_id_str, dev_id_str_len);
 num = 8 + 16 + dev_id_str_len;
 arr[3] = num;
 num += 4;
 if (dev_id_num >= 0) {
  if (sdebug_uuid_ctl) {
   /* Locally assigned UUID */
   arr[num++] = 0x1;  /* binary (not necessarily sas) */
   arr[num++] = 0xa;  /* PIV=0, lu, naa */
   arr[num++] = 0x0;
   arr[num++] = 0x12;
   arr[num++] = 0x10; /* uuid type=1, locally assigned */
   arr[num++] = 0x0;
   memcpy(arr + num, lu_name, 16);
   num += 16;
  } else {
   /* NAA-3, Logical unit identifier (binary) */
   arr[num++] = 0x1;  /* binary (not necessarily sas) */
   arr[num++] = 0x3;  /* PIV=0, lu, naa */
   arr[num++] = 0x0;
   arr[num++] = 0x8;
   put_unaligned_be64(naa3_comp_b + dev_id_num, arr + num);
   num += 8;
  }
  /* Target relative port number */
  arr[num++] = 0x61; /* proto=sas, binary */
  arr[num++] = 0x94; /* PIV=1, target port, rel port */
  arr[num++] = 0x0; /* reserved */
  arr[num++] = 0x4; /* length */
  arr[num++] = 0x0; /* reserved */
  arr[num++] = 0x0; /* reserved */
  arr[num++] = 0x0;
  arr[num++] = 0x1; /* relative port A */
 }
 /* NAA-3, Target port identifier */
 arr[num++] = 0x61; /* proto=sas, binary */
 arr[num++] = 0x93; /* piv=1, target port, naa */
 arr[num++] = 0x0;
 arr[num++] = 0x8;
 put_unaligned_be64(naa3_comp_a + port_a, arr + num);
 num += 8;
 /* NAA-3, Target port group identifier */
 arr[num++] = 0x61; /* proto=sas, binary */
 arr[num++] = 0x95; /* piv=1, target port group id */
 arr[num++] = 0x0;
 arr[num++] = 0x4;
 arr[num++] = 0;
 arr[num++] = 0;
 put_unaligned_be16(port_group_id, arr + num);
 num += 2;
 /* NAA-3, Target device identifier */
 arr[num++] = 0x61; /* proto=sas, binary */
 arr[num++] = 0xa3; /* piv=1, target device, naa */
 arr[num++] = 0x0;
 arr[num++] = 0x8;
 put_unaligned_be64(naa3_comp_a + target_dev_id, arr + num);
 num += 8;
 /* SCSI name string: Target device identifier */
 arr[num++] = 0x63; /* proto=sas, UTF-8 */
 arr[num++] = 0xa8; /* piv=1, target device, SCSI name string */
 arr[num++] = 0x0;
 arr[num++] = 24;
 memcpy(arr + num, "naa.32222220", 12);
 num += 12;
 snprintf(b, sizeof(b), "%08X", target_dev_id);
 memcpy(arr + num, b, 8);
 num += 8;
 memset(arr + num, 0, 4);
 num += 4;
 return num;
}

static unsigned char vpd84_data[] = {
/* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
    0x22,0x22,0x22,0x0,0xbb,0x1,
    0x22,0x22,0x22,0x0,0xbb,0x2,
};

/*  Software interface identification VPD page */
static int inquiry_vpd_84(unsigned char *arr)
{
 memcpy(arr, vpd84_data, sizeof(vpd84_data));
 return sizeof(vpd84_data);
}

/* Management network addresses VPD page */
static int inquiry_vpd_85(unsigned char *arr)
{
 int num = 0;
 const char *na1 = "https://www.kernel.org/config";
 const char *na2 = "http://www.kernel.org/log";
 int plen, olen;

 arr[num++] = 0x1; /* lu, storage config */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0;
 olen = strlen(na1);
 plen = olen + 1;
 if (plen % 4)
  plen = ((plen / 4) + 1) * 4;
 arr[num++] = plen; /* length, null termianted, padded */
 memcpy(arr + num, na1, olen);
 memset(arr + num + olen, 0, plen - olen);
 num += plen;

 arr[num++] = 0x4; /* lu, logging */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0;
 olen = strlen(na2);
 plen = olen + 1;
 if (plen % 4)
  plen = ((plen / 4) + 1) * 4;
 arr[num++] = plen; /* length, null terminated, padded */
 memcpy(arr + num, na2, olen);
 memset(arr + num + olen, 0, plen - olen);
 num += plen;

 return num;
}

/* SCSI ports VPD page */
static int inquiry_vpd_88(unsigned char *arr, int target_dev_id)
{
 int num = 0;
 int port_a, port_b;

 port_a = target_dev_id + 1;
 port_b = port_a + 1;
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0;
 arr[num++] = 0x1; /* relative port 1 (primary) */
 memset(arr + num, 0, 6);
 num += 6;
 arr[num++] = 0x0;
 arr[num++] = 12; /* length tp descriptor */
 /* naa-5 target port identifier (A) */
 arr[num++] = 0x61; /* proto=sas, binary */
 arr[num++] = 0x93; /* PIV=1, target port, NAA */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x8; /* length */
 put_unaligned_be64(naa3_comp_a + port_a, arr + num);
 num += 8;
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x0;
 arr[num++] = 0x2; /* relative port 2 (secondary) */
 memset(arr + num, 0, 6);
 num += 6;
 arr[num++] = 0x0;
 arr[num++] = 12; /* length tp descriptor */
 /* naa-5 target port identifier (B) */
 arr[num++] = 0x61; /* proto=sas, binary */
 arr[num++] = 0x93; /* PIV=1, target port, NAA */
 arr[num++] = 0x0; /* reserved */
 arr[num++] = 0x8; /* length */
 put_unaligned_be64(naa3_comp_a + port_b, arr + num);
 num += 8;

 return num;
}


static unsigned char vpd89_data[] = {
/* from 4th byte */ 0,0,0,0,
'l','i','n','u','x',' ',' ',' ',
'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
'1','2','3','4',
0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
0xec,0,0,0,
0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
0x53,0x41,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x10,0x80,
0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=85 H=100 G=92

¤ Dauer der Verarbeitung: 0.47 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.