This commit is contained in:
Mark Brown
2026-07-31 13:10:31 +01:00
74 changed files with 3931 additions and 865 deletions
+256 -2
View File
@@ -45,19 +45,162 @@ pragma header nfs4;
/*
* Basic typedefs for RFC 1832 data type definitions
*/
typedef hyper int64_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef hyper int64_t;
typedef unsigned hyper uint64_t;
const NFS4_VERIFIER_SIZE = 8;
const NFS4_FHSIZE = 128;
enum nfsstat4 {
NFS4_OK = 0, /* everything is okay */
NFS4ERR_PERM = 1, /* caller not privileged */
NFS4ERR_NOENT = 2, /* no such file/directory */
NFS4ERR_IO = 5, /* hard I/O error */
NFS4ERR_NXIO = 6, /* no such device */
NFS4ERR_ACCESS = 13, /* access denied */
NFS4ERR_EXIST = 17, /* file already exists */
NFS4ERR_XDEV = 18, /* different filesystems */
/*
* Please do not allocate value 19; it was used in NFSv3
* and we do not want a value in NFSv3 to have a different
* meaning in NFSv4.x.
*/
NFS4ERR_NOTDIR = 20, /* should be a directory */
NFS4ERR_ISDIR = 21, /* should not be directory */
NFS4ERR_INVAL = 22, /* invalid argument */
NFS4ERR_FBIG = 27, /* file exceeds server max */
NFS4ERR_NOSPC = 28, /* no space on filesystem */
NFS4ERR_ROFS = 30, /* read-only filesystem */
NFS4ERR_MLINK = 31, /* too many hard links */
NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */
NFS4ERR_NOTEMPTY = 66, /* directory not empty */
NFS4ERR_DQUOT = 69, /* hard quota limit reached*/
NFS4ERR_STALE = 70, /* file no longer exists */
NFS4ERR_BADHANDLE = 10001,/* Illegal filehandle */
NFS4ERR_BAD_COOKIE = 10003,/* READDIR cookie is stale */
NFS4ERR_NOTSUPP = 10004,/* operation not supported */
NFS4ERR_TOOSMALL = 10005,/* response limit exceeded */
NFS4ERR_SERVERFAULT = 10006,/* undefined server error */
NFS4ERR_BADTYPE = 10007,/* type invalid for CREATE */
NFS4ERR_DELAY = 10008,/* file "busy" - retry */
NFS4ERR_SAME = 10009,/* nverify says attrs same */
NFS4ERR_DENIED = 10010,/* lock unavailable */
NFS4ERR_EXPIRED = 10011,/* lock lease expired */
NFS4ERR_LOCKED = 10012,/* I/O failed due to lock */
NFS4ERR_GRACE = 10013,/* in grace period */
NFS4ERR_FHEXPIRED = 10014,/* filehandle expired */
NFS4ERR_SHARE_DENIED = 10015,/* share reserve denied */
NFS4ERR_WRONGSEC = 10016,/* wrong security flavor */
NFS4ERR_CLID_INUSE = 10017,/* clientid in use */
/* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */
NFS4ERR_RESOURCE = 10018,/* resource exhaustion */
NFS4ERR_MOVED = 10019,/* filesystem relocated */
NFS4ERR_NOFILEHANDLE = 10020,/* current FH is not set */
NFS4ERR_MINOR_VERS_MISMATCH= 10021,/* minor vers not supp */
NFS4ERR_STALE_CLIENTID = 10022,/* server has rebooted */
NFS4ERR_STALE_STATEID = 10023,/* server has rebooted */
NFS4ERR_OLD_STATEID = 10024,/* state is out of sync */
NFS4ERR_BAD_STATEID = 10025,/* incorrect stateid */
NFS4ERR_BAD_SEQID = 10026,/* request is out of seq. */
NFS4ERR_NOT_SAME = 10027,/* verify - attrs not same */
NFS4ERR_LOCK_RANGE = 10028,/* overlapping lock range */
NFS4ERR_SYMLINK = 10029,/* should be file/directory*/
NFS4ERR_RESTOREFH = 10030,/* no saved filehandle */
NFS4ERR_LEASE_MOVED = 10031,/* some filesystem moved */
NFS4ERR_ATTRNOTSUPP = 10032,/* recommended attr not sup*/
NFS4ERR_NO_GRACE = 10033,/* reclaim outside of grace*/
NFS4ERR_RECLAIM_BAD = 10034,/* reclaim error at server */
NFS4ERR_RECLAIM_CONFLICT= 10035,/* conflict on reclaim */
NFS4ERR_BADXDR = 10036,/* XDR decode failed */
NFS4ERR_LOCKS_HELD = 10037,/* file locks held at CLOSE*/
NFS4ERR_OPENMODE = 10038,/* conflict in OPEN and I/O*/
NFS4ERR_BADOWNER = 10039,/* owner translation bad */
NFS4ERR_BADCHAR = 10040,/* utf-8 char not supported*/
NFS4ERR_BADNAME = 10041,/* name not supported */
NFS4ERR_BAD_RANGE = 10042,/* lock range not supported*/
NFS4ERR_LOCK_NOTSUPP = 10043,/* no atomic up/downgrade */
NFS4ERR_OP_ILLEGAL = 10044,/* undefined operation */
NFS4ERR_DEADLOCK = 10045,/* file locking deadlock */
NFS4ERR_FILE_OPEN = 10046,/* open file blocks op. */
NFS4ERR_ADMIN_REVOKED = 10047,/* lockowner state revoked */
NFS4ERR_CB_PATH_DOWN = 10048,/* callback path down */
/* NFSv4.1 errors start here. */
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057,/*backchan reqs outstanding*/
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063,/* unexpected seq.ID in req*/
NFS4ERR_SEQUENCE_POS = 10064,/* [CB_]SEQ. op not 1st op */
NFS4ERR_REQ_TOO_BIG = 10065,/* request too big */
NFS4ERR_REP_TOO_BIG = 10066,/* reply too big */
NFS4ERR_REP_TOO_BIG_TO_CACHE =10067,/* rep. not all cached*/
NFS4ERR_RETRY_UNCACHED_REP =10068,/* retry & rep. uncached*/
NFS4ERR_UNSAFE_COMPOUND =10069,/* retry/recovery too hard */
NFS4ERR_TOO_MANY_OPS = 10070,/*too many ops in [CB_]COMP*/
NFS4ERR_OP_NOT_IN_SESSION =10071,/* op needs [CB_]SEQ. op */
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
/* Error 10073 is unused. */
NFS4ERR_CLIENTID_BUSY = 10074,/* clientid has state */
NFS4ERR_PNFS_IO_HOLE = 10075,/* IO to _SPARSE file hole */
NFS4ERR_SEQ_FALSE_RETRY= 10076,/* Retry != original req. */
NFS4ERR_BAD_HIGH_SLOT = 10077,/* req has bad highest_slot*/
NFS4ERR_DEADSESSION = 10078,/*new req sent to dead sess*/
NFS4ERR_ENCR_ALG_UNSUPP= 10079,/* encr alg. not supp. */
NFS4ERR_PNFS_NO_LAYOUT = 10080,/* I/O without a layout */
NFS4ERR_NOT_ONLY_OP = 10081,/* addl ops not allowed */
NFS4ERR_WRONG_CRED = 10082,/* op done by wrong cred */
NFS4ERR_WRONG_TYPE = 10083,/* op on wrong type object */
NFS4ERR_DIRDELEG_UNAVAIL=10084,/* delegation not avail. */
NFS4ERR_REJECT_DELEG = 10085,/* cb rejected delegation */
NFS4ERR_RETURNCONFLICT = 10086,/* layout get before return*/
NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */
NFS4ERR_PARTNER_NOTSUPP = 10088,
NFS4ERR_PARTNER_NO_AUTH = 10089,
NFS4ERR_UNION_NOTSUPP = 10090,
NFS4ERR_OFFLOAD_DENIED = 10091,
NFS4ERR_WRONG_LFS = 10092,
NFS4ERR_BADLABEL = 10093,
NFS4ERR_OFFLOAD_NO_REQS = 10094,
NFS4ERR_NOXATTR = 10095,
NFS4ERR_XATTR2BIG = 10096
};
/*
* Basic data types
*/
typedef opaque attrlist4<>;
typedef uint32_t bitmap4<>;
typedef opaque verifier4[NFS4_VERIFIER_SIZE];
typedef uint64_t nfs_cookie4;
typedef opaque nfs_fh4<NFS4_FHSIZE>;
typedef opaque utf8string<>;
typedef utf8string utf8str_cis;
typedef utf8string utf8str_cs;
typedef utf8string utf8str_mixed;
typedef utf8str_cs component4;
typedef utf8str_cs linktext4;
typedef component4 pathname4<>;
/*
* Timeval
*/
@@ -66,6 +209,21 @@ struct nfstime4 {
uint32_t nseconds;
};
/*
* File attribute container
*/
struct fattr4 {
bitmap4 attrmask;
attrlist4 attr_vals;
};
/*
* Stateid
*/
struct stateid4 {
uint32_t seqid;
opaque other[12];
};
/*
* The following content was extracted from draft-ietf-nfsv4-delstid
@@ -245,3 +403,99 @@ const FATTR4_ACL_TRUEFORM = 89;
const FATTR4_ACL_TRUEFORM_SCOPE = 90;
const FATTR4_POSIX_DEFAULT_ACL = 91;
const FATTR4_POSIX_ACCESS_ACL = 92;
/*
* Directory notification types.
*/
enum notify_type4 {
NOTIFY4_CHANGE_CHILD_ATTRS = 0,
NOTIFY4_CHANGE_DIR_ATTRS = 1,
NOTIFY4_REMOVE_ENTRY = 2,
NOTIFY4_ADD_ENTRY = 3,
NOTIFY4_RENAME_ENTRY = 4,
NOTIFY4_CHANGE_COOKIE_VERIFIER = 5,
/* Proposed in RFC8881bis */
NOTIFY4_GFLAG_EXTEND = 6,
NOTIFY4_AUFLAG_VALID = 7,
NOTIFY4_AUFLAG_USER = 8,
NOTIFY4_AUFLAG_GROUP = 9,
NOTIFY4_AUFLAG_OTHER = 10,
NOTIFY4_CHANGE_AUTH = 11,
NOTIFY4_CFLAG_ORDER = 12,
NOTIFY4_AUFLAG_GANOW = 13,
NOTIFY4_AUFLAG_GALATER = 14,
NOTIFY4_CHANGE_GA = 15,
NOTIFY4_CHANGE_AMASK = 16
};
/* Changed entry information. */
struct notify_entry4 {
component4 ne_file;
fattr4 ne_attrs;
};
/* Previous entry information */
struct prev_entry4 {
notify_entry4 pe_prev_entry;
/* what READDIR returned for this entry */
nfs_cookie4 pe_prev_entry_cookie;
};
struct notify_remove4 {
notify_entry4 nrm_old_entry;
nfs_cookie4 nrm_old_entry_cookie;
};
pragma public notify_remove4;
struct notify_add4 {
/*
* Information on object
* possibly renamed over.
*/
notify_remove4 nad_old_entry<1>;
notify_entry4 nad_new_entry;
/* what READDIR would have returned for this entry */
nfs_cookie4 nad_new_entry_cookie<1>;
prev_entry4 nad_prev_entry<1>;
bool nad_last_entry;
};
pragma public notify_add4;
struct notify_attr4 {
notify_entry4 na_changed_entry;
};
pragma public notify_attr4;
struct notify_rename4 {
notify_remove4 nrn_old_entry;
notify_add4 nrn_new_entry;
};
pragma public notify_rename4;
struct notify_verifier4 {
verifier4 nv_old_cookieverf;
verifier4 nv_new_cookieverf;
};
/*
* Objects of type notify_<>4 and
* notify_device_<>4 are encoded in this.
*/
typedef opaque notifylist4<>;
struct notify4 {
/* composed from notify_type4 or notify_deviceid_type4 */
bitmap4 notify_mask;
notifylist4 notify_vals;
};
struct CB_NOTIFY4args {
stateid4 cna_stateid;
nfs_fh4 cna_fh;
notify4 cna_changes<>;
};
pragma public CB_NOTIFY4args;
struct CB_NOTIFY4res {
nfsstat4 cnr_status;
};
+1 -1
View File
@@ -314,7 +314,7 @@ void nsm_release(struct nsm_handle *nsm);
* This is used in garbage collection and resource reclaim
* A return value != 0 means destroy the lock/block/share
*/
typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
typedef int (*nlm_host_match_fn_t)(void *owner, struct nlm_host *ref);
/*
* Server-side lock handling
+1 -1
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x
// XDR specification modification time: Thu Apr 23 10:56:34 2026
// XDR specification modification time: Mon Jun 29 20:39:34 2026
#include <linux/sunrpc/svc.h>
+1 -1
View File
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm3.x */
/* XDR specification modification time: Thu Apr 23 10:56:34 2026 */
/* XDR specification modification time: Mon Jun 29 20:39:34 2026 */
#ifndef _LINUX_XDRGEN_NLM3_DECL_H
#define _LINUX_XDRGEN_NLM3_DECL_H
+45 -2
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Generated by xdrgen. Manual edits will be lost.
// XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x
// XDR specification modification time: Thu Dec 25 13:10:19 2025
// XDR specification modification time: Mon Jun 29 20:39:36 2026
#include <linux/sunrpc/svc.h>
@@ -20,6 +20,16 @@ xdrgen_decode_fsh4_mode(struct xdr_stream *xdr, fsh4_mode *ptr)
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsm_DN:
case fsm_DR:
case fsm_DW:
case fsm_DRW:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -31,6 +41,16 @@ xdrgen_decode_fsh4_access(struct xdr_stream *xdr, fsh4_access *ptr)
if (xdr_stream_decode_u32(xdr, &val) < 0)
return false;
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case fsa_NONE:
case fsa_R:
case fsa_W:
case fsa_RW:
break;
default:
return false;
}
*ptr = val;
return true;
}
@@ -62,7 +82,30 @@ xdrgen_decode_int32(struct xdr_stream *xdr, int32 *ptr)
static bool __maybe_unused
xdrgen_decode_nlm4_stats(struct xdr_stream *xdr, nlm4_stats *ptr)
{
return xdr_stream_decode_be32(xdr, ptr) == 0;
__be32 raw;
u32 val;
if (xdr_stream_decode_be32(xdr, &raw) < 0)
return false;
val = be32_to_cpu(raw);
/* Compiler may optimize to a range check for dense enums */
switch (val) {
case NLM4_GRANTED:
case NLM4_DENIED:
case NLM4_DENIED_NOLOCKS:
case NLM4_BLOCKED:
case NLM4_DENIED_GRACE_PERIOD:
case NLM4_DEADLCK:
case NLM4_ROFS:
case NLM4_STALE_FH:
case NLM4_FBIG:
case NLM4_FAILED:
break;
default:
return false;
}
*ptr = raw;
return true;
}
static bool __maybe_unused
+1 -1
View File
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Generated by xdrgen. Manual edits will be lost. */
/* XDR specification file: ../../Documentation/sunrpc/xdr/nlm4.x */
/* XDR specification modification time: Thu Dec 25 13:10:19 2025 */
/* XDR specification modification time: Mon Jun 29 20:39:36 2026 */
#ifndef _LINUX_XDRGEN_NLM4_DECL_H
#define _LINUX_XDRGEN_NLM4_DECL_H
+7 -1
View File
@@ -8,9 +8,14 @@
#ifndef _LOCKD_SHARE_H
#define _LOCKD_SHARE_H
#include <linux/bits.h>
/* Synthetic svid for lockowner lookup during share operations */
#define LOCKD_SHARE_SVID (~(u32)0)
/* One bit per (access, deny) pair; index = (access << 2) | deny */
#define LOCKD_FSH_BIT(a, d) BIT(((a) << 2) | (d))
/*
* DOS share for a specific file
*/
@@ -21,12 +26,13 @@ struct lockd_share {
struct xdr_netobj s_owner; /* owner handle */
u32 s_access; /* access mode */
u32 s_mode; /* deny mode */
u16 s_access_deny_bmap; /* held (access, deny) pairs */
};
__be32 nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh, u32 access, u32 mode);
__be32 nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh);
struct xdr_netobj *oh, u32 access, u32 mode);
void nlmsvc_traverse_shares(struct nlm_host *, struct nlm_file *,
nlm_host_match_fn_t);
+2 -2
View File
@@ -47,7 +47,7 @@
static struct svc_program nlmsvc_program;
const struct nlmsvc_binding *nlmsvc_ops;
const struct nlmsvc_binding __rcu *nlmsvc_ops;
EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
@@ -142,7 +142,7 @@ lockd(void *vrqstp)
nlmsvc_retry_blocked(rqstp);
svc_recv(rqstp, 0);
}
if (nlmsvc_ops)
if (rcu_access_pointer(nlmsvc_ops))
nlmsvc_invalidate_all();
nlm_shutdown_hosts();
cancel_delayed_work_sync(&ln->grace_period_end);
+7 -4
View File
@@ -128,7 +128,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -872,7 +872,8 @@ static __be32 nlm4svc_proc_granted_msg(struct svc_rqst *rqstp)
struct nlm4_testargs_wrapper *argp = rqstp->rq_argp;
struct nlm_host *host;
host = nlm4svc_lookup_host(rqstp, argp->xdrgen.alock.caller_name, false);
host = nlmsvc_lookup_host(rqstp, argp->xdrgen.alock.caller_name.data,
argp->xdrgen.alock.caller_name.len);
if (!host)
return rpc_system_err;
@@ -894,7 +895,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm4_res_wrapper *argp = rqstp->rq_argp;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
@@ -1078,7 +1079,9 @@ static __be32 nlm4svc_proc_unshare(struct svc_rqst *rqstp)
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh,
argp->xdrgen.share.access,
argp->xdrgen.share.mode);
nlmsvc_release_lockowner(lock);
+5 -37
View File
@@ -47,40 +47,6 @@ static const struct rpc_call_ops nlmsvc_grant_ops;
static LIST_HEAD(nlm_blocked);
static DEFINE_SPINLOCK(nlm_blocked_lock);
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
/*
* We can get away with a static buffer because this is only called
* from lockd, which is single-threaded.
*/
static char buf[2*NLM_MAXCOOKIELEN+1];
unsigned int i, len = sizeof(buf);
char *p = buf;
len--; /* allow for trailing \0 */
if (len < 3)
return "???";
for (i = 0 ; i < cookie->len ; i++) {
if (len < 2) {
strcpy(p-3, "...");
break;
}
sprintf(p, "%02x", cookie->data[i]);
p += 2;
len -= 2;
}
*p = '\0';
return buf;
}
#else
static inline const char *nlmdbg_cookie2a(const struct lockd_cookie *cookie)
{
return "???";
}
#endif
/*
* Insert a blocked lock into the global list
*/
@@ -155,11 +121,12 @@ nlmsvc_lookup_block(struct nlm_file *file, struct lockd_lock *lock)
spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
fl = &block->b_call->a_args.lock.fl;
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%*phN\n",
block->b_file, fl->c.flc_pid,
(long long)fl->fl_start,
(long long)fl->fl_end, fl->c.flc_type,
nlmdbg_cookie2a(&block->b_call->a_args.cookie));
block->b_call->a_args.cookie.len,
block->b_call->a_args.cookie.data);
if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
@@ -198,7 +165,8 @@ nlmsvc_find_block(struct lockd_cookie *cookie)
return NULL;
found:
dprintk("nlmsvc_find_block(%s): block=%p\n", nlmdbg_cookie2a(cookie), block);
dprintk("nlmsvc_find_block(%*phN): block=%p\n",
cookie->len, cookie->data, block);
kref_get(&block->b_count);
spin_unlock(&nlm_blocked_lock);
return block;
+5 -3
View File
@@ -133,7 +133,7 @@ nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
{
struct nlm_host *host;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return NULL;
host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
if (!host)
@@ -924,7 +924,7 @@ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
{
struct nlm_res_wrapper *argp = rqstp->rq_argp;
if (!nlmsvc_ops)
if (!rcu_access_pointer(nlmsvc_ops))
return rpc_success;
if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
@@ -1098,7 +1098,9 @@ static __be32 nlmsvc_proc_unshare(struct svc_rqst *rqstp)
if (resp->xdrgen.stat)
goto out;
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh);
resp->xdrgen.stat = nlmsvc_unshare_file(host, file, &lock->oh,
argp->xdrgen.share.access,
argp->xdrgen.share.mode);
nlmsvc_release_lockowner(lock);
+31 -5
View File
@@ -25,6 +25,25 @@ nlm_cmp_owner(struct lockd_share *share, struct xdr_netobj *oh)
&& !memcmp(share->s_owner.data, oh->data, oh->len);
}
/*
* Recompute s_access / s_mode as the union of every (access, deny) pair
* whose bit is currently set in s_access_deny_bmap.
*/
static void nlm_recompute_share(struct lockd_share *share)
{
u32 new_access = 0, new_mode = 0;
unsigned int i;
for (i = 0; i < 16; i++) {
if (share->s_access_deny_bmap & BIT(i)) {
new_access |= i >> 2;
new_mode |= i & 3;
}
}
share->s_access = new_access;
share->s_mode = new_mode;
}
/**
* nlmsvc_share_file - create a share
* @host: Network client peer
@@ -64,12 +83,13 @@ nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
share->s_host = host;
share->s_owner.data = ohdata;
share->s_owner.len = oh->len;
share->s_access_deny_bmap = 0;
share->s_next = file->f_shares;
file->f_shares = share;
update:
share->s_access = access;
share->s_mode = mode;
share->s_access_deny_bmap |= LOCKD_FSH_BIT(access, mode);
nlm_recompute_share(share);
return nlm_granted;
}
@@ -78,12 +98,14 @@ update:
* @host: Network client peer
* @file: File to be unshared
* @oh: Share owner handle
* @access: Access mode of the SHARE being released
* @mode: Deny mode of the SHARE being released
*
* Returns an NLM status code.
*/
__be32
nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct xdr_netobj *oh)
struct xdr_netobj *oh, u32 access, u32 mode)
{
struct lockd_share *share, **shpp;
@@ -93,8 +115,12 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
for (shpp = &file->f_shares; (share = *shpp) != NULL;
shpp = &share->s_next) {
if (share->s_host == host && nlm_cmp_owner(share, oh)) {
*shpp = share->s_next;
kfree(share);
share->s_access_deny_bmap &= ~LOCKD_FSH_BIT(access, mode);
nlm_recompute_share(share);
if (!share->s_access_deny_bmap) {
*shpp = share->s_next;
kfree(share);
}
return nlm_granted;
}
}
+76 -31
View File
@@ -90,22 +90,35 @@ int lock_to_openmode(struct file_lock *lock)
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
struct nlm_file *file, int mode)
{
const struct nlmsvc_binding *ops;
__be32 nlmerr = nlm__int__failed;
__be32 deferred = 0;
int error;
int m;
rcu_read_lock();
ops = rcu_dereference(nlmsvc_ops);
if (!ops || !try_module_get(ops->owner)) {
rcu_read_unlock();
return nlm__int__failed;
}
rcu_read_unlock();
for (m = O_RDONLY; m <= O_WRONLY; m++) {
struct file **fp = &file->f_file[m];
if (mode != O_RDWR && mode != m)
continue;
if (*fp)
if (*fp) {
module_put(ops->owner);
return nlm_granted;
}
error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
if (!error)
error = ops->fopen(rqstp, &file->f_handle, fp, m);
if (!error) {
module_put(ops->owner);
return nlm_granted;
}
dprintk("lockd: open failed (errno %d)\n", error);
switch (error) {
@@ -122,6 +135,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
}
}
module_put(ops->owner);
return deferred ? deferred : nlmerr;
}
@@ -185,6 +199,33 @@ out_free:
goto out_unlock;
}
/*
* Release the struct file references held by a nlm_file.
*/
static void nlm_release_files(struct nlm_file *file)
{
const struct nlmsvc_binding *ops;
bool have_ops;
rcu_read_lock();
ops = rcu_dereference(nlmsvc_ops);
have_ops = ops && try_module_get(ops->owner);
rcu_read_unlock();
if (have_ops) {
if (file->f_file[O_RDONLY])
ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
ops->fclose(file->f_file[O_WRONLY]);
module_put(ops->owner);
} else {
if (file->f_file[O_RDONLY])
fput(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
fput(file->f_file[O_WRONLY]);
}
}
/*
* Delete a file after having released all locks, blocks and shares
*/
@@ -194,10 +235,7 @@ nlm_delete_file(struct nlm_file *file)
nlm_debug_print_file("closing file", file);
if (!hlist_unhashed(&file->f_list)) {
hlist_del(&file->f_list);
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
nlm_release_files(file);
kfree(file);
} else {
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
@@ -312,12 +350,10 @@ nlm_file_inuse(struct nlm_file *file)
return 0;
}
static void nlm_close_files(struct nlm_file *file)
static void nlm_file_release(struct nlm_file *file)
{
if (file->f_file[O_RDONLY])
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
if (file->f_file[O_WRONLY])
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
if (!nlm_file_inuse(file))
nlm_delete_file(file);
}
/*
@@ -327,32 +363,41 @@ static int
nlm_traverse_files(void *data, nlm_host_match_fn_t match,
int (*is_failover_file)(void *data, struct nlm_file *file))
{
struct hlist_node *next;
struct nlm_file *file;
struct nlm_file *file, *next;
int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
if (is_failover_file && !is_failover_file(data, file))
continue;
file = hlist_entry_safe(nlm_files[i].first,
struct nlm_file, f_list);
if (file)
file->f_count++;
mutex_unlock(&nlm_file_mutex);
while (file) {
/*
* Pin the next neighbour before we drop the mutex
* for nlm_inspect_file(); a concurrent
* nlm_release_file() under the same mutex would
* otherwise be free to unlink and kfree it during
* the unlock window, leaving us to dereference a
* freed slab when we walked to next afterwards.
*/
next = hlist_entry_safe(file->f_list.next,
struct nlm_file, f_list);
if (next)
next->f_count++;
/* Traverse locks, blocks and shares of this file
* and update file->f_locks count */
if (nlm_inspect_file(data, file, match))
ret = 1;
if (!is_failover_file || is_failover_file(data, file)) {
mutex_unlock(&nlm_file_mutex);
mutex_lock(&nlm_file_mutex);
file->f_count--;
/* No more references to this file. Let go of it. */
if (list_empty(&file->f_blocks) && !file->f_locks
&& !file->f_shares && !file->f_count) {
hlist_del(&file->f_list);
nlm_close_files(file);
kfree(file);
if (nlm_inspect_file(data, file, match))
ret = 1;
mutex_lock(&nlm_file_mutex);
}
file->f_count--;
nlm_file_release(file);
file = next;
}
}
mutex_unlock(&nlm_file_mutex);
@@ -512,7 +557,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
static int
nlmsvc_match_ip(void *datap, struct nlm_host *host)
{
return rpc_cmp_addr(nlm_srcaddr(host), datap);
return rpc_cmp_addr(nlm_srcaddr(datap), (struct sockaddr *)host);
}
/**
+3 -1
View File
@@ -231,8 +231,9 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
cb_info->users++;
err_net:
if (!cb_info->users) {
xprt_svc_shutdown_bc(xprt);
svc_set_num_threads(cb_info->serv, 0, 0);
svc_destroy(&cb_info->serv);
xprt_svc_destroy_nullify_bc(xprt, &cb_info->serv);
}
err_create:
mutex_unlock(&nfs_callback_mutex);
@@ -254,6 +255,7 @@ void nfs_callback_down(int minorversion, struct net *net, struct rpc_xprt *xprt)
mutex_lock(&nfs_callback_mutex);
serv = cb_info->serv;
xprt_svc_shutdown_bc(xprt);
nfs_callback_down_net(minorversion, serv, net);
cb_info->users--;
if (cb_info->users == 0) {
+15 -1
View File
@@ -292,8 +292,22 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred,
nfs_fh, pnf, fmode);
if (!IS_ERR(localio) && nfs_uuid_add_file(uuid, nfl) < 0) {
/* Delete the cached file when racing with nfs_uuid_put() */
/*
* Delete the cached file when racing with nfs_uuid_put().
* Since nfl->nfs_uuid was never published via
* rcu_assign_pointer(), nfs_close_local_fh() will early-return
* and cannot clean up after us. Drop the slot's file ref and
* its paired net ref, then drop the caller-owned nfsd_file ref
* (+1) and the entry-time nfsd_net ref carried via nf->nf_net,
* and return -ENXIO so the caller never dereferences the
* now-cleared localio.
*/
struct nfsd_file __rcu *tmp =
(struct nfsd_file __force __rcu *)localio;
nfs_to_nfsd_file_put_local(pnf);
nfs_to_nfsd_file_put_local(&tmp);
localio = ERR_PTR(-ENXIO);
}
nfs_to_nfsd_net_put(net);
+177 -88
View File
@@ -55,6 +55,17 @@
/* We only care about NFSD_MAY_READ/WRITE for this cache */
#define NFSD_FILE_MAY_MASK (NFSD_MAY_READ|NFSD_MAY_WRITE|NFSD_MAY_LOCALIO)
/* 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.
*
* It also serializes callers of nfsd_file_dispose_list_delayed()
* against per-net shutdown.
*/
static DEFINE_SPINLOCK(nfsd_gc_lock);
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_allocations);
@@ -62,16 +73,12 @@ 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_evictions);
struct nfsd_fcache_disposal {
spinlock_t lock;
struct list_head freeme;
};
static struct kmem_cache *nfsd_file_slab;
static struct kmem_cache *nfsd_file_mark_slab;
static struct list_lru nfsd_file_lru;
static unsigned long nfsd_file_flags;
static struct fsnotify_group *nfsd_file_fsnotify_group;
static struct fsnotify_group *nfsd_dir_fsnotify_group;
static struct delayed_work nfsd_filecache_laundrette;
static struct rhltable nfsd_file_rhltable
____cacheline_aligned_in_smp;
@@ -147,7 +154,7 @@ static void
nfsd_file_mark_put(struct nfsd_file_mark *nfm)
{
if (refcount_dec_and_test(&nfm->nfm_ref)) {
fsnotify_destroy_mark(&nfm->nfm_mark, nfsd_file_fsnotify_group);
fsnotify_destroy_mark(&nfm->nfm_mark, nfm->nfm_mark.group);
fsnotify_put_mark(&nfm->nfm_mark);
}
}
@@ -155,37 +162,40 @@ nfsd_file_mark_put(struct nfsd_file_mark *nfm)
static struct nfsd_file_mark *
nfsd_file_mark_find_or_create(struct inode *inode)
{
int err;
struct fsnotify_mark *mark;
struct nfsd_file_mark *nfm = NULL, *new;
struct fsnotify_group *group;
struct fsnotify_mark *mark;
int err;
group = S_ISDIR(inode->i_mode) ? nfsd_dir_fsnotify_group : nfsd_file_fsnotify_group;
do {
fsnotify_group_lock(nfsd_file_fsnotify_group);
mark = fsnotify_find_inode_mark(inode,
nfsd_file_fsnotify_group);
fsnotify_group_lock(group);
mark = fsnotify_find_inode_mark(inode, group);
if (mark) {
nfm = nfsd_file_mark_get(container_of(mark,
struct nfsd_file_mark,
nfm_mark));
fsnotify_group_unlock(nfsd_file_fsnotify_group);
fsnotify_group_unlock(group);
if (nfm) {
fsnotify_put_mark(mark);
break;
}
/* Avoid soft lockup race with nfsd_file_mark_put() */
fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
fsnotify_destroy_mark(mark, group);
fsnotify_put_mark(mark);
} else {
fsnotify_group_unlock(nfsd_file_fsnotify_group);
fsnotify_group_unlock(group);
}
/* allocate a new nfm */
new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
if (!new)
return NULL;
fsnotify_init_mark(&new->nfm_mark, nfsd_file_fsnotify_group);
fsnotify_init_mark(&new->nfm_mark, group);
new->nfm_mark.mask = FS_ATTRIB|FS_DELETE_SELF;
refcount_set(&new->nfm_ref, 1);
mutex_init(&new->nfm_recalc_mutex);
err = fsnotify_add_inode_mark(&new->nfm_mark, inode, 0);
@@ -327,8 +337,11 @@ static void nfsd_file_lru_add(struct nfsd_file *nf)
refcount_inc(&nf->nf_ref);
if (list_lru_add_obj(&nfsd_file_lru, &nf->nf_lru))
trace_nfsd_file_lru_add(nf);
else
WARN_ON(1);
else {
refcount_dec(&nf->nf_ref);
WARN_ON_ONCE(1);
return;
}
nfsd_file_schedule_laundrette();
}
@@ -419,25 +432,31 @@ nfsd_file_dispose_list(struct list_head *dispose)
}
/**
* nfsd_file_dispose_list_delayed - move list of dead files to net's freeme list
* nfsd_file_dispose_list_delayed - queue dead files for nfsd thread disposal
* @dispose: list of nfsd_files to be disposed
*
* Transfers each file to the "freeme" list for its nfsd_net, to eventually
* be disposed of by the per-net garbage collector.
* Transfers each file to the dispose list in its nfsd_net and wakes an nfsd
* thread to do the actual close. This keeps the cost of fput() in the nfsd
* threads rather than in the shrinker or GC worker.
*
* All callers must hold nfsd_gc_lock, so that nfsd_file_cache_shutdown_net()
* can synchronize against them before draining the per-net dispose list.
* This guarantees nf_net is still live when we call net_generic().
*/
static void
nfsd_file_dispose_list_delayed(struct list_head *dispose)
{
while(!list_empty(dispose)) {
lockdep_assert_held(&nfsd_gc_lock);
while (!list_empty(dispose)) {
struct nfsd_file *nf = list_first_entry(dispose,
struct nfsd_file, nf_gc);
struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
struct svc_serv *serv;
spin_lock(&l->lock);
list_move_tail(&nf->nf_gc, &l->freeme);
spin_unlock(&l->lock);
spin_lock(&nn->fcache_dispose_lock);
list_move_tail(&nf->nf_gc, &nn->fcache_dispose_list);
spin_unlock(&nn->fcache_dispose_lock);
/*
* The filecache laundrette is shut down after the
@@ -461,21 +480,28 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
*/
void nfsd_file_net_dispose(struct nfsd_net *nn)
{
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
if (!list_empty(&l->freeme)) {
if (!list_empty(&nn->fcache_dispose_list)) {
LIST_HEAD(dispose);
int i;
spin_lock(&l->lock);
for (i = 0; i < 8 && !list_empty(&l->freeme); i++)
list_move(l->freeme.next, &dispose);
spin_unlock(&l->lock);
if (!list_empty(&l->freeme))
/* Wake up another thread to share the work
spin_lock(&nn->fcache_dispose_lock);
for (i = 0; i < 8 && !list_empty(&nn->fcache_dispose_list); i++)
list_move(nn->fcache_dispose_list.next, &dispose);
spin_unlock(&nn->fcache_dispose_lock);
if (!list_empty(&nn->fcache_dispose_list)) {
/*
* Wake up another thread to share the work
* *before* doing any actual disposing.
*
* The filecache laundrette is shut down after
* the nn->nfsd_serv pointer is cleared, but
* before the svc_serv is freed.
*/
svc_wake_up(nn->nfsd_serv);
struct svc_serv *serv = nn->nfsd_serv;
if (serv)
svc_wake_up(serv);
}
nfsd_file_dispose_list(&dispose);
}
}
@@ -552,13 +578,6 @@ nfsd_file_gc_cb(struct list_head *item, struct list_lru_one *lru,
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)
{
@@ -581,9 +600,9 @@ nfsd_file_gc(void)
remaining = 0;
}
}
nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
trace_nfsd_file_gc_removed(ret, list_lru_count(&nfsd_file_lru));
nfsd_file_dispose_list_delayed(&dispose);
}
static void
@@ -611,9 +630,9 @@ nfsd_file_lru_scan(struct shrinker *s, struct shrink_control *sc)
ret = list_lru_shrink_walk(&nfsd_file_lru, sc,
nfsd_file_lru_cb, &dispose);
nfsd_file_dispose_list_delayed(&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,11 +705,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
}
/**
* nfsd_file_close_inode - attempt a delayed close of a nfsd_file
* nfsd_file_close_inode - attempt a deferred close of a nfsd_file
* @inode: inode of the file to attempt to remove
*
* Close out any open nfsd_files that can be reaped for @inode. The
* actual freeing is deferred to the dispose_list_delayed infrastructure.
* actual freeing is deferred to the nfsd service threads.
*
* This is used by the fsnotify callbacks and setlease notifier.
*/
@@ -699,8 +718,10 @@ nfsd_file_close_inode(struct inode *inode)
{
LIST_HEAD(dispose);
spin_lock(&nfsd_gc_lock);
nfsd_file_queue_for_close(inode, &dispose);
nfsd_file_dispose_list_delayed(&dispose);
spin_unlock(&nfsd_gc_lock);
}
/**
@@ -812,12 +833,36 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
return 0;
}
#ifdef CONFIG_NFSD_V4
static int
nfsd_dir_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, struct inode *dir,
const struct qstr *name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
return nfsd_handle_dir_event(mask, dir, data, data_type, name);
}
#else
static int
nfsd_dir_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, struct inode *dir,
const struct qstr *name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
return 0;
}
#endif
static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
.handle_inode_event = nfsd_file_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free,
};
static const struct fsnotify_ops nfsd_dir_fsnotify_ops = {
.handle_event = nfsd_dir_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free,
};
int
nfsd_file_cache_init(void)
{
@@ -869,8 +914,7 @@ nfsd_file_cache_init(void)
goto out_shrinker;
}
nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
0);
nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops, 0);
if (IS_ERR(nfsd_file_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_file_fsnotify_group));
@@ -879,11 +923,23 @@ nfsd_file_cache_init(void)
goto out_notifier;
}
nfsd_dir_fsnotify_group = fsnotify_alloc_group(&nfsd_dir_fsnotify_ops, 0);
if (IS_ERR(nfsd_dir_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_dir_fsnotify_group));
ret = PTR_ERR(nfsd_dir_fsnotify_group);
nfsd_dir_fsnotify_group = NULL;
goto out_notify_group;
}
INIT_DELAYED_WORK(&nfsd_filecache_laundrette, nfsd_file_gc_worker);
out:
if (ret)
clear_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags);
return ret;
out_notify_group:
fsnotify_put_group(nfsd_file_fsnotify_group);
nfsd_file_fsnotify_group = NULL;
out_notifier:
lease_unregister_notifier(&nfsd_file_lease_notifier);
out_shrinker:
@@ -940,42 +996,14 @@ __nfsd_file_cache_purge(struct net *net)
nfsd_file_dispose_list(&dispose);
}
static struct nfsd_fcache_disposal *
nfsd_alloc_fcache_disposal(void)
{
struct nfsd_fcache_disposal *l;
l = kmalloc_obj(*l);
if (!l)
return NULL;
spin_lock_init(&l->lock);
INIT_LIST_HEAD(&l->freeme);
return l;
}
static void
nfsd_free_fcache_disposal(struct nfsd_fcache_disposal *l)
{
nfsd_file_dispose_list(&l->freeme);
kfree(l);
}
static void
nfsd_free_fcache_disposal_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct nfsd_fcache_disposal *l = nn->fcache_disposal;
nfsd_free_fcache_disposal(l);
}
int
nfsd_file_cache_start_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nn->fcache_disposal = nfsd_alloc_fcache_disposal();
return nn->fcache_disposal ? 0 : -ENOMEM;
spin_lock_init(&nn->fcache_dispose_lock);
INIT_LIST_HEAD(&nn->fcache_dispose_list);
return 0;
}
/**
@@ -994,8 +1022,18 @@ nfsd_file_cache_purge(struct net *net)
void
nfsd_file_cache_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
nfsd_file_cache_purge(net);
nfsd_free_fcache_disposal_net(net);
/*
* Ensure any in-progress shrinker, GC, or fsnotify/lease callback
* (all of which hold nfsd_gc_lock while calling
* nfsd_file_dispose_list_delayed()) has fully completed before
* draining the per-net dispose list.
*/
spin_lock(&nfsd_gc_lock);
spin_unlock(&nfsd_gc_lock);
nfsd_file_dispose_list(&nn->fcache_dispose_list);
}
void
@@ -1019,6 +1057,8 @@ nfsd_file_cache_shutdown(void)
rcu_barrier();
fsnotify_put_group(nfsd_file_fsnotify_group);
nfsd_file_fsnotify_group = NULL;
fsnotify_put_group(nfsd_dir_fsnotify_group);
nfsd_dir_fsnotify_group = NULL;
kmem_cache_destroy(nfsd_file_slab);
nfsd_file_slab = NULL;
fsnotify_wait_marks_destroyed();
@@ -1223,11 +1263,9 @@ out:
open_file:
trace_nfsd_file_alloc(nf);
if (type == S_IFREG)
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
if (type != S_IFREG || nf->nf_mark) {
if (file) {
nf->nf_mark = nfsd_file_mark_find_or_create(inode);
if (nf->nf_mark) {
if (file && (file->f_mode & FMODE_OPENED)) {
get_file(file);
nf->nf_file = file;
status = nfs_ok;
@@ -1374,12 +1412,12 @@ nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
* @rqstp: the RPC transaction being executed
* @fhp: the NFS filehandle of the file just created
* @may_flags: NFSD_MAY_ settings for the file
* @file: cached, already-open file (may be NULL)
* @file: cached, already-open file (may be NULL or not yet opened)
* @pnf: OUT: new or found "struct nfsd_file" object
*
* Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
* and @file is non-NULL, use it to instantiate a new nfsd_file instead of
* opening a new one.
* and @file has FMODE_OPENED set, use it to instantiate a new nfsd_file
* instead of opening a new one.
*
* Return values:
* %nfs_ok - @pnf points to an nfsd_file with its reference
@@ -1474,3 +1512,54 @@ int nfsd_file_cache_stats_show(struct seq_file *m, void *v)
seq_printf(m, "mean age (ms): -\n");
return 0;
}
/**
* nfsd_fsnotify_recalc_mask - recalculate the fsnotify mask for a nfsd_file
* @nf: nfsd_file to recalculate the mask on
*
* When a directory nfsd_file has a delegation added or removed, that may
* change the events that nfsd requires from the VFS layer. This function
* recalculates the fsnotify mask based on the leases present.
*/
void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf)
{
struct inode *inode = file_inode(nf->nf_file);
u32 lease_mask, set = 0, clear = 0;
struct fsnotify_mark *mark;
/* This is only needed when adding or removing dir delegs */
if (!S_ISDIR(inode->i_mode) || !nf->nf_mark)
return;
mark = &nf->nf_mark->nfm_mark;
/*
* The mark is shared by every nfsd_file on this inode, so concurrent
* delegation add/remove on the same directory can recalc it in
* parallel. Serialize the read of the lease state and the update of
* the mark so that a recalc working from a stale snapshot of the
* lease list can't clobber a concurrent recalc's update.
*/
mutex_lock(&nf->nf_mark->nfm_recalc_mutex);
/* Set up notifications for any ignored delegation events */
lease_mask = inode_lease_ignore_mask(inode);
if (lease_mask & FL_IGN_DIR_CREATE)
set |= FS_CREATE | FS_MOVED_TO;
else
clear |= FS_CREATE | FS_MOVED_TO;
if (lease_mask & FL_IGN_DIR_DELETE)
set |= FS_DELETE | FS_MOVED_FROM;
else
clear |= FS_DELETE | FS_MOVED_FROM;
if (lease_mask & FL_IGN_DIR_RENAME)
set |= FS_RENAME;
else
clear |= FS_RENAME;
fsnotify_modify_mark_mask(mark, set, clear);
mutex_unlock(&nf->nf_mark->nfm_recalc_mutex);
}
+3
View File
@@ -26,6 +26,8 @@
struct nfsd_file_mark {
struct fsnotify_mark nfm_mark;
refcount_t nfm_ref;
/* serializes nfsd_fsnotify_recalc_mask() against itself */
struct mutex nfm_recalc_mutex;
};
/*
@@ -86,4 +88,5 @@ __be32 nfsd_file_acquire_local(struct net *net, struct svc_cred *cred,
__be32 nfsd_file_acquire_dir(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct nfsd_file **pnf);
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
void nfsd_fsnotify_recalc_mask(struct nfsd_file *nf);
#endif /* _FS_NFSD_FILECACHE_H */
+13 -7
View File
@@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
struct ff_idmap uid;
struct ff_idmap gid;
fh_len = 4 + fl->fh.size;
fh_len = 4 + xdr_align_size(fl->fh.size);
uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid));
gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid));
/* 8 + len for recording the length, name, and padding */
ds_len = 20 + sizeof(stateid_opaque_t) + 4 + fh_len +
8 + uid.len + 8 + gid.len;
/* data server entry: deviceid + efficiency + stateid + fh list +
* user + group + flags + stats_collect_hint
*/
ds_len = 16 + 4 + 4 + sizeof(stateid_opaque_t) + 4 + fh_len +
4 + xdr_align_size(uid.len) +
4 + xdr_align_size(gid.len) +
4 + 4;
/* mirror: ds_count + ds */
mirror_len = 4 + ds_len;
/* The layout segment */
len = 20 + mirror_len;
/* stripe_unit + mirror_count + mirror */
len = 12 + mirror_len;
p = xdr_reserve_space(xdr, sizeof(__be32) + len);
if (!p)
@@ -94,7 +99,8 @@ nfsd4_ff_encode_getdeviceinfo(struct xdr_stream *xdr,
}
/* len + padding for two strings */
addr_len = 16 + da->netaddr.netid_len + da->netaddr.addr_len;
addr_len = 8 + xdr_align_size(da->netaddr.netid_len) +
xdr_align_size(da->netaddr.addr_len);
ver_len = 20;
len = 4 + ver_len + 4 + addr_len;
+7 -1
View File
@@ -97,11 +97,15 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
}
nfsd_file_get(localio);
again:
rcu_read_lock();
new = unrcu_pointer(cmpxchg(pnf, NULL, RCU_INITIALIZER(localio)));
if (new) {
/* Some other thread installed an nfsd_file */
if (nfsd_file_get(new) == NULL)
if (nfsd_file_get(new) == NULL) {
rcu_read_unlock();
goto again;
}
rcu_read_unlock();
/*
* Drop the ref we were going to install (both file and
* net) and the one we were going to return (only file).
@@ -110,6 +114,8 @@ nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
nfsd_net_put(net);
nfsd_file_put(localio);
localio = new;
} else {
rcu_read_unlock();
}
} else
nfsd_net_put(net);
+4 -2
View File
@@ -92,6 +92,7 @@ nlm_fclose(struct file *filp)
}
static const struct nlmsvc_binding nfsd_nlm_ops = {
.owner = THIS_MODULE,
.fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */
};
@@ -100,11 +101,12 @@ void
nfsd_lockd_init(void)
{
dprintk("nfsd: initializing lockd\n");
nlmsvc_ops = &nfsd_nlm_ops;
rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
}
void
nfsd_lockd_shutdown(void)
{
nlmsvc_ops = NULL;
RCU_INIT_POINTER(nlmsvc_ops, NULL);
synchronize_rcu();
}

Some files were not shown because too many files have changed in this diff Show More