Skip to content

Commit

Permalink
maple_tree: add mas_next_range() and mas_find_range() interfaces
Browse files Browse the repository at this point in the history
Some users of the maple tree may want to move to the next range in the
tree, even if it stores a NULL.  This family of function provides that
functionality by advancing one slot at a time and returning the result,
while mas_contiguous() will iterate over the range and stop on
encountering the first NULL.

Link: https://lkml.kernel.org/r/20230512182036.359030-29-Liam.Howlett@oracle.com
Signed-off-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Cc: David Binderman <dcb314@hotmail.com>
Cc: Peng Zhang <zhangpeng.00@bytedance.com>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: Vernon Yang <vernon2gm@gmail.com>
Cc: Wei Yang <richard.weiyang@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
howlett authored and akpm00 committed May 16, 2023
1 parent 0eb3562 commit ae8a1cd
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 50 deletions.
15 changes: 15 additions & 0 deletions include/linux/maple_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ void *mas_erase(struct ma_state *mas);
int mas_store_gfp(struct ma_state *mas, void *entry, gfp_t gfp);
void mas_store_prealloc(struct ma_state *mas, void *entry);
void *mas_find(struct ma_state *mas, unsigned long max);
void *mas_find_range(struct ma_state *mas, unsigned long max);
void *mas_find_rev(struct ma_state *mas, unsigned long min);
int mas_preallocate(struct ma_state *mas, gfp_t gfp);
bool mas_is_err(struct ma_state *mas);
Expand All @@ -467,6 +468,7 @@ int mas_expected_entries(struct ma_state *mas, unsigned long nr_entries);

void *mas_prev(struct ma_state *mas, unsigned long min);
void *mas_next(struct ma_state *mas, unsigned long max);
void *mas_next_range(struct ma_state *mas, unsigned long max);

int mas_empty_area(struct ma_state *mas, unsigned long min, unsigned long max,
unsigned long size);
Expand Down Expand Up @@ -528,6 +530,19 @@ static inline void mas_reset(struct ma_state *mas)
#define mas_for_each(__mas, __entry, __max) \
while (((__entry) = mas_find((__mas), (__max))) != NULL)

/**
* mas_contiguous() - Iterate over a contiguous range of the maple tree.
* @__mas: Maple Tree operation state (maple_state)
* @__entry: Entry retrieved from the tree
* @__max: maximum index to retrieve from the tree
*
* When returned, mas->index and mas->last will hold the entire range of the
* entry. The loop will terminate on the first NULL encountered.
*
* Note: may return the zero entry.
*/
#define mas_contiguous(__mas, __entry, __max) \
while (((__entry) = mas_find_range((__mas), (__max))) != NULL)

/**
* mas_set_range() - Set up Maple Tree operation state for a different index.
Expand Down
172 changes: 122 additions & 50 deletions lib/maple_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -4793,13 +4793,10 @@ static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty)
*/
static inline void *mas_next_entry(struct ma_state *mas, unsigned long limit)
{
void *entry = NULL;

if (mas->last >= limit)
return NULL;

entry = mas_next_slot(mas, limit, false);
return entry;
return mas_next_slot(mas, limit, false);
}

/*
Expand Down Expand Up @@ -5880,43 +5877,80 @@ int mas_expected_entries(struct ma_state *mas, unsigned long nr_entries)
}
EXPORT_SYMBOL_GPL(mas_expected_entries);

/**
* mas_next() - Get the next entry.
* @mas: The maple state
* @max: The maximum index to check.
*
* Returns the next entry after @mas->index.
* Must hold rcu_read_lock or the write lock.
* Can return the zero entry.
*
* Return: The next entry or %NULL
*/
void *mas_next(struct ma_state *mas, unsigned long max)
static inline bool mas_next_setup(struct ma_state *mas, unsigned long max,
void **entry)
{
bool was_none = mas_is_none(mas);

if (mas_is_none(mas) || mas_is_paused(mas))
mas->node = MAS_START;

if (mas_is_start(mas))
mas_walk(mas); /* Retries on dead nodes handled by mas_walk */
*entry = mas_walk(mas); /* Retries on dead nodes handled by mas_walk */

if (mas_is_ptr(mas)) {
*entry = NULL;
if (was_none && mas->index == 0) {
mas->index = mas->last = 0;
return mas_root(mas);
return true;
}
mas->index = 1;
mas->last = ULONG_MAX;
mas->node = MAS_NONE;
return NULL;
return true;
}

/* Retries on dead nodes handled by mas_next_entry */
return mas_next_entry(mas, max);
if (mas_is_none(mas))
return true;
return false;
}

