diff options
author | Matthew Wilcox (Oracle) <willy@infradead.org> | 2020-03-12 17:29:11 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-04-17 10:50:18 +0200 |
commit | 07378b099139b0ad3fc1e74f549cdca92a0c1feb (patch) | |
tree | 7edc69d8842380e2c6dc883bac5a5349934f7d61 /lib | |
parent | 8f4c8e92bdac564b8c562460a271d3ff11317fe9 (diff) | |
download | linux-rpi-07378b099139b0ad3fc1e74f549cdca92a0c1feb.tar.gz linux-rpi-07378b099139b0ad3fc1e74f549cdca92a0c1feb.tar.bz2 linux-rpi-07378b099139b0ad3fc1e74f549cdca92a0c1feb.zip |
xarray: Fix early termination of xas_for_each_marked
commit 7e934cf5ace1dceeb804f7493fa28bb697ed3c52 upstream.
xas_for_each_marked() is using entry == NULL as a termination condition
of the iteration. When xas_for_each_marked() is used protected only by
RCU, this can however race with xas_store(xas, NULL) in the following
way:
TASK1 TASK2
page_cache_delete() find_get_pages_range_tag()
xas_for_each_marked()
xas_find_marked()
off = xas_find_chunk()
xas_store(&xas, NULL)
xas_init_marks(&xas);
...
rcu_assign_pointer(*slot, NULL);
entry = xa_entry(off);
And thus xas_for_each_marked() terminates prematurely possibly leading
to missed entries in the iteration (translating to missing writeback of
some pages or a similar problem).
If we find a NULL entry that has been marked, skip it (unless we're trying
to allocate an entry).
Reported-by: Jan Kara <jack@suse.cz>
CC: stable@vger.kernel.org
Fixes: ef8e5717db01 ("page cache: Convert delete_batch to XArray")
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/xarray.c | 2 |
1 files changed, 2 insertions, 0 deletions
diff --git a/lib/xarray.c b/lib/xarray.c index 05324cf571f4..08d71c7b7599 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1208,6 +1208,8 @@ void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark) } entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset); + if (!entry && !(xa_track_free(xas->xa) && mark == XA_FREE_MARK)) + continue; if (!xa_is_node(entry)) return entry; xas->xa_node = xa_to_node(entry); |