162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CIFS filesystem cache interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Novell, Inc. 662306a36Sopenharmony_ci * Author(s): Suresh Jayaraman <sjayaraman@suse.de> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include "fscache.h" 1062306a36Sopenharmony_ci#include "cifsglob.h" 1162306a36Sopenharmony_ci#include "cifs_debug.h" 1262306a36Sopenharmony_ci#include "cifs_fs_sb.h" 1362306a36Sopenharmony_ci#include "cifsproto.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic void cifs_fscache_fill_volume_coherency( 1662306a36Sopenharmony_ci struct cifs_tcon *tcon, 1762306a36Sopenharmony_ci struct cifs_fscache_volume_coherency_data *cd) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci memset(cd, 0, sizeof(*cd)); 2062306a36Sopenharmony_ci cd->resource_id = cpu_to_le64(tcon->resource_id); 2162306a36Sopenharmony_ci cd->vol_create_time = tcon->vol_create_time; 2262306a36Sopenharmony_ci cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciint cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct cifs_fscache_volume_coherency_data cd; 2862306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 2962306a36Sopenharmony_ci struct fscache_volume *vcookie; 3062306a36Sopenharmony_ci const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; 3162306a36Sopenharmony_ci size_t slen, i; 3262306a36Sopenharmony_ci char *sharename; 3362306a36Sopenharmony_ci char *key; 3462306a36Sopenharmony_ci int ret = -ENOMEM; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci tcon->fscache = NULL; 3762306a36Sopenharmony_ci switch (sa->sa_family) { 3862306a36Sopenharmony_ci case AF_INET: 3962306a36Sopenharmony_ci case AF_INET6: 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci default: 4262306a36Sopenharmony_ci cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); 4362306a36Sopenharmony_ci return -EINVAL; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci memset(&key, 0, sizeof(key)); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci sharename = extract_sharename(tcon->tree_name); 4962306a36Sopenharmony_ci if (IS_ERR(sharename)) { 5062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); 5162306a36Sopenharmony_ci return PTR_ERR(sharename); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci slen = strlen(sharename); 5562306a36Sopenharmony_ci for (i = 0; i < slen; i++) 5662306a36Sopenharmony_ci if (sharename[i] == '/') 5762306a36Sopenharmony_ci sharename[i] = ';'; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); 6062306a36Sopenharmony_ci if (!key) 6162306a36Sopenharmony_ci goto out; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci cifs_fscache_fill_volume_coherency(tcon, &cd); 6462306a36Sopenharmony_ci vcookie = fscache_acquire_volume(key, 6562306a36Sopenharmony_ci NULL, /* preferred_cache */ 6662306a36Sopenharmony_ci &cd, sizeof(cd)); 6762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); 6862306a36Sopenharmony_ci if (IS_ERR(vcookie)) { 6962306a36Sopenharmony_ci if (vcookie != ERR_PTR(-EBUSY)) { 7062306a36Sopenharmony_ci ret = PTR_ERR(vcookie); 7162306a36Sopenharmony_ci goto out_2; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci pr_err("Cache volume key already in use (%s)\n", key); 7462306a36Sopenharmony_ci vcookie = NULL; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci tcon->fscache = vcookie; 7862306a36Sopenharmony_ci ret = 0; 7962306a36Sopenharmony_ciout_2: 8062306a36Sopenharmony_ci kfree(key); 8162306a36Sopenharmony_ciout: 8262306a36Sopenharmony_ci kfree(sharename); 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_civoid cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct cifs_fscache_volume_coherency_data cd; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci cifs_fscache_fill_volume_coherency(tcon, &cd); 9362306a36Sopenharmony_ci fscache_relinquish_volume(tcon->fscache, &cd, false); 9462306a36Sopenharmony_ci tcon->fscache = NULL; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_civoid cifs_fscache_get_inode_cookie(struct inode *inode) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct cifs_fscache_inode_coherency_data cd; 10062306a36Sopenharmony_ci struct cifsInodeInfo *cifsi = CIFS_I(inode); 10162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 10262306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci cifsi->netfs.cache = 10762306a36Sopenharmony_ci fscache_acquire_cookie(tcon->fscache, 0, 10862306a36Sopenharmony_ci &cifsi->uniqueid, sizeof(cifsi->uniqueid), 10962306a36Sopenharmony_ci &cd, sizeof(cd), 11062306a36Sopenharmony_ci i_size_read(&cifsi->netfs.inode)); 11162306a36Sopenharmony_ci if (cifsi->netfs.cache) 11262306a36Sopenharmony_ci mapping_set_release_always(inode->i_mapping); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci if (update) { 11862306a36Sopenharmony_ci struct cifs_fscache_inode_coherency_data cd; 11962306a36Sopenharmony_ci loff_t i_size = i_size_read(inode); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci cifs_fscache_fill_coherency(inode, &cd); 12262306a36Sopenharmony_ci fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid cifs_fscache_release_inode_cookie(struct inode *inode) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct cifsInodeInfo *cifsi = CIFS_I(inode); 13162306a36Sopenharmony_ci struct fscache_cookie *cookie = cifs_inode_cookie(inode); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (cookie) { 13462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); 13562306a36Sopenharmony_ci fscache_relinquish_cookie(cookie, false); 13662306a36Sopenharmony_ci cifsi->netfs.cache = NULL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * Fallback page reading interface. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistatic int fscache_fallback_read_page(struct inode *inode, struct page *page) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct netfs_cache_resources cres; 14662306a36Sopenharmony_ci struct fscache_cookie *cookie = cifs_inode_cookie(inode); 14762306a36Sopenharmony_ci struct iov_iter iter; 14862306a36Sopenharmony_ci struct bio_vec bvec; 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci memset(&cres, 0, sizeof(cres)); 15262306a36Sopenharmony_ci bvec_set_page(&bvec, page, PAGE_SIZE, 0); 15362306a36Sopenharmony_ci iov_iter_bvec(&iter, ITER_DEST, &bvec, 1, PAGE_SIZE); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = fscache_begin_read_operation(&cres, cookie); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, 16062306a36Sopenharmony_ci NULL, NULL); 16162306a36Sopenharmony_ci fscache_end_operation(&cres); 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Fallback page writing interface. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic int fscache_fallback_write_pages(struct inode *inode, loff_t start, size_t len, 16962306a36Sopenharmony_ci bool no_space_allocated_yet) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct netfs_cache_resources cres; 17262306a36Sopenharmony_ci struct fscache_cookie *cookie = cifs_inode_cookie(inode); 17362306a36Sopenharmony_ci struct iov_iter iter; 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci memset(&cres, 0, sizeof(cres)); 17762306a36Sopenharmony_ci iov_iter_xarray(&iter, ITER_SOURCE, &inode->i_mapping->i_pages, start, len); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = fscache_begin_write_operation(&cres, cookie); 18062306a36Sopenharmony_ci if (ret < 0) 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), 18462306a36Sopenharmony_ci no_space_allocated_yet); 18562306a36Sopenharmony_ci if (ret == 0) 18662306a36Sopenharmony_ci ret = fscache_write(&cres, start, &iter, NULL, NULL); 18762306a36Sopenharmony_ci fscache_end_operation(&cres); 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/* 19262306a36Sopenharmony_ci * Retrieve a page from FS-Cache 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ciint __cifs_readpage_from_fscache(struct inode *inode, struct page *page) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", 19962306a36Sopenharmony_ci __func__, cifs_inode_cookie(inode), page, inode); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = fscache_fallback_read_page(inode, page); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Read completed synchronously */ 20662306a36Sopenharmony_ci SetPageUptodate(page); 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_civoid __cifs_readahead_to_fscache(struct inode *inode, loff_t pos, size_t len) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci cifs_dbg(FYI, "%s: (fsc: %p, p: %llx, l: %zx, i: %p)\n", 21362306a36Sopenharmony_ci __func__, cifs_inode_cookie(inode), pos, len, inode); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci fscache_fallback_write_pages(inode, pos, len, true); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci * Query the cache occupancy. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ciint __cifs_fscache_query_occupancy(struct inode *inode, 22262306a36Sopenharmony_ci pgoff_t first, unsigned int nr_pages, 22362306a36Sopenharmony_ci pgoff_t *_data_first, 22462306a36Sopenharmony_ci unsigned int *_data_nr_pages) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct netfs_cache_resources cres; 22762306a36Sopenharmony_ci struct fscache_cookie *cookie = cifs_inode_cookie(inode); 22862306a36Sopenharmony_ci loff_t start, data_start; 22962306a36Sopenharmony_ci size_t len, data_len; 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = fscache_begin_read_operation(&cres, cookie); 23362306a36Sopenharmony_ci if (ret < 0) 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci start = first * PAGE_SIZE; 23762306a36Sopenharmony_ci len = nr_pages * PAGE_SIZE; 23862306a36Sopenharmony_ci ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, 23962306a36Sopenharmony_ci &data_start, &data_len); 24062306a36Sopenharmony_ci if (ret == 0) { 24162306a36Sopenharmony_ci *_data_first = data_start / PAGE_SIZE; 24262306a36Sopenharmony_ci *_data_nr_pages = len / PAGE_SIZE; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci fscache_end_operation(&cres); 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci} 248