[SCSI] scsi_debug: support REPORT TARGET PORT GROUPS
authorHannes Reinecke <hare@suse.de>
Fri, 20 Oct 2006 07:58:47 +0000 (09:58 +0200)
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>
Wed, 25 Oct 2006 22:14:13 +0000 (15:14 -0700)
This patch adds support for REPORT TARGET PORT GROUPS. This is used
eg for the multipathing priority callout to determine the path
priority.
With this patch multipath-tools can use the existing mpath_prio_alua
callout to exercise the path priority grouping.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Douglas Gilbert <dougg@torque.net>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/scsi_debug.c
include/scsi/scsi.h

index 9c0f35820e3e3a3c5ca797ee4c57752b22f3ba79..30ee3d72c02151f0b56ca9b69b9c7d10aff7bbff 100644 (file)
@@ -52,7 +52,7 @@
 #include "scsi_debug.h"
 
 #define SCSI_DEBUG_VERSION "1.80"
-static const char * scsi_debug_version_date = "20060914";
+static const char * scsi_debug_version_date = "20061018";
 
 /* Additional Sense Code (ASC) used */
 #define NO_ADDITIONAL_SENSE 0x0
@@ -254,6 +254,8 @@ static int resp_requests(struct scsi_cmnd * SCpnt,
                         struct sdebug_dev_info * devip);
 static int resp_start_stop(struct scsi_cmnd * scp,
                           struct sdebug_dev_info * devip);
+static int resp_report_tgtpgs(struct scsi_cmnd * scp,
+                             struct sdebug_dev_info * devip);
 static int resp_readcap(struct scsi_cmnd * SCpnt,
                        struct sdebug_dev_info * devip);
 static int resp_readcap16(struct scsi_cmnd * SCpnt,
@@ -287,9 +289,9 @@ static void __init sdebug_build_parts(unsigned char * ramp);
 static void __init init_all_queued(void);
 static void stop_all_queued(void);
 static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
-static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
-                          int dev_id_num, const char * dev_id_str,
-                          int dev_id_str_len);
+static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
+                          int target_dev_id, int dev_id_num,
+                          const char * dev_id_str, int dev_id_str_len);
 static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
 static int do_create_driverfs_files(void);
 static void do_remove_driverfs_files(void);
@@ -422,6 +424,15 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
                }
                errsts = resp_readcap16(SCpnt, devip);
                break;
+       case MAINTENANCE_IN:
+               if (MI_REPORT_TARGET_PGS != cmd[1]) {
+                       mk_sense_buffer(devip, ILLEGAL_REQUEST,
+                                       INVALID_OPCODE, 0);
+                       errsts = check_condition_result;
+                       break;
+               }
+               errsts = resp_report_tgtpgs(SCpnt, devip);
+               break;
        case READ_16:
        case READ_12:
        case READ_10:
@@ -665,8 +676,9 @@ static const char * inq_vendor_id = "Linux   ";
 static const char * inq_product_id = "scsi_debug      ";
 static const char * inq_product_rev = "0004";
 
-static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
-                          int dev_id_num, const char * dev_id_str,
+static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
+                          int target_dev_id, int dev_id_num,
+                          const char * dev_id_str,
                           int dev_id_str_len)
 {
        int num, port_a;
@@ -720,6 +732,15 @@ static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
        arr[num++] = (port_a >> 16) & 0xff;
        arr[num++] = (port_a >> 8) & 0xff;
        arr[num++] = port_a & 0xff;
+       /* NAA-5, 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;
+       arr[num++] = (port_group_id >> 8) & 0xff;
+       arr[num++] = port_group_id & 0xff;
        /* NAA-5, Target device identifier */
        arr[num++] = 0x61;      /* proto=sas, binary */
        arr[num++] = 0xa3;      /* piv=1, target device, naa */
@@ -928,12 +949,12 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
                        struct sdebug_dev_info * devip)
 {
        unsigned char pq_pdt;
-       unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
+       unsigned char * arr;
        unsigned char *cmd = (unsigned char *)scp->cmnd;
-       int alloc_len, n;
+       int alloc_len, n, ret;
 
        alloc_len = (cmd[3] << 8) + cmd[4];
-       memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
+       arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_KERNEL);
        if (devip->wlun)
                pq_pdt = 0x1e;  /* present, wlun */
        else if (scsi_debug_no_lun_0 && (0 == devip->lun))
@@ -944,12 +965,15 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
        if (0x2 & cmd[1]) {  /* CMDDT bit set */
                mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
                                0);
+               kfree(arr);
                return check_condition_result;
        } else if (0x1 & cmd[1]) {  /* EVPD bit set */
-               int lu_id_num, target_dev_id, len;
+               int lu_id_num, port_group_id, target_dev_id, len;
                char lu_id_str[6];
                int host_no = devip->sdbg_host->shost->host_no;
                
+               port_group_id = (((host_no + 1) & 0x7f) << 8) +
+                   (devip->channel & 0x7f);
                if (0 == scsi_debug_vpd_use_hostno)
                        host_no = 0;
                lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
@@ -977,8 +1001,9 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
                        memcpy(&arr[4], lu_id_str, len);
                } else if (0x83 == cmd[2]) { /* device identification */
                        arr[1] = cmd[2];        /*sanity */
-                       arr[3] = inquiry_evpd_83(&arr[4], target_dev_id,
-                                                lu_id_num, lu_id_str, len);
+                       arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
+                                                target_dev_id, lu_id_num,
+                                                lu_id_str, len);
                } else if (0x84 == cmd[2]) { /* Software interface ident. */
                        arr[1] = cmd[2];        /*sanity */
                        arr[3] = inquiry_evpd_84(&arr[4]);
@@ -1012,17 +1037,22 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
                        /* Illegal request, invalid field in cdb */
                        mk_sense_buffer(devip, ILLEGAL_REQUEST,
                                        INVALID_FIELD_IN_CDB, 0);
+                       kfree(arr);
                        return check_condition_result;
                }
                len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
