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>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
30 * Copyright (c) 2012 by Delphix. All rights reserved.
34 * NFS LockManager, start/stop, support functions, etc.
35 * Most of the interesting code is here.
37 * Source code derived from FreeBSD nlm_prot_impl.c
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>
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>
57 #include <sys/queue.h>
58 #include <sys/bitmap.h>
60 #include <netinet/in.h>
64 #include <rpc/pmap_prot.h>
65 #include <rpc/pmap_clnt.h>
66 #include <rpc/rpcb_prot.h>
68 #include <rpcsvc/nlm_prot.h>
69 #include <rpcsvc/sm_inter.h>
70 #include <rpcsvc/nsm_addr.h>
73 #include <nfs/nfs_clnt.h>
74 #include <nfs/export.h>
75 #include <nfs/rnode.h>
81 struct knetconfig n_knc
;
86 * Number of attempts NLM tries to obtain RPC binding
89 #define NLM_NSM_RPCBIND_RETRIES 10
92 * Timeout (in seconds) NLM waits before making another
93 * attempt to obtain RPC binding of local statd.
95 #define NLM_NSM_RPCBIND_TIMEOUT 5
98 * Total number of sysids in NLM sysid bitmap
100 #define NLM_BMAP_NITEMS (LM_SYSID_MAX + 1)
103 * Number of ulong_t words in bitmap that is used
104 * for allocation of sysid numbers.
106 #define NLM_BMAP_WORDS (NLM_BMAP_NITEMS / BT_NBIPUL)
109 * Given an integer x, the macro returns
110 * -1 if x is negative,
114 #define SIGN(x) (((x) > 0) - ((x) < 0))
116 #define ARRSIZE(arr) (sizeof (arr) / sizeof ((arr)[0]))
117 #define NLM_KNCS ARRSIZE(nlm_netconfigs)
122 * Zero timeout for asynchronous NLM RPC operations
124 static const struct timeval nlm_rpctv_zero
= { 0, 0 };
127 * List of all Zone globals nlm_globals instences
130 static struct nlm_globals_list nlm_zones_list
; /* (g) */
135 static struct kmem_cache
*nlm_hosts_cache
= NULL
;
136 static struct kmem_cache
*nlm_vhold_cache
= NULL
;
139 * A bitmap for allocation of new sysids.
140 * Sysid is a unique number between LM_SYSID
141 * and LM_SYSID_MAX. Sysid represents unique remote
142 * host that does file locks on the given host.
144 static ulong_t nlm_sysid_bmap
[NLM_BMAP_WORDS
]; /* (g) */
145 static int nlm_sysid_nidx
; /* (g) */
148 * RPC service registration for all transports
150 static SVC_CALLOUT nlm_svcs
[] = {
151 { NLM_PROG
, 4, 4, nlm_prog_4
}, /* NLM4_VERS */
152 { NLM_PROG
, 1, 3, nlm_prog_3
} /* NLM_VERS - NLM_VERSX */
155 static SVC_CALLOUT_TABLE nlm_sct
= {
162 * Static table of all netid/knetconfig network
163 * lock manager can work with. nlm_netconfigs table
164 * is used when we need to get valid knetconfig by
165 * netid and vice versa.
167 * Knetconfigs are activated either by the call from
168 * user-space lockd daemon (server side) or by taking
169 * knetconfig from NFS mountinfo (client side)
171 static struct nlm_knc nlm_netconfigs
[] = { /* (g) */
174 { NC_TPI_CLTS
, NC_INET
, NC_UDP
, NODEV
},
179 { NC_TPI_COTS_ORD
, NC_INET
, NC_TCP
, NODEV
},
184 { NC_TPI_CLTS
, NC_INET6
, NC_UDP
, NODEV
},
189 { NC_TPI_COTS_ORD
, NC_INET6
, NC_TCP
, NODEV
},
192 /* ticlts (loopback over UDP) */
194 { NC_TPI_CLTS
, NC_LOOPBACK
, NC_NOPROTO
, NODEV
},
197 /* ticotsord (loopback over TCP) */
199 { NC_TPI_COTS_ORD
, NC_LOOPBACK
, NC_NOPROTO
, NODEV
},
207 static void nlm_copy_netbuf(struct netbuf
*, struct netbuf
*);
208 static int nlm_netbuf_addrs_cmp(struct netbuf
*, struct netbuf
*);
209 static void nlm_kmem_reclaim(void *);
210 static void nlm_pool_shutdown(void);
211 static void nlm_suspend_zone(struct nlm_globals
*);
212 static void nlm_resume_zone(struct nlm_globals
*);
213 static void nlm_nsm_clnt_init(CLIENT
*, struct nlm_nsm
*);
214 static void nlm_netbuf_to_netobj(struct netbuf
*, int *, netobj
*);
217 * NLM thread functions
219 static void nlm_gc(struct nlm_globals
*);
220 static void nlm_reclaimer(struct nlm_host
*);
225 static int nlm_init_local_knc(struct knetconfig
*);
226 static int nlm_nsm_init_local(struct nlm_nsm
*);
227 static int nlm_nsm_init(struct nlm_nsm
*, struct knetconfig
*, struct netbuf
*);
228 static void nlm_nsm_fini(struct nlm_nsm
*);
229 static enum clnt_stat
nlm_nsm_simu_crash(struct nlm_nsm
*);
230 static enum clnt_stat
nlm_nsm_stat(struct nlm_nsm
*, int32_t *);
231 static enum clnt_stat
nlm_nsm_mon(struct nlm_nsm
*, char *, uint16_t);
232 static enum clnt_stat
nlm_nsm_unmon(struct nlm_nsm
*, char *);
237 static int nlm_host_ctor(void *, void *, int);
238 static void nlm_host_dtor(void *, void *);
239 static void nlm_host_destroy(struct nlm_host
*);
240 static struct nlm_host
*nlm_host_create(char *, const char *,
241 struct knetconfig
*, struct netbuf
*);
242 static struct nlm_host
*nlm_host_find_locked(struct nlm_globals
*,
243 const char *, struct netbuf
*, avl_index_t
*);
244 static void nlm_host_unregister(struct nlm_globals
*, struct nlm_host
*);
245 static void nlm_host_gc_vholds(struct nlm_host
*);
246 static bool_t
nlm_host_has_srv_locks(struct nlm_host
*);
247 static bool_t
nlm_host_has_cli_locks(struct nlm_host
*);
248 static bool_t
nlm_host_has_locks(struct nlm_host
*);
251 * NLM vhold functions
253 static int nlm_vhold_ctor(void *, void *, int);
254 static void nlm_vhold_dtor(void *, void *);
255 static void nlm_vhold_destroy(struct nlm_host
*,
257 static bool_t
nlm_vhold_busy(struct nlm_host
*, struct nlm_vhold
*);
258 static void nlm_vhold_clean(struct nlm_vhold
*, int);
261 * NLM client/server sleeping locks/share reservation functions
263 struct nlm_slreq
*nlm_slreq_find_locked(struct nlm_host
*,
264 struct nlm_vhold
*, struct flock64
*);
265 static struct nlm_shres
*nlm_shres_create_item(struct shrlock
*, vnode_t
*);
266 static void nlm_shres_destroy_item(struct nlm_shres
*);
267 static bool_t
nlm_shres_equal(struct shrlock
*, struct shrlock
*);
270 * NLM initialization functions.
275 nlm_hosts_cache
= kmem_cache_create("nlm_host_cache",
276 sizeof (struct nlm_host
), 0, nlm_host_ctor
, nlm_host_dtor
,
277 nlm_kmem_reclaim
, NULL
, NULL
, 0);
279 nlm_vhold_cache
= kmem_cache_create("nlm_vhold_cache",
280 sizeof (struct nlm_vhold
), 0, nlm_vhold_ctor
, nlm_vhold_dtor
,
281 NULL
, NULL
, NULL
, 0);
284 TAILQ_INIT(&nlm_zones_list
);
286 /* initialize sysids bitmap */
287 bzero(nlm_sysid_bmap
, sizeof (nlm_sysid_bmap
));
291 * Reserv the sysid #0, because it's associated
292 * with local locks only. Don't let to allocate
293 * it for remote locks.
295 BT_SET(nlm_sysid_bmap
, 0);
299 nlm_globals_register(struct nlm_globals
*g
)
301 rw_enter(&lm_lck
, RW_WRITER
);
302 TAILQ_INSERT_TAIL(&nlm_zones_list
, g
, nlm_link
);
307 nlm_globals_unregister(struct nlm_globals
*g
)
309 rw_enter(&lm_lck
, RW_WRITER
);
310 TAILQ_REMOVE(&nlm_zones_list
, g
, nlm_link
);
316 nlm_kmem_reclaim(void *cdrarg
)
318 struct nlm_globals
*g
;
320 rw_enter(&lm_lck
, RW_READER
);
321 TAILQ_FOREACH(g
, &nlm_zones_list
, nlm_link
)
322 cv_broadcast(&g
->nlm_gc_sched_cv
);
328 * NLM garbage collector thread (GC).
330 * NLM GC periodically checks whether there're any host objects
331 * that can be cleaned up. It also releases stale vnodes that
332 * live on the server side (under protection of vhold objects).
334 * NLM host objects are cleaned up from GC thread because
335 * operations helping us to determine whether given host has
336 * any locks can be quite expensive and it's not good to call
337 * them every time the very last reference to the host is dropped.
338 * Thus we use "lazy" approach for hosts cleanup.
340 * The work of GC is to release stale vnodes on the server side
341 * and destroy hosts that haven't any locks and any activity for
342 * some time (i.e. idle hosts).
345 nlm_gc(struct nlm_globals
*g
)
347 struct nlm_host
*hostp
;
348 clock_t now
, idle_period
;
350 idle_period
= SEC_TO_TICK(g
->cn_idle_tmo
);
351 mutex_enter(&g
->lock
);
354 * GC thread can be explicitly scheduled from
355 * memory reclamation function.
357 (void) cv_timedwait(&g
->nlm_gc_sched_cv
, &g
->lock
,
358 ddi_get_lbolt() + idle_period
);
361 * NLM is shutting down, time to die.
363 if (g
->run_status
== NLM_ST_STOPPING
)
366 now
= ddi_get_lbolt();
367 DTRACE_PROBE2(gc__start
, struct nlm_globals
*, g
,
371 * Find all obviously unused vholds and destroy them.
373 for (hostp
= avl_first(&g
->nlm_hosts_tree
); hostp
!= NULL
;
374 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
)) {
375 struct nlm_vhold
*nvp
;
377 mutex_enter(&hostp
->nh_lock
);
379 nvp
= TAILQ_FIRST(&hostp
->nh_vholds_list
);
380 while (nvp
!= NULL
) {
381 struct nlm_vhold
*new_nvp
;
383 new_nvp
= TAILQ_NEXT(nvp
, nv_link
);
386 * If these conditions are met, the vhold is
387 * obviously unused and we will destroy it. In
388 * a case either v_filocks and/or v_shrlocks is
389 * non-NULL the vhold might still be unused by
390 * the host, but it is expensive to check that.
391 * We defer such check until the host is idle.
392 * The expensive check is done below without
393 * the global lock held.
395 if (nvp
->nv_refcnt
== 0 &&
396 nvp
->nv_vp
->v_filocks
== NULL
&&
397 nvp
->nv_vp
->v_shrlocks
== NULL
) {
398 nlm_vhold_destroy(hostp
, nvp
);
404 mutex_exit(&hostp
->nh_lock
);
408 * Handle all hosts that are unused at the moment
409 * until we meet one with idle timeout in future.
411 while ((hostp
= TAILQ_FIRST(&g
->nlm_idle_hosts
)) != NULL
) {
414 if (hostp
->nh_idle_timeout
> now
)
418 * Drop global lock while doing expensive work
419 * on this host. We'll re-check any conditions
420 * that might change after retaking the global
423 mutex_exit(&g
->lock
);
424 mutex_enter(&hostp
->nh_lock
);
427 * nlm_globals lock was dropped earlier because
428 * garbage collecting of vholds and checking whether
429 * host has any locks/shares are expensive operations.
431 nlm_host_gc_vholds(hostp
);
432 has_locks
= nlm_host_has_locks(hostp
);
434 mutex_exit(&hostp
->nh_lock
);
435 mutex_enter(&g
->lock
);
438 * While we were doing expensive operations
439 * outside of nlm_globals critical section,
440 * somebody could take the host and remove it
441 * from the idle list. Whether its been
442 * reinserted or not, our information about
443 * the host is outdated, and we should take no
446 if ((hostp
->nh_flags
& NLM_NH_INIDLE
) == 0 ||
447 hostp
->nh_idle_timeout
> now
)
451 * If the host has locks we have to renew the
452 * host's timeout and put it at the end of LRU
456 TAILQ_REMOVE(&g
->nlm_idle_hosts
,
458 hostp
->nh_idle_timeout
= now
+ idle_period
;
459 TAILQ_INSERT_TAIL(&g
->nlm_idle_hosts
,
465 * We're here if all the following conditions hold:
466 * 1) Host hasn't any locks or share reservations
468 * 3) Host wasn't touched by anyone at least for
469 * g->cn_idle_tmo seconds.
471 * So, now we can destroy it.
473 nlm_host_unregister(g
, hostp
);
474 mutex_exit(&g
->lock
);
476 nlm_host_unmonitor(g
, hostp
);
477 nlm_host_destroy(hostp
);
478 mutex_enter(&g
->lock
);
479 if (g
->run_status
== NLM_ST_STOPPING
)
484 DTRACE_PROBE(gc__end
);
487 DTRACE_PROBE1(gc__exit
, struct nlm_globals
*, g
);
489 /* Let others know that GC has died */
490 g
->nlm_gc_thread
= NULL
;
491 mutex_exit(&g
->lock
);
493 cv_broadcast(&g
->nlm_gc_finish_cv
);
498 * Thread reclaim locks/shares acquired by the client side
499 * on the given server represented by hostp.
502 nlm_reclaimer(struct nlm_host
*hostp
)
504 struct nlm_globals
*g
;
506 mutex_enter(&hostp
->nh_lock
);
507 hostp
->nh_reclaimer
= curthread
;
508 mutex_exit(&hostp
->nh_lock
);
510 g
= zone_getspecific(nlm_zone_key
, curzone
);
511 nlm_reclaim_client(g
, hostp
);
513 mutex_enter(&hostp
->nh_lock
);
514 hostp
->nh_flags
&= ~NLM_NH_RECLAIM
;
515 hostp
->nh_reclaimer
= NULL
;
516 cv_broadcast(&hostp
->nh_recl_cv
);
517 mutex_exit(&hostp
->nh_lock
);
520 * Host was explicitly referenced before
521 * nlm_reclaim() was called, release it
524 nlm_host_release(g
, hostp
);
529 * Copy a struct netobj. (see xdr.h)
532 nlm_copy_netobj(struct netobj
*dst
, struct netobj
*src
)
534 dst
->n_len
= src
->n_len
;
535 dst
->n_bytes
= kmem_alloc(src
->n_len
, KM_SLEEP
);
536 bcopy(src
->n_bytes
, dst
->n_bytes
, src
->n_len
);
540 * An NLM specificw replacement for clnt_call().
541 * nlm_clnt_call() is used by all RPC functions generated
542 * from nlm_prot.x specification. The function is aware
543 * about some pitfalls of NLM RPC procedures and has a logic
544 * that handles them properly.
547 nlm_clnt_call(CLIENT
*clnt
, rpcproc_t procnum
, xdrproc_t xdr_args
,
548 caddr_t argsp
, xdrproc_t xdr_result
, caddr_t resultp
, struct timeval wait
)
552 bool_t sig_blocked
= FALSE
;
555 * If NLM RPC procnum is one of the NLM _RES procedures
556 * that are used to reply to asynchronous NLM RPC
557 * (MSG calls), explicitly set RPC timeout to zero.
558 * Client doesn't send a reply to RES procedures, so
559 * we don't need to wait anything.
561 * NOTE: we ignore NLM4_*_RES procnums because they are
562 * equal to NLM_*_RES numbers.
564 if (procnum
>= NLM_TEST_RES
&& procnum
<= NLM_GRANTED_RES
)
565 wait
= nlm_rpctv_zero
;
568 * We need to block signals in case of NLM_CANCEL RPC
569 * in order to prevent interruption of network RPC
572 if (procnum
== NLM_CANCEL
) {
575 sigfillset(&newmask
);
576 sigreplace(&newmask
, &oldmask
);
580 stat
= clnt_call(clnt
, procnum
, xdr_args
,
581 argsp
, xdr_result
, resultp
, wait
);
584 * Restore signal mask back if signals were blocked
587 sigreplace(&oldmask
, (k_sigset_t
*)NULL
);
593 * Suspend NLM client/server in the given zone.
595 * During suspend operation we mark those hosts
596 * that have any locks with NLM_NH_SUSPEND flags,
597 * so that they can be checked later, when resume
601 nlm_suspend_zone(struct nlm_globals
*g
)
603 struct nlm_host
*hostp
;
604 struct nlm_host_list all_hosts
;
607 * Note that while we're doing suspend, GC thread is active
608 * and it can destroy some hosts while we're walking through
609 * the hosts tree. To prevent that and make suspend logic
610 * a bit more simple we put all hosts to local "all_hosts"
611 * list and increment reference counter of each host.
612 * This guaranties that no hosts will be released while
613 * we're doing suspend.
614 * NOTE: reference of each host must be dropped during
617 TAILQ_INIT(&all_hosts
);
618 mutex_enter(&g
->lock
);
619 for (hostp
= avl_first(&g
->nlm_hosts_tree
); hostp
!= NULL
;
620 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
)) {
622 * If host is idle, remove it from idle list and
623 * clear idle flag. That is done to prevent GC
624 * from touching this host.
626 if (hostp
->nh_flags
& NLM_NH_INIDLE
) {
627 TAILQ_REMOVE(&g
->nlm_idle_hosts
, hostp
, nh_link
);
628 hostp
->nh_flags
&= ~NLM_NH_INIDLE
;
632 TAILQ_INSERT_TAIL(&all_hosts
, hostp
, nh_link
);
636 * Now we can walk through all hosts on the system
637 * with zone globals lock released. The fact the
638 * we have taken a reference to each host guaranties
639 * that no hosts can be destroyed during that process.
641 mutex_exit(&g
->lock
);
642 while ((hostp
= TAILQ_FIRST(&all_hosts
)) != NULL
) {
643 mutex_enter(&hostp
->nh_lock
);
644 if (nlm_host_has_locks(hostp
))
645 hostp
->nh_flags
|= NLM_NH_SUSPEND
;
647 mutex_exit(&hostp
->nh_lock
);
648 TAILQ_REMOVE(&all_hosts
, hostp
, nh_link
);
653 * Resume NLM hosts for the given zone.
655 * nlm_resume_zone() is called after hosts were suspended
656 * (see nlm_suspend_zone) and its main purpose to check
657 * whether remote locks owned by hosts are still in consistent
658 * state. If they aren't, resume function tries to reclaim
659 * locks (for client side hosts) and clean locks (for
660 * server side hosts).
663 nlm_resume_zone(struct nlm_globals
*g
)
665 struct nlm_host
*hostp
, *h_next
;
667 mutex_enter(&g
->lock
);
668 hostp
= avl_first(&g
->nlm_hosts_tree
);
671 * In nlm_suspend_zone() the reference counter of each
672 * host was incremented, so we can safely iterate through
673 * all hosts without worrying that any host we touch will
674 * be removed at the moment.
676 while (hostp
!= NULL
) {
681 bool_t resume_failed
= FALSE
;
683 h_next
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
);
684 mutex_exit(&g
->lock
);
686 DTRACE_PROBE1(resume__host
, struct nlm_host
*, hostp
);
689 * Suspend operation marked that the host doesn't
690 * have any locks. Skip it.
692 if (!(hostp
->nh_flags
& NLM_NH_SUSPEND
))
695 error
= nlm_nsm_init(&nsm
, &hostp
->nh_knc
, &hostp
->nh_addr
);
697 NLM_ERR("Resume: Failed to contact to NSM of host %s "
698 "[error=%d]\n", hostp
->nh_name
, error
);
699 resume_failed
= TRUE
;
703 stat
= nlm_nsm_stat(&nsm
, &sm_state
);
704 if (stat
!= RPC_SUCCESS
) {
705 NLM_ERR("Resume: Failed to call SM_STAT operation for "
706 "host %s [stat=%d]\n", hostp
->nh_name
, stat
);
707 resume_failed
= TRUE
;
712 if (sm_state
!= hostp
->nh_state
) {
714 * Current SM state of the host isn't equal
715 * to the one host had when it was suspended.
716 * Probably it was rebooted. Try to reclaim
717 * locks if the host has any on its client side.
718 * Also try to clean up its server side locks
719 * (if the host has any).
721 nlm_host_notify_client(hostp
, sm_state
);
722 nlm_host_notify_server(hostp
, sm_state
);
730 * Resume failed for the given host.
731 * Just clean up all resources it owns.
733 nlm_host_notify_server(hostp
, 0);
734 nlm_client_cancel_all(g
, hostp
);
737 hostp
->nh_flags
&= ~NLM_NH_SUSPEND
;
738 nlm_host_release(g
, hostp
);
740 mutex_enter(&g
->lock
);
743 mutex_exit(&g
->lock
);
747 * NLM functions responsible for operations on NSM handle.
751 * Initialize knetconfig that is used for communication
752 * with local statd via loopback interface.
755 nlm_init_local_knc(struct knetconfig
*knc
)
760 bzero(knc
, sizeof (*knc
));
761 error
= lookupname("/dev/tcp", UIO_SYSSPACE
,
762 FOLLOW
, NULLVPP
, &vp
);
766 knc
->knc_semantics
= NC_TPI_COTS
;
767 knc
->knc_protofmly
= NC_INET
;
768 knc
->knc_proto
= NC_TCP
;
769 knc
->knc_rdev
= vp
->v_rdev
;
777 * Initialize NSM handle that will be used to talk
778 * to local statd via loopback interface.
781 nlm_nsm_init_local(struct nlm_nsm
*nsm
)
784 struct knetconfig knc
;
785 struct sockaddr_in sin
;
788 error
= nlm_init_local_knc(&knc
);
792 bzero(&sin
, sizeof (sin
));
793 sin
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
794 sin
.sin_family
= AF_INET
;
796 nb
.buf
= (char *)&sin
;
797 nb
.len
= nb
.maxlen
= sizeof (sin
);
799 return (nlm_nsm_init(nsm
, &knc
, &nb
));
803 * Initialize NSM handle used for talking to statd
806 nlm_nsm_init(struct nlm_nsm
*nsm
, struct knetconfig
*knc
, struct netbuf
*nb
)
811 bzero(nsm
, sizeof (*nsm
));
813 nlm_copy_netbuf(&nsm
->ns_addr
, nb
);
816 * Try several times to get the port of statd service,
817 * If rpcbind_getaddr returns RPC_PROGNOTREGISTERED,
818 * retry an attempt, but wait for NLM_NSM_RPCBIND_TIMEOUT
821 for (retries
= 0; retries
< NLM_NSM_RPCBIND_RETRIES
; retries
++) {
822 stat
= rpcbind_getaddr(&nsm
->ns_knc
, SM_PROG
,
823 SM_VERS
, &nsm
->ns_addr
);
824 if (stat
!= RPC_SUCCESS
) {
825 if (stat
== RPC_PROGNOTREGISTERED
) {
826 delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT
));
834 if (stat
!= RPC_SUCCESS
) {
835 DTRACE_PROBE2(rpcbind__error
, enum clnt_stat
, stat
,
842 * Create an RPC handle that'll be used for communication with local
843 * statd using the status monitor protocol.
845 error
= clnt_tli_kcreate(&nsm
->ns_knc
, &nsm
->ns_addr
, SM_PROG
, SM_VERS
,
846 0, NLM_RPC_RETRIES
, kcred
, &nsm
->ns_handle
);
851 * Create an RPC handle that'll be used for communication with the
852 * local statd using the address registration protocol.
854 error
= clnt_tli_kcreate(&nsm
->ns_knc
, &nsm
->ns_addr
, NSM_ADDR_PROGRAM
,
855 NSM_ADDR_V1
, 0, NLM_RPC_RETRIES
, kcred
, &nsm
->ns_addr_handle
);
859 sema_init(&nsm
->ns_sem
, 1, NULL
, SEMA_DEFAULT
, NULL
);
863 kmem_free(nsm
->ns_addr
.buf
, nsm
->ns_addr
.maxlen
);
865 CLNT_DESTROY(nsm
->ns_handle
);
871 nlm_nsm_fini(struct nlm_nsm
*nsm
)
873 kmem_free(nsm
->ns_addr
.buf
, nsm
->ns_addr
.maxlen
);
874 CLNT_DESTROY(nsm
->ns_addr_handle
);
875 nsm
->ns_addr_handle
= NULL
;
876 CLNT_DESTROY(nsm
->ns_handle
);
877 nsm
->ns_handle
= NULL
;
878 sema_destroy(&nsm
->ns_sem
);
881 static enum clnt_stat
882 nlm_nsm_simu_crash(struct nlm_nsm
*nsm
)
886 sema_p(&nsm
->ns_sem
);
887 nlm_nsm_clnt_init(nsm
->ns_handle
, nsm
);
888 stat
= sm_simu_crash_1(NULL
, NULL
, nsm
->ns_handle
);
889 sema_v(&nsm
->ns_sem
);
894 static enum clnt_stat
895 nlm_nsm_stat(struct nlm_nsm
*nsm
, int32_t *out_stat
)
898 struct sm_stat_res res
;
901 args
.mon_name
= uts_nodename();
902 bzero(&res
, sizeof (res
));
904 sema_p(&nsm
->ns_sem
);
905 nlm_nsm_clnt_init(nsm
->ns_handle
, nsm
);
906 stat
= sm_stat_1(&args
, &res
, nsm
->ns_handle
);
907 sema_v(&nsm
->ns_sem
);
909 if (stat
== RPC_SUCCESS
)
910 *out_stat
= res
.state
;
915 static enum clnt_stat
916 nlm_nsm_mon(struct nlm_nsm
*nsm
, char *hostname
, uint16_t priv
)
919 struct sm_stat_res res
;
922 bzero(&args
, sizeof (args
));
923 bzero(&res
, sizeof (res
));
925 args
.mon_id
.mon_name
= hostname
;
926 args
.mon_id
.my_id
.my_name
= uts_nodename();
927 args
.mon_id
.my_id
.my_prog
= NLM_PROG
;
928 args
.mon_id
.my_id
.my_vers
= NLM_SM
;
929 args
.mon_id
.my_id
.my_proc
= NLM_SM_NOTIFY1
;
930 bcopy(&priv
, args
.priv
, sizeof (priv
));
932 sema_p(&nsm
->ns_sem
);
933 nlm_nsm_clnt_init(nsm
->ns_handle
, nsm
);
934 stat
= sm_mon_1(&args
, &res
, nsm
->ns_handle
);
935 sema_v(&nsm
->ns_sem
);
940 static enum clnt_stat
941 nlm_nsm_unmon(struct nlm_nsm
*nsm
, char *hostname
)
947 bzero(&args
, sizeof (args
));
948 bzero(&res
, sizeof (res
));
950 args
.mon_name
= hostname
;
951 args
.my_id
.my_name
= uts_nodename();
952 args
.my_id
.my_prog
= NLM_PROG
;
953 args
.my_id
.my_vers
= NLM_SM
;
954 args
.my_id
.my_proc
= NLM_SM_NOTIFY1
;
956 sema_p(&nsm
->ns_sem
);
957 nlm_nsm_clnt_init(nsm
->ns_handle
, nsm
);
958 stat
= sm_unmon_1(&args
, &res
, nsm
->ns_handle
);
959 sema_v(&nsm
->ns_sem
);
964 static enum clnt_stat
965 nlm_nsmaddr_reg(struct nlm_nsm
*nsm
, char *name
, int family
, netobj
*address
)
967 struct reg1args args
= { 0 };
968 struct reg1res res
= { 0 };
971 args
.family
= family
;
973 args
.address
= *address
;
975 sema_p(&nsm
->ns_sem
);
976 nlm_nsm_clnt_init(nsm
->ns_addr_handle
, nsm
);
977 stat
= nsmaddrproc1_reg_1(&args
, &res
, nsm
->ns_addr_handle
);
978 sema_v(&nsm
->ns_sem
);
984 * Get NLM vhold object corresponding to vnode "vp".
985 * If no such object was found, create a new one.
987 * The purpose of this function is to associate vhold
988 * object with given vnode, so that:
989 * 1) vnode is hold (VN_HOLD) while vhold object is alive.
990 * 2) host has a track of all vnodes it touched by lock
991 * or share operations. These vnodes are accessible
992 * via collection of vhold objects.
995 nlm_vhold_get(struct nlm_host
*hostp
, vnode_t
*vp
)
997 struct nlm_vhold
*nvp
, *new_nvp
= NULL
;
999 mutex_enter(&hostp
->nh_lock
);
1000 nvp
= nlm_vhold_find_locked(hostp
, vp
);
1004 /* nlm_vhold wasn't found, then create a new one */
1005 mutex_exit(&hostp
->nh_lock
);
1006 new_nvp
= kmem_cache_alloc(nlm_vhold_cache
, KM_SLEEP
);
1009 * Check if another thread has already
1010 * created the same nlm_vhold.
1012 mutex_enter(&hostp
->nh_lock
);
1013 nvp
= nlm_vhold_find_locked(hostp
, vp
);
1018 TAILQ_INIT(&nvp
->nv_slreqs
);
1021 VN_HOLD(nvp
->nv_vp
);
1023 VERIFY(mod_hash_insert(hostp
->nh_vholds_by_vp
,
1024 (mod_hash_key_t
)vp
, (mod_hash_val_t
)nvp
) == 0);
1025 TAILQ_INSERT_TAIL(&hostp
->nh_vholds_list
, nvp
, nv_link
);
1029 mutex_exit(&hostp
->nh_lock
);
1030 if (new_nvp
!= NULL
)
1031 kmem_cache_free(nlm_vhold_cache
, new_nvp
);
1037 * Drop a reference to vhold object nvp.
1040 nlm_vhold_release(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
)
1045 mutex_enter(&hostp
->nh_lock
);
1046 ASSERT(nvp
->nv_refcnt
> 0);
1050 * If these conditions are met, the vhold is obviously unused and we
1051 * will destroy it. In a case either v_filocks and/or v_shrlocks is
1052 * non-NULL the vhold might still be unused by the host, but it is
1053 * expensive to check that. We defer such check until the host is
1054 * idle. The expensive check is done in the NLM garbage collector.
1056 if (nvp
->nv_refcnt
== 0 &&
1057 nvp
->nv_vp
->v_filocks
== NULL
&&
1058 nvp
->nv_vp
->v_shrlocks
== NULL
) {
1059 nlm_vhold_destroy(hostp
, nvp
);
1062 mutex_exit(&hostp
->nh_lock
);
1066 * Clean all locks and share reservations on the
1067 * given vhold object that were acquired by the
1071 nlm_vhold_clean(struct nlm_vhold
*nvp
, int sysid
)
1073 cleanlocks(nvp
->nv_vp
, IGN_PID
, sysid
);
1074 cleanshares_by_sysid(nvp
->nv_vp
, sysid
);
1078 nlm_vhold_destroy(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
)
1080 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1082 ASSERT(nvp
->nv_refcnt
== 0);
1083 ASSERT(TAILQ_EMPTY(&nvp
->nv_slreqs
));
1085 VERIFY(mod_hash_remove(hostp
->nh_vholds_by_vp
,
1086 (mod_hash_key_t
)nvp
->nv_vp
,
1087 (mod_hash_val_t
)&nvp
) == 0);
1089 TAILQ_REMOVE(&hostp
->nh_vholds_list
, nvp
, nv_link
);
1090 VN_RELE(nvp
->nv_vp
);
1093 kmem_cache_free(nlm_vhold_cache
, nvp
);
1097 * Return TRUE if the given vhold is busy.
1098 * Vhold object is considered to be "busy" when
1099 * all the following conditions hold:
1100 * 1) No one uses it at the moment;
1101 * 2) It hasn't any locks;
1102 * 3) It hasn't any share reservations;
1105 nlm_vhold_busy(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
)
1110 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1112 if (nvp
->nv_refcnt
> 0)
1116 sysid
= hostp
->nh_sysid
;
1117 if (flk_has_remote_locks_for_sysid(vp
, sysid
) ||
1118 shr_has_remote_shares(vp
, sysid
))
1126 nlm_vhold_ctor(void *datap
, void *cdrarg
, int kmflags
)
1128 struct nlm_vhold
*nvp
= (struct nlm_vhold
*)datap
;
1130 bzero(nvp
, sizeof (*nvp
));
1136 nlm_vhold_dtor(void *datap
, void *cdrarg
)
1138 struct nlm_vhold
*nvp
= (struct nlm_vhold
*)datap
;
1140 ASSERT(nvp
->nv_refcnt
== 0);
1141 ASSERT(TAILQ_EMPTY(&nvp
->nv_slreqs
));
1142 ASSERT(nvp
->nv_vp
== NULL
);
1146 nlm_vhold_find_locked(struct nlm_host
*hostp
, const vnode_t
*vp
)
1148 struct nlm_vhold
*nvp
= NULL
;
1150 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1151 (void) mod_hash_find(hostp
->nh_vholds_by_vp
,
1153 (mod_hash_val_t
)&nvp
);
1162 * NLM host functions
1165 nlm_copy_netbuf(struct netbuf
*dst
, struct netbuf
*src
)
1167 ASSERT(src
->len
<= src
->maxlen
);
1169 dst
->maxlen
= src
->maxlen
;
1170 dst
->len
= src
->len
;
1171 dst
->buf
= kmem_zalloc(src
->maxlen
, KM_SLEEP
);
1172 bcopy(src
->buf
, dst
->buf
, src
->len
);
1177 nlm_host_ctor(void *datap
, void *cdrarg
, int kmflags
)
1179 struct nlm_host
*hostp
= (struct nlm_host
*)datap
;
1181 bzero(hostp
, sizeof (*hostp
));
1187 nlm_host_dtor(void *datap
, void *cdrarg
)
1189 struct nlm_host
*hostp
= (struct nlm_host
*)datap
;
1190 ASSERT(hostp
->nh_refs
== 0);
1194 nlm_host_unregister(struct nlm_globals
*g
, struct nlm_host
*hostp
)
1196 ASSERT(hostp
->nh_refs
== 0);
1197 ASSERT(hostp
->nh_flags
& NLM_NH_INIDLE
);
1199 avl_remove(&g
->nlm_hosts_tree
, hostp
);
1200 VERIFY(mod_hash_remove(g
->nlm_hosts_hash
,
1201 (mod_hash_key_t
)(uintptr_t)hostp
->nh_sysid
,
1202 (mod_hash_val_t
)&hostp
) == 0);
1203 TAILQ_REMOVE(&g
->nlm_idle_hosts
, hostp
, nh_link
);
1204 hostp
->nh_flags
&= ~NLM_NH_INIDLE
;
1208 * Free resources used by a host. This is called after the reference
1209 * count has reached zero so it doesn't need to worry about locks.
1212 nlm_host_destroy(struct nlm_host
*hostp
)
1214 ASSERT(hostp
->nh_name
!= NULL
);
1215 ASSERT(hostp
->nh_netid
!= NULL
);
1216 ASSERT(TAILQ_EMPTY(&hostp
->nh_vholds_list
));
1218 strfree(hostp
->nh_name
);
1219 strfree(hostp
->nh_netid
);
1220 kmem_free(hostp
->nh_addr
.buf
, hostp
->nh_addr
.maxlen
);
1222 if (hostp
->nh_sysid
!= LM_NOSYSID
)
1223 nlm_sysid_free(hostp
->nh_sysid
);
1225 nlm_rpc_cache_destroy(hostp
);
1227 ASSERT(TAILQ_EMPTY(&hostp
->nh_vholds_list
));
1228 mod_hash_destroy_ptrhash(hostp
->nh_vholds_by_vp
);
1230 mutex_destroy(&hostp
->nh_lock
);
1231 cv_destroy(&hostp
->nh_rpcb_cv
);
1232 cv_destroy(&hostp
->nh_recl_cv
);
1234 kmem_cache_free(nlm_hosts_cache
, hostp
);
1238 * Cleanup SERVER-side state after a client restarts,
1239 * or becomes unresponsive, or whatever.
1241 * We unlock any active locks owned by the host.
1242 * When rpc.lockd is shutting down,
1243 * this function is called with newstate set to zero
1244 * which allows us to cancel any pending async locks
1245 * and clear the locking state.
1247 * When "state" is 0, we don't update host's state,
1248 * but cleanup all remote locks on the host.
1249 * It's useful to call this function for resources
1253 nlm_host_notify_server(struct nlm_host
*hostp
, int32_t state
)
1255 struct nlm_vhold
*nvp
;
1256 struct nlm_slreq
*slr
;
1257 struct nlm_slreq_list slreqs2free
;
1259 TAILQ_INIT(&slreqs2free
);
1260 mutex_enter(&hostp
->nh_lock
);
1262 hostp
->nh_state
= state
;
1264 TAILQ_FOREACH(nvp
, &hostp
->nh_vholds_list
, nv_link
) {
1266 /* cleanup sleeping requests at first */
1267 while ((slr
= TAILQ_FIRST(&nvp
->nv_slreqs
)) != NULL
) {
1268 TAILQ_REMOVE(&nvp
->nv_slreqs
, slr
, nsr_link
);
1271 * Instead of freeing cancelled sleeping request
1272 * here, we add it to the linked list created
1273 * on the stack in order to do all frees outside
1274 * the critical section.
1276 TAILQ_INSERT_TAIL(&slreqs2free
, slr
, nsr_link
);
1280 mutex_exit(&hostp
->nh_lock
);
1282 nlm_vhold_clean(nvp
, hostp
->nh_sysid
);
1284 mutex_enter(&hostp
->nh_lock
);
1288 mutex_exit(&hostp
->nh_lock
);
1289 while ((slr
= TAILQ_FIRST(&slreqs2free
)) != NULL
) {
1290 TAILQ_REMOVE(&slreqs2free
, slr
, nsr_link
);
1291 kmem_free(slr
, sizeof (*slr
));
1296 * Cleanup CLIENT-side state after a server restarts,
1297 * or becomes unresponsive, or whatever.
1299 * This is called by the local NFS statd when we receive a
1300 * host state change notification. (also nlm_svc_stopping)
1302 * Deal with a server restart. If we are stopping the
1303 * NLM service, we'll have newstate == 0, and will just
1304 * cancel all our client-side lock requests. Otherwise,
1305 * start the "recovery" process to reclaim any locks
1306 * we hold on this server.
1309 nlm_host_notify_client(struct nlm_host
*hostp
, int32_t state
)
1311 mutex_enter(&hostp
->nh_lock
);
1312 hostp
->nh_state
= state
;
1313 if (hostp
->nh_flags
& NLM_NH_RECLAIM
) {
1315 * Either host's state is up to date or
1316 * host is already in recovery.
1318 mutex_exit(&hostp
->nh_lock
);
1322 hostp
->nh_flags
|= NLM_NH_RECLAIM
;
1325 * Host will be released by the recovery thread,
1326 * thus we need to increment refcount.
1329 mutex_exit(&hostp
->nh_lock
);
1331 (void) zthread_create(NULL
, 0, nlm_reclaimer
,
1332 hostp
, 0, minclsyspri
);
1336 * The function is called when NLM client detects that
1337 * server has entered in grace period and client needs
1338 * to wait until reclamation process (if any) does
1342 nlm_host_wait_grace(struct nlm_host
*hostp
)
1344 struct nlm_globals
*g
;
1347 g
= zone_getspecific(nlm_zone_key
, curzone
);
1348 mutex_enter(&hostp
->nh_lock
);
1353 rc
= cv_timedwait_sig(&hostp
->nh_recl_cv
,
1354 &hostp
->nh_lock
, ddi_get_lbolt() +
1355 SEC_TO_TICK(g
->retrans_tmo
));
1361 } while (hostp
->nh_flags
& NLM_NH_RECLAIM
);
1363 mutex_exit(&hostp
->nh_lock
);
1368 * Create a new NLM host.
1370 * NOTE: The in-kernel RPC (kRPC) subsystem uses TLI/XTI,
1371 * which needs both a knetconfig and an address when creating
1372 * endpoints. Thus host object stores both knetconfig and
1375 static struct nlm_host
*
1376 nlm_host_create(char *name
, const char *netid
,
1377 struct knetconfig
*knc
, struct netbuf
*naddr
)
1379 struct nlm_host
*host
;
1381 host
= kmem_cache_alloc(nlm_hosts_cache
, KM_SLEEP
);
1383 mutex_init(&host
->nh_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
1384 cv_init(&host
->nh_rpcb_cv
, NULL
, CV_DEFAULT
, NULL
);
1385 cv_init(&host
->nh_recl_cv
, NULL
, CV_DEFAULT
, NULL
);
1387 host
->nh_sysid
= LM_NOSYSID
;
1389 host
->nh_name
= strdup(name
);
1390 host
->nh_netid
= strdup(netid
);
1391 host
->nh_knc
= *knc
;
1392 nlm_copy_netbuf(&host
->nh_addr
, naddr
);
1395 host
->nh_rpcb_state
= NRPCB_NEED_UPDATE
;
1398 host
->nh_vholds_by_vp
= mod_hash_create_ptrhash("nlm vholds hash",
1399 32, mod_hash_null_valdtor
, sizeof (vnode_t
));
1401 TAILQ_INIT(&host
->nh_vholds_list
);
1402 TAILQ_INIT(&host
->nh_rpchc
);
1408 * Cancel all client side sleeping locks owned by given host.
1411 nlm_host_cancel_slocks(struct nlm_globals
*g
, struct nlm_host
*hostp
)
1413 struct nlm_slock
*nslp
;
1415 mutex_enter(&g
->lock
);
1416 TAILQ_FOREACH(nslp
, &g
->nlm_slocks
, nsl_link
) {
1417 if (nslp
->nsl_host
== hostp
) {
1418 nslp
->nsl_state
= NLM_SL_CANCELLED
;
1419 cv_broadcast(&nslp
->nsl_cond
);
1423 mutex_exit(&g
->lock
);
1427 * Garbage collect stale vhold objects.
1429 * In other words check whether vnodes that are
1430 * held by vhold objects still have any locks
1431 * or shares or still in use. If they aren't,
1432 * just destroy them.
1435 nlm_host_gc_vholds(struct nlm_host
*hostp
)
1437 struct nlm_vhold
*nvp
;
1439 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1441 nvp
= TAILQ_FIRST(&hostp
->nh_vholds_list
);
1442 while (nvp
!= NULL
) {
1443 struct nlm_vhold
*nvp_tmp
;
1445 if (nlm_vhold_busy(hostp
, nvp
)) {
1446 nvp
= TAILQ_NEXT(nvp
, nv_link
);
1450 nvp_tmp
= TAILQ_NEXT(nvp
, nv_link
);
1451 nlm_vhold_destroy(hostp
, nvp
);
1457 * Check whether the given host has any
1458 * server side locks or share reservations.
1461 nlm_host_has_srv_locks(struct nlm_host
*hostp
)
1464 * It's cheap and simple: if server has
1465 * any locks/shares there must be vhold
1466 * object storing the affected vnode.
1468 * NOTE: We don't need to check sleeping
1469 * locks on the server side, because if
1470 * server side sleeping lock is alive,
1471 * there must be a vhold object corresponding
1474 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1475 if (!TAILQ_EMPTY(&hostp
->nh_vholds_list
))
1482 * Check whether the given host has any client side
1483 * locks or share reservations.
1486 nlm_host_has_cli_locks(struct nlm_host
*hostp
)
1488 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
1491 * XXX: It's not the way I'd like to do the check,
1492 * because flk_sysid_has_locks() can be very
1493 * expensive by design. Unfortunatelly it iterates
1494 * through all locks on the system, doesn't matter
1495 * were they made on remote system via NLM or
1496 * on local system via reclock. To understand the
1497 * problem, consider that there're dozens of thousands
1498 * of locks that are made on some ZFS dataset. And there's
1499 * another dataset shared by NFS where NLM client had locks
1500 * some time ago, but doesn't have them now.
1501 * In this case flk_sysid_has_locks() will iterate
1502 * thrught dozens of thousands locks until it returns us
1504 * Oh, I hope that in shiny future somebody will make
1505 * local lock manager (os/flock.c) better, so that
1506 * it'd be more friedly to remote locks and
1507 * flk_sysid_has_locks() wouldn't be so expensive.
1509 if (flk_sysid_has_locks(hostp
->nh_sysid
|
1510 LM_SYSID_CLIENT
, FLK_QUERY_ACTIVE
))
1514 * Check whether host has any share reservations
1515 * registered on the client side.
1517 if (hostp
->nh_shrlist
!= NULL
)
1524 * Determine whether the given host owns any
1525 * locks or share reservations.
1528 nlm_host_has_locks(struct nlm_host
*hostp
)
1530 if (nlm_host_has_srv_locks(hostp
))
1533 return (nlm_host_has_cli_locks(hostp
));
1537 * This function compares only addresses of two netbufs
1538 * that belong to NC_TCP[6] or NC_UDP[6] protofamily.
1539 * Port part of netbuf is ignored.
1542 * -1: nb1's address is "smaller" than nb2's
1543 * 0: addresses are equal
1544 * 1: nb1's address is "greater" than nb2's
1547 nlm_netbuf_addrs_cmp(struct netbuf
*nb1
, struct netbuf
*nb2
)
1551 struct sockaddr_in sin
;
1552 struct sockaddr_in6 sin6
;
1556 /* LINTED E_BAD_PTR_CAST_ALIGN */
1557 na1
= (union nlm_addr
*)nb1
->buf
;
1558 /* LINTED E_BAD_PTR_CAST_ALIGN */
1559 na2
= (union nlm_addr
*)nb2
->buf
;
1561 if (na1
->sa
.sa_family
< na2
->sa
.sa_family
)
1563 if (na1
->sa
.sa_family
> na2
->sa
.sa_family
)
1566 switch (na1
->sa
.sa_family
) {
1568 res
= memcmp(&na1
->sin
.sin_addr
, &na2
->sin
.sin_addr
,
1569 sizeof (na1
->sin
.sin_addr
));
1572 res
= memcmp(&na1
->sin6
.sin6_addr
, &na2
->sin6
.sin6_addr
,
1573 sizeof (na1
->sin6
.sin6_addr
));
1584 * Compare two nlm hosts.
1586 * -1: host1 is "smaller" than host2
1587 * 0: host1 is equal to host2
1588 * 1: host1 is "greater" than host2
1591 nlm_host_cmp(const void *p1
, const void *p2
)
1593 struct nlm_host
*h1
= (struct nlm_host
*)p1
;
1594 struct nlm_host
*h2
= (struct nlm_host
*)p2
;
1597 res
= strcmp(h1
->nh_netid
, h2
->nh_netid
);
1601 res
= nlm_netbuf_addrs_cmp(&h1
->nh_addr
, &h2
->nh_addr
);
1606 * Find the host specified by... (see below)
1607 * If found, increment the ref count.
1609 static struct nlm_host
*
1610 nlm_host_find_locked(struct nlm_globals
*g
, const char *netid
,
1611 struct netbuf
*naddr
, avl_index_t
*wherep
)
1613 struct nlm_host
*hostp
, key
;
1616 ASSERT(MUTEX_HELD(&g
->lock
));
1618 key
.nh_netid
= (char *)netid
;
1619 key
.nh_addr
.buf
= naddr
->buf
;
1620 key
.nh_addr
.len
= naddr
->len
;
1621 key
.nh_addr
.maxlen
= naddr
->maxlen
;
1623 hostp
= avl_find(&g
->nlm_hosts_tree
, &key
, &pos
);
1625 if (hostp
!= NULL
) {
1627 * Host is inuse now. Remove it from idle
1628 * hosts list if needed.
1630 if (hostp
->nh_flags
& NLM_NH_INIDLE
) {
1631 TAILQ_REMOVE(&g
->nlm_idle_hosts
, hostp
, nh_link
);
1632 hostp
->nh_flags
&= ~NLM_NH_INIDLE
;
1644 * Find NLM host for the given name and address.
1647 nlm_host_find(struct nlm_globals
*g
, const char *netid
,
1648 struct netbuf
*addr
)
1650 struct nlm_host
*hostp
= NULL
;
1652 mutex_enter(&g
->lock
);
1653 if (g
->run_status
!= NLM_ST_UP
)
1656 hostp
= nlm_host_find_locked(g
, netid
, addr
, NULL
);
1659 mutex_exit(&g
->lock
);
1665 * Find or create an NLM host for the given name and address.
1667 * The remote host is determined by all of: name, netid, address.
1668 * Note that the netid is whatever nlm_svc_add_ep() gave to
1669 * svc_tli_kcreate() for the service binding. If any of these
1670 * are different, allocate a new host (new sysid).
1673 nlm_host_findcreate(struct nlm_globals
*g
, char *name
,
1674 const char *netid
, struct netbuf
*addr
)
1677 struct nlm_host
*host
, *newhost
= NULL
;
1678 struct knetconfig knc
;
1681 mutex_enter(&g
->lock
);
1682 if (g
->run_status
!= NLM_ST_UP
) {
1683 mutex_exit(&g
->lock
);
1687 host
= nlm_host_find_locked(g
, netid
, addr
, NULL
);
1688 mutex_exit(&g
->lock
);
1692 err
= nlm_knc_from_netid(netid
, &knc
);
1696 * Do allocations (etc.) outside of mutex,
1697 * and then check again before inserting.
1699 newhost
= nlm_host_create(name
, netid
, &knc
, addr
);
1700 newhost
->nh_sysid
= nlm_sysid_alloc();
1701 if (newhost
->nh_sysid
== LM_NOSYSID
)
1704 mutex_enter(&g
->lock
);
1705 host
= nlm_host_find_locked(g
, netid
, addr
, &where
);
1711 * Insert host to the hosts AVL tree that is
1712 * used to lookup by <netid, address> pair.
1714 avl_insert(&g
->nlm_hosts_tree
, host
, where
);
1717 * Insert host to the hosts hash table that is
1718 * used to lookup host by sysid.
1720 VERIFY(mod_hash_insert(g
->nlm_hosts_hash
,
1721 (mod_hash_key_t
)(uintptr_t)host
->nh_sysid
,
1722 (mod_hash_val_t
)host
) == 0);
1725 mutex_exit(&g
->lock
);
1728 if (newhost
!= NULL
) {
1730 * We do not need the preallocated nlm_host
1731 * so decrement the reference counter
1735 nlm_host_destroy(newhost
);
1742 * Find the NLM host that matches the value of 'sysid'.
1743 * If found, return it with a new ref,
1747 nlm_host_find_by_sysid(struct nlm_globals
*g
, sysid_t sysid
)
1749 struct nlm_host
*hostp
= NULL
;
1751 mutex_enter(&g
->lock
);
1752 if (g
->run_status
!= NLM_ST_UP
)
1755 (void) mod_hash_find(g
->nlm_hosts_hash
,
1756 (mod_hash_key_t
)(uintptr_t)sysid
,
1757 (mod_hash_val_t
)&hostp
);
1763 * Host is inuse now. Remove it
1764 * from idle hosts list if needed.
1766 if (hostp
->nh_flags
& NLM_NH_INIDLE
) {
1767 TAILQ_REMOVE(&g
->nlm_idle_hosts
, hostp
, nh_link
);
1768 hostp
->nh_flags
&= ~NLM_NH_INIDLE
;
1774 mutex_exit(&g
->lock
);
1779 * Release the given host.
1780 * I.e. drop a reference that was taken earlier by one of
1781 * the following functions: nlm_host_findcreate(), nlm_host_find(),
1782 * nlm_host_find_by_sysid().
1784 * When the very last reference is dropped, host is moved to
1785 * so-called "idle state". All hosts that are in idle state
1786 * have an idle timeout. If timeout is expired, GC thread
1787 * checks whether hosts have any locks and if they heven't
1788 * any, it removes them.
1789 * NOTE: only unused hosts can be in idle state.
1792 nlm_host_release_locked(struct nlm_globals
*g
, struct nlm_host
*hostp
)
1797 ASSERT(MUTEX_HELD(&g
->lock
));
1798 ASSERT(hostp
->nh_refs
> 0);
1801 if (hostp
->nh_refs
!= 0)
1805 * The very last reference to the host was dropped,
1806 * thus host is unused now. Set its idle timeout
1807 * and move it to the idle hosts LRU list.
1809 hostp
->nh_idle_timeout
= ddi_get_lbolt() +
1810 SEC_TO_TICK(g
->cn_idle_tmo
);
1812 ASSERT((hostp
->nh_flags
& NLM_NH_INIDLE
) == 0);
1813 TAILQ_INSERT_TAIL(&g
->nlm_idle_hosts
, hostp
, nh_link
);
1814 hostp
->nh_flags
|= NLM_NH_INIDLE
;
1818 nlm_host_release(struct nlm_globals
*g
, struct nlm_host
*hostp
)
1823 mutex_enter(&g
->lock
);
1824 nlm_host_release_locked(g
, hostp
);
1825 mutex_exit(&g
->lock
);
1829 * Unregister this NLM host (NFS client) with the local statd
1830 * due to idleness (no locks held for a while).
1833 nlm_host_unmonitor(struct nlm_globals
*g
, struct nlm_host
*host
)
1835 enum clnt_stat stat
;
1837 VERIFY(host
->nh_refs
== 0);
1838 if (!(host
->nh_flags
& NLM_NH_MONITORED
))
1841 host
->nh_flags
&= ~NLM_NH_MONITORED
;
1842 stat
= nlm_nsm_unmon(&g
->nlm_nsm
, host
->nh_name
);
1843 if (stat
!= RPC_SUCCESS
) {
1844 NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat
);
1850 * Ask the local NFS statd to begin monitoring this host.
1851 * It will call us back when that host restarts, using the
1852 * prog,vers,proc specified below, i.e. NLM_SM_NOTIFY1,
1853 * which is handled in nlm_do_notify1().
1856 nlm_host_monitor(struct nlm_globals
*g
, struct nlm_host
*host
, int state
)
1860 enum clnt_stat stat
;
1862 if (state
!= 0 && host
->nh_state
== 0) {
1864 * This is the first time we have seen an NSM state
1865 * Value for this host. We record it here to help
1866 * detect host reboots.
1868 host
->nh_state
= state
;
1871 mutex_enter(&host
->nh_lock
);
1872 if (host
->nh_flags
& NLM_NH_MONITORED
) {
1873 mutex_exit(&host
->nh_lock
);
1877 host
->nh_flags
|= NLM_NH_MONITORED
;
1878 mutex_exit(&host
->nh_lock
);
1881 * Before we begin monitoring the host register the network address
1882 * associated with this hostname.
1884 nlm_netbuf_to_netobj(&host
->nh_addr
, &family
, &obj
);
1885 stat
= nlm_nsmaddr_reg(&g
->nlm_nsm
, host
->nh_name
, family
, &obj
);
1886 if (stat
!= RPC_SUCCESS
) {
1887 NLM_WARN("Failed to register address, stat=%d\n", stat
);
1888 mutex_enter(&g
->lock
);
1889 host
->nh_flags
&= ~NLM_NH_MONITORED
;
1890 mutex_exit(&g
->lock
);
1896 * Tell statd how to call us with status updates for
1897 * this host. Updates arrive via nlm_do_notify1().
1899 * We put our assigned system ID value in the priv field to
1900 * make it simpler to find the host if we are notified of a
1903 stat
= nlm_nsm_mon(&g
->nlm_nsm
, host
->nh_name
, host
->nh_sysid
);
1904 if (stat
!= RPC_SUCCESS
) {
1905 NLM_WARN("Failed to contact local NSM, stat=%d\n", stat
);
1906 mutex_enter(&g
->lock
);
1907 host
->nh_flags
&= ~NLM_NH_MONITORED
;
1908 mutex_exit(&g
->lock
);
1915 nlm_host_get_state(struct nlm_host
*hostp
)
1918 return (hostp
->nh_state
);
1922 * NLM client/server sleeping locks
1926 * Register client side sleeping lock.
1928 * Our client code calls this to keep information
1929 * about sleeping lock somewhere. When it receives
1930 * grant callback from server or when it just
1931 * needs to remove all sleeping locks from vnode,
1932 * it uses this information for remove/apply lock
1937 struct nlm_globals
*g
,
1938 struct nlm_host
*host
,
1939 struct nlm4_lock
*lock
,
1942 struct nlm_slock
*nslp
;
1944 nslp
= kmem_zalloc(sizeof (*nslp
), KM_SLEEP
);
1945 cv_init(&nslp
->nsl_cond
, NULL
, CV_DEFAULT
, NULL
);
1946 nslp
->nsl_lock
= *lock
;
1947 nlm_copy_netobj(&nslp
->nsl_fh
, &nslp
->nsl_lock
.fh
);
1948 nslp
->nsl_state
= NLM_SL_BLOCKED
;
1949 nslp
->nsl_host
= host
;
1952 mutex_enter(&g
->lock
);
1953 TAILQ_INSERT_TAIL(&g
->nlm_slocks
, nslp
, nsl_link
);
1954 mutex_exit(&g
->lock
);
1960 * Remove this lock from the wait list and destroy it.
1963 nlm_slock_unregister(struct nlm_globals
*g
, struct nlm_slock
*nslp
)
1965 mutex_enter(&g
->lock
);
1966 TAILQ_REMOVE(&g
->nlm_slocks
, nslp
, nsl_link
);
1967 mutex_exit(&g
->lock
);
1969 kmem_free(nslp
->nsl_fh
.n_bytes
, nslp
->nsl_fh
.n_len
);
1970 cv_destroy(&nslp
->nsl_cond
);
1971 kmem_free(nslp
, sizeof (*nslp
));
1975 * Wait for a granted callback or cancellation event
1976 * for a sleeping lock.
1978 * If a signal interrupted the wait or if the lock
1979 * was cancelled, return EINTR - the caller must arrange to send
1980 * a cancellation to the server.
1982 * If timeout occurred, return ETIMEDOUT - the caller must
1983 * resend the lock request to the server.
1985 * On success return 0.
1988 nlm_slock_wait(struct nlm_globals
*g
,
1989 struct nlm_slock
*nslp
, uint_t timeo_secs
)
1991 clock_t timeo_ticks
;
1995 * If the granted message arrived before we got here,
1996 * nslp->nsl_state will be NLM_SL_GRANTED - in that case don't sleep.
1999 timeo_ticks
= ddi_get_lbolt() + SEC_TO_TICK(timeo_secs
);
2001 mutex_enter(&g
->lock
);
2002 while (nslp
->nsl_state
== NLM_SL_BLOCKED
&& cv_res
> 0) {
2003 cv_res
= cv_timedwait_sig(&nslp
->nsl_cond
,
2004 &g
->lock
, timeo_ticks
);
2008 * No matter why we wake up, if the lock was
2009 * cancelled, let the function caller to know
2010 * about it by returning EINTR.
2012 if (nslp
->nsl_state
== NLM_SL_CANCELLED
) {
2018 /* We were woken up either by timeout or by interrupt */
2019 error
= (cv_res
< 0) ? ETIMEDOUT
: EINTR
;
2022 * The granted message may arrive after the
2023 * interrupt/timeout but before we manage to lock the
2024 * mutex. Detect this by examining nslp.
2026 if (nslp
->nsl_state
== NLM_SL_GRANTED
)
2028 } else { /* Awaken via cv_signal()/cv_broadcast() or didn't block */
2030 VERIFY(nslp
->nsl_state
== NLM_SL_GRANTED
);
2034 mutex_exit(&g
->lock
);
2039 * Mark client side sleeping lock as granted
2040 * and wake up a process blocked on the lock.
2041 * Called from server side NLM_GRANT handler.
2043 * If sleeping lock is found return 0, otherwise
2047 nlm_slock_grant(struct nlm_globals
*g
,
2048 struct nlm_host
*hostp
, struct nlm4_lock
*alock
)
2050 struct nlm_slock
*nslp
;
2053 mutex_enter(&g
->lock
);
2054 TAILQ_FOREACH(nslp
, &g
->nlm_slocks
, nsl_link
) {
2055 if ((nslp
->nsl_state
!= NLM_SL_BLOCKED
) ||
2056 (nslp
->nsl_host
!= hostp
))
2059 if (alock
->svid
== nslp
->nsl_lock
.svid
&&
2060 alock
->l_offset
== nslp
->nsl_lock
.l_offset
&&
2061 alock
->l_len
== nslp
->nsl_lock
.l_len
&&
2062 alock
->fh
.n_len
== nslp
->nsl_lock
.fh
.n_len
&&
2063 bcmp(alock
->fh
.n_bytes
, nslp
->nsl_lock
.fh
.n_bytes
,
2064 nslp
->nsl_lock
.fh
.n_len
) == 0) {
2065 nslp
->nsl_state
= NLM_SL_GRANTED
;
2066 cv_broadcast(&nslp
->nsl_cond
);
2072 mutex_exit(&g
->lock
);
2077 * Register sleeping lock request corresponding to
2078 * flp on the given vhold object.
2079 * On success function returns 0, otherwise (if
2080 * lock request with the same flp is already
2081 * registered) function returns EEXIST.
2084 nlm_slreq_register(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
,
2085 struct flock64
*flp
)
2087 struct nlm_slreq
*slr
, *new_slr
= NULL
;
2090 mutex_enter(&hostp
->nh_lock
);
2091 slr
= nlm_slreq_find_locked(hostp
, nvp
, flp
);
2095 mutex_exit(&hostp
->nh_lock
);
2096 new_slr
= kmem_zalloc(sizeof (*slr
), KM_SLEEP
);
2097 bcopy(flp
, &new_slr
->nsr_fl
, sizeof (*flp
));
2099 mutex_enter(&hostp
->nh_lock
);
2100 slr
= nlm_slreq_find_locked(hostp
, nvp
, flp
);
2106 TAILQ_INSERT_TAIL(&nvp
->nv_slreqs
, slr
, nsr_link
);
2110 mutex_exit(&hostp
->nh_lock
);
2111 if (new_slr
!= NULL
)
2112 kmem_free(new_slr
, sizeof (*new_slr
));
2118 * Unregister sleeping lock request corresponding
2119 * to flp from the given vhold object.
2120 * On success function returns 0, otherwise (if
2121 * lock request corresponding to flp isn't found
2122 * on the given vhold) function returns ENOENT.
2125 nlm_slreq_unregister(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
,
2126 struct flock64
*flp
)
2128 struct nlm_slreq
*slr
;
2130 mutex_enter(&hostp
->nh_lock
);
2131 slr
= nlm_slreq_find_locked(hostp
, nvp
, flp
);
2133 mutex_exit(&hostp
->nh_lock
);
2137 TAILQ_REMOVE(&nvp
->nv_slreqs
, slr
, nsr_link
);
2138 mutex_exit(&hostp
->nh_lock
);
2140 kmem_free(slr
, sizeof (*slr
));
2145 * Find sleeping lock request on the given vhold object by flp.
2148 nlm_slreq_find_locked(struct nlm_host
*hostp
, struct nlm_vhold
*nvp
,
2149 struct flock64
*flp
)
2151 struct nlm_slreq
*slr
= NULL
;
2153 ASSERT(MUTEX_HELD(&hostp
->nh_lock
));
2154 TAILQ_FOREACH(slr
, &nvp
->nv_slreqs
, nsr_link
) {
2155 if (slr
->nsr_fl
.l_start
== flp
->l_start
&&
2156 slr
->nsr_fl
.l_len
== flp
->l_len
&&
2157 slr
->nsr_fl
.l_pid
== flp
->l_pid
&&
2158 slr
->nsr_fl
.l_type
== flp
->l_type
)
2166 * NLM tracks active share reservations made on the client side.
2167 * It needs to have a track of share reservations for two purposes
2168 * 1) to determine if nlm_host is busy (if it has active locks and/or
2169 * share reservations, it is)
2170 * 2) to recover active share reservations when NLM server reports
2171 * that it has rebooted.
2173 * Unfortunately Illumos local share reservations manager (see os/share.c)
2174 * doesn't have an ability to lookup all reservations on the system
2175 * by sysid (like local lock manager) or get all reservations by sysid.
2176 * It tracks reservations per vnode and is able to get/looup them
2177 * on particular vnode. It's not what NLM needs. Thus it has that ugly
2178 * share reservations tracking scheme.
2182 nlm_shres_track(struct nlm_host
*hostp
, vnode_t
*vp
, struct shrlock
*shrp
)
2184 struct nlm_shres
*nsp
, *nsp_new
;
2187 * NFS code must fill the s_owner, so that
2188 * s_own_len is never 0.
2190 ASSERT(shrp
->s_own_len
> 0);
2191 nsp_new
= nlm_shres_create_item(shrp
, vp
);
2193 mutex_enter(&hostp
->nh_lock
);
2194 for (nsp
= hostp
->nh_shrlist
; nsp
!= NULL
; nsp
= nsp
->ns_next
)
2195 if (nsp
->ns_vp
== vp
&& nlm_shres_equal(shrp
, nsp
->ns_shr
))
2200 * Found a duplicate. Do nothing.
2208 nsp
->ns_next
= hostp
->nh_shrlist
;
2209 hostp
->nh_shrlist
= nsp
;
2212 mutex_exit(&hostp
->nh_lock
);
2213 if (nsp_new
!= NULL
)
2214 nlm_shres_destroy_item(nsp_new
);
2218 nlm_shres_untrack(struct nlm_host
*hostp
, vnode_t
*vp
, struct shrlock
*shrp
)
2220 struct nlm_shres
*nsp
, *nsp_prev
= NULL
;
2222 mutex_enter(&hostp
->nh_lock
);
2223 nsp
= hostp
->nh_shrlist
;
2224 while (nsp
!= NULL
) {
2225 if (nsp
->ns_vp
== vp
&& nlm_shres_equal(shrp
, nsp
->ns_shr
)) {
2226 struct nlm_shres
*nsp_del
;
2230 if (nsp_prev
!= NULL
)
2231 nsp_prev
->ns_next
= nsp
;
2233 hostp
->nh_shrlist
= nsp
;
2235 nlm_shres_destroy_item(nsp_del
);
2243 mutex_exit(&hostp
->nh_lock
);
2247 * Get a _copy_ of the list of all active share reservations
2248 * made by the given host.
2249 * NOTE: the list function returns _must_ be released using
2250 * nlm_free_shrlist().
2253 nlm_get_active_shres(struct nlm_host
*hostp
)
2255 struct nlm_shres
*nsp
, *nslist
= NULL
;
2257 mutex_enter(&hostp
->nh_lock
);
2258 for (nsp
= hostp
->nh_shrlist
; nsp
!= NULL
; nsp
= nsp
->ns_next
) {
2259 struct nlm_shres
*nsp_new
;
2261 nsp_new
= nlm_shres_create_item(nsp
->ns_shr
, nsp
->ns_vp
);
2262 nsp_new
->ns_next
= nslist
;
2266 mutex_exit(&hostp
->nh_lock
);
2271 * Free memory allocated for the active share reservations
2272 * list created by nlm_get_active_shres() function.
2275 nlm_free_shrlist(struct nlm_shres
*nslist
)
2277 struct nlm_shres
*nsp
;
2279 while (nslist
!= NULL
) {
2281 nslist
= nslist
->ns_next
;
2283 nlm_shres_destroy_item(nsp
);
2288 nlm_shres_equal(struct shrlock
*shrp1
, struct shrlock
*shrp2
)
2290 if (shrp1
->s_sysid
== shrp2
->s_sysid
&&
2291 shrp1
->s_pid
== shrp2
->s_pid
&&
2292 shrp1
->s_own_len
== shrp2
->s_own_len
&&
2293 bcmp(shrp1
->s_owner
, shrp2
->s_owner
,
2294 shrp1
->s_own_len
) == 0)
2300 static struct nlm_shres
*
2301 nlm_shres_create_item(struct shrlock
*shrp
, vnode_t
*vp
)
2303 struct nlm_shres
*nsp
;
2305 nsp
= kmem_alloc(sizeof (*nsp
), KM_SLEEP
);
2306 nsp
->ns_shr
= kmem_alloc(sizeof (*shrp
), KM_SLEEP
);
2307 bcopy(shrp
, nsp
->ns_shr
, sizeof (*shrp
));
2308 nsp
->ns_shr
->s_owner
= kmem_alloc(shrp
->s_own_len
, KM_SLEEP
);
2309 bcopy(shrp
->s_owner
, nsp
->ns_shr
->s_owner
, shrp
->s_own_len
);
2316 nlm_shres_destroy_item(struct nlm_shres
*nsp
)
2318 kmem_free(nsp
->ns_shr
->s_owner
,
2319 nsp
->ns_shr
->s_own_len
);
2320 kmem_free(nsp
->ns_shr
, sizeof (struct shrlock
));
2321 kmem_free(nsp
, sizeof (*nsp
));
2325 * Called by klmmod.c when lockd adds a network endpoint
2326 * on which we should begin RPC services.
2329 nlm_svc_add_ep(struct file
*fp
, const char *netid
, struct knetconfig
*knc
)
2331 SVCMASTERXPRT
*xprt
= NULL
;
2334 error
= svc_tli_kcreate(fp
, 0, (char *)netid
, NULL
, &xprt
,
2335 &nlm_sct
, NULL
, NLM_SVCPOOL_ID
, FALSE
);
2339 (void) nlm_knc_to_netid(knc
);
2344 * Start NLM service.
2347 nlm_svc_starting(struct nlm_globals
*g
, struct file
*fp
,
2348 const char *netid
, struct knetconfig
*knc
)
2351 enum clnt_stat stat
;
2353 VERIFY(g
->run_status
== NLM_ST_STARTING
);
2354 VERIFY(g
->nlm_gc_thread
== NULL
);
2356 error
= nlm_nsm_init_local(&g
->nlm_nsm
);
2358 NLM_ERR("Failed to initialize NSM handler "
2359 "(error=%d)\n", error
);
2360 g
->run_status
= NLM_ST_DOWN
;
2367 * Create an NLM garbage collector thread that will
2368 * clean up stale vholds and hosts objects.
2370 g
->nlm_gc_thread
= zthread_create(NULL
, 0, nlm_gc
,
2374 * Send SIMU_CRASH to local statd to report that
2375 * NLM started, so that statd can report other hosts
2376 * about NLM state change.
2379 stat
= nlm_nsm_simu_crash(&g
->nlm_nsm
);
2380 if (stat
!= RPC_SUCCESS
) {
2381 NLM_ERR("Failed to connect to local statd "
2382 "(rpcerr=%d)\n", stat
);
2386 stat
= nlm_nsm_stat(&g
->nlm_nsm
, &g
->nsm_state
);
2387 if (stat
!= RPC_SUCCESS
) {
2388 NLM_ERR("Failed to get the status of local statd "
2389 "(rpcerr=%d)\n", stat
);
2393 g
->grace_threshold
= ddi_get_lbolt() +
2394 SEC_TO_TICK(g
->grace_period
);
2396 /* Register endpoint used for communications with local NLM */
2397 error
= nlm_svc_add_ep(fp
, netid
, knc
);
2401 (void) svc_pool_control(NLM_SVCPOOL_ID
,
2402 SVCPSET_SHUTDOWN_PROC
, (void *)nlm_pool_shutdown
);
2403 g
->run_status
= NLM_ST_UP
;
2407 mutex_enter(&g
->lock
);
2408 g
->run_status
= NLM_ST_STOPPING
;
2409 mutex_exit(&g
->lock
);
2411 nlm_svc_stopping(g
);
2416 * Called when the server pool is destroyed, so that
2417 * all transports are closed and no any server threads
2420 * Just call lm_shutdown() to shut NLM down properly.
2423 nlm_pool_shutdown(void)
2425 (void) lm_shutdown();
2429 * Stop NLM service, cleanup all resources
2430 * NLM owns at the moment.
2432 * NOTE: NFS code can call NLM while it's
2433 * stopping or even if it's shut down. Any attempt
2434 * to lock file either on client or on the server
2435 * will fail if NLM isn't in NLM_ST_UP state.
2438 nlm_svc_stopping(struct nlm_globals
*g
)
2440 mutex_enter(&g
->lock
);
2441 ASSERT(g
->run_status
== NLM_ST_STOPPING
);
2444 * Ask NLM GC thread to exit and wait until it dies.
2446 cv_signal(&g
->nlm_gc_sched_cv
);
2447 while (g
->nlm_gc_thread
!= NULL
)
2448 cv_wait(&g
->nlm_gc_finish_cv
, &g
->lock
);
2450 mutex_exit(&g
->lock
);
2453 * Cleanup locks owned by NLM hosts.
2454 * NOTE: New hosts won't be created while
2457 while (!avl_is_empty(&g
->nlm_hosts_tree
)) {
2458 struct nlm_host
*hostp
;
2462 * Iterate through all NLM hosts in the system
2463 * and drop the locks they own by force.
2465 hostp
= avl_first(&g
->nlm_hosts_tree
);
2466 while (hostp
!= NULL
) {
2467 /* Cleanup all client and server side locks */
2468 nlm_client_cancel_all(g
, hostp
);
2469 nlm_host_notify_server(hostp
, 0);
2471 mutex_enter(&hostp
->nh_lock
);
2472 nlm_host_gc_vholds(hostp
);
2473 if (hostp
->nh_refs
> 0 || nlm_host_has_locks(hostp
)) {
2475 * Oh, it seems the host is still busy, let
2476 * it some time to release and go to the
2480 mutex_exit(&hostp
->nh_lock
);
2481 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
);
2486 mutex_exit(&hostp
->nh_lock
);
2487 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
);
2491 * All hosts go to nlm_idle_hosts list after
2492 * all locks they own are cleaned up and last refereces
2493 * were dropped. Just destroy all hosts in nlm_idle_hosts
2494 * list, they can not be removed from there while we're
2495 * in stopping state.
2497 while ((hostp
= TAILQ_FIRST(&g
->nlm_idle_hosts
)) != NULL
) {
2498 nlm_host_unregister(g
, hostp
);
2499 nlm_host_destroy(hostp
);
2502 if (busy_hosts
> 0) {
2504 * There're some hosts that weren't cleaned
2505 * up. Probably they're in resource cleanup
2506 * process. Give them some time to do drop
2509 delay(MSEC_TO_TICK(500));
2513 ASSERT(TAILQ_EMPTY(&g
->nlm_slocks
));
2515 nlm_nsm_fini(&g
->nlm_nsm
);
2517 g
->run_status
= NLM_ST_DOWN
;
2521 * Returns TRUE if the given vnode has
2522 * any active or sleeping locks.
2525 nlm_vp_active(const vnode_t
*vp
)
2527 struct nlm_globals
*g
;
2528 struct nlm_host
*hostp
;
2529 struct nlm_vhold
*nvp
;
2532 g
= zone_getspecific(nlm_zone_key
, curzone
);
2535 * Server side NLM has locks on the given vnode
2536 * if there exist a vhold object that holds
2537 * the given vnode "vp" in one of NLM hosts.
2539 mutex_enter(&g
->lock
);
2540 hostp
= avl_first(&g
->nlm_hosts_tree
);
2541 while (hostp
!= NULL
) {
2542 mutex_enter(&hostp
->nh_lock
);
2543 nvp
= nlm_vhold_find_locked(hostp
, vp
);
2544 mutex_exit(&hostp
->nh_lock
);
2550 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
);
2553 mutex_exit(&g
->lock
);
2558 * Called right before NFS export is going to
2559 * dissapear. The function finds all vnodes
2560 * belonging to the given export and cleans
2561 * all remote locks and share reservations
2565 nlm_unexport(struct exportinfo
*exi
)
2567 struct nlm_globals
*g
;
2568 struct nlm_host
*hostp
;
2570 g
= zone_getspecific(nlm_zone_key
, curzone
);
2572 mutex_enter(&g
->lock
);
2573 hostp
= avl_first(&g
->nlm_hosts_tree
);
2574 while (hostp
!= NULL
) {
2575 struct nlm_vhold
*nvp
;
2577 if (hostp
->nh_flags
& NLM_NH_INIDLE
) {
2578 TAILQ_REMOVE(&g
->nlm_idle_hosts
, hostp
, nh_link
);
2579 hostp
->nh_flags
&= ~NLM_NH_INIDLE
;
2583 mutex_exit(&g
->lock
);
2585 mutex_enter(&hostp
->nh_lock
);
2586 TAILQ_FOREACH(nvp
, &hostp
->nh_vholds_list
, nv_link
) {
2590 mutex_exit(&hostp
->nh_lock
);
2594 if (!EQFSID(&exi
->exi_fsid
, &vp
->v_vfsp
->vfs_fsid
))
2598 * Ok, it we found out that vnode vp is under
2599 * control by the exportinfo exi, now we need
2600 * to drop all locks from this vnode, let's
2603 nlm_vhold_clean(nvp
, hostp
->nh_sysid
);
2606 mutex_enter(&hostp
->nh_lock
);
2609 mutex_exit(&hostp
->nh_lock
);
2611 mutex_enter(&g
->lock
);
2612 nlm_host_release_locked(g
, hostp
);
2614 hostp
= AVL_NEXT(&g
->nlm_hosts_tree
, hostp
);
2617 mutex_exit(&g
->lock
);
2621 * Allocate new unique sysid.
2622 * In case of failure (no available sysids)
2623 * return LM_NOSYSID.
2626 nlm_sysid_alloc(void)
2628 sysid_t ret_sysid
= LM_NOSYSID
;
2630 rw_enter(&lm_lck
, RW_WRITER
);
2631 if (nlm_sysid_nidx
> LM_SYSID_MAX
)
2632 nlm_sysid_nidx
= LM_SYSID
;
2634 if (!BT_TEST(nlm_sysid_bmap
, nlm_sysid_nidx
)) {
2635 BT_SET(nlm_sysid_bmap
, nlm_sysid_nidx
);
2636 ret_sysid
= nlm_sysid_nidx
++;
2640 id
= bt_availbit(nlm_sysid_bmap
, NLM_BMAP_NITEMS
);
2642 nlm_sysid_nidx
= id
+ 1;
2644 BT_SET(nlm_sysid_bmap
, id
);
2653 nlm_sysid_free(sysid_t sysid
)
2655 ASSERT(sysid
>= LM_SYSID
&& sysid
<= LM_SYSID_MAX
);
2657 rw_enter(&lm_lck
, RW_WRITER
);
2658 ASSERT(BT_TEST(nlm_sysid_bmap
, sysid
));
2659 BT_CLEAR(nlm_sysid_bmap
, sysid
);
2664 * Return true if the request came from a local caller.
2665 * By necessity, this "knows" the netid names invented
2666 * in lm_svc() and nlm_netid_from_knetconfig().
2669 nlm_caller_is_local(SVCXPRT
*transp
)
2672 struct netbuf
*rtaddr
;
2674 netid
= svc_getnetid(transp
);
2675 rtaddr
= svc_getrpccaller(transp
);
2680 if (strcmp(netid
, "ticlts") == 0 ||
2681 strcmp(netid
, "ticotsord") == 0)
2684 if (strcmp(netid
, "tcp") == 0 || strcmp(netid
, "udp") == 0) {
2685 struct sockaddr_in
*sin
= (void *)rtaddr
->buf
;
2686 if (sin
->sin_addr
.s_addr
== htonl(INADDR_LOOPBACK
))
2689 if (strcmp(netid
, "tcp6") == 0 || strcmp(netid
, "udp6") == 0) {
2690 struct sockaddr_in6
*sin6
= (void *)rtaddr
->buf
;
2691 if (IN6_IS_ADDR_LOOPBACK(&sin6
->sin6_addr
))
2695 return (FALSE
); /* unknown transport */
2699 * Get netid string correspondig to the given knetconfig.
2700 * If not done already, save knc->knc_rdev in our table.
2703 nlm_knc_to_netid(struct knetconfig
*knc
)
2708 const char *netid
= NULL
;
2710 rw_enter(&lm_lck
, RW_READER
);
2711 for (i
= 0; i
< NLM_KNCS
; i
++) {
2712 nc
= &nlm_netconfigs
[i
];
2714 if (nc
->n_knc
.knc_semantics
== knc
->knc_semantics
&&
2715 strcmp(nc
->n_knc
.knc_protofmly
,
2716 knc
->knc_protofmly
) == 0) {
2717 netid
= nc
->n_netid
;
2718 rdev
= nc
->n_knc
.knc_rdev
;
2724 if (netid
!= NULL
&& rdev
== NODEV
) {
2725 rw_enter(&lm_lck
, RW_WRITER
);
2726 if (nc
->n_knc
.knc_rdev
== NODEV
)
2727 nc
->n_knc
.knc_rdev
= knc
->knc_rdev
;
2735 * Get a knetconfig corresponding to the given netid.
2736 * If there's no knetconfig for this netid, ENOENT
2740 nlm_knc_from_netid(const char *netid
, struct knetconfig
*knc
)
2745 for (i
= 0; i
< NLM_KNCS
; i
++) {
2746 struct nlm_knc
*nknc
;
2748 nknc
= &nlm_netconfigs
[i
];
2749 if (strcmp(netid
, nknc
->n_netid
) == 0 &&
2750 nknc
->n_knc
.knc_rdev
!= NODEV
) {
2761 nlm_cprsuspend(void)
2763 struct nlm_globals
*g
;
2765 rw_enter(&lm_lck
, RW_READER
);
2766 TAILQ_FOREACH(g
, &nlm_zones_list
, nlm_link
)
2767 nlm_suspend_zone(g
);
2775 struct nlm_globals
*g
;
2777 rw_enter(&lm_lck
, RW_READER
);
2778 TAILQ_FOREACH(g
, &nlm_zones_list
, nlm_link
)
2785 nlm_nsm_clnt_init(CLIENT
*clnt
, struct nlm_nsm
*nsm
)
2787 (void) clnt_tli_kinit(clnt
, &nsm
->ns_knc
, &nsm
->ns_addr
, 0,
2788 NLM_RPC_RETRIES
, kcred
);
2792 nlm_netbuf_to_netobj(struct netbuf
*addr
, int *family
, netobj
*obj
)
2794 /* LINTED pointer alignment */
2795 struct sockaddr
*sa
= (struct sockaddr
*)addr
->buf
;
2797 *family
= sa
->sa_family
;
2799 switch (sa
->sa_family
) {
2801 /* LINTED pointer alignment */
2802 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
2804 obj
->n_len
= sizeof (sin
->sin_addr
);
2805 obj
->n_bytes
= (char *)&sin
->sin_addr
;
2810 /* LINTED pointer alignment */
2811 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
2813 obj
->n_len
= sizeof (sin6
->sin6_addr
);
2814 obj
->n_bytes
= (char *)&sin6
->sin6_addr
;