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