Skip to content

Commit

Permalink
Merge tag 'xarray-5.0-rc3' of git://git.infradead.org/users/willy/lin…
Browse files Browse the repository at this point in the history
…ux-dax

Pull XArray fixes from Matthew Wilcox:
 "Fix some oversights in the XArray porcelain API:

   - support for m68k's two-byte aligned pointers

   - reserving entries using xa_insert()

   - missing xa_insert_bh() and xa_insert_irq() functions

   - simplify using xa_for_each()

   - use lockdep correctly

   - a few other minor fixes and improvements"

* tag 'xarray-5.0-rc3' of git://git.infradead.org/users/willy/linux-dax:
  XArray: Fix an arithmetic error in xa_is_err
  XArray tests: Check mark 2 gets squashed
  XArray: Fix typo in comment
  XArray: Honour reserved entries in xa_insert
  XArray: Permit storing 2-byte-aligned pointers
  XArray: Change xa_for_each iterator
  XArray: Turn xa_init_flags into a static inline
  XArray tests: Add RCU locking
  • Loading branch information
Linus Torvalds committed Jan 22, 2019
2 parents f8ff6c7 + edcddd4 commit 48b1619
Show file tree
Hide file tree
Showing 4 changed files with 278 additions and 113 deletions.
15 changes: 9 additions & 6 deletions Documentation/core-api/xarray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ some, but not all of the other indices changing.

Sometimes you need to ensure that a subsequent call to :c:func:`xa_store`
will not need to allocate memory. The :c:func:`xa_reserve` function
will store a reserved entry at the indicated index. Users of the normal
API will see this entry as containing ``NULL``. If you do not need to
use the reserved entry, you can call :c:func:`xa_release` to remove the
unused entry. If another user has stored to the entry in the meantime,
:c:func:`xa_release` will do nothing; if instead you want the entry to
become ``NULL``, you should use :c:func:`xa_erase`.
will store a reserved entry at the indicated index. Users of the
normal API will see this entry as containing ``NULL``. If you do
not need to use the reserved entry, you can call :c:func:`xa_release`
to remove the unused entry. If another user has stored to the entry
in the meantime, :c:func:`xa_release` will do nothing; if instead you
want the entry to become ``NULL``, you should use :c:func:`xa_erase`.
Using :c:func:`xa_insert` on a reserved entry will fail.

If all entries in the array are ``NULL``, the :c:func:`xa_empty` function
will return ``true``.
Expand Down Expand Up @@ -183,6 +184,8 @@ Takes xa_lock internally:
* :c:func:`xa_store_bh`
* :c:func:`xa_store_irq`
* :c:func:`xa_insert`
* :c:func:`xa_insert_bh`
* :c:func:`xa_insert_irq`
* :c:func:`xa_erase`
* :c:func:`xa_erase_bh`
* :c:func:`xa_erase_irq`
Expand Down
227 changes: 170 additions & 57 deletions include/linux/xarray.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ static inline bool xa_is_internal(const void *entry)
*/
static inline bool xa_is_err(const void *entry)
{
return unlikely(xa_is_internal(entry));
return unlikely(xa_is_internal(entry) &&
entry >= xa_mk_internal(-MAX_ERRNO));
}

/**
Expand Down Expand Up @@ -286,7 +287,6 @@ struct xarray {
*/
#define DEFINE_XARRAY_ALLOC(name) DEFINE_XARRAY_FLAGS(name, XA_FLAGS_ALLOC)

void xa_init_flags(struct xarray *, gfp_t flags);
void *xa_load(struct xarray *, unsigned long index);
void *xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
void *xa_erase(struct xarray *, unsigned long index);
Expand All @@ -303,6 +303,24 @@ unsigned int xa_extract(struct xarray *, void **dst, unsigned long start,
unsigned long max, unsigned int n, xa_mark_t);
void xa_destroy(struct xarray *);

/**
* xa_init_flags() - Initialise an empty XArray with flags.
* @xa: XArray.
* @flags: XA_FLAG values.
*
* If you need to initialise an XArray with special flags (eg you need
* to take the lock from interrupt context), use this function instead
* of xa_init().
*
* Context: Any context.
*/
static inline void xa_init_flags(struct xarray *xa, gfp_t flags)
{
spin_lock_init(&xa->xa_lock);
xa->xa_flags = flags;
xa->xa_head = NULL;
}

/**
* xa_init() - Initialise an empty XArray.
* @xa: XArray.
Expand Down Expand Up @@ -342,20 +360,45 @@ static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark)
}

/**
* xa_for_each() - Iterate over a portion of an XArray.
* xa_for_each_start() - Iterate over a portion of an XArray.
* @xa: XArray.
* @index: Index of @entry.
* @entry: Entry retrieved from array.
* @start: First index to retrieve from array.
*
* During the iteration, @entry will have the value of the entry stored
* in @xa at @index. You may modify @index during the iteration if you
* want to skip or reprocess indices. It is safe to modify the array
* during the iteration. At the end of the iteration, @entry will be set
* to NULL and @index will have a value less than or equal to max.
*
* xa_for_each_start() is O(n.log(n)) while xas_for_each() is O(n). You have
* to handle your own locking with xas_for_each(), and if you have to unlock
* after each iteration, it will also end up being O(n.log(n)).
* xa_for_each_start() will spin if it hits a retry entry; if you intend to
* see retry entries, you should use the xas_for_each() iterator instead.
* The xas_for_each() iterator will expand into more inline code than
* xa_for_each_start().
*
* Context: Any context. Takes and releases the RCU lock.
*/
#define xa_for_each_start(xa, index, entry, start) \
for (index = start, \
entry = xa_find(xa, &index, ULONG_MAX, XA_PRESENT); \
entry; \
entry = xa_find_after(xa, &index, ULONG_MAX, XA_PRESENT))

/**
* xa_for_each() - Iterate over present entries in an XArray.
* @xa: XArray.
* @index: Index of @entry.
* @max: Maximum index to retrieve from array.
* @filter: Selection criterion.
* @entry: Entry retrieved from array.
*
* Initialise @index to the lowest index you want to retrieve from the
* array. During the iteration, @entry will have the value of the entry
* stored in @xa at @index. The iteration will skip all entries in the
* array which do not match @filter. You may modify @index during the
* iteration if you want to skip or reprocess indices. It is safe to modify
* the array during the iteration. At the end of the iteration, @entry will
* be set to NULL and @index will have a value less than or equal to max.
* During the iteration, @entry will have the value of the entry stored
* in @xa at @index. You may modify @index during the iteration if you want
* to skip or reprocess indices. It is safe to modify the array during the
* iteration. At the end of the iteration, @entry will be set to NULL and
* @index will have a value less than or equal to max.
*
* xa_for_each() is O(n.log(n)) while xas_for_each() is O(n). You have
* to handle your own locking with xas_for_each(), and if you have to unlock
Expand All @@ -366,9 +409,36 @@ static inline bool xa_marked(const struct xarray *xa, xa_mark_t mark)
*
* Context: Any context. Takes and releases the RCU lock.
*/
#define xa_for_each(xa, entry, index, max, filter) \
for (entry = xa_find(xa, &index, max, filter); entry; \
entry = xa_find_after(xa, &index, max, filter))
#define xa_for_each(xa, index, entry) \
xa_for_each_start(xa, index, entry, 0)

/**
* xa_for_each_marked() - Iterate over marked entries in an XArray.
* @xa: XArray.
* @index: Index of @entry.
* @entry: Entry retrieved from array.
* @filter: Selection criterion.
*
* During the iteration, @entry will have the value of the entry stored
* in @xa at @index. The iteration will skip all entries in the array
* which do not match @filter. You may modify @index during the iteration
* if you want to skip or reprocess indices. It is safe to modify the array
* during the iteration. At the end of the iteration, @entry will be set to
* NULL and @index will have a value less than or equal to max.
*
* xa_for_each_marked() is O(n.log(n)) while xas_for_each_marked() is O(n).
* You have to handle your own locking with xas_for_each(), and if you have
* to unlock after each iteration, it will also end up being O(n.log(n)).
* xa_for_each_marked() will spin if it hits a retry entry; if you intend to
* see retry entries, you should use the xas_for_each_marked() iterator
* instead. The xas_for_each_marked() iterator will expand into more inline
* code than xa_for_each_marked().
*
* Context: Any context. Takes and releases the RCU lock.
*/
#define xa_for_each_marked(xa, index, entry, filter) \
for (index = 0, entry = xa_find(xa, &index, ULONG_MAX, filter); \
entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))

#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock)
#define xa_lock(xa) spin_lock(&(xa)->xa_lock)
Expand All @@ -393,39 +463,12 @@ void *__xa_erase(struct xarray *, unsigned long index);
void *__xa_store(struct xarray *, unsigned long index, void *entry, gfp_t);
void *__xa_cmpxchg(struct xarray *, unsigned long index, void *old,
void *entry, gfp_t);
int __xa_insert(struct xarray *, unsigned long index, void *entry, gfp_t);
int __xa_alloc(struct xarray *, u32 *id, u32 max, void *entry, gfp_t);
int __xa_reserve(struct xarray *, unsigned long index, gfp_t);
void __xa_set_mark(struct xarray *, unsigned long index, xa_mark_t);
void __xa_clear_mark(struct xarray *, unsigned long index, xa_mark_t);

/**
* __xa_insert() - Store this entry in the XArray unless another entry is
* already present.
* @xa: XArray.
* @index: Index into array.
* @entry: New entry.
* @gfp: Memory allocation flags.
*
* If you would rather see the existing entry in the array, use __xa_cmpxchg().
* This function is for users who don't care what the entry is, only that
* one is present.
*
* Context: Any context. Expects xa_lock to be held on entry. May
* release and reacquire xa_lock if the @gfp flags permit.
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
* -ENOMEM if memory could not be allocated.
*/
static inline int __xa_insert(struct xarray *xa, unsigned long index,
void *entry, gfp_t gfp)
{
void *curr = __xa_cmpxchg(xa, index, NULL, entry, gfp);
if (!curr)
return 0;
if (xa_is_err(curr))
return xa_err(curr);
return -EEXIST;
}

