NFSD 6.15 Release Notes

Neil Brown contributed more scalability improvements to NFSD's
 open file cache, and Jeff Layton contributed a menagerie of
 repairs to NFSD's NFSv4 callback / backchannel implementation.
 
 Mike Snitzer contributed a change to NFS re-export support that
 disables support for file locking on a re-exported NFSv4 mount.
 This is because NFSv4 state recovery is currently difficult if
 not impossible for re-exported NFS mounts. The change aims to
 prevent data integrity exposures after the re-export server
 crashes.
 
 Work continues on the evolving NFSD netlink administrative API.
 
 Many thanks to the contributors, reviewers, testers, and bug
 reporters who participated during the v6.15 development cycle.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmfmpMIACgkQM2qzM29m
 f5f6DA/+P0YqoRg3Zk/4oWwXZWbfEOMhWFltT+D1PE2QjUfOZpiwUSFQfsfYgXO6
 OFu0iDQ4g8BxBeP6Umv61qy7Cv6n4fVzIHqzymXQvymh9JzoQiXlE9/fA8nAHuiH
 u7kkNPRi7faBz1sMg/WpN9CHctg7STPOhhG/JrZcSFZnh87mU1i4i4bZBNz8tVnK
 ZWf483OUuSmJY2/bUTkwvr4GbceTKBlLWFFjiRhfAKvJBWvu4myfC0DI5QzxmsgI
 MJ62do7AFJP1ww2Ih9LLi2kFIt/yyInSVAgyts1CPhlJ4BfPnTSOw/i2+CuF3D/M
 bZYEAOjH3AqjBZmq58sIQezpD5f9/TOrTSwYwS31zl/THYE413WiW80/MDoWqo0y
 9cSNkD3nJlPVLLCfF58vXLoe7wpLoN/ZbTdxoozzUWEFR5A4Jz3XP8F/Cws0cjem
 uWWAQMItiQpg1+RYJYfu4dg5+iN6dbgYbvzlr7buISwFNXi3Zo99MkJ4wHj9TJbL
 Tpjth1rWGPwwSOMT6ojKiYMq1oUzx5PuAm9Saq9oIzQAbBySmxHF/LSDz3wEuBoO
 MK1jzKroEmMk3fJOOAajSDLOdAbL3vfj6H/xi2IHvKnaz9yHCZNu2YGV05BBMprd
 hWePf69AO5Ky5Q9KuGClEtwvJ9ZR5pb4DO2dqaYu8ximu3O4vPo=
 =e2E2
 -----END PGP SIGNATURE-----

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

Pull nfsd updates from Chuck Lever:
 "Neil Brown contributed more scalability improvements to NFSD's open
  file cache, and Jeff Layton contributed a menagerie of repairs to
  NFSD's NFSv4 callback / backchannel implementation.

  Mike Snitzer contributed a change to NFS re-export support that
  disables support for file locking on a re-exported NFSv4 mount. This
  is because NFSv4 state recovery is currently difficult if not
  impossible for re-exported NFS mounts. The change aims to prevent data
  integrity exposures after the re-export server crashes.

  Work continues on the evolving NFSD netlink administrative API.

  Many thanks to the contributors, reviewers, testers, and bug reporters
  who participated during the v6.15 development cycle"

* tag 'nfsd-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (45 commits)
  NFSD: Add a Kconfig setting to enable delegated timestamps
  sysctl: Fixes nsm_local_state bounds
  nfsd: use a long for the count in nfsd4_state_shrinker_count()
  nfsd: remove obsolete comment from nfs4_alloc_stid
  nfsd: remove unneeded forward declaration of nfsd4_mark_cb_fault()
  nfsd: reorganize struct nfs4_delegation for better packing
  nfsd: handle errors from rpc_call_async()
  nfsd: move cb_need_restart flag into cb_flags
  nfsd: replace CB_GETATTR_BUSY with NFSD4_CALLBACK_RUNNING
  nfsd: eliminate cl_ra_cblist and NFSD4_CLIENT_CB_RECALL_ANY
  nfsd: prevent callback tasks running concurrently
  nfsd: disallow file locking and delegations for NFSv4 reexport
  nfsd: filecache: drop the list_lru lock during lock gc scans
  nfsd: filecache: don't repeatedly add/remove files on the lru list
  nfsd: filecache: introduce NFSD_FILE_RECENT
  nfsd: filecache: use list_lru_walk_node() in nfsd_file_gc()
  nfsd: filecache: use nfsd_file_dispose_list() in nfsd_file_close_inode_sync()
  NFSD: Re-organize nfsd_file_gc_worker()
  nfsd: filecache: remove race handling.
  fs: nfs: acl: Avoid -Wflex-array-member-not-at-end warning
  ...
This commit is contained in:
Linus Torvalds 2025-03-31 17:28:17 -07:00
commit b6dde1e527
29 changed files with 685 additions and 405 deletions

View File

@ -26,9 +26,13 @@ Reboot recovery
---------------
The NFS protocol's normal reboot recovery mechanisms don't work for the
case when the reexport server reboots. Clients will lose any locks
they held before the reboot, and further IO will result in errors.
Closing and reopening files should clear the errors.
case when the reexport server reboots because the source server has not
rebooted, and so it is not in grace. Since the source server is not in
grace, it cannot offer any guarantees that the file won't have been
changed between the locks getting lost and any attempt to recover them.
The same applies to delegations and any associated locks. Clients are
not allowed to get file locks or delegations from a reexport server, any
attempts will fail with operation not supported.
Filehandle limits
-----------------

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
name: lockd
protocol: genetlink
uapi-header: linux/lockd_netlink.h
doc: lockd configuration over generic netlink
attribute-sets:
-
name: server
attributes:
-
name: gracetime
type: u32
-
name: tcp-port
type: u16
-
name: udp-port
type: u16
operations:
list:
-
name: server-set
doc: set the lockd server parameters
attribute-set: server
flags: [ admin-perm ]
do:
request:
attributes:
- gracetime
- tcp-port
- udp-port
-
name: server-get
doc: get the lockd server parameters
attribute-set: server
do:
reply:
attributes:
- gracetime
- tcp-port
- udp-port

View File

@ -8,6 +8,6 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_LOCKD) += lockd.o
lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o
svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o netlink.o
lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-$(CONFIG_PROC_FS) += procfs.o

44
fs/lockd/netlink.c Normal file
View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel source */
#include <net/netlink.h>
#include <net/genetlink.h>
#include "netlink.h"
#include <uapi/linux/lockd_netlink.h>
/* LOCKD_CMD_SERVER_SET - do */
static const struct nla_policy lockd_server_set_nl_policy[LOCKD_A_SERVER_UDP_PORT + 1] = {
[LOCKD_A_SERVER_GRACETIME] = { .type = NLA_U32, },
[LOCKD_A_SERVER_TCP_PORT] = { .type = NLA_U16, },
[LOCKD_A_SERVER_UDP_PORT] = { .type = NLA_U16, },
};
/* Ops table for lockd */
static const struct genl_split_ops lockd_nl_ops[] = {
{
.cmd = LOCKD_CMD_SERVER_SET,
.doit = lockd_nl_server_set_doit,
.policy = lockd_server_set_nl_policy,
.maxattr = LOCKD_A_SERVER_UDP_PORT,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = LOCKD_CMD_SERVER_GET,
.doit = lockd_nl_server_get_doit,
.flags = GENL_CMD_CAP_DO,
},
};
struct genl_family lockd_nl_family __ro_after_init = {
.name = LOCKD_FAMILY_NAME,
.version = LOCKD_FAMILY_VERSION,
.netnsok = true,
.parallel_ops = true,
.module = THIS_MODULE,
.split_ops = lockd_nl_ops,
.n_split_ops = ARRAY_SIZE(lockd_nl_ops),
};

19
fs/lockd/netlink.h Normal file
View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN kernel header */
#ifndef _LINUX_LOCKD_GEN_H
#define _LINUX_LOCKD_GEN_H
#include <net/netlink.h>
#include <net/genetlink.h>
#include <uapi/linux/lockd_netlink.h>
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info);
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info);
extern struct genl_family lockd_nl_family;
#endif /* _LINUX_LOCKD_GEN_H */

View File

