// SPDX-License-Identifier: GPL-2.0-only
/*
* z3fold.c
*
* Author: Vitaly Wool <vitaly.wool@konsulko.com>
* Copyright (C) 2016, Sony Mobile Communications Inc.
*
* This implementation is based on zbud written by Seth Jennings.
*
* z3fold is an special purpose allocator for storing compressed pages. It
* can store up to three compressed pages per page which improves the
* compression ratio of zbud while retaining its main concepts (e. g. always
* storing an integral number of objects per page) and simplicity.
* It still has simple and deterministic reclaim properties that make it
* preferable to a higher density approach (with no requirement on integral
* number of object per page) when reclaim is used.
*
* As in zbud, pages are divided into "chunks". The size of the chunks is
* fixed at compile time and is determined by NCHUNKS_ORDER below.
*
* z3fold doesn't export any API and is meant to be used via zpool API.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/atomic.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/page-flags.h>
#include <linux/migrate.h>
#include <linux/node.h>
#include <linux/compaction.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/zpool.h>
#include <linux/kmemleak.h>
/*
* NCHUNKS_ORDER determines the internal allocation granularity, effectively
* adjusting internal fragmentation. It also determines the number of
* freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
* allocation granularity will be in chunks of size PAGE_SIZE/64. Some chunks
* in the beginning of an allocated page are occupied by z3fold header, so
* NCHUNKS will be calculated to 63 (or 62 in case CONFIG_DEBUG_SPINLOCK=y),
* which shows the max number of free chunks in z3fold page, also there will
* be 63, or 62, respectively, freelists per pool.
*/
#define NCHUNKS_ORDER 6
#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
#define CHUNK_SIZE (1 << CHUNK_SHIFT)
#define ZHDR_SIZE_ALIGNED round_up(sizeof(struct z3fold_header), CHUNK_SIZE)
#define ZHDR_CHUNKS (ZHDR_SIZE_ALIGNED >> CHUNK_SHIFT)
#define TOTAL_CHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
#define NCHUNKS (TOTAL_CHUNKS - ZHDR_CHUNKS)
#define BUDDY_MASK (0x3)
#define BUDDY_SHIFT 2
#define SLOTS_ALIGN (0x40)
/*****************
* Structures
*****************/
struct z3fold_pool;
enum buddy {
HEADLESS = 0,
FIRST,
MIDDLE,
LAST,
BUDDIES_MAX = LAST
};
struct z3fold_buddy_slots {
/*
* we are using BUDDY_MASK in handle_to_buddy etc. so there should
* be enough slots to hold all possible variants
*/
unsigned long slot[BUDDY_MASK + 1];
unsigned long pool; /* back link */
rwlock_t lock;
};
#define HANDLE_FLAG_MASK (0x03)
/*
* struct z3fold_header -