aboutsummaryrefslogtreecommitdiff
path: root/fs/lockd
diff options
context:
space:
mode:
Diffstat (limited to 'fs/lockd')
-rw-r--r--fs/lockd/Makefile6
-rw-r--r--fs/lockd/clnt4xdr.c599
-rw-r--r--fs/lockd/clntlock.c95
-rw-r--r--fs/lockd/clntproc.c276
-rw-r--r--fs/lockd/clntxdr.c621
-rw-r--r--fs/lockd/grace.c65
-rw-r--r--fs/lockd/host.c778
-rw-r--r--fs/lockd/mon.c629
-rw-r--r--fs/lockd/netns.h23
-rw-r--r--fs/lockd/svc.c502
-rw-r--r--fs/lockd/svc4proc.c83
-rw-r--r--fs/lockd/svclock.c214
-rw-r--r--fs/lockd/svcproc.c96
-rw-r--r--fs/lockd/svcshare.c3
-rw-r--r--fs/lockd/svcsubs.c129
-rw-r--r--fs/lockd/xdr.c297
-rw-r--r--fs/lockd/xdr4.c263
17 files changed, 3120 insertions, 1559 deletions
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile
index 7725a0a9a55..ca58d64374c 100644
--- a/fs/lockd/Makefile
+++ b/fs/lockd/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_LOCKD) += lockd.o
-lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
- svcproc.o svcsubs.o mon.o xdr.o
-lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
+lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
+ svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o
+lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-objs := $(lockd-objs-y)
diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
new file mode 100644
index 00000000000..d3e40db2893
--- /dev/null
+++ b/fs/lockd/clnt4xdr.c
@@ -0,0 +1,599 @@
+/*
+ * linux/fs/lockd/clnt4xdr.c
+ *
+ * XDR functions to encode/decode NLM version 4 RPC arguments and results.
+ *
+ * NLM client-side only.
+ *
+ * Copyright (C) 2010, Oracle. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+
+#include <uapi/linux/nfs3.h>
+
+#define NLMDBG_FACILITY NLMDBG_XDR
+
+#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
+# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
+#endif
+
+#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
+# error "NLM host name cannot be larger than NLM's maximum string length!"
+#endif
+
+/*
+ * Declare the space requirements for NLM arguments and replies as
+ * number of 32bit-words
+ */
+#define NLM4_void_sz (0)
+#define NLM4_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
+#define NLM4_caller_sz (1+(NLMCLNT_OHSIZE>>2))
+#define NLM4_owner_sz (1+(NLMCLNT_OHSIZE>>2))
+#define NLM4_fhandle_sz (1+(NFS3_FHSIZE>>2))
+#define NLM4_lock_sz (5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz)
+#define NLM4_holder_sz (6+NLM4_owner_sz)
+
+#define NLM4_testargs_sz (NLM4_cookie_sz+1+NLM4_lock_sz)
+#define NLM4_lockargs_sz (NLM4_cookie_sz+4+NLM4_lock_sz)
+#define NLM4_cancargs_sz (NLM4_cookie_sz+2+NLM4_lock_sz)
+#define NLM4_unlockargs_sz (NLM4_cookie_sz+NLM4_lock_sz)
+
+#define NLM4_testres_sz (NLM4_cookie_sz+1+NLM4_holder_sz)
+#define NLM4_res_sz (NLM4_cookie_sz+1)
+#define NLM4_norep_sz (0)
+
+
+static s64 loff_t_to_s64(loff_t offset)
+{
+ s64 res;
+
+ if (offset >= NLM4_OFFSET_MAX)
+ res = NLM4_OFFSET_MAX;
+ else if (offset <= -NLM4_OFFSET_MAX)
+ res = -NLM4_OFFSET_MAX;
+ else
+ res = offset;
+ return res;
+}
+
+static void nlm4_compute_offsets(const struct nlm_lock *lock,
+ u64 *l_offset, u64 *l_len)
+{
+ const struct file_lock *fl = &lock->fl;
+
+ *l_offset = loff_t_to_s64(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ *l_len = 0;
+ else
+ *l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
+}
+
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+ dprintk("lockd: %s prematurely hit the end of our receive buffer. "
+ "Remaining buffer length is %tu words.\n",
+ func, xdr->end - xdr->p);
+}
+
+
+/*
+ * Encode/decode NLMv4 basic data types
+ *
+ * Basic NLMv4 data types are defined in Appendix II, section 6.1.4
+ * of RFC 1813: "NFS Version 3 Protocol Specification" and in Chapter
+ * 10 of X/Open's "Protocols for Interworking: XNFS, Version 3W".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions. For run-time efficiency, some data types are encoded
+ * or decoded inline.
+ */
+
+static void encode_bool(struct xdr_stream *xdr, const int value)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4);
+ *p = value ? xdr_one : xdr_zero;
+}
+
+static void encode_int32(struct xdr_stream *xdr, const s32 value)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4);
+ *p = cpu_to_be32(value);
+}
+
+/*
+ * typedef opaque netobj<MAXNETOBJ_SZ>
+ */
+static void encode_netobj(struct xdr_stream *xdr,
+ const u8 *data, const unsigned int length)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4 + length);
+ xdr_encode_opaque(p, data, length);
+}
+
+static int decode_netobj(struct xdr_stream *xdr,
+ struct xdr_netobj *obj)
+{
+ u32 length;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ length = be32_to_cpup(p++);
+ if (unlikely(length > XDR_MAX_NETOBJ))
+ goto out_size;
+ obj->len = length;
+ obj->data = (u8 *)p;
+ return 0;
+out_size:
+ dprintk("NFS: returned netobj was too long: %u\n", length);
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * netobj cookie;
+ */
+static void encode_cookie(struct xdr_stream *xdr,
+ const struct nlm_cookie *cookie)
+{
+ encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
+}
+
+static int decode_cookie(struct xdr_stream *xdr,
+ struct nlm_cookie *cookie)
+{
+ u32 length;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ length = be32_to_cpup(p++);
+ /* apparently HPUX can return empty cookies */
+ if (length == 0)
+ goto out_hpux;
+ if (length > NLM_MAXCOOKIELEN)
+ goto out_size;
+ p = xdr_inline_decode(xdr, length);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ cookie->len = length;
+ memcpy(cookie->data, p, length);
+ return 0;
+out_hpux:
+ cookie->len = 4;
+ memset(cookie->data, 0, 4);
+ return 0;
+out_size:
+ dprintk("NFS: returned cookie was too long: %u\n", length);
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * netobj fh;
+ */
+static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+{
+ encode_netobj(xdr, (u8 *)&fh->data, fh->size);
+}
+
+/*
+ * enum nlm4_stats {
+ * NLM4_GRANTED = 0,
+ * NLM4_DENIED = 1,
+ * NLM4_DENIED_NOLOCKS = 2,
+ * NLM4_BLOCKED = 3,
+ * NLM4_DENIED_GRACE_PERIOD = 4,
+ * NLM4_DEADLCK = 5,
+ * NLM4_ROFS = 6,
+ * NLM4_STALE_FH = 7,
+ * NLM4_FBIG = 8,
+ * NLM4_FAILED = 9
+ * };
+ *
+ * struct nlm4_stat {
+ * nlm4_stats stat;
+ * };
+ *
+ * NB: we don't swap bytes for the NLM status values. The upper
+ * layers deal directly with the status value in network byte
+ * order.
+ */
+static void encode_nlm4_stat(struct xdr_stream *xdr,
+ const __be32 stat)
+{
+ __be32 *p;
+
+ BUG_ON(be32_to_cpu(stat) > NLM_FAILED);
+ p = xdr_reserve_space(xdr, 4);
+ *p = stat;
+}
+
+static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat)
+{
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ if (unlikely(ntohl(*p) > ntohl(nlm4_failed)))
+ goto out_bad_xdr;
+ *stat = *p;
+ return 0;
+out_bad_xdr:
+ dprintk("%s: server returned invalid nlm4_stats value: %u\n",
+ __func__, be32_to_cpup(p));
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * struct nlm4_holder {
+ * bool exclusive;
+ * int32 svid;
+ * netobj oh;
+ * uint64 l_offset;
+ * uint64 l_len;
+ * };
+ */
+static void encode_nlm4_holder(struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ const struct nlm_lock *lock = &result->lock;
+ u64 l_offset, l_len;
+ __be32 *p;
+
+ encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
+ encode_int32(xdr, lock->svid);
+ encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+ p = xdr_reserve_space(xdr, 4 + 4);
+ nlm4_compute_offsets(lock, &l_offset, &l_len);
+ p = xdr_encode_hyper(p, l_offset);
+ xdr_encode_hyper(p, l_len);
+}
+
+static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
+{
+ struct nlm_lock *lock = &result->lock;
+ struct file_lock *fl = &lock->fl;
+ u64 l_offset, l_len;
+ u32 exclusive;
+ int error;
+ __be32 *p;
+ s32 end;
+
+ memset(lock, 0, sizeof(*lock));
+ locks_init_lock(fl);
+
+ p = xdr_inline_decode(xdr, 4 + 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ exclusive = be32_to_cpup(p++);
+ lock->svid = be32_to_cpup(p);
+ fl->fl_pid = (pid_t)lock->svid;
+
+ error = decode_netobj(xdr, &lock->oh);
+ if (unlikely(error))
+ goto out;
+
+ p = xdr_inline_decode(xdr, 8 + 8);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
+ p = xdr_decode_hyper(p, &l_offset);
+ xdr_decode_hyper(p, &l_len);
+ end = l_offset + l_len - 1;
+
+ fl->fl_start = (loff_t)l_offset;
+ if (l_len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = (loff_t)end;
+ error = 0;
+out:
+ return error;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * string caller_name<LM_MAXSTRLEN>;
+ */
+static void encode_caller_name(struct xdr_stream *xdr, const char *name)
+{
+ /* NB: client-side does not set lock->len */
+ u32 length = strlen(name);
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4 + length);
+ xdr_encode_opaque(p, name, length);
+}
+
+/*
+ * struct nlm4_lock {
+ * string caller_name<LM_MAXSTRLEN>;
+ * netobj fh;
+ * netobj oh;
+ * int32 svid;
+ * uint64 l_offset;
+ * uint64 l_len;
+ * };
+ */
+static void encode_nlm4_lock(struct xdr_stream *xdr,
+ const struct nlm_lock *lock)
+{
+ u64 l_offset, l_len;
+ __be32 *p;
+
+ encode_caller_name(xdr, lock->caller);
+ encode_fh(xdr, &lock->fh);
+ encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+ p = xdr_reserve_space(xdr, 4 + 8 + 8);
+ *p++ = cpu_to_be32(lock->svid);
+
+ nlm4_compute_offsets(lock, &l_offset, &l_len);
+ p = xdr_encode_hyper(p, l_offset);
+ xdr_encode_hyper(p, l_len);
+}
+
+
+/*
+ * NLMv4 XDR encode functions
+ *
+ * NLMv4 argument types are defined in Appendix II of RFC 1813:
+ * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ * struct nlm4_testargs {
+ * netobj cookie;
+ * bool exclusive;
+ * struct nlm4_lock alock;
+ * };
+ */
+static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ * struct nlm4_lockargs {
+ * netobj cookie;
+ * bool block;
+ * bool exclusive;
+ * struct nlm4_lock alock;
+ * bool reclaim;
+ * int state;
+ * };
+ */
+static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, args->block);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm4_lock(xdr, lock);
+ encode_bool(xdr, args->reclaim);
+ encode_int32(xdr, args->state);
+}
+
+/*
+ * struct nlm4_cancargs {
+ * netobj cookie;
+ * bool block;
+ * bool exclusive;
+ * struct nlm4_lock alock;
+ * };
+ */
+static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, args->block);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ * struct nlm4_unlockargs {
+ * netobj cookie;
+ * struct nlm4_lock alock;
+ * };
+ */
+static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ * struct nlm4_res {
+ * netobj cookie;
+ * nlm4_stat stat;
+ * };
+ */
+static void nlm4_xdr_enc_res(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ encode_cookie(xdr, &result->cookie);
+ encode_nlm4_stat(xdr, result->status);
+}
+
+/*
+ * union nlm4_testrply switch (nlm4_stats stat) {
+ * case NLM4_DENIED:
+ * struct nlm4_holder holder;
+ * default:
+ * void;
+ * };
+ *
+ * struct nlm4_testres {
+ * netobj cookie;
+ * nlm4_testrply test_stat;
+ * };
+ */
+static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ encode_cookie(xdr, &result->cookie);
+ encode_nlm4_stat(xdr, result->status);
+ if (result->status == nlm_lck_denied)
+ encode_nlm4_holder(xdr, result);
+}
+
+
+/*
+ * NLMv4 XDR decode functions
+ *
+ * NLMv4 argument types are defined in Appendix II of RFC 1813:
+ * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ * union nlm4_testrply switch (nlm4_stats stat) {
+ * case NLM4_DENIED:
+ * struct nlm4_holder holder;
+ * default:
+ * void;
+ * };
+ *
+ * struct nlm4_testres {
+ * netobj cookie;
+ * nlm4_testrply test_stat;
+ * };
+ */
+static int decode_nlm4_testrply(struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_nlm4_stat(xdr, &result->status);
+ if (unlikely(error))
+ goto out;
+ if (result->status == nlm_lck_denied)
+ error = decode_nlm4_holder(xdr, result);
+out:
+ return error;
+}
+
+static int nlm4_xdr_dec_testres(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_cookie(xdr, &result->cookie);
+ if (unlikely(error))
+ goto out;
+ error = decode_nlm4_testrply(xdr, result);
+out:
+ return error;
+}
+
+/*
+ * struct nlm4_res {
+ * netobj cookie;
+ * nlm4_stat stat;
+ * };
+ */
+static int nlm4_xdr_dec_res(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_cookie(xdr, &result->cookie);
+ if (unlikely(error))
+ goto out;
+ error = decode_nlm4_stat(xdr, &result->status);
+out:
+ return error;
+}
+
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm4_xdr_dec_norep NULL
+
+#define PROC(proc, argtype, restype) \
+[NLMPROC_##proc] = { \
+ .p_proc = NLMPROC_##proc, \
+ .p_encode = (kxdreproc_t)nlm4_xdr_enc_##argtype, \
+ .p_decode = (kxdrdproc_t)nlm4_xdr_dec_##restype, \
+ .p_arglen = NLM4_##argtype##_sz, \
+ .p_replen = NLM4_##restype##_sz, \
+ .p_statidx = NLMPROC_##proc, \
+ .p_name = #proc, \
+ }
+
+static struct rpc_procinfo nlm4_procedures[] = {
+ PROC(TEST, testargs, testres),
+ PROC(LOCK, lockargs, res),
+ PROC(CANCEL, cancargs, res),
+ PROC(UNLOCK, unlockargs, res),
+ PROC(GRANTED, testargs, res),
+ PROC(TEST_MSG, testargs, norep),
+ PROC(LOCK_MSG, lockargs, norep),
+ PROC(CANCEL_MSG, cancargs, norep),
+ PROC(UNLOCK_MSG, unlockargs, norep),
+ PROC(GRANTED_MSG, testargs, norep),
+ PROC(TEST_RES, testres, norep),
+ PROC(LOCK_RES, res, norep),
+ PROC(CANCEL_RES, res, norep),
+ PROC(UNLOCK_RES, res, norep),
+ PROC(GRANTED_RES, res, norep),
+};
+
+const struct rpc_version nlm_version4 = {
+ .number = 4,
+ .nrprocs = ARRAY_SIZE(nlm4_procedures),
+ .procs = nlm4_procedures,
+};
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
index 0b45fd3a4bf..41e491b8e5d 100644
--- a/fs/lockd/clntlock.c
+++ b/fs/lockd/clntlock.c
@@ -8,12 +8,13 @@
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/time.h>
#include <linux/nfs_fs.h>
-#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
-#include <linux/smp_lock.h>
+#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
@@ -40,6 +41,7 @@ struct nlm_wait {
};
static LIST_HEAD(nlm_blocked);
+static DEFINE_SPINLOCK(nlm_blocked_lock);
/**
* nlmclnt_init - Set up per-NFS mount point lockd data structures
@@ -54,20 +56,25 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
int status;
- status = lockd_up(nlm_init->protocol);
+ status = lockd_up(nlm_init->net);
if (status < 0)
return ERR_PTR(status);
- host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address,
+ host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
nlm_init->protocol, nlm_version,
- nlm_init->hostname,
- strlen(nlm_init->hostname));
- if (host == NULL) {
- lockd_down();
- return ERR_PTR(-ENOLCK);
- }
+ nlm_init->hostname, nlm_init->noresvport,
+ nlm_init->net);
+ if (host == NULL)
+ goto out_nohost;
+ if (host->h_rpcclnt == NULL && nlm_bind_host(host) == NULL)
+ goto out_nobind;
return host;
+out_nobind:
+ nlmclnt_release_host(host);
+out_nohost:
+ lockd_down(nlm_init->net);
+ return ERR_PTR(-ENOLCK);
}
EXPORT_SYMBOL_GPL(nlmclnt_init);
@@ -78,8 +85,10 @@ EXPORT_SYMBOL_GPL(nlmclnt_init);
*/
void nlmclnt_done(struct nlm_host *host)
{
- nlm_release_host(host);
- lockd_down();
+ struct net *net = host->net;
+
+ nlmclnt_release_host(host);
+ lockd_down(net);
}
EXPORT_SYMBOL_GPL(nlmclnt_done);
@@ -96,7 +105,10 @@ struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *
block->b_lock = fl;
init_waitqueue_head(&block->b_wait);
block->b_status = nlm_lck_blocked;
+
+ spin_lock(&nlm_blocked_lock);
list_add(&block->b_list, &nlm_blocked);
+ spin_unlock(&nlm_blocked_lock);
}
return block;
}
@@ -105,7 +117,9 @@ void nlmclnt_finish_block(struct nlm_wait *block)
{
if (block == NULL)
return;
+ spin_lock(&nlm_blocked_lock);
list_del(&block->b_list);
+ spin_unlock(&nlm_blocked_lock);
kfree(block);
}
@@ -135,6 +149,9 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
timeout);
if (ret < 0)
return -ERESTARTSYS;
+ /* Reset the lock status after a server reboot so we resend */
+ if (block->b_status == nlm_lck_denied_grace_period)
+ block->b_status = nlm_lck_blocked;
req->a_res.status = block->b_status;
return 0;
}
@@ -142,7 +159,7 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
/*
* The server lockd has called us back to tell us the lock was granted
*/
-__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
+__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
{
const struct file_lock *fl = &lock->fl;
const struct nfs_fh *fh = &lock->fh;
@@ -153,6 +170,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock
* Look up blocked request based on arguments.
* Warning: must not use cookie to match it!
*/
+ spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
struct file_lock *fl_blocked = block->b_lock;
@@ -166,9 +184,9 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock
*/
if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
continue;
- if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
+ if (!rpc_cmp_addr(nlm_addr(block->b_host), addr))
continue;
- if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0)
+ if (nfs_compare_fh(NFS_FH(file_inode(fl_blocked->fl_file)) ,fh) != 0)
continue;
/* Alright, we found a lock. Set the return status
* and wake up the caller
@@ -177,6 +195,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock
wake_up(&block->b_wait);
res = nlm_granted;
}
+ spin_unlock(&nlm_blocked_lock);
return res;
}
@@ -192,11 +211,15 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock
void
nlmclnt_recovery(struct nlm_host *host)
{
+ struct task_struct *task;
+
if (!host->h_reclaiming++) {
nlm_get_host(host);
- __module_get(THIS_MODULE);
- if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0)
- module_put(THIS_MODULE);
+ task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
+ if (IS_ERR(task))
+ printk(KERN_ERR "lockd: unable to spawn reclaimer "
+ "thread. Locks for %s won't be reclaimed! "
+ "(%ld)\n", host->h_name, PTR_ERR(task));
}
}
@@ -205,18 +228,23 @@ reclaimer(void *ptr)
{
struct nlm_host *host = (struct nlm_host *) ptr;
struct nlm_wait *block;
+ struct nlm_rqst *req;
struct file_lock *fl, *next;
u32 nsmstate;
+ struct net *net = host->net;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ printk(KERN_ERR "lockd: reclaimer unable to alloc memory."
+ " Locks for %s won't be reclaimed!\n",
+ host->h_name);
+ return 0;
+ }
- daemonize("%s-reclaim", host->h_name);
allow_signal(SIGKILL);
down_write(&host->h_rwsem);
-
- /* This one ensures that our parent doesn't terminate while the
- * reclaim is in progress */
- lock_kernel();
- lockd_up(0); /* note: this cannot fail as lockd is already running */
+ lockd_up(net); /* note: this cannot fail as lockd is already running */
dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
@@ -234,10 +262,15 @@ restart:
list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
list_del_init(&fl->fl_u.nfs_fl.list);
- /* Why are we leaking memory here? --okir */
+ /*
+ * sending this thread a SIGKILL will result in any unreclaimed
+ * locks being removed from the h_granted list. This means that
+ * the kernel will not attempt to reclaim them again if a new
+ * reclaimer thread is spawned for this host.
+ */
if (signalled())
continue;
- if (nlmclnt_reclaim(host, fl) != 0)
+ if (nlmclnt_reclaim(host, fl, req) != 0)
continue;
list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted);
if (host->h_nsmstate != nsmstate) {
@@ -251,16 +284,18 @@ restart:
dprintk("NLM: done reclaiming locks for host %s\n", host->h_name);
/* Now, wake up all processes that sleep on a blocked lock */
+ spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
if (block->b_host == host) {
block->b_status = nlm_lck_denied_grace_period;
wake_up(&block->b_wait);
}
}
+ spin_unlock(&nlm_blocked_lock);
/* Release host handle after use */
- nlm_release_host(host);
- lockd_down();
- unlock_kernel();
- module_put_and_exit(0);
+ nlmclnt_release_host(host);
+ lockd_down(net);
+ kfree(req);
+ return 0;
}
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index b6b74a60e1e..acd39471634 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -16,7 +17,6 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
#define NLMCLNT_GRACE_WAIT (5*HZ)
@@ -58,7 +58,7 @@ static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
return;
list_del(&lockowner->list);
spin_unlock(&lockowner->host->h_lock);
- nlm_release_host(lockowner->host);
+ nlmclnt_release_host(lockowner->host);
kfree(lockowner);
}
@@ -125,15 +125,15 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_args *argp = &req->a_args;
struct nlm_lock *lock = &argp->lock;
+ char *nodename = req->a_host->h_rpcclnt->cl_nodename;
nlmclnt_next_cookie(&argp->cookie);
- argp->state = nsm_local_state;
- memcpy(&lock->fh, NFS_FH(fl->fl_file->f_path.dentry->d_inode), sizeof(struct nfs_fh));
- lock->caller = utsname()->nodename;
+ memcpy(&lock->fh, NFS_FH(file_inode(fl->fl_file)), sizeof(struct nfs_fh));
+ lock->caller = nodename;
lock->oh.data = req->a_owner;
lock->oh.len = snprintf(req->a_owner, sizeof(req->a_owner), "%u@%s",
(unsigned int)fl->fl_u.nfs_fl.owner->pid,
- utsname()->nodename);
+ nodename);
lock->svid = fl->fl_u.nfs_fl.owner->pid;
lock->fl.fl_start = fl->fl_start;
lock->fl.fl_end = fl->fl_end;
@@ -142,7 +142,7 @@ static void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
static void nlmclnt_release_lockargs(struct nlm_rqst *req)
{
- BUG_ON(req->a_args.lock.fl.fl_ops != NULL);
+ WARN_ON_ONCE(req->a_args.lock.fl.fl_ops != NULL);
}
/**
@@ -155,35 +155,21 @@ static void nlmclnt_release_lockargs(struct nlm_rqst *req)
int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
{
struct nlm_rqst *call;
- sigset_t oldset;
- unsigned long flags;
int status;
- nlm_get_host(host);
call = nlm_alloc_call(host);
if (call == NULL)
return -ENOMEM;
nlmclnt_locks_init_private(fl, host);
+ if (!fl->fl_u.nfs_fl.owner) {
+ /* lockowner allocation has failed */
+ nlmclnt_release_call(call);
+ return -ENOMEM;
+ }
/* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
- /* Keep the old signal mask */
- spin_lock_irqsave(&current->sighand->siglock, flags);
- oldset = current->blocked;
-
- /* If we're cleaning up locks because the process is exiting,
- * perform the RPC call asynchronously. */
- if ((IS_SETLK(cmd) || IS_SETLKW(cmd))
- && fl->fl_type == F_UNLCK
- && (current->flags & PF_EXITING)) {
- sigfillset(&current->blocked); /* Mask all signals */
- recalc_sigpending();
-
- call->a_flags = RPC_TASK_ASYNC;
- }
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
if (fl->fl_type != F_UNLCK) {
call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
@@ -194,15 +180,9 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
status = nlmclnt_test(call, fl);
else
status = -EINVAL;
-
fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
dprintk("lockd: clnt proc returns %d\n", status);
return status;
}
@@ -210,9 +190,6 @@ EXPORT_SYMBOL_GPL(nlmclnt_proc);
/*
* Allocate an NLM RPC call struct
- *
- * Note: the caller must hold a reference to host. In case of failure,
- * this reference will be released.
*/
struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
{
@@ -221,9 +198,10 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
for(;;) {
call = kzalloc(sizeof(*call), GFP_KERNEL);
if (call != NULL) {
+ atomic_set(&call->a_count, 1);
locks_init_lock(&call->a_args.lock.fl);
locks_init_lock(&call->a_res.lock.fl);
- call->a_host = host;
+ call->a_host = nlm_get_host(host);
return call;
}
if (signalled())
@@ -231,20 +209,21 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ);
}
- nlm_release_host(host);
return NULL;
}
-void nlm_release_call(struct nlm_rqst *call)
+void nlmclnt_release_call(struct nlm_rqst *call)
{
- nlm_release_host(call->a_host);
+ if (!atomic_dec_and_test(&call->a_count))
+ return;
+ nlmclnt_release_host(call->a_host);
nlmclnt_release_lockargs(call);
kfree(call);
}
static void nlmclnt_rpc_release(void *data)
{
- nlm_release_call(data);
+ nlmclnt_release_call(data);
}
static int nlm_wait_on_grace(wait_queue_head_t *queue)
@@ -267,7 +246,7 @@ static int nlm_wait_on_grace(wait_queue_head_t *queue)
* Generic NLM call
*/
static int
-nlmclnt_call(struct nlm_rqst *req, u32 proc)
+nlmclnt_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
@@ -276,6 +255,7 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
struct rpc_message msg = {
.rpc_argp = argp,
.rpc_resp = resp,
+ .rpc_cred = cred,
};
int status;
@@ -323,7 +303,8 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
/* We appear to be out of the grace period */
wake_up_all(&host->h_gracewait);
}
- dprintk("lockd: server returns status %d\n", resp->status);
+ dprintk("lockd: server returns status %d\n",
+ ntohl(resp->status));
return 0; /* Okay, call complete */
}
@@ -343,10 +324,16 @@ in_grace_period:
/*
* Generic NLM call, async version.
*/
-static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
+static struct rpc_task *__nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
{
struct nlm_host *host = req->a_host;
struct rpc_clnt *clnt;
+ struct rpc_task_setup task_setup_data = {
+ .rpc_message = msg,
+ .callback_ops = tk_ops,
+ .callback_data = req,
+ .flags = RPC_TASK_ASYNC,
+ };
dprintk("lockd: call procedure %d on %s (async)\n",
(int)proc, host->h_name);
@@ -356,21 +343,36 @@ static int __nlm_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *
if (clnt == NULL)
goto out_err;
msg->rpc_proc = &clnt->cl_procinfo[proc];
+ task_setup_data.rpc_client = clnt;
/* bootstrap and kick off the async RPC call */
- return rpc_call_async(clnt, msg, RPC_TASK_ASYNC, tk_ops, req);
+ return rpc_run_task(&task_setup_data);
out_err:
tk_ops->rpc_release(req);
- return -ENOLCK;
+ return ERR_PTR(-ENOLCK);
+}
+
+static int nlm_do_async_call(struct nlm_rqst *req, u32 proc, struct rpc_message *msg, const struct rpc_call_ops *tk_ops)
+{
+ struct rpc_task *task;
+
+ task = __nlm_async_call(req, proc, msg, tk_ops);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ rpc_put_task(task);
+ return 0;
}
+/*
+ * NLM asynchronous call.
+ */
int nlm_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
{
struct rpc_message msg = {
.rpc_argp = &req->a_args,
.rpc_resp = &req->a_res,
};
- return __nlm_async_call(req, proc, &msg, tk_ops);
+ return nlm_do_async_call(req, proc, &msg, tk_ops);
}
int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
@@ -378,7 +380,33 @@ int nlm_async_reply(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *t
struct rpc_message msg = {
.rpc_argp = &req->a_res,
};
- return __nlm_async_call(req, proc, &msg, tk_ops);
+ return nlm_do_async_call(req, proc, &msg, tk_ops);
+}
+
+/*
+ * NLM client asynchronous call.
+ *
+ * Note that although the calls are asynchronous, and are therefore
+ * guaranteed to complete, we still always attempt to wait for
+ * completion in order to be able to correctly track the lock
+ * state.
+ */
+static int nlmclnt_async_call(struct rpc_cred *cred, struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops)
+{
+ struct rpc_message msg = {
+ .rpc_argp = &req->a_args,
+ .rpc_resp = &req->a_res,
+ .rpc_cred = cred,
+ };
+ struct rpc_task *task;
+ int err;
+
+ task = __nlm_async_call(req, proc, &msg, tk_ops);
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ err = rpc_wait_for_completion_task(task);
+ rpc_put_task(task);
+ return err;
}
/*
@@ -389,7 +417,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
{
int status;
- status = nlmclnt_call(req, NLMPROC_TEST);
+ status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_TEST);
if (status < 0)
goto out;
@@ -402,7 +430,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
* Report the conflicting lock back to the application.
*/
fl->fl_start = req->a_res.lock.fl.fl_start;
- fl->fl_end = req->a_res.lock.fl.fl_start;
+ fl->fl_end = req->a_res.lock.fl.fl_end;
fl->fl_type = req->a_res.lock.fl.fl_type;
fl->fl_pid = 0;
break;
@@ -410,31 +438,34 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
status = nlm_stat_to_errno(req->a_res.status);
}
out:
- nlm_release_call(req);
+ nlmclnt_release_call(req);
return status;
}
static void nlmclnt_locks_copy_lock(struct file_lock *new, struct file_lock *fl)
{
+ spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
new->fl_u.nfs_fl.state = fl->fl_u.nfs_fl.state;
new->fl_u.nfs_fl.owner = nlm_get_lockowner(fl->fl_u.nfs_fl.owner);
list_add_tail(&new->fl_u.nfs_fl.list, &fl->fl_u.nfs_fl.owner->host->h_granted);
+ spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
}
static void nlmclnt_locks_release_private(struct file_lock *fl)
{
+ spin_lock(&fl->fl_u.nfs_fl.owner->host->h_lock);
list_del(&fl->fl_u.nfs_fl.list);
+ spin_unlock(&fl->fl_u.nfs_fl.owner->host->h_lock);
nlm_put_lockowner(fl->fl_u.nfs_fl.owner);
}
-static struct file_lock_operations nlmclnt_lock_ops = {
+static const struct file_lock_operations nlmclnt_lock_ops = {
.fl_copy_lock = nlmclnt_locks_copy_lock,
.fl_release_private = nlmclnt_locks_release_private,
};
static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host)
{
- BUG_ON(fl->fl_ops != NULL);
fl->fl_u.nfs_fl.state = 0;
fl->fl_u.nfs_fl.owner = nlm_find_lockowner(host, fl->fl_owner);
INIT_LIST_HEAD(&fl->fl_u.nfs_fl.list);
@@ -480,31 +511,36 @@ static int do_vfs_lock(struct file_lock *fl)
static int
nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
{
+ struct rpc_cred *cred = nfs_file_cred(fl->fl_file);
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
struct nlm_wait *block = NULL;
unsigned char fl_flags = fl->fl_flags;
+ unsigned char fl_type;
int status = -ENOLCK;
- if (nsm_monitor(host) < 0) {
- printk(KERN_NOTICE "lockd: failed to monitor %s\n",
- host->h_name);
+ if (nsm_monitor(host) < 0)
goto out;
- }
+ req->a_args.state = nsm_local_state;
+
fl->fl_flags |= FL_ACCESS;
status = do_vfs_lock(fl);
+ fl->fl_flags = fl_flags;
if (status < 0)
goto out;
block = nlmclnt_prepare_block(host, fl);
again:
+ /*
+ * Initialise resp->status to a valid non-zero value,
+ * since 0 == nlm_lck_granted
+ */
+ resp->status = nlm_lck_blocked;
for(;;) {
/* Reboot protection */
fl->fl_u.nfs_fl.state = host->h_state;
- status = nlmclnt_call(req, NLMPROC_LOCK);
+ status = nlmclnt_call(cred, req, NLMPROC_LOCK);
if (status < 0)
- goto out_unblock;
- if (!req->a_args.block)
break;
/* Did a reclaimer thread notify us of a server reboot? */
if (resp->status == nlm_lck_denied_grace_period)
@@ -513,15 +549,22 @@ again:
break;
/* Wait on an NLM blocking lock */
status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
- /* if we were interrupted. Send a CANCEL request to the server
- * and exit
- */
if (status < 0)
- goto out_unblock;
+ break;
if (resp->status != nlm_lck_blocked)
break;
}
+ /* if we were interrupted while blocking, then cancel the lock request
+ * and exit
+ */
+ if (resp->status == nlm_lck_blocked) {
+ if (!req->a_args.block)
+ goto out_unlock;
+ if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
+ goto out_unblock;
+ }
+
if (resp->status == nlm_granted) {
down_read(&host->h_rwsem);
/* Check whether or not the server has rebooted */
@@ -530,20 +573,42 @@ again:
goto again;
}
/* Ensure the resulting lock will get added to granted list */
- fl->fl_flags = fl_flags | FL_SLEEP;
+ fl->fl_flags |= FL_SLEEP;
if (do_vfs_lock(fl) < 0)
- printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
+ printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
up_read(&host->h_rwsem);
+ fl->fl_flags = fl_flags;
+ status = 0;
}
- status = nlm_stat_to_errno(resp->status);
+ if (status < 0)
+ goto out_unlock;
+ /*
+ * EAGAIN doesn't make sense for sleeping locks, and in some
+ * cases NLM_LCK_DENIED is returned for a permanent error. So
+ * turn it into an ENOLCK.
+ */
+ if (resp->status == nlm_lck_denied && (fl_flags & FL_SLEEP))
+ status = -ENOLCK;
+ else
+ status = nlm_stat_to_errno(resp->status);
out_unblock:
nlmclnt_finish_block(block);
- /* Cancel the blocked request if it is still pending */
- if (resp->status == nlm_lck_blocked)
- nlmclnt_cancel(host, req->a_args.block, fl);
out:
- nlm_release_call(req);
+ nlmclnt_release_call(req);
+ return status;
+out_unlock:
+ /* Fatal error: ensure that we remove the lock altogether */
+ dprintk("lockd: lock attempt ended in fatal error.\n"
+ " Attempting to unlock.\n");
+ nlmclnt_finish_block(block);
+ fl_type = fl->fl_type;
+ fl->fl_type = F_UNLCK;
+ down_read(&host->h_rwsem);
+ do_vfs_lock(fl);
+ up_read(&host->h_rwsem);
+ fl->fl_type = fl_type;
fl->fl_flags = fl_flags;
+ nlmclnt_async_call(cred, req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
return status;
}
@@ -551,24 +616,22 @@ out:
* RECLAIM: Try to reclaim a lock
*/
int
-nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
+nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl,
+ struct nlm_rqst *req)
{
- struct nlm_rqst reqst, *req;
int status;
- req = &reqst;
memset(req, 0, sizeof(*req));
locks_init_lock(&req->a_args.lock.fl);
locks_init_lock(&req->a_res.lock.fl);
req->a_host = host;
- req->a_flags = 0;
/* Set up the argument struct */
nlmclnt_setlockargs(req, fl);
req->a_args.reclaim = 1;
- if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
- && req->a_res.status == nlm_granted)
+ status = nlmclnt_call(nfs_file_cred(fl->fl_file), req, NLMPROC_LOCK);
+ if (status >= 0 && req->a_res.status == nlm_granted)
return 0;
printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
@@ -598,7 +661,8 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
{
struct nlm_host *host = req->a_host;
struct nlm_res *resp = &req->a_res;
- int status = 0;
+ int status;
+ unsigned char fl_flags = fl->fl_flags;
/*
* Note: the server is supposed to either grant us the unlock
@@ -607,16 +671,17 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
*/
fl->fl_flags |= FL_EXISTS;
down_read(&host->h_rwsem);
- if (do_vfs_lock(fl) == -ENOENT) {
- up_read(&host->h_rwsem);
+ status = do_vfs_lock(fl);
+ up_read(&host->h_rwsem);
+ fl->fl_flags = fl_flags;
+ if (status == -ENOENT) {
+ status = 0;
goto out;
}
- up_read(&host->h_rwsem);
- if (req->a_flags & RPC_TASK_ASYNC)
- return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
-
- status = nlmclnt_call(req, NLMPROC_UNLOCK);
+ atomic_inc(&req->a_count);
+ status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+ NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
if (status < 0)
goto out;
@@ -624,11 +689,12 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
goto out;
if (resp->status != nlm_lck_denied_nolocks)
- printk("lockd: unexpected unlock status: %d\n", resp->status);
+ printk("lockd: unexpected unlock status: %d\n",
+ ntohl(resp->status));
/* What to do now? I'm out of my depth... */
status = -ENOLCK;
out:
- nlm_release_call(req);
+ nlmclnt_release_call(req);
return status;
}
@@ -642,7 +708,13 @@ static void nlmclnt_unlock_callback(struct rpc_task *task, void *data)
if (task->tk_status < 0) {
dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
- goto retry_rebind;
+ switch (task->tk_status) {
+ case -EACCES:
+ case -EIO:
+ goto die;
+ default:
+ goto retry_rebind;
+ }
}
if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
rpc_delay(task, NLMCLNT_GRACE_WAIT);
@@ -671,18 +743,12 @@ static const struct rpc_call_ops nlmclnt_unlock_ops = {
static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl)
{
struct nlm_rqst *req;
- unsigned long flags;
- sigset_t oldset;
- int status;
+ int status;
- /* Block all signals while setting up call */
- spin_lock_irqsave(&current->sighand->siglock, flags);
- oldset = current->blocked;
- sigfillset(&current->blocked);
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
+ dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
+ " Attempting to cancel lock.\n");
- req = nlm_alloc_call(nlm_get_host(host));
+ req = nlm_alloc_call(host);
if (!req)
return -ENOMEM;
req->a_flags = RPC_TASK_ASYNC;
@@ -690,13 +756,12 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
nlmclnt_setlockargs(req, fl);
req->a_args.block = block;
- status = nlm_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops);
-
- spin_lock_irqsave(&current->sighand->siglock, flags);
- current->blocked = oldset;
- recalc_sigpending();
- spin_unlock_irqrestore(&current->sighand->siglock, flags);
-
+ atomic_inc(&req->a_count);
+ status = nlmclnt_async_call(nfs_file_cred(fl->fl_file), req,
+ NLMPROC_CANCEL, &nlmclnt_cancel_ops);
+ if (status == 0 && req->a_res.status == nlm_lck_denied)
+ status = -ENOLCK;
+ nlmclnt_release_call(req);
return status;
}
@@ -778,6 +843,7 @@ nlm_stat_to_errno(__be32 status)
return -ENOLCK;
#endif
}
- printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
+ printk(KERN_NOTICE "lockd: unexpected server status %d\n",
+ ntohl(status));
return -ENOLCK;
}
diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
new file mode 100644
index 00000000000..3e9f7874b97
--- /dev/null
+++ b/fs/lockd/clntxdr.c
@@ -0,0 +1,621 @@
+/*
+ * linux/fs/lockd/clntxdr.c
+ *
+ * XDR functions to encode/decode NLM version 3 RPC arguments and results.
+ * NLM version 3 is backwards compatible with NLM versions 1 and 2.
+ *
+ * NLM client-side only.
+ *
+ * Copyright (C) 2010, Oracle. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+
+#include <uapi/linux/nfs2.h>
+
+#define NLMDBG_FACILITY NLMDBG_XDR
+
+#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
+# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
+#endif
+
+/*
+ * Declare the space requirements for NLM arguments and replies as
+ * number of 32bit-words
+ */
+#define NLM_cookie_sz (1+(NLM_MAXCOOKIELEN>>2))
+#define NLM_caller_sz (1+(NLMCLNT_OHSIZE>>2))
+#define NLM_owner_sz (1+(NLMCLNT_OHSIZE>>2))
+#define NLM_fhandle_sz (1+(NFS2_FHSIZE>>2))
+#define NLM_lock_sz (3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz)
+#define NLM_holder_sz (4+NLM_owner_sz)
+
+#define NLM_testargs_sz (NLM_cookie_sz+1+NLM_lock_sz)
+#define NLM_lockargs_sz (NLM_cookie_sz+4+NLM_lock_sz)
+#define NLM_cancargs_sz (NLM_cookie_sz+2+NLM_lock_sz)
+#define NLM_unlockargs_sz (NLM_cookie_sz+NLM_lock_sz)
+
+#define NLM_testres_sz (NLM_cookie_sz+1+NLM_holder_sz)
+#define NLM_res_sz (NLM_cookie_sz+1)
+#define NLM_norep_sz (0)
+
+
+static s32 loff_t_to_s32(loff_t offset)
+{
+ s32 res;
+
+ if (offset >= NLM_OFFSET_MAX)
+ res = NLM_OFFSET_MAX;
+ else if (offset <= -NLM_OFFSET_MAX)
+ res = -NLM_OFFSET_MAX;
+ else
+ res = offset;
+ return res;
+}
+
+static void nlm_compute_offsets(const struct nlm_lock *lock,
+ u32 *l_offset, u32 *l_len)
+{
+ const struct file_lock *fl = &lock->fl;
+
+ *l_offset = loff_t_to_s32(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ *l_len = 0;
+ else
+ *l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
+}
+
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+ dprintk("lockd: %s prematurely hit the end of our receive buffer. "
+ "Remaining buffer length is %tu words.\n",
+ func, xdr->end - xdr->p);
+}
+
+
+/*
+ * Encode/decode NLMv3 basic data types
+ *
+ * Basic NLMv3 data types are not defined in an IETF standards
+ * document. X/Open has a description of these data types that
+ * is useful. See Chapter 10 of "Protocols for Interworking:
+ * XNFS, Version 3W".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions. For run-time efficiency, some data types are encoded
+ * or decoded inline.
+ */
+
+static void encode_bool(struct xdr_stream *xdr, const int value)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4);
+ *p = value ? xdr_one : xdr_zero;
+}
+
+static void encode_int32(struct xdr_stream *xdr, const s32 value)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4);
+ *p = cpu_to_be32(value);
+}
+
+/*
+ * typedef opaque netobj<MAXNETOBJ_SZ>
+ */
+static void encode_netobj(struct xdr_stream *xdr,
+ const u8 *data, const unsigned int length)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4 + length);
+ xdr_encode_opaque(p, data, length);
+}
+
+static int decode_netobj(struct xdr_stream *xdr,
+ struct xdr_netobj *obj)
+{
+ u32 length;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ length = be32_to_cpup(p++);
+ if (unlikely(length > XDR_MAX_NETOBJ))
+ goto out_size;
+ obj->len = length;
+ obj->data = (u8 *)p;
+ return 0;
+out_size:
+ dprintk("NFS: returned netobj was too long: %u\n", length);
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * netobj cookie;
+ */
+static void encode_cookie(struct xdr_stream *xdr,
+ const struct nlm_cookie *cookie)
+{
+ encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
+}
+
+static int decode_cookie(struct xdr_stream *xdr,
+ struct nlm_cookie *cookie)
+{
+ u32 length;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ length = be32_to_cpup(p++);
+ /* apparently HPUX can return empty cookies */
+ if (length == 0)
+ goto out_hpux;
+ if (length > NLM_MAXCOOKIELEN)
+ goto out_size;
+ p = xdr_inline_decode(xdr, length);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ cookie->len = length;
+ memcpy(cookie->data, p, length);
+ return 0;
+out_hpux:
+ cookie->len = 4;
+ memset(cookie->data, 0, 4);
+ return 0;
+out_size:
+ dprintk("NFS: returned cookie was too long: %u\n", length);
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * netobj fh;
+ */
+static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+{
+ encode_netobj(xdr, (u8 *)&fh->data, NFS2_FHSIZE);
+}
+
+/*
+ * enum nlm_stats {
+ * LCK_GRANTED = 0,
+ * LCK_DENIED = 1,
+ * LCK_DENIED_NOLOCKS = 2,
+ * LCK_BLOCKED = 3,
+ * LCK_DENIED_GRACE_PERIOD = 4
+ * };
+ *
+ *
+ * struct nlm_stat {
+ * nlm_stats stat;
+ * };
+ *
+ * NB: we don't swap bytes for the NLM status values. The upper
+ * layers deal directly with the status value in network byte
+ * order.
+ */
+
+static void encode_nlm_stat(struct xdr_stream *xdr,
+ const __be32 stat)
+{
+ __be32 *p;
+
+ WARN_ON_ONCE(be32_to_cpu(stat) > NLM_LCK_DENIED_GRACE_PERIOD);
+ p = xdr_reserve_space(xdr, 4);
+ *p = stat;
+}
+
+static int decode_nlm_stat(struct xdr_stream *xdr,
+ __be32 *stat)
+{
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ if (unlikely(ntohl(*p) > ntohl(nlm_lck_denied_grace_period)))
+ goto out_enum;
+ *stat = *p;
+ return 0;
+out_enum:
+ dprintk("%s: server returned invalid nlm_stats value: %u\n",
+ __func__, be32_to_cpup(p));
+ return -EIO;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * struct nlm_holder {
+ * bool exclusive;
+ * int uppid;
+ * netobj oh;
+ * unsigned l_offset;
+ * unsigned l_len;
+ * };
+ */
+static void encode_nlm_holder(struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ const struct nlm_lock *lock = &result->lock;
+ u32 l_offset, l_len;
+ __be32 *p;
+
+ encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
+ encode_int32(xdr, lock->svid);
+ encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+ p = xdr_reserve_space(xdr, 4 + 4);
+ nlm_compute_offsets(lock, &l_offset, &l_len);
+ *p++ = cpu_to_be32(l_offset);
+ *p = cpu_to_be32(l_len);
+}
+
+static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result)
+{
+ struct nlm_lock *lock = &result->lock;
+ struct file_lock *fl = &lock->fl;
+ u32 exclusive, l_offset, l_len;
+ int error;
+ __be32 *p;
+ s32 end;
+
+ memset(lock, 0, sizeof(*lock));
+ locks_init_lock(fl);
+
+ p = xdr_inline_decode(xdr, 4 + 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+ exclusive = be32_to_cpup(p++);
+ lock->svid = be32_to_cpup(p);
+ fl->fl_pid = (pid_t)lock->svid;
+
+ error = decode_netobj(xdr, &lock->oh);
+ if (unlikely(error))
+ goto out;
+
+ p = xdr_inline_decode(xdr, 4 + 4);
+ if (unlikely(p == NULL))
+ goto out_overflow;
+
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = exclusive != 0 ? F_WRLCK : F_RDLCK;
+ l_offset = be32_to_cpup(p++);
+ l_len = be32_to_cpup(p);
+ end = l_offset + l_len - 1;
+
+ fl->fl_start = (loff_t)l_offset;
+ if (l_len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = (loff_t)end;
+ error = 0;
+out:
+ return error;
+out_overflow:
+ print_overflow_msg(__func__, xdr);
+ return -EIO;
+}
+
+/*
+ * string caller_name<LM_MAXSTRLEN>;
+ */
+static void encode_caller_name(struct xdr_stream *xdr, const char *name)
+{
+ /* NB: client-side does not set lock->len */
+ u32 length = strlen(name);
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, 4 + length);
+ xdr_encode_opaque(p, name, length);
+}
+
+/*
+ * struct nlm_lock {
+ * string caller_name<LM_MAXSTRLEN>;
+ * netobj fh;
+ * netobj oh;
+ * int uppid;
+ * unsigned l_offset;
+ * unsigned l_len;
+ * };
+ */
+static void encode_nlm_lock(struct xdr_stream *xdr,
+ const struct nlm_lock *lock)
+{
+ u32 l_offset, l_len;
+ __be32 *p;
+
+ encode_caller_name(xdr, lock->caller);
+ encode_fh(xdr, &lock->fh);
+ encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+ p = xdr_reserve_space(xdr, 4 + 4 + 4);
+ *p++ = cpu_to_be32(lock->svid);
+
+ nlm_compute_offsets(lock, &l_offset, &l_len);
+ *p++ = cpu_to_be32(l_offset);
+ *p = cpu_to_be32(l_len);
+}
+
+
+/*
+ * NLMv3 XDR encode functions
+ *
+ * NLMv3 argument types are defined in Chapter 10 of The Open Group's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ * struct nlm_testargs {
+ * netobj cookie;
+ * bool exclusive;
+ * struct nlm_lock alock;
+ * };
+ */
+static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm_lock(xdr, lock);
+}
+
+/*
+ * struct nlm_lockargs {
+ * netobj cookie;
+ * bool block;
+ * bool exclusive;
+ * struct nlm_lock alock;
+ * bool reclaim;
+ * int state;
+ * };
+ */
+static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, args->block);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm_lock(xdr, lock);
+ encode_bool(xdr, args->reclaim);
+ encode_int32(xdr, args->state);
+}
+
+/*
+ * struct nlm_cancargs {
+ * netobj cookie;
+ * bool block;
+ * bool exclusive;
+ * struct nlm_lock alock;
+ * };
+ */
+static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_bool(xdr, args->block);
+ encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+ encode_nlm_lock(xdr, lock);
+}
+
+/*
+ * struct nlm_unlockargs {
+ * netobj cookie;
+ * struct nlm_lock alock;
+ * };
+ */
+static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_args *args)
+{
+ const struct nlm_lock *lock = &args->lock;
+
+ encode_cookie(xdr, &args->cookie);
+ encode_nlm_lock(xdr, lock);
+}
+
+/*
+ * struct nlm_res {
+ * netobj cookie;
+ * nlm_stat stat;
+ * };
+ */
+static void nlm_xdr_enc_res(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ encode_cookie(xdr, &result->cookie);
+ encode_nlm_stat(xdr, result->status);
+}
+
+/*
+ * union nlm_testrply switch (nlm_stats stat) {
+ * case LCK_DENIED:
+ * struct nlm_holder holder;
+ * default:
+ * void;
+ * };
+ *
+ * struct nlm_testres {
+ * netobj cookie;
+ * nlm_testrply test_stat;
+ * };
+ */
+static void encode_nlm_testrply(struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ if (result->status == nlm_lck_denied)
+ encode_nlm_holder(xdr, result);
+}
+
+static void nlm_xdr_enc_testres(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ const struct nlm_res *result)
+{
+ encode_cookie(xdr, &result->cookie);
+ encode_nlm_stat(xdr, result->status);
+ encode_nlm_testrply(xdr, result);
+}
+
+
+/*
+ * NLMv3 XDR decode functions
+ *
+ * NLMv3 result types are defined in Chapter 10 of The Open Group's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ * union nlm_testrply switch (nlm_stats stat) {
+ * case LCK_DENIED:
+ * struct nlm_holder holder;
+ * default:
+ * void;
+ * };
+ *
+ * struct nlm_testres {
+ * netobj cookie;
+ * nlm_testrply test_stat;
+ * };
+ */
+static int decode_nlm_testrply(struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_nlm_stat(xdr, &result->status);
+ if (unlikely(error))
+ goto out;
+ if (result->status == nlm_lck_denied)
+ error = decode_nlm_holder(xdr, result);
+out:
+ return error;
+}
+
+static int nlm_xdr_dec_testres(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_cookie(xdr, &result->cookie);
+ if (unlikely(error))
+ goto out;
+ error = decode_nlm_testrply(xdr, result);
+out:
+ return error;
+}
+
+/*
+ * struct nlm_res {
+ * netobj cookie;
+ * nlm_stat stat;
+ * };
+ */
+static int nlm_xdr_dec_res(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ struct nlm_res *result)
+{
+ int error;
+
+ error = decode_cookie(xdr, &result->cookie);
+ if (unlikely(error))
+ goto out;
+ error = decode_nlm_stat(xdr, &result->status);
+out:
+ return error;
+}
+
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm_xdr_dec_norep NULL
+
+#define PROC(proc, argtype, restype) \
+[NLMPROC_##proc] = { \
+ .p_proc = NLMPROC_##proc, \
+ .p_encode = (kxdreproc_t)nlm_xdr_enc_##argtype, \
+ .p_decode = (kxdrdproc_t)nlm_xdr_dec_##restype, \
+ .p_arglen = NLM_##argtype##_sz, \
+ .p_replen = NLM_##restype##_sz, \
+ .p_statidx = NLMPROC_##proc, \
+ .p_name = #proc, \
+ }
+
+static struct rpc_procinfo nlm_procedures[] = {
+ PROC(TEST, testargs, testres),
+ PROC(LOCK, lockargs, res),
+ PROC(CANCEL, cancargs, res),
+ PROC(UNLOCK, unlockargs, res),
+ PROC(GRANTED, testargs, res),
+ PROC(TEST_MSG, testargs, norep),
+ PROC(LOCK_MSG, lockargs, norep),
+ PROC(CANCEL_MSG, cancargs, norep),
+ PROC(UNLOCK_MSG, unlockargs, norep),
+ PROC(GRANTED_MSG, testargs, norep),
+ PROC(TEST_RES, testres, norep),
+ PROC(LOCK_RES, res, norep),
+ PROC(CANCEL_RES, res, norep),
+ PROC(UNLOCK_RES, res, norep),
+ PROC(GRANTED_RES, res, norep),
+};
+
+static const struct rpc_version nlm_version1 = {
+ .number = 1,
+ .nrprocs = ARRAY_SIZE(nlm_procedures),
+ .procs = nlm_procedures,
+};
+
+static const struct rpc_version nlm_version3 = {
+ .number = 3,
+ .nrprocs = ARRAY_SIZE(nlm_procedures),
+ .procs = nlm_procedures,
+};
+
+static const struct rpc_version *nlm_versions[] = {
+ [1] = &nlm_version1,
+ [3] = &nlm_version3,
+#ifdef CONFIG_LOCKD_V4
+ [4] = &nlm_version4,
+#endif
+};
+
+static struct rpc_stat nlm_rpc_stats;
+
+const struct rpc_program nlm_program = {
+ .name = "lockd",
+ .number = NLM_PROGRAM,
+ .nrvers = ARRAY_SIZE(nlm_versions),
+ .version = nlm_versions,
+ .stats = &nlm_rpc_stats,
+};
diff --git a/fs/lockd/grace.c b/fs/lockd/grace.c
new file mode 100644
index 00000000000..6d1ee7204c8
--- /dev/null
+++ b/fs/lockd/grace.c
@@ -0,0 +1,65 @@
+/*
+ * Common code for control of lockd and nfsv4 grace periods.
+ */
+
+#include <linux/module.h>
+#include <linux/lockd/bind.h>
+#include <net/net_namespace.h>
+
+#include "netns.h"
+
+static DEFINE_SPINLOCK(grace_lock);
+
+/**
+ * locks_start_grace
+ * @lm: who this grace period is for
+ *
+ * A grace period is a period during which locks should not be given
+ * out. Currently grace periods are only enforced by the two lock
+ * managers (lockd and nfsd), using the locks_in_grace() function to
+ * check when they are in a grace period.
+ *
+ * This function is called to start a grace period.
+ */
+void locks_start_grace(struct net *net, struct lock_manager *lm)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ spin_lock(&grace_lock);
+ list_add(&lm->list, &ln->grace_list);
+ spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_start_grace);
+
+/**
+ * locks_end_grace
+ * @lm: who this grace period is for
+ *
+ * Call this function to state that the given lock manager is ready to
+ * resume regular locking. The grace period will not end until all lock
+ * managers that called locks_start_grace() also call locks_end_grace().
+ * Note that callers count on it being safe to call this more than once,
+ * and the second call should be a no-op.
+ */
+void locks_end_grace(struct lock_manager *lm)
+{
+ spin_lock(&grace_lock);
+ list_del_init(&lm->list);
+ spin_unlock(&grace_lock);
+}
+EXPORT_SYMBOL_GPL(locks_end_grace);
+
+/**
+ * locks_in_grace
+ *
+ * Lock managers call this function to determine when it is OK for them
+ * to answer ordinary lock requests, and when they should accept only
+ * lock reclaims.
+ */
+int locks_in_grace(struct net *net)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ return !list_empty(&ln->grace_list);
+}
+EXPORT_SYMBOL_GPL(locks_in_grace);
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index f1ef49fff11..969d589c848 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -11,193 +11,404 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/in.h>
+#include <linux/in6.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
#include <linux/mutex.h>
+#include <linux/sunrpc/svc_xprt.h>
+
+#include <net/ipv6.h>
+
+#include "netns.h"
#define NLMDBG_FACILITY NLMDBG_HOSTCACHE
-#define NLM_HOST_MAX 64
#define NLM_HOST_NRHASH 32
-#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1))
#define NLM_HOST_REBIND (60 * HZ)
-#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
-#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ)
+#define NLM_HOST_EXPIRE (300 * HZ)
+#define NLM_HOST_COLLECT (120 * HZ)
+
+static struct hlist_head nlm_server_hosts[NLM_HOST_NRHASH];
+static struct hlist_head nlm_client_hosts[NLM_HOST_NRHASH];
+
+#define for_each_host(host, chain, table) \
+ for ((chain) = (table); \
+ (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+ hlist_for_each_entry((host), (chain), h_hash)
-static struct hlist_head nlm_hosts[NLM_HOST_NRHASH];
-static unsigned long next_gc;
-static int nrhosts;
+#define for_each_host_safe(host, next, chain, table) \
+ for ((chain) = (table); \
+ (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+ hlist_for_each_entry_safe((host), (next), \
+ (chain), h_hash)
+
+static unsigned long nrhosts;
static DEFINE_MUTEX(nlm_host_mutex);
+static void nlm_gc_hosts(struct net *net);
-static void nlm_gc_hosts(void);
-static struct nsm_handle * __nsm_find(const struct sockaddr_in *,
- const char *, unsigned int, int);
-static struct nsm_handle * nsm_find(const struct sockaddr_in *sin,
- const char *hostname,
- unsigned int hostname_len);
+struct nlm_lookup_host_info {
+ const int server; /* search for server|client */
+ const struct sockaddr *sap; /* address to search for */
+ const size_t salen; /* it's length */
+ const unsigned short protocol; /* transport to search for*/
+ const u32 version; /* NLM version to search for */
+ const char *hostname; /* remote's hostname */
+ const size_t hostname_len; /* it's length */
+ const int noresvport; /* use non-priv port */
+ struct net *net; /* network namespace to bind */
+};
/*
- * Common host lookup routine for server & client
+ * Hash function must work well on big- and little-endian platforms
*/
-static struct nlm_host *
-nlm_lookup_host(int server, const struct sockaddr_in *sin,
- int proto, int version, const char *hostname,
- unsigned int hostname_len,
- const struct sockaddr_in *ssin)
+static unsigned int __nlm_hash32(const __be32 n)
{
- struct hlist_head *chain;
- struct hlist_node *pos;
- struct nlm_host *host;
- struct nsm_handle *nsm = NULL;
- int hash;
-
- dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT
- ", p=%d, v=%d, my role=%s, name=%.*s)\n",
- NIPQUAD(ssin->sin_addr.s_addr),
- NIPQUAD(sin->sin_addr.s_addr), proto, version,
- server? "server" : "client",
- hostname_len,
- hostname? hostname : "<none>");
-
-
- hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
-
- /* Lock hash table */
- mutex_lock(&nlm_host_mutex);
-
- if (time_after_eq(jiffies, next_gc))
- nlm_gc_hosts();
-
- /* We may keep several nlm_host objects for a peer, because each
- * nlm_host is identified by
- * (address, protocol, version, server/client)
- * We could probably simplify this a little by putting all those
- * different NLM rpc_clients into one single nlm_host object.
- * This would allow us to have one nlm_host per address.
- */
- chain = &nlm_hosts[hash];
- hlist_for_each_entry(host, pos, chain, h_hash) {
- if (!nlm_cmp_addr(&host->h_addr, sin))
- continue;
-
- /* See if we have an NSM handle for this client */
- if (!nsm)
- nsm = host->h_nsmhandle;
+ unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16);
+ return hash ^ (hash >> 8);
+}
- if (host->h_proto != proto)
- continue;
- if (host->h_version != version)
- continue;
- if (host->h_server != server)
- continue;
- if (!nlm_cmp_addr(&host->h_saddr, ssin))
- continue;
+static unsigned int __nlm_hash_addr4(const struct sockaddr *sap)
+{
+ const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+ return __nlm_hash32(sin->sin_addr.s_addr);
+}
- /* Move to head of hash chain. */
- hlist_del(&host->h_hash);
- hlist_add_head(&host->h_hash, chain);
+static unsigned int __nlm_hash_addr6(const struct sockaddr *sap)
+{
+ const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+ const struct in6_addr addr = sin6->sin6_addr;
+ return __nlm_hash32(addr.s6_addr32[0]) ^
+ __nlm_hash32(addr.s6_addr32[1]) ^
+ __nlm_hash32(addr.s6_addr32[2]) ^
+ __nlm_hash32(addr.s6_addr32[3]);
+}
- nlm_get_host(host);
- goto out;
+static unsigned int nlm_hash_address(const struct sockaddr *sap)
+{
+ unsigned int hash;
+
+ switch (sap->sa_family) {
+ case AF_INET:
+ hash = __nlm_hash_addr4(sap);
+ break;
+ case AF_INET6:
+ hash = __nlm_hash_addr6(sap);
+ break;
+ default:
+ hash = 0;
}
- if (nsm)
- atomic_inc(&nsm->sm_count);
+ return hash & (NLM_HOST_NRHASH - 1);
+}
- host = NULL;
+/*
+ * Allocate and initialize an nlm_host. Common to both client and server.
+ */
+static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
+ struct nsm_handle *nsm)
+{
+ struct nlm_host *host = NULL;
+ unsigned long now = jiffies;
- /* Sadly, the host isn't in our hash table yet. See if
- * we have an NSM handle for it. If not, create one.
- */
- if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len)))
- goto out;
+ if (nsm != NULL)
+ atomic_inc(&nsm->sm_count);
+ else {
+ host = NULL;
+ nsm = nsm_get_handle(ni->sap, ni->salen,
+ ni->hostname, ni->hostname_len);
+ if (unlikely(nsm == NULL)) {
+ dprintk("lockd: %s failed; no nsm handle\n",
+ __func__);
+ goto out;
+ }
+ }
- host = kzalloc(sizeof(*host), GFP_KERNEL);
- if (!host) {
+ host = kmalloc(sizeof(*host), GFP_KERNEL);
+ if (unlikely(host == NULL)) {
+ dprintk("lockd: %s failed; no memory\n", __func__);
nsm_release(nsm);
goto out;
}
- host->h_name = nsm->sm_name;
- host->h_addr = *sin;
- host->h_addr.sin_port = 0; /* ouch! */
- host->h_saddr = *ssin;
- host->h_version = version;
- host->h_proto = proto;
+
+ memcpy(nlm_addr(host), ni->sap, ni->salen);
+ host->h_addrlen = ni->salen;
+ rpc_set_port(nlm_addr(host), 0);
+ host->h_srcaddrlen = 0;
+
host->h_rpcclnt = NULL;
- mutex_init(&host->h_mutex);
- host->h_nextrebind = jiffies + NLM_HOST_REBIND;
- host->h_expires = jiffies + NLM_HOST_EXPIRE;
- atomic_set(&host->h_count, 1);
+ host->h_name = nsm->sm_name;
+ host->h_version = ni->version;
+ host->h_proto = ni->protocol;
+ host->h_reclaiming = 0;
+ host->h_server = ni->server;
+ host->h_noresvport = ni->noresvport;
+ host->h_inuse = 0;
init_waitqueue_head(&host->h_gracewait);
init_rwsem(&host->h_rwsem);
- host->h_state = 0; /* pseudo NSM state */
- host->h_nsmstate = 0; /* real NSM state */
- host->h_nsmhandle = nsm;
- host->h_server = server;
- hlist_add_head(&host->h_hash, chain);
+ host->h_state = 0;
+ host->h_nsmstate = 0;
+ host->h_pidcount = 0;
+ atomic_set(&host->h_count, 1);
+ mutex_init(&host->h_mutex);
+ host->h_nextrebind = now + NLM_HOST_REBIND;
+ host->h_expires = now + NLM_HOST_EXPIRE;
INIT_LIST_HEAD(&host->h_lockowners);
spin_lock_init(&host->h_lock);
INIT_LIST_HEAD(&host->h_granted);
INIT_LIST_HEAD(&host->h_reclaim);
-
- if (++nrhosts > NLM_HOST_MAX)
- next_gc = 0;
+ host->h_nsmhandle = nsm;
+ host->h_addrbuf = nsm->sm_addrbuf;
+ host->net = ni->net;
out:
- mutex_unlock(&nlm_host_mutex);
return host;
}
/*
- * Destroy a host
+ * Destroy an nlm_host and free associated resources
+ *
+ * Caller must hold nlm_host_mutex.
*/
-static void
-nlm_destroy_host(struct nlm_host *host)
+static void nlm_destroy_host_locked(struct nlm_host *host)
{
struct rpc_clnt *clnt;
+ struct lockd_net *ln = net_generic(host->net, lockd_net_id);
- BUG_ON(!list_empty(&host->h_lockowners));
- BUG_ON(atomic_read(&host->h_count));
+ dprintk("lockd: destroy host %s\n", host->h_name);
+
+ hlist_del_init(&host->h_hash);
- /*
- * Release NSM handle and unmonitor host.
- */
nsm_unmonitor(host);
+ nsm_release(host->h_nsmhandle);
clnt = host->h_rpcclnt;
if (clnt != NULL)
rpc_shutdown_client(clnt);
kfree(host);
+
+ ln->nrhosts--;
+ nrhosts--;
}
-/*
- * Find an NLM server handle in the cache. If there is none, create it.
+/**
+ * nlmclnt_lookup_host - Find an NLM host handle matching a remote server
+ * @sap: network address of server
+ * @salen: length of server address
+ * @protocol: transport protocol to use
+ * @version: NLM protocol version
+ * @hostname: '\0'-terminated hostname of server
+ * @noresvport: 1 if non-privileged port should be used
+ *
+ * Returns an nlm_host structure that matches the passed-in
+ * [server address, transport protocol, NLM version, server hostname].
+ * If one doesn't already exist in the host cache, a new handle is
+ * created and returned.
*/
-struct nlm_host *
-nlmclnt_lookup_host(const struct sockaddr_in *sin, int proto, int version,
- const char *hostname, unsigned int hostname_len)
+struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
+ const size_t salen,
+ const unsigned short protocol,
+ const u32 version,
+ const char *hostname,
+ int noresvport,
+ struct net *net)
{
- struct sockaddr_in ssin = {0};
+ struct nlm_lookup_host_info ni = {
+ .server = 0,
+ .sap = sap,
+ .salen = salen,
+ .protocol = protocol,
+ .version = version,
+ .hostname = hostname,
+ .hostname_len = strlen(hostname),
+ .noresvport = noresvport,
+ .net = net,
+ };
+ struct hlist_head *chain;
+ struct nlm_host *host;
+ struct nsm_handle *nsm = NULL;
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
- return nlm_lookup_host(0, sin, proto, version,
- hostname, hostname_len, &ssin);
+ dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
+ (hostname ? hostname : "<none>"), version,
+ (protocol == IPPROTO_UDP ? "udp" : "tcp"));
+
+ mutex_lock(&nlm_host_mutex);
+
+ chain = &nlm_client_hosts[nlm_hash_address(sap)];
+ hlist_for_each_entry(host, chain, h_hash) {
+ if (host->net != net)
+ continue;
+ if (!rpc_cmp_addr(nlm_addr(host), sap))
+ continue;
+
+ /* Same address. Share an NSM handle if we already have one */
+ if (nsm == NULL)
+ nsm = host->h_nsmhandle;
+
+ if (host->h_proto != protocol)
+ continue;
+ if (host->h_version != version)
+ continue;
+
+ nlm_get_host(host);
+ dprintk("lockd: %s found host %s (%s)\n", __func__,
+ host->h_name, host->h_addrbuf);
+ goto out;
+ }
+
+ host = nlm_alloc_host(&ni, nsm);
+ if (unlikely(host == NULL))
+ goto out;
+
+ hlist_add_head(&host->h_hash, chain);
+ ln->nrhosts++;
+ nrhosts++;
+
+ dprintk("lockd: %s created host %s (%s)\n", __func__,
+ host->h_name, host->h_addrbuf);
+
+out:
+ mutex_unlock(&nlm_host_mutex);
+ return host;
}
-/*
- * Find an NLM client handle in the cache. If there is none, create it.
+/**
+ * nlmclnt_release_host - release client nlm_host
+ * @host: nlm_host to release
+ *
+ */
+void nlmclnt_release_host(struct nlm_host *host)
+{
+ if (host == NULL)
+ return;
+
+ dprintk("lockd: release client host %s\n", host->h_name);
+
+ WARN_ON_ONCE(host->h_server);
+
+ if (atomic_dec_and_test(&host->h_count)) {
+ WARN_ON_ONCE(!list_empty(&host->h_lockowners));
+ WARN_ON_ONCE(!list_empty(&host->h_granted));
+ WARN_ON_ONCE(!list_empty(&host->h_reclaim));
+
+ mutex_lock(&nlm_host_mutex);
+ nlm_destroy_host_locked(host);
+ mutex_unlock(&nlm_host_mutex);
+ }
+}
+
+/**
+ * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
+ * @rqstp: incoming NLM request
+ * @hostname: name of client host
+ * @hostname_len: length of client hostname
+ *
+ * Returns an nlm_host structure that matches the [client address,
+ * transport protocol, NLM version, client hostname] of the passed-in
+ * NLM request. If one doesn't already exist in the host cache, a
+ * new handle is created and returned.
+ *
+ * Before possibly creating a new nlm_host, construct a sockaddr
+ * for a specific source address in case the local system has
+ * multiple network addresses. The family of the address in
+ * rq_daddr is guaranteed to be the same as the family of the
+ * address in rq_addr, so it's safe to use the same family for
+ * the source address.
+ */
+struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
+ const char *hostname,
+ const size_t hostname_len)
+{
+ struct hlist_head *chain;
+ struct nlm_host *host = NULL;
+ struct nsm_handle *nsm = NULL;
+ struct sockaddr *src_sap = svc_daddr(rqstp);
+ size_t src_len = rqstp->rq_daddrlen;
+ struct net *net = SVC_NET(rqstp);
+ struct nlm_lookup_host_info ni = {
+ .server = 1,
+ .sap = svc_addr(rqstp),
+ .salen = rqstp->rq_addrlen,
+ .protocol = rqstp->rq_prot,
+ .version = rqstp->rq_vers,
+ .hostname = hostname,
+ .hostname_len = hostname_len,
+ .net = net,
+ };
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
+ (int)hostname_len, hostname, rqstp->rq_vers,
+ (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
+
+ mutex_lock(&nlm_host_mutex);
+
+ if (time_after_eq(jiffies, ln->next_gc))
+ nlm_gc_hosts(net);
+
+ chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
+ hlist_for_each_entry(host, chain, h_hash) {
+ if (host->net != net)
+ continue;
+ if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
+ continue;
+
+ /* Same address. Share an NSM handle if we already have one */
+ if (nsm == NULL)
+ nsm = host->h_nsmhandle;
+
+ if (host->h_proto != ni.protocol)
+ continue;
+ if (host->h_version != ni.version)
+ continue;
+ if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
+ continue;
+
+ /* Move to head of hash chain. */
+ hlist_del(&host->h_hash);
+ hlist_add_head(&host->h_hash, chain);
+
+ nlm_get_host(host);
+ dprintk("lockd: %s found host %s (%s)\n",
+ __func__, host->h_name, host->h_addrbuf);
+ goto out;
+ }
+
+ host = nlm_alloc_host(&ni, nsm);
+ if (unlikely(host == NULL))
+ goto out;
+
+ memcpy(nlm_srcaddr(host), src_sap, src_len);
+ host->h_srcaddrlen = src_len;
+ hlist_add_head(&host->h_hash, chain);
+ ln->nrhosts++;
+ nrhosts++;
+
+ dprintk("lockd: %s created host %s (%s)\n",
+ __func__, host->h_name, host->h_addrbuf);
+
+out:
+ mutex_unlock(&nlm_host_mutex);
+ return host;
+}
+
+/**
+ * nlmsvc_release_host - release server nlm_host
+ * @host: nlm_host to release
+ *
+ * Host is destroyed later in nlm_gc_host().
*/
-struct nlm_host *
-nlmsvc_lookup_host(struct svc_rqst *rqstp,
- const char *hostname, unsigned int hostname_len)
+void nlmsvc_release_host(struct nlm_host *host)
{
- struct sockaddr_in ssin = {0};
+ if (host == NULL)
+ return;
+
+ dprintk("lockd: release server host %s\n", host->h_name);
- ssin.sin_addr = rqstp->rq_daddr.addr;
- return nlm_lookup_host(1, svc_addr_in(rqstp),
- rqstp->rq_prot, rqstp->rq_vers,
- hostname, hostname_len, &ssin);
+ WARN_ON_ONCE(!host->h_server);
+ atomic_dec(&host->h_count);
}
/*
@@ -208,9 +419,8 @@ nlm_bind_host(struct nlm_host *host)
{
struct rpc_clnt *clnt;
- dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n",
- NIPQUAD(host->h_saddr.sin_addr),
- NIPQUAD(host->h_addr.sin_addr));
+ dprintk("lockd: nlm_bind_host %s (%s)\n",
+ host->h_name, host->h_addrbuf);
/* Lock host handle */
mutex_lock(&host->h_mutex);
@@ -222,7 +432,7 @@ nlm_bind_host(struct nlm_host *host)
if (time_after_eq(jiffies, host->h_nextrebind)) {
rpc_force_rebind(clnt);
host->h_nextrebind = jiffies + NLM_HOST_REBIND;
- dprintk("lockd: next rebind in %ld jiffies\n",
+ dprintk("lockd: next rebind in %lu jiffies\n",
host->h_nextrebind - jiffies);
}
} else {
@@ -234,10 +444,10 @@ nlm_bind_host(struct nlm_host *host)
.to_retries = 5U,
};
struct rpc_create_args args = {
+ .net = host->net,
.protocol = host->h_proto,
- .address = (struct sockaddr *)&host->h_addr,
- .addrsize = sizeof(host->h_addr),
- .saddress = (struct sockaddr *)&host->h_saddr,
+ .address = nlm_addr(host),
+ .addrsize = host->h_addrlen,
.timeout = &timeparms,
.servername = host->h_name,
.program = &nlm_program,
@@ -254,6 +464,10 @@ nlm_bind_host(struct nlm_host *host)
*/
if (!host->h_server)
args.flags |= RPC_CLNT_CREATE_HARDRTRY;
+ if (host->h_noresvport)
+ args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+ if (host->h_srcaddrlen)
+ args.saddress = nlm_srcaddr(host);
clnt = rpc_create(&args);
if (!IS_ERR(clnt))
@@ -294,124 +508,127 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
return host;
}
-/*
- * Release NLM host after use
- */
-void nlm_release_host(struct nlm_host *host)
+static struct nlm_host *next_host_state(struct hlist_head *cache,
+ struct nsm_handle *nsm,
+ const struct nlm_reboot *info)
{
- if (host != NULL) {
- dprintk("lockd: release host %s\n", host->h_name);
- BUG_ON(atomic_read(&host->h_count) < 0);
- if (atomic_dec_and_test(&host->h_count)) {
- BUG_ON(!list_empty(&host->h_lockowners));
- BUG_ON(!list_empty(&host->h_granted));
- BUG_ON(!list_empty(&host->h_reclaim));
+ struct nlm_host *host;
+ struct hlist_head *chain;
+
+ mutex_lock(&nlm_host_mutex);
+ for_each_host(host, chain, cache) {
+ if (host->h_nsmhandle == nsm
+ && host->h_nsmstate != info->state) {
+ host->h_nsmstate = info->state;
+ host->h_state++;
+
+ nlm_get_host(host);
+ mutex_unlock(&nlm_host_mutex);
+ return host;
}
}
+
+ mutex_unlock(&nlm_host_mutex);
+ return NULL;
}
-/*
- * We were notified that the host indicated by address &sin
- * has rebooted.
- * Release all resources held by that peer.
+/**
+ * nlm_host_rebooted - Release all resources held by rebooted host
+ * @info: pointer to decoded results of NLM_SM_NOTIFY call
+ *
+ * We were notified that the specified host has rebooted. Release
+ * all resources held by that peer.
*/
-void nlm_host_rebooted(const struct sockaddr_in *sin,
- const char *hostname,
- unsigned int hostname_len,
- u32 new_state)
+void nlm_host_rebooted(const struct nlm_reboot *info)
{
- struct hlist_head *chain;
- struct hlist_node *pos;
struct nsm_handle *nsm;
struct nlm_host *host;
- dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n",
- hostname, NIPQUAD(sin->sin_addr));
-
- /* Find the NSM handle for this peer */
- if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0)))
+ nsm = nsm_reboot_lookup(info);
+ if (unlikely(nsm == NULL))
return;
- /* When reclaiming locks on this peer, make sure that
- * we set up a new notification */
- nsm->sm_monitored = 0;
-
/* Mark all hosts tied to this NSM state as having rebooted.
* We run the loop repeatedly, because we drop the host table
* lock for this.
* To avoid processing a host several times, we match the nsmstate.
*/
-again: mutex_lock(&nlm_host_mutex);
- for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
- hlist_for_each_entry(host, pos, chain, h_hash) {
- if (host->h_nsmhandle == nsm
- && host->h_nsmstate != new_state) {
- host->h_nsmstate = new_state;
- host->h_state++;
-
- nlm_get_host(host);
- mutex_unlock(&nlm_host_mutex);
-
- if (host->h_server) {
- /* We're server for this guy, just ditch
- * all the locks he held. */
- nlmsvc_free_host_resources(host);
- } else {
- /* He's the server, initiate lock recovery. */
- nlmclnt_recovery(host);
- }
-
- nlm_release_host(host);
- goto again;
- }
- }
+ while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
+ nlmsvc_free_host_resources(host);
+ nlmsvc_release_host(host);
+ }
+ while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
+ nlmclnt_recovery(host);
+ nlmclnt_release_host(host);
}
- mutex_unlock(&nlm_host_mutex);
+ nsm_release(nsm);
+}
+
+static void nlm_complain_hosts(struct net *net)
+{
+ struct hlist_head *chain;
+ struct nlm_host *host;
+
+ if (net) {
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ if (ln->nrhosts == 0)
+ return;
+ printk(KERN_WARNING "lockd: couldn't shutdown host module for net %p!\n", net);
+ dprintk("lockd: %lu hosts left in net %p:\n", ln->nrhosts, net);
+ } else {
+ if (nrhosts == 0)
+ return;
+ printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
+ dprintk("lockd: %lu hosts left:\n", nrhosts);
+ }
+
+ for_each_host(host, chain, nlm_server_hosts) {
+ if (net && host->net != net)
+ continue;
+ dprintk(" %s (cnt %d use %d exp %ld net %p)\n",
+ host->h_name, atomic_read(&host->h_count),
+ host->h_inuse, host->h_expires, host->net);
+ }
}
-/*
- * Shut down the hosts module.
- * Note that this routine is called only at server shutdown time.
- */
void
-nlm_shutdown_hosts(void)
+nlm_shutdown_hosts_net(struct net *net)
{
struct hlist_head *chain;
- struct hlist_node *pos;
struct nlm_host *host;
- dprintk("lockd: shutting down host module\n");
mutex_lock(&nlm_host_mutex);
/* First, make all hosts eligible for gc */
- dprintk("lockd: nuking all hosts...\n");
- for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
- hlist_for_each_entry(host, pos, chain, h_hash) {
- host->h_expires = jiffies - 1;
- if (host->h_rpcclnt) {
- rpc_shutdown_client(host->h_rpcclnt);
- host->h_rpcclnt = NULL;
- }
+ dprintk("lockd: nuking all hosts in net %p...\n", net);
+ for_each_host(host, chain, nlm_server_hosts) {
+ if (net && host->net != net)
+ continue;
+ host->h_expires = jiffies - 1;
+ if (host->h_rpcclnt) {
+ rpc_shutdown_client(host->h_rpcclnt);
+ host->h_rpcclnt = NULL;
}
}
/* Then, perform a garbage collection pass */
- nlm_gc_hosts();
+ nlm_gc_hosts(net);
mutex_unlock(&nlm_host_mutex);
- /* complain if any hosts are left */
- if (nrhosts) {
- printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
- dprintk("lockd: %d hosts left:\n", nrhosts);
- for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
- hlist_for_each_entry(host, pos, chain, h_hash) {
- dprintk(" %s (cnt %d use %d exp %ld)\n",
- host->h_name, atomic_read(&host->h_count),
- host->h_inuse, host->h_expires);
- }
- }
- }
+ nlm_complain_hosts(net);
+}
+
+/*
+ * Shut down the hosts module.
+ * Note that this routine is called only at server shutdown time.
+ */
+void
+nlm_shutdown_hosts(void)
+{
+ dprintk("lockd: shutting down host module\n");
+ nlm_shutdown_hosts_net(NULL);
}
/*
@@ -420,124 +637,39 @@ nlm_shutdown_hosts(void)
* mark & sweep for resources held by remote clients.
*/
static void
-nlm_gc_hosts(void)
+nlm_gc_hosts(struct net *net)
{
struct hlist_head *chain;
- struct hlist_node *pos, *next;
+ struct hlist_node *next;
struct nlm_host *host;
- dprintk("lockd: host garbage collection\n");
- for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
- hlist_for_each_entry(host, pos, chain, h_hash)
- host->h_inuse = 0;
+ dprintk("lockd: host garbage collection for net %p\n", net);
+ for_each_host(host, chain, nlm_server_hosts) {
+ if (net && host->net != net)
+ continue;
+ host->h_inuse = 0;
}
/* Mark all hosts that hold locks, blocks or shares */
- nlmsvc_mark_resources();
-
- for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
- hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
- if (atomic_read(&host->h_count) || host->h_inuse
- || time_before(jiffies, host->h_expires)) {
- dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
- host->h_name, atomic_read(&host->h_count),
- host->h_inuse, host->h_expires);
- continue;
- }
- dprintk("lockd: delete host %s\n", host->h_name);
- hlist_del_init(&host->h_hash);
-
- nlm_destroy_host(host);
- nrhosts--;
- }
- }
+ nlmsvc_mark_resources(net);
- next_gc = jiffies + NLM_HOST_COLLECT;
-}
-
-
-/*
- * Manage NSM handles
- */
-static LIST_HEAD(nsm_handles);
-static DEFINE_MUTEX(nsm_mutex);
-
-static struct nsm_handle *
-__nsm_find(const struct sockaddr_in *sin,
- const char *hostname, unsigned int hostname_len,
- int create)
-{
- struct nsm_handle *nsm = NULL;
- struct list_head *pos;
-
- if (!sin)
- return NULL;
-
- if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
- if (printk_ratelimit()) {
- printk(KERN_WARNING "Invalid hostname \"%.*s\" "
- "in NFS lock request\n",
- hostname_len, hostname);
- }
- return NULL;
- }
-
- mutex_lock(&nsm_mutex);
- list_for_each(pos, &nsm_handles) {
- nsm = list_entry(pos, struct nsm_handle, sm_link);
-
- if (hostname && nsm_use_hostnames) {
- if (strlen(nsm->sm_name) != hostname_len
- || memcmp(nsm->sm_name, hostname, hostname_len))
- continue;
- } else if (!nlm_cmp_addr(&nsm->sm_addr, sin))
+ for_each_host_safe(host, next, chain, nlm_server_hosts) {
+ if (net && host->net != net)
continue;
- atomic_inc(&nsm->sm_count);
- goto out;
- }
-
- if (!create) {
- nsm = NULL;
- goto out;
- }
-
- nsm = kzalloc(sizeof(*nsm) + hostname_len + 1, GFP_KERNEL);
- if (nsm != NULL) {
- nsm->sm_addr = *sin;
- nsm->sm_name = (char *) (nsm + 1);
- memcpy(nsm->sm_name, hostname, hostname_len);
- nsm->sm_name[hostname_len] = '\0';
- atomic_set(&nsm->sm_count, 1);
-
- list_add(&nsm->sm_link, &nsm_handles);
+ if (atomic_read(&host->h_count) || host->h_inuse
+ || time_before(jiffies, host->h_expires)) {
+ dprintk("nlm_gc_hosts skipping %s "
+ "(cnt %d use %d exp %ld net %p)\n",
+ host->h_name, atomic_read(&host->h_count),
+ host->h_inuse, host->h_expires, host->net);
+ continue;
+ }
+ nlm_destroy_host_locked(host);
}
-out:
- mutex_unlock(&nsm_mutex);
- return nsm;
-}
-
-static struct nsm_handle *
-nsm_find(const struct sockaddr_in *sin, const char *hostname,
- unsigned int hostname_len)
-{
- return __nsm_find(sin, hostname, hostname_len, 1);
-}
+ if (net) {
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
-/*
- * Release an NSM handle
- */
-void
-nsm_release(struct nsm_handle *nsm)
-{
- if (!nsm)
- return;
- if (atomic_dec_and_test(&nsm->sm_count)) {
- mutex_lock(&nsm_mutex);
- if (atomic_read(&nsm->sm_count) == 0) {
- list_del(&nsm->sm_link);
- kfree(nsm);
- }
- mutex_unlock(&nsm_mutex);
+ ln->next_gc = jiffies + NLM_HOST_COLLECT;
}
}
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 908b23fadd0..1812f026960 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -7,261 +7,606 @@
*/
#include <linux/types.h>
-#include <linux/utsname.h>
#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+
#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/xprtsock.h>
#include <linux/sunrpc/svc.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
+#include <asm/unaligned.h>
+
+#include "netns.h"
#define NLMDBG_FACILITY NLMDBG_MONITOR
+#define NSM_PROGRAM 100024
+#define NSM_VERSION 1
+
+enum {
+ NSMPROC_NULL,
+ NSMPROC_STAT,
+ NSMPROC_MON,
+ NSMPROC_UNMON,
+ NSMPROC_UNMON_ALL,
+ NSMPROC_SIMU_CRASH,
+ NSMPROC_NOTIFY,
+};
+
+struct nsm_args {
+ struct nsm_private *priv;
+ u32 prog; /* RPC callback info */
+ u32 vers;
+ u32 proc;
+
+ char *mon_name;
+ char *nodename;
+};
-static struct rpc_clnt * nsm_create(void);
+struct nsm_res {
+ u32 status;
+ u32 state;
+};
-static struct rpc_program nsm_program;
+static const struct rpc_program nsm_program;
+static LIST_HEAD(nsm_handles);
+static DEFINE_SPINLOCK(nsm_lock);
/*
* Local NSM state
*/
-int nsm_local_state;
+u32 __read_mostly nsm_local_state;
+bool __read_mostly nsm_use_hostnames;
-/*
- * Common procedure for SM_MON/SM_UNMON calls
- */
-static int
-nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res)
+static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
+{
+ return (struct sockaddr *)&nsm->sm_addr;
+}
+
+static struct rpc_clnt *nsm_create(struct net *net)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ struct rpc_create_args args = {
+ .net = net,
+ .protocol = XPRT_TRANSPORT_TCP,
+ .address = (struct sockaddr *)&sin,
+ .addrsize = sizeof(sin),
+ .servername = "rpc.statd",
+ .program = &nsm_program,
+ .version = NSM_VERSION,
+ .authflavor = RPC_AUTH_NULL,
+ .flags = RPC_CLNT_CREATE_NOPING,
+ };
+
+ return rpc_create(&args);
+}
+
+static struct rpc_clnt *nsm_client_set(struct lockd_net *ln,
+ struct rpc_clnt *clnt)
+{
+ spin_lock(&ln->nsm_clnt_lock);
+ if (ln->nsm_users == 0) {
+ if (clnt == NULL)
+ goto out;
+ ln->nsm_clnt = clnt;
+ }
+ clnt = ln->nsm_clnt;
+ ln->nsm_users++;
+out:
+ spin_unlock(&ln->nsm_clnt_lock);
+ return clnt;
+}
+
+static struct rpc_clnt *nsm_client_get(struct net *net)
+{
+ struct rpc_clnt *clnt, *new;
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ clnt = nsm_client_set(ln, NULL);
+ if (clnt != NULL)
+ goto out;
+
+ clnt = new = nsm_create(net);
+ if (IS_ERR(clnt))
+ goto out;
+
+ clnt = nsm_client_set(ln, new);
+ if (clnt != new)
+ rpc_shutdown_client(new);
+out:
+ return clnt;
+}
+
+static void nsm_client_put(struct net *net)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+ struct rpc_clnt *clnt = NULL;
+
+ spin_lock(&ln->nsm_clnt_lock);
+ ln->nsm_users--;
+ if (ln->nsm_users == 0) {
+ clnt = ln->nsm_clnt;
+ ln->nsm_clnt = NULL;
+ }
+ spin_unlock(&ln->nsm_clnt_lock);
+ if (clnt != NULL)
+ rpc_shutdown_client(clnt);
+}
+
+static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
+ struct rpc_clnt *clnt)
{
- struct rpc_clnt *clnt;
int status;
- struct nsm_args args;
+ struct nsm_args args = {
+ .priv = &nsm->sm_priv,
+ .prog = NLM_PROGRAM,
+ .vers = 3,
+ .proc = NLMPROC_NSM_NOTIFY,
+ .mon_name = nsm->sm_mon_name,
+ .nodename = clnt->cl_nodename,
+ };
struct rpc_message msg = {
.rpc_argp = &args,
.rpc_resp = res,
};
- clnt = nsm_create();
- if (IS_ERR(clnt)) {
- status = PTR_ERR(clnt);
- goto out;
- }
-
- memset(&args, 0, sizeof(args));
- args.mon_name = nsm->sm_name;
- args.addr = nsm->sm_addr.sin_addr.s_addr;
- args.prog = NLM_PROGRAM;
- args.vers = 3;
- args.proc = NLMPROC_NSM_NOTIFY;
memset(res, 0, sizeof(*res));
msg.rpc_proc = &clnt->cl_procinfo[proc];
- status = rpc_call_sync(clnt, &msg, 0);
+ status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
if (status < 0)
- printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
- status);
+ dprintk("lockd: NSM upcall RPC failed, status=%d\n",
+ status);
else
status = 0;
- rpc_shutdown_client(clnt);
- out:
return status;
}
-/*
- * Set up monitoring of a remote host
+/**
+ * nsm_monitor - Notify a peer in case we reboot
+ * @host: pointer to nlm_host of peer to notify
+ *
+ * If this peer is not already monitored, this function sends an
+ * upcall to the local rpc.statd to record the name/address of
+ * the peer to notify in case we reboot.
+ *
+ * Returns zero if the peer is monitored by the local rpc.statd;
+ * otherwise a negative errno value is returned.
*/
-int
-nsm_monitor(struct nlm_host *host)
+int nsm_monitor(const struct nlm_host *host)
{
struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res;
int status;
+ struct rpc_clnt *clnt;
- dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
- BUG_ON(nsm == NULL);
+ dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
if (nsm->sm_monitored)
return 0;
- status = nsm_mon_unmon(nsm, SM_MON, &res);
+ /*
+ * Choose whether to record the caller_name or IP address of
+ * this peer in the local rpc.statd's database.
+ */
+ nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
- if (status < 0 || res.status != 0)
- printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
- else
- nsm->sm_monitored = 1;
- return status;
+ clnt = nsm_client_get(host->net);
+ if (IS_ERR(clnt)) {
+ status = PTR_ERR(clnt);
+ dprintk("lockd: failed to create NSM upcall transport, "
+ "status=%d, net=%p\n", status, host->net);
+ return status;
+ }
+
+ status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, clnt);
+ if (unlikely(res.status != 0))
+ status = -EIO;
+ if (unlikely(status < 0)) {
+ printk(KERN_NOTICE "lockd: cannot monitor %s\n", nsm->sm_name);
+ return status;
+ }
+
+ nsm->sm_monitored = 1;
+ if (unlikely(nsm_local_state != res.state)) {
+ nsm_local_state = res.state;
+ dprintk("lockd: NSM state changed to %d\n", nsm_local_state);
+ }
+ return 0;
}
-/*
- * Cease to monitor remote host
+/**
+ * nsm_unmonitor - Unregister peer notification
+ * @host: pointer to nlm_host of peer to stop monitoring
+ *
+ * If this peer is monitored, this function sends an upcall to
+ * tell the local rpc.statd not to send this peer a notification
+ * when we reboot.
*/
-int
-nsm_unmonitor(struct nlm_host *host)
+void nsm_unmonitor(const struct nlm_host *host)
{
struct nsm_handle *nsm = host->h_nsmhandle;
struct nsm_res res;
- int status = 0;
-
- if (nsm == NULL)
- return 0;
- host->h_nsmhandle = NULL;
+ int status;
if (atomic_read(&nsm->sm_count) == 1
&& nsm->sm_monitored && !nsm->sm_sticky) {
- dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
+ struct lockd_net *ln = net_generic(host->net, lockd_net_id);
- status = nsm_mon_unmon(nsm, SM_UNMON, &res);
+ dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
+
+ status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, ln->nsm_clnt);
+ if (res.status != 0)
+ status = -EIO;
if (status < 0)
printk(KERN_NOTICE "lockd: cannot unmonitor %s\n",
- host->h_name);
+ nsm->sm_name);
else
nsm->sm_monitored = 0;
+
+ nsm_client_put(host->net);
}
- nsm_release(nsm);
- return status;
+}
+
+static struct nsm_handle *nsm_lookup_hostname(const char *hostname,
+ const size_t len)
+{
+ struct nsm_handle *nsm;
+
+ list_for_each_entry(nsm, &nsm_handles, sm_link)
+ if (strlen(nsm->sm_name) == len &&
+ memcmp(nsm->sm_name, hostname, len) == 0)
+ return nsm;
+ return NULL;
+}
+
+static struct nsm_handle *nsm_lookup_addr(const struct sockaddr *sap)
+{
+ struct nsm_handle *nsm;
+
+ list_for_each_entry(nsm, &nsm_handles, sm_link)
+ if (rpc_cmp_addr(nsm_addr(nsm), sap))
+ return nsm;
+ return NULL;
+}
+
+static struct nsm_handle *nsm_lookup_priv(const struct nsm_private *priv)
+{
+ struct nsm_handle *nsm;
+
+ list_for_each_entry(nsm, &nsm_handles, sm_link)
+ if (memcmp(nsm->sm_priv.data, priv->data,
+ sizeof(priv->data)) == 0)
+ return nsm;
+ return NULL;
}
/*
- * Create NSM client for the local host
+ * Construct a unique cookie to match this nsm_handle to this monitored
+ * host. It is passed to the local rpc.statd via NSMPROC_MON, and
+ * returned via NLMPROC_SM_NOTIFY, in the "priv" field of these
+ * requests.
+ *
+ * The NSM protocol requires that these cookies be unique while the
+ * system is running. We prefer a stronger requirement of making them
+ * unique across reboots. If user space bugs cause a stale cookie to
+ * be sent to the kernel, it could cause the wrong host to lose its
+ * lock state if cookies were not unique across reboots.
+ *
+ * The cookies are exposed only to local user space via loopback. They
+ * do not appear on the physical network. If we want greater security
+ * for some reason, nsm_init_private() could perform a one-way hash to
+ * obscure the contents of the cookie.
*/
-static struct rpc_clnt *
-nsm_create(void)
+static void nsm_init_private(struct nsm_handle *nsm)
{
- struct sockaddr_in sin = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
- .sin_port = 0,
- };
- struct rpc_create_args args = {
- .protocol = XPRT_TRANSPORT_UDP,
- .address = (struct sockaddr *)&sin,
- .addrsize = sizeof(sin),
- .servername = "localhost",
- .program = &nsm_program,
- .version = SM_VERSION,
- .authflavor = RPC_AUTH_NULL,
- };
+ u64 *p = (u64 *)&nsm->sm_priv.data;
+ struct timespec ts;
+ s64 ns;
+
+ ktime_get_ts(&ts);
+ ns = timespec_to_ns(&ts);
+ put_unaligned(ns, p);
+ put_unaligned((unsigned long)nsm, p + 1);
+}
- return rpc_create(&args);
+static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
+ const size_t salen,
+ const char *hostname,
+ const size_t hostname_len)
+{
+ struct nsm_handle *new;
+
+ new = kzalloc(sizeof(*new) + hostname_len + 1, GFP_KERNEL);
+ if (unlikely(new == NULL))
+ return NULL;
+
+ atomic_set(&new->sm_count, 1);
+ new->sm_name = (char *)(new + 1);
+ memcpy(nsm_addr(new), sap, salen);
+ new->sm_addrlen = salen;
+ nsm_init_private(new);
+
+ if (rpc_ntop(nsm_addr(new), new->sm_addrbuf,
+ sizeof(new->sm_addrbuf)) == 0)
+ (void)snprintf(new->sm_addrbuf, sizeof(new->sm_addrbuf),
+ "unsupported address family");
+ memcpy(new->sm_name, hostname, hostname_len);
+ new->sm_name[hostname_len] = '\0';
+
+ return new;
+}
+
+/**
+ * nsm_get_handle - Find or create a cached nsm_handle
+ * @sap: pointer to socket address of handle to find
+ * @salen: length of socket address
+ * @hostname: pointer to C string containing hostname to find
+ * @hostname_len: length of C string
+ *
+ * Behavior is modulated by the global nsm_use_hostnames variable.
+ *
+ * Returns a cached nsm_handle after bumping its ref count, or
+ * returns a fresh nsm_handle if a handle that matches @sap and/or
+ * @hostname cannot be found in the handle cache. Returns NULL if
+ * an error occurs.
+ */
+struct nsm_handle *nsm_get_handle(const struct sockaddr *sap,
+ const size_t salen, const char *hostname,
+ const size_t hostname_len)
+{
+ struct nsm_handle *cached, *new = NULL;
+
+ if (hostname && memchr(hostname, '/', hostname_len) != NULL) {
+ if (printk_ratelimit()) {
+ printk(KERN_WARNING "Invalid hostname \"%.*s\" "
+ "in NFS lock request\n",
+ (int)hostname_len, hostname);
+ }
+ return NULL;
+ }
+
+retry:
+ spin_lock(&nsm_lock);
+
+ if (nsm_use_hostnames && hostname != NULL)
+ cached = nsm_lookup_hostname(hostname, hostname_len);
+ else
+ cached = nsm_lookup_addr(sap);
+
+ if (cached != NULL) {
+ atomic_inc(&cached->sm_count);
+ spin_unlock(&nsm_lock);
+ kfree(new);
+ dprintk("lockd: found nsm_handle for %s (%s), "
+ "cnt %d\n", cached->sm_name,
+ cached->sm_addrbuf,
+ atomic_read(&cached->sm_count));
+ return cached;
+ }
+
+ if (new != NULL) {
+ list_add(&new->sm_link, &nsm_handles);
+ spin_unlock(&nsm_lock);
+ dprintk("lockd: created nsm_handle for %s (%s)\n",
+ new->sm_name, new->sm_addrbuf);
+ return new;
+ }
+
+ spin_unlock(&nsm_lock);
+
+ new = nsm_create_handle(sap, salen, hostname, hostname_len);
+ if (unlikely(new == NULL))
+ return NULL;
+ goto retry;
+}
+
+/**
+ * nsm_reboot_lookup - match NLMPROC_SM_NOTIFY arguments to an nsm_handle
+ * @info: pointer to NLMPROC_SM_NOTIFY arguments
+ *
+ * Returns a matching nsm_handle if found in the nsm cache. The returned
+ * nsm_handle's reference count is bumped. Otherwise returns NULL if some
+ * error occurred.
+ */
+struct nsm_handle *nsm_reboot_lookup(const struct nlm_reboot *info)
+{
+ struct nsm_handle *cached;
+
+ spin_lock(&nsm_lock);
+
+ cached = nsm_lookup_priv(&info->priv);
+ if (unlikely(cached == NULL)) {
+ spin_unlock(&nsm_lock);
+ dprintk("lockd: never saw rebooted peer '%.*s' before\n",
+ info->len, info->mon);
+ return cached;
+ }
+
+ atomic_inc(&cached->sm_count);
+ spin_unlock(&nsm_lock);
+
+ dprintk("lockd: host %s (%s) rebooted, cnt %d\n",
+ cached->sm_name, cached->sm_addrbuf,
+ atomic_read(&cached->sm_count));
+ return cached;
+}
+
+/**
+ * nsm_release - Release an NSM handle
+ * @nsm: pointer to handle to be released
+ *
+ */
+void nsm_release(struct nsm_handle *nsm)
+{
+ if (atomic_dec_and_lock(&nsm->sm_count, &nsm_lock)) {
+ list_del(&nsm->sm_link);
+ spin_unlock(&nsm_lock);
+ dprintk("lockd: destroyed nsm_handle for %s (%s)\n",
+ nsm->sm_name, nsm->sm_addrbuf);
+ kfree(nsm);
+ }
}
/*
* XDR functions for NSM.
+ *
+ * See http://www.opengroup.org/ for details on the Network
+ * Status Monitor wire protocol.
*/
-static __be32 *
-xdr_encode_common(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+static void encode_nsm_string(struct xdr_stream *xdr, const char *string)
{
- char buffer[20], *name;
+ const u32 len = strlen(string);
+ __be32 *p;
- /*
- * Use the dotted-quad IP address of the remote host as
- * identifier. Linux statd always looks up the canonical
- * hostname first for whatever remote hostname it receives,
- * so this works alright.
- */
- if (nsm_use_hostnames) {
- name = argp->mon_name;
- } else {
- sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(argp->addr));
- name = buffer;
- }
- if (!(p = xdr_encode_string(p, name))
- || !(p = xdr_encode_string(p, utsname()->nodename)))
- return ERR_PTR(-EIO);
- *p++ = htonl(argp->prog);
- *p++ = htonl(argp->vers);
- *p++ = htonl(argp->proc);
-
- return p;
+ p = xdr_reserve_space(xdr, 4 + len);
+ xdr_encode_opaque(p, string, len);
}
-static int
-xdr_encode_mon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+/*
+ * "mon_name" specifies the host to be monitored.
+ */
+static void encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
{
- p = xdr_encode_common(rqstp, p, argp);
- if (IS_ERR(p))
- return PTR_ERR(p);
-
- /* Surprise - there may even be room for an IPv6 address now */
- *p++ = argp->addr;
- *p++ = 0;
- *p++ = 0;
- *p++ = 0;
- rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
- return 0;
+ encode_nsm_string(xdr, argp->mon_name);
}
-static int
-xdr_encode_unmon(struct rpc_rqst *rqstp, __be32 *p, struct nsm_args *argp)
+/*
+ * The "my_id" argument specifies the hostname and RPC procedure
+ * to be called when the status manager receives notification
+ * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
+ * has changed.
+ */
+static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
{
- p = xdr_encode_common(rqstp, p, argp);
- if (IS_ERR(p))
- return PTR_ERR(p);
- rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
- return 0;
+ __be32 *p;
+
+ encode_nsm_string(xdr, argp->nodename);
+ p = xdr_reserve_space(xdr, 4 + 4 + 4);
+ *p++ = cpu_to_be32(argp->prog);
+ *p++ = cpu_to_be32(argp->vers);
+ *p = cpu_to_be32(argp->proc);
+}
+
+/*
+ * The "mon_id" argument specifies the non-private arguments
+ * of an NSMPROC_MON or NSMPROC_UNMON call.
+ */
+static void encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
+{
+ encode_mon_name(xdr, argp);
+ encode_my_id(xdr, argp);
+}
+
+/*
+ * The "priv" argument may contain private information required
+ * by the NSMPROC_MON call. This information will be supplied in the
+ * NLMPROC_SM_NOTIFY call.
+ */
+static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
+{
+ __be32 *p;
+
+ p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
+ xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
+}
+
+static void nsm_xdr_enc_mon(struct rpc_rqst *req, struct xdr_stream *xdr,
+ const struct nsm_args *argp)
+{
+ encode_mon_id(xdr, argp);
+ encode_priv(xdr, argp);
+}
+
+static void nsm_xdr_enc_unmon(struct rpc_rqst *req, struct xdr_stream *xdr,
+ const struct nsm_args *argp)
+{
+ encode_mon_id(xdr, argp);
}
-static int
-xdr_decode_stat_res(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
+static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
+ struct xdr_stream *xdr,
+ struct nsm_res *resp)
{
- resp->status = ntohl(*p++);
- resp->state = ntohl(*p++);
- dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
- resp->status, resp->state);
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4 + 4);
+ if (unlikely(p == NULL))
+ return -EIO;
+ resp->status = be32_to_cpup(p++);
+ resp->state = be32_to_cpup(p);
+
+ dprintk("lockd: %s status %d state %d\n",
+ __func__, resp->status, resp->state);
return 0;
}
-static int
-xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
+static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,
+ struct xdr_stream *xdr,
+ struct nsm_res *resp)
{
- resp->state = ntohl(*p++);
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(p == NULL))
+ return -EIO;
+ resp->state = be32_to_cpup(p);
+
+ dprintk("lockd: %s state %d\n", __func__, resp->state);
return 0;
}
#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
-#define SM_my_id_sz (3+1+SM_my_name_sz)
-#define SM_mon_id_sz (1+XDR_QUADLEN(20)+SM_my_id_sz)
-#define SM_mon_sz (SM_mon_id_sz+4)
+#define SM_my_id_sz (SM_my_name_sz+3)
+#define SM_mon_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
+#define SM_mon_id_sz (SM_mon_name_sz+SM_my_id_sz)
+#define SM_priv_sz (XDR_QUADLEN(SM_PRIV_SIZE))
+#define SM_mon_sz (SM_mon_id_sz+SM_priv_sz)
#define SM_monres_sz 2
#define SM_unmonres_sz 1
static struct rpc_procinfo nsm_procedures[] = {
-[SM_MON] = {
- .p_proc = SM_MON,
- .p_encode = (kxdrproc_t) xdr_encode_mon,
- .p_decode = (kxdrproc_t) xdr_decode_stat_res,
+[NSMPROC_MON] = {
+ .p_proc = NSMPROC_MON,
+ .p_encode = (kxdreproc_t)nsm_xdr_enc_mon,
+ .p_decode = (kxdrdproc_t)nsm_xdr_dec_stat_res,
.p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz,
- .p_statidx = SM_MON,
+ .p_statidx = NSMPROC_MON,
.p_name = "MONITOR",
},
-[SM_UNMON] = {
- .p_proc = SM_UNMON,
- .p_encode = (kxdrproc_t) xdr_encode_unmon,
- .p_decode = (kxdrproc_t) xdr_decode_stat,
+[NSMPROC_UNMON] = {
+ .p_proc = NSMPROC_UNMON,
+ .p_encode = (kxdreproc_t)nsm_xdr_enc_unmon,
+ .p_decode = (kxdrdproc_t)nsm_xdr_dec_stat,
.p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz,
- .p_statidx = SM_UNMON,
+ .p_statidx = NSMPROC_UNMON,
.p_name = "UNMONITOR",
},
};
-static struct rpc_version nsm_version1 = {
+static const struct rpc_version nsm_version1 = {
.number = 1,
.nrprocs = ARRAY_SIZE(nsm_procedures),
.procs = nsm_procedures
};
-static struct rpc_version * nsm_version[] = {
+static const struct rpc_version *nsm_version[] = {
[1] = &nsm_version1,
};
static struct rpc_stat nsm_stats;
-static struct rpc_program nsm_program = {
+static const struct rpc_program nsm_program = {
.name = "statd",
- .number = SM_PROGRAM,
+ .number = NSM_PROGRAM,
.nrvers = ARRAY_SIZE(nsm_version),
.version = nsm_version,
.stats = &nsm_stats
diff --git a/fs/lockd/netns.h b/fs/lockd/netns.h
new file mode 100644
index 00000000000..5010b55628b
--- /dev/null
+++ b/fs/lockd/netns.h
@@ -0,0 +1,23 @@
+#ifndef __LOCKD_NETNS_H__
+#define __LOCKD_NETNS_H__
+
+#include <linux/fs.h>
+#include <net/netns/generic.h>
+
+struct lockd_net {
+ unsigned int nlmsvc_users;
+ unsigned long next_gc;
+ unsigned long nrhosts;
+
+ struct delayed_work grace_period_end;
+ struct lock_manager lockd_manager;
+ struct list_head grace_list;
+
+ spinlock_t nsm_clnt_lock;
+ unsigned int nsm_users;
+ struct rpc_clnt *nsm_clnt;
+};
+
+extern int lockd_net_id;
+
+#endif
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 1ed8bd4de94..8f27c93f8d2 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -21,10 +21,9 @@
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/uio.h>
-#include <linux/slab.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/sunrpc/types.h>
@@ -34,9 +33,10 @@
#include <linux/sunrpc/svcsock.h>
#include <net/ip.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
#include <linux/nfs.h>
+#include "netns.h"
+
#define NLMDBG_FACILITY NLMDBG_SVC
#define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE)
#define ALLOWED_SIGS (sigmask(SIGKILL))
@@ -44,17 +44,15 @@
static struct svc_program nlmsvc_program;
struct nlmsvc_binding * nlmsvc_ops;
-EXPORT_SYMBOL(nlmsvc_ops);
+EXPORT_SYMBOL_GPL(nlmsvc_ops);
static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users;
-static pid_t nlmsvc_pid;
-static struct svc_serv *nlmsvc_serv;
-int nlmsvc_grace_period;
+static struct task_struct *nlmsvc_task;
+static struct svc_rqst *nlmsvc_rqst;
unsigned long nlmsvc_timeout;
-static DECLARE_COMPLETION(lockd_start_done);
-static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
+int lockd_net_id;
/*
* These can be set at insmod time (useful for NFS as root filesystem),
@@ -63,7 +61,9 @@ static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
static unsigned long nlm_grace_period;
static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
static int nlm_udpport, nlm_tcpport;
-int nsm_use_hostnames = 0;
+
+/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */
+static unsigned int nlm_max_connections = 1024;
/*
* Constants needed for the sysctl interface.
@@ -74,7 +74,9 @@ static const unsigned long nlm_timeout_min = 3;
static const unsigned long nlm_timeout_max = 20;
static const int nlm_port_min = 0, nlm_port_max = 65535;
+#ifdef CONFIG_SYSCTL
static struct ctl_table_header * nlm_sysctl_table;
+#endif
static unsigned long get_lockd_grace_period(void)
{
@@ -85,57 +87,52 @@ static unsigned long get_lockd_grace_period(void)
return nlm_timeout * 5 * HZ;
}
-unsigned long get_nfs_grace_period(void)
+static void grace_ender(struct work_struct *grace)
{
- unsigned long lockdgrace = get_lockd_grace_period();
- unsigned long nfsdgrace = 0;
+ struct delayed_work *dwork = container_of(grace, struct delayed_work,
+ work);
+ struct lockd_net *ln = container_of(dwork, struct lockd_net,
+ grace_period_end);
- if (nlmsvc_ops)
- nfsdgrace = nlmsvc_ops->get_grace_period();
-
- return max(lockdgrace, nfsdgrace);
+ locks_end_grace(&ln->lockd_manager);
}
-EXPORT_SYMBOL(get_nfs_grace_period);
-static unsigned long set_grace_period(void)
+static void set_grace_period(struct net *net)
{
- nlmsvc_grace_period = 1;
- return get_nfs_grace_period() + jiffies;
+ unsigned long grace_period = get_lockd_grace_period();
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ locks_start_grace(net, &ln->lockd_manager);
+ cancel_delayed_work_sync(&ln->grace_period_end);
+ schedule_delayed_work(&ln->grace_period_end, grace_period);
}
-static inline void clear_grace_period(void)
+static void restart_grace(void)
{
- nlmsvc_grace_period = 0;
+ if (nlmsvc_ops) {
+ struct net *net = &init_net;
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ cancel_delayed_work_sync(&ln->grace_period_end);
+ locks_end_grace(&ln->lockd_manager);
+ nlmsvc_invalidate_all();
+ set_grace_period(net);
+ }
}
/*
* This is the lockd kernel thread
*/
-static void
-lockd(struct svc_rqst *rqstp)
+static int
+lockd(void *vrqstp)
{
int err = 0;
- unsigned long grace_period_expire;
+ struct svc_rqst *rqstp = vrqstp;
- /* Lock module and set up kernel thread */
- /* lockd_up is waiting for us to startup, so will
- * be holding a reference to this module, so it
- * is safe to just claim another reference
- */
- __module_get(THIS_MODULE);
- lock_kernel();
-
- /*
- * Let our maker know we're running.
- */
- nlmsvc_pid = current->pid;
- nlmsvc_serv = rqstp->rq_server;
- complete(&lockd_start_done);
-
- daemonize("lockd");
+ /* try_to_freeze() is called from svc_recv() */
set_freezable();
- /* Process request with signals blocked, but allow SIGKILL. */
+ /* Allow SIGKILL to tell lockd to drop all of its locks */
allow_signal(SIGKILL);
dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
@@ -144,35 +141,24 @@ lockd(struct svc_rqst *rqstp)
nlm_timeout = LOCKD_DFLT_TIMEO;
nlmsvc_timeout = nlm_timeout * HZ;
- grace_period_expire = set_grace_period();
-
/*
* The main request loop. We don't terminate until the last
- * NFS mount or NFS daemon has gone away, and we've been sent a
- * signal, or else another process has taken over our job.
+ * NFS mount or NFS daemon has gone away.
*/
- while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) {
+ while (!kthread_should_stop()) {
long timeout = MAX_SCHEDULE_TIMEOUT;
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
+ /* update sv_maxconn if it has changed */
+ rqstp->rq_server->sv_maxconn = nlm_max_connections;
+
if (signalled()) {
flush_signals(current);
- if (nlmsvc_ops) {
- nlmsvc_invalidate_all();
- grace_period_expire = set_grace_period();
- }
+ restart_grace();
+ continue;
}
- /*
- * Retry any blocked locks that have been notified by
- * the VFS. Don't do this during grace period.
- * (Theoretically, there shouldn't even be blocked locks
- * during grace period).
- */
- if (!nlmsvc_grace_period) {
- timeout = nlmsvc_retry_blocked();
- } else if (time_before(grace_period_expire, jiffies))
- clear_grace_period();
+ timeout = nlmsvc_retry_blocked();
/*
* Find a socket with data available and call its
@@ -181,97 +167,177 @@ lockd(struct svc_rqst *rqstp)
err = svc_recv(rqstp, timeout);
if (err == -EAGAIN || err == -EINTR)
continue;
- if (err < 0) {
- printk(KERN_WARNING
- "lockd: terminating on error %d\n",
- -err);
- break;
- }
-
dprintk("lockd: request from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
svc_process(rqstp);
}
-
flush_signals(current);
+ if (nlmsvc_ops)
+ nlmsvc_invalidate_all();
+ nlm_shutdown_hosts();
+ return 0;
+}
- /*
- * Check whether there's a new lockd process before
- * shutting down the hosts and clearing the slot.
- */
- if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
- if (nlmsvc_ops)
- nlmsvc_invalidate_all();
- nlm_shutdown_hosts();
- nlmsvc_pid = 0;
- nlmsvc_serv = NULL;
- } else
- printk(KERN_DEBUG
- "lockd: new process, skipping host shutdown\n");
- wake_up(&lockd_exit);
-
- /* Exit the RPC thread */
- svc_exit_thread(rqstp);
-
- /* Release module */
- unlock_kernel();
- module_put_and_exit(0);
+static int create_lockd_listener(struct svc_serv *serv, const char *name,
+ struct net *net, const int family,
+ const unsigned short port)
+{
+ struct svc_xprt *xprt;
+
+ xprt = svc_find_xprt(serv, name, net, family, 0);
+ if (xprt == NULL)
+ return svc_create_xprt(serv, name, net, family, port,
+ SVC_SOCK_DEFAULTS);
+ svc_xprt_put(xprt);
+ return 0;
+}
+
+static int create_lockd_family(struct svc_serv *serv, struct net *net,
+ const int family)
+{
+ int err;
+
+ err = create_lockd_listener(serv, "udp", net, family, nlm_udpport);
+ if (err < 0)
+ return err;
+
+ return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport);
}
/*
- * Make any sockets that are needed but not present.
- * If nlm_udpport or nlm_tcpport were set as module
- * options, make those sockets unconditionally
+ * Ensure there are active UDP and TCP listeners for lockd.
+ *
+ * Even if we have only TCP NFS mounts and/or TCP NFSDs, some
+ * local services (such as rpc.statd) still require UDP, and
+ * some NFS servers do not yet support NLM over TCP.
+ *
+ * Returns zero if all listeners are available; otherwise a
+ * negative errno value is returned.
*/
-static int make_socks(struct svc_serv *serv, int proto)
+static int make_socks(struct svc_serv *serv, struct net *net)
{
static int warned;
- struct svc_xprt *xprt;
- int err = 0;
-
- if (proto == IPPROTO_UDP || nlm_udpport) {
- xprt = svc_find_xprt(serv, "udp", 0, 0);
- if (!xprt)
- err = svc_create_xprt(serv, "udp", nlm_udpport,
- SVC_SOCK_DEFAULTS);
- else
- svc_xprt_put(xprt);
+ int err;
+
+ err = create_lockd_family(serv, net, PF_INET);
+ if (err < 0)
+ goto out_err;
+
+ err = create_lockd_family(serv, net, PF_INET6);
+ if (err < 0 && err != -EAFNOSUPPORT)
+ goto out_err;
+
+ warned = 0;
+ return 0;
+
+out_err:
+ if (warned++ == 0)
+ printk(KERN_WARNING
+ "lockd_up: makesock failed, error=%d\n", err);
+ svc_shutdown_net(serv, net);
+ return err;
+}
+
+static int lockd_up_net(struct svc_serv *serv, struct net *net)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+ int error;
+
+ if (ln->nlmsvc_users++)
+ return 0;
+
+ error = svc_bind(serv, net);
+ if (error)
+ goto err_bind;
+
+ error = make_socks(serv, net);
+ if (error < 0)
+ goto err_socks;
+ set_grace_period(net);
+ dprintk("lockd_up_net: per-net data created; net=%p\n", net);
+ return 0;
+
+err_socks:
+ svc_rpcb_cleanup(serv, net);
+err_bind:
+ ln->nlmsvc_users--;
+ return error;
+}
+
+static void lockd_down_net(struct svc_serv *serv, struct net *net)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ if (ln->nlmsvc_users) {
+ if (--ln->nlmsvc_users == 0) {
+ nlm_shutdown_hosts_net(net);
+ cancel_delayed_work_sync(&ln->grace_period_end);
+ locks_end_grace(&ln->lockd_manager);
+ svc_shutdown_net(serv, net);
+ dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net);
+ }
+ } else {
+ printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n",
+ nlmsvc_task, net);
+ BUG();
}
- if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) {
- xprt = svc_find_xprt(serv, "tcp", 0, 0);
- if (!xprt)
- err = svc_create_xprt(serv, "tcp", nlm_tcpport,
- SVC_SOCK_DEFAULTS);
- else
- svc_xprt_put(xprt);
+}
+
+static int lockd_start_svc(struct svc_serv *serv)
+{
+ int error;
+
+ if (nlmsvc_rqst)
+ return 0;
+
+ /*
+ * Create the kernel thread and wait for it to start.
+ */
+ nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
+ if (IS_ERR(nlmsvc_rqst)) {
+ error = PTR_ERR(nlmsvc_rqst);
+ printk(KERN_WARNING
+ "lockd_up: svc_rqst allocation failed, error=%d\n",
+ error);
+ goto out_rqst;
}
- if (err >= 0) {
- warned = 0;
- err = 0;
- } else if (warned++ == 0)
+
+ svc_sock_update_bufs(serv);
+ serv->sv_maxconn = nlm_max_connections;
+
+ nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, "%s", serv->sv_name);
+ if (IS_ERR(nlmsvc_task)) {
+ error = PTR_ERR(nlmsvc_task);
printk(KERN_WARNING
- "lockd_up: makesock failed, error=%d\n", err);
- return err;
+ "lockd_up: kthread_run failed, error=%d\n", error);
+ goto out_task;
+ }
+ dprintk("lockd_up: service started\n");
+ return 0;
+
+out_task:
+ svc_exit_thread(nlmsvc_rqst);
+ nlmsvc_task = NULL;
+out_rqst:
+ nlmsvc_rqst = NULL;
+ return error;
}
-/*
- * Bring up the lockd process if it's not already up.
- */
-int
-lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
+static struct svc_serv *lockd_create_svc(void)
{
- struct svc_serv * serv;
- int error = 0;
+ struct svc_serv *serv;
- mutex_lock(&nlmsvc_mutex);
/*
* Check whether we're already up and running.
*/
- if (nlmsvc_pid) {
- if (proto)
- error = make_socks(nlmsvc_serv, proto);
- goto out;
+ if (nlmsvc_rqst) {
+ /*
+ * Note: increase service usage, because later in case of error
+ * svc_destroy() will be called.
+ */
+ svc_get(nlmsvc_rqst->rq_server);
+ return nlmsvc_rqst->rq_server;
}
/*
@@ -282,167 +348,168 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */
printk(KERN_WARNING
"lockd_up: no pid, %d users??\n", nlmsvc_users);
- error = -ENOMEM;
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
if (!serv) {
printk(KERN_WARNING "lockd_up: create service failed\n");
- goto out;
+ return ERR_PTR(-ENOMEM);
}
+ dprintk("lockd_up: service created\n");
+ return serv;
+}
+
+/*
+ * Bring up the lockd process if it's not already up.
+ */
+int lockd_up(struct net *net)
+{
+ struct svc_serv *serv;
+ int error;
- if ((error = make_socks(serv, proto)) < 0)
- goto destroy_and_out;
+ mutex_lock(&nlmsvc_mutex);
- /*
- * Create the kernel thread and wait for it to start.
- */
- error = svc_create_thread(lockd, serv);
- if (error) {
- printk(KERN_WARNING
- "lockd_up: create thread failed, error=%d\n", error);
- goto destroy_and_out;
+ serv = lockd_create_svc();
+ if (IS_ERR(serv)) {
+ error = PTR_ERR(serv);
+ goto err_create;
}
- wait_for_completion(&lockd_start_done);
+ error = lockd_up_net(serv, net);
+ if (error < 0)
+ goto err_net;
+
+ error = lockd_start_svc(serv);
+ if (error < 0)
+ goto err_start;
+
+ nlmsvc_users++;
/*
* Note: svc_serv structures have an initial use count of 1,
* so we exit through here on both success and failure.
*/
-destroy_and_out:
+err_net:
svc_destroy(serv);
-out:
- if (!error)
- nlmsvc_users++;
+err_create:
mutex_unlock(&nlmsvc_mutex);
return error;
+
+err_start:
+ lockd_down_net(serv, net);
+ goto err_net;
}
-EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL_GPL(lockd_up);
/*
* Decrement the user count and bring down lockd if we're the last.
*/
void
-lockd_down(void)
+lockd_down(struct net *net)
{
- static int warned;
-
mutex_lock(&nlmsvc_mutex);
+ lockd_down_net(nlmsvc_rqst->rq_server, net);
if (nlmsvc_users) {
if (--nlmsvc_users)
goto out;
- } else
- printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid);
-
- if (!nlmsvc_pid) {
- if (warned++ == 0)
- printk(KERN_WARNING "lockd_down: no lockd running.\n");
- goto out;
+ } else {
+ printk(KERN_ERR "lockd_down: no users! task=%p\n",
+ nlmsvc_task);
+ BUG();
}
- warned = 0;
- kill_proc(nlmsvc_pid, SIGKILL, 1);
- /*
- * Wait for the lockd process to exit, but since we're holding
- * the lockd semaphore, we can't wait around forever ...
- */
- clear_thread_flag(TIF_SIGPENDING);
- interruptible_sleep_on_timeout(&lockd_exit, HZ);
- if (nlmsvc_pid) {
- printk(KERN_WARNING
- "lockd_down: lockd failed to exit, clearing pid\n");
- nlmsvc_pid = 0;
+ if (!nlmsvc_task) {
+ printk(KERN_ERR "lockd_down: no lockd running.\n");
+ BUG();
}
- spin_lock_irq(&current->sighand->siglock);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ kthread_stop(nlmsvc_task);
+ dprintk("lockd_down: service stopped\n");
+ svc_exit_thread(nlmsvc_rqst);
+ dprintk("lockd_down: service destroyed\n");
+ nlmsvc_task = NULL;
+ nlmsvc_rqst = NULL;
out:
mutex_unlock(&nlmsvc_mutex);
}
-EXPORT_SYMBOL(lockd_down);
+EXPORT_SYMBOL_GPL(lockd_down);
+
+#ifdef CONFIG_SYSCTL
/*
* Sysctl parameters (same as module parameters, different interface).
*/
-static ctl_table nlm_sysctls[] = {
+static struct ctl_table nlm_sysctls[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nlm_grace_period",
.data = &nlm_grace_period,
.maxlen = sizeof(unsigned long),
.mode = 0644,
- .proc_handler = &proc_doulongvec_minmax,
+ .proc_handler = proc_doulongvec_minmax,
.extra1 = (unsigned long *) &nlm_grace_period_min,
.extra2 = (unsigned long *) &nlm_grace_period_max,
},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nlm_timeout",
.data = &nlm_timeout,
.maxlen = sizeof(unsigned long),
.mode = 0644,
- .proc_handler = &proc_doulongvec_minmax,
+ .proc_handler = proc_doulongvec_minmax,
.extra1 = (unsigned long *) &nlm_timeout_min,
.extra2 = (unsigned long *) &nlm_timeout_max,
},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nlm_udpport",
.data = &nlm_udpport,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = (int *) &nlm_port_min,
.extra2 = (int *) &nlm_port_max,
},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nlm_tcpport",
.data = &nlm_tcpport,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = (int *) &nlm_port_min,
.extra2 = (int *) &nlm_port_max,
},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nsm_use_hostnames",
.data = &nsm_use_hostnames,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nsm_local_state",
.data = &nsm_local_state,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = proc_dointvec,
},
- { .ctl_name = 0 }
+ { }
};
-static ctl_table nlm_sysctl_dir[] = {
+static struct ctl_table nlm_sysctl_dir[] = {
{
- .ctl_name = CTL_UNNUMBERED,
.procname = "nfs",
.mode = 0555,
.child = nlm_sysctls,
},
- { .ctl_name = 0 }
+ { }
};
-static ctl_table nlm_sysctl_root[] = {
+static struct ctl_table nlm_sysctl_root[] = {
{
- .ctl_name = CTL_FS,
.procname = "fs",
.mode = 0555,
.child = nlm_sysctl_dir,
},
- { .ctl_name = 0 }
+ { }
};
+#endif /* CONFIG_SYSCTL */
+
/*
* Module (and sysfs) parameters.
*/
@@ -454,7 +521,7 @@ static int param_set_##name(const char *val, struct kernel_param *kp) \
__typeof__(type) num = which_strtol(val, &endp, 0); \
if (endp == val || *endp || num < (min) || num > (max)) \
return -EINVAL; \
- *((int *) kp->arg) = num; \
+ *((type *) kp->arg) = num; \
return 0; \
}
@@ -509,6 +576,29 @@ module_param_call(nlm_udpport, param_set_port, param_get_int,
module_param_call(nlm_tcpport, param_set_port, param_get_int,
&nlm_tcpport, 0644);
module_param(nsm_use_hostnames, bool, 0644);
+module_param(nlm_max_connections, uint, 0644);
+
+static int lockd_init_net(struct net *net)
+{
+ struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+ INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
+ INIT_LIST_HEAD(&ln->grace_list);
+ spin_lock_init(&ln->nsm_clnt_lock);
+ return 0;
+}
+
+static void lockd_exit_net(struct net *net)
+{
+}
+
+static struct pernet_operations lockd_net_ops = {
+ .init = lockd_init_net,
+ .exit = lockd_exit_net,
+ .id = &lockd_net_id,
+ .size = sizeof(struct lockd_net),
+};
+
/*
* Initialising and terminating the module.
@@ -516,15 +606,35 @@ module_param(nsm_use_hostnames, bool, 0644);
static int __init init_nlm(void)
{
+ int err;
+
+#ifdef CONFIG_SYSCTL
+ err = -ENOMEM;
nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
- return nlm_sysctl_table ? 0 : -ENOMEM;
+ if (nlm_sysctl_table == NULL)
+ goto err_sysctl;
+#endif
+ err = register_pernet_subsys(&lockd_net_ops);
+ if (err)
+ goto err_pernet;
+ return 0;
+
+err_pernet:
+#ifdef CONFIG_SYSCTL
+ unregister_sysctl_table(nlm_sysctl_table);
+err_sysctl:
+#endif
+ return err;
}
static void __exit exit_nlm(void)
{
/* FIXME: delete all NLM clients */
nlm_shutdown_hosts();
+ unregister_pernet_subsys(&lockd_net_ops);
+#ifdef CONFIG_SYSCTL
unregister_sysctl_table(nlm_sysctl_table);
+#endif
}
module_init(init_nlm);
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 385437e3387..b147d1ae71f 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -9,15 +9,9 @@
#include <linux/types.h>
#include <linux/time.h>
-#include <linux/slab.h>
-#include <linux/in.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/nfsd/nfsd.h>
#include <linux/lockd/lockd.h>
#include <linux/lockd/share.h>
-#include <linux/lockd/sm_inter.h>
-
+#include <linux/sunrpc/svc_xprt.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
@@ -58,8 +52,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0;
no_locks:
- if (host)
- nlm_release_host(host);
+ nlmsvc_release_host(host);
if (error)
return error;
return nlm_lck_denied_nolocks;
@@ -84,29 +77,23 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
- int rc = rpc_success;
+ __be32 rc = rpc_success;
dprintk("lockd: TEST4 called\n");
resp->cookie = argp->cookie;
- /* Don't accept test requests during grace period */
- if (nlmsvc_grace_period) {
- resp->status = nlm_lck_denied_grace_period;
- return rc;
- }
-
/* Obtain client and file */
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now check for conflicting locks */
- resp->status = nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie);
+ resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
@@ -117,18 +104,12 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
- int rc = rpc_success;
+ __be32 rc = rpc_success;
dprintk("lockd: LOCK called\n");
resp->cookie = argp->cookie;
- /* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period && !argp->reclaim) {
- resp->status = nlm_lck_denied_grace_period;
- return rc;
- }
-
/* Obtain client and file */
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -146,14 +127,15 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
#endif
/* Now try to lock the file */
- resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
- argp->block, &argp->cookie);
+ resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
+ argp->block, &argp->cookie,
+ argp->reclaim);
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
@@ -170,7 +152,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(SVC_NET(rqstp))) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -180,10 +162,10 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Try to cancel request. */
- resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+ resp->status = nlmsvc_cancel_blocked(SVC_NET(rqstp), file, &argp->lock);
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -203,7 +185,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(SVC_NET(rqstp))) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -213,10 +195,10 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now try to remove the lock */
- resp->status = nlmsvc_unlock(file, &argp->lock);
+ resp->status = nlmsvc_unlock(SVC_NET(rqstp), file, &argp->lock);
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -232,7 +214,7 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
dprintk("lockd: GRANTED called\n");
- resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+ resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
return rpc_success;
}
@@ -248,7 +230,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
static void nlm4svc_callback_release(void *data)
{
- nlm_release_call(data);
+ nlmsvc_release_call(data);
}
static const struct rpc_call_ops nlm4svc_callback_ops = {
@@ -275,12 +257,13 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
return rpc_system_err;
call = nlm_alloc_call(host);
+ nlmsvc_release_host(host);
if (call == NULL)
return rpc_system_err;
stat = func(rqstp, argp, &call->a_res);
if (stat != 0) {
- nlm_release_call(call);
+ nlmsvc_release_call(call);
return stat;
}
@@ -340,7 +323,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period && !argp->reclaim) {
+ if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -353,7 +336,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_share_file(host, file, argp);
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -373,7 +356,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(SVC_NET(rqstp))) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -386,7 +369,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = nlmsvc_unshare_file(host, file, argp);
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -418,7 +401,7 @@ nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success;
nlmsvc_free_host_resources(host);
- nlm_release_host(host);
+ nlmsvc_release_host(host);
return rpc_success;
}
@@ -429,26 +412,16 @@ static __be32
nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
void *resp)
{
- struct sockaddr_in saddr;
-
- memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
dprintk("lockd: SM_NOTIFY called\n");
- if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
- || ntohs(saddr.sin_port) >= 1024) {
+
+ if (!nlm_privileged_requester(rqstp)) {
char buf[RPC_MAX_ADDRBUFLEN];
printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
return rpc_system_err;
}
- /* Obtain the host pointer for this NFS server and try to
- * reclaim all locks we hold on this server.
- */
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_addr.s_addr = argp->addr;
- nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
-
+ nlm_host_rebooted(argp);
return rpc_success;
}
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index fe9bdb4a220..ab798a88ec1 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -21,14 +21,15 @@
*/
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/sunrpc/clnt.h>
-#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svc_xprt.h>
#include <linux/lockd/nlm.h>
#include <linux/lockd/lockd.h>
+#include <linux/kthread.h>
#define NLMDBG_FACILITY NLMDBG_SVCLOCK
@@ -50,12 +51,42 @@ static const struct rpc_call_ops nlmsvc_grant_ops;
* The list of blocked locks to retry
*/
static LIST_HEAD(nlm_blocked);
+static DEFINE_SPINLOCK(nlm_blocked_lock);
+
+#ifdef LOCKD_DEBUG
+static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
+{
+ /*
+ * We can get away with a static buffer because we're only
+ * called with BKL held.
+ */
+ 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;
+}
+#endif
/*
* Insert a blocked lock into the global list
*/
static void
-nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+nlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
{
struct nlm_block *b;
struct list_head *pos;
@@ -85,6 +116,13 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
block->b_when = when;
}
+static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+{
+ spin_lock(&nlm_blocked_lock);
+ nlmsvc_insert_block_locked(block, when);
+ spin_unlock(&nlm_blocked_lock);
+}
+
/*
* Remove a block from the global list
*/
@@ -92,7 +130,9 @@ static inline void
nlmsvc_remove_block(struct nlm_block *block)
{
if (!list_empty(&block->b_list)) {
+ spin_lock(&nlm_blocked_lock);
list_del_init(&block->b_list);
+ spin_unlock(&nlm_blocked_lock);
nlmsvc_release_block(block);
}
}
@@ -128,9 +168,9 @@ nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock)
static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
{
- if(a->len != b->len)
+ if (a->len != b->len)
return 0;
- if(memcmp(a->data,b->data,a->len))
+ if (memcmp(a->data, b->data, a->len))
return 0;
return 1;
}
@@ -221,13 +261,12 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
failed_free:
kfree(block);
failed:
- nlm_release_call(call);
+ nlmsvc_release_call(call);
return NULL;
}
/*
- * Delete a block. If the lock was cancelled or the grant callback
- * failed, unlock is set to 1.
+ * Delete a block.
* It is the caller's responsibility to check whether the file
* can be closed hereafter.
*/
@@ -237,7 +276,7 @@ static int nlmsvc_unlink_block(struct nlm_block *block)
dprintk("lockd: unlinking block %p...\n", block);
/* Remove block from list */
- status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl);
+ status = posix_unblock_lock(&block->b_call->a_args.lock.fl);
nlmsvc_remove_block(block);
return status;
}
@@ -250,12 +289,11 @@ static void nlmsvc_free_block(struct kref *kref)
dprintk("lockd: freeing block %p...\n", block);
/* Remove block from file's list of blocks */
- mutex_lock(&file->f_mutex);
list_del_init(&block->b_flist);
mutex_unlock(&file->f_mutex);
nlmsvc_freegrantargs(block->b_call);
- nlm_release_call(block->b_call);
+ nlmsvc_release_call(block->b_call);
nlm_release_file(block->b_file);
kfree(block->b_fl);
kfree(block);
@@ -264,7 +302,7 @@ static void nlmsvc_free_block(struct kref *kref)
static void nlmsvc_release_block(struct nlm_block *block)
{
if (block != NULL)
- kref_put(&block->b_count, nlmsvc_free_block);
+ kref_put_mutex(&block->b_count, nlmsvc_free_block, &block->b_file->f_mutex);
}
/*
@@ -325,6 +363,8 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
{
if (call->a_args.lock.oh.data != call->a_owner)
kfree(call->a_args.lock.oh.data);
+
+ locks_release_private(&call->a_args.lock.fl);
}
/*
@@ -358,26 +398,21 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block)
*/
__be32
nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
- struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
+ struct nlm_host *host, struct nlm_lock *lock, int wait,
+ struct nlm_cookie *cookie, int reclaim)
{
struct nlm_block *block = NULL;
- struct nlm_host *host;
int error;
__be32 ret;
dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
- file->f_file->f_path.dentry->d_inode->i_sb->s_id,
- file->f_file->f_path.dentry->d_inode->i_ino,
+ file_inode(file->f_file)->i_sb->s_id,
+ file_inode(file->f_file)->i_ino,
lock->fl.fl_type, lock->fl.fl_pid,
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end,
wait);
- /* Create host handle for callback */
- host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len);
- if (host == NULL)
- return nlm_lck_denied_nolocks;
-
/* Lock file against concurrent access */
mutex_lock(&file->f_mutex);
/* Get existing block (in case client is busy-waiting)
@@ -385,8 +420,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
*/
block = nlmsvc_lookup_block(file, lock);
if (block == NULL) {
- block = nlmsvc_create_block(rqstp, nlm_get_host(host), file,
- lock, cookie);
+ block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
ret = nlm_lck_denied_nolocks;
if (block == NULL)
goto out;
@@ -411,20 +445,36 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}
+ if (locks_in_grace(SVC_NET(rqstp)) && !reclaim) {
+ ret = nlm_lck_denied_grace_period;
+ goto out;
+ }
+ if (reclaim && !locks_in_grace(SVC_NET(rqstp))) {
+ ret = nlm_lck_denied_grace_period;
+ goto out;
+ }
+
if (!wait)
lock->fl.fl_flags &= ~FL_SLEEP;
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP;
dprintk("lockd: vfs_lock_file returned %d\n", error);
- switch(error) {
+ switch (error) {
case 0:
ret = nlm_granted;
goto out;
case -EAGAIN:
+ /*
+ * If this is a blocking request for an
+ * already pending lock request then we need
+ * to put it back on lockd's block list
+ */
+ if (wait)
+ break;
ret = nlm_lck_denied;
- break;
- case -EINPROGRESS:
+ goto out;
+ case FILE_LOCK_DEFERRED:
if (wait)
break;
/* Filesystem lock operation is in progress
@@ -439,10 +489,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}
- ret = nlm_lck_denied;
- if (!wait)
- goto out;
-
ret = nlm_lck_blocked;
/* Append to list of blocked */
@@ -450,7 +496,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
out:
mutex_unlock(&file->f_mutex);
nlmsvc_release_block(block);
- nlm_release_host(host);
dprintk("lockd: nlmsvc_lock returned %u\n", ret);
return ret;
}
@@ -460,16 +505,16 @@ out:
*/
__be32
nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
- struct nlm_lock *lock, struct nlm_lock *conflock,
- struct nlm_cookie *cookie)
+ struct nlm_host *host, struct nlm_lock *lock,
+ struct nlm_lock *conflock, struct nlm_cookie *cookie)
{
struct nlm_block *block = NULL;
int error;
__be32 ret;
dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n",
- file->f_file->f_path.dentry->d_inode->i_sb->s_id,
- file->f_file->f_path.dentry->d_inode->i_ino,
+ file_inode(file->f_file)->i_sb->s_id,
+ file_inode(file->f_file)->i_ino,
lock->fl.fl_type,
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end);
@@ -479,16 +524,9 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
if (block == NULL) {
struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
- struct nlm_host *host;
if (conf == NULL)
return nlm_granted;
- /* Create host handle for callback */
- host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len);
- if (host == NULL) {
- kfree(conf);
- return nlm_lck_denied_nolocks;
- }
block = nlmsvc_create_block(rqstp, host, file, lock, cookie);
if (block == NULL) {
kfree(conf);
@@ -519,8 +557,12 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
goto out;
}
+ if (locks_in_grace(SVC_NET(rqstp))) {
+ ret = nlm_lck_denied_grace_period;
+ goto out;
+ }
error = vfs_test_lock(file->f_file, &lock->fl);
- if (error == -EINPROGRESS) {
+ if (error == FILE_LOCK_DEFERRED) {
ret = nlmsvc_defer_lock_rqst(rqstp, block);
goto out;
}
@@ -559,19 +601,19 @@ out:
* must be removed.
*/
__be32
-nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
{
int error;
dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
- file->f_file->f_path.dentry->d_inode->i_sb->s_id,
- file->f_file->f_path.dentry->d_inode->i_ino,
+ file_inode(file->f_file)->i_sb->s_id,
+ file_inode(file->f_file)->i_ino,
lock->fl.fl_pid,
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end);
/* First, cancel any lock that might be there */
- nlmsvc_cancel_blocked(file, lock);
+ nlmsvc_cancel_blocked(net, file, lock);
lock->fl.fl_type = F_UNLCK;
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
@@ -587,18 +629,21 @@ nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
* The calling procedure must check whether the file can be closed.
*/
__be32
-nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
+nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
{
struct nlm_block *block;
int status = 0;
dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
- file->f_file->f_path.dentry->d_inode->i_sb->s_id,
- file->f_file->f_path.dentry->d_inode->i_ino,
+ file_inode(file->f_file)->i_sb->s_id,
+ file_inode(file->f_file)->i_ino,
lock->fl.fl_pid,
(long long)lock->fl.fl_start,
(long long)lock->fl.fl_end);
+ if (locks_in_grace(net))
+ return nlm_lck_denied_grace_period;
+
mutex_lock(&file->f_mutex);
block = nlmsvc_lookup_block(file, lock);
mutex_unlock(&file->f_mutex);
@@ -613,7 +658,7 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
/*
* This is a callback from the filesystem for VFS file lock requests.
- * It will be used if fl_grant is defined and the filesystem can not
+ * It will be used if lm_grant is defined and the filesystem can not
* respond to the request immediately.
* For GETLK request it will copy the reply to the nlm_block.
* For SETLK or SETLKW request it will get the local posix lock.
@@ -632,7 +677,7 @@ nlmsvc_update_deferred_block(struct nlm_block *block, struct file_lock *conf,
block->b_flags |= B_TIMED_OUT;
if (conf) {
if (block->b_fl)
- locks_copy_lock(block->b_fl, conf);
+ __locks_copy_lock(block->b_fl, conf);
}
}
@@ -642,7 +687,7 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
struct nlm_block *block;
int rc = -ENOENT;
- lock_kernel();
+ spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
@@ -656,13 +701,13 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
} else if (result == 0)
block->b_granted = 1;
- nlmsvc_insert_block(block, 0);
+ nlmsvc_insert_block_locked(block, 0);
svc_wake_up(block->b_daemon);
rc = 0;
break;
}
}
- unlock_kernel();
+ spin_unlock(&nlm_blocked_lock);
if (rc == -ENOENT)
printk(KERN_WARNING "lockd: grant for unknown block\n");
return rc;
@@ -681,14 +726,16 @@ nlmsvc_notify_blocked(struct file_lock *fl)
struct nlm_block *block;
dprintk("lockd: VFS unblock notification for block %p\n", fl);
+ spin_lock(&nlm_blocked_lock);
list_for_each_entry(block, &nlm_blocked, b_list) {
if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
- nlmsvc_insert_block(block, 0);
+ nlmsvc_insert_block_locked(block, 0);
+ spin_unlock(&nlm_blocked_lock);
svc_wake_up(block->b_daemon);
return;
}
}
-
+ spin_unlock(&nlm_blocked_lock);
printk(KERN_WARNING "lockd: notification for unknown block!\n");
}
@@ -697,10 +744,22 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2)
return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid;
}
-struct lock_manager_operations nlmsvc_lock_operations = {
- .fl_compare_owner = nlmsvc_same_owner,
- .fl_notify = nlmsvc_notify_blocked,
- .fl_grant = nlmsvc_grant_deferred,
+/*
+ * Since NLM uses two "keys" for tracking locks, we need to hash them down
+ * to one for the blocked_hash. Here, we're just xor'ing the host address
+ * with the pid in order to create a key value for picking a hash bucket.
+ */
+static unsigned long
+nlmsvc_owner_key(struct file_lock *fl)
+{
+ return (unsigned long)fl->fl_owner ^ (unsigned long)fl->fl_pid;
+}
+
+const struct lock_manager_operations nlmsvc_lock_operations = {
+ .lm_compare_owner = nlmsvc_same_owner,
+ .lm_owner_key = nlmsvc_owner_key,
+ .lm_notify = nlmsvc_notify_blocked,
+ .lm_grant = nlmsvc_grant_deferred,
};
/*
@@ -720,6 +779,7 @@ nlmsvc_grant_blocked(struct nlm_block *block)
struct nlm_file *file = block->b_file;
struct nlm_lock *lock = &block->b_call->a_args.lock;
int error;
+ loff_t fl_start, fl_end;
dprintk("lockd: grant blocked lock %p\n", block);
@@ -737,22 +797,28 @@ nlmsvc_grant_blocked(struct nlm_block *block)
}
/* Try the lock operation again */
+ /* vfs_lock_file() can mangle fl_start and fl_end, but we need
+ * them unchanged for the GRANT_MSG
+ */
lock->fl.fl_flags |= FL_SLEEP;
+ fl_start = lock->fl.fl_start;
+ fl_end = lock->fl.fl_end;
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
lock->fl.fl_flags &= ~FL_SLEEP;
+ lock->fl.fl_start = fl_start;
+ lock->fl.fl_end = fl_end;
switch (error) {
case 0:
break;
- case -EAGAIN:
- case -EINPROGRESS:
+ case FILE_LOCK_DEFERRED:
dprintk("lockd: lock still blocked error %d\n", error);
nlmsvc_insert_block(block, NLM_NEVER);
nlmsvc_release_block(block);
return;
default:
printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
- -error, __FUNCTION__);
+ -error, __func__);
nlmsvc_insert_block(block, 10 * HZ);
nlmsvc_release_block(block);
return;
@@ -795,6 +861,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
dprintk("lockd: GRANT_MSG RPC callback\n");
+ spin_lock(&nlm_blocked_lock);
/* if the block is not on a list at this point then it has
* been invalidated. Don't try to requeue it.
*
@@ -804,7 +871,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
* for nlm_blocked?
*/
if (list_empty(&block->b_list))
- return;
+ goto out;
/* Technically, we should down the file semaphore here. Since we
* move the block towards the head of the queue only, no harm
@@ -816,14 +883,19 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
/* Call was successful, now wait for client callback */
timeout = 60 * HZ;
}
- nlmsvc_insert_block(block, timeout);
+ nlmsvc_insert_block_locked(block, timeout);
svc_wake_up(block->b_daemon);
+out:
+ spin_unlock(&nlm_blocked_lock);
}
+/*
+ * FIXME: nlmsvc_release_block() grabs a mutex. This is not allowed for an
+ * .rpc_release rpc_call_op
+ */
static void nlmsvc_grant_release(void *data)
{
struct nlm_rqst *call = data;
-
nlmsvc_release_block(call->a_block);
}
@@ -887,15 +959,17 @@ nlmsvc_retry_blocked(void)
unsigned long timeout = MAX_SCHEDULE_TIMEOUT;
struct nlm_block *block;
- while (!list_empty(&nlm_blocked)) {
+ spin_lock(&nlm_blocked_lock);
+ while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
if (block->b_when == NLM_NEVER)
break;
- if (time_after(block->b_when,jiffies)) {
+ if (time_after(block->b_when, jiffies)) {
timeout = block->b_when - jiffies;
break;
}
+ spin_unlock(&nlm_blocked_lock);
dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
block, block->b_when);
@@ -905,7 +979,9 @@ nlmsvc_retry_blocked(void)
retry_deferred_block(block);
} else
nlmsvc_grant_blocked(block);
+ spin_lock(&nlm_blocked_lock);
}
+ spin_unlock(&nlm_blocked_lock);
return timeout;
}
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 88379cc6e0b..21171f0c647 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -9,15 +9,9 @@
#include <linux/types.h>
#include <linux/time.h>
-#include <linux/slab.h>
-#include <linux/in.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/nfsd/nfsd.h>
#include <linux/lockd/lockd.h>
#include <linux/lockd/share.h>
-#include <linux/lockd/sm_inter.h>
-
+#include <linux/sunrpc/svc_xprt.h>
#define NLMDBG_FACILITY NLMDBG_CLIENT
@@ -74,7 +68,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
/* Obtain file pointer. Not used by FREE_ALL call. */
if (filp != NULL) {
- if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+ error = cast_status(nlm_lookup_file(rqstp, &file, &lock->fh));
+ if (error != 0)
goto no_locks;
*filp = file;
@@ -87,8 +82,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
return 0;
no_locks:
- if (host)
- nlm_release_host(host);
+ nlmsvc_release_host(host);
if (error)
return error;
return nlm_lck_denied_nolocks;
@@ -113,30 +107,24 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
- int rc = rpc_success;
+ __be32 rc = rpc_success;
dprintk("lockd: TEST called\n");
resp->cookie = argp->cookie;
- /* Don't accept test requests during grace period */
- if (nlmsvc_grace_period) {
- resp->status = nlm_lck_denied_grace_period;
- return rc;
- }
-
/* Obtain client and file */
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now check for conflicting locks */
- resp->status = cast_status(nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie));
+ resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
dprintk("lockd: TEST status %d vers %d\n",
ntohl(resp->status), rqstp->rq_vers);
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
@@ -147,18 +135,12 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
- int rc = rpc_success;
+ __be32 rc = rpc_success;
dprintk("lockd: LOCK called\n");
resp->cookie = argp->cookie;
- /* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period && !argp->reclaim) {
- resp->status = nlm_lck_denied_grace_period;
- return rc;
- }
-
/* Obtain client and file */
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
@@ -176,14 +158,15 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
#endif
/* Now try to lock the file */
- resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock,
- argp->block, &argp->cookie));
+ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
+ argp->block, &argp->cookie,
+ argp->reclaim));
if (resp->status == nlm_drop_reply)
rc = rpc_drop_reply;
else
dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rc;
}
@@ -194,13 +177,14 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
+ struct net *net = SVC_NET(rqstp);
dprintk("lockd: CANCEL called\n");
resp->cookie = argp->cookie;
/* Don't accept requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(net)) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -210,10 +194,10 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Try to cancel request. */
- resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
+ resp->status = cast_status(nlmsvc_cancel_blocked(net, file, &argp->lock));
dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -227,13 +211,14 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
{
struct nlm_host *host;
struct nlm_file *file;
+ struct net *net = SVC_NET(rqstp);
dprintk("lockd: UNLOCK called\n");
resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(net)) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -243,10 +228,10 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
/* Now try to remove the lock */
- resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
+ resp->status = cast_status(nlmsvc_unlock(net, file, &argp->lock));
dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -262,7 +247,7 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
dprintk("lockd: GRANTED called\n");
- resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock);
+ resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock);
dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
return rpc_success;
}
@@ -276,9 +261,17 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
-task->tk_status);
}
+void nlmsvc_release_call(struct nlm_rqst *call)
+{
+ if (!atomic_dec_and_test(&call->a_count))
+ return;
+ nlmsvc_release_host(call->a_host);
+ kfree(call);
+}
+
static void nlmsvc_callback_release(void *data)
{
- nlm_release_call(data);
+ nlmsvc_release_call(data);
}
static const struct rpc_call_ops nlmsvc_callback_ops = {
@@ -305,12 +298,13 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
return rpc_system_err;
call = nlm_alloc_call(host);
+ nlmsvc_release_host(host);
if (call == NULL)
return rpc_system_err;
stat = func(rqstp, argp, &call->a_res);
if (stat != 0) {
- nlm_release_call(call);
+ nlmsvc_release_call(call);
return stat;
}
@@ -372,7 +366,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */
- if (nlmsvc_grace_period && !argp->reclaim) {
+ if (locks_in_grace(SVC_NET(rqstp)) && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -385,7 +379,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_share_file(host, file, argp));
dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -405,7 +399,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie;
/* Don't accept requests during grace period */
- if (nlmsvc_grace_period) {
+ if (locks_in_grace(SVC_NET(rqstp))) {
resp->status = nlm_lck_denied_grace_period;
return rpc_success;
}
@@ -418,7 +412,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
- nlm_release_host(host);
+ nlmsvc_release_host(host);
nlm_release_file(file);
return rpc_success;
}
@@ -450,7 +444,7 @@ nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
return rpc_success;
nlmsvc_free_host_resources(host);
- nlm_release_host(host);
+ nlmsvc_release_host(host);
return rpc_success;
}
@@ -461,26 +455,16 @@ static __be32
nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
void *resp)
{
- struct sockaddr_in saddr;
-
- memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr));
-
dprintk("lockd: SM_NOTIFY called\n");
- if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
- || ntohs(saddr.sin_port) >= 1024) {
+
+ if (!nlm_privileged_requester(rqstp)) {
char buf[RPC_MAX_ADDRBUFLEN];
printk(KERN_WARNING "lockd: rejected NSM callback from %s\n",
svc_print_addr(rqstp, buf, sizeof(buf)));
return rpc_system_err;
}
- /* Obtain the host pointer for this NFS server and try to
- * reclaim all locks we hold on this server.
- */
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_addr.s_addr = argp->addr;
- nlm_host_rebooted(&saddr, argp->mon, argp->len, argp->state);
-
+ nlm_host_rebooted(argp);
return rpc_success;
}
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
index 068886de4dd..b0ae0700870 100644
--- a/fs/lockd/svcshare.c
+++ b/fs/lockd/svcshare.c
@@ -71,7 +71,8 @@ nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
struct nlm_share *share, **shpp;
struct xdr_netobj *oh = &argp->lock.oh;
- for (shpp = &file->f_shares; (share = *shpp) != 0; shpp = &share->s_next) {
+ 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);
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index dbbefbcd671..b6f3b84b6e9 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -10,14 +10,15 @@
#include <linux/string.h>
#include <linux/time.h>
#include <linux/in.h>
+#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/nfsd/nfsfh.h>
-#include <linux/nfsd/export.h>
+#include <linux/sunrpc/addr.h>
#include <linux/lockd/lockd.h>
#include <linux/lockd/share.h>
-#include <linux/lockd/sm_inter.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_SVCSUBS
@@ -43,7 +44,7 @@ static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
{
- struct inode *inode = file->f_file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file->f_file);
dprintk("lockd: %s %s/%ld\n",
msg, inode->i_sb->s_id, inode->i_ino);
@@ -82,7 +83,6 @@ __be32
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
struct nfs_fh *f)
{
- struct hlist_node *pos;
struct nlm_file *file;
unsigned int hash;
__be32 nfserr;
@@ -94,7 +94,7 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
/* Lock file table */
mutex_lock(&nlm_file_mutex);
- hlist_for_each_entry(file, pos, &nlm_files[hash], f_list)
+ hlist_for_each_entry(file, &nlm_files[hash], f_list)
if (!nfs_compare_fh(&file->f_handle, f))
goto found;
@@ -168,6 +168,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
again:
file->f_locks = 0;
+ spin_lock(&inode->i_lock);
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
if (fl->fl_lmops != &nlmsvc_lock_operations)
continue;
@@ -179,6 +180,7 @@ again:
if (match(lockhost, host)) {
struct file_lock lock = *fl;
+ spin_unlock(&inode->i_lock);
lock.fl_type = F_UNLCK;
lock.fl_start = 0;
lock.fl_end = OFFSET_MAX;
@@ -190,10 +192,17 @@ again:
goto again;
}
}
+ spin_unlock(&inode->i_lock);
return 0;
}
+static int
+nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
+{
+ return 1;
+}
+
/*
* Inspect a single file
*/
@@ -218,10 +227,14 @@ nlm_file_inuse(struct nlm_file *file)
if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
return 1;
+ spin_lock(&inode->i_lock);
for (fl = inode->i_flock; fl; fl = fl->fl_next) {
- if (fl->fl_lmops == &nlmsvc_lock_operations)
+ if (fl->fl_lmops == &nlmsvc_lock_operations) {
+ spin_unlock(&inode->i_lock);
return 1;
+ }
}
+ spin_unlock(&inode->i_lock);
file->f_locks = 0;
return 0;
}
@@ -230,21 +243,24 @@ nlm_file_inuse(struct nlm_file *file)
* Loop over all files in the file table.
*/
static int
-nlm_traverse_files(struct nlm_host *host, nlm_host_match_fn_t match)
+nlm_traverse_files(void *data, nlm_host_match_fn_t match,
+ int (*is_failover_file)(void *data, struct nlm_file *file))
{
- struct hlist_node *pos, *next;
+ struct hlist_node *next;
struct nlm_file *file;
int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
- hlist_for_each_entry_safe(file, pos, next, &nlm_files[i], f_list) {
+ hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
+ if (is_failover_file && !is_failover_file(data, file))
+ continue;
file->f_count++;
mutex_unlock(&nlm_file_mutex);
/* Traverse locks, blocks and shares of this file
* and update file->f_locks count */
- if (nlm_inspect_file(host, file, match))
+ if (nlm_inspect_file(data, file, match))
ret = 1;
mutex_lock(&nlm_file_mutex);
@@ -291,7 +307,8 @@ nlm_release_file(struct nlm_file *file)
* Helpers function for resource traversal
*
* nlmsvc_mark_host:
- * used by the garbage collector; simply sets h_inuse.
+ * used by the garbage collector; simply sets h_inuse only for those
+ * hosts, which passed network check.
* Always returns 0.
*
* nlmsvc_same_host:
@@ -302,22 +319,31 @@ nlm_release_file(struct nlm_file *file)
* returns 1 iff the host is a client.
* Used by nlmsvc_invalidate_all
*/
+
static int
-nlmsvc_mark_host(struct nlm_host *host, struct nlm_host *dummy)
+nlmsvc_mark_host(void *data, struct nlm_host *hint)
{
- host->h_inuse = 1;
+ struct nlm_host *host = data;
+
+ if ((hint->net == NULL) ||
+ (host->net == hint->net))
+ host->h_inuse = 1;
return 0;
}
static int
-nlmsvc_same_host(struct nlm_host *host, struct nlm_host *other)
+nlmsvc_same_host(void *data, struct nlm_host *other)
{
+ struct nlm_host *host = data;
+
return host == other;
}
static int
-nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy)
+nlmsvc_is_client(void *data, struct nlm_host *dummy)
{
+ struct nlm_host *host = data;
+
if (host->h_server) {
/* we are destroying locks even though the client
* hasn't asked us too, so don't unmonitor the
@@ -334,10 +360,13 @@ nlmsvc_is_client(struct nlm_host *host, struct nlm_host *dummy)
* Mark all hosts that still hold resources
*/
void
-nlmsvc_mark_resources(void)
+nlmsvc_mark_resources(struct net *net)
{
- dprintk("lockd: nlmsvc_mark_resources\n");
- nlm_traverse_files(NULL, nlmsvc_mark_host);
+ struct nlm_host hint;
+
+ dprintk("lockd: nlmsvc_mark_resources for net %p\n", net);
+ hint.net = net;
+ nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
}
/*
@@ -348,7 +377,7 @@ nlmsvc_free_host_resources(struct nlm_host *host)
{
dprintk("lockd: nlmsvc_free_host_resources\n");
- if (nlm_traverse_files(host, nlmsvc_same_host)) {
+ if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
printk(KERN_WARNING
"lockd: couldn't remove all locks held by %s\n",
host->h_name);
@@ -356,17 +385,67 @@ nlmsvc_free_host_resources(struct nlm_host *host)
}
}
-/*
- * Remove all locks held for clients
+/**
+ * nlmsvc_invalidate_all - remove all locks held for clients
+ *
+ * Release all locks held by NFS clients.
+ *
*/
void
nlmsvc_invalidate_all(void)
{
- /* Release all locks held by NFS clients.
+ /*
* Previously, the code would call
* nlmsvc_free_host_resources for each client in
* turn, which is about as inefficient as it gets.
* Now we just do it once in nlm_traverse_files.
*/
- nlm_traverse_files(NULL, nlmsvc_is_client);
+ nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
+}
+
+static int
+nlmsvc_match_sb(void *datap, struct nlm_file *file)
+{
+ struct super_block *sb = datap;
+
+ return sb == file->f_file->f_path.dentry->d_sb;
+}
+
+/**
+ * nlmsvc_unlock_all_by_sb - release locks held on this file system
+ * @sb: super block
+ *
+ * Release all locks held by clients accessing this file system.
+ */
+int
+nlmsvc_unlock_all_by_sb(struct super_block *sb)
+{
+ int ret;
+
+ ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
+ return ret ? -EIO : 0;
+}
+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);
+}
+
+/**
+ * nlmsvc_unlock_all_by_ip - release local locks by IP address
+ * @server_addr: server's IP address as seen by clients
+ *
+ * Release all locks held by clients accessing this host
+ * via the passed in IP address.
+ */
+int
+nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
+{
+ int ret;
+
+ ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
+ return ret ? -EIO : 0;
}
+EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
index 3e459e18cc3..9340e7e10ef 100644
--- a/fs/lockd/xdr.c
+++ b/fs/lockd/xdr.c
@@ -8,7 +8,6 @@
#include <linux/types.h>
#include <linux/sched.h>
-#include <linux/utsname.h>
#include <linux/nfs.h>
#include <linux/sunrpc/xdr.h>
@@ -16,7 +15,8 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
+
+#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_XDR
@@ -151,37 +151,6 @@ nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
}
/*
- * Encode a lock as part of an NLM call
- */
-static __be32 *
-nlm_encode_lock(__be32 *p, struct nlm_lock *lock)
-{
- struct file_lock *fl = &lock->fl;
- __s32 start, len;
-
- if (!(p = xdr_encode_string(p, lock->caller))
- || !(p = nlm_encode_fh(p, &lock->fh))
- || !(p = nlm_encode_oh(p, &lock->oh)))
- return NULL;
-
- if (fl->fl_start > NLM_OFFSET_MAX
- || (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
- return NULL;
-
- start = loff_t_to_s32(fl->fl_start);
- if (fl->fl_end == OFFSET_MAX)
- len = 0;
- else
- len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
-
- *p++ = htonl(lock->svid);
- *p++ = htonl(start);
- *p++ = htonl(len);
-
- return p;
-}
-
-/*
* Encode result of a TEST/TEST_MSG call
*/
static __be32 *
@@ -349,10 +318,8 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0;
argp->state = ntohl(*p++);
- /* Preserve the address in network byte order */
- argp->addr = *p++;
- argp->vers = *p++;
- argp->proto = *p++;
+ memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
+ p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p);
}
@@ -376,259 +343,3 @@ nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
-
-/*
- * Now, the client side XDR functions
- */
-#ifdef NLMCLNT_SUPPORT_SHARES
-static int
-nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
-{
- return 0;
-}
-#endif
-
-static int
-nlmclt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm_decode_cookie(p, &resp->cookie)))
- return -EIO;
- resp->status = *p++;
- if (resp->status == nlm_lck_denied) {
- struct file_lock *fl = &resp->lock.fl;
- u32 excl;
- s32 start, len, end;
-
- memset(&resp->lock, 0, sizeof(resp->lock));
- locks_init_lock(fl);
- excl = ntohl(*p++);
- resp->lock.svid = ntohl(*p++);
- fl->fl_pid = (pid_t)resp->lock.svid;
- if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
- return -EIO;
-
- fl->fl_flags = FL_POSIX;
- fl->fl_type = excl? F_WRLCK : F_RDLCK;
- start = ntohl(*p++);
- len = ntohl(*p++);
- end = start + len - 1;
-
- fl->fl_start = s32_to_loff_t(start);
- if (len == 0 || end < 0)
- fl->fl_end = OFFSET_MAX;
- else
- fl->fl_end = s32_to_loff_t(end);
- }
- return 0;
-}
-
-
-static int
-nlmclt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = argp->block? xdr_one : xdr_zero;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm_encode_lock(p, lock)))
- return -EIO;
- *p++ = argp->reclaim? xdr_one : xdr_zero;
- *p++ = htonl(argp->state);
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = argp->block? xdr_one : xdr_zero;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm_encode_cookie(p, &argp->cookie)))
- return -EIO;
- if (!(p = nlm_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm_encode_cookie(p, &resp->cookie)))
- return -EIO;
- *p++ = resp->status;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm_encode_testres(p, resp)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm_decode_cookie(p, &resp->cookie)))
- return -EIO;
- resp->status = *p++;
- return 0;
-}
-
-#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
-# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
-#endif
-
-/*
- * Buffer requirements for NLM
- */
-#define NLM_void_sz 0
-#define NLM_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
-#define NLM_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM_fhandle_sz 1+XDR_QUADLEN(NFS2_FHSIZE)
-#define NLM_lock_sz 3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz
-#define NLM_holder_sz 4+NLM_owner_sz
-
-#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz
-#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz
-#define NLM_cancargs_sz NLM_cookie_sz+2+NLM_lock_sz
-#define NLM_unlockargs_sz NLM_cookie_sz+NLM_lock_sz
-
-#define NLM_testres_sz NLM_cookie_sz+1+NLM_holder_sz
-#define NLM_res_sz NLM_cookie_sz+1
-#define NLM_norep_sz 0
-
-/*
- * For NLM, a void procedure really returns nothing
- */
-#define nlmclt_decode_norep NULL
-
-#define PROC(proc, argtype, restype) \
-[NLMPROC_##proc] = { \
- .p_proc = NLMPROC_##proc, \
- .p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \
- .p_decode = (kxdrproc_t) nlmclt_decode_##restype, \
- .p_arglen = NLM_##argtype##_sz, \
- .p_replen = NLM_##restype##_sz, \
- .p_statidx = NLMPROC_##proc, \
- .p_name = #proc, \
- }
-
-static struct rpc_procinfo nlm_procedures[] = {
- PROC(TEST, testargs, testres),
- PROC(LOCK, lockargs, res),
- PROC(CANCEL, cancargs, res),
- PROC(UNLOCK, unlockargs, res),
- PROC(GRANTED, testargs, res),
- PROC(TEST_MSG, testargs, norep),
- PROC(LOCK_MSG, lockargs, norep),
- PROC(CANCEL_MSG, cancargs, norep),
- PROC(UNLOCK_MSG, unlockargs, norep),
- PROC(GRANTED_MSG, testargs, norep),
- PROC(TEST_RES, testres, norep),
- PROC(LOCK_RES, res, norep),
- PROC(CANCEL_RES, res, norep),
- PROC(UNLOCK_RES, res, norep),
- PROC(GRANTED_RES, res, norep),
-#ifdef NLMCLNT_SUPPORT_SHARES
- PROC(SHARE, shareargs, shareres),
- PROC(UNSHARE, shareargs, shareres),
- PROC(NM_LOCK, lockargs, res),
- PROC(FREE_ALL, notify, void),
-#endif
-};
-
-static struct rpc_version nlm_version1 = {
- .number = 1,
- .nrprocs = 16,
- .procs = nlm_procedures,
-};
-
-static struct rpc_version nlm_version3 = {
- .number = 3,
- .nrprocs = 24,
- .procs = nlm_procedures,
-};
-
-static struct rpc_version * nlm_versions[] = {
- [1] = &nlm_version1,
- [3] = &nlm_version3,
-#ifdef CONFIG_LOCKD_V4
- [4] = &nlm_version4,
-#endif
-};
-
-static struct rpc_stat nlm_stats;
-
-struct rpc_program nlm_program = {
- .name = "lockd",
- .number = NLM_PROGRAM,
- .nrvers = ARRAY_SIZE(nlm_versions),
- .version = nlm_versions,
- .stats = &nlm_stats,
-};
-
-#ifdef RPC_DEBUG
-const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
-{
- /*
- * We can get away with a static buffer because we're only
- * called with BKL held.
- */
- 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;
-}
-#endif
diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c
index 43ff9397e6c..dfa4789cd46 100644
--- a/fs/lockd/xdr4.c
+++ b/fs/lockd/xdr4.c
@@ -9,7 +9,6 @@
#include <linux/types.h>
#include <linux/sched.h>
-#include <linux/utsname.h>
#include <linux/nfs.h>
#include <linux/sunrpc/xdr.h>
@@ -17,7 +16,6 @@
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h>
-#include <linux/lockd/sm_inter.h>
#define NLMDBG_FACILITY NLMDBG_XDR
@@ -95,15 +93,6 @@ nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
return p + XDR_QUADLEN(f->size);
}
-static __be32 *
-nlm4_encode_fh(__be32 *p, struct nfs_fh *f)
-{
- *p++ = htonl(f->size);
- if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */
- memcpy(p, f->data, f->size);
- return p + XDR_QUADLEN(f->size);
-}
-
/*
* Encode and decode owner handle
*/
@@ -114,12 +103,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
}
static __be32 *
-nlm4_encode_oh(__be32 *p, struct xdr_netobj *oh)
-{
- return xdr_encode_netobj(p, oh);
-}
-
-static __be32 *
nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
{
struct file_lock *fl = &lock->fl;
@@ -152,38 +135,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
}
/*
- * Encode a lock as part of an NLM call
- */
-static __be32 *
-nlm4_encode_lock(__be32 *p, struct nlm_lock *lock)
-{
- struct file_lock *fl = &lock->fl;
- __s64 start, len;
-
- if (!(p = xdr_encode_string(p, lock->caller))
- || !(p = nlm4_encode_fh(p, &lock->fh))
- || !(p = nlm4_encode_oh(p, &lock->oh)))
- return NULL;
-
- if (fl->fl_start > NLM4_OFFSET_MAX
- || (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
- return NULL;
-
- *p++ = htonl(lock->svid);
-
- start = loff_t_to_s64(fl->fl_start);
- if (fl->fl_end == OFFSET_MAX)
- len = 0;
- else
- len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
-
- p = xdr_encode_hyper(p, start);
- p = xdr_encode_hyper(p, len);
-
- return p;
-}
-
-/*
* Encode result of a TEST/TEST_MSG call
*/
static __be32 *
@@ -356,10 +307,8 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
return 0;
argp->state = ntohl(*p++);
- /* Preserve the address in network byte order */
- argp->addr = *p++;
- argp->vers = *p++;
- argp->proto = *p++;
+ memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
+ p += XDR_QUADLEN(SM_PRIV_SIZE);
return xdr_argsize_check(rqstp, p);
}
@@ -383,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
{
return xdr_ressize_check(rqstp, p);
}
-
-/*
- * Now, the client side XDR functions
- */
-#ifdef NLMCLNT_SUPPORT_SHARES
-static int
-nlm4clt_decode_void(struct rpc_rqst *req, __be32 *p, void *ptr)
-{
- return 0;
-}
-#endif
-
-static int
-nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm4_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
- return -EIO;
- resp->status = *p++;
- if (resp->status == nlm_lck_denied) {
- struct file_lock *fl = &resp->lock.fl;
- u32 excl;
- __u64 start, len;
- __s64 end;
-
- memset(&resp->lock, 0, sizeof(resp->lock));
- locks_init_lock(fl);
- excl = ntohl(*p++);
- resp->lock.svid = ntohl(*p++);
- fl->fl_pid = (pid_t)resp->lock.svid;
- if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
- return -EIO;
-
- fl->fl_flags = FL_POSIX;
- fl->fl_type = excl? F_WRLCK : F_RDLCK;
- p = xdr_decode_hyper(p, &start);
- p = xdr_decode_hyper(p, &len);
- end = start + len - 1;
-
- fl->fl_start = s64_to_loff_t(start);
- if (len == 0 || end < 0)
- fl->fl_end = OFFSET_MAX;
- else
- fl->fl_end = s64_to_loff_t(end);
- }
- return 0;
-}
-
-
-static int
-nlm4clt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = argp->block? xdr_one : xdr_zero;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm4_encode_lock(p, lock)))
- return -EIO;
- *p++ = argp->reclaim? xdr_one : xdr_zero;
- *p++ = htonl(argp->state);
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
- return -EIO;
- *p++ = argp->block? xdr_one : xdr_zero;
- *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
- if (!(p = nlm4_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
- struct nlm_lock *lock = &argp->lock;
-
- if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
- return -EIO;
- if (!(p = nlm4_encode_lock(p, lock)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
- return -EIO;
- *p++ = resp->status;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm4_encode_testres(p, resp)))
- return -EIO;
- req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
- return 0;
-}
-
-static int
-nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
- if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
- return -EIO;
- resp->status = *p++;
- return 0;
-}
-
-#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
-# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
-#endif
-
-#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
-# error "NLM host name cannot be larger than NLM's maximum string length!"
-#endif
-
-/*
- * Buffer requirements for NLM
- */
-#define NLM4_void_sz 0
-#define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
-#define NLM4_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM4_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE)
-#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz
-#define NLM4_holder_sz 6+NLM4_owner_sz
-
-#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz
-#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz
-#define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz
-#define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz
-
-#define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz
-#define NLM4_res_sz NLM4_cookie_sz+1
-#define NLM4_norep_sz 0
-
-/*
- * For NLM, a void procedure really returns nothing
- */
-#define nlm4clt_decode_norep NULL
-
-#define PROC(proc, argtype, restype) \
-[NLMPROC_##proc] = { \
- .p_proc = NLMPROC_##proc, \
- .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \
- .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \
- .p_arglen = NLM4_##argtype##_sz, \
- .p_replen = NLM4_##restype##_sz, \
- .p_statidx = NLMPROC_##proc, \
- .p_name = #proc, \
- }
-
-static struct rpc_procinfo nlm4_procedures[] = {
- PROC(TEST, testargs, testres),
- PROC(LOCK, lockargs, res),
- PROC(CANCEL, cancargs, res),
- PROC(UNLOCK, unlockargs, res),
- PROC(GRANTED, testargs, res),
- PROC(TEST_MSG, testargs, norep),
- PROC(LOCK_MSG, lockargs, norep),
- PROC(CANCEL_MSG, cancargs, norep),
- PROC(UNLOCK_MSG, unlockargs, norep),
- PROC(GRANTED_MSG, testargs, norep),
- PROC(TEST_RES, testres, norep),
- PROC(LOCK_RES, res, norep),
- PROC(CANCEL_RES, res, norep),
- PROC(UNLOCK_RES, res, norep),
- PROC(GRANTED_RES, res, norep),
-#ifdef NLMCLNT_SUPPORT_SHARES
- PROC(SHARE, shareargs, shareres),
- PROC(UNSHARE, shareargs, shareres),
- PROC(NM_LOCK, lockargs, res),
- PROC(FREE_ALL, notify, void),
-#endif
-};
-
-struct rpc_version nlm_version4 = {
- .number = 4,
- .nrprocs = 24,
- .procs = nlm4_procedures,
-};