[SCSI] scsi_debug: support REPORT TARGET PORT GROUPS
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>
This commit is contained in:
parent
cd00b7f5d8
commit
5a09e39810
|
@ -52,7 +52,7 @@
|
||||||
#include "scsi_debug.h"
|
#include "scsi_debug.h"
|
||||||
|
|
||||||
#define SCSI_DEBUG_VERSION "1.80"
|
#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 */
|
/* Additional Sense Code (ASC) used */
|
||||||
#define NO_ADDITIONAL_SENSE 0x0
|
#define NO_ADDITIONAL_SENSE 0x0
|
||||||
|
@ -254,6 +254,8 @@ static int resp_requests(struct scsi_cmnd * SCpnt,
|
||||||
struct sdebug_dev_info * devip);
|
struct sdebug_dev_info * devip);
|
||||||
static int resp_start_stop(struct scsi_cmnd * scp,
|
static int resp_start_stop(struct scsi_cmnd * scp,
|
||||||
struct sdebug_dev_info * devip);
|
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,
|
static int resp_readcap(struct scsi_cmnd * SCpnt,
|
||||||
struct sdebug_dev_info * devip);
|
struct sdebug_dev_info * devip);
|
||||||
static int resp_readcap16(struct scsi_cmnd * SCpnt,
|
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 __init init_all_queued(void);
|
||||||
static void stop_all_queued(void);
|
static void stop_all_queued(void);
|
||||||
static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
|
static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
|
||||||
static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
|
static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
|
||||||
int dev_id_num, const char * dev_id_str,
|
int target_dev_id, int dev_id_num,
|
||||||
int dev_id_str_len);
|
const char * dev_id_str, int dev_id_str_len);
|
||||||
static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
|
static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
|
||||||
static int do_create_driverfs_files(void);
|
static int do_create_driverfs_files(void);
|
||||||
static void do_remove_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);
|
errsts = resp_readcap16(SCpnt, devip);
|
||||||
break;
|
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_16:
|
||||||
case READ_12:
|
case READ_12:
|
||||||
case READ_10:
|
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_id = "scsi_debug ";
|
||||||
static const char * inq_product_rev = "0004";
|
static const char * inq_product_rev = "0004";
|
||||||
|
|
||||||
static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
|
static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
|
||||||
int dev_id_num, const char * dev_id_str,
|
int target_dev_id, int dev_id_num,
|
||||||
|
const char * dev_id_str,
|
||||||
int dev_id_str_len)
|
int dev_id_str_len)
|
||||||
{
|
{
|
||||||
int num, port_a;
|
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 >> 16) & 0xff;
|
||||||
arr[num++] = (port_a >> 8) & 0xff;
|
arr[num++] = (port_a >> 8) & 0xff;
|
||||||
arr[num++] = port_a & 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 */
|
/* NAA-5, Target device identifier */
|
||||||
arr[num++] = 0x61; /* proto=sas, binary */
|
arr[num++] = 0x61; /* proto=sas, binary */
|
||||||
arr[num++] = 0xa3; /* piv=1, target device, naa */
|
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)
|
struct sdebug_dev_info * devip)
|
||||||
{
|
{
|
||||||
unsigned char pq_pdt;
|
unsigned char pq_pdt;
|
||||||
unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
|
unsigned char * arr;
|
||||||
unsigned char *cmd = (unsigned char *)scp->cmnd;
|
unsigned char *cmd = (unsigned char *)scp->cmnd;
|
||||||
int alloc_len, n;
|
int alloc_len, n, ret;
|
||||||
|
|
||||||
alloc_len = (cmd[3] << 8) + cmd[4];
|
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)
|
if (devip->wlun)
|
||||||
pq_pdt = 0x1e; /* present, wlun */
|
pq_pdt = 0x1e; /* present, wlun */
|
||||||
else if (scsi_debug_no_lun_0 && (0 == devip->lun))
|
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 */
|
if (0x2 & cmd[1]) { /* CMDDT bit set */
|
||||||
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
|
mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
|
||||||
0);
|
0);
|
||||||
|
kfree(arr);
|
||||||
return check_condition_result;
|
return check_condition_result;
|
||||||
} else if (0x1 & cmd[1]) { /* EVPD bit set */
|
} 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];
|
char lu_id_str[6];
|
||||||
int host_no = devip->sdbg_host->shost->host_no;
|
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)
|
if (0 == scsi_debug_vpd_use_hostno)
|
||||||
host_no = 0;
|
host_no = 0;
|
||||||
lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
|
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);
|
memcpy(&arr[4], lu_id_str, len);
|
||||||
} else if (0x83 == cmd[2]) { /* device identification */
|
} else if (0x83 == cmd[2]) { /* device identification */
|
||||||
arr[1] = cmd[2]; /*sanity */
|
arr[1] = cmd[2]; /*sanity */
|
||||||
arr[3] = inquiry_evpd_83(&arr[4], target_dev_id,
|
arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
|
||||||
lu_id_num, lu_id_str, len);
|
target_dev_id, lu_id_num,
|
||||||
|
lu_id_str, len);
|
||||||
} else if (0x84 == cmd[2]) { /* Software interface ident. */
|
} else if (0x84 == cmd[2]) { /* Software interface ident. */
|
||||||
arr[1] = cmd[2]; /*sanity */
|
arr[1] = cmd[2]; /*sanity */
|
||||||
arr[3] = inquiry_evpd_84(&arr[4]);
|
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 */
|
/* Illegal request, invalid field in cdb */
|
||||||
mk_sense_buffer(devip, ILLEGAL_REQUEST,
|
mk_sense_buffer(devip, ILLEGAL_REQUEST,
|
||||||
INVALID_FIELD_IN_CDB, 0);
|
INVALID_FIELD_IN_CDB, 0);
|
||||||
|
kfree(arr);
|
||||||
return check_condition_result;
|
return check_condition_result;
|
||||||
}
|
}
|
||||||
len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
|
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));
|
min(len, SDEBUG_MAX_INQ_ARR_SZ));
|
||||||
|
kfree(arr);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
/* drops through here for a standard inquiry */
|
/* drops through here for a standard inquiry */
|
||||||
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
|
arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */
|
||||||
arr[2] = scsi_debug_scsi_level;
|
arr[2] = scsi_debug_scsi_level;
|
||||||
arr[3] = 2; /* response_data_format==2 */
|
arr[3] = 2; /* response_data_format==2 */
|
||||||
arr[4] = SDEBUG_LONG_INQ_SZ - 5;
|
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] = 0x10; /* claim: MultiP */
|
||||||
/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
|
/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
|
||||||
arr[7] = 0xa; /* claim: LINKED + CMDQUE */
|
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++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
|
||||||
}
|
}
|
||||||
arr[n++] = 0xc; arr[n++] = 0xf; /* SAS-1.1 rev 10 */
|
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));
|
min(alloc_len, SDEBUG_LONG_INQ_SZ));
|
||||||
|
kfree(arr);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int resp_requests(struct scsi_cmnd * scp,
|
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));
|
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>> */
|
/* <<Following mode page info copied from ST318451LW>> */
|
||||||
|
|
||||||
static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
|
static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
|
||||||
|
|
|
@ -97,6 +97,7 @@ extern const unsigned char scsi_command_size[8];
|
||||||
#define PERSISTENT_RESERVE_IN 0x5e
|
#define PERSISTENT_RESERVE_IN 0x5e
|
||||||
#define PERSISTENT_RESERVE_OUT 0x5f
|
#define PERSISTENT_RESERVE_OUT 0x5f
|
||||||
#define REPORT_LUNS 0xa0
|
#define REPORT_LUNS 0xa0
|
||||||
|
#define MAINTENANCE_IN 0xa3
|
||||||
#define MOVE_MEDIUM 0xa5
|
#define MOVE_MEDIUM 0xa5
|
||||||
#define EXCHANGE_MEDIUM 0xa6
|
#define EXCHANGE_MEDIUM 0xa6
|
||||||
#define READ_12 0xa8
|
#define READ_12 0xa8
|
||||||
|
@ -114,6 +115,8 @@ extern const unsigned char scsi_command_size[8];
|
||||||
#define SERVICE_ACTION_IN 0x9e
|
#define SERVICE_ACTION_IN 0x9e
|
||||||
/* values for service action in */
|
/* values for service action in */
|
||||||
#define SAI_READ_CAPACITY_16 0x10
|
#define SAI_READ_CAPACITY_16 0x10
|
||||||
|
/* values for maintenance in */
|
||||||
|
#define MI_REPORT_TARGET_PGS 0x0a
|
||||||
|
|
||||||
/* Values for T10/04-262r7 */
|
/* Values for T10/04-262r7 */
|
||||||
#define ATA_16 0x85 /* 16-byte pass-thru */
|
#define ATA_16 0x85 /* 16-byte pass-thru */
|
||||||
|
|
Loading…
Reference in a new issue