summaryrefslogtreecommitdiffstats
path: root/fs/lockd
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /fs/lockd
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'fs/lockd')
-rw-r--r--fs/lockd/Makefile16
-rw-r--r--fs/lockd/clntlock.c203
-rw-r--r--fs/lockd/clntproc.c540
-rw-r--r--fs/lockd/host.c323
-rw-r--r--fs/lockd/lockd_syms.c42
-rw-r--r--fs/lockd/mon.c228
-rw-r--r--fs/lockd/svc.c284
-rw-r--r--fs/lockd/svclock.c620
-rw-r--r--fs/lockd/svcproc.c551
-rw-r--r--fs/lockd/svcshare.c111
-rw-r--r--fs/lockd/svcsubs.c278
-rw-r--r--fs/lockd/xdr.c605
12 files changed, 3801 insertions, 0 deletions
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile
new file mode 100644
index 000000000..7c319ffc3
--- /dev/null
+++ b/fs/lockd/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux lock manager stuff
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := lockd.o
+O_OBJS := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
+ svcproc.o svcsubs.o mon.o xdr.o
+OX_OBJS := lockd_syms.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
new file mode 100644
index 000000000..8c41d2f39
--- /dev/null
+++ b/fs/lockd/clntlock.c
@@ -0,0 +1,203 @@
+/*
+ * linux/fs/lockd/clntlock.c
+ *
+ * Lock handling for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+#define NLMDBG_FACILITY NLMDBG_CIENT
+
+/*
+ * Local function prototypes
+ */
+static int reclaimer(void *ptr);
+
+/*
+ * The following functions handle blocking and granting from the
+ * client perspective.
+ */
+
+/*
+ * This is the representation of a blocked client lock.
+ */
+struct nlm_wait {
+ struct nlm_wait * b_next; /* linked list */
+ struct wait_queue * b_wait; /* where to wait on */
+ struct nlm_host * b_host;
+ struct file_lock * b_lock; /* local file lock */
+ unsigned short b_reclaim; /* got to reclaim lock */
+ u32 b_status; /* grant callback status */
+};
+
+static struct nlm_wait * nlm_blocked = NULL;
+
+/*
+ * Block on a lock
+ */
+int
+nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
+{
+ struct nlm_wait block, **head;
+ int err;
+ u32 pstate;
+
+ block.b_host = host;
+ block.b_lock = fl;
+ block.b_wait = NULL;
+ block.b_status = NLM_LCK_BLOCKED;
+ block.b_next = nlm_blocked;
+ nlm_blocked = &block;
+
+ /* Remember pseudo nsm state */
+ pstate = host->h_state;
+
+ /* Go to sleep waiting for GRANT callback. Some servers seem
+ * to lose callbacks, however, so we're going to poll from
+ * time to time just to make sure.
+ *
+ * For now, the retry frequency is pretty high; normally
+ * a 1 minute timeout would do. See the comment before
+ * nlmclnt_lock for an explanation.
+ */
+ current->timeout = jiffies + 30 * HZ;
+ interruptible_sleep_on(&block.b_wait);
+
+ for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
+ if (*head == &block) {
+ *head = block.b_next;
+ break;
+ }
+ }
+
+ if (!signalled()) {
+ *statp = block.b_status;
+ return 0;
+ }
+
+ /* Okay, we were interrupted. Cancel the pending request
+ * unless the server has rebooted.
+ */
+ if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
+ printk(KERN_NOTICE
+ "lockd: CANCEL call failed (errno %d)\n", -err);
+
+ return -ERESTARTSYS;
+}
+
+/*
+ * The server lockd has called us back to tell us the lock was granted
+ */
+u32
+nlmclnt_grant(struct nlm_lock *lock)
+{
+ struct nlm_wait *block;
+
+ /*
+ * Look up blocked request based on arguments.
+ * Warning: must not use cookie to match it!
+ */
+ for (block = nlm_blocked; block; block = block->b_next) {
+ if (nlm_compare_locks(block->b_lock, &lock->fl))
+ break;
+ }
+
+ /* Ooops, no blocked request found. */
+ if (block == NULL)
+ return nlm_lck_denied;
+
+ /* Alright, we found the lock. Set the return status and
+ * wake up the caller.
+ */
+ block->b_status = NLM_LCK_GRANTED;
+ wake_up(&block->b_wait);
+
+ return nlm_granted;
+}
+
+/*
+ * The following procedures deal with the recovery of locks after a
+ * server crash.
+ */
+
+/*
+ * Reclaim all locks on server host. We do this by spawning a separate
+ * reclaimer thread.
+ * FIXME: should bump MOD_USE_COUNT while reclaiming
+ */
+void
+nlmclnt_recovery(struct nlm_host *host, u32 newstate)
+{
+ if (!host->h_reclaiming++) {
+ if (host->h_nsmstate == newstate)
+ return;
+ printk(KERN_WARNING
+ "lockd: Uh-oh! Interfering reclaims for host %s",
+ host->h_name);
+ host->h_monitored = 0;
+ host->h_nsmstate = newstate;
+ host->h_state++;
+ nlm_release_host(host);
+ } else {
+ host->h_monitored = 0;
+ host->h_nsmstate = newstate;
+ host->h_state++;
+ host->h_count++;
+ kernel_thread(reclaimer, host, 0);
+ }
+}
+
+static int
+reclaimer(void *ptr)
+{
+ struct nlm_host *host = (struct nlm_host *) ptr;
+ struct nlm_wait *block;
+ struct file_lock *fl;
+ struct inode *inode;
+
+ /* This one ensures that our parent doesn't terminate while the
+ * reclaim is in progress */
+ lockd_up();
+
+ /* First, reclaim all locks that have been granted previously. */
+ do {
+ for (fl = file_lock_table; fl; fl = fl->fl_next) {
+ inode = fl->fl_file->f_inode;
+ if (inode->i_sb->s_magic == NFS_SUPER_MAGIC
+ && nlm_cmp_addr(NFS_ADDR(inode), &host->h_addr)
+ && fl->fl_u.nfs_fl.state != host->h_state
+ && (fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED)) {
+ fl->fl_u.nfs_fl.flags &= ~ NFS_LCK_GRANTED;
+ nlmclnt_reclaim(host, fl);
+ break;
+ }
+ }
+ } while (fl);
+
+ host->h_reclaiming = 0;
+ wake_up(&host->h_gracewait);
+
+ /* Now, wake up all processes that sleep on a blocked lock */
+ for (block = nlm_blocked; block; block = block->b_next) {
+ if (block->b_host == host) {
+ block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
+ wake_up(&block->b_wait);
+ }
+ }
+
+ /* Release host handle after use */
+ nlm_release_host(host);
+ lockd_down();
+
+ return 0;
+}
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
new file mode 100644
index 000000000..1506a2ba6
--- /dev/null
+++ b/fs/lockd/clntproc.c
@@ -0,0 +1,540 @@
+/*
+ * linux/fs/lockd/clntproc.c
+ *
+ * RPC procedures for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/nfs_fs.h>
+#include <linux/utsname.h>
+#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
+
+static int nlmclnt_test(struct nlm_rqst *, struct file_lock *);
+static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
+static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
+static void nlmclnt_unlock_callback(struct rpc_task *);
+static void nlmclnt_cancel_callback(struct rpc_task *);
+static int nlm_stat_to_errno(u32 stat);
+
+/*
+ * Cookie counter for NLM requests
+ */
+static u32 nlm_cookie = 0x1234;
+
+/*
+ * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
+ */
+static inline void
+nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_lock *lock = &argp->lock;
+
+ memset(argp, 0, sizeof(*argp));
+ argp->cookie = nlm_cookie++;
+ argp->state = nsm_local_state;
+ lock->fh = *NFS_FH(fl->fl_file->f_inode);
+ lock->caller = system_utsname.nodename;
+ lock->oh.data = req->a_owner;
+ lock->oh.len = sprintf(req->a_owner, "%d@%s",
+ current->pid, system_utsname.nodename);
+ lock->fl = *fl;
+}
+
+/*
+ * Initialize arguments for GRANTED call
+ */
+int
+nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
+{
+ struct nlm_args *argp = &call->a_args;
+ struct nlm_lock *alock = &argp->lock;
+ void *data = NULL;
+
+ if (lock->oh.len > NLMCLNT_OHSIZE
+ && !(data = kmalloc(lock->oh.len, GFP_KERNEL)))
+ return 0;
+
+ argp->cookie = nlm_cookie++;
+ argp->lock = *lock;
+ alock->caller = system_utsname.nodename;
+ if (data)
+ alock->oh.data = (u8 *) data;
+ else
+ alock->oh.data = call->a_owner;
+ memcpy(alock->oh.data, lock->oh.data, lock->oh.len);
+ return 1;
+}
+
+void
+nlmclnt_freegrantargs(struct nlm_rqst *call)
+{
+ kfree(call->a_args.lock.caller);
+}
+
+/*
+ * This is the main entry point for the NLM client.
+ */
+int
+nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
+{
+ struct nfs_server *nfssrv = NFS_SERVER(inode);
+ struct nlm_host *host;
+ struct nlm_rqst reqst, *call = &reqst;
+ unsigned long oldmask;
+ int status;
+
+ /* Always use NLM version 1 over UDP for now... */
+ if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), IPPROTO_UDP, 1)))
+ return -ENOLCK;
+
+ /* Create RPC client handle if not there, and copy soft
+ * and intr flags from NFS client. */
+ if (host->h_rpcclnt == NULL) {
+ struct rpc_clnt *clnt;
+
+ /* Bind an rpc client to this host handle (does not
+ * perform a portmapper lookup) */
+ if (!(clnt = nlm_bind_host(host))) {
+ status = -ENOLCK;
+ goto done;
+ }
+ clnt->cl_softrtry = nfssrv->client->cl_softrtry;
+ clnt->cl_intr = nfssrv->client->cl_intr;
+ clnt->cl_chatty = nfssrv->client->cl_chatty;
+ }
+
+ /* Keep the old signal mask */
+ oldmask = current->blocked;
+
+ /* If we're cleaning up locks because the process is exiting,
+ * perform the RPC call asynchronously. */
+ if (cmd == F_SETLK && fl->fl_type == F_UNLCK
+ && (current->flags & PF_EXITING)) {
+ current->blocked = ~0UL; /* Mask all signals */
+ call = nlmclnt_alloc_call();
+ call->a_flags = RPC_TASK_ASYNC;
+ } else {
+ call->a_flags = 0;
+ }
+ call->a_host = host;
+
+ /* Set up the argument struct */
+ nlmclnt_setlockargs(call, fl);
+
+ if (cmd == F_GETLK) {
+ status = nlmclnt_test(call, fl);
+ } else if (cmd == F_SETLK && fl->fl_type == F_UNLCK) {
+ status = nlmclnt_unlock(call, fl);
+ } else if (cmd == F_SETLK || cmd == F_SETLKW) {
+ call->a_args.block = (cmd == F_SETLKW)? 1 : 0;
+ status = nlmclnt_lock(call, fl);
+ } else {
+ status = -EINVAL;
+ }
+
+ if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
+ rpc_free(call);
+
+ current->blocked = oldmask;
+
+done:
+ dprintk("lockd: clnt proc returns %d\n", status);
+ nlm_release_host(host);
+ return status;
+}
+
+/*
+ * Wait while server is in grace period
+ */
+static inline int
+nlmclnt_grace_wait(struct nlm_host *host)
+{
+ if (!host->h_reclaiming)
+ current->timeout = 10 * HZ;
+ interruptible_sleep_on(&host->h_gracewait);
+ return signalled()? -ERESTARTSYS : 0;
+}
+
+/*
+ * Allocate an NLM RPC call struct
+ */
+struct nlm_rqst *
+nlmclnt_alloc_call(void)
+{
+ struct nlm_rqst *call;
+
+ while (!signalled()) {
+ call = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
+ sizeof(struct nlm_rqst));
+ if (call)
+ return call;
+ current->timeout = 5 * HZ;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ }
+ return NULL;
+}
+
+/*
+ * Generic NLM call
+ */
+int
+nlmclnt_call(struct nlm_rqst *req, u32 proc)
+{
+ struct nlm_host *host = req->a_host;
+ struct rpc_clnt *clnt;
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ dprintk("lockd: call procedure %s on %s\n",
+ nlm_procname(proc), host->h_name);
+
+ do {
+ if (host->h_reclaiming && !argp->reclaim) {
+ interruptible_sleep_on(&host->h_gracewait);
+ continue;
+ }
+
+ /* If we have no RPC client yet, create one. */
+ if ((clnt = nlm_bind_host(host)) == NULL)
+ return -ENOLCK;
+
+ /* Perform the RPC call. If an error occurs, try again */
+ if ((status = rpc_call(clnt, proc, argp, resp, 0)) < 0) {
+ dprintk("lockd: rpc_call returned error %d\n", -status);
+ if (status == -ERESTARTSYS)
+ return status;
+ nlm_rebind_host(host);
+ } else
+ if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
+ dprintk("lockd: server in grace period\n");
+ if (argp->reclaim) {
+ printk(KERN_WARNING
+ "lockd: spurious grace period reject?!\n");
+ return -ENOLCK;
+ }
+ } else {
+ dprintk("lockd: server returns status %d\n", resp->status);
+ return 0; /* Okay, call complete */
+ }
+
+ /* Back off a little and try again */
+ current->timeout = jiffies + 15 * HZ;
+ interruptible_sleep_on(&host->h_gracewait);
+ } while (!signalled());
+
+ return -ERESTARTSYS;
+}
+
+/*
+ * Generic NLM call, async version.
+ */
+int
+nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+ struct nlm_host *host = req->a_host;
+ struct rpc_clnt *clnt;
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ dprintk("lockd: call procedure %s on %s (async)\n",
+ nlm_procname(proc), host->h_name);
+
+ /* If we have no RPC client yet, create one. */
+ if ((clnt = nlm_bind_host(host)) == NULL)
+ return -ENOLCK;
+
+ /* bootstrap and kick off the async RPC call */
+ status = rpc_do_call(clnt, proc, argp, resp, RPC_TASK_ASYNC,
+ callback, req);
+
+ /* If the async call is proceeding, increment host refcount */
+ if (status >= 0 && (req->a_flags & RPC_TASK_ASYNC))
+ host->h_count++;
+ return status;
+}
+
+/*
+ * TEST for the presence of a conflicting lock
+ */
+static int
+nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
+{
+ int status;
+
+ if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
+ return status;
+
+ status = req->a_res.status;
+ if (status == NLM_LCK_GRANTED) {
+ fl->fl_type = F_UNLCK;
+ } if (status == NLM_LCK_DENIED) {
+ /*
+ * Report the conflicting lock back to the application.
+ * FIXME: Is it OK to report the pid back as well?
+ */
+ memcpy(fl, &req->a_res.lock.fl, sizeof(*fl));
+ /* fl->fl_pid = 0; */
+ } else {
+ return nlm_stat_to_errno(req->a_res.status);
+ }
+
+ return 0;
+}
+
+/*
+ * LOCK: Try to create a lock
+ *
+ * Programmer Harassment Alert
+ *
+ * When given a blocking lock request in a sync RPC call, the HPUX lockd
+ * will faithfully return LCK_BLOCKED but never cares to notify us when
+ * the lock could be granted. This way, our local process could hang
+ * around forever waiting for the callback.
+ *
+ * Solution A: Implement busy-waiting
+ * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
+ *
+ * For now I am implementing solution A, because I hate the idea of
+ * re-implementing lockd for a third time in two months. The async
+ * calls shouldn't be too hard to do, however.
+ *
+ * This is one of the lovely things about standards in the NFS area:
+ * they're so soft and squishy you can't really blame HP for doing this.
+ */
+static int
+nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_host *host = req->a_host;
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ if (!host->h_monitored && nsm_monitor(host) < 0) {
+ printk(KERN_NOTICE "lockd: failed to monitor %s\n",
+ host->h_name);
+ return -ENOLCK;
+ }
+
+ while (1) {
+ if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
+ if (resp->status != NLM_LCK_BLOCKED)
+ break;
+ status = nlmclnt_block(host, fl, &resp->status);
+ }
+ if (status < 0)
+ return status;
+ }
+
+ if (resp->status == NLM_LCK_GRANTED) {
+ fl->fl_u.nfs_fl.state = host->h_state;
+ fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
+ }
+
+ return nlm_stat_to_errno(resp->status);
+}
+
+/*
+ * RECLAIM: Try to reclaim a lock
+ */
+int
+nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
+{
+ struct nlm_rqst reqst, *req;
+ int status;
+
+ req = &reqst;
+ 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_LCK_GRANTED)
+ return 0;
+
+ printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
+ "(errno %d, status %d)\n", fl->fl_pid,
+ status, req->a_res.status);
+
+ /*
+ * FIXME: This is a serious failure. We can
+ *
+ * a. Ignore the problem
+ * b. Send the owning process some signal (Linux doesn't have
+ * SIGLOST, though...)
+ * c. Retry the operation
+ *
+ * Until someone comes up with a simple implementation
+ * for b or c, I'll choose option a.
+ */
+
+ return -ENOLCK;
+}
+
+/*
+ * UNLOCK: remove an existing lock
+ */
+static int
+nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ /* Clean the GRANTED flag now so the lock doesn't get
+ * reclaimed while we're stuck in the unlock call. */
+ fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
+
+ if (req->a_flags & RPC_TASK_ASYNC) {
+ return nlmclnt_async_call(req, NLMPROC_UNLOCK,
+ nlmclnt_unlock_callback);
+ }
+
+ if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0)
+ return status;
+
+ if (resp->status == NLM_LCK_GRANTED)
+ return 0;
+
+ if (resp->status != NLM_LCK_DENIED_NOLOCKS)
+ printk("lockd: unexpected unlock status: %d\n", resp->status);
+
+ /* What to do now? I'm out of my depth... */
+
+ return -ENOLCK;
+}
+
+static void
+nlmclnt_unlock_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+ int status = req->a_res.status;
+
+ if (RPC_ASSASSINATED(task))
+ goto die;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
+ nlm_rebind_host(req->a_host);
+ rpc_restart_call(task);
+ return;
+ }
+ if (status != NLM_LCK_GRANTED
+ && status != NLM_LCK_DENIED_GRACE_PERIOD) {
+ printk("lockd: unexpected unlock status: %d\n", status);
+ }
+
+die:
+ rpc_release_task(task);
+}
+
+/*
+ * Cancel a blocked lock request.
+ * We always use an async RPC call for this in order not to hang a
+ * process that has been Ctrl-C'ed.
+ */
+int
+nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
+{
+ struct nlm_rqst *req;
+ unsigned long oldmask = current->blocked;
+ int status;
+
+ /* Block all signals while setting up call */
+ current->blocked = ~0UL;
+
+ do {
+ req = (struct nlm_rqst *) rpc_allocate(RPC_TASK_ASYNC,
+ sizeof(*req));
+ } while (req == NULL);
+ req->a_host = host;
+ req->a_flags = RPC_TASK_ASYNC;
+
+ nlmclnt_setlockargs(req, fl);
+
+ status = nlmclnt_async_call(req, NLMPROC_CANCEL,
+ nlmclnt_cancel_callback);
+ if (status < 0)
+ rpc_free(req);
+
+ current->blocked = oldmask;
+ return status;
+}
+
+static void
+nlmclnt_cancel_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+
+ if (RPC_ASSASSINATED(task))
+ goto die;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: CANCEL call error %d, retrying.\n",
+ task->tk_status);
+ goto retry_cancel;
+ }
+
+ dprintk("lockd: cancel status %d (task %d)\n",
+ req->a_res.status, task->tk_pid);
+
+ switch (req->a_res.status) {
+ case NLM_LCK_GRANTED:
+ case NLM_LCK_DENIED_GRACE_PERIOD:
+ /* Everything's good */
+ break;
+ case NLM_LCK_DENIED_NOLOCKS:
+ dprintk("lockd: CANCEL failed (server has no locks)\n");
+ goto retry_cancel;
+ default:
+ printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
+ req->a_res.status);
+ }
+
+die:
+ rpc_release_task(task);
+ nlm_release_host(req->a_host);
+ kfree(req);
+ return;
+
+retry_cancel:
+ nlm_rebind_host(req->a_host);
+ rpc_restart_call(task);
+ rpc_delay(task, 30 * HZ);
+ return;
+}
+
+/*
+ * Convert an NLM status code to a generic kernel errno
+ */
+static int
+nlm_stat_to_errno(u32 status)
+{
+ switch(status) {
+ case NLM_LCK_GRANTED:
+ return 0;
+ case NLM_LCK_DENIED:
+ return -EAGAIN;
+ case NLM_LCK_DENIED_NOLOCKS:
+ case NLM_LCK_DENIED_GRACE_PERIOD:
+ return -ENOLCK;
+ case NLM_LCK_BLOCKED:
+ printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
+ return -ENOLCK;
+ }
+ printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
+ return -ENOLCK;
+}
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
new file mode 100644
index 000000000..027c230a8
--- /dev/null
+++ b/fs/lockd/host.c
@@ -0,0 +1,323 @@
+/*
+ * linux/fs/lockd/host.c
+ *
+ * Management for NLM peer hosts. The nlm_host struct is shared
+ * between client and server implementation. The only reason to
+ * do so is to reduce code bloat.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.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_PTRHASH(ptr) ((((u32)(unsigned long) ptr) / 32) & (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_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr)
+
+static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH];
+static unsigned long next_gc = 0;
+static int nrhosts = 0;
+static struct semaphore nlm_host_sema = MUTEX;
+
+
+static void nlm_gc_hosts(void);
+
+/*
+ * Find an NLM server handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version)
+{
+ return nlm_lookup_host(NULL, sin, proto, version);
+}
+
+/*
+ * Find an NLM client handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmsvc_lookup_host(struct svc_rqst *rqstp)
+{
+ return nlm_lookup_host(rqstp->rq_client, &rqstp->rq_addr, 0, 0);
+}
+
+/*
+ * Match the given host against client/address
+ */
+static inline int
+nlm_match_host(struct nlm_host *host, struct svc_client *clnt,
+ struct sockaddr_in *sin)
+{
+ if (clnt)
+ return host->h_exportent == clnt;
+ return nlm_cmp_addr(&host->h_addr, sin);
+}
+
+/*
+ * Common host lookup routine for server & client
+ */
+struct nlm_host *
+nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin,
+ int proto, int version)
+{
+ struct nlm_host *host, **hp;
+ u32 addr;
+ int hash;
+
+ if (!clnt && !sin) {
+ printk(KERN_NOTICE "lockd: no clnt or addr in lookup_host!\n");
+ return NULL;
+ }
+
+ dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n",
+ (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version);
+
+ if (clnt)
+ hash = NLM_PTRHASH(clnt);
+ else
+ hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
+
+ /* Lock hash table */
+ down(&nlm_host_sema);
+
+ if (next_gc < jiffies)
+ nlm_gc_hosts();
+
+ for (hp = &nlm_hosts[hash]; (host = *hp); hp = &host->h_next) {
+ if (host->h_version != version || host->h_proto != proto)
+ continue;
+
+ if (nlm_match_host(host, clnt, sin)) {
+ if (hp != nlm_hosts + hash) {
+ *hp = host->h_next;
+ host->h_next = nlm_hosts[hash];
+ nlm_hosts[hash] = host;
+ }
+ host->h_expires = jiffies + NLM_HOST_EXPIRE;
+ host->h_count++;
+ up(&nlm_host_sema);
+ return host;
+ }
+ }
+
+ /* special hack for nlmsvc_invalidate_client */
+ if (sin == NULL)
+ goto nohost;
+
+ /* Ooops, no host found, create it */
+ dprintk("lockd: creating host entry\n");
+
+ if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL)))
+ goto nohost;
+ memset(host, 0, sizeof(*host));
+
+ addr = sin->sin_addr.s_addr;
+ sprintf(host->h_name, "%d.%d.%d.%d",
+ (unsigned char) (ntohl(addr) >> 24),
+ (unsigned char) (ntohl(addr) >> 16),
+ (unsigned char) (ntohl(addr) >> 8),
+ (unsigned char) (ntohl(addr) >> 0));
+
+ host->h_addr = *sin;
+ host->h_addr.sin_port = 0; /* ouch! */
+ host->h_version = version;
+ host->h_proto = proto;
+ host->h_authflavor = RPC_AUTH_NULL;
+ host->h_rpcclnt = NULL;
+ host->h_sema = MUTEX;
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ host->h_expires = jiffies + NLM_HOST_EXPIRE;
+ host->h_count = 1;
+ host->h_state = 0; /* pseudo NSM state */
+ host->h_nsmstate = 0; /* real NSM state */
+ host->h_exportent = clnt;
+
+ host->h_next = nlm_hosts[hash];
+ nlm_hosts[hash] = host;
+
+ if (++nrhosts > NLM_HOST_MAX)
+ next_gc = 0;
+
+nohost:
+ up(&nlm_host_sema);
+ return host;
+}
+
+/*
+ * Create the NLM RPC client for an NLM peer
+ */
+struct rpc_clnt *
+nlm_bind_host(struct nlm_host *host)
+{
+ struct rpc_clnt *clnt;
+ struct rpc_xprt *xprt;
+
+ dprintk("lockd: nlm_bind_host(%08x)\n",
+ (unsigned)ntohl(host->h_addr.sin_addr.s_addr));
+
+ /* Lock host handle */
+ down(&host->h_sema);
+
+ /* If we've already created an RPC client, check whether
+ * RPC rebind is required */
+ if ((clnt = host->h_rpcclnt) != NULL) {
+ if (host->h_nextrebind < jiffies) {
+ clnt->cl_port = 0;
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ dprintk("lockd: next rebind in %ld jiffies\n",
+ host->h_nextrebind - jiffies);
+ }
+ } else {
+ uid_t saved_euid = current->euid;
+
+ /* Create RPC socket as root user so we get a priv port */
+ current->euid = 0;
+ xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
+ current->euid = saved_euid;
+ if (xprt == NULL)
+ goto forgetit;
+
+ xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
+
+ clnt = rpc_create_client(xprt, host->h_name, &nlm_program,
+ host->h_version, host->h_authflavor);
+ if (clnt == NULL) {
+ xprt_destroy(xprt);
+ goto forgetit;
+ }
+ clnt->cl_autobind = 1; /* turn on pmap queries */
+ xprt->nocong = 1; /* No congestion control for NLM */
+
+ host->h_rpcclnt = clnt;
+ }
+
+ up(&host->h_sema);
+ return clnt;
+
+forgetit:
+ printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
+ up(&host->h_sema);
+ return NULL;
+}
+
+/*
+ * Force a portmap lookup of the remote lockd port
+ */
+void
+nlm_rebind_host(struct nlm_host *host)
+{
+ dprintk("lockd: rebind host %s\n", host->h_name);
+ if (host->h_rpcclnt && host->h_nextrebind < jiffies) {
+ host->h_rpcclnt->cl_port = 0;
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ }
+}
+
+/*
+ * Release NLM host after use
+ */
+void
+nlm_release_host(struct nlm_host *host)
+{
+ dprintk("lockd: release host %s\n", host->h_name);
+ host->h_count -= 1;
+}
+
+/*
+ * Shut down the hosts module.
+ * Note that this routine is called only at server shutdown time.
+ */
+void
+nlm_shutdown_hosts(void)
+{
+ struct nlm_host *host;
+ int i;
+
+ dprintk("lockd: shutting down host module\n");
+ down(&nlm_host_sema);
+
+ /* First, make all hosts eligible for gc */
+ dprintk("lockd: nuking all hosts...\n");
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next)
+ host->h_expires = 0;
+ }
+
+ /* Then, perform a garbage collection pass */
+ nlm_gc_hosts();
+ up(&nlm_host_sema);
+
+ /* 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 (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next) {
+ dprintk(" %s (cnt %d use %d exp %ld)\n",
+ host->h_name, host->h_count,
+ host->h_inuse, host->h_expires);
+ }
+ }
+ }
+}
+
+/*
+ * Garbage collect any unused NLM hosts.
+ * This GC combines reference counting for async operations with
+ * mark & sweep for resources held by remote clients.
+ */
+static void
+nlm_gc_hosts(void)
+{
+ struct nlm_host **q, *host;
+ struct rpc_clnt *clnt;
+ int i;
+
+ dprintk("lockd: host garbage collection\n");
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next)
+ host->h_inuse = 0;
+ }
+
+ /* Mark all hosts that hold locks, blocks or shares */
+ nlmsvc_mark_resources();
+
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ q = &nlm_hosts[i];
+ while ((host = *q) != NULL) {
+ if (host->h_count || host->h_inuse
+ || host->h_expires >= jiffies) {
+ q = &host->h_next;
+ continue;
+ }
+ dprintk("lockd: delete host %s\n", host->h_name);
+ *q = host->h_next;
+ if ((clnt = host->h_rpcclnt) != NULL) {
+ if (clnt->cl_users) {
+ printk(KERN_WARNING
+ "lockd: active RPC handle\n");
+ clnt->cl_dead = 1;
+ } else {
+ rpc_destroy_client(host->h_rpcclnt);
+ }
+ }
+ kfree(host);
+ nrhosts--;
+ }
+ }
+
+ next_gc = jiffies + NLM_HOST_COLLECT;
+}
+
diff --git a/fs/lockd/lockd_syms.c b/fs/lockd/lockd_syms.c
new file mode 100644
index 000000000..b3990ed98
--- /dev/null
+++ b/fs/lockd/lockd_syms.c
@@ -0,0 +1,42 @@
+/*
+ * linux/fs/lockd/lockd_syms.c
+ *
+ * Symbols exported by the lockd module.
+ *
+ * Authors: Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+/* Start/stop the daemon */
+EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL(lockd_down);
+
+/* NFS client entry */
+EXPORT_SYMBOL(nlmclnt_proc);
+
+/* NFS server entry points/hooks */
+EXPORT_SYMBOL(nlmsvc_invalidate_client);
+EXPORT_SYMBOL(nlmsvc_ops);
+
+/* Configuration at insmod time */
+EXPORT_SYMBOL(nlmsvc_grace_period);
+EXPORT_SYMBOL(nlmsvc_timeout);
+
+#endif /* CONFIG_MODULES */
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
new file mode 100644
index 000000000..bcd747e3f
--- /dev/null
+++ b/fs/lockd/mon.c
@@ -0,0 +1,228 @@
+/*
+ * linux/fs/lockd/mon.c
+ *
+ * The kernel statd client.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/kernel.h>
+#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_MONITOR
+
+static struct rpc_clnt * nsm_create(void);
+
+extern struct rpc_program nsm_program;
+
+/*
+ * Local NSM state
+ */
+u32 nsm_local_state = 0;
+
+/*
+ * Common procedure for SM_MON/SM_UNMON calls
+ */
+static int
+nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
+{
+ struct rpc_clnt *clnt;
+ struct nsm_args args;
+ struct nsm_res res;
+ int status;
+
+ dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
+ if (!(clnt = nsm_create()))
+ return -EACCES;
+
+ args.addr = host->h_addr.sin_addr.s_addr;
+ args.prog = NLM_PROGRAM;
+ args.vers = 1;
+ args.proc = NLMPROC_NSM_NOTIFY;
+
+ if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0)
+ return status;
+
+ if (res.status != 0) {
+ printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
+ return -EACCES;
+ }
+
+ nsm_local_state = res.state;
+ return 0;
+}
+
+/*
+ * Set up monitoring of a remote host
+ */
+int
+nsm_monitor(struct nlm_host *host)
+{
+ int status;
+
+ if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0)
+ host->h_monitored = 1;
+ return status;
+}
+
+/*
+ * Cease to monitor remote host
+ */
+int
+nsm_unmonitor(struct nlm_host *host)
+{
+ int status;
+
+ if ((status = nsm_mon_unmon(host, "unmonitor", SM_UNMON)) >= 0)
+ host->h_monitored = 0;
+ return status;
+}
+
+/*
+ * Create NSM client for the local host
+ */
+static struct rpc_clnt *
+nsm_create(void)
+{
+ struct sockaddr_in sin;
+ struct rpc_xprt *xprt;
+ struct rpc_clnt *clnt;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = 0;
+
+ if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL)))
+ return NULL;
+
+ clnt = rpc_create_client(xprt, "localhost",
+ &nsm_program, SM_VERSION,
+ RPC_AUTH_NULL);
+ if (!clnt) {
+ xprt_destroy(xprt);
+ } else {
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+ }
+ return clnt;
+}
+
+/*
+ * XDR functions for NSM.
+ */
+static int
+xdr_error(struct rpc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return -EACCES;
+}
+
+static int
+xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp)
+{
+ char buffer[20];
+ u32 addr = ntohl(argp->addr);
+
+ dprintk("nsm: xdr_encode_mon(%08lx, %ld, %ld, %ld)\n",
+ htonl(argp->addr), htonl(argp->proc),
+ htonl(argp->vers), htonl(argp->proc));
+
+ /*
+ * 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.
+ */
+ sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
+ (addr>>8) & 0xff, (addr) & 0xff);
+ if (!(p = xdr_encode_string(p, buffer))
+ || !(p = xdr_encode_string(p, system_utsname.nodename)))
+ return -EIO;
+ *p++ = htonl(argp->prog);
+ *p++ = htonl(argp->vers);
+ *p++ = htonl(argp->proc);
+
+ /* This is the private part. Needed only for SM_MON call */
+ if (rqstp->rq_task->tk_proc == SM_MON) {
+ *p++ = argp->addr;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+
+ rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
+ return 0;
+}
+
+static int
+xdr_decode_stat_res(struct rpc_rqst *rqstp, u32 *p, 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);
+ return 0;
+}
+
+static int
+xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
+{
+ resp->status = ntohl(*p++);
+ 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)
+
+static struct rpc_procinfo nsm_procedures[] = {
+ { "sm_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_stat",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_mon",
+ (kxdrproc_t) xdr_encode_mon,
+ (kxdrproc_t) xdr_decode_stat_res, SM_mon_sz, 2 },
+ { "sm_unmon",
+ (kxdrproc_t) xdr_encode_mon,
+ (kxdrproc_t) xdr_decode_stat, SM_mon_id_sz, 1 },
+ { "sm_unmon_all",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_simu_crash",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_notify",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+};
+
+static struct rpc_version nsm_version1 = {
+ 1,
+ sizeof(nsm_procedures)/sizeof(nsm_procedures[0]),
+ nsm_procedures
+};
+
+static struct rpc_version * nsm_version[] = {
+ NULL,
+ &nsm_version1,
+};
+
+static struct rpc_stat nsm_stats;
+
+struct rpc_program nsm_program = {
+ "statd",
+ SM_PROGRAM,
+ sizeof(nsm_version)/sizeof(nsm_version[0]),
+ nsm_version,
+ &nsm_stats
+};
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
new file mode 100644
index 000000000..fc133b51e
--- /dev/null
+++ b/fs/lockd/svc.c
@@ -0,0 +1,284 @@
+/*
+ * linux/fs/lockd/svc.c
+ *
+ * This is the central lockd service.
+ *
+ * FIXME: Separate the lockd NFS server functionality from the lockd NFS
+ * client functionality. Oh why didn't Sun create two separate
+ * services in the first place?
+ *
+ * Authors: Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/nfs.h>
+#include <linux/in.h>
+#include <linux/uio.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/lockd/lockd.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_SVC
+#define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE)
+#define BLOCKABLE_SIGS (~(_S(SIGKILL) | _S(SIGSTOP)))
+#define _S(sig) (1 << ((sig) - 1))
+
+extern struct svc_program nlmsvc_program;
+struct nlmsvc_binding * nlmsvc_ops = NULL;
+static int nlmsvc_sema = 0;
+static int nlmsvc_pid = 0;
+unsigned long nlmsvc_grace_period = 0;
+unsigned long nlmsvc_timeout = 0;
+
+/*
+ * Currently the following can be set only at insmod time.
+ * Ideally, they would be accessible through the sysctl interface.
+ */
+unsigned long nlm_grace_period = 0;
+unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
+
+/*
+ * This is the lockd kernel thread
+ */
+static void
+lockd(struct svc_rqst *rqstp)
+{
+ struct svc_serv *serv = rqstp->rq_server;
+ sigset_t oldsigmask;
+ int err = 0;
+
+ lock_kernel();
+ /* Lock module and set up kernel thread */
+ MOD_INC_USE_COUNT;
+ /* exit_files(current); */
+ exit_mm(current);
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "lockd");
+
+ /* kick rpciod */
+ rpciod_up();
+
+ dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
+
+ if (!nlm_timeout)
+ nlm_timeout = LOCKD_DFLT_TIMEO;
+
+#ifdef RPC_DEBUG
+ nlmsvc_grace_period = 10 * HZ;
+#else
+ if (nlm_grace_period) {
+ nlmsvc_grace_period += (1 + nlm_grace_period / nlm_timeout)
+ * nlm_timeout * HZ;
+ } else {
+ nlmsvc_grace_period += 5 * nlm_timeout * HZ;
+ }
+#endif
+
+ nlmsvc_grace_period += jiffies;
+ nlmsvc_timeout = nlm_timeout * HZ;
+ nlmsvc_pid = current->pid;
+
+ /*
+ * 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.
+ */
+ while (nlmsvc_sema || !signalled()) {
+ if (signalled())
+ current->signal = 0;
+
+ /*
+ * 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) {
+ current->timeout = nlmsvc_retry_blocked();
+ } else if (nlmsvc_grace_period < jiffies)
+ nlmsvc_grace_period = 0;
+
+ /*
+ * Find a socket with data available and call its
+ * recvfrom routine.
+ */
+ if ((err = svc_recv(serv, rqstp)) == -EAGAIN)
+ continue;
+ if (err < 0) {
+ if (err != -EINTR)
+ printk(KERN_WARNING
+ "lockd: terminating on error %d\n",
+ -err);
+ break;
+ }
+
+ dprintk("lockd: request from %08x\n",
+ (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
+
+ /*
+ * Look up the NFS client handle. The handle is needed for
+ * all but the GRANTED callback RPCs.
+ */
+ if (nlmsvc_ops) {
+ nlmsvc_ops->exp_readlock();
+ rqstp->rq_client =
+ nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
+ } else {
+ rqstp->rq_client = NULL;
+ }
+
+ /* Process request with all signals blocked. */
+ oldsigmask = current->blocked;
+ current->blocked = BLOCKABLE_SIGS;
+ svc_process(serv, rqstp);
+ current->blocked = oldsigmask;
+
+ /* Unlock export hash tables */
+ if (nlmsvc_ops)
+ nlmsvc_ops->exp_unlock();
+ }
+
+ nlm_shutdown_hosts();
+
+ /* Exit the RPC thread */
+ svc_exit_thread(rqstp);
+
+ /* release rpciod */
+ rpciod_down();
+
+ /* Release module */
+ MOD_DEC_USE_COUNT;
+ nlmsvc_pid = 0;
+}
+
+/*
+ * Make a socket for lockd
+ * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd.
+ */
+static int
+lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
+{
+ struct sockaddr_in sin;
+
+ dprintk("lockd: creating socket proto = %d\n", protocol);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
+ return svc_create_socket(serv, protocol, &sin);
+}
+
+int
+lockd_up(void)
+{
+ struct svc_serv * serv;
+ int error;
+
+ if (nlmsvc_pid || nlmsvc_sema++)
+ return 0;
+
+ dprintk("lockd: creating service\n");
+ if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL)
+ return -ENOMEM;
+
+ if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0
+ || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
+ svc_destroy(serv);
+ return error;
+ }
+
+ if ((error = svc_create_thread(lockd, serv)) < 0)
+ nlmsvc_sema--;
+
+ /* Release server */
+ svc_destroy(serv);
+ return 0;
+}
+
+void
+lockd_down(void)
+{
+ if (!nlmsvc_pid || --nlmsvc_sema > 0)
+ return;
+
+ kill_proc(nlmsvc_pid, SIGKILL, 1);
+ nlmsvc_sema = 0;
+ nlmsvc_pid = 0;
+}
+
+#ifdef MODULE
+/* New module support in 2.1.18 */
+#if LINUX_VERSION_CODE >= 0x020112
+ EXPORT_NO_SYMBOLS;
+ MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+ MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
+ MODULE_PARM(nlm_grace_period, "10-240l");
+ MODULE_PARM(nlm_timeout, "3-20l");
+#endif
+int
+init_module(void)
+{
+ nlmxdr_init();
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ /* FIXME: delete all NLM clients */
+ nlm_shutdown_hosts();
+}
+#endif
+
+/*
+ * Define NLM program and procedures
+ */
+static struct svc_version nlmsvc_version1 = {
+ 1, 16, nlmsvc_procedures, NULL
+};
+static struct svc_version nlmsvc_version3 = {
+ 3, 24, nlmsvc_procedures, NULL
+};
+#ifdef CONFIG_NFSD_NFS3
+static struct svc_version nlmsvc_version4 = {
+ 4, 24, nlmsvc_procedures4, NULL
+};
+#endif
+static struct svc_version * nlmsvc_version[] = {
+ NULL,
+ &nlmsvc_version1,
+ NULL,
+ &nlmsvc_version3,
+#ifdef CONFIG_NFSD_NFS3
+ &nlmsvc_version4,
+#endif
+};
+
+static struct svc_stat nlmsvc_stats;
+
+#define NLM_NRVERS (sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0]))
+struct svc_program nlmsvc_program = {
+ NLM_PROGRAM, /* program number */
+ 1, NLM_NRVERS-1, /* version range */
+ NLM_NRVERS, /* number of entries in nlmsvc_version */
+ nlmsvc_version, /* version table */
+ "lockd", /* service name */
+ &nlmsvc_stats, /* stats table */
+};
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
new file mode 100644
index 000000000..b4d74f745
--- /dev/null
+++ b/fs/lockd/svclock.c
@@ -0,0 +1,620 @@
+/*
+ * linux/fs/lockd/svclock.c
+ *
+ * Handling of server-side locks, mostly of the blocked variety.
+ * This is the ugliest part of lockd because we tread on very thin ice.
+ * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
+ * IMNSHO introducing the grant callback into the NLM protocol was one
+ * of the worst ideas Sun ever had. Except maybe for the idea of doing
+ * NFS file locking at all.
+ *
+ * I'm trying hard to avoid race conditions by protecting most accesses
+ * to a file's list of blocked locks through a semaphore. The global
+ * list of blocked locks is not protected in this fashion however.
+ * Therefore, some functions (such as the RPC callback for the async grant
+ * call) move blocked locks towards the head of the list *while some other
+ * process might be traversing it*. This should not be a problem in
+ * practice, because this will only cause functions traversing the list
+ * to visit some blocks twice.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/nlm.h>
+#include <linux/lockd/lockd.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_SVCLOCK
+
+static void nlmsvc_insert_block(struct nlm_block *block, unsigned long);
+static int nlmsvc_remove_block(struct nlm_block *block);
+static void nlmsvc_grant_callback(struct rpc_task *task);
+static void nlmsvc_notify_blocked(struct file_lock *);
+
+/*
+ * The list of blocked locks to retry
+ */
+static struct nlm_block * nlm_blocked = NULL;
+
+/*
+ * Insert a blocked lock into the global list
+ */
+static void
+nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+{
+ struct nlm_block **bp, *b;
+
+ dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
+ if (block->b_queued)
+ nlmsvc_remove_block(block);
+ for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next)
+ if (when < b->b_when)
+ break;
+
+ block->b_queued = 1;
+ block->b_when = when;
+ block->b_next = b;
+ *bp = block;
+}
+
+/*
+ * Remove a block from the global list
+ */
+static int
+nlmsvc_remove_block(struct nlm_block *block)
+{
+ struct nlm_block **bp, *b;
+
+ if (!block->b_queued)
+ return 1;
+ for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) {
+ if (b == block) {
+ *bp = block->b_next;
+ block->b_queued = 0;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find a block for a given lock and optionally remove it from
+ * the list.
+ */
+static struct nlm_block *
+nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove)
+{
+ struct nlm_block **head, *block;
+ struct file_lock *fl;
+
+ dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %ld-%ld ty=%d\n",
+ file, lock->fl.fl_pid, lock->fl.fl_start,
+ lock->fl.fl_end, lock->fl.fl_type);
+ for (head = &nlm_blocked; (block = *head); head = &block->b_next) {
+ fl = &block->b_call.a_args.lock.fl;
+ dprintk(" check f=%p pd=%d %ld-%ld ty=%d\n",
+ block->b_file, fl->fl_pid, fl->fl_start,
+ fl->fl_end, fl->fl_type);
+ if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
+ if (remove)
+ *head = block->b_next;
+ return block;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a block with a given NLM cookie.
+ */
+static inline struct nlm_block *
+nlmsvc_find_block(u32 cookie)
+{
+ struct nlm_block *block;
+
+ for (block = nlm_blocked; block; block = block->b_next) {
+ if (block->b_call.a_args.cookie == cookie)
+ break;
+ }
+
+ return block;
+}
+
+/*
+ * Create a block and initialize it.
+ *
+ * Note: we explicitly set the cookie of the grant reply to that of
+ * the blocked lock request. The spec explicitly mentions that the client
+ * should _not_ rely on the callback containing the same cookie as the
+ * request, but (as I found out later) that's because some implementations
+ * do just this. Never mind the standards comittees, they support our
+ * logging industries.
+ */
+static inline struct nlm_block *
+nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
+ struct nlm_lock *lock, u32 cookie)
+{
+ struct nlm_block *block;
+ struct nlm_host *host;
+ struct nlm_rqst *call;
+
+ /* Create host handle for callback */
+ host = nlmclnt_lookup_host(&rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+ if (host == NULL)
+ return NULL;
+
+ /* Allocate memory for block, and initialize arguments */
+ if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
+ goto failed;
+ memset(block, 0, sizeof(*block));
+
+ /* Set notifier function for VFS, and init args */
+ lock->fl.fl_notify = nlmsvc_notify_blocked;
+ if (!nlmclnt_setgrantargs(&block->b_call, lock)) {
+ kfree(block);
+ goto failed;
+ }
+ block->b_call.a_args.cookie = cookie; /* see above */
+
+ dprintk("lockd: created block %p...\n", block);
+
+ /* Create and initialize the block */
+ block->b_daemon = rqstp->rq_server;
+ block->b_host = host;
+ block->b_file = file;
+
+ /* Add to file's list of blocks */
+ block->b_fnext = file->f_blocks;
+ file->f_blocks = block;
+
+ /* Set up RPC arguments for callback */
+ call = &block->b_call;
+ call->a_host = host;
+ call->a_flags = RPC_TASK_ASYNC;
+
+ return block;
+
+failed:
+ nlm_release_host(host);
+ return NULL;
+}
+
+/*
+ * Delete a block. If the lock was cancelled or the grant callback
+ * failed, unlock is set to 1.
+ * It is the caller's responsibility to check whether the file
+ * can be closed hereafter.
+ */
+static void
+nlmsvc_delete_block(struct nlm_block *block, int unlock)
+{
+ struct file_lock *fl = &block->b_call.a_args.lock.fl;
+ struct nlm_file *file = block->b_file;
+ struct nlm_block **bp;
+
+ dprintk("lockd: deleting block %p...\n", block);
+
+ /* Remove block from list */
+ nlmsvc_remove_block(block);
+
+ /* If granted, unlock it, else remove from inode block list */
+ if (unlock && block->b_granted) {
+ dprintk("lockd: deleting granted lock\n");
+ fl->fl_type = F_UNLCK;
+ posix_lock_file(&block->b_file->f_file, fl, 0);
+ block->b_granted = 0;
+ } else {
+ dprintk("lockd: unblocking blocked lock\n");
+ posix_unblock_lock(fl);
+ }
+
+ /* If the block is in the middle of a GRANT callback,
+ * don't kill it yet. */
+ if (block->b_incall) {
+ nlmsvc_insert_block(block, NLM_NEVER);
+ block->b_done = 1;
+ return;
+ }
+
+ /* Remove block from file's list of blocks */
+ for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) {
+ if (*bp == block) {
+ *bp = block->b_fnext;
+ break;
+ }
+ }
+
+ if (block->b_host)
+ nlm_release_host(block->b_host);
+ nlmclnt_freegrantargs(&block->b_call);
+ kfree(block);
+}
+
+/*
+ * Loop over all blocks and perform the action specified.
+ * (NLM_ACT_CHECK handled by nlmsvc_inspect_file).
+ */
+int
+nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct nlm_block *block, *next;
+
+ down(&file->f_sema);
+ for (block = file->f_blocks; block; block = next) {
+ next = block->b_fnext;
+ if (action == NLM_ACT_MARK)
+ block->b_host->h_inuse = 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ if (host == NULL || host == block->b_host)
+ nlmsvc_delete_block(block, 1);
+ }
+ }
+ up(&file->f_sema);
+ return 0;
+}
+
+/*
+ * Attempt to establish a lock, and if it can't be granted, block it
+ * if required.
+ */
+u32
+nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
+ struct nlm_lock *lock, int wait, u32 cookie)
+{
+ struct file_lock *conflock;
+ struct nlm_block *block;
+ int error;
+
+ dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n",
+ file->f_file.f_inode->i_dev,
+ file->f_file.f_inode->i_ino,
+ lock->fl.fl_type, lock->fl.fl_pid,
+ lock->fl.fl_start,
+ lock->fl.fl_end,
+ wait);
+
+ /* Lock file against concurrent access */
+ down(&file->f_sema);
+
+ /* Get existing block (in case client is busy-waiting) */
+ block = nlmsvc_lookup_block(file, lock, 0);
+
+ lock->fl.fl_flags |= FL_LOCKD;
+
+again:
+ if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
+ error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+ if (block)
+ nlmsvc_delete_block(block, 0);
+ up(&file->f_sema);
+
+ dprintk("lockd: posix_lock_file returned %d\n", -error);
+ switch(-error) {
+ case 0:
+ return nlm_granted;
+ case EDEADLK: /* no applicable NLM status */
+ case EAGAIN:
+ return nlm_lck_denied;
+ default: /* includes ENOLCK */
+ return nlm_lck_denied_nolocks;
+ }
+ }
+
+ if (!wait) {
+ up(&file->f_sema);
+ return nlm_lck_denied;
+ }
+
+ /* If we don't have a block, create and initialize it. Then
+ * retry because we may have slept in kmalloc. */
+ if (block == NULL) {
+ dprintk("lockd: blocking on this lock (allocating).\n");
+ if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
+ return nlm_lck_denied_nolocks;
+ goto again;
+ }
+
+ /* Append to list of blocked */
+ nlmsvc_insert_block(block, NLM_NEVER);
+
+ /* Now add block to block list of the conflicting lock */
+ dprintk("lockd: blocking on this lock.\n");
+ posix_block_lock(conflock, &block->b_call.a_args.lock.fl);
+
+ up(&file->f_sema);
+ return nlm_lck_blocked;
+}
+
+/*
+ * Test for presence of a conflicting lock.
+ */
+u32
+nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
+ struct nlm_lock *conflock)
+{
+ struct file_lock *fl;
+
+ dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %ld-%ld)\n",
+ file->f_file.f_inode->i_dev,
+ file->f_file.f_inode->i_ino,
+ lock->fl.fl_type,
+ lock->fl.fl_start,
+ lock->fl.fl_end);
+
+ if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+ dprintk("lockd: conflicting lock(ty=%d, %ld-%ld)\n",
+ fl->fl_type, fl->fl_start, fl->fl_end);
+ conflock->caller = "somehost"; /* FIXME */
+ conflock->oh.len = 0; /* don't return OH info */
+ conflock->fl = *fl;
+ return nlm_lck_denied;
+ }
+
+ return nlm_granted;
+}
+
+/*
+ * Remove a lock.
+ * This implies a CANCEL call: We send a GRANT_MSG, the client replies
+ * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
+ * afterwards. In this case the block will still be there, and hence
+ * must be removed.
+ */
+u32
+nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
+{
+ int error;
+
+ dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %ld-%ld)\n",
+ file->f_file.f_inode->i_dev,
+ file->f_file.f_inode->i_ino,
+ lock->fl.fl_pid,
+ lock->fl.fl_start,
+ lock->fl.fl_end);
+
+ /* First, cancel any lock that might be there */
+ nlmsvc_cancel_blocked(file, lock);
+
+ lock->fl.fl_type = F_UNLCK;
+ error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+ return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
+}
+
+/*
+ * Cancel a previously blocked request.
+ *
+ * A cancel request always overrides any grant that may currently
+ * be in progress.
+ * The calling procedure must check whether the file can be closed.
+ */
+u32
+nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
+{
+ struct nlm_block *block;
+
+ dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %ld-%ld)\n",
+ file->f_file.f_inode->i_dev,
+ file->f_file.f_inode->i_ino,
+ lock->fl.fl_pid,
+ lock->fl.fl_start,
+ lock->fl.fl_end);
+
+ down(&file->f_sema);
+ if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
+ nlmsvc_delete_block(block, 1);
+ up(&file->f_sema);
+ return nlm_granted;
+}
+
+/*
+ * Unblock a blocked lock request. This is a callback invoked from the
+ * VFS layer when a lock on which we blocked is removed.
+ *
+ * This function doesn't grant the blocked lock instantly, but rather moves
+ * the block to the head of nlm_blocked where it can be picked up by lockd.
+ */
+static void
+nlmsvc_notify_blocked(struct file_lock *fl)
+{
+ struct nlm_block **bp, *block;
+
+ dprintk("lockd: VFS unblock notification for block %p\n", fl);
+ posix_unblock_lock(fl);
+ for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) {
+ if (&block->b_call.a_args.lock.fl == fl) {
+ svc_wake_up(block->b_daemon);
+ nlmsvc_insert_block(block, 0);
+ return;
+ }
+ }
+
+ printk(KERN_WARNING "lockd: notification for unknown block!\n");
+}
+
+/*
+ * Try to claim a lock that was previously blocked.
+ *
+ * Note that we use both the RPC_GRANTED_MSG call _and_ an async
+ * RPC thread when notifying the client. This seems like overkill...
+ * Here's why:
+ * - we don't want to use a synchronous RPC thread, otherwise
+ * we might find ourselves hanging on a dead portmapper.
+ * - Some lockd implementations (e.g. HP) don't react to
+ * RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
+ */
+static void
+nlmsvc_grant_blocked(struct nlm_block *block)
+{
+ struct nlm_file *file = block->b_file;
+ struct nlm_lock *lock = &block->b_call.a_args.lock;
+ struct file_lock *conflock;
+ int error;
+
+ dprintk("lockd: grant blocked lock %p\n", block);
+
+ /* First thing is lock the file */
+ down(&file->f_sema);
+
+ /* Unlink block request from list */
+ nlmsvc_remove_block(block);
+
+ /* If b_granted is true this means we've been here before.
+ * Just retry the grant callback, possibly refreshing the RPC
+ * binding */
+ if (block->b_granted) {
+ nlm_rebind_host(block->b_host);
+ goto callback;
+ }
+
+ /* Try the lock operation again */
+ if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+ /* Bummer, we blocked again */
+ dprintk("lockd: lock still blocked\n");
+ nlmsvc_insert_block(block, NLM_NEVER);
+ posix_block_lock(conflock, &lock->fl);
+ up(&file->f_sema);
+ return;
+ }
+
+ /* Alright, no conflicting lock. Now lock it for real. If the
+ * following yields an error, this is most probably due to low
+ * memory. Retry the lock in a few seconds.
+ */
+ if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) {
+ printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
+ -error, __FUNCTION__);
+ nlmsvc_insert_block(block, jiffies + 10 * HZ);
+ up(&file->f_sema);
+ return;
+ }
+
+callback:
+ /* Lock was granted by VFS. */
+ dprintk("lockd: GRANTing blocked lock.\n");
+ block->b_granted = 1;
+ block->b_incall = 1;
+
+ /* Schedule next grant callback in 30 seconds */
+ nlmsvc_insert_block(block, jiffies + 30 * HZ);
+
+ /* Call the client */
+ nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+ nlmsvc_grant_callback);
+ up(&file->f_sema);
+}
+
+/*
+ * This is the callback from the RPC layer when the NLM_GRANTED_MSG
+ * RPC call has succeeded or timed out.
+ * Like all RPC callbacks, it is invoked by the rpciod process, so it
+ * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
+ * chain once more in order to have it removed by lockd itself (which can
+ * then sleep on the file semaphore without disrupting e.g. the nfs client).
+ */
+static void
+nlmsvc_grant_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
+ struct nlm_block *block;
+ unsigned long timeout;
+
+ dprintk("lockd: GRANT_MSG RPC callback\n");
+ if (!(block = nlmsvc_find_block(call->a_args.cookie))) {
+ dprintk("lockd: no block for cookie %x\n", call->a_args.cookie);
+ return;
+ }
+
+ /* Technically, we should down the file semaphore here. Since we
+ * move the block towards the head of the queue only, no harm
+ * can be done, though. */
+ if (task->tk_status < 0) {
+ /* RPC error: Re-insert for retransmission */
+ timeout = jiffies + 10 * HZ;
+ } else if (block->b_done) {
+ /* Block already removed, kill it for real */
+ timeout = 0;
+ } else {
+ /* Call was successful, now wait for client callback */
+ timeout = jiffies + 60 * HZ;
+ }
+ nlmsvc_insert_block(block, timeout);
+ svc_wake_up(block->b_daemon);
+ block->b_incall = 0;
+
+ nlm_release_host(call->a_host);
+ rpc_release_task(task);
+}
+
+/*
+ * We received a GRANT_RES callback. Try to find the corresponding
+ * block.
+ */
+void
+nlmsvc_grant_reply(u32 cookie, u32 status)
+{
+ struct nlm_block *block;
+ struct nlm_file *file;
+
+ if (!(block = nlmsvc_find_block(cookie)))
+ return;
+ file = block->b_file;
+
+ file->f_count++;
+ down(&file->f_sema);
+ if ((block = nlmsvc_find_block(cookie)) != NULL) {
+ if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
+ /* Try again in a couple of seconds */
+ nlmsvc_insert_block(block, jiffies + 10 * HZ);
+ block = NULL;
+ } else {
+ /* Lock is now held by client, or has been rejected.
+ * In both cases, the block should be removed. */
+ file->f_count++;
+ up(&file->f_sema);
+ if (status == NLM_LCK_GRANTED)
+ nlmsvc_delete_block(block, 0);
+ else
+ nlmsvc_delete_block(block, 1);
+ }
+ }
+ if (!block)
+ up(&file->f_sema);
+ nlm_release_file(file);
+}
+
+/*
+ * Retry all blocked locks that have been notified. This is where lockd
+ * picks up locks that can be granted, or grant notifications that must
+ * be retransmitted.
+ */
+unsigned long
+nlmsvc_retry_blocked(void)
+{
+ struct nlm_block *block;
+
+ dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
+ nlm_blocked,
+ nlm_blocked? nlm_blocked->b_when : 0);
+ while ((block = nlm_blocked) && block->b_when < jiffies) {
+ dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
+ block, block->b_when, block->b_done);
+ if (block->b_done)
+ nlmsvc_delete_block(block, 0);
+ else
+ nlmsvc_grant_blocked(block);
+ }
+
+ if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
+ return block->b_when;
+ return 0;
+}
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
new file mode 100644
index 000000000..eed15bead
--- /dev/null
+++ b/fs/lockd/svcproc.c
@@ -0,0 +1,551 @@
+/*
+ * linux/fs/lockd/svcproc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.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>
+
+
+#define NLMDBG_FACILITY NLMDBG_CLIENT
+
+static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void nlmsvc_callback_exit(struct rpc_task *);
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_host **hostp, struct nlm_file **filp)
+{
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
+ struct nlm_lock *lock = &argp->lock;
+ u32 error;
+
+ /* nfsd callbacks must have been installed for this procedure */
+ if (!nlmsvc_ops)
+ return nlm_lck_denied_nolocks;
+
+ /* Obtain handle for client host */
+ if (rqstp->rq_client == NULL) {
+ printk(KERN_NOTICE
+ "lockd: unauthenticated request from (%08lx:%d)\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return nlm_lck_denied_nolocks;
+ }
+
+ /* Obtain host handle */
+ if (!(host = nlmsvc_lookup_host(rqstp))
+ || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+ goto no_locks;
+ *hostp = host;
+
+ /* Obtain file pointer. Not used by FREE_ALL call. */
+ if (filp != NULL) {
+ if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+ goto no_locks;
+ *filp = file;
+
+ /* Set up the missing parts of the file_lock structure */
+ lock->fl.fl_file = &file->f_file;
+ lock->fl.fl_owner = host;
+ }
+
+ return 0;
+
+no_locks:
+ if (host)
+ nlm_release_host(host);
+ return nlm_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlmsvc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ dprintk("lockd: NULL called\n");
+ return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ 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 rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now check for conflicting locks */
+ resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+
+ dprintk("lockd: TEST status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ 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 rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+#if 0
+ /* If supplied state doesn't match current state, we assume it's
+ * an old request that time-warped somehow. Any error return would
+ * do in this case because it's irrelevant anyway.
+ *
+ * NB: We don't retrieve the remote host's state yet.
+ */
+ if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+ resp->status = nlm_lck_denied_nolocks;
+ } else
+#endif
+
+ /* Now try to lock the file */
+ resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
+ argp->block, argp->cookie);
+
+ dprintk("lockd: LOCK status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: CANCEL called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Try to cancel request. */
+ resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+
+ dprintk("lockd: CANCEL status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNLOCK called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to remove the lock */
+ resp->status = nlmsvc_unlock(file, &argp->lock);
+
+ dprintk("lockd: UNLOCK status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ resp->cookie = argp->cookie;
+
+ dprintk("lockd: GRANTED called\n");
+ resp->status = nlmclnt_grant(&argp->lock);
+ dprintk("lockd: GRANTED status %ld\n", ntohl(resp->status));
+ return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: TEST_MSG called\n");
+
+ if ((stat = nlmsvc_proc_test(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_TEST_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: LOCK_MSG called\n");
+
+ if ((stat = nlmsvc_proc_lock(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: CANCEL_MSG called\n");
+
+ if ((stat = nlmsvc_proc_cancel(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: UNLOCK_MSG called\n");
+
+ if ((stat = nlmsvc_proc_unlock(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: GRANTED_MSG called\n");
+
+ if ((stat = nlmsvc_proc_granted(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+ return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: SHARE 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 rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to create the share */
+ resp->status = nlmsvc_share_file(host, file, argp);
+
+ dprintk("lockd: SHARE status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNSHARE called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to lock the file */
+ resp->status = nlmsvc_unshare_file(host, file, argp);
+
+ dprintk("lockd: UNSHARE status %ld\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlmsvc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ dprintk("lockd: NM_LOCK called\n");
+
+ argp->monitor = 0; /* just clean the monitor flag */
+ return nlmsvc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_host *host;
+
+ /* Obtain client */
+ if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL))
+ return rpc_success;
+
+ nlmsvc_free_host_resources(host);
+ nlm_release_host(host);
+ return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+ void *resp)
+{
+ struct sockaddr_in saddr = rqstp->rq_addr;
+ struct nlm_host *host;
+
+ dprintk("lockd: SM_NOTIFY called\n");
+ if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+ || ntohs(saddr.sin_port) >= 1024) {
+ printk(KERN_WARNING
+ "lockd: rejected NSM callback from %08lx:%d\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return rpc_system_err;
+ }
+
+ /* Obtain the host pointer for this NFS server and try to
+ * reclaim all locks we hold on this server.
+ */
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((host = nlm_lookup_host(NULL, &saddr, IPPROTO_UDP, 1)) != NULL) {
+ nlmclnt_recovery(host, argp->state);
+ nlm_release_host(host);
+ }
+
+ /* If we run on an NFS server, delete all locks held by the client */
+ if (nlmsvc_ops != NULL) {
+ struct svc_client *clnt;
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL
+ && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+ nlmsvc_free_host_resources(host);
+ }
+ nlm_release_host(host);
+ }
+
+ return rpc_success;
+}
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_rqst *call;
+
+ if (!(call = nlmclnt_alloc_call()))
+ return rpc_system_err;
+
+ host = nlmclnt_lookup_host(&rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+ if (!host) {
+ kfree(call);
+ return rpc_system_err;
+ }
+
+ call->a_flags = RPC_TASK_ASYNC;
+ call->a_host = host;
+ memcpy(&call->a_args, resp, sizeof(*resp));
+
+ if (nlmclnt_async_call(call, proc, nlmsvc_callback_exit) < 0)
+ return rpc_system_err;
+
+ return rpc_success;
+}
+
+static void
+nlmsvc_callback_exit(struct rpc_task *task)
+{
+ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: %4d callback failed (errno = %d)\n",
+ task->tk_pid, -task->tk_status);
+ }
+ nlm_release_host(call->a_host);
+ rpc_release_task(task);
+ kfree(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+#define nlmsvc_proc_none NULL
+#define nlmsvc_encode_norep NULL
+#define nlmsvc_decode_norep NULL
+#define nlmsvc_decode_testres NULL
+#define nlmsvc_proc_test_res NULL
+#define nlmsvc_proc_lock_res NULL
+#define nlmsvc_proc_cancel_res NULL
+#define nlmsvc_proc_unlock_res NULL
+#define nlmsvc_proc_granted_res NULL
+struct nlm_void { int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest) \
+ { (svc_procfunc) nlmsvc_proc_##name, \
+ (kxdrproc_t) nlmsvc_decode_##xargt, \
+ (kxdrproc_t) nlmsvc_encode_##xrest, \
+ NULL, \
+ sizeof(struct nlm_##argt), \
+ sizeof(struct nlm_##rest), \
+ 0, \
+ 0 \
+ }
+struct svc_procedure nlmsvc_procedures[] = {
+ PROC(null, void, void, void, void),
+ PROC(test, testargs, testres, args, res),
+ PROC(lock, lockargs, res, args, res),
+ PROC(cancel, cancargs, res, args, res),
+ PROC(unlock, unlockargs, res, args, res),
+ PROC(granted, testargs, res, args, res),
+ PROC(test_msg, testargs, norep, args, void),
+ PROC(lock_msg, lockargs, norep, args, void),
+ PROC(cancel_msg, cancargs, norep, args, void),
+ PROC(unlock_msg, unlockargs, norep, args, void),
+ PROC(granted_msg, testargs, norep, args, void),
+ PROC(test_res, testres, norep, res, void),
+ PROC(lock_res, res, norep, res, void),
+ PROC(cancel_res, res, norep, res, void),
+ PROC(unlock_res, res, norep, res, void),
+ PROC(granted_res, res, norep, res, void),
+ PROC(none, void, void, void, void),
+ PROC(none, void, void, void, void),
+ PROC(none, void, void, void, void),
+ PROC(none, void, void, void, void),
+ PROC(share, shareargs, shareres, args, res),
+ PROC(unshare, shareargs, shareres, args, res),
+ PROC(nm_lock, lockargs, res, args, res),
+ PROC(free_all, notify, void, args, void),
+
+ /* statd callback */
+ PROC(sm_notify, reboot, void, reboot, void),
+};
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
new file mode 100644
index 000000000..a8e7af942
--- /dev/null
+++ b/fs/lockd/svcshare.c
@@ -0,0 +1,111 @@
+/*
+ * linux/fs/lockd/svcshare.c
+ *
+ * Management of DOS shares.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+
+static inline int
+nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh)
+{
+ return share->s_owner.len == oh->len
+ && !memcmp(share->s_owner.data, oh->data, oh->len);
+}
+
+u32
+nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
+ struct nlm_args *argp)
+{
+ struct nlm_share *share;
+ struct xdr_netobj *oh = &argp->lock.oh;
+ u8 *ohdata;
+
+ for (share = file->f_shares; share; share = share->s_next) {
+ if (share->s_host == host && nlm_cmp_owner(share, oh))
+ goto update;
+ if ((argp->fsm_access & share->s_mode)
+ || (argp->fsm_mode & share->s_access ))
+ return nlm_lck_denied;
+ }
+
+ share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len,
+ GFP_KERNEL);
+ if (share == NULL)
+ return nlm_lck_denied_nolocks;
+
+ /* Copy owner handle */
+ ohdata = (u8 *) (share + 1);
+ memcpy(ohdata, oh->data, oh->len);
+
+ share->s_file = file;
+ share->s_host = host;
+ share->s_owner.data = ohdata;
+ share->s_owner.len = oh->len;
+ share->s_next = file->f_shares;
+ file->f_shares = share;
+ file->f_count += 1;
+
+update:
+ share->s_access = argp->fsm_access;
+ share->s_mode = argp->fsm_mode;
+ return nlm_granted;
+}
+
+/*
+ * Delete a share.
+ */
+u32
+nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
+ struct nlm_args *argp)
+{
+ struct nlm_share *share, **shpp;
+ struct xdr_netobj *oh = &argp->lock.oh;
+
+ for (shpp = &file->f_shares; (share = *shpp); shpp = &share->s_next) {
+ if (share->s_host == host && nlm_cmp_owner(share, oh)) {
+ *shpp = share->s_next;
+ kfree(share);
+ return nlm_granted;
+ }
+ }
+
+ /* X/Open spec says return success even if there was no
+ * corresponding share. */
+ return nlm_granted;
+}
+
+/*
+ * Traverse all shares for a given file (and host).
+ * NLM_ACT_CHECK is handled by nlmsvc_inspect_file.
+ */
+int
+nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct nlm_share *share, **shpp;
+
+ shpp = &file->f_shares;
+ while ((share = *shpp) != NULL) {
+ if (action == NLM_ACT_MARK)
+ share->s_host->h_inuse = 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ if (host == NULL || host == share->s_host) {
+ *shpp = share->s_next;
+ kfree(share);
+ continue;
+ }
+ }
+ shpp = &share->s_next;
+ }
+
+ return 0;
+}
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
new file mode 100644
index 000000000..24093a615
--- /dev/null
+++ b/fs/lockd/svcsubs.c
@@ -0,0 +1,278 @@
+/*
+ * linux/fs/lockd/svcsubs.c
+ *
+ * Various support routines for the NLM server.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/export.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY NLMDBG_SVCSUBS
+
+
+/*
+ * Global file hash table
+ */
+#define FILE_NRHASH 32
+#define FILE_HASH(dev, ino) (((dev) + (ino)) & FILE_NRHASH)
+static struct nlm_file * nlm_files[FILE_NRHASH];
+static struct semaphore nlm_file_sema = MUTEX;
+
+/*
+ * Lookup file info. If it doesn't exist, create a file info struct
+ * and open a (VFS) file for the given inode.
+ *
+ * FIXME:
+ * Note that we open the file O_RDONLY even when creating write locks.
+ * This is not quite right, but for now, we assume the client performs
+ * the proper R/W checking.
+ */
+u32
+nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
+ struct nfs_fh *f)
+{
+ struct nlm_file *file;
+ struct knfs_fh *fh = (struct knfs_fh *) f;
+ unsigned int hash = FILE_HASH(fh->fh_dev, fh->fh_ino);
+ u32 nfserr;
+
+ dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino);
+
+ /* Lock file table */
+ down(&nlm_file_sema);
+
+ for (file = nlm_files[hash]; file; file = file->f_next) {
+ if (file->f_handle.fh_ino == fh->fh_ino
+ && !memcmp(&file->f_handle, fh, sizeof(*fh)))
+ goto found;
+ }
+
+ dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino);
+ if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
+ up(&nlm_file_sema);
+ return nlm_lck_denied_nolocks;
+ }
+
+ memset(file, 0, sizeof(*file));
+ file->f_handle = *fh;
+ file->f_sema = MUTEX;
+
+ /* Open the file. Note that this must not sleep for too long, else
+ * we would lock up lockd:-) So no NFS re-exports, folks. */
+ if ((nfserr = nlmsvc_ops->fopen(rqstp, fh, &file->f_file)) != 0) {
+ dprintk("lockd: open failed (nfserr %ld)\n", ntohl(nfserr));
+ kfree(file);
+ up(&nlm_file_sema);
+ return nlm_lck_denied;
+ }
+
+ file->f_next = nlm_files[hash];
+ nlm_files[hash] = file;
+
+found:
+ dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
+ *result = file;
+ up(&nlm_file_sema);
+ file->f_count++;
+ return 0;
+}
+
+/*
+ * Delete a file after having released all locks, blocks and shares
+ */
+static inline void
+nlm_delete_file(struct nlm_file *file)
+{
+ struct inode *inode = nlmsvc_file_inode(file);
+ struct nlm_file **fp, *f;
+
+ dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino);
+ fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino);
+ while ((f = *fp) != NULL) {
+ if (f == file) {
+ *fp = file->f_next;
+ nlmsvc_ops->fclose(&file->f_file);
+ kfree(file);
+ return;
+ }
+ fp = &file->f_next;
+ }
+
+ printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
+}
+
+/*
+ * Loop over all locks on the given file and perform the specified
+ * action.
+ */
+static int
+nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct inode *inode = nlmsvc_file_inode(file);
+ struct file_lock *fl;
+ struct nlm_host *lockhost;
+
+again:
+ file->f_locks = 0;
+ for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+ if (!(fl->fl_flags & FL_LOCKD))
+ continue;
+
+ /* update current lock count */
+ file->f_locks++;
+ lockhost = (struct nlm_host *) fl->fl_owner;
+ if (action == NLM_ACT_MARK)
+ lockhost->h_inuse = 1;
+ else if (action == NLM_ACT_CHECK)
+ return 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ struct file_lock lock = *fl;
+
+ if (host && lockhost != host)
+ continue;
+
+ lock.fl_type = F_UNLCK;
+ lock.fl_start = 0;
+ lock.fl_end = NLM_OFFSET_MAX;
+ if (posix_lock_file(&file->f_file, &lock, 0) < 0) {
+ printk("lockd: unlock failure in %s:%d\n",
+ __FILE__, __LINE__);
+ return 1;
+ }
+ goto again;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Operate on a single file
+ */
+static inline int
+nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ if (action == NLM_ACT_CHECK) {
+ /* Fast path for mark and sweep garbage collection */
+ if (file->f_count || file->f_blocks || file->f_shares)
+ return 1;
+ } else {
+ if (nlmsvc_traverse_blocks(host, file, action)
+ || nlmsvc_traverse_shares(host, file, action))
+ return 1;
+ }
+ return nlm_traverse_locks(host, file, action);
+}
+
+/*
+ * Loop over all files in the file table.
+ */
+static int
+nlm_traverse_files(struct nlm_host *host, int action)
+{
+ struct nlm_file *file, **fp;
+ int i;
+
+ down(&nlm_file_sema);
+ for (i = 0; i < FILE_NRHASH; i++) {
+ fp = nlm_files + i;
+ while ((file = *fp) != NULL) {
+ /* Traverse locks, blocks and shares of this file
+ * and update file->f_locks count */
+ if (nlm_inspect_file(host, file, action)) {
+ up(&nlm_file_sema);
+ return 1;
+ }
+
+ /* No more references to this file. Let go of it. */
+ if (!file->f_blocks && !file->f_locks
+ && !file->f_shares && !file->f_count) {
+ *fp = file->f_next;
+ nlmsvc_ops->fclose(&file->f_file);
+ kfree(file);
+ } else {
+ fp = &file->f_next;
+ }
+ }
+ }
+ up(&nlm_file_sema);
+ return 0;
+}
+
+/*
+ * Release file. If there are no more remote locks on this file,
+ * close it and free the handle.
+ *
+ * Note that we can't do proper reference counting without major
+ * contortions because the code in fs/locks.c creates, deletes and
+ * splits locks without notification. Our only way is to walk the
+ * entire lock list each time we remove a lock.
+ */
+void
+nlm_release_file(struct nlm_file *file)
+{
+ dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
+ file, file->f_count);
+
+ /* Lock file table */
+ down(&nlm_file_sema);
+
+ /* If there are no more locks etc, delete the file */
+ if (--(file->f_count) == 0
+ && !nlm_inspect_file(NULL, file, NLM_ACT_CHECK))
+ nlm_delete_file(file);
+
+ up(&nlm_file_sema);
+ return;
+}
+
+/*
+ * Mark all hosts that still hold resources
+ */
+void
+nlmsvc_mark_resources(void)
+{
+ dprintk("lockd: nlmsvc_mark_resources\n");
+
+ nlm_traverse_files(NULL, NLM_ACT_MARK);
+}
+
+/*
+ * Release all resources held by the given client
+ */
+void
+nlmsvc_free_host_resources(struct nlm_host *host)
+{
+ dprintk("lockd: nlmsvc_free_host_resources\n");
+
+ if (nlm_traverse_files(host, NLM_ACT_UNLOCK))
+ printk(KERN_WARNING
+ "lockd: couldn't remove all locks held by %s",
+ host->h_name);
+}
+
+/*
+ * Delete a client when the nfsd entry is removed.
+ */
+void
+nlmsvc_invalidate_client(struct svc_client *clnt)
+{
+ struct nlm_host *host;
+
+ if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) {
+ dprintk("lockd: invalidating client for %s\n", host->h_name);
+ nlmsvc_free_host_resources(host);
+ host->h_expires = 0;
+ nlm_release_host(host);
+ }
+}
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
new file mode 100644
index 000000000..9c23733ac
--- /dev/null
+++ b/fs/lockd/xdr.c
@@ -0,0 +1,605 @@
+/*
+ * linux/fs/lockd/xdr.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#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
+#define NLM_MAXSTRLEN 1024
+
+#define QUADLEN(len) (((len) + 3) >> 2)
+
+
+u32 nlm_granted, nlm_lck_denied, nlm_lck_denied_nolocks,
+ nlm_lck_blocked, nlm_lck_denied_grace_period;
+
+
+typedef struct nlm_args nlm_args;
+
+/*
+ * Initialization of NFS status variables
+ */
+void
+nlmxdr_init(void)
+{
+ static int inited = 0;
+
+ if (inited)
+ return;
+
+ nlm_granted = htonl(NLM_LCK_GRANTED);
+ nlm_lck_denied = htonl(NLM_LCK_DENIED);
+ nlm_lck_denied_nolocks = htonl(NLM_LCK_DENIED_NOLOCKS);
+ nlm_lck_blocked = htonl(NLM_LCK_BLOCKED);
+ nlm_lck_denied_grace_period = htonl(NLM_LCK_DENIED_GRACE_PERIOD);
+
+ inited = 1;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static inline u32 *
+nlm_decode_cookie(u32 *p, u32 *c)
+{
+ unsigned int len;
+
+ if ((len = ntohl(*p++)) == 4) {
+ *c = ntohl(*p++);
+ } else if (len == 0) { /* hockeypux brain damage */
+ *c = 0;
+ } else {
+ printk(KERN_NOTICE
+ "lockd: bad cookie size %d (should be 4)\n", len);
+ return NULL;
+ }
+ return p;
+}
+
+static inline u32 *
+nlm_encode_cookie(u32 *p, u32 c)
+{
+ *p++ = htonl(sizeof(c));
+ *p++ = htonl(c);
+ return p;
+}
+
+static inline u32 *
+nlm_decode_fh(u32 *p, struct nfs_fh *f)
+{
+ unsigned int len;
+
+ if ((len = ntohl(*p++)) != sizeof(*f)) {
+ printk(KERN_NOTICE
+ "lockd: bad fhandle size %x (should be %d)\n",
+ len, sizeof(*f));
+ return NULL;
+ }
+ memcpy(f, p, sizeof(*f));
+ return p + XDR_QUADLEN(sizeof(*f));
+}
+
+static inline u32 *
+nlm_encode_fh(u32 *p, struct nfs_fh *f)
+{
+ *p++ = htonl(sizeof(*f));
+ memcpy(p, f, sizeof(*f));
+ return p + XDR_QUADLEN(sizeof(*f));
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static inline u32 *
+nlm_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_decode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_encode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+ int len;
+
+ if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+ || !(p = nlm_decode_fh(p, &lock->fh))
+ || !(p = nlm_decode_oh(p, &lock->oh)))
+ return NULL;
+
+ memset(fl, 0, sizeof(*fl));
+ fl->fl_owner = current;
+ fl->fl_pid = ntohl(*p++);
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = F_RDLCK; /* as good as anything else */
+ fl->fl_start = ntohl(*p++);
+ len = ntohl(*p++);
+ if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
+ fl->fl_end = NLM_OFFSET_MAX;
+ return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+
+ if (!(p = xdr_encode_string(p, lock->caller))
+ || !(p = nlm_encode_fh(p, &lock->fh))
+ || !(p = nlm_encode_oh(p, &lock->oh)))
+ return NULL;
+
+ *p++ = htonl(fl->fl_pid);
+ *p++ = htonl(lock->fl.fl_start);
+ if (lock->fl.fl_end == NLM_OFFSET_MAX)
+ *p++ = xdr_zero;
+ else
+ *p++ = htonl(lock->fl.fl_end - lock->fl.fl_start + 1);
+
+ return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+static u32 *
+nlm_encode_testres(u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+
+ if (resp->status == nlm_lck_denied) {
+ struct file_lock *fl = &resp->lock.fl;
+
+ *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+ *p++ = htonl(fl->fl_pid);
+
+ /* Encode owner handle. */
+ if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+ return 0;
+
+ *p++ = htonl(fl->fl_start);
+ if (fl->fl_end == NLM_OFFSET_MAX)
+ *p++ = xdr_zero;
+ else
+ *p++ = htonl(fl->fl_end - fl->fl_start + 1);
+ }
+
+ return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlmsvc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_testres(p, resp)))
+ return 0;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ argp->reclaim = ntohl(*p++);
+ argp->state = ntohl(*p++);
+ argp->monitor = 1; /* monitor client by default */
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ if (!(p = nlm_decode_cookie(p, &argp->cookie))
+ || !(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ argp->lock.fl.fl_type = F_UNLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+ int len;
+
+ memset(lock, 0, sizeof(*lock));
+ lock->fl.fl_pid = ~(u32) 0;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie))
+ || !(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN))
+ || !(p = nlm_decode_fh(p, &lock->fh))
+ || !(p = nlm_decode_oh(p, &lock->oh)))
+ return 0;
+ argp->fsm_mode = ntohl(*p++);
+ argp->fsm_access = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ *p++ = xdr_zero; /* sequence argument */
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+ int len;
+
+ if (!(p = xdr_decode_string(p, &lock->caller, &len, NLM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+ if (!(p = xdr_decode_string(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ argp->addr = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return 0;
+ resp->status = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlmclt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ return 0;
+}
+
+static int
+nlmclt_encode_testargs(struct rpc_rqst *req, u32 *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, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ if (resp->status == NLM_LCK_DENIED) {
+ struct file_lock *fl = &resp->lock.fl;
+ u32 excl, len;
+
+ memset(&resp->lock, 0, sizeof(resp->lock));
+ excl = ntohl(*p++);
+ fl->fl_pid = ntohl(*p++);
+ if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
+ return -EIO;
+
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = excl? F_WRLCK : F_RDLCK;
+ fl->fl_start = ntohl(*p++);
+ len = ntohl(*p++);
+ if (len == 0 || (fl->fl_end = fl->fl_start + len - 1) < 0)
+ fl->fl_end = NLM_OFFSET_MAX;
+ }
+ return 0;
+}
+
+
+static int
+nlmclt_encode_lockargs(struct rpc_rqst *req, u32 *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, u32 *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, u32 *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, u32 *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, u32 *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, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM_void_sz 0
+#define NLM_cookie_sz 2
+#define NLM_caller_sz 1+QUADLEN(sizeof(system_utsname.nodename))
+#define NLM_netobj_sz 1+QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM_owner_sz 1+QUADLEN(NLM_MAXOWNER) */
+#define NLM_fhandle_sz 1+QUADLEN(NFS_FHSIZE)
+#define NLM_lock_sz 3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
+#define NLM_holder_sz 4+NLM_netobj_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
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlmclt_decode_norep NULL
+
+#define PROC(proc, argtype, restype) \
+ { "nlm_" #proc, \
+ (kxdrproc_t) nlmclt_encode_##argtype, \
+ (kxdrproc_t) nlmclt_decode_##restype, \
+ MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \
+ }
+
+static struct rpc_procinfo nlm_procedures[] = {
+ PROC(null, void, void),
+ PROC(test, testargs, testres),
+ PROC(lock, lockargs, res),
+ PROC(canc, cancargs, res),
+ PROC(unlock, unlockargs, res),
+ PROC(granted, testargs, res),
+ PROC(test_msg, testargs, norep),
+ PROC(lock_msg, lockargs, norep),
+ PROC(canc_msg, cancargs, norep),
+ PROC(unlock_msg, unlockargs, norep),
+ PROC(granted_msg, testargs, norep),
+ PROC(test_res, testres, norep),
+ PROC(lock_res, res, norep),
+ PROC(canc_res, res, norep),
+ PROC(unlock_res, res, norep),
+ PROC(granted_res, res, norep),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+ PROC(share, shareargs, shareres),
+ PROC(unshare, shareargs, shareres),
+ PROC(nm_lock, lockargs, res),
+ PROC(free_all, notify, void),
+#else
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#endif
+};
+
+static struct rpc_version nlm_version1 = {
+ 1, 16, nlm_procedures,
+};
+
+static struct rpc_version nlm_version3 = {
+ 3, 24, nlm_procedures,
+};
+
+static struct rpc_version * nlm_versions[] = {
+ NULL,
+ &nlm_version1,
+ NULL,
+ &nlm_version3,
+};
+
+static struct rpc_stat nlm_stats;
+
+struct rpc_program nlm_program = {
+ "lockd",
+ NLM_PROGRAM,
+ sizeof(nlm_versions) / sizeof(nlm_versions[0]),
+ nlm_versions,
+ &nlm_stats,
+};
+
+#ifdef LOCKD_DEBUG
+char *
+nlm_procname(u32 proc)
+{
+ if (proc < sizeof(nlm_procedures)/sizeof(nlm_procedures[0]))
+ return nlm_procedures[proc].p_procname;
+ return "unknown";
+}
+#endif
+