Skip to content

Commit

Permalink
Merge tag 'xarray-4.20-rc4' of git://git.infradead.org/users/willy/li…
Browse files Browse the repository at this point in the history
…nux-dax

Pull XArray updates from Matthew Wilcox:
 "We found some bugs in the DAX conversion to XArray (and one bug which
  predated the XArray conversion). There were a couple of bugs in some
  of the higher-level functions, which aren't actually being called in
  today's kernel, but surfaced as a result of converting existing radix
  tree & IDR users over to the XArray.

  Some of the other changes to how the higher-level APIs work were also
  motivated by converting various users; again, they're not in use in
  today's kernel, so changing them has a low probability of introducing
  a bug.

  Dan can still trigger a bug in the DAX code with hot-offline/online,
  and we're working on tracking that down"

* tag 'xarray-4.20-rc4' of git://git.infradead.org/users/willy/linux-dax:
  XArray tests: Add missing locking
  dax: Avoid losing wakeup in dax_lock_mapping_entry
  dax: Fix huge page faults
  dax: Fix dax_unlock_mapping_entry for PMD pages
  dax: Reinstate RCU protection of inode
  dax: Make sure the unlocking entry isn't locked
  dax: Remove optimisation from dax_lock_mapping_entry
  XArray tests: Correct some 64-bit assumptions
  XArray: Correct xa_store_range
  XArray: Fix Documentation
  XArray: Handle NULL pointers differently for allocation
  XArray: Unify xa_store and __xa_store
  XArray: Add xa_store_bh() and xa_store_irq()
  XArray: Turn xa_erase into an exported function
  XArray: Unify xa_cmpxchg and __xa_cmpxchg
  XArray: Regularise xa_reserve
  nilfs2: Use xa_erase_irq
  XArray: Export __xa_foo to non-GPL modules
  XArray: Fix xa_for_each with a single element at 0
  • Loading branch information
torvalds committed Nov 25, 2018
2 parents e195ca6 + fffc9a2 commit e2125da
Show file tree
Hide file tree
Showing 6 changed files with 387 additions and 185 deletions.
52 changes: 41 additions & 11 deletions Documentation/core-api/xarray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ using :c:func:`xa_load`. xa_store will overwrite any entry with the
new entry and return the previous entry stored at that index. You can
use :c:func:`xa_erase` instead of calling :c:func:`xa_store` with a
``NULL`` entry. There is no difference between an entry that has never
been stored to and one that has most recently had ``NULL`` stored to it.
been stored to, one that has been erased and one that has most recently
had ``NULL`` stored to it.

You can conditionally replace an entry at an index by using
:c:func:`xa_cmpxchg`. Like :c:func:`cmpxchg`, it will only succeed if
Expand Down Expand Up @@ -105,23 +106,44 @@ may result in the entry being marked at some, but not all of the other
indices. Storing into one index may result in the entry retrieved by
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`.

If all entries in the array are ``NULL``, the :c:func:`xa_empty` function
will return ``true``.

Finally, you can remove all entries from an XArray by calling
:c:func:`xa_destroy`. If the XArray entries are pointers, you may wish
to free the entries first. You can do this by iterating over all present
entries in the XArray using the :c:func:`xa_for_each` iterator.

ID assignment
-------------
Allocating XArrays
------------------

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`,
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
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
interrupts while allocating the ID. Unlike :c:func:`xa_store`, allocating
a ``NULL`` pointer does not delete an entry. Instead it reserves an
entry like :c:func:`xa_reserve` and you can release it using either
:c:func:`xa_erase` or :c:func:`xa_release`. To use ID assignment, the
XArray must be defined with :c:func:`DEFINE_XARRAY_ALLOC`, or initialised
by passing ``XA_FLAGS_ALLOC`` to :c:func:`xa_init_flags`,
interrupts while allocating the ID.