-               return fill_from_dev_buffer(scp, arr,
+               ret = fill_from_dev_buffer(scp, arr,
                            min(len, SDEBUG_MAX_INQ_ARR_SZ));
+               kfree(arr);
+               return ret;
        }
        /* drops through here for a standard inquiry */
        arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;     /* Removable disk */
        arr[2] = scsi_debug_scsi_level;
        arr[3] = 2;    /* response_data_format==2 */
        arr[4] = SDEBUG_LONG_INQ_SZ - 5;
+       if (0 == scsi_debug_vpd_use_hostno)
+               arr[5] = 0x10; /* claim: implicit TGPS */
        arr[6] = 0x10; /* claim: MultiP */
        /* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
        arr[7] = 0xa; /* claim: LINKED + CMDQUE */
@@ -1039,8 +1069,10 @@ static int resp_inquiry(struct scsi_cmnd * scp, int target,
                arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
        }
        arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
-       return fill_from_dev_buffer(scp, arr,
+       ret = fill_from_dev_buffer(scp, arr,
                            min(alloc_len, SDEBUG_LONG_INQ_SZ));
+       kfree(arr);
+       return ret;
 }
 
 static int resp_requests(struct scsi_cmnd * scp,
@@ -1171,6 +1203,87 @@ static int resp_readcap16(struct scsi_cmnd * scp,
                                    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
 }
 
+#define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
+
+static int resp_report_tgtpgs(struct scsi_cmnd * scp,
+                             struct sdebug_dev_info * devip)
+{
+       unsigned char *cmd = (unsigned char *)scp->cmnd;
+       unsigned char * arr;
+       int host_no = devip->sdbg_host->shost->host_no;
+       int n, ret, alen, rlen;
+       int port_group_a, port_group_b, port_a, port_b;
+
+       alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
+               + cmd[9]);
+
+       arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_KERNEL);
+       /*
+        * EVPD page 0x88 states we have two ports, one
+        * real and a fake port with no device connected.
+        * So we create two port groups with one port each
+        * and set the group with port B to unavailable.
+        */
+       port_a = 0x1; /* relative port A */
+       port_b = 0x2; /* relative port B */
+       port_group_a = (((host_no + 1) & 0x7f) << 8) +
+           (devip->channel & 0x7f);
+       port_group_b = (((host_no + 1) & 0x7f) << 8) +
+           (devip->channel & 0x7f) + 0x80;
+
+       /*
+        * The asymmetric access state is cycled according to the host_id.
+        */
+       n = 4;
+       if (0 == scsi_debug_vpd_use_hostno) {
+           arr[n++] = host_no % 3; /* Asymm access state */
+           arr[n++] = 0x0F; /* claim: all states are supported */
+       } else {
+           arr[n++] = 0x0; /* Active/Optimized path */
+           arr[n++] = 0x01; /* claim: only support active/optimized paths */
+       }
+       arr[n++] = (port_group_a >> 8) & 0xff;
+       arr[n++] = port_group_a & 0xff;
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = 0;    /* Status code */
+       arr[n++] = 0;    /* Vendor unique */
+       arr[n++] = 0x1;  /* One port per group */
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = (port_a >> 8) & 0xff;
+       arr[n++] = port_a & 0xff;
+       arr[n++] = 3;    /* Port unavailable */
+       arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
+       arr[n++] = (port_group_b >> 8) & 0xff;
+       arr[n++] = port_group_b & 0xff;
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = 0;    /* Status code */
+       arr[n++] = 0;    /* Vendor unique */
+       arr[n++] = 0x1;  /* One port per group */
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = 0;    /* Reserved */
+       arr[n++] = (port_b >> 8) & 0xff;
+       arr[n++] = port_b & 0xff;
+
+       rlen = n - 4;
+       arr[0] = (rlen >> 24) & 0xff;
+       arr[1] = (rlen >> 16) & 0xff;
+       arr[2] = (rlen >> 8) & 0xff;
+       arr[3] = rlen & 0xff;
+
+       /*
+        * Return the smallest value of either
+        * - The allocated length
+        * - The constructed command length
+        * - The maximum array size
+        */
+       rlen = min(alen,n);
+       ret = fill_from_dev_buffer(scp, arr,
+                                  min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
+       kfree(arr);
+       return ret;
+}
+
 /* <<Following mode page info copied from ST318451LW>> */
 
 static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
index 84a6d5fe0920c019bd400c8681f9a417f5a14243..8a3f0bd0d45adef1fde36ae143873c62a9e9bf5f 100644 (file)
@@ -97,6 +97,7 @@ extern const unsigned char scsi_command_size[8];
 #define PERSISTENT_RESERVE_IN 0x5e
 #define PERSISTENT_RESERVE_OUT 0x5f
 #define REPORT_LUNS           0xa0
+#define MAINTENANCE_IN        0xa3
 #define MOVE_MEDIUM           0xa5
 #define EXCHANGE_MEDIUM       0xa6
 #define READ_12               0xa8
@@ -114,6 +115,8 @@ extern const unsigned char scsi_command_size[8];
 #define SERVICE_ACTION_IN     0x9e
 /* values for service action in */
 #define        SAI_READ_CAPACITY_16  0x10
+/* values for maintenance in */
+#define MI_REPORT_TARGET_PGS  0x0a
 
 /* Values for T10/04-262r7 */
 #define        ATA_16                0x85      /* 16-byte pass-thru */