Skip to content

Commit b9f28d8

Browse files
James BottomleyJames Bottomley
authored andcommitted
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 <[email protected]>] Acked-by: Ulf Hansson <[email protected]> Reviewed-by: Andrew Morton <[email protected]> Signed-off-by: James Bottomley <[email protected]>
1 parent cad2e03 commit b9f28d8

File tree

5 files changed

+60
-31
lines changed

5 files changed

+60
-31
lines changed

drivers/block/virtio_blk.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ static void virtblk_config_changed_work(struct work_struct *work)
342342
struct request_queue *q = vblk->disk->queue;
343343
char cap_str_2[10], cap_str_10[10];
344344
char *envp[] = { "RESIZE=1", NULL };
345-
u64 capacity, size;
345+
u64 capacity;
346346

347347
/* Host must always specify the capacity. */
348348
virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);
@@ -354,9 +354,10 @@ static void virtblk_config_changed_work(struct work_struct *work)
354354
capacity = (sector_t)-1;
355355
}
356356

357-
size = capacity * queue_logical_block_size(q);
358-
string_get_size(size, STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
359-
string_get_size(size, STRING_UNITS_10, cap_str_10, sizeof(cap_str_10));
357+
string_get_size(capacity, queue_logical_block_size(q),
358+
STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
359+
string_get_size(capacity, queue_logical_block_size(q),
360+
STRING_UNITS_10, cap_str_10, sizeof(cap_str_10));
360361

361362
dev_notice(&vdev->dev,
362363
"new size: %llu %d-byte logical blocks (%s/%s)\n",

drivers/mmc/card/block.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,7 +2230,7 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
22302230
part_md->part_type = part_type;
22312231
list_add(&part_md->part, &md->part);
22322232

2233-
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
2233+
string_get_size((u64)get_capacity(part_md->disk), 512, STRING_UNITS_2,
22342234
cap_str, sizeof(cap_str));
22352235
pr_info("%s: %s %s partition %u %s\n",
22362236
part_md->disk->disk_name, mmc_card_id(card),
@@ -2436,7 +2436,7 @@ static int mmc_blk_probe(struct device *dev)
24362436
if (IS_ERR(md))
24372437
return PTR_ERR(md);
24382438

2439-
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
2439+
string_get_size((u64)get_capacity(md->disk), 512, STRING_UNITS_2,
24402440
cap_str, sizeof(cap_str));
24412441
pr_info("%s: %s %s %s %s\n",
24422442
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),

drivers/scsi/sd.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,11 +2211,11 @@ sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)
22112211

22122212
{
22132213
char cap_str_2[10], cap_str_10[10];
2214-
u64 sz = (u64)sdkp->capacity << ilog2(sector_size);
22152214

2216-
string_get_size(sz, STRING_UNITS_2, cap_str_2,
2217-
sizeof(cap_str_2));
2218-
string_get_size(sz, STRING_UNITS_10, cap_str_10,
2215+
string_get_size(sdkp->capacity, sector_size,
2216+
STRING_UNITS_2, cap_str_2, sizeof(cap_str_2));
2217+
string_get_size(sdkp->capacity, sector_size,
2218+
STRING_UNITS_10, cap_str_10,
22192219
sizeof(cap_str_10));
22202220

22212221
if (sdkp->first_scan || old_capacity != sdkp->capacity) {

include/linux/string_helpers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ enum string_size_units {
1010
STRING_UNITS_2, /* use binary powers of 2^10 */
1111
};
1212

13-
void string_get_size(u64 size, enum string_size_units units,
13+
void string_get_size(u64 size, u64 blk_size, enum string_size_units units,
1414
char *buf, int len);
1515

1616
#define UNESCAPE_SPACE 0x01

lib/string_helpers.c

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Copyright 31 August 2008 James Bottomley
55
* Copyright (C) 2013, Intel Corporation
66
*/
7+
#include <linux/bug.h>
78
#include <linux/kernel.h>
89
#include <linux/math64.h>
910
#include <linux/export.h>
@@ -14,7 +15,8 @@
1415

1516
/**
1617
* string_get_size - get the size in the specified units
17-
* @size: The size to be converted
18+
* @size: The size to be converted in blocks
19+
* @blk_size: Size of the block (use 1 for size in bytes)
1820
* @units: units to use (powers of 1000 or 1024)
1921
* @buf: buffer to format to
2022
* @len: length of buffer
@@ -24,14 +26,14 @@
2426
* at least 9 bytes and will always be zero terminated.
2527
*
2628
*/
27-
void string_get_size(u64 size, const enum string_size_units units,
29+
void string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
2830
char *buf, int len)
2931
{
3032
static const char *const units_10[] = {
31-
"B", "kB", "MB", "GB", "TB", "PB", "EB"
33+
"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
3234
};
3335
static const char *const units_2[] = {
34-
"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"
36+
"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
3537
};
3638
static const char *const *const units_str[] = {
3739
[STRING_UNITS_10] = units_10,
@@ -42,31 +44,57 @@ void string_get_size(u64 size, const enum string_size_units units,
4244
[STRING_UNITS_2] = 1024,
4345
};
4446
int i, j;
45-
u32 remainder = 0, sf_cap;
47+
u32 remainder = 0, sf_cap, exp;
4648
char tmp[8];
49+
const char *unit;
4750

4851
tmp[0] = '\0';
4952
i = 0;
50-
if (size >= divisor[units]) {
51-
while (size >= divisor[units]) {
52-
remainder = do_div(size, divisor[units]);
53-
i++;
54-
}
53+
if (!size)
54+
goto out;
5555

56-
sf_cap = size;
57-
for (j = 0; sf_cap*10 < 1000; j++)
58-
sf_cap *= 10;
56+
while (blk_size >= divisor[units]) {
57+
remainder = do_div(blk_size, divisor[units]);
58+
i++;
59+
}
5960

60-
if (j) {
61-
remainder *= 1000;
62-
remainder /= divisor[units];
63-
snprintf(tmp, sizeof(tmp), ".%03u", remainder);
64-
tmp[j+1] = '\0';
65-
}
61+
exp = divisor[units] / (u32)blk_size;
62+
if (size >= exp) {
63+
remainder = do_div(size, divisor[units]);
64+
remainder *= blk_size;
65+
i++;
66+
} else {
67+
remainder *= size;
68+
}
69+
70+
size *= blk_size;
71+
size += remainder / divisor[units];
72+
remainder %= divisor[units];
73+
74+
while (size >= divisor[units]) {
75+
remainder = do_div(size, divisor[units]);
76+
i++;
6677
}
6778

79+
sf_cap = size;
80+
for (j = 0; sf_cap*10 < 1000; j++)
81+
sf_cap *= 10;
82+
83+
if (j) {
84+
remainder *= 1000;
85+
remainder /= divisor[units];
86+
snprintf(tmp, sizeof(tmp), ".%03u", remainder);
87+
tmp[j+1] = '\0';
88+
}
89+
90+
out:
91+
if (i >= ARRAY_SIZE(units_2))
92+
unit = "UNK";
93+
else
94+
unit = units_str[units][i];
95+
6896
snprintf(buf, len, "%u%s %s", (u32)size,
69-
tmp, units_str[units][i]);
97+
tmp, unit);
7098
}
7199
EXPORT_SYMBOL(string_get_size);
72100

0 commit comments

Comments
 (0)