![Jeff Layton](/assets/img/avatar_default.png)
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: Jeff Layton <jeff.layton@primarydata.com> Signed-off-by: Weston Andros Adamson <dros@primarydata.com> Signed-off-by: Richard Sharpe <richard.sharpe@primarydata.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
313 lines
8.5 KiB
C
313 lines
8.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2014 Christoph Hellwig.
|
|
*/
|
|
#undef TRACE_SYSTEM
|
|
#define TRACE_SYSTEM nfsd
|
|
|
|
#if !defined(_NFSD_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
|
#define _NFSD_TRACE_H
|
|
|
|
#include <linux/tracepoint.h>
|
|
#include "nfsfh.h"
|
|
|
|
TRACE_EVENT(nfsd_compound,
|
|
TP_PROTO(const struct svc_rqst *rqst,
|
|
u32 args_opcnt),
|
|
TP_ARGS(rqst, args_opcnt),
|
|
TP_STRUCT__entry(
|
|
__field(u32, xid)
|
|
__field(u32, args_opcnt)
|
|
),
|
|
TP_fast_assign(
|
|
__entry->xid = be32_to_cpu(rqst->rq_xid);
|
|
__entry->args_opcnt = args_opcnt;
|
|
),
|
|
TP_printk("xid=0x%08x opcnt=%u",
|
|
__entry->xid, __entry->args_opcnt)
|
|
)
|
|
|
|
TRACE_EVENT(nfsd_compound_status,
|
|
TP_PROTO(u32 args_opcnt,
|
|
u32 resp_opcnt,
|
|
__be32 status,
|
|
const char *name),
|
|
TP_ARGS(args_opcnt, resp_opcnt, status, name),
|
|
TP_STRUCT__entry(
|
|
__field(u32, args_opcnt)
|
|
__field(u32, resp_opcnt)
|
|
__field(int, status)
|
|
__string(name, name)
|
|
),
|
|
TP_fast_assign(
|
|
__entry->args_opcnt = args_opcnt;
|
|
__entry->resp_opcnt = resp_opcnt;
|
|
__entry->status = be32_to_cpu(status);
|
|
__assign_str(name, name);
|
|
),
|
|
TP_printk("op=%u/%u %s status=%d",
|
|
__entry->resp_opcnt, __entry->args_opcnt,
|
|
__get_str(name), __entry->status)
|
|
)
|
|
|
|
DECLARE_EVENT_CLASS(nfsd_io_class,
|
|
TP_PROTO(struct svc_rqst *rqstp,
|
|
struct svc_fh *fhp,
|
|
loff_t offset,
|
|
unsigned long len),
|
|
TP_ARGS(rqstp, fhp, offset, len),
|
|
TP_STRUCT__entry(
|
|
__field(u32, xid)
|
|
__field(u32, fh_hash)
|
|
__field(loff_t, offset)
|
|
__field(unsigned long, len)
|
|
),
|
|
TP_fast_assign(
|
|
__entry->xid = be32_to_cpu(rqstp->rq_xid);
|
|
__entry->fh_hash = knfsd_fh_hash(&fhp->fh_handle);
|
|
__entry->offset = offset;
|
|
__entry->len = len;
|
|
),
|
|
TP_printk("xid=0x%08x fh_hash=0x%08x offset=%lld len=%lu",
|
|
__entry->xid, __entry->fh_hash,
|
|
__entry->offset, __entry->len)
|
|
)
|
|
|
|
#define DEFINE_NFSD_IO_EVENT(name) \
|
|
DEFINE_EVENT(nfsd_io_class, nfsd_##name, \
|
|
TP_PROTO(struct svc_rqst *rqstp, \
|
|
struct svc_fh *fhp, \
|
|
loff_t offset, \
|
|
unsigned long len), \
|
|
TP_ARGS(rqstp, fhp, offset, len))
|
|
|
|
DEFINE_NFSD_IO_EVENT(read_start);
|
|
DEFINE_NFSD_IO_EVENT(read_splice);
|
|
DEFINE_NFSD_IO_EVENT(read_vector);
|
|
DEFINE_NFSD_IO_EVENT(read_io_done);
|
|
DEFINE_NFSD_IO_EVENT(read_done);
|
|
DEFINE_NFSD_IO_EVENT(write_start);
|
|
DEFINE_NFSD_IO_EVENT(write_opened);
|
|
DEFINE_NFSD_IO_EVENT(write_io_done);
|
|
DEFINE_NFSD_IO_EVENT(write_done);
|
|
|
|
DECLARE_EVENT_CLASS(nfsd_err_class,
|
|
TP_PROTO(struct svc_rqst *rqstp,
|
|
struct svc_fh *fhp,
|
|
loff_t offset,
|
|
int status),
|
|
TP_ARGS(rqstp, fhp, offset, status),
|
|
TP_STRUCT__entry(
|
|
__field(u32, xid)
|
|
__field(u32, fh_hash)
|
|
__field(loff_t, offset)
|
|
__field(int, status)
|
|
),
|
|
TP_fast_assign(
|
|
__entry->xid = be32_to_cpu(rqstp->rq_xid);
|
|
__entry->fh_hash = knfsd_fh_hash(&fhp->fh_handle);
|
|
__entry->offset = offset;
|
|
__entry->status = status;
|
|
),
|
|
TP_printk("xid=0x%08x fh_hash=0x%08x offset=%lld status=%d",
|
|
__entry->xid, __entry->fh_hash,
|
|
__entry->offset, __entry->status)
|
|
)
|
|
|
|
#define DEFINE_NFSD_ERR_EVENT(name) \
|
|
DEFINE_EVENT(nfsd_err_class, nfsd_##name, \
|
|
TP_PROTO(struct svc_rqst *rqstp, \
|
|
struct svc_fh *fhp, \
|
|
loff_t offset, \
|
|
int len), \
|
|
TP_ARGS(rqstp, fhp, offset, len))
|
|
|
|
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),
|
|
TP_ARGS(stp),
|
|
TP_STRUCT__entry(
|
|
__field(u32, cl_boot)
|
|
__field(u32, cl_id)
|
|
__field(u32, si_id)
|
|
__field(u32, si_generation)
|
|
),
|
|
TP_fast_assign(
|
|
__entry->cl_boot = stp->si_opaque.so_clid.cl_boot;
|
|
__entry->cl_id = stp->si_opaque.so_clid.cl_id;
|
|
__entry->si_id = stp->si_opaque.so_id;
|
|
__entry->si_generation = stp->si_generation;
|
|
),
|
|
TP_printk("client %08x:%08x stateid %08x:%08x",
|
|
__entry->cl_boot,
|
|
__entry->cl_id,
|
|
__entry->si_id,
|
|
__entry->si_generation)
|
|
)
|
|
|
|
#define DEFINE_STATEID_EVENT(name) \
|
|
DEFINE_EVENT(nfsd_stateid_class, nfsd_##name, \
|
|
TP_PROTO(stateid_t *stp), \
|
|
TP_ARGS(stp))
|
|
DEFINE_STATEID_EVENT(layoutstate_alloc);
|
|
DEFINE_STATEID_EVENT(layoutstate_unhash);
|
|
DEFINE_STATEID_EVENT(layoutstate_free);
|
|
DEFINE_STATEID_EVENT(layout_get_lookup_fail);
|
|
DEFINE_STATEID_EVENT(layout_commit_lookup_fail);
|
|
DEFINE_STATEID_EVENT(layout_return_lookup_fail);
|
|
DEFINE_STATEID_EVENT(layout_recall);
|
|
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
|
|
#define TRACE_INCLUDE_PATH .
|
|
#define TRACE_INCLUDE_FILE trace
|
|
#include <trace/define_trace.h>
|