@ -10,6 +10,9 @@ struct lockd_net {
unsigned int nlmsvc_users;
unsigned long next_gc;
unsigned long nrhosts;
u32 gracetime;
u16 tcp_port;
u16 udp_port;
struct delayed_work grace_period_end;
struct lock_manager lockd_manager;

View File

@ -41,6 +41,7 @@
#include "netns.h"
#include "procfs.h"
#include "netlink.h"
#define NLMDBG_FACILITY NLMDBG_SVC
#define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE)
@ -83,8 +84,14 @@ static const int nlm_port_min = 0, nlm_port_max = 65535;
static struct ctl_table_header * nlm_sysctl_table;
#endif
static unsigned long get_lockd_grace_period(void)
static unsigned long get_lockd_grace_period(struct net *net)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
/* Return the net-ns specific grace period, if there is one */
if (ln->gracetime)
return ln->gracetime * HZ;
/* Note: nlm_timeout should always be nonzero */
if (nlm_grace_period)
return roundup(nlm_grace_period, nlm_timeout) * HZ;
@ -103,7 +110,7 @@ static void grace_ender(struct work_struct *grace)
static void set_grace_period(struct net *net)
{
unsigned long grace_period = get_lockd_grace_period();
unsigned long grace_period = get_lockd_grace_period(net);
struct lockd_net *ln = net_generic(net, lockd_net_id);
locks_start_grace(net, &ln->lockd_manager);
@ -166,15 +173,16 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
static int create_lockd_family(struct svc_serv *serv, struct net *net,
const int family, const struct cred *cred)
{
struct lockd_net *ln = net_generic(net, lockd_net_id);
int err;
err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
cred);
err = create_lockd_listener(serv, "udp", net, family,
ln->udp_port ? ln->udp_port : nlm_udpport, cred);
if (err < 0)
return err;
return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
cred);
return create_lockd_listener(serv, "tcp", net, family,
ln->tcp_port ? ln->tcp_port : nlm_tcpport, cred);
}
/*
@ -459,9 +467,10 @@ static const struct ctl_table nlm_sysctls[] = {
{
.procname = "nsm_local_state",
.data = &nsm_local_state,
.maxlen = sizeof(int),
.maxlen = sizeof(nsm_local_state),
.mode = 0644,
.proc_handler = proc_dointvec,
.proc_handler = proc_douintvec,
.extra1 = SYSCTL_ZERO,
},
};
@ -588,6 +597,10 @@ static int __init init_nlm(void)
if (err)
goto err_pernet;
err = genl_register_family(&lockd_nl_family);
if (err)
goto err_netlink;
err = lockd_create_procfs();
if (err)
goto err_procfs;
@ -595,6 +608,8 @@ static int __init init_nlm(void)
return 0;
err_procfs:
genl_unregister_family(&lockd_nl_family);
err_netlink:
unregister_pernet_subsys(&lockd_net_ops);
err_pernet:
#ifdef CONFIG_SYSCTL
@ -608,6 +623,7 @@ static void __exit exit_nlm(void)
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
genl_unregister_family(&lockd_nl_family);
lockd_remove_procfs();
unregister_pernet_subsys(&lockd_net_ops);
#ifdef CONFIG_SYSCTL
@ -710,3 +726,94 @@ static struct svc_program nlmsvc_program = {
.pg_init_request = svc_generic_init_request,
.pg_rpcbind_set = svc_generic_rpcbind_set,
};
/**
* lockd_nl_server_set_doit - set the lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* This updates the per-net values. When updating the values in the init_net
* namespace, also update the "legacy" global values.
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_set_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
const struct nlattr *attr;
if (GENL_REQ_ATTR_CHECK(info, LOCKD_A_SERVER_GRACETIME))
return -EINVAL;
if (info->attrs[LOCKD_A_SERVER_GRACETIME] ||
info->attrs[LOCKD_A_SERVER_TCP_PORT] ||
info->attrs[LOCKD_A_SERVER_UDP_PORT]) {
attr = info->attrs[LOCKD_A_SERVER_GRACETIME];
if (attr) {
u32 gracetime = nla_get_u32(attr);
if (gracetime > nlm_grace_period_max)
return -EINVAL;
ln->gracetime = gracetime;
if (net == &init_net)
nlm_grace_period = gracetime;
}
attr = info->attrs[LOCKD_A_SERVER_TCP_PORT];
if (attr) {
ln->tcp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_tcpport = ln->tcp_port;
}
attr = info->attrs[LOCKD_A_SERVER_UDP_PORT];
if (attr) {
ln->udp_port = nla_get_u16(attr);
if (net == &init_net)
nlm_udpport = ln->udp_port;
}
}
return 0;
}
/**
* lockd_nl_server_get_doit - get lockd server parameters via netlink
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* Return 0 on success or a negative errno.
*/
int lockd_nl_server_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
struct lockd_net *ln = net_generic(net, lockd_net_id);
void *hdr;
int err;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = genlmsg_iput(skb, info);
if (!hdr) {
err = -EMSGSIZE;
goto err_free_msg;
}
err = nla_put_u32(skb, LOCKD_A_SERVER_GRACETIME, ln->gracetime) ||
nla_put_u16(skb, LOCKD_A_SERVER_TCP_PORT, ln->tcp_port) ||
nla_put_u16(skb, LOCKD_A_SERVER_UDP_PORT, ln->udp_port);
if (err)
goto err_free_msg;
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_free_msg:
nlmsg_free(skb);
return err;
}

View File

@ -154,5 +154,6 @@ const struct export_operations nfs_export_ops = {
EXPORT_OP_CLOSE_BEFORE_UNLINK |
EXPORT_OP_REMOTE_FS |
EXPORT_OP_NOATOMIC_ATTR |
EXPORT_OP_FLUSH_ON_CLOSE,
EXPORT_OP_FLUSH_ON_CLOSE |
EXPORT_OP_NOLOCKS,
};

View File

@ -42,7 +42,7 @@ struct nfsacl_encode_desc {
};
struct nfsacl_simple_acl {
struct posix_acl acl;
struct posix_acl_hdr acl;
struct posix_acl_entry ace[4];
};
@ -112,7 +112,8 @@ int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
xdr_encode_word(buf, base, entries))
return -EINVAL;
if (encode_entries && acl && acl->a_count == 3) {
struct posix_acl *acl2 = &aclbuf.acl;
struct posix_acl *acl2 =
container_of(&aclbuf.acl, struct posix_acl, hdr);
/* Avoid the use of posix_acl_alloc(). nfsacl_encode() is
* invoked in contexts where a memory allocation failure is
@ -177,7 +178,8 @@ bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
return false;
if (encode_entries && acl && acl->a_count == 3) {
struct posix_acl *acl2 = &aclbuf.acl;
struct posix_acl *acl2 =
container_of(&aclbuf.acl, struct posix_acl, hdr);
/* Avoid the use of posix_acl_alloc(). nfsacl_encode() is
* invoked in contexts where a memory allocation failure is

View File

@ -172,6 +172,16 @@ config NFSD_LEGACY_CLIENT_TRACKING
recoverydir, or spawn a process directly using a usermodehelper
upcall.
These legacy client tracking methods have proven to be probelmatic
These legacy client tracking methods have proven to be problematic
and will be removed in the future. Say Y here if you need support
for them in the interim.
config NFSD_V4_DELEG_TIMESTAMPS
bool "Support delegated timestamps"
depends on NFSD_V4
default n
help
NFSD implements delegated timestamps according to
draft-ietf-nfsv4-delstid-08 "Extending the Opening of Files". This
is currently an experimental feature and is therefore left disabled
by default.

View File

@ -319,15 +319,14 @@ nfsd_file_check_writeback(struct nfsd_file *nf)
mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK);
}
static bool nfsd_file_lru_add(struct nfsd_file *nf)
static void nfsd_file_lru_add(struct nfsd_file *nf)
{
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru)) {
refcount_inc(&nf->nf_ref);
if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru))
trace_nfsd_file_lru_add(nf);
return true;
}
return false;
else
WARN_ON(1);
nfsd_file_schedule_laundrette();
}
static bool nfsd_file_lru_remove(struct nfsd_file *nf)
@ -363,30 +362,10 @@ nfsd_file_put(struct nfsd_file *nf)
if (test_bit(NFSD_FILE_GC, &nf->nf_flags) &&
test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
/*
* If this is the last reference (nf_ref == 1), then try to
* transfer it to the LRU.
*/
if (refcount_dec_not_one(&nf->nf_ref))
return;
/* Try to add it to the LRU. If that fails, decrement. */
if (nfsd_file_lru_add(nf)) {
/* If it's still hashed, we're done */
if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags)) {
nfsd_file_schedule_laundrette();
return;
}
/*
* We're racing with unhashing, so try to remove it from
* the LRU. If removal fails, then someone else already
* has our reference.
*/
if (!nfsd_file_lru_remove(nf))
return;
}
set_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
set_bit(NFSD_FILE_RECENT, &nf->nf_flags);
}
if (refcount_dec_and_test(&nf->nf_ref))
nfsd_file_free(nf);
}
@ -530,13 +509,12 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
}
/*
* Put the reference held on behalf of the LRU. If it wasn't the last
* one, then just remove it from the LRU and ignore it.
* Put the reference held on behalf of the LRU if it is the last
* reference, else rotate.
*/
if (!refcount_dec_and_test(&nf->nf_ref)) {
if (!refcount_dec_if_one(&nf->nf_ref)) {
trace_nfsd_file_gc_in_use(nf);
list_lru_isolate(lru, &nf->nf_lru);
return LRU_REMOVED;
return LRU_ROTATE;
}
/* Refcount went to zero. Unhash it and queue it to the dispose list */
@ -548,14 +526,54 @@ nfsd_file_lru_cb(struct list_head *item, struct list_lru_one *lru,
return LRU_REMOVED;
}
static enum lru_status
nfsd_file_gc_cb(struct list_head *item, struct list_lru_one *lru,
void *arg)
{
struct nfsd_file *nf = list_entry(item, struct nfsd_file, nf_lru);
if (test_and_clear_bit(NFSD_FILE_RECENT, &nf->nf_flags)) {
/*
* "REFERENCED" really means "should be at the end of the
* LRU. As we are putting it there we can clear the flag.
*/
clear_bit(NFSD_FILE_REFERENCED, &nf->nf_flags);
trace_nfsd_file_gc_aged(nf);
return LRU_ROTATE;
}
return nfsd_file_lru_cb(item, lru, arg);
}
/* If the shrinker runs between calls to list_lru_walk_node() in
* nfsd_file_gc(), the "remaining" count will be wrong. This could
* result in premature freeing of some files. This may not matter much
* but is easy to fix with this spinlock which temporarily disables
* the shrinker.
*/
static DEFINE_SPINLOCK(nfsd_gc_lock);
static void
nfsd_file_gc(void)
{
unsigned long ret = 0;
LIST_HEAD(dispose);
unsigned long ret;
int nid;
ret = list_lru_walk(&nfsd_file_lru, nfsd_file_lru_cb,
&dispose, list_lru_count(&nfsd_file_lru));
spin_lock(&nfsd_gc_lock);
for_each_node_state(nid, N_NORMAL_MEMORY) {
unsigned long remaining = list_lru_count_node(&nfsd_file_lru, nid);
while (remaining > 0) {
unsigned long nr = min(remaining, NFSD_FILE_GC_BATCH);
remaining -= nr;
ret += list_lru_walk_node(&nfsd_file_lru, nid, nfsd_file_gc_cb,
&dispose, &nr);
if (nr)
/* walk aborted early */
remaining = 0;
}
}
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
nfsd_file_dispose_list_delayed(&dispose);
}
@ -563,9 +581,9 @@ nfsd_file_gc(void)
static void
nfsd_file_gc_worker(struct work_struct *work)
{
nfsd_file_gc();
if (list_lru_count(&nfsd_file_lru))
nfsd_file_schedule_laundrette();
nfsd_file_gc();
nfsd_file_schedule_laundrette();
}
static unsigned long
@ -580,8 +598,12 @@ nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
LIST_HEAD(dispose);
unsigned long ret;
if (!spin_trylock(&nfsd_gc_lock))
return SHRINK_STOP;
ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
nfsd_file_lru_cb, &dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_shrinker_removed(ret, list_lru_count(&nfsd_file_lru));
nfsd_file_dispose_list_delayed(&dispose);
return ret;
@ -686,17 +708,12 @@ nfsd_file_close_inode(struct inode *inode)
void
nfsd_file_close_inode_sync(struct inode *inode)
{
struct nfsd_file *nf;
LIST_HEAD(dispose);
trace_nfsd_file_close(inode);
nfsd_file_queue_for_close(inode, &dispose);
while (!list_empty(&dispose)) {
nf = list_first_entry(&dispose, struct nfsd_file, nf_gc);
list_del_init(&nf->nf_gc);
nfsd_file_free(nf);
}
nfsd_file_dispose_list(&dispose);
}
static int
@ -1058,16 +1075,8 @@ retry:
nf = nfsd_file_lookup_locked(net, current_cred(), inode, need, want_gc);
rcu_read_unlock();
if (nf) {
/*
* If the nf is on the LRU then it holds an extra reference
* that must be put if it's removed. It had better not be
* the last one however, since we should hold another.
*/
if (nfsd_file_lru_remove(nf))
refcount_dec(&nf->nf_ref);
if (nf)
goto wait_for_construction;
}
new = nfsd_file_alloc(net, inode, need, want_gc);
if (!new) {
@ -1161,6 +1170,9 @@ open_file:
*/
if (status != nfs_ok || inode->i_nlink == 0)
nfsd_file_unhash(nf);
else if (want_gc)
nfsd_file_lru_add(nf);
clear_and_wake_up_bit(NFSD_FILE_PENDING, &nf->nf_flags);
if (status == nfs_ok)
goto out;

View File

@ -3,6 +3,12 @@
#include <linux/fsnotify_backend.h>
/*
* Limit the time that the list_lru_one lock is held during
* an LRU scan.
*/
#define NFSD_FILE_GC_BATCH (16UL)
/*
* This is the fsnotify_mark container that nfsd attaches to the files that it
* is holding open. Note that we have a separate refcount here aside from the
@ -38,6 +44,7 @@ struct nfsd_file {
#define NFSD_FILE_PENDING (1)
#define NFSD_FILE_REFERENCED (2)
#define NFSD_FILE_GC (3)
#define NFSD_FILE_RECENT (4)
unsigned long nf_flags;
refcount_t nf_ref;
unsigned char nf_may;

View File

@ -46,8 +46,6 @@
#define NFSDDBG_FACILITY NFSDDBG_PROC
static void nfsd4_mark_cb_fault(struct nfs4_client *clp);
#define NFSPROC4_CB_NULL 0
#define NFSPROC4_CB_COMPOUND 1
@ -101,15 +99,15 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
if (bitmap[0] & FATTR4_WORD0_CHANGE)
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_change) < 0)
return -NFSERR_BAD_XDR;
return -EIO;
if (bitmap[0] & FATTR4_WORD0_SIZE)
if (xdr_stream_decode_u64(xdr, &fattr->ncf_cb_fsize) < 0)
return -NFSERR_BAD_XDR;
return -EIO;
if (bitmap[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) {
fattr4_time_deleg_access access;
if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access))
return -NFSERR_BAD_XDR;
return -EIO;
fattr->ncf_cb_atime.tv_sec = access.seconds;
fattr->ncf_cb_atime.tv_nsec = access.nseconds;
@ -118,7 +116,7 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap,
fattr4_time_deleg_modify modify;
if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify))
return -NFSERR_BAD_XDR;
return -EIO;
fattr->ncf_cb_mtime.tv_sec = modify.seconds;
fattr->ncf_cb_mtime.tv_nsec = modify.nseconds;
@ -682,15 +680,15 @@ static int nfs4_xdr_dec_cb_getattr(struct rpc_rqst *rqstp,
if (unlikely(status || cb->cb_status))
return status;
if (xdr_stream_decode_uint32_array(xdr, bitmap, 3) < 0)
return -NFSERR_BAD_XDR;
return -EIO;
if (xdr_stream_decode_u32(xdr, &attrlen) < 0)
return -NFSERR_BAD_XDR;
return -EIO;
maxlen = sizeof(ncf->ncf_cb_change) + sizeof(ncf->ncf_cb_fsize);
if (bitmap[2] != 0)
maxlen += (sizeof(ncf->ncf_cb_mtime.tv_sec) +
sizeof(ncf->ncf_cb_mtime.tv_nsec)) * 2;
if (attrlen > maxlen)
return -NFSERR_BAD_XDR;
return -EIO;
status = decode_cb_fattr4(xdr, bitmap, ncf);
return status;
}
@ -1064,6 +1062,17 @@ static bool nfsd4_queue_cb(struct nfsd4_callback *cb)
return queue_work(clp->cl_callback_wq, &cb->cb_work);
}
static void nfsd4_requeue_cb(struct rpc_task *task, struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
if (!test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) {
trace_nfsd_cb_restart(clp, cb);
task->tk_status = 0;
set_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags);
}
}
static void nfsd41_cb_inflight_begin(struct nfs4_client *clp)
{
atomic_inc(&clp->cl_cb_inflight);
@ -1301,6 +1310,11 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
trace_nfsd_cb_destroy(clp, cb);
nfsd41_cb_release_slot(cb);
if (test_bit(NFSD4_CALLBACK_WAKE, &cb->cb_flags))
clear_and_wake_up_bit(NFSD4_CALLBACK_RUNNING, &cb->cb_flags);
else
clear_bit(NFSD4_CALLBACK_RUNNING, &cb->cb_flags);
if (cb->cb_ops && cb->cb_ops->release)
cb->cb_ops->release(cb);
nfsd41_cb_inflight_end(clp);
@ -1328,30 +1342,14 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
rpc_call_start(task);
}
/* Returns true if CB_COMPOUND processing should continue */
static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *session = clp->cl_cb_session;
bool ret = true;
if (!clp->cl_minorversion) {
/*
* If the backchannel connection was shut down while this
* task was queued, we need to resubmit it after setting up
* a new backchannel connection.
*
* Note that if we lost our callback connection permanently
* the submission code will error out, so we don't need to
* handle that case here.
*/
if (RPC_SIGNALLED(task))
goto need_restart;
return true;
}
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
bool ret = false;
if (cb->cb_held_slot < 0)
goto need_restart;
goto requeue;
/* This is the operation status code for CB_SEQUENCE */
trace_nfsd_cb_seq_status(task, cb);
@ -1365,11 +1363,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
* (sequence ID, cached reply) MUST NOT change.
*/
++session->se_cb_seq_nr[cb->cb_held_slot];
ret = true;
break;
case -ESERVERFAULT:
++session->se_cb_seq_nr[cb->cb_held_slot];
/*
* Call succeeded, but the session, slot index, or slot
* sequence number in the response do not match the same
* in the server's call. The sequence information is thus
* untrustworthy.
*/
nfsd4_mark_cb_fault(cb->cb_clp);
ret = false;
break;
case 1:
/*
@ -1381,43 +1384,42 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
fallthrough;
case -NFS4ERR_BADSESSION:
nfsd4_mark_cb_fault(cb->cb_clp);
ret = false;
goto need_restart;
goto requeue;
case -NFS4ERR_DELAY:
cb->cb_seq_status = 1;
if (!rpc_restart_call(task))
goto out;
if (RPC_SIGNALLED(task) || !rpc_restart_call(task))
goto requeue;
rpc_delay(task, 2 * HZ);
return false;
case -NFS4ERR_BADSLOT:
goto retry_nowait;
case -NFS4ERR_SEQ_MISORDERED:
if (session->se_cb_seq_nr[cb->cb_held_slot] != 1) {
session->se_cb_seq_nr[cb->cb_held_slot] = 1;
goto retry_nowait;
}
break;
case -NFS4ERR_BADSLOT:
/*
* A SEQ_MISORDERED or BADSLOT error means that the client and
* server are out of sync as to the backchannel parameters. Mark
* the backchannel faulty and restart the RPC, but leak the slot
* so that it's no longer used.
*/
nfsd4_mark_cb_fault(cb->cb_clp);
cb->cb_held_slot = -1;
goto retry_nowait;
default:
nfsd4_mark_cb_fault(cb->cb_clp);
}
trace_nfsd_cb_free_slot(task, cb);
nfsd41_cb_release_slot(cb);
if (RPC_SIGNALLED(task))
goto need_restart;
out:
return ret;
retry_nowait:
if (rpc_restart_call_prepare(task))
ret = false;
goto out;
need_restart:
if (!test_bit(NFSD4_CLIENT_CB_KILL, &clp->cl_flags)) {
trace_nfsd_cb_restart(clp, cb);
task->tk_status = 0;
cb->cb_need_restart = true;
/*
* RPC_SIGNALLED() means that the rpc_client is being torn down and
* (possibly) recreated. Requeue the call in that case.
*/
if (!RPC_SIGNALLED(task)) {
if (rpc_restart_call_prepare(task))
return false;
}
requeue:
nfsd41_cb_release_slot(cb);
nfsd4_requeue_cb(task, cb);
return false;
}
@ -1428,8 +1430,21 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
trace_nfsd_cb_rpc_done(clp);
if (!nfsd4_cb_sequence_done(task, cb))
if (!clp->cl_minorversion) {
/*
* If the backchannel connection was shut down while this
* task was queued, we need to resubmit it after setting up
* a new backchannel connection.
*
* Note that if we lost our callback connection permanently
* the submission code will error out, so we don't need to
* handle that case here.
*/
if (RPC_SIGNALLED(task))
nfsd4_requeue_cb(task, cb);
} else if (!nfsd4_cb_sequence_done(task, cb)) {
return;
}
if (cb->cb_status) {
WARN_ONCE(task->tk_status,
@ -1462,7 +1477,7 @@ static void nfsd4_cb_release(void *calldata)
trace_nfsd_cb_rpc_release(cb->cb_clp);
if (cb->cb_need_restart)
if (test_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags))
nfsd4_queue_cb(cb);
else
nfsd41_destroy_cb(cb);
@ -1575,7 +1590,7 @@ nfsd4_run_cb_work(struct work_struct *work)
container_of(work, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp;
struct rpc_clnt *clnt;
int flags;
int flags, ret;
trace_nfsd_cb_start(clp);
@ -1601,16 +1616,19 @@ nfsd4_run_cb_work(struct work_struct *work)
return;
}
if (cb->cb_need_restart) {
cb->cb_need_restart = false;
} else {
if (!test_and_clear_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags)) {
if (cb->cb_ops && cb->cb_ops->prepare)
cb->cb_ops->prepare(cb);
}
cb->cb_msg.rpc_cred = clp->cl_cb_cred;
flags = clp->cl_minorversion ? RPC_TASK_NOCONNECT : RPC_TASK_SOFTCONN;
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | flags,
cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb);
ret = rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | flags,
cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb);
if (ret != 0) {
set_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags);
nfsd4_queue_cb(cb);
}
}
void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
@ -1620,10 +1638,10 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[op];
cb->cb_msg.rpc_argp = cb;
cb->cb_msg.rpc_resp = cb;
cb->cb_flags = 0;
cb->cb_ops = ops;
INIT_WORK(&cb->cb_work, nfsd4_run_cb_work);
cb->cb_status = 0;
cb->cb_need_restart = false;
cb->cb_held_slot = -1;
}

