mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-17 14:15:58 -04:00
btrfs: avoid taking the device_list_mutex in btrfs_run_dev_stats()
btrfs_run_dev_stats() is called during the critical section of a transaction commit and it takes the device_list_mutex, which is also acquired by fitrim, which does discard operations while holding that mutex. Most of the time, if we are on a healthy filesystem, we don't have new stat updates to persist in the device tree, so blocking on the device_list_mutex is just wasting time and making any tasks that need to start a new transaction wait longer that necessary. Since the device list is RCU safe/protected, make btrfs_run_dev_stats() do an initial check for device stat updates using RCU and quit without taking the device_list_mutex in case there are no new device stats that need to be persisted in the device tree. Also note that adding/removing devices also requires starting a transaction, and since btrfs_run_dev_stats() is called from the critical section of a transaction commit, no one can be concurrently adding or removing a device while btrfs_run_dev_stats() is called. Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
committed by
David Sterba
parent
e0a85137a8
commit
cee4cfd6cc
@@ -8250,6 +8250,36 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans)
|
||||
struct btrfs_device *device;
|
||||
int stats_cnt;
|
||||
int ret = 0;
|
||||
bool need_update_dev_stats = false;
|
||||
|
||||
/*
|
||||
* Do an initial pass using RCU to see if we need to update any dev
|
||||
* stats item. This is to avoid taking the device_list_mutex which is
|
||||
* acquired by the fitrim operation and can take a while since it does
|
||||
* discard operations while holding that mutex. Most of the time, if
|
||||
* we are on a healthy filesystem, we don't have new stat updates, so
|
||||
* this avoids blocking on that mutex, which is specially important
|
||||
* because we are called during the critical section of a transaction
|
||||
* commit, therefore blocking new transactions from starting while
|
||||
* discard is running.
|
||||
*
|
||||
* Also note that adding/removing devices also requires starting a
|
||||
* transaction, and since we are called from the critical section of a
|
||||
* transaction commit, no one can be concurrently adding or removing a
|
||||
* device.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(device, &fs_devices->devices, dev_list) {
|
||||
if (device->dev_stats_valid &&
|
||||
atomic_read(&device->dev_stats_ccnt) != 0) {
|
||||
need_update_dev_stats = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!need_update_dev_stats)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&fs_devices->device_list_mutex);
|
||||
list_for_each_entry(device, &fs_devices->devices, dev_list) {
|
||||
|
||||
Reference in New Issue
Block a user