Path: csiph.com!news.redatomik.org!aioe.org!gothmog.csi.it!bofh.it!news.nic.it!robomod From: Vitaly Wool Newsgroups: linux.kernel Subject: [PATCH] z3fold: add shrinker Date: Tue, 11 Oct 2016 23:20:02 +0200 Message-ID: Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=date:from:to:cc:subject:message-id:mime-version :content-transfer-encoding; bh=SIzXlbSb6LkusYaOR59mFXBIPEqc+LvWqrF6tgL8MKI=; b=C6hGWEcFktNYkWbN3wsdEdPDiLoLOhtWFQzwZ36Z49oZxZ3/QdoFBBjSHErIiEJAIc X12sKW1dn0Nq3OYixCJXf12OYrIZMDHqbMCr+k2kM/tLzVfKZP3stdHSD7evjuz0/eBR w/PiisBU2RVYRqaQHU+8ninx4uN9EAL3oTc4Jd/owdpepcsyYgWn2cO9uC03cwg9sJRc 5+OYXOT5bn4QAyU+MPj7mB2BjqHbCZleFpqmoB5GnqfMNnlTFsl5UGrXBgACQ1Mdwxpb 92D5hqrZteUMMUoqAmW3SxGgRmWBj2o2pFmwGAMrzzav9tn9xPeMofiQxzOM+pSdLexz ZZHw== X-Google-Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-transfer-encoding; bh=SIzXlbSb6LkusYaOR59mFXBIPEqc+LvWqrF6tgL8MKI=; b=DT0YVKdKf18ZxVRiu8vd/SmJ2HpT/cb7lTM3VE42nBiaVTUKZv4ZsQO6Q8MaWAB6uV v0gTDCr9Ozg8bPh0f6kgfqXJd3suELcQqXc5UJ3PtK+d/KXudzE17Luliiax2ce8pXiU fIojps57SMuATxncXvQF++aVB1KsOAQ6qVMpu1ur0U5TTK6e0M2i9uunfqmu1c6n5W14 Z8VDPWjp9vHbscSjllm7bEnveXrpySiDDpemPZMdZN3fTn+MnxMf3YVLy3XPqYHuVONJ dCpkLs/0lRjNcX8euvnpLn+NaBtWaBB/+pVz89xjY4zrzk7tNIJ6fhfJ4waAnKMccbRH nocw== X-Gm-Message-State: AA6/9Rnm9BHqeIbAgmevMz3sstLvKba91v/Ne8E4gSqKf07AOmRIWfUdy+9sJTZRUEnJUg== X-Received: by 10.66.172.12 with SMTP id ay12mr10084628pac.180.1476220456093; Tue, 11 Oct 2016 14:14:16 -0700 (PDT) X-Mailer: Sylpheed 3.4.1 (GTK+ 2.24.23; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: robomod@news.nic.it List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Approved: robomod@news.nic.it Lines: 266 Organization: linux.* mail to news gateway X-Original-Cc: Seth Jennings , Dan Streetman , Andrew Morton X-Original-Date: Tue, 11 Oct 2016 23:14:08 +0200 X-Original-Message-ID: <20161011231408.2728c93ad89acb517fc6c9f0@gmail.com> X-Original-Sender: linux-kernel-owner@vger.kernel.org Xref: csiph.com linux.kernel:1499209 This patch implements shrinker for z3fold. This shrinker implementation does not free up any pages directly but it allows for a denser placement of compressed objects which results in less actual pages consumed and higher compression ratio therefore. Signed-off-by: Vitaly Wool --- mm/z3fold.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 24 deletions(-) diff --git a/mm/z3fold.c b/mm/z3fold.c index 8f9e89c..6dbf162 100644 --- a/mm/z3fold.c +++ b/mm/z3fold.c @@ -30,6 +30,7 @@ #include #include #include +#include /***************** * Structures @@ -69,8 +70,10 @@ struct z3fold_ops { * @lru: list tracking the z3fold pages in LRU order by most recently * added buddy. * @pages_nr: number of z3fold pages in the pool. + * @unbuddied_nr: number of unbuddied z3fold pages in the pool. * @ops: pointer to a structure of user defined operations specified at * pool creation time. + * @shrinker: shrinker structure to optimize page layout in background * * This structure is allocated at pool creation time and maintains metadata * pertaining to a particular z3fold pool. @@ -81,9 +84,11 @@ struct z3fold_pool { struct list_head buddied; struct list_head lru; u64 pages_nr; + u64 unbuddied_nr; const struct z3fold_ops *ops; struct zpool *zpool; const struct zpool_ops *zpool_ops; + struct shrinker shrinker; }; enum buddy { @@ -134,6 +139,9 @@ static int size_to_chunks(size_t size) #define for_each_unbuddied_list(_iter, _begin) \ for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++) +#define for_each_unbuddied_list_down(_iter, _end) \ + for ((_iter) = (_end); (_iter) > 0; (_iter)--) + /* Initializes the z3fold header of a newly allocated z3fold page */ static struct z3fold_header *init_z3fold_page(struct page *page) { @@ -209,6 +217,90 @@ static int num_free_chunks(struct z3fold_header *zhdr) return nfree; } +/* Has to be called with lock held */ +static int z3fold_compact_page(struct z3fold_header *zhdr, bool sync) +{ + struct page *page = virt_to_page(zhdr); + void *beg = zhdr; + + + if (!test_bit(MIDDLE_CHUNK_MAPPED, &page->private)) { + if (zhdr->middle_chunks != 0 && + zhdr->first_chunks == 0 && + zhdr->last_chunks == 0) { + memmove(beg + ZHDR_SIZE_ALIGNED, + beg + (zhdr->start_middle << CHUNK_SHIFT), + zhdr->middle_chunks << CHUNK_SHIFT); + zhdr->first_chunks = zhdr->middle_chunks; + zhdr->middle_chunks = 0; + zhdr->start_middle = 0; + zhdr->first_num++; + return 1; + } + if (sync) + goto out; + + /* moving data is expensive, so let's only do that if + * there's substantial gain (2+ chunks) + */ + if (zhdr->middle_chunks != 0 && zhdr->first_chunks != 0 && + zhdr->last_chunks == 0 && + zhdr->start_middle > zhdr->first_chunks + 2) { + unsigned short new_start = zhdr->first_chunks + 1; + memmove(beg + (new_start << CHUNK_SHIFT), + beg + (zhdr->start_middle << CHUNK_SHIFT), + zhdr->middle_chunks << CHUNK_SHIFT); + zhdr->start_middle = new_start; + return 1; + } + if (zhdr->middle_chunks != 0 && zhdr->last_chunks != 0 && + zhdr->first_chunks == 0 && + zhdr->middle_chunks + zhdr->last_chunks <= + NCHUNKS - zhdr->start_middle - 2) { + unsigned short new_start = NCHUNKS - zhdr->last_chunks - + zhdr->middle_chunks; + memmove(beg + (new_start << CHUNK_SHIFT), + beg + (zhdr->start_middle << CHUNK_SHIFT), + zhdr->middle_chunks << CHUNK_SHIFT); + zhdr->start_middle = new_start; + return 1; + } + } +out: + return 0; +} + +static int z3fold_shrink(struct shrinker *shrink, struct shrink_control *sc) +{ + struct z3fold_pool *pool = container_of(shrink, struct z3fold_pool, + shrinker); + struct z3fold_header *zhdr; + int i, nr_to_scan = sc->nr_to_scan; + + if (!nr_to_scan) + return pool->unbuddied_nr; + + spin_lock(&pool->lock); + + for_each_unbuddied_list_down(i, NCHUNKS - 3) { + if (!list_empty(&pool->unbuddied[i])) { + zhdr = list_first_entry(&pool->unbuddied[i], + struct z3fold_header, buddy); + if (z3fold_compact_page(zhdr, false)) { + int nchunks = num_free_chunks(zhdr); + list_del(&zhdr->buddy); + list_add(&zhdr->buddy, + &pool->unbuddied[nchunks]); + } + if (!--nr_to_scan) + break; + } + } + spin_unlock(&pool->lock); + return 0; +} + + /***************** * API Functions *****************/ @@ -234,6 +326,9 @@ static struct z3fold_pool *z3fold_create_pool(gfp_t gfp, INIT_LIST_HEAD(&pool->unbuddied[i]); INIT_LIST_HEAD(&pool->buddied); INIT_LIST_HEAD(&pool->lru); + pool->shrinker.shrink = z3fold_shrink; + pool->shrinker.seeks = DEFAULT_SEEKS; + register_shrinker(&pool->shrinker); pool->pages_nr = 0; pool->ops = ops; return pool; @@ -247,31 +342,10 @@ static struct z3fold_pool *z3fold_create_pool(gfp_t gfp, */ static void z3fold_destroy_pool(struct z3fold_pool *pool) { + unregister_shrinker(&pool->shrinker); kfree(pool); } -/* Has to be called with lock held */ -static int z3fold_compact_page(struct z3fold_header *zhdr) -{ - struct page *page = virt_to_page(zhdr); - void *beg = zhdr; - - - if (!test_bit(MIDDLE_CHUNK_MAPPED, &page->private) && - zhdr->middle_chunks != 0 && - zhdr->first_chunks == 0 && zhdr->last_chunks == 0) { - memmove(beg + ZHDR_SIZE_ALIGNED, - beg + (zhdr->start_middle << CHUNK_SHIFT), - zhdr->middle_chunks << CHUNK_SHIFT); - zhdr->first_chunks = zhdr->middle_chunks; - zhdr->middle_chunks = 0; - zhdr->start_middle = 0; - zhdr->first_num++; - return 1; - } - return 0; -} - /** * z3fold_alloc() - allocates a region of a given size * @pool: z3fold pool from which to allocate @@ -334,6 +408,7 @@ static int z3fold_alloc(struct z3fold_pool *pool, size_t size, gfp_t gfp, continue; } list_del(&zhdr->buddy); + pool->unbuddied_nr--; goto found; } } @@ -369,6 +444,7 @@ found: /* Add to unbuddied list */ freechunks = num_free_chunks(zhdr); list_add(&zhdr->buddy, &pool->unbuddied[freechunks]); + pool->unbuddied_nr++; } else { /* Add to buddied list */ list_add(&zhdr->buddy, &pool->buddied); @@ -412,6 +488,11 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) /* HEADLESS page stored */ bud = HEADLESS; } else { + if (zhdr->first_chunks == 0 || + zhdr->middle_chunks == 0 || + zhdr->last_chunks == 0) + pool->unbuddied_nr--; + bud = handle_to_buddy(handle); switch (bud) { @@ -428,6 +509,7 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) default: pr_err("%s: unknown bud %d\n", __func__, bud); WARN_ON(1); + pool->unbuddied_nr++; spin_unlock(&pool->lock); return; } @@ -453,10 +535,11 @@ static void z3fold_free(struct z3fold_pool *pool, unsigned long handle) free_z3fold_page(zhdr); pool->pages_nr--; } else { - z3fold_compact_page(zhdr); + z3fold_compact_page(zhdr, true); /* Add to the unbuddied list */ freechunks = num_free_chunks(zhdr); list_add(&zhdr->buddy, &pool->unbuddied[freechunks]); + pool->unbuddied_nr++; } spin_unlock(&pool->lock); @@ -520,6 +603,11 @@ static int z3fold_reclaim_page(struct z3fold_pool *pool, unsigned int retries) zhdr = page_address(page); if (!test_bit(PAGE_HEADLESS, &page->private)) { list_del(&zhdr->buddy); + if (zhdr->first_chunks == 0 || + zhdr->middle_chunks == 0 || + zhdr->last_chunks == 0) + pool->unbuddied_nr--; + /* * We need encode the handles before unlocking, since * we can race with free that will set @@ -579,11 +667,12 @@ next: /* Full, add to buddied list */ list_add(&zhdr->buddy, &pool->buddied); } else { - z3fold_compact_page(zhdr); + z3fold_compact_page(zhdr, true); /* add to unbuddied list */ freechunks = num_free_chunks(zhdr); list_add(&zhdr->buddy, &pool->unbuddied[freechunks]); + pool->unbuddied_nr++; } } -- 2.4.2