Merge tag 'nfsd-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd updates from Chuck Lever:
 "This release introduces support for the CB_RECALL_ANY operation. NFSD
  can send this operation to request that clients return any delegations
  they choose. The server uses this operation to handle low memory
  scenarios or indicate to a client when that client has reached the
  maximum number of delegations the server supports.

  The NFSv4.2 READ_PLUS operation has been simplified temporarily whilst
  support for sparse files in local filesystems and the VFS is improved.

  Two major data structure fixes appear in this release:

   - The nfs4_file hash table is replaced with a resizable hash table to
     reduce the latency of NFSv4 OPEN operations.

   - Reference counting in the NFSD filecache has been hardened against
     races.

  In furtherance of removing support for NFSv2 in a subsequent kernel
  release, a new Kconfig option enables server-side support for NFSv2 to
  be left out of a kernel build.

  MAINTAINERS has been updated to indicate that changes to fs/exportfs
  should go through the NFSD tree"

* tag 'nfsd-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (49 commits)
  NFSD: Avoid clashing function prototypes
  SUNRPC: Fix crasher in unwrap_integ_data()
  SUNRPC: Make the svc_authenticate tracepoint conditional
  NFSD: Use only RQ_DROPME to signal the need to drop a reply
  SUNRPC: Clean up xdr_write_pages()
  SUNRPC: Don't leak netobj memory when gss_read_proxy_verf() fails
  NFSD: add CB_RECALL_ANY tracepoints
  NFSD: add delegation reaper to react to low memory condition
  NFSD: add support for sending CB_RECALL_ANY
  NFSD: refactoring courtesy_client_reaper to a generic low memory shrinker
  trace: Relocate event helper files
  NFSD: pass range end to vfs_fsync_range() instead of count
  lockd: fix file selection in nlmsvc_cancel_blocked
  lockd: ensure we use the correct file descriptor when unlocking
  lockd: set missing fl_flags field when retrieving args
  NFSD: Use struct_size() helper in alloc_session()
  nfsd: return error if nfs4_setacl fails
  lockd: set other missing fields when unlocking files
  NFSD: Add an nfsd_file_fsync tracepoint
  sunrpc: svc: Remove an unused static function svc_ungetu32()
  ...
This commit is contained in:
Linus Torvalds
2022-12-12 20:54:39 -08:00
49 changed files with 1302 additions and 808 deletions

View File

@@ -10082,6 +10082,7 @@ F: drivers/infiniband/
F: include/rdma/ F: include/rdma/
F: include/trace/events/ib_mad.h F: include/trace/events/ib_mad.h
F: include/trace/events/ib_umad.h F: include/trace/events/ib_umad.h
F: include/trace/misc/rdma.h
F: include/uapi/linux/if_infiniband.h F: include/uapi/linux/if_infiniband.h
F: include/uapi/rdma/ F: include/uapi/rdma/
F: samples/bpf/ibumad_kern.c F: samples/bpf/ibumad_kern.c
@@ -11168,11 +11169,18 @@ L: linux-nfs@vger.kernel.org
S: Supported S: Supported
W: http://nfs.sourceforge.net/ W: http://nfs.sourceforge.net/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
F: fs/exportfs/
F: fs/lockd/ F: fs/lockd/
F: fs/nfs_common/ F: fs/nfs_common/
F: fs/nfsd/ F: fs/nfsd/
F: include/linux/lockd/ F: include/linux/lockd/
F: include/linux/sunrpc/ F: include/linux/sunrpc/
F: include/trace/events/rpcgss.h
F: include/trace/events/rpcrdma.h
F: include/trace/events/sunrpc.h
F: include/trace/misc/fs.h
F: include/trace/misc/nfs.h
F: include/trace/misc/sunrpc.h
F: include/uapi/linux/nfsd/ F: include/uapi/linux/nfsd/
F: include/uapi/linux/sunrpc/ F: include/uapi/linux/sunrpc/
F: net/sunrpc/ F: net/sunrpc/

View File

@@ -16,7 +16,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <rdma/ib_cm.h> #include <rdma/ib_cm.h>
#include <trace/events/rdma.h> #include <trace/misc/rdma.h>
/* /*
* enum ib_cm_state, from include/rdma/ib_cm.h * enum ib_cm_state, from include/rdma/ib_cm.h

View File

@@ -15,7 +15,7 @@
#define _TRACE_RDMA_CMA_H #define _TRACE_RDMA_CMA_H
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <trace/events/rdma.h> #include <trace/misc/rdma.h>
DECLARE_EVENT_CLASS(cma_fsm_class, DECLARE_EVENT_CLASS(cma_fsm_class,

View File

@@ -18,7 +18,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/cred.h> #include <linux/cred.h>
#define dprintk(fmt, args...) do{}while(0) #define dprintk(fmt, args...) pr_debug(fmt, ##args)
static int get_name(const struct path *path, char *name, struct dentry *child); static int get_name(const struct path *path, char *name, struct dentry *child);
@@ -132,8 +132,8 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
inode_unlock(dentry->d_inode); inode_unlock(dentry->d_inode);
if (IS_ERR(parent)) { if (IS_ERR(parent)) {
dprintk("%s: get_parent of %ld failed, err %d\n", dprintk("get_parent of %lu failed, err %ld\n",
__func__, dentry->d_inode->i_ino, PTR_ERR(parent)); dentry->d_inode->i_ino, PTR_ERR(parent));
return parent; return parent;
} }
@@ -147,7 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
dprintk("%s: found name: %s\n", __func__, nbuf); dprintk("%s: found name: %s\n", __func__, nbuf);
tmp = lookup_one_unlocked(mnt_user_ns(mnt), nbuf, parent, strlen(nbuf)); tmp = lookup_one_unlocked(mnt_user_ns(mnt), nbuf, parent, strlen(nbuf));
if (IS_ERR(tmp)) { if (IS_ERR(tmp)) {
dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); dprintk("lookup failed: %ld\n", PTR_ERR(tmp));
err = PTR_ERR(tmp); err = PTR_ERR(tmp);
goto out_err; goto out_err;
} }

View File

@@ -52,6 +52,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
*filp = file; *filp = file;
/* Set up the missing parts of the file_lock structure */ /* Set up the missing parts of the file_lock structure */
lock->fl.fl_flags = FL_POSIX;
lock->fl.fl_file = file->f_file[mode]; lock->fl.fl_file = file->f_file[mode];
lock->fl.fl_pid = current->tgid; lock->fl.fl_pid = current->tgid;
lock->fl.fl_start = (loff_t)lock->lock_start; lock->fl.fl_start = (loff_t)lock->lock_start;

View File

@@ -659,11 +659,13 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
nlmsvc_cancel_blocked(net, file, lock); nlmsvc_cancel_blocked(net, file, lock);
lock->fl.fl_type = F_UNLCK; lock->fl.fl_type = F_UNLCK;
if (file->f_file[O_RDONLY]) lock->fl.fl_file = file->f_file[O_RDONLY];
error = vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, if (lock->fl.fl_file)
error = vfs_lock_file(lock->fl.fl_file, F_SETLK,
&lock->fl, NULL); &lock->fl, NULL);
if (file->f_file[O_WRONLY]) lock->fl.fl_file = file->f_file[O_WRONLY];
error = vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, if (lock->fl.fl_file)
error |= vfs_lock_file(lock->fl.fl_file, F_SETLK,
&lock->fl, NULL); &lock->fl, NULL);
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted; return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
@@ -697,9 +699,10 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
block = nlmsvc_lookup_block(file, lock); block = nlmsvc_lookup_block(file, lock);
mutex_unlock(&file->f_mutex); mutex_unlock(&file->f_mutex);
if (block != NULL) { if (block != NULL) {
mode = lock_to_openmode(&lock->fl); struct file_lock *fl = &block->b_call->a_args.lock.fl;
vfs_cancel_lock(block->b_file->f_file[mode],
&block->b_call->a_args.lock.fl); mode = lock_to_openmode(fl);
vfs_cancel_lock(block->b_file->f_file[mode], fl);
status = nlmsvc_unlink_block(block); status = nlmsvc_unlink_block(block);
nlmsvc_release_block(block); nlmsvc_release_block(block);
} }

View File

@@ -77,6 +77,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Set up the missing parts of the file_lock structure */ /* Set up the missing parts of the file_lock structure */
mode = lock_to_openmode(&lock->fl); mode = lock_to_openmode(&lock->fl);
lock->fl.fl_flags = FL_POSIX;
lock->fl.fl_file = file->f_file[mode]; lock->fl.fl_file = file->f_file[mode];
lock->fl.fl_pid = current->tgid; lock->fl.fl_pid = current->tgid;
lock->fl.fl_lmops = &nlmsvc_lock_operations; lock->fl.fl_lmops = &nlmsvc_lock_operations;

View File

@@ -176,7 +176,7 @@ nlm_delete_file(struct nlm_file *file)
} }
} }
static int nlm_unlock_files(struct nlm_file *file, fl_owner_t owner) static int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
{ {
struct file_lock lock; struct file_lock lock;
@@ -184,12 +184,15 @@ static int nlm_unlock_files(struct nlm_file *file, fl_owner_t owner)
lock.fl_type = F_UNLCK; lock.fl_type = F_UNLCK;
lock.fl_start = 0; lock.fl_start = 0;
lock.fl_end = OFFSET_MAX; lock.fl_end = OFFSET_MAX;
lock.fl_owner = owner; lock.fl_owner = fl->fl_owner;
if (file->f_file[O_RDONLY] && lock.fl_pid = fl->fl_pid;
vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, &lock, NULL)) lock.fl_flags = FL_POSIX;
lock.fl_file = file->f_file[O_RDONLY];
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
goto out_err; goto out_err;
if (file->f_file[O_WRONLY] && lock.fl_file = file->f_file[O_WRONLY];
vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, &lock, NULL)) if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
goto out_err; goto out_err;
return 0; return 0;
out_err: out_err:
@@ -226,7 +229,7 @@ again:
if (match(lockhost, host)) { if (match(lockhost, host)) {
spin_unlock(&flctx->flc_lock); spin_unlock(&flctx->flc_lock);
if (nlm_unlock_files(file, fl->fl_owner)) if (nlm_unlock_files(file, fl))
return 1; return 1;
goto again; goto again;
} }

View File

