Skip to content

Commit fb0cc8d

Browse files
doug-gilbertmartinkpetersen
authored andcommitted
scsi_debug: fix sleep in invalid context
In this post: http://www.spinics.net/lists/linux-scsi/msg97124.html the author shows some kernel infrastructure complaining about a sleep in an invalid context. Remove offending call to vmalloc(). Instead of using kzalloc() which reviewers didn't like, use a bucket system (64 bytes on the stack) and potentially multiple calls to sg_pcopy_from_buffer() to construct the 'data-in' buffer for the SCSI REPORT LUNS command. Signed-off-by: Douglas Gilbert <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Reviewed-by: Bart Van Assche <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent baa6719 commit fb0cc8d

File tree

1 file changed

+66
-27
lines changed

1 file changed

+66
-27
lines changed

drivers/scsi/scsi_debug.c

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ static int make_ua(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
890890
return 0;
891891
}
892892

893-
/* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
893+
/* Build SCSI "data-in" buffer. Returns 0 if ok else (DID_ERROR << 16). */
894894
static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
895895
int arr_len)
896896
{
@@ -909,7 +909,35 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
909909
return 0;
910910
}
911911

912-
/* Returns number of bytes fetched into 'arr' or -1 if error. */
912+
/* Partial build of SCSI "data-in" buffer. Returns 0 if ok else
913+
* (DID_ERROR << 16). Can write to offset in data-in buffer. If multiple
914+
* calls, not required to write in ascending offset order. Assumes resid
915+
* set to scsi_bufflen() prior to any calls.
916+
*/
917+
static int p_fill_from_dev_buffer(struct scsi_cmnd *scp, const void *arr,
918+
int arr_len, unsigned int off_dst)
919+
{
920+
int act_len, n;
921+
struct scsi_data_buffer *sdb = scsi_in(scp);
922+
off_t skip = off_dst;
923+
924+
if (sdb->length <= off_dst)
925+
return 0;
926+
if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
927+
return DID_ERROR << 16;
928+
929+
act_len = sg_pcopy_from_buffer(sdb->table.sgl, sdb->table.nents,
930+
arr, arr_len, skip);
931+
pr_debug("%s: off_dst=%u, scsi_bufflen=%u, act_len=%u, resid=%d\n",
932+
__func__, off_dst, scsi_bufflen(scp), act_len, sdb->resid);
933+
n = (int)scsi_bufflen(scp) - ((int)off_dst + act_len);
934+
sdb->resid = min(sdb->resid, n);
935+
return 0;
936+
}
937+
938+
/* Fetches from SCSI "data-out" buffer. Returns number of bytes fetched into
939+
* 'arr' or -1 if error.
940+
*/
913941
static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
914942
int arr_len)
915943
{
@@ -3269,6 +3297,8 @@ static int resp_get_lba_status(struct scsi_cmnd *scp,
32693297
return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
32703298
}
32713299

3300+
#define RL_BUCKET_ELEMS 8
3301+
32723302
/* Even though each pseudo target has a REPORT LUNS "well known logical unit"
32733303
* (W-LUN), the normal Linux scanning logic does not associate it with a
32743304
* device (e.g. /dev/sg7). The following magic will make that association:
@@ -3285,12 +3315,14 @@ static int resp_report_luns(struct scsi_cmnd *scp,
32853315
unsigned char select_report;
32863316
u64 lun;
32873317
struct scsi_lun *lun_p;
3288-
u8 *arr;
3318+
u8 arr[RL_BUCKET_ELEMS * sizeof(struct scsi_lun)];
32893319
unsigned int lun_cnt; /* normal LUN count (max: 256) */
32903320
unsigned int wlun_cnt; /* report luns W-LUN count */
32913321
unsigned int tlun_cnt; /* total LUN count */
32923322
unsigned int rlen; /* response length (in bytes) */
3293-
int i, res;
3323+
int k, j, n, res;
3324+
unsigned int off_rsp = 0;
3325+
const int sz_lun = sizeof(struct scsi_lun);
32943326

32953327
clear_luns_changed_on_target(devip);
32963328

@@ -3329,33 +3361,40 @@ static int resp_report_luns(struct scsi_cmnd *scp,
33293361
--lun_cnt;
33303362

33313363
tlun_cnt = lun_cnt + wlun_cnt;
3332-
3333-
rlen = (tlun_cnt * sizeof(struct scsi_lun)) + 8;
3334-
arr = vmalloc(rlen);
3335-
if (!arr) {
3336-
mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
3337-
INSUFF_RES_ASCQ);
3338-
return check_condition_result;
3339-
}
3340-
memset(arr, 0, rlen);
3364+
rlen = tlun_cnt * sz_lun; /* excluding 8 byte header */
3365+
scsi_set_resid(scp, scsi_bufflen(scp));
33413366
pr_debug("select_report %d luns = %d wluns = %d no_lun0 %d\n",
33423367
select_report, lun_cnt, wlun_cnt, sdebug_no_lun_0);
33433368

3344-
/* luns start at byte 8 in response following the header */
3345-
lun_p = (struct scsi_lun *)&arr[8];
3346-
3347-
/* LUNs use single level peripheral device addressing method */
3369+
/* loops rely on sizeof response header same as sizeof lun (both 8) */
33483370
lun = sdebug_no_lun_0 ? 1 : 0;
3349-
for (i = 0; i < lun_cnt; i++)
3350-
int_to_scsilun(lun++, lun_p++);
3351-
3352-
if (wlun_cnt)
3353-
int_to_scsilun(SCSI_W_LUN_REPORT_LUNS, lun_p++);
3354-
3355-
put_unaligned_be32(rlen - 8, &arr[0]);
3356-
3357-
res = fill_from_dev_buffer(scp, arr, rlen);
3358-
vfree(arr);
3371+
for (k = 0, j = 0, res = 0; true; ++k, j = 0) {
3372+
memset(arr, 0, sizeof(arr));
3373+
lun_p = (struct scsi_lun *)&arr[0];
3374+
if (k == 0) {
3375+
put_unaligned_be32(rlen, &arr[0]);
3376+
++lun_p;
3377+
j = 1;
3378+
}
3379+
for ( ; j < RL_BUCKET_ELEMS; ++j, ++lun_p) {
3380+
if ((k * RL_BUCKET_ELEMS) + j > lun_cnt)
3381+
break;
3382+
int_to_scsilun(lun++, lun_p);
3383+
}
3384+
if (j < RL_BUCKET_ELEMS)
3385+
break;
3386+
n = j * sz_lun;
3387+
res = p_fill_from_dev_buffer(scp, arr, n, off_rsp);
3388+
if (res)
3389+
return res;
3390+
off_rsp += n;
3391+
}
3392+
if (wlun_cnt) {
3393+
int_to_scsilun(SCSI_W_LUN_REPORT_LUNS, lun_p);
3394+
++j;
3395+
}
3396+
if (j > 0)
3397+
res = p_fill_from_dev_buffer(scp, arr, j * sz_lun, off_rsp);
33593398
return res;
33603399
}
33613400

0 commit comments

Comments
 (0)