diff options
author | Josef Bacik <josef@redhat.com> | 2012-06-04 14:03:51 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2012-06-14 21:29:16 -0400 |
commit | 606686eeac4550d2212bf3d621a810407ef5e9bf (patch) | |
tree | 50adade3a750137b68304dc280d7a75436417b12 /fs/btrfs | |
parent | 17ca04aff7e6171df684b7b65804df8830eb8c15 (diff) |
Btrfs: use rcu to protect device->name
Al pointed out that we can just toss out the old name on a device and add a
new one arbitrarily, so anybody who uses device->name in printk could
possibly use free'd memory. Instead of adding locking around all of this he
suggested doing it with RCU, so I've introduced a struct rcu_string that
does just that and have gone through and protected all accesses to
device->name that aren't under the uuid_mutex with rcu_read_lock(). This
protects us and I will use it for dealing with removing the device that we
used to mount the file system in a later patch. Thanks,
Reviewed-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Josef Bacik <josef@redhat.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/check-integrity.c | 16 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 10 | ||||
-rw-r--r-- | fs/btrfs/extent_io.c | 7 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 13 | ||||
-rw-r--r-- | fs/btrfs/rcu-string.h | 56 | ||||
-rw-r--r-- | fs/btrfs/scrub.c | 30 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 92 | ||||
-rw-r--r-- | fs/btrfs/volumes.h | 2 |
8 files changed, 162 insertions, 64 deletions
diff --git a/fs/btrfs/check-integrity.c b/fs/btrfs/check-integrity.c index 9cebb1fd6a3c..da6e9364a5e3 100644 --- a/fs/btrfs/check-integrity.c +++ b/fs/btrfs/check-integrity.c | |||
@@ -93,6 +93,7 @@ | |||
93 | #include "print-tree.h" | 93 | #include "print-tree.h" |
94 | #include "locking.h" | 94 | #include "locking.h" |
95 | #include "check-integrity.h" | 95 | #include "check-integrity.h" |
96 | #include "rcu-string.h" | ||
96 | 97 | ||
97 | #define BTRFSIC_BLOCK_HASHTABLE_SIZE 0x10000 | 98 | #define BTRFSIC_BLOCK_HASHTABLE_SIZE 0x10000 |
98 | #define BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE 0x10000 | 99 | #define BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE 0x10000 |
@@ -843,13 +844,14 @@ static int btrfsic_process_superblock_dev_mirror( | |||
843 | superblock_tmp->never_written = 0; | 844 | superblock_tmp->never_written = 0; |
844 | superblock_tmp->mirror_num = 1 + superblock_mirror_num; | 845 | superblock_tmp->mirror_num = 1 + superblock_mirror_num; |
845 | if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE) | 846 | if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE) |
846 | printk(KERN_INFO "New initial S-block (bdev %p, %s)" | 847 | printk_in_rcu(KERN_INFO "New initial S-block (bdev %p, %s)" |
847 | " @%llu (%s/%llu/%d)\n", | 848 | " @%llu (%s/%llu/%d)\n", |
848 | superblock_bdev, device->name, | 849 | superblock_bdev, |
849 | (unsigned long long)dev_bytenr, | 850 | rcu_str_deref(device->name), |
850 | dev_state->name, | 851 | (unsigned long long)dev_bytenr, |
851 | (unsigned long long)dev_bytenr, | 852 | dev_state->name, |
852 | superblock_mirror_num); | 853 | (unsigned long long)dev_bytenr, |
854 | superblock_mirror_num); | ||
853 | list_add(&superblock_tmp->all_blocks_node, | 855 | list_add(&superblock_tmp->all_blocks_node, |
854 | &state->all_blocks_list); | 856 | &state->all_blocks_list); |
855 | btrfsic_block_hashtable_add(superblock_tmp, | 857 | btrfsic_block_hashtable_add(superblock_tmp, |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 5a3bf323e2bd..1c9664b16cd1 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "free-space-cache.h" | 44 | #include "free-space-cache.h" |
45 | #include "inode-map.h" | 45 | #include "inode-map.h" |
46 | #include "check-integrity.h" | 46 | #include "check-integrity.h" |
47 | #include "rcu-string.h" | ||
47 | 48 | ||
48 | static struct extent_io_ops btree_extent_io_ops; | 49 | static struct extent_io_ops btree_extent_io_ops; |
49 | static void end_workqueue_fn(struct btrfs_work *work); | 50 | static void end_workqueue_fn(struct btrfs_work *work); |
@@ -2575,8 +2576,9 @@ static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate) | |||
2575 | struct btrfs_device *device = (struct btrfs_device *) | 2576 | struct btrfs_device *device = (struct btrfs_device *) |
2576 | bh->b_private; | 2577 | bh->b_private; |
2577 | 2578 | ||
2578 | printk_ratelimited(KERN_WARNING "lost page write due to " | 2579 | printk_ratelimited_in_rcu(KERN_WARNING "lost page write due to " |
2579 | "I/O error on %s\n", device->name); | 2580 | "I/O error on %s\n", |
2581 | rcu_str_deref(device->name)); | ||
2580 | /* note, we dont' set_buffer_write_io_error because we have | 2582 | /* note, we dont' set_buffer_write_io_error because we have |
2581 | * our own ways of dealing with the IO errors | 2583 | * our own ways of dealing with the IO errors |
2582 | */ | 2584 | */ |
@@ -2749,8 +2751,8 @@ static int write_dev_flush(struct btrfs_device *device, int wait) | |||
2749 | wait_for_completion(&device->flush_wait); | 2751 | wait_for_completion(&device->flush_wait); |
2750 | 2752 | ||
2751 | if (bio_flagged(bio, BIO_EOPNOTSUPP)) { | 2753 | if (bio_flagged(bio, BIO_EOPNOTSUPP)) { |
2752 | printk("btrfs: disabling barriers on dev %s\n", | 2754 | printk_in_rcu("btrfs: disabling barriers on dev %s\n", |
2753 | device->name); | 2755 | rcu_str_deref(device->name)); |
2754 | device->nobarriers = 1; | 2756 | device->nobarriers = 1; |
2755 | } | 2757 | } |
2756 | if (!bio_flagged(bio, BIO_UPTODATE)) { | 2758 | if (!bio_flagged(bio, BIO_UPTODATE)) { |
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 2c8f7b204617..aaa12c1eb348 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "volumes.h" | 20 | #include "volumes.h" |
21 | #include "check-integrity.h" | 21 | #include "check-integrity.h" |
22 | #include "locking.h" | 22 | #include "locking.h" |
23 | #include "rcu-string.h" | ||
23 | 24 | ||
24 | static struct kmem_cache *extent_state_cache; | 25 | static struct kmem_cache *extent_state_cache; |
25 | static struct kmem_cache *extent_buffer_cache; | 26 | static struct kmem_cache *extent_buffer_cache; |
@@ -1917,9 +1918,9 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start, | |||
1917 | return -EIO; | 1918 | return -EIO; |
1918 | } | 1919 | } |
1919 | 1920 | ||
1920 | printk(KERN_INFO "btrfs read error corrected: ino %lu off %llu (dev %s " | 1921 | printk_in_rcu(KERN_INFO "btrfs read error corrected: ino %lu off %llu " |
1921 | "sector %llu)\n", page->mapping->host->i_ino, start, | 1922 | "(dev %s sector %llu)\n", page->mapping->host->i_ino, |
1922 | dev->name, sector); | 1923 | start, rcu_str_deref(dev->name), sector); |
1923 | 1924 | ||
1924 | bio_put(bio); | 1925 | bio_put(bio); |
1925 | return 0; | 1926 | return 0; |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 24b776c08d99..c5254dde1f0f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -52,6 +52,7 @@ | |||
52 | #include "locking.h" | 52 | #include "locking.h" |
53 | #include "inode-map.h" | 53 | #include "inode-map.h" |
54 | #include "backref.h" | 54 | #include "backref.h" |
55 | #include "rcu-string.h" | ||
55 | 56 | ||
56 | /* Mask out flags that are inappropriate for the given type of inode. */ | 57 | /* Mask out flags that are inappropriate for the given type of inode. */ |
57 | static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) | 58 | static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) |
@@ -1345,8 +1346,9 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root, | |||
1345 | do_div(new_size, root->sectorsize); | 1346 | do_div(new_size, root->sectorsize); |
1346 | new_size *= root->sectorsize; | 1347 | new_size *= root->sectorsize; |
1347 | 1348 | ||
1348 | printk(KERN_INFO "btrfs: new size for %s is %llu\n", | 1349 | printk_in_rcu(KERN_INFO "btrfs: new size for %s is %llu\n", |
1349 | device->name, (unsigned long long)new_size); | 1350 | rcu_str_deref(device->name), |
1351 | (unsigned long long)new_size); | ||
1350 | 1352 | ||
1351 | if (new_size > old_size) { | 1353 | if (new_size > old_size) { |
1352 | trans = btrfs_start_transaction(root, 0); | 1354 | trans = btrfs_start_transaction(root, 0); |
@@ -2264,7 +2266,12 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg) | |||
2264 | di_args->total_bytes = dev->total_bytes; | 2266 | di_args->total_bytes = dev->total_bytes; |
2265 | memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid)); | 2267 | memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid)); |
2266 | if (dev->name) { | 2268 | if (dev->name) { |
2267 | strncpy(di_args->path, dev->name, sizeof(di_args->path)); | 2269 | struct rcu_string *name; |
2270 | |||
2271 | rcu_read_lock(); | ||
2272 | name = rcu_dereference(dev->name); | ||
2273 | strncpy(di_args->path, name->str, sizeof(di_args->path)); | ||
2274 | rcu_read_unlock(); | ||
2268 | di_args->path[sizeof(di_args->path) - 1] = 0; | 2275 | di_args->path[sizeof(di_args->path) - 1] = 0; |
2269 | } else { | 2276 | } else { |
2270 | di_args->path[0] = '\0'; | 2277 | di_args->path[0] = '\0'; |
diff --git a/fs/btrfs/rcu-string.h b/fs/btrfs/rcu-string.h new file mode 100644 index 000000000000..9e111e4576d4 --- /dev/null +++ b/fs/btrfs/rcu-string.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Red Hat. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public | ||
6 | * License v2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | * General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public | ||
14 | * License along with this program; if not, write to the | ||
15 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
16 | * Boston, MA 021110-1307, USA. | ||
17 | */ | ||
18 | |||
19 | struct rcu_string { | ||
20 | struct rcu_head rcu; | ||
21 | char str[0]; | ||
22 | }; | ||
23 | |||
24 | static inline struct rcu_string *rcu_string_strdup(const char *src, gfp_t mask) | ||
25 | { | ||
26 | size_t len = strlen(src) + 1; | ||
27 | struct rcu_string *ret = kzalloc(sizeof(struct rcu_string) + | ||
28 | (len * sizeof(char)), mask); | ||
29 | if (!ret) | ||
30 | return ret; | ||
31 | strncpy(ret->str, src, len); | ||
32 | return ret; | ||
33 | } | ||
34 | |||
35 | static inline void rcu_string_free(struct rcu_string *str) | ||
36 | { | ||
37 | if (str) | ||
38 | kfree_rcu(str, rcu); | ||
39 | } | ||
40 | |||
41 | #define printk_in_rcu(fmt, ...) do { \ | ||
42 | rcu_read_lock(); \ | ||
43 | printk(fmt, __VA_ARGS__); \ | ||
44 | rcu_read_unlock(); \ | ||
45 | } while (0) | ||
46 | |||
47 | #define printk_ratelimited_in_rcu(fmt, ...) do { \ | ||
48 | rcu_read_lock(); \ | ||
49 | printk_ratelimited(fmt, __VA_ARGS__); \ | ||
50 | rcu_read_unlock(); \ | ||
51 | } while (0) | ||
52 | |||
53 | #define rcu_str_deref(rcu_str) ({ \ | ||
54 | struct rcu_string *__str = rcu_dereference(rcu_str); \ | ||
55 | __str->str; \ | ||
56 | }) | ||
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index a38cfa4f251e..b223620cd5a6 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "backref.h" | 26 | #include "backref.h" |
27 | #include "extent_io.h" | 27 | #include "extent_io.h" |
28 | #include "check-integrity.h" | 28 | #include "check-integrity.h" |
29 | #include "rcu-string.h" | ||
29 | 30 | ||
30 | /* | 31 | /* |
31 | * This is only the first step towards a full-features scrub. It reads all | 32 | * This is only the first step towards a full-features scrub. It reads all |
@@ -320,10 +321,10 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx) | |||
320 | * hold all of the paths here | 321 | * hold all of the paths here |
321 | */ | 322 | */ |
322 | for (i = 0; i < ipath->fspath->elem_cnt; ++i) | 323 | for (i = 0; i < ipath->fspath->elem_cnt; ++i) |
323 | printk(KERN_WARNING "btrfs: %s at logical %llu on dev " | 324 | printk_in_rcu(KERN_WARNING "btrfs: %s at logical %llu on dev " |
324 | "%s, sector %llu, root %llu, inode %llu, offset %llu, " | 325 | "%s, sector %llu, root %llu, inode %llu, offset %llu, " |
325 | "length %llu, links %u (path: %s)\n", swarn->errstr, | 326 | "length %llu, links %u (path: %s)\n", swarn->errstr, |
326 | swarn->logical, swarn->dev->name, | 327 | swarn->logical, rcu_str_deref(swarn->dev->name), |
327 | (unsigned long long)swarn->sector, root, inum, offset, | 328 | (unsigned long long)swarn->sector, root, inum, offset, |
328 | min(isize - offset, (u64)PAGE_SIZE), nlink, | 329 | min(isize - offset, (u64)PAGE_SIZE), nlink, |
329 | (char *)(unsigned long)ipath->fspath->val[i]); | 330 | (char *)(unsigned long)ipath->fspath->val[i]); |
@@ -332,10 +333,10 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx) | |||
332 | return 0; | 333 | return 0; |
333 | 334 | ||
334 | err: | 335 | err: |
335 | printk(KERN_WARNING "btrfs: %s at logical %llu on dev " | 336 | printk_in_rcu(KERN_WARNING "btrfs: %s at logical %llu on dev " |
336 | "%s, sector %llu, root %llu, inode %llu, offset %llu: path " | 337 | "%s, sector %llu, root %llu, inode %llu, offset %llu: path " |
337 | "resolving failed with ret=%d\n", swarn->errstr, | 338 | "resolving failed with ret=%d\n", swarn->errstr, |
338 | swarn->logical, swarn->dev->name, | 339 | swarn->logical, rcu_str_deref(swarn->dev->name), |
339 | (unsigned long long)swarn->sector, root, inum, offset, ret); | 340 | (unsigned long long)swarn->sector, root, inum, offset, ret); |
340 | 341 | ||
341 | free_ipath(ipath); | 342 | free_ipath(ipath); |
@@ -390,10 +391,11 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock) | |||
390 | do { | 391 | do { |
391 | ret = tree_backref_for_extent(&ptr, eb, ei, item_size, | 392 | ret = tree_backref_for_extent(&ptr, eb, ei, item_size, |
392 | &ref_root, &ref_level); | 393 | &ref_root, &ref_level); |
393 | printk(KERN_WARNING | 394 | printk_in_rcu(KERN_WARNING |
394 | "btrfs: %s at logical %llu on dev %s, " | 395 | "btrfs: %s at logical %llu on dev %s, " |
395 | "sector %llu: metadata %s (level %d) in tree " | 396 | "sector %llu: metadata %s (level %d) in tree " |
396 | "%llu\n", errstr, swarn.logical, dev->name, | 397 | "%llu\n", errstr, swarn.logical, |
398 | rcu_str_deref(dev->name), | ||
397 | (unsigned long long)swarn.sector, | 399 | (unsigned long long)swarn.sector, |
398 | ref_level ? "node" : "leaf", | 400 | ref_level ? "node" : "leaf", |
399 | ret < 0 ? -1 : ref_level, | 401 | ret < 0 ? -1 : ref_level, |
@@ -580,9 +582,11 @@ out: | |||
580 | spin_lock(&sdev->stat_lock); | 582 | spin_lock(&sdev->stat_lock); |
581 | ++sdev->stat.uncorrectable_errors; | 583 | ++sdev->stat.uncorrectable_errors; |
582 | spin_unlock(&sdev->stat_lock); | 584 | spin_unlock(&sdev->stat_lock); |
583 | printk_ratelimited(KERN_ERR | 585 | |
586 | printk_ratelimited_in_rcu(KERN_ERR | ||
584 | "btrfs: unable to fixup (nodatasum) error at logical %llu on dev %s\n", | 587 | "btrfs: unable to fixup (nodatasum) error at logical %llu on dev %s\n", |
585 | (unsigned long long)fixup->logical, sdev->dev->name); | 588 | (unsigned long long)fixup->logical, |
589 | rcu_str_deref(sdev->dev->name)); | ||
586 | } | 590 | } |
587 | 591 | ||
588 | btrfs_free_path(path); | 592 | btrfs_free_path(path); |
@@ -936,18 +940,20 @@ corrected_error: | |||
936 | spin_lock(&sdev->stat_lock); | 940 | spin_lock(&sdev->stat_lock); |
937 | sdev->stat.corrected_errors++; | 941 | sdev->stat.corrected_errors++; |
938 | spin_unlock(&sdev->stat_lock); | 942 | spin_unlock(&sdev->stat_lock); |
939 | printk_ratelimited(KERN_ERR | 943 | printk_ratelimited_in_rcu(KERN_ERR |
940 | "btrfs: fixed up error at logical %llu on dev %s\n", | 944 | "btrfs: fixed up error at logical %llu on dev %s\n", |
941 | (unsigned long long)logical, sdev->dev->name); | 945 | (unsigned long long)logical, |
946 | rcu_str_deref(sdev->dev->name)); | ||
942 | } | 947 | } |
943 | } else { | 948 | } else { |
944 | did_not_correct_error: | 949 | did_not_correct_error: |
945 | spin_lock(&sdev->stat_lock); | 950 | spin_lock(&sdev->stat_lock); |
946 | sdev->stat.uncorrectable_errors++; | 951 | sdev->stat.uncorrectable_errors++; |
947 | spin_unlock(&sdev->stat_lock); | 952 | spin_unlock(&sdev->stat_lock); |
948 | printk_ratelimited(KERN_ERR | 953 | printk_ratelimited_in_rcu(KERN_ERR |
949 | "btrfs: unable to fixup (regular) error at logical %llu on dev %s\n", | 954 | "btrfs: unable to fixup (regular) error at logical %llu on dev %s\n", |
950 | (unsigned long long)logical, sdev->dev->name); | 955 | (unsigned long long)logical, |
956 | rcu_str_deref(sdev->dev->name)); | ||
951 | } | 957 | } |
952 | 958 | ||
953 | out: | 959 | out: |
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7782020996fe..8a3d2594b807 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include "volumes.h" | 35 | #include "volumes.h" |
36 | #include "async-thread.h" | 36 | #include "async-thread.h" |
37 | #include "check-integrity.h" | 37 | #include "check-integrity.h" |
38 | #include "rcu-string.h" | ||
38 | 39 | ||
39 | static int init_first_rw_device(struct btrfs_trans_handle *trans, | 40 | static int init_first_rw_device(struct btrfs_trans_handle *trans, |
40 | struct btrfs_root *root, | 41 | struct btrfs_root *root, |
@@ -64,7 +65,7 @@ static void free_fs_devices(struct btrfs_fs_devices *fs_devices) | |||
64 | device = list_entry(fs_devices->devices.next, | 65 | device = list_entry(fs_devices->devices.next, |
65 | struct btrfs_device, dev_list); | 66 | struct btrfs_device, dev_list); |
66 | list_del(&device->dev_list); | 67 | list_del(&device->dev_list); |
67 | kfree(device->name); | 68 | rcu_string_free(device->name); |
68 | kfree(device); | 69 | kfree(device); |
69 | } | 70 | } |
70 | kfree(fs_devices); | 71 | kfree(fs_devices); |
@@ -334,8 +335,8 @@ static noinline int device_list_add(const char *path, | |||
334 | { | 335 | { |
335 | struct btrfs_device *device; | 336 | struct btrfs_device *device; |
336 | struct btrfs_fs_devices *fs_devices; | 337 | struct btrfs_fs_devices *fs_devices; |
338 | struct rcu_string *name; | ||
337 | u64 found_transid = btrfs_super_generation(disk_super); | 339 | u64 found_transid = btrfs_super_generation(disk_super); |
338 | char *name; | ||
339 | 340 | ||
340 | fs_devices = find_fsid(disk_super->fsid); | 341 | fs_devices = find_fsid(disk_super->fsid); |
341 | if (!fs_devices) { | 342 | if (!fs_devices) { |
@@ -369,11 +370,13 @@ static noinline int device_list_add(const char *path, | |||
369 | memcpy(device->uuid, disk_super->dev_item.uuid, | 370 | memcpy(device->uuid, disk_super->dev_item.uuid, |
370 | BTRFS_UUID_SIZE); | 371 | BTRFS_UUID_SIZE); |
371 | spin_lock_init(&device->io_lock); | 372 | spin_lock_init(&device->io_lock); |
372 | device->name = kstrdup(path, GFP_NOFS); | 373 | |
373 | if (!device->name) { | 374 | name = rcu_string_strdup(path, GFP_NOFS); |
375 | if (!name) { | ||
374 | kfree(device); | 376 | kfree(device); |
375 | return -ENOMEM; | 377 | return -ENOMEM; |
376 | } | 378 | } |
379 | rcu_assign_pointer(device->name, name); | ||
377 | INIT_LIST_HEAD(&device->dev_alloc_list); | 380 | INIT_LIST_HEAD(&device->dev_alloc_list); |
378 | 381 | ||
379 | /* init readahead state */ | 382 | /* init readahead state */ |
@@ -390,12 +393,12 @@ static noinline int device_list_add(const char *path, | |||
390 | 393 | ||
391 | device->fs_devices = fs_devices; | 394 | device->fs_devices = fs_devices; |
392 | fs_devices->num_devices++; | 395 | fs_devices->num_devices++; |
393 | } else if (!device->name || strcmp(device->name, path)) { | 396 | } else if (!device->name || strcmp(device->name->str, path)) { |
394 | name = kstrdup(path, GFP_NOFS); | 397 | name = rcu_string_strdup(path, GFP_NOFS); |
395 | if (!name) | 398 | if (!name) |
396 | return -ENOMEM; | 399 | return -ENOMEM; |
397 | kfree(device->name); | 400 | rcu_string_free(device->name); |
398 | device->name = name; | 401 | rcu_assign_pointer(device->name, name); |
399 | if (device->missing) { | 402 | if (device->missing) { |
400 | fs_devices->missing_devices--; | 403 | fs_devices->missing_devices--; |
401 | device->missing = 0; | 404 | device->missing = 0; |
@@ -430,15 +433,22 @@ static struct btrfs_fs_devices *clone_fs_devices(struct btrfs_fs_devices *orig) | |||
430 | 433 | ||
431 | /* We have held the volume lock, it is safe to get the devices. */ | 434 | /* We have held the volume lock, it is safe to get the devices. */ |
432 | list_for_each_entry(orig_dev, &orig->devices, dev_list) { | 435 | list_for_each_entry(orig_dev, &orig->devices, dev_list) { |
436 | struct rcu_string *name; | ||
437 | |||
433 | device = kzalloc(sizeof(*device), GFP_NOFS); | 438 | device = kzalloc(sizeof(*device), GFP_NOFS); |
434 | if (!device) | 439 | if (!device) |
435 | goto error; | 440 | goto error; |
436 | 441 | ||
437 | device->name = kstrdup(orig_dev->name, GFP_NOFS); | 442 | /* |
438 | if (!device->name) { | 443 | * This is ok to do without rcu read locked because we hold the |
444 | * uuid mutex so nothing we touch in here is going to disappear. | ||
445 | */ | ||
446 | name = rcu_string_strdup(orig_dev->name->str, GFP_NOFS); | ||
447 | if (!name) { | ||
439 | kfree(device); | 448 | kfree(device); |
440 | goto error; | 449 | goto error; |
441 | } | 450 | } |
451 | rcu_assign_pointer(device->name, name); | ||
442 | 452 | ||
443 | device->devid = orig_dev->devid; | 453 | device->devid = orig_dev->devid; |
444 | device->work.func = pending_bios_fn; | 454 | device->work.func = pending_bios_fn; |
@@ -491,7 +501,7 @@ again: | |||
491 | } | 501 | } |
492 | list_del_init(&device->dev_list); | 502 | list_del_init(&device->dev_list); |
493 | fs_devices->num_devices--; | 503 | fs_devices->num_devices--; |
494 | kfree(device->name); | 504 | rcu_string_free(device->name); |
495 | kfree(device); | 505 | kfree(device); |
496 | } | 506 | } |
497 | 507 | ||
@@ -516,7 +526,7 @@ static void __free_device(struct work_struct *work) | |||
516 | if (device->bdev) | 526 | if (device->bdev) |
517 | blkdev_put(device->bdev, device->mode); | 527 | blkdev_put(device->bdev, device->mode); |
518 | 528 | ||
519 | kfree(device->name); | 529 | rcu_string_free(device->name); |
520 | kfree(device); | 530 | kfree(device); |
521 | } | 531 | } |
522 | 532 | ||
@@ -540,6 +550,7 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) | |||
540 | mutex_lock(&fs_devices->device_list_mutex); | 550 | mutex_lock(&fs_devices->device_list_mutex); |
541 | list_for_each_entry(device, &fs_devices->devices, dev_list) { | 551 | list_for_each_entry(device, &fs_devices->devices, dev_list) { |
542 | struct btrfs_device *new_device; | 552 | struct btrfs_device *new_device; |
553 | struct rcu_string *name; | ||
543 | 554 | ||
544 | if (device->bdev) | 555 | if (device->bdev) |
545 | fs_devices->open_devices--; | 556 | fs_devices->open_devices--; |
@@ -555,8 +566,11 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices) | |||
555 | new_device = kmalloc(sizeof(*new_device), GFP_NOFS); | 566 | new_device = kmalloc(sizeof(*new_device), GFP_NOFS); |
556 | BUG_ON(!new_device); /* -ENOMEM */ | 567 | BUG_ON(!new_device); /* -ENOMEM */ |
557 | memcpy(new_device, device, sizeof(*new_device)); | 568 | memcpy(new_device, device, sizeof(*new_device)); |
558 | new_device->name = kstrdup(device->name, GFP_NOFS); | 569 | |
559 | BUG_ON(device->name && !new_device->name); /* -ENOMEM */ | 570 | /* Safe because we are under uuid_mutex */ |
571 | name = rcu_string_strdup(device->name->str, GFP_NOFS); | ||
572 | BUG_ON(device->name && !name); /* -ENOMEM */ | ||
573 | rcu_assign_pointer(new_device->name, name); | ||
560 | new_device->bdev = NULL; | 574 | new_device->bdev = NULL; |
561 | new_device->writeable = 0; | 575 | new_device->writeable = 0; |
562 | new_device->in_fs_metadata = 0; | 576 | new_device->in_fs_metadata = 0; |
@@ -621,9 +635,9 @@ static int __btrfs_open_devices(struct btrfs_fs_devices *fs_devices, | |||
621 | if (!device->name) | 635 | if (!device->name) |
622 | continue; | 636 | continue; |
623 | 637 | ||
624 | bdev = blkdev_get_by_path(device->name, flags, holder); | 638 | bdev = blkdev_get_by_path(device->name->str, flags, holder); |
625 | if (IS_ERR(bdev)) { | 639 | if (IS_ERR(bdev)) { |
626 | printk(KERN_INFO "open %s failed\n", device->name); | 640 | printk(KERN_INFO "open %s failed\n", device->name->str); |
627 | goto error; | 641 | goto error; |
628 | } | 642 | } |
629 | filemap_write_and_wait(bdev->bd_inode->i_mapping); | 643 | filemap_write_and_wait(bdev->bd_inode->i_mapping); |
@@ -1632,6 +1646,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) | |||
1632 | struct block_device *bdev; | 1646 | struct block_device *bdev; |
1633 | struct list_head *devices; | 1647 | struct list_head *devices; |
1634 | struct super_block *sb = root->fs_info->sb; | 1648 | struct super_block *sb = root->fs_info->sb; |
1649 | struct rcu_string *name; | ||
1635 | u64 total_bytes; | 1650 | u64 total_bytes; |
1636 | int seeding_dev = 0; | 1651 | int seeding_dev = 0; |
1637 | int ret = 0; | 1652 | int ret = 0; |
@@ -1671,23 +1686,24 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) | |||
1671 | goto error; | 1686 | goto error; |
1672 | } | 1687 | } |
1673 | 1688 | ||
1674 | device->name = kstrdup(device_path, GFP_NOFS); | 1689 | name = rcu_string_strdup(device_path, GFP_NOFS); |
1675 | if (!device->name) { | 1690 | if (!name) { |
1676 | kfree(device); | 1691 | kfree(device); |
1677 | ret = -ENOMEM; | 1692 | ret = -ENOMEM; |
1678 | goto error; | 1693 | goto error; |
1679 | } | 1694 | } |
1695 | rcu_assign_pointer(device->name, name); | ||
1680 | 1696 | ||
1681 | ret = find_next_devid(root, &device->devid); | 1697 | ret = find_next_devid(root, &device->devid); |
1682 | if (ret) { | 1698 | if (ret) { |
1683 | kfree(device->name); | 1699 | rcu_string_free(device->name); |
1684 | kfree(device); | 1700 | kfree(device); |
1685 | goto error; | 1701 | goto error; |
1686 | } | 1702 | } |
1687 | 1703 | ||
1688 | trans = btrfs_start_transaction(root, 0); | 1704 | trans = btrfs_start_transaction(root, 0); |
1689 | if (IS_ERR(trans)) { | 1705 | if (IS_ERR(trans)) { |
1690 | kfree(device->name); | 1706 | rcu_string_free(device->name); |
1691 | kfree(device); | 1707 | kfree(device); |
1692 | ret = PTR_ERR(trans); | 1708 | ret = PTR_ERR(trans); |
1693 | goto error; | 1709 | goto error; |
@@ -1796,7 +1812,7 @@ error_trans: | |||
1796 | unlock_chunks(root); | 1812 | unlock_chunks(root); |
1797 | btrfs_abort_transaction(trans, root, ret); | 1813 | btrfs_abort_transaction(trans, root, ret); |
1798 | btrfs_end_transaction(trans, root); | 1814 | btrfs_end_transaction(trans, root); |
1799 | kfree(device->name); | 1815 | rcu_string_free(device->name); |
1800 | kfree(device); | 1816 | kfree(device); |
1801 | error: | 1817 | error: |
1802 | blkdev_put(bdev, FMODE_EXCL); | 1818 | blkdev_put(bdev, FMODE_EXCL); |
@@ -4204,10 +4220,17 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio, | |||
4204 | bio->bi_sector = bbio->stripes[dev_nr].physical >> 9; | 4220 | bio->bi_sector = bbio->stripes[dev_nr].physical >> 9; |
4205 | dev = bbio->stripes[dev_nr].dev; | 4221 | dev = bbio->stripes[dev_nr].dev; |
4206 | if (dev && dev->bdev && (rw != WRITE || dev->writeable)) { | 4222 | if (dev && dev->bdev && (rw != WRITE || dev->writeable)) { |
4223 | #ifdef DEBUG | ||
4224 | struct rcu_string *name; | ||
4225 | |||
4226 | rcu_read_lock(); | ||
4227 | name = rcu_dereference(dev->name); | ||
4207 | pr_debug("btrfs_map_bio: rw %d, secor=%llu, dev=%lu " | 4228 | pr_debug("btrfs_map_bio: rw %d, secor=%llu, dev=%lu " |
4208 | "(%s id %llu), size=%u\n", rw, | 4229 | "(%s id %llu), size=%u\n", rw, |
4209 | (u64)bio->bi_sector, (u_long)dev->bdev->bd_dev, | 4230 | (u64)bio->bi_sector, (u_long)dev->bdev->bd_dev, |
4210 | dev->name, dev->devid, bio->bi_size); | 4231 | name->str, dev->devid, bio->bi_size); |
4232 | rcu_read_unlock(); | ||
4233 | #endif | ||
4211 | bio->bi_bdev = dev->bdev; | 4234 | bio->bi_bdev = dev->bdev; |
4212 | if (async_submit) | 4235 | if (async_submit) |
4213 | schedule_bio(root, dev, rw, bio); | 4236 | schedule_bio(root, dev, rw, bio); |
@@ -4694,8 +4717,9 @@ int btrfs_init_dev_stats(struct btrfs_fs_info *fs_info) | |||
4694 | key.offset = device->devid; | 4717 | key.offset = device->devid; |
4695 | ret = btrfs_search_slot(NULL, dev_root, &key, path, 0, 0); | 4718 | ret = btrfs_search_slot(NULL, dev_root, &key, path, 0, 0); |
4696 | if (ret) { | 4719 | if (ret) { |
4697 | printk(KERN_WARNING "btrfs: no dev_stats entry found for device %s (devid %llu) (OK on first mount after mkfs)\n", | 4720 | printk_in_rcu(KERN_WARNING "btrfs: no dev_stats entry found for device %s (devid %llu) (OK on first mount after mkfs)\n", |
4698 | device->name, (unsigned long long)device->devid); | 4721 | rcu_str_deref(device->name), |
4722 | (unsigned long long)device->devid); | ||
4699 | __btrfs_reset_dev_stats(device); | 4723 | __btrfs_reset_dev_stats(device); |
4700 | device->dev_stats_valid = 1; | 4724 | device->dev_stats_valid = 1; |
4701 | btrfs_release_path(path); | 4725 | btrfs_release_path(path); |
@@ -4747,8 +4771,8 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans, | |||
4747 | BUG_ON(!path); | 4771 | BUG_ON(!path); |
4748 | ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1); | 4772 | ret = btrfs_search_slot(trans, dev_root, &key, path, -1, 1); |
4749 | if (ret < 0) { | 4773 | if (ret < 0) { |
4750 | printk(KERN_WARNING "btrfs: error %d while searching for dev_stats item for device %s!\n", | 4774 | printk_in_rcu(KERN_WARNING "btrfs: error %d while searching for dev_stats item for device %s!\n", |
4751 | ret, device->name); | 4775 | ret, rcu_str_deref(device->name)); |
4752 | goto out; | 4776 | goto out; |
4753 | } | 4777 | } |
4754 | 4778 | ||
@@ -4757,8 +4781,8 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans, | |||
4757 | /* need to delete old one and insert a new one */ | 4781 | /* need to delete old one and insert a new one */ |
4758 | ret = btrfs_del_item(trans, dev_root, path); | 4782 | ret = btrfs_del_item(trans, dev_root, path); |
4759 | if (ret != 0) { | 4783 | if (ret != 0) { |
4760 | printk(KERN_WARNING "btrfs: delete too small dev_stats item for device %s failed %d!\n", | 4784 | printk_in_rcu(KERN_WARNING "btrfs: delete too small dev_stats item for device %s failed %d!\n", |
4761 | device->name, ret); | 4785 | rcu_str_deref(device->name), ret); |
4762 | goto out; | 4786 | goto out; |
4763 | } | 4787 | } |
4764 | ret = 1; | 4788 | ret = 1; |
@@ -4770,8 +4794,8 @@ static int update_dev_stat_item(struct btrfs_trans_handle *trans, | |||
4770 | ret = btrfs_insert_empty_item(trans, dev_root, path, | 4794 | ret = btrfs_insert_empty_item(trans, dev_root, path, |
4771 | &key, sizeof(*ptr)); | 4795 | &key, sizeof(*ptr)); |
4772 | if (ret < 0) { | 4796 | if (ret < 0) { |
4773 | printk(KERN_WARNING "btrfs: insert dev_stats item for device %s failed %d!\n", | 4797 | printk_in_rcu(KERN_WARNING "btrfs: insert dev_stats item for device %s failed %d!\n", |
4774 | device->name, ret); | 4798 | rcu_str_deref(device->name), ret); |
4775 | goto out; | 4799 | goto out; |
4776 | } | 4800 | } |
4777 | } | 4801 | } |
@@ -4823,9 +4847,9 @@ void btrfs_dev_stat_print_on_error(struct btrfs_device *dev) | |||
4823 | { | 4847 | { |
4824 | if (!dev->dev_stats_valid) | 4848 | if (!dev->dev_stats_valid) |
4825 | return; | 4849 | return; |
4826 | printk_ratelimited(KERN_ERR | 4850 | printk_ratelimited_in_rcu(KERN_ERR |
4827 | "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n", | 4851 | "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n", |
4828 | dev->name, | 4852 | rcu_str_deref(dev->name), |
4829 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS), | 4853 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS), |
4830 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS), | 4854 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS), |
4831 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS), | 4855 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS), |
@@ -4837,8 +4861,8 @@ void btrfs_dev_stat_print_on_error(struct btrfs_device *dev) | |||
4837 | 4861 | ||
4838 | static void btrfs_dev_stat_print_on_load(struct btrfs_device *dev) | 4862 | static void btrfs_dev_stat_print_on_load(struct btrfs_device *dev) |
4839 | { | 4863 | { |
4840 | printk(KERN_INFO "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n", | 4864 | printk_in_rcu(KERN_INFO "btrfs: bdev %s errs: wr %u, rd %u, flush %u, corrupt %u, gen %u\n", |
4841 | dev->name, | 4865 | rcu_str_deref(dev->name), |
4842 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS), | 4866 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_WRITE_ERRS), |
4843 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS), | 4867 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_READ_ERRS), |
4844 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS), | 4868 | btrfs_dev_stat_read(dev, BTRFS_DEV_STAT_FLUSH_ERRS), |
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 3406a88ca83e..74366f27a76b 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h | |||
@@ -58,7 +58,7 @@ struct btrfs_device { | |||
58 | /* the mode sent to blkdev_get */ | 58 | /* the mode sent to blkdev_get */ |
59 | fmode_t mode; | 59 | fmode_t mode; |
60 | 60 | ||
61 | char *name; | 61 | struct rcu_string *name; |
62 | 62 | ||
63 | /* the internal btrfs device id */ | 63 | /* the internal btrfs device id */ |
64 | u64 devid; | 64 | u64 devid; |