Skip to content

Commit

Permalink
Btrfs: use rcu to protect device->name
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Josef Bacik authored and chrismason-xx committed Jun 15, 2012
1 parent 17ca04a commit 606686e
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 64 deletions.
16 changes: 9 additions & 7 deletions fs/btrfs/check-integrity.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
#include "print-tree.h"
#include "locking.h"
#include "check-integrity.h"
#include "rcu-string.h"

#define BTRFSIC_BLOCK_HASHTABLE_SIZE 0x10000
#define BTRFSIC_BLOCK_LINK_HASHTABLE_SIZE 0x10000
Expand Down Expand Up @@ -843,13 +844,14 @@ static int btrfsic_process_superblock_dev_mirror(
superblock_tmp->never_written = 0;
superblock_tmp->mirror_num = 1 + superblock_mirror_num;
if (state->print_mask & BTRFSIC_PRINT_MASK_SUPERBLOCK_WRITE)
printk(KERN_INFO "New initial S-block (bdev %p, %s)"
" @%llu (%s/%llu/%d)\n",
superblock_bdev, device->name,
(unsigned long long)dev_bytenr,
dev_state->name,
(unsigned long long)dev_bytenr,
superblock_mirror_num);
printk_in_rcu(KERN_INFO "New initial S-block (bdev %p, %s)"
" @%llu (%s/%llu/%d)\n",
superblock_bdev,
rcu_str_deref(device->name),
(unsigned long long)dev_bytenr,
dev_state->name,
(unsigned long long)dev_bytenr,
superblock_mirror_num);
list_add(&superblock_tmp->all_blocks_node,
&state->all_blocks_list);
btrfsic_block_hashtable_add(superblock_tmp,
Expand Down
10 changes: 6 additions & 4 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "free-space-cache.h"
#include "inode-map.h"
#include "check-integrity.h"
#include "rcu-string.h"

static struct extent_io_ops btree_extent_io_ops;
static void end_workqueue_fn(struct btrfs_work *work);
Expand Down Expand Up @@ -2575,8 +2576,9 @@ static void btrfs_end_buffer_write_sync(struct buffer_head *bh, int uptodate)
struct btrfs_device *device = (struct btrfs_device *)
bh->b_private;

printk_ratelimited(KERN_WARNING "lost page write due to "
"I/O error on %s\n", device->name);
printk_ratelimited_in_rcu(KERN_WARNING "lost page write due to "
"I/O error on %s\n",
rcu_str_deref(device->name));
/* note, we dont' set_buffer_write_io_error because we have
* our own ways of dealing with the IO errors
*/
Expand Down Expand Up @@ -2749,8 +2751,8 @@ static int write_dev_flush(struct btrfs_device *device, int wait)
wait_for_completion(&device->flush_wait);

if (bio_flagged(bio, BIO_EOPNOTSUPP)) {
printk("btrfs: disabling barriers on dev %s\n",
device->name);
printk_in_rcu("btrfs: disabling barriers on dev %s\n",
rcu_str_deref(device->name));
device->nobarriers = 1;
}
if (!bio_flagged(bio, BIO_UPTODATE)) {
Expand Down
7 changes: 4 additions & 3 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "volumes.h"
#include "check-integrity.h"
#include "locking.h"
#include "rcu-string.h"

static struct kmem_cache *extent_state_cache;
static struct kmem_cache *extent_buffer_cache;
Expand Down Expand Up @@ -1917,9 +1918,9 @@ int repair_io_failure(struct btrfs_mapping_tree *map_tree, u64 start,
return -EIO;
}

printk(KERN_INFO "btrfs read error corrected: ino %lu off %llu (dev %s "
"sector %llu)\n", page->mapping->host->i_ino, start,
dev->name, sector);
printk_in_rcu(KERN_INFO "btrfs read error corrected: ino %lu off %llu "
"(dev %s sector %llu)\n", page->mapping->host->i_ino,
start, rcu_str_deref(dev->name), sector);

bio_put(bio);
return 0;
Expand Down
13 changes: 10 additions & 3 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "locking.h"
#include "inode-map.h"
#include "backref.h"
#include "rcu-string.h"

/* Mask out flags that are inappropriate for the given type of inode. */
static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags)
Expand Down Expand Up @@ -1345,8 +1346,9 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
do_div(new_size, root->sectorsize);
new_size *= root->sectorsize;

printk(KERN_INFO "btrfs: new size for %s is %llu\n",
device->name, (unsigned long long)new_size);
printk_in_rcu(KERN_INFO "btrfs: new size for %s is %llu\n",
rcu_str_deref(device->name),
(unsigned long long)new_size);

if (new_size > old_size) {
trans = btrfs_start_transaction(root, 0);
Expand Down Expand Up @@ -2264,7 +2266,12 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)
di_args->total_bytes = dev->total_bytes;
memcpy(di_args->uuid, dev->uuid, sizeof(di_args->uuid));
if (dev->name) {
strncpy(di_args->path, dev->name, sizeof(di_args->path));
struct rcu_string *name;

rcu_read_lock();
name = rcu_dereference(dev->name);
strncpy(di_args->path, name->str, sizeof(di_args->path));
rcu_read_unlock();
di_args->path[sizeof(di_args->path) - 1] = 0;
} else {
di_args->path[0] = '\0';
Expand Down
56 changes: 56 additions & 0 deletions fs/btrfs/rcu-string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2012 Red Hat. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/

struct rcu_string {
struct rcu_head rcu;
char str[0];
};

static inline struct rcu_string *rcu_string_strdup(const char *src, gfp_t mask)
{
size_t len = strlen(src) + 1;
struct rcu_string *ret = kzalloc(sizeof(struct rcu_string) +
(len * sizeof(char)), mask);
if (!ret)
return ret;
strncpy(ret->str, src, len);
return ret;
}

static inline void rcu_string_free(struct rcu_string *str)
{
if (str)
kfree_rcu(str, rcu);
}

#define printk_in_rcu(fmt, ...) do { \
rcu_read_lock(); \
printk(fmt, __VA_ARGS__); \
rcu_read_unlock(); \
} while (0)

#define printk_ratelimited_in_rcu(fmt, ...) do { \
rcu_read_lock(); \
printk_ratelimited(fmt, __VA_ARGS__); \
rcu_read_unlock(); \
} while (0)

#define rcu_str_deref(rcu_str) ({ \
struct rcu_string *__str = rcu_dereference(rcu_str); \
__str->str; \
})
30 changes: 18 additions & 12 deletions fs/btrfs/scrub.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "backref.h"
#include "extent_io.h"
#include "check-integrity.h"
#include "rcu-string.h"

