linux/fs/cifs/fscache.c
David Howells bc899ee1c8 netfs: Add a netfs inode context
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
2022-03-18 09:29:05 +00:00

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;
}