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