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 | 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 | /* |