mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 02:36:02 +00:00
NFSD 6.2 Release Notes
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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmOXPE4ACgkQM2qzM29m f5faaRAAh7YT5V61afPbfgBybO5AbDzztpZSNjNjLZs78piSnFp6hP75yNtTviwQ 1o7St13/NkCmDaIdGUpr02U01zbM1BDOq2wGckImOJLNSgb7xHV5r4PqkRiFkh0t QYSnwG+wp8fDUJeCL/nAOAu9I9EQUqHzWchxiU/h8ln2hN3rXUlIRSeo17Wy7zkD cNIcoAjTi9fzY3dE6H4r+lZTdNCYH+AdzChmKrHdRZQwq0Xs3FWv4gAMTLbDuD4P B6NDHz0Umn6XnFsJGptwozkwaWeMQw4GyJj/3iUiO8JF209SaoYXMPjJAyG6tYYa fUrgv4UXGeXjigDbLBA5IYxfhX7GXjMQSaj3edhzyrl8P74q4/Cq/8fDUnAZ841m E+TGSCPIQD0QuIjdXxLv9KLY8JNThSfcAt6jr5GBXhPZQr8xpS0BqK/Onr68fgZC Lpull5xN68L4A1B7cf2GNPuMyvkBKxwSGXOehldh/BkvpVMjFwqd4/q5xWC+6CcQ tbOkjTbbSS71nzJwZip0NphaYCa3qQPzKT4SZzn/I4I9W5otbwYBx734Bw46gTDE ZPUXTuJ00VPgX07wbLRahg521Fwzr+8sk1WnVYq82PoaMh1l9FjzLNGouQWBdo3E UzIo/KUfQKmoZce6O723L6OI4ffdK5oMtfaTpe+SiUPpV1lUAcA= =jNlu -----END PGP SIGNATURE----- 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:
commit
764822972d
@ -10082,6 +10082,7 @@ F: drivers/infiniband/
|
||||
F: include/rdma/
|
||||
F: include/trace/events/ib_mad.h
|
||||
F: include/trace/events/ib_umad.h
|
||||
F: include/trace/misc/rdma.h
|
||||
F: include/uapi/linux/if_infiniband.h
|
||||
F: include/uapi/rdma/
|
||||
F: samples/bpf/ibumad_kern.c
|
||||
@ -11168,11 +11169,18 @@ L: linux-nfs@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://nfs.sourceforge.net/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
|
||||
F: fs/exportfs/
|
||||
F: fs/lockd/
|
||||
F: fs/nfs_common/
|
||||
F: fs/nfsd/
|
||||
F: include/linux/lockd/
|
||||
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/sunrpc/
|
||||
F: net/sunrpc/
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#include <linux/tracepoint.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
|
||||
|
@ -15,7 +15,7 @@
|
||||
#define _TRACE_RDMA_CMA_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <trace/events/rdma.h>
|
||||
#include <trace/misc/rdma.h>
|
||||
|
||||
|
||||
DECLARE_EVENT_CLASS(cma_fsm_class,
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/sched.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);
|
||||
@ -132,8 +132,8 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
|
||||
inode_unlock(dentry->d_inode);
|
||||
|
||||
if (IS_ERR(parent)) {
|
||||
dprintk("%s: get_parent of %ld failed, err %d\n",
|
||||
__func__, dentry->d_inode->i_ino, PTR_ERR(parent));
|
||||
dprintk("get_parent of %lu failed, err %ld\n",
|
||||
dentry->d_inode->i_ino, PTR_ERR(parent));
|
||||
return parent;
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
|
||||
dprintk("%s: found name: %s\n", __func__, nbuf);
|
||||
tmp = lookup_one_unlocked(mnt_user_ns(mnt), nbuf, parent, strlen(nbuf));
|
||||
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);
|
||||
goto out_err;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
*filp = file;
|
||||
|
||||
/* 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_pid = current->tgid;
|
||||
lock->fl.fl_start = (loff_t)lock->lock_start;
|
||||
|
@ -659,11 +659,13 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
|
||||
nlmsvc_cancel_blocked(net, file, lock);
|
||||
|
||||
lock->fl.fl_type = F_UNLCK;
|
||||
if (file->f_file[O_RDONLY])
|
||||
error = vfs_lock_file(file->f_file[O_RDONLY], F_SETLK,
|
||||
lock->fl.fl_file = file->f_file[O_RDONLY];
|
||||
if (lock->fl.fl_file)
|
||||
error = vfs_lock_file(lock->fl.fl_file, F_SETLK,
|
||||
&lock->fl, NULL);
|
||||
if (file->f_file[O_WRONLY])
|
||||
error = vfs_lock_file(file->f_file[O_WRONLY], F_SETLK,
|
||||
lock->fl.fl_file = file->f_file[O_WRONLY];
|
||||
if (lock->fl.fl_file)
|
||||
error |= vfs_lock_file(lock->fl.fl_file, F_SETLK,
|
||||
&lock->fl, NULL);
|
||||
|
||||
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);
|
||||
mutex_unlock(&file->f_mutex);
|
||||
if (block != NULL) {
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
vfs_cancel_lock(block->b_file->f_file[mode],
|
||||
&block->b_call->a_args.lock.fl);
|
||||
struct file_lock *fl = &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);
|
||||
nlmsvc_release_block(block);
|
||||
}
|
||||
|
@ -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 */
|
||||
mode = lock_to_openmode(&lock->fl);
|
||||
lock->fl.fl_flags = FL_POSIX;
|
||||
lock->fl.fl_file = file->f_file[mode];
|
||||
lock->fl.fl_pid = current->tgid;
|
||||
lock->fl.fl_lmops = &nlmsvc_lock_operations;
|
||||
|
@ -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;
|
||||
|
||||
@ -184,12 +184,15 @@ static int nlm_unlock_files(struct nlm_file *file, fl_owner_t owner)
|
||||
lock.fl_type = F_UNLCK;
|
||||
lock.fl_start = 0;
|
||||
lock.fl_end = OFFSET_MAX;
|
||||
lock.fl_owner = owner;
|
||||
if (file->f_file[O_RDONLY] &&
|
||||
vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, &lock, NULL))
|
||||
lock.fl_owner = fl->fl_owner;
|
||||
lock.fl_pid = fl->fl_pid;
|
||||
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;
|
||||
if (file->f_file[O_WRONLY] &&
|
||||
vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, &lock, NULL))
|
||||
lock.fl_file = file->f_file[O_WRONLY];
|
||||
if (lock.fl_file && vfs_lock_file(lock.fl_file, F_SETLK, &lock, NULL))
|
||||
goto out_err;
|
||||
return 0;
|
||||
out_err:
|
||||
@ -226,7 +229,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
|
||||
if (match(lockhost, host)) {
|
||||
|
||||
spin_unlock(&flctx->flc_lock);
|
||||
if (nlm_unlock_files(file, fl->fl_owner))
|
||||
if (nlm_unlock_files(file, fl))
|
||||
return 1;
|
||||
goto again;
|
||||
}
|
||||
|
@ -9,10 +9,10 @@
|
||||
#define _TRACE_NFS4_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <trace/events/sunrpc_base.h>
|
||||
#include <trace/misc/sunrpc.h>
|
||||
|
||||
#include <trace/events/fs.h>
|
||||
#include <trace/events/nfs.h>
|
||||
#include <trace/misc/fs.h>
|
||||
#include <trace/misc/nfs.h>
|
||||
|
||||
#define show_nfs_fattr_flags(valid) \
|
||||
__print_flags((unsigned long)valid, "|", \
|
||||
|
@ -11,9 +11,9 @@
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/iversion.h>
|
||||
|
||||
#include <trace/events/fs.h>
|
||||
#include <trace/events/nfs.h>
|
||||
#include <trace/events/sunrpc_base.h>
|
||||
#include <trace/misc/fs.h>
|
||||
#include <trace/misc/nfs.h>
|
||||
#include <trace/misc/sunrpc.h>
|
||||
|
||||
#define nfs_show_cache_validity(v) \
|
||||
__print_flags(v, "|", \
|
||||
|
@ -8,6 +8,7 @@ config NFSD
|
||||
select SUNRPC
|
||||
select EXPORTFS
|
||||
select NFS_ACL_SUPPORT if NFSD_V2_ACL
|
||||
select NFS_ACL_SUPPORT if NFSD_V3_ACL
|
||||
depends on MULTIUSER
|
||||
help
|
||||
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
|
||||
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.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config NFSD_V2_ACL
|
||||
bool
|
||||
config NFSD_V2
|
||||
bool "NFS server support for NFS version 2 (DEPRECATED)"
|
||||
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
|
||||
bool "NFS server support for the NFSv3 ACL protocol extension"
|
||||
depends on NFSD
|
||||
select NFSD_V2_ACL
|
||||
help
|
||||
Solaris NFS servers support an auxiliary NFSv3 ACL protocol that
|
||||
never became an official part of the NFS version 3 protocol.
|
||||
|
@ -10,9 +10,10 @@ obj-$(CONFIG_NFSD) += nfsd.o
|
||||
# this one should be compiled first, as the tracing macros can easily blow up
|
||||
nfsd-y += trace.o
|
||||
|
||||
nfsd-y += nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
|
||||
export.o auth.o lockd.o nfscache.o nfsxdr.o \
|
||||
nfsd-y += nfssvc.o nfsctl.o nfsfh.o vfs.o \
|
||||
export.o auth.o lockd.o nfscache.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_V3_ACL) += nfs3acl.o
|
||||
nfsd-$(CONFIG_NFSD_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o \
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "blocklayoutxdr.h"
|
||||
#include "pnfs.h"
|
||||
#include "filecache.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nfsd.h"
|
||||
#include "blocklayoutxdr.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
|
@ -115,7 +115,6 @@ struct svc_export * rqst_find_fsidzero_export(struct svc_rqst *);
|
||||
int exp_rootfh(struct net *, struct auth_domain *,
|
||||
char *path, struct knfsd_fh *, int maxsize);
|
||||
__be32 exp_pseudoroot(struct svc_rqst *, struct svc_fh *);
|
||||
__be32 nfserrno(int errno);
|
||||
|
||||
static inline void exp_put(struct svc_export *exp)
|
||||
{
|
||||
|
@ -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>
|
||||
*
|
||||
* 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>
|
||||
@ -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_releases);
|
||||
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);
|
||||
|
||||
struct nfsd_fcache_disposal {
|
||||
@ -63,6 +87,7 @@ struct nfsd_file_lookup_key {
|
||||
struct net *net;
|
||||
const struct cred *cred;
|
||||
unsigned char need;
|
||||
bool gc;
|
||||
enum nfsd_file_lookup_type type;
|
||||
};
|
||||
|
||||
@ -162,6 +187,8 @@ static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg,
|
||||
return 1;
|
||||
if (!nfsd_match_cred(nf->nf_cred, key->cred))
|
||||
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)
|
||||
return 1;
|
||||
break;
|
||||
@ -184,12 +211,9 @@ static const struct rhashtable_params nfsd_file_rhash_params = {
|
||||
static void
|
||||
nfsd_file_schedule_laundrette(void)
|
||||
{
|
||||
if ((atomic_read(&nfsd_file_rhash_tbl.nelems) == 0) ||
|
||||
test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 0)
|
||||
return;
|
||||
|
||||
queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
|
||||
NFSD_LAUNDRETTE_DELAY);
|
||||
if (test_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags))
|
||||
queue_delayed_work(system_wq, &nfsd_filecache_laundrette,
|
||||
NFSD_LAUNDRETTE_DELAY);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -297,6 +321,8 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
|
||||
nf->nf_flags = 0;
|
||||
__set_bit(NFSD_FILE_HASHED, &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_ref is pre-incremented for hash table */
|
||||
refcount_set(&nf->nf_ref, 2);
|
||||
@ -306,16 +332,62 @@ nfsd_file_alloc(struct nfsd_file_lookup_key *key, unsigned int may)
|
||||
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
|
||||
nfsd_file_free(struct nfsd_file *nf)
|
||||
{
|
||||
s64 age = ktime_to_ms(ktime_sub(ktime_get(), nf->nf_birthtime));
|
||||
bool flush = false;
|
||||
|
||||
trace_nfsd_file_free(nf);
|
||||
|
||||
this_cpu_inc(nfsd_file_releases);
|
||||
this_cpu_add(nfsd_file_total_age, age);
|
||||
|
||||
trace_nfsd_file_put_final(nf);
|
||||
if (nf->nf_mark)
|
||||
nfsd_file_mark_put(nf->nf_mark);
|
||||
if (nf->nf_file) {
|
||||
@ -349,28 +421,6 @@ nfsd_file_check_writeback(struct nfsd_file *nf)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
nfsd_file_hash_remove(struct nfsd_file *nf)
|
||||
struct nfsd_file *
|
||||
nfsd_file_get(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;
|
||||
if (likely(refcount_inc_not_zero(&nf->nf_ref)))
|
||||
return nf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)) {
|
||||
/* caller must call nfsd_file_dispose_list() later */
|
||||
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
|
||||
nfsd_file_put(struct nfsd_file *nf)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
nfsd_file_lru_add(nf);
|
||||
if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0) {
|
||||
nfsd_file_flush(nf);
|
||||
if (test_bit(NFSD_FILE_GC, &nf->nf_flags))
|
||||
nfsd_file_lru_add(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);
|
||||
} 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_schedule_laundrette();
|
||||
} else
|
||||
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
|
||||
nfsd_file_dispose_list(struct list_head *dispose)
|
||||
{
|
||||
@ -478,7 +500,7 @@ nfsd_file_dispose_list(struct list_head *dispose)
|
||||
while(!list_empty(dispose)) {
|
||||
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
||||
list_del_init(&nf->nf_lru);
|
||||
nfsd_file_flush(nf);
|
||||
nfsd_file_fsync(nf);
|
||||
nfsd_file_put_noref(nf);
|
||||
}
|
||||
}
|
||||
@ -492,7 +514,7 @@ nfsd_file_dispose_list_sync(struct list_head *dispose)
|
||||
while(!list_empty(dispose)) {
|
||||
nf = list_first_entry(dispose, struct nfsd_file, nf_lru);
|
||||
list_del_init(&nf->nf_lru);
|
||||
nfsd_file_flush(nf);
|
||||
nfsd_file_fsync(nf);
|
||||
if (!refcount_dec_and_test(&nf->nf_ref))
|
||||
continue;
|
||||
if (nfsd_file_free(nf))
|
||||
@ -644,7 +666,8 @@ static void
|
||||
nfsd_file_gc_worker(struct work_struct *work)
|
||||
{
|
||||
nfsd_file_gc();
|
||||
nfsd_file_schedule_laundrette();
|
||||
if (list_lru_count(&nfsd_file_lru))
|
||||
nfsd_file_schedule_laundrette();
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
@ -692,7 +715,7 @@ __nfsd_file_close_inode(struct inode *inode, struct list_head *dispose)
|
||||
nfsd_file_rhash_params);
|
||||
if (!nf)
|
||||
break;
|
||||
nfsd_file_unhash_and_dispose(nf, dispose);
|
||||
nfsd_file_unhash_and_queue(nf, dispose);
|
||||
count++;
|
||||
} while (1);
|
||||
rcu_read_unlock();
|
||||
@ -894,7 +917,7 @@ __nfsd_file_cache_purge(struct net *net)
|
||||
nf = rhashtable_walk_next(&iter);
|
||||
while (!IS_ERR_OR_NULL(nf)) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1000,7 +1023,6 @@ nfsd_file_cache_shutdown(void)
|
||||
per_cpu(nfsd_file_acquisitions, i) = 0;
|
||||
per_cpu(nfsd_file_releases, 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;
|
||||
}
|
||||
}
|
||||
@ -1034,12 +1056,14 @@ nfsd_file_is_cached(struct inode *inode)
|
||||
|
||||
static __be32
|
||||
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 = {
|
||||
.type = NFSD_FILE_KEY_FULL,
|
||||
.need = may_flags & NFSD_FILE_MAY_MASK,
|
||||
.net = SVC_NET(rqstp),
|
||||
.gc = want_gc,
|
||||
};
|
||||
bool open_retry = true;
|
||||
struct nfsd_file *nf;
|
||||
@ -1135,14 +1159,35 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
* then unhash.
|
||||
*/
|
||||
if (status != nfs_ok || key.inode->i_nlink == 0)
|
||||
if (nfsd_file_unhash(nf))
|
||||
nfsd_file_put_noref(nf);
|
||||
nfsd_file_unhash_and_put(nf);
|
||||
clear_bit_unlock(NFSD_FILE_PENDING, &nf->nf_flags);
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&nf->nf_flags, NFSD_FILE_PENDING);
|
||||
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
|
||||
* @rqstp: the RPC transaction being executed
|
||||
@ -1150,6 +1195,10 @@ nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
* @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
|
||||
* 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
|
||||
* network byte order is returned.
|
||||
*/
|
||||
@ -1157,7 +1206,7 @@ __be32
|
||||
nfsd_file_acquire(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);
|
||||
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
|
||||
* @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
|
||||
* network byte order is returned.
|
||||
*/
|
||||
@ -1174,7 +1227,7 @@ __be32
|
||||
nfsd_file_create(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, 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)
|
||||
{
|
||||
unsigned long releases = 0, pages_flushed = 0, evictions = 0;
|
||||
unsigned long releases = 0, evictions = 0;
|
||||
unsigned long hits = 0, acquisitions = 0;
|
||||
unsigned int i, count = 0, buckets = 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);
|
||||
total_age += per_cpu(nfsd_file_total_age, 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);
|
||||
@ -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);
|
||||
else
|
||||
seq_printf(m, "mean age (ms): -\n");
|
||||
seq_printf(m, "pages flushed: %lu\n", pages_flushed);
|
||||
return 0;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ struct nfsd_file {
|
||||
#define NFSD_FILE_HASHED (0)
|
||||
#define NFSD_FILE_PENDING (1)
|
||||
#define NFSD_FILE_REFERENCED (2)
|
||||
#define NFSD_FILE_GC (3)
|
||||
unsigned long nf_flags;
|
||||
struct inode *nf_inode; /* don't deref */
|
||||
refcount_t nf_ref;
|
||||
@ -52,10 +53,11 @@ void nfsd_file_cache_shutdown(void);
|
||||
int nfsd_file_cache_start_net(struct net *net);
|
||||
void nfsd_file_cache_shutdown_net(struct net *net);
|
||||
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);
|
||||
void nfsd_file_close_inode_sync(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,
|
||||
unsigned int may_flags, struct nfsd_file **nfp);
|
||||
__be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "flexfilelayoutxdr.h"
|
||||
#include "pnfs.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PNFS
|
||||
|
||||
|
@ -246,7 +246,6 @@ nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
||||
struct dentry *dentry = resp->fh.fh_dentry;
|
||||
struct inode *inode;
|
||||
int w;
|
||||
|
||||
if (!svcxdr_encode_stat(xdr, resp->status))
|
||||
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)
|
||||
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,
|
||||
resp->mask & NFS_ACL, 0))
|
||||
return false;
|
||||
|
@ -171,11 +171,7 @@ nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
{
|
||||
struct nfsd3_getaclres *resp = rqstp->rq_resp;
|
||||
struct dentry *dentry = resp->fh.fh_dentry;
|
||||
struct kvec *head = rqstp->rq_res.head;
|
||||
struct inode *inode;
|
||||
unsigned int base;
|
||||
int n;
|
||||
int w;
|
||||
|
||||
if (!svcxdr_encode_nfsstat3(xdr, resp->status))
|
||||
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)
|
||||
return false;
|
||||
|
||||
base = (char *)xdr->p - (char *)head->iov_base;
|
||||
|
||||
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 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)
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
|
||||
resp->mask & NFS_ACL, 0))
|
||||
return false;
|
||||
if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
|
||||
resp->mask & NFS_DFACL,
|
||||
NFS_ACL_DEFAULT))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "cache.h"
|
||||
#include "xdr3.h"
|
||||
#include "vfs.h"
|
||||
#include "filecache.h"
|
||||
|
||||
#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_commitres *resp = rqstp->rq_resp;
|
||||
struct nfsd_file *nf;
|
||||
|
||||
dprintk("nfsd: COMMIT(3) %s %u@%Lu\n",
|
||||
SVCFH_fmt(&argp->fh),
|
||||
@ -770,8 +772,14 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
|
||||
(unsigned long long) argp->offset);
|
||||
|
||||
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);
|
||||
nfsd_file_put(nf);
|
||||
out:
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,17 @@ static __be32 *xdr_encode_empty_array(__be32 *p)
|
||||
* 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
|
||||
*
|
||||
@ -328,6 +339,24 @@ static void encode_cb_recall4args(struct xdr_stream *xdr,
|
||||
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
|
||||
*
|
||||
@ -482,6 +511,26 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
|
||||
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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
/*
|
||||
* CB_LAYOUTRECALL4args
|
||||
@ -783,6 +854,7 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = {
|
||||
#endif
|
||||
PROC(CB_NOTIFY_LOCK, COMPOUND, cb_notify_lock, cb_notify_lock),
|
||||
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)];
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "idmap.h"
|
||||
#include "nfsd.h"
|
||||
#include "netns.h"
|
||||
#include "vfs.h"
|
||||
|
||||
/*
|
||||
* Turn off idmapping when using AUTH_SYS.
|
||||
|
@ -731,10 +731,19 @@ nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
union nfsd4_op_u *u)
|
||||
{
|
||||
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,
|
||||
(__be32 *)commit->co_verf.data);
|
||||
nfsd_file_put(nf);
|
||||
return status;
|
||||
}
|
||||
|
||||
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,
|
||||
&read->rd_stateid, RD_STATE,
|
||||
&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_fhp = &cstate->current_fh;
|
||||
return status;
|
||||
@ -1108,10 +1112,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
status = nfs4_preprocess_stateid_op(rqstp, cstate,
|
||||
&cstate->current_fh, &setattr->sa_stateid,
|
||||
WR_STATE, NULL, NULL);
|
||||
if (status) {
|
||||
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
err = fh_want_write(&cstate->current_fh);
|
||||
if (err)
|
||||
@ -1133,6 +1135,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
0, (time64_t)0);
|
||||
if (!status)
|
||||
status = nfserrno(attrs.na_labelerr);
|
||||
if (!status)
|
||||
status = nfserrno(attrs.na_aclerr);
|
||||
out:
|
||||
nfsd_attrs_free(&attrs);
|
||||
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);
|
||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||
stateid, WR_STATE, &nf, NULL);
|
||||
if (status) {
|
||||
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
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,
|
||||
src_stateid, RD_STATE, src, NULL);
|
||||
if (status) {
|
||||
dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
|
||||
dst_stateid, WR_STATE, dst, NULL);
|
||||
if (status) {
|
||||
dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
|
||||
if (status)
|
||||
goto out_put_src;
|
||||
}
|
||||
|
||||
/* fix up for NFS-specific error code */
|
||||
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 dst_pos = copy->cp_dst_pos;
|
||||
int status;
|
||||
loff_t end;
|
||||
|
||||
/* See RFC 7862 p.67: */
|
||||
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 */
|
||||
if (nfsd4_copy_is_async(copy) && copy->cp_res.wr_bytes_written > 0) {
|
||||
since = READ_ONCE(dst->f_wb_err);
|
||||
status = vfs_fsync_range(dst, copy->cp_dst_pos,
|
||||
copy->cp_res.wr_bytes_written, 0);
|
||||
end = copy->cp_dst_pos + copy->cp_res.wr_bytes_written - 1;
|
||||
status = vfs_fsync_range(dst, copy->cp_dst_pos, end, 0);
|
||||
if (!status)
|
||||
status = filemap_check_wb_err(dst->f_mapping, since);
|
||||
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,
|
||||
&fallocate->falloc_stateid,
|
||||
WR_STATE, &nf, NULL);
|
||||
if (status != nfs_ok) {
|
||||
dprintk("NFSD: nfsd4_fallocate: couldn't process stateid!\n");
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
}
|
||||
|
||||
status = nfsd4_vfs_fallocate(rqstp, &cstate->current_fh, nf->nf_file,
|
||||
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,
|
||||
&seek->seek_stateid,
|
||||
RD_STATE, &nf, NULL);
|
||||
if (status) {
|
||||
dprintk("NFSD: nfsd4_seek: couldn't process stateid!\n");
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (seek->seek_whence) {
|
||||
case NFS4_CONTENT_DATA:
|
||||
|
@ -44,7 +44,9 @@
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/nfs_ssc.h>
|
||||
|
||||
#include "xdr4.h"
|
||||
#include "xdr4cb.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);
|
||||
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 nfsd4_file_hash_remove(struct nfs4_file *fi);
|
||||
|
||||
/* Locking: */
|
||||
|
||||
@ -588,11 +591,8 @@ static void nfsd4_free_file_rcu(struct rcu_head *rcu)
|
||||
void
|
||||
put_nfs4_file(struct nfs4_file *fi)
|
||||
{
|
||||
might_lock(&state_lock);
|
||||
|
||||
if (refcount_dec_and_lock(&fi->fi_ref, &state_lock)) {
|
||||
hlist_del_rcu(&fi->fi_hash);
|
||||
spin_unlock(&state_lock);
|
||||
if (refcount_dec_and_test(&fi->fi_ref)) {
|
||||
nfsd4_file_hash_remove(fi);
|
||||
WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
|
||||
WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
|
||||
call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
|
||||
@ -675,15 +675,26 @@ find_any_file(struct nfs4_file *f)
|
||||
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)
|
||||
ret = nfsd_file_get(f->fi_deleg_file);
|
||||
spin_unlock(&f->fi_lock);
|
||||
return ret;
|
||||
return f->fi_deleg_file;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static atomic_long_t num_delegations;
|
||||
@ -706,19 +717,20 @@ static unsigned int ownerstr_hashval(struct xdr_netobj *ownername)
|
||||
return ret & OWNER_HASH_MASK;
|
||||
}
|
||||
|
||||
/* hash table for nfs4_file */
|
||||
#define FILE_HASH_BITS 8
|
||||
#define FILE_HASH_SIZE (1 << FILE_HASH_BITS)
|
||||
static struct rhltable nfs4_file_rhltable ____cacheline_aligned_in_smp;
|
||||
|
||||
static unsigned int file_hashval(struct svc_fh *fh)
|
||||
{
|
||||
struct inode *inode = d_inode(fh->fh_dentry);
|
||||
static const struct rhashtable_params nfs4_file_rhash_params = {
|
||||
.key_len = sizeof_field(struct nfs4_file, fi_inode),
|
||||
.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);
|
||||
}
|
||||
|
||||
static struct hlist_head file_hashtbl[FILE_HASH_SIZE];
|
||||
/*
|
||||
* Start with a single page hash table to reduce resizing churn
|
||||
* on light workloads.
|
||||
*/
|
||||
.min_size = 256,
|
||||
.automatic_shrinking = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* 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]);
|
||||
spin_unlock(&fp->fi_lock);
|
||||
if (f1)
|
||||
nfsd_file_close(f1);
|
||||
nfsd_file_put(f1);
|
||||
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));
|
||||
|
||||
trace_nfsd_stid_revoke(&dp->dl_stid);
|
||||
|
||||
if (clp->cl_minorversion) {
|
||||
dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
|
||||
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 slotsize = slot_bytes(fattrs);
|
||||
struct nfsd4_session *new;
|
||||
int mem, i;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
|
||||
+ sizeof(struct nfsd4_session) > PAGE_SIZE);
|
||||
mem = numslots * sizeof(struct nfsd4_slot *);
|
||||
BUILD_BUG_ON(struct_size(new, se_slots, NFSD_MAX_SLOTS_PER_SESSION)
|
||||
> PAGE_SIZE);
|
||||
|
||||
new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
|
||||
new = kzalloc(struct_size(new, se_slots, numslots), GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
/* 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_name.data);
|
||||
idr_destroy(&clp->cl_stateids);
|
||||
kfree(clp->cl_ra);
|
||||
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);
|
||||
oo = ols->st_stateowner;
|
||||
nf = st->sc_file;
|
||||
file = find_any_file(nf);
|
||||
|
||||
spin_lock(&nf->fi_lock);
|
||||
file = find_any_file_locked(nf);
|
||||
if (!file)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
seq_printf(s, "- ");
|
||||
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, ", ");
|
||||
nfs4_show_owner(s, oo);
|
||||
seq_printf(s, " }\n");
|
||||
nfsd_file_put(file);
|
||||
|
||||
out:
|
||||
spin_unlock(&nf->fi_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2652,9 +2668,10 @@ static int nfs4_show_lock(struct seq_file *s, struct nfs4_stid *st)
|
||||
ols = openlockstateid(st);
|
||||
oo = ols->st_stateowner;
|
||||
nf = st->sc_file;
|
||||
file = find_any_file(nf);
|
||||
spin_lock(&nf->fi_lock);
|
||||
file = find_any_file_locked(nf);
|
||||
if (!file)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
seq_printf(s, "- ");
|
||||
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, ", ");
|
||||
nfs4_show_owner(s, oo);
|
||||
seq_printf(s, " }\n");
|
||||
nfsd_file_put(file);
|
||||
|
||||
out:
|
||||
spin_unlock(&nf->fi_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2687,9 +2704,10 @@ static int nfs4_show_deleg(struct seq_file *s, struct nfs4_stid *st)
|
||||
|
||||
ds = delegstateid(st);
|
||||
nf = st->sc_file;
|
||||
file = find_deleg_file(nf);
|
||||
spin_lock(&nf->fi_lock);
|
||||
file = find_deleg_file_locked(nf);
|
||||
if (!file)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
seq_printf(s, "- ");
|
||||
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, ", ");
|
||||
nfs4_show_fname(s, file);
|
||||
seq_printf(s, " }\n");
|
||||
nfsd_file_put(file);
|
||||
|
||||
out:
|
||||
spin_unlock(&nf->fi_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2854,6 +2872,37 @@ static const struct tree_descr client_files[] = {
|
||||
[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,
|
||||
struct svc_rqst *rqstp, nfs4_verifier *verf)
|
||||
{
|
||||
@ -2891,6 +2940,14 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||
free_client(clp);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -4260,11 +4317,9 @@ static struct nfs4_file *nfsd4_alloc_file(void)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
spin_lock_init(&fp->fi_lock);
|
||||
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);
|
||||
atomic_set(&fp->fi_lo_recalls, 0);
|
||||
#endif
|
||||
hlist_add_head_rcu(&fp->fi_hash, &file_hashtbl[hashval]);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4347,20 +4401,22 @@ nfsd4_init_slabs(void)
|
||||
}
|
||||
|
||||
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, nfsd_client_shrinker);
|
||||
|
||||
cnt = atomic_read(&nn->nfsd_courtesy_clients);
|
||||
if (cnt > 0)
|
||||
count = atomic_read(&nn->nfsd_courtesy_clients);
|
||||
if (!count)
|
||||
count = atomic_long_read(&num_delegations);
|
||||
if (count)
|
||||
mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
|
||||
return (unsigned long)cnt;
|
||||
return (unsigned long)count;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
atomic_set(&nn->nfsd_courtesy_clients, 0);
|
||||
nn->nfsd_client_shrinker.scan_objects = nfsd_courtesy_client_scan;
|
||||
nn->nfsd_client_shrinker.count_objects = nfsd_courtesy_client_count;
|
||||
nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
|
||||
nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
|
||||
nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
|
||||
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);
|
||||
}
|
||||
|
||||
/* search file_hashtbl[] for file */
|
||||
static struct nfs4_file *
|
||||
find_file_locked(struct svc_fh *fh, unsigned int hashval)
|
||||
static noinline_for_stack struct nfs4_file *
|
||||
nfsd4_file_hash_lookup(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 *fi;
|
||||
|
||||
hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash,
|
||||
lockdep_is_held(&state_lock)) {
|
||||
if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) {
|
||||
if (refcount_inc_not_zero(&fp->fi_ref))
|
||||
return fp;
|
||||
rcu_read_lock();
|
||||
list = rhltable_lookup(&nfs4_file_rhltable, &inode,
|
||||
nfs4_file_rhash_params);
|
||||
rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
bool alias_found = false;
|
||||
struct nfs4_file *fi;
|
||||
int err;
|
||||
|
||||
spin_lock(&state_lock);
|
||||
hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash,
|
||||
lockdep_is_held(&state_lock)) {
|
||||
if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) {
|
||||
if (refcount_inc_not_zero(&fp->fi_ref))
|
||||
ret = fp;
|
||||
} else if (d_inode(fh->fh_dentry) == fp->fi_inode)
|
||||
fp->fi_aliased = alias_found = true;
|
||||
rcu_read_lock();
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
list = rhltable_lookup(&nfs4_file_rhltable, &inode,
|
||||
nfs4_file_rhash_params);
|
||||
rhl_for_each_entry_rcu(fi, tmp, list, fi_rlist) {
|
||||
if (fh_match(&fi->fi_fhandle, &fhp->fh_handle)) {
|
||||
if (refcount_inc_not_zero(&fi->fi_ref))
|
||||
ret = fi;
|
||||
} else
|
||||
fi->fi_aliased = alias_found = true;
|
||||
}
|
||||
if (likely(ret == NULL)) {
|
||||
nfsd4_init_file(fh, hashval, new);
|
||||
new->fi_aliased = alias_found;
|
||||
ret = new;
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
nfsd4_file_init(fhp, new);
|
||||
err = rhltable_insert(&nfs4_file_rhltable, &new->fi_rlist,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int hashval = file_hashval(fh);
|
||||
|
||||
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);
|
||||
rhltable_remove(&nfs4_file_rhltable, &fi->fi_rlist,
|
||||
nfs4_file_rhash_params);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4744,9 +4809,10 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
|
||||
struct nfs4_file *fp;
|
||||
__be32 ret = nfs_ok;
|
||||
|
||||
fp = find_file(current_fh);
|
||||
fp = nfsd4_file_hash_lookup(current_fh);
|
||||
if (!fp)
|
||||
return ret;
|
||||
|
||||
/* Check for conflicting share reservations */
|
||||
spin_lock(&fp->fi_lock);
|
||||
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.
|
||||
* 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) {
|
||||
status = nfs4_check_deleg(cl, open, &dp);
|
||||
if (status)
|
||||
@ -6125,17 +6193,64 @@ laundromat_main(struct work_struct *laundry)
|
||||
}
|
||||
|
||||
static void
|
||||
courtesy_client_reaper(struct work_struct *reaper)
|
||||
courtesy_client_reaper(struct nfsd_net *nn)
|
||||
{
|
||||
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_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)
|
||||
{
|
||||
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)
|
||||
goto put_stateid;
|
||||
|
||||
trace_nfsd_deleg_return(stateid);
|
||||
wake_up_var(d_inode(cstate->current_fh.fh_dentry));
|
||||
destroy_delegation(dp);
|
||||
put_stateid:
|
||||
@ -7958,7 +8074,7 @@ static int nfs4_state_create_net(struct net *net)
|
||||
INIT_LIST_HEAD(&nn->blocked_locks_lru);
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
@ -8034,10 +8150,16 @@ nfs4_state_start(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nfsd4_create_callback_queue();
|
||||
ret = rhltable_init(&nfs4_file_rhltable, &nfs4_file_rhash_params);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nfsd4_create_callback_queue();
|
||||
if (ret) {
|
||||
rhltable_destroy(&nfs4_file_rhltable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
set_max_delegations();
|
||||
return 0;
|
||||
}
|
||||
@ -8068,6 +8190,7 @@ nfs4_state_shutdown_net(struct net *net)
|
||||
|
||||
nfsd4_client_tracking_exit(net);
|
||||
nfs4_state_destroy_net(net);
|
||||
rhltable_destroy(&nfs4_file_rhltable);
|
||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||
nfsd4_ssc_shutdown_umount(nn);
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -581,7 +581,9 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
||||
|
||||
cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET;
|
||||
switch(num) {
|
||||
#ifdef CONFIG_NFSD_V2
|
||||
case 2:
|
||||
#endif
|
||||
case 3:
|
||||
nfsd_vers(nn, num, cmd);
|
||||
break;
|
||||
@ -601,7 +603,9 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
/* Ignore requests to disable non-existent versions */
|
||||
if (cmd == NFSD_SET)
|
||||
return -EINVAL;
|
||||
}
|
||||
vers += len + 1;
|
||||
} while ((len = qword_get(&mesg, vers, size)) > 0);
|
||||
|
@ -64,8 +64,7 @@ struct readdir_cd {
|
||||
|
||||
|
||||
extern struct svc_program nfsd_program;
|
||||
extern const struct svc_version nfsd_version2, nfsd_version3,
|
||||
nfsd_version4;
|
||||
extern const struct svc_version nfsd_version2, nfsd_version3, nfsd_version4;
|
||||
extern struct mutex nfsd_mutex;
|
||||
extern spinlock_t nfsd_drc_lock;
|
||||
extern unsigned long nfsd_drc_max_mem;
|
||||
|
@ -220,7 +220,7 @@ __be32 fh_update(struct svc_fh *);
|
||||
void fh_put(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);
|
||||
|
||||
@ -229,7 +229,7 @@ fh_copy(struct svc_fh *dst, struct svc_fh *src)
|
||||
}
|
||||
|
||||
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;
|
||||
memcpy(&dst->fh_raw, &src->fh_raw, src->fh_size);
|
||||
@ -243,7 +243,8 @@ fh_init(struct svc_fh *fhp, int maxsize)
|
||||
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)
|
||||
return false;
|
||||
@ -252,7 +253,8 @@ static inline bool fh_match(struct knfsd_fh *fh1, struct knfsd_fh *fh2)
|
||||
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)
|
||||
return false;
|
||||
|
@ -211,7 +211,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
|
||||
if (resp->status == nfs_ok)
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
else if (resp->status == nfserr_jukebox)
|
||||
return rpc_drop_reply;
|
||||
__set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -246,7 +246,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
|
||||
if (resp->status == nfs_ok)
|
||||
resp->status = fh_getattr(&resp->fh, &resp->stat);
|
||||
else if (resp->status == nfserr_jukebox)
|
||||
return rpc_drop_reply;
|
||||
__set_bit(RQ_DROPME, &rqstp->rq_flags);
|
||||
return rpc_success;
|
||||
}
|
||||
|
||||
@ -848,65 +848,3 @@ const struct svc_version nfsd_version2 = {
|
||||
.vs_dispatch = nfsd_dispatch,
|
||||
.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;
|
||||
}
|
||||
|
||||
|
@ -91,8 +91,12 @@ unsigned long nfsd_drc_mem_used;
|
||||
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
|
||||
static struct svc_stat nfsd_acl_svcstats;
|
||||
static const struct svc_version *nfsd_acl_version[] = {
|
||||
# if defined(CONFIG_NFSD_V2_ACL)
|
||||
[2] = &nfsd_acl_version2,
|
||||
# endif
|
||||
# if defined(CONFIG_NFSD_V3_ACL)
|
||||
[3] = &nfsd_acl_version3,
|
||||
# endif
|
||||
};
|
||||
|
||||
#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) */
|
||||
|
||||
static const struct svc_version *nfsd_version[] = {
|
||||
#if defined(CONFIG_NFSD_V2)
|
||||
[2] = &nfsd_version2,
|
||||
#endif
|
||||
[3] = &nfsd_version3,
|
||||
#if defined(CONFIG_NFSD_V4)
|
||||
[4] = &nfsd_version4,
|
||||
@ -1054,7 +1060,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
|
||||
svcxdr_init_encode(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;
|
||||
|
||||
if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
|
||||
|
@ -368,6 +368,7 @@ struct nfs4_client {
|
||||
#define NFSD4_CLIENT_UPCALL_LOCK (5) /* upcall serialization */
|
||||
#define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \
|
||||
1 << NFSD4_CLIENT_CB_KILL)
|
||||
#define NFSD4_CLIENT_CB_RECALL_ANY (6)
|
||||
unsigned long cl_flags;
|
||||
const struct cred *cl_cb_cred;
|
||||
struct rpc_clnt *cl_cb_client;
|
||||
@ -411,6 +412,10 @@ struct nfs4_client {
|
||||
|
||||
unsigned int cl_state;
|
||||
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
|
||||
@ -536,16 +541,13 @@ struct nfs4_clnt_odstate {
|
||||
* inode can have multiple filehandles associated with it, so there is
|
||||
* (potentially) a many to one relationship between this struct and struct
|
||||
* inode.
|
||||
*
|
||||
* These are hashed by filehandle in the file_hashtbl, which is protected by
|
||||
* the global state_lock spinlock.
|
||||
*/
|
||||
struct nfs4_file {
|
||||
refcount_t fi_ref;
|
||||
struct inode * fi_inode;
|
||||
bool fi_aliased;
|
||||
spinlock_t fi_lock;
|
||||
struct hlist_node fi_hash; /* hash on fi_fhandle */
|
||||
struct rhlist_head fi_rlist;
|
||||
struct list_head fi_stateids;
|
||||
union {
|
||||
struct list_head fi_delegations;
|
||||
@ -639,6 +641,7 @@ enum nfsd4_cb_op {
|
||||
NFSPROC4_CLNT_CB_OFFLOAD,
|
||||
NFSPROC4_CLNT_CB_SEQUENCE,
|
||||
NFSPROC4_CLNT_CB_NOTIFY_LOCK,
|
||||
NFSPROC4_CLNT_CB_RECALL_ANY,
|
||||
};
|
||||
|
||||
/* Returns true iff a is later than b: */
|
||||
|
144
fs/nfsd/trace.h
144
fs/nfsd/trace.h
@ -9,9 +9,12 @@
|
||||
#define _NFSD_TRACE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/sunrpc/xprt.h>
|
||||
#include <trace/misc/nfs.h>
|
||||
|
||||
#include "export.h"
|
||||
#include "nfsfh.h"
|
||||
#include "xdr4.h"
|
||||
|
||||
#define NFSD_TRACE_PROC_RES_FIELDS \
|
||||
__field(unsigned int, netns_ino) \
|
||||
@ -604,6 +607,7 @@ DEFINE_STATEID_EVENT(layout_recall_release);
|
||||
|
||||
DEFINE_STATEID_EVENT(open);
|
||||
DEFINE_STATEID_EVENT(deleg_read);
|
||||
DEFINE_STATEID_EVENT(deleg_return);
|
||||
DEFINE_STATEID_EVENT(deleg_recall);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfsd_stateseqid_class,
|
||||
@ -636,6 +640,61 @@ DEFINE_EVENT(nfsd_stateseqid_class, nfsd_##name, \
|
||||
DEFINE_STATESEQID_EVENT(preprocess);
|
||||
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,
|
||||
TP_PROTO(const clientid_t *clid),
|
||||
TP_ARGS(clid),
|
||||
@ -817,7 +876,8 @@ DEFINE_CLID_EVENT(confirmed_r);
|
||||
__print_flags(val, "|", \
|
||||
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \
|
||||
{ 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,
|
||||
TP_PROTO(struct nfsd_file *nf),
|
||||
@ -849,10 +909,10 @@ DEFINE_EVENT(nfsd_file_class, name, \
|
||||
TP_PROTO(struct nfsd_file *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_put);
|
||||
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_dispose);
|
||||
DEFINE_NFSD_FILE_EVENT(nfsd_file_unhash_and_queue);
|
||||
|
||||
TRACE_EVENT(nfsd_file_alloc,
|
||||
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_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"
|
||||
|
||||
TRACE_DEFINE_ENUM(RC_DROPIT);
|
||||
@ -1474,6 +1565,32 @@ TRACE_EVENT(nfsd_cb_offload,
|
||||
__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,
|
||||
TP_PROTO(
|
||||
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_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 */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
@ -49,6 +49,69 @@
|
||||
|
||||
#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
|
||||
* a mount point.
|
||||
@ -1085,7 +1148,7 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
__be32 err;
|
||||
|
||||
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)
|
||||
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);
|
||||
|
||||
err = nfsd_file_acquire(rqstp, fhp, NFSD_MAY_WRITE, &nf);
|
||||
err = nfsd_file_acquire_gc(rqstp, fhp, NFSD_MAY_WRITE, &nf);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -1133,6 +1196,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
|
||||
* nfsd_commit - Commit pending writes to stable storage
|
||||
* @rqstp: RPC request being processed
|
||||
* @fhp: NFS filehandle
|
||||
* @nf: target file
|
||||
* @offset: raw offset from beginning of file
|
||||
* @count: raw count of bytes to sync
|
||||
* @verf: filled in with the server's current write verifier
|
||||
@ -1149,19 +1213,13 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
|
||||
* An nfsstat value in network byte order.
|
||||
*/
|
||||
__be32
|
||||
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, u64 offset,
|
||||
u32 count, __be32 *verf)
|
||||
nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfsd_file *nf,
|
||||
u64 offset, u32 count, __be32 *verf)
|
||||
{
|
||||
__be32 err = nfs_ok;
|
||||
u64 maxbytes;
|
||||
loff_t start, end;
|
||||
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
|
||||
@ -1202,8 +1260,6 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, u64 offset,
|
||||
} else
|
||||
nfsd_copy_write_verifier(verf, nn);
|
||||
|
||||
nfsd_file_put(nf);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1305,7 +1361,6 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
iap->ia_mode &= ~current_umask();
|
||||
|
||||
err = 0;
|
||||
host_err = 0;
|
||||
switch (type) {
|
||||
case S_IFREG:
|
||||
host_err = vfs_create(&init_user_ns, dirp, dchild, iap->ia_mode, true);
|
||||
|
@ -60,6 +60,7 @@ static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
|
||||
posix_acl_release(attrs->na_dpacl);
|
||||
}
|
||||
|
||||
__be32 nfserrno (int errno);
|
||||
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
|
||||
struct svc_export **expp);
|
||||
__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,
|
||||
struct svc_fh *resfhp, struct nfsd_attrs *iap);
|
||||
__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
|
||||
__be32 nfsd_getxattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
char *name, void **bufp, int *lenp);
|
||||
|
@ -896,5 +896,10 @@ struct nfsd4_operation {
|
||||
union nfsd4_op_u *);
|
||||
};
|
||||
|
||||
struct nfsd4_cb_recall_any {
|
||||
struct nfsd4_callback ra_cb;
|
||||
u32 ra_keep;
|
||||
u32 ra_bmval[1];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -48,3 +48,9 @@
|
||||
#define NFS4_dec_cb_offload_sz (cb_compound_dec_hdr_sz + \
|
||||
cb_sequence_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)
|
||||
|
@ -732,4 +732,17 @@ enum nfs4_setxattr_options {
|
||||
SETXATTR4_CREATE = 1,
|
||||
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
|
||||
|
@ -220,13 +220,6 @@ static inline __be32 svc_getu32(struct kvec *iov)
|
||||
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)
|
||||
{
|
||||
__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 svc_cacherep * rq_cacherep; /* cache info */
|
||||
struct task_struct *rq_task; /* service thread */
|
||||
spinlock_t rq_lock; /* per-request lock */
|
||||
struct net *rq_bc_net; /* pointer to backchannel's
|
||||
* net namespace
|
||||
*/
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#include <trace/events/sunrpc_base.h>
|
||||
#include <trace/misc/sunrpc.h>
|
||||
|
||||
/**
|
||||
** GSS-API related trace events
|
||||
|
@ -15,8 +15,8 @@
|
||||
#include <linux/tracepoint.h>
|
||||
#include <rdma/ib_cm.h>
|
||||
|
||||
#include <trace/events/rdma.h>
|
||||
#include <trace/events/sunrpc_base.h>
|
||||
#include <trace/misc/rdma.h>
|
||||
#include <trace/misc/sunrpc.h>
|
||||
|
||||
/**
|
||||
** Event classes
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <linux/net.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_DGRAM);
|
||||
@ -1666,11 +1666,13 @@ TRACE_DEFINE_ENUM(SVC_COMPLETE);
|
||||
#define SVC_RQST_ENDPOINT_VARARGS \
|
||||
__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_ARGS(rqst, auth_res),
|
||||
|
||||
TP_CONDITION(auth_res != SVC_OK && auth_res != SVC_COMPLETE),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
SVC_RQST_ENDPOINT_FIELDS(rqst)
|
||||
|
||||
|
@ -360,6 +360,18 @@ TRACE_DEFINE_ENUM(IOMODE_ANY);
|
||||
{ IOMODE_RW, "RW" }, \
|
||||
{ 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) \
|
||||
__print_flags(x, "|", \
|
||||
{ SEQ4_STATUS_CB_PATH_DOWN, "CB_PATH_DOWN" }, \
|
@ -49,11 +49,36 @@
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
|
||||
#include <trace/events/rpcgss.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
|
||||
* into replies.
|
||||
@ -887,13 +912,11 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
|
||||
static int
|
||||
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;
|
||||
int stat = -EINVAL;
|
||||
struct xdr_netobj mic;
|
||||
struct xdr_buf integ_buf;
|
||||
|
||||
mic.data = NULL;
|
||||
|
||||
/* NFS READ normally uses splice to send data in-place. However
|
||||
* 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
|
||||
@ -917,11 +940,9 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
|
||||
/* copy out mic... */
|
||||
if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
|
||||
goto unwrap_failed;
|
||||
if (mic.len > RPC_MAX_AUTH_SIZE)
|
||||
goto unwrap_failed;
|
||||
mic.data = kmalloc(mic.len, GFP_KERNEL);
|
||||
if (!mic.data)
|
||||
if (mic.len > sizeof(gsd->gsd_scratch))
|
||||
goto unwrap_failed;
|
||||
mic.data = gsd->gsd_scratch;
|
||||
if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
|
||||
goto unwrap_failed;
|
||||
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;
|
||||
/* trim off the mic and padding at the end before returning */
|
||||
xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
|
||||
stat = 0;
|
||||
out:
|
||||
kfree(mic.data);
|
||||
return stat;
|
||||
return 0;
|
||||
|
||||
unwrap_failed:
|
||||
trace_rpcgss_svc_unwrap_failed(rqstp);
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
bad_seqno:
|
||||
trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
bad_mic:
|
||||
trace_rpcgss_svc_mic(rqstp, maj_stat);
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -1023,15 +1041,6 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
|
||||
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
|
||||
svcauth_gss_set_client(struct svc_rqst *rqstp)
|
||||
{
|
||||
@ -1162,18 +1171,23 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
|
||||
return res;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
|
||||
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;
|
||||
}
|
||||
in_token->page_base = 0;
|
||||
in_token->page_len = inlen;
|
||||
for (i = 0; i < pages; i++) {
|
||||
in_token->pages[i] = alloc_page(GFP_KERNEL);
|
||||
if (!in_token->pages[i]) {
|
||||
kfree(in_handle->data);
|
||||
gss_free_in_token_pages(in_token);
|
||||
return SVC_DENIED;
|
||||
}
|
||||
|
@ -638,7 +638,6 @@ svc_rqst_alloc(struct svc_serv *serv, struct svc_pool *pool, int node)
|
||||
return rqstp;
|
||||
|
||||
__set_bit(RQ_BUSY, &rqstp->rq_flags);
|
||||
spin_lock_init(&rqstp->rq_lock);
|
||||
rqstp->rq_server = serv;
|
||||
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: */
|
||||
if (auth_res == SVC_OK && progp)
|
||||
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) {
|
||||
case SVC_OK:
|
||||
break;
|
||||
|
@ -1224,30 +1224,34 @@ EXPORT_SYMBOL(xdr_restrict_buflen);
|
||||
/**
|
||||
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending
|
||||
* @xdr: pointer to xdr_stream
|
||||
* @pages: list of pages
|
||||
* @base: offset of first byte
|
||||
* @len: length of data in bytes
|
||||
* @pages: array of pages to insert
|
||||
* @base: starting offset of first data byte in @pages
|
||||
* @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,
|
||||
unsigned int len)
|
||||
{
|
||||
struct xdr_buf *buf = xdr->buf;
|
||||
struct kvec *iov = buf->tail;
|
||||
struct kvec *tail = buf->tail;
|
||||
|
||||
buf->pages = pages;
|
||||
buf->page_base = base;
|
||||
buf->page_len = len;
|
||||
|
||||
iov->iov_base = (char *)xdr->p;
|
||||
iov->iov_len = 0;
|
||||
xdr->iov = iov;
|
||||
tail->iov_base = xdr->p;
|
||||
tail->iov_len = 0;
|
||||
xdr->iov = tail;
|
||||
|
||||
if (len & 3) {
|
||||
unsigned int pad = 4 - (len & 3);
|
||||
|
||||
BUG_ON(xdr->p >= xdr->end);
|
||||
iov->iov_base = (char *)xdr->p + (len & 3);
|
||||
iov->iov_len += pad;
|
||||
tail->iov_base = (char *)xdr->p + (len & 3);
|
||||
tail->iov_len += pad;
|
||||
len += pad;
|
||||
*xdr->p++ = 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user