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