Merge git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6
[linux-2.6-block.git] / drivers / scsi / scsi_debug.c
index 4aca1b0378c2458212945a3877b303062ea529ca..1f8e2dc9c616a0b7e02559537c05b109123b5b80 100644 (file)
@@ -80,6 +80,8 @@ static const char *scsi_debug_version_date = "20141022";
 #define INVALID_FIELD_IN_PARAM_LIST 0x26
 #define UA_RESET_ASC 0x29
 #define UA_CHANGED_ASC 0x2a
+#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
@@ -91,6 +93,8 @@ static const char *scsi_debug_version_date = "20141022";
 #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
 
 /* Additional Sense Code Qualifier (ASCQ) */
 #define ACK_NAK_TO 0x3
@@ -180,7 +184,10 @@ static const char *scsi_debug_version_date = "20141022";
 #define SDEBUG_UA_BUS_RESET 1
 #define SDEBUG_UA_MODE_CHANGED 2
 #define SDEBUG_UA_CAPACITY_CHANGED 3
-#define SDEBUG_NUM_UAS 4
+#define SDEBUG_UA_LUNS_CHANGED 4
+#define SDEBUG_UA_MICROCODE_CHANGED 5  /* simulate firmware change */
+#define SDEBUG_UA_MICROCODE_CHANGED_WO_RESET 6
+#define SDEBUG_NUM_UAS 7
 
 /* for check_readiness() */
 #define UAS_ONLY 1     /* check for UAs only */
