History

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).

Matthew Wilcox (Oracle) committed 6 years ago

Commit 7e934cf1 parent 34eee836 files · +98 −3Browse files

Showing the changes to include/linux (1 of 6 files). Show all

Unified · Split

include/linux/xarray.h+5−1
@@ -1648,6 +1648,7 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, · 1647 unmodified lines
1648 xa_mark_t mark)1648 xa_mark_t mark)
1649{1649{
1650 struct xa_node *node = xas->xa_node;1650 struct xa_node *node = xas->xa_node;
1651 void *entry;
1651 unsigned int offset;1652 unsigned int offset;
1652 1653
1653 if (unlikely(xas_not_node(node) || node->shift))1654 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, · 5 unmodified lines
1659 return NULL;1660 return NULL;
1660 if (offset == XA_CHUNK_SIZE)1661 if (offset == XA_CHUNK_SIZE)
1661 return xas_find_marked(xas, max, mark);1662 return xas_find_marked(xas, max, mark);
1662 return xa_entry(xas->xa, node, offset);1663 entry = xa_entry(xas->xa, node, offset);
1664 if (!entry)
1665 return xas_find_marked(xas, max, mark);
1666 return entry;
1663}1667}
1664 1668
1665/*1669/*