View File

@ -344,9 +344,10 @@ nfsd4_recall_file_layout(struct nfs4_layout_stateid *ls)
atomic_inc(&ls->ls_stid.sc_file->fi_lo_recalls);
trace_nfsd_layout_recall(&ls->ls_stid.sc_stateid);
refcount_inc(&ls->ls_stid.sc_count);
nfsd4_run_cb(&ls->ls_recall);
if (!test_and_set_bit(NFSD4_CALLBACK_RUNNING, &ls->ls_recall.cb_flags)) {
refcount_inc(&ls->ls_stid.sc_count);
nfsd4_run_cb(&ls->ls_recall);
}
out_unlock:
spin_unlock(&ls->ls_lock);
}

View File

@ -1847,7 +1847,7 @@ static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
NFSPROC4_CLNT_CB_OFFLOAD);
trace_nfsd_cb_offload(copy->cp_clp, &cbo->co_res.cb_stateid,
&cbo->co_fh, copy->cp_count, copy->nfserr);
nfsd4_run_cb(&cbo->co_cb);
nfsd4_try_run_cb(&cbo->co_cb);
}
/**

View File

@ -946,15 +946,6 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla
spin_lock_init(&stid->sc_lock);
INIT_LIST_HEAD(&stid->sc_cp_list);
/*
* It shouldn't be a problem to reuse an opaque stateid value.
* I don't think it is for 4.1. But with 4.0 I worry that, for
* example, a stray write retransmission could be accepted by
* the server when it should have been rejected. Therefore,
* adopt a trick from the sctp code to attempt to maximize the
* amount of time until an id is reused, by ensuring they always
* "increase" (mod INT_MAX):
*/
return stid;
out_free:
kmem_cache_free(slab, stid);
@ -1050,6 +1041,12 @@ static struct nfs4_ol_stateid * nfs4_alloc_open_stateid(struct nfs4_client *clp)
return openlockstateid(stid);
}
/*
* As the sc_free callback of deleg, this may be called by nfs4_put_stid
* in nfsd_break_one_deleg.
* Considering nfsd_break_one_deleg is called with the flc->flc_lock held,
* this function mustn't ever sleep.
*/
static void nfs4_free_deleg(struct nfs4_stid *stid)
{
struct nfs4_delegation *dp = delegstateid(stid);
@ -1378,7 +1375,8 @@ static void revoke_delegation(struct nfs4_delegation *dp)
struct nfs4_client *clp = dp->dl_stid.sc_client;
WARN_ON(!list_empty(&dp->dl_recall_lru));
WARN_ON_ONCE(!(dp->dl_stid.sc_status &
WARN_ON_ONCE(dp->dl_stid.sc_client->cl_minorversion > 0 &&
!(dp->dl_stid.sc_status &
(SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED)));
trace_nfsd_stid_revoke(&dp->dl_stid);
@ -3168,7 +3166,6 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
{
struct nfs4_client *clp = cb->cb_clp;
clear_bit(NFSD4_CLIENT_CB_RECALL_ANY, &clp->cl_flags);
drop_client(clp);
}
@ -3199,7 +3196,6 @@ nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
struct nfs4_delegation *dp =
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
clear_and_wake_up_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags);
nfs4_put_stid(&dp->dl_stid);
}
@ -3220,11 +3216,15 @@ static void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
struct nfs4_delegation *dp =
container_of(ncf, struct nfs4_delegation, dl_cb_fattr);
if (test_and_set_bit(CB_GETATTR_BUSY, &ncf->ncf_cb_flags))
if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &ncf->ncf_getattr.cb_flags))
return;
/* set to proper status when nfsd4_cb_getattr_done runs */
ncf->ncf_cb_status = NFS4ERR_IO;
/* ensure that wake_bit is done when RUNNING is cleared */
set_bit(NFSD4_CALLBACK_WAKE, &ncf->ncf_getattr.cb_flags);
refcount_inc(&dp->dl_stid.sc_count);
nfsd4_run_cb(&ncf->ncf_getattr);
}
@ -4815,8 +4815,8 @@ out:
static unsigned long
nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
{
int count;
struct nfsd_net *nn = shrink->private_data;
long count;
count = atomic_read(&nn->nfsd_courtesy_clients);
if (!count)
@ -5414,6 +5414,11 @@ static const struct nfsd4_callback_ops nfsd4_cb_recall_ops = {
static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
{
bool queued;
if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &dp->dl_recall.cb_flags))
return;
/*
* We're assuming the state code never drops its reference
* without first removing the lease. Since we're in this lease
@ -5422,7 +5427,10 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
* we know it's safe to take a reference.
*/
refcount_inc(&dp->dl_stid.sc_count);
WARN_ON_ONCE(!nfsd4_run_cb(&dp->dl_recall));
queued = nfsd4_run_cb(&dp->dl_recall);
WARN_ON_ONCE(!queued);
if (!queued)
nfs4_put_stid(&dp->dl_stid);
}
/* Called from break_lease() with flc_lock held. */
@ -5948,11 +5956,23 @@ nfsd4_verify_setuid_write(struct nfsd4_open *open, struct nfsd_file *nf)
return 0;
}
#ifdef CONFIG_NFSD_V4_DELEG_TIMESTAMPS
static bool nfsd4_want_deleg_timestamps(const struct nfsd4_open *open)
{
return open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
}
#else /* CONFIG_NFSD_V4_DELEG_TIMESTAMPS */
static bool nfsd4_want_deleg_timestamps(const struct nfsd4_open *open)
{
return false;
}
#endif /* CONFIG NFSD_V4_DELEG_TIMESTAMPS */
static struct nfs4_delegation *
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *parent)
{
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
bool deleg_ts = nfsd4_want_deleg_timestamps(open);
struct nfs4_client *clp = stp->st_stid.sc_client;
struct nfs4_file *fp = stp->st_stid.sc_file;
struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
@ -5999,6 +6019,15 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
if (!nf)
return ERR_PTR(-EAGAIN);
/*
* File delegations and associated locks cannot be recovered if the
* export is from an NFS proxy server.
*/
if (exportfs_cannot_lock(nf->nf_file->f_path.mnt->mnt_sb->s_export_op)) {
nfsd_file_put(nf);
return ERR_PTR(-EOPNOTSUPP);
}
spin_lock(&state_lock);
spin_lock(&fp->fi_lock);
if (nfs4_delegation_exists(clp, fp))
@ -6151,8 +6180,8 @@ static void
nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
struct svc_fh *currentfh)
{
bool deleg_ts = open->op_deleg_want & OPEN4_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS;
struct nfs4_openowner *oo = openowner(stp->st_stateowner);
bool deleg_ts = nfsd4_want_deleg_timestamps(open);
struct nfs4_client *clp = stp->st_stid.sc_client;
struct svc_fh *parent = NULL;
struct nfs4_delegation *dp;
@ -6855,38 +6884,34 @@ deleg_reaper(struct nfsd_net *nn)
{
struct list_head *pos, *next;
struct nfs4_client *clp;
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)) {
if (clp->cl_state != NFSD4_ACTIVE)
continue;
if (list_empty(&clp->cl_delegations))
continue;
if (atomic_read(&clp->cl_delegs_in_recall))
continue;
if (test_and_set_bit(NFSD4_CALLBACK_RUNNING, &clp->cl_ra->ra_cb.cb_flags))
continue;
if (ktime_get_boottime_seconds() - clp->cl_ra_time < 5)
continue;
if (clp->cl_cb_state != NFSD4_CB_UP)
continue;
}
list_add(&clp->cl_ra_cblist, &cblist);
/* release in nfsd4_cb_recall_any_release */
kref_get(&clp->cl_nfsdfs.cl_ref);
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) |
BIT(RCA4_TYPE_MASK_WDATA_DLG);
trace_nfsd_cb_recall_any(clp->cl_ra);
nfsd4_run_cb(&clp->cl_ra->ra_cb);
}
spin_unlock(&nn->client_lock);
}
static void
@ -7051,7 +7076,7 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
*/
statusmask |= SC_STATUS_REVOKED;
statusmask |= SC_STATUS_ADMIN_REVOKED;
statusmask |= SC_STATUS_ADMIN_REVOKED | SC_STATUS_FREEABLE;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid) ||
CLOSE_STATEID(stateid))
@ -7706,9 +7731,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
return status;
status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG,
SC_STATUS_REVOKED | SC_STATUS_FREEABLE,
&s, nn);
status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, SC_STATUS_REVOKED, &s, nn);
if (status)
goto out;
dp = delegstateid(s);
@ -7816,7 +7839,7 @@ nfsd4_lm_notify(struct file_lock *fl)
if (queue) {
trace_nfsd_cb_notify_lock(lo, nbl);
nfsd4_run_cb(&nbl->nbl_cb);
nfsd4_try_run_cb(&nbl->nbl_cb);
}
}
@ -8134,6 +8157,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0);
if (status != nfs_ok)
return status;
if (exportfs_cannot_lock(cstate->current_fh.fh_dentry->d_sb->s_export_op)) {
status = nfserr_notsupp;
goto out;
}
if (lock->lk_is_new) {
if (nfsd4_has_session(cstate))
@ -8473,6 +8500,11 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
status = nfserr_lock_range;
goto put_stateid;
}
if (exportfs_cannot_lock(nf->nf_file->f_path.mnt->mnt_sb->s_export_op)) {
status = nfserr_notsupp;
goto put_file;
}
file_lock = locks_alloc_lock();
if (!file_lock) {
dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
@ -9182,8 +9214,8 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
nfs4_cb_getattr(&dp->dl_cb_fattr);
spin_unlock(&ctx->flc_lock);
wait_on_bit_timeout(&ncf->ncf_cb_flags, CB_GETATTR_BUSY,
TASK_INTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
wait_on_bit_timeout(&ncf->ncf_getattr.cb_flags, NFSD4_CALLBACK_RUNNING,
TASK_UNINTERRUPTIBLE, NFSD_CB_GETATTR_TIMEOUT);
if (ncf->ncf_cb_status) {
/* Recall delegation only if client didn't respond */
status = nfserrno(nfsd_open_break_lease(inode, NFSD_MAY_READ));

View File

@ -1917,6 +1917,7 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
struct svc_serv *serv;
LIST_HEAD(permsocks);
struct nfsd_net *nn;
bool delete = false;
int err, rem;
mutex_lock(&nfsd_mutex);
@ -1977,34 +1978,28 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
}
}
/* For now, no removing old sockets while server is running */
if (serv->sv_nrthreads && !list_empty(&permsocks)) {
/*
* If there are listener transports remaining on the permsocks list,
* it means we were asked to remove a listener.
*/
if (!list_empty(&permsocks)) {
list_splice_init(&permsocks, &serv->sv_permsocks);
spin_unlock_bh(&serv->sv_lock);
delete = true;
}
spin_unlock_bh(&serv->sv_lock);
/* Do not remove listeners while there are active threads. */
if (serv->sv_nrthreads) {
err = -EBUSY;
goto out_unlock_mtx;
}
/* Close the remaining sockets on the permsocks list */
while (!list_empty(&permsocks)) {
xprt = list_first_entry(&permsocks, struct svc_xprt, xpt_list);
list_move(&xprt->xpt_list, &serv->sv_permsocks);
/*
* Newly-created sockets are born with the BUSY bit set. Clear
* it if there are no threads, since nothing can pick it up
* in that case.
*/
if (!serv->sv_nrthreads)
clear_bit(XPT_BUSY, &xprt->xpt_flags);
set_bit(XPT_CLOSE, &xprt->xpt_flags);
spin_unlock_bh(&serv->sv_lock);
svc_xprt_close(xprt);
spin_lock_bh(&serv->sv_lock);
}
spin_unlock_bh(&serv->sv_lock);
/*
* Since we can't delete an arbitrary llist entry, destroy the
* remaining listeners and recreate the list.
*/
if (delete)
svc_xprt_destroy_all(serv, net);
/* walk list of addrs again, open any that still don't exist */
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
@ -2031,6 +2026,9 @@ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
xprt = svc_find_listener(serv, xcl_name, net, sa);
if (xprt) {
if (delete)
WARN_ONCE(1, "Transport type=%s already exists\n",
xcl_name);
svc_xprt_put(xprt);
continue;
}
@ -2204,8 +2202,14 @@ static __net_init int nfsd_net_init(struct net *net)
NFSD_STATS_COUNTERS_NUM);
if (retval)
goto out_repcache_error;
memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats));
nn->nfsd_svcstats.program = &nfsd_programs[0];
if (!nfsd_proc_stat_init(net)) {
retval = -ENOMEM;
goto out_proc_error;
}
for (i = 0; i < sizeof(nn->nfsd_versions); i++)
nn->nfsd_versions[i] = nfsd_support_version(i);
for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++)
@ -2215,13 +2219,14 @@ static __net_init int nfsd_net_init(struct net *net)
nfsd4_init_leases_net(nn);
get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key));
seqlock_init(&nn->writeverf_lock);
nfsd_proc_stat_init(net);
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
spin_lock_init(&nn->local_clients_lock);
INIT_LIST_HEAD(&nn->local_clients);
#endif
return 0;
out_proc_error:
percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM);
out_repcache_error:
nfsd_idmap_shutdown(net);
out_idmap_error:

