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