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