diff options
author | Miao Xie <miaox@cn.fujitsu.com> | 2014-09-03 09:35:38 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-09-17 16:38:38 -0400 |
commit | 7cc8e58d53cd2295c3c1cee7b503bd1790ea4486 (patch) | |
tree | 1befd9a2bea8e1808f0fe1b12406c05b3f7dbe2d /fs/btrfs/volumes.h | |
parent | 1c1161870c8bcb0d966ebbf1aec05a87a79a4175 (diff) |
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 <miaox@cn.fujitsu.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/volumes.h')
-rw-r--r-- | fs/btrfs/volumes.h | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index f79d532fedb0..76600a3fedbe 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -32,6 +32,19 @@ struct btrfs_pending_bios { | |||
32 | struct bio *tail; | 32 | struct bio *tail; |
33 | }; | 33 | }; |
34 | 34 | ||
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 | |||
35 | struct btrfs_device { | 48 | struct btrfs_device { |
36 | struct list_head dev_list; | 49 | struct list_head dev_list; |
37 | struct list_head dev_alloc_list; | 50 | struct list_head dev_alloc_list; |
@@ -61,6 +74,10 @@ struct btrfs_device { | |||
61 | int can_discard; | 74 | int can_discard; |
62 | int is_tgtdev_for_dev_replace; | 75 | int is_tgtdev_for_dev_replace; |
63 | 76 | ||
77 | #ifdef __BTRFS_NEED_DEVICE_DATA_ORDERED | ||
78 | seqcount_t data_seqcount; | ||
79 | #endif | ||
80 | |||
64 | /* the internal btrfs device id */ | 81 | /* the internal btrfs device id */ |
65 | u64 devid; | 82 | u64 devid; |
66 | 83 | ||
@@ -133,6 +150,73 @@ struct btrfs_device { | |||
133 | atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; | 150 | atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX]; |
134 | }; | 151 | }; |
135 | 152 | ||
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 | |||
136 | struct btrfs_fs_devices { | 220 | struct btrfs_fs_devices { |
137 | u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ | 221 | u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ |
138 | 222 | ||