/*
* This is only the first step towards a full-features scrub. It reads all
Expand Down Expand Up @@ -320,10 +321,10 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx)
* hold all of the paths here
*/
for (i = 0; i < ipath->fspath->elem_cnt; ++i)
printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
printk_in_rcu(KERN_WARNING "btrfs: %s at logical %llu on dev "
"%s, sector %llu, root %llu, inode %llu, offset %llu, "
"length %llu, links %u (path: %s)\n", swarn->errstr,
swarn->logical, swarn->dev->name,
swarn->logical, rcu_str_deref(swarn->dev->name),
(unsigned long long)swarn->sector, root, inum, offset,
min(isize - offset, (u64)PAGE_SIZE), nlink,
(char *)(unsigned long)ipath->fspath->val[i]);
Expand All @@ -332,10 +333,10 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx)
return 0;

err:
printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
printk_in_rcu(KERN_WARNING "btrfs: %s at logical %llu on dev "
"%s, sector %llu, root %llu, inode %llu, offset %llu: path "
"resolving failed with ret=%d\n", swarn->errstr,
swarn->logical, swarn->dev->name,
swarn->logical, rcu_str_deref(swarn->dev->name),
(unsigned long long)swarn->sector, root, inum, offset, ret);

free_ipath(ipath);
Expand Down Expand Up @@ -390,10 +391,11 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
do {
ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
&ref_root, &ref_level);
printk(KERN_WARNING
printk_in_rcu(KERN_WARNING
"btrfs: %s at logical %llu on dev %s, "
"sector %llu: metadata %s (level %d) in tree "
"%llu\n", errstr, swarn.logical, dev->name,
"%llu\n", errstr, swarn.logical,
rcu_str_deref(dev->name),
(unsigned long long)swarn.sector,
ref_level ? "node" : "leaf",
ret < 0 ? -1 : ref_level,
Expand Down Expand Up @@ -580,9 +582,11 @@ static void scrub_fixup_nodatasum(struct btrfs_work *work)
spin_lock(&sdev->stat_lock);
++sdev->stat.uncorrectable_errors;
spin_unlock(&sdev->stat_lock);
printk_ratelimited(KERN_ERR

printk_ratelimited_in_rcu(KERN_ERR
"btrfs: unable to fixup (nodatasum) error at logical %llu on dev %s\n",
(unsigned long long)fixup->logical, sdev->dev->name);
(unsigned long long)fixup->logical,
rcu_str_deref(sdev->dev->name));
}

btrfs_free_path(path);
Expand Down Expand Up @@ -936,18 +940,20 @@ static int scrub_handle_errored_block(struct scrub_block *sblock_to_check)
spin_lock(&sdev->stat_lock);
sdev->stat.corrected_errors++;
spin_unlock(&sdev->stat_lock);
printk_ratelimited(KERN_ERR
printk_ratelimited_in_rcu(KERN_ERR
"btrfs: fixed up error at logical %llu on dev %s\n",
(unsigned long long)logical, sdev->dev->name);
(unsigned long long)logical,
rcu_str_deref(sdev->dev->name));
}
} else {
did_not_correct_error:
spin_lock(&sdev->stat_lock);
sdev->stat.uncorrectable_errors++;
spin_unlock(&sdev->stat_lock);
printk_ratelimited(KERN_ERR
printk_ratelimited_in_rcu(KERN_ERR
"btrfs: unable to fixup (regular) error at logical %llu on dev %s\n",
(unsigned long long)logical, sdev->dev->name);
(unsigned long long)logical,
rcu_str_deref(sdev->dev->name));
}

out:
Expand Down
Loading

0 comments on commit 606686e

Please sign in to comment.