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 *
lm_zone_init(zoneid_t zoneid)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
lm_zone_fini(zoneid_t zoneid,void * data)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
_init()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
_fini()156 _fini()
157 {
158 /* Don't unload. */
159 return (EBUSY);
160 }
161
162 int
_info(struct modinfo * modinfop)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
lm_svc(struct lm_svc_args * args)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
lm_shutdown(void)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
lm_unexport(struct exportinfo * exi)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
lm_cprsuspend(void)370 lm_cprsuspend(void)
371 {
372 nlm_cprsuspend();
373 }
374
375 void
lm_cprresume(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
lm_set_nlmid_flk(int * new_sysid)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
lm_free_config(struct knetconfig * knc)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
lm_vp_active(const vnode_t * vp)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 *
lm_get_sysid(struct knetconfig * knc,struct netbuf * addr,char * name,bool_t * nc_changed)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
lm_rel_sysid(struct lm_sysid * sysid)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
lm_alloc_sysidt(void)471 lm_alloc_sysidt(void)
472 {
473 return (nlm_sysid_alloc());
474 }
475
476 void
lm_free_sysidt(sysid_t sysid)477 lm_free_sysidt(sysid_t sysid)
478 {
479 nlm_sysid_free(sysid);
480 }
481
482 /* Access private member lms->sysid */
483 sysid_t
lm_sysidt(struct lm_sysid * lms)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
lm_safelock(vnode_t * vp,const struct flock64 * fl,cred_t * cr)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
lm_safemap(const vnode_t * vp)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
lm_has_sleep(const vnode_t * vp)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