@@ -9,10 +9,10 @@
#define _TRACE_NFS4_H #define _TRACE_NFS4_H
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <trace/events/sunrpc_base.h> #include <trace/misc/sunrpc.h>
#include <trace/events/fs.h> #include <trace/misc/fs.h>
#include <trace/events/nfs.h> #include <trace/misc/nfs.h>
#define show_nfs_fattr_flags(valid) \ #define show_nfs_fattr_flags(valid) \
__print_flags((unsigned long)valid, "|", \ __print_flags((unsigned long)valid, "|", \

View File

@@ -11,9 +11,9 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <linux/iversion.h> #include <linux/iversion.h>
#include <trace/events/fs.h> #include <trace/misc/fs.h>
#include <trace/events/nfs.h> #include <trace/misc/nfs.h>
#include <trace/events/sunrpc_base.h> #include <trace/misc/sunrpc.h>
#define nfs_show_cache_validity(v) \ #define nfs_show_cache_validity(v) \
__print_flags(v, "|", \ __print_flags(v, "|", \

View File

@@ -8,6 +8,7 @@ config NFSD
select SUNRPC select SUNRPC
select EXPORTFS select EXPORTFS
select NFS_ACL_SUPPORT if NFSD_V2_ACL select NFS_ACL_SUPPORT if NFSD_V2_ACL
select NFS_ACL_SUPPORT if NFSD_V3_ACL
depends on MULTIUSER depends on MULTIUSER
help help
Choose Y here if you want to allow other computers to access Choose Y here if you want to allow other computers to access
@@ -26,19 +27,29 @@ config NFSD
Below you can choose which versions of the NFS protocol are Below you can choose which versions of the NFS protocol are
available to clients mounting the NFS server on this system. available to clients mounting the NFS server on this system.
Support for NFS version 2 (RFC 1094) is always available when Support for NFS version 3 (RFC 1813) is always available when
CONFIG_NFSD is selected. CONFIG_NFSD is selected.
If unsure, say N. If unsure, say N.
config NFSD_V2_ACL config NFSD_V2
bool bool "NFS server support for NFS version 2 (DEPRECATED)"
depends on NFSD depends on NFSD
default n
help
NFSv2 (RFC 1094) was the first publicly-released version of NFS.
Unless you are hosting ancient (1990's era) NFS clients, you don't
need this.
If unsure, say N.
config NFSD_V2_ACL
bool "NFS server support for the NFSv2 ACL protocol extension"
depends on NFSD_V2
config NFSD_V3_ACL config NFSD_V3_ACL
bool "NFS server support for the NFSv3 ACL protocol extension" bool "NFS server support for the NFSv3 ACL protocol extension"
depends on NFSD depends on NFSD
select NFSD_V2_ACL
help help
Solaris NFS servers support an auxiliary NFSv3 ACL protocol that Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
never became an official part of the NFS version 3 protocol. never became an official part of the NFS version 3 protocol.

View File

@@ -10,9 +10,10 @@ obj-$(CONFIG_NFSD) += nfsd.o
# this one should be compiled first, as the tracing macros can easily blow up # this one should be compiled first, as the tracing macros can easily blow up
nfsd-y += trace.o nfsd-y += trace.o
nfsd-y += nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ nfsd-y += nfssvc.o nfsctl.o nfsfh.o vfs.o \
export.o auth.o lockd.o nfscache.o nfsxdr.o \ export.o auth.o lockd.o nfscache.o \
stats.o filecache.o nfs3proc.o nfs3xdr.o stats.o filecache.o nfs3proc.o nfs3xdr.o
nfsd-$(CONFIG_NFSD_V2) += nfsproc.o nfsxdr.o
nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o
nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \ nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \

View File

@@ -12,6 +12,7 @@
#include "blocklayoutxdr.h" #include "blocklayoutxdr.h"
#include "pnfs.h" #include "pnfs.h"
#include "filecache.h" #include "filecache.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS

View File

@@ -9,6 +9,7 @@
#include "nfsd.h" #include "nfsd.h"
#include "blocklayoutxdr.h" #include "blocklayoutxdr.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS

View File

@@ -115,7 +115,6 @@ struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
int exp_rootfh(struct net *, struct auth_domain *, int exp_rootfh(struct net *, struct auth_domain *,
char *path, struct knfsd_fh *, int maxsize); char *path, struct knfsd_fh *, int maxsize);
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *); __be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
__be32 nfserrno(int errno);
static inline void exp_put(struct svc_export *exp) static inline void exp_put(struct svc_export *exp)
{ {

View File

@@ -1,7 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Open file cache. * The NFSD open file cache.
* *
* (c) 2015 - Jeff Layton <jeff.layton@primarydata.com> * (c) 2015 - Jeff Layton <jeff.layton@primarydata.com>
*
* An nfsd_file object is a per-file collection of open state that binds
* together:
* - a struct file *
* - a user credential
* - a network namespace
* - a read-ahead context
* - monitoring for writeback errors
*
* nfsd_file objects are reference-counted. Consumers acquire a new
* object via the nfsd_file_acquire API. They manage their interest in
* the acquired object, and hence the object's reference count, via
* nfsd_file_get and nfsd_file_put. There are two varieties of nfsd_file
* object:
*
* * non-garbage-collected: When a consumer wants to precisely control
* the lifetime of a file's open state, it acquires a non-garbage-
* collected nfsd_file. The final nfsd_file_put releases the open
* state immediately.
*
* * garbage-collected: When a consumer does not control the lifetime
* of open state, it acquires a garbage-collected nfsd_file. The
* final nfsd_file_put allows the open state to linger for a period
* during which it may be re-used.
*/ */
#include <linux/hash.h> #include <linux/hash.h>
@@ -33,7 +58,6 @@ static DEFINE_PER_CPU(unsigned long, nfsd_file_cache_hits);
static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions); static DEFINE_PER_CPU(unsigned long, nfsd_file_acquisitions);
static DEFINE_PER_CPU(unsigned long, nfsd_file_releases); static DEFINE_PER_CPU(unsigned long, nfsd_file_releases);
static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age); static DEFINE_PER_CPU(unsigned long, nfsd_file_total_age);
static DEFINE_PER_CPU(unsigned long, nfsd_file_pages_flushed);
static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions); static DEFINE_PER_CPU(unsigned long, nfsd_file_evictions);
struct nfsd_fcache_disposal { struct nfsd_fcache_disposal {
@@ -63,6 +87,7 @@ struct nfsd_file_lookup_key {
struct net *net; struct net *net;
const struct cred *cred; const struct cred *cred;
unsigned char need; unsigned char need;
bool gc;
enum nfsd_file_lookup_type type; enum nfsd_file_lookup_type type;
}; };
@@ -162,6 +187,8 @@ static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg,
return 1; return 1;
if (!nfsd_match_cred(nf->nf_cred, key->cred)) if (!nfsd_match_cred(nf->nf_cred, key->cred))
return 1; return 1;
if (!!test_bit(NFSD_FILE_GC, &nf->nf_flags) != key->gc)
return 1;
if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0)
return 1; return 1;
break; break;
@@ -184,12 +211,9 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
static void static void
nfsd_file_schedule_laundrette(void) nfsd_file_schedule_laundrette(void)
{ {
if ((atomic_read(&nfsd_file_rhash_tbl.nelems) == 0) || if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0) queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
return; NFSD_LAUNDRETTE_DELAY);
queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
NFSD_LAUNDRETTE_DELAY);
} }
static void static void
@@ -297,6 +321,8 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
nf->nf_flags = 0; nf->nf_flags = 0;
__set_bit(NFSD_FILE_HASHED, &nf->nf_flags); __set_bit(NFSD_FILE_HASHED, &nf->nf_flags);
__set_bit(NFSD_FILE_PENDING, &nf->nf_flags); __set_bit(NFSD_FILE_PENDING, &nf->nf_flags);
if (key->gc)
__set_bit(NFSD_FILE_GC, &nf->nf_flags);
nf->nf_inode = key->inode; nf->nf_inode = key->inode;
/* nf_ref is pre-incremented for hash table */ /* nf_ref is pre-incremented for hash table */
refcount_set(&nf->nf_ref, 2); refcount_set(&nf->nf_ref, 2);
@@ -306,16 +332,62 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
return nf; return nf;
} }
static void
nfsd_file_fsync(struct nfsd_file *nf)
{
struct file *file = nf->nf_file;
int ret;
if (!file || !(file->f_mode & FMODE_WRITE))
return;
ret = vfs_fsync(file, 1);
trace_nfsd_file_fsync(nf, ret);
if (ret)
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
}
static int
nfsd_file_check_write_error(struct nfsd_file *nf)
{
struct file *file = nf->nf_file;
if (!file || !(file->f_mode & FMODE_WRITE))
return 0;
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
}
static void
nfsd_file_hash_remove(struct nfsd_file *nf)
{
trace_nfsd_file_unhash(nf);
if (nfsd_file_check_write_error(nf))
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash,
nfsd_file_rhash_params);
}
static bool
nfsd_file_unhash(struct nfsd_file *nf)
{
if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
nfsd_file_hash_remove(nf);
return true;
}
return false;
}
static bool static bool
nfsd_file_free(struct nfsd_file *nf) nfsd_file_free(struct nfsd_file *nf)
{ {
s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime)); s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime));
bool flush = false; bool flush = false;
trace_nfsd_file_free(nf);
this_cpu_inc(nfsd_file_releases); this_cpu_inc(nfsd_file_releases);
this_cpu_add(nfsd_file_total_age, age); this_cpu_add(nfsd_file_total_age, age);
trace_nfsd_file_put_final(nf);
if (nf->nf_mark) if (nf->nf_mark)
nfsd_file_mark_put(nf->nf_mark); nfsd_file_mark_put(nf->nf_mark);
if (nf->nf_file) { if (nf->nf_file) {
@@ -349,28 +421,6 @@ nfsd_file_check_writeback(struct nfsd_file *nf)
mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK); mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
} }
static int
nfsd_file_check_write_error(struct nfsd_file *nf)
{
struct file *file = nf->nf_file;
if (!file || !(file->f_mode & FMODE_WRITE))
return 0;
return filemap_check_wb_err(file->f_mapping, READ_ONCE(file->f_wb_err));
}
static void
nfsd_file_flush(struct nfsd_file *nf)
{
struct file *file = nf->nf_file;
if (!file || !(file->f_mode & FMODE_WRITE))
return;
this_cpu_add(nfsd_file_pages_flushed, file->f_mapping->nrpages);
if (vfs_fsync(file, 1) != 0)
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
}
static void nfsd_file_lru_add(struct nfsd_file *nf) static void nfsd_file_lru_add(struct nfsd_file *nf)
{ {
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags); set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
@@ -384,31 +434,18 @@ static void nfsd_file_lru_remove(struct nfsd_file *nf)
trace_nfsd_file_lru_del(nf); trace_nfsd_file_lru_del(nf);
} }
static void struct nfsd_file *
nfsd_file_hash_remove(struct nfsd_file *nf) nfsd_file_get(struct nfsd_file *nf)
{ {
trace_nfsd_file_unhash(nf); if (likely(refcount_inc_not_zero(&nf->nf_ref)))
return nf;
if (nfsd_file_check_write_error(nf)) return NULL;
nfsd_reset_write_verifier(net_generic(nf->nf_net, nfsd_net_id));
rhashtable_remove_fast(&nfsd_file_rhash_tbl, &nf->nf_rhash,
nfsd_file_rhash_params);
}
static bool
nfsd_file_unhash(struct nfsd_file *nf)
{
if (test_and_clear_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
nfsd_file_hash_remove(nf);
return true;
}
return false;
} }
static void static void
nfsd_file_unhash_and_dispose(struct nfsd_file *nf, struct list_head *dispose) nfsd_file_unhash_and_queue(struct nfsd_file *nf, struct list_head *dispose)
{ {
trace_nfsd_file_unhash_and_dispose(nf); trace_nfsd_file_unhash_and_queue(nf);
if (nfsd_file_unhash(nf)) { if (nfsd_file_unhash(nf)) {
/* caller must call nfsd_file_dispose_list() later */ /* caller must call nfsd_file_dispose_list() later */
nfsd_file_lru_remove(nf); nfsd_file_lru_remove(nf);
@@ -428,48 +465,33 @@ nfsd_file_put_noref(struct nfsd_file *nf)
} }
} }
static void
nfsd_file_unhash_and_put(struct nfsd_file *nf)
{
if (nfsd_file_unhash(nf))
nfsd_file_put_noref(nf);
}
void void
nfsd_file_put(struct nfsd_file *nf) nfsd_file_put(struct nfsd_file *nf)
{ {
might_sleep(); might_sleep();
nfsd_file_lru_add(nf); if (test_bit(NFSD_FILE_GC, &nf->nf_flags))
if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) { nfsd_file_lru_add(nf);
nfsd_file_flush(nf); else if (refcount_read(&nf->nf_ref) == 2)
nfsd_file_unhash_and_put(nf);
if (!test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
nfsd_file_fsync(nf);
nfsd_file_put_noref(nf); nfsd_file_put_noref(nf);
} else if (nf->nf_file) { } else if (nf->nf_file && test_bit(NFSD_FILE_GC, &nf->nf_flags)) {
nfsd_file_put_noref(nf); nfsd_file_put_noref(nf);
nfsd_file_schedule_laundrette(); nfsd_file_schedule_laundrette();
} else } else
nfsd_file_put_noref(nf); nfsd_file_put_noref(nf);
} }
/**
* nfsd_file_close - Close an nfsd_file
* @nf: nfsd_file to close
*
* If this is the final reference for @nf, free it immediately.
* This reflects an on-the-wire CLOSE or DELEGRETURN into the
* VFS and exported filesystem.
*/
void nfsd_file_close(struct nfsd_file *nf)
{
nfsd_file_put(nf);
if (refcount_dec_if_one(&nf->nf_ref)) {
nfsd_file_unhash(nf);
nfsd_file_lru_remove(nf);
nfsd_file_free(nf);
}
}
struct nfsd_file *
nfsd_file_get(struct nfsd_file *nf)
{
if (likely(refcount_inc_not_zero(&nf->nf_ref)))
return nf;
return NULL;
}
static void static void
nfsd_file_dispose_list(struct list_head *dispose) nfsd_file_dispose_list(struct list_head *dispose)
{ {
@@ -478,7 +500,7 @@ nfsd_file_dispose_list(struct list_head *dispose)
while(!list_empty(dispose)) { while(!list_empty(dispose)) {
nf = list_first_entry(dispose, struct nfsd_file, nf_lru); nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
list_del_init(&nf->nf_lru); list_del_init(&nf->nf_lru);
nfsd_file_flush(nf); nfsd_file_fsync(nf);
nfsd_file_put_noref(nf); nfsd_file_put_noref(nf);
} }
} }
@@ -492,7 +514,7 @@ nfsd_file_dispose_list_sync(struct list_head *dispose)
while(!list_empty(dispose)) { while(!list_empty(dispose)) {
nf = list_first_entry(dispose, struct nfsd_file, nf_lru); nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
list_del_init(&nf->nf_lru); list_del_init(&nf->nf_lru);
nfsd_file_flush(nf); nfsd_file_fsync(nf);
if (!refcount_dec_and_test(&nf->nf_ref)) if (!refcount_dec_and_test(&nf->nf_ref))
continue; continue;
if (nfsd_file_free(nf)) if (nfsd_file_free(nf))
@@ -644,7 +666,8 @@ static void
nfsd_file_gc_worker(struct work_struct *work) nfsd_file_gc_worker(struct work_struct *work)
{ {
nfsd_file_gc(); nfsd_file_gc();
nfsd_file_schedule_laundrette(); if (list_lru_count(&nfsd_file_lru))
nfsd_file_schedule_laundrette();
} }
static unsigned long static unsigned long
@@ -692,7 +715,7 @@ __nfsd_file_close_inode(struct inode *inode, struct list_head *dispose)
nfsd_file_rhash_params); nfsd_file_rhash_params);
if (!nf) if (!nf)
break; break;
nfsd_file_unhash_and_dispose(nf, dispose); nfsd_file_unhash_and_queue(nf, dispose);
count++; count++;
} while (1); } while (1);
rcu_read_unlock(); rcu_read_unlock();
@@ -894,7 +917,7 @@ __nfsd_file_cache_purge(struct net *net)
nf = rhashtable_walk_next(&iter); nf = rhashtable_walk_next(&iter);
while (!IS_ERR_OR_NULL(nf)) { while (!IS_ERR_OR_NULL(nf)) {
if (!net || nf->nf_net == net) if (!net || nf->nf_net == net)
nfsd_file_unhash_and_dispose(nf, &dispose); nfsd_file_unhash_and_queue(nf, &dispose);
nf = rhashtable_walk_next(&iter); nf = rhashtable_walk_next(&iter);
} }
@@ -1000,7 +1023,6 @@ nfsd_file_cache_shutdown(void)
per_cpu(nfsd_file_acquisitions, i) = 0; per_cpu(nfsd_file_acquisitions, i) = 0;
per_cpu(nfsd_file_releases, i) = 0; per_cpu(nfsd_file_releases, i) = 0;
per_cpu(nfsd_file_total_age, i) = 0; per_cpu(nfsd_file_total_age, i) = 0;
per_cpu(nfsd_file_pages_flushed, i) = 0;
per_cpu(nfsd_file_evictions, i) = 0; per_cpu(nfsd_file_evictions, i) = 0;
} }
} }
@@ -1034,12 +1056,14 @@ nfsd_file_is_cached(struct inode *inode)
static __be32 static __be32
nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf, bool open) unsigned int may_flags, struct nfsd_file **pnf,
bool open, bool want_gc)
{ {
struct nfsd_file_lookup_key key = { struct nfsd_file_lookup_key key = {
.type = NFSD_FILE_KEY_FULL, .type = NFSD_FILE_KEY_FULL,
.need = may_flags & NFSD_FILE_MAY_MASK, .need = may_flags & NFSD_FILE_MAY_MASK,
.net = SVC_NET(rqstp), .net = SVC_NET(rqstp),
.gc = want_gc,
}; };
bool open_retry = true; bool open_retry = true;
struct nfsd_file *nf; struct nfsd_file *nf;
@@ -1135,14 +1159,35 @@ open_file:
* then unhash. * then unhash.
*/ */
if (status != nfs_ok || key.inode->i_nlink == 0) if (status != nfs_ok || key.inode->i_nlink == 0)
if (nfsd_file_unhash(nf)) nfsd_file_unhash_and_put(nf);
nfsd_file_put_noref(nf);
clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags); clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags);
smp_mb__after_atomic(); smp_mb__after_atomic();
wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING); wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING);
goto out; goto out;
} }
/**
* nfsd_file_acquire_gc - Get a struct nfsd_file with an open file
* @rqstp: the RPC transaction being executed
* @fhp: the NFS filehandle of the file to be opened
* @may_flags: NFSD_MAY_ settings for the file
* @pnf: OUT: new or found "struct nfsd_file" object
*
* The nfsd_file object returned by this API is reference-counted
* and garbage-collected. The object is retained for a few
* seconds after the final nfsd_file_put() in case the caller
* wants to re-use it.
*
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
* network byte order is returned.
*/
__be32
nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf)
{
return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, true);
}
/** /**
* nfsd_file_acquire - Get a struct nfsd_file with an open file * nfsd_file_acquire - Get a struct nfsd_file with an open file
* @rqstp: the RPC transaction being executed * @rqstp: the RPC transaction being executed
@@ -1150,6 +1195,10 @@ open_file:
* @may_flags: NFSD_MAY_ settings for the file * @may_flags: NFSD_MAY_ settings for the file
* @pnf: OUT: new or found "struct nfsd_file" object * @pnf: OUT: new or found "struct nfsd_file" object
* *
* The nfsd_file_object returned by this API is reference-counted
* but not garbage-collected. The object is unhashed after the
* final nfsd_file_put().
*
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
* network byte order is returned. * network byte order is returned.
*/ */
@@ -1157,7 +1206,7 @@ __be32
nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf) unsigned int may_flags, struct nfsd_file **pnf)
{ {
return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true); return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, false);
} }
/** /**
@@ -1167,6 +1216,10 @@ nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
* @may_flags: NFSD_MAY_ settings for the file * @may_flags: NFSD_MAY_ settings for the file
* @pnf: OUT: new or found "struct nfsd_file" object * @pnf: OUT: new or found "struct nfsd_file" object
* *
* The nfsd_file_object returned by this API is reference-counted
* but not garbage-collected. The object is released immediately
* one RCU grace period after the final nfsd_file_put().
*
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in * Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
* network byte order is returned. * network byte order is returned.
*/ */
@@ -1174,7 +1227,7 @@ __be32
nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp, nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf) unsigned int may_flags, struct nfsd_file **pnf)
{ {
return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false); return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false, false);
} }
/* /*
@@ -1184,7 +1237,7 @@ nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
*/ */
int nfsd_file_cache_stats_show(struct seq_file *m, void *v) int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
{ {
unsigned long releases = 0, pages_flushed = 0, evictions = 0; unsigned long releases = 0, evictions = 0;
unsigned long hits = 0, acquisitions = 0; unsigned long hits = 0, acquisitions = 0;
unsigned int i, count = 0, buckets = 0; unsigned int i, count = 0, buckets = 0;
unsigned long lru = 0, total_age = 0; unsigned long lru = 0, total_age = 0;
@@ -1212,7 +1265,6 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
releases += per_cpu(nfsd_file_releases, i); releases += per_cpu(nfsd_file_releases, i);
total_age += per_cpu(nfsd_file_total_age, i); total_age += per_cpu(nfsd_file_total_age, i);
evictions += per_cpu(nfsd_file_evictions, i); evictions += per_cpu(nfsd_file_evictions, i);
pages_flushed += per_cpu(nfsd_file_pages_flushed, i);
} }
seq_printf(m, "total entries: %u\n", count); seq_printf(m, "total entries: %u\n", count);
@@ -1226,6 +1278,5 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
seq_printf(m, "mean age (ms): %ld\n", total_age / releases); seq_printf(m, "mean age (ms): %ld\n", total_age / releases);
else else
seq_printf(m, "mean age (ms): -\n"); seq_printf(m, "mean age (ms): -\n");
seq_printf(m, "pages flushed: %lu\n", pages_flushed);
return 0; return 0;
} }

