Replace custom device-replace locking with rwsem
David Sterba
maintainercover Replace custom device-replace locking with rwsem
The first cleanup part went to 4.19, the actual switch from the custom locking to rswem was postponed as I found performance degradation. This turned out to be related to VM cache settings, so I'm resending the series again.
The custom locking is based on rwlock protected reader/writer counters, waitqueues, which essentially is what the readwrite semaphore does.
Previous patchset:
https://lore.kernel.org/linux-btrfs/[email protected]/
Patches correspond to 8/11-11/11 and there's no change besides refreshing on top of current misc-next.
David Sterba (4):
btrfs: reada: reorder dev-replace locks before radix tree preload
btrfs: dev-replace: swich locking to rw semaphore
btrfs: dev-replace: remove custom read/write blocking scheme
btrfs: dev-replace: open code trivial locking helpers
fs/btrfs/ctree.h | 4 +- fs/btrfs/dev-replace.c | 97 ++++++++++++++---------------------------- fs/btrfs/dev-replace.h | 5 --- fs/btrfs/disk-io.c | 4 +- fs/btrfs/reada.c | 16 ++++--- fs/btrfs/scrub.c | 15 ++++--- fs/btrfs/volumes.c | 27 ++++++------ 7 files changed, 63 insertions(+), 105 deletions(-)
Signature · 1 line
2.19.1
David Sterba
maintainerPATCH 1/4 btrfs: reada: reorder dev-replace locks before radix tree preload
The read lock is going to use rw semaphore that might sleep, this is not possible in the radix tree preload section. The lock nesting is now:
* device replace
* radix tree preload
* readahead spinlock
--- fs/btrfs/reada.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
fs/btrfs/reada.c +7 -5
@@ -376,26 +376,28 @@ static struct reada_extent *reada_find_extent(struct btrfs_fs_info *fs_info, goto error; } +/* insert extent in reada_tree + all per-device trees, all or nothing */ +btrfs_dev_replace_read_lock (&fs_info->dev_replace); ret =radix_tree_preload (GFP_KERNEL ); -if (ret) +if (ret) { +btrfs_dev_replace_read_unlock (&fs_info->dev_replace); goto error; + } -/* insert extent in reada_tree + all per-device trees, all or nothing */ -btrfs_dev_replace_read_lock (&fs_info->dev_replace);spin_lock (&fs_info->reada_lock); ret =radix_tree_insert (&fs_info->reada_tree, index, re);if (ret == -EEXIST ) { re_exist =radix_tree_lookup (&fs_info->reada_tree, index); re_exist->refcnt++;spin_unlock (&fs_info->reada_lock); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace);radix_tree_preload_end (); +btrfs_dev_replace_read_unlock (&fs_info->dev_replace); goto error; }if (ret) {spin_unlock (&fs_info->reada_lock); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace);radix_tree_preload_end (); +btrfs_dev_replace_read_unlock (&fs_info->dev_replace); goto error; }radix_tree_preload_end ();
Signature · 1 line
2.19.1
David Sterba
maintainerPATCH 2/4 btrfs: dev-replace: swich locking to rw semaphore
In reply to David SterbaThe this is first part of removing the custom locking and waiting scheme used for device replace. It was probably copied from extent buffer locking, but there's nothing that would require more than is provided by the common locking primitives.
The rw spinlock protects waiting tasks counter in case of incompatible locks and the waitqueue. Same as rw semaphore.
This patch only switches the locking primitive, for better bisectability. There should be no functional change other than the overhead of the locking and potential sleeping instead of spinning when the lock is contended.
--- fs/btrfs/ctree.h | 2 +- fs/btrfs/dev-replace.c | 12 ++++++------ fs/btrfs/disk-io.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-)
fs/btrfs/ctree.h +1 -1
@@ -360,7 +360,7 @@ struct btrfs_dev_replace {struct btrfs_device *tgtdev;struct mutex lock_finishing_cancel_unmount; -rwlock_t lock; +struct rw_semaphore rwsem;atomic_t blocking_readers;wait_queue_head_t read_lock_wq;
fs/btrfs/dev-replace.c +6 -6
@@ -1001,12 +1001,12 @@ int btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace)void btrfs_dev_replace_read_lock (struct btrfs_dev_replace *dev_replace) { -read_lock (&dev_replace->lock); +down_read (&dev_replace->rwsem); }void btrfs_dev_replace_read_unlock (struct btrfs_dev_replace *dev_replace) { -read_unlock (&dev_replace->lock); +up_read (&dev_replace->rwsem); }void btrfs_dev_replace_write_lock (struct btrfs_dev_replace *dev_replace)
@@ -1014,16 +1014,16 @@ void btrfs_dev_replace_write_lock(struct btrfs_dev_replace *dev_replace) again:wait_event (dev_replace->read_lock_wq,atomic_read (&dev_replace->blocking_readers) ==0 ); -write_lock (&dev_replace->lock); +down_write (&dev_replace->rwsem);if (atomic_read (&dev_replace->blocking_readers)) { -write_unlock (&dev_replace->lock); +up_write (&dev_replace->rwsem); goto again; } }void btrfs_dev_replace_write_unlock (struct btrfs_dev_replace *dev_replace) { -write_unlock (&dev_replace->lock); +up_write (&dev_replace->rwsem); }/* inc blocking cnt and release read lock */
@@ -1032,7 +1032,7 @@ void btrfs_dev_replace_set_lock_blocking( {/* only set blocking for read lock */ atomic_inc (&dev_replace->blocking_readers); -read_unlock (&dev_replace->lock); +up_read (&dev_replace->rwsem); }void btrfs_bio_counter_inc_noblocked (struct btrfs_fs_info *fs_info)
fs/btrfs/disk-io.c +1 -1
@@ -2119,7 +2119,7 @@ static void btrfs_init_btree_inode(struct btrfs_fs_info *fs_info)static void btrfs_init_dev_replace_locks (struct btrfs_fs_info *fs_info) {mutex_init (&fs_info->dev_replace.lock_finishing_cancel_unmount); -rwlock_init (&fs_info->dev_replace.lock); +init_rwsem (&fs_info->dev_replace.rwsem);atomic_set (&fs_info->dev_replace.blocking_readers,0 );init_waitqueue_head (&fs_info->dev_replace.replace_wait);init_waitqueue_head (&fs_info->dev_replace.read_lock_wq);
Signature · 1 line
2.19.1
David Sterba
maintainerPATCH 3/4 btrfs: dev-replace: remove custom read/write blocking scheme
In reply to David SterbaAfter the rw semaphore has been added, the custom blocking using ::blocking_readers and ::read_lock_wq is redundant.
The blocking logic in __btrfs_map_block is replaced by extending the time the semaphore is held, that has the same blocking effect on writes as the previous custom scheme that waited until ::blocking_readers was zero.
--- fs/btrfs/ctree.h | 2 -- fs/btrfs/dev-replace.c | 16 ---------------- fs/btrfs/dev-replace.h | 1 - fs/btrfs/disk-io.c | 2 -- fs/btrfs/volumes.c | 13 ++++++------- 5 files changed, 6 insertions(+), 28 deletions(-)
fs/btrfs/ctree.h +0 -2
@@ -361,8 +361,6 @@ struct btrfs_dev_replace {struct mutex lock_finishing_cancel_unmount;struct rw_semaphore rwsem; -atomic_t blocking_readers; -wait_queue_head_t read_lock_wq;struct btrfs_scrub_progress scrub_progress;
fs/btrfs/dev-replace.c +0 -16
@@ -1011,14 +1011,7 @@ void btrfs_dev_replace_read_unlock(struct btrfs_dev_replace *dev_replace)void btrfs_dev_replace_write_lock (struct btrfs_dev_replace *dev_replace) { -again: -wait_event (dev_replace->read_lock_wq, -atomic_read (&dev_replace->blocking_readers) ==0 );down_write (&dev_replace->rwsem); -if (atomic_read (&dev_replace->blocking_readers)) { -up_write (&dev_replace->rwsem); - goto again; - } }void btrfs_dev_replace_write_unlock (struct btrfs_dev_replace *dev_replace)
@@ -1026,15 +1019,6 @@ void btrfs_dev_replace_write_unlock(struct btrfs_dev_replace *dev_replace)up_write (&dev_replace->rwsem); } -/* inc blocking cnt and release read lock */ -void btrfs_dev_replace_set_lock_blocking ( -struct btrfs_dev_replace *dev_replace) -{ -/* only set blocking for read lock */ -atomic_inc (&dev_replace->blocking_readers); -up_read (&dev_replace->rwsem); -} -void btrfs_bio_counter_inc_noblocked (struct btrfs_fs_info *fs_info) {percpu_counter_inc (&fs_info->dev_replace.bio_counter);
fs/btrfs/dev-replace.h +0 -1
@@ -23,6 +23,5 @@ void btrfs_dev_replace_read_lock(struct btrfs_dev_replace *dev_replace);void btrfs_dev_replace_read_unlock (struct btrfs_dev_replace *dev_replace);void btrfs_dev_replace_write_lock (struct btrfs_dev_replace *dev_replace);void btrfs_dev_replace_write_unlock (struct btrfs_dev_replace *dev_replace); -void btrfs_dev_replace_set_lock_blocking (struct btrfs_dev_replace *dev_replace);#endif
fs/btrfs/disk-io.c +0 -2
@@ -2120,9 +2120,7 @@ static void btrfs_init_dev_replace_locks(struct btrfs_fs_info *fs_info) {mutex_init (&fs_info->dev_replace.lock_finishing_cancel_unmount);init_rwsem (&fs_info->dev_replace.rwsem); -atomic_set (&fs_info->dev_replace.blocking_readers,0 );init_waitqueue_head (&fs_info->dev_replace.replace_wait); -init_waitqueue_head (&fs_info->dev_replace.read_lock_wq); }static void btrfs_init_qgroup (struct btrfs_fs_info *fs_info)
fs/btrfs/volumes.c +6 -7
@@ -5759,10 +5759,12 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info,btrfs_dev_replace_read_lock (dev_replace); dev_replace_is_ongoing =btrfs_dev_replace_is_ongoing (dev_replace); +/* +* Hold the semaphore for read during the whole operation, write is +* requested at commit time but must wait. +*/ if (!dev_replace_is_ongoing)btrfs_dev_replace_read_unlock (dev_replace); -else -btrfs_dev_replace_set_lock_blocking (dev_replace);if (dev_replace_is_ongoing && mirror_num == map->num_stripes +1 && !need_full_stripe (op) && dev_replace->tgtdev !=NULL ) {
@@ -5957,11 +5959,8 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, } out:if (dev_replace_is_ongoing) { -ASSERT (atomic_read (&dev_replace->blocking_readers) >0 ); -btrfs_dev_replace_read_lock (dev_replace); -/* Barrier implied by atomic_dec_and_test */ -if (atomic_dec_and_test (&dev_replace->blocking_readers)) -cond_wake_up_nomb (&dev_replace->read_lock_wq); +lockdep_assert_held (&dev_replace->rwsem); +/* Unlock and let waiting writers proceed */ btrfs_dev_replace_read_unlock (dev_replace); }free_extent_map (em);
Signature · 1 line
2.19.1
David Sterba
maintainerPATCH 4/4 btrfs: dev-replace: open code trivial locking helpers
In reply to David SterbaThe dev-replace locking functions are now trivial wrappers around rw semaphore that can be used directly everywhere. No functional change.
--- fs/btrfs/dev-replace.c | 81 ++++++++++++++++-------------------------- fs/btrfs/dev-replace.h | 4 --- fs/btrfs/reada.c | 12 +++---- fs/btrfs/scrub.c | 15 ++++---- fs/btrfs/volumes.c | 14 ++++---- 5 files changed, 52 insertions(+), 74 deletions(-)
fs/btrfs/dev-replace.c +31 -50
@@ -284,13 +284,13 @@ int btrfs_run_dev_replace(struct btrfs_trans_handle *trans,struct btrfs_dev_replace_item *ptr;struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; -btrfs_dev_replace_read_lock (dev_replace); +down_read (&dev_replace->rwsem);if (!dev_replace->is_valid || !dev_replace->item_needs_writeback) { -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem);return 0 ; } -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem); key.objectid =0 ; key.type =BTRFS_DEV_REPLACE_KEY ;
@@ -348,7 +348,7 @@ int btrfs_run_dev_replace(struct btrfs_trans_handle *trans, ptr =btrfs_item_ptr (eb, path->slots[0 ], struct btrfs_dev_replace_item); -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem);if (dev_replace->srcdev)btrfs_set_dev_replace_src_devid (eb, ptr, dev_replace->srcdev->devid);
@@ -371,7 +371,7 @@ int btrfs_run_dev_replace(struct btrfs_trans_handle *trans,btrfs_set_dev_replace_cursor_right (eb, ptr, dev_replace->cursor_right); dev_replace->item_needs_writeback =0 ; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);btrfs_mark_buffer_dirty (eb);
@@ -432,7 +432,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, } need_unlock = true; -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem);switch (dev_replace->replace_state) {case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED :case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED :
@@ -470,7 +470,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, dev_replace->item_needs_writeback =1 ;atomic64_set (&dev_replace->num_write_errors,0 );atomic64_set (&dev_replace->num_uncorrectable_read_errors,0 ); -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem); need_unlock = false; ret =btrfs_sysfs_add_device_link (tgt_device->fs_devices, tgt_device);
@@ -484,7 +484,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info,if (IS_ERR (trans)) { ret =PTR_ERR (trans); need_unlock = true; -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem); dev_replace->replace_state =BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED ; dev_replace->srcdev =NULL ;
@@ -511,7 +511,7 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info, leave:if (need_unlock) -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);btrfs_destroy_dev_replace_tgtdev (tgt_device);return ret; }
@@ -579,18 +579,18 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,/* don't allow cancel or unmount to disturb the finishing procedure */ mutex_lock (&dev_replace->lock_finishing_cancel_unmount); -btrfs_dev_replace_read_lock (dev_replace); +down_read (&dev_replace->rwsem);/* was the operation canceled, or is it finished? */ if (dev_replace->replace_state !=BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED ) { -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem);mutex_unlock (&dev_replace->lock_finishing_cancel_unmount);return 0 ; } tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem);/* * flush all outstanding I/O and inode extent mappings before the
@@ -614,7 +614,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,/* keep away write_all_supers() during the finishing procedure */ mutex_lock (&fs_info->fs_devices->device_list_mutex);mutex_lock (&fs_info->chunk_mutex); -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem); dev_replace->replace_state = scrub_ret ?BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED :BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED ;
@@ -634,7 +634,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,btrfs_dev_name (src_device), src_device->devid, rcu_str_deref(tgt_device->name), scrub_ret); -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);mutex_unlock (&fs_info->chunk_mutex);mutex_unlock (&fs_info->fs_devices->device_list_mutex);btrfs_rm_dev_replace_blocked (fs_info);
@@ -670,8 +670,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,list_add (&tgt_device->dev_alloc_list, &fs_info->fs_devices->alloc_list); fs_info->fs_devices->rw_devices++; -btrfs_dev_replace_write_unlock (dev_replace); - +up_write (&dev_replace->rwsem);btrfs_rm_dev_replace_blocked (fs_info);btrfs_rm_dev_replace_remove_srcdev (src_device);
@@ -768,7 +767,7 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, {struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; -btrfs_dev_replace_read_lock (dev_replace); +down_read (&dev_replace->rwsem);/* even if !dev_replace_is_valid, the values are good enough for * the replace_status ioctl */ args->result =BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR ;
@@ -780,7 +779,7 @@ void btrfs_dev_replace_status(struct btrfs_fs_info *fs_info, args->status.num_uncorrectable_read_errors =atomic64_read (&dev_replace->num_uncorrectable_read_errors); args->status.progress_1000 =btrfs_dev_replace_progress (fs_info); -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem); }int btrfs_dev_replace_cancel (struct btrfs_fs_info *fs_info)
@@ -797,18 +796,18 @@ int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info)return -EROFS ;mutex_lock (&dev_replace->lock_finishing_cancel_unmount); -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem);switch (dev_replace->replace_state) {case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED :case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED :case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED : result =BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED ; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);break ;case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED : tgt_device = dev_replace->tgtdev; src_device = dev_replace->srcdev; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem); ret =btrfs_scrub_cancel (fs_info);if (ret <0 ) { result =BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED ;
@@ -839,7 +838,7 @@ int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info) dev_replace->time_stopped =ktime_get_real_seconds (); dev_replace->item_needs_writeback =1 ; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);/* Scrub for replace must not be running in suspended state */ ret =btrfs_scrub_cancel (fs_info);
@@ -874,7 +873,8 @@ void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info)struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;mutex_lock (&dev_replace->lock_finishing_cancel_unmount); -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem); +switch (dev_replace->replace_state) {case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED :case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED :
@@ -890,7 +890,7 @@ void btrfs_dev_replace_suspend_for_unmount(struct btrfs_fs_info *fs_info)break ; } -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);mutex_unlock (&dev_replace->lock_finishing_cancel_unmount); }
@@ -900,12 +900,13 @@ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)struct task_struct *task;struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace; -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem); +switch (dev_replace->replace_state) {case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED :case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED :case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED : -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);return 0 ;case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED :break ;
@@ -921,10 +922,10 @@ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info) "you may cancel the operation after 'mount -o degraded'"); dev_replace->replace_state =BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED ; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);return 0 ; } -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);/* * This could collide with a paused balance, but the exclusive op logic
@@ -932,10 +933,10 @@ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)* dev-replace to start anyway. */ if (test_and_set_bit (BTRFS_FS_EXCL_OP , &fs_info->flags)) { -btrfs_dev_replace_write_lock (dev_replace); +down_write (&dev_replace->rwsem); dev_replace->replace_state =BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED ; -btrfs_dev_replace_write_unlock (dev_replace); +up_write (&dev_replace->rwsem);btrfs_info (fs_info,"cannot resume dev-replace, other exclusive operation running" );return 0 ;
@@ -999,26 +1000,6 @@ int btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace)return 1 ; } -void btrfs_dev_replace_read_lock (struct btrfs_dev_replace *dev_replace) -{ -down_read (&dev_replace->rwsem); -} - -void btrfs_dev_replace_read_unlock (struct btrfs_dev_replace *dev_replace) -{ -up_read (&dev_replace->rwsem); -} - -void btrfs_dev_replace_write_lock (struct btrfs_dev_replace *dev_replace) -{ -down_write (&dev_replace->rwsem); -} - -void btrfs_dev_replace_write_unlock (struct btrfs_dev_replace *dev_replace) -{ -up_write (&dev_replace->rwsem); -} -void btrfs_bio_counter_inc_noblocked (struct btrfs_fs_info *fs_info) {percpu_counter_inc (&fs_info->dev_replace.bio_counter);
fs/btrfs/dev-replace.h +0 -4
@@ -19,9 +19,5 @@ int btrfs_dev_replace_cancel(struct btrfs_fs_info *fs_info);void btrfs_dev_replace_suspend_for_unmount (struct btrfs_fs_info *fs_info);int btrfs_resume_dev_replace_async (struct btrfs_fs_info *fs_info);int btrfs_dev_replace_is_ongoing (struct btrfs_dev_replace *dev_replace); -void btrfs_dev_replace_read_lock (struct btrfs_dev_replace *dev_replace); -void btrfs_dev_replace_read_unlock (struct btrfs_dev_replace *dev_replace); -void btrfs_dev_replace_write_lock (struct btrfs_dev_replace *dev_replace); -void btrfs_dev_replace_write_unlock (struct btrfs_dev_replace *dev_replace);#endif
fs/btrfs/reada.c +6 -6
@@ -377,10 +377,10 @@ static struct reada_extent *reada_find_extent(struct btrfs_fs_info *fs_info, }/* insert extent in reada_tree + all per-device trees, all or nothing */ -btrfs_dev_replace_read_lock (&fs_info->dev_replace); +down_read (&fs_info->dev_replace.rwsem); ret =radix_tree_preload (GFP_KERNEL );if (ret) { -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem); goto error; }
@@ -391,13 +391,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_fs_info *fs_info, re_exist->refcnt++;spin_unlock (&fs_info->reada_lock);radix_tree_preload_end (); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem); goto error; }if (ret) {spin_unlock (&fs_info->reada_lock);radix_tree_preload_end (); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem); goto error; }radix_tree_preload_end ();
@@ -439,13 +439,13 @@ static struct reada_extent *reada_find_extent(struct btrfs_fs_info *fs_info, }radix_tree_delete (&fs_info->reada_tree, index);spin_unlock (&fs_info->reada_lock); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem); goto error; } have_zone =1 ; }spin_unlock (&fs_info->reada_lock); -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem);if (!have_zone) goto error;
fs/btrfs/scrub.c +8 -7
@@ -3596,11 +3596,12 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,break ; } -btrfs_dev_replace_write_lock (&fs_info->dev_replace); +down_write (&fs_info->dev_replace.rwsem); dev_replace->cursor_right = found_key.offset + length; dev_replace->cursor_left = found_key.offset; dev_replace->item_needs_writeback =1 ; -btrfs_dev_replace_write_unlock (&fs_info->dev_replace); +up_write (&dev_replace->rwsem); + ret =scrub_chunk (sctx, scrub_dev, chunk_offset, length, found_key.offset, cache);
@@ -3636,10 +3637,10 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,scrub_pause_off (fs_info); -btrfs_dev_replace_write_lock (&fs_info->dev_replace); +down_write (&fs_info->dev_replace.rwsem); dev_replace->cursor_left = dev_replace->cursor_right; dev_replace->item_needs_writeback =1 ; -btrfs_dev_replace_write_unlock (&fs_info->dev_replace); +up_write (&fs_info->dev_replace.rwsem);if (ro_set)btrfs_dec_block_group_ro (cache);
@@ -3838,16 +3839,16 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start,return -EIO ; } -btrfs_dev_replace_read_lock (&fs_info->dev_replace); +down_read (&fs_info->dev_replace.rwsem);if (dev->scrub_ctx || (!is_dev_replace &&btrfs_dev_replace_is_ongoing (&fs_info->dev_replace))) { -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem);mutex_unlock (&fs_info->scrub_lock);mutex_unlock (&fs_info->fs_devices->device_list_mutex);return -EINPROGRESS ; } -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem); ret =scrub_workers_get (fs_info, is_dev_replace);if (ret) {
fs/btrfs/volumes.c +7 -7
@@ -1898,12 +1898,12 @@ static u64 btrfs_num_devices(struct btrfs_fs_info *fs_info) {u64 num_devices = fs_info->fs_devices->num_devices; -btrfs_dev_replace_read_lock (&fs_info->dev_replace); +down_read (&fs_info->dev_replace.rwsem);if (btrfs_dev_replace_is_ongoing (&fs_info->dev_replace)) {ASSERT (num_devices >1 ); num_devices--; } -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem);return num_devices; }
@@ -5175,11 +5175,11 @@ int btrfs_num_copies(struct btrfs_fs_info *fs_info, u64 logical, u64 len) ret =1 ;free_extent_map (em); -btrfs_dev_replace_read_lock (&fs_info->dev_replace); +down_read (&fs_info->dev_replace.rwsem);if (btrfs_dev_replace_is_ongoing (&fs_info->dev_replace) && fs_info->dev_replace.tgtdev) ret++; -btrfs_dev_replace_read_unlock (&fs_info->dev_replace); +up_read (&fs_info->dev_replace.rwsem);return ret; }
@@ -5757,14 +5757,14 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info,if (!bbio_ret) goto out; -btrfs_dev_replace_read_lock (dev_replace); +down_read (&dev_replace->rwsem); dev_replace_is_ongoing =btrfs_dev_replace_is_ongoing (dev_replace);/* * Hold the semaphore for read during the whole operation, write is * requested at commit time but must wait. */ if (!dev_replace_is_ongoing) -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem);if (dev_replace_is_ongoing && mirror_num == map->num_stripes +1 && !need_full_stripe (op) && dev_replace->tgtdev !=NULL ) {
@@ -5961,7 +5961,7 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info,if (dev_replace_is_ongoing) {lockdep_assert_held (&dev_replace->rwsem);/* Unlock and let waiting writers proceed */ -btrfs_dev_replace_read_unlock (dev_replace); +up_read (&dev_replace->rwsem); }free_extent_map (em);return ret;
Signature · 1 line
2.19.1
Quoted 11 lines · David Sterba
The first cleanup part went to 4.19, the actual switch from the custom locking to rswem was postponed as I found performance degradation. This turned out to be related to VM cache settings, so I'm resending the series again.
The custom locking is based on rwlock protected reader/writer counters, waitqueues, which essentially is what the readwrite semaphore does.
Previous patchset:
https://lore.kernel.org/linux-btrfs/[email protected]/
Quoted 8 lines
Patches correspond to 8/11-11/11 and there's no change besides refreshing on top of current misc-next.
David Sterba (4):
btrfs: reada: reorder dev-replace locks before radix tree preload
btrfs: dev-replace: swich locking to rw semaphore
btrfs: dev-replace: remove custom read/write blocking scheme
btrfs: dev-replace: open code trivial locking helpers
This has been sitting in for-next for some time, no problems reported. If anybody wants to add a review tag, please let me know. I'm going to add the patchset to misc-next soon.