Commit ea295481 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xarray-5.1-rc1' of git://git.infradead.org/users/willy/linux-dax

Pull XArray updates from Matthew Wilcox:
 "This pull request changes the xa_alloc() API. I'm only aware of one
  subsystem that has started trying to use it, and we agree on the fixup
  as part of the merge.

  The xa_insert() error code also changed to match xa_alloc() (EEXIST to
  EBUSY), and I added xa_alloc_cyclic(). Beyond that, the usual
  bugfixes, optimisations and tweaking.

  I now have a git tree with all users of the radix tree and IDR
  converted over to the XArray that I'll be feeding to maintainers over
  the next few weeks"

* tag 'xarray-5.1-rc1' of git://git.infradead.org/users/willy/linux-dax:
  XArray: Fix xa_reserve for 2-byte aligned entries
  XArray: Fix xa_erase of 2-byte aligned entries
  XArray: Use xa_cmpxchg to implement xa_reserve
  XArray: Fix xa_release in allocating arrays
  XArray: Mark xa_insert and xa_reserve as must_check
  XArray: Add cyclic allocation
  XArray: Redesign xa_alloc API
  XArray: Add support for 1s-based allocation
  XArray: Change xa_insert to return -EBUSY
  XArray: Update xa_erase family descriptions
  XArray tests: RCU lock prohibits GFP_KERNEL