@@ -326,6 +333,7 @@ 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_xdwriteread_10(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 *);
 
 struct opcode_info_t {
        u8 num_attached;        /* 0 if this is it (i.e. a leaf); use 0xff
@@ -480,8 +488,9 @@ static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
        {0, 0x53, 0, F_D_IN | F_D_OUT | FF_DIRECT_IO, resp_xdwriteread_10,
            NULL, {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7,
                   0, 0, 0, 0, 0, 0} },
-       {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* WRITE_BUFFER */
-           {0,  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+       {0, 0x3b, 0, 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 */
        {1, 0x41, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_10,
            write_same_iarr, {10,  0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff,
                              0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
@@ -782,6 +791,22 @@ static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
        /* return -ENOTTY; // correct return but upsets fdisk */
 }
 
+static void clear_luns_changed_on_target(struct sdebug_dev_info *devip)
+{
+       struct sdebug_host_info *sdhp;
+       struct sdebug_dev_info *dp;
+
+       spin_lock(&sdebug_host_list_lock);
+       list_for_each_entry(sdhp, &sdebug_host_list, host_list) {
+               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);
+               }
+       }
+       spin_unlock(&sdebug_host_list_lock);
+}
+
 static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only,
                           struct sdebug_dev_info * devip)
 {
@@ -817,6 +842,36 @@ static int check_readiness(struct scsi_cmnd *SCpnt, int uas_only,
                        if (debug)
                                cp = "capacity data changed";
                        break;
+               case SDEBUG_UA_MICROCODE_CHANGED:
+                       mk_sense_buffer(SCpnt, UNIT_ATTENTION,
+                                TARGET_CHANGED_ASC, MICROCODE_CHANGED_ASCQ);
+                       if (debug)
+                               cp = "microcode has been changed";
+                       break;
+               case SDEBUG_UA_MICROCODE_CHANGED_WO_RESET:
+                       mk_sense_buffer(SCpnt, UNIT_ATTENTION,
+                                       TARGET_CHANGED_ASC,
+                                       MICROCODE_CHANGED_WO_RESET_ASCQ);
+                       if (debug)
+                               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:  scsi_debug_scsi_level does not use the same
+                        * values as struct scsi_device->scsi_level.
+                        */
+                       if (scsi_debug_scsi_level >= 6) /* SPC-4 and above */
+                               clear_luns_changed_on_target(devip);
+                       mk_sense_buffer(SCpnt, UNIT_ATTENTION,
+                                       TARGET_CHANGED_ASC,
+                                       LUNS_CHANGED_ASCQ);
+                       if (debug)
+                               cp = "reported luns data has changed";
+                       break;
                default:
                        pr_warn("%s: unexpected unit attention code=%d\n",
                                __func__, k);
@@ -3033,6 +3088,55 @@ resp_write_same_16(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
        return resp_write_same(scp, lba, num, ei_lba, unmap, ndob);
 }
 
+/* Note the mode field is in the same position as the (lower) service action
+ * field. For the Report supported operation codes command, SPC-4 suggests
+ * each mode of this command should be reported separately; for future. */
+static int
+resp_write_buffer(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+       u8 *cmd = scp->cmnd;
+       struct scsi_device *sdp = scp->device;
+       struct sdebug_dev_info *dp;
+       u8 mode;
+
+       mode = cmd[1] & 0x1f;
+       switch (mode) {
+       case 0x4:       /* download microcode (MC) and activate (ACT) */
+               /* set UAs on this device only */
+               set_bit(SDEBUG_UA_BUS_RESET, devip->uas_bm);
+               set_bit(SDEBUG_UA_MICROCODE_CHANGED, devip->uas_bm);
+               break;
+       case 0x5:       /* download MC, save and ACT */
+               set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET, devip->uas_bm);
+               break;
+       case 0x6:       /* download MC with offsets and ACT */
+               /* set UAs on most devices (LUs) in this target */
+               list_for_each_entry(dp,
+                                   &devip->sdbg_host->dev_info_list,
+                                   dev_list)
+                       if (dp->target == sdp->id) {
+                               set_bit(SDEBUG_UA_BUS_RESET, dp->uas_bm);
+                               if (devip != dp)
+                                       set_bit(SDEBUG_UA_MICROCODE_CHANGED,
+                                               dp->uas_bm);
+                       }
+               break;
+       case 0x7:       /* download MC with offsets, save, and ACT */
+               /* set UA on all devices (LUs) in this target */
+               list_for_each_entry(dp,
+                                   &devip->sdbg_host->dev_info_list,
+                                   dev_list)
+                       if (dp->target == sdp->id)
+                               set_bit(SDEBUG_UA_MICROCODE_CHANGED_WO_RESET,
+                                       dp->uas_bm);
+               break;
+       default:
+               /* do nothing for this command for other mode values */
+               break;
+       }
+       return 0;
+}
+
 static int
 resp_comp_write(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 {
@@ -3229,6 +3333,7 @@ static int resp_report_luns(struct scsi_cmnd * scp,
        unsigned char arr[SDEBUG_RLUN_ARR_SZ];
        unsigned char * max_addr;
 
+       clear_luns_changed_on_target(devip);
        alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
        shortish = (alloc_len < 4);
        if (shortish || (select_report > 2)) {
@@ -4369,10 +4474,27 @@ static ssize_t max_luns_store(struct device_driver *ddp, const char *buf,
                              size_t count)
 {
         int n;
+       bool changed;
 
        if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+               changed = (scsi_debug_max_luns != n);
                scsi_debug_max_luns = n;
                sdebug_max_tgts_luns();
+               if (changed && (scsi_debug_scsi_level >= 5)) {  /* >= SPC-3 */
+                       struct sdebug_host_info *sdhp;
+                       struct sdebug_dev_info *dp;
+
+                       spin_lock(&sdebug_host_list_lock);
+                       list_for_each_entry(sdhp, &sdebug_host_list,
+                                           host_list) {
+                               list_for_each_entry(dp, &sdhp->dev_info_list,
+                                                   dev_list) {
+                                       set_bit(SDEBUG_UA_LUNS_CHANGED,
+                                               dp->uas_bm);
+                               }
+                       }
+                       spin_unlock(&sdebug_host_list_lock);
+               }
                return count;
        }
        return -EINVAL;
@@ -4536,10 +4658,10 @@ static ssize_t map_show(struct device_driver *ddp, char *buf)
                return scnprintf(buf, PAGE_SIZE, "0-%u\n",
                                 sdebug_store_sectors);
 
-       count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
-
+       count = scnprintf(buf, PAGE_SIZE - 1, "%*pbl",
+                         (int)map_size, map_storep);
        buf[count++] = '\n';
-       buf[count++] = 0;
+       buf[count] = '\0';
 
        return count;
 }