View File

@@ -38,6 +38,7 @@ struct nfsd_file {
#define NFSD_FILE_HASHED (0) #define NFSD_FILE_HASHED (0)
#define NFSD_FILE_PENDING (1) #define NFSD_FILE_PENDING (1)
#define NFSD_FILE_REFERENCED (2) #define NFSD_FILE_REFERENCED (2)
#define NFSD_FILE_GC (3)
unsigned long nf_flags; unsigned long nf_flags;
struct inode *nf_inode; /* don't deref */ struct inode *nf_inode; /* don't deref */
refcount_t nf_ref; refcount_t nf_ref;
@@ -52,10 +53,11 @@ void nfsd_file_cache_shutdown(void);
int nfsd_file_cache_start_net(struct net *net); int nfsd_file_cache_start_net(struct net *net);
void nfsd_file_cache_shutdown_net(struct net *net); void nfsd_file_cache_shutdown_net(struct net *net);
void nfsd_file_put(struct nfsd_file *nf); void nfsd_file_put(struct nfsd_file *nf);
void nfsd_file_close(struct nfsd_file *nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf); struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode); void nfsd_file_close_inode_sync(struct inode *inode);
bool nfsd_file_is_cached(struct inode *inode); bool nfsd_file_is_cached(struct inode *inode);
__be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp);
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp); unsigned int may_flags, struct nfsd_file **nfp);
__be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,

View File

@@ -15,6 +15,7 @@
#include "flexfilelayoutxdr.h" #include "flexfilelayoutxdr.h"
#include "pnfs.h" #include "pnfs.h"
#include "vfs.h"
#define NFSDDBG_FACILITY NFSDDBG_PNFS #define NFSDDBG_FACILITY NFSDDBG_PNFS

View File

@@ -246,7 +246,6 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
struct nfsd3_getaclres *resp = rqstp->rq_resp; struct nfsd3_getaclres *resp = rqstp->rq_resp;
struct dentry *dentry = resp->fh.fh_dentry; struct dentry *dentry = resp->fh.fh_dentry;
struct inode *inode; struct inode *inode;
int w;
if (!svcxdr_encode_stat(xdr, resp->status)) if (!svcxdr_encode_stat(xdr, resp->status))
return false; return false;
@@ -260,15 +259,6 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
if (xdr_stream_encode_u32(xdr, resp->mask) < 0) if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
return false; return false;
rqstp->rq_res.page_len = w = nfsacl_size(
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
while (w > 0) {
if (!*(rqstp->rq_next_page++))
return true;
w -= PAGE_SIZE;
}
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access, if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
resp->mask & NFS_ACL, 0)) resp->mask & NFS_ACL, 0))
return false; return false;

View File

@@ -171,11 +171,7 @@ nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{ {
struct nfsd3_getaclres *resp = rqstp->rq_resp; struct nfsd3_getaclres *resp = rqstp->rq_resp;
struct dentry *dentry = resp->fh.fh_dentry; struct dentry *dentry = resp->fh.fh_dentry;
struct kvec *head = rqstp->rq_res.head;
struct inode *inode; struct inode *inode;
unsigned int base;
int n;
int w;
if (!svcxdr_encode_nfsstat3(xdr, resp->status)) if (!svcxdr_encode_nfsstat3(xdr, resp->status))
return false; return false;
@@ -187,26 +183,12 @@ nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
if (xdr_stream_encode_u32(xdr, resp->mask) < 0) if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
return false; return false;
base = (char *)xdr->p - (char *)head->iov_base; if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
resp->mask & NFS_ACL, 0))
rqstp->rq_res.page_len = w = nfsacl_size( return false;
(resp->mask & NFS_ACL) ? resp->acl_access : NULL, if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL); resp->mask & NFS_DFACL,
while (w > 0) { NFS_ACL_DEFAULT))
if (!*(rqstp->rq_next_page++))
return false;
w -= PAGE_SIZE;
}
n = nfsacl_encode(&rqstp->rq_res, base, inode,
resp->acl_access,
resp->mask & NFS_ACL, 0);
if (n > 0)
n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
resp->acl_default,
resp->mask & NFS_DFACL,
NFS_ACL_DEFAULT);
if (n <= 0)
return false; return false;
break; break;
default: default:

View File

@@ -13,6 +13,7 @@
#include "cache.h" #include "cache.h"
#include "xdr3.h" #include "xdr3.h"
#include "vfs.h" #include "vfs.h"
#include "filecache.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC #define NFSDDBG_FACILITY NFSDDBG_PROC
@@ -763,6 +764,7 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
{ {
struct nfsd3_commitargs *argp = rqstp->rq_argp; struct nfsd3_commitargs *argp = rqstp->rq_argp;
struct nfsd3_commitres *resp = rqstp->rq_resp; struct nfsd3_commitres *resp = rqstp->rq_resp;
struct nfsd_file *nf;
dprintk("nfsd: COMMIT(3) %s %u@%Lu\n", dprintk("nfsd: COMMIT(3) %s %u@%Lu\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
@@ -770,8 +772,14 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
(unsigned long long) argp->offset); (unsigned long long) argp->offset);
fh_copy(&resp->fh, &argp->fh); fh_copy(&resp->fh, &argp->fh);
resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset, resp->status = nfsd_file_acquire_gc(rqstp, &resp->fh, NFSD_MAY_WRITE |
NFSD_MAY_NOT_BREAK_LEASE, &nf);
if (resp->status)
goto out;
resp->status = nfsd_commit(rqstp, &resp->fh, nf, argp->offset,
argp->count, resp->verf); argp->count, resp->verf);
nfsd_file_put(nf);
out:
return rpc_success; return rpc_success;
} }

View File

