Commit 65294c1f authored by Jeff Layton's avatar Jeff Layton Committed by J. Bruce Fields

nfsd: add a new struct file caching facility to nfsd

Currently, NFSv2/3 reads and writes have to open a file, do the read or
write and then close it again for each RPC. This is highly inefficient,
especially when the underlying filesystem has a relatively slow open
routine.

This patch adds a new open file cache to knfsd. Rather than doing an
open for each RPC, the read/write handlers can call into this cache to
see if there is one already there for the correct filehandle and
NFS_MAY_READ/WRITE flags.

If there isn't an entry, then we create a new one and attempt to
perform the open. If there is, then we wait until the entry is fully
instantiated and return it if it is at the end of the wait. If it's
not, then we attempt to take over construction.

Since the main goal is to speed up NFSv2/3 I/O, we don't want to
close these files on last put of these objects. We need to keep them
around for a little while since we never know when the next READ/WRITE
will come in.

Cache entries have a hardcoded 1s timeout, and we have a recurring
workqueue job that walks the cache and purges any entries that have
expired.
Signed-off-by: default avatarJeff Layton <jeff.layton@primarydata.com>
Signed-off-by: default avatarWeston Andros Adamson <dros@primarydata.com>
Signed-off-by: default avatarRichard Sharpe <richard.sharpe@primarydata.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 7239a40c
......@@ -3,6 +3,7 @@ config NFSD
tristate "NFS server support"
depends on INET
depends on FILE_LOCKING
depends on FSNOTIFY
select LOCKD
select SUNRPC
select EXPORTFS
......
......@@ -11,7 +11,8 @@ obj-$(CONFIG_NFSD) += nfsd.o
nfsd-y += trace.o
nfsd-y += nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
export.o auth.o lockd.o nfscache.o nfsxdr.o \
stats.o filecache.o
nfsd-$(CONFIG_NFSD_FAULT_INJECTION) += fault_inject.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
......
......@@ -22,6 +22,7 @@
#include "nfsfh.h"
#include "netns.h"
#include "pnfs.h"
#include "filecache.h"
#define NFSDDBG_FACILITY NFSDDBG_EXPORT
......@@ -232,6 +233,17 @@ static struct cache_head *expkey_alloc(void)
return NULL;
}
static void expkey_flush(void)
{
/*
* Take the nfsd_mutex here to ensure that the file cache is not
* destroyed while we're in the middle of flushing.
*/
mutex_lock(&nfsd_mutex);
nfsd_file_cache_purge();
mutex_unlock(&nfsd_mutex);
}
static const struct cache_detail svc_expkey_cache_template = {
.owner = THIS_MODULE,
.hash_size = EXPKEY_HASHMAX,
......@@ -244,6 +256,7 @@ static const struct cache_detail svc_expkey_cache_template = {
.init = expkey_init,
.update = expkey_update,
.alloc = expkey_alloc,
.flush = expkey_flush,
};
static int
......
This diff is collapsed.
#ifndef _FS_NFSD_FILECACHE_H
#define _FS_NFSD_FILECACHE_H
#include <linux/fsnotify_backend.h>
/*
* This is the fsnotify_mark container that nfsd attaches to the files that it
* is holding open. Note that we have a separate refcount here aside from the
* one in the fsnotify_mark. We only want a single fsnotify_mark attached to
* the inode, and for each nfsd_file to hold a reference to it.
*
* The fsnotify_mark is itself refcounted, but that's not sufficient to tell us
* how to put that reference. If there are still outstanding nfsd_files that
* reference the mark, then we would want to call fsnotify_put_mark on it.
* If there were not, then we'd need to call fsnotify_destroy_mark. Since we
* can't really tell the difference, we use the nfm_mark to keep track of how
* many nfsd_files hold references to the mark. When that counter goes to zero
* then we know to call fsnotify_destroy_mark on it.
*/
struct nfsd_file_mark {
struct fsnotify_mark nfm_mark;
atomic_t nfm_ref;
};
/*
* A representation of a file that has been opened by knfsd. These are hashed
* in the hashtable by inode pointer value. Note that this object doesn't
* hold a reference to the inode by itself, so the nf_inode pointer should
* never be dereferenced, only used for comparison.
*/
struct nfsd_file {
struct hlist_node nf_node;
struct list_head nf_lru;
struct rcu_head nf_rcu;
struct file *nf_file;
const struct cred *nf_cred;
#define NFSD_FILE_HASHED (0)
#define NFSD_FILE_PENDING (1)
#define NFSD_FILE_BREAK_READ (2)
#define NFSD_FILE_BREAK_WRITE (3)
#define NFSD_FILE_REFERENCED (4)
unsigned long nf_flags;
struct inode *nf_inode;
unsigned int nf_hashval;
atomic_t nf_ref;
unsigned char nf_may;
struct nfsd_file_mark *nf_mark;
};
int nfsd_file_cache_init(void);
void nfsd_file_cache_purge(void);
void nfsd_file_cache_shutdown(void);
void nfsd_file_put(struct nfsd_file *nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode);
bool nfsd_file_is_cached(struct inode *inode);
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp);
int nfsd_file_cache_stats_open(struct inode *, struct file *);
#endif /* _FS_NFSD_FILECACHE_H */
......@@ -27,6 +27,7 @@
#include "cache.h"
#include "vfs.h"
#include "netns.h"
#include "filecache.h"
#define NFSDDBG_FACILITY NFSDDBG_SVC
......@@ -313,6 +314,9 @@ static int nfsd_startup_generic(int nrservs)
if (nfsd_users++)
return 0;
ret = nfsd_file_cache_init();
if (ret)
goto dec_users;
/*
* Readahead param cache - will no-op if it already exists.
* (Note therefore results will be suboptimal if number of
......@@ -320,7 +324,7 @@ static int nfsd_startup_generic(int nrservs)
*/
ret = nfsd_racache_init(2*nrservs);
if (ret)
goto dec_users;
goto out_file_cache;
ret = nfs4_state_start();
if (ret)
......@@ -329,6 +333,8 @@ static int nfsd_startup_generic(int nrservs)
out_racache:
nfsd_racache_shutdown();
out_file_cache:
nfsd_file_cache_shutdown();
dec_users:
nfsd_users--;
return ret;
......@@ -340,6 +346,7 @@ static void nfsd_shutdown_generic(void)
return;
nfs4_state_shutdown();
nfsd_file_cache_shutdown();
nfsd_racache_shutdown();
}
......
......@@ -126,6 +126,8 @@ DEFINE_NFSD_ERR_EVENT(read_err);
DEFINE_NFSD_ERR_EVENT(write_err);
#include "state.h"
#include "filecache.h"
#include "vfs.h"
DECLARE_EVENT_CLASS(nfsd_stateid_class,
TP_PROTO(stateid_t *stp),
......@@ -164,6 +166,144 @@ DEFINE_STATEID_EVENT(layout_recall_done);
DEFINE_STATEID_EVENT(layout_recall_fail);
DEFINE_STATEID_EVENT(layout_recall_release);
#define show_nf_flags(val) \
__print_flags(val, "|", \
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \
{ 1 << NFSD_FILE_PENDING, "PENDING" }, \
{ 1 << NFSD_FILE_BREAK_READ, "BREAK_READ" }, \
{ 1 << NFSD_FILE_BREAK_WRITE, "BREAK_WRITE" }, \
{ 1 << NFSD_FILE_REFERENCED, "REFERENCED"})
/* FIXME: This should probably be fleshed out in the future. */
#define show_nf_may(val) \
__print_flags(val, "|", \
{ NFSD_MAY_READ, "READ" }, \
{ NFSD_MAY_WRITE, "WRITE" }, \
{ NFSD_MAY_NOT_BREAK_LEASE, "NOT_BREAK_LEASE" })
DECLARE_EVENT_CLASS(nfsd_file_class,
TP_PROTO(struct nfsd_file *nf),
TP_ARGS(nf),
TP_STRUCT__entry(
__field(unsigned int, nf_hashval)
__field(void *, nf_inode)
__field(int, nf_ref)
__field(unsigned long, nf_flags)
__field(unsigned char, nf_may)
__field(struct file *, nf_file)
),
TP_fast_assign(
__entry->nf_hashval = nf->nf_hashval;
__entry->nf_inode = nf->nf_inode;
__entry->nf_ref = atomic_read(&nf->nf_ref);
__entry->nf_flags = nf->nf_flags;
__entry->nf_may = nf->nf_may;
__entry->nf_file = nf->nf_file;
),
TP_printk("hash=0x%x inode=0x%p ref=%d flags=%s may=%s file=%p",
__entry->nf_hashval,
__entry->nf_inode,
__entry->nf_ref,
show_nf_flags(__entry->nf_flags),
show_nf_may(__entry->nf_may),
__entry->nf_file)
)
#define DEFINE_NFSD_FILE_EVENT(name) \
DEFINE_EVENT(nfsd_file_class, name, \
TP_PROTO(struct nfsd_file *nf), \
TP_ARGS(nf))
DEFINE_NFSD_FILE_EVENT(nfsd_file_alloc);
DEFINE_NFSD_FILE_EVENT(nfsd_file_put_final);
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash);
DEFINE_NFSD_FILE_EVENT(nfsd_file_put);
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_release_locked);
TRACE_EVENT(nfsd_file_acquire,
TP_PROTO(struct svc_rqst *rqstp, unsigned int hash,
struct inode *inode, unsigned int may_flags,
struct nfsd_file *nf, __be32 status),
TP_ARGS(rqstp, hash, inode, may_flags, nf, status),
TP_STRUCT__entry(
__field(__be32, xid)
__field(unsigned int, hash)
__field(void *, inode)
__field(unsigned int, may_flags)
__field(int, nf_ref)
__field(unsigned long, nf_flags)
__field(unsigned char, nf_may)
__field(struct file *, nf_file)
__field(__be32, status)
),
TP_fast_assign(
__entry->xid = rqstp->rq_xid;
__entry->hash = hash;
__entry->inode = inode;
__entry->may_flags = may_flags;
__entry->nf_ref = nf ? atomic_read(&nf->nf_ref) : 0;
__entry->nf_flags = nf ? nf->nf_flags : 0;
__entry->nf_may = nf ? nf->nf_may : 0;
__entry->nf_file = nf ? nf->nf_file : NULL;
__entry->status = status;
),
TP_printk("xid=0x%x hash=0x%x inode=0x%p may_flags=%s ref=%d nf_flags=%s nf_may=%s nf_file=0x%p status=%u",
be32_to_cpu(__entry->xid), __entry->hash, __entry->inode,
show_nf_may(__entry->may_flags), __entry->nf_ref,
show_nf_flags(__entry->nf_flags),
show_nf_may(__entry->nf_may), __entry->nf_file,
be32_to_cpu(__entry->status))
);
DECLARE_EVENT_CLASS(nfsd_file_search_class,
TP_PROTO(struct inode *inode, unsigned int hash, int found),
TP_ARGS(inode, hash, found),
TP_STRUCT__entry(
__field(struct inode *, inode)
__field(unsigned int, hash)
__field(int, found)
),
TP_fast_assign(
__entry->inode = inode;
__entry->hash = hash;
__entry->found = found;
),
TP_printk("hash=0x%x inode=0x%p found=%d", __entry->hash,
__entry->inode, __entry->found)
);
#define DEFINE_NFSD_FILE_SEARCH_EVENT(name) \
DEFINE_EVENT(nfsd_file_search_class, name, \
TP_PROTO(struct inode *inode, unsigned int hash, int found), \
TP_ARGS(inode, hash, found))
DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_close_inode_sync);
DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_close_inode);
DEFINE_NFSD_FILE_SEARCH_EVENT(nfsd_file_is_cached);
TRACE_EVENT(nfsd_file_fsnotify_handle_event,
TP_PROTO(struct inode *inode, u32 mask),
TP_ARGS(inode, mask),
TP_STRUCT__entry(
__field(struct inode *, inode)
__field(unsigned int, nlink)
__field(umode_t, mode)
__field(u32, mask)
),
TP_fast_assign(
__entry->inode = inode;
__entry->nlink = inode->i_nlink;
__entry->mode = inode->i_mode;
__entry->mask = mask;
),
TP_printk("inode=0x%p nlink=%u mode=0%ho mask=0x%x", __entry->inode,
__entry->nlink, __entry->mode, __entry->mask)
);
#endif /* _NFSD_TRACE_H */
#undef TRACE_INCLUDE_PATH
......
......@@ -699,7 +699,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
}
#endif /* CONFIG_NFSD_V3 */
static int nfsd_open_break_lease(struct inode *inode, int access)
int nfsd_open_break_lease(struct inode *inode, int access)
{
unsigned int mode;
......@@ -715,8 +715,8 @@ static int nfsd_open_break_lease(struct inode *inode, int access)
* and additional flags.
* N.B. After this call fhp needs an fh_put
*/
__be32
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
static __be32
__nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
int may_flags, struct file **filp)
{
struct path path;
......@@ -726,25 +726,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
__be32 err;
int host_err = 0;
validate_process_creds();
/*
* If we get here, then the client has already done an "open",
* and (hopefully) checked permission - so allow OWNER_OVERRIDE
* in case a chmod has now revoked permission.
*
* Arguably we should also allow the owner override for
* directories, but we never have and it doesn't seem to have
* caused anyone a problem. If we were to change this, note
* also that our filldir callbacks would need a variant of
* lookup_one_len that doesn't check permissions.
*/
if (type == S_IFREG)
may_flags |= NFSD_MAY_OWNER_OVERRIDE;
err = fh_verify(rqstp, fhp, type, may_flags);
if (err)
goto out;
path.mnt = fhp->fh_export->ex_path.mnt;
path.dentry = fhp->fh_dentry;
inode = d_inode(path.dentry);
......@@ -798,10 +779,50 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
out_nfserr:
err = nfserrno(host_err);
out:
return err;
}
__be32
nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
int may_flags, struct file **filp)
{
__be32 err;
validate_process_creds();
/*
* If we get here, then the client has already done an "open",
* and (hopefully) checked permission - so allow OWNER_OVERRIDE
* in case a chmod has now revoked permission.
*
* Arguably we should also allow the owner override for
* directories, but we never have and it doesn't seem to have
* caused anyone a problem. If we were to change this, note
* also that our filldir callbacks would need a variant of
* lookup_one_len that doesn't check permissions.
*/
if (type == S_IFREG)
may_flags |= NFSD_MAY_OWNER_OVERRIDE;
err = fh_verify(rqstp, fhp, type, may_flags);
if (!err)
err = __nfsd_open(rqstp, fhp, type, may_flags, filp);
validate_process_creds();
return err;
}
__be32
nfsd_open_verified(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type,
int may_flags, struct file **filp)
{
__be32 err;
validate_process_creds();
err = __nfsd_open(rqstp, fhp, type, may_flags, filp);
validate_process_creds();
return err;
}
struct raparms *
nfsd_init_raparms(struct file *file)
{
......
......@@ -75,8 +75,11 @@ __be32 do_nfsd_create(struct svc_rqst *, struct svc_fh *,
__be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
loff_t, unsigned long);
#endif /* CONFIG_NFSD_V3 */
int nfsd_open_break_lease(struct inode *, int);
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **);
__be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **);
struct raparms;
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment