Skip to content

Commit

Permalink
btrfs: use RCU in btrfs_show_devname for device list traversal
Browse files Browse the repository at this point in the history
The show_devname callback is used to print device name in
/proc/self/mounts, we need to traverse the device list consistently and
read the name that's copied to a seq buffer so we don't need further
locking.

If the first device is being deleted at the same time, the RCU will
allow us to read the device name, though it will become stale right
after the RCU protection ends. This is unavoidable and the user can
expect that the device will disappear from the filesystem's list at some
point.

The device_list_mutex was pretty heavy as it is used eg. for writing
superblock and a few other IO related contexts. This can stall any
application that reads the proc file for no reason.

Reviewed-by: Anand Jain <anand.jain@oracle.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
David Sterba committed Mar 31, 2018
1 parent d198013 commit 88c1459
Showing 1 changed file with 10 additions and 5 deletions.
15 changes: 10 additions & 5 deletions fs/btrfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2304,11 +2304,18 @@ static int btrfs_show_devname(struct seq_file *m, struct dentry *root)
struct list_head *head;
struct rcu_string *name;

mutex_lock(&fs_info->fs_devices->device_list_mutex);
/*
* Lightweight locking of the devices. We should not need
* device_list_mutex here as we only read the device data and the list
* is protected by RCU. Even if a device is deleted during the list
* traversals, we'll get valid data, the freeing callback will wait at
* least until until the rcu_read_unlock.
*/
rcu_read_lock();
cur_devices = fs_info->fs_devices;
while (cur_devices) {
head = &cur_devices->devices;
list_for_each_entry(dev, head, dev_list) {
list_for_each_entry_rcu(dev, head, dev_list) {
if (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state))
continue;
if (!dev->name)
Expand All @@ -2320,14 +2327,12 @@ static int btrfs_show_devname(struct seq_file *m, struct dentry *root)
}

if (first_dev) {
rcu_read_lock();
name = rcu_dereference(first_dev->name);
seq_escape(m, name->str, " \t\n\\");
rcu_read_unlock();
} else {
WARN_ON(1);
}
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
rcu_read_unlock();
return 0;
}

Expand Down

0 comments on commit 88c1459

Please sign in to comment.