1/*
2 * linux/fs/lockd/clntlock.c
3 *
4 * Lock handling for the client side NLM implementation
5 *
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
8
9#define __KERNEL_SYSCALLS__
10
11#include <linux/module.h>
12#include <linux/types.h>
13#include <linux/sched.h>
14#include <linux/nfs_fs.h>
15#include <linux/unistd.h>
16#include <linux/sunrpc/clnt.h>
17#include <linux/sunrpc/svc.h>
18#include <linux/lockd/lockd.h>
19#include <linux/smp_lock.h>
20
21#define NLMDBG_FACILITY		NLMDBG_CLIENT
22
23/*
24 * Local function prototypes
25 */
26static int			reclaimer(void *ptr);
27
28/*
29 * The following functions handle blocking and granting from the
30 * client perspective.
31 */
32
33/*
34 * This is the representation of a blocked client lock.
35 */
36struct nlm_wait {
37	struct nlm_wait *	b_next;		/* linked list */
38	wait_queue_head_t	b_wait;		/* where to wait on */
39	struct nlm_host *	b_host;
40	struct file_lock *	b_lock;		/* local file lock */
41	unsigned short		b_reclaim;	/* got to reclaim lock */
42	u32			b_status;	/* grant callback status */
43};
44
45static struct nlm_wait *	nlm_blocked;
46
47/*
48 * Block on a lock
49 */
50int
51nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
52{
53	struct nlm_wait	block, **head;
54	int		err;
55	u32		pstate;
56
57	block.b_host   = host;
58	block.b_lock   = fl;
59	init_waitqueue_head(&block.b_wait);
60	block.b_status = NLM_LCK_BLOCKED;
61	block.b_next   = nlm_blocked;
62	nlm_blocked    = &block;
63
64	/* Remember pseudo nsm state */
65	pstate = host->h_state;
66
67	/* Go to sleep waiting for GRANT callback. Some servers seem
68	 * to lose callbacks, however, so we're going to poll from
69	 * time to time just to make sure.
70	 *
71	 * For now, the retry frequency is pretty high; normally
72	 * a 1 minute timeout would do. See the comment before
73	 * nlmclnt_lock for an explanation.
74	 */
75	sleep_on_timeout(&block.b_wait, 30*HZ);
76
77	for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
78		if (*head == &block) {
79			*head = block.b_next;
80			break;
81		}
82	}
83
84	if (!signalled()) {
85		*statp = block.b_status;
86		return 0;
87	}
88
89	/* Okay, we were interrupted. Cancel the pending request
90	 * unless the server has rebooted.
91	 */
92	if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
93		printk(KERN_NOTICE
94			"lockd: CANCEL call failed (errno %d)\n", -err);
95
96	return -ERESTARTSYS;
97}
98
99/*
100 * The server lockd has called us back to tell us the lock was granted
101 */
102u32
103nlmclnt_grant(struct nlm_lock *lock)
104{
105	struct nlm_wait	*block;
106
107	/*
108	 * Look up blocked request based on arguments.
109	 * Warning: must not use cookie to match it!
110	 */
111	for (block = nlm_blocked; block; block = block->b_next) {
112		if (nlm_compare_locks(block->b_lock, &lock->fl))
113			break;
114	}
115
116	/* Ooops, no blocked request found. */
117	if (block == NULL)
118		return nlm_lck_denied;
119
120	/* Alright, we found the lock. Set the return status and
121	 * wake up the caller.
122	 */
123	block->b_status = NLM_LCK_GRANTED;
124	wake_up(&block->b_wait);
125
126	return nlm_granted;
127}
128
129/*
130 * The following procedures deal with the recovery of locks after a
131 * server crash.
132 */
133
134static
135void nlmclnt_mark_reclaim(struct nlm_host *host)
136{
137	struct file_lock *fl;
138	struct inode *inode;
139	struct list_head *tmp;
140
141	list_for_each(tmp, &file_lock_list) {
142		fl = list_entry(tmp, struct file_lock, fl_link);
143
144		inode = fl->fl_file->f_dentry->d_inode;
145		if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
146			continue;
147		if (fl->fl_u.nfs_fl.host != host)
148			continue;
149		if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
150			continue;
151		fl->fl_u.nfs_fl.flags |= NFS_LCK_RECLAIM;
152	}
153}
154
155/*
156 * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number,
157 * that we mark locks for reclaiming, and that we bump the pseudo NSM state.
158 */
159static inline
160void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate)
161{
162	host->h_monitored = 0;
163	host->h_nsmstate = newstate;
164	host->h_state++;
165	host->h_nextrebind = 0;
166	nlm_rebind_host(host);
167	nlmclnt_mark_reclaim(host);
168	dprintk("NLM: reclaiming locks for host %s", host->h_name);
169}
170
171/*
172 * Reclaim all locks on server host. We do this by spawning a separate
173 * reclaimer thread.
174 */
175void
176nlmclnt_recovery(struct nlm_host *host, u32 newstate)
177{
178	if (host->h_reclaiming++) {
179		if (host->h_nsmstate == newstate)
180			return;
181		nlmclnt_prepare_reclaim(host, newstate);
182	} else {
183		nlmclnt_prepare_reclaim(host, newstate);
184		nlm_get_host(host);
185		MOD_INC_USE_COUNT;
186		kernel_thread(reclaimer, host, CLONE_SIGNAL);
187	}
188}
189
190static int
191reclaimer(void *ptr)
192{
193	struct nlm_host	  *host = (struct nlm_host *) ptr;
194	struct nlm_wait	  *block;
195	struct list_head *tmp;
196	struct file_lock *fl;
197	struct inode *inode;
198
199	daemonize();
200	reparent_to_init();
201	snprintf(current->comm, sizeof(current->comm),
202		 "%s-reclaim",
203		 host->h_name);
204
205	/* This one ensures that our parent doesn't terminate while the
206	 * reclaim is in progress */
207	lock_kernel();
208	lockd_up();
209
210	/* First, reclaim all locks that have been marked. */
211restart:
212	list_for_each(tmp, &file_lock_list) {
213		fl = list_entry(tmp, struct file_lock, fl_link);
214
215		inode = fl->fl_file->f_dentry->d_inode;
216		if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
217			continue;
218		if (fl->fl_u.nfs_fl.host != host)
219			continue;
220		if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
221			continue;
222
223		fl->fl_u.nfs_fl.flags &= ~NFS_LCK_RECLAIM;
224		nlmclnt_reclaim(host, fl);
225		if (signalled())
226			break;
227		goto restart;
228	}
229
230	host->h_reclaiming = 0;
231	wake_up(&host->h_gracewait);
232
233	/* Now, wake up all processes that sleep on a blocked lock */
234	for (block = nlm_blocked; block; block = block->b_next) {
235		if (block->b_host == host) {
236			block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
237			wake_up(&block->b_wait);
238		}
239	}
240
241	/* Release host handle after use */
242	nlm_release_host(host);
243	lockd_down();
244	unlock_kernel();
245	MOD_DEC_USE_COUNT;
246
247	return 0;
248}
249