parents f3124ccf 4a5c8d89
...@@ -85,7 +85,7 @@ which was at that index; if it returns the same entry which was passed as ...@@ -85,7 +85,7 @@ which was at that index; if it returns the same entry which was passed as
If you want to only store a new entry to an index if the current entry If you want to only store a new entry to an index if the current entry
at that index is ``NULL``, you can use :c:func:`xa_insert` which at that index is ``NULL``, you can use :c:func:`xa_insert` which
returns ``-EEXIST`` if the entry is not empty. returns ``-EBUSY`` if the entry is not empty.
You can enquire whether a mark is set on an entry by using You can enquire whether a mark is set on an entry by using
:c:func:`xa_get_mark`. If the entry is not ``NULL``, you can set a mark :c:func:`xa_get_mark`. If the entry is not ``NULL``, you can set a mark
...@@ -131,17 +131,23 @@ If you use :c:func:`DEFINE_XARRAY_ALLOC` to define the XArray, or ...@@ -131,17 +131,23 @@ If you use :c:func:`DEFINE_XARRAY_ALLOC` to define the XArray, or
initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`, initialise it by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
the XArray changes to track whether entries are in use or not. the XArray changes to track whether entries are in use or not.
You can call :c:func:`xa_alloc` to store the entry at any unused index You can call :c:func:`xa_alloc` to store the entry at an unused index
in the XArray. If you need to modify the array from interrupt context, in the XArray. If you need to modify the array from interrupt context,
you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable you can use :c:func:`xa_alloc_bh` or :c:func:`xa_alloc_irq` to disable
interrupts while allocating the ID. interrupts while allocating the ID.
Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert` will
will mark the entry as being allocated. Unlike a normal XArray, storing also mark the entry as being allocated. Unlike a normal XArray, storing
``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`. ``NULL`` will mark the entry as being in use, like :c:func:`xa_reserve`.
To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if To free an entry, use :c:func:`xa_erase` (or :c:func:`xa_release` if
you only want to free the entry if it's ``NULL``). you only want to free the entry if it's ``NULL``).
By default, the lowest free entry is allocated starting from 0. If you
want to allocate entries starting at 1, it is more efficient to use
:c:func:`DEFINE_XARRAY_ALLOC1` or ``XA_FLAGS_ALLOC1``. If you want to
allocate IDs up to a maximum, then wrap back around to the lowest free
ID, you can use :c:func:`xa_alloc_cyclic`.
You cannot use ``XA_MARK_0`` with an allocating XArray as this mark You cannot use ``XA_MARK_0`` with an allocating XArray as this mark
is used to track whether an entry is free or not. The other marks are is used to track whether an entry is free or not. The other marks are
available for your use. available for your use.
...@@ -209,7 +215,6 @@ Assumes xa_lock held on entry: ...@@ -209,7 +215,6 @@ Assumes xa_lock held on entry:
* :c:func:`__xa_erase` * :c:func:`__xa_erase`
* :c:func:`__xa_cmpxchg` * :c:func:`__xa_cmpxchg`
* :c:func:`__xa_alloc` * :c:func:`__xa_alloc`
* :c:func:`__xa_reserve`
* :c:func:`__xa_set_mark` * :c:func:`__xa_set_mark`
* :c:func:`__xa_clear_mark` * :c:func:`__xa_clear_mark`
......
...@@ -668,19 +668,10 @@ static int assign_name(struct ib_device *device, const char *name) ...@@ -668,19 +668,10 @@ static int assign_name(struct ib_device *device, const char *name)
} }
strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX); strlcpy(device->name, dev_name(&device->dev), IB_DEVICE_NAME_MAX);
/* Cyclically allocate a user visible ID for the device */ ret = xa_alloc_cyclic(&devices, &device->index, device, xa_limit_31b,
device->index = last_id; &last_id, GFP_KERNEL);
ret = xa_alloc(&devices, &device->index, INT_MAX, device, GFP_KERNEL); if (ret > 0)
if (ret == -ENOSPC) { ret = 0;
device->index = 0;
ret = xa_alloc(&devices, &device->index, INT_MAX, device,
GFP_KERNEL);
}
if (ret)
goto out;
last_id = device->index + 1;
ret = 0;
out: out:
up_write(&devices_rwsem); up_write(&devices_rwsem);
...@@ -1059,14 +1050,15 @@ static int assign_client_id(struct ib_client *client) ...@@ -1059,14 +1050,15 @@ static int assign_client_id(struct ib_client *client)
* to get the LIFO order. The extra linked list can go away if xarray * to get the LIFO order. The extra linked list can go away if xarray
* learns to reverse iterate. * learns to reverse iterate.
*/ */
if (list_empty(&client_list)) if (list_empty(&client_list)) {
client->client_id = 0; client->client_id = 0;
else } else {
client->client_id = struct ib_client *last;
list_last_entry(&client_list, struct ib_client, list)
->client_id; last = list_last_entry(&client_list, struct ib_client, list);
ret = xa_alloc(&clients, &client->client_id, INT_MAX, client, client->client_id = last->client_id + 1;
GFP_KERNEL); }
ret = xa_insert(&clients, client->client_id, client, GFP_KERNEL);
if (ret) if (ret)
goto out; goto out;
......
...@@ -13,28 +13,6 @@ ...@@ -13,28 +13,6 @@
#include "cma_priv.h" #include "cma_priv.h"
#include "restrack.h" #include "restrack.h"
static int rt_xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry,
u32 *next)
{
int err;
*id = *next;
if (*next == U32_MAX)
*id = 0;
xa_lock(xa);
err = __xa_alloc(xa, id, U32_MAX, entry, GFP_KERNEL);
if (err && *next != U32_MAX) {
*id = 0;
err = __xa_alloc(xa, id, *next, entry, GFP_KERNEL);
}
if (!err)
*next = *id + 1;
xa_unlock(xa);
return err;
}
/** /**
* rdma_restrack_init() - initialize and allocate resource tracking * rdma_restrack_init() - initialize and allocate resource tracking
* @dev: IB device * @dev: IB device
...@@ -226,7 +204,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res) ...@@ -226,7 +204,8 @@ static void rdma_restrack_add(struct rdma_restrack_entry *res)
kref_init(&res->kref); kref_init(&res->kref);
init_completion(&res->comp); init_completion(&res->comp);
if (res->type != RDMA_RESTRACK_QP) if (res->type != RDMA_RESTRACK_QP)
ret = rt_xa_alloc_cyclic(&rt->xa, &res->id, res, &rt->next_id); ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b,
&rt->next_id, GFP_KERNEL);
else { else {
/* Special case to ensure that LQPN points to right QP */ /* Special case to ensure that LQPN points to right QP */
struct ib_qp *qp = container_of(res, struct ib_qp, res); struct ib_qp *qp = container_of(res, struct ib_qp, res);
......
...@@ -189,7 +189,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc, ...@@ -189,7 +189,7 @@ int nilfs_btnode_prepare_change_key(struct address_space *btnc,
*/ */
if (!err) if (!err)
return 0; return 0;
else if (err != -EEXIST) else if (err != -EBUSY)
goto failed_unlock; goto failed_unlock;
err = invalidate_inode_pages2_range(btnc, newkey, newkey); err = invalidate_inode_pages2_range(btnc, newkey, newkey);
......
This diff is collapsed.
This diff is collapsed.
...@@ -57,6 +57,11 @@ static inline bool xa_track_free(const struct xarray *xa) ...@@ -57,6 +57,11 @@ static inline bool xa_track_free(const struct xarray *xa)
return xa->xa_flags & XA_FLAGS_TRACK_FREE; return xa->xa_flags & XA_FLAGS_TRACK_FREE;
} }
static inline bool xa_zero_busy(const struct xarray *xa)
{
return xa->xa_flags & XA_FLAGS_ZERO_BUSY;
}
static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark) static inline void xa_mark_set(struct xarray *xa, xa_mark_t mark)
{ {
if (!(xa->xa_flags & XA_FLAGS_MARK(mark))) if (!(xa->xa_flags & XA_FLAGS_MARK(mark)))
...@@ -432,6 +437,8 @@ static void xas_shrink(struct xa_state *xas) ...@@ -432,6 +437,8 @@ static void xas_shrink(struct xa_state *xas)
break; break;
if (!xa_is_node(entry) && node->shift) if (!xa_is_node(entry) && node->shift)
break; break;
if (xa_is_zero(entry) && xa_zero_busy(xa))
entry = NULL;
xas->xa_node = XAS_BOUNDS; xas->xa_node = XAS_BOUNDS;
RCU_INIT_POINTER(xa->xa_head, entry); RCU_INIT_POINTER(xa->xa_head, entry);
...@@ -628,6 +635,8 @@ static void *xas_create(struct xa_state *xas, bool allow_root) ...@@ -628,6 +635,8 @@ static void *xas_create(struct xa_state *xas, bool allow_root)
if (xas_top(node)) { if (xas_top(node)) {
entry = xa_head_locked(xa); entry = xa_head_locked(xa);
xas->xa_node = NULL; xas->xa_node = NULL;
if (!entry && xa_zero_busy(xa))
entry = XA_ZERO_ENTRY;
shift = xas_expand(xas, entry); shift = xas_expand(xas, entry);
if (shift < 0) if (shift < 0)
return NULL; return NULL;
...@@ -758,10 +767,12 @@ void *xas_store(struct xa_state *xas, void *entry) ...@@ -758,10 +767,12 @@ void *xas_store(struct xa_state *xas, void *entry)
void *first, *next; void *first, *next;
bool value = xa_is_value(entry); bool value = xa_is_value(entry);
if (entry) if (entry) {
first = xas_create(xas, !xa_is_node(entry)); bool allow_root = !xa_is_node(entry) && !xa_is_zero(entry);
else first = xas_create(xas, allow_root);
} else {
first = xas_load(xas); first = xas_load(xas);
}
if (xas_invalid(xas)) if (xas_invalid(xas))
return first; return first;
...@@ -791,7 +802,7 @@ void *xas_store(struct xa_state *xas, void *entry) ...@@ -791,7 +802,7 @@ void *xas_store(struct xa_state *xas, void *entry)
* entry is set to NULL. * entry is set to NULL.
*/ */
rcu_assign_pointer(*slot, entry); rcu_assign_pointer(*slot, entry);
if (xa_is_node(next)) if (xa_is_node(next) && (!node || node->shift))
xas_free_nodes(xas, xa_to_node(next)); xas_free_nodes(xas, xa_to_node(next));
if (!node) if (!node)
break; break;
...@@ -1294,13 +1305,12 @@ static void *xas_result(struct xa_state *xas, void *curr) ...@@ -1294,13 +1305,12 @@ static void *xas_result(struct xa_state *xas, void *curr)
* @xa: XArray. * @xa: XArray.
* @index: Index into array. * @index: Index into array.
* *
* If the entry at this index is a multi-index entry then all indices will * After this function returns, loading from @index will return %NULL.
* be erased, and the entry will no longer be a multi-index entry. * If the index is part of a multi-index entry, all indices will be erased
* This function expects the xa_lock to be held on entry. * and none of the entries will be part of a multi-index entry.
* *
* Context: Any context. Expects xa_lock to be held on entry. May * Context: Any context. Expects xa_lock to be held on entry.
* release and reacquire xa_lock if @gfp flags permit. * Return: The entry which used to be at this index.
* Return: The old entry at this index.
*/ */
void *__xa_erase(struct xarray *xa, unsigned long index) void *__xa_erase(struct xarray *xa, unsigned long index)
{ {
...@@ -1314,9 +1324,9 @@ EXPORT_SYMBOL(__xa_erase); ...@@ -1314,9 +1324,9 @@ EXPORT_SYMBOL(__xa_erase);
* @xa: XArray. * @xa: XArray.
* @index: Index of entry. * @index: Index of entry.
* *
* This function is the equivalent of calling xa_store() with %NULL as * After this function returns, loading from @index will return %NULL.
* the third argument. The XArray does not need to allocate memory, so * If the index is part of a multi-index entry, all indices will be erased
* the user does not need to provide GFP flags. * and none of the entries will be part of a multi-index entry.
* *
* Context: Any context. Takes and releases the xa_lock. * Context: Any context. Takes and releases the xa_lock.
* Return: The entry which used to be at this index. * Return: The entry which used to be at this index.
...@@ -1421,16 +1431,12 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index, ...@@ -1421,16 +1431,12 @@ void *__xa_cmpxchg(struct xarray *xa, unsigned long index,
if (WARN_ON_ONCE(xa_is_advanced(entry))) if (WARN_ON_ONCE(xa_is_advanced(entry)))
return XA_ERROR(-EINVAL); return XA_ERROR(-EINVAL);
if (xa_track_free(xa) && !entry)
entry = XA_ZERO_ENTRY;
do { do {
curr = xas_load(&xas); curr = xas_load(&xas);
if (curr == XA_ZERO_ENTRY)
curr = NULL;
if (curr == old) { if (curr == old) {
xas_store(&xas, entry); xas_store(&xas, entry);
if (xa_track_free(xa)) if (xa_track_free(xa) && entry && !curr)
xas_clear_mark(&xas, XA_FREE_MARK); xas_clear_mark(&xas, XA_FREE_MARK);
} }
} while (__xas_nomem(&xas, gfp)); } while (__xas_nomem(&xas, gfp));
...@@ -1452,7 +1458,7 @@ EXPORT_SYMBOL(__xa_cmpxchg); ...@@ -1452,7 +1458,7 @@ EXPORT_SYMBOL(__xa_cmpxchg);
* *
* Context: Any context. Expects xa_lock to be held on entry. May * Context: Any context. Expects xa_lock to be held on entry. May
* release and reacquire xa_lock if @gfp flags permit. * release and reacquire xa_lock if @gfp flags permit.
* Return: 0 if the store succeeded. -EEXIST if another entry was present. * Return: 0 if the store succeeded. -EBUSY if another entry was present.
* -ENOMEM if memory could not be allocated. * -ENOMEM if memory could not be allocated.
*/ */
int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
...@@ -1472,7 +1478,7 @@ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) ...@@ -1472,7 +1478,7 @@ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
if (xa_track_free(xa)) if (xa_track_free(xa))
xas_clear_mark(&xas, XA_FREE_MARK); xas_clear_mark(&xas, XA_FREE_MARK);
} else { } else {
xas_set_err(&xas, -EEXIST); xas_set_err(&xas, -EBUSY);
} }
} while (__xas_nomem(&xas, gfp)); } while (__xas_nomem(&xas, gfp));
...@@ -1480,42 +1486,6 @@ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp) ...@@ -1480,42 +1486,6 @@ int __xa_insert(struct xarray *xa, unsigned long index, void *entry, gfp_t gfp)
} }
EXPORT_SYMBOL(__xa_insert); EXPORT_SYMBOL(__xa_insert);
/**
* __xa_reserve() - Reserve this index in the XArray.
* @xa: XArray.
* @index: Index into array.
* @gfp: Memory allocation flags.
*
* Ensures there is somewhere to store an entry at @index in the array.
* If there is already something stored at @index, this function does
* nothing. If there was nothing there, the entry is marked as reserved.
* Loading from a reserved entry returns a %NULL pointer.
*
* If you do not use the entry that you have reserved, call xa_release()
* or xa_erase() to free any unnecessary memory.
*
* Context: Any context. Expects the xa_lock to be held on entry. May
* release the lock, sleep and reacquire the lock if the @gfp flags permit.
* Return: 0 if the reservation succeeded or -ENOMEM if it failed.
*/
int __xa_reserve(struct xarray *xa, unsigned long index, gfp_t gfp)
{
XA_STATE(xas, xa, index);
void *curr;
do {
curr = xas_load(&xas);
if (!curr) {
xas_store(&xas, XA_ZERO_ENTRY);
if (xa_track_free(xa))
xas_clear_mark(&xas, XA_FREE_MARK);
}
} while (__xas_nomem(&xas, gfp));
return xas_error(&xas);
}
EXPORT_SYMBOL(__xa_reserve);
#ifdef CONFIG_XARRAY_MULTI #ifdef CONFIG_XARRAY_MULTI
static void xas_set_range(struct xa_state *xas, unsigned long first, static void xas_set_range(struct xa_state *xas, unsigned long first,
unsigned long last) unsigned long last)
...@@ -1607,23 +1577,23 @@ EXPORT_SYMBOL(xa_store_range); ...@@ -1607,23 +1577,23 @@ EXPORT_SYMBOL(xa_store_range);
* __xa_alloc() - Find somewhere to store this entry in the XArray. * __xa_alloc() - Find somewhere to store this entry in the XArray.
* @xa: XArray. * @xa: XArray.
* @id: Pointer to ID. * @id: Pointer to ID.
* @max: Maximum ID to allocate (inclusive). * @limit: Range for allocated ID.
* @entry: New entry. * @entry: New entry.
* @gfp: Memory allocation flags. * @gfp: Memory allocation flags.
* *
* Allocates an unused ID in the range specified by @id and @max. * Finds an empty entry in @xa between @limit.min and @limit.max,
* Updates the @id pointer with the index, then stores the entry at that * stores the index into the @id pointer, then stores the entry at
* index. A concurrent lookup will not see an uninitialised @id. * that index. A concurrent lookup will not see an uninitialised @id.
* *
* Context: Any context. Expects xa_lock to be held on entry. May * Context: Any context. Expects xa_lock to be held on entry. May
* release and reacquire xa_lock if @gfp flags permit. * release and reacquire xa_lock if @gfp flags permit.
* Return: 0 on success, -ENOMEM if memory allocation fails or -ENOSPC if * Return: 0 on success, -ENOMEM if memory could not be allocated or
* there is no more space in the XArray. * -EBUSY if there are no free entries in @limit.
*/ */
int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) int __xa_alloc(struct xarray *xa, u32 *id, void *entry,
struct xa_limit limit, gfp_t gfp)
{ {
XA_STATE(xas, xa, 0); XA_STATE(xas, xa, 0);
int err;
if (WARN_ON_ONCE(xa_is_advanced(entry))) if (WARN_ON_ONCE(xa_is_advanced(entry)))
return -EINVAL; return -EINVAL;
...@@ -1634,21 +1604,70 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp) ...@@ -1634,21 +1604,70 @@ int __xa_alloc(struct xarray *xa, u32 *id, u32 max, void *entry, gfp_t gfp)
entry = XA_ZERO_ENTRY; entry = XA_ZERO_ENTRY;
do { do {
xas.xa_index = *id; xas.xa_index = limit.min;
xas_find_marked(&xas, max, XA_FREE_MARK); xas_find_marked(&xas, limit.max, XA_FREE_MARK);
if (xas.xa_node == XAS_RESTART) if (xas.xa_node == XAS_RESTART)
xas_set_err(&xas, -ENOSPC); xas_set_err(&xas, -EBUSY);
else
*id = xas.xa_index;
xas_store(&xas, entry); xas_store(&xas, entry);
xas_clear_mark(&xas, XA_FREE_MARK); xas_clear_mark(&xas, XA_FREE_MARK);
} while (__xas_nomem(&xas, gfp)); } while (__xas_nomem(&xas, gfp));
err = xas_error(&xas); return xas_error(&xas);
if (!err)
*id = xas.xa_index;
return err;
} }
EXPORT_SYMBOL(__xa_alloc); EXPORT_SYMBOL(__xa_alloc);
/**
* __xa_alloc_cyclic() - Find somewhere to store this entry in the XArray.
* @xa: XArray.
* @id: Pointer to ID.
* @entry: New entry.
* @limit: Range of allocated ID.
* @next: Pointer to next ID to allocate.
* @gfp: Memory allocation flags.
*
* Finds an empty entry in @xa between @limit.min and @limit.max,
* stores the index into the @id pointer, then stores the entry at
* that index. A concurrent lookup will not see an uninitialised @id.
* The search for an empty entry will start at @next and will wrap
* around if necessary.
*
* Context: Any context. Expects xa_lock to be held on entry. May
* release and reacquire xa_lock if @gfp flags permit.
* Return: 0 if the allocation succeeded without wrapping. 1 if the
* allocation succeeded after wrapping, -ENOMEM if memory could not be
* allocated or -EBUSY if there are no free entries in @limit.
*/
int __xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry,
struct xa_limit limit, u32 *next, gfp_t gfp)
{
u32 min = limit.min;
int ret;
limit.min = max(min, *next);
ret = __xa_alloc(xa, id, entry, limit, gfp);
if ((xa->xa_flags & XA_FLAGS_ALLOC_WRAPPED) && ret == 0) {
xa->xa_flags &= ~XA_FLAGS_ALLOC_WRAPPED;
ret = 1;
}
if (ret < 0 && limit.min > min) {
limit.min = min;
ret = __xa_alloc(xa, id, entry, limit, gfp);
if (ret == 0)
ret = 1;
}
if (ret >= 0) {
*next = *id + 1;
if (*next == 0)
xa->xa_flags |= XA_FLAGS_ALLOC_WRAPPED;
}
return ret;
}
EXPORT_SYMBOL(__xa_alloc_cyclic);
/** /**
* __xa_set_mark() - Set this mark on this entry while locked. * __xa_set_mark() - Set this mark on this entry while locked.
* @xa: XArray. * @xa: XArray.
...@@ -1943,6 +1962,8 @@ void xa_destroy(struct xarray *xa) ...@@ -1943,6 +1962,8 @@ void xa_destroy(struct xarray *xa)
entry = xa_head_locked(xa); entry = xa_head_locked(xa);
RCU_INIT_POINTER(xa->xa_head, NULL); RCU_INIT_POINTER(xa->xa_head, NULL);
xas_init_marks(&xas); xas_init_marks(&xas);
if (xa_zero_busy(xa))
xa_mark_clear(xa, XA_FREE_MARK);
/* lockdep checks we're still holding the lock in xas_free_nodes() */ /* lockdep checks we're still holding the lock in xas_free_nodes() */
if (xa_is_node(entry)) if (xa_is_node(entry))
xas_free_nodes(&xas, xa_to_node(entry)); xas_free_nodes(&xas, xa_to_node(entry));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment