Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 606686ee authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason
Browse files

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: default avatarDavid Sterba <dsterba@suse.cz>
Signed-off-by: default avatarJosef Bacik <josef@redhat.com>
parent 17ca04af
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -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
@@ -843,9 +844,10 @@ 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)"
			printk_in_rcu(KERN_INFO "New initial S-block (bdev %p, %s)"
				     " @%llu (%s/%llu/%d)\n",
			       superblock_bdev, device->name,
				     superblock_bdev,
				     rcu_str_deref(device->name),
				     (unsigned long long)dev_bytenr,
				     dev_state->name,
				     (unsigned long long)dev_bytenr,
+6 −4
Original line number Diff line number Diff line
@@ -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);
@@ -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
		 */
@@ -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)) {
+4 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+10 −3
Original line number Diff line number Diff line
@@ -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)
@@ -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);
@@ -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';

fs/btrfs/rcu-string.h

0 → 100644
+56 −0
Original line number Diff line number Diff line
/*
 * 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;						\
})
Loading