* [4.19-stable PATCH 0/2] dax: Don't access a freed inode
@ 2019-01-05 19:45 Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 1/2] " Dan Williams
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Dan Williams @ 2019-01-05 19:45 UTC (permalink / raw)
To: stable
Cc: Matthew Wilcox, Jan Kara, Linus Torvalds, sashal, gregkh, linux-kernel
The original upstream fix, commit 55e56f06ed71 "dax: Don't access a freed
inode", prompted an immediate cleanup request. Now that the cleanup has
landed, commit d8a706414af4 "dax: Use non-exclusive wait in
wait_entry_unlocked()", backport them both to -stable.
---
Dan Williams (1):
dax: Use non-exclusive wait in wait_entry_unlocked()
Matthew Wilcox (1):
dax: Don't access a freed inode
fs/dax.c | 69 ++++++++++++++++++++++++++++----------------------------------
1 file changed, 31 insertions(+), 38 deletions(-)
^ permalink raw reply [flat|nested] 4+ messages in thread
* [4.19-stable PATCH 1/2] dax: Don't access a freed inode
2019-01-05 19:45 [4.19-stable PATCH 0/2] dax: Don't access a freed inode Dan Williams
@ 2019-01-05 19:45 ` Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 2/2] dax: Use non-exclusive wait in wait_entry_unlocked() Dan Williams
2019-01-06 17:45 ` [4.19-stable PATCH 0/2] dax: Don't access a freed inode Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Dan Williams @ 2019-01-05 19:45 UTC (permalink / raw)
To: stable; +Cc: Matthew Wilcox, Jan Kara, sashal, gregkh, linux-kernel
From: Matthew Wilcox <willy@infradead.org>
commit 55e56f06ed71d9441f3abd5b1d3c1a870812b3fe upstream.
After we drop the i_pages lock, the inode can be freed at any time.
The get_unlocked_entry() code has no choice but to reacquire the lock,
so it can't be used here. Create a new wait_entry_unlocked() which takes
care not to acquire the lock or dereference the address_space in any way.
Fixes: c2a7d2a11552 ("filesystem-dax: Introduce dax_lock_mapping_entry()")
Cc: <stable@vger.kernel.org>
Signed-off-by: Matthew Wilcox <willy@infradead.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
fs/dax.c | 69 +++++++++++++++++++++++++++++---------------------------------
1 file changed, 32 insertions(+), 37 deletions(-)
diff --git a/fs/dax.c b/fs/dax.c
index 3a2682a6c832..415605fafaeb 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -229,8 +229,8 @@ static void put_unlocked_mapping_entry(struct address_space *mapping,
*
* Must be called with the i_pages lock held.
*/
-static void *__get_unlocked_mapping_entry(struct address_space *mapping,
- pgoff_t index, void ***slotp, bool (*wait_fn)(void))
+static void *get_unlocked_mapping_entry(struct address_space *mapping,
+ pgoff_t index, void ***slotp)
{
void *entry, **slot;
struct wait_exceptional_entry_queue ewait;
@@ -240,8 +240,6 @@ static void *__get_unlocked_mapping_entry(struct address_space *mapping,
ewait.wait.func = wake_exceptional_entry_func;
for (;;) {
- bool revalidate;
-
entry = __radix_tree_lookup(&mapping->i_pages, index, NULL,
&slot);
if (!entry ||
@@ -256,30 +254,39 @@ static void *__get_unlocked_mapping_entry(struct address_space *mapping,
prepare_to_wait_exclusive(wq, &ewait.wait,
TASK_UNINTERRUPTIBLE);
xa_unlock_irq(&mapping->i_pages);
- revalidate = wait_fn();
+ schedule();
finish_wait(wq, &ewait.wait);
xa_lock_irq(&mapping->i_pages);
- if (revalidate) {
- put_unlocked_mapping_entry(mapping, index, entry);
- return ERR_PTR(-EAGAIN);
- }
}
}
-static bool entry_wait(void)
+/*
+ * The only thing keeping the address space around is the i_pages lock
+ * (it's cycled in clear_inode() after removing the entries from i_pages)
+ * After we call xas_unlock_irq(), we cannot touch xas->xa.
+ */
+static void wait_entry_unlocked(struct address_space *mapping, pgoff_t index,
+ void ***slotp, void *entry)
{
+ struct wait_exceptional_entry_queue ewait;
+ wait_queue_head_t *wq;
+
+ init_wait(&ewait.wait);
+ ewait.wait.func = wake_exceptional_entry_func;
+
+ wq = dax_entry_waitqueue(mapping, index, entry, &ewait.key);
+ prepare_to_wait_exclusive(wq, &ewait.wait, TASK_UNINTERRUPTIBLE);
+ xa_unlock_irq(&mapping->i_pages);
schedule();
+ finish_wait(wq, &ewait.wait);
+
/*
- * Never return an ERR_PTR() from
- * __get_unlocked_mapping_entry(), just keep looping.
+ * Entry lock waits are exclusive. Wake up the next waiter since
+ * we aren't sure we will acquire the entry lock and thus wake
+ * the next waiter up on unlock.
*/
- return false;
-}
-
-static void *get_unlocked_mapping_entry(struct address_space *mapping,
- pgoff_t index, void ***slotp)
-{
- return __get_unlocked_mapping_entry(mapping, index, slotp, entry_wait);
+ if (waitqueue_active(wq))
+ __wake_up(wq, TASK_NORMAL, 1, &ewait.key);
}
static void unlock_mapping_entry(struct address_space *mapping, pgoff_t index)
@@ -398,19 +405,6 @@ static struct page *dax_busy_page(void *entry)
return NULL;
}
-static bool entry_wait_revalidate(void)
-{
- rcu_read_unlock();
- schedule();
- rcu_read_lock();
-
- /*
- * Tell __get_unlocked_mapping_entry() to take a break, we need
- * to revalidate page->mapping after dropping locks
- */
- return true;
-}
-
bool dax_lock_mapping_entry(struct page *page)
{
pgoff_t index;
@@ -446,14 +440,15 @@ bool dax_lock_mapping_entry(struct page *page)
}
index = page->index;
- entry = __get_unlocked_mapping_entry(mapping, index, &slot,
- entry_wait_revalidate);
+ entry = __radix_tree_lookup(&mapping->i_pages, index,
+ NULL, &slot);
if (!entry) {
xa_unlock_irq(&mapping->i_pages);
break;
- } else if (IS_ERR(entry)) {
- xa_unlock_irq(&mapping->i_pages);
- WARN_ON_ONCE(PTR_ERR(entry) != -EAGAIN);
+ } else if (slot_locked(mapping, slot)) {
+ rcu_read_unlock();
+ wait_entry_unlocked(mapping, index, &slot, entry);
+ rcu_read_lock();
continue;
}
lock_slot(mapping, slot);
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [4.19-stable PATCH 2/2] dax: Use non-exclusive wait in wait_entry_unlocked()
2019-01-05 19:45 [4.19-stable PATCH 0/2] dax: Don't access a freed inode Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 1/2] " Dan Williams
@ 2019-01-05 19:45 ` Dan Williams
2019-01-06 17:45 ` [4.19-stable PATCH 0/2] dax: Don't access a freed inode Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Dan Williams @ 2019-01-05 19:45 UTC (permalink / raw)
To: stable
Cc: Matthew Wilcox, Linus Torvalds, Jan Kara, sashal, gregkh, linux-kernel
commit d8a706414af4827fc0b4b1c0c631c607351938b9 upstream.
get_unlocked_entry() uses an exclusive wait because it is guaranteed to
eventually obtain the lock and follow on with an unlock+wakeup cycle.
The wait_entry_unlocked() path does not have the same guarantee. Rather
than open-code an extra wakeup, just switch to a non-exclusive wait.
Cc: Matthew Wilcox <willy@infradead.org>
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
fs/dax.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/fs/dax.c b/fs/dax.c
index 415605fafaeb..09fa70683c41 100644
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -275,18 +275,16 @@ static void wait_entry_unlocked(struct address_space *mapping, pgoff_t index,
ewait.wait.func = wake_exceptional_entry_func;
wq = dax_entry_waitqueue(mapping, index, entry, &ewait.key);
- prepare_to_wait_exclusive(wq, &ewait.wait, TASK_UNINTERRUPTIBLE);
+ /*
+ * Unlike get_unlocked_entry() there is no guarantee that this
+ * path ever successfully retrieves an unlocked entry before an
+ * inode dies. Perform a non-exclusive wait in case this path
+ * never successfully performs its own wake up.
+ */
+ prepare_to_wait(wq, &ewait.wait, TASK_UNINTERRUPTIBLE);
xa_unlock_irq(&mapping->i_pages);
schedule();
finish_wait(wq, &ewait.wait);
-
- /*
- * Entry lock waits are exclusive. Wake up the next waiter since
- * we aren't sure we will acquire the entry lock and thus wake
- * the next waiter up on unlock.
- */
- if (waitqueue_active(wq))
- __wake_up(wq, TASK_NORMAL, 1, &ewait.key);
}
static void unlock_mapping_entry(struct address_space *mapping, pgoff_t index)
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [4.19-stable PATCH 0/2] dax: Don't access a freed inode
2019-01-05 19:45 [4.19-stable PATCH 0/2] dax: Don't access a freed inode Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 1/2] " Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 2/2] dax: Use non-exclusive wait in wait_entry_unlocked() Dan Williams
@ 2019-01-06 17:45 ` Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2019-01-06 17:45 UTC (permalink / raw)
To: Dan Williams
Cc: stable, Matthew Wilcox, Jan Kara, Linus Torvalds, gregkh, linux-kernel
On Sat, Jan 05, 2019 at 11:45:02AM -0800, Dan Williams wrote:
>The original upstream fix, commit 55e56f06ed71 "dax: Don't access a freed
>inode", prompted an immediate cleanup request. Now that the cleanup has
>landed, commit d8a706414af4 "dax: Use non-exclusive wait in
>wait_entry_unlocked()", backport them both to -stable.
Queued for 4.19, thank you.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2019-01-06 17:45 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-05 19:45 [4.19-stable PATCH 0/2] dax: Don't access a freed inode Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 1/2] " Dan Williams
2019-01-05 19:45 ` [4.19-stable PATCH 2/2] dax: Use non-exclusive wait in wait_entry_unlocked() Dan Williams
2019-01-06 17:45 ` [4.19-stable PATCH 0/2] dax: Don't access a freed inode Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).