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 948f930eaSIlya Dryomov #include <linux/ceph/ceph_debug.h> 1048f930eaSIlya Dryomov 1199ccbd22SMilosz Tanski #include "super.h" 1299ccbd22SMilosz Tanski #include "cache.h" 1399ccbd22SMilosz Tanski 1499ccbd22SMilosz Tanski struct ceph_aux_inode { 15f6973c09SYan, Zheng u64 version; 169bbeab41SArnd Bergmann u64 mtime_sec; 179bbeab41SArnd Bergmann u64 mtime_nsec; 1899ccbd22SMilosz Tanski }; 1999ccbd22SMilosz Tanski 2099ccbd22SMilosz Tanski struct fscache_netfs ceph_cache_netfs = { 2199ccbd22SMilosz Tanski .name = "ceph", 2299ccbd22SMilosz Tanski .version = 0, 2399ccbd22SMilosz Tanski }; 2499ccbd22SMilosz Tanski 251d8f8360SYan, Zheng static DEFINE_MUTEX(ceph_fscache_lock); 261d8f8360SYan, Zheng static LIST_HEAD(ceph_fscache_list); 271d8f8360SYan, Zheng 281d8f8360SYan, Zheng struct ceph_fscache_entry { 291d8f8360SYan, Zheng struct list_head list; 301d8f8360SYan, Zheng struct fscache_cookie *fscache; 311d8f8360SYan, Zheng size_t uniq_len; 32402cb8ddSDavid Howells /* The following members must be last */ 33402cb8ddSDavid Howells struct ceph_fsid fsid; 341d8f8360SYan, Zheng char uniquifier[0]; 351d8f8360SYan, Zheng }; 361d8f8360SYan, Zheng 3799ccbd22SMilosz Tanski static const struct fscache_cookie_def ceph_fscache_fsid_object_def = { 3899ccbd22SMilosz Tanski .name = "CEPH.fsid", 3999ccbd22SMilosz Tanski .type = FSCACHE_COOKIE_TYPE_INDEX, 4099ccbd22SMilosz Tanski }; 4199ccbd22SMilosz Tanski 4257a35dfbSChengguang Xu int __init ceph_fscache_register(void) 4399ccbd22SMilosz Tanski { 4499ccbd22SMilosz Tanski return fscache_register_netfs(&ceph_cache_netfs); 4599ccbd22SMilosz Tanski } 4699ccbd22SMilosz Tanski 47971f0bdeSMilosz Tanski void ceph_fscache_unregister(void) 4899ccbd22SMilosz Tanski { 4999ccbd22SMilosz Tanski fscache_unregister_netfs(&ceph_cache_netfs); 5099ccbd22SMilosz Tanski } 5199ccbd22SMilosz Tanski 5299ccbd22SMilosz Tanski int ceph_fscache_register_fs(struct ceph_fs_client* fsc) 5399ccbd22SMilosz Tanski { 541d8f8360SYan, Zheng const struct ceph_fsid *fsid = &fsc->client->fsid; 551d8f8360SYan, Zheng const char *fscache_uniq = fsc->mount_options->fscache_uniq; 561d8f8360SYan, Zheng size_t uniq_len = fscache_uniq ? strlen(fscache_uniq) : 0; 571d8f8360SYan, Zheng struct ceph_fscache_entry *ent; 581d8f8360SYan, Zheng int err = 0; 591d8f8360SYan, Zheng 601d8f8360SYan, Zheng mutex_lock(&ceph_fscache_lock); 611d8f8360SYan, Zheng list_for_each_entry(ent, &ceph_fscache_list, list) { 621d8f8360SYan, Zheng if (memcmp(&ent->fsid, fsid, sizeof(*fsid))) 631d8f8360SYan, Zheng continue; 641d8f8360SYan, Zheng if (ent->uniq_len != uniq_len) 651d8f8360SYan, Zheng continue; 661d8f8360SYan, Zheng if (uniq_len && memcmp(ent->uniquifier, fscache_uniq, uniq_len)) 671d8f8360SYan, Zheng continue; 681d8f8360SYan, Zheng 691d8f8360SYan, Zheng pr_err("fscache cookie already registered for fsid %pU\n", fsid); 701d8f8360SYan, Zheng pr_err(" use fsc=%%s mount option to specify a uniquifier\n"); 711d8f8360SYan, Zheng err = -EBUSY; 721d8f8360SYan, Zheng goto out_unlock; 731d8f8360SYan, Zheng } 741d8f8360SYan, Zheng 751d8f8360SYan, Zheng ent = kzalloc(sizeof(*ent) + uniq_len, GFP_KERNEL); 761d8f8360SYan, Zheng if (!ent) { 771d8f8360SYan, Zheng err = -ENOMEM; 781d8f8360SYan, Zheng goto out_unlock; 791d8f8360SYan, Zheng } 801d8f8360SYan, Zheng 811d8f8360SYan, Zheng memcpy(&ent->fsid, fsid, sizeof(*fsid)); 821d8f8360SYan, Zheng if (uniq_len > 0) { 831d8f8360SYan, Zheng memcpy(&ent->uniquifier, fscache_uniq, uniq_len); 841d8f8360SYan, Zheng ent->uniq_len = uniq_len; 851d8f8360SYan, Zheng } 86402cb8ddSDavid Howells 87402cb8ddSDavid Howells fsc->fscache = fscache_acquire_cookie(ceph_cache_netfs.primary_index, 88402cb8ddSDavid Howells &ceph_fscache_fsid_object_def, 89402cb8ddSDavid Howells &ent->fsid, sizeof(ent->fsid) + uniq_len, 90402cb8ddSDavid Howells NULL, 0, 91ee1235a9SDavid Howells fsc, 0, true); 92402cb8ddSDavid Howells 93402cb8ddSDavid Howells if (fsc->fscache) { 941d8f8360SYan, Zheng ent->fscache = fsc->fscache; 951d8f8360SYan, Zheng list_add_tail(&ent->list, &ceph_fscache_list); 961d8f8360SYan, Zheng } else { 971d8f8360SYan, Zheng kfree(ent); 981d8f8360SYan, Zheng pr_err("unable to register fscache cookie for fsid %pU\n", 991d8f8360SYan, Zheng fsid); 1001d8f8360SYan, Zheng /* all other fs ignore this error */ 1011d8f8360SYan, Zheng } 1021d8f8360SYan, Zheng out_unlock: 1031d8f8360SYan, Zheng mutex_unlock(&ceph_fscache_lock); 1041d8f8360SYan, Zheng return err; 10599ccbd22SMilosz Tanski } 10699ccbd22SMilosz Tanski 10799ccbd22SMilosz Tanski static enum fscache_checkaux ceph_fscache_inode_check_aux( 108ee1235a9SDavid Howells void *cookie_netfs_data, const void *data, uint16_t dlen, 109ee1235a9SDavid Howells loff_t object_size) 11099ccbd22SMilosz Tanski { 11199ccbd22SMilosz Tanski struct ceph_aux_inode aux; 11299ccbd22SMilosz Tanski struct ceph_inode_info* ci = cookie_netfs_data; 11399ccbd22SMilosz Tanski struct inode* inode = &ci->vfs_inode; 11499ccbd22SMilosz Tanski 115ee1235a9SDavid Howells if (dlen != sizeof(aux) || 116ee1235a9SDavid Howells i_size_read(inode) != object_size) 11799ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OBSOLETE; 11899ccbd22SMilosz Tanski 11999ccbd22SMilosz Tanski memset(&aux, 0, sizeof(aux)); 120f6973c09SYan, Zheng aux.version = ci->i_version; 1219bbeab41SArnd Bergmann aux.mtime_sec = inode->i_mtime.tv_sec; 1229bbeab41SArnd Bergmann aux.mtime_nsec = inode->i_mtime.tv_nsec; 12399ccbd22SMilosz Tanski 12499ccbd22SMilosz Tanski if (memcmp(data, &aux, sizeof(aux)) != 0) 12599ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OBSOLETE; 12699ccbd22SMilosz Tanski 1274c069a58SChengguang Xu dout("ceph inode 0x%p cached okay\n", ci); 12899ccbd22SMilosz Tanski return FSCACHE_CHECKAUX_OKAY; 12999ccbd22SMilosz Tanski } 13099ccbd22SMilosz Tanski 13199ccbd22SMilosz Tanski static const struct fscache_cookie_def ceph_fscache_inode_object_def = { 13299ccbd22SMilosz Tanski .name = "CEPH.inode", 13399ccbd22SMilosz Tanski .type = FSCACHE_COOKIE_TYPE_DATAFILE, 13499ccbd22SMilosz Tanski .check_aux = ceph_fscache_inode_check_aux, 13599ccbd22SMilosz Tanski }; 13699ccbd22SMilosz Tanski 13746b59b2bSYan, Zheng void ceph_fscache_register_inode_cookie(struct inode *inode) 13899ccbd22SMilosz Tanski { 13946b59b2bSYan, Zheng struct ceph_inode_info *ci = ceph_inode(inode); 14046b59b2bSYan, Zheng struct ceph_fs_client *fsc = ceph_inode_to_client(inode); 141402cb8ddSDavid Howells struct ceph_aux_inode aux; 14299ccbd22SMilosz Tanski 14399ccbd22SMilosz Tanski /* No caching for filesystem */ 144d37b1d99SMarkus Elfring if (!fsc->fscache) 14599ccbd22SMilosz Tanski return; 14699ccbd22SMilosz Tanski 14799ccbd22SMilosz Tanski /* Only cache for regular files that are read only */ 14846b59b2bSYan, Zheng if (!S_ISREG(inode->i_mode)) 14999ccbd22SMilosz Tanski return; 15099ccbd22SMilosz Tanski 15146b59b2bSYan, Zheng inode_lock_nested(inode, I_MUTEX_CHILD); 15246b59b2bSYan, Zheng if (!ci->fscache) { 153402cb8ddSDavid Howells memset(&aux, 0, sizeof(aux)); 154402cb8ddSDavid Howells aux.version = ci->i_version; 1559bbeab41SArnd Bergmann aux.mtime_sec = inode->i_mtime.tv_sec; 1569bbeab41SArnd Bergmann aux.mtime_nsec = inode->i_mtime.tv_nsec; 15799ccbd22SMilosz Tanski ci->fscache = fscache_acquire_cookie(fsc->fscache, 15899ccbd22SMilosz Tanski &ceph_fscache_inode_object_def, 159402cb8ddSDavid Howells &ci->i_vino, sizeof(ci->i_vino), 160402cb8ddSDavid Howells &aux, sizeof(aux), 161ee1235a9SDavid Howells ci, i_size_read(inode), false); 16246b59b2bSYan, Zheng } 1635955102cSAl Viro inode_unlock(inode); 16499ccbd22SMilosz Tanski } 16599ccbd22SMilosz Tanski 16699ccbd22SMilosz Tanski void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci) 16799ccbd22SMilosz Tanski { 16899ccbd22SMilosz Tanski struct fscache_cookie* cookie; 16999ccbd22SMilosz Tanski 17099ccbd22SMilosz Tanski if ((cookie = ci->fscache) == NULL) 17199ccbd22SMilosz Tanski return; 17299ccbd22SMilosz Tanski 17399ccbd22SMilosz Tanski ci->fscache = NULL; 17499ccbd22SMilosz Tanski 17599ccbd22SMilosz Tanski fscache_uncache_all_inode_pages(cookie, &ci->vfs_inode); 176402cb8ddSDavid Howells fscache_relinquish_cookie(cookie, &ci->i_vino, false); 17799ccbd22SMilosz Tanski } 17899ccbd22SMilosz Tanski 17946b59b2bSYan, Zheng static bool ceph_fscache_can_enable(void *data) 18046b59b2bSYan, Zheng { 18146b59b2bSYan, Zheng struct inode *inode = data; 18246b59b2bSYan, Zheng return !inode_is_open_for_write(inode); 18346b59b2bSYan, Zheng } 18446b59b2bSYan, Zheng 18546b59b2bSYan, Zheng void ceph_fscache_file_set_cookie(struct inode *inode, struct file *filp) 18646b59b2bSYan, Zheng { 18746b59b2bSYan, Zheng struct ceph_inode_info *ci = ceph_inode(inode); 18846b59b2bSYan, Zheng 18946b59b2bSYan, Zheng if (!fscache_cookie_valid(ci->fscache)) 19046b59b2bSYan, Zheng return; 19146b59b2bSYan, Zheng 19246b59b2bSYan, Zheng if (inode_is_open_for_write(inode)) { 19346b59b2bSYan, Zheng dout("fscache_file_set_cookie %p %p disabling cache\n", 19446b59b2bSYan, Zheng inode, filp); 195402cb8ddSDavid Howells fscache_disable_cookie(ci->fscache, &ci->i_vino, false); 19646b59b2bSYan, Zheng fscache_uncache_all_inode_pages(ci->fscache, inode); 19746b59b2bSYan, Zheng } else { 198ee1235a9SDavid Howells fscache_enable_cookie(ci->fscache, &ci->i_vino, i_size_read(inode), 199402cb8ddSDavid Howells ceph_fscache_can_enable, inode); 20046b59b2bSYan, Zheng if (fscache_cookie_enabled(ci->fscache)) { 2010fbc5360SColin Ian King dout("fscache_file_set_cookie %p %p enabling cache\n", 20246b59b2bSYan, Zheng inode, filp); 20346b59b2bSYan, Zheng } 20446b59b2bSYan, Zheng } 20546b59b2bSYan, Zheng } 20646b59b2bSYan, Zheng 207dd2bc473SYan, Zheng static void ceph_readpage_from_fscache_complete(struct page *page, void *data, int error) 20899ccbd22SMilosz Tanski { 20999ccbd22SMilosz Tanski if (!error) 21099ccbd22SMilosz Tanski SetPageUptodate(page); 21199ccbd22SMilosz Tanski 21299ccbd22SMilosz Tanski unlock_page(page); 21399ccbd22SMilosz Tanski } 21499ccbd22SMilosz Tanski 2153b33f692SZhang Zhuoyu static inline bool cache_valid(struct ceph_inode_info *ci) 21699ccbd22SMilosz Tanski { 217f7f7e7a0SYan, Zheng return ci->i_fscache_gen == ci->i_rdcache_gen; 21899ccbd22SMilosz Tanski } 21999ccbd22SMilosz Tanski 22099ccbd22SMilosz Tanski 22199ccbd22SMilosz Tanski /* Atempt to read from the fscache, 22299ccbd22SMilosz Tanski * 22399ccbd22SMilosz Tanski * This function is called from the readpage_nounlock context. DO NOT attempt to 22499ccbd22SMilosz Tanski * unlock the page here (or in the callback). 22599ccbd22SMilosz Tanski */ 22699ccbd22SMilosz Tanski int ceph_readpage_from_fscache(struct inode *inode, struct page *page) 22799ccbd22SMilosz Tanski { 22899ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 22999ccbd22SMilosz Tanski int ret; 23099ccbd22SMilosz Tanski 23199ccbd22SMilosz Tanski if (!cache_valid(ci)) 23299ccbd22SMilosz Tanski return -ENOBUFS; 23399ccbd22SMilosz Tanski 23499ccbd22SMilosz Tanski ret = fscache_read_or_alloc_page(ci->fscache, page, 235dd2bc473SYan, Zheng ceph_readpage_from_fscache_complete, NULL, 23699ccbd22SMilosz Tanski GFP_KERNEL); 23799ccbd22SMilosz Tanski 23899ccbd22SMilosz Tanski switch (ret) { 23999ccbd22SMilosz Tanski case 0: /* Page found */ 24099ccbd22SMilosz Tanski dout("page read submitted\n"); 24199ccbd22SMilosz Tanski return 0; 24299ccbd22SMilosz Tanski case -ENOBUFS: /* Pages were not found, and can't be */ 24399ccbd22SMilosz Tanski case -ENODATA: /* Pages were not found */ 24499ccbd22SMilosz Tanski dout("page/inode not in cache\n"); 24599ccbd22SMilosz Tanski return ret; 24699ccbd22SMilosz Tanski default: 24799ccbd22SMilosz Tanski dout("%s: unknown error ret = %i\n", __func__, ret); 24899ccbd22SMilosz Tanski return ret; 24999ccbd22SMilosz Tanski } 25099ccbd22SMilosz Tanski } 25199ccbd22SMilosz Tanski 25299ccbd22SMilosz Tanski int ceph_readpages_from_fscache(struct inode *inode, 25399ccbd22SMilosz Tanski struct address_space *mapping, 25499ccbd22SMilosz Tanski struct list_head *pages, 25599ccbd22SMilosz Tanski unsigned *nr_pages) 25699ccbd22SMilosz Tanski { 25799ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 25899ccbd22SMilosz Tanski int ret; 25999ccbd22SMilosz Tanski 26099ccbd22SMilosz Tanski if (!cache_valid(ci)) 26199ccbd22SMilosz Tanski return -ENOBUFS; 26299ccbd22SMilosz Tanski 26399ccbd22SMilosz Tanski ret = fscache_read_or_alloc_pages(ci->fscache, mapping, pages, nr_pages, 264dd2bc473SYan, Zheng ceph_readpage_from_fscache_complete, 26599ccbd22SMilosz Tanski NULL, mapping_gfp_mask(mapping)); 26699ccbd22SMilosz Tanski 26799ccbd22SMilosz Tanski switch (ret) { 26899ccbd22SMilosz Tanski case 0: /* All pages found */ 26999ccbd22SMilosz Tanski dout("all-page read submitted\n"); 27099ccbd22SMilosz Tanski return 0; 27199ccbd22SMilosz Tanski case -ENOBUFS: /* Some pages were not found, and can't be */ 27299ccbd22SMilosz Tanski case -ENODATA: /* some pages were not found */ 27399ccbd22SMilosz Tanski dout("page/inode not in cache\n"); 27499ccbd22SMilosz Tanski return ret; 27599ccbd22SMilosz Tanski default: 27699ccbd22SMilosz Tanski dout("%s: unknown error ret = %i\n", __func__, ret); 27799ccbd22SMilosz Tanski return ret; 27899ccbd22SMilosz Tanski } 27999ccbd22SMilosz Tanski } 28099ccbd22SMilosz Tanski 28199ccbd22SMilosz Tanski void ceph_readpage_to_fscache(struct inode *inode, struct page *page) 28299ccbd22SMilosz Tanski { 28399ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 28499ccbd22SMilosz Tanski int ret; 28599ccbd22SMilosz Tanski 2869b8dd1e8SMilosz Tanski if (!PageFsCache(page)) 2879b8dd1e8SMilosz Tanski return; 2889b8dd1e8SMilosz Tanski 28999ccbd22SMilosz Tanski if (!cache_valid(ci)) 29099ccbd22SMilosz Tanski return; 29199ccbd22SMilosz Tanski 292ee1235a9SDavid Howells ret = fscache_write_page(ci->fscache, page, i_size_read(inode), 293ee1235a9SDavid Howells GFP_KERNEL); 29499ccbd22SMilosz Tanski if (ret) 29599ccbd22SMilosz Tanski fscache_uncache_page(ci->fscache, page); 29699ccbd22SMilosz Tanski } 29799ccbd22SMilosz Tanski 29899ccbd22SMilosz Tanski void ceph_invalidate_fscache_page(struct inode* inode, struct page *page) 29999ccbd22SMilosz Tanski { 30099ccbd22SMilosz Tanski struct ceph_inode_info *ci = ceph_inode(inode); 30199ccbd22SMilosz Tanski 302ffc79664SMilosz Tanski if (!PageFsCache(page)) 303ffc79664SMilosz Tanski return; 304ffc79664SMilosz Tanski 30599ccbd22SMilosz Tanski fscache_wait_on_page_write(ci->fscache, page); 30699ccbd22SMilosz Tanski fscache_uncache_page(ci->fscache, page); 30799ccbd22SMilosz Tanski } 30899ccbd22SMilosz Tanski 30999ccbd22SMilosz Tanski void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc) 31099ccbd22SMilosz Tanski { 3111d8f8360SYan, Zheng if (fscache_cookie_valid(fsc->fscache)) { 3121d8f8360SYan, Zheng struct ceph_fscache_entry *ent; 3131d8f8360SYan, Zheng bool found = false; 3141d8f8360SYan, Zheng 3151d8f8360SYan, Zheng mutex_lock(&ceph_fscache_lock); 3161d8f8360SYan, Zheng list_for_each_entry(ent, &ceph_fscache_list, list) { 3171d8f8360SYan, Zheng if (ent->fscache == fsc->fscache) { 3181d8f8360SYan, Zheng list_del(&ent->list); 3191d8f8360SYan, Zheng kfree(ent); 3201d8f8360SYan, Zheng found = true; 3211d8f8360SYan, Zheng break; 3221d8f8360SYan, Zheng } 3231d8f8360SYan, Zheng } 3241d8f8360SYan, Zheng WARN_ON_ONCE(!found); 3251d8f8360SYan, Zheng mutex_unlock(&ceph_fscache_lock); 3261d8f8360SYan, Zheng 327402cb8ddSDavid Howells __fscache_relinquish_cookie(fsc->fscache, NULL, false); 3281d8f8360SYan, Zheng } 32999ccbd22SMilosz Tanski fsc->fscache = NULL; 33099ccbd22SMilosz Tanski } 33199ccbd22SMilosz Tanski 332f7f7e7a0SYan, Zheng /* 333f7f7e7a0SYan, Zheng * caller should hold CEPH_CAP_FILE_{RD,CACHE} 334f7f7e7a0SYan, Zheng */ 335f7f7e7a0SYan, Zheng void ceph_fscache_revalidate_cookie(struct ceph_inode_info *ci) 33699ccbd22SMilosz Tanski { 337f7f7e7a0SYan, Zheng if (cache_valid(ci)) 338e81568ebSMilosz Tanski return; 339e81568ebSMilosz Tanski 340f7f7e7a0SYan, Zheng /* resue i_truncate_mutex. There should be no pending 341f7f7e7a0SYan, Zheng * truncate while the caller holds CEPH_CAP_FILE_RD */ 342f7f7e7a0SYan, Zheng mutex_lock(&ci->i_truncate_mutex); 343f7f7e7a0SYan, Zheng if (!cache_valid(ci)) { 344402cb8ddSDavid Howells if (fscache_check_consistency(ci->fscache, &ci->i_vino)) 345f7f7e7a0SYan, Zheng fscache_invalidate(ci->fscache); 346f7f7e7a0SYan, Zheng spin_lock(&ci->i_ceph_lock); 347f7f7e7a0SYan, Zheng ci->i_fscache_gen = ci->i_rdcache_gen; 348f7f7e7a0SYan, Zheng spin_unlock(&ci->i_ceph_lock); 34999ccbd22SMilosz Tanski } 350f7f7e7a0SYan, Zheng mutex_unlock(&ci->i_truncate_mutex); 35199ccbd22SMilosz Tanski } 352