Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NLM: Convert lockd to use kthreads

Have lockd_up start lockd using kthread_run. With this change,
lockd_down now blocks until lockd actually exits, so there's no longer
need for the waitqueue code at the end of lockd_down. This also means
that only one lockd can be running at a time which simplifies the code
within lockd's main loop.

This also adds a check for kthread_should_stop in the main loop of
nlmsvc_retry_blocked and after that function returns. There's no sense
continuing to retry blocks if lockd is coming down anyway.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

authored by

Jeff Layton and committed by
J. Bruce Fields
d751a7cd 7086721f

+60 -75
+58 -74
fs/lockd/svc.c
··· 25 25 #include <linux/smp.h> 26 26 #include <linux/smp_lock.h> 27 27 #include <linux/mutex.h> 28 + #include <linux/kthread.h> 28 29 #include <linux/freezer.h> 29 30 30 31 #include <linux/sunrpc/types.h> ··· 49 48 50 49 static DEFINE_MUTEX(nlmsvc_mutex); 51 50 static unsigned int nlmsvc_users; 52 - static pid_t nlmsvc_pid; 51 + static struct task_struct *nlmsvc_task; 53 52 static struct svc_serv *nlmsvc_serv; 54 53 int nlmsvc_grace_period; 55 54 unsigned long nlmsvc_timeout; 56 - 57 - static DECLARE_COMPLETION(lockd_start_done); 58 - static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); 59 55 60 56 /* 61 57 * These can be set at insmod time (useful for NFS as root filesystem), ··· 109 111 /* 110 112 * This is the lockd kernel thread 111 113 */ 112 - static void 113 - lockd(struct svc_rqst *rqstp) 114 + static int 115 + lockd(void *vrqstp) 114 116 { 115 117 int err = 0; 118 + struct svc_rqst *rqstp = vrqstp; 116 119 unsigned long grace_period_expire; 117 120 118 - /* Lock module and set up kernel thread */ 119 - /* lockd_up is waiting for us to startup, so will 120 - * be holding a reference to this module, so it 121 - * is safe to just claim another reference 122 - */ 123 - __module_get(THIS_MODULE); 124 - lock_kernel(); 125 - 126 - /* 127 - * Let our maker know we're running. 128 - */ 129 - nlmsvc_pid = current->pid; 130 - nlmsvc_serv = rqstp->rq_server; 131 - complete(&lockd_start_done); 132 - 133 - daemonize("lockd"); 121 + /* try_to_freeze() is called from svc_recv() */ 134 122 set_freezable(); 135 123 136 - /* Process request with signals blocked, but allow SIGKILL. */ 124 + /* Allow SIGKILL to tell lockd to drop all of its locks */ 137 125 allow_signal(SIGKILL); 138 126 139 127 dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); 128 + 129 + /* 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 132 + * have someone clarify what it's intended to protect here. I've 133 + * seen some handwavy posts about posix locking needing to be 134 + * done under the BKL, but it's far from clear. 135 + */ 136 + lock_kernel(); 140 137 141 138 if (!nlm_timeout) 142 139 nlm_timeout = LOCKD_DFLT_TIMEO; ··· 141 148 142 149 /* 143 150 * The main request loop. We don't terminate until the last 144 - * NFS mount or NFS daemon has gone away, and we've been sent a 145 - * signal, or else another process has taken over our job. 151 + * NFS mount or NFS daemon has gone away. 146 152 */ 147 - while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) { 153 + while (!kthread_should_stop()) { 148 154 long timeout = MAX_SCHEDULE_TIMEOUT; 149 155 RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); 150 156 ··· 153 161 nlmsvc_invalidate_all(); 154 162 grace_period_expire = set_grace_period(); 155 163 } 164 + continue; 156 165 } 157 166 158 167 /* ··· 188 195 } 189 196 190 197 flush_signals(current); 198 + if (nlmsvc_ops) 199 + nlmsvc_invalidate_all(); 200 + nlm_shutdown_hosts(); 191 201 192 - /* 193 - * Check whether there's a new lockd process before 194 - * shutting down the hosts and clearing the slot. 195 - */ 196 - if (!nlmsvc_pid || current->pid == nlmsvc_pid) { 197 - if (nlmsvc_ops) 198 - nlmsvc_invalidate_all(); 199 - nlm_shutdown_hosts(); 200 - nlmsvc_pid = 0; 201 - nlmsvc_serv = NULL; 202 - } else 203 - printk(KERN_DEBUG 204 - "lockd: new process, skipping host shutdown\n"); 205 - wake_up(&lockd_exit); 202 + unlock_kernel(); 203 + 204 + nlmsvc_task = NULL; 205 + nlmsvc_serv = NULL; 206 206 207 207 /* Exit the RPC thread */ 208 208 svc_exit_thread(rqstp); 209 209 210 - /* Release module */ 211 - unlock_kernel(); 212 - module_put_and_exit(0); 210 + return 0; 213 211 } 214 212 215 213 /* ··· 245 261 int 246 262 lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ 247 263 { 248 - struct svc_serv * serv; 249 - int error = 0; 264 + struct svc_serv *serv; 265 + struct svc_rqst *rqstp; 266 + int error = 0; 250 267 251 268 mutex_lock(&nlmsvc_mutex); 252 269 /* 253 270 * Check whether we're already up and running. 254 271 */ 255 - if (nlmsvc_pid) { 272 + if (nlmsvc_serv) { 256 273 if (proto) 257 274 error = make_socks(nlmsvc_serv, proto); 258 275 goto out; ··· 280 295 /* 281 296 * Create the kernel thread and wait for it to start. 282 297 */ 283 - error = svc_create_thread(lockd, serv); 284 - if (error) { 298 + rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); 299 + if (IS_ERR(rqstp)) { 300 + error = PTR_ERR(rqstp); 285 301 printk(KERN_WARNING 286 - "lockd_up: create thread failed, error=%d\n", error); 302 + "lockd_up: svc_rqst allocation failed, error=%d\n", 303 + error); 287 304 goto destroy_and_out; 288 305 } 289 - wait_for_completion(&lockd_start_done); 306 + 307 + svc_sock_update_bufs(serv); 308 + nlmsvc_serv = rqstp->rq_server; 309 + 310 + nlmsvc_task = kthread_run(lockd, rqstp, serv->sv_name); 311 + if (IS_ERR(nlmsvc_task)) { 312 + error = PTR_ERR(nlmsvc_task); 313 + nlmsvc_task = NULL; 314 + nlmsvc_serv = NULL; 315 + printk(KERN_WARNING 316 + "lockd_up: kthread_run failed, error=%d\n", error); 317 + svc_exit_thread(rqstp); 318 + goto destroy_and_out; 319 + } 290 320 291 321 /* 292 322 * Note: svc_serv structures have an initial use count of 1, ··· 323 323 void 324 324 lockd_down(void) 325 325 { 326 - static int warned; 327 - 328 326 mutex_lock(&nlmsvc_mutex); 329 327 if (nlmsvc_users) { 330 328 if (--nlmsvc_users) 331 329 goto out; 332 - } else 333 - printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid); 334 - 335 - if (!nlmsvc_pid) { 336 - if (warned++ == 0) 337 - printk(KERN_WARNING "lockd_down: no lockd running.\n"); 338 - goto out; 330 + } else { 331 + printk(KERN_ERR "lockd_down: no users! task=%p\n", 332 + nlmsvc_task); 333 + BUG(); 339 334 } 340 - warned = 0; 341 335 342 - kill_proc(nlmsvc_pid, SIGKILL, 1); 343 - /* 344 - * Wait for the lockd process to exit, but since we're holding 345 - * the lockd semaphore, we can't wait around forever ... 346 - */ 347 - clear_thread_flag(TIF_SIGPENDING); 348 - interruptible_sleep_on_timeout(&lockd_exit, HZ); 349 - if (nlmsvc_pid) { 350 - printk(KERN_WARNING 351 - "lockd_down: lockd failed to exit, clearing pid\n"); 352 - nlmsvc_pid = 0; 336 + if (!nlmsvc_task) { 337 + printk(KERN_ERR "lockd_down: no lockd running.\n"); 338 + BUG(); 353 339 } 354 - spin_lock_irq(&current->sighand->siglock); 355 - recalc_sigpending(); 356 - spin_unlock_irq(&current->sighand->siglock); 340 + kthread_stop(nlmsvc_task); 357 341 out: 358 342 mutex_unlock(&nlmsvc_mutex); 359 343 }
+2 -1
fs/lockd/svclock.c
··· 29 29 #include <linux/sunrpc/svc.h> 30 30 #include <linux/lockd/nlm.h> 31 31 #include <linux/lockd/lockd.h> 32 + #include <linux/kthread.h> 32 33 33 34 #define NLMDBG_FACILITY NLMDBG_SVCLOCK 34 35 ··· 888 887 unsigned long timeout = MAX_SCHEDULE_TIMEOUT; 889 888 struct nlm_block *block; 890 889 891 - while (!list_empty(&nlm_blocked)) { 890 + while (!list_empty(&nlm_blocked) && !kthread_should_stop()) { 892 891 block = list_entry(nlm_blocked.next, struct nlm_block, b_list); 893 892 894 893 if (block->b_when == NLM_NEVER)