@@ -76,6 +76,17 @@ static __be32 *xdr_encode_empty_array(__be32 *p)
* 1 Protocol" * 1 Protocol"
*/ */
static void encode_uint32(struct xdr_stream *xdr, u32 n)
{
WARN_ON_ONCE(xdr_stream_encode_u32(xdr, n) < 0);
}
static void encode_bitmap4(struct xdr_stream *xdr, const __u32 *bitmap,
size_t len)
{
WARN_ON_ONCE(xdr_stream_encode_uint32_array(xdr, bitmap, len) < 0);
}
/* /*
* nfs_cb_opnum4 * nfs_cb_opnum4
* *
@@ -328,6 +339,24 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
hdr->nops++; hdr->nops++;
} }
/*
* CB_RECALLANY4args
*
* struct CB_RECALLANY4args {
* uint32_t craa_objects_to_keep;
* bitmap4 craa_type_mask;
* };
*/
static void
encode_cb_recallany4args(struct xdr_stream *xdr,
struct nfs4_cb_compound_hdr *hdr, struct nfsd4_cb_recall_any *ra)
{
encode_nfs_cb_opnum4(xdr, OP_CB_RECALL_ANY);
encode_uint32(xdr, ra->ra_keep);
encode_bitmap4(xdr, ra->ra_bmval, ARRAY_SIZE(ra->ra_bmval));
hdr->nops++;
}
/* /*
* CB_SEQUENCE4args * CB_SEQUENCE4args
* *
@@ -482,6 +511,26 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_cb_nops(&hdr); encode_cb_nops(&hdr);
} }
/*
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
*/
static void
nfs4_xdr_enc_cb_recall_any(struct rpc_rqst *req,
struct xdr_stream *xdr, const void *data)
{
const struct nfsd4_callback *cb = data;
struct nfsd4_cb_recall_any *ra;
struct nfs4_cb_compound_hdr hdr = {
.ident = cb->cb_clp->cl_cb_ident,
.minorversion = cb->cb_clp->cl_minorversion,
};
ra = container_of(cb, struct nfsd4_cb_recall_any, ra_cb);
encode_cb_compound4args(xdr, &hdr);
encode_cb_sequence4args(xdr, cb, &hdr);
encode_cb_recallany4args(xdr, &hdr, ra);
encode_cb_nops(&hdr);
}
/* /*
* NFSv4.0 and NFSv4.1 XDR decode functions * NFSv4.0 and NFSv4.1 XDR decode functions
@@ -520,6 +569,28 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status); return decode_cb_op_status(xdr, OP_CB_RECALL, &cb->cb_status);
} }
/*
* 20.6. Operation 8: CB_RECALL_ANY - Keep Any N Recallable Objects
*/
static int
nfs4_xdr_dec_cb_recall_any(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data)
{
struct nfsd4_callback *cb = data;
struct nfs4_cb_compound_hdr hdr;
int status;
status = decode_cb_compound4res(xdr, &hdr);
if (unlikely(status))
return status;
status = decode_cb_sequence4res(xdr, cb);
if (unlikely(status || cb->cb_seq_status))
return status;
status = decode_cb_op_status(xdr, OP_CB_RECALL_ANY, &cb->cb_status);
return status;
}
#ifdef CONFIG_NFSD_PNFS #ifdef CONFIG_NFSD_PNFS
/* /*
* CB_LAYOUTRECALL4args * CB_LAYOUTRECALL4args
@@ -783,6 +854,7 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
#endif #endif
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock), PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload), PROC(CB_OFFLOAD, COMPOUND, cb_offload, cb_offload),
PROC(CB_RECALL_ANY, COMPOUND, cb_recall_any, cb_recall_any),
}; };
static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)]; static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)];

View File

@@ -41,6 +41,7 @@
#include "idmap.h" #include "idmap.h"
#include "nfsd.h" #include "nfsd.h"
#include "netns.h" #include "netns.h"
#include "vfs.h"
/* /*
* Turn off idmapping when using AUTH_SYS. * Turn off idmapping when using AUTH_SYS.

View File

@@ -731,10 +731,19 @@ nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
union nfsd4_op_u *u) union nfsd4_op_u *u)
{ {
struct nfsd4_commit *commit = &u->commit; struct nfsd4_commit *commit = &u->commit;
struct nfsd_file *nf;
__be32 status;
return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, status = nfsd_file_acquire(rqstp, &cstate->current_fh, NFSD_MAY_WRITE |
NFSD_MAY_NOT_BREAK_LEASE, &nf);
if (status != nfs_ok)
return status;
status = nfsd_commit(rqstp, &cstate->current_fh, nf, commit->co_offset,
commit->co_count, commit->co_count,
(__be32 *)commit->co_verf.data); (__be32 *)commit->co_verf.data);
nfsd_file_put(nf);
return status;
} }
static __be32 static __be32
@@ -934,12 +943,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&read->rd_stateid, RD_STATE, &read->rd_stateid, RD_STATE,
&read->rd_nf, NULL); &read->rd_nf, NULL);
if (status) {
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out;
}
status = nfs_ok;
out:
read->rd_rqstp = rqstp; read->rd_rqstp = rqstp;
read->rd_fhp = &cstate->current_fh; read->rd_fhp = &cstate->current_fh;
return status; return status;
@@ -1108,10 +1112,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_stateid_op(rqstp, cstate, status = nfs4_preprocess_stateid_op(rqstp, cstate,
&cstate->current_fh, &setattr->sa_stateid, &cstate->current_fh, &setattr->sa_stateid,
WR_STATE, NULL, NULL); WR_STATE, NULL, NULL);
if (status) { if (status)
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
return status; return status;
}
} }
err = fh_want_write(&cstate->current_fh); err = fh_want_write(&cstate->current_fh);
if (err) if (err)
@@ -1133,6 +1135,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
0, (time64_t)0); 0, (time64_t)0);
if (!status) if (!status)
status = nfserrno(attrs.na_labelerr); status = nfserrno(attrs.na_labelerr);
if (!status)
status = nfserrno(attrs.na_aclerr);
out: out:
nfsd_attrs_free(&attrs); nfsd_attrs_free(&attrs);
fh_drop_write(&cstate->current_fh); fh_drop_write(&cstate->current_fh);
@@ -1159,10 +1163,8 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
write->wr_offset, cnt); write->wr_offset, cnt);
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
stateid, WR_STATE, &nf, NULL); stateid, WR_STATE, &nf, NULL);
if (status) { if (status)
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
return status; return status;
}
write->wr_how_written = write->wr_stable_how; write->wr_how_written = write->wr_stable_how;
@@ -1193,17 +1195,13 @@ nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
src_stateid, RD_STATE, src, NULL); src_stateid, RD_STATE, src, NULL);
if (status) { if (status)
dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
goto out; goto out;
}
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
dst_stateid, WR_STATE, dst, NULL); dst_stateid, WR_STATE, dst, NULL);
if (status) { if (status)
dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
goto out_put_src; goto out_put_src;
}
/* fix up for NFS-specific error code */ /* fix up for NFS-specific error code */
if (!S_ISREG(file_inode((*src)->nf_file)->i_mode) || if (!S_ISREG(file_inode((*src)->nf_file)->i_mode) ||
@@ -1644,6 +1642,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
u64 src_pos = copy->cp_src_pos; u64 src_pos = copy->cp_src_pos;
u64 dst_pos = copy->cp_dst_pos; u64 dst_pos = copy->cp_dst_pos;
int status; int status;
loff_t end;
/* See RFC 7862 p.67: */ /* See RFC 7862 p.67: */
if (bytes_total == 0) if (bytes_total == 0)
@@ -1663,8 +1662,8 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
/* for a non-zero asynchronous copy do a commit of data */ /* for a non-zero asynchronous copy do a commit of data */
if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) { if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) {
since = READ_ONCE(dst->f_wb_err); since = READ_ONCE(dst->f_wb_err);
status = vfs_fsync_range(dst, copy->cp_dst_pos, end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1;
copy->cp_res.wr_bytes_written, 0); status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0);
if (!status) if (!status)
status = filemap_check_wb_err(dst->f_mapping, since); status = filemap_check_wb_err(dst->f_mapping, since);
if (!status) if (!status)
@@ -1948,10 +1947,8 @@ nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&fallocate->falloc_stateid, &fallocate->falloc_stateid,
WR_STATE, &nf, NULL); WR_STATE, &nf, NULL);
if (status != nfs_ok) { if (status != nfs_ok)
dprintk("NFSD: nfsd4_fallocate: couldn't process stateid!\n");
return status; return status;
}
status = nfsd4_vfs_fallocate(rqstp, &cstate->current_fh, nf->nf_file, status = nfsd4_vfs_fallocate(rqstp, &cstate->current_fh, nf->nf_file,
fallocate->falloc_offset, fallocate->falloc_offset,
@@ -2007,10 +2004,8 @@ nfsd4_seek(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh, status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
&seek->seek_stateid, &seek->seek_stateid,
RD_STATE, &nf, NULL); RD_STATE, &nf, NULL);
if (status) { if (status)
dprintk("NFSD: nfsd4_seek: couldn't process stateid!\n");
return status; return status;
}
switch (seek->seek_whence) { switch (seek->seek_whence) {
case NFS4_CONTENT_DATA: case NFS4_CONTENT_DATA:

View File

@@ -44,7 +44,9 @@
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/string_helpers.h> #include <linux/string_helpers.h>
#include <linux/fsnotify.h> #include <linux/fsnotify.h>
#include <linux/rhashtable.h>
#include <linux/nfs_ssc.h> #include <linux/nfs_ssc.h>
#include "xdr4.h" #include "xdr4.h"
#include "xdr4cb.h" #include "xdr4cb.h"
#include "vfs.h" #include "vfs.h"
@@ -84,6 +86,7 @@ static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner)
static void nfs4_free_ol_stateid(struct nfs4_stid *stid); static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
void nfsd4_end_grace(struct nfsd_net *nn); void nfsd4_end_grace(struct nfsd_net *nn);
static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps); static void _free_cpntf_state_locked(struct nfsd_net *nn, struct nfs4_cpntf_state *cps);
static void nfsd4_file_hash_remove(struct nfs4_file *fi);
/* Locking: */ /* Locking: */
@@ -588,11 +591,8 @@ static void nfsd4_free_file_rcu(struct rcu_head *rcu)
void void
put_nfs4_file(struct nfs4_file *fi) put_nfs4_file(struct nfs4_file *fi)
{ {
might_lock(&state_lock); if (refcount_dec_and_test(&fi->fi_ref)) {
nfsd4_file_hash_remove(fi);
if (refcount_dec_and_lock(&fi->fi_ref, &state_lock)) {
hlist_del_rcu(&fi->fi_hash);
spin_unlock(&state_lock);
WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate)); WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
WARN_ON_ONCE(!list_empty(&fi->fi_delegations)); WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu); call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
@@ -675,15 +675,26 @@ find_any_file(struct nfs4_file *f)
return ret; return ret;
} }
static struct nfsd_file *find_deleg_file(struct nfs4_file *f) static struct nfsd_file *find_any_file_locked(struct nfs4_file *f)
{ {
struct nfsd_file *ret = NULL; lockdep_assert_held(&f->fi_lock);
if (f->fi_fds[O_RDWR])
return f->fi_fds[O_RDWR];
if (f->fi_fds[O_WRONLY])
return f->fi_fds[O_WRONLY];
if (f->fi_fds[O_RDONLY])
return f->fi_fds[O_RDONLY];
return NULL;
}
static struct nfsd_file *find_deleg_file_locked(struct nfs4_file *f)
{
lockdep_assert_held(&f->fi_lock);
spin_lock(&f->fi_lock);
if (f->fi_deleg_file) if (f->fi_deleg_file)
ret = nfsd_file_get(f->fi_deleg_file); return f->fi_deleg_file;
spin_unlock(&f->fi_lock); return NULL;
return ret;
} }
static atomic_long_t num_delegations; static atomic_long_t num_delegations;
@@ -706,19 +717,20 @@ static unsigned int ownerstr_hashval(struct xdr_netobj *ownername)
return ret & OWNER_HASH_MASK; return ret & OWNER_HASH_MASK;
} }
/* hash table for nfs4_file */ static struct rhltable nfs4_file_rhltable ____cacheline_aligned_in_smp;
#define FILE_HASH_BITS 8
#define FILE_HASH_SIZE (1 << FILE_HASH_BITS)
static unsigned int file_hashval(struct svc_fh *fh) static const struct rhashtable_params nfs4_file_rhash_params = {
{ .key_len = sizeof_field(struct nfs4_file, fi_inode),
struct inode *inode = d_inode(fh->fh_dentry); .key_offset = offsetof(struct nfs4_file, fi_inode),
.head_offset = offsetof(struct nfs4_file, fi_rlist),
/* XXX: why not (here & in file cache) use inode? */ /*
return (unsigned int)hash_long(inode->i_ino, FILE_HASH_BITS); * Start with a single page hash table to reduce resizing churn
} * on light workloads.
*/
static struct hlist_head file_hashtbl[FILE_HASH_SIZE]; .min_size = 256,
.automatic_shrinking = true,
};
/* /*
* Check if courtesy clients have conflicting access and resolve it if possible * Check if courtesy clients have conflicting access and resolve it if possible
@@ -831,9 +843,9 @@ static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag)
swap(f2, fp->fi_fds[O_RDWR]); swap(f2, fp->fi_fds[O_RDWR]);
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
if (f1) if (f1)
nfsd_file_close(f1); nfsd_file_put(f1);
if (f2) if (f2)
nfsd_file_close(f2); nfsd_file_put(f2);
} }
} }
@@ -1355,6 +1367,8 @@ static void revoke_delegation(struct nfs4_delegation *dp)
WARN_ON(!list_empty(&dp->dl_recall_lru)); WARN_ON(!list_empty(&dp->dl_recall_lru));
trace_nfsd_stid_revoke(&dp->dl_stid);
if (clp->cl_minorversion) { if (clp->cl_minorversion) {
dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID; dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
refcount_inc(&dp->dl_stid.sc_count); refcount_inc(&dp->dl_stid.sc_count);
@@ -1819,13 +1833,12 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
int numslots = fattrs->maxreqs; int numslots = fattrs->maxreqs;
int slotsize = slot_bytes(fattrs); int slotsize = slot_bytes(fattrs);
struct nfsd4_session *new; struct nfsd4_session *new;
int mem, i; int i;
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *) BUILD_BUG_ON(struct_size(new, se_slots, NFSD_MAX_SLOTS_PER_SESSION)
+ sizeof(struct nfsd4_session) > PAGE_SIZE); > PAGE_SIZE);
mem = numslots * sizeof(struct nfsd4_slot *);
new = kzalloc(sizeof(*new) + mem, GFP_KERNEL); new = kzalloc(struct_size(new, se_slots, numslots), GFP_KERNEL);
if (!new) if (!new)
return NULL; return NULL;
/* allocate each struct nfsd4_slot and data cache in one piece */ /* allocate each struct nfsd4_slot and data cache in one piece */
@@ -2131,6 +2144,7 @@ static void __free_client(struct kref *k)
kfree(clp->cl_nii_domain.data); kfree(clp->cl_nii_domain.data);
kfree(clp->cl_nii_name.data); kfree(clp->cl_nii_name.data);
idr_destroy(&clp->cl_stateids); idr_destroy(&clp->cl_stateids);
kfree(clp->cl_ra);
kmem_cache_free(client_slab, clp); kmem_cache_free(client_slab, clp);
} }
@@ -2613,9 +2627,11 @@ static int nfs4_show_open(struct seq_file *s, struct nfs4_stid *st)
ols = openlockstateid(st); ols = openlockstateid(st);
oo = ols->st_stateowner; oo = ols->st_stateowner;
nf = st->sc_file; nf = st->sc_file;
file = find_any_file(nf);
spin_lock(&nf->fi_lock);
file = find_any_file_locked(nf);
if (!file) if (!file)
return 0; goto out;
seq_printf(s, "- "); seq_printf(s, "- ");
nfs4_show_stateid(s, &st->sc_stateid); nfs4_show_stateid(s, &st->sc_stateid);
@@ -2637,8 +2653,8 @@ static int nfs4_show_open(struct seq_file *s, struct nfs4_stid *st)
seq_printf(s, ", "); seq_printf(s, ", ");
nfs4_show_owner(s, oo); nfs4_show_owner(s, oo);
seq_printf(s, " }\n"); seq_printf(s, " }\n");
nfsd_file_put(file); out:
spin_unlock(&nf->fi_lock);
return 0; return 0;
} }
@@ -2652,9 +2668,10 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
ols = openlockstateid(st); ols = openlockstateid(st);
oo = ols->st_stateowner; oo = ols->st_stateowner;
nf = st->sc_file; nf = st->sc_file;
file = find_any_file(nf); spin_lock(&nf->fi_lock);
file = find_any_file_locked(nf);
if (!file) if (!file)
return 0; goto out;
seq_printf(s, "- "); seq_printf(s, "- ");
nfs4_show_stateid(s, &st->sc_stateid); nfs4_show_stateid(s, &st->sc_stateid);
@@ -2674,8 +2691,8 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
seq_printf(s, ", "); seq_printf(s, ", ");
nfs4_show_owner(s, oo); nfs4_show_owner(s, oo);
seq_printf(s, " }\n"); seq_printf(s, " }\n");
nfsd_file_put(file); out:
spin_unlock(&nf->fi_lock);
return 0; return 0;
} }
@@ -2687,9 +2704,10 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
ds = delegstateid(st); ds = delegstateid(st);
nf = st->sc_file; nf = st->sc_file;
file = find_deleg_file(nf); spin_lock(&nf->fi_lock);
file = find_deleg_file_locked(nf);
if (!file) if (!file)
return 0; goto out;
seq_printf(s, "- "); seq_printf(s, "- ");
nfs4_show_stateid(s, &st->sc_stateid); nfs4_show_stateid(s, &st->sc_stateid);
@@ -2705,8 +2723,8 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
seq_printf(s, ", "); seq_printf(s, ", ");
nfs4_show_fname(s, file); nfs4_show_fname(s, file);
seq_printf(s, " }\n"); seq_printf(s, " }\n");
nfsd_file_put(file); out:
spin_unlock(&nf->fi_lock);
return 0; return 0;
} }
@@ -2854,6 +2872,37 @@ static const struct tree_descr client_files[] = {
[3] = {""}, [3] = {""},
}; };
static int
nfsd4_cb_recall_any_done(struct nfsd4_callback *cb,
struct rpc_task *task)
{
trace_nfsd_cb_recall_any_done(cb, task);
switch (task->tk_status) {
case -NFS4ERR_DELAY:
rpc_delay(task, 2 * HZ);
return 0;
default:
return 1;
}
}
static void
nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
spin_lock(&nn->client_lock);
clear_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
put_client_renew_locked(clp);
spin_unlock(&nn->client_lock);
}
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
.done = nfsd4_cb_recall_any_done,
.release = nfsd4_cb_recall_any_release,
};
static struct nfs4_client *create_client(struct xdr_netobj name, static struct nfs4_client *create_client(struct xdr_netobj name,
struct svc_rqst *rqstp, nfs4_verifier *verf) struct svc_rqst *rqstp, nfs4_verifier *verf)
{ {
@@ -2891,6 +2940,14 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
free_client(clp); free_client(clp);
return NULL; return NULL;
} }
clp->cl_ra = kzalloc(sizeof(*clp->cl_ra), GFP_KERNEL);
if (!clp->cl_ra) {
free_client(clp);
return NULL;
}
clp->cl_ra_time = 0;
nfsd4_init_cb(&clp->cl_ra->ra_cb, clp, &nfsd4_cb_recall_any_ops,
NFSPROC4_CLNT_CB_RECALL_ANY);
return clp; return clp;
} }
@@ -4260,11 +4317,9 @@ static struct nfs4_file *nfsd4_alloc_file(void)
} }
/* OPEN Share state helper functions */ /* OPEN Share state helper functions */
static void nfsd4_init_file(struct svc_fh *fh, unsigned int hashval,
struct nfs4_file *fp)
{
lockdep_assert_held(&state_lock);
static void nfsd4_file_init(const struct svc_fh *fh, struct nfs4_file *fp)
{
refcount_set(&fp->fi_ref, 1); refcount_set(&fp->fi_ref, 1);
spin_lock_init(&fp->fi_lock); spin_lock_init(&fp->fi_lock);
INIT_LIST_HEAD(&fp->fi_stateids); INIT_LIST_HEAD(&fp->fi_stateids);
@@ -4282,7 +4337,6 @@ static void nfsd4_init_file(struct svc_fh *fh, unsigned int hashval,
INIT_LIST_HEAD(&fp->fi_lo_states); INIT_LIST_HEAD(&fp->fi_lo_states);
atomic_set(&fp->fi_lo_recalls, 0); atomic_set(&fp->fi_lo_recalls, 0);
#endif #endif
hlist_add_head_rcu(&fp->fi_hash, &file_hashtbl[hashval]);
} }
void void
@@ -4347,20 +4401,22 @@ out:
} }
static unsigned long static unsigned long
nfsd_courtesy_client_count(struct shrinker *shrink, struct shrink_control *sc) nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
{ {
int cnt; int count;
struct nfsd_net *nn = container_of(shrink, struct nfsd_net *nn = container_of(shrink,
struct nfsd_net, nfsd_client_shrinker); struct nfsd_net, nfsd_client_shrinker);
cnt = atomic_read(&nn->nfsd_courtesy_clients); count = atomic_read(&nn->nfsd_courtesy_clients);
if (cnt > 0) if (!count)
count = atomic_long_read(&num_delegations);
if (count)
mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0); mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
return (unsigned long)cnt; return (unsigned long)count;
} }
static unsigned long static unsigned long
nfsd_courtesy_client_scan(struct shrinker *shrink, struct shrink_control *sc) nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
{ {
return SHRINK_STOP; return SHRINK_STOP;
} }
@@ -4387,8 +4443,8 @@ nfsd4_init_leases_net(struct nfsd_net *nn)
nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB); nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB);
atomic_set(&nn->nfsd_courtesy_clients, 0); atomic_set(&nn->nfsd_courtesy_clients, 0);
nn->nfsd_client_shrinker.scan_objects = nfsd_courtesy_client_scan; nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
nn->nfsd_client_shrinker.count_objects = nfsd_courtesy_client_count; nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS; nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
return register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client"); return register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client");
} }
@@ -4667,71 +4723,80 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
nfs4_put_stid(&last->st_stid); nfs4_put_stid(&last->st_stid);
} }
/* search file_hashtbl[] for file */ static noinline_for_stack struct nfs4_file *
static struct nfs4_file * nfsd4_file_hash_lookup(const struct svc_fh *fhp)
find_file_locked(struct svc_fh *fh, unsigned int hashval)
{ {
struct nfs4_file *fp; struct inode *inode = d_inode(fhp->fh_dentry);
struct rhlist_head *tmp, *list;
struct nfs4_file *fi;
hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash, rcu_read_lock();
lockdep_is_held(&state_lock)) { list = rhltable_lookup(&nfs4_file_rhltable, &inode,
if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) { nfs4_file_rhash_params);
if (refcount_inc_not_zero(&fp->fi_ref)) rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
return fp; if (fh_match(&fi->fi_fhandle, &fhp->fh_handle)) {
if (refcount_inc_not_zero(&fi->fi_ref)) {
rcu_read_unlock();
return fi;
}
} }
} }
rcu_read_unlock();
return NULL; return NULL;
} }
static struct nfs4_file *insert_file(struct nfs4_file *new, struct svc_fh *fh, /*
unsigned int hashval) * On hash insertion, identify entries with the same inode but
* distinct filehandles. They will all be on the list returned
* by rhltable_lookup().
*
* inode->i_lock prevents racing insertions from adding an entry
* for the same inode/fhp pair twice.
*/
static noinline_for_stack struct nfs4_file *
nfsd4_file_hash_insert(struct nfs4_file *new, const struct svc_fh *fhp)
{ {
struct nfs4_file *fp; struct inode *inode = d_inode(fhp->fh_dentry);
struct rhlist_head *tmp, *list;
struct nfs4_file *ret = NULL; struct nfs4_file *ret = NULL;
bool alias_found = false; bool alias_found = false;
struct nfs4_file *fi;
int err;
spin_lock(&state_lock); rcu_read_lock();
hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash, spin_lock(&inode->i_lock);
lockdep_is_held(&state_lock)) {
if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) { list = rhltable_lookup(&nfs4_file_rhltable, &inode,
if (refcount_inc_not_zero(&fp->fi_ref)) nfs4_file_rhash_params);
ret = fp; rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
} else if (d_inode(fh->fh_dentry) == fp->fi_inode) if (fh_match(&fi->fi_fhandle, &fhp->fh_handle)) {
fp->fi_aliased = alias_found = true; if (refcount_inc_not_zero(&fi->fi_ref))
ret = fi;
} else
fi->fi_aliased = alias_found = true;
} }
if (likely(ret == NULL)) { if (ret)
nfsd4_init_file(fh, hashval, new); goto out_unlock;
new->fi_aliased = alias_found;
ret = new; nfsd4_file_init(fhp, new);
} err = rhltable_insert(&nfs4_file_rhltable, &new->fi_rlist,
spin_unlock(&state_lock); nfs4_file_rhash_params);
if (err)
goto out_unlock;
new->fi_aliased = alias_found;
ret = new;
out_unlock:
spin_unlock(&inode->i_lock);
rcu_read_unlock();
return ret; return ret;
} }
static struct nfs4_file * find_file(struct svc_fh *fh) static noinline_for_stack void nfsd4_file_hash_remove(struct nfs4_file *fi)
{ {
struct nfs4_file *fp; rhltable_remove(&nfs4_file_rhltable, &fi->fi_rlist,
unsigned int hashval = file_hashval(fh); nfs4_file_rhash_params);
rcu_read_lock();
fp = find_file_locked(fh, hashval);
rcu_read_unlock();
return fp;
}
static struct nfs4_file *
find_or_add_file(struct nfs4_file *new, struct svc_fh *fh)
{
struct nfs4_file *fp;
unsigned int hashval = file_hashval(fh);
rcu_read_lock();
fp = find_file_locked(fh, hashval);
rcu_read_unlock();
if (fp)
return fp;
return insert_file(new, fh, hashval);
} }
/* /*
@@ -4744,9 +4809,10 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
struct nfs4_file *fp; struct nfs4_file *fp;
__be32 ret = nfs_ok; __be32 ret = nfs_ok;
fp = find_file(current_fh); fp = nfsd4_file_hash_lookup(current_fh);
if (!fp) if (!fp)
return ret; return ret;
/* Check for conflicting share reservations */ /* Check for conflicting share reservations */
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
if (fp->fi_share_deny & deny_type) if (fp->fi_share_deny & deny_type)
@@ -5620,7 +5686,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
* and check for delegations in the process of being recalled. * and check for delegations in the process of being recalled.
* If not found, create the nfs4_file struct * If not found, create the nfs4_file struct
*/ */
fp = find_or_add_file(open->op_file, current_fh); fp = nfsd4_file_hash_insert(open->op_file, current_fh);
if (unlikely(!fp))
return nfserr_jukebox;
if (fp != open->op_file) { if (fp != open->op_file) {
status = nfs4_check_deleg(cl, open, &dp); status = nfs4_check_deleg(cl, open, &dp);
if (status) if (status)
@@ -6125,17 +6193,64 @@ laundromat_main(struct work_struct *laundry)
} }
static void static void
courtesy_client_reaper(struct work_struct *reaper) courtesy_client_reaper(struct nfsd_net *nn)
{ {
struct list_head reaplist; struct list_head reaplist;
struct delayed_work *dwork = to_delayed_work(reaper);
struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
nfsd_shrinker_work);
nfs4_get_courtesy_client_reaplist(nn, &reaplist); nfs4_get_courtesy_client_reaplist(nn, &reaplist);
nfs4_process_client_reaplist(&reaplist); nfs4_process_client_reaplist(&reaplist);
} }
static void
deleg_reaper(struct nfsd_net *nn)
{
struct list_head *pos, *next;
struct nfs4_client *clp;
struct list_head cblist;
INIT_LIST_HEAD(&cblist);
spin_lock(&nn->client_lock);
list_for_each_safe(pos, next, &nn->client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru);
if (clp->cl_state != NFSD4_ACTIVE ||
list_empty(&clp->cl_delegations) ||
atomic_read(&clp->cl_delegs_in_recall) ||
test_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags) ||
(ktime_get_boottime_seconds() -
clp->cl_ra_time < 5)) {
continue;
}
list_add(&clp->cl_ra_cblist, &cblist);
/* release in nfsd4_cb_recall_any_release */
atomic_inc(&clp->cl_rpc_users);
set_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
clp->cl_ra_time = ktime_get_boottime_seconds();
}
spin_unlock(&nn->client_lock);
while (!list_empty(&cblist)) {
clp = list_first_entry(&cblist, struct nfs4_client,
cl_ra_cblist);
list_del_init(&clp->cl_ra_cblist);
clp->cl_ra->ra_keep = 0;
clp->cl_ra->ra_bmval[0] = BIT(RCA4_TYPE_MASK_RDATA_DLG);
trace_nfsd_cb_recall_any(clp->cl_ra);
nfsd4_run_cb(&clp->cl_ra->ra_cb);
}
}
static void
nfsd4_state_shrinker_worker(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
nfsd_shrinker_work);
courtesy_client_reaper(nn);
deleg_reaper(nn);
}
static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp) static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stid *stp)
{ {
if (!fh_match(&fhp->fh_handle, &stp->sc_file->fi_fhandle)) if (!fh_match(&fhp->fh_handle, &stp->sc_file->fi_fhandle))
@@ -6902,6 +7017,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (status) if (status)
goto put_stateid; goto put_stateid;
trace_nfsd_deleg_return(stateid);
wake_up_var(d_inode(cstate->current_fh.fh_dentry)); wake_up_var(d_inode(cstate->current_fh.fh_dentry));
destroy_delegation(dp); destroy_delegation(dp);
put_stateid: put_stateid:
@@ -7958,7 +8074,7 @@ static int nfs4_state_create_net(struct net *net)
INIT_LIST_HEAD(&nn->blocked_locks_lru); INIT_LIST_HEAD(&nn->blocked_locks_lru);
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
INIT_DELAYED_WORK(&nn->nfsd_shrinker_work, courtesy_client_reaper); INIT_DELAYED_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
get_net(net); get_net(net);
return 0; return 0;
@@ -8034,10 +8150,16 @@ nfs4_state_start(void)
{ {
int ret; int ret;
ret = nfsd4_create_callback_queue(); ret = rhltable_init(&nfs4_file_rhltable, &nfs4_file_rhash_params);
if (ret) if (ret)
return ret; return ret;
ret = nfsd4_create_callback_queue();
if (ret) {
rhltable_destroy(&nfs4_file_rhltable);
return ret;
}
set_max_delegations(); set_max_delegations();
return 0; return 0;
} }
@@ -8068,6 +8190,7 @@ nfs4_state_shutdown_net(struct net *net)
nfsd4_client_tracking_exit(net); nfsd4_client_tracking_exit(net);
nfs4_state_destroy_net(net); nfs4_state_destroy_net(net);
rhltable_destroy(&nfs4_file_rhltable);
#ifdef CONFIG_NFSD_V4_2_INTER_SSC #ifdef CONFIG_NFSD_V4_2_INTER_SSC
nfsd4_ssc_shutdown_umount(nn); nfsd4_ssc_shutdown_umount(nn);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -581,7 +581,9 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET;
switch(num) { switch(num) {
#ifdef CONFIG_NFSD_V2
case 2: case 2:
#endif
case 3: case 3:
nfsd_vers(nn, num, cmd); nfsd_vers(nn, num, cmd);
break; break;
@@ -601,7 +603,9 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
} }
break; break;
default: default:
return -EINVAL; /* Ignore requests to disable non-existent versions */
if (cmd == NFSD_SET)
return -EINVAL;
} }
vers += len + 1; vers += len + 1;
} while ((len = qword_get(&mesg, vers, size)) > 0); } while ((len = qword_get(&mesg, vers, size)) > 0);

View File

@@ -64,8 +64,7 @@ struct readdir_cd {
extern struct svc_program nfsd_program; extern struct svc_program nfsd_program;
extern const struct svc_version nfsd_version2, nfsd_version3, extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
nfsd_version4;
extern struct mutex nfsd_mutex; extern struct mutex nfsd_mutex;
extern spinlock_t nfsd_drc_lock; extern spinlock_t nfsd_drc_lock;
extern unsigned long nfsd_drc_max_mem; extern unsigned long nfsd_drc_max_mem;

View File

@@ -220,7 +220,7 @@ __be32 fh_update(struct svc_fh *);
void fh_put(struct svc_fh *); void fh_put(struct svc_fh *);
static __inline__ struct svc_fh * static __inline__ struct svc_fh *
fh_copy(struct svc_fh *dst, struct svc_fh *src) fh_copy(struct svc_fh *dst, const struct svc_fh *src)
{ {
WARN_ON(src->fh_dentry); WARN_ON(src->fh_dentry);
@@ -229,7 +229,7 @@ fh_copy(struct svc_fh *dst, struct svc_fh *src)
} }
static inline void static inline void
fh_copy_shallow(struct knfsd_fh *dst, struct knfsd_fh *src) fh_copy_shallow(struct knfsd_fh *dst, const struct knfsd_fh *src)
{ {
dst->fh_size = src->fh_size; dst->fh_size = src->fh_size;
memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size); memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size);
@@ -243,7 +243,8 @@ fh_init(struct svc_fh *fhp, int maxsize)
return fhp; return fhp;
} }
static inline bool fh_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2) static inline bool fh_match(const struct knfsd_fh *fh1,
const struct knfsd_fh *fh2)
{ {
if (fh1->fh_size != fh2->fh_size) if (fh1->fh_size != fh2->fh_size)
return false; return false;
@@ -252,7 +253,8 @@ static inline bool fh_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
return true; return true;
} }
static inline bool fh_fsid_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2) static inline bool fh_fsid_match(const struct knfsd_fh *fh1,
const struct knfsd_fh *fh2)
{ {
if (fh1->fh_fsid_type != fh2->fh_fsid_type) if (fh1->fh_fsid_type != fh2->fh_fsid_type)
return false; return false;

View File

@@ -211,7 +211,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
if (resp->status == nfs_ok) if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat); resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox) else if (resp->status == nfserr_jukebox)
return rpc_drop_reply; __set_bit(RQ_DROPME, &rqstp->rq_flags);
return rpc_success; return rpc_success;
} }
@@ -246,7 +246,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
if (resp->status == nfs_ok) if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat); resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox) else if (resp->status == nfserr_jukebox)
return rpc_drop_reply; __set_bit(RQ_DROPME, &rqstp->rq_flags);
return rpc_success; return rpc_success;
} }
@@ -848,65 +848,3 @@ const struct svc_version nfsd_version2 = {
.vs_dispatch = nfsd_dispatch, .vs_dispatch = nfsd_dispatch,
.vs_xdrsize = NFS2_SVC_XDRSIZE, .vs_xdrsize = NFS2_SVC_XDRSIZE,
}; };
/*
* Map errnos to NFS errnos.
*/
__be32
nfserrno (int errno)
{
static struct {
__be32 nfserr;
int syserr;
} nfs_errtbl[] = {
{ nfs_ok, 0 },
{ nfserr_perm, -EPERM },
{ nfserr_noent, -ENOENT },
{ nfserr_io, -EIO },
{ nfserr_nxio, -ENXIO },
{ nfserr_fbig, -E2BIG },
{ nfserr_stale, -EBADF },
{ nfserr_acces, -EACCES },
{ nfserr_exist, -EEXIST },
{ nfserr_xdev, -EXDEV },
{ nfserr_mlink, -EMLINK },
{ nfserr_nodev, -ENODEV },
{ nfserr_notdir, -ENOTDIR },
{ nfserr_isdir, -EISDIR },
{ nfserr_inval, -EINVAL },
{ nfserr_fbig, -EFBIG },
{ nfserr_nospc, -ENOSPC },
{ nfserr_rofs, -EROFS },
{ nfserr_mlink, -EMLINK },
{ nfserr_nametoolong, -ENAMETOOLONG },
{ nfserr_notempty, -ENOTEMPTY },
#ifdef EDQUOT
{ nfserr_dquot, -EDQUOT },
#endif
{ nfserr_stale, -ESTALE },
{ nfserr_jukebox, -ETIMEDOUT },
{ nfserr_jukebox, -ERESTARTSYS },
{ nfserr_jukebox, -EAGAIN },
{ nfserr_jukebox, -EWOULDBLOCK },
{ nfserr_jukebox, -ENOMEM },
{ nfserr_io, -ETXTBSY },
{ nfserr_notsupp, -EOPNOTSUPP },
{ nfserr_toosmall, -ETOOSMALL },
{ nfserr_serverfault, -ESERVERFAULT },
{ nfserr_serverfault, -ENFILE },
{ nfserr_io, -EREMOTEIO },
{ nfserr_stale, -EOPENSTALE },
{ nfserr_io, -EUCLEAN },
{ nfserr_perm, -ENOKEY },
{ nfserr_no_grace, -ENOGRACE},
};
int i;
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].syserr == errno)
return nfs_errtbl[i].nfserr;
}
WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
return nfserr_io;
}

View File

@@ -91,8 +91,12 @@ unsigned long nfsd_drc_mem_used;
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
static struct svc_stat nfsd_acl_svcstats; static struct svc_stat nfsd_acl_svcstats;
static const struct svc_version *nfsd_acl_version[] = { static const struct svc_version *nfsd_acl_version[] = {
# if defined(CONFIG_NFSD_V2_ACL)
[2] = &nfsd_acl_version2, [2] = &nfsd_acl_version2,
# endif
# if defined(CONFIG_NFSD_V3_ACL)
[3] = &nfsd_acl_version3, [3] = &nfsd_acl_version3,
# endif
}; };
#define NFSD_ACL_MINVERS 2 #define NFSD_ACL_MINVERS 2
@@ -116,7 +120,9 @@ static struct svc_stat nfsd_acl_svcstats = {
#endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
static const struct svc_version *nfsd_version[] = { static const struct svc_version *nfsd_version[] = {
#if defined(CONFIG_NFSD_V2)
[2] = &nfsd_version2, [2] = &nfsd_version2,
#endif
[3] = &nfsd_version3, [3] = &nfsd_version3,
#if defined(CONFIG_NFSD_V4) #if defined(CONFIG_NFSD_V4)
[4] = &nfsd_version4, [4] = &nfsd_version4,
@@ -1054,7 +1060,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
svcxdr_init_encode(rqstp); svcxdr_init_encode(rqstp);
*statp = proc->pc_func(rqstp); *statp = proc->pc_func(rqstp);
if (*statp == rpc_drop_reply || test_bit(RQ_DROPME, &rqstp->rq_flags)) if (test_bit(RQ_DROPME, &rqstp->rq_flags))
goto out_update_drop; goto out_update_drop;
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream)) if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))

View File

@@ -368,6 +368,7 @@ struct nfs4_client {
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */ #define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
1 << NFSD4_CLIENT_CB_KILL) 1 << NFSD4_CLIENT_CB_KILL)
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
unsigned long cl_flags; unsigned long cl_flags;
const struct cred *cl_cb_cred; const struct cred *cl_cb_cred;
struct rpc_clnt *cl_cb_client; struct rpc_clnt *cl_cb_client;
@@ -411,6 +412,10 @@ struct nfs4_client {
unsigned int cl_state; unsigned int cl_state;
atomic_t cl_delegs_in_recall; atomic_t cl_delegs_in_recall;
struct nfsd4_cb_recall_any *cl_ra;
time64_t cl_ra_time;
struct list_head cl_ra_cblist;
}; };
/* struct nfs4_client_reset /* struct nfs4_client_reset
@@ -536,16 +541,13 @@ struct nfs4_clnt_odstate {
* inode can have multiple filehandles associated with it, so there is * inode can have multiple filehandles associated with it, so there is
* (potentially) a many to one relationship between this struct and struct * (potentially) a many to one relationship between this struct and struct
* inode. * inode.
*
* These are hashed by filehandle in the file_hashtbl, which is protected by
* the global state_lock spinlock.
*/ */
struct nfs4_file { struct nfs4_file {
refcount_t fi_ref; refcount_t fi_ref;
struct inode * fi_inode; struct inode * fi_inode;
bool fi_aliased; bool fi_aliased;
spinlock_t fi_lock; spinlock_t fi_lock;
struct hlist_node fi_hash; /* hash on fi_fhandle */ struct rhlist_head fi_rlist;
struct list_head fi_stateids; struct list_head fi_stateids;
union { union {
struct list_head fi_delegations; struct list_head fi_delegations;
@@ -639,6 +641,7 @@ enum nfsd4_cb_op {
NFSPROC4_CLNT_CB_OFFLOAD, NFSPROC4_CLNT_CB_OFFLOAD,
NFSPROC4_CLNT_CB_SEQUENCE, NFSPROC4_CLNT_CB_SEQUENCE,
NFSPROC4_CLNT_CB_NOTIFY_LOCK, NFSPROC4_CLNT_CB_NOTIFY_LOCK,
NFSPROC4_CLNT_CB_RECALL_ANY,
}; };
/* Returns true iff a is later than b: */ /* Returns true iff a is later than b: */

View File

@@ -9,9 +9,12 @@
#define _NFSD_TRACE_H #define _NFSD_TRACE_H
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <linux/sunrpc/xprt.h>
#include <trace/misc/nfs.h>
#include "export.h" #include "export.h"
#include "nfsfh.h" #include "nfsfh.h"
#include "xdr4.h"
#define NFSD_TRACE_PROC_RES_FIELDS \ #define NFSD_TRACE_PROC_RES_FIELDS \
__field(unsigned int, netns_ino) \ __field(unsigned int, netns_ino) \
@@ -604,6 +607,7 @@ DEFINE_STATEID_EVENT(layout_recall_release);
DEFINE_STATEID_EVENT(open); DEFINE_STATEID_EVENT(open);
DEFINE_STATEID_EVENT(deleg_read); DEFINE_STATEID_EVENT(deleg_read);
DEFINE_STATEID_EVENT(deleg_return);
DEFINE_STATEID_EVENT(deleg_recall); DEFINE_STATEID_EVENT(deleg_recall);
DECLARE_EVENT_CLASS(nfsd_stateseqid_class, DECLARE_EVENT_CLASS(nfsd_stateseqid_class,
@@ -636,6 +640,61 @@ DEFINE_EVENT(nfsd_stateseqid_class, nfsd_##name, \
DEFINE_STATESEQID_EVENT(preprocess); DEFINE_STATESEQID_EVENT(preprocess);
DEFINE_STATESEQID_EVENT(open_confirm); DEFINE_STATESEQID_EVENT(open_confirm);
TRACE_DEFINE_ENUM(NFS4_OPEN_STID);
TRACE_DEFINE_ENUM(NFS4_LOCK_STID);
TRACE_DEFINE_ENUM(NFS4_DELEG_STID);
TRACE_DEFINE_ENUM(NFS4_CLOSED_STID);
TRACE_DEFINE_ENUM(NFS4_REVOKED_DELEG_STID);
TRACE_DEFINE_ENUM(NFS4_CLOSED_DELEG_STID);
TRACE_DEFINE_ENUM(NFS4_LAYOUT_STID);
#define show_stid_type(x) \
__print_flags(x, "|", \
{ NFS4_OPEN_STID, "OPEN" }, \
{ NFS4_LOCK_STID, "LOCK" }, \
{ NFS4_DELEG_STID, "DELEG" }, \
{ NFS4_CLOSED_STID, "CLOSED" }, \
{ NFS4_REVOKED_DELEG_STID, "REVOKED" }, \
{ NFS4_CLOSED_DELEG_STID, "CLOSED_DELEG" }, \
{ NFS4_LAYOUT_STID, "LAYOUT" })
DECLARE_EVENT_CLASS(nfsd_stid_class,
TP_PROTO(
const struct nfs4_stid *stid
),
TP_ARGS(stid),
TP_STRUCT__entry(
__field(unsigned long, sc_type)
__field(int, sc_count)
__field(u32, cl_boot)
__field(u32, cl_id)
__field(u32, si_id)
__field(u32, si_generation)
),
TP_fast_assign(
const stateid_t *stp = &stid->sc_stateid;
__entry->sc_type = stid->sc_type;
__entry->sc_count = refcount_read(&stid->sc_count);
__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 ref=%d type=%s",
__entry->cl_boot, __entry->cl_id,
__entry->si_id, __entry->si_generation,
__entry->sc_count, show_stid_type(__entry->sc_type)
)
);
#define DEFINE_STID_EVENT(name) \
DEFINE_EVENT(nfsd_stid_class, nfsd_stid_##name, \
TP_PROTO(const struct nfs4_stid *stid), \
TP_ARGS(stid))
DEFINE_STID_EVENT(revoke);
DECLARE_EVENT_CLASS(nfsd_clientid_class, DECLARE_EVENT_CLASS(nfsd_clientid_class,
TP_PROTO(const clientid_t *clid), TP_PROTO(const clientid_t *clid),
TP_ARGS(clid), TP_ARGS(clid),
@@ -817,7 +876,8 @@ DEFINE_CLID_EVENT(confirmed_r);
__print_flags(val, "|", \ __print_flags(val, "|", \
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \ { 1 << NFSD_FILE_HASHED, "HASHED" }, \
{ 1 << NFSD_FILE_PENDING, "PENDING" }, \ { 1 << NFSD_FILE_PENDING, "PENDING" }, \
{ 1 << NFSD_FILE_REFERENCED, "REFERENCED"}) { 1 << NFSD_FILE_REFERENCED, "REFERENCED"}, \
{ 1 << NFSD_FILE_GC, "GC"})
DECLARE_EVENT_CLASS(nfsd_file_class, DECLARE_EVENT_CLASS(nfsd_file_class,
TP_PROTO(struct nfsd_file *nf), TP_PROTO(struct nfsd_file *nf),
@@ -849,10 +909,10 @@ DEFINE_EVENT(nfsd_file_class, name, \
TP_PROTO(struct nfsd_file *nf), \ TP_PROTO(struct nfsd_file *nf), \
TP_ARGS(nf)) TP_ARGS(nf))
DEFINE_NFSD_FILE_EVENT(nfsd_file_put_final); DEFINE_NFSD_FILE_EVENT(nfsd_file_free);
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash); DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash);
DEFINE_NFSD_FILE_EVENT(nfsd_file_put); DEFINE_NFSD_FILE_EVENT(nfsd_file_put);
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_dispose); DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_queue);
TRACE_EVENT(nfsd_file_alloc, TRACE_EVENT(nfsd_file_alloc,
TP_PROTO( TP_PROTO(
@@ -1181,6 +1241,37 @@ DEFINE_EVENT(nfsd_file_lruwalk_class, name, \
DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_gc_removed); DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_gc_removed);
DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_shrinker_removed); DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_shrinker_removed);
TRACE_EVENT(nfsd_file_fsync,
TP_PROTO(
const struct nfsd_file *nf,
int ret
),
TP_ARGS(nf, ret),
TP_STRUCT__entry(
__field(void *, nf_inode)
__field(int, nf_ref)
__field(int, ret)
__field(unsigned long, nf_flags)
__field(unsigned char, nf_may)
__field(struct file *, nf_file)
),
TP_fast_assign(
__entry->nf_inode = nf->nf_inode;
__entry->nf_ref = refcount_read(&nf->nf_ref);
__entry->ret = ret;
__entry->nf_flags = nf->nf_flags;
__entry->nf_may = nf->nf_may;
__entry->nf_file = nf->nf_file;
),
TP_printk("inode=%p ref=%d flags=%s may=%s nf_file=%p ret=%d",
__entry->nf_inode,
__entry->nf_ref,
show_nf_flags(__entry->nf_flags),
show_nfsd_may_flags(__entry->nf_may),
__entry->nf_file, __entry->ret
)
);
#include "cache.h" #include "cache.h"
TRACE_DEFINE_ENUM(RC_DROPIT); TRACE_DEFINE_ENUM(RC_DROPIT);
@@ -1474,6 +1565,32 @@ TRACE_EVENT(nfsd_cb_offload,
__entry->fh_hash, __entry->count, __entry->status) __entry->fh_hash, __entry->count, __entry->status)
); );
TRACE_EVENT(nfsd_cb_recall_any,
TP_PROTO(
const struct nfsd4_cb_recall_any *ra
),
TP_ARGS(ra),
TP_STRUCT__entry(
__field(u32, cl_boot)
__field(u32, cl_id)
__field(u32, keep)
__field(unsigned long, bmval0)
__sockaddr(addr, ra->ra_cb.cb_clp->cl_cb_conn.cb_addrlen)
),
TP_fast_assign(
__entry->cl_boot = ra->ra_cb.cb_clp->cl_clientid.cl_boot;
__entry->cl_id = ra->ra_cb.cb_clp->cl_clientid.cl_id;
__entry->keep = ra->ra_keep;
__entry->bmval0 = ra->ra_bmval[0];
__assign_sockaddr(addr, &ra->ra_cb.cb_clp->cl_addr,
ra->ra_cb.cb_clp->cl_cb_conn.cb_addrlen);
),
TP_printk("addr=%pISpc client %08x:%08x keep=%u bmval0=%s",
__get_sockaddr(addr), __entry->cl_boot, __entry->cl_id,
__entry->keep, show_rca_mask(__entry->bmval0)
)
);
DECLARE_EVENT_CLASS(nfsd_cb_done_class, DECLARE_EVENT_CLASS(nfsd_cb_done_class,
TP_PROTO( TP_PROTO(
const stateid_t *stp, const stateid_t *stp,
@@ -1513,6 +1630,27 @@ DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_notify_lock_done);
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_layout_done); DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_layout_done);
DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_offload_done); DEFINE_NFSD_CB_DONE_EVENT(nfsd_cb_offload_done);
TRACE_EVENT(nfsd_cb_recall_any_done,
TP_PROTO(
const struct nfsd4_callback *cb,
const struct rpc_task *task
),
TP_ARGS(cb, task),
TP_STRUCT__entry(
__field(u32, cl_boot)
__field(u32, cl_id)
__field(int, status)
),
TP_fast_assign(
__entry->status = task->tk_status;
__entry->cl_boot = cb->cb_clp->cl_clientid.cl_boot;
__entry->cl_id = cb->cb_clp->cl_clientid.cl_id;
),
TP_printk("client %08x:%08x status=%d",
__entry->cl_boot, __entry->cl_id, __entry->status
)
);
#endif /* _NFSD_TRACE_H */ #endif /* _NFSD_TRACE_H */
#undef TRACE_INCLUDE_PATH #undef TRACE_INCLUDE_PATH

View File

@@ -49,6 +49,69 @@
#define NFSDDBG_FACILITY NFSDDBG_FILEOP #define NFSDDBG_FACILITY NFSDDBG_FILEOP
/**
* nfserrno - Map Linux errnos to NFS errnos
* @errno: POSIX(-ish) error code to be mapped
*
* Returns the appropriate (net-endian) nfserr_* (or nfs_ok if errno is 0). If
* it's an error we don't expect, log it once and return nfserr_io.
*/
__be32
nfserrno (int errno)
{
static struct {
__be32 nfserr;
int syserr;
} nfs_errtbl[] = {
{ nfs_ok, 0 },
{ nfserr_perm, -EPERM },
{ nfserr_noent, -ENOENT },
{ nfserr_io, -EIO },
{ nfserr_nxio, -ENXIO },
{ nfserr_fbig, -E2BIG },
{ nfserr_stale, -EBADF },
{ nfserr_acces, -EACCES },
{ nfserr_exist, -EEXIST },
{ nfserr_xdev, -EXDEV },
{ nfserr_mlink, -EMLINK },
{ nfserr_nodev, -ENODEV },
{ nfserr_notdir, -ENOTDIR },
{ nfserr_isdir, -EISDIR },
{ nfserr_inval, -EINVAL },
{ nfserr_fbig, -EFBIG },
{ nfserr_nospc, -ENOSPC },
{ nfserr_rofs, -EROFS },
{ nfserr_mlink, -EMLINK },
{ nfserr_nametoolong, -ENAMETOOLONG },
{ nfserr_notempty, -ENOTEMPTY },
{ nfserr_dquot, -EDQUOT },
{ nfserr_stale, -ESTALE },
{ nfserr_jukebox, -ETIMEDOUT },
{ nfserr_jukebox, -ERESTARTSYS },
{ nfserr_jukebox, -EAGAIN },
{ nfserr_jukebox, -EWOULDBLOCK },
{ nfserr_jukebox, -ENOMEM },
{ nfserr_io, -ETXTBSY },
{ nfserr_notsupp, -EOPNOTSUPP },
{ nfserr_toosmall, -ETOOSMALL },
{ nfserr_serverfault, -ESERVERFAULT },
{ nfserr_serverfault, -ENFILE },
{ nfserr_io, -EREMOTEIO },
{ nfserr_stale, -EOPENSTALE },
{ nfserr_io, -EUCLEAN },
{ nfserr_perm, -ENOKEY },
{ nfserr_no_grace, -ENOGRACE},
};
int i;
for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
if (nfs_errtbl[i].syserr == errno)
return nfs_errtbl[i].nfserr;
}
WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
return nfserr_io;
}
/* /*
* Called from nfsd_lookup and encode_dirent. Check if we have crossed * Called from nfsd_lookup and encode_dirent. Check if we have crossed
* a mount point. * a mount point.
@@ -1085,7 +1148,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 err; __be32 err;
trace_nfsd_read_start(rqstp, fhp, offset, *count); trace_nfsd_read_start(rqstp, fhp, offset, *count);
err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_READ, &nf); err = nfsd_file_acquire_gc(rqstp, fhp, NFSD_MAY_READ, &nf);
if (err) if (err)
return err; return err;
@@ -1117,7 +1180,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
trace_nfsd_write_start(rqstp, fhp, offset, *cnt); trace_nfsd_write_start(rqstp, fhp, offset, *cnt);
err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_WRITE, &nf); err = nfsd_file_acquire_gc(rqstp, fhp, NFSD_MAY_WRITE, &nf);
if (err) if (err)
goto out; goto out;
@@ -1133,6 +1196,7 @@ out:
* nfsd_commit - Commit pending writes to stable storage * nfsd_commit - Commit pending writes to stable storage
* @rqstp: RPC request being processed * @rqstp: RPC request being processed
* @fhp: NFS filehandle * @fhp: NFS filehandle
* @nf: target file
* @offset: raw offset from beginning of file * @offset: raw offset from beginning of file
* @count: raw count of bytes to sync * @count: raw count of bytes to sync
* @verf: filled in with the server's current write verifier * @verf: filled in with the server's current write verifier
@@ -1149,19 +1213,13 @@ out:
* An nfsstat value in network byte order. * An nfsstat value in network byte order.
*/ */
__be32 __be32
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, u64 offset, nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
u32 count, __be32 *verf) u64 offset, u32 count, __be32 *verf)
{ {
__be32 err = nfs_ok;
u64 maxbytes; u64 maxbytes;
loff_t start, end; loff_t start, end;
struct nfsd_net *nn; struct nfsd_net *nn;
struct nfsd_file *nf;
__be32 err;
err = nfsd_file_acquire(rqstp, fhp,
NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &nf);
if (err)
goto out;
/* /*
* Convert the client-provided (offset, count) range to a * Convert the client-provided (offset, count) range to a
@@ -1202,8 +1260,6 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, u64 offset,
} else } else
nfsd_copy_write_verifier(verf, nn); nfsd_copy_write_verifier(verf, nn);
nfsd_file_put(nf);
out:
return err; return err;
} }
@@ -1305,7 +1361,6 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
iap->ia_mode &= ~current_umask(); iap->ia_mode &= ~current_umask();
err = 0; err = 0;
host_err = 0;
switch (type) { switch (type) {
case S_IFREG: case S_IFREG:
host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true); host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true);

View File

@@ -60,6 +60,7 @@ static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
posix_acl_release(attrs->na_dpacl); posix_acl_release(attrs->na_dpacl);
} }
__be32 nfserrno (int errno);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp); struct svc_export **expp);
__be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *, __be32 nfsd_lookup(struct svc_rqst *, struct svc_fh *,
@@ -88,7 +89,8 @@ __be32 nfsd_access(struct svc_rqst *, struct svc_fh *, u32 *, u32 *);
__be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct svc_fh *resfhp, struct nfsd_attrs *iap); struct svc_fh *resfhp, struct nfsd_attrs *iap);
__be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp, __be32 nfsd_commit(struct svc_rqst *rqst, struct svc_fh *fhp,
u64 offset, u32 count, __be32 *verf); struct nfsd_file *nf, u64 offset, u32 count,
__be32 *verf);
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
char *name, void **bufp, int *lenp); char *name, void **bufp, int *lenp);

View File

@@ -896,5 +896,10 @@ struct nfsd4_operation {
union nfsd4_op_u *); union nfsd4_op_u *);
}; };
struct nfsd4_cb_recall_any {
struct nfsd4_callback ra_cb;
u32 ra_keep;
u32 ra_bmval[1];
};
#endif #endif

View File

@@ -48,3 +48,9 @@
#define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \ #define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \ cb_sequence_dec_sz + \
op_dec_sz) op_dec_sz)
#define NFS4_enc_cb_recall_any_sz (cb_compound_enc_hdr_sz + \
cb_sequence_enc_sz + \
1 + 1 + 1)
#define NFS4_dec_cb_recall_any_sz (cb_compound_dec_hdr_sz + \
cb_sequence_dec_sz + \
op_dec_sz)

View File

@@ -732,4 +732,17 @@ enum nfs4_setxattr_options {
SETXATTR4_CREATE = 1, SETXATTR4_CREATE = 1,
SETXATTR4_REPLACE = 2, SETXATTR4_REPLACE = 2,
}; };
enum {
RCA4_TYPE_MASK_RDATA_DLG = 0,
RCA4_TYPE_MASK_WDATA_DLG = 1,
RCA4_TYPE_MASK_DIR_DLG = 2,
RCA4_TYPE_MASK_FILE_LAYOUT = 3,
RCA4_TYPE_MASK_BLK_LAYOUT = 4,
RCA4_TYPE_MASK_OBJ_LAYOUT_MIN = 8,
RCA4_TYPE_MASK_OBJ_LAYOUT_MAX = 9,
RCA4_TYPE_MASK_OTHER_LAYOUT_MIN = 12,
RCA4_TYPE_MASK_OTHER_LAYOUT_MAX = 15,
};
#endif #endif

View File

@@ -220,13 +220,6 @@ static inline __be32 svc_getu32(struct kvec *iov)
return val; return val;
} }
static inline void svc_ungetu32(struct kvec *iov)
{
__be32 *vp = (__be32 *)iov->iov_base;
iov->iov_base = (void *)(vp - 1);
iov->iov_len += sizeof(*vp);
}
static inline void svc_putu32(struct kvec *iov, __be32 val) static inline void svc_putu32(struct kvec *iov, __be32 val)
{ {
__be32 *vp = iov->iov_base + iov->iov_len; __be32 *vp = iov->iov_base + iov->iov_len;
@@ -311,7 +304,6 @@ struct svc_rqst {
struct auth_domain * rq_gssclient; /* "gss/"-style peer info */ struct auth_domain * rq_gssclient; /* "gss/"-style peer info */
struct svc_cacherep * rq_cacherep; /* cache info */ struct svc_cacherep * rq_cacherep; /* cache info */
struct task_struct *rq_task; /* service thread */ struct task_struct *rq_task; /* service thread */
spinlock_t rq_lock; /* per-request lock */
struct net *rq_bc_net; /* pointer to backchannel's struct net *rq_bc_net; /* pointer to backchannel's
* net namespace * net namespace
*/ */

View File

@@ -13,7 +13,7 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <trace/events/sunrpc_base.h> #include <trace/misc/sunrpc.h>
/** /**
** GSS-API related trace events ** GSS-API related trace events

View File

@@ -15,8 +15,8 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <rdma/ib_cm.h> #include <rdma/ib_cm.h>
#include <trace/events/rdma.h> #include <trace/misc/rdma.h>
#include <trace/events/sunrpc_base.h> #include <trace/misc/sunrpc.h>
/** /**
** Event classes ** Event classes

View File

@@ -14,7 +14,7 @@
#include <linux/net.h> #include <linux/net.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <trace/events/sunrpc_base.h> #include <trace/misc/sunrpc.h>
TRACE_DEFINE_ENUM(SOCK_STREAM); TRACE_DEFINE_ENUM(SOCK_STREAM);
TRACE_DEFINE_ENUM(SOCK_DGRAM); TRACE_DEFINE_ENUM(SOCK_DGRAM);
@@ -1666,11 +1666,13 @@ TRACE_DEFINE_ENUM(SVC_COMPLETE);
#define SVC_RQST_ENDPOINT_VARARGS \ #define SVC_RQST_ENDPOINT_VARARGS \
__entry->xid, __get_sockaddr(server), __get_sockaddr(client) __entry->xid, __get_sockaddr(server), __get_sockaddr(client)
TRACE_EVENT(svc_authenticate, TRACE_EVENT_CONDITION(svc_authenticate,
TP_PROTO(const struct svc_rqst *rqst, int auth_res), TP_PROTO(const struct svc_rqst *rqst, int auth_res),
TP_ARGS(rqst, auth_res), TP_ARGS(rqst, auth_res),
TP_CONDITION(auth_res != SVC_OK && auth_res != SVC_COMPLETE),
TP_STRUCT__entry( TP_STRUCT__entry(
SVC_RQST_ENDPOINT_FIELDS(rqst) SVC_RQST_ENDPOINT_FIELDS(rqst)

View File

@@ -360,6 +360,18 @@ TRACE_DEFINE_ENUM(IOMODE_ANY);
{ IOMODE_RW, "RW" }, \ { IOMODE_RW, "RW" }, \
{ IOMODE_ANY, "ANY" }) { IOMODE_ANY, "ANY" })
#define show_rca_mask(x) \
__print_flags(x, "|", \
{ BIT(RCA4_TYPE_MASK_RDATA_DLG), "RDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_WDATA_DLG), "WDATA_DLG" }, \
{ BIT(RCA4_TYPE_MASK_DIR_DLG), "DIR_DLG" }, \
{ BIT(RCA4_TYPE_MASK_FILE_LAYOUT), "FILE_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_BLK_LAYOUT), "BLK_LAYOUT" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MIN), "OBJ_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OBJ_LAYOUT_MAX), "OBJ_LAYOUT_MAX" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MIN), "OTHER_LAYOUT_MIN" }, \
{ BIT(RCA4_TYPE_MASK_OTHER_LAYOUT_MAX), "OTHER_LAYOUT_MAX" })
#define show_nfs4_seq4_status(x) \ #define show_nfs4_seq4_status(x) \
__print_flags(x, "|", \ __print_flags(x, "|", \
{ SEQ4_STATUS_CB_PATH_DOWN, "CB_PATH_DOWN" }, \ { SEQ4_STATUS_CB_PATH_DOWN, "CB_PATH_DOWN" }, \

View File

@@ -49,11 +49,36 @@
#include <linux/sunrpc/svcauth.h> #include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/svcauth_gss.h> #include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/cache.h> #include <linux/sunrpc/cache.h>
#include <linux/sunrpc/gss_krb5.h>
#include <trace/events/rpcgss.h> #include <trace/events/rpcgss.h>
#include "gss_rpc_upcall.h" #include "gss_rpc_upcall.h"
/*
* Unfortunately there isn't a maximum checksum size exported via the
* GSS API. Manufacture one based on GSS mechanisms supported by this
* implementation.
*/
#define GSS_MAX_CKSUMSIZE (GSS_KRB5_TOK_HDR_LEN + GSS_KRB5_MAX_CKSUM_LEN)
/*
* This value may be increased in the future to accommodate other
* usage of the scratch buffer.
*/
#define GSS_SCRATCH_SIZE GSS_MAX_CKSUMSIZE
struct gss_svc_data {
/* decoded gss client cred: */
struct rpc_gss_wire_cred clcred;
/* save a pointer to the beginning of the encoded verifier,
* for use in encryption/checksumming in svcauth_gss_release: */
__be32 *verf_start;
struct rsc *rsci;
/* for temporary results */
u8 gsd_scratch[GSS_SCRATCH_SIZE];
};
/* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests /* The rpcsec_init cache is used for mapping RPCSEC_GSS_{,CONT_}INIT requests
* into replies. * into replies.
@@ -887,13 +912,11 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
static int static int
unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
{ {
struct gss_svc_data *gsd = rqstp->rq_auth_data;
u32 integ_len, rseqno, maj_stat; u32 integ_len, rseqno, maj_stat;
int stat = -EINVAL;
struct xdr_netobj mic; struct xdr_netobj mic;
struct xdr_buf integ_buf; struct xdr_buf integ_buf;
mic.data = NULL;
/* NFS READ normally uses splice to send data in-place. However /* NFS READ normally uses splice to send data in-place. However
* the data in cache can change after the reply's MIC is computed * the data in cache can change after the reply's MIC is computed
* but before the RPC reply is sent. To prevent the client from * but before the RPC reply is sent. To prevent the client from
@@ -917,11 +940,9 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
/* copy out mic... */ /* copy out mic... */
if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
goto unwrap_failed; goto unwrap_failed;
if (mic.len > RPC_MAX_AUTH_SIZE) if (mic.len > sizeof(gsd->gsd_scratch))
goto unwrap_failed;
mic.data = kmalloc(mic.len, GFP_KERNEL);
if (!mic.data)
goto unwrap_failed; goto unwrap_failed;
mic.data = gsd->gsd_scratch;
if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
goto unwrap_failed; goto unwrap_failed;
maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
@@ -932,20 +953,17 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
goto bad_seqno; goto bad_seqno;
/* trim off the mic and padding at the end before returning */ /* trim off the mic and padding at the end before returning */
xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
stat = 0; return 0;
out:
kfree(mic.data);
return stat;
unwrap_failed: unwrap_failed:
trace_rpcgss_svc_unwrap_failed(rqstp); trace_rpcgss_svc_unwrap_failed(rqstp);
goto out; return -EINVAL;
bad_seqno: bad_seqno:
trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno); trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
goto out; return -EINVAL;
bad_mic: bad_mic:
trace_rpcgss_svc_mic(rqstp, maj_stat); trace_rpcgss_svc_mic(rqstp, maj_stat);
goto out; return -EINVAL;
} }
static inline int static inline int
@@ -1023,15 +1041,6 @@ bad_unwrap:
return -EINVAL; return -EINVAL;
} }
struct gss_svc_data {
/* decoded gss client cred: */
struct rpc_gss_wire_cred clcred;
/* save a pointer to the beginning of the encoded verifier,
* for use in encryption/checksumming in svcauth_gss_release: */
__be32 *verf_start;
struct rsc *rsci;
};
static int static int
svcauth_gss_set_client(struct svc_rqst *rqstp) svcauth_gss_set_client(struct svc_rqst *rqstp)
{ {
@@ -1162,18 +1171,23 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
return res; return res;
inlen = svc_getnl(argv); inlen = svc_getnl(argv);
if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) {
kfree(in_handle->data);
return SVC_DENIED; return SVC_DENIED;
}
pages = DIV_ROUND_UP(inlen, PAGE_SIZE); pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL); in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL);
if (!in_token->pages) if (!in_token->pages) {
kfree(in_handle->data);
return SVC_DENIED; return SVC_DENIED;
}
in_token->page_base = 0; in_token->page_base = 0;
in_token->page_len = inlen; in_token->page_len = inlen;
for (i = 0; i < pages; i++) { for (i = 0; i < pages; i++) {
in_token->pages[i] = alloc_page(GFP_KERNEL); in_token->pages[i] = alloc_page(GFP_KERNEL);
if (!in_token->pages[i]) { if (!in_token->pages[i]) {
kfree(in_handle->data);
gss_free_in_token_pages(in_token); gss_free_in_token_pages(in_token);
return SVC_DENIED; return SVC_DENIED;
} }

View File

@@ -638,7 +638,6 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
return rqstp; return rqstp;
__set_bit(RQ_BUSY, &rqstp->rq_flags); __set_bit(RQ_BUSY, &rqstp->rq_flags);
spin_lock_init(&rqstp->rq_lock);
rqstp->rq_server = serv; rqstp->rq_server = serv;
rqstp->rq_pool = pool; rqstp->rq_pool = pool;
@@ -1281,8 +1280,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
/* Also give the program a chance to reject this call: */ /* Also give the program a chance to reject this call: */
if (auth_res == SVC_OK && progp) if (auth_res == SVC_OK && progp)
auth_res = progp->pg_authenticate(rqstp); auth_res = progp->pg_authenticate(rqstp);
if (auth_res != SVC_OK) trace_svc_authenticate(rqstp, auth_res);
trace_svc_authenticate(rqstp, auth_res);
switch (auth_res) { switch (auth_res) {
case SVC_OK: case SVC_OK:
break; break;

View File

@@ -1224,30 +1224,34 @@ EXPORT_SYMBOL(xdr_restrict_buflen);
/** /**
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
* @pages: list of pages * @pages: array of pages to insert
* @base: offset of first byte * @base: starting offset of first data byte in @pages
* @len: length of data in bytes * @len: number of data bytes in @pages to insert
* *
* After the @pages are added, the tail iovec is instantiated pointing to
* end of the head buffer, and the stream is set up to encode subsequent
* items into the tail.
*/ */
void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base, void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
unsigned int len) unsigned int len)
{ {
struct xdr_buf *buf = xdr->buf; struct xdr_buf *buf = xdr->buf;
struct kvec *iov = buf->tail; struct kvec *tail = buf->tail;
buf->pages = pages; buf->pages = pages;
buf->page_base = base; buf->page_base = base;
buf->page_len = len; buf->page_len = len;
iov->iov_base = (char *)xdr->p; tail->iov_base = xdr->p;
iov->iov_len = 0; tail->iov_len = 0;
xdr->iov = iov; xdr->iov = tail;
if (len & 3) { if (len & 3) {
unsigned int pad = 4 - (len & 3); unsigned int pad = 4 - (len & 3);
BUG_ON(xdr->p >= xdr->end); BUG_ON(xdr->p >= xdr->end);
iov->iov_base = (char *)xdr->p + (len & 3); tail->iov_base = (char *)xdr->p + (len & 3);
iov->iov_len += pad; tail->iov_len += pad;
len += pad; len += pad;
*xdr->p++ = 0; *xdr->p++ = 0;
} }