1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy is of the CDDL is also available via the Internet 9 * at http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 14 * Copyright (c) 2012 by Delphix. All rights reserved. 15 */ 16 17 /* 18 * NFS Lock Manager, server-side and common. 19 * 20 * This file contains all the external entry points of klmmod. 21 * Basically, this is the "glue" to the BSD nlm code. 22 */ 23 24 #include <sys/types.h> 25 #include <sys/errno.h> 26 #include <sys/modctl.h> 27 #include <sys/flock.h> 28 29 #include <nfs/nfs.h> 30 #include <nfs/nfssys.h> 31 #include <nfs/lm.h> 32 #include <rpcsvc/nlm_prot.h> 33 #include "nlm_impl.h" 34 35 static struct modlmisc modlmisc = { 36 &mod_miscops, "lock mgr common module" 37 }; 38 39 static struct modlinkage modlinkage = { 40 MODREV_1, &modlmisc, NULL 41 }; 42 43 /* 44 * Cluster node ID. Zero unless we're part of a cluster. 45 * Set by lm_set_nlmid_flk. Pass to lm_set_nlm_status. 46 * We're not yet doing "clustered" NLM stuff. 47 */ 48 int lm_global_nlmid = 0; 49 50 /* 51 * Call-back hook for clusters: Set lock manager status. 52 * If this hook is set, call this instead of the ususal 53 * flk_set_lockmgr_status(FLK_LOCKMGR_UP / DOWN); 54 */ 55 void (*lm_set_nlm_status)(int nlm_id, flk_nlm_status_t) = NULL; 56 57 /* 58 * Call-back hook for clusters: Delete all locks held by sysid. 59 * Call from code that drops all client locks (for which we're 60 * the server) i.e. after the SM tells us a client has crashed. 61 */ 62 void (*lm_remove_file_locks)(int) = NULL; 63 64 zone_key_t nlm_zone_key; 65 66 /* 67 * Init/fini per-zone stuff for klm 68 */ 69 /* ARGSUSED */ 70 void * 71 lm_zone_init(zoneid_t zoneid) 72 { 73 struct nlm_globals *g; 74 75 g = kmem_zalloc(sizeof (*g), KM_SLEEP); 76 77 avl_create(&g->nlm_hosts_tree, nlm_host_cmp, 78 sizeof (struct nlm_host), 79 offsetof(struct nlm_host, nh_by_addr)); 80 81 g->nlm_hosts_hash = mod_hash_create_idhash("nlm_host_by_sysid", 82 64, mod_hash_null_valdtor); 83 84 TAILQ_INIT(&g->nlm_idle_hosts); 85 TAILQ_INIT(&g->nlm_slocks); 86 87 mutex_init(&g->lock, NULL, MUTEX_DEFAULT, NULL); 88 cv_init(&g->nlm_gc_sched_cv, NULL, CV_DEFAULT, NULL); 89 cv_init(&g->nlm_gc_finish_cv, NULL, CV_DEFAULT, NULL); 90 mutex_init(&g->clean_lock, NULL, MUTEX_DEFAULT, NULL); 91 92 g->lockd_pid = 0; 93 g->run_status = NLM_ST_DOWN; 94 g->nlm_zoneid = zoneid; 95 96 nlm_globals_register(g); 97 return (g); 98 } 99 100 /* ARGSUSED */ 101 void 102 lm_zone_fini(zoneid_t zoneid, void *data) 103 { 104 struct nlm_globals *g = data; 105 106 nlm_globals_unregister(g); 107 108 ASSERT(avl_is_empty(&g->nlm_hosts_tree)); 109 avl_destroy(&g->nlm_hosts_tree); 110 mod_hash_destroy_idhash(g->nlm_hosts_hash); 111 112 ASSERT(g->nlm_gc_thread == NULL); 113 mutex_destroy(&g->lock); 114 cv_destroy(&g->nlm_gc_sched_cv); 115 cv_destroy(&g->nlm_gc_finish_cv); 116 mutex_destroy(&g->clean_lock); 117 118 kmem_free(g, sizeof (*g)); 119 } 120 121 122 123 /* 124 * **************************************************************** 125 * module init, fini, info 126 */ 127 int 128 _init() 129 { 130 int retval; 131 132 rw_init(&lm_lck, NULL, RW_DEFAULT, NULL); 133 nlm_init(); 134 135 zone_key_create(&nlm_zone_key, lm_zone_init, NULL, lm_zone_fini); 136 /* Per-zone lockmgr data. See: os/flock.c */ 137 zone_key_create(&flock_zone_key, flk_zone_init, NULL, flk_zone_fini); 138 139 retval = mod_install(&modlinkage); 140 if (retval == 0) 141 return (0); 142 143 /* 144 * mod_install failed! undo above, reverse order 145 */ 146 147 (void) zone_key_delete(flock_zone_key); 148 flock_zone_key = ZONE_KEY_UNINITIALIZED; 149 (void) zone_key_delete(nlm_zone_key); 150 rw_destroy(&lm_lck); 151 152 return (retval); 153 } 154 155 int 156 _fini() 157 { 158 /* Don't unload. */ 159 return (EBUSY); 160 } 161 162 int 163 _info(struct modinfo *modinfop) 164 { 165 return (mod_info(&modlinkage, modinfop)); 166 } 167 168 169 170 /* 171 * **************************************************************** 172 * Stubs listed in modstubs.s 173 */ 174 175 /* 176 * klm system calls. Start service on some endpoint. 177 * Called by nfssys() LM_SVC, from lockd. 178 */ 179 int 180 lm_svc(struct lm_svc_args *args) 181 { 182 struct knetconfig knc; 183 const char *netid; 184 struct nlm_globals *g; 185 struct file *fp = NULL; 186 int err = 0; 187 188 /* Get our "globals" */ 189 g = zone_getspecific(nlm_zone_key, curzone); 190 191 /* 192 * Check version of lockd calling. 193 */ 194 if (args->version != LM_SVC_CUR_VERS) { 195 NLM_ERR("lm_svc: Version mismatch " 196 "(given 0x%x, expected 0x%x)\n", 197 args->version, LM_SVC_CUR_VERS); 198 return (EINVAL); 199 } 200 201 /* 202 * Build knetconfig, checking arg values. 203 * Also come up with the "netid" string. 204 * (With some knowledge of /etc/netconfig) 205 */ 206 bzero(&knc, sizeof (knc)); 207 switch (args->n_proto) { 208 case LM_TCP: 209 knc.knc_semantics = NC_TPI_COTS_ORD; 210 knc.knc_proto = NC_TCP; 211 break; 212 case LM_UDP: 213 knc.knc_semantics = NC_TPI_CLTS; 214 knc.knc_proto = NC_UDP; 215 break; 216 default: 217 NLM_ERR("nlm_build_knetconfig: Unknown " 218 "lm_proto=0x%x\n", args->n_proto); 219 return (EINVAL); 220 } 221 222 switch (args->n_fmly) { 223 case LM_INET: 224 knc.knc_protofmly = NC_INET; 225 break; 226 case LM_INET6: 227 knc.knc_protofmly = NC_INET6; 228 break; 229 case LM_LOOPBACK: 230 knc.knc_protofmly = NC_LOOPBACK; 231 /* Override what we set above. */ 232 knc.knc_proto = NC_NOPROTO; 233 break; 234 default: 235 NLM_ERR("nlm_build_knetconfig: Unknown " 236 "lm_fmly=0x%x\n", args->n_fmly); 237 return (EINVAL); 238 } 239 240 knc.knc_rdev = args->n_rdev; 241 netid = nlm_knc_to_netid(&knc); 242 if (!netid) 243 return (EINVAL); 244 245 /* 246 * Setup service on the passed transport. 247 * NB: must releasef(fp) after this. 248 */ 249 if ((fp = getf(args->fd)) == NULL) 250 return (EBADF); 251 252 mutex_enter(&g->lock); 253 /* 254 * Don't try to start while still shutting down, 255 * or lots of things will fail... 256 */ 257 if (g->run_status == NLM_ST_STOPPING) { 258 err = EAGAIN; 259 goto out; 260 } 261 262 /* 263 * There is no separate "initialize" sub-call for nfssys, 264 * and we want to do some one-time work when the first 265 * binding comes in from lockd. 266 */ 267 if (g->run_status == NLM_ST_DOWN) { 268 g->run_status = NLM_ST_STARTING; 269 g->lockd_pid = curproc->p_pid; 270 271 /* Save the options. */ 272 g->cn_idle_tmo = args->timout; 273 g->grace_period = args->grace; 274 g->retrans_tmo = args->retransmittimeout; 275 276 /* See nfs_sys.c (not yet per-zone) */ 277 if (INGLOBALZONE(curproc)) { 278 rfs4_grace_period = args->grace; 279 rfs4_lease_time = args->grace; 280 } 281 282 mutex_exit(&g->lock); 283 err = nlm_svc_starting(g, fp, netid, &knc); 284 mutex_enter(&g->lock); 285 } else { 286 /* 287 * If KLM is not started and the very first endpoint lockd 288 * tries to add is not a loopback device, report an error. 289 */ 290 if (g->run_status != NLM_ST_UP) { 291 err = ENOTACTIVE; 292 goto out; 293 } 294 if (g->lockd_pid != curproc->p_pid) { 295 /* Check if caller has the same PID lockd does */ 296 err = EPERM; 297 goto out; 298 } 299 300 err = nlm_svc_add_ep(fp, netid, &knc); 301 } 302 303 out: 304 mutex_exit(&g->lock); 305 if (fp != NULL) 306 releasef(args->fd); 307 308 return (err); 309 } 310 311 /* 312 * klm system calls. Kill the lock manager. 313 * Called by nfssys() KILL_LOCKMGR, 314 * liblm:lm_shutdown() <- unused? 315 */ 316 int 317 lm_shutdown(void) 318 { 319 struct nlm_globals *g; 320 proc_t *p; 321 pid_t pid; 322 323 /* Get our "globals" */ 324 g = zone_getspecific(nlm_zone_key, curzone); 325 326 mutex_enter(&g->lock); 327 if (g->run_status != NLM_ST_UP) { 328 mutex_exit(&g->lock); 329 return (EBUSY); 330 } 331 332 g->run_status = NLM_ST_STOPPING; 333 pid = g->lockd_pid; 334 mutex_exit(&g->lock); 335 nlm_svc_stopping(g); 336 337 mutex_enter(&pidlock); 338 p = prfind(pid); 339 if (p != NULL) 340 psignal(p, SIGTERM); 341 342 mutex_exit(&pidlock); 343 return (0); 344 } 345 346 /* 347 * Cleanup remote locks on FS un-export. 348 * 349 * NOTE: called from nfs_export.c:unexport() 350 * right before the share is going to 351 * be unexported. 352 */ 353 void 354 lm_unexport(struct exportinfo *exi) 355 { 356 nlm_unexport(exi); 357 } 358 359 /* 360 * CPR suspend/resume hooks. 361 * See:cpr_suspend, cpr_resume 362 * 363 * Before suspend, get current state from "statd" on 364 * all remote systems for which we have locks. 365 * 366 * After resume, check with those systems again, 367 * and either reclaim locks, or do SIGLOST. 368 */ 369 void 370 lm_cprsuspend(void) 371 { 372 nlm_cprsuspend(); 373 } 374 375 void 376 lm_cprresume(void) 377 { 378 nlm_cprresume(); 379 } 380 381 /* 382 * Add the nlm_id bits to the sysid (by ref). 383 */ 384 void 385 lm_set_nlmid_flk(int *new_sysid) 386 { 387 if (lm_global_nlmid != 0) 388 *new_sysid |= (lm_global_nlmid << BITS_IN_SYSID); 389 } 390 391 /* 392 * It seems that closed source klmmod used 393 * this function to release knetconfig stored 394 * in mntinfo structure (see mntinfo's mi_klmconfig 395 * field). 396 * We store knetconfigs differently, thus we don't 397 * need this function. 398 */ 399 void 400 lm_free_config(struct knetconfig *knc) 401 { 402 _NOTE(ARGUNUSED(knc)); 403 } 404 405 /* 406 * Called by NFS4 delegation code to check if there are any 407 * NFSv2/v3 locks for the file, so it should not delegate. 408 * 409 * NOTE: called from NFSv4 code 410 * (see nfs4_srv_deleg.c:rfs4_bgrant_delegation()) 411 */ 412 int 413 lm_vp_active(const vnode_t *vp) 414 { 415 return (nlm_vp_active(vp)); 416 } 417 418 /* 419 * Find or create a "sysid" for given knc+addr. 420 * name is optional. Sets nc_changed if the 421 * found knc_proto is different from passed. 422 * Increments the reference count. 423 * 424 * Called internally, and in nfs4_find_sysid() 425 */ 426 struct lm_sysid * 427 lm_get_sysid(struct knetconfig *knc, struct netbuf *addr, 428 char *name, bool_t *nc_changed) 429 { 430 struct nlm_globals *g; 431 const char *netid; 432 struct nlm_host *hostp; 433 434 _NOTE(ARGUNUSED(nc_changed)); 435 netid = nlm_knc_to_netid(knc); 436 if (netid == NULL) 437 return (NULL); 438 439 g = zone_getspecific(nlm_zone_key, curzone); 440 441 hostp = nlm_host_findcreate(g, name, netid, addr); 442 if (hostp == NULL) 443 return (NULL); 444 445 return ((struct lm_sysid *)hostp); 446 } 447 448 /* 449 * Release a reference on a "sysid". 450 */ 451 void 452 lm_rel_sysid(struct lm_sysid *sysid) 453 { 454 struct nlm_globals *g; 455 456 g = zone_getspecific(nlm_zone_key, curzone); 457 nlm_host_release(g, (struct nlm_host *)sysid); 458 } 459 460 /* 461 * Alloc/free a sysid_t (a unique number between 462 * LM_SYSID and LM_SYSID_MAX). 463 * 464 * Used by NFSv4 rfs4_op_lockt and smbsrv/smb_fsop_frlock, 465 * both to represent non-local locks outside of klm. 466 * 467 * NOTE: called from NFSv4 and SMBFS to allocate unique 468 * sysid. 469 */ 470 sysid_t 471 lm_alloc_sysidt(void) 472 { 473 return (nlm_sysid_alloc()); 474 } 475 476 void 477 lm_free_sysidt(sysid_t sysid) 478 { 479 nlm_sysid_free(sysid); 480 } 481 482 /* Access private member lms->sysid */ 483 sysid_t 484 lm_sysidt(struct lm_sysid *lms) 485 { 486 return (((struct nlm_host *)lms)->nh_sysid); 487 } 488 489 /* 490 * Called by nfs_frlock to check lock constraints. 491 * Return non-zero if the lock request is "safe", i.e. 492 * the range is not mapped, not MANDLOCK, etc. 493 * 494 * NOTE: callde from NFSv3/NFSv2 frlock() functions to 495 * determine whether it's safe to add new lock. 496 */ 497 int 498 lm_safelock(vnode_t *vp, const struct flock64 *fl, cred_t *cr) 499 { 500 return (nlm_safelock(vp, fl, cr)); 501 } 502 503 /* 504 * Called by nfs_lockcompletion to check whether it's "safe" 505 * to map the file (and cache it's data). Walks the list of 506 * file locks looking for any that are not "whole file". 507 * 508 * NOTE: called from nfs_client.c:nfs_lockcompletion() 509 */ 510 int 511 lm_safemap(const vnode_t *vp) 512 { 513 return (nlm_safemap(vp)); 514 } 515 516 /* 517 * Called by nfs_map() for the MANDLOCK case. 518 * Return non-zero if the file has any locks with a 519 * blocked request (sleep). 520 * 521 * NOTE: called from NFSv3/NFSv2 map() functions in 522 * order to determine whether it's safe to add new 523 * mapping. 524 */ 525 int 526 lm_has_sleep(const vnode_t *vp) 527 { 528 return (nlm_has_sleep(vp)); 529 } 530 531 /* 532 * **************************************************************** 533 * Stuff needed by klmops? 534 */ 535