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).
Showing the changes to include/linux (1 of 6 files). Show all
Unified · Split
| @@ -1648,6 +1648,7 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, · 1647 unmodified lines | ||
| 1648 | 1648 | xa_mark_t mark) |
| 1649 | 1649 | { |
| 1650 | 1650 | struct xa_node *node = xas->xa_node; |
| 1651 | + void *entry; | |
| 1651 | 1652 | unsigned int offset; |
| 1652 | 1653 | |
| 1653 | 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 | 1660 | return NULL; |
| 1660 | 1661 | if (offset == XA_CHUNK_SIZE) |
| 1661 | 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 | /* |