View File

@ -67,12 +67,15 @@ typedef struct {
struct nfsd4_callback {
struct nfs4_client *cb_clp;
struct rpc_message cb_msg;
#define NFSD4_CALLBACK_RUNNING (0)
#define NFSD4_CALLBACK_WAKE (1)
#define NFSD4_CALLBACK_REQUEUE (2)
unsigned long cb_flags;
const struct nfsd4_callback_ops *cb_ops;
struct work_struct cb_work;
int cb_seq_status;
int cb_status;
int cb_held_slot;
bool cb_need_restart;
};
struct nfsd4_callback_ops {
@ -162,15 +165,11 @@ struct nfs4_cb_fattr {
struct timespec64 ncf_cb_mtime;
struct timespec64 ncf_cb_atime;
unsigned long ncf_cb_flags;
bool ncf_file_modified;
u64 ncf_initial_cinfo;
u64 ncf_cur_fsize;
};
/* bits for ncf_cb_flags */
#define CB_GETATTR_BUSY 0
/*
* Represents a delegation stateid. The nfs4_client holds references to these
* and they are put when it is being destroyed or when the delegation is
@ -198,8 +197,8 @@ struct nfs4_delegation {
struct list_head dl_perclnt;
struct list_head dl_recall_lru; /* delegation recalled */
struct nfs4_clnt_odstate *dl_clnt_odstate;
u32 dl_type;
time64_t dl_time;
u32 dl_type;
/* For recall: */
int dl_retries;
struct nfsd4_callback dl_recall;
@ -452,7 +451,6 @@ 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;
struct workqueue_struct *cl_callback_wq;
@ -498,7 +496,6 @@ struct nfs4_client {
struct nfsd4_cb_recall_any *cl_ra;
time64_t cl_ra_time;
struct list_head cl_ra_cblist;
};
/* struct nfs4_client_reset
@ -780,6 +777,13 @@ extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *
extern void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
const struct nfsd4_callback_ops *ops, enum nfsd4_cb_op op);
extern bool nfsd4_run_cb(struct nfsd4_callback *cb);
static inline void nfsd4_try_run_cb(struct nfsd4_callback *cb)
{
if (!test_and_set_bit(NFSD4_CALLBACK_RUNNING, &cb->cb_flags))
WARN_ON_ONCE(!nfsd4_run_cb(cb));
}
extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
void nfsd4_async_copy_reaper(struct nfsd_net *nn);

View File

@ -73,11 +73,11 @@ static int nfsd_show(struct seq_file *seq, void *v)
DEFINE_PROC_SHOW_ATTRIBUTE(nfsd);
void nfsd_proc_stat_init(struct net *net)
struct proc_dir_entry *nfsd_proc_stat_init(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
svc_proc_register(net, &nn->nfsd_svcstats, &nfsd_proc_ops);
return svc_proc_register(net, &nn->nfsd_svcstats, &nfsd_proc_ops);
}
void nfsd_proc_stat_shutdown(struct net *net)

View File

@ -10,7 +10,7 @@
#include <uapi/linux/nfsd/stats.h>
#include <linux/percpu_counter.h>
void nfsd_proc_stat_init(struct net *net);
struct proc_dir_entry *nfsd_proc_stat_init(struct net *net);
void nfsd_proc_stat_shutdown(struct net *net);
static inline void nfsd_stats_rc_hits_inc(struct nfsd_net *nn)

View File

@ -803,6 +803,14 @@ DEFINE_EVENT(nfsd_cs_slot_class, nfsd_##name, \
DEFINE_CS_SLOT_EVENT(slot_seqid_conf);
DEFINE_CS_SLOT_EVENT(slot_seqid_unconf);
#define show_nfs_slot_flags(val) \
__print_flags(val, "|", \
{ NFSD4_SLOT_INUSE, "INUSE" }, \
{ NFSD4_SLOT_CACHETHIS, "CACHETHIS" }, \
{ NFSD4_SLOT_INITIALIZED, "INITIALIZED" }, \
{ NFSD4_SLOT_CACHED, "CACHED" }, \
{ NFSD4_SLOT_REUSED, "REUSED" })
TRACE_EVENT(nfsd_slot_seqid_sequence,
TP_PROTO(
const struct nfs4_client *clp,
@ -813,10 +821,11 @@ TRACE_EVENT(nfsd_slot_seqid_sequence,
TP_STRUCT__entry(
__field(u32, seqid)
__field(u32, slot_seqid)
__field(u32, slot_index)
__field(unsigned long, slot_flags)
__field(u32, cl_boot)
__field(u32, cl_id)
__sockaddr(addr, clp->cl_cb_conn.cb_addrlen)
__field(bool, in_use)
),
TP_fast_assign(
__entry->cl_boot = clp->cl_clientid.cl_boot;
@ -825,11 +834,13 @@ TRACE_EVENT(nfsd_slot_seqid_sequence,
clp->cl_cb_conn.cb_addrlen);
__entry->seqid = seq->seqid;
__entry->slot_seqid = slot->sl_seqid;
__entry->slot_index = seq->slotid;
__entry->slot_flags = slot->sl_flags;
),
TP_printk("addr=%pISpc client %08x:%08x seqid=%u slot_seqid=%u (%sin use)",
TP_printk("addr=%pISpc client %08x:%08x idx=%u seqid=%u slot_seqid=%u flags=%s",
__get_sockaddr(addr), __entry->cl_boot, __entry->cl_id,
__entry->seqid, __entry->slot_seqid,
__entry->in_use ? "" : "not "
__entry->slot_index, __entry->seqid, __entry->slot_seqid,
show_nfs_slot_flags(__entry->slot_flags)
)
);
@ -1039,6 +1050,7 @@ DEFINE_CLID_EVENT(confirmed_r);
{ 1 << NFSD_FILE_HASHED, "HASHED" }, \
{ 1 << NFSD_FILE_PENDING, "PENDING" }, \
{ 1 << NFSD_FILE_REFERENCED, "REFERENCED" }, \
{ 1 << NFSD_FILE_RECENT, "RECENT" }, \
{ 1 << NFSD_FILE_GC, "GC" })
DECLARE_EVENT_CLASS(nfsd_file_class,
@ -1317,6 +1329,7 @@ DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_lru_del_disposed);
DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_in_use);
DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_writeback);
DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_referenced);
DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_aged);
DEFINE_NFSD_FILE_GC_EVENT(nfsd_file_gc_disposed);
DECLARE_EVENT_CLASS(nfsd_file_lruwalk_class,
@ -1346,6 +1359,7 @@ DEFINE_EVENT(nfsd_file_lruwalk_class, name, \
TP_ARGS(removed, remaining))
DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_gc_removed);
DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_gc_recent);
DEFINE_NFSD_FILE_LRUWALK_EVENT(nfsd_file_shrinker_removed);
TRACE_EVENT(nfsd_file_close,
@ -1602,7 +1616,7 @@ DECLARE_EVENT_CLASS(nfsd_cb_lifetime_class,
__entry->cl_id = clp->cl_clientid.cl_id;
__entry->cb = cb;
__entry->opcode = cb->cb_ops ? cb->cb_ops->opcode : _CB_NULL;
__entry->need_restart = cb->cb_need_restart;
__entry->need_restart = test_bit(NFSD4_CALLBACK_REQUEUE, &cb->cb_flags);
__assign_sockaddr(addr, &clp->cl_cb_conn.cb_addr,
clp->cl_cb_conn.cb_addrlen)
),

View File

@ -71,7 +71,6 @@ nfserrno (int errno)
{ nfserr_acces, -EACCES },
{ nfserr_exist, -EEXIST },
{ nfserr_xdev, -EXDEV },
{ nfserr_mlink, -EMLINK },
{ nfserr_nodev, -ENODEV },
{ nfserr_notdir, -ENOTDIR },
{ nfserr_isdir, -EISDIR },
@ -1687,9 +1686,17 @@ out:
return err;
}
/*
* Create a hardlink
* N.B. After this call _both_ ffhp and tfhp need an fh_put
/**
* nfsd_link - create a link
* @rqstp: RPC transaction context
* @ffhp: the file handle of the directory where the new link is to be created
* @name: the filename of the new link
* @len: the length of @name in octets
* @tfhp: the file handle of an existing file object
*
* After this call _both_ ffhp and tfhp need an fh_put.
*
* Returns a generic NFS status code in network byte-order.
*/
__be32
nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
@ -1697,6 +1704,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
{
struct dentry *ddir, *dnew, *dold;
struct inode *dirp;
int type;
__be32 err;
int host_err;
@ -1716,11 +1724,11 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
if (isdotent(name, len))
goto out;
err = nfs_ok;
type = d_inode(tfhp->fh_dentry)->i_mode & S_IFMT;
host_err = fh_want_write(tfhp);
if (host_err) {
err = nfserrno(host_err);
if (host_err)
goto out;
}
ddir = ffhp->fh_dentry;
dirp = d_inode(ddir);
@ -1728,7 +1736,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
dnew = lookup_one_len(name, ddir, len);
if (IS_ERR(dnew)) {
err = nfserrno(PTR_ERR(dnew));
host_err = PTR_ERR(dnew);
goto out_unlock;
}
@ -1744,17 +1752,26 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
fh_fill_post_attrs(ffhp);
inode_unlock(dirp);
if (!host_err) {
err = nfserrno(commit_metadata(ffhp));
if (!err)
err = nfserrno(commit_metadata(tfhp));
} else {
err = nfserrno(host_err);
host_err = commit_metadata(ffhp);
if (!host_err)
host_err = commit_metadata(tfhp);
}
dput(dnew);
out_drop_write:
fh_drop_write(tfhp);
if (host_err == -EBUSY) {
/*
* See RFC 8881 Section 18.9.4 para 1-2: NFSv4 LINK
* wants a status unique to the object type.
*/
if (type != S_IFDIR)
err = nfserr_file_open;
else
err = nfserr_acces;
}
out:
return err;
return err != nfs_ok ? err : nfserrno(host_err);
out_dput:
dput(dnew);
@ -1783,9 +1800,19 @@ nfsd_has_cached_files(struct dentry *dentry)
return ret;
}
/*
* Rename a file
* N.B. After this call _both_ ffhp and tfhp need an fh_put
/**
* nfsd_rename - rename a directory entry
* @rqstp: RPC transaction context
* @ffhp: the file handle of parent directory containing the entry to be renamed
* @fname: the filename of directory entry to be renamed
* @flen: the length of @fname in octets
* @tfhp: the file handle of parent directory to contain the renamed entry
* @tname: the filename of the new entry
* @tlen: the length of @tlen in octets
*
* After this call _both_ ffhp and tfhp need an fh_put.
*
* Returns a generic NFS status code in network byte-order.
*/
__be32
nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
@ -1793,6 +1820,7 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
{
struct dentry *fdentry, *tdentry, *odentry, *ndentry, *trap;
struct inode *fdir, *tdir;
int type = S_IFDIR;
__be32 err;
int host_err;
bool close_cached = false;
@ -1850,11 +1878,14 @@ retry:
host_err = -EINVAL;
if (odentry == trap)
goto out_dput_old;
type = d_inode(odentry)->i_mode & S_IFMT;
ndentry = lookup_one_len(tname, tdentry, tlen);
host_err = PTR_ERR(ndentry);
if (IS_ERR(ndentry))
goto out_dput_old;
if (d_inode(ndentry))
type = d_inode(ndentry)->i_mode & S_IFMT;
host_err = -ENOTEMPTY;
if (ndentry == trap)
goto out_dput_new;
@ -1892,7 +1923,18 @@ retry:
out_dput_old:
dput(odentry);
out_nfserr:
err = nfserrno(host_err);
if (host_err == -EBUSY) {
/*
* See RFC 8881 Section 18.26.4 para 1-3: NFSv4 RENAME
* wants a status unique to the object type.
*/
if (type != S_IFDIR)
err = nfserr_file_open;
else
err = nfserr_acces;
} else {
err = nfserrno(host_err);
}
if (!close_cached) {
fh_fill_post_attrs(ffhp);
@ -1919,9 +1961,17 @@ out:
return err;
}
/*
* Unlink a file or directory
* N.B. After this call fhp needs an fh_put
/**
* nfsd_unlink - remove a directory entry
* @rqstp: RPC transaction context
* @fhp: the file handle of the parent directory to be modified
* @type: enforced file type of the object to be removed
* @fname: the name of directory entry to be removed
* @flen: length of @fname in octets
*
* After this call fhp needs an fh_put.
*
* Returns a generic NFS status code in network byte-order.
*/
__be32
nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
@ -1995,15 +2045,17 @@ out_drop_write:
fh_drop_write(fhp);
out_nfserr:
if (host_err == -EBUSY) {
/* name is mounted-on. There is no perfect
* error status.
/*
* See RFC 8881 Section 18.25.4 para 4: NFSv4 REMOVE
* wants a status unique to the object type.
*/
err = nfserr_file_open;
} else {
err = nfserrno(host_err);
if (type != S_IFDIR)
err = nfserr_file_open;
else
err = nfserr_acces;
}
out:
return err;
return err != nfs_ok ? err : nfserrno(host_err);
out_unlock:
inode_unlock(dirp);
goto out_drop_write;

View File

@ -279,10 +279,22 @@ struct export_operations {
atomic attribute updates
*/
#define EXPORT_OP_FLUSH_ON_CLOSE (0x20) /* fs flushes file data on close */
#define EXPORT_OP_ASYNC_LOCK (0x40) /* fs can do async lock request */
#define EXPORT_OP_NOLOCKS (0x40) /* no file locking support */
unsigned long flags;
};
/**
* exportfs_cannot_lock() - check if export implements file locking
* @export_ops: the nfs export operations to check
*
* Returns true if the export does not support file locking.
*/
static inline bool
exportfs_cannot_lock(const struct export_operations *export_ops)
{
return export_ops->flags & EXPORT_OP_NOLOCKS;
}
extern int exportfs_encode_inode_fh(struct inode *inode, struct fid *fid,
int *max_len, struct inode *parent,
int flags);

View File

@ -27,11 +27,16 @@ struct posix_acl_entry {
};
struct posix_acl {
refcount_t a_refcount;
unsigned int a_count;
struct rcu_head a_rcu;
/* New members MUST be added within the struct_group() macro below. */
struct_group_tagged(posix_acl_hdr, hdr,
refcount_t a_refcount;
unsigned int a_count;
struct rcu_head a_rcu;
);
struct posix_acl_entry a_entries[] __counted_by(a_count);
};
static_assert(offsetof(struct posix_acl, a_entries) == sizeof(struct posix_acl_hdr),
"struct member likely outside of struct_group_tagged()");
#define FOREACH_ACL_ENTRY(pa, acl, pe) \
for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; pa<pe; pa++)

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/lockd.yaml */
/* YNL-GEN uapi header */
#ifndef _UAPI_LINUX_LOCKD_NETLINK_H
#define _UAPI_LINUX_LOCKD_NETLINK_H
#define LOCKD_FAMILY_NAME "lockd"
#define LOCKD_FAMILY_VERSION 1
enum {
LOCKD_A_SERVER_GRACETIME = 1,
LOCKD_A_SERVER_TCP_PORT,
LOCKD_A_SERVER_UDP_PORT,
__LOCKD_A_SERVER_MAX,
LOCKD_A_SERVER_MAX = (__LOCKD_A_SERVER_MAX - 1)
};
enum {
LOCKD_CMD_SERVER_SET = 1,
LOCKD_CMD_SERVER_GET,
__LOCKD_CMD_MAX,
LOCKD_CMD_MAX = (__LOCKD_CMD_MAX - 1)
};
#endif /* _UAPI_LINUX_LOCKD_NETLINK_H */

View File

@ -138,60 +138,6 @@ out:
return ret;
}
/**
* krb5_decrypt - simple decryption of an RPCSEC GSS payload
* @tfm: initialized cipher transform
* @iv: pointer to an IV
* @in: ciphertext to decrypt
* @out: OUT: plaintext
* @length: length of input and output buffers, in bytes
*
* @iv may be NULL to force the use of an all-zero IV.
* The buffer containing the IV must be as large as the
* cipher's ivsize.
*
* Return values:
* %0: @in successfully decrypted into @out
* negative errno: @in not decrypted
*/
u32
krb5_decrypt(
struct crypto_sync_skcipher *tfm,
void * iv,
void * in,
void * out,
int length)
{
u32 ret = -EINVAL;
struct scatterlist sg[1];
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
goto out;
if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
dprintk("RPC: gss_k5decrypt: tfm iv size too large %d\n",
crypto_sync_skcipher_ivsize(tfm));
goto out;
}
if (iv)
memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
memcpy(out, in, length);
sg_init_one(sg, out, length);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg, sg, length, local_iv);
ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
out:
dprintk("RPC: gss_k5decrypt returns %d\n",ret);
return ret;
}
static int
checksummer(struct scatterlist *sg, void *data)
{
@ -202,96 +148,6 @@ checksummer(struct scatterlist *sg, void *data)
return crypto_ahash_update(req);
}
/*
* checksum the plaintext data and hdrlen bytes of the token header
* The checksum is performed over the first 8 bytes of the
* gss token header and then over the data body
*/
u32
make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
struct xdr_buf *body, int body_offset, u8 *cksumkey,
unsigned int usage, struct xdr_netobj *cksumout)
{
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist sg[1];
int err = -1;
u8 *checksumdata;
unsigned int checksumlen;
if (cksumout->len < kctx->gk5e->cksumlength) {
dprintk("%s: checksum buffer length, %u, too small for %s\n",
__func__, cksumout->len, kctx->gk5e->name);
return GSS_S_FAILURE;
}
checksumdata = kmalloc(GSS_KRB5_MAX_CKSUM_LEN, GFP_KERNEL);
if (checksumdata == NULL)
return GSS_S_FAILURE;
tfm = crypto_alloc_ahash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
goto out_free_cksum;
req = ahash_request_alloc(tfm, GFP_KERNEL);
if (!req)
goto out_free_ahash;
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
checksumlen = crypto_ahash_digestsize(tfm);
if (cksumkey != NULL) {
err = crypto_ahash_setkey(tfm, cksumkey,
kctx->gk5e->keylength);
if (err)
goto out;
}
err = crypto_ahash_init(req);
if (err)
goto out;
sg_init_one(sg, header, hdrlen);
ahash_request_set_crypt(req, sg, NULL, hdrlen);
err = crypto_ahash_update(req);
if (err)
goto out;
err = xdr_process_buf(body, body_offset, body->len - body_offset,
checksummer, req);
if (err)
goto out;
ahash_request_set_crypt(req, NULL, checksumdata, 0);
err = crypto_ahash_final(req);
if (err)
goto out;
switch (kctx->gk5e->ctype) {
case CKSUMTYPE_RSA_MD5:
err = krb5_encrypt(kctx->seq, NULL, checksumdata,
checksumdata, checksumlen);
if (err)
goto out;
memcpy(cksumout->data,
checksumdata + checksumlen - kctx->gk5e->cksumlength,
kctx->gk5e->cksumlength);
break;
case CKSUMTYPE_HMAC_SHA1_DES3:
memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
break;
default:
BUG();
break;
}
cksumout->len = kctx->gk5e->cksumlength;
out:
ahash_request_free(req);
out_free_ahash:
crypto_free_ahash(tfm);
out_free_cksum:
kfree(checksumdata);
return err ? GSS_S_FAILURE : 0;
}
/**
* gss_krb5_checksum - Compute the MAC for a GSS Wrap or MIC token
* @tfm: an initialized hash transform

View File

@ -155,10 +155,6 @@ static inline int krb5_derive_key(struct krb5_ctx *kctx,
void krb5_make_confounder(u8 *p, int conflen);
u32 make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
struct xdr_buf *body, int body_offset, u8 *cksumkey,
unsigned int usage, struct xdr_netobj *cksumout);
u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
const struct xdr_buf *body, int body_offset,
struct xdr_netobj *cksumout);
@ -166,9 +162,6 @@ u32 gss_krb5_checksum(struct crypto_ahash *tfm, char *header, int hdrlen,
u32 krb5_encrypt(struct crypto_sync_skcipher *key, void *iv, void *in,
void *out, int length);
u32 krb5_decrypt(struct crypto_sync_skcipher *key, void *iv, void *in,
void *out, int length);
int xdr_extend_head(struct xdr_buf *buf, unsigned int base,
unsigned int shiftlen);

View File

@ -1536,9 +1536,13 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
* or by one second if it has already reached the current time.
* Newly added cache entries will always have ->last_refresh greater
* that ->flush_time, so they don't get flushed prematurely.
*
* If someone frequently calls the flush interface, we should
* immediately clean the corresponding cache_detail instead of
* continuously accumulating nextcheck.
*/
if (cd->flush_time >= now)
if (cd->flush_time >= now && cd->flush_time < (now + 5))
now = cd->flush_time + 1;
cd->flush_time = now;

View File

@ -621,7 +621,8 @@ static void __svc_rdma_free(struct work_struct *work)
/* Destroy the CM ID */
rdma_destroy_id(rdma->sc_cm_id);
rpcrdma_rn_unregister(device, &rdma->sc_rn);
if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
rpcrdma_rn_unregister(device, &rdma->sc_rn);
kfree(rdma);
}