18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Cleancache frontend
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This code provides the generic "frontend" layer to call a matching
68c2ecf20Sopenharmony_ci * "backend" driver implementation of cleancache.  See
78c2ecf20Sopenharmony_ci * Documentation/vm/cleancache.rst for more information.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Oracle Corp. All rights reserved.
108c2ecf20Sopenharmony_ci * Author: Dan Magenheimer
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/fs.h>
158c2ecf20Sopenharmony_ci#include <linux/exportfs.h>
168c2ecf20Sopenharmony_ci#include <linux/mm.h>
178c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
188c2ecf20Sopenharmony_ci#include <linux/cleancache.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * cleancache_ops is set by cleancache_register_ops to contain the pointers
228c2ecf20Sopenharmony_ci * to the cleancache "backend" implementation functions.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic const struct cleancache_ops *cleancache_ops __read_mostly;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * Counters available via /sys/kernel/debug/cleancache (if debugfs is
288c2ecf20Sopenharmony_ci * properly configured.  These are for information only so are not protected
298c2ecf20Sopenharmony_ci * against increment races.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic u64 cleancache_succ_gets;
328c2ecf20Sopenharmony_cistatic u64 cleancache_failed_gets;
338c2ecf20Sopenharmony_cistatic u64 cleancache_puts;
348c2ecf20Sopenharmony_cistatic u64 cleancache_invalidates;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void cleancache_register_ops_sb(struct super_block *sb, void *unused)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	switch (sb->cleancache_poolid) {
398c2ecf20Sopenharmony_ci	case CLEANCACHE_NO_BACKEND:
408c2ecf20Sopenharmony_ci		__cleancache_init_fs(sb);
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	case CLEANCACHE_NO_BACKEND_SHARED:
438c2ecf20Sopenharmony_ci		__cleancache_init_shared_fs(sb);
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Register operations for cleancache. Returns 0 on success.
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ciint cleancache_register_ops(const struct cleancache_ops *ops)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	if (cmpxchg(&cleancache_ops, NULL, ops))
548c2ecf20Sopenharmony_ci		return -EBUSY;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * A cleancache backend can be built as a module and hence loaded after
588c2ecf20Sopenharmony_ci	 * a cleancache enabled filesystem has called cleancache_init_fs. To
598c2ecf20Sopenharmony_ci	 * handle such a scenario, here we call ->init_fs or ->init_shared_fs
608c2ecf20Sopenharmony_ci	 * for each active super block. To differentiate between local and
618c2ecf20Sopenharmony_ci	 * shared filesystems, we temporarily initialize sb->cleancache_poolid
628c2ecf20Sopenharmony_ci	 * to CLEANCACHE_NO_BACKEND or CLEANCACHE_NO_BACKEND_SHARED
638c2ecf20Sopenharmony_ci	 * respectively in case there is no backend registered at the time
648c2ecf20Sopenharmony_ci	 * cleancache_init_fs or cleancache_init_shared_fs is called.
658c2ecf20Sopenharmony_ci	 *
668c2ecf20Sopenharmony_ci	 * Since filesystems can be mounted concurrently with cleancache
678c2ecf20Sopenharmony_ci	 * backend registration, we have to be careful to guarantee that all
688c2ecf20Sopenharmony_ci	 * cleancache enabled filesystems that has been mounted by the time
698c2ecf20Sopenharmony_ci	 * cleancache_register_ops is called has got and all mounted later will
708c2ecf20Sopenharmony_ci	 * get cleancache_poolid. This is assured by the following statements
718c2ecf20Sopenharmony_ci	 * tied together:
728c2ecf20Sopenharmony_ci	 *
738c2ecf20Sopenharmony_ci	 * a) iterate_supers skips only those super blocks that has started
748c2ecf20Sopenharmony_ci	 *    ->kill_sb
758c2ecf20Sopenharmony_ci	 *
768c2ecf20Sopenharmony_ci	 * b) if iterate_supers encounters a super block that has not finished
778c2ecf20Sopenharmony_ci	 *    ->mount yet, it waits until it is finished
788c2ecf20Sopenharmony_ci	 *
798c2ecf20Sopenharmony_ci	 * c) cleancache_init_fs is called from ->mount and
808c2ecf20Sopenharmony_ci	 *    cleancache_invalidate_fs is called from ->kill_sb
818c2ecf20Sopenharmony_ci	 *
828c2ecf20Sopenharmony_ci	 * d) we call iterate_supers after cleancache_ops has been set
838c2ecf20Sopenharmony_ci	 *
848c2ecf20Sopenharmony_ci	 * From a) it follows that if iterate_supers skips a super block, then
858c2ecf20Sopenharmony_ci	 * either the super block is already dead, in which case we do not need
868c2ecf20Sopenharmony_ci	 * to bother initializing cleancache for it, or it was mounted after we
878c2ecf20Sopenharmony_ci	 * initiated iterate_supers. In the latter case, it must have seen
888c2ecf20Sopenharmony_ci	 * cleancache_ops set according to d) and initialized cleancache from
898c2ecf20Sopenharmony_ci	 * ->mount by itself according to c). This proves that we call
908c2ecf20Sopenharmony_ci	 * ->init_fs at least once for each active super block.
918c2ecf20Sopenharmony_ci	 *
928c2ecf20Sopenharmony_ci	 * From b) and c) it follows that if iterate_supers encounters a super
938c2ecf20Sopenharmony_ci	 * block that has already started ->init_fs, it will wait until ->mount
948c2ecf20Sopenharmony_ci	 * and hence ->init_fs has finished, then check cleancache_poolid, see
958c2ecf20Sopenharmony_ci	 * that it has already been set and therefore do nothing. This proves
968c2ecf20Sopenharmony_ci	 * that we call ->init_fs no more than once for each super block.
978c2ecf20Sopenharmony_ci	 *
988c2ecf20Sopenharmony_ci	 * Combined together, the last two paragraphs prove the function
998c2ecf20Sopenharmony_ci	 * correctness.
1008c2ecf20Sopenharmony_ci	 *
1018c2ecf20Sopenharmony_ci	 * Note that various cleancache callbacks may proceed before this
1028c2ecf20Sopenharmony_ci	 * function is called or even concurrently with it, but since
1038c2ecf20Sopenharmony_ci	 * CLEANCACHE_NO_BACKEND is negative, they will all result in a noop
1048c2ecf20Sopenharmony_ci	 * until the corresponding ->init_fs has been actually called and
1058c2ecf20Sopenharmony_ci	 * cleancache_ops has been set.
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	iterate_supers(cleancache_register_ops_sb, NULL);
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cleancache_register_ops);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* Called by a cleancache-enabled filesystem at time of mount */
1138c2ecf20Sopenharmony_civoid __cleancache_init_fs(struct super_block *sb)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	int pool_id = CLEANCACHE_NO_BACKEND;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (cleancache_ops) {
1188c2ecf20Sopenharmony_ci		pool_id = cleancache_ops->init_fs(PAGE_SIZE);
1198c2ecf20Sopenharmony_ci		if (pool_id < 0)
1208c2ecf20Sopenharmony_ci			pool_id = CLEANCACHE_NO_POOL;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	sb->cleancache_poolid = pool_id;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_init_fs);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* Called by a cleancache-enabled clustered filesystem at time of mount */
1278c2ecf20Sopenharmony_civoid __cleancache_init_shared_fs(struct super_block *sb)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int pool_id = CLEANCACHE_NO_BACKEND_SHARED;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (cleancache_ops) {
1328c2ecf20Sopenharmony_ci		pool_id = cleancache_ops->init_shared_fs(&sb->s_uuid, PAGE_SIZE);
1338c2ecf20Sopenharmony_ci		if (pool_id < 0)
1348c2ecf20Sopenharmony_ci			pool_id = CLEANCACHE_NO_POOL;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	sb->cleancache_poolid = pool_id;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_init_shared_fs);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/*
1418c2ecf20Sopenharmony_ci * If the filesystem uses exportable filehandles, use the filehandle as
1428c2ecf20Sopenharmony_ci * the key, else use the inode number.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistatic int cleancache_get_key(struct inode *inode,
1458c2ecf20Sopenharmony_ci			      struct cleancache_filekey *key)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int (*fhfn)(struct inode *, __u32 *fh, int *, struct inode *);
1488c2ecf20Sopenharmony_ci	int len = 0, maxlen = CLEANCACHE_KEY_MAX;
1498c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	key->u.ino = inode->i_ino;
1528c2ecf20Sopenharmony_ci	if (sb->s_export_op != NULL) {
1538c2ecf20Sopenharmony_ci		fhfn = sb->s_export_op->encode_fh;
1548c2ecf20Sopenharmony_ci		if  (fhfn) {
1558c2ecf20Sopenharmony_ci			len = (*fhfn)(inode, &key->u.fh[0], &maxlen, NULL);
1568c2ecf20Sopenharmony_ci			if (len <= FILEID_ROOT || len == FILEID_INVALID)
1578c2ecf20Sopenharmony_ci				return -1;
1588c2ecf20Sopenharmony_ci			if (maxlen > CLEANCACHE_KEY_MAX)
1598c2ecf20Sopenharmony_ci				return -1;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * "Get" data from cleancache associated with the poolid/inode/index
1678c2ecf20Sopenharmony_ci * that were specified when the data was put to cleanache and, if
1688c2ecf20Sopenharmony_ci * successful, use it to fill the specified page with data and return 0.
1698c2ecf20Sopenharmony_ci * The pageframe is unchanged and returns -1 if the get fails.
1708c2ecf20Sopenharmony_ci * Page must be locked by caller.
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci * The function has two checks before any action is taken - whether
1738c2ecf20Sopenharmony_ci * a backend is registered and whether the sb->cleancache_poolid
1748c2ecf20Sopenharmony_ci * is correct.
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_ciint __cleancache_get_page(struct page *page)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	int ret = -1;
1798c2ecf20Sopenharmony_ci	int pool_id;
1808c2ecf20Sopenharmony_ci	struct cleancache_filekey key = { .u.key = { 0 } };
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!cleancache_ops) {
1838c2ecf20Sopenharmony_ci		cleancache_failed_gets++;
1848c2ecf20Sopenharmony_ci		goto out;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	VM_BUG_ON_PAGE(!PageLocked(page), page);
1888c2ecf20Sopenharmony_ci	pool_id = page->mapping->host->i_sb->cleancache_poolid;
1898c2ecf20Sopenharmony_ci	if (pool_id < 0)
1908c2ecf20Sopenharmony_ci		goto out;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (cleancache_get_key(page->mapping->host, &key) < 0)
1938c2ecf20Sopenharmony_ci		goto out;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ret = cleancache_ops->get_page(pool_id, key, page->index, page);
1968c2ecf20Sopenharmony_ci	if (ret == 0)
1978c2ecf20Sopenharmony_ci		cleancache_succ_gets++;
1988c2ecf20Sopenharmony_ci	else
1998c2ecf20Sopenharmony_ci		cleancache_failed_gets++;
2008c2ecf20Sopenharmony_ciout:
2018c2ecf20Sopenharmony_ci	return ret;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_get_page);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * "Put" data from a page to cleancache and associate it with the
2078c2ecf20Sopenharmony_ci * (previously-obtained per-filesystem) poolid and the page's,
2088c2ecf20Sopenharmony_ci * inode and page index.  Page must be locked.  Note that a put_page
2098c2ecf20Sopenharmony_ci * always "succeeds", though a subsequent get_page may succeed or fail.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * The function has two checks before any action is taken - whether
2128c2ecf20Sopenharmony_ci * a backend is registered and whether the sb->cleancache_poolid
2138c2ecf20Sopenharmony_ci * is correct.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_civoid __cleancache_put_page(struct page *page)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	int pool_id;
2188c2ecf20Sopenharmony_ci	struct cleancache_filekey key = { .u.key = { 0 } };
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (!cleancache_ops) {
2218c2ecf20Sopenharmony_ci		cleancache_puts++;
2228c2ecf20Sopenharmony_ci		return;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	VM_BUG_ON_PAGE(!PageLocked(page), page);
2268c2ecf20Sopenharmony_ci	pool_id = page->mapping->host->i_sb->cleancache_poolid;
2278c2ecf20Sopenharmony_ci	if (pool_id >= 0 &&
2288c2ecf20Sopenharmony_ci		cleancache_get_key(page->mapping->host, &key) >= 0) {
2298c2ecf20Sopenharmony_ci		cleancache_ops->put_page(pool_id, key, page->index, page);
2308c2ecf20Sopenharmony_ci		cleancache_puts++;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_put_page);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/*
2368c2ecf20Sopenharmony_ci * Invalidate any data from cleancache associated with the poolid and the
2378c2ecf20Sopenharmony_ci * page's inode and page index so that a subsequent "get" will fail.
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * The function has two checks before any action is taken - whether
2408c2ecf20Sopenharmony_ci * a backend is registered and whether the sb->cleancache_poolid
2418c2ecf20Sopenharmony_ci * is correct.
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_civoid __cleancache_invalidate_page(struct address_space *mapping,
2448c2ecf20Sopenharmony_ci					struct page *page)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	/* careful... page->mapping is NULL sometimes when this is called */
2478c2ecf20Sopenharmony_ci	int pool_id = mapping->host->i_sb->cleancache_poolid;
2488c2ecf20Sopenharmony_ci	struct cleancache_filekey key = { .u.key = { 0 } };
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (!cleancache_ops)
2518c2ecf20Sopenharmony_ci		return;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (pool_id >= 0) {
2548c2ecf20Sopenharmony_ci		VM_BUG_ON_PAGE(!PageLocked(page), page);
2558c2ecf20Sopenharmony_ci		if (cleancache_get_key(mapping->host, &key) >= 0) {
2568c2ecf20Sopenharmony_ci			cleancache_ops->invalidate_page(pool_id,
2578c2ecf20Sopenharmony_ci					key, page->index);
2588c2ecf20Sopenharmony_ci			cleancache_invalidates++;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_invalidate_page);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/*
2658c2ecf20Sopenharmony_ci * Invalidate all data from cleancache associated with the poolid and the
2668c2ecf20Sopenharmony_ci * mappings's inode so that all subsequent gets to this poolid/inode
2678c2ecf20Sopenharmony_ci * will fail.
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * The function has two checks before any action is taken - whether
2708c2ecf20Sopenharmony_ci * a backend is registered and whether the sb->cleancache_poolid
2718c2ecf20Sopenharmony_ci * is correct.
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_civoid __cleancache_invalidate_inode(struct address_space *mapping)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	int pool_id = mapping->host->i_sb->cleancache_poolid;
2768c2ecf20Sopenharmony_ci	struct cleancache_filekey key = { .u.key = { 0 } };
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!cleancache_ops)
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (pool_id >= 0 && cleancache_get_key(mapping->host, &key) >= 0)
2828c2ecf20Sopenharmony_ci		cleancache_ops->invalidate_inode(pool_id, key);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_invalidate_inode);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/*
2878c2ecf20Sopenharmony_ci * Called by any cleancache-enabled filesystem at time of unmount;
2888c2ecf20Sopenharmony_ci * note that pool_id is surrendered and may be returned by a subsequent
2898c2ecf20Sopenharmony_ci * cleancache_init_fs or cleancache_init_shared_fs.
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_civoid __cleancache_invalidate_fs(struct super_block *sb)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	int pool_id;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	pool_id = sb->cleancache_poolid;
2968c2ecf20Sopenharmony_ci	sb->cleancache_poolid = CLEANCACHE_NO_POOL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (cleancache_ops && pool_id >= 0)
2998c2ecf20Sopenharmony_ci		cleancache_ops->invalidate_fs(pool_id);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cleancache_invalidate_fs);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic int __init init_cleancache(void)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
3068c2ecf20Sopenharmony_ci	struct dentry *root = debugfs_create_dir("cleancache", NULL);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	debugfs_create_u64("succ_gets", 0444, root, &cleancache_succ_gets);
3098c2ecf20Sopenharmony_ci	debugfs_create_u64("failed_gets", 0444, root, &cleancache_failed_gets);
3108c2ecf20Sopenharmony_ci	debugfs_create_u64("puts", 0444, root, &cleancache_puts);
3118c2ecf20Sopenharmony_ci	debugfs_create_u64("invalidates", 0444, root, &cleancache_invalidates);
3128c2ecf20Sopenharmony_ci#endif
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_cimodule_init(init_cleancache)
316