Skip to content

Commit 7cc8e58

Browse files
Miao Xiemasoncl
authored andcommitted
Btrfs: fix unprotected device's variants on 32bits machine
->total_bytes,->disk_total_bytes,->bytes_used is protected by chunk lock when we change them, but sometimes we read them without any lock, and we might get unexpected value. We fix this problem like inode's i_size. Signed-off-by: Miao Xie <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent 1c11618 commit 7cc8e58

4 files changed

Lines changed: 124 additions & 29 deletions

File tree

fs/btrfs/dev-replace.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ int btrfs_dev_replace_start(struct btrfs_root *root,
418418

419419
/* the disk copy procedure reuses the scrub code */
420420
ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
421-
src_device->total_bytes,
421+
btrfs_device_get_total_bytes(src_device),
422422
&dev_replace->scrub_progress, 0, 1);
423423

424424
ret = btrfs_dev_replace_finishing(root->fs_info, ret);
@@ -555,11 +555,12 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
555555
memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp));
556556
memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid));
557557
memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid));
558-
tgt_device->total_bytes = src_device->total_bytes;
559-
tgt_device->disk_total_bytes = src_device->disk_total_bytes;
558+
btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes);
559+
btrfs_device_set_disk_total_bytes(tgt_device,
560+
src_device->disk_total_bytes);
561+
btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
560562
ASSERT(list_empty(&src_device->resized_list));
561563
tgt_device->commit_total_bytes = src_device->commit_total_bytes;
562-
tgt_device->bytes_used = src_device->bytes_used;
563564
tgt_device->commit_bytes_used = src_device->bytes_used;
564565
if (fs_info->sb->s_bdev == src_device->bdev)
565566
fs_info->sb->s_bdev = tgt_device->bdev;
@@ -650,6 +651,7 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
650651
struct btrfs_ioctl_dev_replace_args *args)
651652
{
652653
struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
654+
struct btrfs_device *srcdev;
653655

654656
btrfs_dev_replace_lock(dev_replace);
655657
/* even if !dev_replace_is_valid, the values are good enough for
@@ -672,8 +674,9 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info,
672674
break;
673675
case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
674676
case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
677+
srcdev = dev_replace->srcdev;
675678
args->status.progress_1000 = div64_u64(dev_replace->cursor_left,
676-
div64_u64(dev_replace->srcdev->total_bytes, 1000));
679+
div64_u64(btrfs_device_get_total_bytes(srcdev), 1000));
677680
break;
678681
}
679682
btrfs_dev_replace_unlock(dev_replace);
@@ -832,7 +835,7 @@ static int btrfs_dev_replace_continue_on_mount(struct btrfs_fs_info *fs_info)
832835

833836
ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid,
834837
dev_replace->committed_cursor_left,
835-
dev_replace->srcdev->total_bytes,
838+
btrfs_device_get_total_bytes(dev_replace->srcdev),
836839
&dev_replace->scrub_progress, 0, 1);
837840
ret = btrfs_dev_replace_finishing(fs_info, ret);
838841
WARN_ON(ret);

fs/btrfs/ioctl.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,7 +1553,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
15531553
goto out_free;
15541554
}
15551555

1556-
old_size = device->total_bytes;
1556+
old_size = btrfs_device_get_total_bytes(device);
15571557

15581558
if (mod < 0) {
15591559
if (new_size > old_size) {
@@ -2740,8 +2740,8 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
27402740
}
27412741

27422742
di_args->devid = dev->devid;
2743-
di_args->bytes_used = dev->bytes_used;
2744-
di_args->total_bytes = dev->total_bytes;
2743+
di_args->bytes_used = btrfs_device_get_bytes_used(dev);
2744+
di_args->total_bytes = btrfs_device_get_total_bytes(dev);
27452745
memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
27462746
if (dev->name) {
27472747
struct rcu_string *name;

fs/btrfs/volumes.c

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,7 +1308,7 @@ static int btrfs_free_dev_extent(struct btrfs_trans_handle *trans,
13081308

13091309
if (device->bytes_used > 0) {
13101310
u64 len = btrfs_dev_extent_length(leaf, extent);
1311-
device->bytes_used -= len;
1311+
btrfs_device_set_bytes_used(device, device->bytes_used - len);
13121312
spin_lock(&root->fs_info->free_chunk_lock);
13131313
root->fs_info->free_chunk_space += len;
13141314
spin_unlock(&root->fs_info->free_chunk_lock);
@@ -1462,8 +1462,10 @@ static int btrfs_add_device(struct btrfs_trans_handle *trans,
14621462
btrfs_set_device_io_align(leaf, dev_item, device->io_align);
14631463
btrfs_set_device_io_width(leaf, dev_item, device->io_width);
14641464
btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
1465-
btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
1466-
btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
1465+
btrfs_set_device_total_bytes(leaf, dev_item,
1466+
btrfs_device_get_disk_total_bytes(device));
1467+
btrfs_set_device_bytes_used(leaf, dev_item,
1468+
btrfs_device_get_bytes_used(device));
14671469
btrfs_set_device_group(leaf, dev_item, 0);
14681470
btrfs_set_device_seek_speed(leaf, dev_item, 0);
14691471
btrfs_set_device_bandwidth(leaf, dev_item, 0);
@@ -2330,7 +2332,8 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
23302332
}
23312333

23322334

2333-
if (i_size_read(bdev->bd_inode) < srcdev->total_bytes) {
2335+
if (i_size_read(bdev->bd_inode) <
2336+
btrfs_device_get_total_bytes(srcdev)) {
23342337
btrfs_err(fs_info, "target device is smaller than source device!");
23352338
ret = -EINVAL;
23362339
goto error;
@@ -2360,11 +2363,11 @@ int btrfs_init_dev_replace_tgtdev(struct btrfs_root *root, char *device_path,
23602363
device->io_width = root->sectorsize;
23612364
device->io_align = root->sectorsize;
23622365
device->sector_size = root->sectorsize;
2363-
device->total_bytes = srcdev->total_bytes;
2364-
device->disk_total_bytes = srcdev->disk_total_bytes;
2366+
device->total_bytes = btrfs_device_get_total_bytes(srcdev);
2367+
device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev);
2368+
device->bytes_used = btrfs_device_get_bytes_used(srcdev);
23652369
ASSERT(list_empty(&srcdev->resized_list));
23662370
device->commit_total_bytes = srcdev->commit_total_bytes;
2367-
device->bytes_used = srcdev->bytes_used;
23682371
device->commit_bytes_used = device->bytes_used;
23692372
device->dev_root = fs_info->dev_root;
23702373
device->bdev = bdev;
@@ -2435,8 +2438,10 @@ static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
24352438
btrfs_set_device_io_align(leaf, dev_item, device->io_align);
24362439
btrfs_set_device_io_width(leaf, dev_item, device->io_width);
24372440
btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
2438-
btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
2439-
btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
2441+
btrfs_set_device_total_bytes(leaf, dev_item,
2442+
btrfs_device_get_disk_total_bytes(device));
2443+
btrfs_set_device_bytes_used(leaf, dev_item,
2444+
btrfs_device_get_bytes_used(device));
24402445
btrfs_mark_buffer_dirty(leaf);
24412446

24422447
out:
@@ -2464,8 +2469,8 @@ static int __btrfs_grow_device(struct btrfs_trans_handle *trans,
24642469
btrfs_set_super_total_bytes(super_copy, old_total + diff);
24652470
device->fs_devices->total_rw_bytes += diff;
24662471

2467-
device->total_bytes = new_size;
2468-
device->disk_total_bytes = new_size;
2472+
btrfs_device_set_total_bytes(device, new_size);
2473+
btrfs_device_set_disk_total_bytes(device, new_size);
24692474
btrfs_clear_space_info_full(device->dev_root->fs_info);
24702475
if (list_empty(&device->resized_list))
24712476
list_add_tail(&device->resized_list,
@@ -3110,11 +3115,12 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info)
31103115
/* step one make some room on all the devices */
31113116
devices = &fs_info->fs_devices->devices;
31123117
list_for_each_entry(device, devices, dev_list) {
3113-
old_size = device->total_bytes;
3118+
old_size = btrfs_device_get_total_bytes(device);
31143119
size_to_free = div_factor(old_size, 1);
31153120
size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);
31163121
if (!device->writeable ||
3117-
device->total_bytes - device->bytes_used > size_to_free ||
3122+
btrfs_device_get_total_bytes(device) -
3123+
btrfs_device_get_bytes_used(device) > size_to_free ||
31183124
device->is_tgtdev_for_dev_replace)
31193125
continue;
31203126

@@ -3920,8 +3926,8 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
39203926
struct btrfs_key key;
39213927
struct btrfs_super_block *super_copy = root->fs_info->super_copy;
39223928
u64 old_total = btrfs_super_total_bytes(super_copy);
3923-
u64 old_size = device->total_bytes;
3924-
u64 diff = device->total_bytes - new_size;
3929+
u64 old_size = btrfs_device_get_total_bytes(device);
3930+
u64 diff = old_size - new_size;
39253931

39263932
if (device->is_tgtdev_for_dev_replace)
39273933
return -EINVAL;
@@ -3934,7 +3940,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
39343940

39353941
lock_chunks(root);
39363942

3937-
device->total_bytes = new_size;
3943+
btrfs_device_set_total_bytes(device, new_size);
39383944
if (device->writeable) {
39393945
device->fs_devices->total_rw_bytes -= diff;
39403946
spin_lock(&root->fs_info->free_chunk_lock);
@@ -4000,7 +4006,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
40004006
ret = -ENOSPC;
40014007
lock_chunks(root);
40024008

4003-
device->total_bytes = old_size;
4009+
btrfs_device_set_total_bytes(device, old_size);
40044010
if (device->writeable)
40054011
device->fs_devices->total_rw_bytes += diff;
40064012
spin_lock(&root->fs_info->free_chunk_lock);
@@ -4018,7 +4024,7 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
40184024
}
40194025

40204026
lock_chunks(root);
4021-
device->disk_total_bytes = new_size;
4027+
btrfs_device_set_disk_total_bytes(device, new_size);
40224028
if (list_empty(&device->resized_list))
40234029
list_add_tail(&device->resized_list,
40244030
&root->fs_info->fs_devices->resized_devices);
@@ -4429,8 +4435,10 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
44294435
if (ret)
44304436
goto error_del_extent;
44314437

4432-
for (i = 0; i < map->num_stripes; i++)
4433-
map->stripes[i].dev->bytes_used += stripe_size;
4438+
for (i = 0; i < map->num_stripes; i++) {
4439+
num_bytes = map->stripes[i].dev->bytes_used + stripe_size;
4440+
btrfs_device_set_bytes_used(map->stripes[i].dev, num_bytes);
4441+
}
44344442

44354443
spin_lock(&extent_root->fs_info->free_chunk_lock);
44364444
extent_root->fs_info->free_chunk_space -= (stripe_size *

fs/btrfs/volumes.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ struct btrfs_pending_bios {
3232
struct bio *tail;
3333
};
3434

35+
/*
36+
* Use sequence counter to get consistent device stat data on
37+
* 32-bit processors.
38+
*/
39+
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
40+
#include <linux/seqlock.h>
41+
#define __BTRFS_NEED_DEVICE_DATA_ORDERED
42+
#define btrfs_device_data_ordered_init(device) \
43+
seqcount_init(&device->data_seqcount)
44+
#else
45+
#define btrfs_device_data_ordered_init(device) do { } while (0)
46+
#endif
47+
3548
struct btrfs_device {
3649
struct list_head dev_list;
3750
struct list_head dev_alloc_list;
@@ -61,6 +74,10 @@ struct btrfs_device {
6174
int can_discard;
6275
int is_tgtdev_for_dev_replace;
6376

77+
#ifdef __BTRFS_NEED_DEVICE_DATA_ORDERED
78+
seqcount_t data_seqcount;
79+
#endif
80+
6481
/* the internal btrfs device id */
6582
u64 devid;
6683

@@ -133,6 +150,73 @@ struct btrfs_device {
133150
atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX];
134151
};
135152

153+
/*
154+
* If we read those variants at the context of their own lock, we needn't
155+
* use the following helpers, reading them directly is safe.
156+
*/
157+
#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
158+
#define BTRFS_DEVICE_GETSET_FUNCS(name) \
159+
static inline u64 \
160+
btrfs_device_get_##name(const struct btrfs_device *dev) \
161+
{ \
162+
u64 size; \
163+
unsigned int seq; \
164+
\
165+
do { \
166+
seq = read_seqcount_begin(&dev->data_seqcount); \
167+
size = dev->name; \
168+
} while (read_seqcount_retry(&dev->data_seqcount, seq)); \
169+
return size; \
170+
} \
171+
\
172+
static inline void \
173+
btrfs_device_set_##name(struct btrfs_device *dev, u64 size) \
174+
{ \
175+
preempt_disable(); \
176+
write_seqcount_begin(&dev->data_seqcount); \
177+
dev->name = size; \
178+
write_seqcount_end(&dev->data_seqcount); \
179+
preempt_enable(); \
180+
}
181+
#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
182+
#define BTRFS_DEVICE_GETSET_FUNCS(name) \
183+
static inline u64 \
184+
btrfs_device_get_##name(const struct btrfs_device *dev) \
185+
{ \
186+
u64 size; \
187+
\
188+
preempt_disable(); \
189+
size = dev->name; \
190+
preempt_enable(); \
191+
return size; \
192+
} \
193+
\
194+
static inline void \
195+
btrfs_device_set_##name(struct btrfs_device *dev, u64 size) \
196+
{ \
197+
preempt_disable(); \
198+
dev->name = size; \
199+
preempt_enable(); \
200+
}
201+
#else
202+
#define BTRFS_DEVICE_GETSET_FUNCS(name) \
203+
static inline u64 \
204+
btrfs_device_get_##name(const struct btrfs_device *dev) \
205+
{ \
206+
return dev->name; \
207+
} \
208+
\
209+
static inline void \
210+
btrfs_device_set_##name(struct btrfs_device *dev, u64 size) \
211+
{ \
212+
dev->name = size; \
213+
}
214+
#endif
215+
216+
BTRFS_DEVICE_GETSET_FUNCS(total_bytes);
217+
BTRFS_DEVICE_GETSET_FUNCS(disk_total_bytes);
218+
BTRFS_DEVICE_GETSET_FUNCS(bytes_used);
219+
136220
struct btrfs_fs_devices {
137221
u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
138222

0 commit comments

Comments
 (0)