xref: /illumos-gate/usr/src/uts/common/klm/klmmod.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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  */
536