diff mbox series

[3/4] btrfs: assert we are holding the reada_lock when releasing a readahead zone

Message ID 6c59a12446b7583172c886bee886d5229f7dccd5.1602499588.git.fdmanana@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: some readhead fixes after removing or replacing a device | expand

Commit Message

Filipe Manana Oct. 12, 2020, 10:55 a.m. UTC
From: Filipe Manana <fdmanana@suse.com>

When we drop the last reference of a zone, we end up releasing it through
the callback reada_zone_release(), which deletes the zone from a device's
reada_zones radix tree. This tree is protected by the global readahead
lock at fs_info->reada_lock. Currently all places that are sure that they
are dropping the last reference on a zone, are calling kref_put() in a
critical section delimited by this lock, while all other places that are
sure they are not dropping the last reference, do not bother calling
kref_put() while holding that lock.

When working on the previous fix for hangs and use-after-frees in the
readahead code, my initial attempts were different and I actually ended
up having reada_zone_release() called when not holding the lock, which
resulted in weird and unexpected problems. So just add an assertion
there to detect such problem more quickly and make the dependency more
obvious.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
---
 fs/btrfs/reada.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

Johannes Thumshirn Oct. 13, 2020, 9:07 a.m. UTC | #1
On 12/10/2020 12:55, fdmanana@kernel.org wrote:
> critical section delimited by this lock, while all other places that are
> sure they are not dropping the last reference, do not bother calling
> kref_put() while holding that lock.

Quick question for me to understand this change, is 'reada_pick_zone' the place
that's certain it doesn't put the last reference?

If yes we should maybe add a comment in there.
Filipe Manana Oct. 13, 2020, 9:15 a.m. UTC | #2
On Tue, Oct 13, 2020 at 10:08 AM Johannes Thumshirn
<Johannes.Thumshirn@wdc.com> wrote:
>
> On 12/10/2020 12:55, fdmanana@kernel.org wrote:
> > critical section delimited by this lock, while all other places that are
> > sure they are not dropping the last reference, do not bother calling
> > kref_put() while holding that lock.
>
> Quick question for me to understand this change, is 'reada_pick_zone' the place
> that's certain it doesn't put the last reference?

All places that don't take the lock before calling kref_put() are the
places that don't drop the last reference, and there are several -
reada_find_extent(), reada_extent_put() and reada_pick_zone() at
least.
As far as I could see, all are correct. The change is motivated
because I had an earlier fix that did the final kref_put() without
holding the lock.

Thanks.


>
> If yes we should maybe add a comment in there.
>
Johannes Thumshirn Oct. 13, 2020, 9:22 a.m. UTC | #3
On 13/10/2020 11:15, Filipe Manana wrote:
> On Tue, Oct 13, 2020 at 10:08 AM Johannes Thumshirn
> <Johannes.Thumshirn@wdc.com> wrote:
>>
>> On 12/10/2020 12:55, fdmanana@kernel.org wrote:
>>> critical section delimited by this lock, while all other places that are
>>> sure they are not dropping the last reference, do not bother calling
>>> kref_put() while holding that lock.
>>
>> Quick question for me to understand this change, is 'reada_pick_zone' the place
>> that's certain it doesn't put the last reference?
> 
> All places that don't take the lock before calling kref_put() are the
> places that don't drop the last reference, and there are several -
> reada_find_extent(), reada_extent_put() and reada_pick_zone() at
> least.
> As far as I could see, all are correct. The change is motivated
> because I had an earlier fix that did the final kref_put() without
> holding the lock.
> 

Am I looking at the wrong tree?

reada_find_extent() takes the lock:
fs/btrfs/reada.c-               spin_lock(&fs_info->reada_lock);
fs/btrfs/reada.c:               kref_put(&zone->refcnt, reada_zone_release);
fs/btrfs/reada.c-               spin_unlock(&fs_info->reada_lock);

and so does reada_extent_put:
fs/btrfs/reada.c-       for (i = 0; i < re->nzones; ++i) {
fs/btrfs/reada.c-               struct reada_zone *zone = re->zones[i];
fs/btrfs/reada.c-
fs/btrfs/reada.c-               kref_get(&zone->refcnt);
fs/btrfs/reada.c-               spin_lock(&zone->lock);
fs/btrfs/reada.c-               --zone->elems;
fs/btrfs/reada.c-               if (zone->elems == 0) {
fs/btrfs/reada.c-                       /* no fs_info->reada_lock needed, as this can't be
fs/btrfs/reada.c-                        * the last ref */
fs/btrfs/reada.c:                       kref_put(&zone->refcnt, reada_zone_release);
fs/btrfs/reada.c-               }
fs/btrfs/reada.c-               spin_unlock(&zone->lock);
fs/btrfs/reada.c-
fs/btrfs/reada.c-               spin_lock(&fs_info->reada_lock);
fs/btrfs/reada.c:               kref_put(&zone->refcnt, reada_zone_release);
fs/btrfs/reada.c-               spin_unlock(&fs_info->reada_lock);
Johannes Thumshirn Oct. 13, 2020, 9:25 a.m. UTC | #4
On 13/10/2020 11:22, Johannes Thumshirn wrote:
> fs/btrfs/reada.c-                       /* no fs_info->reada_lock needed, as this can't be
> fs/btrfs/reada.c-                        * the last ref */
> fs/btrfs/reada.c:                       kref_put(&zone->refcnt, reada_zone_release);
> fs/btrfs/reada.c-               }

I'm sorry, I'm stupid
Johannes Thumshirn Oct. 13, 2020, 9:26 a.m. UTC | #5
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Josef Bacik Oct. 13, 2020, 2:40 p.m. UTC | #6
On 10/12/20 6:55 AM, fdmanana@kernel.org wrote:
> From: Filipe Manana <fdmanana@suse.com>
> 
> When we drop the last reference of a zone, we end up releasing it through
> the callback reada_zone_release(), which deletes the zone from a device's
> reada_zones radix tree. This tree is protected by the global readahead
> lock at fs_info->reada_lock. Currently all places that are sure that they
> are dropping the last reference on a zone, are calling kref_put() in a
> critical section delimited by this lock, while all other places that are
> sure they are not dropping the last reference, do not bother calling
> kref_put() while holding that lock.
> 
> When working on the previous fix for hangs and use-after-frees in the
> readahead code, my initial attempts were different and I actually ended
> up having reada_zone_release() called when not holding the lock, which
> resulted in weird and unexpected problems. So just add an assertion
> there to detect such problem more quickly and make the dependency more
> obvious.
> 
> Signed-off-by: Filipe Manana <fdmanana@suse.com>

Reviewed-by: Josef Bacik <josef@toxicpanda.com>

Thanks,

Josef
diff mbox series

Patch

diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c
index d9a166eb344e..6e33cb755fa5 100644
--- a/fs/btrfs/reada.c
+++ b/fs/btrfs/reada.c
@@ -531,6 +531,8 @@  static void reada_zone_release(struct kref *kref)
 {
 	struct reada_zone *zone = container_of(kref, struct reada_zone, refcnt);
 
+	lockdep_assert_held(&zone->device->fs_info->reada_lock);
+
 	radix_tree_delete(&zone->device->reada_zones,
 			  zone->end >> PAGE_SHIFT);