/**
* xa_store_bh() - Store this entry in the XArray.
* @xa: XArray.
Expand Down Expand Up @@ -453,7 +496,7 @@ static inline void *xa_store_bh(struct xarray *xa, unsigned long index,
}

/**
* xa_store_irq() - Erase this entry from the XArray.
* xa_store_irq() - Store this entry in the XArray.
* @xa: XArray.
* @index: Index into array.
* @entry: New entry.
Expand Down Expand Up @@ -615,24 +658,83 @@ static inline void *xa_cmpxchg_irq(struct xarray *xa, unsigned long index,
* @entry: New entry.
* @gfp: Memory allocation flags.
*
* If you would rather see the existing entry in the array, use xa_cmpxchg().
* This function is for users who don't care what the entry is, only that
* one is present.
* Inserting a NULL entry will store a reserved entry (like xa_reserve())
* if no entry is present. Inserting will fail if a reserved entry is
* present, even though loading from this index will return NULL.
*
* Context: Process context. Takes and releases the xa_lock.
* May sleep if the @gfp flags permit.
* Context: Any context. Takes and releases the xa_lock. May sleep if
* the @gfp flags permit.
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
* -ENOMEM if memory could not be allocated.
*/
static inline int xa_insert(struct xarray *xa, unsigned long index,
void *entry, gfp_t gfp)
{
void *curr = xa_cmpxchg(xa, index, NULL, entry, gfp);
if (!curr)
return 0;
if (xa_is_err(curr))
return xa_err(curr);
return -EEXIST;
int err;

xa_lock(xa);
err = __xa_insert(xa, index, entry, gfp);
xa_unlock(xa);

return err;
}

/**
* xa_insert_bh() - Store this entry in the XArray unless another entry is
* already present.
* @xa: XArray.
* @index: Index into array.
* @entry: New entry.
* @gfp: Memory allocation flags.
*
* Inserting a NULL entry will store a reserved entry (like xa_reserve())
* if no entry is present. Inserting will fail if a reserved entry is
* present, even though loading from this index will return NULL.
*
* Context: Any context. Takes and releases the xa_lock while
* disabling softirqs. May sleep if the @gfp flags permit.
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
* -ENOMEM if memory could not be allocated.
*/
static inline int xa_insert_bh(struct xarray *xa, unsigned long index,
void *entry, gfp_t gfp)
{
int err;

xa_lock_bh(xa);
err = __xa_insert(xa, index, entry, gfp);
xa_unlock_bh(xa);

return err;
}

/**
* xa_insert_irq() - Store this entry in the XArray unless another entry is
* already present.
* @xa: XArray.
* @index: Index into array.
* @entry: New entry.
* @gfp: Memory allocation flags.
*
* Inserting a NULL entry will store a reserved entry (like xa_reserve())
* if no entry is present. Inserting will fail if a reserved entry is
* present, even though loading from this index will return NULL.
*
* Context: Process context. Takes and releases the xa_lock while
* disabling interrupts. May sleep if the @gfp flags permit.
* Return: 0 if the store succeeded. -EEXIST if another entry was present.
* -ENOMEM if memory could not be allocated.
*/
static inline int xa_insert_irq(struct xarray *xa, unsigned long index,
void *entry, gfp_t gfp)
{
int err;

xa_lock_irq(xa);
err = __xa_insert(xa, index, entry, gfp);
xa_unlock_irq(xa);

return err;
}

/**
Expand Down Expand Up @@ -970,8 +1072,8 @@ static inline bool xa_is_sibling(const void *entry)
(entry < xa_mk_sibling(XA_CHUNK_SIZE - 1));
}

#define XA_ZERO_ENTRY xa_mk_internal(256)
#define XA_RETRY_ENTRY xa_mk_internal(257)
#define XA_RETRY_ENTRY xa_mk_internal(256)
#define XA_ZERO_ENTRY xa_mk_internal(257)

/**
* xa_is_zero() - Is the entry a zero entry?
Expand All @@ -995,6 +1097,17 @@ static inline bool xa_is_retry(const void *entry)
return unlikely(entry == XA_RETRY_ENTRY);
}

/**
* xa_is_advanced() - Is the entry only permitted for the advanced API?
* @entry: Entry to be stored in the XArray.
*
* Return: %true if the entry cannot be stored by the normal API.
*/
static inline bool xa_is_advanced(const void *entry)
{
return xa_is_internal(entry) && (entry <= XA_RETRY_ENTRY);
}

/**
* typedef xa_update_node_t - A callback function from the XArray.
* @node: The node which is being processed
Expand Down
Loading

0 comments on commit 48b1619

Please sign in to comment.