Using :c:func:`xa_store`, :c:func:`xa_cmpxchg` or :c:func:`xa_insert`
will 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`.
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 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
available for your use.

Memory allocation
-----------------
Expand Down Expand Up @@ -158,6 +180,8 @@ Takes RCU read lock:

Takes xa_lock internally:
* :c:func:`xa_store`
* :c:func:`xa_store_bh`
* :c:func:`xa_store_irq`
* :c:func:`xa_insert`
* :c:func:`xa_erase`
* :c:func:`xa_erase_bh`
Expand All @@ -167,6 +191,9 @@ Takes xa_lock internally:
* :c:func:`xa_alloc`
* :c:func:`xa_alloc_bh`
* :c:func:`xa_alloc_irq`
* :c:func:`xa_reserve`
* :c:func:`xa_reserve_bh`
* :c:func:`xa_reserve_irq`
* :c:func:`xa_destroy`
* :c:func:`xa_set_mark`
* :c:func:`xa_clear_mark`
Expand All @@ -177,6 +204,7 @@ Assumes xa_lock held on entry:
* :c:func:`__xa_erase`
* :c:func:`__xa_cmpxchg`
* :c:func:`__xa_alloc`
* :c:func:`__xa_reserve`
* :c:func:`__xa_set_mark`
* :c:func:`__xa_clear_mark`

Expand Down Expand Up @@ -234,7 +262,8 @@ Sharing the XArray with interrupt context is also possible, either
using :c:func:`xa_lock_irqsave` in both the interrupt handler and process
context, or :c:func:`xa_lock_irq` in process context and :c:func:`xa_lock`
in the interrupt handler. Some of the more common patterns have helper
functions such as :c:func:`xa_erase_bh` and :c:func:`xa_erase_irq`.
functions such as :c:func:`xa_store_bh`, :c:func:`xa_store_irq`,
:c:func:`xa_erase_bh` and :c:func:`xa_erase_irq`.

Sometimes you need to protect access to the XArray with a mutex because
that lock sits above another mutex in the locking hierarchy. That does
Expand Down Expand Up @@ -322,7 +351,8 @@ to :c:func:`xas_retry`, and retry the operation if it returns ``true``.
- :c:func:`xa_is_zero`
- Zero entries appear as ``NULL`` through the Normal API, but occupy
an entry in the XArray which can be used to reserve the index for
future use.
future use. This is used by allocating XArrays for allocated entries
which are ``NULL``.

Other internal entries may be added in the future. As far as possible, they
will be handled by :c:func:`xas_retry`.
Expand Down
60 changes: 35 additions & 25 deletions fs/dax.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ static void *dax_make_entry(pfn_t pfn, unsigned long flags)
return xa_mk_value(flags | (pfn_t_to_pfn(pfn) << DAX_SHIFT));
}

static void *dax_make_page_entry(struct page *page)
{
pfn_t pfn = page_to_pfn_t(page);
return dax_make_entry(pfn, PageHead(page) ? DAX_PMD : 0);
}

static bool dax_is_locked(void *entry)
{
return xa_to_value(entry) & DAX_LOCKED;
Expand All @@ -116,12 +110,12 @@ static unsigned int dax_entry_order(void *entry)
return 0;
}

static int dax_is_pmd_entry(void *entry)
static unsigned long dax_is_pmd_entry(void *entry)
{
return xa_to_value(entry) & DAX_PMD;
}

static int dax_is_pte_entry(void *entry)
static bool dax_is_pte_entry(void *entry)
{
return !(xa_to_value(entry) & DAX_PMD);
}
Expand Down Expand Up @@ -222,9 +216,8 @@ static void *get_unlocked_entry(struct xa_state *xas)
ewait.wait.func = wake_exceptional_entry_func;

for (;;) {
entry = xas_load(xas);
if (!entry || xa_is_internal(entry) ||
WARN_ON_ONCE(!xa_is_value(entry)) ||
entry = xas_find_conflict(xas);
if (!entry || WARN_ON_ONCE(!xa_is_value(entry)) ||
!dax_is_locked(entry))
return entry;

Expand Down Expand Up @@ -255,6 +248,7 @@ static void dax_unlock_entry(struct xa_state *xas, void *entry)
{
void *old;

BUG_ON(dax_is_locked(entry));
xas_reset(xas);
xas_lock_irq(xas);
old = xas_store(xas, entry);
Expand Down Expand Up @@ -352,16 +346,27 @@ static struct page *dax_busy_page(void *entry)
return NULL;
}

/*
* dax_lock_mapping_entry - Lock the DAX entry corresponding to a page
* @page: The page whose entry we want to lock
*
* Context: Process context.
* Return: %true if the entry was locked or does not need to be locked.
*/
bool dax_lock_mapping_entry(struct page *page)
{
XA_STATE(xas, NULL, 0);
void *entry;
bool locked;

/* Ensure page->mapping isn't freed while we look at it */
rcu_read_lock();
for (;;) {
struct address_space *mapping = READ_ONCE(page->mapping);

locked = false;
if (!dax_mapping(mapping))
return false;
break;

/*
* In the device-dax case there's no need to lock, a
Expand All @@ -370,8 +375,9 @@ bool dax_lock_mapping_entry(struct page *page)
* otherwise we would not have a valid pfn_to_page()
* translation.
*/
locked = true;
if (S_ISCHR(mapping->host->i_mode))
return true;
break;

xas.xa = &mapping->i_pages;
xas_lock_irq(&xas);
Expand All @@ -382,28 +388,35 @@ bool dax_lock_mapping_entry(struct page *page)
xas_set(&xas, page->index);
entry = xas_load(&xas);
if (dax_is_locked(entry)) {
rcu_read_unlock();
entry = get_unlocked_entry(&xas);
/* Did the page move while we slept? */
if (dax_to_pfn(entry) != page_to_pfn(page)) {
xas_unlock_irq(&xas);
continue;
}
xas_unlock_irq(&xas);
put_unlocked_entry(&xas, entry);
rcu_read_lock();
continue;
}
dax_lock_entry(&xas, entry);
xas_unlock_irq(&xas);
return true;
break;
}
rcu_read_unlock();
return locked;
}

void dax_unlock_mapping_entry(struct page *page)
{
struct address_space *mapping = page->mapping;
XA_STATE(xas, &mapping->i_pages, page->index);
void *entry;

if (S_ISCHR(mapping->host->i_mode))
return;

dax_unlock_entry(&xas, dax_make_page_entry(page));
rcu_read_lock();
entry = xas_load(&xas);
rcu_read_unlock();
entry = dax_make_entry(page_to_pfn_t(page), dax_is_pmd_entry(entry));
dax_unlock_entry(&xas, entry);
}

/*
Expand Down Expand Up @@ -445,11 +458,9 @@ static void *grab_mapping_entry(struct xa_state *xas,
retry:
xas_lock_irq(xas);
entry = get_unlocked_entry(xas);
if (xa_is_internal(entry))
goto fallback;

if (entry) {
if (WARN_ON_ONCE(!xa_is_value(entry))) {
if (!xa_is_value(entry)) {
xas_set_err(xas, EIO);
goto out_unlock;
}
Expand Down Expand Up @@ -1628,8 +1639,7 @@ dax_insert_pfn_mkwrite(struct vm_fault *vmf, pfn_t pfn, unsigned int order)
/* Did we race with someone splitting entry or so? */
if (!entry ||
(order == 0 && !dax_is_pte_entry(entry)) ||
(order == PMD_ORDER && (xa_is_internal(entry) ||
!dax_is_pmd_entry(entry)))) {
(order == PMD_ORDER && !dax_is_pmd_entry(entry))) {
put_unlocked_entry(&xas, entry);
xas_unlock_irq(&xas);
trace_dax_insert_pfn_mkwrite_no_entry(mapping->host, vmf,
Expand Down
4 changes: 1 addition & 3 deletions fs/nilfs2/btnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,7 @@ void nilfs_btnode_abort_change_key(struct address_space *btnc,
return;

if (nbh == NULL) { /* blocksize == pagesize */
xa_lock_irq(&btnc->i_pages);
__xa_erase(&btnc->i_pages, newkey);
xa_unlock_irq(&btnc->i_pages);
xa_erase_irq(&btnc->i_pages, newkey);
unlock_page(ctxt->bh->b_page);
} else
brelse(nbh);
Expand Down
Loading

0 comments on commit e2125da

Please sign in to comment.