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