bc899ee1c8
Add a netfs_i_context struct that should be included in the network filesystem's own inode struct wrapper, directly after the VFS's inode struct, e.g.: struct my_inode { struct { /* These must be contiguous */ struct inode vfs_inode; struct netfs_i_context netfs_ctx; }; }; The netfs_i_context struct so far contains a single field for the network filesystem to use - the cache cookie: struct netfs_i_context { ... struct fscache_cookie *cache; }; Three functions are provided to help with this: (1) void netfs_i_context_init(struct inode *inode, const struct netfs_request_ops *ops); Initialise the netfs context and set the operations. (2) struct netfs_i_context *netfs_i_context(struct inode *inode); Find the netfs context from the VFS inode. (3) struct inode *netfs_inode(struct netfs_i_context *ctx); Find the VFS inode from the netfs context. Changes ======= ver #4) - Fix netfs_is_cache_enabled() to check cookie->cache_priv to see if a cache is present[3]. - Fix netfs_skip_folio_read() to zero out all of the page, not just some of it[3]. ver #3) - Split out the bit to move ceph cap-getting on readahead into ceph_init_request()[1]. - Stick in a comment to the netfs inode structs indicating the contiguity requirements[2]. ver #2) - Adjust documentation to match. - Use "#if IS_ENABLED()" in netfs_i_cookie(), not "#ifdef". - Move the cap check from ceph_readahead() to ceph_init_request() to be called from netfslib. - Remove ceph_readahead() and use netfs_readahead() directly instead. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Jeff Layton <jlayton@kernel.org> cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/8af0d47f17d89c06bbf602496dd845f2b0bf25b3.camel@kernel.org/ [1] Link: https://lore.kernel.org/r/beaf4f6a6c2575ed489adb14b257253c868f9a5c.camel@kernel.org/ [2] Link: https://lore.kernel.org/r/3536452.1647421585@warthog.procyon.org.uk/ [3] Link: https://lore.kernel.org/r/164622984545.3564931.15691742939278418580.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/164678213320.1200972.16807551936267647470.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/164692909854.2099075.9535537286264248057.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/306388.1647595110@warthog.procyon.org.uk/ # v4
254 lines
6.2 KiB
C
254 lines
6.2 KiB
C
// SPDX-License-Identifier: LGPL-2.1
|
|
/*
|
|
* CIFS filesystem cache interface
|
|
*
|
|
* Copyright (c) 2010 Novell, Inc.
|
|
* Author(s): Suresh Jayaraman <sjayaraman@suse.de>
|
|
*
|
|
*/
|
|
#include "fscache.h"
|
|
#include "cifsglob.h"
|
|
#include "cifs_debug.h"
|
|
#include "cifs_fs_sb.h"
|
|
#include "cifsproto.h"
|
|
|
|
static void cifs_fscache_fill_volume_coherency(
|
|
struct cifs_tcon *tcon,
|
|
struct cifs_fscache_volume_coherency_data *cd)
|
|
{
|
|
memset(cd, 0, sizeof(*cd));
|
|
cd->resource_id = cpu_to_le64(tcon->resource_id);
|
|
cd->vol_create_time = tcon->vol_create_time;
|
|
cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number);
|
|
}
|
|
|
|
int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
|
|
{
|
|
struct cifs_fscache_volume_coherency_data cd;
|
|
struct TCP_Server_Info *server = tcon->ses->server;
|
|
struct fscache_volume *vcookie;
|
|
const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
|
|
size_t slen, i;
|
|
char *sharename;
|
|
char *key;
|
|
int ret = -ENOMEM;
|
|
|
|
tcon->fscache = NULL;
|
|
switch (sa->sa_family) {
|
|
case AF_INET:
|
|
case AF_INET6:
|
|
break;
|
|
default:
|
|
cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family);
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
|
|
sharename = extract_sharename(tcon->treeName);
|
|
if (IS_ERR(sharename)) {
|
|
cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
slen = strlen(sharename);
|
|
for (i = 0; i < slen; i++)
|
|
if (sharename[i] == '/')
|
|
sharename[i] = ';';
|
|
|
|
key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename);
|
|
if (!key)
|
|
goto out;
|
|
|
|
cifs_fscache_fill_volume_coherency(tcon, &cd);
|
|
vcookie = fscache_acquire_volume(key,
|
|
NULL, /* preferred_cache */
|
|
&cd, sizeof(cd));
|
|
cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie);
|
|
if (IS_ERR(vcookie)) {
|
|
if (vcookie != ERR_PTR(-EBUSY)) {
|
|
ret = PTR_ERR(vcookie);
|
|
goto out_2;
|
|
}
|
|
pr_err("Cache volume key already in use (%s)\n", key);
|
|
vcookie = NULL;
|
|
}
|
|
|
|
tcon->fscache = vcookie;
|
|
ret = 0;
|
|
out_2:
|
|
kfree(key);
|
|
out:
|
|
kfree(sharename);
|
|
return ret;
|
|
}
|
|
|
|
void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon)
|
|
{
|
|
struct cifs_fscache_volume_coherency_data cd;
|
|
|
|
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache);
|
|
|
|
cifs_fscache_fill_volume_coherency(tcon, &cd);
|
|
fscache_relinquish_volume(tcon->fscache, &cd, false);
|
|
tcon->fscache = NULL;
|
|
}
|
|
|
|
void cifs_fscache_get_inode_cookie(struct inode *inode)
|
|
{
|
|
struct cifs_fscache_inode_coherency_data cd;
|
|
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
|
|
|
cifs_fscache_fill_coherency(&cifsi->vfs_inode, &cd);
|
|
|
|
cifsi->netfs_ctx.cache =
|
|
fscache_acquire_cookie(tcon->fscache, 0,
|
|
&cifsi->uniqueid, sizeof(cifsi->uniqueid),
|
|
&cd, sizeof(cd),
|
|
i_size_read(&cifsi->vfs_inode));
|
|
}
|
|
|
|
void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update)
|
|
{
|
|
if (update) {
|
|
struct cifs_fscache_inode_coherency_data cd;
|
|
loff_t i_size = i_size_read(inode);
|
|
|
|
cifs_fscache_fill_coherency(inode, &cd);
|
|
fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size);
|
|
} else {
|
|
fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL);
|
|
}
|
|
}
|
|
|
|
void cifs_fscache_release_inode_cookie(struct inode *inode)
|
|
{
|
|
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
|
|
|
if (cookie) {
|
|
cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie);
|
|
fscache_relinquish_cookie(cookie, false);
|
|
cifsi->netfs_ctx.cache = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fallback page reading interface.
|
|
*/
|
|
static int fscache_fallback_read_page(struct inode *inode, struct page *page)
|
|
{
|
|
struct netfs_cache_resources cres;
|
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
|
struct iov_iter iter;
|
|
struct bio_vec bvec[1];
|
|
int ret;
|
|
|
|
memset(&cres, 0, sizeof(cres));
|
|
bvec[0].bv_page = page;
|
|
bvec[0].bv_offset = 0;
|
|
bvec[0].bv_len = PAGE_SIZE;
|
|
iov_iter_bvec(&iter, READ, bvec, ARRAY_SIZE(bvec), PAGE_SIZE);
|
|
|
|
ret = fscache_begin_read_operation(&cres, cookie);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL,
|
|
NULL, NULL);
|
|
fscache_end_operation(&cres);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Fallback page writing interface.
|
|
*/
|
|
static int fscache_fallback_write_page(struct inode *inode, struct page *page,
|
|
bool no_space_allocated_yet)
|
|
{
|
|
struct netfs_cache_resources cres;
|
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
|
struct iov_iter iter;
|
|
struct bio_vec bvec[1];
|
|
loff_t start = page_offset(page);
|
|
size_t len = PAGE_SIZE;
|
|
int ret;
|
|
|
|
memset(&cres, 0, sizeof(cres));
|
|
bvec[0].bv_page = page;
|
|
bvec[0].bv_offset = 0;
|
|
bvec[0].bv_len = PAGE_SIZE;
|
|
iov_iter_bvec(&iter, WRITE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE);
|
|
|
|
ret = fscache_begin_write_operation(&cres, cookie);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode),
|
|
no_space_allocated_yet);
|
|
if (ret == 0)
|
|
ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL);
|
|
fscache_end_operation(&cres);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Retrieve a page from FS-Cache
|
|
*/
|
|
int __cifs_readpage_from_fscache(struct inode *inode, struct page *page)
|
|
{
|
|
int ret;
|
|
|
|
cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n",
|
|
__func__, cifs_inode_cookie(inode), page, inode);
|
|
|
|
ret = fscache_fallback_read_page(inode, page);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Read completed synchronously */
|
|
SetPageUptodate(page);
|
|
return 0;
|
|
}
|
|
|
|
void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)
|
|
{
|
|
cifs_dbg(FYI, "%s: (fsc: %p, p: %p, i: %p)\n",
|
|
__func__, cifs_inode_cookie(inode), page, inode);
|
|
|
|
fscache_fallback_write_page(inode, page, true);
|
|
}
|
|
|
|
/*
|
|
* Query the cache occupancy.
|
|
*/
|
|
int __cifs_fscache_query_occupancy(struct inode *inode,
|
|
pgoff_t first, unsigned int nr_pages,
|
|
pgoff_t *_data_first,
|
|
unsigned int *_data_nr_pages)
|
|
{
|
|
struct netfs_cache_resources cres;
|
|
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
|
|
loff_t start, data_start;
|
|
size_t len, data_len;
|
|
int ret;
|
|
|
|
ret = fscache_begin_read_operation(&cres, cookie);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
start = first * PAGE_SIZE;
|
|
len = nr_pages * PAGE_SIZE;
|
|
ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE,
|
|
&data_start, &data_len);
|
|
if (ret == 0) {
|
|
*_data_first = data_start / PAGE_SIZE;
|
|
*_data_nr_pages = len / PAGE_SIZE;
|
|
}
|
|
|
|
fscache_end_operation(&cres);
|
|
return ret;
|
|
}
|