xarray: Replace exceptional entries
Introduce xarray value entries and tagged pointers to replace radix tree exceptional entries. This is a slight change in encoding to allow the use of an extra bit (we can now store BITS_PER_LONG - 1 bits in a value entry). It is also a change in emphasis; exceptional entries are intimidating and different. As the comment explains, you can choose to store values or pointers in the xarray and they are both first-class citizens. Signed-off-by: Matthew Wilcox <willy@infradead.org> Reviewed-by: Josef Bacik <jbacik@fb.com>
This commit is contained in:
parent
66ee620f06
commit
3159f943aa
26 changed files with 278 additions and 232 deletions
|
|
@ -28,34 +28,26 @@
|
|||
#include <linux/rcupdate.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
/*
|
||||
* The bottom two bits of the slot determine how the remaining bits in the
|
||||
* slot are interpreted:
|
||||
*
|
||||
* 00 - data pointer
|
||||
* 01 - internal entry
|
||||
* 10 - exceptional entry
|
||||
* 11 - this bit combination is currently unused/reserved
|
||||
* 10 - internal entry
|
||||
* x1 - value entry
|
||||
*
|
||||
* The internal entry may be a pointer to the next level in the tree, a
|
||||
* sibling entry, or an indicator that the entry in this slot has been moved
|
||||
* to another location in the tree and the lookup should be restarted. While
|
||||
* NULL fits the 'data pointer' pattern, it means that there is no entry in
|
||||
* the tree for this index (no matter what level of the tree it is found at).
|
||||
* This means that you cannot store NULL in the tree as a value for the index.
|
||||
* This means that storing a NULL entry in the tree is the same as deleting
|
||||
* the entry from the tree.
|
||||
*/
|
||||
#define RADIX_TREE_ENTRY_MASK 3UL
|
||||
#define RADIX_TREE_INTERNAL_NODE 1UL
|
||||
|
||||
/*
|
||||
* Most users of the radix tree store pointers but shmem/tmpfs stores swap
|
||||
* entries in the same tree. They are marked as exceptional entries to
|
||||
* distinguish them from pointers to struct page.
|
||||
* EXCEPTIONAL_ENTRY tests the bit, EXCEPTIONAL_SHIFT shifts content past it.
|
||||
*/
|
||||
#define RADIX_TREE_EXCEPTIONAL_ENTRY 2
|
||||
#define RADIX_TREE_EXCEPTIONAL_SHIFT 2
|
||||
#define RADIX_TREE_INTERNAL_NODE 2UL
|
||||
|
||||
static inline bool radix_tree_is_internal_node(void *ptr)
|
||||
{
|
||||
|
|
@ -83,11 +75,10 @@ static inline bool radix_tree_is_internal_node(void *ptr)
|
|||
|
||||
/*
|
||||
* @count is the count of every non-NULL element in the ->slots array
|
||||
* whether that is an exceptional entry, a retry entry, a user pointer,
|
||||
* whether that is a value entry, a retry entry, a user pointer,
|
||||
* a sibling entry or a pointer to the next level of the tree.
|
||||
* @exceptional is the count of every element in ->slots which is
|
||||
* either radix_tree_exceptional_entry() or is a sibling entry for an
|
||||
* exceptional entry.
|
||||
* either a value entry or a sibling of a value entry.
|
||||
*/
|
||||
struct radix_tree_node {
|
||||
unsigned char shift; /* Bits remaining in each slot */
|
||||
|
|
@ -268,17 +259,6 @@ static inline int radix_tree_deref_retry(void *arg)
|
|||
return unlikely(radix_tree_is_internal_node(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
* radix_tree_exceptional_entry - radix_tree_deref_slot gave exceptional entry?
|
||||
* @arg: value returned by radix_tree_deref_slot
|
||||
* Returns: 0 if well-aligned pointer, non-0 if exceptional entry.
|
||||
*/
|
||||
static inline int radix_tree_exceptional_entry(void *arg)
|
||||
{
|
||||
/* Not unlikely because radix_tree_exception often tested first */
|
||||
return (unsigned long)arg & RADIX_TREE_EXCEPTIONAL_ENTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* radix_tree_exception - radix_tree_deref_slot returned either exception?
|
||||
* @arg: value returned by radix_tree_deref_slot
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@
|
|||
*
|
||||
* swp_entry_t's are *never* stored anywhere in their arch-dependent format.
|
||||
*/
|
||||
#define SWP_TYPE_SHIFT(e) ((sizeof(e.val) * 8) - \
|
||||
(MAX_SWAPFILES_SHIFT + RADIX_TREE_EXCEPTIONAL_SHIFT))
|
||||
#define SWP_OFFSET_MASK(e) ((1UL << SWP_TYPE_SHIFT(e)) - 1)
|
||||
#define SWP_TYPE_SHIFT (BITS_PER_XA_VALUE - MAX_SWAPFILES_SHIFT)
|
||||
#define SWP_OFFSET_MASK ((1UL << SWP_TYPE_SHIFT) - 1)
|
||||
|
||||
/*
|
||||
* Store a type+offset into a swp_entry_t in an arch-independent format
|
||||
|
|
@ -29,8 +28,7 @@ static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset)
|
|||
{
|
||||
swp_entry_t ret;
|
||||
|
||||
ret.val = (type << SWP_TYPE_SHIFT(ret)) |
|
||||
(offset & SWP_OFFSET_MASK(ret));
|
||||
ret.val = (type << SWP_TYPE_SHIFT) | (offset & SWP_OFFSET_MASK);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +38,7 @@ static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset)
|
|||
*/
|
||||
static inline unsigned swp_type(swp_entry_t entry)
|
||||
{
|
||||
return (entry.val >> SWP_TYPE_SHIFT(entry));
|
||||
return (entry.val >> SWP_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -49,7 +47,7 @@ static inline unsigned swp_type(swp_entry_t entry)
|
|||
*/
|
||||
static inline pgoff_t swp_offset(swp_entry_t entry)
|
||||
{
|
||||
return entry.val & SWP_OFFSET_MASK(entry);
|
||||
return entry.val & SWP_OFFSET_MASK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
|
|
@ -90,16 +88,13 @@ static inline swp_entry_t radix_to_swp_entry(void *arg)
|
|||
{
|
||||
swp_entry_t entry;
|
||||
|
||||
entry.val = (unsigned long)arg >> RADIX_TREE_EXCEPTIONAL_SHIFT;
|
||||
entry.val = xa_to_value(arg);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static inline void *swp_to_radix_entry(swp_entry_t entry)
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
value = entry.val << RADIX_TREE_EXCEPTIONAL_SHIFT;
|
||||
return (void *)(value | RADIX_TREE_EXCEPTIONAL_ENTRY);
|
||||
return xa_mk_value(entry.val);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEVICE_PRIVATE)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,111 @@
|
|||
* eXtensible Arrays
|
||||
* Copyright (c) 2017 Microsoft Corporation
|
||||
* Author: Matthew Wilcox <willy@infradead.org>
|
||||
*
|
||||
* See Documentation/core-api/xarray.rst for how to use the XArray.
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* The bottom two bits of the entry determine how the XArray interprets
|
||||
* the contents:
|
||||
*
|
||||
* 00: Pointer entry
|
||||
* 10: Internal entry
|
||||
* x1: Value entry or tagged pointer
|
||||
*
|
||||
* Attempting to store internal entries in the XArray is a bug.
|
||||
*/
|
||||
|
||||
#define BITS_PER_XA_VALUE (BITS_PER_LONG - 1)
|
||||
|
||||
/**
|
||||
* xa_mk_value() - Create an XArray entry from an integer.
|
||||
* @v: Value to store in XArray.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: An entry suitable for storing in the XArray.
|
||||
*/
|
||||
static inline void *xa_mk_value(unsigned long v)
|
||||
{
|
||||
WARN_ON((long)v < 0);
|
||||
return (void *)((v << 1) | 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_to_value() - Get value stored in an XArray entry.
|
||||
* @entry: XArray entry.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: The value stored in the XArray entry.
|
||||
*/
|
||||
static inline unsigned long xa_to_value(const void *entry)
|
||||
{
|
||||
return (unsigned long)entry >> 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_is_value() - Determine if an entry is a value.
|
||||
* @entry: XArray entry.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: True if the entry is a value, false if it is a pointer.
|
||||
*/
|
||||
static inline bool xa_is_value(const void *entry)
|
||||
{
|
||||
return (unsigned long)entry & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_tag_pointer() - Create an XArray entry for a tagged pointer.
|
||||
* @p: Plain pointer.
|
||||
* @tag: Tag value (0, 1 or 3).
|
||||
*
|
||||
* If the user of the XArray prefers, they can tag their pointers instead
|
||||
* of storing value entries. Three tags are available (0, 1 and 3).
|
||||
* These are distinct from the xa_mark_t as they are not replicated up
|
||||
* through the array and cannot be searched for.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: An XArray entry.
|
||||
*/
|
||||
static inline void *xa_tag_pointer(void *p, unsigned long tag)
|
||||
{
|
||||
return (void *)((unsigned long)p | tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_untag_pointer() - Turn an XArray entry into a plain pointer.
|
||||
* @entry: XArray entry.
|
||||
*
|
||||
* If you have stored a tagged pointer in the XArray, call this function
|
||||
* to get the untagged version of the pointer.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: A pointer.
|
||||
*/
|
||||
static inline void *xa_untag_pointer(void *entry)
|
||||
{
|
||||
return (void *)((unsigned long)entry & ~3UL);
|
||||
}
|
||||
|
||||
/**
|
||||
* xa_pointer_tag() - Get the tag stored in an XArray entry.
|
||||
* @entry: XArray entry.
|
||||
*
|
||||
* If you have stored a tagged pointer in the XArray, call this function
|
||||
* to get the tag of that pointer.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: A tag.
|
||||
*/
|
||||
static inline unsigned int xa_pointer_tag(void *entry)
|
||||
{
|
||||
return (unsigned long)entry & 3UL;
|
||||
}
|
||||
|
||||
#define xa_trylock(xa) spin_trylock(&(xa)->xa_lock)
|
||||
#define xa_lock(xa) spin_lock(&(xa)->xa_lock)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue