scsi_debug: rework resp_report_luns
Based on "[PATH V2] scsi_debug: rework resp_report_luns" patch
sent by Tomas Winkler on Thursday, 26 Feb 2015. His notes:
1. Remove duplicated boundary checks which simplify the fill-in
loop
2. Use more of scsi generic API
Replace fixed length response array a with heap allocation
allowing up to 256 normal LUNs per target.
Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Reviewed-by: Hannes Reinicke <hare@suse.de>
Reviewed-by: Tomas Winkler <tomas.winkler@intel.com>
Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
committed by
Martin K. Petersen
parent
b01f6f8316
commit
8d039e22b5
@@ -3208,63 +3208,94 @@ static int resp_get_lba_status(struct scsi_cmnd *scp,
|
|||||||
return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
|
return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SDEBUG_RLUN_ARR_SZ 256
|
/* Even though each pseudo target has a REPORT LUNS "well known logical unit"
|
||||||
|
* (W-LUN), the normal Linux scanning logic does not associate it with a
|
||||||
static int resp_report_luns(struct scsi_cmnd * scp,
|
* device (e.g. /dev/sg7). The following magic will make that association:
|
||||||
struct sdebug_dev_info * devip)
|
* "cd /sys/class/scsi_host/host<n> ; echo '- - 49409' > scan"
|
||||||
|
* where <n> is a host number. If there are multiple targets in a host then
|
||||||
|
* the above will associate a W-LUN to each target. To only get a W-LUN
|
||||||
|
* for target 2, then use "echo '- 2 49409' > scan" .
|
||||||
|
*/
|
||||||
|
static int resp_report_luns(struct scsi_cmnd *scp,
|
||||||
|
struct sdebug_dev_info *devip)
|
||||||
{
|
{
|
||||||
unsigned int alloc_len;
|
|
||||||
int lun_cnt, i, upper, num, n, want_wlun, shortish;
|
|
||||||
u64 lun;
|
|
||||||
unsigned char *cmd = scp->cmnd;
|
unsigned char *cmd = scp->cmnd;
|
||||||
int select_report = (int)cmd[2];
|
unsigned int alloc_len;
|
||||||
struct scsi_lun *one_lun;
|
unsigned char select_report;
|
||||||
unsigned char arr[SDEBUG_RLUN_ARR_SZ];
|
u64 lun;
|
||||||
unsigned char * max_addr;
|
struct scsi_lun *lun_p;
|
||||||
|
u8 *arr;
|
||||||
|
unsigned int lun_cnt; /* normal LUN count (max: 256) */
|
||||||
|
unsigned int wlun_cnt; /* report luns W-LUN count */
|
||||||
|
unsigned int tlun_cnt; /* total LUN count */
|
||||||
|
unsigned int rlen; /* response length (in bytes) */
|
||||||
|
int i, res;
|
||||||
|
|
||||||
clear_luns_changed_on_target(devip);
|
clear_luns_changed_on_target(devip);
|
||||||
alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
|
|
||||||
shortish = (alloc_len < 4);
|
select_report = cmd[2];
|
||||||
if (shortish || (select_report > 2)) {
|
alloc_len = get_unaligned_be32(cmd + 6);
|
||||||
mk_sense_invalid_fld(scp, SDEB_IN_CDB, shortish ? 6 : 2, -1);
|
|
||||||
|
if (alloc_len < 4) {
|
||||||
|
pr_err("alloc len too small %d\n", alloc_len);
|
||||||
|
mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1);
|
||||||
return check_condition_result;
|
return check_condition_result;
|
||||||
}
|
}
|
||||||
/* can produce response with up to 16k luns (lun 0 to lun 16383) */
|
|
||||||
memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
|
switch (select_report) {
|
||||||
lun_cnt = sdebug_max_luns;
|
case 0: /* all LUNs apart from W-LUNs */
|
||||||
if (1 == select_report)
|
lun_cnt = sdebug_max_luns;
|
||||||
|
wlun_cnt = 0;
|
||||||
|
break;
|
||||||
|
case 1: /* only W-LUNs */
|
||||||
lun_cnt = 0;
|
lun_cnt = 0;
|
||||||
else if (sdebug_no_lun_0 && (lun_cnt > 0))
|
wlun_cnt = 1;
|
||||||
|
break;
|
||||||
|
case 2: /* all LUNs */
|
||||||
|
lun_cnt = sdebug_max_luns;
|
||||||
|
wlun_cnt = 1;
|
||||||
|
break;
|
||||||
|
case 0x10: /* only administrative LUs */
|
||||||
|
case 0x11: /* see SPC-5 */
|
||||||
|
case 0x12: /* only subsiduary LUs owned by referenced LU */
|
||||||
|
default:
|
||||||
|
pr_debug("select report invalid %d\n", select_report);
|
||||||
|
mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
|
||||||
|
return check_condition_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdebug_no_lun_0 && (lun_cnt > 0))
|
||||||
--lun_cnt;
|
--lun_cnt;
|
||||||
want_wlun = (select_report > 0) ? 1 : 0;
|
|
||||||
num = lun_cnt + want_wlun;
|
tlun_cnt = lun_cnt + wlun_cnt;
|
||||||
arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
|
|
||||||
arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
|
rlen = (tlun_cnt * sizeof(struct scsi_lun)) + 8;
|
||||||
n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
|
arr = vmalloc(rlen);
|
||||||
sizeof(struct scsi_lun)), num);
|
if (!arr) {
|
||||||
if (n < num) {
|
mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
|
||||||
want_wlun = 0;
|
INSUFF_RES_ASCQ);
|
||||||
lun_cnt = n;
|
return check_condition_result;
|
||||||
}
|
}
|
||||||
one_lun = (struct scsi_lun *) &arr[8];
|
memset(arr, 0, rlen);
|
||||||
max_addr = arr + SDEBUG_RLUN_ARR_SZ;
|
pr_debug("select_report %d luns = %d wluns = %d no_lun0 %d\n",
|
||||||
for (i = 0, lun = (sdebug_no_lun_0 ? 1 : 0);
|
select_report, lun_cnt, wlun_cnt, sdebug_no_lun_0);
|
||||||
((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
|
|
||||||
i++, lun++) {
|
/* luns start at byte 8 in response following the header */
|
||||||
upper = (lun >> 8) & 0x3f;
|
lun_p = (struct scsi_lun *)&arr[8];
|
||||||
if (upper)
|
|
||||||
one_lun[i].scsi_lun[0] =
|
/* LUNs use single level peripheral device addressing method */
|
||||||
(upper | (SAM2_LUN_ADDRESS_METHOD << 6));
|
lun = sdebug_no_lun_0 ? 1 : 0;
|
||||||
one_lun[i].scsi_lun[1] = lun & 0xff;
|
for (i = 0; i < lun_cnt; i++)
|
||||||
}
|
int_to_scsilun(lun++, lun_p++);
|
||||||
if (want_wlun) {
|
|
||||||
one_lun[i].scsi_lun[0] = (SCSI_W_LUN_REPORT_LUNS >> 8) & 0xff;
|
if (wlun_cnt)
|
||||||
one_lun[i].scsi_lun[1] = SCSI_W_LUN_REPORT_LUNS & 0xff;
|
int_to_scsilun(SCSI_W_LUN_REPORT_LUNS, lun_p++);
|
||||||
i++;
|
|
||||||
}
|
put_unaligned_be32(rlen - 8, &arr[0]);
|
||||||
alloc_len = (unsigned char *)(one_lun + i) - arr;
|
|
||||||
return fill_from_dev_buffer(scp, arr,
|
res = fill_from_dev_buffer(scp, arr, rlen);
|
||||||
min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
|
vfree(arr);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
|
static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
|
||||||
@@ -4303,6 +4334,10 @@ static ssize_t max_luns_store(struct device_driver *ddp, const char *buf,
|
|||||||
bool changed;
|
bool changed;
|
||||||
|
|
||||||
if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
|
if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
|
||||||
|
if (n > 256) {
|
||||||
|
pr_warn("max_luns can be no more than 256\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
changed = (sdebug_max_luns != n);
|
changed = (sdebug_max_luns != n);
|
||||||
sdebug_max_luns = n;
|
sdebug_max_luns = n;
|
||||||
sdebug_max_tgts_luns();
|
sdebug_max_tgts_luns();
|
||||||
@@ -4647,6 +4682,10 @@ static int __init scsi_debug_init(void)
|
|||||||
pr_err("invalid physblk_exp %u\n", sdebug_physblk_exp);
|
pr_err("invalid physblk_exp %u\n", sdebug_physblk_exp);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (sdebug_max_luns > 256) {
|
||||||
|
pr_warn("max_luns can be no more than 256, use default\n");
|
||||||
|
sdebug_max_luns = DEF_MAX_LUNS;
|
||||||
|
}
|
||||||
|
|
||||||
if (sdebug_lowest_aligned > 0x3fff) {
|
if (sdebug_lowest_aligned > 0x3fff) {
|
||||||
pr_err("lowest_aligned too big: %u\n", sdebug_lowest_aligned);
|
pr_err("lowest_aligned too big: %u\n", sdebug_lowest_aligned);
|
||||||
|
|||||||
Reference in New Issue
Block a user