xref: /kernel/linux/linux-5.10/fs/erofs/zdata.h (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2018 HUAWEI, Inc.
48c2ecf20Sopenharmony_ci *             https://www.huawei.com/
58c2ecf20Sopenharmony_ci * Created by Gao Xiang <gaoxiang25@huawei.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#ifndef __EROFS_FS_ZDATA_H
88c2ecf20Sopenharmony_ci#define __EROFS_FS_ZDATA_H
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "internal.h"
118c2ecf20Sopenharmony_ci#include "zpvec.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define Z_EROFS_NR_INLINE_PAGEVECS      3
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * Structure fields follow one of the following exclusion rules.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * I: Modifiable by initialization/destruction paths and read-only
198c2ecf20Sopenharmony_ci *    for everyone else;
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * L: Field should be protected by pageset lock;
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * A: Field should be accessed / updated in atomic for parallelized code.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_cistruct z_erofs_collection {
268c2ecf20Sopenharmony_ci	struct mutex lock;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	/* I: page offset of start position of decompression */
298c2ecf20Sopenharmony_ci	unsigned short pageofs;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	/* L: maximum relative page index in pagevec[] */
328c2ecf20Sopenharmony_ci	unsigned short nr_pages;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/* L: total number of pages in pagevec[] */
358c2ecf20Sopenharmony_ci	unsigned int vcnt;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	union {
388c2ecf20Sopenharmony_ci		/* L: inline a certain number of pagevecs for bootstrap */
398c2ecf20Sopenharmony_ci		erofs_vtptr_t pagevec[Z_EROFS_NR_INLINE_PAGEVECS];
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci		/* I: can be used to free the pcluster by RCU. */
428c2ecf20Sopenharmony_ci		struct rcu_head rcu;
438c2ecf20Sopenharmony_ci	};
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define Z_EROFS_PCLUSTER_FULL_LENGTH    0x00000001
478c2ecf20Sopenharmony_ci#define Z_EROFS_PCLUSTER_LENGTH_BIT     1
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * let's leave a type here in case of introducing
518c2ecf20Sopenharmony_ci * another tagged pointer later.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_citypedef void *z_erofs_next_pcluster_t;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct z_erofs_pcluster {
568c2ecf20Sopenharmony_ci	struct erofs_workgroup obj;
578c2ecf20Sopenharmony_ci	struct z_erofs_collection primary_collection;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* A: point to next chained pcluster or TAILs */
608c2ecf20Sopenharmony_ci	z_erofs_next_pcluster_t next;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* A: compressed pages (including multi-usage pages) */
638c2ecf20Sopenharmony_ci	struct page *compressed_pages[Z_EROFS_CLUSTER_MAX_PAGES];
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	/* A: lower limit of decompressed length and if full length or not */
668c2ecf20Sopenharmony_ci	unsigned int length;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* I: compression algorithm format */
698c2ecf20Sopenharmony_ci	unsigned char algorithmformat;
708c2ecf20Sopenharmony_ci	/* I: bit shift of physical cluster size */
718c2ecf20Sopenharmony_ci	unsigned char clusterbits;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define z_erofs_primarycollection(pcluster) (&(pcluster)->primary_collection)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* let's avoid the valid 32-bit kernel addresses */
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* the chained workgroup has't submitted io (still open) */
798c2ecf20Sopenharmony_ci#define Z_EROFS_PCLUSTER_TAIL           ((void *)0x5F0ECAFE)
808c2ecf20Sopenharmony_ci/* the chained workgroup has already submitted io */
818c2ecf20Sopenharmony_ci#define Z_EROFS_PCLUSTER_TAIL_CLOSED    ((void *)0x5F0EDEAD)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define Z_EROFS_PCLUSTER_NIL            (NULL)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define Z_EROFS_WORKGROUP_SIZE  sizeof(struct z_erofs_pcluster)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistruct z_erofs_decompressqueue {
888c2ecf20Sopenharmony_ci	struct super_block *sb;
898c2ecf20Sopenharmony_ci	atomic_t pending_bios;
908c2ecf20Sopenharmony_ci	z_erofs_next_pcluster_t head;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	union {
938c2ecf20Sopenharmony_ci		wait_queue_head_t wait;
948c2ecf20Sopenharmony_ci		struct work_struct work;
958c2ecf20Sopenharmony_ci	} u;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define MNGD_MAPPING(sbi)	((sbi)->managed_cache->i_mapping)
998c2ecf20Sopenharmony_cistatic inline bool erofs_page_is_managed(const struct erofs_sb_info *sbi,
1008c2ecf20Sopenharmony_ci					 struct page *page)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return page->mapping == MNGD_MAPPING(sbi);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define Z_EROFS_ONLINEPAGE_COUNT_BITS   2
1068c2ecf20Sopenharmony_ci#define Z_EROFS_ONLINEPAGE_COUNT_MASK   ((1 << Z_EROFS_ONLINEPAGE_COUNT_BITS) - 1)
1078c2ecf20Sopenharmony_ci#define Z_EROFS_ONLINEPAGE_INDEX_SHIFT  (Z_EROFS_ONLINEPAGE_COUNT_BITS)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * waiters (aka. ongoing_packs): # to unlock the page
1118c2ecf20Sopenharmony_ci * sub-index: 0 - for partial page, >= 1 full page sub-index
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_citypedef atomic_t z_erofs_onlinepage_t;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* type punning */
1168c2ecf20Sopenharmony_ciunion z_erofs_onlinepage_converter {
1178c2ecf20Sopenharmony_ci	z_erofs_onlinepage_t *o;
1188c2ecf20Sopenharmony_ci	unsigned long *v;
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic inline unsigned int z_erofs_onlinepage_index(struct page *page)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	union z_erofs_onlinepage_converter u;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	DBG_BUGON(!PagePrivate(page));
1268c2ecf20Sopenharmony_ci	u.v = &page_private(page);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return atomic_read(u.o) >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic inline void z_erofs_onlinepage_init(struct page *page)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	union {
1348c2ecf20Sopenharmony_ci		z_erofs_onlinepage_t o;
1358c2ecf20Sopenharmony_ci		unsigned long v;
1368c2ecf20Sopenharmony_ci	/* keep from being unlocked in advance */
1378c2ecf20Sopenharmony_ci	} u = { .o = ATOMIC_INIT(1) };
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	set_page_private(page, u.v);
1408c2ecf20Sopenharmony_ci	smp_wmb();
1418c2ecf20Sopenharmony_ci	SetPagePrivate(page);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline void z_erofs_onlinepage_fixup(struct page *page,
1458c2ecf20Sopenharmony_ci	uintptr_t index, bool down)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	union z_erofs_onlinepage_converter u = { .v = &page_private(page) };
1488c2ecf20Sopenharmony_ci	int orig, orig_index, val;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cirepeat:
1518c2ecf20Sopenharmony_ci	orig = atomic_read(u.o);
1528c2ecf20Sopenharmony_ci	orig_index = orig >> Z_EROFS_ONLINEPAGE_INDEX_SHIFT;
1538c2ecf20Sopenharmony_ci	if (orig_index) {
1548c2ecf20Sopenharmony_ci		if (!index)
1558c2ecf20Sopenharmony_ci			return;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		DBG_BUGON(orig_index != index);
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	val = (index << Z_EROFS_ONLINEPAGE_INDEX_SHIFT) |
1618c2ecf20Sopenharmony_ci		((orig & Z_EROFS_ONLINEPAGE_COUNT_MASK) + (unsigned int)down);
1628c2ecf20Sopenharmony_ci	if (atomic_cmpxchg(u.o, orig, val) != orig)
1638c2ecf20Sopenharmony_ci		goto repeat;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic inline void z_erofs_onlinepage_endio(struct page *page)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	union z_erofs_onlinepage_converter u;
1698c2ecf20Sopenharmony_ci	unsigned int v;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	DBG_BUGON(!PagePrivate(page));
1728c2ecf20Sopenharmony_ci	u.v = &page_private(page);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	v = atomic_dec_return(u.o);
1758c2ecf20Sopenharmony_ci	if (!(v & Z_EROFS_ONLINEPAGE_COUNT_MASK)) {
1768c2ecf20Sopenharmony_ci		ClearPagePrivate(page);
1778c2ecf20Sopenharmony_ci		if (!PageError(page))
1788c2ecf20Sopenharmony_ci			SetPageUptodate(page);
1798c2ecf20Sopenharmony_ci		unlock_page(page);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	erofs_dbg("%s, page %p value %x", __func__, page, atomic_read(u.o));
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#define Z_EROFS_VMAP_ONSTACK_PAGES	\
1858c2ecf20Sopenharmony_ci	min_t(unsigned int, THREAD_SIZE / 8 / sizeof(struct page *), 96U)
1868c2ecf20Sopenharmony_ci#define Z_EROFS_VMAP_GLOBAL_PAGES	2048
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci#endif
1898c2ecf20Sopenharmony_ci
190