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