1 /*
2 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3 * Authors: Doug Rabson <dfr@rabson.org>
4 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
30 * Copyright (c) 2012 by Delphix. All rights reserved.
31 * Copyright 2017 Joyent, Inc. All rights reserved.
32 */
33
34 /*
35 * NFS LockManager, start/stop, support functions, etc.
36 * Most of the interesting code is here.
37 *
38 * Source code derived from FreeBSD nlm_prot_impl.c
39 */
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/thread.h>
44 #include <sys/fcntl.h>
45 #include <sys/flock.h>
46 #include <sys/mount.h>
47 #include <sys/priv.h>
48 #include <sys/proc.h>
49 #include <sys/share.h>
50 #include <sys/socket.h>
51 #include <sys/syscall.h>
52 #include <sys/syslog.h>
53 #include <sys/systm.h>
54 #include <sys/class.h>
55 #include <sys/unistd.h>
56 #include <sys/vnode.h>
57 #include <sys/vfs.h>
58 #include <sys/queue.h>
59 #include <sys/bitmap.h>
60 #include <sys/sdt.h>
61 #include <sys/brand.h>
62 #include <netinet/in.h>
63
64 #include <rpc/rpc.h>
65 #include <rpc/xdr.h>
66 #include <rpc/pmap_prot.h>
67 #include <rpc/pmap_clnt.h>
68 #include <rpc/rpcb_prot.h>
69
70 #include <rpcsvc/nlm_prot.h>
71 #include <rpcsvc/sm_inter.h>
72 #include <rpcsvc/nsm_addr.h>
73
74 #include <nfs/nfs.h>
75 #include <nfs/nfs_clnt.h>
76 #include <nfs/export.h>
77 #include <nfs/rnode.h>
78 #include <nfs/lm.h>
79
80 #include "nlm_impl.h"
81
82 struct nlm_knc {
83 struct knetconfig n_knc;
84 const char *n_netid;
85 };
86
87 /*
88 * Number of attempts NLM tries to obtain RPC binding
89 * of local statd.
90 */
91 #define NLM_NSM_RPCBIND_RETRIES 10
92
93 /*
94 * Timeout (in seconds) NLM waits before making another
95 * attempt to obtain RPC binding of local statd.
96 */
97 #define NLM_NSM_RPCBIND_TIMEOUT 5
98
99 /*
100 * Total number of sysids in NLM sysid bitmap
101 */
102 #define NLM_BMAP_NITEMS (LM_SYSID_MAX + 1)
103
104 /*
105 * Number of ulong_t words in bitmap that is used
106 * for allocation of sysid numbers.
107 */
108 #define NLM_BMAP_WORDS (NLM_BMAP_NITEMS / BT_NBIPUL)
109
110 /*
111 * Given an integer x, the macro returns
112 * -1 if x is negative,
113 * 0 if x is zero
114 * 1 if x is positive
115 */
116 #define SIGN(x) (((x) > 0) - ((x) < 0))
117
118 #define ARRSIZE(arr) (sizeof (arr) / sizeof ((arr)[0]))
119 #define NLM_KNCS ARRSIZE(nlm_netconfigs)
120
121 krwlock_t lm_lck;
122
123 /*
124 * Zero timeout for asynchronous NLM RPC operations
125 */
126 static const struct timeval nlm_rpctv_zero = { 0, 0 };
127
128 /*
129 * List of all Zone globals nlm_globals instences
130 * linked together.
131 */
132 static struct nlm_globals_list nlm_zones_list; /* (g) */
133
134 /*
135 * NLM kmem caches
136 */
137 static struct kmem_cache *nlm_hosts_cache = NULL;
138 static struct kmem_cache *nlm_vhold_cache = NULL;
139
140 /*
141 * A bitmap for allocation of new sysids.
142 * Sysid is a unique number between LM_SYSID
143 * and LM_SYSID_MAX. Sysid represents unique remote
144 * host that does file locks on the given host.
145 */
146 static ulong_t nlm_sysid_bmap[NLM_BMAP_WORDS]; /* (g) */
147 static int nlm_sysid_nidx; /* (g) */
148
149 /*
150 * RPC service registration for all transports
151 */
152 static SVC_CALLOUT nlm_svcs[] = {
153 { NLM_PROG, 4, 4, nlm_prog_4 }, /* NLM4_VERS */
154 { NLM_PROG, 1, 3, nlm_prog_3 } /* NLM_VERS - NLM_VERSX */
155 };
156
157 static SVC_CALLOUT_TABLE nlm_sct = {
158 ARRSIZE(nlm_svcs),
159 FALSE,
160 nlm_svcs
161 };
162
163 /*
164 * Static table of all netid/knetconfig network
165 * lock manager can work with. nlm_netconfigs table
166 * is used when we need to get valid knetconfig by
167 * netid and vice versa.
168 *
169 * Knetconfigs are activated either by the call from
170 * user-space lockd daemon (server side) or by taking
171 * knetconfig from NFS mountinfo (client side)
172 */
173 static struct nlm_knc nlm_netconfigs[] = { /* (g) */
174 /* UDP */
175 {
176 { NC_TPI_CLTS, NC_INET, NC_UDP, NODEV },
177 "udp",
178 },
179 /* TCP */
180 {
181 { NC_TPI_COTS_ORD, NC_INET, NC_TCP, NODEV },
182 "tcp",
183 },
184 /* UDP over IPv6 */
185 {
186 { NC_TPI_CLTS, NC_INET6, NC_UDP, NODEV },
187 "udp6",
188 },
189 /* TCP over IPv6 */
190 {
191 { NC_TPI_COTS_ORD, NC_INET6, NC_TCP, NODEV },
192 "tcp6",
193 },
194 /* ticlts (loopback over UDP) */
195 {
196 { NC_TPI_CLTS, NC_LOOPBACK, NC_NOPROTO, NODEV },
197 "ticlts",
198 },
199 /* ticotsord (loopback over TCP) */
200 {
201 { NC_TPI_COTS_ORD, NC_LOOPBACK, NC_NOPROTO, NODEV },
202 "ticotsord",
203 },
204 };
205
206 /*
207 * NLM functions which can be called by a brand hook.
208 */
209 void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
210 void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
211
212 /*
213 * NLM misc. function
214 */
215 static void nlm_copy_netbuf(struct netbuf *, struct netbuf *);
216 static int nlm_netbuf_addrs_cmp(struct netbuf *, struct netbuf *);
217 static void nlm_kmem_reclaim(void *);
218 static void nlm_pool_shutdown(void);
219 static void nlm_suspend_zone(struct nlm_globals *);
220 static void nlm_resume_zone(struct nlm_globals *);
221
222 /*
223 * NLM thread functions
224 */
225 static void nlm_gc(struct nlm_globals *);
226 static void nlm_reclaimer(struct nlm_host *);
227
228 /*
229 * NLM NSM functions
230 */
231 static int nlm_init_local_knc(struct knetconfig *);
232 static int nlm_nsm_init_local(struct nlm_nsm *);
233 static int nlm_nsm_init(struct nlm_nsm *, struct knetconfig *, struct netbuf *);
234 static void nlm_nsm_fini(struct nlm_nsm *);
235 static enum clnt_stat nlm_nsm_simu_crash(struct nlm_nsm *);
236 static enum clnt_stat nlm_nsm_stat(struct nlm_nsm *, int32_t *);
237 static enum clnt_stat nlm_nsm_mon(struct nlm_nsm *, char *, uint16_t);
238 static enum clnt_stat nlm_nsm_unmon(struct nlm_nsm *, char *);
239
240 /*
241 * NLM host functions
242 */
243 static int nlm_host_ctor(void *, void *, int);
244 static void nlm_host_dtor(void *, void *);
245 static void nlm_host_destroy(struct nlm_host *);
246 static struct nlm_host *nlm_host_create(char *, const char *,
247 struct knetconfig *, struct netbuf *);
248 static struct nlm_host *nlm_host_find_locked(struct nlm_globals *,
249 const char *, struct netbuf *, avl_index_t *);
250 static void nlm_host_unregister(struct nlm_globals *, struct nlm_host *);
251 static void nlm_host_gc_vholds(struct nlm_host *);
252 static bool_t nlm_host_has_srv_locks(struct nlm_host *);
253 static bool_t nlm_host_has_cli_locks(struct nlm_host *);
254 static bool_t nlm_host_has_locks(struct nlm_host *);
255
256 /*
257 * NLM vhold functions
258 */
259 static int nlm_vhold_ctor(void *, void *, int);
260 static void nlm_vhold_dtor(void *, void *);
261 static void nlm_vhold_destroy(struct nlm_host *,
262 struct nlm_vhold *);
263 static bool_t nlm_vhold_busy(struct nlm_host *, struct nlm_vhold *);
264 static void nlm_vhold_clean(struct nlm_vhold *, int);
265
266 /*
267 * NLM client/server sleeping locks/share reservation functions
268 */
269 struct nlm_slreq *nlm_slreq_find_locked(struct nlm_host *,
270 struct nlm_vhold *, struct flock64 *);
271 static struct nlm_shres *nlm_shres_create_item(struct shrlock *, vnode_t *);
272 static void nlm_shres_destroy_item(struct nlm_shres *);
273 static bool_t nlm_shres_equal(struct shrlock *, struct shrlock *);
274
275 /*
276 * NLM initialization functions.
277 */
278 void
279 nlm_init(void)
280 {
281 nlm_hosts_cache = kmem_cache_create("nlm_host_cache",
282 sizeof (struct nlm_host), 0, nlm_host_ctor, nlm_host_dtor,
283 nlm_kmem_reclaim, NULL, NULL, 0);
284
285 nlm_vhold_cache = kmem_cache_create("nlm_vhold_cache",
286 sizeof (struct nlm_vhold), 0, nlm_vhold_ctor, nlm_vhold_dtor,
287 NULL, NULL, NULL, 0);
288
289 nlm_rpc_init();
290 TAILQ_INIT(&nlm_zones_list);
291
292 /* initialize sysids bitmap */
293 bzero(nlm_sysid_bmap, sizeof (nlm_sysid_bmap));
294 nlm_sysid_nidx = 1;
295
296 /*
297 * Reserv the sysid #0, because it's associated
298 * with local locks only. Don't let to allocate
299 * it for remote locks.
300 */
301 BT_SET(nlm_sysid_bmap, 0);
302 }
303
304 void
305 nlm_globals_register(struct nlm_globals *g)
306 {
307 rw_enter(&lm_lck, RW_WRITER);
308 TAILQ_INSERT_TAIL(&nlm_zones_list, g, nlm_link);
309 rw_exit(&lm_lck);
310 }
311
312 void
313 nlm_globals_unregister(struct nlm_globals *g)
314 {
315 rw_enter(&lm_lck, RW_WRITER);
316 TAILQ_REMOVE(&nlm_zones_list, g, nlm_link);
317 rw_exit(&lm_lck);
318 }
319
320 /* ARGSUSED */
321 static void
322 nlm_kmem_reclaim(void *cdrarg)
323 {
324 struct nlm_globals *g;
325
326 rw_enter(&lm_lck, RW_READER);
327 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
328 cv_broadcast(&g->nlm_gc_sched_cv);
329
330 rw_exit(&lm_lck);
331 }
332
333 /*
334 * NLM garbage collector thread (GC).
335 *
336 * NLM GC periodically checks whether there're any host objects
337 * that can be cleaned up. It also releases stale vnodes that
338 * live on the server side (under protection of vhold objects).
339 *
340 * NLM host objects are cleaned up from GC thread because
341 * operations helping us to determine whether given host has
342 * any locks can be quite expensive and it's not good to call
343 * them every time the very last reference to the host is dropped.
344 * Thus we use "lazy" approach for hosts cleanup.
345 *
346 * The work of GC is to release stale vnodes on the server side
347 * and destroy hosts that haven't any locks and any activity for
348 * some time (i.e. idle hosts).
349 */
350 static void
351 nlm_gc(struct nlm_globals *g)
352 {
353 struct nlm_host *hostp;
354 clock_t now, idle_period;
355
356 idle_period = SEC_TO_TICK(g->cn_idle_tmo);
357 mutex_enter(&g->lock);
358 for (;;) {
359 /*
360 * GC thread can be explicitly scheduled from
361 * memory reclamation function.
362 */
363 (void) cv_timedwait(&g->nlm_gc_sched_cv, &g->lock,
364 ddi_get_lbolt() + idle_period);
365
366 /*
367 * NLM is shutting down, time to die.
368 */
369 if (g->run_status == NLM_ST_STOPPING)
370 break;
371
372 now = ddi_get_lbolt();
373 DTRACE_PROBE2(gc__start, struct nlm_globals *, g,
374 clock_t, now);
375
376 /*
377 * Find all obviously unused vholds and destroy them.
378 */
379 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
380 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
381 struct nlm_vhold *nvp;
382
383 mutex_enter(&hostp->nh_lock);
384
385 nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
386 while (nvp != NULL) {
387 struct nlm_vhold *new_nvp;
388
389 new_nvp = TAILQ_NEXT(nvp, nv_link);
390
391 /*
392 * If these conditions are met, the vhold is
393 * obviously unused and we will destroy it. In
394 * a case either v_filocks and/or v_shrlocks is
395 * non-NULL the vhold might still be unused by
396 * the host, but it is expensive to check that.
397 * We defer such check until the host is idle.
398 * The expensive check is done below without
399 * the global lock held.
400 */
401 if (nvp->nv_refcnt == 0 &&
402 nvp->nv_vp->v_filocks == NULL &&
403 nvp->nv_vp->v_shrlocks == NULL) {
404 nlm_vhold_destroy(hostp, nvp);
405 }
406
407 nvp = new_nvp;
408 }
409
410 mutex_exit(&hostp->nh_lock);
411 }
412
413 /*
414 * Handle all hosts that are unused at the moment
415 * until we meet one with idle timeout in future.
416 */
417 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
418 bool_t has_locks;
419
420 if (hostp->nh_idle_timeout > now)
421 break;
422
423 /*
424 * Drop global lock while doing expensive work
425 * on this host. We'll re-check any conditions
426 * that might change after retaking the global
427 * lock.
428 */
429 mutex_exit(&g->lock);
430 mutex_enter(&hostp->nh_lock);
431
432 /*
433 * nlm_globals lock was dropped earlier because
434 * garbage collecting of vholds and checking whether
435 * host has any locks/shares are expensive operations.
436 */
437 nlm_host_gc_vholds(hostp);
438 has_locks = nlm_host_has_locks(hostp);
439
440 mutex_exit(&hostp->nh_lock);
441 mutex_enter(&g->lock);
442
443 /*
444 * While we were doing expensive operations
445 * outside of nlm_globals critical section,
446 * somebody could take the host and remove it
447 * from the idle list. Whether its been
448 * reinserted or not, our information about
449 * the host is outdated, and we should take no
450 * further action.
451 */
452 if ((hostp->nh_flags & NLM_NH_INIDLE) == 0 ||
453 hostp->nh_idle_timeout > now)
454 continue;
455
456 /*
457 * If the host has locks we have to renew the
458 * host's timeout and put it at the end of LRU
459 * list.
460 */
461 if (has_locks) {
462 TAILQ_REMOVE(&g->nlm_idle_hosts,
463 hostp, nh_link);
464 hostp->nh_idle_timeout = now + idle_period;
465 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts,
466 hostp, nh_link);
467 continue;
468 }
469
470 /*
471 * We're here if all the following conditions hold:
472 * 1) Host hasn't any locks or share reservations
473 * 2) Host is unused
474 * 3) Host wasn't touched by anyone at least for
475 * g->cn_idle_tmo seconds.
476 *
477 * So, now we can destroy it.
478 */
479 nlm_host_unregister(g, hostp);
480 mutex_exit(&g->lock);
481
482 nlm_host_unmonitor(g, hostp);
483 nlm_host_destroy(hostp);
484 mutex_enter(&g->lock);
485 if (g->run_status == NLM_ST_STOPPING)
486 break;
487
488 }
489
490 DTRACE_PROBE(gc__end);
491 }
492
493 DTRACE_PROBE1(gc__exit, struct nlm_globals *, g);
494
495 /* Let others know that GC has died */
496 g->nlm_gc_thread = NULL;
497 mutex_exit(&g->lock);
498
499 cv_broadcast(&g->nlm_gc_finish_cv);
500 zthread_exit();
501 }
502
503 /*
504 * Thread reclaim locks/shares acquired by the client side
505 * on the given server represented by hostp.
506 */
507 static void
508 nlm_reclaimer(struct nlm_host *hostp)
509 {
510 struct nlm_globals *g;
511
512 mutex_enter(&hostp->nh_lock);
513 hostp->nh_reclaimer = curthread;
514 mutex_exit(&hostp->nh_lock);
515
516 g = zone_getspecific(nlm_zone_key, curzone);
517 nlm_reclaim_client(g, hostp);
518
519 mutex_enter(&hostp->nh_lock);
520 hostp->nh_flags &= ~NLM_NH_RECLAIM;
521 hostp->nh_reclaimer = NULL;
522 cv_broadcast(&hostp->nh_recl_cv);
523 mutex_exit(&hostp->nh_lock);
524
525 /*
526 * Host was explicitly referenced before
527 * nlm_reclaim() was called, release it
528 * here.
529 */
530 nlm_host_release(g, hostp);
531 zthread_exit();
532 }
533
534 /*
535 * Copy a struct netobj. (see xdr.h)
536 */
537 void
538 nlm_copy_netobj(struct netobj *dst, struct netobj *src)
539 {
540 dst->n_len = src->n_len;
541 dst->n_bytes = kmem_alloc(src->n_len, KM_SLEEP);
542 bcopy(src->n_bytes, dst->n_bytes, src->n_len);
543 }
544
545 /*
546 * An NLM specificw replacement for clnt_call().
547 * nlm_clnt_call() is used by all RPC functions generated
548 * from nlm_prot.x specification. The function is aware
549 * about some pitfalls of NLM RPC procedures and has a logic
550 * that handles them properly.
551 */
552 enum clnt_stat
553 nlm_clnt_call(CLIENT *clnt, rpcproc_t procnum, xdrproc_t xdr_args,
554 caddr_t argsp, xdrproc_t xdr_result, caddr_t resultp, struct timeval wait)
555 {
556 k_sigset_t oldmask;
557 enum clnt_stat stat;
558 bool_t sig_blocked = FALSE;
559
560 /*
561 * If NLM RPC procnum is one of the NLM _RES procedures
562 * that are used to reply to asynchronous NLM RPC
563 * (MSG calls), explicitly set RPC timeout to zero.
564 * Client doesn't send a reply to RES procedures, so
565 * we don't need to wait anything.
566 *
567 * NOTE: we ignore NLM4_*_RES procnums because they are
568 * equal to NLM_*_RES numbers.
569 */
570 if (procnum >= NLM_TEST_RES && procnum <= NLM_GRANTED_RES)
571 wait = nlm_rpctv_zero;
572
573 /*
574 * We need to block signals in case of NLM_CANCEL RPC
575 * in order to prevent interruption of network RPC
576 * calls.
577 */
578 if (procnum == NLM_CANCEL) {
579 k_sigset_t newmask;
580
581 sigfillset(&newmask);
582 sigreplace(&newmask, &oldmask);
583 sig_blocked = TRUE;
584 }
585
586 stat = clnt_call(clnt, procnum, xdr_args,
587 argsp, xdr_result, resultp, wait);
588
589 /*
590 * Restore signal mask back if signals were blocked
591 */
592 if (sig_blocked)
593 sigreplace(&oldmask, (k_sigset_t *)NULL);
594
595 return (stat);
596 }
597
598 /*
599 * Suspend NLM client/server in the given zone.
600 *
601 * During suspend operation we mark those hosts
602 * that have any locks with NLM_NH_SUSPEND flags,
603 * so that they can be checked later, when resume
604 * operation occurs.
605 */
606 static void
607 nlm_suspend_zone(struct nlm_globals *g)
608 {
609 struct nlm_host *hostp;
610 struct nlm_host_list all_hosts;
611
612 /*
613 * Note that while we're doing suspend, GC thread is active
614 * and it can destroy some hosts while we're walking through
615 * the hosts tree. To prevent that and make suspend logic
616 * a bit more simple we put all hosts to local "all_hosts"
617 * list and increment reference counter of each host.
618 * This guaranties that no hosts will be released while
619 * we're doing suspend.
620 * NOTE: reference of each host must be dropped during
621 * resume operation.
622 */
623 TAILQ_INIT(&all_hosts);
624 mutex_enter(&g->lock);
625 for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
626 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
627 /*
628 * If host is idle, remove it from idle list and
629 * clear idle flag. That is done to prevent GC
630 * from touching this host.
631 */
632 if (hostp->nh_flags & NLM_NH_INIDLE) {
633 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
634 hostp->nh_flags &= ~NLM_NH_INIDLE;
635 }
636
637 hostp->nh_refs++;
638 TAILQ_INSERT_TAIL(&all_hosts, hostp, nh_link);
639 }
640
641 /*
642 * Now we can walk through all hosts on the system
643 * with zone globals lock released. The fact the
644 * we have taken a reference to each host guaranties
645 * that no hosts can be destroyed during that process.
646 */
647 mutex_exit(&g->lock);
648 while ((hostp = TAILQ_FIRST(&all_hosts)) != NULL) {
649 mutex_enter(&hostp->nh_lock);
650 if (nlm_host_has_locks(hostp))
651 hostp->nh_flags |= NLM_NH_SUSPEND;
652
653 mutex_exit(&hostp->nh_lock);
654 TAILQ_REMOVE(&all_hosts, hostp, nh_link);
655 }
656 }
657
658 /*
659 * Resume NLM hosts for the given zone.
660 *
661 * nlm_resume_zone() is called after hosts were suspended
662 * (see nlm_suspend_zone) and its main purpose to check
663 * whether remote locks owned by hosts are still in consistent
664 * state. If they aren't, resume function tries to reclaim
665 * locks (for client side hosts) and clean locks (for
666 * server side hosts).
667 */
668 static void
669 nlm_resume_zone(struct nlm_globals *g)
670 {
671 struct nlm_host *hostp, *h_next;
672
673 mutex_enter(&g->lock);
674 hostp = avl_first(&g->nlm_hosts_tree);
675
676 /*
677 * In nlm_suspend_zone() the reference counter of each
678 * host was incremented, so we can safely iterate through
679 * all hosts without worrying that any host we touch will
680 * be removed at the moment.
681 */
682 while (hostp != NULL) {
683 struct nlm_nsm nsm;
684 enum clnt_stat stat;
685 int32_t sm_state;
686 int error;
687 bool_t resume_failed = FALSE;
688
689 h_next = AVL_NEXT(&g->nlm_hosts_tree, hostp);
690 mutex_exit(&g->lock);
691
692 DTRACE_PROBE1(resume__host, struct nlm_host *, hostp);
693
694 /*
695 * Suspend operation marked that the host doesn't
696 * have any locks. Skip it.
697 */
698 if (!(hostp->nh_flags & NLM_NH_SUSPEND))
699 goto cycle_end;
700
701 error = nlm_nsm_init(&nsm, &hostp->nh_knc, &hostp->nh_addr);
702 if (error != 0) {
703 NLM_ERR("Resume: Failed to contact to NSM of host %s "
704 "[error=%d]\n", hostp->nh_name, error);
705 resume_failed = TRUE;
706 goto cycle_end;
707 }
708
709 stat = nlm_nsm_stat(&nsm, &sm_state);
710 if (stat != RPC_SUCCESS) {
711 NLM_ERR("Resume: Failed to call SM_STAT operation for "
712 "host %s [stat=%d]\n", hostp->nh_name, stat);
713 resume_failed = TRUE;
714 nlm_nsm_fini(&nsm);
715 goto cycle_end;
716 }
717
718 if (sm_state != hostp->nh_state) {
719 /*
720 * Current SM state of the host isn't equal
721 * to the one host had when it was suspended.
722 * Probably it was rebooted. Try to reclaim
723 * locks if the host has any on its client side.
724 * Also try to clean up its server side locks
725 * (if the host has any).
726 */
727 nlm_host_notify_client(hostp, sm_state);
728 nlm_host_notify_server(hostp, sm_state);
729 }
730
731 nlm_nsm_fini(&nsm);
732
733 cycle_end:
734 if (resume_failed) {
735 /*
736 * Resume failed for the given host.
737 * Just clean up all resources it owns.
738 */
739 nlm_host_notify_server(hostp, 0);
740 nlm_client_cancel_all(g, hostp);
741 }
742
743 hostp->nh_flags &= ~NLM_NH_SUSPEND;
744 nlm_host_release(g, hostp);
745 hostp = h_next;
746 mutex_enter(&g->lock);
747 }
748
749 mutex_exit(&g->lock);
750 }
751
752 /*
753 * NLM functions responsible for operations on NSM handle.
754 */
755
756 /*
757 * Initialize knetconfig that is used for communication
758 * with local statd via loopback interface.
759 */
760 static int
761 nlm_init_local_knc(struct knetconfig *knc)
762 {
763 int error;
764 vnode_t *vp;
765
766 bzero(knc, sizeof (*knc));
767 error = lookupname("/dev/tcp", UIO_SYSSPACE,
768 FOLLOW, NULLVPP, &vp);
769 if (error != 0)
770 return (error);
771
772 knc->knc_semantics = NC_TPI_COTS;
773 knc->knc_protofmly = NC_INET;
774 knc->knc_proto = NC_TCP;
775 knc->knc_rdev = vp->v_rdev;
776 VN_RELE(vp);
777
778
779 return (0);
780 }
781
782 /*
783 * Initialize NSM handle that will be used to talk
784 * to local statd via loopback interface.
785 */
786 static int
787 nlm_nsm_init_local(struct nlm_nsm *nsm)
788 {
789 int error;
790 struct knetconfig knc;
791 struct sockaddr_in sin;
792 struct netbuf nb;
793
794 error = nlm_init_local_knc(&knc);
795 if (error != 0)
796 return (error);
797
798 bzero(&sin, sizeof (sin));
799 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
800 sin.sin_family = AF_INET;
801
802 nb.buf = (char *)&sin;
803 nb.len = nb.maxlen = sizeof (sin);
804
805 return (nlm_nsm_init(nsm, &knc, &nb));
806 }
807
808 /*
809 * Initialize NSM handle used for talking to statd
810 */
811 static int
812 nlm_nsm_init(struct nlm_nsm *nsm, struct knetconfig *knc, struct netbuf *nb)
813 {
814 enum clnt_stat stat;
815 int error, retries;
816
817 bzero(nsm, sizeof (*nsm));
818 nsm->ns_knc = *knc;
819 nlm_copy_netbuf(&nsm->ns_addr, nb);
820
821 /*
822 * Try several times to get the port of statd service,
823 * If rpcbind_getaddr returns RPC_PROGNOTREGISTERED,
824 * retry an attempt, but wait for NLM_NSM_RPCBIND_TIMEOUT
825 * seconds berofore.
826 */
827 for (retries = 0; retries < NLM_NSM_RPCBIND_RETRIES; retries++) {
828 stat = rpcbind_getaddr(&nsm->ns_knc, SM_PROG,
829 SM_VERS, &nsm->ns_addr);
830 if (stat != RPC_SUCCESS) {
831 if (stat == RPC_PROGNOTREGISTERED) {
832 delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT));
833 continue;
834 }
835 }
836
837 break;
838 }
839
840 if (stat != RPC_SUCCESS) {
841 DTRACE_PROBE2(rpcbind__error, enum clnt_stat, stat,
842 int, retries);
843 error = ENOENT;
844 goto error;
845 }
846
847 /*
848 * Create an RPC handle that'll be used for communication with local
849 * statd using the status monitor protocol.
850 */
851 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS,
852 0, NLM_RPC_RETRIES, kcred, &nsm->ns_handle);
853 if (error != 0)
854 goto error;
855
856 /*
857 * Create an RPC handle that'll be used for communication with the
858 * local statd using the address registration protocol.
859 */
860 error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM,
861 NSM_ADDR_V1, 0, NLM_RPC_RETRIES, kcred, &nsm->ns_addr_handle);
862 if (error != 0)
863 goto error;
864
865 sema_init(&nsm->ns_sem, 1, NULL, SEMA_DEFAULT, NULL);
866 return (0);
867
868 error:
869 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
870 if (nsm->ns_handle)
871 CLNT_DESTROY(nsm->ns_handle);
872
873 return (error);
874 }
875
876 static void
877 nlm_nsm_fini(struct nlm_nsm *nsm)
878 {
879 kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
880 CLNT_DESTROY(nsm->ns_addr_handle);
881 nsm->ns_addr_handle = NULL;
882 CLNT_DESTROY(nsm->ns_handle);
883 nsm->ns_handle = NULL;
884 sema_destroy(&nsm->ns_sem);
885 }
886
887 static enum clnt_stat
888 nlm_nsm_simu_crash(struct nlm_nsm *nsm)
889 {
890 enum clnt_stat stat;
891
892 sema_p(&nsm->ns_sem);
893 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
894 stat = sm_simu_crash_1(NULL, NULL, nsm->ns_handle);
895 sema_v(&nsm->ns_sem);
896
897 return (stat);
898 }
899
900 static enum clnt_stat
901 nlm_nsm_stat(struct nlm_nsm *nsm, int32_t *out_stat)
902 {
903 struct sm_name args;
904 struct sm_stat_res res;
905 enum clnt_stat stat;
906
907 args.mon_name = uts_nodename();
908 bzero(&res, sizeof (res));
909
910 sema_p(&nsm->ns_sem);
911 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
912 stat = sm_stat_1(&args, &res, nsm->ns_handle);
913 sema_v(&nsm->ns_sem);
914
915 if (stat == RPC_SUCCESS)
916 *out_stat = res.state;
917
918 return (stat);
919 }
920
921 static enum clnt_stat
922 nlm_nsm_mon(struct nlm_nsm *nsm, char *hostname, uint16_t priv)
923 {
924 struct mon args;
925 struct sm_stat_res res;
926 enum clnt_stat stat;
927
928 bzero(&args, sizeof (args));
929 bzero(&res, sizeof (res));
930
931 args.mon_id.mon_name = hostname;
932 args.mon_id.my_id.my_name = uts_nodename();
933 args.mon_id.my_id.my_prog = NLM_PROG;
934 args.mon_id.my_id.my_vers = NLM_SM;
935 args.mon_id.my_id.my_proc = NLM_SM_NOTIFY1;
936 bcopy(&priv, args.priv, sizeof (priv));
937
938 sema_p(&nsm->ns_sem);
939 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
940 stat = sm_mon_1(&args, &res, nsm->ns_handle);
941 sema_v(&nsm->ns_sem);
942
943 return (stat);
944 }
945
946 static enum clnt_stat
947 nlm_nsm_unmon(struct nlm_nsm *nsm, char *hostname)
948 {
949 struct mon_id args;
950 struct sm_stat res;
951 enum clnt_stat stat;
952
953 bzero(&args, sizeof (args));
954 bzero(&res, sizeof (res));
955
956 args.mon_name = hostname;
957 args.my_id.my_name = uts_nodename();
958 args.my_id.my_prog = NLM_PROG;
959 args.my_id.my_vers = NLM_SM;
960 args.my_id.my_proc = NLM_SM_NOTIFY1;
961
962 sema_p(&nsm->ns_sem);
963 nlm_nsm_clnt_init(nsm->ns_handle, nsm);
964 stat = sm_unmon_1(&args, &res, nsm->ns_handle);
965 sema_v(&nsm->ns_sem);
966
967 return (stat);
968 }
969
970 static enum clnt_stat
971 nlm_nsmaddr_reg(struct nlm_nsm *nsm, char *name, int family, netobj *address)
972 {
973 struct reg1args args = { 0 };
974 struct reg1res res = { 0 };
975 enum clnt_stat stat;
976
977 args.family = family;
978 args.name = name;
979 args.address = *address;
980
981 sema_p(&nsm->ns_sem);
982 nlm_nsm_clnt_init(nsm->ns_addr_handle, nsm);
983 stat = nsmaddrproc1_reg_1(&args, &res, nsm->ns_addr_handle);
984 sema_v(&nsm->ns_sem);
985
986 return (stat);
987 }
988
989 /*
990 * Get NLM vhold object corresponding to vnode "vp".
991 * If no such object was found, create a new one.
992 *
993 * The purpose of this function is to associate vhold
994 * object with given vnode, so that:
995 * 1) vnode is hold (VN_HOLD) while vhold object is alive.
996 * 2) host has a track of all vnodes it touched by lock
997 * or share operations. These vnodes are accessible
998 * via collection of vhold objects.
999 */
1000 struct nlm_vhold *
1001 nlm_vhold_get(struct nlm_host *hostp, vnode_t *vp)
1002 {
1003 struct nlm_vhold *nvp, *new_nvp = NULL;
1004
1005 mutex_enter(&hostp->nh_lock);
1006 nvp = nlm_vhold_find_locked(hostp, vp);
1007 if (nvp != NULL)
1008 goto out;
1009
1010 /* nlm_vhold wasn't found, then create a new one */
1011 mutex_exit(&hostp->nh_lock);
1012 new_nvp = kmem_cache_alloc(nlm_vhold_cache, KM_SLEEP);
1013
1014 /*
1015 * Check if another thread has already
1016 * created the same nlm_vhold.
1017 */
1018 mutex_enter(&hostp->nh_lock);
1019 nvp = nlm_vhold_find_locked(hostp, vp);
1020 if (nvp == NULL) {
1021 nvp = new_nvp;
1022 new_nvp = NULL;
1023
1024 TAILQ_INIT(&nvp->nv_slreqs);
1025 nvp->nv_vp = vp;
1026 nvp->nv_refcnt = 1;
1027 VN_HOLD(nvp->nv_vp);
1028
1029 VERIFY(mod_hash_insert(hostp->nh_vholds_by_vp,
1030 (mod_hash_key_t)vp, (mod_hash_val_t)nvp) == 0);
1031 TAILQ_INSERT_TAIL(&hostp->nh_vholds_list, nvp, nv_link);
1032 }
1033
1034 out:
1035 mutex_exit(&hostp->nh_lock);
1036 if (new_nvp != NULL)
1037 kmem_cache_free(nlm_vhold_cache, new_nvp);
1038
1039 return (nvp);
1040 }
1041
1042 /*
1043 * Drop a reference to vhold object nvp.
1044 */
1045 void
1046 nlm_vhold_release(struct nlm_host *hostp, struct nlm_vhold *nvp)
1047 {
1048 if (nvp == NULL)
1049 return;
1050
1051 mutex_enter(&hostp->nh_lock);
1052 ASSERT(nvp->nv_refcnt > 0);
1053 nvp->nv_refcnt--;
1054
1055 /*
1056 * If these conditions are met, the vhold is obviously unused and we
1057 * will destroy it. In a case either v_filocks and/or v_shrlocks is
1058 * non-NULL the vhold might still be unused by the host, but it is
1059 * expensive to check that. We defer such check until the host is
1060 * idle. The expensive check is done in the NLM garbage collector.
1061 */
1062 if (nvp->nv_refcnt == 0 &&
1063 nvp->nv_vp->v_filocks == NULL &&
1064 nvp->nv_vp->v_shrlocks == NULL) {
1065 nlm_vhold_destroy(hostp, nvp);
1066 }
1067
1068 mutex_exit(&hostp->nh_lock);
1069 }
1070
1071 /*
1072 * Clean all locks and share reservations on the
1073 * given vhold object that were acquired by the
1074 * given sysid
1075 */
1076 static void
1077 nlm_vhold_clean(struct nlm_vhold *nvp, int sysid)
1078 {
1079 cleanlocks(nvp->nv_vp, IGN_PID, sysid);
1080 cleanshares_by_sysid(nvp->nv_vp, sysid);
1081 }
1082
1083 static void
1084 nlm_vhold_destroy(struct nlm_host *hostp, struct nlm_vhold *nvp)
1085 {
1086 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1087
1088 ASSERT(nvp->nv_refcnt == 0);
1089 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
1090
1091 VERIFY(mod_hash_remove(hostp->nh_vholds_by_vp,
1092 (mod_hash_key_t)nvp->nv_vp,
1093 (mod_hash_val_t)&nvp) == 0);
1094
1095 TAILQ_REMOVE(&hostp->nh_vholds_list, nvp, nv_link);
1096 VN_RELE(nvp->nv_vp);
1097 nvp->nv_vp = NULL;
1098
1099 kmem_cache_free(nlm_vhold_cache, nvp);
1100 }
1101
1102 /*
1103 * Return TRUE if the given vhold is busy.
1104 * Vhold object is considered to be "busy" when
1105 * all the following conditions hold:
1106 * 1) No one uses it at the moment;
1107 * 2) It hasn't any locks;
1108 * 3) It hasn't any share reservations;
1109 */
1110 static bool_t
1111 nlm_vhold_busy(struct nlm_host *hostp, struct nlm_vhold *nvp)
1112 {
1113 vnode_t *vp;
1114 int sysid;
1115
1116 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1117
1118 if (nvp->nv_refcnt > 0)
1119 return (TRUE);
1120
1121 vp = nvp->nv_vp;
1122 sysid = hostp->nh_sysid;
1123 if (flk_has_remote_locks_for_sysid(vp, sysid) ||
1124 shr_has_remote_shares(vp, sysid))
1125 return (TRUE);
1126
1127 return (FALSE);
1128 }
1129
1130 /* ARGSUSED */
1131 static int
1132 nlm_vhold_ctor(void *datap, void *cdrarg, int kmflags)
1133 {
1134 struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
1135
1136 bzero(nvp, sizeof (*nvp));
1137 return (0);
1138 }
1139
1140 /* ARGSUSED */
1141 static void
1142 nlm_vhold_dtor(void *datap, void *cdrarg)
1143 {
1144 struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
1145
1146 ASSERT(nvp->nv_refcnt == 0);
1147 ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
1148 ASSERT(nvp->nv_vp == NULL);
1149 }
1150
1151 struct nlm_vhold *
1152 nlm_vhold_find_locked(struct nlm_host *hostp, const vnode_t *vp)
1153 {
1154 struct nlm_vhold *nvp = NULL;
1155
1156 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1157 (void) mod_hash_find(hostp->nh_vholds_by_vp,
1158 (mod_hash_key_t)vp,
1159 (mod_hash_val_t)&nvp);
1160
1161 if (nvp != NULL)
1162 nvp->nv_refcnt++;
1163
1164 return (nvp);
1165 }
1166
1167 /*
1168 * NLM host functions
1169 */
1170 static void
1171 nlm_copy_netbuf(struct netbuf *dst, struct netbuf *src)
1172 {
1173 ASSERT(src->len <= src->maxlen);
1174
1175 dst->maxlen = src->maxlen;
1176 dst->len = src->len;
1177 dst->buf = kmem_zalloc(src->maxlen, KM_SLEEP);
1178 bcopy(src->buf, dst->buf, src->len);
1179 }
1180
1181 /* ARGSUSED */
1182 static int
1183 nlm_host_ctor(void *datap, void *cdrarg, int kmflags)
1184 {
1185 struct nlm_host *hostp = (struct nlm_host *)datap;
1186
1187 bzero(hostp, sizeof (*hostp));
1188 return (0);
1189 }
1190
1191 /* ARGSUSED */
1192 static void
1193 nlm_host_dtor(void *datap, void *cdrarg)
1194 {
1195 struct nlm_host *hostp = (struct nlm_host *)datap;
1196 ASSERT(hostp->nh_refs == 0);
1197 }
1198
1199 static void
1200 nlm_host_unregister(struct nlm_globals *g, struct nlm_host *hostp)
1201 {
1202 ASSERT(hostp->nh_refs == 0);
1203 ASSERT(hostp->nh_flags & NLM_NH_INIDLE);
1204
1205 avl_remove(&g->nlm_hosts_tree, hostp);
1206 VERIFY(mod_hash_remove(g->nlm_hosts_hash,
1207 (mod_hash_key_t)(uintptr_t)hostp->nh_sysid,
1208 (mod_hash_val_t)&hostp) == 0);
1209 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1210 hostp->nh_flags &= ~NLM_NH_INIDLE;
1211 }
1212
1213 /*
1214 * Free resources used by a host. This is called after the reference
1215 * count has reached zero so it doesn't need to worry about locks.
1216 */
1217 static void
1218 nlm_host_destroy(struct nlm_host *hostp)
1219 {
1220 ASSERT(hostp->nh_name != NULL);
1221 ASSERT(hostp->nh_netid != NULL);
1222 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
1223
1224 strfree(hostp->nh_name);
1225 strfree(hostp->nh_netid);
1226 kmem_free(hostp->nh_addr.buf, hostp->nh_addr.maxlen);
1227
1228 if (hostp->nh_sysid != LM_NOSYSID)
1229 nlm_sysid_free(hostp->nh_sysid);
1230
1231 nlm_rpc_cache_destroy(hostp);
1232
1233 ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
1234 mod_hash_destroy_ptrhash(hostp->nh_vholds_by_vp);
1235
1236 mutex_destroy(&hostp->nh_lock);
1237 cv_destroy(&hostp->nh_rpcb_cv);
1238 cv_destroy(&hostp->nh_recl_cv);
1239
1240 kmem_cache_free(nlm_hosts_cache, hostp);
1241 }
1242
1243 /*
1244 * Cleanup SERVER-side state after a client restarts,
1245 * or becomes unresponsive, or whatever.
1246 *
1247 * We unlock any active locks owned by the host.
1248 * When rpc.lockd is shutting down,
1249 * this function is called with newstate set to zero
1250 * which allows us to cancel any pending async locks
1251 * and clear the locking state.
1252 *
1253 * When "state" is 0, we don't update host's state,
1254 * but cleanup all remote locks on the host.
1255 * It's useful to call this function for resources
1256 * cleanup.
1257 */
1258 void
1259 nlm_host_notify_server(struct nlm_host *hostp, int32_t state)
1260 {
1261 struct nlm_vhold *nvp;
1262 struct nlm_slreq *slr;
1263 struct nlm_slreq_list slreqs2free;
1264
1265 TAILQ_INIT(&slreqs2free);
1266 mutex_enter(&hostp->nh_lock);
1267 if (state != 0)
1268 hostp->nh_state = state;
1269
1270 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
1271
1272 /* cleanup sleeping requests at first */
1273 while ((slr = TAILQ_FIRST(&nvp->nv_slreqs)) != NULL) {
1274 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
1275
1276 /*
1277 * Instead of freeing cancelled sleeping request
1278 * here, we add it to the linked list created
1279 * on the stack in order to do all frees outside
1280 * the critical section.
1281 */
1282 TAILQ_INSERT_TAIL(&slreqs2free, slr, nsr_link);
1283 }
1284
1285 nvp->nv_refcnt++;
1286 mutex_exit(&hostp->nh_lock);
1287
1288 nlm_vhold_clean(nvp, hostp->nh_sysid);
1289
1290 mutex_enter(&hostp->nh_lock);
1291 nvp->nv_refcnt--;
1292 }
1293
1294 mutex_exit(&hostp->nh_lock);
1295 while ((slr = TAILQ_FIRST(&slreqs2free)) != NULL) {
1296 TAILQ_REMOVE(&slreqs2free, slr, nsr_link);
1297 kmem_free(slr, sizeof (*slr));
1298 }
1299 }
1300
1301 /*
1302 * Cleanup CLIENT-side state after a server restarts,
1303 * or becomes unresponsive, or whatever.
1304 *
1305 * This is called by the local NFS statd when we receive a
1306 * host state change notification. (also nlm_svc_stopping)
1307 *
1308 * Deal with a server restart. If we are stopping the
1309 * NLM service, we'll have newstate == 0, and will just
1310 * cancel all our client-side lock requests. Otherwise,
1311 * start the "recovery" process to reclaim any locks
1312 * we hold on this server.
1313 */
1314 void
1315 nlm_host_notify_client(struct nlm_host *hostp, int32_t state)
1316 {
1317 mutex_enter(&hostp->nh_lock);
1318 hostp->nh_state = state;
1319 if (hostp->nh_flags & NLM_NH_RECLAIM) {
1320 /*
1321 * Either host's state is up to date or
1322 * host is already in recovery.
1323 */
1324 mutex_exit(&hostp->nh_lock);
1325 return;
1326 }
1327
1328 hostp->nh_flags |= NLM_NH_RECLAIM;
1329
1330 /*
1331 * Host will be released by the recovery thread,
1332 * thus we need to increment refcount.
1333 */
1334 hostp->nh_refs++;
1335 mutex_exit(&hostp->nh_lock);
1336
1337 (void) zthread_create(NULL, 0, nlm_reclaimer,
1338 hostp, 0, minclsyspri);
1339 }
1340
1341 /*
1342 * The function is called when NLM client detects that
1343 * server has entered in grace period and client needs
1344 * to wait until reclamation process (if any) does
1345 * its job.
1346 */
1347 int
1348 nlm_host_wait_grace(struct nlm_host *hostp)
1349 {
1350 struct nlm_globals *g;
1351 int error = 0;
1352
1353 g = zone_getspecific(nlm_zone_key, curzone);
1354 mutex_enter(&hostp->nh_lock);
1355
1356 do {
1357 int rc;
1358
1359 rc = cv_timedwait_sig(&hostp->nh_recl_cv,
1360 &hostp->nh_lock, ddi_get_lbolt() +
1361 SEC_TO_TICK(g->retrans_tmo));
1362
1363 if (rc == 0) {
1364 error = EINTR;
1365 break;
1366 }
1367 } while (hostp->nh_flags & NLM_NH_RECLAIM);
1368
1369 mutex_exit(&hostp->nh_lock);
1370 return (error);
1371 }
1372
1373 /*
1374 * Create a new NLM host.
1375 *
1376 * NOTE: The in-kernel RPC (kRPC) subsystem uses TLI/XTI,
1377 * which needs both a knetconfig and an address when creating
1378 * endpoints. Thus host object stores both knetconfig and
1379 * netid.
1380 */
1381 static struct nlm_host *
1382 nlm_host_create(char *name, const char *netid,
1383 struct knetconfig *knc, struct netbuf *naddr)
1384 {
1385 struct nlm_host *host;
1386
1387 host = kmem_cache_alloc(nlm_hosts_cache, KM_SLEEP);
1388
1389 mutex_init(&host->nh_lock, NULL, MUTEX_DEFAULT, NULL);
1390 cv_init(&host->nh_rpcb_cv, NULL, CV_DEFAULT, NULL);
1391 cv_init(&host->nh_recl_cv, NULL, CV_DEFAULT, NULL);
1392
1393 host->nh_sysid = LM_NOSYSID;
1394 host->nh_refs = 1;
1395 host->nh_name = strdup(name);
1396 host->nh_netid = strdup(netid);
1397 host->nh_knc = *knc;
1398 nlm_copy_netbuf(&host->nh_addr, naddr);
1399
1400 host->nh_state = 0;
1401 host->nh_rpcb_state = NRPCB_NEED_UPDATE;
1402 host->nh_flags = 0;
1403
1404 host->nh_vholds_by_vp = mod_hash_create_ptrhash("nlm vholds hash",
1405 32, mod_hash_null_valdtor, sizeof (vnode_t));
1406
1407 TAILQ_INIT(&host->nh_vholds_list);
1408 TAILQ_INIT(&host->nh_rpchc);
1409
1410 return (host);
1411 }
1412
1413 /*
1414 * Cancel all client side sleeping locks owned by given host.
1415 */
1416 void
1417 nlm_host_cancel_slocks(struct nlm_globals *g, struct nlm_host *hostp)
1418 {
1419 struct nlm_slock *nslp;
1420
1421 mutex_enter(&g->lock);
1422 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
1423 if (nslp->nsl_host == hostp) {
1424 nslp->nsl_state = NLM_SL_CANCELLED;
1425 cv_broadcast(&nslp->nsl_cond);
1426 }
1427 }
1428
1429 mutex_exit(&g->lock);
1430 }
1431
1432 /*
1433 * Garbage collect stale vhold objects.
1434 *
1435 * In other words check whether vnodes that are
1436 * held by vhold objects still have any locks
1437 * or shares or still in use. If they aren't,
1438 * just destroy them.
1439 */
1440 static void
1441 nlm_host_gc_vholds(struct nlm_host *hostp)
1442 {
1443 struct nlm_vhold *nvp;
1444
1445 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1446
1447 nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
1448 while (nvp != NULL) {
1449 struct nlm_vhold *nvp_tmp;
1450
1451 if (nlm_vhold_busy(hostp, nvp)) {
1452 nvp = TAILQ_NEXT(nvp, nv_link);
1453 continue;
1454 }
1455
1456 nvp_tmp = TAILQ_NEXT(nvp, nv_link);
1457 nlm_vhold_destroy(hostp, nvp);
1458 nvp = nvp_tmp;
1459 }
1460 }
1461
1462 /*
1463 * Check whether the given host has any
1464 * server side locks or share reservations.
1465 */
1466 static bool_t
1467 nlm_host_has_srv_locks(struct nlm_host *hostp)
1468 {
1469 /*
1470 * It's cheap and simple: if server has
1471 * any locks/shares there must be vhold
1472 * object storing the affected vnode.
1473 *
1474 * NOTE: We don't need to check sleeping
1475 * locks on the server side, because if
1476 * server side sleeping lock is alive,
1477 * there must be a vhold object corresponding
1478 * to target vnode.
1479 */
1480 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1481 if (!TAILQ_EMPTY(&hostp->nh_vholds_list))
1482 return (TRUE);
1483
1484 return (FALSE);
1485 }
1486
1487 /*
1488 * Check whether the given host has any client side
1489 * locks or share reservations.
1490 */
1491 static bool_t
1492 nlm_host_has_cli_locks(struct nlm_host *hostp)
1493 {
1494 ASSERT(MUTEX_HELD(&hostp->nh_lock));
1495
1496 /*
1497 * XXX: It's not the way I'd like to do the check,
1498 * because flk_sysid_has_locks() can be very
1499 * expensive by design. Unfortunatelly it iterates
1500 * through all locks on the system, doesn't matter
1501 * were they made on remote system via NLM or
1502 * on local system via reclock. To understand the
1503 * problem, consider that there're dozens of thousands
1504 * of locks that are made on some ZFS dataset. And there's
1505 * another dataset shared by NFS where NLM client had locks
1506 * some time ago, but doesn't have them now.
1507 * In this case flk_sysid_has_locks() will iterate
1508 * thrught dozens of thousands locks until it returns us
1509 * FALSE.
1510 * Oh, I hope that in shiny future somebody will make
1511 * local lock manager (os/flock.c) better, so that
1512 * it'd be more friedly to remote locks and
1513 * flk_sysid_has_locks() wouldn't be so expensive.
1514 */
1515 if (flk_sysid_has_locks(hostp->nh_sysid |
1516 LM_SYSID_CLIENT, FLK_QUERY_ACTIVE))
1517 return (TRUE);
1518
1519 /*
1520 * Check whether host has any share reservations
1521 * registered on the client side.
1522 */
1523 if (hostp->nh_shrlist != NULL)
1524 return (TRUE);
1525
1526 return (FALSE);
1527 }
1528
1529 /*
1530 * Determine whether the given host owns any
1531 * locks or share reservations.
1532 */
1533 static bool_t
1534 nlm_host_has_locks(struct nlm_host *hostp)
1535 {
1536 if (nlm_host_has_srv_locks(hostp))
1537 return (TRUE);
1538
1539 return (nlm_host_has_cli_locks(hostp));
1540 }
1541
1542 /*
1543 * This function compares only addresses of two netbufs
1544 * that belong to NC_TCP[6] or NC_UDP[6] protofamily.
1545 * Port part of netbuf is ignored.
1546 *
1547 * Return values:
1548 * -1: nb1's address is "smaller" than nb2's
1549 * 0: addresses are equal
1550 * 1: nb1's address is "greater" than nb2's
1551 */
1552 static int
1553 nlm_netbuf_addrs_cmp(struct netbuf *nb1, struct netbuf *nb2)
1554 {
1555 union nlm_addr {
1556 struct sockaddr sa;
1557 struct sockaddr_in sin;
1558 struct sockaddr_in6 sin6;
1559 } *na1, *na2;
1560 int res;
1561
1562 /* LINTED E_BAD_PTR_CAST_ALIGN */
1563 na1 = (union nlm_addr *)nb1->buf;
1564 /* LINTED E_BAD_PTR_CAST_ALIGN */
1565 na2 = (union nlm_addr *)nb2->buf;
1566
1567 if (na1->sa.sa_family < na2->sa.sa_family)
1568 return (-1);
1569 if (na1->sa.sa_family > na2->sa.sa_family)
1570 return (1);
1571
1572 switch (na1->sa.sa_family) {
1573 case AF_INET:
1574 res = memcmp(&na1->sin.sin_addr, &na2->sin.sin_addr,
1575 sizeof (na1->sin.sin_addr));
1576 break;
1577 case AF_INET6:
1578 res = memcmp(&na1->sin6.sin6_addr, &na2->sin6.sin6_addr,
1579 sizeof (na1->sin6.sin6_addr));
1580 break;
1581 default:
1582 VERIFY(0);
1583 return (0);
1584 }
1585
1586 return (SIGN(res));
1587 }
1588
1589 /*
1590 * Compare two nlm hosts.
1591 * Return values:
1592 * -1: host1 is "smaller" than host2
1593 * 0: host1 is equal to host2
1594 * 1: host1 is "greater" than host2
1595 */
1596 int
1597 nlm_host_cmp(const void *p1, const void *p2)
1598 {
1599 struct nlm_host *h1 = (struct nlm_host *)p1;
1600 struct nlm_host *h2 = (struct nlm_host *)p2;
1601 int res;
1602
1603 res = strcmp(h1->nh_netid, h2->nh_netid);
1604 if (res != 0)
1605 return (SIGN(res));
1606
1607 res = nlm_netbuf_addrs_cmp(&h1->nh_addr, &h2->nh_addr);
1608 return (res);
1609 }
1610
1611 /*
1612 * Find the host specified by... (see below)
1613 * If found, increment the ref count.
1614 */
1615 static struct nlm_host *
1616 nlm_host_find_locked(struct nlm_globals *g, const char *netid,
1617 struct netbuf *naddr, avl_index_t *wherep)
1618 {
1619 struct nlm_host *hostp, key;
1620 avl_index_t pos;
1621
1622 ASSERT(MUTEX_HELD(&g->lock));
1623
1624 key.nh_netid = (char *)netid;
1625 key.nh_addr.buf = naddr->buf;
1626 key.nh_addr.len = naddr->len;
1627 key.nh_addr.maxlen = naddr->maxlen;
1628
1629 hostp = avl_find(&g->nlm_hosts_tree, &key, &pos);
1630
1631 if (hostp != NULL) {
1632 /*
1633 * Host is inuse now. Remove it from idle
1634 * hosts list if needed.
1635 */
1636 if (hostp->nh_flags & NLM_NH_INIDLE) {
1637 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1638 hostp->nh_flags &= ~NLM_NH_INIDLE;
1639 }
1640
1641 hostp->nh_refs++;
1642 }
1643 if (wherep != NULL)
1644 *wherep = pos;
1645
1646 return (hostp);
1647 }
1648
1649 /*
1650 * Find NLM host for the given name and address.
1651 */
1652 struct nlm_host *
1653 nlm_host_find(struct nlm_globals *g, const char *netid,
1654 struct netbuf *addr)
1655 {
1656 struct nlm_host *hostp = NULL;
1657
1658 mutex_enter(&g->lock);
1659 if (g->run_status != NLM_ST_UP)
1660 goto out;
1661
1662 hostp = nlm_host_find_locked(g, netid, addr, NULL);
1663
1664 out:
1665 mutex_exit(&g->lock);
1666 return (hostp);
1667 }
1668
1669
1670 /*
1671 * Find or create an NLM host for the given name and address.
1672 *
1673 * The remote host is determined by all of: name, netid, address.
1674 * Note that the netid is whatever nlm_svc_add_ep() gave to
1675 * svc_tli_kcreate() for the service binding. If any of these
1676 * are different, allocate a new host (new sysid).
1677 */
1678 struct nlm_host *
1679 nlm_host_findcreate(struct nlm_globals *g, char *name,
1680 const char *netid, struct netbuf *addr)
1681 {
1682 int err;
1683 struct nlm_host *host, *newhost = NULL;
1684 struct knetconfig knc;
1685 avl_index_t where;
1686
1687 mutex_enter(&g->lock);
1688 if (g->run_status != NLM_ST_UP) {
1689 mutex_exit(&g->lock);
1690 return (NULL);
1691 }
1692
1693 host = nlm_host_find_locked(g, netid, addr, NULL);
1694 mutex_exit(&g->lock);
1695 if (host != NULL)
1696 return (host);
1697
1698 err = nlm_knc_from_netid(netid, &knc);
1699 if (err != 0)
1700 return (NULL);
1701 /*
1702 * Do allocations (etc.) outside of mutex,
1703 * and then check again before inserting.
1704 */
1705 newhost = nlm_host_create(name, netid, &knc, addr);
1706 newhost->nh_sysid = nlm_sysid_alloc();
1707 if (newhost->nh_sysid == LM_NOSYSID)
1708 goto out;
1709
1710 mutex_enter(&g->lock);
1711 host = nlm_host_find_locked(g, netid, addr, &where);
1712 if (host == NULL) {
1713 host = newhost;
1714 newhost = NULL;
1715
1716 /*
1717 * Insert host to the hosts AVL tree that is
1718 * used to lookup by <netid, address> pair.
1719 */
1720 avl_insert(&g->nlm_hosts_tree, host, where);
1721
1722 /*
1723 * Insert host to the hosts hash table that is
1724 * used to lookup host by sysid.
1725 */
1726 VERIFY(mod_hash_insert(g->nlm_hosts_hash,
1727 (mod_hash_key_t)(uintptr_t)host->nh_sysid,
1728 (mod_hash_val_t)host) == 0);
1729 }
1730
1731 mutex_exit(&g->lock);
1732
1733 out:
1734 if (newhost != NULL) {
1735 /*
1736 * We do not need the preallocated nlm_host
1737 * so decrement the reference counter
1738 * and destroy it.
1739 */
1740 newhost->nh_refs--;
1741 nlm_host_destroy(newhost);
1742 }
1743
1744 return (host);
1745 }
1746
1747 /*
1748 * Find the NLM host that matches the value of 'sysid'.
1749 * If found, return it with a new ref,
1750 * else return NULL.
1751 */
1752 struct nlm_host *
1753 nlm_host_find_by_sysid(struct nlm_globals *g, sysid_t sysid)
1754 {
1755 struct nlm_host *hostp = NULL;
1756
1757 mutex_enter(&g->lock);
1758 if (g->run_status != NLM_ST_UP)
1759 goto out;
1760
1761 (void) mod_hash_find(g->nlm_hosts_hash,
1762 (mod_hash_key_t)(uintptr_t)sysid,
1763 (mod_hash_val_t)&hostp);
1764
1765 if (hostp == NULL)
1766 goto out;
1767
1768 /*
1769 * Host is inuse now. Remove it
1770 * from idle hosts list if needed.
1771 */
1772 if (hostp->nh_flags & NLM_NH_INIDLE) {
1773 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
1774 hostp->nh_flags &= ~NLM_NH_INIDLE;
1775 }
1776
1777 hostp->nh_refs++;
1778
1779 out:
1780 mutex_exit(&g->lock);
1781 return (hostp);
1782 }
1783
1784 /*
1785 * Release the given host.
1786 * I.e. drop a reference that was taken earlier by one of
1787 * the following functions: nlm_host_findcreate(), nlm_host_find(),
1788 * nlm_host_find_by_sysid().
1789 *
1790 * When the very last reference is dropped, host is moved to
1791 * so-called "idle state". All hosts that are in idle state
1792 * have an idle timeout. If timeout is expired, GC thread
1793 * checks whether hosts have any locks and if they heven't
1794 * any, it removes them.
1795 * NOTE: only unused hosts can be in idle state.
1796 */
1797 static void
1798 nlm_host_release_locked(struct nlm_globals *g, struct nlm_host *hostp)
1799 {
1800 if (hostp == NULL)
1801 return;
1802
1803 ASSERT(MUTEX_HELD(&g->lock));
1804 ASSERT(hostp->nh_refs > 0);
1805
1806 hostp->nh_refs--;
1807 if (hostp->nh_refs != 0)
1808 return;
1809
1810 /*
1811 * The very last reference to the host was dropped,
1812 * thus host is unused now. Set its idle timeout
1813 * and move it to the idle hosts LRU list.
1814 */
1815 hostp->nh_idle_timeout = ddi_get_lbolt() +
1816 SEC_TO_TICK(g->cn_idle_tmo);
1817
1818 ASSERT((hostp->nh_flags & NLM_NH_INIDLE) == 0);
1819 TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, hostp, nh_link);
1820 hostp->nh_flags |= NLM_NH_INIDLE;
1821 }
1822
1823 void
1824 nlm_host_release(struct nlm_globals *g, struct nlm_host *hostp)
1825 {
1826 if (hostp == NULL)
1827 return;
1828
1829 mutex_enter(&g->lock);
1830 nlm_host_release_locked(g, hostp);
1831 mutex_exit(&g->lock);
1832 }
1833
1834 /*
1835 * Unregister this NLM host (NFS client) with the local statd
1836 * due to idleness (no locks held for a while).
1837 */
1838 void
1839 nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host)
1840 {
1841 enum clnt_stat stat;
1842
1843 VERIFY(host->nh_refs == 0);
1844 if (!(host->nh_flags & NLM_NH_MONITORED))
1845 return;
1846
1847 host->nh_flags &= ~NLM_NH_MONITORED;
1848
1849 if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
1850 ZBROP(curzone)->b_rpc_statd(SM_UNMON, g, host);
1851 return;
1852 }
1853
1854 stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name);
1855 if (stat != RPC_SUCCESS) {
1856 NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat);
1857 return;
1858 }
1859 }
1860
1861 /*
1862 * Ask the local NFS statd to begin monitoring this host.
1863 * It will call us back when that host restarts, using the
1864 * prog,vers,proc specified below, i.e. NLM_SM_NOTIFY1,
1865 * which is handled in nlm_do_notify1().
1866 */
1867 void
1868 nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state)
1869 {
1870 int family;
1871 netobj obj;
1872 enum clnt_stat stat;
1873
1874 if (state != 0 && host->nh_state == 0) {
1875 /*
1876 * This is the first time we have seen an NSM state
1877 * Value for this host. We record it here to help
1878 * detect host reboots.
1879 */
1880 host->nh_state = state;
1881 }
1882
1883 mutex_enter(&host->nh_lock);
1884 if (host->nh_flags & NLM_NH_MONITORED) {
1885 mutex_exit(&host->nh_lock);
1886 return;
1887 }
1888
1889 host->nh_flags |= NLM_NH_MONITORED;
1890 mutex_exit(&host->nh_lock);
1891
1892 if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
1893 ZBROP(curzone)->b_rpc_statd(SM_MON, g, host);
1894 return;
1895 }
1896
1897 /*
1898 * Before we begin monitoring the host register the network address
1899 * associated with this hostname.
1900 */
1901 nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj);
1902 stat = nlm_nsmaddr_reg(&g->nlm_nsm, host->nh_name, family, &obj);
1903 if (stat != RPC_SUCCESS) {
1904 NLM_WARN("Failed to register address, stat=%d\n", stat);
1905 mutex_enter(&g->lock);
1906 host->nh_flags &= ~NLM_NH_MONITORED;
1907 mutex_exit(&g->lock);
1908
1909 return;
1910 }
1911
1912 /*
1913 * Tell statd how to call us with status updates for
1914 * this host. Updates arrive via nlm_do_notify1().
1915 *
1916 * We put our assigned system ID value in the priv field to
1917 * make it simpler to find the host if we are notified of a
1918 * host restart.
1919 */
1920 stat = nlm_nsm_mon(&g->nlm_nsm, host->nh_name, host->nh_sysid);
1921 if (stat != RPC_SUCCESS) {
1922 NLM_WARN("Failed to contact local NSM, stat=%d\n", stat);
1923 mutex_enter(&g->lock);
1924 host->nh_flags &= ~NLM_NH_MONITORED;
1925 mutex_exit(&g->lock);
1926
1927 return;
1928 }
1929 }
1930
1931 int
1932 nlm_host_get_state(struct nlm_host *hostp)
1933 {
1934
1935 return (hostp->nh_state);
1936 }
1937
1938 /*
1939 * NLM client/server sleeping locks
1940 */
1941
1942 /*
1943 * Register client side sleeping lock.
1944 *
1945 * Our client code calls this to keep information
1946 * about sleeping lock somewhere. When it receives
1947 * grant callback from server or when it just
1948 * needs to remove all sleeping locks from vnode,
1949 * it uses this information for remove/apply lock
1950 * properly.
1951 */
1952 struct nlm_slock *
1953 nlm_slock_register(
1954 struct nlm_globals *g,
1955 struct nlm_host *host,
1956 struct nlm4_lock *lock,
1957 struct vnode *vp)
1958 {
1959 struct nlm_slock *nslp;
1960
1961 nslp = kmem_zalloc(sizeof (*nslp), KM_SLEEP);
1962 cv_init(&nslp->nsl_cond, NULL, CV_DEFAULT, NULL);
1963 nslp->nsl_lock = *lock;
1964 nlm_copy_netobj(&nslp->nsl_fh, &nslp->nsl_lock.fh);
1965 nslp->nsl_state = NLM_SL_BLOCKED;
1966 nslp->nsl_host = host;
1967 nslp->nsl_vp = vp;
1968
1969 mutex_enter(&g->lock);
1970 TAILQ_INSERT_TAIL(&g->nlm_slocks, nslp, nsl_link);
1971 mutex_exit(&g->lock);
1972
1973 return (nslp);
1974 }
1975
1976 /*
1977 * Remove this lock from the wait list and destroy it.
1978 */
1979 void
1980 nlm_slock_unregister(struct nlm_globals *g, struct nlm_slock *nslp)
1981 {
1982 mutex_enter(&g->lock);
1983 TAILQ_REMOVE(&g->nlm_slocks, nslp, nsl_link);
1984 mutex_exit(&g->lock);
1985
1986 kmem_free(nslp->nsl_fh.n_bytes, nslp->nsl_fh.n_len);
1987 cv_destroy(&nslp->nsl_cond);
1988 kmem_free(nslp, sizeof (*nslp));
1989 }
1990
1991 /*
1992 * Wait for a granted callback or cancellation event
1993 * for a sleeping lock.
1994 *
1995 * If a signal interrupted the wait or if the lock
1996 * was cancelled, return EINTR - the caller must arrange to send
1997 * a cancellation to the server.
1998 *
1999 * If timeout occurred, return ETIMEDOUT - the caller must
2000 * resend the lock request to the server.
2001 *
2002 * On success return 0.
2003 */
2004 int
2005 nlm_slock_wait(struct nlm_globals *g,
2006 struct nlm_slock *nslp, uint_t timeo_secs)
2007 {
2008 clock_t timeo_ticks;
2009 int cv_res, error;
2010
2011 /*
2012 * If the granted message arrived before we got here,
2013 * nslp->nsl_state will be NLM_SL_GRANTED - in that case don't sleep.
2014 */
2015 cv_res = 1;
2016 timeo_ticks = ddi_get_lbolt() + SEC_TO_TICK(timeo_secs);
2017
2018 mutex_enter(&g->lock);
2019 while (nslp->nsl_state == NLM_SL_BLOCKED && cv_res > 0) {
2020 cv_res = cv_timedwait_sig(&nslp->nsl_cond,
2021 &g->lock, timeo_ticks);
2022 }
2023
2024 /*
2025 * No matter why we wake up, if the lock was
2026 * cancelled, let the function caller to know
2027 * about it by returning EINTR.
2028 */
2029 if (nslp->nsl_state == NLM_SL_CANCELLED) {
2030 error = EINTR;
2031 goto out;
2032 }
2033
2034 if (cv_res <= 0) {
2035 /* We were woken up either by timeout or by interrupt */
2036 error = (cv_res < 0) ? ETIMEDOUT : EINTR;
2037
2038 /*
2039 * The granted message may arrive after the
2040 * interrupt/timeout but before we manage to lock the
2041 * mutex. Detect this by examining nslp.
2042 */
2043 if (nslp->nsl_state == NLM_SL_GRANTED)
2044 error = 0;
2045 } else { /* Awaken via cv_signal()/cv_broadcast() or didn't block */
2046 error = 0;
2047 VERIFY(nslp->nsl_state == NLM_SL_GRANTED);
2048 }
2049
2050 out:
2051 mutex_exit(&g->lock);
2052 return (error);
2053 }
2054
2055 /*
2056 * Mark client side sleeping lock as granted
2057 * and wake up a process blocked on the lock.
2058 * Called from server side NLM_GRANT handler.
2059 *
2060 * If sleeping lock is found return 0, otherwise
2061 * return ENOENT.
2062 */
2063 int
2064 nlm_slock_grant(struct nlm_globals *g,
2065 struct nlm_host *hostp, struct nlm4_lock *alock)
2066 {
2067 struct nlm_slock *nslp;
2068 int error = ENOENT;
2069
2070 mutex_enter(&g->lock);
2071 TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
2072 if ((nslp->nsl_state != NLM_SL_BLOCKED) ||
2073 (nslp->nsl_host != hostp))
2074 continue;
2075
2076 if (alock->svid == nslp->nsl_lock.svid &&
2077 alock->l_offset == nslp->nsl_lock.l_offset &&
2078 alock->l_len == nslp->nsl_lock.l_len &&
2079 alock->fh.n_len == nslp->nsl_lock.fh.n_len &&
2080 bcmp(alock->fh.n_bytes, nslp->nsl_lock.fh.n_bytes,
2081 nslp->nsl_lock.fh.n_len) == 0) {
2082 nslp->nsl_state = NLM_SL_GRANTED;
2083 cv_broadcast(&nslp->nsl_cond);
2084 error = 0;
2085 break;
2086 }
2087 }
2088
2089 mutex_exit(&g->lock);
2090 return (error);
2091 }
2092
2093 /*
2094 * Register sleeping lock request corresponding to
2095 * flp on the given vhold object.
2096 * On success function returns 0, otherwise (if
2097 * lock request with the same flp is already
2098 * registered) function returns EEXIST.
2099 */
2100 int
2101 nlm_slreq_register(struct nlm_host *hostp, struct nlm_vhold *nvp,
2102 struct flock64 *flp)
2103 {
2104 struct nlm_slreq *slr, *new_slr = NULL;
2105 int ret = EEXIST;
2106
2107 mutex_enter(&hostp->nh_lock);
2108 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2109 if (slr != NULL)
2110 goto out;
2111
2112 mutex_exit(&hostp->nh_lock);
2113 new_slr = kmem_zalloc(sizeof (*slr), KM_SLEEP);
2114 bcopy(flp, &new_slr->nsr_fl, sizeof (*flp));
2115
2116 mutex_enter(&hostp->nh_lock);
2117 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2118 if (slr == NULL) {
2119 slr = new_slr;
2120 new_slr = NULL;
2121 ret = 0;
2122
2123 TAILQ_INSERT_TAIL(&nvp->nv_slreqs, slr, nsr_link);
2124 }
2125
2126 out:
2127 mutex_exit(&hostp->nh_lock);
2128 if (new_slr != NULL)
2129 kmem_free(new_slr, sizeof (*new_slr));
2130
2131 return (ret);
2132 }
2133
2134 /*
2135 * Unregister sleeping lock request corresponding
2136 * to flp from the given vhold object.
2137 * On success function returns 0, otherwise (if
2138 * lock request corresponding to flp isn't found
2139 * on the given vhold) function returns ENOENT.
2140 */
2141 int
2142 nlm_slreq_unregister(struct nlm_host *hostp, struct nlm_vhold *nvp,
2143 struct flock64 *flp)
2144 {
2145 struct nlm_slreq *slr;
2146
2147 mutex_enter(&hostp->nh_lock);
2148 slr = nlm_slreq_find_locked(hostp, nvp, flp);
2149 if (slr == NULL) {
2150 mutex_exit(&hostp->nh_lock);
2151 return (ENOENT);
2152 }
2153
2154 TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
2155 mutex_exit(&hostp->nh_lock);
2156
2157 kmem_free(slr, sizeof (*slr));
2158 return (0);
2159 }
2160
2161 /*
2162 * Find sleeping lock request on the given vhold object by flp.
2163 */
2164 struct nlm_slreq *
2165 nlm_slreq_find_locked(struct nlm_host *hostp, struct nlm_vhold *nvp,
2166 struct flock64 *flp)
2167 {
2168 struct nlm_slreq *slr = NULL;
2169
2170 ASSERT(MUTEX_HELD(&hostp->nh_lock));
2171 TAILQ_FOREACH(slr, &nvp->nv_slreqs, nsr_link) {
2172 if (slr->nsr_fl.l_start == flp->l_start &&
2173 slr->nsr_fl.l_len == flp->l_len &&
2174 slr->nsr_fl.l_pid == flp->l_pid &&
2175 slr->nsr_fl.l_type == flp->l_type)
2176 break;
2177 }
2178
2179 return (slr);
2180 }
2181
2182 /*
2183 * NLM tracks active share reservations made on the client side.
2184 * It needs to have a track of share reservations for two purposes
2185 * 1) to determine if nlm_host is busy (if it has active locks and/or
2186 * share reservations, it is)
2187 * 2) to recover active share reservations when NLM server reports
2188 * that it has rebooted.
2189 *
2190 * Unfortunately Illumos local share reservations manager (see os/share.c)
2191 * doesn't have an ability to lookup all reservations on the system
2192 * by sysid (like local lock manager) or get all reservations by sysid.
2193 * It tracks reservations per vnode and is able to get/looup them
2194 * on particular vnode. It's not what NLM needs. Thus it has that ugly
2195 * share reservations tracking scheme.
2196 */
2197
2198 void
2199 nlm_shres_track(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
2200 {
2201 struct nlm_shres *nsp, *nsp_new;
2202
2203 /*
2204 * NFS code must fill the s_owner, so that
2205 * s_own_len is never 0.
2206 */
2207 ASSERT(shrp->s_own_len > 0);
2208 nsp_new = nlm_shres_create_item(shrp, vp);
2209
2210 mutex_enter(&hostp->nh_lock);
2211 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next)
2212 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr))
2213 break;
2214
2215 if (nsp != NULL) {
2216 /*
2217 * Found a duplicate. Do nothing.
2218 */
2219
2220 goto out;
2221 }
2222
2223 nsp = nsp_new;
2224 nsp_new = NULL;
2225 nsp->ns_next = hostp->nh_shrlist;
2226 hostp->nh_shrlist = nsp;
2227
2228 out:
2229 mutex_exit(&hostp->nh_lock);
2230 if (nsp_new != NULL)
2231 nlm_shres_destroy_item(nsp_new);
2232 }
2233
2234 void
2235 nlm_shres_untrack(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
2236 {
2237 struct nlm_shres *nsp, *nsp_prev = NULL;
2238
2239 mutex_enter(&hostp->nh_lock);
2240 nsp = hostp->nh_shrlist;
2241 while (nsp != NULL) {
2242 if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) {
2243 struct nlm_shres *nsp_del;
2244
2245 nsp_del = nsp;
2246 nsp = nsp->ns_next;
2247 if (nsp_prev != NULL)
2248 nsp_prev->ns_next = nsp;
2249 else
2250 hostp->nh_shrlist = nsp;
2251
2252 nlm_shres_destroy_item(nsp_del);
2253 continue;
2254 }
2255
2256 nsp_prev = nsp;
2257 nsp = nsp->ns_next;
2258 }
2259
2260 mutex_exit(&hostp->nh_lock);
2261 }
2262
2263 /*
2264 * Get a _copy_ of the list of all active share reservations
2265 * made by the given host.
2266 * NOTE: the list function returns _must_ be released using
2267 * nlm_free_shrlist().
2268 */
2269 struct nlm_shres *
2270 nlm_get_active_shres(struct nlm_host *hostp)
2271 {
2272 struct nlm_shres *nsp, *nslist = NULL;
2273
2274 mutex_enter(&hostp->nh_lock);
2275 for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) {
2276 struct nlm_shres *nsp_new;
2277
2278 nsp_new = nlm_shres_create_item(nsp->ns_shr, nsp->ns_vp);
2279 nsp_new->ns_next = nslist;
2280 nslist = nsp_new;
2281 }
2282
2283 mutex_exit(&hostp->nh_lock);
2284 return (nslist);
2285 }
2286
2287 /*
2288 * Free memory allocated for the active share reservations
2289 * list created by nlm_get_active_shres() function.
2290 */
2291 void
2292 nlm_free_shrlist(struct nlm_shres *nslist)
2293 {
2294 struct nlm_shres *nsp;
2295
2296 while (nslist != NULL) {
2297 nsp = nslist;
2298 nslist = nslist->ns_next;
2299
2300 nlm_shres_destroy_item(nsp);
2301 }
2302 }
2303
2304 static bool_t
2305 nlm_shres_equal(struct shrlock *shrp1, struct shrlock *shrp2)
2306 {
2307 if (shrp1->s_sysid == shrp2->s_sysid &&
2308 shrp1->s_pid == shrp2->s_pid &&
2309 shrp1->s_own_len == shrp2->s_own_len &&
2310 bcmp(shrp1->s_owner, shrp2->s_owner,
2311 shrp1->s_own_len) == 0)
2312 return (TRUE);
2313
2314 return (FALSE);
2315 }
2316
2317 static struct nlm_shres *
2318 nlm_shres_create_item(struct shrlock *shrp, vnode_t *vp)
2319 {
2320 struct nlm_shres *nsp;
2321
2322 nsp = kmem_alloc(sizeof (*nsp), KM_SLEEP);
2323 nsp->ns_shr = kmem_alloc(sizeof (*shrp), KM_SLEEP);
2324 bcopy(shrp, nsp->ns_shr, sizeof (*shrp));
2325 nsp->ns_shr->s_owner = kmem_alloc(shrp->s_own_len, KM_SLEEP);
2326 bcopy(shrp->s_owner, nsp->ns_shr->s_owner, shrp->s_own_len);
2327 nsp->ns_vp = vp;
2328
2329 return (nsp);
2330 }
2331
2332 static void
2333 nlm_shres_destroy_item(struct nlm_shres *nsp)
2334 {
2335 kmem_free(nsp->ns_shr->s_owner,
2336 nsp->ns_shr->s_own_len);
2337 kmem_free(nsp->ns_shr, sizeof (struct shrlock));
2338 kmem_free(nsp, sizeof (*nsp));
2339 }
2340
2341 /*
2342 * Called by klmmod.c when lockd adds a network endpoint
2343 * on which we should begin RPC services.
2344 */
2345 int
2346 nlm_svc_add_ep(struct file *fp, const char *netid, struct knetconfig *knc)
2347 {
2348 SVCMASTERXPRT *xprt = NULL;
2349 int error;
2350
2351 error = svc_tli_kcreate(fp, 0, (char *)netid, NULL, &xprt,
2352 &nlm_sct, NULL, NLM_SVCPOOL_ID, FALSE);
2353 if (error != 0)
2354 return (error);
2355
2356 (void) nlm_knc_to_netid(knc);
2357 return (0);
2358 }
2359
2360 /*
2361 * Start NLM service.
2362 */
2363 int
2364 nlm_svc_starting(struct nlm_globals *g, struct file *fp,
2365 const char *netid, struct knetconfig *knc)
2366 {
2367 int error;
2368 enum clnt_stat stat;
2369
2370 VERIFY(g->run_status == NLM_ST_STARTING);
2371 VERIFY(g->nlm_gc_thread == NULL);
2372
2373 error = nlm_nsm_init_local(&g->nlm_nsm);
2374 if (error != 0) {
2375 NLM_ERR("Failed to initialize NSM handler "
2376 "(error=%d)\n", error);
2377 g->run_status = NLM_ST_DOWN;
2378 return (error);
2379 }
2380
2381 error = EIO;
2382
2383 /*
2384 * Create an NLM garbage collector thread that will
2385 * clean up stale vholds and hosts objects.
2386 */
2387 g->nlm_gc_thread = zthread_create(NULL, 0, nlm_gc,
2388 g, 0, minclsyspri);
2389
2390 /*
2391 * Send SIMU_CRASH to local statd to report that
2392 * NLM started, so that statd can report other hosts
2393 * about NLM state change.
2394 */
2395
2396 stat = nlm_nsm_simu_crash(&g->nlm_nsm);
2397 if (stat != RPC_SUCCESS) {
2398 NLM_ERR("Failed to connect to local statd "
2399 "(rpcerr=%d)\n", stat);
2400 goto shutdown_lm;
2401 }
2402
2403 stat = nlm_nsm_stat(&g->nlm_nsm, &g->nsm_state);
2404 if (stat != RPC_SUCCESS) {
2405 NLM_ERR("Failed to get the status of local statd "
2406 "(rpcerr=%d)\n", stat);
2407 goto shutdown_lm;
2408 }
2409
2410 g->grace_threshold = ddi_get_lbolt() +
2411 SEC_TO_TICK(g->grace_period);
2412
2413 /* Register endpoint used for communications with local NLM */
2414 error = nlm_svc_add_ep(fp, netid, knc);
2415 if (error != 0)
2416 goto shutdown_lm;
2417
2418 (void) svc_pool_control(NLM_SVCPOOL_ID,
2419 SVCPSET_SHUTDOWN_PROC, (void *)nlm_pool_shutdown);
2420 g->run_status = NLM_ST_UP;
2421 return (0);
2422
2423 shutdown_lm:
2424 mutex_enter(&g->lock);
2425 g->run_status = NLM_ST_STOPPING;
2426 mutex_exit(&g->lock);
2427
2428 nlm_svc_stopping(g);
2429 return (error);
2430 }
2431
2432 /*
2433 * Called when the server pool is destroyed, so that
2434 * all transports are closed and no any server threads
2435 * exist.
2436 *
2437 * Just call lm_shutdown() to shut NLM down properly.
2438 */
2439 static void
2440 nlm_pool_shutdown(void)
2441 {
2442 (void) lm_shutdown();
2443 }
2444
2445 /*
2446 * Stop NLM service, cleanup all resources
2447 * NLM owns at the moment.
2448 *
2449 * NOTE: NFS code can call NLM while it's
2450 * stopping or even if it's shut down. Any attempt
2451 * to lock file either on client or on the server
2452 * will fail if NLM isn't in NLM_ST_UP state.
2453 */
2454 void
2455 nlm_svc_stopping(struct nlm_globals *g)
2456 {
2457 mutex_enter(&g->lock);
2458 ASSERT(g->run_status == NLM_ST_STOPPING);
2459
2460 /*
2461 * Ask NLM GC thread to exit and wait until it dies.
2462 */
2463 cv_signal(&g->nlm_gc_sched_cv);
2464 while (g->nlm_gc_thread != NULL)
2465 cv_wait(&g->nlm_gc_finish_cv, &g->lock);
2466
2467 mutex_exit(&g->lock);
2468
2469 /*
2470 * Cleanup locks owned by NLM hosts.
2471 * NOTE: New hosts won't be created while
2472 * NLM is stopping.
2473 */
2474 while (!avl_is_empty(&g->nlm_hosts_tree)) {
2475 struct nlm_host *hostp;
2476 int busy_hosts = 0;
2477
2478 /*
2479 * Iterate through all NLM hosts in the system
2480 * and drop the locks they own by force.
2481 */
2482 hostp = avl_first(&g->nlm_hosts_tree);
2483 while (hostp != NULL) {
2484 /* Cleanup all client and server side locks */
2485 nlm_client_cancel_all(g, hostp);
2486 nlm_host_notify_server(hostp, 0);
2487
2488 mutex_enter(&hostp->nh_lock);
2489 nlm_host_gc_vholds(hostp);
2490 if (hostp->nh_refs > 0 || nlm_host_has_locks(hostp)) {
2491 /*
2492 * Oh, it seems the host is still busy, let
2493 * it some time to release and go to the
2494 * next one.
2495 */
2496
2497 mutex_exit(&hostp->nh_lock);
2498 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2499 busy_hosts++;
2500 continue;
2501 }
2502
2503 mutex_exit(&hostp->nh_lock);
2504 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2505 }
2506
2507 /*
2508 * All hosts go to nlm_idle_hosts list after
2509 * all locks they own are cleaned up and last refereces
2510 * were dropped. Just destroy all hosts in nlm_idle_hosts
2511 * list, they can not be removed from there while we're
2512 * in stopping state.
2513 */
2514 while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
2515 nlm_host_unregister(g, hostp);
2516 nlm_host_destroy(hostp);
2517 }
2518
2519 if (busy_hosts > 0) {
2520 /*
2521 * There're some hosts that weren't cleaned
2522 * up. Probably they're in resource cleanup
2523 * process. Give them some time to do drop
2524 * references.
2525 */
2526 delay(MSEC_TO_TICK(500));
2527 }
2528 }
2529
2530 ASSERT(TAILQ_EMPTY(&g->nlm_slocks));
2531
2532 nlm_nsm_fini(&g->nlm_nsm);
2533 g->lockd_pid = 0;
2534 g->run_status = NLM_ST_DOWN;
2535 }
2536
2537 /*
2538 * Returns TRUE if the given vnode has
2539 * any active or sleeping locks.
2540 */
2541 int
2542 nlm_vp_active(const vnode_t *vp)
2543 {
2544 struct nlm_globals *g;
2545 struct nlm_host *hostp;
2546 struct nlm_vhold *nvp;
2547 int active = 0;
2548
2549 g = zone_getspecific(nlm_zone_key, curzone);
2550
2551 /*
2552 * Server side NLM has locks on the given vnode
2553 * if there exist a vhold object that holds
2554 * the given vnode "vp" in one of NLM hosts.
2555 */
2556 mutex_enter(&g->lock);
2557 hostp = avl_first(&g->nlm_hosts_tree);
2558 while (hostp != NULL) {
2559 mutex_enter(&hostp->nh_lock);
2560 nvp = nlm_vhold_find_locked(hostp, vp);
2561 mutex_exit(&hostp->nh_lock);
2562 if (nvp != NULL) {
2563 active = 1;
2564 break;
2565 }
2566
2567 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2568 }
2569
2570 mutex_exit(&g->lock);
2571 return (active);
2572 }
2573
2574 /*
2575 * Called right before NFS export is going to
2576 * dissapear. The function finds all vnodes
2577 * belonging to the given export and cleans
2578 * all remote locks and share reservations
2579 * on them.
2580 */
2581 void
2582 nlm_unexport(struct exportinfo *exi)
2583 {
2584 struct nlm_globals *g;
2585 struct nlm_host *hostp;
2586
2587 /* This may be called on behalf of global-zone doing shutdown. */
2588 ASSERT(exi->exi_zone == curzone || curzone == global_zone);
2589 g = zone_getspecific(nlm_zone_key, exi->exi_zone);
2590 if (g == NULL) {
2591 /* Did zone cleanup get here already? */
2592 return;
2593 }
2594
2595 mutex_enter(&g->lock);
2596 hostp = avl_first(&g->nlm_hosts_tree);
2597 while (hostp != NULL) {
2598 struct nlm_vhold *nvp;
2599
2600 if (hostp->nh_flags & NLM_NH_INIDLE) {
2601 TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
2602 hostp->nh_flags &= ~NLM_NH_INIDLE;
2603 }
2604 hostp->nh_refs++;
2605
2606 mutex_exit(&g->lock);
2607
2608 mutex_enter(&hostp->nh_lock);
2609 TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
2610 vnode_t *vp;
2611
2612 nvp->nv_refcnt++;
2613 mutex_exit(&hostp->nh_lock);
2614
2615 vp = nvp->nv_vp;
2616
2617 if (!EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
2618 goto next_iter;
2619
2620 /*
2621 * Ok, it we found out that vnode vp is under
2622 * control by the exportinfo exi, now we need
2623 * to drop all locks from this vnode, let's
2624 * do it.
2625 */
2626 nlm_vhold_clean(nvp, hostp->nh_sysid);
2627
2628 next_iter:
2629 mutex_enter(&hostp->nh_lock);
2630 nvp->nv_refcnt--;
2631 }
2632 mutex_exit(&hostp->nh_lock);
2633
2634 mutex_enter(&g->lock);
2635 nlm_host_release_locked(g, hostp);
2636
2637 hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
2638 }
2639
2640 mutex_exit(&g->lock);
2641 }
2642
2643 /*
2644 * Allocate new unique sysid.
2645 * In case of failure (no available sysids)
2646 * return LM_NOSYSID.
2647 */
2648 sysid_t
2649 nlm_sysid_alloc(void)
2650 {
2651 sysid_t ret_sysid = LM_NOSYSID;
2652
2653 rw_enter(&lm_lck, RW_WRITER);
2654 if (nlm_sysid_nidx > LM_SYSID_MAX)
2655 nlm_sysid_nidx = LM_SYSID;
2656
2657 if (!BT_TEST(nlm_sysid_bmap, nlm_sysid_nidx)) {
2658 BT_SET(nlm_sysid_bmap, nlm_sysid_nidx);
2659 ret_sysid = nlm_sysid_nidx++;
2660 } else {
2661 index_t id;
2662
2663 id = bt_availbit(nlm_sysid_bmap, NLM_BMAP_NITEMS);
2664 if (id > 0) {
2665 nlm_sysid_nidx = id + 1;
2666 ret_sysid = id;
2667 BT_SET(nlm_sysid_bmap, id);
2668 }
2669 }
2670
2671 rw_exit(&lm_lck);
2672 return (ret_sysid);
2673 }
2674
2675 void
2676 nlm_sysid_free(sysid_t sysid)
2677 {
2678 ASSERT(sysid >= LM_SYSID && sysid <= LM_SYSID_MAX);
2679
2680 rw_enter(&lm_lck, RW_WRITER);
2681 ASSERT(BT_TEST(nlm_sysid_bmap, sysid));
2682 BT_CLEAR(nlm_sysid_bmap, sysid);
2683 rw_exit(&lm_lck);
2684 }
2685
2686 /*
2687 * Return true if the request came from a local caller.
2688 * By necessity, this "knows" the netid names invented
2689 * in lm_svc() and nlm_netid_from_knetconfig().
2690 */
2691 bool_t
2692 nlm_caller_is_local(SVCXPRT *transp)
2693 {
2694 char *netid;
2695 struct netbuf *rtaddr;
2696
2697 netid = svc_getnetid(transp);
2698 rtaddr = svc_getrpccaller(transp);
2699
2700 if (netid == NULL)
2701 return (FALSE);
2702
2703 if (strcmp(netid, "ticlts") == 0 ||
2704 strcmp(netid, "ticotsord") == 0)
2705 return (TRUE);
2706
2707 if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
2708 struct sockaddr_in *sin = (void *)rtaddr->buf;
2709 if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
2710 return (TRUE);
2711 }
2712 if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
2713 struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
2714 if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
2715 return (TRUE);
2716 }
2717
2718 return (FALSE); /* unknown transport */
2719 }
2720
2721 /*
2722 * Get netid string correspondig to the given knetconfig.
2723 * If not done already, save knc->knc_rdev in our table.
2724 */
2725 const char *
2726 nlm_knc_to_netid(struct knetconfig *knc)
2727 {
2728 int i;
2729 dev_t rdev;
2730 struct nlm_knc *nc;
2731 const char *netid = NULL;
2732
2733 rw_enter(&lm_lck, RW_READER);
2734 for (i = 0; i < NLM_KNCS; i++) {
2735 nc = &nlm_netconfigs[i];
2736
2737 if (nc->n_knc.knc_semantics == knc->knc_semantics &&
2738 strcmp(nc->n_knc.knc_protofmly,
2739 knc->knc_protofmly) == 0) {
2740 netid = nc->n_netid;
2741 rdev = nc->n_knc.knc_rdev;
2742 break;
2743 }
2744 }
2745 rw_exit(&lm_lck);
2746
2747 if (netid != NULL && rdev == NODEV) {
2748 rw_enter(&lm_lck, RW_WRITER);
2749 if (nc->n_knc.knc_rdev == NODEV)
2750 nc->n_knc.knc_rdev = knc->knc_rdev;
2751 rw_exit(&lm_lck);
2752 }
2753
2754 return (netid);
2755 }
2756
2757 /*
2758 * Get a knetconfig corresponding to the given netid.
2759 * If there's no knetconfig for this netid, ENOENT
2760 * is returned.
2761 */
2762 int
2763 nlm_knc_from_netid(const char *netid, struct knetconfig *knc)
2764 {
2765 int i, ret;
2766
2767 ret = ENOENT;
2768 for (i = 0; i < NLM_KNCS; i++) {
2769 struct nlm_knc *nknc;
2770
2771 nknc = &nlm_netconfigs[i];
2772 if (strcmp(netid, nknc->n_netid) == 0 &&
2773 nknc->n_knc.knc_rdev != NODEV) {
2774 *knc = nknc->n_knc;
2775 ret = 0;
2776 break;
2777 }
2778 }
2779
2780 return (ret);
2781 }
2782
2783 void
2784 nlm_cprsuspend(void)
2785 {
2786 struct nlm_globals *g;
2787
2788 rw_enter(&lm_lck, RW_READER);
2789 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
2790 nlm_suspend_zone(g);
2791
2792 rw_exit(&lm_lck);
2793 }
2794
2795 void
2796 nlm_cprresume(void)
2797 {
2798 struct nlm_globals *g;
2799
2800 rw_enter(&lm_lck, RW_READER);
2801 TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
2802 nlm_resume_zone(g);
2803
2804 rw_exit(&lm_lck);
2805 }
2806
2807 void
2808 nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
2809 {
2810 (void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
2811 NLM_RPC_RETRIES, kcred);
2812 }
2813
2814 void
2815 nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj)
2816 {
2817 /* LINTED pointer alignment */
2818 struct sockaddr *sa = (struct sockaddr *)addr->buf;
2819
2820 *family = sa->sa_family;
2821
2822 switch (sa->sa_family) {
2823 case AF_INET: {
2824 /* LINTED pointer alignment */
2825 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
2826
2827 obj->n_len = sizeof (sin->sin_addr);
2828 obj->n_bytes = (char *)&sin->sin_addr;
2829 break;
2830 }
2831
2832 case AF_INET6: {
2833 /* LINTED pointer alignment */
2834 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
2835
2836 obj->n_len = sizeof (sin6->sin6_addr);
2837 obj->n_bytes = (char *)&sin6->sin6_addr;
2838 break;
2839 }
2840
2841 default:
2842 VERIFY(0);
2843 break;
2844 }
2845 }