4.14-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Vitaly Wool <[email protected]>

commit 5d03a6613957785e94af7a4a6212ad4af66aa5c2 upstream.

There is a race in the current z3fold implementation between
do_compact() called in a work queue context and the page release
procedure when page's kref goes to 0.

do_compact() may be waiting for page lock, which is released by
release_z3fold_page_locked right before putting the page onto the
"stale" list, and then the page may be freed as do_compact() modifies
its contents.

The mechanism currently implemented to handle that (checking the
PAGE_STALE flag) is not reliable enough.  Instead, we'll use page's kref
counter to guarantee that the page is not released if its compaction is
scheduled.  It then becomes compaction function's responsibility to
decrease the counter and quit immediately if the page was actually
freed.

Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Vitaly Wool <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

---
 mm/z3fold.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

--- a/mm/z3fold.c
+++ b/mm/z3fold.c
@@ -404,8 +404,7 @@ static void do_compact_page(struct z3fol
                WARN_ON(z3fold_page_trylock(zhdr));
        else
                z3fold_page_lock(zhdr);
-       if (test_bit(PAGE_STALE, &page->private) ||
-           !test_and_clear_bit(NEEDS_COMPACTING, &page->private)) {
+       if (WARN_ON(!test_and_clear_bit(NEEDS_COMPACTING, &page->private))) {
                z3fold_page_unlock(zhdr);
                return;
        }
@@ -413,6 +412,11 @@ static void do_compact_page(struct z3fol
        list_del_init(&zhdr->buddy);
        spin_unlock(&pool->lock);
 
+       if (kref_put(&zhdr->refcount, release_z3fold_page_locked)) {
+               atomic64_dec(&pool->pages_nr);
+               return;
+       }
+
        z3fold_compact_page(zhdr);
        unbuddied = get_cpu_ptr(pool->unbuddied);
        fchunks = num_free_chunks(zhdr);
@@ -753,9 +757,11 @@ static void z3fold_free(struct z3fold_po
                list_del_init(&zhdr->buddy);
                spin_unlock(&pool->lock);
                zhdr->cpu = -1;
+               kref_get(&zhdr->refcount);
                do_compact_page(zhdr, true);
                return;
        }
+       kref_get(&zhdr->refcount);
        queue_work_on(zhdr->cpu, pool->compact_wq, &zhdr->work);
        z3fold_page_unlock(zhdr);
 }


Reply via email to