24e42e32d3
Change the 9p filesystem to take account of the changes to fscache's indexing rewrite and reenable caching in 9p. The following changes have been made: (1) The fscache_netfs struct is no more, and there's no need to register the filesystem as a whole. (2) The session cookie is now an fscache_volume cookie, allocated with fscache_acquire_volume(). That takes three parameters: a string representing the "volume" in the index, a string naming the cache to use (or NULL) and a u64 that conveys coherency metadata for the volume. For 9p, I've made it render the volume name string as: "9p,<devname>,<cachetag>" where the cachetag is replaced by the aname if it wasn't supplied. This probably needs rethinking a bit as the aname can have slashes in it. It might be better to hash the cachetag and use the hash or I could substitute commas for the slashes or something. (3) The fscache_cookie_def is no more and needed information is passed directly to fscache_acquire_cookie(). The cache no longer calls back into the filesystem, but rather metadata changes are indicated at other times. fscache_acquire_cookie() is passed the same keying and coherency information as before. (4) The functions to set/reset/flush cookies are removed and fscache_use_cookie() and fscache_unuse_cookie() are used instead. fscache_use_cookie() is passed a flag to indicate if the cookie is opened for writing. fscache_unuse_cookie() is passed updates for the metadata if we changed it (ie. if the file was opened for writing). These are called when the file is opened or closed. (5) wait_on_page_bit[_killable]() is replaced with the specific wait functions for the bits waited upon. (6) I've got rid of some of the 9p-specific cache helper functions and called things like fscache_relinquish_cookie() directly as they'll optimise away if v9fs_inode_cookie() returns an unconditional NULL (which will be the case if CONFIG_9P_FSCACHE=n). (7) v9fs_vfs_setattr() is made to call fscache_resize() to change the size of the cache object. Notes: (A) We should call fscache_invalidate() if we detect that the server's copy of a file got changed by a third party, but I don't know where to do that. We don't need to do that when allocating the cookie as we get a check-and-invalidate when we initially bind to the cache object. (B) The copy-to-cache-on-writeback side of things will be handled in separate patch. Changes ======= ver #3: - Canonicalise the cookie key and coherency data to make them endianness-independent. ver #2: - Use gfpflags_allow_blocking() rather than using flag directly. - fscache_acquire_volume() now returns errors. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Jeff Layton <jlayton@kernel.org> Tested-by: Dominique Martinet <asmadeus@codewreck.org> cc: Eric Van Hensbergen <ericvh@gmail.com> cc: Latchesar Ionkov <lucho@ionkov.net> cc: v9fs-developer@lists.sourceforge.net cc: linux-cachefs@redhat.com Link: https://lore.kernel.org/r/163819664645.215744.1555314582005286846.stgit@warthog.procyon.org.uk/ # v1 Link: https://lore.kernel.org/r/163906975017.143852.3459573173204394039.stgit@warthog.procyon.org.uk/ # v2 Link: https://lore.kernel.org/r/163967178512.1823006.17377493641569138183.stgit@warthog.procyon.org.uk/ # v3 Link: https://lore.kernel.org/r/164021573143.640689.3977487095697717967.stgit@warthog.procyon.org.uk/ # v4
251 lines
5.2 KiB
C
251 lines
5.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* This file contains vfs directory ops for the 9P2000 protocol.
|
|
*
|
|
* Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
|
|
* Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/inet.h>
|
|
#include <linux/idr.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/fscache.h>
|
|
#include <net/9p/9p.h>
|
|
#include <net/9p/client.h>
|
|
|
|
#include "v9fs.h"
|
|
#include "v9fs_vfs.h"
|
|
#include "fid.h"
|
|
|
|
/**
|
|
* struct p9_rdir - readdir accounting
|
|
* @head: start offset of current dirread buffer
|
|
* @tail: end offset of current dirread buffer
|
|
* @buf: dirread buffer
|
|
*
|
|
* private structure for keeping track of readdir
|
|
* allocated on demand
|
|
*/
|
|
|
|
struct p9_rdir {
|
|
int head;
|
|
int tail;
|
|
uint8_t buf[];
|
|
};
|
|
|
|
/**
|
|
* dt_type - return file type
|
|
* @mistat: mistat structure
|
|
*
|
|
*/
|
|
|
|
static inline int dt_type(struct p9_wstat *mistat)
|
|
{
|
|
unsigned long perm = mistat->mode;
|
|
int rettype = DT_REG;
|
|
|
|
if (perm & P9_DMDIR)
|
|
rettype = DT_DIR;
|
|
if (perm & P9_DMSYMLINK)
|
|
rettype = DT_LNK;
|
|
|
|
return rettype;
|
|
}
|
|
|
|
/**
|
|
* v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
|
|
* @filp: opened file structure
|
|
* @buflen: Length in bytes of buffer to allocate
|
|
*
|
|
*/
|
|
|
|
static struct p9_rdir *v9fs_alloc_rdir_buf(struct file *filp, int buflen)
|
|
{
|
|
struct p9_fid *fid = filp->private_data;
|
|
|
|
if (!fid->rdir)
|
|
fid->rdir = kzalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
|
|
return fid->rdir;
|
|
}
|
|
|
|
/**
|
|
* v9fs_dir_readdir - iterate through a directory
|
|
* @file: opened file structure
|
|
* @ctx: actor we feed the entries to
|
|
*
|
|
*/
|
|
|
|
static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx)
|
|
{
|
|
bool over;
|
|
struct p9_wstat st;
|
|
int err = 0;
|
|
struct p9_fid *fid;
|
|
int buflen;
|
|
struct p9_rdir *rdir;
|
|
struct kvec kvec;
|
|
|
|
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
fid = file->private_data;
|
|
|
|
buflen = fid->clnt->msize - P9_IOHDRSZ;
|
|
|
|
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
if (!rdir)
|
|
return -ENOMEM;
|
|
kvec.iov_base = rdir->buf;
|
|
kvec.iov_len = buflen;
|
|
|
|
while (1) {
|
|
if (rdir->tail == rdir->head) {
|
|
struct iov_iter to;
|
|
int n;
|
|
|
|
iov_iter_kvec(&to, READ, &kvec, 1, buflen);
|
|
n = p9_client_read(file->private_data, ctx->pos, &to,
|
|
&err);
|
|
if (err)
|
|
return err;
|
|
if (n == 0)
|
|
return 0;
|
|
|
|
rdir->head = 0;
|
|
rdir->tail = n;
|
|
}
|
|
while (rdir->head < rdir->tail) {
|
|
err = p9stat_read(fid->clnt, rdir->buf + rdir->head,
|
|
rdir->tail - rdir->head, &st);
|
|
if (err <= 0) {
|
|
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
return -EIO;
|
|
}
|
|
|
|
over = !dir_emit(ctx, st.name, strlen(st.name),
|
|
v9fs_qid2ino(&st.qid), dt_type(&st));
|
|
p9stat_free(&st);
|
|
if (over)
|
|
return 0;
|
|
|
|
rdir->head += err;
|
|
ctx->pos += err;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* v9fs_dir_readdir_dotl - iterate through a directory
|
|
* @file: opened file structure
|
|
* @ctx: actor we feed the entries to
|
|
*
|
|
*/
|
|
static int v9fs_dir_readdir_dotl(struct file *file, struct dir_context *ctx)
|
|
{
|
|
int err = 0;
|
|
struct p9_fid *fid;
|
|
int buflen;
|
|
struct p9_rdir *rdir;
|
|
struct p9_dirent curdirent;
|
|
|
|
p9_debug(P9_DEBUG_VFS, "name %pD\n", file);
|
|
fid = file->private_data;
|
|
|
|
buflen = fid->clnt->msize - P9_READDIRHDRSZ;
|
|
|
|
rdir = v9fs_alloc_rdir_buf(file, buflen);
|
|
if (!rdir)
|
|
return -ENOMEM;
|
|
|
|
while (1) {
|
|
if (rdir->tail == rdir->head) {
|
|
err = p9_client_readdir(fid, rdir->buf, buflen,
|
|
ctx->pos);
|
|
if (err <= 0)
|
|
return err;
|
|
|
|
rdir->head = 0;
|
|
rdir->tail = err;
|
|
}
|
|
|
|
while (rdir->head < rdir->tail) {
|
|
|
|
err = p9dirent_read(fid->clnt, rdir->buf + rdir->head,
|
|
rdir->tail - rdir->head,
|
|
&curdirent);
|
|
if (err < 0) {
|
|
p9_debug(P9_DEBUG_VFS, "returned %d\n", err);
|
|
return -EIO;
|
|
}
|
|
|
|
if (!dir_emit(ctx, curdirent.d_name,
|
|
strlen(curdirent.d_name),
|
|
v9fs_qid2ino(&curdirent.qid),
|
|
curdirent.d_type))
|
|
return 0;
|
|
|
|
ctx->pos = curdirent.d_off;
|
|
rdir->head += err;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* v9fs_dir_release - close a directory
|
|
* @inode: inode of the directory
|
|
* @filp: file pointer to a directory
|
|
*
|
|
*/
|
|
|
|
int v9fs_dir_release(struct inode *inode, struct file *filp)
|
|
{
|
|
struct v9fs_inode *v9inode = V9FS_I(inode);
|
|
struct p9_fid *fid;
|
|
__le32 version;
|
|
loff_t i_size;
|
|
|
|
fid = filp->private_data;
|
|
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
|
inode, filp, fid ? fid->fid : -1);
|
|
if (fid) {
|
|
spin_lock(&inode->i_lock);
|
|
hlist_del(&fid->ilist);
|
|
spin_unlock(&inode->i_lock);
|
|
p9_client_clunk(fid);
|
|
}
|
|
|
|
if ((filp->f_mode & FMODE_WRITE)) {
|
|
version = cpu_to_le32(v9inode->qid.version);
|
|
i_size = i_size_read(inode);
|
|
fscache_unuse_cookie(v9fs_inode_cookie(v9inode),
|
|
&version, &i_size);
|
|
} else {
|
|
fscache_unuse_cookie(v9fs_inode_cookie(v9inode), NULL, NULL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const struct file_operations v9fs_dir_operations = {
|
|
.read = generic_read_dir,
|
|
.llseek = generic_file_llseek,
|
|
.iterate_shared = v9fs_dir_readdir,
|
|
.open = v9fs_file_open,
|
|
.release = v9fs_dir_release,
|
|
};
|
|
|
|
const struct file_operations v9fs_dir_operations_dotl = {
|
|
.read = generic_read_dir,
|
|
.llseek = generic_file_llseek,
|
|
.iterate_shared = v9fs_dir_readdir_dotl,
|
|
.open = v9fs_file_open,
|
|
.release = v9fs_dir_release,
|
|
.fsync = v9fs_file_fsync_dotl,
|
|
};
|