diff options
author | Akinobu Mita <akinobu.mita@gmail.com> | 2013-07-08 19:01:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-09 13:33:30 -0400 |
commit | a451751172b39702e94c683882ab01d816b673c7 (patch) | |
tree | 27a77c2517c4c8af1dcb81c8a6cea0d3c9fba5c3 /drivers/scsi/scsi_debug.c | |
parent | d05257238f9bbe96477162448c2dda08309af208 (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.c | 48 |
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. */ | ||
1696 | static int do_device_access(struct scsi_cmnd *scmd, | 1694 | static 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 | ||
1855 | void dump_sector(unsigned char *buf, int len) | 1881 | void dump_sector(unsigned char *buf, int len) |