Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Douglas Gilbert and committed by
Martin K. Petersen
8d039e22 b01f6f83

+87 -48
+87 -48
drivers/scsi/scsi_debug.c
··· 3208 3208 return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN); 3209 3209 } 3210 3210 3211 - #define SDEBUG_RLUN_ARR_SZ 256 3212 - 3213 - static int resp_report_luns(struct scsi_cmnd * scp, 3214 - struct sdebug_dev_info * devip) 3211 + /* Even though each pseudo target has a REPORT LUNS "well known logical unit" 3212 + * (W-LUN), the normal Linux scanning logic does not associate it with a 3213 + * device (e.g. /dev/sg7). The following magic will make that association: 3214 + * "cd /sys/class/scsi_host/host<n> ; echo '- - 49409' > scan" 3215 + * where <n> is a host number. If there are multiple targets in a host then 3216 + * the above will associate a W-LUN to each target. To only get a W-LUN 3217 + * for target 2, then use "echo '- 2 49409' > scan" . 3218 + */ 3219 + static int resp_report_luns(struct scsi_cmnd *scp, 3220 + struct sdebug_dev_info *devip) 3215 3221 { 3216 - unsigned int alloc_len; 3217 - int lun_cnt, i, upper, num, n, want_wlun, shortish; 3218 - u64 lun; 3219 3222 unsigned char *cmd = scp->cmnd; 3220 - int select_report = (int)cmd[2]; 3221 - struct scsi_lun *one_lun; 3222 - unsigned char arr[SDEBUG_RLUN_ARR_SZ]; 3223 - unsigned char * max_addr; 3223 + unsigned int alloc_len; 3224 + unsigned char select_report; 3225 + u64 lun; 3226 + struct scsi_lun *lun_p; 3227 + u8 *arr; 3228 + unsigned int lun_cnt; /* normal LUN count (max: 256) */ 3229 + unsigned int wlun_cnt; /* report luns W-LUN count */ 3230 + unsigned int tlun_cnt; /* total LUN count */ 3231 + unsigned int rlen; /* response length (in bytes) */ 3232 + int i, res; 3224 3233 3225 3234 clear_luns_changed_on_target(devip); 3226 - alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); 3227 - shortish = (alloc_len < 4); 3228 - if (shortish || (select_report > 2)) { 3229 - mk_sense_invalid_fld(scp, SDEB_IN_CDB, shortish ? 6 : 2, -1); 3235 + 3236 + select_report = cmd[2]; 3237 + alloc_len = get_unaligned_be32(cmd + 6); 3238 + 3239 + if (alloc_len < 4) { 3240 + pr_err("alloc len too small %d\n", alloc_len); 3241 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); 3230 3242 return check_condition_result; 3231 3243 } 3232 - /* can produce response with up to 16k luns (lun 0 to lun 16383) */ 3233 - memset(arr, 0, SDEBUG_RLUN_ARR_SZ); 3234 - lun_cnt = sdebug_max_luns; 3235 - if (1 == select_report) 3244 + 3245 + switch (select_report) { 3246 + case 0: /* all LUNs apart from W-LUNs */ 3247 + lun_cnt = sdebug_max_luns; 3248 + wlun_cnt = 0; 3249 + break; 3250 + case 1: /* only W-LUNs */ 3236 3251 lun_cnt = 0; 3237 - else if (sdebug_no_lun_0 && (lun_cnt > 0)) 3252 + wlun_cnt = 1; 3253 + break; 3254 + case 2: /* all LUNs */ 3255 + lun_cnt = sdebug_max_luns; 3256 + wlun_cnt = 1; 3257 + break; 3258 + case 0x10: /* only administrative LUs */ 3259 + case 0x11: /* see SPC-5 */ 3260 + case 0x12: /* only subsiduary LUs owned by referenced LU */ 3261 + default: 3262 + pr_debug("select report invalid %d\n", select_report); 3263 + mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1); 3264 + return check_condition_result; 3265 + } 3266 + 3267 + if (sdebug_no_lun_0 && (lun_cnt > 0)) 3238 3268 --lun_cnt; 3239 - want_wlun = (select_report > 0) ? 1 : 0; 3240 - num = lun_cnt + want_wlun; 3241 - arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff; 3242 - arr[3] = (sizeof(struct scsi_lun) * num) & 0xff; 3243 - n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) / 3244 - sizeof(struct scsi_lun)), num); 3245 - if (n < num) { 3246 - want_wlun = 0; 3247 - lun_cnt = n; 3269 + 3270 + tlun_cnt = lun_cnt + wlun_cnt; 3271 + 3272 + rlen = (tlun_cnt * sizeof(struct scsi_lun)) + 8; 3273 + arr = vmalloc(rlen); 3274 + if (!arr) { 3275 + mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, 3276 + INSUFF_RES_ASCQ); 3277 + return check_condition_result; 3248 3278 } 3249 - one_lun = (struct scsi_lun *) &arr[8]; 3250 - max_addr = arr + SDEBUG_RLUN_ARR_SZ; 3251 - for (i = 0, lun = (sdebug_no_lun_0 ? 1 : 0); 3252 - ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr)); 3253 - i++, lun++) { 3254 - upper = (lun >> 8) & 0x3f; 3255 - if (upper) 3256 - one_lun[i].scsi_lun[0] = 3257 - (upper | (SAM2_LUN_ADDRESS_METHOD << 6)); 3258 - one_lun[i].scsi_lun[1] = lun & 0xff; 3259 - } 3260 - if (want_wlun) { 3261 - one_lun[i].scsi_lun[0] = (SCSI_W_LUN_REPORT_LUNS >> 8) & 0xff; 3262 - one_lun[i].scsi_lun[1] = SCSI_W_LUN_REPORT_LUNS & 0xff; 3263 - i++; 3264 - } 3265 - alloc_len = (unsigned char *)(one_lun + i) - arr; 3266 - return fill_from_dev_buffer(scp, arr, 3267 - min((int)alloc_len, SDEBUG_RLUN_ARR_SZ)); 3279 + memset(arr, 0, rlen); 3280 + pr_debug("select_report %d luns = %d wluns = %d no_lun0 %d\n", 3281 + select_report, lun_cnt, wlun_cnt, sdebug_no_lun_0); 3282 + 3283 + /* luns start at byte 8 in response following the header */ 3284 + lun_p = (struct scsi_lun *)&arr[8]; 3285 + 3286 + /* LUNs use single level peripheral device addressing method */ 3287 + lun = sdebug_no_lun_0 ? 1 : 0; 3288 + for (i = 0; i < lun_cnt; i++) 3289 + int_to_scsilun(lun++, lun_p++); 3290 + 3291 + if (wlun_cnt) 3292 + int_to_scsilun(SCSI_W_LUN_REPORT_LUNS, lun_p++); 3293 + 3294 + put_unaligned_be32(rlen - 8, &arr[0]); 3295 + 3296 + res = fill_from_dev_buffer(scp, arr, rlen); 3297 + vfree(arr); 3298 + return res; 3268 3299 } 3269 3300 3270 3301 static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, ··· 4334 4303 bool changed; 4335 4304 4336 4305 if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { 4306 + if (n > 256) { 4307 + pr_warn("max_luns can be no more than 256\n"); 4308 + return -EINVAL; 4309 + } 4337 4310 changed = (sdebug_max_luns != n); 4338 4311 sdebug_max_luns = n; 4339 4312 sdebug_max_tgts_luns(); ··· 4681 4646 if (sdebug_physblk_exp > 15) { 4682 4647 pr_err("invalid physblk_exp %u\n", sdebug_physblk_exp); 4683 4648 return -EINVAL; 4649 + } 4650 + if (sdebug_max_luns > 256) { 4651 + pr_warn("max_luns can be no more than 256, use default\n"); 4652 + sdebug_max_luns = DEF_MAX_LUNS; 4684 4653 } 4685 4654 4686 4655 if (sdebug_lowest_aligned > 0x3fff) {