Commit abd1ec4efd82ca06127bce833ad8a4bbec8a0dcb
Committed by
J. Bruce Fields
1 parent
a75c5d01e4
Exists in
master
and in
7 other branches
lockd: close potential race with rapid lockd_up/lockd_down cycle
If lockd_down is called very rapidly after lockd_up returns, then there is a slim chance that lockd() will never be called. kthread() will return before calling the function, so we'll end up never actually calling the cleanup functions for the thread. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Showing 1 changed file with 13 additions and 20 deletions Inline Diff
fs/lockd/svc.c
1 | /* | 1 | /* |
2 | * linux/fs/lockd/svc.c | 2 | * linux/fs/lockd/svc.c |
3 | * | 3 | * |
4 | * This is the central lockd service. | 4 | * This is the central lockd service. |
5 | * | 5 | * |
6 | * FIXME: Separate the lockd NFS server functionality from the lockd NFS | 6 | * FIXME: Separate the lockd NFS server functionality from the lockd NFS |
7 | * client functionality. Oh why didn't Sun create two separate | 7 | * client functionality. Oh why didn't Sun create two separate |
8 | * services in the first place? | 8 | * services in the first place? |
9 | * | 9 | * |
10 | * Authors: Olaf Kirch (okir@monad.swb.de) | 10 | * Authors: Olaf Kirch (okir@monad.swb.de) |
11 | * | 11 | * |
12 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | 12 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/sysctl.h> | 17 | #include <linux/sysctl.h> |
18 | #include <linux/moduleparam.h> | 18 | #include <linux/moduleparam.h> |
19 | 19 | ||
20 | #include <linux/sched.h> | 20 | #include <linux/sched.h> |
21 | #include <linux/errno.h> | 21 | #include <linux/errno.h> |
22 | #include <linux/in.h> | 22 | #include <linux/in.h> |
23 | #include <linux/uio.h> | 23 | #include <linux/uio.h> |
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/smp.h> | 25 | #include <linux/smp.h> |
26 | #include <linux/smp_lock.h> | 26 | #include <linux/smp_lock.h> |
27 | #include <linux/mutex.h> | 27 | #include <linux/mutex.h> |
28 | #include <linux/kthread.h> | 28 | #include <linux/kthread.h> |
29 | #include <linux/freezer.h> | 29 | #include <linux/freezer.h> |
30 | 30 | ||
31 | #include <linux/sunrpc/types.h> | 31 | #include <linux/sunrpc/types.h> |
32 | #include <linux/sunrpc/stats.h> | 32 | #include <linux/sunrpc/stats.h> |
33 | #include <linux/sunrpc/clnt.h> | 33 | #include <linux/sunrpc/clnt.h> |
34 | #include <linux/sunrpc/svc.h> | 34 | #include <linux/sunrpc/svc.h> |
35 | #include <linux/sunrpc/svcsock.h> | 35 | #include <linux/sunrpc/svcsock.h> |
36 | #include <net/ip.h> | 36 | #include <net/ip.h> |
37 | #include <linux/lockd/lockd.h> | 37 | #include <linux/lockd/lockd.h> |
38 | #include <linux/lockd/sm_inter.h> | 38 | #include <linux/lockd/sm_inter.h> |
39 | #include <linux/nfs.h> | 39 | #include <linux/nfs.h> |
40 | 40 | ||
41 | #define NLMDBG_FACILITY NLMDBG_SVC | 41 | #define NLMDBG_FACILITY NLMDBG_SVC |
42 | #define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE) | 42 | #define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE) |
43 | #define ALLOWED_SIGS (sigmask(SIGKILL)) | 43 | #define ALLOWED_SIGS (sigmask(SIGKILL)) |
44 | 44 | ||
45 | static struct svc_program nlmsvc_program; | 45 | static struct svc_program nlmsvc_program; |
46 | 46 | ||
47 | struct nlmsvc_binding * nlmsvc_ops; | 47 | struct nlmsvc_binding * nlmsvc_ops; |
48 | EXPORT_SYMBOL(nlmsvc_ops); | 48 | EXPORT_SYMBOL(nlmsvc_ops); |
49 | 49 | ||
50 | static DEFINE_MUTEX(nlmsvc_mutex); | 50 | static DEFINE_MUTEX(nlmsvc_mutex); |
51 | static unsigned int nlmsvc_users; | 51 | static unsigned int nlmsvc_users; |
52 | static struct task_struct *nlmsvc_task; | 52 | static struct task_struct *nlmsvc_task; |
53 | static struct svc_serv *nlmsvc_serv; | 53 | static struct svc_rqst *nlmsvc_rqst; |
54 | int nlmsvc_grace_period; | 54 | int nlmsvc_grace_period; |
55 | unsigned long nlmsvc_timeout; | 55 | unsigned long nlmsvc_timeout; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * These can be set at insmod time (useful for NFS as root filesystem), | 58 | * These can be set at insmod time (useful for NFS as root filesystem), |
59 | * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 | 59 | * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 |
60 | */ | 60 | */ |
61 | static unsigned long nlm_grace_period; | 61 | static unsigned long nlm_grace_period; |
62 | static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; | 62 | static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; |
63 | static int nlm_udpport, nlm_tcpport; | 63 | static int nlm_udpport, nlm_tcpport; |
64 | int nsm_use_hostnames = 0; | 64 | int nsm_use_hostnames = 0; |
65 | 65 | ||
66 | /* | 66 | /* |
67 | * Constants needed for the sysctl interface. | 67 | * Constants needed for the sysctl interface. |
68 | */ | 68 | */ |
69 | static const unsigned long nlm_grace_period_min = 0; | 69 | static const unsigned long nlm_grace_period_min = 0; |
70 | static const unsigned long nlm_grace_period_max = 240; | 70 | static const unsigned long nlm_grace_period_max = 240; |
71 | static const unsigned long nlm_timeout_min = 3; | 71 | static const unsigned long nlm_timeout_min = 3; |
72 | static const unsigned long nlm_timeout_max = 20; | 72 | static const unsigned long nlm_timeout_max = 20; |
73 | static const int nlm_port_min = 0, nlm_port_max = 65535; | 73 | static const int nlm_port_min = 0, nlm_port_max = 65535; |
74 | 74 | ||
75 | #ifdef CONFIG_SYSCTL | 75 | #ifdef CONFIG_SYSCTL |
76 | static struct ctl_table_header * nlm_sysctl_table; | 76 | static struct ctl_table_header * nlm_sysctl_table; |
77 | #endif | 77 | #endif |
78 | 78 | ||
79 | static unsigned long get_lockd_grace_period(void) | 79 | static unsigned long get_lockd_grace_period(void) |
80 | { | 80 | { |
81 | /* Note: nlm_timeout should always be nonzero */ | 81 | /* Note: nlm_timeout should always be nonzero */ |
82 | if (nlm_grace_period) | 82 | if (nlm_grace_period) |
83 | return roundup(nlm_grace_period, nlm_timeout) * HZ; | 83 | return roundup(nlm_grace_period, nlm_timeout) * HZ; |
84 | else | 84 | else |
85 | return nlm_timeout * 5 * HZ; | 85 | return nlm_timeout * 5 * HZ; |
86 | } | 86 | } |
87 | 87 | ||
88 | unsigned long get_nfs_grace_period(void) | 88 | unsigned long get_nfs_grace_period(void) |
89 | { | 89 | { |
90 | unsigned long lockdgrace = get_lockd_grace_period(); | 90 | unsigned long lockdgrace = get_lockd_grace_period(); |
91 | unsigned long nfsdgrace = 0; | 91 | unsigned long nfsdgrace = 0; |
92 | 92 | ||
93 | if (nlmsvc_ops) | 93 | if (nlmsvc_ops) |
94 | nfsdgrace = nlmsvc_ops->get_grace_period(); | 94 | nfsdgrace = nlmsvc_ops->get_grace_period(); |
95 | 95 | ||
96 | return max(lockdgrace, nfsdgrace); | 96 | return max(lockdgrace, nfsdgrace); |
97 | } | 97 | } |
98 | EXPORT_SYMBOL(get_nfs_grace_period); | 98 | EXPORT_SYMBOL(get_nfs_grace_period); |
99 | 99 | ||
100 | static unsigned long set_grace_period(void) | 100 | static unsigned long set_grace_period(void) |
101 | { | 101 | { |
102 | nlmsvc_grace_period = 1; | 102 | nlmsvc_grace_period = 1; |
103 | return get_nfs_grace_period() + jiffies; | 103 | return get_nfs_grace_period() + jiffies; |
104 | } | 104 | } |
105 | 105 | ||
106 | static inline void clear_grace_period(void) | 106 | static inline void clear_grace_period(void) |
107 | { | 107 | { |
108 | nlmsvc_grace_period = 0; | 108 | nlmsvc_grace_period = 0; |
109 | } | 109 | } |
110 | 110 | ||
111 | /* | 111 | /* |
112 | * This is the lockd kernel thread | 112 | * This is the lockd kernel thread |
113 | */ | 113 | */ |
114 | static int | 114 | static int |
115 | lockd(void *vrqstp) | 115 | lockd(void *vrqstp) |
116 | { | 116 | { |
117 | int err = 0, preverr = 0; | 117 | int err = 0, preverr = 0; |
118 | struct svc_rqst *rqstp = vrqstp; | 118 | struct svc_rqst *rqstp = vrqstp; |
119 | unsigned long grace_period_expire; | 119 | unsigned long grace_period_expire; |
120 | 120 | ||
121 | /* try_to_freeze() is called from svc_recv() */ | 121 | /* try_to_freeze() is called from svc_recv() */ |
122 | set_freezable(); | 122 | set_freezable(); |
123 | 123 | ||
124 | /* Allow SIGKILL to tell lockd to drop all of its locks */ | 124 | /* Allow SIGKILL to tell lockd to drop all of its locks */ |
125 | allow_signal(SIGKILL); | 125 | allow_signal(SIGKILL); |
126 | 126 | ||
127 | dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); | 127 | dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); |
128 | 128 | ||
129 | /* | 129 | /* |
130 | * FIXME: it would be nice if lockd didn't spend its entire life | 130 | * FIXME: it would be nice if lockd didn't spend its entire life |
131 | * running under the BKL. At the very least, it would be good to | 131 | * running under the BKL. At the very least, it would be good to |
132 | * have someone clarify what it's intended to protect here. I've | 132 | * have someone clarify what it's intended to protect here. I've |
133 | * seen some handwavy posts about posix locking needing to be | 133 | * seen some handwavy posts about posix locking needing to be |
134 | * done under the BKL, but it's far from clear. | 134 | * done under the BKL, but it's far from clear. |
135 | */ | 135 | */ |
136 | lock_kernel(); | 136 | lock_kernel(); |
137 | 137 | ||
138 | if (!nlm_timeout) | 138 | if (!nlm_timeout) |
139 | nlm_timeout = LOCKD_DFLT_TIMEO; | 139 | nlm_timeout = LOCKD_DFLT_TIMEO; |
140 | nlmsvc_timeout = nlm_timeout * HZ; | 140 | nlmsvc_timeout = nlm_timeout * HZ; |
141 | 141 | ||
142 | grace_period_expire = set_grace_period(); | 142 | grace_period_expire = set_grace_period(); |
143 | 143 | ||
144 | /* | 144 | /* |
145 | * The main request loop. We don't terminate until the last | 145 | * The main request loop. We don't terminate until the last |
146 | * NFS mount or NFS daemon has gone away. | 146 | * NFS mount or NFS daemon has gone away. |
147 | */ | 147 | */ |
148 | while (!kthread_should_stop()) { | 148 | while (!kthread_should_stop()) { |
149 | long timeout = MAX_SCHEDULE_TIMEOUT; | 149 | long timeout = MAX_SCHEDULE_TIMEOUT; |
150 | RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); | 150 | RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); |
151 | 151 | ||
152 | if (signalled()) { | 152 | if (signalled()) { |
153 | flush_signals(current); | 153 | flush_signals(current); |
154 | if (nlmsvc_ops) { | 154 | if (nlmsvc_ops) { |
155 | nlmsvc_invalidate_all(); | 155 | nlmsvc_invalidate_all(); |
156 | grace_period_expire = set_grace_period(); | 156 | grace_period_expire = set_grace_period(); |
157 | } | 157 | } |
158 | continue; | 158 | continue; |
159 | } | 159 | } |
160 | 160 | ||
161 | /* | 161 | /* |
162 | * Retry any blocked locks that have been notified by | 162 | * Retry any blocked locks that have been notified by |
163 | * the VFS. Don't do this during grace period. | 163 | * the VFS. Don't do this during grace period. |
164 | * (Theoretically, there shouldn't even be blocked locks | 164 | * (Theoretically, there shouldn't even be blocked locks |
165 | * during grace period). | 165 | * during grace period). |
166 | */ | 166 | */ |
167 | if (!nlmsvc_grace_period) { | 167 | if (!nlmsvc_grace_period) { |
168 | timeout = nlmsvc_retry_blocked(); | 168 | timeout = nlmsvc_retry_blocked(); |
169 | } else if (time_before(grace_period_expire, jiffies)) | 169 | } else if (time_before(grace_period_expire, jiffies)) |
170 | clear_grace_period(); | 170 | clear_grace_period(); |
171 | 171 | ||
172 | /* | 172 | /* |
173 | * Find a socket with data available and call its | 173 | * Find a socket with data available and call its |
174 | * recvfrom routine. | 174 | * recvfrom routine. |
175 | */ | 175 | */ |
176 | err = svc_recv(rqstp, timeout); | 176 | err = svc_recv(rqstp, timeout); |
177 | if (err == -EAGAIN || err == -EINTR) { | 177 | if (err == -EAGAIN || err == -EINTR) { |
178 | preverr = err; | 178 | preverr = err; |
179 | continue; | 179 | continue; |
180 | } | 180 | } |
181 | if (err < 0) { | 181 | if (err < 0) { |
182 | if (err != preverr) { | 182 | if (err != preverr) { |
183 | printk(KERN_WARNING "%s: unexpected error " | 183 | printk(KERN_WARNING "%s: unexpected error " |
184 | "from svc_recv (%d)\n", __func__, err); | 184 | "from svc_recv (%d)\n", __func__, err); |
185 | preverr = err; | 185 | preverr = err; |
186 | } | 186 | } |
187 | schedule_timeout_interruptible(HZ); | 187 | schedule_timeout_interruptible(HZ); |
188 | continue; | 188 | continue; |
189 | } | 189 | } |
190 | preverr = err; | 190 | preverr = err; |
191 | 191 | ||
192 | dprintk("lockd: request from %s\n", | 192 | dprintk("lockd: request from %s\n", |
193 | svc_print_addr(rqstp, buf, sizeof(buf))); | 193 | svc_print_addr(rqstp, buf, sizeof(buf))); |
194 | 194 | ||
195 | svc_process(rqstp); | 195 | svc_process(rqstp); |
196 | } | 196 | } |
197 | |||
198 | flush_signals(current); | 197 | flush_signals(current); |
199 | if (nlmsvc_ops) | 198 | if (nlmsvc_ops) |
200 | nlmsvc_invalidate_all(); | 199 | nlmsvc_invalidate_all(); |
201 | nlm_shutdown_hosts(); | 200 | nlm_shutdown_hosts(); |
202 | |||
203 | unlock_kernel(); | 201 | unlock_kernel(); |
204 | |||
205 | nlmsvc_task = NULL; | ||
206 | nlmsvc_serv = NULL; | ||
207 | |||
208 | /* Exit the RPC thread */ | ||
209 | svc_exit_thread(rqstp); | ||
210 | |||
211 | return 0; | 202 | return 0; |
212 | } | 203 | } |
213 | 204 | ||
214 | /* | 205 | /* |
215 | * Make any sockets that are needed but not present. | 206 | * Make any sockets that are needed but not present. |
216 | * If nlm_udpport or nlm_tcpport were set as module | 207 | * If nlm_udpport or nlm_tcpport were set as module |
217 | * options, make those sockets unconditionally | 208 | * options, make those sockets unconditionally |
218 | */ | 209 | */ |
219 | static int make_socks(struct svc_serv *serv, int proto) | 210 | static int make_socks(struct svc_serv *serv, int proto) |
220 | { | 211 | { |
221 | static int warned; | 212 | static int warned; |
222 | struct svc_xprt *xprt; | 213 | struct svc_xprt *xprt; |
223 | int err = 0; | 214 | int err = 0; |
224 | 215 | ||
225 | if (proto == IPPROTO_UDP || nlm_udpport) { | 216 | if (proto == IPPROTO_UDP || nlm_udpport) { |
226 | xprt = svc_find_xprt(serv, "udp", 0, 0); | 217 | xprt = svc_find_xprt(serv, "udp", 0, 0); |
227 | if (!xprt) | 218 | if (!xprt) |
228 | err = svc_create_xprt(serv, "udp", nlm_udpport, | 219 | err = svc_create_xprt(serv, "udp", nlm_udpport, |
229 | SVC_SOCK_DEFAULTS); | 220 | SVC_SOCK_DEFAULTS); |
230 | else | 221 | else |
231 | svc_xprt_put(xprt); | 222 | svc_xprt_put(xprt); |
232 | } | 223 | } |
233 | if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) { | 224 | if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) { |
234 | xprt = svc_find_xprt(serv, "tcp", 0, 0); | 225 | xprt = svc_find_xprt(serv, "tcp", 0, 0); |
235 | if (!xprt) | 226 | if (!xprt) |
236 | err = svc_create_xprt(serv, "tcp", nlm_tcpport, | 227 | err = svc_create_xprt(serv, "tcp", nlm_tcpport, |
237 | SVC_SOCK_DEFAULTS); | 228 | SVC_SOCK_DEFAULTS); |
238 | else | 229 | else |
239 | svc_xprt_put(xprt); | 230 | svc_xprt_put(xprt); |
240 | } | 231 | } |
241 | if (err >= 0) { | 232 | if (err >= 0) { |
242 | warned = 0; | 233 | warned = 0; |
243 | err = 0; | 234 | err = 0; |
244 | } else if (warned++ == 0) | 235 | } else if (warned++ == 0) |
245 | printk(KERN_WARNING | 236 | printk(KERN_WARNING |
246 | "lockd_up: makesock failed, error=%d\n", err); | 237 | "lockd_up: makesock failed, error=%d\n", err); |
247 | return err; | 238 | return err; |
248 | } | 239 | } |
249 | 240 | ||
250 | /* | 241 | /* |
251 | * Bring up the lockd process if it's not already up. | 242 | * Bring up the lockd process if it's not already up. |
252 | */ | 243 | */ |
253 | int | 244 | int |
254 | lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ | 245 | lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ |
255 | { | 246 | { |
256 | struct svc_serv *serv; | 247 | struct svc_serv *serv; |
257 | struct svc_rqst *rqstp; | ||
258 | int error = 0; | 248 | int error = 0; |
259 | 249 | ||
260 | mutex_lock(&nlmsvc_mutex); | 250 | mutex_lock(&nlmsvc_mutex); |
261 | /* | 251 | /* |
262 | * Check whether we're already up and running. | 252 | * Check whether we're already up and running. |
263 | */ | 253 | */ |
264 | if (nlmsvc_serv) { | 254 | if (nlmsvc_rqst) { |
265 | if (proto) | 255 | if (proto) |
266 | error = make_socks(nlmsvc_serv, proto); | 256 | error = make_socks(nlmsvc_rqst->rq_server, proto); |
267 | goto out; | 257 | goto out; |
268 | } | 258 | } |
269 | 259 | ||
270 | /* | 260 | /* |
271 | * Sanity check: if there's no pid, | 261 | * Sanity check: if there's no pid, |
272 | * we should be the first user ... | 262 | * we should be the first user ... |
273 | */ | 263 | */ |
274 | if (nlmsvc_users) | 264 | if (nlmsvc_users) |
275 | printk(KERN_WARNING | 265 | printk(KERN_WARNING |
276 | "lockd_up: no pid, %d users??\n", nlmsvc_users); | 266 | "lockd_up: no pid, %d users??\n", nlmsvc_users); |
277 | 267 | ||
278 | error = -ENOMEM; | 268 | error = -ENOMEM; |
279 | serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL); | 269 | serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL); |
280 | if (!serv) { | 270 | if (!serv) { |
281 | printk(KERN_WARNING "lockd_up: create service failed\n"); | 271 | printk(KERN_WARNING "lockd_up: create service failed\n"); |
282 | goto out; | 272 | goto out; |
283 | } | 273 | } |
284 | 274 | ||
285 | if ((error = make_socks(serv, proto)) < 0) | 275 | if ((error = make_socks(serv, proto)) < 0) |
286 | goto destroy_and_out; | 276 | goto destroy_and_out; |
287 | 277 | ||
288 | /* | 278 | /* |
289 | * Create the kernel thread and wait for it to start. | 279 | * Create the kernel thread and wait for it to start. |
290 | */ | 280 | */ |
291 | rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); | 281 | nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); |
292 | if (IS_ERR(rqstp)) { | 282 | if (IS_ERR(nlmsvc_rqst)) { |
293 | error = PTR_ERR(rqstp); | 283 | error = PTR_ERR(nlmsvc_rqst); |
284 | nlmsvc_rqst = NULL; | ||
294 | printk(KERN_WARNING | 285 | printk(KERN_WARNING |
295 | "lockd_up: svc_rqst allocation failed, error=%d\n", | 286 | "lockd_up: svc_rqst allocation failed, error=%d\n", |
296 | error); | 287 | error); |
297 | goto destroy_and_out; | 288 | goto destroy_and_out; |
298 | } | 289 | } |
299 | 290 | ||
300 | svc_sock_update_bufs(serv); | 291 | svc_sock_update_bufs(serv); |
301 | nlmsvc_serv = rqstp->rq_server; | ||
302 | 292 | ||
303 | nlmsvc_task = kthread_run(lockd, rqstp, serv->sv_name); | 293 | nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name); |
304 | if (IS_ERR(nlmsvc_task)) { | 294 | if (IS_ERR(nlmsvc_task)) { |
305 | error = PTR_ERR(nlmsvc_task); | 295 | error = PTR_ERR(nlmsvc_task); |
296 | svc_exit_thread(nlmsvc_rqst); | ||
306 | nlmsvc_task = NULL; | 297 | nlmsvc_task = NULL; |
307 | nlmsvc_serv = NULL; | 298 | nlmsvc_rqst = NULL; |
308 | printk(KERN_WARNING | 299 | printk(KERN_WARNING |
309 | "lockd_up: kthread_run failed, error=%d\n", error); | 300 | "lockd_up: kthread_run failed, error=%d\n", error); |
310 | svc_exit_thread(rqstp); | ||
311 | goto destroy_and_out; | 301 | goto destroy_and_out; |
312 | } | 302 | } |
313 | 303 | ||
314 | /* | 304 | /* |
315 | * Note: svc_serv structures have an initial use count of 1, | 305 | * Note: svc_serv structures have an initial use count of 1, |
316 | * so we exit through here on both success and failure. | 306 | * so we exit through here on both success and failure. |
317 | */ | 307 | */ |
318 | destroy_and_out: | 308 | destroy_and_out: |
319 | svc_destroy(serv); | 309 | svc_destroy(serv); |
320 | out: | 310 | out: |
321 | if (!error) | 311 | if (!error) |
322 | nlmsvc_users++; | 312 | nlmsvc_users++; |
323 | mutex_unlock(&nlmsvc_mutex); | 313 | mutex_unlock(&nlmsvc_mutex); |
324 | return error; | 314 | return error; |
325 | } | 315 | } |
326 | EXPORT_SYMBOL(lockd_up); | 316 | EXPORT_SYMBOL(lockd_up); |
327 | 317 | ||
328 | /* | 318 | /* |
329 | * Decrement the user count and bring down lockd if we're the last. | 319 | * Decrement the user count and bring down lockd if we're the last. |
330 | */ | 320 | */ |
331 | void | 321 | void |
332 | lockd_down(void) | 322 | lockd_down(void) |
333 | { | 323 | { |
334 | mutex_lock(&nlmsvc_mutex); | 324 | mutex_lock(&nlmsvc_mutex); |
335 | if (nlmsvc_users) { | 325 | if (nlmsvc_users) { |
336 | if (--nlmsvc_users) | 326 | if (--nlmsvc_users) |
337 | goto out; | 327 | goto out; |
338 | } else { | 328 | } else { |
339 | printk(KERN_ERR "lockd_down: no users! task=%p\n", | 329 | printk(KERN_ERR "lockd_down: no users! task=%p\n", |
340 | nlmsvc_task); | 330 | nlmsvc_task); |
341 | BUG(); | 331 | BUG(); |
342 | } | 332 | } |
343 | 333 | ||
344 | if (!nlmsvc_task) { | 334 | if (!nlmsvc_task) { |
345 | printk(KERN_ERR "lockd_down: no lockd running.\n"); | 335 | printk(KERN_ERR "lockd_down: no lockd running.\n"); |
346 | BUG(); | 336 | BUG(); |
347 | } | 337 | } |
348 | kthread_stop(nlmsvc_task); | 338 | kthread_stop(nlmsvc_task); |
339 | svc_exit_thread(nlmsvc_rqst); | ||
340 | nlmsvc_task = NULL; | ||
341 | nlmsvc_rqst = NULL; | ||
349 | out: | 342 | out: |
350 | mutex_unlock(&nlmsvc_mutex); | 343 | mutex_unlock(&nlmsvc_mutex); |
351 | } | 344 | } |
352 | EXPORT_SYMBOL(lockd_down); | 345 | EXPORT_SYMBOL(lockd_down); |
353 | 346 | ||
354 | #ifdef CONFIG_SYSCTL | 347 | #ifdef CONFIG_SYSCTL |
355 | 348 | ||
356 | /* | 349 | /* |
357 | * Sysctl parameters (same as module parameters, different interface). | 350 | * Sysctl parameters (same as module parameters, different interface). |
358 | */ | 351 | */ |
359 | 352 | ||
360 | static ctl_table nlm_sysctls[] = { | 353 | static ctl_table nlm_sysctls[] = { |
361 | { | 354 | { |
362 | .ctl_name = CTL_UNNUMBERED, | 355 | .ctl_name = CTL_UNNUMBERED, |
363 | .procname = "nlm_grace_period", | 356 | .procname = "nlm_grace_period", |
364 | .data = &nlm_grace_period, | 357 | .data = &nlm_grace_period, |
365 | .maxlen = sizeof(unsigned long), | 358 | .maxlen = sizeof(unsigned long), |
366 | .mode = 0644, | 359 | .mode = 0644, |
367 | .proc_handler = &proc_doulongvec_minmax, | 360 | .proc_handler = &proc_doulongvec_minmax, |
368 | .extra1 = (unsigned long *) &nlm_grace_period_min, | 361 | .extra1 = (unsigned long *) &nlm_grace_period_min, |
369 | .extra2 = (unsigned long *) &nlm_grace_period_max, | 362 | .extra2 = (unsigned long *) &nlm_grace_period_max, |
370 | }, | 363 | }, |
371 | { | 364 | { |
372 | .ctl_name = CTL_UNNUMBERED, | 365 | .ctl_name = CTL_UNNUMBERED, |
373 | .procname = "nlm_timeout", | 366 | .procname = "nlm_timeout", |
374 | .data = &nlm_timeout, | 367 | .data = &nlm_timeout, |
375 | .maxlen = sizeof(unsigned long), | 368 | .maxlen = sizeof(unsigned long), |
376 | .mode = 0644, | 369 | .mode = 0644, |
377 | .proc_handler = &proc_doulongvec_minmax, | 370 | .proc_handler = &proc_doulongvec_minmax, |
378 | .extra1 = (unsigned long *) &nlm_timeout_min, | 371 | .extra1 = (unsigned long *) &nlm_timeout_min, |
379 | .extra2 = (unsigned long *) &nlm_timeout_max, | 372 | .extra2 = (unsigned long *) &nlm_timeout_max, |
380 | }, | 373 | }, |
381 | { | 374 | { |
382 | .ctl_name = CTL_UNNUMBERED, | 375 | .ctl_name = CTL_UNNUMBERED, |
383 | .procname = "nlm_udpport", | 376 | .procname = "nlm_udpport", |
384 | .data = &nlm_udpport, | 377 | .data = &nlm_udpport, |
385 | .maxlen = sizeof(int), | 378 | .maxlen = sizeof(int), |
386 | .mode = 0644, | 379 | .mode = 0644, |
387 | .proc_handler = &proc_dointvec_minmax, | 380 | .proc_handler = &proc_dointvec_minmax, |
388 | .extra1 = (int *) &nlm_port_min, | 381 | .extra1 = (int *) &nlm_port_min, |
389 | .extra2 = (int *) &nlm_port_max, | 382 | .extra2 = (int *) &nlm_port_max, |
390 | }, | 383 | }, |
391 | { | 384 | { |
392 | .ctl_name = CTL_UNNUMBERED, | 385 | .ctl_name = CTL_UNNUMBERED, |
393 | .procname = "nlm_tcpport", | 386 | .procname = "nlm_tcpport", |
394 | .data = &nlm_tcpport, | 387 | .data = &nlm_tcpport, |
395 | .maxlen = sizeof(int), | 388 | .maxlen = sizeof(int), |
396 | .mode = 0644, | 389 | .mode = 0644, |
397 | .proc_handler = &proc_dointvec_minmax, | 390 | .proc_handler = &proc_dointvec_minmax, |
398 | .extra1 = (int *) &nlm_port_min, | 391 | .extra1 = (int *) &nlm_port_min, |
399 | .extra2 = (int *) &nlm_port_max, | 392 | .extra2 = (int *) &nlm_port_max, |
400 | }, | 393 | }, |
401 | { | 394 | { |
402 | .ctl_name = CTL_UNNUMBERED, | 395 | .ctl_name = CTL_UNNUMBERED, |
403 | .procname = "nsm_use_hostnames", | 396 | .procname = "nsm_use_hostnames", |
404 | .data = &nsm_use_hostnames, | 397 | .data = &nsm_use_hostnames, |
405 | .maxlen = sizeof(int), | 398 | .maxlen = sizeof(int), |
406 | .mode = 0644, | 399 | .mode = 0644, |
407 | .proc_handler = &proc_dointvec, | 400 | .proc_handler = &proc_dointvec, |
408 | }, | 401 | }, |
409 | { | 402 | { |
410 | .ctl_name = CTL_UNNUMBERED, | 403 | .ctl_name = CTL_UNNUMBERED, |
411 | .procname = "nsm_local_state", | 404 | .procname = "nsm_local_state", |
412 | .data = &nsm_local_state, | 405 | .data = &nsm_local_state, |
413 | .maxlen = sizeof(int), | 406 | .maxlen = sizeof(int), |
414 | .mode = 0644, | 407 | .mode = 0644, |
415 | .proc_handler = &proc_dointvec, | 408 | .proc_handler = &proc_dointvec, |
416 | }, | 409 | }, |
417 | { .ctl_name = 0 } | 410 | { .ctl_name = 0 } |
418 | }; | 411 | }; |
419 | 412 | ||
420 | static ctl_table nlm_sysctl_dir[] = { | 413 | static ctl_table nlm_sysctl_dir[] = { |
421 | { | 414 | { |
422 | .ctl_name = CTL_UNNUMBERED, | 415 | .ctl_name = CTL_UNNUMBERED, |
423 | .procname = "nfs", | 416 | .procname = "nfs", |
424 | .mode = 0555, | 417 | .mode = 0555, |
425 | .child = nlm_sysctls, | 418 | .child = nlm_sysctls, |
426 | }, | 419 | }, |
427 | { .ctl_name = 0 } | 420 | { .ctl_name = 0 } |
428 | }; | 421 | }; |
429 | 422 | ||
430 | static ctl_table nlm_sysctl_root[] = { | 423 | static ctl_table nlm_sysctl_root[] = { |
431 | { | 424 | { |
432 | .ctl_name = CTL_FS, | 425 | .ctl_name = CTL_FS, |
433 | .procname = "fs", | 426 | .procname = "fs", |
434 | .mode = 0555, | 427 | .mode = 0555, |
435 | .child = nlm_sysctl_dir, | 428 | .child = nlm_sysctl_dir, |
436 | }, | 429 | }, |
437 | { .ctl_name = 0 } | 430 | { .ctl_name = 0 } |
438 | }; | 431 | }; |
439 | 432 | ||
440 | #endif /* CONFIG_SYSCTL */ | 433 | #endif /* CONFIG_SYSCTL */ |
441 | 434 | ||
442 | /* | 435 | /* |
443 | * Module (and sysfs) parameters. | 436 | * Module (and sysfs) parameters. |
444 | */ | 437 | */ |
445 | 438 | ||
446 | #define param_set_min_max(name, type, which_strtol, min, max) \ | 439 | #define param_set_min_max(name, type, which_strtol, min, max) \ |
447 | static int param_set_##name(const char *val, struct kernel_param *kp) \ | 440 | static int param_set_##name(const char *val, struct kernel_param *kp) \ |
448 | { \ | 441 | { \ |
449 | char *endp; \ | 442 | char *endp; \ |
450 | __typeof__(type) num = which_strtol(val, &endp, 0); \ | 443 | __typeof__(type) num = which_strtol(val, &endp, 0); \ |
451 | if (endp == val || *endp || num < (min) || num > (max)) \ | 444 | if (endp == val || *endp || num < (min) || num > (max)) \ |
452 | return -EINVAL; \ | 445 | return -EINVAL; \ |
453 | *((int *) kp->arg) = num; \ | 446 | *((int *) kp->arg) = num; \ |
454 | return 0; \ | 447 | return 0; \ |
455 | } | 448 | } |
456 | 449 | ||
457 | static inline int is_callback(u32 proc) | 450 | static inline int is_callback(u32 proc) |
458 | { | 451 | { |
459 | return proc == NLMPROC_GRANTED | 452 | return proc == NLMPROC_GRANTED |
460 | || proc == NLMPROC_GRANTED_MSG | 453 | || proc == NLMPROC_GRANTED_MSG |
461 | || proc == NLMPROC_TEST_RES | 454 | || proc == NLMPROC_TEST_RES |
462 | || proc == NLMPROC_LOCK_RES | 455 | || proc == NLMPROC_LOCK_RES |
463 | || proc == NLMPROC_CANCEL_RES | 456 | || proc == NLMPROC_CANCEL_RES |
464 | || proc == NLMPROC_UNLOCK_RES | 457 | || proc == NLMPROC_UNLOCK_RES |
465 | || proc == NLMPROC_NSM_NOTIFY; | 458 | || proc == NLMPROC_NSM_NOTIFY; |
466 | } | 459 | } |
467 | 460 | ||
468 | 461 | ||
469 | static int lockd_authenticate(struct svc_rqst *rqstp) | 462 | static int lockd_authenticate(struct svc_rqst *rqstp) |
470 | { | 463 | { |
471 | rqstp->rq_client = NULL; | 464 | rqstp->rq_client = NULL; |
472 | switch (rqstp->rq_authop->flavour) { | 465 | switch (rqstp->rq_authop->flavour) { |
473 | case RPC_AUTH_NULL: | 466 | case RPC_AUTH_NULL: |
474 | case RPC_AUTH_UNIX: | 467 | case RPC_AUTH_UNIX: |
475 | if (rqstp->rq_proc == 0) | 468 | if (rqstp->rq_proc == 0) |
476 | return SVC_OK; | 469 | return SVC_OK; |
477 | if (is_callback(rqstp->rq_proc)) { | 470 | if (is_callback(rqstp->rq_proc)) { |
478 | /* Leave it to individual procedures to | 471 | /* Leave it to individual procedures to |
479 | * call nlmsvc_lookup_host(rqstp) | 472 | * call nlmsvc_lookup_host(rqstp) |
480 | */ | 473 | */ |
481 | return SVC_OK; | 474 | return SVC_OK; |
482 | } | 475 | } |
483 | return svc_set_client(rqstp); | 476 | return svc_set_client(rqstp); |
484 | } | 477 | } |
485 | return SVC_DENIED; | 478 | return SVC_DENIED; |
486 | } | 479 | } |
487 | 480 | ||
488 | 481 | ||
489 | param_set_min_max(port, int, simple_strtol, 0, 65535) | 482 | param_set_min_max(port, int, simple_strtol, 0, 65535) |
490 | param_set_min_max(grace_period, unsigned long, simple_strtoul, | 483 | param_set_min_max(grace_period, unsigned long, simple_strtoul, |
491 | nlm_grace_period_min, nlm_grace_period_max) | 484 | nlm_grace_period_min, nlm_grace_period_max) |
492 | param_set_min_max(timeout, unsigned long, simple_strtoul, | 485 | param_set_min_max(timeout, unsigned long, simple_strtoul, |
493 | nlm_timeout_min, nlm_timeout_max) | 486 | nlm_timeout_min, nlm_timeout_max) |
494 | 487 | ||
495 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); | 488 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); |
496 | MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); | 489 | MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); |
497 | MODULE_LICENSE("GPL"); | 490 | MODULE_LICENSE("GPL"); |
498 | 491 | ||
499 | module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong, | 492 | module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong, |
500 | &nlm_grace_period, 0644); | 493 | &nlm_grace_period, 0644); |
501 | module_param_call(nlm_timeout, param_set_timeout, param_get_ulong, | 494 | module_param_call(nlm_timeout, param_set_timeout, param_get_ulong, |
502 | &nlm_timeout, 0644); | 495 | &nlm_timeout, 0644); |
503 | module_param_call(nlm_udpport, param_set_port, param_get_int, | 496 | module_param_call(nlm_udpport, param_set_port, param_get_int, |
504 | &nlm_udpport, 0644); | 497 | &nlm_udpport, 0644); |
505 | module_param_call(nlm_tcpport, param_set_port, param_get_int, | 498 | module_param_call(nlm_tcpport, param_set_port, param_get_int, |
506 | &nlm_tcpport, 0644); | 499 | &nlm_tcpport, 0644); |
507 | module_param(nsm_use_hostnames, bool, 0644); | 500 | module_param(nsm_use_hostnames, bool, 0644); |
508 | 501 | ||
509 | /* | 502 | /* |
510 | * Initialising and terminating the module. | 503 | * Initialising and terminating the module. |
511 | */ | 504 | */ |
512 | 505 | ||
513 | static int __init init_nlm(void) | 506 | static int __init init_nlm(void) |
514 | { | 507 | { |
515 | #ifdef CONFIG_SYSCTL | 508 | #ifdef CONFIG_SYSCTL |
516 | nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root); | 509 | nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root); |
517 | return nlm_sysctl_table ? 0 : -ENOMEM; | 510 | return nlm_sysctl_table ? 0 : -ENOMEM; |
518 | #else | 511 | #else |
519 | return 0; | 512 | return 0; |
520 | #endif | 513 | #endif |
521 | } | 514 | } |
522 | 515 | ||
523 | static void __exit exit_nlm(void) | 516 | static void __exit exit_nlm(void) |
524 | { | 517 | { |
525 | /* FIXME: delete all NLM clients */ | 518 | /* FIXME: delete all NLM clients */ |
526 | nlm_shutdown_hosts(); | 519 | nlm_shutdown_hosts(); |
527 | #ifdef CONFIG_SYSCTL | 520 | #ifdef CONFIG_SYSCTL |
528 | unregister_sysctl_table(nlm_sysctl_table); | 521 | unregister_sysctl_table(nlm_sysctl_table); |
529 | #endif | 522 | #endif |
530 | } | 523 | } |
531 | 524 | ||
532 | module_init(init_nlm); | 525 | module_init(init_nlm); |
533 | module_exit(exit_nlm); | 526 | module_exit(exit_nlm); |
534 | 527 | ||
535 | /* | 528 | /* |
536 | * Define NLM program and procedures | 529 | * Define NLM program and procedures |
537 | */ | 530 | */ |
538 | static struct svc_version nlmsvc_version1 = { | 531 | static struct svc_version nlmsvc_version1 = { |
539 | .vs_vers = 1, | 532 | .vs_vers = 1, |
540 | .vs_nproc = 17, | 533 | .vs_nproc = 17, |
541 | .vs_proc = nlmsvc_procedures, | 534 | .vs_proc = nlmsvc_procedures, |
542 | .vs_xdrsize = NLMSVC_XDRSIZE, | 535 | .vs_xdrsize = NLMSVC_XDRSIZE, |
543 | }; | 536 | }; |
544 | static struct svc_version nlmsvc_version3 = { | 537 | static struct svc_version nlmsvc_version3 = { |
545 | .vs_vers = 3, | 538 | .vs_vers = 3, |
546 | .vs_nproc = 24, | 539 | .vs_nproc = 24, |
547 | .vs_proc = nlmsvc_procedures, | 540 | .vs_proc = nlmsvc_procedures, |
548 | .vs_xdrsize = NLMSVC_XDRSIZE, | 541 | .vs_xdrsize = NLMSVC_XDRSIZE, |
549 | }; | 542 | }; |
550 | #ifdef CONFIG_LOCKD_V4 | 543 | #ifdef CONFIG_LOCKD_V4 |
551 | static struct svc_version nlmsvc_version4 = { | 544 | static struct svc_version nlmsvc_version4 = { |
552 | .vs_vers = 4, | 545 | .vs_vers = 4, |
553 | .vs_nproc = 24, | 546 | .vs_nproc = 24, |
554 | .vs_proc = nlmsvc_procedures4, | 547 | .vs_proc = nlmsvc_procedures4, |
555 | .vs_xdrsize = NLMSVC_XDRSIZE, | 548 | .vs_xdrsize = NLMSVC_XDRSIZE, |
556 | }; | 549 | }; |
557 | #endif | 550 | #endif |
558 | static struct svc_version * nlmsvc_version[] = { | 551 | static struct svc_version * nlmsvc_version[] = { |
559 | [1] = &nlmsvc_version1, | 552 | [1] = &nlmsvc_version1, |
560 | [3] = &nlmsvc_version3, | 553 | [3] = &nlmsvc_version3, |
561 | #ifdef CONFIG_LOCKD_V4 | 554 | #ifdef CONFIG_LOCKD_V4 |
562 | [4] = &nlmsvc_version4, | 555 | [4] = &nlmsvc_version4, |
563 | #endif | 556 | #endif |
564 | }; | 557 | }; |
565 | 558 | ||
566 | static struct svc_stat nlmsvc_stats; | 559 | static struct svc_stat nlmsvc_stats; |
567 | 560 | ||
568 | #define NLM_NRVERS ARRAY_SIZE(nlmsvc_version) | 561 | #define NLM_NRVERS ARRAY_SIZE(nlmsvc_version) |
569 | static struct svc_program nlmsvc_program = { | 562 | static struct svc_program nlmsvc_program = { |
570 | .pg_prog = NLM_PROGRAM, /* program number */ | 563 | .pg_prog = NLM_PROGRAM, /* program number */ |
571 | .pg_nvers = NLM_NRVERS, /* number of entries in nlmsvc_version */ | 564 | .pg_nvers = NLM_NRVERS, /* number of entries in nlmsvc_version */ |
572 | .pg_vers = nlmsvc_version, /* version table */ | 565 | .pg_vers = nlmsvc_version, /* version table */ |
573 | .pg_name = "lockd", /* service name */ | 566 | .pg_name = "lockd", /* service name */ |