Skip to content

Commit a451751

Browse files
mitatorvalds
authored andcommitted
scsi_debug: fix do_device_access() with wrap around range
do_device_access() is a function that abstracts copying SG list from/to ramdisk storage (fake_storep). It must deal with the ranges exceeding actual fake_storep size, because such ranges are valid if virtual_gb is set greater than zero, and they should be treated as fake_storep is repeatedly mirrored up to virtual size. Unfortunately, it can't deal with the range which wraps around the end of fake_storep. A wrap around range is copied by two sg_copy_{from,to}_buffer() calls, but sg_copy_{from,to}_buffer() can't copy from/to in the middle of SG list, therefore the second call can't copy correctly. This fixes it by using sg_pcopy_{from,to}_buffer() that can copy from/to the middle of SG list. This also simplifies the assignment of sdb->resid in fill_from_dev_buffer(). Because fill_from_dev_buffer() is now only called once per command execution cycle. So it is not necessary to take care to decrease sdb->resid if fill_from_dev_buffer() is called more than once. Signed-off-by: Akinobu Mita <[email protected]> Cc: "David S. Miller" <[email protected]> Cc: "James E.J. Bottomley" <[email protected]> Cc: Douglas Gilbert <[email protected]> Cc: Herbert Xu <[email protected]> Cc: Horia Geanta <[email protected]> Cc: Imre Deak <[email protected]> Cc: Tejun Heo <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent d052572 commit a451751

File tree

1 file changed

+37
-11
lines changed

1 file changed

+37
-11
lines changed

drivers/scsi/scsi_debug.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,7 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
439439

440440
act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
441441
arr, arr_len);
442-
if (sdb->resid)
443-
sdb->resid -= act_len;
444-
else
445-
sdb->resid = scsi_bufflen(scp) - act_len;
442+
sdb->resid = scsi_bufflen(scp) - act_len;
446443

447444
return 0;
448445
}
@@ -1693,24 +1690,48 @@ static int check_device_access_params(struct sdebug_dev_info *devi,
16931690
return 0;
16941691
}
16951692

1693+
/* Returns number of bytes copied or -1 if error. */
16961694
static int do_device_access(struct scsi_cmnd *scmd,
16971695
struct sdebug_dev_info *devi,
16981696
unsigned long long lba, unsigned int num, int write)
16991697
{
17001698
int ret;
17011699
unsigned long long block, rest = 0;
1702-
int (*func)(struct scsi_cmnd *, unsigned char *, int);
1700+
struct scsi_data_buffer *sdb;
1701+
enum dma_data_direction dir;
1702+
size_t (*func)(struct scatterlist *, unsigned int, void *, size_t,
1703+
off_t);
1704+
1705+
if (write) {
1706+
sdb = scsi_out(scmd);
1707+
dir = DMA_TO_DEVICE;
1708+
func = sg_pcopy_to_buffer;
1709+
} else {
1710+
sdb = scsi_in(scmd);
1711+
dir = DMA_FROM_DEVICE;
1712+
func = sg_pcopy_from_buffer;
1713+
}
17031714

1704-
func = write ? fetch_to_dev_buffer : fill_from_dev_buffer;
1715+
if (!sdb->length)
1716+
return 0;
1717+
if (!(scsi_bidi_cmnd(scmd) || scmd->sc_data_direction == dir))
1718+
return -1;
17051719

17061720
block = do_div(lba, sdebug_store_sectors);
17071721
if (block + num > sdebug_store_sectors)
17081722
rest = block + num - sdebug_store_sectors;
17091723

1710-
ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
1711-
(num - rest) * scsi_debug_sector_size);
1712-
if (!ret && rest)
1713-
ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
1724+
ret = func(sdb->table.sgl, sdb->table.nents,
1725+
fake_storep + (block * scsi_debug_sector_size),
1726+
(num - rest) * scsi_debug_sector_size, 0);
1727+
if (ret != (num - rest) * scsi_debug_sector_size)
1728+
return ret;
1729+
1730+
if (rest) {
1731+
ret += func(sdb->table.sgl, sdb->table.nents,
1732+
fake_storep, rest * scsi_debug_sector_size,
1733+
(num - rest) * scsi_debug_sector_size);
1734+
}
17141735

17151736
return ret;
17161737
}
@@ -1849,7 +1870,12 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
18491870
read_lock_irqsave(&atomic_rw, iflags);
18501871
ret = do_device_access(SCpnt, devip, lba, num, 0);
18511872
read_unlock_irqrestore(&atomic_rw, iflags);
1852-
return ret;
1873+
if (ret == -1)
1874+
return DID_ERROR << 16;
1875+
1876+
scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret;
1877+
1878+
return 0;
18531879
}
18541880

18551881
void dump_sector(unsigned char *buf, int len)

0 commit comments

Comments
 (0)