On 11/23/25 14:27, Shardul Bankar wrote:
When collapse_file() fails after xas_create_range() succeeds, the
rollback path does not clean up pre-allocated XArray nodes stored in
xas->xa_alloc. These nodes are allocated by xas_nomem() when
xas_create() fails with GFP_NOWAIT and need to be freed.
The leak occurs because:
1. xas_create_range() may call xas_nomem() which allocates a node
and stores it in xas->xa_alloc
Do you mean that, if xas_create_range() failed, collapse_file() will call xas_nomem() to preallocate memory?
I don't immediately see how xas_create_range() would call xas_nomem().
2. If the collapse operation fails later, the rollback path jumps
to the 'rollback:' label
3. The rollback path cleans up folios but does not call xas_destroy()
to free the pre-allocated nodes in xas->xa_alloc
Note that after we call xas_nomem(), we retry xas_create_range() -- that previously failed to to -ENOMEM.
So the assumption is that the xas_create_range() call would consume that memory.
I'm sure there is some corner case where it is not the case (some concurrent action? not sure)
Someone else can put slots in the xarray since we dropped the lock. I cannot prove this, but surely
disproving this is harder : )
Fix this by calling xas_destroy(&xas) at the beginning of the rollback
path to free any pre-allocated nodes. This is safe because xas_destroy()
only frees nodes in xas->xa_alloc that were never inserted into the
XArray tree.
Shouldn't we just call xas_destroy() in any case, also when everything succeeded?
Yeah you are right. We should probably do