aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorJames Bottomley <JBottomley@Odin.com>2015-03-05 21:47:01 -0500
committerJames Bottomley <JBottomley@Odin.com>2015-04-10 19:27:48 -0400
commitb9f28d863594c429e1df35a0474d2663ca28b307 (patch)
tree75a15cd66637d33e6d8e6e0c6cdd67e44790b376 /drivers/scsi
parentcad2e03d8607793fd71a830b4b41fc8e9e9995ea (diff)
sd, mmc, virtio_blk, string_helpers: fix block size units
The current string_get_size() overflows when the device size goes over 2^64 bytes because the string helper routine computes the suffix from the size in bytes. However, the entirety of Linux thinks in terms of blocks, not bytes, so this will artificially induce an overflow on very large devices. Fix this by making the function string_get_size() take blocks and the block size instead of bytes. This should allow us to keep working until the current SCSI standard overflows. Also fix virtio_blk and mmc (both of which were also artificially multiplying by the block size to pass a byte side to string_get_size()). The mathematics of this is pretty simple: we're taking a product of size in blocks (S) and block size (B) and trying to re-express this in exponential form: S*B = R*N^E (where N, the exponent is either 1000 or 1024) and R < N. Mathematically, S = RS*N^ES and B=RB*N^EB, so if RS*RB < N it's easy to see that S*B = RS*RB*N^(ES+EB). However, if RS*BS > N, we can see that this can be re-expressed as RS*BS = R*N (where R = RS*BS/N < N) so the whole exponent becomes R*N^(ES+EB+1) [jejb: fix incorrect 32 bit do_div spotted by kbuild test robot <fengguang.wu@intel.com>] Acked-by: Ulf Hansson <ulf.hansson@linaro.org> Reviewed-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: James Bottomley <JBottomley@Odin.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/sd.c8
1 files changed, 4 insertions, 4 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 9e0c63e57aff..dcc42446f58a 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2211,11 +2211,11 @@ got_data:
2211 2211
2212 { 2212 {
2213 char cap_str_2[10], cap_str_10[10]; 2213 char cap_str_2[10], cap_str_10[10];
2214 u64 sz = (u64)sdkp->capacity << ilog2(sector_size);
2215 2214
2216 string_get_size(sz, STRING_UNITS_2, cap_str_2, 2215 string_get_size(sdkp->capacity, sector_size,
2217 sizeof(cap_str_2)); 2216 STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
2218 string_get_size(sz, STRING_UNITS_10, cap_str_10, 2217 string_get_size(sdkp->capacity, sector_size,
2218 STRING_UNITS_10, cap_str_10,
2219 sizeof(cap_str_10)); 2219 sizeof(cap_str_10));
2220 2220
2221 if (sdkp->first_scan || old_capacity != sdkp->capacity) { 2221 if (sdkp->first_scan || old_capacity != sdkp->capacity) {