xref: /linux/fs/lockd/clntlock.c (revision f3d9478b2ce468c3115b02ecae7e975990697f15)
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 #include <linux/module.h>
10 #include <linux/types.h>
11 #include <linux/time.h>
12 #include <linux/nfs_fs.h>
13 #include <linux/sunrpc/clnt.h>
14 #include <linux/sunrpc/svc.h>
15 #include <linux/lockd/lockd.h>
16 #include <linux/smp_lock.h>
17 
18 #define NLMDBG_FACILITY		NLMDBG_CLIENT
19 
20 /*
21  * Local function prototypes
22  */
23 static int			reclaimer(void *ptr);
24 
25 /*
26  * The following functions handle blocking and granting from the
27  * client perspective.
28  */
29 
30 /*
31  * This is the representation of a blocked client lock.
32  */
33 struct nlm_wait {
34 	struct list_head	b_list;		/* linked list */
35 	wait_queue_head_t	b_wait;		/* where to wait on */
36 	struct nlm_host *	b_host;
37 	struct file_lock *	b_lock;		/* local file lock */
38 	unsigned short		b_reclaim;	/* got to reclaim lock */
39 	u32			b_status;	/* grant callback status */
40 };
41 
42 static LIST_HEAD(nlm_blocked);
43 
44 /*
45  * Queue up a lock for blocking so that the GRANTED request can see it
46  */
47 struct nlm_wait *nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl)
48 {
49 	struct nlm_wait *block;
50 
51 	block = kmalloc(sizeof(*block), GFP_KERNEL);
52 	if (block != NULL) {
53 		block->b_host = host;
54 		block->b_lock = fl;
55 		init_waitqueue_head(&block->b_wait);
56 		block->b_status = NLM_LCK_BLOCKED;
57 		list_add(&block->b_list, &nlm_blocked);
58 	}
59 	return block;
60 }
61 
62 void nlmclnt_finish_block(struct nlm_wait *block)
63 {
64 	if (block == NULL)
65 		return;
66 	list_del(&block->b_list);
67 	kfree(block);
68 }
69 
70 /*
71  * Block on a lock
72  */
73 int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
74 {
75 	long ret;
76 
77 	/* A borken server might ask us to block even if we didn't
78 	 * request it. Just say no!
79 	 */
80 	if (block == NULL)
81 		return -EAGAIN;
82 
83 	/* Go to sleep waiting for GRANT callback. Some servers seem
84 	 * to lose callbacks, however, so we're going to poll from
85 	 * time to time just to make sure.
86 	 *
87 	 * For now, the retry frequency is pretty high; normally
88 	 * a 1 minute timeout would do. See the comment before
89 	 * nlmclnt_lock for an explanation.
90 	 */
91 	ret = wait_event_interruptible_timeout(block->b_wait,
92 			block->b_status != NLM_LCK_BLOCKED,
93 			timeout);
94 	if (ret < 0)
95 		return -ERESTARTSYS;
96 	req->a_res.status = block->b_status;
97 	return 0;
98 }
99 
100 /*
101  * The server lockd has called us back to tell us the lock was granted
102  */
103 u32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock)
104 {
105 	const struct file_lock *fl = &lock->fl;
106 	const struct nfs_fh *fh = &lock->fh;
107 	struct nlm_wait	*block;
108 	u32 res = nlm_lck_denied;
109 
110 	/*
111 	 * Look up blocked request based on arguments.
112 	 * Warning: must not use cookie to match it!
113 	 */
114 	list_for_each_entry(block, &nlm_blocked, b_list) {
115 		struct file_lock *fl_blocked = block->b_lock;
116 
117 		if (fl_blocked->fl_start != fl->fl_start)
118 			continue;
119 		if (fl_blocked->fl_end != fl->fl_end)
120 			continue;
121 		/*
122 		 * Careful! The NLM server will return the 32-bit "pid" that
123 		 * we put on the wire: in this case the lockowner "pid".
124 		 */
125 		if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid)
126 			continue;
127 		if (!nlm_cmp_addr(&block->b_host->h_addr, addr))
128 			continue;
129 		if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_dentry->d_inode) ,fh) != 0)
130 			continue;
131 		/* Alright, we found a lock. Set the return status
132 		 * and wake up the caller
133 		 */
134 		block->b_status = NLM_LCK_GRANTED;
135 		wake_up(&block->b_wait);
136 		res = nlm_granted;
137 	}
138 	return res;
139 }
140 
141 /*
142  * The following procedures deal with the recovery of locks after a
143  * server crash.
144  */
145 
146 /*
147  * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number,
148  * that we mark locks for reclaiming, and that we bump the pseudo NSM state.
149  */
150 static inline
151 void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate)
152 {
153 	host->h_monitored = 0;
154 	host->h_nsmstate = newstate;
155 	host->h_state++;
156 	host->h_nextrebind = 0;
157 	nlm_rebind_host(host);
158 
159 	/*
160 	 * Mark the locks for reclaiming.
161 	 */
162 	list_splice_init(&host->h_granted, &host->h_reclaim);
163 
164 	dprintk("NLM: reclaiming locks for host %s", host->h_name);
165 }
166 
167 /*
168  * Reclaim all locks on server host. We do this by spawning a separate
169  * reclaimer thread.
170  */
171 void
172 nlmclnt_recovery(struct nlm_host *host, u32 newstate)
173 {
174 	if (host->h_reclaiming++) {
175 		if (host->h_nsmstate == newstate)
176 			return;
177 		nlmclnt_prepare_reclaim(host, newstate);
178 	} else {
179 		nlmclnt_prepare_reclaim(host, newstate);
180 		nlm_get_host(host);
181 		__module_get(THIS_MODULE);
182 		if (kernel_thread(reclaimer, host, CLONE_KERNEL) < 0)
183 			module_put(THIS_MODULE);
184 	}
185 }
186 
187 static int
188 reclaimer(void *ptr)
189 {
190 	struct nlm_host	  *host = (struct nlm_host *) ptr;
191 	struct nlm_wait	  *block;
192 	struct file_lock *fl, *next;
193 
194 	daemonize("%s-reclaim", host->h_name);
195 	allow_signal(SIGKILL);
196 
197 	/* This one ensures that our parent doesn't terminate while the
198 	 * reclaim is in progress */
199 	lock_kernel();
200 	lockd_up();
201 
202 	/* First, reclaim all locks that have been marked. */
203 restart:
204 	list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
205 		list_del_init(&fl->fl_u.nfs_fl.list);
206 
207 		if (signalled())
208 			continue;
209 		if (nlmclnt_reclaim(host, fl) == 0)
210 			list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted);
211 		goto restart;
212 	}
213 
214 	host->h_reclaiming = 0;
215 
216 	/* Now, wake up all processes that sleep on a blocked lock */
217 	list_for_each_entry(block, &nlm_blocked, b_list) {
218 		if (block->b_host == host) {
219 			block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
220 			wake_up(&block->b_wait);
221 		}
222 	}
223 
224 	/* Release host handle after use */
225 	nlm_release_host(host);
226 	lockd_down();
227 	unlock_kernel();
228 	module_put_and_exit(0);
229 }
230