mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-24 05:01:03 +02:00
xarray: Fix early termination of xas_for_each_marked
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: ef8e5717db
("page cache: Convert delete_batch to XArray")
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
This commit is contained in:
@@ -1648,6 +1648,7 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max,
|
||||
xa_mark_t mark)
|
||||
{
|
||||
struct xa_node *node = xas->xa_node;
|
||||
void *entry;
|
||||
unsigned int offset;
|
||||
|
||||
if (unlikely(xas_not_node(node) || node->shift))
|
||||
@@ -1659,7 +1660,10 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max,
|
||||
return NULL;
|
||||
if (offset == XA_CHUNK_SIZE)
|
||||
return xas_find_marked(xas, max, mark);
|
||||
return xa_entry(xas->xa, node, offset);
|
||||
entry = xa_entry(xas->xa, node, offset);
|
||||
if (!entry)
|
||||
return xas_find_marked(xas, max, mark);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user