11f327613SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 299ccbd22SMilosz Tanski /* 399ccbd22SMilosz Tanski * Ceph cache definitions. 499ccbd22SMilosz Tanski * 599ccbd22SMilosz Tanski * Copyright (C) 2013 by Adfin Solutions, Inc. All Rights Reserved. 699ccbd22SMilosz Tanski * Written by Milosz Tanski (milosz@adfin.com) 799ccbd22SMilosz Tanski */ 899ccbd22SMilosz Tanski 999ccbd22SMilosz Tanski #include "super.h" 1099ccbd22SMilosz Tanski #include "cache.h" 1199ccbd22SMilosz Tanski 1299ccbd22SMilosz Tanski struct ceph_aux_inode { 13f6973c09SYan, Zheng u64 version; 149bbeab41SArnd Bergmann u64 mtime_sec; 159bbeab41SArnd Bergmann u64 mtime_nsec; 1699ccbd22SMilosz Tanski }; 1799ccbd22SMilosz Tanski 1899ccbd22SMilosz Tanski struct fscache_netfs ceph_cache_netfs = { 1999ccbd22SMilosz Tanski .name = "ceph", 2099ccbd22SMilosz Tanski .version = 0, 2199ccbd22SMilosz Tanski }; 2299ccbd22SMilosz Tanski 231d8f8360SYan, Zheng static DEFINE_MUTEX(ceph_fscache_lock); 241d8f8360SYan, Zheng static LIST_HEAD(ceph_fscache_list); 251d8f8360SYan, Zheng 261d8f8360SYan, Zheng struct ceph_fscache_entry { 271d8f8360SYan, Zheng struct list_head list; 281d8f8360SYan, Zheng struct fscache_cookie *fscache; 291d8f8360SYan, Zheng size_t uniq_len; 30402cb8ddSDavid Howells /* The following members must be last */ 31402cb8ddSDavid Howells struct ceph_fsid fsid; 321d8f8360SYan, Zheng char uniquifier[0]; 331d8f8360SYan, Zheng }; 341d8f8360SYan, Zheng 3599ccbd22SMilosz Tanski static const struct fscache_cookie_def ceph_fscache_fsid_object_def = { 3699ccbd22SMilosz Tanski .name = "CEPH.fsid", 3799ccbd22SMilosz Tanski .type = FSCACHE_COOKIE_TYPE_INDEX, 3899ccbd22SMilosz Tanski }; 3999ccbd22SMilosz Tanski 4057a35dfbSChengguang Xu int __init ceph_fscache_register(void) 4199ccbd22SMilosz Tanski { 4299ccbd22SMilosz Tanski return fscache_register_netfs(&ceph_cache_netfs); 4399ccbd22SMilosz Tanski } 4499ccbd22SMilosz Tanski 45971f0bdeSMilosz Tanski void ceph_fscache_unregister(void) 4699ccbd22SMilosz Tanski { 4799ccbd22SMilosz Tanski fscache_unregister_netfs(&ceph_cache_netfs); 4899ccbd22SMilosz Tanski } 4999ccbd22SMilosz Tanski 5099ccbd22SMilosz Tanski int ceph_fscache_register_fs(struct ceph_fs_client* fsc) 5199ccbd22SMilosz Tanski { 521d8f8360SYan, Zheng const struct ceph_fsid *fsid = &fsc->client->fsid; 531d8f8360SYan, Zheng const char *fscache_uniq = fsc->mount_options->fscache_uniq; 541d8f8360SYan, Zheng size_t uniq_len = fscache_uniq ? strlen(fscache_uniq) : 0; 551d8f8360SYan, Zheng struct ceph_fscache_entry *ent; 561d8f8360SYan, Zheng int err = 0; 571d8f8360SYan, Zheng 581d8f8360SYan, Zheng mutex_lock(&ceph_fscache_lock); 591d8f8360SYan, Zheng list_for_each_entry(ent, &ceph_fscache_list, list) { 601d8f8360SYan, Zheng if (memcmp(&ent->fsid, fsid, sizeof(*fsid))) 611d8f8360SYan, Zheng continue; 621d8f8360SYan, Zheng if (ent->uniq_len != uniq_len) 631d8f8360SYan, Zheng continue; 641d8f8360SYan, Zheng if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len)) 651d8f8360SYan, Zheng continue; 661d8f8360SYan, Zheng 671d8f8360SYan, Zheng pr_err("fscache cookie already registered for fsid %pU\n", fsid); 681d8f8360SYan, Zheng pr_err(" use fsc=%%s mount option to specify a uniquifier\n"); 691d8f8360SYan, Zheng err = -EBUSY; 701d8f8360SYan, Zheng goto out_unlock; 711d8f8360SYan, Zheng } 721d8f8360SYan, Zheng 731d8f8360SYan, Zheng ent = kzalloc(sizeof(*ent) + uniq_len, GFP_KERNEL); 741d8f8360SYan, Zheng if (!ent) { 751d8f8360SYan, Zheng err = -ENOMEM; 761d8f8360SYan, Zheng goto out_unlock; 771d8f8360SYan, Zheng } 781d8f8360SYan, Zheng 791d8f8360SYan, Zheng memcpy(&ent->fsid, fsid, sizeof(*fsid)); 801d8f8360SYan, Zheng if (uniq_len > 0) { 811d8f8360SYan, Zheng memcpy(&ent->uniquifier, fscache_uniq, uniq_len); 821d8f8360SYan, Zheng ent->uniq_len = uniq_len; 831d8f8360SYan, Zheng } 84402cb8ddSDavid Howells 85402cb8ddSDavid Howells fsc->fscache = fscache_acquire_cookie(ceph_cache_netfs.primary_index, 86402cb8ddSDavid Howells &ceph_fscache_fsid_object_def, 87402cb8ddSDavid Howells &ent->fsid, sizeof(ent->fsid) + uniq_len, 88402cb8ddSDavid Howells NULL, 0, 89ee1235a9SDavid Howells fsc, 0, true); 90402cb8ddSDavid Howells 91402cb8ddSDavid Howells if (fsc->fscache) { 921d8f8360SYan, Zheng ent->fscache = fsc->fscache; 931d8f8360SYan, Zheng list_add_tail(&ent->list, &ceph_fscache_list); 941d8f8360SYan, Zheng } else { 951d8f8360SYan, Zheng kfree(ent); 961d8f8360SYan, Zheng pr_err("unable to register fscache cookie for fsid %pU\n", 971d8f8360SYan, Zheng fsid); 981d8f8360SYan, Zheng /* all other fs ignore this error */ 991d8f8360SYan, Zheng } 1001d8f8360SYan, Zheng out_unlock: 1011d8f8360SYan, Zheng mutex_unlock(&ceph_fscache_lock); 1021d8f8360SYan, Zheng return err; 10399ccbd22SMilosz Tanski } 10499ccbd22SMilosz Tanski 10599ccbd22SMilosz Tanski static enum fscache_checkaux ceph_fscache_inode_check_aux( 106ee1235a9SDavid Howells void *cookie_netfs_data, const void *data, uint16_t dlen, 107ee1235a9SDavid Howells loff_t object_size) 10899ccbd22SMilosz Tanski { 10999ccbd22SMilosz Tanski struct ceph_aux_inode aux; 11099ccbd22SMilosz Tanski struct ceph_inode_info* ci = cookie_netfs_data; 11199ccbd22SMilosz Tanski struct inode* inode = &ci->vfs_inode; 11299ccbd22SMilosz Tanski 113ee1235a9SDavid Howells if (dlen != sizeof(aux) || 114ee1235a9SDavid Howells i_size_read(inode) != object_size) 11599ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OBSOLETE; 11699ccbd22SMilosz Tanski 11799ccbd22SMilosz Tanski memset(&aux, 0, sizeof(aux)); 118f6973c09SYan, Zheng aux.version = ci->i_version; 1199bbeab41SArnd Bergmann aux.mtime_sec = inode->i_mtime.tv_sec; 1209bbeab41SArnd Bergmann aux.mtime_nsec = inode->i_mtime.tv_nsec; 12199ccbd22SMilosz Tanski 12299ccbd22SMilosz Tanski if (memcmp(data, &aux, sizeof(aux)) != 0) 12399ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OBSOLETE; 12499ccbd22SMilosz Tanski 1254c069a58SChengguang Xu dout("ceph inode 0x%p cached okay\n", ci); 12699ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OKAY; 12799ccbd22SMilosz Tanski } 12899ccbd22SMilosz Tanski 12999ccbd22SMilosz Tanski static const struct fscache_cookie_def ceph_fscache_inode_object_def = { 13099ccbd22SMilosz Tanski .name = "CEPH.inode", 13199ccbd22SMilosz Tanski .type = FSCACHE_COOKIE_TYPE_DATAFILE, 13299ccbd22SMilosz Tanski .check_aux = ceph_fscache_inode_check_aux, 13399ccbd22SMilosz Tanski }; 13499ccbd22SMilosz Tanski 13546b59b2bSYan, Zheng void ceph_fscache_register_inode_cookie(struct inode *inode) 13699ccbd22SMilosz Tanski { 13746b59b2bSYan, Zheng struct ceph_inode_info *ci = ceph_inode(inode); 13846b59b2bSYan, Zheng struct ceph_fs_client *fsc = ceph_inode_to_client(inode); 139402cb8ddSDavid Howells struct ceph_aux_inode aux; 14099ccbd22SMilosz Tanski 14199ccbd22SMilosz Tanski /* No caching for filesystem */ 142d37b1d99SMarkus Elfring if (!fsc->fscache) 14399ccbd22SMilosz Tanski return; 14499ccbd22SMilosz Tanski 14599ccbd22SMilosz Tanski /* Only cache for regular files that are read only */ 14646b59b2bSYan, Zheng if (!S_ISREG(inode->i_mode)) 14799ccbd22SMilosz Tanski return; 14899ccbd22SMilosz Tanski 14946b59b2bSYan, Zheng inode_lock_nested(inode, I_MUTEX_CHILD); 15046b59b2bSYan, Zheng if (!ci->fscache) { 151402cb8ddSDavid Howells memset(&aux, 0, sizeof(aux)); 152402cb8ddSDavid Howells aux.version = ci->i_version; 1539bbeab41SArnd Bergmann aux.mtime_sec = inode->i_mtime.tv_sec; 1549bbeab41SArnd Bergmann aux.mtime_nsec = inode->i_mtime.tv_nsec; 15599ccbd22SMilosz Tanski ci->fscache = fscache_acquire_cookie(fsc->fscache, 15699ccbd22SMilosz Tanski &ceph_fscache_inode_object_def, 157402cb8ddSDavid Howells &ci->i_vino, sizeof(ci->i_vino), 158402cb8ddSDavid Howells &aux, sizeof(aux), 159ee1235a9SDavid Howells ci, i_size_read(inode), false); 16046b59b2bSYan, Zheng } 1615955102cSAl Viro inode_unlock(inode); 16299ccbd22SMilosz Tanski } 16399ccbd22SMilosz Tanski 16499ccbd22SMilosz Tanski void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci) 16599ccbd22SMilosz Tanski { 16699ccbd22SMilosz Tanski struct fscache_cookie* cookie; 16799ccbd22SMilosz Tanski 16899ccbd22SMilosz Tanski if ((cookie = ci->fscache) == NULL) 16999ccbd22SMilosz Tanski return; 17099ccbd22SMilosz Tanski 17199ccbd22SMilosz Tanski ci->fscache = NULL; 17299ccbd22SMilosz Tanski 17399ccbd22SMilosz Tanski fscache_uncache_all_inode_pages(cookie, &ci->vfs_inode); 174402cb8ddSDavid Howells fscache_relinquish_cookie(cookie, &ci->i_vino, false); 17599ccbd22SMilosz Tanski } 17699ccbd22SMilosz Tanski 17746b59b2bSYan, Zheng static bool ceph_fscache_can_enable(void *data) 17846b59b2bSYan, Zheng { 17946b59b2bSYan, Zheng struct inode *inode = data; 18046b59b2bSYan, Zheng return !inode_is_open_for_write(inode); 18146b59b2bSYan, Zheng } 18246b59b2bSYan, Zheng 18346b59b2bSYan, Zheng void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp) 18446b59b2bSYan, Zheng { 18546b59b2bSYan, Zheng struct ceph_inode_info *ci = ceph_inode(inode); 18646b59b2bSYan, Zheng 18746b59b2bSYan, Zheng if (!fscache_cookie_valid(ci->fscache)) 18846b59b2bSYan, Zheng return; 18946b59b2bSYan, Zheng 19046b59b2bSYan, Zheng if (inode_is_open_for_write(inode)) { 19146b59b2bSYan, Zheng dout("fscache_file_set_cookie %p %p disabling cache\n", 19246b59b2bSYan, Zheng inode, filp); 193402cb8ddSDavid Howells fscache_disable_cookie(ci->fscache, &ci->i_vino, false); 19446b59b2bSYan, Zheng fscache_uncache_all_inode_pages(ci->fscache, inode); 19546b59b2bSYan, Zheng } else { 196ee1235a9SDavid Howells fscache_enable_cookie(ci->fscache, &ci->i_vino, i_size_read(inode), 197402cb8ddSDavid Howells ceph_fscache_can_enable, inode); 19846b59b2bSYan, Zheng if (fscache_cookie_enabled(ci->fscache)) { 1990fbc5360SColin Ian King dout("fscache_file_set_cookie %p %p enabling cache\n", 20046b59b2bSYan, Zheng inode, filp); 20146b59b2bSYan, Zheng } 20246b59b2bSYan, Zheng } 20346b59b2bSYan, Zheng } 20446b59b2bSYan, Zheng 205dd2bc473SYan, Zheng static void ceph_readpage_from_fscache_complete(struct page *page, void *data, int error) 20699ccbd22SMilosz Tanski { 20799ccbd22SMilosz Tanski if (!error) 20899ccbd22SMilosz Tanski SetPageUptodate(page); 20999ccbd22SMilosz Tanski 21099ccbd22SMilosz Tanski unlock_page(page); 21199ccbd22SMilosz Tanski } 21299ccbd22SMilosz Tanski 2133b33f692SZhang Zhuoyu static inline bool cache_valid(struct ceph_inode_info *ci) 21499ccbd22SMilosz Tanski { 215f7f7e7a0SYan, Zheng return ci->i_fscache_gen == ci->i_rdcache_gen; 21699ccbd22SMilosz Tanski } 21799ccbd22SMilosz Tanski 21899ccbd22SMilosz Tanski 21999ccbd22SMilosz Tanski /* Atempt to read from the fscache, 22099ccbd22SMilosz Tanski * 22199ccbd22SMilosz Tanski * This function is called from the readpage_nounlock context. DO NOT attempt to 22299ccbd22SMilosz Tanski * unlock the page here (or in the callback). 22399ccbd22SMilosz Tanski */ 22499ccbd22SMilosz Tanski int ceph_readpage_from_fscache(struct inode *inode, struct page *page) 22599ccbd22SMilosz Tanski { 22699ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 22799ccbd22SMilosz Tanski int ret; 22899ccbd22SMilosz Tanski 22999ccbd22SMilosz Tanski if (!cache_valid(ci)) 23099ccbd22SMilosz Tanski return -ENOBUFS; 23199ccbd22SMilosz Tanski 23299ccbd22SMilosz Tanski ret = fscache_read_or_alloc_page(ci->fscache, page, 233dd2bc473SYan, Zheng ceph_readpage_from_fscache_complete, NULL, 23499ccbd22SMilosz Tanski GFP_KERNEL); 23599ccbd22SMilosz Tanski 23699ccbd22SMilosz Tanski switch (ret) { 23799ccbd22SMilosz Tanski case 0: /* Page found */ 23899ccbd22SMilosz Tanski dout("page read submitted\n"); 23999ccbd22SMilosz Tanski return 0; 24099ccbd22SMilosz Tanski case -ENOBUFS: /* Pages were not found, and can't be */ 24199ccbd22SMilosz Tanski case -ENODATA: /* Pages were not found */ 24299ccbd22SMilosz Tanski dout("page/inode not in cache\n"); 24399ccbd22SMilosz Tanski return ret; 24499ccbd22SMilosz Tanski default: 24599ccbd22SMilosz Tanski dout("%s: unknown error ret = %i\n", __func__, ret); 24699ccbd22SMilosz Tanski return ret; 24799ccbd22SMilosz Tanski } 24899ccbd22SMilosz Tanski } 24999ccbd22SMilosz Tanski 25099ccbd22SMilosz Tanski int ceph_readpages_from_fscache(struct inode *inode, 25199ccbd22SMilosz Tanski struct address_space *mapping, 25299ccbd22SMilosz Tanski struct list_head *pages, 25399ccbd22SMilosz Tanski unsigned *nr_pages) 25499ccbd22SMilosz Tanski { 25599ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 25699ccbd22SMilosz Tanski int ret; 25799ccbd22SMilosz Tanski 25899ccbd22SMilosz Tanski if (!cache_valid(ci)) 25999ccbd22SMilosz Tanski return -ENOBUFS; 26099ccbd22SMilosz Tanski 26199ccbd22SMilosz Tanski ret = fscache_read_or_alloc_pages(ci->fscache, mapping, pages, nr_pages, 262dd2bc473SYan, Zheng ceph_readpage_from_fscache_complete, 26399ccbd22SMilosz Tanski NULL, mapping_gfp_mask(mapping)); 26499ccbd22SMilosz Tanski 26599ccbd22SMilosz Tanski switch (ret) { 26699ccbd22SMilosz Tanski case 0: /* All pages found */ 26799ccbd22SMilosz Tanski dout("all-page read submitted\n"); 26899ccbd22SMilosz Tanski return 0; 26999ccbd22SMilosz Tanski case -ENOBUFS: /* Some pages were not found, and can't be */ 27099ccbd22SMilosz Tanski case -ENODATA: /* some pages were not found */ 27199ccbd22SMilosz Tanski dout("page/inode not in cache\n"); 27299ccbd22SMilosz Tanski return ret; 27399ccbd22SMilosz Tanski default: 27499ccbd22SMilosz Tanski dout("%s: unknown error ret = %i\n", __func__, ret); 27599ccbd22SMilosz Tanski return ret; 27699ccbd22SMilosz Tanski } 27799ccbd22SMilosz Tanski } 27899ccbd22SMilosz Tanski 27999ccbd22SMilosz Tanski void ceph_readpage_to_fscache(struct inode *inode, struct page *page) 28099ccbd22SMilosz Tanski { 28199ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 28299ccbd22SMilosz Tanski int ret; 28399ccbd22SMilosz Tanski 2849b8dd1e8SMilosz Tanski if (!PageFsCache(page)) 2859b8dd1e8SMilosz Tanski return; 2869b8dd1e8SMilosz Tanski 28799ccbd22SMilosz Tanski if (!cache_valid(ci)) 28899ccbd22SMilosz Tanski return; 28999ccbd22SMilosz Tanski 290ee1235a9SDavid Howells ret = fscache_write_page(ci->fscache, page, i_size_read(inode), 291ee1235a9SDavid Howells GFP_KERNEL); 29299ccbd22SMilosz Tanski if (ret) 29399ccbd22SMilosz Tanski fscache_uncache_page(ci->fscache, page); 29499ccbd22SMilosz Tanski } 29599ccbd22SMilosz Tanski 29699ccbd22SMilosz Tanski void ceph_invalidate_fscache_page(struct inode* inode, struct page *page) 29799ccbd22SMilosz Tanski { 29899ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 29999ccbd22SMilosz Tanski 300ffc79664SMilosz Tanski if (!PageFsCache(page)) 301ffc79664SMilosz Tanski return; 302ffc79664SMilosz Tanski 30399ccbd22SMilosz Tanski fscache_wait_on_page_write(ci->fscache, page); 30499ccbd22SMilosz Tanski fscache_uncache_page(ci->fscache, page); 30599ccbd22SMilosz Tanski } 30699ccbd22SMilosz Tanski 30799ccbd22SMilosz Tanski void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc) 30899ccbd22SMilosz Tanski { 3091d8f8360SYan, Zheng if (fscache_cookie_valid(fsc->fscache)) { 3101d8f8360SYan, Zheng struct ceph_fscache_entry *ent; 3111d8f8360SYan, Zheng bool found = false; 3121d8f8360SYan, Zheng 3131d8f8360SYan, Zheng mutex_lock(&ceph_fscache_lock); 3141d8f8360SYan, Zheng list_for_each_entry(ent, &ceph_fscache_list, list) { 3151d8f8360SYan, Zheng if (ent->fscache == fsc->fscache) { 3161d8f8360SYan, Zheng list_del(&ent->list); 3171d8f8360SYan, Zheng kfree(ent); 3181d8f8360SYan, Zheng found = true; 3191d8f8360SYan, Zheng break; 3201d8f8360SYan, Zheng } 3211d8f8360SYan, Zheng } 3221d8f8360SYan, Zheng WARN_ON_ONCE(!found); 3231d8f8360SYan, Zheng mutex_unlock(&ceph_fscache_lock); 3241d8f8360SYan, Zheng 325402cb8ddSDavid Howells __fscache_relinquish_cookie(fsc->fscache, NULL, false); 3261d8f8360SYan, Zheng } 32799ccbd22SMilosz Tanski fsc->fscache = NULL; 32899ccbd22SMilosz Tanski } 32999ccbd22SMilosz Tanski 330f7f7e7a0SYan, Zheng /* 331f7f7e7a0SYan, Zheng * caller should hold CEPH_CAP_FILE_{RD,CACHE} 332f7f7e7a0SYan, Zheng */ 333f7f7e7a0SYan, Zheng void ceph_fscache_revalidate_cookie(struct ceph_inode_info *ci) 33499ccbd22SMilosz Tanski { 335f7f7e7a0SYan, Zheng if (cache_valid(ci)) 336e81568ebSMilosz Tanski return; 337e81568ebSMilosz Tanski 338f7f7e7a0SYan, Zheng /* resue i_truncate_mutex. There should be no pending 339f7f7e7a0SYan, Zheng * truncate while the caller holds CEPH_CAP_FILE_RD */ 340f7f7e7a0SYan, Zheng mutex_lock(&ci->i_truncate_mutex); 341f7f7e7a0SYan, Zheng if (!cache_valid(ci)) { 342402cb8ddSDavid Howells if (fscache_check_consistency(ci->fscache, &ci->i_vino)) 343f7f7e7a0SYan, Zheng fscache_invalidate(ci->fscache); 344f7f7e7a0SYan, Zheng spin_lock(&ci->i_ceph_lock); 345f7f7e7a0SYan, Zheng ci->i_fscache_gen = ci->i_rdcache_gen; 346f7f7e7a0SYan, Zheng spin_unlock(&ci->i_ceph_lock); 34799ccbd22SMilosz Tanski } 348f7f7e7a0SYan, Zheng mutex_unlock(&ci->i_truncate_mutex); 34999ccbd22SMilosz Tanski } 350