/**
* mas_next() - Get the next entry.
* @mas: The maple state
* @max: The maximum index to check.
*
* Returns the next entry after @mas->index.
* Must hold rcu_read_lock or the write lock.
* Can return the zero entry.
*
* Return: The next entry or %NULL
*/
void *mas_next(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;

if (mas_next_setup(mas, max, &entry))
return entry;

/* Retries on dead nodes handled by mas_next_slot */
return mas_next_slot(mas, max, false);
}
EXPORT_SYMBOL_GPL(mas_next);

/**
* mas_next_range() - Advance the maple state to the next range
* @mas: The maple state
* @max: The maximum index to check.
*
* Sets @mas->index and @mas->last to the range.
* Must hold rcu_read_lock or the write lock.
* Can return the zero entry.
*
* Return: The next entry or %NULL
*/
void *mas_next_range(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;

if (mas_next_setup(mas, max, &entry))
return entry;

/* Retries on dead nodes handled by mas_next_slot */
return mas_next_slot(mas, max, true);
}
EXPORT_SYMBOL_GPL(mas_next_range);

/**
* mt_next() - get the next value in the maple tree
* @mt: The maple tree
Expand Down Expand Up @@ -6026,73 +6060,111 @@ void mas_pause(struct ma_state *mas)
EXPORT_SYMBOL_GPL(mas_pause);

/**
* mas_find() - On the first call, find the entry at or after mas->index up to
* %max. Otherwise, find the entry after mas->index.
* mas_find_setup() - Internal function to set up mas_find*().
* @mas: The maple state
* @max: The maximum value to check.
*
* Must hold rcu_read_lock or the write lock.
* If an entry exists, last and index are updated accordingly.
* May set @mas->node to MAS_NONE.
* @max: The maximum index
* @entry: Pointer to the entry
*
* Return: The entry or %NULL.
* Returns: True if entry is the answer, false otherwise.
*/
void *mas_find(struct ma_state *mas, unsigned long max)
static inline bool mas_find_setup(struct ma_state *mas, unsigned long max,
void **entry)
{
*entry = NULL;

if (unlikely(mas_is_none(mas))) {
if (unlikely(mas->last >= max))
return NULL;
return true;

mas->index = mas->last;
mas->node = MAS_START;
}

if (unlikely(mas_is_paused(mas))) {
} else if (unlikely(mas_is_paused(mas))) {
if (unlikely(mas->last >= max))
return NULL;
return true;

mas->node = MAS_START;
mas->index = ++mas->last;
}


if (unlikely(mas_is_ptr(mas)))
} else if (unlikely(mas_is_ptr(mas)))
goto ptr_out_of_range;

if (unlikely(mas_is_start(mas))) {
/* First run or continue */
void *entry;

if (mas->index > max)
return NULL;
return true;

entry = mas_walk(mas);
if (entry)
return entry;
*entry = mas_walk(mas);
if (*entry)
return true;

}

if (unlikely(!mas_searchable(mas))) {
if (unlikely(mas_is_ptr(mas)))
goto ptr_out_of_range;

return NULL;
return true;
}

if (mas->index == max)
return NULL;
return true;

/* Retries on dead nodes handled by mas_next_slot */
return mas_next_slot(mas, max, false);
return false;

ptr_out_of_range:
mas->node = MAS_NONE;
mas->index = 1;
mas->last = ULONG_MAX;
return NULL;
return true;
}

/**
* mas_find() - On the first call, find the entry at or after mas->index up to
* %max. Otherwise, find the entry after mas->index.
* @mas: The maple state
* @max: The maximum value to check.
*
* Must hold rcu_read_lock or the write lock.
* If an entry exists, last and index are updated accordingly.
* May set @mas->node to MAS_NONE.
*
* Return: The entry or %NULL.
*/
void *mas_find(struct ma_state *mas, unsigned long max)
{
void *entry = NULL;

if (mas_find_setup(mas, max, &entry))
return entry;

/* Retries on dead nodes handled by mas_next_slot */
return mas_next_slot(mas, max, false);
}
EXPORT_SYMBOL_GPL(mas_find);

/**
* mas_find_range() - On the first call, find the entry at or after
* mas->index up to %max. Otherwise, advance to the next slot mas->index.
* @mas: The maple state
* @max: The maximum value to check.
*
* Must hold rcu_read_lock or the write lock.
* If an entry exists, last and index are updated accordingly.
* May set @mas->node to MAS_NONE.
*
* Return: The entry or %NULL.
*/
void *mas_find_range(struct ma_state *mas, unsigned long max)
{
void *entry;

if (mas_find_setup(mas, max, &entry))
return entry;

/* Retries on dead nodes handled by mas_next_slot */
return mas_next_slot(mas, max, true);
}
EXPORT_SYMBOL_GPL(mas_find_range);

/**
* mas_find_rev: On the first call, find the first non-null entry at or below
* mas->index down to %min. Otherwise find the first non-null entry below
Expand Down

0 comments on commit ae8a1cd

Please sign in to comment.