aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_debug.c
diff options
context:
space:
mode:
authorAkinobu Mita <akinobu.mita@gmail.com>2013-07-08 19:01:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-09 13:33:30 -0400
commita451751172b39702e94c683882ab01d816b673c7 (patch)
tree27a77c2517c4c8af1dcb81c8a6cea0d3c9fba5c3 /drivers/scsi/scsi_debug.c
parentd05257238f9bbe96477162448c2dda08309af208 (diff)
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 <akinobu.mita@gmail.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: "James E.J. Bottomley" <JBottomley@parallels.com> Cc: Douglas Gilbert <dgilbert@interlog.com> Cc: Herbert Xu <herbert@gondor.apana.org.au> Cc: Horia Geanta <horia.geanta@freescale.com> Cc: Imre Deak <imre.deak@intel.com> Cc: Tejun Heo <tj@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/scsi/scsi_debug.c')
-rw-r--r--drivers/scsi/scsi_debug.c48
1 files changed, 37 insertions, 11 deletions
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 0a537a0515ca..d055450c2a4a 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -439,10 +439,7 @@ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
439 439
440 act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, 440 act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
441 arr, arr_len); 441 arr, arr_len);
442 if (sdb->resid) 442 sdb->resid = scsi_bufflen(scp) - act_len;
443 sdb->resid -= act_len;
444 else
445 sdb->resid = scsi_bufflen(scp) - act_len;
446 443
447 return 0; 444 return 0;
448} 445}
@@ -1693,24 +1690,48 @@ static int check_device_access_params(struct sdebug_dev_info *devi,
1693 return 0; 1690 return 0;
1694} 1691}
1695 1692
1693/* Returns number of bytes copied or -1 if error. */
1696static int do_device_access(struct scsi_cmnd *scmd, 1694static int do_device_access(struct scsi_cmnd *scmd,
1697 struct sdebug_dev_info *devi, 1695 struct sdebug_dev_info *devi,
1698 unsigned long long lba, unsigned int num, int write) 1696 unsigned long long lba, unsigned int num, int write)
1699{ 1697{
1700 int ret; 1698 int ret;
1701 unsigned long long block, rest = 0; 1699 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 }
1703 1714
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;
1705 1719
1706 block = do_div(lba, sdebug_store_sectors); 1720 block = do_div(lba, sdebug_store_sectors);
1707 if (block + num > sdebug_store_sectors) 1721 if (block + num > sdebug_store_sectors)
1708 rest = block + num - sdebug_store_sectors; 1722 rest = block + num - sdebug_store_sectors;
1709 1723
1710 ret = func(scmd, fake_storep + (block * scsi_debug_sector_size), 1724 ret = func(sdb->table.sgl, sdb->table.nents,
1711 (num - rest) * scsi_debug_sector_size); 1725 fake_storep + (block * scsi_debug_sector_size),
1712 if (!ret && rest) 1726 (num - rest) * scsi_debug_sector_size, 0);
1713 ret = func(scmd, fake_storep, rest * scsi_debug_sector_size); 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 }
1714 1735
1715 return ret; 1736 return ret;
1716} 1737}
@@ -1849,7 +1870,12 @@ static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
1849 read_lock_irqsave(&atomic_rw, iflags); 1870 read_lock_irqsave(&atomic_rw, iflags);
1850 ret = do_device_access(SCpnt, devip, lba, num, 0); 1871 ret = do_device_access(SCpnt, devip, lba, num, 0);
1851 read_unlock_irqrestore(&atomic_rw, iflags); 1872 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;
1853} 1879}
1854 1880
1855void dump_sector(unsigned char *buf, int len) 1881void dump_sector(unsigned char *buf, int len)