Commit 76e32a2a authored by Vitaly Wool's avatar Vitaly Wool Committed by Linus Torvalds

z3fold: fix page locking in z3fold_alloc()

Stress testing of the current z3fold implementation on a 8-core system
revealed it was possible that a z3fold page deleted from its unbuddied
list in z3fold_alloc() would be put on another unbuddied list by
z3fold_free() while z3fold_alloc() is still processing it.  This has
been introduced with commit 5a27aa82 ("z3fold: add kref refcounting")
due to the removal of special handling of a z3fold page not on any list
in z3fold_free().

To fix this, the z3fold page lock should be taken in z3fold_alloc()
before the pool lock is released.  To avoid deadlocking, we just try to
lock the page as soon as we get a hold of it, and if trylock fails, we
drop this page and take the next one.
Signed-off-by: default avatarVitaly Wool <vitalywool@gmail.com>
Cc: Dan Streetman <ddstreet@ieee.org>
Cc: <Oleksiy.Avramchenko@sony.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 27600782
...@@ -185,6 +185,12 @@ static inline void z3fold_page_lock(struct z3fold_header *zhdr) ...@@ -185,6 +185,12 @@ static inline void z3fold_page_lock(struct z3fold_header *zhdr)
spin_lock(&zhdr->page_lock); spin_lock(&zhdr->page_lock);
} }
/* Try to lock a z3fold page */
static inline int z3fold_page_trylock(struct z3fold_header *zhdr)
{
return spin_trylock(&zhdr->page_lock);
}
/* Unlock a z3fold page */ /* Unlock a z3fold page */
static inline void z3fold_page_unlock(struct z3fold_header *zhdr) static inline void z3fold_page_unlock(struct z3fold_header *zhdr)
{ {
...@@ -385,7 +391,7 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp, ...@@ -385,7 +391,7 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
spin_lock(&pool->lock); spin_lock(&pool->lock);
zhdr = list_first_entry_or_null(&pool->unbuddied[i], zhdr = list_first_entry_or_null(&pool->unbuddied[i],
struct z3fold_header, buddy); struct z3fold_header, buddy);
if (!zhdr) { if (!zhdr || !z3fold_page_trylock(zhdr)) {
spin_unlock(&pool->lock); spin_unlock(&pool->lock);
continue; continue;
} }
...@@ -394,7 +400,6 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp, ...@@ -394,7 +400,6 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp,
spin_unlock(&pool->lock); spin_unlock(&pool->lock);
page = virt_to_page(zhdr); page = virt_to_page(zhdr);
z3fold_page_lock(zhdr);
if (zhdr->first_chunks == 0) { if (zhdr->first_chunks == 0) {
if (zhdr->middle_chunks != 0 && if (zhdr->middle_chunks != 0 &&
chunks >= zhdr->start_middle) chunks >= zhdr->start_middle)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment