changeset 14165:89c6cfbfee9f

195 Need replacement for nfs/lockd+klm Reviewed by: Gordon Ross <gordon.ross@nexenta.com> Reviewed by: Jeremy Jones <jeremy@delphix.com> Reviewed by: Jeff Biseda <jbiseda@delphix.com> Approved by: Garrett D'Amore <garrett@damore.org>
author Dan Kruchinin <dan.kruchinin@nexenta.com>
date Mon, 26 Aug 2013 20:42:32 -0800
parents dceb17481b99
children 6b7f159a0cf1
files usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c usr/src/cmd/fs.d/nfs/Makefile usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c usr/src/cmd/fs.d/nfs/lib/smfcfg.c usr/src/cmd/fs.d/nfs/lib/smfcfg.h usr/src/cmd/fs.d/nfs/lockd/Makefile usr/src/cmd/fs.d/nfs/lockd/lockd.c usr/src/cmd/fs.d/nfs/mount/Makefile usr/src/cmd/fs.d/nfs/statd/sm_proc.c usr/src/cmd/fs.d/nfs/statd/sm_statd.c usr/src/cmd/fs.d/nfs/statd/sm_statd.h usr/src/cmd/fs.d/nfs/statd/sm_svc.c usr/src/head/Makefile usr/src/head/rpcsvc/nlm_prot.x usr/src/lib/librpcsvc/common/mapfile-vers usr/src/uts/Makefile usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/tl.c usr/src/uts/common/klm/Makefile usr/src/uts/common/klm/klmmod.c usr/src/uts/common/klm/klmops.c usr/src/uts/common/klm/mapfile-mod usr/src/uts/common/klm/mapfile-ops usr/src/uts/common/klm/nlm_client.c usr/src/uts/common/klm/nlm_dispatch.c usr/src/uts/common/klm/nlm_impl.c usr/src/uts/common/klm/nlm_impl.h usr/src/uts/common/klm/nlm_prot_clnt.sed usr/src/uts/common/klm/nlm_rpc_clnt.c usr/src/uts/common/klm/nlm_rpc_handle.c usr/src/uts/common/klm/nlm_rpc_svc.c usr/src/uts/common/klm/nlm_service.c usr/src/uts/common/klm/nsm_addr_clnt.sed usr/src/uts/common/klm/sm_inter_clnt.sed usr/src/uts/common/nfs/lm.h usr/src/uts/common/os/flock.c usr/src/uts/common/os/share.c usr/src/uts/common/rpcsvc/Makefile usr/src/uts/common/rpcsvc/nlm_prot.x usr/src/uts/common/rpcsvc/sm_inter.x usr/src/uts/common/sys/flock.h usr/src/uts/common/sys/share.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/klmmod/Makefile usr/src/uts/intel/klmops/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/klmmod/Makefile usr/src/uts/sparc/klmops/Makefile
diffstat 49 files changed, 11560 insertions(+), 781 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nlm.c	Mon Aug 26 20:42:32 2013 -0800
@@ -24,6 +24,11 @@
  * All rights reserved.
  */
 
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
 #include <sys/types.h>
 #include <setjmp.h>
 #include <string.h>
@@ -818,17 +823,17 @@
 				procnames_short_4[proc]);
 			line += strlen(line);
 			switch (proc) {
-			case NLMPROC4_TEST:
-			case NLMPROC4_GRANTED:
-			case NLMPROC4_TEST_MSG:
-			case NLMPROC4_GRANTED_MSG:
+			case NLM4_TEST:
+			case NLM4_GRANTED:
+			case NLM4_TEST_MSG:
+			case NLM4_GRANTED_MSG:
 				/* testargs */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) getxdr_bool();	/* Excl */
 				(void) strcat(line, sum_lock4());
 				break;
-			case NLMPROC4_LOCK:
-			case NLMPROC4_LOCK_MSG:
+			case NLM4_LOCK:
+			case NLM4_LOCK_MSG:
 				/* lockargs */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) getxdr_bool();	/* Block */
@@ -836,43 +841,43 @@
 				(void) strcat(line, sum_lock4());
 				/* ignore reclaim, state fields */
 				break;
-			case NLMPROC4_CANCEL:
-			case NLMPROC4_CANCEL_MSG:
+			case NLM4_CANCEL:
+			case NLM4_CANCEL_MSG:
 				/* cancargs */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) getxdr_bool();	/* Block */
 				(void) getxdr_bool();	/* Excl */
 				(void) strcat(line, sum_lock4());
 				break;
-			case NLMPROC4_UNLOCK:
-			case NLMPROC4_UNLOCK_MSG:
+			case NLM4_UNLOCK:
+			case NLM4_UNLOCK_MSG:
 				/* unlockargs */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, sum_lock4());
 				break;
-			case NLMPROC4_TEST_RES:
+			case NLM4_TEST_RES:
 				/* testres */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, " ");
 				(void) strcat(line,
 				    nameof_stat4(getxdr_u_long()));
 				break;
-			case NLMPROC4_LOCK_RES:
-			case NLMPROC4_CANCEL_RES:
-			case NLMPROC4_UNLOCK_RES:
-			case NLMPROC4_GRANTED_RES:
+			case NLM4_LOCK_RES:
+			case NLM4_CANCEL_RES:
+			case NLM4_UNLOCK_RES:
+			case NLM4_GRANTED_RES:
 				/* res */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, " ");
 				(void) strcat(line,
 					nameof_stat4(getxdr_u_long()));
 				break;
-			case NLMPROC4_SHARE:
-			case NLMPROC4_UNSHARE:
+			case NLM4_SHARE:
+			case NLM4_UNSHARE:
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, sum_share());
 				break;
-			case NLMPROC4_NM_LOCK:
+			case NLM4_NM_LOCK:
 				/* lockargs */
 				skip_netobj();		/* Cookie */
 				(void) getxdr_bool();	/* Block */
@@ -880,7 +885,7 @@
 				(void) strcat(line, sum_lock4());
 				/* skip reclaim & state fields */
 				break;
-			case NLMPROC4_FREE_ALL:
+			case NLM4_FREE_ALL:
 				(void) sprintf(line,
 					" %s", sum_notify());
 				break;
@@ -891,33 +896,33 @@
 				procnames_short_4[proc]);
 			line += strlen(line);
 			switch (proc) {
-			case NLMPROC4_TEST:
+			case NLM4_TEST:
 				/* testres */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, " ");
 				(void) strcat(line,
 				    nameof_stat4(getxdr_u_long()));
 				break;
-			case NLMPROC4_LOCK:
-			case NLMPROC4_CANCEL:
-			case NLMPROC4_UNLOCK:
-			case NLMPROC4_GRANTED:
-			case NLMPROC4_NM_LOCK:
+			case NLM4_LOCK:
+			case NLM4_CANCEL:
+			case NLM4_UNLOCK:
+			case NLM4_GRANTED:
+			case NLM4_NM_LOCK:
 				/* res */
 				(void) strcat(line, sum_netobj("OH"));
 				(void) strcat(line, " ");
 				(void) strcat(line,
 					nameof_stat4(getxdr_u_long()));
 				break;
-			case NLMPROC4_SHARE:
-			case NLMPROC4_UNSHARE:
+			case NLM4_SHARE:
+			case NLM4_UNSHARE:
 				/* shareres */
 				pl = sum_netobj("OH");
 				i = getxdr_u_long();
 				sprintf(line, "%s %s %ld",
 					pl, nameof_stat4(i), getxdr_long());
 				break;
-			case NLMPROC4_FREE_ALL:
+			case NLM4_FREE_ALL:
 				break;
 			}
 		}
@@ -934,64 +939,64 @@
 			proc, procnames_long_4[proc]);
 		if (type == CALL) {
 			switch (proc) {
-			case NLMPROC4_TEST:
-			case NLMPROC4_GRANTED:
-			case NLMPROC4_TEST_MSG:
-			case NLMPROC4_GRANTED_MSG:
+			case NLM4_TEST:
+			case NLM4_GRANTED:
+			case NLM4_TEST_MSG:
+			case NLM4_GRANTED_MSG:
 				show_testargs4();
 				break;
-			case NLMPROC4_LOCK:
-			case NLMPROC4_LOCK_MSG:
-			case NLMPROC4_NM_LOCK:
+			case NLM4_LOCK:
+			case NLM4_LOCK_MSG:
+			case NLM4_NM_LOCK:
 				show_lockargs4();
 				break;
-			case NLMPROC4_CANCEL:
-			case NLMPROC4_CANCEL_MSG:
+			case NLM4_CANCEL:
+			case NLM4_CANCEL_MSG:
 				show_cancargs4();
 				break;
-			case NLMPROC4_UNLOCK:
-			case NLMPROC4_UNLOCK_MSG:
+			case NLM4_UNLOCK:
+			case NLM4_UNLOCK_MSG:
 				show_unlockargs4();
 				break;
-			case NLMPROC4_TEST_RES:
+			case NLM4_TEST_RES:
 				show_testres4();
 				break;
-			case NLMPROC4_LOCK_RES:
-			case NLMPROC4_CANCEL_RES:
-			case NLMPROC4_UNLOCK_RES:
-			case NLMPROC4_GRANTED_RES:
+			case NLM4_LOCK_RES:
+			case NLM4_CANCEL_RES:
+			case NLM4_UNLOCK_RES:
+			case NLM4_GRANTED_RES:
 				show_res4();
 				break;
-			case NLMPROC4_SHARE:
-			case NLMPROC4_UNSHARE:
+			case NLM4_SHARE:
+			case NLM4_UNSHARE:
 				show_shareargs();
 				break;
-			case NLMPROC4_FREE_ALL:
+			case NLM4_FREE_ALL:
 				show_notify();
 				break;
 			}
 		} else {
 			switch (proc) {
-			case NLMPROC4_TEST:
+			case NLM4_TEST:
 				show_testres4();
 				break;
-			case NLMPROC4_LOCK:
-			case NLMPROC4_CANCEL:
-			case NLMPROC4_UNLOCK:
-			case NLMPROC4_GRANTED:
+			case NLM4_LOCK:
+			case NLM4_CANCEL:
+			case NLM4_UNLOCK:
+			case NLM4_GRANTED:
 			case NLM_NM_LOCK:
 				show_res4();
 				break;
-			case NLMPROC4_TEST_MSG:
-			case NLMPROC4_LOCK_MSG:
-			case NLMPROC4_CANCEL_MSG:
-			case NLMPROC4_UNLOCK_MSG:
-			case NLMPROC4_GRANTED_MSG:
-			case NLMPROC4_TEST_RES:
-			case NLMPROC4_LOCK_RES:
-			case NLMPROC4_CANCEL_RES:
-			case NLMPROC4_UNLOCK_RES:
-			case NLMPROC4_GRANTED_RES:
+			case NLM4_TEST_MSG:
+			case NLM4_LOCK_MSG:
+			case NLM4_CANCEL_MSG:
+			case NLM4_UNLOCK_MSG:
+			case NLM4_GRANTED_MSG:
+			case NLM4_TEST_RES:
+			case NLM4_LOCK_RES:
+			case NLM4_CANCEL_RES:
+			case NLM4_UNLOCK_RES:
+			case NLM4_GRANTED_RES:
 				break;
 			case NLM_SHARE:
 			case NLM_UNSHARE:
@@ -1082,16 +1087,16 @@
 	ulong_t s;
 {
 	switch ((enum nlm4_stats) s) {
-	case NLM4_GRANTED:	return ("granted");
-	case NLM4_DENIED:	return ("denied");
-	case NLM4_DENIED_NOLOCKS:return ("denied (no locks)");
-	case NLM4_BLOCKED:	return ("blocked");
-	case NLM4_DENIED_GRACE_PERIOD: return ("denied (grace period)");
-	case NLM4_DEADLCK:	return ("deadlock");
-	case NLM4_ROFS:		return ("read-only fs");
-	case NLM4_STALE_FH:	return ("stale fh");
-	case NLM4_FBIG:		return ("file too big");
-	case NLM4_FAILED:	return ("failed");
+	case nlm4_granted:	return ("granted");
+	case nlm4_denied:	return ("denied");
+	case nlm4_denied_nolocks:return ("denied (no locks)");
+	case nlm4_blocked:	return ("blocked");
+	case nlm4_denied_grace_period: return ("denied (grace period)");
+	case nlm4_deadlck:	return ("deadlock");
+	case nlm4_rofs:		return ("read-only fs");
+	case nlm4_stale_fh:	return ("stale fh");
+	case nlm4_fbig:		return ("file too big");
+	case nlm4_failed:	return ("failed");
 	default:		return ("?");
 	}
 }
--- a/usr/src/cmd/fs.d/nfs/Makefile	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -34,7 +34,9 @@
 SUBDIR1=	exportfs nfsd rquotad \
 		statd nfsstat mountd dfshares \
 		nfsfind nfs4cbd share
-SUBDIR2=	clear_locks umount showmount \
+
+# These do "make catalog"
+SUBDIR2=	clear_locks lockd umount showmount \
 		mount dfmounts nfslog nfsmapid \
 		nfsref rp_basic
 
@@ -46,11 +48,6 @@
 POFILES=        $(SUBDIR2:%=%/%.po)
 POFILE=         nfs.po
 
-LOCKD=		$(CLOSED)/cmd/fs.d/nfs/lockd
-$(CLOSED_BUILD)CLOSED_SUBDIR2=	$(LOCKD)
-$(CLOSED_BUILD)POFILES +=	$(LOCKD)/lockd.po
-$(CLOSED_BUILD)SUBDIRS +=	$(CLOSED_SUBDIR2)
-
 all:=		TARGET= all
 install:=	TARGET= install
 clean:=		TARGET= clean
@@ -64,7 +61,7 @@
 
 all install clean clobber lint: $(SUBDIRS)
 
-catalog: $(SUBDIR2) $(CLOSED_SUBDIR2)
+catalog: $(SUBDIR2)
 	$(RM) $(POFILE)
 	cat $(POFILES) > $(POFILE)
 
--- a/usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c	Mon Aug 26 20:42:32 2013 -0800
@@ -18,11 +18,11 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-/*
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 
@@ -1698,9 +1698,9 @@
  * to all-ones. The port number part of the mask is zeroes.
  */
 static int
-set_addrmask(fd, nconf, mask)
-	struct netconfig *nconf;
-	struct netbuf *mask;
+set_addrmask(int fd,
+	struct netconfig *nconf,
+	struct netbuf *mask)
 {
 	struct t_info info;
 
@@ -1725,8 +1725,7 @@
 			return (0);
 		}
 
-		syslog(LOG_ERR, "set_addrmask: address size: %ld",
-			info.addr);
+		syslog(LOG_ERR, "set_addrmask: address size: %ld", info.addr);
 		return (-1);
 	}
 
@@ -1743,17 +1742,17 @@
 		 */
 		/* LINTED pointer alignment */
 		((struct sockaddr_in *)mask->buf)->sin_addr.s_addr =
-								(ulong_t)~0;
+		    (ulong_t)~0;
 		/* LINTED pointer alignment */
 		((struct sockaddr_in *)mask->buf)->sin_family =
-								(ushort_t)~0;
+		    (ushort_t)~0;
 	} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
 		/* LINTED pointer alignment */
 		(void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr,
-			(uchar_t)~0, sizeof (struct in6_addr));
+		    (uchar_t)~0, sizeof (struct in6_addr));
 		/* LINTED pointer alignment */
 		((struct sockaddr_in6 *)mask->buf)->sin6_family =
-								(ushort_t)~0;
+		    (ushort_t)~0;
 	} else {
 
 		/*
--- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c	Mon Aug 26 20:42:32 2013 -0800
@@ -18,8 +18,10 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 #include <stdio.h>
 #include <stdlib.h>
@@ -358,6 +360,27 @@
 	    instance, sctype, svc_name, bufsz));
 }
 
+/* Get an integer (base 10) property */
+int
+nfs_smf_get_iprop(char *prop_name, int *rvp, char *instance,
+    scf_type_t sctype, char *svc_name)
+{
+	char propbuf[32];
+	int bufsz, rc, val;
+
+	bufsz = sizeof (propbuf);
+	rc = fs_smf_get_prop(NFS_SMF, prop_name, propbuf,
+	    instance, sctype, svc_name, &bufsz);
+	if (rc != SA_OK)
+		return (rc);
+	errno = 0;
+	val = strtol(propbuf, NULL, 10);
+	if (errno != 0)
+		return (SA_BAD_VALUE);
+	*rvp = val;
+	return (SA_OK);
+}
+
 int
 nfs_smf_set_prop(char *prop_name, char *value, char *instance,
     scf_type_t type, char *svc_name)
--- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.h	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.h	Mon Aug 26 20:42:32 2013 -0800
@@ -90,6 +90,7 @@
 #define	SMF_NO_PERMISSION	2
 #define	SMF_NO_PGTYPE		3
 
+extern int nfs_smf_get_iprop(char *, int *, char *, scf_type_t, char *);
 extern int nfs_smf_get_prop(char *, char *, char *, scf_type_t, char *, int *);
 extern int fs_smf_get_prop(smf_fstype_t,  char *, char *, char *, scf_type_t,
 	char *, int *);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lockd/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012 by Delphix. All rights reserved.
+#
+
+FSTYPE	  =	nfs
+TYPEPROG  =	lockd
+ATTMK	  =	$(TYPEPROG)
+
+include		../../Makefile.fstype
+
+LOCAL	  =	lockd.o
+OBJS	  =	$(LOCAL) daemon.o nfs_tbind.o smfcfg.o thrpool.o
+
+POFILE	  =	lockd.po
+
+SRCS	  =	$(LOCAL:%.o=%.c) ../lib/daemon.c ../lib/nfs_tbind.c \
+		../lib/smfcfg.c ../lib/thrpool.c
+LDLIBS	 +=	-lnsl -lscf
+CPPFLAGS +=	-I../lib
+C99MODE	  =	$(C99_ENABLE)
+
+CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-switch
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-uninitialized
+
+$(TYPEPROG):	$(OBJS)
+		$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+		$(POST_PROCESS)
+
+lockd.o:	lockd.c
+		$(COMPILE.c) lockd.c
+
+nfs_tbind.o:	../lib/nfs_tbind.c
+		$(COMPILE.c) ../lib/nfs_tbind.c
+
+thrpool.o:	../lib/thrpool.c
+		$(COMPILE.c) ../lib/thrpool.c
+
+daemon.o:	../lib/daemon.c
+		$(COMPILE.c) ../lib/daemon.c
+
+smfcfg.o:	../lib/smfcfg.c
+		$(COMPILE.c) ../lib/smfcfg.c
+
+#
+# message catalog
+#
+catalog: $(POFILE)
+
+$(POFILE): $(SRCS)
+	$(RM) $@
+	$(COMPILE.cpp) $(SRCS)   > $(POFILE).i
+	$(XGETTEXT)     $(XGETFLAGS) $(POFILE).i
+	sed "/^domain/d"        messages.po     > $@
+	$(RM) $(POFILE).i messages.po
+
+lint:
+	$(LINT.c) $(SRCS) $(LDLIBS)
+
+clean:
+	$(RM) $(OBJS) $(DOBJ)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fs.d/nfs/lockd/lockd.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,550 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T		*/
+/*	  All Rights Reserved  	*/
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+/*
+ * NLM server
+ *
+ * Most of this copied from ../nfsd/nfsd.c
+ * and then s:NFS:NLM: applied, etc.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <tiuser.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <thread.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <nfs/nfs.h>
+#include <nfs/nfssys.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <netconfig.h>
+#include <netdir.h>
+#include <string.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/tihdr.h>
+#include <poll.h>
+#include <priv_utils.h>
+#include <sys/tiuser.h>
+#include <netinet/tcp.h>
+#include <deflt.h>
+#include <rpcsvc/daemon_utils.h>
+#include <rpcsvc/nlm_prot.h>
+#include <libintl.h>
+#include <libscf.h>
+#include <libshare.h>
+#include "nfs_tbind.h"
+#include "thrpool.h"
+#include "smfcfg.h"
+
+/* Option defaults.  See nfssys.h */
+struct lm_svc_args lmargs = {
+	.version = LM_SVC_CUR_VERS,
+	/* fd, n_fmly, n_proto, n_rdev (below) */
+	.debug = 0,
+	.timout = 5 * 60,
+	.grace = 60,
+	.retransmittimeout = 15
+};
+int max_servers = 20;
+
+
+#define	RET_OK		0	/* return code for no error */
+#define	RET_ERR		33	/* return code for error(s) */
+
+static	int	nlmsvc(int fd, struct netbuf addrmask,
+			struct netconfig *nconf);
+static int nlmsvcpool(int max_servers);
+static	void	usage(void);
+
+extern	int	_nfssys(int, void *);
+static void sigterm_handler(void);
+static void shutdown_lockd(void);
+
+extern int	daemonize_init(void);
+extern void	daemonize_fini(int fd);
+
+static	char	*MyName;
+
+/*
+ * We want to bind to these TLI providers, and in this order,
+ * because the kernel NLM needs the loopback first for its
+ * initialization. (It uses it to talk to statd.)
+ */
+static  NETSELDECL(defaultproviders)[] = {
+	"/dev/ticotsord",
+	"/dev/tcp",
+	"/dev/udp",
+	"/dev/tcp6",
+	"/dev/udp6",
+	NULL
+};
+
+/*
+ * The following are all globals used by routines in nfs_tbind.c.
+ */
+size_t	end_listen_fds;		/* used by conn_close_oldest() */
+size_t	num_fds = 0;		/* used by multiple routines */
+int	listen_backlog = 32;	/* used by bind_to_{provider,proto}() */
+int	(*Mysvc)(int, struct netbuf, struct netconfig *) = nlmsvc;
+				/* used by cots_listen_event() */
+int	max_conns_allowed = -1;	/* used by cots_listen_event() */
+
+int
+main(int ac, char *av[])
+{
+	char *propname = NULL;
+	char *dir = "/";
+	char *provider = (char *)NULL;
+	struct protob *protobp;
+	NETSELPDECL(providerp);
+	sigset_t sgset;
+	int i, c, pid, ret, val;
+	int pipe_fd = -1;
+	struct sigaction act;
+
+	MyName = *av;
+
+	/*
+	 * Initializations that require more privileges than we need to run.
+	 */
+	(void) _create_daemon_lock(LOCKD, DAEMON_UID, DAEMON_GID);
+	svcsetprio();
+
+	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
+	    DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, NULL) == -1) {
+		(void) fprintf(stderr, "%s should be run with"
+		    " sufficient privileges\n", av[0]);
+		exit(1);
+	}
+
+	(void) enable_extended_FILE_stdio(-1, -1);
+
+	/*
+	 * Read in the values from SMF first before we check
+	 * command line options so the options override SMF values.
+	 */
+
+	/* How long to keep idle connections. */
+	propname = "conn_idle_timeout"; /* also -t */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			lmargs.timout = val;
+	}
+
+	/* Note: debug_level can only be set by args. */
+
+	/* How long to wait for clients to re-establish locks. */
+	propname = "grace_period"; /* also -g */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			lmargs.grace = val;
+	}
+
+	propname = "listen_backlog"; /* also -l */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			listen_backlog = val;
+	}
+
+	propname = "max_connections"; /* also -c */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			max_conns_allowed = val;
+	}
+
+	propname = "max_servers"; /* also argv[1] */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			max_servers = val;
+	}
+
+	propname = "retrans_timeout"; /* also -r */
+	ret = nfs_smf_get_iprop(propname, &val,
+	    DEFAULT_INSTANCE, SCF_TYPE_INTEGER, LOCKD);
+	if (ret == SA_OK) {
+		if (val <= 0)
+			fprintf(stderr, gettext(
+			    "Invalid %s from SMF"), propname);
+		else
+			lmargs.retransmittimeout = val;
+	}
+
+
+	while ((c = getopt(ac, av, "c:d:g:l:r:t:")) != EOF)
+		switch (c) {
+		case 'c': /* max_connections */
+			if ((val = atoi(optarg)) <= 0)
+				goto badval;
+			max_conns_allowed = val;
+			break;
+
+		case 'd': /* debug */
+			lmargs.debug = atoi(optarg);
+			break;
+
+		case 'g': /* grace_period */
+			if ((val = atoi(optarg)) <= 0)
+				goto badval;
+			lmargs.grace = val;
+			break;
+
+		case 'l': /* listen_backlog */
+			if ((val = atoi(optarg)) <= 0)
+				goto badval;
+			listen_backlog = val;
+			break;
+
+		case 'r': /* retrans_timeout */
+			if ((val = atoi(optarg)) <= 0)
+				goto badval;
+			lmargs.retransmittimeout = val;
+			break;
+
+		case 't': /* conn_idle_timeout */
+			if ((val = atoi(optarg)) <= 0)
+				goto badval;
+			lmargs.timout = val;
+			break;
+
+		badval:
+			fprintf(stderr, gettext(
+			    "Invalid -%c option value"), c);
+			/* FALLTHROUGH */
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+
+	/*
+	 * If there is exactly one more argument, it is the number of
+	 * servers.
+	 */
+	if (optind < ac) {
+		val = atoi(av[optind]);
+		if (val <= 0) {
+			fprintf(stderr, gettext(
+			    "Invalid max_servers argument"));
+			usage();
+		}
+		max_servers = val;
+		optind++;
+	}
+	/*
+	 * If there are two or more arguments, then this is a usage error.
+	 */
+	if (optind != ac)
+		usage();
+
+	if (lmargs.debug) {
+		printf("%s: debug= %d, conn_idle_timout= %d,"
+		    " grace_period= %d, listen_backlog= %d,"
+		    " max_connections= %d, max_servers= %d,"
+		    " retrans_timeout= %d\n",
+		    MyName, lmargs.debug, lmargs.timout,
+		    lmargs.grace, listen_backlog,
+		    max_conns_allowed, max_servers,
+		    lmargs.retransmittimeout);
+	}
+
+	/*
+	 * Set current dir to server root
+	 */
+	if (chdir(dir) < 0) {
+		(void) fprintf(stderr, "%s:  ", MyName);
+		perror(dir);
+		exit(1);
+	}
+
+	/* Daemonize, if not debug. */
+	if (lmargs.debug == 0)
+		pipe_fd = daemonize_init();
+
+	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+	/*
+	 * establish our lock on the lock file and write our pid to it.
+	 * exit if some other process holds the lock, or if there's any
+	 * error in writing/locking the file.
+	 */
+	pid = _enter_daemon_lock(LOCKD);
+	switch (pid) {
+	case 0:
+		break;
+	case -1:
+		fprintf(stderr, "error locking for %s: %s", LOCKD,
+		    strerror(errno));
+		exit(2);
+	default:
+		/* daemon was already running */
+		exit(0);
+	}
+
+	/*
+	 * Block all signals till we spawn other
+	 * threads.
+	 */
+	(void) sigfillset(&sgset);
+	(void) thr_sigsetmask(SIG_BLOCK, &sgset, NULL);
+
+	/* Unregister any previous versions. */
+	for (i = NLM_VERS; i < NLM4_VERS; i++) {
+		svc_unreg(NLM_PROG, i);
+	}
+
+	/*
+	 * Set up kernel RPC thread pool for the NLM server.
+	 */
+	if (nlmsvcpool(max_servers)) {
+		fprintf(stderr, "Can't set up kernel NLM service: %s. Exiting",
+		    strerror(errno));
+		exit(1);
+	}
+
+	/*
+	 * Set up blocked thread to do LWP creation on behalf of the kernel.
+	 */
+	if (svcwait(NLM_SVCPOOL_ID)) {
+		fprintf(stderr, "Can't set up NLM pool creator: %s. Exiting",
+		    strerror(errno));
+		exit(1);
+	}
+
+	/*
+	 * Install atexit and sigterm handlers
+	 */
+	act.sa_handler = sigterm_handler;
+	act.sa_flags = 0;
+
+	(void) sigaction(SIGTERM, &act, NULL);
+	(void) atexit(shutdown_lockd);
+
+	/*
+	 * Now open up for signal delivery
+	 */
+	(void) thr_sigsetmask(SIG_UNBLOCK, &sgset, NULL);
+
+	/*
+	 * Build a protocol block list for registration.
+	 */
+	protobp = (struct protob *)malloc(sizeof (struct protob));
+	protobp->serv = "NLM";
+	protobp->versmin = NLM_VERS;
+	protobp->versmax = NLM4_VERS;
+	protobp->program = NLM_PROG;
+	protobp->next = (struct protob *)NULL;
+
+	for (providerp = defaultproviders;
+	    *providerp != NULL; providerp++) {
+		provider = *providerp;
+		do_one(provider, NULL, protobp, nlmsvc);
+	}
+
+	free(protobp);
+
+	if (num_fds == 0) {
+		fprintf(stderr, "Could not start NLM service for any protocol."
+		    " Exiting");
+		exit(1);
+	}
+
+	end_listen_fds = num_fds;
+
+	/*
+	 * lockd is up and running as far as we are concerned.
+	 */
+	if (lmargs.debug == 0)
+		daemonize_fini(pipe_fd);
+
+	/*
+	 * Get rid of unneeded privileges.
+	 */
+	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
+	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
+
+	/*
+	 * Poll for non-data control events on the transport descriptors.
+	 */
+	poll_for_action();
+
+	/*
+	 * If we get here, something failed in poll_for_action().
+	 */
+	return (1);
+}
+
+static int
+nlmsvcpool(int maxservers)
+{
+	struct svcpool_args npa;
+
+	npa.id = NLM_SVCPOOL_ID;
+	npa.maxthreads = maxservers;
+	npa.redline = 0;
+	npa.qsize = 0;
+	npa.timeout = 0;
+	npa.stksize = 0;
+	npa.max_same_xprt = 0;
+	return (_nfssys(SVCPOOL_CREATE, &npa));
+}
+
+static int
+ncfmly_to_lmfmly(const char *ncfmly)
+{
+	if (0 == strcmp(ncfmly, NC_INET))
+		return (LM_INET);
+	if (0 == strcmp(ncfmly, NC_INET6))
+		return (LM_INET6);
+	if (0 == strcmp(ncfmly, NC_LOOPBACK))
+		return (LM_LOOPBACK);
+	return (-1);
+}
+
+static int
+nctype_to_lmprot(uint_t semantics)
+{
+	switch (semantics) {
+	case NC_TPI_CLTS:
+		return (LM_UDP);
+	case NC_TPI_COTS_ORD:
+		return (LM_TCP);
+	}
+	return (-1);
+}
+
+static dev_t
+ncdev_to_rdev(const char *ncdev)
+{
+	struct stat st;
+
+	if (stat(ncdev, &st) < 0)
+		return (NODEV);
+	return (st.st_rdev);
+}
+
+static void
+sigterm_handler(void)
+{
+	/* to call atexit handler */
+	exit(0);
+}
+
+static void
+shutdown_lockd(void)
+{
+	(void) _nfssys(KILL_LOCKMGR, NULL);
+}
+
+
+/*
+ * Establish NLM service thread.
+ */
+static int
+nlmsvc(int fd, struct netbuf addrmask, struct netconfig *nconf)
+{
+	struct lm_svc_args lma;
+
+	lma = lmargs; /* init by struct copy */
+
+	/*
+	 * The kernel code needs to reconstruct a complete
+	 * knetconfig from n_fmly, n_proto.  We use these
+	 * two fields to convey the family and semantics.
+	 */
+	lma.fd = fd;
+	lma.n_fmly = ncfmly_to_lmfmly(nconf->nc_protofmly);
+	lma.n_proto = nctype_to_lmprot(nconf->nc_semantics);
+	lma.n_rdev = ncdev_to_rdev(nconf->nc_device);
+
+	return (_nfssys(LM_SVC, &lma));
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "usage: %s [options] [max_servers]\n"), MyName);
+	(void) fprintf(stderr, gettext(
+	    "options:  (see SMF property descriptions)\n"));
+	/* Note: don't translate these */
+	(void) fprintf(stderr, "\t-c max_connections\n");
+	(void) fprintf(stderr, "\t-d debug_level\n");
+	(void) fprintf(stderr, "\t-g grace_period\n");
+	(void) fprintf(stderr, "\t-l listen_backlog\n");
+	(void) fprintf(stderr, "\t-r retrans_timeout\n");
+	(void) fprintf(stderr, "\t-t conn_idle_timeout\n");
+
+	exit(1);
+}
--- a/usr/src/cmd/fs.d/nfs/mount/Makefile	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/mount/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -118,5 +118,5 @@
 
 lint:	webnfs.h webnfs_xdr.c webnfs_client.c lint_SRCS
 
-clean:     
+clean:
 	$(RM) $(OBJS) webnfs.x webnfs.h webnfs_xdr.c webnfs_client.c
--- a/usr/src/cmd/fs.d/nfs/statd/sm_proc.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/statd/sm_proc.c	Mon Aug 26 20:42:32 2013 -0800
@@ -25,6 +25,7 @@
  */
 /*
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -59,6 +60,8 @@
 #include <netdir.h>
 #include <synch.h>
 #include <thread.h>
+#include <ifaddrs.h>
+#include <errno.h>
 #include <assert.h>
 #include "sm_statd.h"
 
@@ -89,14 +92,12 @@
 
 /* ARGSUSED */
 void
-sm_status(namep, resp)
-	sm_name *namep;
-	sm_stat_res *resp;
+sm_stat_svc(sm_name *namep, sm_stat_res *resp)
 {
 
 	if (debug)
 		(void) printf("proc sm_stat: mon_name = %s\n",
-				namep->mon_name);
+		    namep->mon_name);
 
 	resp->res_stat = stat_succ;
 	resp->state = LOCAL_STATE;
@@ -104,9 +105,7 @@
 
 /* ARGSUSED */
 void
-sm_mon(monp, resp)
-	mon *monp;
-	sm_stat_res *resp;
+sm_mon_svc(mon *monp, sm_stat_res *resp)
 {
 	mon_id *monidp;
 	monidp = &monp->mon_id;
@@ -114,7 +113,7 @@
 	rw_rdlock(&thr_rwlock);
 	if (debug) {
 		(void) printf("proc sm_mon: mon_name = %s, id = %d\n",
-		monidp->mon_name, * ((int *)monp->priv));
+		    monidp->mon_name, * ((int *)monp->priv));
 		pr_mon(monp->mon_id.mon_name);
 	}
 
@@ -132,17 +131,15 @@
 
 /* ARGSUSED */
 void
-sm_unmon(monidp, resp)
-	mon_id *monidp;
-	sm_stat *resp;
+sm_unmon_svc(mon_id *monidp, sm_stat *resp)
 {
 	rw_rdlock(&thr_rwlock);
 	if (debug) {
 		(void) printf(
-			"proc sm_unmon: mon_name = %s, [%s, %d, %d, %d]\n",
-			monidp->mon_name, monidp->my_id.my_name,
-			monidp->my_id.my_prog, monidp->my_id.my_vers,
-			monidp->my_id.my_proc);
+		    "proc sm_unmon: mon_name = %s, [%s, %d, %d, %d]\n",
+		    monidp->mon_name, monidp->my_id.my_name,
+		    monidp->my_id.my_prog, monidp->my_id.my_vers,
+		    monidp->my_id.my_proc);
 		pr_mon(monidp->mon_name);
 	}
 
@@ -154,16 +151,14 @@
 
 /* ARGSUSED */
 void
-sm_unmon_all(myidp, resp)
-	my_id *myidp;
-	sm_stat *resp;
+sm_unmon_all_svc(my_id *myidp, sm_stat *resp)
 {
 	rw_rdlock(&thr_rwlock);
 	if (debug)
 		(void) printf("proc sm_unmon_all: [%s, %d, %d, %d]\n",
-		myidp->my_name,
-		myidp->my_prog, myidp->my_vers,
-		myidp->my_proc);
+		    myidp->my_name,
+		    myidp->my_prog, myidp->my_vers,
+		    myidp->my_proc);
 	delete_mon((char *)NULL, myidp);
 	pr_mon(NULL);
 	resp->state = local_state;
@@ -174,21 +169,19 @@
  * Notifies lockd specified by name that state has changed for this server.
  */
 void
-sm_notify(ntfp)
-	stat_chge *ntfp;
+sm_notify_svc(stat_chge *ntfp)
 {
 	rw_rdlock(&thr_rwlock);
 	if (debug)
 		(void) printf("sm_notify: %s state =%d\n",
-			ntfp->mon_name, ntfp->state);
+		    ntfp->mon_name, ntfp->state);
 	send_notice(ntfp->mon_name, ntfp->state);
 	rw_unlock(&thr_rwlock);
 }
 
 /* ARGSUSED */
 void
-sm_simu_crash(myidp)
-	void *myidp;
+sm_simu_crash_svc(void *myidp)
 {
 	int i;
 	struct mon_entry *monitor_q;
@@ -727,7 +720,6 @@
 	moninfo_t *minfop;
 
 	minfop = (moninfo_t *)arg;
-
 	if (statd_call_lockd(&minfop->id, minfop->state) == -1) {
 		if (debug && minfop->id.mon_id.mon_name)
 			(void) printf("problem with notifying %s failure, "
@@ -759,7 +751,7 @@
 {
 	enum clnt_stat clnt_stat;
 	struct timeval tottimeout;
-	struct status stat;
+	struct sm_status stat;
 	my_id *my_idp;
 	char *mon_name;
 	int i;
@@ -768,7 +760,7 @@
 
 	mon_name = monp->mon_id.mon_name;
 	my_idp = &monp->mon_id.my_id;
-	(void) memset(&stat, 0, sizeof (struct status));
+	(void) memset(&stat, 0, sizeof (stat));
 	stat.mon_name = mon_name;
 	stat.state = state;
 	for (i = 0; i < 16; i++) {
@@ -781,12 +773,14 @@
 	tottimeout.tv_sec = SM_RPC_TIMEOUT;
 	tottimeout.tv_usec = 0;
 
-	if ((clnt = create_client(my_idp->my_name, my_idp->my_prog,
-		my_idp->my_vers, &tottimeout)) == (CLIENT *) NULL) {
-			return (-1);
+	clnt = create_client(my_idp->my_name, my_idp->my_prog, my_idp->my_vers,
+	    "ticotsord", &tottimeout);
+	if (clnt == NULL) {
+		return (-1);
 	}
 
-	clnt_stat = clnt_call(clnt, my_idp->my_proc, xdr_status, (char *)&stat,
+	clnt_stat = clnt_call(clnt, my_idp->my_proc,
+				xdr_sm_status, (char *)&stat,
 				xdr_void, NULL, tottimeout);
 	if (debug) {
 		(void) printf("clnt_stat=%s(%d)\n",
@@ -808,21 +802,35 @@
  * Client handle created.
  */
 CLIENT *
-create_client(host, prognum, versnum, utimeout)
-	char	*host;
-	int	prognum;
-	int	versnum;
-	struct timeval	*utimeout;
+create_client(char *host, int prognum, int versnum, char *netid,
+    struct timeval *utimeout)
 {
 	int		fd;
 	struct timeval	timeout;
 	CLIENT		*client;
 	struct t_info	tinfo;
 
-	if ((client = clnt_create_timed(host, prognum, versnum,
-			"netpath", utimeout)) == NULL) {
+	if (netid == NULL) {
+		client = clnt_create_timed(host, prognum, versnum,
+		    "netpath", utimeout);
+	} else {
+		struct netconfig *nconf;
+
+		nconf = getnetconfigent(netid);
+		if (nconf == NULL) {
+			return (NULL);
+		}
+
+		client = clnt_tp_create_timed(host, prognum, versnum, nconf,
+		    utimeout);
+
+		freenetconfigent(nconf);
+	}
+
+	if (client == NULL) {
 		return (NULL);
 	}
+
 	(void) CLNT_CONTROL(client, CLGET_FD, (caddr_t)&fd);
 	if (t_getinfo(fd, &tinfo) != -1) {
 		if (tinfo.servtype == T_CLTS) {
@@ -832,7 +840,7 @@
 			timeout.tv_usec = 0;
 			timeout.tv_sec = SM_CLTS_TIMEOUT;
 			(void) CLNT_CONTROL(client,
-				CLSET_RETRY_TIMEOUT, (caddr_t)&timeout);
+			    CLSET_RETRY_TIMEOUT, (caddr_t)&timeout);
 		}
 	} else
 		return (NULL);
@@ -1301,3 +1309,72 @@
 	}
 	return (1);
 }
+
+/*
+ * Add IP address strings to the host_name list.
+ */
+void
+merge_ips(void)
+{
+	struct ifaddrs *ifap, *cifap;
+	int error;
+
+	error = getifaddrs(&ifap);
+	if (error) {
+		syslog(LOG_WARNING, "getifaddrs error: '%s'",
+		    strerror(errno));
+		return;
+	}
+
+	for (cifap = ifap; cifap != NULL; cifap = cifap->ifa_next) {
+		struct sockaddr *sa = cifap->ifa_addr;
+		char addr_str[INET6_ADDRSTRLEN];
+		void *addr = NULL;
+
+		switch (sa->sa_family) {
+		case AF_INET: {
+			struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+			/* Skip loopback addresses. */
+			if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+				continue;
+			}
+
+			addr = &sin->sin_addr;
+			break;
+		}
+
+		case AF_INET6: {
+			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+			/* Skip loopback addresses. */
+			if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
+				continue;
+			}
+
+			addr = &sin6->sin6_addr;
+			break;
+		}
+
+		default:
+			syslog(LOG_WARNING, "Unknown address family %d for "
+			    "interface %s", sa->sa_family, cifap->ifa_name);
+			continue;
+		}
+
+		if (inet_ntop(sa->sa_family, addr, addr_str, sizeof (addr_str))
+		    == NULL) {
+			syslog(LOG_WARNING, "Failed to convert address into "
+			    "string representation for interface '%s' "
+			    "address family %d", cifap->ifa_name,
+			    sa->sa_family);
+			continue;
+		}
+
+		if (!in_host_array(addr_str)) {
+			add_to_host_array(addr_str);
+		}
+	}
+
+	freeifaddrs(ifap);
+}
--- a/usr/src/cmd/fs.d/nfs/statd/sm_statd.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/statd/sm_statd.c	Mon Aug 26 20:42:32 2013 -0800
@@ -36,7 +36,9 @@
  * contributors.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
 
 /*
  * sm_statd.c consists of routines used for the intermediate
@@ -130,20 +132,20 @@
 	for (i = 0; i < pathix; i++) {
 		(void) sprintf(state_file, "%s/statmon/state", path_name[i]);
 		if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) {
-			if ((fp_tmp = fopen(state_file, "w+"))
-				== (FILE *)NULL) {
+			if ((fp_tmp = fopen(state_file, "w+")) ==
+			    (FILE *)NULL) {
 				if (debug)
-				    syslog(LOG_ERR,
-					"can't open %s: %m",
-					state_file);
+					syslog(LOG_ERR,
+					    "can't open %s: %m",
+					    state_file);
 				continue;
 			} else
 				(void) chmod(state_file, 0644);
 		}
 		if ((fscanf(fp_tmp, "%d", &tmp_state)) == EOF) {
 			if (debug)
-			    syslog(LOG_ERR,
-				"statd: %s: file empty\n", state_file);
+				syslog(LOG_ERR,
+				    "statd: %s: file empty\n", state_file);
 			(void) fclose(fp_tmp);
 			continue;
 		}
@@ -151,7 +153,7 @@
 			LOCAL_STATE = tmp_state;
 			if (debug)
 				(void) printf("Update LOCAL STATE: %d\n",
-						tmp_state);
+				    tmp_state);
 		}
 		(void) fclose(fp_tmp);
 	}
@@ -180,8 +182,8 @@
 	for (i = 0; i < pathix; i++) {
 		(void) sprintf(state_file, "%s/statmon/state", path_name[i]);
 		if ((fp_tmp = fopen(state_file, "r+")) == (FILE *)NULL) {
-			if ((fp_tmp = fopen(state_file, "w+"))
-				== (FILE *)NULL) {
+			if ((fp_tmp = fopen(state_file, "w+")) ==
+			    (FILE *)NULL) {
 				syslog(LOG_ERR,
 				    "can't open %s: %m", state_file);
 				continue;
@@ -223,8 +225,8 @@
 
 	while ((dirp = readdir(dp)) != NULL) {
 		if (strcmp(dirp->d_name, ".") != 0 &&
-			strcmp(dirp->d_name, "..") != 0) {
-		/* rename all entries from CURRENT to BACKUP */
+		    strcmp(dirp->d_name, "..") != 0) {
+			/* rename all entries from CURRENT to BACKUP */
 			(void) move_file(CURRENT, dirp->d_name, BACKUP);
 		}
 	}
@@ -297,17 +299,17 @@
 		name = strdup(dirp->d_name);
 		if (name == (char *)NULL) {
 			syslog(LOG_ERR,
-				"statd: unable to allocate space for name %s\n",
-				dirp->d_name);
+			    "statd: unable to allocate space for name %s\n",
+			    dirp->d_name);
 			continue;
 		}
 
 		/* Create a thread to do a statd_call_statd for name */
 		if (thr_create(NULL, NULL, thr_call_statd,
-					(void *) name, 0, 0)) {
+		    (void *) name, 0, 0)) {
 			syslog(LOG_ERR,
-		"statd: unable to create thr_call_statd() for name %s.\n",
-				dirp->d_name);
+			    "statd: unable to create thr_call_statd() "
+			    "for name %s.\n", dirp->d_name);
 			free(name);
 			continue;
 		}
@@ -334,7 +336,7 @@
 	num_threads = 0;
 	while ((dirp = readdir(dp)) != NULL) {
 		if (strcmp(dirp->d_name, ".") == 0 ||
-			strcmp(dirp->d_name, "..") == 0) {
+		    strcmp(dirp->d_name, "..") == 0) {
 			continue;
 		}
 
@@ -350,7 +352,7 @@
 
 		if (debug) {
 			(void) printf("thr_statd_init: legacy %s\n",
-					dirp->d_name);
+			    dirp->d_name);
 		}
 
 		/*
@@ -372,17 +374,17 @@
 		name = strdup(dirp->d_name);
 		if (name == (char *)NULL) {
 			syslog(LOG_ERR,
-			"statd: unable to allocate space for name %s\n",
-				dirp->d_name);
+			    "statd: unable to allocate space for name %s\n",
+			    dirp->d_name);
 			continue;
 		}
 
 		/* Create a thread to do a statd_call_statd for name */
 		if (thr_create(NULL, NULL, thr_call_statd,
-					(void *) name, 0, 0)) {
+		    (void *) name, 0, 0)) {
 			syslog(LOG_ERR,
-		"statd: unable to create thr_call_statd() for name %s.\n",
-				dirp->d_name);
+			    "statd: unable to create thr_call_statd() "
+			    "for name %s.\n", dirp->d_name);
 			free(name);
 			continue;
 		}
@@ -410,7 +412,7 @@
 		if ((mkdir(buf, SM_DIRECTORY_MODE)) == -1) {
 			if (errno != EEXIST)
 				syslog(LOG_ERR, "statd: mkdir %s error %m\n",
-					buf);
+				    buf);
 			else
 				copydir_from_to(BACKUP, buf);
 		} else
@@ -434,7 +436,7 @@
 	/* Continue to notify statd on hosts that were unreachable. */
 	if (thr_create(NULL, NULL, sm_try, NULL, THR_DETACHED, 0))
 		syslog(LOG_ERR,
-			"statd: unable to create thread for sm_try().\n");
+		    "statd: unable to create thread for sm_try().\n");
 	thr_exit((void *) 0);
 #ifdef lint
 	return (0);
@@ -489,8 +491,8 @@
 			if (n <= 0) {
 				if (debug >= 2) {
 					(void) printf(
-					"thr_call_statd: can't read link %s\n",
-							path);
+					    "thr_call_statd: can't read "
+					    "link %s\n", path);
 				}
 			} else {
 				rname[n] = '\0';
@@ -581,8 +583,8 @@
 	tottimeout.tv_sec = SM_RPC_TIMEOUT;
 	tottimeout.tv_usec = 0;
 
-	if ((clnt = create_client(name_or_addr, SM_PROG, SM_VERS,
-	    &tottimeout)) == (CLIENT *) NULL) {
+	if ((clnt = create_client(name_or_addr, SM_PROG, SM_VERS, NULL,
+	    &tottimeout)) == NULL) {
 		return (-1);
 	}
 
@@ -675,8 +677,8 @@
 				 */
 				if (delay == 0)
 					syslog(LOG_WARNING,
-					"statd: host %s is not responding\n",
-						nl->name);
+					    "statd: host %s is not "
+					    "responding\n", nl->name);
 			}
 		}
 		/*
@@ -1035,16 +1037,16 @@
 	char dirpath[MAXPATHLEN];
 	char rname[MAXNAMELEN + 1]; /* +1 for NULL term */
 
-	if (strlen(name) + strlen(dir1) + (dir2 != NULL ? strlen(dir2) : 0)
-			+ 3 > MAXPATHLEN) {
+	if (strlen(name) + strlen(dir1) + (dir2 != NULL ? strlen(dir2) : 0) +
+	    3 > MAXPATHLEN) {
 		if (dir2 != NULL)
 			syslog(LOG_ERR,
-				"statd: pathname too long: %s/%s/%s\n",
-						dir1, dir2, name);
+			    "statd: pathname too long: %s/%s/%s\n",
+			    dir1, dir2, name);
 		else
 			syslog(LOG_ERR,
-				"statd: pathname too long: %s/%s\n",
-						dir1, name);
+			    "statd: pathname too long: %s/%s\n",
+			    dir1, name);
 
 		return;
 	}
@@ -1078,12 +1080,13 @@
 				if (debug >= 2) {
 					if (error < 0) {
 						(void) printf(
-					"remove_name: can't unlink %s\n",
-							dirpath);
+						    "remove_name: can't "
+						    "unlink %s\n",
+						    dirpath);
 					} else {
 						(void) printf(
-					"remove_name: unlinked %s\n",
-							dirpath);
+						    "remove_name: unlinked ",
+						    "%s\n", dirpath);
 					}
 				}
 			}
@@ -1093,7 +1096,7 @@
 			 * here for analysis by the system administrator.
 			 */
 			syslog(LOG_ERR,
-				"statd: can't read link %s: %m\n", path);
+			    "statd: can't read link %s: %m\n", path);
 		}
 	}
 
@@ -1122,13 +1125,13 @@
 
 	if ((dp = opendir(dir)) == (DIR *)NULL) {
 		syslog(LOG_ERR, "count_symlinks: open %s dir, error %m\n",
-			dir);
+		    dir);
 		return (-1);
 	}
 
 	while ((dirp = readdir(dp)) != NULL) {
 		if (strcmp(dirp->d_name, ".") == 0 ||
-			strcmp(dirp->d_name, "..") == 0) {
+		    strcmp(dirp->d_name, "..") == 0) {
 			continue;
 		}
 
@@ -1141,8 +1144,8 @@
 			if (n <= 0) {
 				if (debug >= 2) {
 					(void) printf(
-					"count_symlinks: can't read link %s\n",
-						lpath);
+					    "count_symlinks: can't read link "
+					    "%s\n", lpath);
 				}
 				continue;
 			}
@@ -1329,15 +1332,15 @@
 			(void) printf("record_addr: addr= %x\n", addr.s_addr);
 		else if (family == AF_INET6)
 			(void) printf("record_addr: addr= %x\n", \
-				((struct in6_addr *)addr6)->s6_addr);
+			    ((struct in6_addr *)addr6)->s6_addr);
 	}
 
 	if (family == AF_INET) {
 		if (addr.s_addr == INADDR_ANY ||
 		    ((addr.s_addr && 0xff000000) == 0)) {
 			syslog(LOG_DEBUG,
-				"record_addr: illegal IP address %x\n",
-				addr.s_addr);
+			    "record_addr: illegal IP address %x\n",
+			    addr.s_addr);
 			return;
 		}
 	}
@@ -1346,28 +1349,27 @@
 	famstr = family2string(family);
 	if (famstr == NULL) {
 		syslog(LOG_DEBUG,
-			"record_addr: unsupported address family %d\n",
-			family);
+		    "record_addr: unsupported address family %d\n",
+		    family);
 		return;
 	}
 
 	switch (family) {
 		char abuf[INET6_ADDRSTRLEN];
-	    case AF_INET:
+	case AF_INET:
 		(void) sprintf(ascii_addr, "%s.%s", famstr, inet_ntoa(addr));
 		break;
 
-	    case AF_INET6:
+	case AF_INET6:
 		(void) sprintf(ascii_addr, "%s.%s", famstr,\
 		    inet_ntop(family, addr6, abuf, sizeof (abuf)));
 		break;
 
-	    default:
+	default:
 		if (debug) {
 			(void) printf(
-		"record_addr: family2string supports unknown family %d (%s)\n",
-				family,
-				famstr);
+			    "record_addr: family2string supports unknown "
+			    "family %d (%s)\n", family, famstr);
 		}
 		free(famstr);
 		return;
@@ -1389,13 +1391,13 @@
 	 */
 	for (i = 0; i < pathix; i++) {
 		path_len = strlen(path_name[i]) +
-					strlen("/statmon/sm/") +
-					strlen(name) + 1;
+		    strlen("/statmon/sm/") +
+		    strlen(name) + 1;
 
 		if (path_len > MAXPATHLEN) {
 			syslog(LOG_ERR,
-				"statd: pathname too long: %s/statmon/sm/%s\n",
-				path_name[i], name);
+			    "statd: pathname too long: %s/statmon/sm/%s\n",
+			    path_name[i], name);
 			continue;
 		}
 		(void) strcpy(path, path_name[i]);
--- a/usr/src/cmd/fs.d/nfs/statd/sm_statd.h	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/statd/sm_statd.h	Mon Aug 26 20:42:32 2013 -0800
@@ -37,11 +37,14 @@
  * contributors.
  */
 
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
 #ifndef	_SM_STATD_H
 #define	_SM_STATD_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -182,20 +185,27 @@
 extern void	delete_file(char *name);
 extern void	record_name(char *name, int op);
 extern void	sm_crash(void);
-extern void	sm_notify(stat_chge *ntfp);
 extern void	statd_init();
 extern void	merge_hosts(void);
-extern CLIENT	*create_client(char *, int, int, struct timeval *);
+extern void	merge_ips(void);
+extern CLIENT	*create_client(char *, int, int, char *, struct timeval *);
 extern char	*xmalloc(unsigned);
-extern void	sm_status(sm_name *namep, sm_stat_res *resp);
-extern void	sm_mon(mon *monp, sm_stat_res *resp);
-extern void	sm_unmon(mon_id *monidp, sm_stat *resp);
-extern void	sm_unmon_all(my_id *myidp, sm_stat *resp);
-extern void	sm_simu_crash(void *myidp);
+
+/*
+ * RPC service functions, slightly different here than the
+ * generated ones in sm_inter.h
+ */
+extern void	nsmaddrproc1_reg(reg1args *, reg1res *);
+extern void	sm_stat_svc(sm_name *namep, sm_stat_res *resp);
+extern void	sm_mon_svc(mon *monp, sm_stat_res *resp);
+extern void	sm_unmon_svc(mon_id *monidp, sm_stat *resp);
+extern void	sm_unmon_all_svc(my_id *myidp, sm_stat *resp);
+extern void	sm_simu_crash_svc(void *myidp);
+extern void	sm_notify_svc(stat_chge *ntfp);
+
 extern void	sm_inithash();
 extern void	copydir_from_to(char *from_dir, char *to_dir);
 extern int	str_cmp_unqual_hostname(char *, char *);
-extern void	nsmaddrproc1_reg(reg1args *, reg1res *);
 extern void	record_addr(char *name, sa_family_t family, struct netobj *ah);
 extern int	is_symlink(char *file);
 extern int	create_symlink(char *todir, char *rname, char *lname);
--- a/usr/src/cmd/fs.d/nfs/statd/sm_svc.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/cmd/fs.d/nfs/statd/sm_svc.c	Mon Aug 26 20:42:32 2013 -0800
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -35,6 +36,10 @@
  * contributors.
  */
 
+/*
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
@@ -219,11 +224,13 @@
 			local = (char *(*)()) nsmaddrproc1_reg;
 			break;
 
+		case NSMADDRPROC1_UNREG: /* Not impl. */
 		default:
 			svcerr_noproc(transp);
 			return;
 		}
 	} else {
+		/* Must be SM_PROG */
 		switch (rqstp->rq_proc) {
 		case NULLPROC:
 			svc_sendreply(transp, xdr_void, (caddr_t)NULL);
@@ -232,37 +239,37 @@
 		case SM_STAT:
 			xdr_argument = xdr_sm_name;
 			xdr_result = xdr_sm_stat_res;
-			local = (char *(*)()) sm_status;
+			local = (char *(*)()) sm_stat_svc;
 			break;
 
 		case SM_MON:
 			xdr_argument = xdr_mon;
 			xdr_result = xdr_sm_stat_res;
-			local = (char *(*)()) sm_mon;
+			local = (char *(*)()) sm_mon_svc;
 			break;
 
 		case SM_UNMON:
 			xdr_argument = xdr_mon_id;
 			xdr_result = xdr_sm_stat;
-			local = (char *(*)()) sm_unmon;
+			local = (char *(*)()) sm_unmon_svc;
 			break;
 
 		case SM_UNMON_ALL:
 			xdr_argument = xdr_my_id;
 			xdr_result = xdr_sm_stat;
-			local = (char *(*)()) sm_unmon_all;
+			local = (char *(*)()) sm_unmon_all_svc;
 			break;
 
 		case SM_SIMU_CRASH:
 			xdr_argument = xdr_void;
 			xdr_result = xdr_void;
-			local = (char *(*)()) sm_simu_crash;
+			local = (char *(*)()) sm_simu_crash_svc;
 			break;
 
 		case SM_NOTIFY:
 			xdr_argument = xdr_stat_chge;
 			xdr_result = xdr_void;
-			local = (char *(*)()) sm_notify;
+			local = (char *(*)()) sm_notify_svc;
 			break;
 
 		default:
@@ -284,8 +291,8 @@
 	}
 
 	if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
-			syslog(LOG_ERR, "statd: unable to free arguments\n");
-		}
+		syslog(LOG_ERR, "statd: unable to free arguments\n");
+	}
 }
 
 /*
@@ -584,6 +591,9 @@
 	/* Get other aliases from each interface. */
 	merge_hosts();
 
+	/* Get all of the configured IP addresses. */
+	merge_ips();
+
 	/*
 	 * Set to automatic mode such that threads are automatically
 	 * created
--- a/usr/src/head/Makefile	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/head/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -263,7 +263,6 @@
 	nfs_prot.h	\
 	nfs4_prot.h	\
 	nis.h		\
-	nlm_prot.h	\
 	rex.h		\
 	rquota.h	\
 	rstat.h		\
--- a/usr/src/head/rpcsvc/nlm_prot.x	Mon Aug 26 13:13:26 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,452 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Network lock manager protocol definition
- * Copyright (C) 1986, 1992, 1993, 1997, 1999 by Sun Microsystems, Inc.
- * All rights reserved.
- *
- * Protocol used between local lock manager and remote lock manager.
- *
- * There are currently 3 versions of the protocol in use.  Versions 1
- * and 3 are used with NFS version 2.  Version 4 is used with NFS
- * version 3.
- *
- * (Note: there is also a version 2, but it defines an orthogonal set of
- * procedures that the status monitor uses to notify the lock manager of
- * changes in monitored systems.)
- */
-
-%#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#if RPC_HDR
-%
-%#include <rpc/rpc_sztypes.h>
-%
-#endif
-
-#ifdef RPC_HDR
-%#define LM_MAXSTRLEN	1024
-%#define LM_MAXNAMELEN	(LM_MAXSTRLEN + 1)
-#endif
-
-/*
- * Types for versions 1 and 3.
- */
-
-/*
- * Status of a call to the lock manager.  The lower case enums violate the
- * current style guide, but we're stuck with 'em.
- */
-
-enum nlm_stats {
-	nlm_granted = 0,
-	nlm_denied = 1,
-	nlm_denied_nolocks = 2,
-	nlm_blocked = 3,
-	nlm_denied_grace_period = 4,
-	nlm_deadlck = 5
-};
-
-/*
- * The holder of a conflicting lock.
- */
-
-struct nlm_holder {
-	bool exclusive;
-	int svid;
-	netobj oh;
-	unsigned l_offset;
-	unsigned l_len;
-};
-
-union nlm_testrply switch (nlm_stats stat) {
-	case nlm_denied:
-		struct nlm_holder holder;
-	default:
-		void;
-};
-
-struct nlm_stat {
-	nlm_stats stat;
-};
-
-struct nlm_res {
-	netobj cookie;
-	nlm_stat stat;
-};
-
-struct nlm_testres {
-	netobj cookie;
-	nlm_testrply stat;
-};
-
-struct nlm_lock {
-	string caller_name<LM_MAXSTRLEN>;
-	netobj fh;		/* identify a file */
-	netobj oh;		/* identify owner of a lock */
-	int svid;		/* generated from pid for svid */
-	unsigned l_offset;
-	unsigned l_len;
-};
-
-struct nlm_lockargs {
-	netobj cookie;
-	bool block;
-	bool exclusive;
-	struct nlm_lock alock;
-	bool reclaim;		/* used for recovering locks */
-	int state;		/* specify local status monitor state */
-};
-
-struct nlm_cancargs {
-	netobj cookie;
-	bool block;
-	bool exclusive;
-	struct nlm_lock alock;
-};
-
-struct nlm_testargs {
-	netobj cookie;
-	bool exclusive;
-	struct nlm_lock alock;
-};
-
-struct nlm_unlockargs {
-	netobj cookie;
-	struct nlm_lock alock;
-};
-
-#ifdef RPC_HDR
-%/*
-% * The following enums are actually bit encoded for efficient
-% * boolean algebra.... DON'T change them.....
-% * The mixed-case enums violate the present style guide, but we're
-% * stuck with 'em.
-% */
-#endif
-
-enum	fsh_mode {
-	fsm_DN  = 0,	/* deny none */
-	fsm_DR  = 1,	/* deny read */
-	fsm_DW  = 2,	/* deny write */
-	fsm_DRW = 3	/* deny read/write */
-};
-
-enum	fsh_access {
-	fsa_NONE = 0,	/* for completeness */
-	fsa_R    = 1,	/* read only */
-	fsa_W    = 2,	/* write only */
-	fsa_RW   = 3	/* read/write */
-};
-
-struct	nlm_share {
-	string caller_name<LM_MAXSTRLEN>;
-	netobj	fh;
-	netobj	oh;
-	fsh_mode	mode;
-	fsh_access	access;
-};
-
-struct	nlm_shareargs {
-	netobj	cookie;
-	nlm_share	share;
-	bool	reclaim;
-};
-
-struct	nlm_shareres {
-	netobj	cookie;
-	nlm_stats	stat;
-	int	sequence;
-};
-
-struct	nlm_notify {
-	string name<LM_MAXNAMELEN>;
-	int state;
-};
-
-/*
- * Types for version 4.
- *
- * This revision is designed to work with NFS V3.  The main changes from
- * NFS V2 to V3 that affect the NLM protocol are that all file offsets
- * and sizes are now unsigned 64-bit ints, and file handles are now
- * variable length.  In NLM V1 and V3, the fixed-length V2 file handle
- * was encoded as a 'netobj', which is a count followed by the data
- * bytes.  For NLM 4, the file handle is already a count followed by
- * data bytes, so the handle is copied directly into the netobj, rather
- * than being encoded with an additional byte count.
- */
-
-/*
- * Status of a call to the lock manager.
- */
-
-enum nlm4_stats {
-	NLM4_GRANTED = 0,		/* lock was granted */
-	NLM4_DENIED = 1,		/* lock was not granted, usually */
-					/* due to conflicting lock */
-	NLM4_DENIED_NOLOCKS = 2,	/* not granted: out of resources */
-	NLM4_BLOCKED = 3,		/* not granted: expect callback */
-					/* when granted */
-	NLM4_DENIED_GRACE_PERIOD = 4,	/* not granted: server is */
-					/* reestablishing old locks */
-	NLM4_DEADLCK = 5,		/* not granted: deadlock detected */
-	NLM4_ROFS = 6,			/* not granted: read-only filesystem */
-	NLM4_STALE_FH = 7,		/* not granted: stale file handle */
-	NLM4_FBIG = 8,			/* not granted: offset or length */
-					/* too big */
-	NLM4_FAILED = 9			/* not granted: some other error */
-};
-
-/*
- * The holder of a conflicting lock.
- */
-
-struct nlm4_holder {
-	bool exclusive;
-	int32 svid;
-	netobj oh;
-	uint64 l_offset;
-	uint64 l_len;
-};
-
-union nlm4_testrply switch (nlm4_stats stat) {
-	case NLM4_DENIED:
-		struct nlm4_holder holder;
-	default:
-		void;
-};
-
-struct nlm4_stat {
-	nlm4_stats stat;
-};
-
-struct nlm4_res {
-	netobj cookie;
-	nlm4_stat stat;
-};
-
-struct nlm4_testres {
-	netobj cookie;
-	nlm4_testrply stat;
-};
-
-struct nlm4_lock {
-	string caller_name<LM_MAXSTRLEN>;
-	netobj fh;		/* identify a file */
-	netobj oh;		/* identify owner of a lock */
-	int32 svid;		/* generated from pid for svid */
-	uint64 l_offset;
-	uint64 l_len;
-};
-
-struct nlm4_lockargs {
-	netobj cookie;
-	bool block;
-	bool exclusive;
-	struct nlm4_lock alock;
-	bool reclaim;		/* used for recovering locks */
-	int32 state;		/* specify local status monitor state */
-};
-
-struct nlm4_cancargs {
-	netobj cookie;
-	bool block;
-	bool exclusive;
-	struct nlm4_lock alock;
-};
-
-struct nlm4_testargs {
-	netobj cookie;
-	bool exclusive;
-	struct nlm4_lock alock;
-};
-
-struct nlm4_unlockargs {
-	netobj cookie;
-	struct nlm4_lock alock;
-};
-
-#ifdef RPC_HDR
-%/*
-% * The following enums are actually bit encoded for efficient
-% * boolean algebra.... DON'T change them.....
-% */
-#endif
-
-enum	fsh4_mode {
-	FSM_DN  = 0,	/* deny none */
-	FSM_DR  = 1,	/* deny read */
-	FSM_DW  = 2,	/* deny write */
-	FSM_DRW = 3	/* deny read/write */
-};
-
-enum	fsh4_access {
-	FSA_NONE = 0,	/* for completeness */
-	FSA_R    = 1,	/* read only */
-	FSA_W    = 2,	/* write only */
-	FSA_RW   = 3	/* read/write */
-};
-
-struct	nlm4_share {
-	string caller_name<LM_MAXSTRLEN>;
-	netobj	fh;
-	netobj	oh;
-	fsh4_mode	mode;
-	fsh4_access	access;
-};
-
-struct	nlm4_shareargs {
-	netobj	cookie;
-	nlm4_share	share;
-	bool	reclaim;
-};
-
-struct	nlm4_shareres {
-	netobj	cookie;
-	nlm4_stats	stat;
-	int32	sequence;
-};
-
-struct	nlm4_notify {
-	string name<LM_MAXNAMELEN>;
-	int32 state;
-};
-
-/*
- * Over-the-wire protocol used between the network lock managers
- */
-
-program NLM_PROG {
-	version NLM_VERS {
-
-		nlm_testres
-			NLM_TEST(nlm_testargs) =		1;
-
-		nlm_res
-			NLM_LOCK(nlm_lockargs) =		2;
-
-		nlm_res
-			NLM_CANCEL(nlm_cancargs) =		3;
-
-		nlm_res
-			NLM_UNLOCK(nlm_unlockargs) =		4;
-		/*
-		 * remote lock manager call-back to grant lock
-		 */
-		nlm_res
-			NLM_GRANTED(nlm_testargs) =		5;
-
-		/*
-		 * message passing style of requesting lock
-		 */
-
-		void
-			NLM_TEST_MSG(nlm_testargs) =		6;
-		void
-			NLM_LOCK_MSG(nlm_lockargs) =		7;
-		void
-			NLM_CANCEL_MSG(nlm_cancargs) =		8;
-		void
-			NLM_UNLOCK_MSG(nlm_unlockargs) =	9;
-		void
-			NLM_GRANTED_MSG(nlm_testargs) =		10;
-		void
-			NLM_TEST_RES(nlm_testres) =		11;
-		void
-			NLM_LOCK_RES(nlm_res) =			12;
-		void
-			NLM_CANCEL_RES(nlm_res) =		13;
-		void
-			NLM_UNLOCK_RES(nlm_res) =		14;
-		void
-			NLM_GRANTED_RES(nlm_res) =		15;
-	} = 1;
-
-	version NLM_VERSX {
-		nlm_shareres
-			NLM_SHARE(nlm_shareargs) =		20;
-		nlm_shareres
-			NLM_UNSHARE(nlm_shareargs) =		21;
-		nlm_res
-			NLM_NM_LOCK(nlm_lockargs) =		22;
-		void
-			NLM_FREE_ALL(nlm_notify) =		23;
-	} = 3;
-
-	version NLM4_VERS {
-		void
-			NLMPROC4_NULL(void) =			0;
-		nlm4_testres
-			NLMPROC4_TEST(nlm4_testargs) =		1;
-		nlm4_res
-			NLMPROC4_LOCK(nlm4_lockargs) =		2;
-		nlm4_res
-			NLMPROC4_CANCEL(nlm4_cancargs) =	3;
-		nlm4_res
-			NLMPROC4_UNLOCK(nlm4_unlockargs) =	4;
-		/*
-		 * remote lock manager call-back to grant lock
-		 */
-		nlm4_res
-			NLMPROC4_GRANTED(nlm4_testargs) =	5;
-
-		/*
-		 * message passing style of requesting lock
-		 */
-
-		void
-			NLMPROC4_TEST_MSG(nlm4_testargs) =	6;
-		void
-			NLMPROC4_LOCK_MSG(nlm4_lockargs) =	7;
-		void
-			NLMPROC4_CANCEL_MSG(nlm4_cancargs) =	8;
-		void
-			NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) =	9;
-		void
-			NLMPROC4_GRANTED_MSG(nlm4_testargs) =	10;
-		void
-			NLMPROC4_TEST_RES(nlm4_testres) =	11;
-		void
-			NLMPROC4_LOCK_RES(nlm4_res) =		12;
-		void
-			NLMPROC4_CANCEL_RES(nlm4_res) =		13;
-		void
-			NLMPROC4_UNLOCK_RES(nlm4_res) =		14;
-		void
-			NLMPROC4_GRANTED_RES(nlm4_res) =	15;
-
-		/*
-		 * DOS-style file sharing
-		 */
-
-		nlm4_shareres
-			NLMPROC4_SHARE(nlm4_shareargs) =	20;
-		nlm4_shareres
-			NLMPROC4_UNSHARE(nlm4_shareargs) =	21;
-		nlm4_res
-			NLMPROC4_NM_LOCK(nlm4_lockargs) =	22;
-		void
-			NLMPROC4_FREE_ALL(nlm4_notify) =	23;
-	} = 4;
-
-} = 100021;
--- a/usr/src/lib/librpcsvc/common/mapfile-vers	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/lib/librpcsvc/common/mapfile-vers	Mon Aug 26 20:42:32 2013 -0800
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 #
 
 #
@@ -76,8 +77,6 @@
 	xdr_fhandle;
 	xdr_fhandle3;
 	xdr_fhstatus;
-	xdr_fsh4_access;
-	xdr_fsh4_mode;
 	xdr_fsh_access;
 	xdr_fsh_mode;
 	xdr_groupnode;
@@ -127,17 +126,17 @@
 	xdr_ppathcnf;
 	xdr_reg1args;
 	xdr_reg1res;
-	xdr_res;
 	xdr_rstat_timeval;
 	xdr_rusers_utmp;
 	xdr_sm_name;
+	xdr_sm_res;
 	xdr_sm_stat;
 	xdr_sm_stat_res;
+	xdr_sm_status;
 	xdr_sprayarr;
 	xdr_spraycumul;
 	xdr_spraytimeval;
 	xdr_stat_chge;
-	xdr_status;
 	xdr_timeval;
 	xdr_uint32;
 	xdr_uint64;
--- a/usr/src/uts/Makefile	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/uts/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -135,6 +135,7 @@
 		common/fs \
 		common/gssapi \
 		common/idmap \
+		common/klm \
 		common/inet \
 		common/inet/ipf/netinet \
 		common/inet/kssl \
@@ -161,11 +162,14 @@
 
 #
 # Subset of COMMON_HDRDIRS in which at least one header is generated
-# at runtime (e.g., rpcgen).  (This is a partial list; there are
-# other directories that should be included and do not yet have the
-# necessary Makefile support.  See 6414855.)
+# at runtime (e.g., rpcgen), and in which "make clean" should run.
+# Other directories should be included here, but do not yet have the
+# necessary Makefile support (make clean).  See 6414855.
 #
-DYNHDRDIRS = common/rpcsvc common/idmap common/sys
+DYNHDRDIRS =	common/idmap \
+		common/klm \
+		common/rpcsvc \
+		common/sys
 
 sparc_HDRDIRS= sun/sys
 i386_HDRDIRS= i86pc/vm i86xpv/vm
@@ -185,6 +189,7 @@
 	@cd common/rpcsvc; pwd; $(MAKE) $@
 	@cd common/gssapi; pwd; $(MAKE) $@
 	@cd common/idmap; pwd; $(MAKE) $@
+	@cd common/klm; pwd; $(MAKE) $@
 
 clean clobber: $($(MACH)_ARCHITECTURES) $(DYNHDRDIRS)
 	@if [ '$(PATCH_BUILD)' != '#' ] ; then \
--- a/usr/src/uts/common/Makefile.files	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/uts/common/Makefile.files	Mon Aug 26 20:42:32 2013 -0800
@@ -1467,6 +1467,23 @@
 		rpcsys.o	xdr_sizeof.o	clnt_rdma.o	svc_rdma.o \
 		xdr_rdma.o	rdma_subr.o	xdrrdma_sizeof.o
 
+KLMMOD_OBJS +=	klmmod.o \
+		nlm_impl.o \
+		nlm_rpc_handle.o \
+		nlm_dispatch.o \
+		nlm_rpc_svc.o \
+		nlm_client.o \
+		nlm_service.o \
+		nlm_prot_clnt.o \
+		nlm_prot_xdr.o \
+		nlm_rpc_clnt.o \
+		nsm_addr_clnt.o \
+		nsm_addr_xdr.o \
+		sm_inter_clnt.o \
+		sm_inter_xdr.o
+
+KLMOPS_OBJS +=	klmops.o
+
 TLIMOD_OBJS +=	tlimod.o	t_kalloc.o	t_kbind.o	t_kclose.o \
 		t_kconnect.o	t_kfree.o	t_kgtstate.o	t_kopen.o \
 		t_krcvudat.o	t_ksndudat.o	t_kspoll.o	t_kunbind.o \
--- a/usr/src/uts/common/Makefile.rules	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/uts/common/Makefile.rules	Mon Aug 26 20:42:32 2013 -0800
@@ -1392,6 +1392,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/klm/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/kmdb/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -2592,6 +2596,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/kiconv/kiconv_tc/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/klm/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/kmdb/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- a/usr/src/uts/common/io/tl.c	Mon Aug 26 13:13:26 2013 -0800
+++ b/usr/src/uts/common/io/tl.c	Mon Aug 26 20:42:32 2013 -0800
@@ -24,6 +24,7 @@
  */
 /*
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
 /*
@@ -4252,7 +4253,7 @@
 static void
 tl_connected_cots_addr_req(mblk_t *mp, tl_endpt_t *tep)
 {
-	tl_endpt_t		*peer_tep;
+	tl_endpt_t		*peer_tep = tep->te_conp;
 	size_t			ack_sz;
 	mblk_t			*ackmp;
 	struct T_addr_ack	*taa;
@@ -4263,11 +4264,15 @@
 		return;
 	}
 
+	if (peer_tep == NULL || peer_tep->te_closing) {
+		tl_error_ack(tep->te_wq, mp, TSYSERR, ECONNRESET, T_ADDR_REQ);
+		return;
+	}
+
 	ASSERT(tep->te_state >= TS_IDLE);
 
 	ack_sz = sizeof (struct T_addr_ack);
 	ack_sz += T_ALIGN(tep->te_alen);
-	peer_tep = tep->te_conp;
 	ack_sz += peer_tep->te_alen;
 
 	ackmp = tpi_ack_alloc(mp, ack_sz, M_PCPROTO, T_ADDR_ACK);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/Makefile	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,73 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
+# Copyright (c) 2012 by Delphix. All rights reserved.
+#
+
+include ../../../Makefile.master
+
+NLM_PROT_X= ../rpcsvc/nlm_prot.x
+SM_INTER_X= ../rpcsvc/sm_inter.x
+NSM_ADDR_X= ../rpcsvc/nsm_addr.x
+
+RPCGENFLAGS = -C -M -i 0
+SED_INCL='/^.include/s:\.\..*/rpcsvc:rpcsvc:'
+
+DERIVED_FILES= nlm_prot_clnt.c nlm_prot_xdr.c \
+		sm_inter_clnt.c sm_inter_xdr.c \
+		nsm_addr_clnt.c nsm_addr_xdr.c
+
+install_h: all_h
+
+all_h: $(DERIVED_FILES)
+
+nlm_prot_clnt.c : $(NLM_PROT_X) nlm_prot_clnt.sed
+	$(RPCGEN) $(RPCGENFLAGS) -l -o $@.tmp $(NLM_PROT_X)
+	sed -f nlm_prot_clnt.sed < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+nlm_prot_xdr.c : $(NLM_PROT_X)
+	$(RPCGEN) $(RPCGENFLAGS) -c -o $@.tmp $(NLM_PROT_X)
+	sed -e $(SED_INCL) < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+sm_inter_clnt.c : $(SM_INTER_X) sm_inter_clnt.sed
+	$(RPCGEN) $(RPCGENFLAGS) -l -o $@.tmp $(SM_INTER_X)
+	sed -f sm_inter_clnt.sed < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+sm_inter_xdr.c : $(SM_INTER_X)
+	$(RPCGEN) $(RPCGENFLAGS) -c -o $@.tmp $(SM_INTER_X)
+	sed -e $(SED_INCL) < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+nsm_addr_clnt.c : $(NSM_ADDR_X) nsm_addr_clnt.sed
+	$(RPCGEN) $(RPCGENFLAGS) -l -o $@.tmp $(NSM_ADDR_X)
+	sed -f nsm_addr_clnt.sed < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+nsm_addr_xdr.c : $(NSM_ADDR_X)
+	$(RPCGEN) $(RPCGENFLAGS) -c -o $@.tmp $(NSM_ADDR_X)
+	sed -e $(SED_INCL) < $@.tmp > $@
+	$(RM) -f $@.tmp
+
+check:
+
+clean:
+	$(RM) $(DERIVED_FILES)
+
+clobber:	clean
+
+lint:
+
+.KEEP_STATE:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/klmmod.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,533 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy is of the CDDL is also available via the Internet
+ * at http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager, server-side and common.
+ *
+ * This file contains all the external entry points of klmmod.
+ * Basically, this is the "glue" to the BSD nlm code.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/flock.h>
+
+#include <nfs/nfs.h>
+#include <nfs/nfssys.h>
+#include <nfs/lm.h>
+#include <rpcsvc/nlm_prot.h>
+#include "nlm_impl.h"
+
+static struct modlmisc modlmisc = {
+	&mod_miscops, "lock mgr common module"
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modlmisc, NULL
+};
+
+/*
+ * Cluster node ID.  Zero unless we're part of a cluster.
+ * Set by lm_set_nlmid_flk.  Pass to lm_set_nlm_status.
+ * We're not yet doing "clustered" NLM stuff.
+ */
+int lm_global_nlmid = 0;
+
+/*
+ * Call-back hook for clusters: Set lock manager status.
+ * If this hook is set, call this instead of the ususal
+ * flk_set_lockmgr_status(FLK_LOCKMGR_UP / DOWN);
+ */
+void (*lm_set_nlm_status)(int nlm_id, flk_nlm_status_t) = NULL;
+
+/*
+ * Call-back hook for clusters: Delete all locks held by sysid.
+ * Call from code that drops all client locks (for which we're
+ * the server) i.e. after the SM tells us a client has crashed.
+ */
+void (*lm_remove_file_locks)(int) = NULL;
+
+krwlock_t		lm_lck;
+zone_key_t		nlm_zone_key;
+
+/*
+ * Init/fini per-zone stuff for klm
+ */
+/* ARGSUSED */
+void *
+lm_zone_init(zoneid_t zoneid)
+{
+	struct nlm_globals *g;
+
+	g = kmem_zalloc(sizeof (*g), KM_SLEEP);
+
+	avl_create(&g->nlm_hosts_tree, nlm_host_cmp,
+	    sizeof (struct nlm_host),
+	    offsetof(struct nlm_host, nh_by_addr));
+
+	g->nlm_hosts_hash = mod_hash_create_idhash("nlm_host_by_sysid",
+	    64, mod_hash_null_valdtor);
+
+	TAILQ_INIT(&g->nlm_idle_hosts);
+	TAILQ_INIT(&g->nlm_slocks);
+
+	mutex_init(&g->lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&g->nlm_gc_sched_cv, NULL, CV_DEFAULT, NULL);
+	cv_init(&g->nlm_gc_finish_cv, NULL, CV_DEFAULT, NULL);
+	mutex_init(&g->clean_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	g->lockd_pid = 0;
+	g->run_status = NLM_ST_DOWN;
+
+	nlm_globals_register(g);
+	return (g);
+}
+
+/* ARGSUSED */
+void
+lm_zone_fini(zoneid_t zoneid, void *data)
+{
+	struct nlm_globals *g = data;
+
+	ASSERT(avl_is_empty(&g->nlm_hosts_tree));
+	avl_destroy(&g->nlm_hosts_tree);
+	mod_hash_destroy_idhash(g->nlm_hosts_hash);
+
+	ASSERT(g->nlm_gc_thread == NULL);
+	mutex_destroy(&g->lock);
+	cv_destroy(&g->nlm_gc_sched_cv);
+	cv_destroy(&g->nlm_gc_finish_cv);
+	mutex_destroy(&g->clean_lock);
+
+	nlm_globals_unregister(g);
+	kmem_free(g, sizeof (*g));
+}
+
+
+
+/*
+ * ****************************************************************
+ * module init, fini, info
+ */
+int
+_init()
+{
+	int retval;
+
+	rw_init(&lm_lck, NULL, RW_DEFAULT, NULL);
+	nlm_init();
+
+	zone_key_create(&nlm_zone_key, lm_zone_init, NULL, lm_zone_fini);
+	/* Per-zone lockmgr data.  See: os/flock.c */
+	zone_key_create(&flock_zone_key, flk_zone_init, NULL, flk_zone_fini);
+
+	retval = mod_install(&modlinkage);
+	if (retval == 0)
+		return (0);
+
+	/*
+	 * mod_install failed! undo above, reverse order
+	 */
+
+	(void) zone_key_delete(flock_zone_key);
+	flock_zone_key = ZONE_KEY_UNINITIALIZED;
+	(void) zone_key_delete(nlm_zone_key);
+	rw_destroy(&lm_lck);
+
+	return (retval);
+}
+
+int
+_fini()
+{
+	/* Don't unload. */
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/*
+ * ****************************************************************
+ * Stubs listed in modstubs.s
+ */
+
+/*
+ * klm system calls.  Start service on some endpoint.
+ * Called by nfssys() LM_SVC, from lockd.
+ */
+int
+lm_svc(struct lm_svc_args *args)
+{
+	struct knetconfig knc;
+	const char *netid;
+	struct nlm_globals *g;
+	struct file *fp = NULL;
+	int err = 0;
+
+	/* Get our "globals" */
+	g = zone_getspecific(nlm_zone_key, curzone);
+
+	/*
+	 * Check version of lockd calling.
+	 */
+	if (args->version != LM_SVC_CUR_VERS) {
+		NLM_ERR("lm_svc: Version mismatch "
+		    "(given 0x%x, expected 0x%x)\n",
+		    args->version, LM_SVC_CUR_VERS);
+		return (EINVAL);
+	}
+
+	/*
+	 * Build knetconfig, checking arg values.
+	 * Also come up with the "netid" string.
+	 * (With some knowledge of /etc/netconfig)
+	 */
+	bzero(&knc, sizeof (knc));
+	switch (args->n_proto) {
+	case LM_TCP:
+		knc.knc_semantics = NC_TPI_COTS_ORD;
+		knc.knc_proto = NC_TCP;
+		break;
+	case LM_UDP:
+		knc.knc_semantics = NC_TPI_CLTS;
+		knc.knc_proto = NC_UDP;
+		break;
+	default:
+		NLM_ERR("nlm_build_knetconfig: Unknown "
+		    "lm_proto=0x%x\n", args->n_proto);
+		return (EINVAL);
+	}
+
+	switch (args->n_fmly) {
+	case LM_INET:
+		knc.knc_protofmly = NC_INET;
+		break;
+	case LM_INET6:
+		knc.knc_protofmly = NC_INET6;
+		break;
+	case LM_LOOPBACK:
+		knc.knc_protofmly = NC_LOOPBACK;
+		/* Override what we set above. */
+		knc.knc_proto = NC_NOPROTO;
+		break;
+	default:
+		NLM_ERR("nlm_build_knetconfig: Unknown "
+		    "lm_fmly=0x%x\n", args->n_fmly);
+		return (EINVAL);
+	}
+
+	knc.knc_rdev = args->n_rdev;
+	netid = nlm_knc_to_netid(&knc);
+	if (!netid)
+		return (EINVAL);
+
+	/*
+	 * Setup service on the passed transport.
+	 * NB: must releasef(fp) after this.
+	 */
+	if ((fp = getf(args->fd)) == NULL)
+		return (EBADF);
+
+	mutex_enter(&g->lock);
+	/*
+	 * Don't try to start while still shutting down,
+	 * or lots of things will fail...
+	 */
+	if (g->run_status == NLM_ST_STOPPING) {
+		err = EAGAIN;
+		goto out;
+	}
+
+	/*
+	 * There is no separate "initialize" sub-call for nfssys,
+	 * and we want to do some one-time work when the first
+	 * binding comes in from lockd.
+	 */
+	if (g->run_status == NLM_ST_DOWN) {
+		g->run_status = NLM_ST_STARTING;
+		g->lockd_pid = curproc->p_pid;
+
+		/* Save the options. */
+		g->cn_idle_tmo = args->timout;
+		g->grace_period = args->grace;
+		g->retrans_tmo = args->retransmittimeout;
+
+		/* See nfs_sys.c (not yet per-zone) */
+		if (INGLOBALZONE(curproc)) {
+			rfs4_grace_period = args->grace;
+			rfs4_lease_time   = args->grace;
+		}
+
+		mutex_exit(&g->lock);
+		err = nlm_svc_starting(g, fp, netid, &knc);
+		mutex_enter(&g->lock);
+	} else {
+		/*
+		 * If KLM is not started and the very first endpoint lockd
+		 * tries to add is not a loopback device, report an error.
+		 */
+		if (g->run_status != NLM_ST_UP) {
+			err = ENOTACTIVE;
+			goto out;
+		}
+		if (g->lockd_pid != curproc->p_pid) {
+			/* Check if caller has the same PID lockd does */
+			err = EPERM;
+			goto out;
+		}
+
+		err = nlm_svc_add_ep(fp, netid, &knc);
+	}
+
+out:
+	mutex_exit(&g->lock);
+	if (fp != NULL)
+		releasef(args->fd);
+
+	return (err);
+}
+
+/*
+ * klm system calls.  Kill the lock manager.
+ * Called by nfssys() KILL_LOCKMGR,
+ * liblm:lm_shutdown() <- unused?
+ */
+int
+lm_shutdown(void)
+{
+	struct nlm_globals *g;
+	proc_t *p;
+	pid_t pid;
+
+	/* Get our "globals" */
+	g = zone_getspecific(nlm_zone_key, curzone);
+
+	mutex_enter(&g->lock);
+	if (g->run_status != NLM_ST_UP) {
+		mutex_exit(&g->lock);
+		return (EBUSY);
+	}
+
+	g->run_status = NLM_ST_STOPPING;
+	pid = g->lockd_pid;
+	mutex_exit(&g->lock);
+	nlm_svc_stopping(g);
+
+	mutex_enter(&pidlock);
+	p = prfind(pid);
+	if (p != NULL)
+		psignal(p, SIGTERM);
+
+	mutex_exit(&pidlock);
+	return (0);
+}
+
+/*
+ * Cleanup remote locks on FS un-export.
+ *
+ * NOTE: called from nfs_export.c:unexport()
+ * right before the share is going to
+ * be unexported.
+ */
+void
+lm_unexport(struct exportinfo *exi)
+{
+	nlm_unexport(exi);
+}
+
+/*
+ * CPR suspend/resume hooks.
+ * See:cpr_suspend, cpr_resume
+ *
+ * Before suspend, get current state from "statd" on
+ * all remote systems for which we have locks.
+ *
+ * After resume, check with those systems again,
+ * and either reclaim locks, or do SIGLOST.
+ */
+void
+lm_cprsuspend(void)
+{
+	nlm_cprsuspend();
+}
+
+void
+lm_cprresume(void)
+{
+	nlm_cprresume();
+}
+
+/*
+ * Add the nlm_id bits to the sysid (by ref).
+ */
+void
+lm_set_nlmid_flk(int *new_sysid)
+{
+	if (lm_global_nlmid != 0)
+		*new_sysid |= (lm_global_nlmid << BITS_IN_SYSID);
+}
+
+/*
+ * It seems that closed source klmmod used
+ * this function to release knetconfig stored
+ * in mntinfo structure (see mntinfo's mi_klmconfig
+ * field).
+ * We store knetconfigs differently, thus we don't
+ * need this function.
+ */
+void
+lm_free_config(struct knetconfig *knc)
+{
+	_NOTE(ARGUNUSED(knc));
+}
+
+/*
+ * Called by NFS4 delegation code to check if there are any
+ * NFSv2/v3 locks for the file, so it should not delegate.
+ *
+ * NOTE: called from NFSv4 code
+ * (see nfs4_srv_deleg.c:rfs4_bgrant_delegation())
+ */
+int
+lm_vp_active(const vnode_t *vp)
+{
+	return (nlm_vp_active(vp));
+}
+
+/*
+ * Find or create a "sysid" for given knc+addr.
+ * name is optional.  Sets nc_changed if the
+ * found knc_proto is different from passed.
+ * Increments the reference count.
+ *
+ * Called internally, and in nfs4_find_sysid()
+ */
+struct lm_sysid *
+lm_get_sysid(struct knetconfig *knc, struct netbuf *addr,
+    char *name, bool_t *nc_changed)
+{
+	struct nlm_globals *g;
+	const char *netid;
+	struct nlm_host *hostp;
+
+	_NOTE(ARGUNUSED(nc_changed));
+	netid = nlm_knc_to_netid(knc);
+	if (netid == NULL)
+		return (NULL);
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+
+	hostp = nlm_host_findcreate(g, name, netid, addr);
+	if (hostp == NULL)
+		return (NULL);
+
+	return ((struct lm_sysid *)hostp);
+}
+
+/*
+ * Release a reference on a "sysid".
+ */
+void
+lm_rel_sysid(struct lm_sysid *sysid)
+{
+	struct nlm_globals *g;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	nlm_host_release(g, (struct nlm_host *)sysid);
+}
+
+/*
+ * Alloc/free a sysid_t (a unique number between
+ * LM_SYSID and LM_SYSID_MAX).
+ *
+ * Used by NFSv4 rfs4_op_lockt and smbsrv/smb_fsop_frlock,
+ * both to represent non-local locks outside of klm.
+ *
+ * NOTE: called from NFSv4 and SMBFS to allocate unique
+ * sysid.
+ */
+sysid_t
+lm_alloc_sysidt(void)
+{
+	return (nlm_sysid_alloc());
+}
+
+void
+lm_free_sysidt(sysid_t sysid)
+{
+	nlm_sysid_free(sysid);
+}
+
+/* Access private member lms->sysid */
+sysid_t
+lm_sysidt(struct lm_sysid *lms)
+{
+	return (((struct nlm_host *)lms)->nh_sysid);
+}
+
+/*
+ * Called by nfs_frlock to check lock constraints.
+ * Return non-zero if the lock request is "safe", i.e.
+ * the range is not mapped, not MANDLOCK, etc.
+ *
+ * NOTE: callde from NFSv3/NFSv2 frlock() functions to
+ * determine whether it's safe to add new lock.
+ */
+int
+lm_safelock(vnode_t *vp, const struct flock64 *fl, cred_t *cr)
+{
+	return (nlm_safelock(vp, fl, cr));
+}
+
+/*
+ * Called by nfs_lockcompletion to check whether it's "safe"
+ * to map the file (and cache it's data).  Walks the list of
+ * file locks looking for any that are not "whole file".
+ *
+ * NOTE: called from nfs_client.c:nfs_lockcompletion()
+ */
+int
+lm_safemap(const vnode_t *vp)
+{
+	return (nlm_safemap(vp));
+}
+
+/*
+ * Called by nfs_map() for the MANDLOCK case.
+ * Return non-zero if the file has any locks with a
+ * blocked request (sleep).
+ *
+ * NOTE: called from NFSv3/NFSv2 map() functions in
+ * order to determine whether it's safe to add new
+ * mapping.
+ */
+int
+lm_has_sleep(const vnode_t *vp)
+{
+	return (nlm_has_sleep(vp));
+}
+
+/*
+ * ****************************************************************
+ * Stuff needed by klmops?
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/klmops.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,170 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy is of the CDDL is also available via the Internet
+ * at http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager, client-side
+ * Note: depends on (links with) klmmod
+ *
+ * This file contains all the external entry points of klmops.
+ * Basically, this is the "glue" to the BSD nlm code.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/modctl.h>
+#include <sys/flock.h>
+
+#include <nfs/lm.h>
+#include <rpcsvc/nlm_prot.h>
+#include "nlm_impl.h"
+
+
+static struct modlmisc modlmisc = {
+	&mod_miscops, "lock mgr calls"
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modlmisc, NULL
+};
+
+
+
+/*
+ * ****************************************************************
+ * module init, fini, info
+ */
+int
+_init()
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+	/* Don't unload. */
+	return (EBUSY);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+
+/*
+ * ****************************************************************
+ * Stubs listed in modstubs.s
+ * These are called from fs/nfs
+ */
+
+/*
+ * NFSv2 lock/unlock.  Called by nfs_frlock()
+ * Uses NLM version 1 (NLM_VERS)
+ */
+int
+lm_frlock(struct vnode *vp, int cmd, struct flock64 *flk, int flags,
+    u_offset_t off, struct cred *cr, struct netobj *fh,
+    struct flk_callback *flcb)
+{
+	return (nlm_frlock(vp, cmd, flk, flags, off,
+	    cr, fh, flcb, NLM_VERS));
+}
+
+/*
+ * NFSv3 lock/unlock.  Called by nfs3_frlock()
+ * Uses NLM version 4 (NLM4_VERS)
+ */
+int
+lm4_frlock(struct vnode *vp, int cmd, struct flock64 *flk, int flags,
+	u_offset_t off, struct cred *cr, struct netobj *fh,
+	struct flk_callback *flcb)
+{
+	int err;
+	err = nlm_frlock(vp, cmd, flk, flags, off,
+	    cr, fh, flcb, NLM4_VERS);
+	return (err);
+}
+
+/*
+ * NFSv2 shrlk/unshrlk.  See nfs_shrlock
+ * Uses NLM version 3 (NLM_VERSX)
+ */
+int
+lm_shrlock(struct vnode *vp, int cmd,
+    struct shrlock *shr, int flags, struct netobj *fh)
+{
+	return (nlm_shrlock(vp, cmd, shr, flags, fh, NLM_VERSX));
+}
+
+/*
+ * NFSv3 shrlk/unshrlk.  See nfs3_shrlock
+ * Uses NLM version 4 (NLM4_VERS)
+ */
+int
+lm4_shrlock(struct vnode *vp, int cmd,
+    struct shrlock *shr, int flags, struct netobj *fh)
+{
+	return (nlm_shrlock(vp, cmd, shr, flags, fh, NLM4_VERS));
+}
+
+/*
+ * Helper for lm_frlock, lm4_frlock, nfs_lockrelease
+ * After getting a lock from a remote lock manager,
+ * register the lock locally.
+ */
+void
+lm_register_lock_locally(struct vnode *vp, struct lm_sysid *ls,
+    struct flock64 *flk, int flags, u_offset_t offset)
+{
+	nlm_register_lock_locally(vp, (struct nlm_host *)ls,
+	    flk, flags, offset);
+}
+
+/*
+ * Old RPC service dispatch functions, no longer used.
+ * Here only to satisfy modstubs.s references.
+ */
+void
+lm_nlm_dispatch(struct svc_req *req, SVCXPRT *xprt)
+{
+	_NOTE(ARGUNUSED(req, xprt))
+}
+
+void
+lm_nlm4_dispatch(struct svc_req *req, SVCXPRT *xprt)
+{
+	_NOTE(ARGUNUSED(req, xprt))
+}
+
+/*
+ * Old internal functions used for reclaiming locks
+ * our NFS client holds after some server restarts.
+ * The new NLM code does this differently, so these
+ * are here only to satisfy modstubs.s references.
+ */
+void
+lm_nlm_reclaim(struct vnode *vp, struct flock64 *flkp)
+{
+	_NOTE(ARGUNUSED(vp, flkp))
+}
+
+void
+lm_nlm4_reclaim(struct vnode *vp, struct flock64 *flkp)
+{
+	_NOTE(ARGUNUSED(vp, flkp))
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/mapfile-mod	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,55 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+#
+
+
+$mapfile_version 2
+
+SYMBOL_SCOPE {
+	global:
+# loadable module linkage
+		_fini;
+		_info;
+		_init;
+# These are all the symbols referenced in ml/modstubs.s
+# If we want to remain a drop-in replacment for the old
+# (closed source) klm, we need to define all of these.
+		lm_alloc_sysidt;
+		lm_cprresume;
+		lm_cprsuspend;
+		lm_free_config;
+		lm_free_sysidt;
+		lm_get_sysid;
+		lm_global_nlmid;
+		lm_has_sleep;
+		lm_rel_sysid;
+		lm_remove_file_locks;
+		lm_safelock;
+		lm_safemap;
+		lm_set_nlmid_flk;
+		lm_shutdown;
+		lm_svc;
+		lm_sysidt;
+		lm_unexport;
+		lm_vp_active;
+# The following three functions are not mentioned in modstubs.s
+# files, because they are not an entry points to KLM. They
+# are called from klmops only.
+		nlm_frlock;
+		nlm_register_lock_locally;
+		nlm_shrlock;
+
+	local:
+		*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/mapfile-ops	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,40 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+#
+
+
+$mapfile_version 2
+
+SYMBOL_SCOPE {
+	global:
+# loadable module linkage
+		_fini;
+		_info;
+		_init;
+# These are all the symbols referenced in ml/modstubs.s
+# If we want to remain a drop-in replacment for the old
+# (closed source) klm, we need to define all of these.
+
+		lm4_frlock;
+		lm4_shrlock;
+		lm_frlock;
+		lm_nlm4_dispatch;
+		lm_nlm4_reclaim;
+		lm_nlm_dispatch;
+		lm_nlm_reclaim;
+		lm_register_lock_locally;
+
+	local:
+		*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_client.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,1622 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * Client-side support for (NFS) VOP_FRLOCK, VOP_SHRLOCK.
+ * (called via klmops.c: lm_frlock, lm4_frlock)
+ *
+ * Source code derived from FreeBSD nlm_advlock.c
+ */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <sys/flock.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/share.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+#include <sys/queue.h>
+#include <sys/sdt.h>
+#include <netinet/in.h>
+
+#include <fs/fs_subr.h>
+#include <rpcsvc/nlm_prot.h>
+
+#include <nfs/nfs.h>
+#include <nfs/nfs_clnt.h>
+#include <nfs/export.h>
+#include <nfs/rnode.h>
+#include <nfs/lm.h>
+
+#include "nlm_impl.h"
+
+/* Extra flags for nlm_call_lock() - xflags */
+#define	NLM_X_RECLAIM	1
+#define	NLM_X_BLOCKING	2
+
+/*
+ * Max. number of retries nlm_call_cancel() does
+ * when NLM server is in grace period or doesn't
+ * respond correctly.
+ */
+#define	NLM_CANCEL_NRETRS 5
+
+/*
+ * Determines wether given lock "flp" is safe.
+ * The lock is considered to be safe when it
+ * acquires the whole file (i.e. its start
+ * and len are zeroes).
+ */
+#define	NLM_FLOCK_IS_SAFE(flp) \
+	((flp)->l_start == 0 && (flp)->l_len == 0)
+
+static volatile uint32_t nlm_xid = 1;
+
+static int nlm_init_fh_by_vp(vnode_t *, struct netobj *, rpcvers_t *);
+static int nlm_map_status(nlm4_stats);
+static int nlm_map_clnt_stat(enum clnt_stat);
+static void nlm_send_siglost(pid_t);
+
+static int nlm_frlock_getlk(struct nlm_host *, vnode_t *,
+    struct flock64 *, int, u_offset_t, struct netobj *, int);
+
+static int nlm_frlock_setlk(struct nlm_host *, vnode_t *,
+    struct flock64 *, int, u_offset_t, struct netobj *,
+    struct flk_callback *, int, bool_t);
+
+static int nlm_reclaim_lock(struct nlm_host *, vnode_t *,
+    struct flock64 *, int32_t);
+
+static void nlm_init_lock(struct nlm4_lock *,
+    const struct flock64 *, struct netobj *,
+    struct nlm_owner_handle *);
+
+static int nlm_call_lock(vnode_t *, struct flock64 *,
+    struct nlm_host *, struct netobj *,
+    struct flk_callback *, int, int);
+static int nlm_call_unlock(struct flock64 *, struct nlm_host *,
+    struct netobj *, int);
+static int nlm_call_test(struct flock64 *, struct nlm_host *,
+    struct netobj *, int);
+static int nlm_call_cancel(struct nlm4_lockargs *,
+    struct nlm_host *, int);
+
+static int nlm_local_getlk(vnode_t *, struct flock64 *, int);
+static int nlm_local_setlk(vnode_t *, struct flock64 *, int);
+static void nlm_local_cancelk(vnode_t *, struct flock64 *);
+
+static void nlm_init_share(struct nlm4_share *,
+    const struct shrlock *, struct netobj *);
+
+static int nlm_call_share(struct shrlock *, struct nlm_host *,
+    struct netobj *, int, int);
+static int nlm_call_unshare(struct shrlock *, struct nlm_host *,
+    struct netobj *, int);
+static int nlm_reclaim_share(struct nlm_host *, vnode_t *,
+    struct shrlock *, uint32_t);
+static int nlm_local_shrlock(vnode_t *, struct shrlock *, int, int);
+static void nlm_local_shrcancel(vnode_t *, struct shrlock *);
+
+/*
+ * Reclaim locks/shares acquired by the client side
+ * on the given server represented by hostp.
+ * The function is called from a dedicated thread
+ * when server reports us that it's entered grace
+ * period.
+ */
+void
+nlm_reclaim_client(struct nlm_globals *g, struct nlm_host *hostp)
+{
+	int32_t state;
+	int error, sysid;
+	struct locklist *llp_head, *llp;
+	struct nlm_shres *nsp_head, *nsp;
+	bool_t restart;
+
+	sysid = hostp->nh_sysid | LM_SYSID_CLIENT;
+	do {
+		error = 0;
+		restart = FALSE;
+		state = nlm_host_get_state(hostp);
+
+		DTRACE_PROBE3(reclaim__iter, struct nlm_globals *, g,
+		    struct nlm_host *, hostp, int, state);
+
+		/*
+		 * We cancel all sleeping locks that were
+		 * done by the host, because we don't allow
+		 * reclamation of sleeping locks. The reason
+		 * we do this is that allowing of sleeping locks
+		 * reclamation can potentially break locks recovery
+		 * order.
+		 *
+		 * Imagine that we have two client machines A and B
+		 * and an NLM server machine. A adds a non sleeping
+		 * lock to the file F and aquires this file. Machine
+		 * B in its turn adds sleeping lock to the file
+		 * F and blocks because F is already aquired by
+		 * the machine A. Then server crashes and after the
+		 * reboot it notifies its clients about the crash.
+		 * If we would allow sleeping locks reclamation,
+		 * there would be possible that machine B recovers
+		 * its lock faster than machine A (by some reason).
+		 * So that B aquires the file F after server crash and
+		 * machine A (that by some reason recovers slower) fails
+		 * to recover its non sleeping lock. Thus the original
+		 * locks order becames broken.
+		 */
+		nlm_host_cancel_slocks(g, hostp);
+
+		/*
+		 * Try to reclaim all active locks we have
+		 */
+		llp_head = llp = flk_get_active_locks(sysid, NOPID);
+		while (llp != NULL) {
+			error = nlm_reclaim_lock(hostp, llp->ll_vp,
+			    &llp->ll_flock, state);
+
+			if (error == 0) {
+				llp = llp->ll_next;
+				continue;
+			} else if (error == ERESTART) {
+				restart = TRUE;
+				break;
+			} else {
+				/*
+				 * Critical error occurred, the lock
+				 * can not be recovered, just take it away.
+				 */
+				nlm_local_cancelk(llp->ll_vp, &llp->ll_flock);
+			}
+
+			llp = llp->ll_next;
+		}
+
+		flk_free_locklist(llp_head);
+		if (restart) {
+			/*
+			 * Lock reclamation fucntion reported us that
+			 * the server state was changed (again), so
+			 * try to repeat the whole reclamation process.
+			 */
+			continue;
+		}
+
+		nsp_head = nsp = nlm_get_active_shres(hostp);
+		while (nsp != NULL) {
+			error = nlm_reclaim_share(hostp, nsp->ns_vp,
+			    nsp->ns_shr, state);
+
+			if (error == 0) {
+				nsp = nsp->ns_next;
+				continue;
+			} else if (error == ERESTART) {
+				break;
+			} else {
+				/* Failed to reclaim share */
+				nlm_shres_untrack(hostp, nsp->ns_vp,
+				    nsp->ns_shr);
+				nlm_local_shrcancel(nsp->ns_vp,
+				    nsp->ns_shr);
+			}
+
+			nsp = nsp->ns_next;
+		}
+
+		nlm_free_shrlist(nsp_head);
+	} while (state != nlm_host_get_state(hostp));
+}
+
+/*
+ * nlm_frlock --
+ *      NFS advisory byte-range locks.
+ *	Called in klmops.c
+ *
+ * Note that the local locking code (os/flock.c) is used to
+ * keep track of remote locks granted by some server, so we
+ * can reclaim those locks after a server restarts.  We can
+ * also sometimes use this as a cache of lock information.
+ *
+ * Was: nlm_advlock()
+ */
+/* ARGSUSED */
+int
+nlm_frlock(struct vnode *vp, int cmd, struct flock64 *flkp,
+	int flags, u_offset_t offset, struct cred *crp,
+	struct netobj *fhp, struct flk_callback *flcb, int vers)
+{
+	mntinfo_t *mi;
+	servinfo_t *sv;
+	const char *netid;
+	struct nlm_host *hostp;
+	int error;
+	struct nlm_globals *g;
+
+	mi = VTOMI(vp);
+	sv = mi->mi_curr_serv;
+
+	netid = nlm_knc_to_netid(sv->sv_knconf);
+	if (netid == NULL) {
+		NLM_ERR("nlm_frlock: unknown NFS netid");
+		return (ENOSYS);
+	}
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	hostp = nlm_host_findcreate(g, sv->sv_hostname, netid, &sv->sv_addr);
+	if (hostp == NULL)
+		return (ENOSYS);
+
+	/*
+	 * Purge cached attributes in order to make sure that
+	 * future calls of convoff()/VOP_GETATTR() will get the
+	 * latest data.
+	 */
+	if (flkp->l_whence == SEEK_END)
+		PURGE_ATTRCACHE(vp);
+
+	/* Now flk0 is the zero-based lock request. */
+	switch (cmd) {
+	case F_GETLK:
+		error = nlm_frlock_getlk(hostp, vp, flkp, flags,
+		    offset, fhp, vers);
+		break;
+
+	case F_SETLK:
+	case F_SETLKW:
+		error = nlm_frlock_setlk(hostp, vp, flkp, flags,
+		    offset, fhp, flcb, vers, (cmd == F_SETLKW));
+		if (error == 0)
+			nlm_host_monitor(g, hostp, 0);
+		break;
+
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	nlm_host_release(g, hostp);
+	return (error);
+}
+
+static int
+nlm_frlock_getlk(struct nlm_host *hostp, vnode_t *vp,
+    struct flock64 *flkp, int flags, u_offset_t offset,
+    struct netobj *fhp, int vers)
+{
+	struct flock64 flk0;
+	int error;
+
+	/*
+	 * Check local (cached) locks first.
+	 * If we find one, no need for RPC.
+	 */
+	flk0 = *flkp;
+	flk0.l_pid = curproc->p_pid;
+	error = nlm_local_getlk(vp, &flk0, flags);
+	if (error != 0)
+		return (error);
+	if (flk0.l_type != F_UNLCK) {
+		*flkp = flk0;
+		return (0);
+	}
+
+	/* Not found locally.  Try remote. */
+	flk0 = *flkp;
+	flk0.l_pid = curproc->p_pid;
+	error = convoff(vp, &flk0, 0, (offset_t)offset);
+	if (error != 0)
+		return (error);
+
+	error = nlm_call_test(&flk0, hostp, fhp, vers);
+	if (error != 0)
+		return (error);
+
+	if (flk0.l_type == F_UNLCK) {
+		/*
+		 * Update the caller's *flkp with information
+		 * on the conflicting lock (or lack thereof).
+		 */
+		flkp->l_type = F_UNLCK;
+	} else {
+		/*
+		 * Found a conflicting lock.  Set the
+		 * caller's *flkp with the info, first
+		 * converting to the caller's whence.
+		 */
+		(void) convoff(vp, &flk0, flkp->l_whence, (offset_t)offset);
+		*flkp = flk0;
+	}
+
+	return (0);
+}
+
+static int
+nlm_frlock_setlk(struct nlm_host *hostp, vnode_t *vp,
+    struct flock64 *flkp, int flags, u_offset_t offset,
+    struct netobj *fhp, struct flk_callback *flcb,
+    int vers, bool_t do_block)
+{
+	int error, xflags;
+
+	error = convoff(vp, flkp, 0, (offset_t)offset);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * NFS v2 clients should not request locks where any part
+	 * of the lock range is beyond 0xffffffff.  The NFS code
+	 * checks that (see nfs_frlock, flk_check_lock_data), but
+	 * as that's outside this module, let's check here too.
+	 * This check ensures that we will be able to convert this
+	 * lock request into 32-bit form without change, and that
+	 * (more importantly) when the granted call back arrives,
+	 * it's unchanged when converted back into 64-bit form.
+	 * If this lock range were to change in any way during
+	 * either of those conversions, the "granted" call back
+	 * from the NLM server would not find our sleeping lock.
+	 */
+	if (vers < NLM4_VERS) {
+		if (flkp->l_start > MAX_UOFF32 ||
+		    flkp->l_start + flkp->l_len > MAX_UOFF32 + 1)
+			return (EINVAL);
+	}
+
+	/*
+	 * Fill in l_sysid for the local locking calls.
+	 * Also, let's not trust the caller's l_pid.
+	 */
+	flkp->l_sysid = hostp->nh_sysid | LM_SYSID_CLIENT;
+	flkp->l_pid = curproc->p_pid;
+
+	if (flkp->l_type == F_UNLCK) {
+		/*
+		 * Purge local (cached) lock information first,
+		 * then clear the remote lock.
+		 */
+		(void) nlm_local_setlk(vp, flkp, flags);
+		error = nlm_call_unlock(flkp, hostp, fhp, vers);
+
+		return (error);
+	}
+
+	if (!do_block) {
+		/*
+		 * This is a non-blocking "set" request,
+		 * so we can check locally first, and
+		 * sometimes avoid an RPC call.
+		 */
+		struct flock64 flk0;
+
+		flk0 = *flkp;
+		error = nlm_local_getlk(vp, &flk0, flags);
+		if (error != 0 && flk0.l_type != F_UNLCK) {
+			/* Found a conflicting lock. */
+			return (EAGAIN);
+		}
+
+		xflags = 0;
+	} else {
+		xflags = NLM_X_BLOCKING;
+	}
+
+	nfs_add_locking_id(vp, curproc->p_pid, RLMPL_PID,
+	    (char *)&curproc->p_pid, sizeof (pid_t));
+
+	error = nlm_call_lock(vp, flkp, hostp, fhp, flcb, vers, xflags);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Save the lock locally.  This should not fail,
+	 * because the server is authoritative about locks
+	 * and it just told us we have the lock!
+	 */
+	error = nlm_local_setlk(vp, flkp, flags);
+	if (error != 0) {
+		/*
+		 * That's unexpected situation. Just ignore the error.
+		 */
+		NLM_WARN("nlm_frlock_setlk: Failed to set local lock. "
+		    "[err=%d]\n", error);
+		error = 0;
+	}
+
+	return (error);
+}
+
+/*
+ * Cancel all client side remote locks/shares on the
+ * given host. Report to the processes that own
+ * cancelled locks that they are removed by force
+ * by sending SIGLOST.
+ */
+void
+nlm_client_cancel_all(struct nlm_globals *g, struct nlm_host *hostp)
+{
+	struct locklist *llp_head, *llp;
+	struct nlm_shres *nsp_head, *nsp;
+	struct netobj lm_fh;
+	rpcvers_t vers;
+	int error, sysid;
+
+	sysid = hostp->nh_sysid | LM_SYSID_CLIENT;
+	nlm_host_cancel_slocks(g, hostp);
+
+	/*
+	 * Destroy all active locks
+	 */
+	llp_head = llp = flk_get_active_locks(sysid, NOPID);
+	while (llp != NULL) {
+		llp->ll_flock.l_type = F_UNLCK;
+
+		error = nlm_init_fh_by_vp(llp->ll_vp, &lm_fh, &vers);
+		if (error == 0)
+			(void) nlm_call_unlock(&llp->ll_flock, hostp,
+			    &lm_fh, vers);
+
+		nlm_local_cancelk(llp->ll_vp, &llp->ll_flock);
+		llp = llp->ll_next;
+	}
+
+	flk_free_locklist(llp_head);
+
+	/*
+	 * Destroy all active share reservations
+	 */
+	nsp_head = nsp = nlm_get_active_shres(hostp);
+	while (nsp != NULL) {
+		error = nlm_init_fh_by_vp(nsp->ns_vp, &lm_fh, &vers);
+		if (error == 0)
+			(void) nlm_call_unshare(nsp->ns_shr, hostp,
+			    &lm_fh, vers);
+
+		nlm_local_shrcancel(nsp->ns_vp, nsp->ns_shr);
+		nlm_shres_untrack(hostp, nsp->ns_vp, nsp->ns_shr);
+		nsp = nsp->ns_next;
+	}
+
+	nlm_free_shrlist(nsp_head);
+}
+
+/*
+ * The function determines whether the lock "fl" can
+ * be safely applied to the file vnode "vp" corresponds to.
+ * The lock can be "safely" applied if all the conditions
+ * above are held:
+ *  - It's not a mandatory lock
+ *  - The vnode wasn't mapped by anyone
+ *  - The vnode was mapped, but it hasn't any locks on it.
+ *  - The vnode was mapped and all locks it has occupies
+ *    the whole file.
+ */
+int
+nlm_safelock(vnode_t *vp, const struct flock64 *fl, cred_t *cr)
+{
+	rnode_t *rp = VTOR(vp);
+	struct vattr va;
+	int err;
+
+	if ((rp->r_mapcnt > 0) && (fl->l_start != 0 || fl->l_len != 0))
+		return (0);
+
+	va.va_mask = AT_MODE;
+	err = VOP_GETATTR(vp, &va, 0, cr, NULL);
+	if (err != 0)
+		return (0);
+
+	/* NLM4 doesn't allow mandatory file locking */
+	if (MANDLOCK(vp, va.va_mode))
+		return (0);
+
+	return (1);
+}
+
+/*
+ * The function determines whether it's safe to map
+ * a file correspoding to vnode vp.
+ * The mapping is considered to be "safe" if file
+ * either has no any locks on it or all locks it
+ * has occupy the whole file.
+ */
+int
+nlm_safemap(const vnode_t *vp)
+{
+	struct locklist *llp, *llp_next;
+	struct nlm_slock *nslp;
+	struct nlm_globals *g;
+	int safe = 1;
+
+	/* Check active locks at first */
+	llp = flk_active_locks_for_vp(vp);
+	while (llp != NULL) {
+		if ((llp->ll_vp == vp) &&
+		    !NLM_FLOCK_IS_SAFE(&llp->ll_flock))
+			safe = 0;
+
+		llp_next = llp->ll_next;
+		VN_RELE(llp->ll_vp);
+		kmem_free(llp, sizeof (*llp));
+		llp = llp_next;
+	}
+	if (!safe)
+		return (safe);
+
+	/* Then check sleeping locks if any */
+	g = zone_getspecific(nlm_zone_key, curzone);
+	mutex_enter(&g->lock);
+	TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
+		if (nslp->nsl_state == NLM_SL_BLOCKED &&
+		    nslp->nsl_vp == vp &&
+		    (nslp->nsl_lock.l_offset != 0 ||
+		    nslp->nsl_lock.l_len != 0)) {
+			safe = 0;
+			break;
+		}
+	}
+
+	mutex_exit(&g->lock);
+	return (safe);
+}
+
+int
+nlm_has_sleep(const vnode_t *vp)
+{
+	struct nlm_globals *g;
+	struct nlm_slock *nslp;
+	int has_slocks = FALSE;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	mutex_enter(&g->lock);
+	TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
+		if (nslp->nsl_state == NLM_SL_BLOCKED &&
+		    nslp->nsl_vp == vp) {
+			has_slocks = TRUE;
+			break;
+		}
+	}
+
+	mutex_exit(&g->lock);
+	return (has_slocks);
+}
+
+void
+nlm_register_lock_locally(struct vnode *vp, struct nlm_host *hostp,
+    struct flock64 *flk, int flags, u_offset_t offset)
+{
+	int sysid = 0;
+
+	if (hostp != NULL) {
+		sysid = hostp->nh_sysid | LM_SYSID_CLIENT;
+	}
+
+	flk->l_sysid = sysid;
+	(void) convoff(vp, flk, 0, (offset_t)offset);
+	(void) nlm_local_setlk(vp, flk, flags);
+}
+
+
+/*
+ * The BSD code had functions here to "reclaim" (destroy)
+ * remote locks when a vnode is being forcibly destroyed.
+ * We just keep vnodes around until statd tells us the
+ * client has gone away.
+ */
+
+static int
+nlm_reclaim_lock(struct nlm_host *hostp, vnode_t *vp,
+    struct flock64 *flp, int32_t orig_state)
+{
+	struct netobj lm_fh;
+	int error, state;
+	rpcvers_t vers;
+
+	/*
+	 * If the remote NSM state changes during recovery, the host
+	 * must have rebooted a second time. In that case, we must
+	 * restart the recovery.
+	 */
+	state = nlm_host_get_state(hostp);
+	if (state != orig_state)
+		return (ERESTART);
+
+	error = nlm_init_fh_by_vp(vp, &lm_fh, &vers);
+	if (error != 0)
+		return (error);
+
+	return (nlm_call_lock(vp, flp, hostp, &lm_fh,
+	    NULL, vers, NLM_X_RECLAIM));
+}
+
+/*
+ * Get local lock information for some NFS server.
+ *
+ * This gets (checks for) a local conflicting lock.
+ * Note: Modifies passed flock, if a conflict is found,
+ * but the caller expects that.
+ */
+static int
+nlm_local_getlk(vnode_t *vp, struct flock64 *fl, int flags)
+{
+	VERIFY(fl->l_whence == SEEK_SET);
+	return (reclock(vp, fl, 0, flags, 0, NULL));
+}
+
+/*
+ * Set local lock information for some NFS server.
+ *
+ * Called after a lock request (set or clear) succeeded. We record the
+ * details in the local lock manager. Note that since the remote
+ * server has granted the lock, we can be sure that it doesn't
+ * conflict with any other locks we have in the local lock manager.
+ *
+ * Since it is possible that host may also make NLM client requests to
+ * our NLM server, we use a different sysid value to record our own
+ * client locks.
+ *
+ * Note that since it is possible for us to receive replies from the
+ * server in a different order than the locks were granted (e.g. if
+ * many local threads are contending for the same lock), we must use a
+ * blocking operation when registering with the local lock manager.
+ * We expect that any actual wait will be rare and short hence we
+ * ignore signals for this.
+ */
+static int
+nlm_local_setlk(vnode_t *vp, struct flock64 *fl, int flags)
+{
+	VERIFY(fl->l_whence == SEEK_SET);
+	return (reclock(vp, fl, SETFLCK, flags, 0, NULL));
+}
+
+/*
+ * Cancel local lock and send send SIGLOST signal
+ * to the lock owner.
+ *
+ * NOTE: modifies flp
+ */
+static void
+nlm_local_cancelk(vnode_t *vp, struct flock64 *flp)
+{
+	flp->l_type = F_UNLCK;
+	(void) nlm_local_setlk(vp, flp, FREAD | FWRITE);
+	nlm_send_siglost(flp->l_pid);
+}
+
+/*
+ * Do NLM_LOCK call.
+ * Was: nlm_setlock()
+ *
+ * NOTE: nlm_call_lock() function should care about locking/unlocking
+ * of rnode->r_lkserlock which should be released before nlm_call_lock()
+ * sleeps on waiting lock and acquired when it wakes up.
+ */
+static int
+nlm_call_lock(vnode_t *vp, struct flock64 *flp,
+	struct nlm_host *hostp, struct netobj *fhp,
+	struct flk_callback *flcb, int vers, int xflags)
+{
+	struct nlm4_lockargs args;
+	struct nlm_owner_handle oh;
+	struct nlm_globals *g;
+	rnode_t *rnp = VTOR(vp);
+	struct nlm_slock *nslp = NULL;
+	uint32_t xid;
+	int error = 0;
+
+	bzero(&args, sizeof (args));
+	g = zone_getspecific(nlm_zone_key, curzone);
+	nlm_init_lock(&args.alock, flp, fhp, &oh);
+
+	args.exclusive = (flp->l_type == F_WRLCK);
+	args.reclaim = xflags & NLM_X_RECLAIM;
+	args.state = g->nsm_state;
+	args.cookie.n_len = sizeof (xid);
+	args.cookie.n_bytes = (char *)&xid;
+
+	oh.oh_sysid = hostp->nh_sysid;
+	xid = atomic_inc_32_nv(&nlm_xid);
+
+	if (xflags & NLM_X_BLOCKING) {
+		args.block = TRUE;
+		nslp = nlm_slock_register(g, hostp, &args.alock, vp);
+	}
+
+	for (;;) {
+		nlm_rpc_t *rpcp;
+		enum clnt_stat stat;
+		struct nlm4_res res;
+		enum nlm4_stats nlm_err;
+
+		error = nlm_host_get_rpc(hostp, vers, &rpcp);
+		if (error != 0) {
+			error = ENOLCK;
+			goto out;
+		}
+
+		bzero(&res, sizeof (res));
+		stat = nlm_lock_rpc(&args, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(hostp, rpcp);
+
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			goto out;
+		}
+
+		DTRACE_PROBE1(lock__res, enum nlm4_stats, res.stat.stat);
+		nlm_err = res.stat.stat;
+		xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
+		if (nlm_err == nlm4_denied_grace_period) {
+			if (args.reclaim) {
+				error = ENOLCK;
+				goto out;
+			}
+
+			error = nlm_host_wait_grace(hostp);
+			if (error != 0)
+				goto out;
+
+			continue;
+		}
+
+		switch (nlm_err) {
+		case nlm4_granted:
+		case nlm4_blocked:
+			error = 0;
+			break;
+
+		case nlm4_denied:
+			if (nslp != NULL) {
+				NLM_WARN("nlm_call_lock: got nlm4_denied for "
+				    "blocking lock\n");
+			}
+
+			error = EAGAIN;
+			break;
+
+		default:
+			error = nlm_map_status(nlm_err);
+		}
+
+		/*
+		 * If we deal with either non-blocking lock or
+		 * with a blocking locks that wasn't blocked on
+		 * the server side (by some reason), our work
+		 * is finished.
+		 */
+		if (nslp == NULL			||
+		    nlm_err != nlm4_blocked		||
+		    error != 0)
+			goto out;
+
+		/*
+		 * Before releasing the r_lkserlock of rnode, we should
+		 * check whether the new lock is "safe". If it's not
+		 * safe, disable caching for the given vnode. That is done
+		 * for sleeping locks only that are waiting for a GRANT reply
+		 * from the NLM server.
+		 *
+		 * NOTE: the vnode cache can be enabled back later if an
+		 * unsafe lock will be merged with existent locks so that
+		 * it will become safe. This condition is checked in the
+		 * NFSv3 code (see nfs_lockcompletion).
+		 */
+		if (!NLM_FLOCK_IS_SAFE(flp)) {
+			mutex_enter(&vp->v_lock);
+			vp->v_flag &= ~VNOCACHE;
+			mutex_exit(&vp->v_lock);
+		}
+
+		/*
+		 * The server should call us back with a
+		 * granted message when the lock succeeds.
+		 * In order to deal with broken servers,
+		 * lost granted messages, or server reboots,
+		 * we will also re-try every few seconds.
+		 *
+		 * Note: We're supposed to call these
+		 * flk_invoke_callbacks when blocking.
+		 * Take care on rnode->r_lkserlock, we should
+		 * release it before going to sleep.
+		 */
+		(void) flk_invoke_callbacks(flcb, FLK_BEFORE_SLEEP);
+		nfs_rw_exit(&rnp->r_lkserlock);
+
+		error = nlm_slock_wait(g, nslp, g->retrans_tmo);
+
+		/*
+		 * NFS expects that we return with rnode->r_lkserlock
+		 * locked on write, lock it back.
+		 *
+		 * NOTE: nfs_rw_enter_sig() can be either interruptible
+		 * or not. It depends on options of NFS mount. Here
+		 * we're _always_ uninterruptible (independently of mount
+		 * options), because nfs_frlock/nfs3_frlock expects that
+		 * we return with rnode->r_lkserlock acquired. So we don't
+		 * want our lock attempt to be interrupted by a signal.
+		 */
+		(void) nfs_rw_enter_sig(&rnp->r_lkserlock, RW_WRITER, 0);
+		(void) flk_invoke_callbacks(flcb, FLK_AFTER_SLEEP);
+
+		if (error == 0) {
+			break;
+		} else if (error == EINTR) {
+			/*
+			 * We need to call the server to cancel our
+			 * lock request.
+			 */
+			DTRACE_PROBE1(cancel__lock, int, error);
+			(void) nlm_call_cancel(&args, hostp, vers);
+			break;
+		} else {
+			/*
+			 * Timeout happened, resend the lock request to
+			 * the server. Well, we're a bit paranoid here,
+			 * but keep in mind previous request could lost
+			 * (especially with conectionless transport).
+			 */
+
+			ASSERT(error == ETIMEDOUT);
+			continue;
+		}
+	}
+
+	/*
+	 * We could disable the vnode cache for the given _sleeping_
+	 * (codition: nslp != NULL) lock if it was unsafe. Normally,
+	 * nfs_lockcompletion() function can enable the vnode cache
+	 * back if the lock becomes safe after activativation. But it
+	 * will not happen if any error occurs on the locking path.
+	 *
+	 * Here we enable the vnode cache back if the error occurred
+	 * and if there aren't any unsafe locks on the given vnode.
+	 * Note that if error happened, sleeping lock was derigistered.
+	 */
+	if (error != 0 && nslp != NULL && nlm_safemap(vp)) {
+		mutex_enter(&vp->v_lock);
+		vp->v_flag |= VNOCACHE;
+		mutex_exit(&vp->v_lock);
+	}
+
+out:
+	if (nslp != NULL)
+		nlm_slock_unregister(g, nslp);
+
+	return (error);
+}
+
+/*
+ * Do NLM_CANCEL call.
+ * Helper for nlm_call_lock() error recovery.
+ */
+static int
+nlm_call_cancel(struct nlm4_lockargs *largs,
+	struct nlm_host *hostp, int vers)
+{
+	nlm4_cancargs cargs;
+	uint32_t xid;
+	int error, retries;
+
+	bzero(&cargs, sizeof (cargs));
+
+	xid = atomic_inc_32_nv(&nlm_xid);
+	cargs.cookie.n_len = sizeof (xid);
+	cargs.cookie.n_bytes = (char *)&xid;
+	cargs.block	= largs->block;
+	cargs.exclusive	= largs->exclusive;
+	cargs.alock	= largs->alock;
+
+	/*
+	 * Unlike all other nlm_call_* functions, nlm_call_cancel
+	 * doesn't spin forever until it gets reasonable response
+	 * from NLM server. It makes limited number of retries and
+	 * if server doesn't send a reasonable reply, it returns an
+	 * error. It behaves like that because it's called from nlm_call_lock
+	 * with blocked signals and thus it can not be interrupted from
+	 * user space.
+	 */
+	for (retries = 0; retries < NLM_CANCEL_NRETRS; retries++) {
+		nlm_rpc_t *rpcp;
+		enum clnt_stat stat;
+		struct nlm4_res res;
+
+		error = nlm_host_get_rpc(hostp, vers, &rpcp);
+		if (error != 0)
+			return (ENOLCK);
+
+		bzero(&res, sizeof (res));
+		stat = nlm_cancel_rpc(&cargs, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(hostp, rpcp);
+
+		DTRACE_PROBE1(cancel__rloop_end, enum clnt_stat, stat);
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			return (error);
+		}
+
+		DTRACE_PROBE1(cancel__res, enum nlm4_stats, res.stat.stat);
+		switch (res.stat.stat) {
+			/*
+			 * There was nothing to cancel. We are going to go ahead
+			 * and assume we got the lock.
+			 */
+		case nlm_denied:
+			/*
+			 * The server has recently rebooted.  Treat this as a
+			 * successful cancellation.
+			 */
+		case nlm4_denied_grace_period:
+			/*
+			 * We managed to cancel.
+			 */
+		case nlm4_granted:
+			error = 0;
+			break;
+
+		default:
+			/*
+			 * Broken server implementation.  Can't really do
+			 * anything here.
+			 */
+			error = EIO;
+			break;
+		}
+
+		xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * Do NLM_UNLOCK call.
+ * Was: nlm_clearlock
+ */
+static int
+nlm_call_unlock(struct flock64 *flp, struct nlm_host *hostp,
+    struct netobj *fhp, int vers)
+{
+	struct nlm4_unlockargs args;
+	struct nlm_owner_handle oh;
+	enum nlm4_stats nlm_err;
+	uint32_t xid;
+	int error;
+
+	bzero(&args, sizeof (args));
+	nlm_init_lock(&args.alock, flp, fhp, &oh);
+
+	oh.oh_sysid = hostp->nh_sysid;
+	xid = atomic_inc_32_nv(&nlm_xid);
+	args.cookie.n_len = sizeof (xid);
+	args.cookie.n_bytes = (char *)&xid;
+
+	for (;;) {
+		nlm_rpc_t *rpcp;
+		struct nlm4_res res;
+		enum clnt_stat stat;
+
+		error = nlm_host_get_rpc(hostp, vers, &rpcp);
+		if (error != 0)
+			return (ENOLCK);
+
+		bzero(&res, sizeof (res));
+		stat = nlm_unlock_rpc(&args, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(hostp, rpcp);
+
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			return (error);
+		}
+
+		DTRACE_PROBE1(unlock__res, enum nlm4_stats, res.stat.stat);
+		nlm_err = res.stat.stat;
+		xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
+		if (nlm_err == nlm4_denied_grace_period) {
+			error = nlm_host_wait_grace(hostp);
+			if (error != 0)
+				return (error);
+
+			continue;
+		}
+
+		break;
+	}
+
+	/* special cases */
+	switch (nlm_err) {
+	case nlm4_denied:
+		error = EINVAL;
+		break;
+	default:
+		error = nlm_map_status(nlm_err);
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * Do NLM_TEST call.
+ * Was: nlm_getlock()
+ */
+static int
+nlm_call_test(struct flock64 *flp, struct nlm_host *hostp,
+    struct netobj *fhp, int vers)
+{
+	struct nlm4_testargs args;
+	struct nlm4_holder h;
+	struct nlm_owner_handle oh;
+	enum nlm4_stats nlm_err;
+	uint32_t xid;
+	int error;
+
+	bzero(&args, sizeof (args));
+	nlm_init_lock(&args.alock, flp, fhp, &oh);
+
+	args.exclusive = (flp->l_type == F_WRLCK);
+	oh.oh_sysid = hostp->nh_sysid;
+	xid = atomic_inc_32_nv(&nlm_xid);
+	args.cookie.n_len = sizeof (xid);
+	args.cookie.n_bytes = (char *)&xid;
+
+	for (;;) {
+		nlm_rpc_t *rpcp;
+		struct nlm4_testres res;
+		enum clnt_stat stat;
+
+		error = nlm_host_get_rpc(hostp, vers, &rpcp);
+		if (error != 0)
+			return (ENOLCK);
+
+		bzero(&res, sizeof (res));
+		stat = nlm_test_rpc(&args, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(hostp, rpcp);
+
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			return (error);
+		}
+
+		DTRACE_PROBE1(test__res, enum nlm4_stats, res.stat.stat);
+		nlm_err = res.stat.stat;
+		bcopy(&res.stat.nlm4_testrply_u.holder, &h, sizeof (h));
+		xdr_free((xdrproc_t)xdr_nlm4_testres, (void *)&res);
+		if (nlm_err == nlm4_denied_grace_period) {
+			error = nlm_host_wait_grace(hostp);
+			if (error != 0)
+				return (error);
+
+			continue;
+		}
+
+		break;
+	}
+
+	switch (nlm_err) {
+	case nlm4_granted:
+		flp->l_type = F_UNLCK;
+		error = 0;
+		break;
+
+	case nlm4_denied:
+		flp->l_start = h.l_offset;
+		flp->l_len = h.l_len;
+		flp->l_pid = h.svid;
+		flp->l_type = (h.exclusive) ? F_WRLCK : F_RDLCK;
+		flp->l_whence = SEEK_SET;
+		flp->l_sysid = 0;
+		error = 0;
+		break;
+
+	default:
+		error = nlm_map_status(nlm_err);
+		break;
+	}
+
+	return (error);
+}
+
+
+static void
+nlm_init_lock(struct nlm4_lock *lock,
+	const struct flock64 *fl, struct netobj *fh,
+	struct nlm_owner_handle *oh)
+{
+
+	/* Caller converts to zero-base. */
+	VERIFY(fl->l_whence == SEEK_SET);
+	bzero(lock, sizeof (*lock));
+	bzero(oh, sizeof (*oh));
+
+	lock->caller_name = uts_nodename();
+	lock->fh.n_len = fh->n_len;
+	lock->fh.n_bytes = fh->n_bytes;
+	lock->oh.n_len = sizeof (*oh);
+	lock->oh.n_bytes = (void *)oh;
+	lock->svid = fl->l_pid;
+	lock->l_offset = fl->l_start;
+	lock->l_len = fl->l_len;
+}
+
+/* ************************************************************** */
+
+int
+nlm_shrlock(struct vnode *vp, int cmd, struct shrlock *shr,
+	int flags, struct netobj *fh, int vers)
+{
+	struct shrlock shlk;
+	mntinfo_t *mi;
+	servinfo_t *sv;
+	const char *netid;
+	struct nlm_host *host = NULL;
+	int error;
+	struct nlm_globals *g;
+
+	mi = VTOMI(vp);
+	sv = mi->mi_curr_serv;
+
+	netid = nlm_knc_to_netid(sv->sv_knconf);
+	if (netid == NULL) {
+		NLM_ERR("nlm_shrlock: unknown NFS netid\n");
+		return (ENOSYS);
+	}
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	host = nlm_host_findcreate(g, sv->sv_hostname, netid, &sv->sv_addr);
+	if (host == NULL)
+		return (ENOSYS);
+
+	/*
+	 * Fill in s_sysid for the local locking calls.
+	 * Also, let's not trust the caller's l_pid.
+	 */
+	shlk = *shr;
+	shlk.s_sysid = host->nh_sysid | LM_SYSID_CLIENT;
+	shlk.s_pid = curproc->p_pid;
+
+	if (cmd == F_UNSHARE) {
+		/*
+		 * Purge local (cached) share information first,
+		 * then clear the remote share.
+		 */
+		(void) nlm_local_shrlock(vp, &shlk, cmd, flags);
+		nlm_shres_untrack(host, vp, &shlk);
+		error = nlm_call_unshare(&shlk, host, fh, vers);
+		goto out;
+	}
+
+	nfs_add_locking_id(vp, curproc->p_pid, RLMPL_OWNER,
+	    shr->s_owner, shr->s_own_len);
+
+	error = nlm_call_share(&shlk, host, fh, vers, FALSE);
+	if (error != 0)
+		goto out;
+
+	/*
+	 * Save the share locally.  This should not fail,
+	 * because the server is authoritative about shares
+	 * and it just told us we have the share reservation!
+	 */
+	error = nlm_local_shrlock(vp, shr, cmd, flags);
+	if (error != 0) {
+		/*
+		 * Oh oh, we really don't expect an error here.
+		 */
+		NLM_WARN("nlm_shrlock: set locally, err %d\n", error);
+		error = 0;
+	}
+
+	nlm_shres_track(host, vp, &shlk);
+	nlm_host_monitor(g, host, 0);
+
+out:
+	nlm_host_release(g, host);
+
+	return (error);
+}
+
+static int
+nlm_reclaim_share(struct nlm_host *hostp, vnode_t *vp,
+    struct shrlock *shr, uint32_t orig_state)
+{
+	struct netobj lm_fh;
+	int error, state;
+	rpcvers_t vers;
+
+	state = nlm_host_get_state(hostp);
+	if (state != orig_state) {
+		/*
+		 * It seems that NLM server rebooted while
+		 * we were busy with recovery.
+		 */
+		return (ERESTART);
+	}
+
+	error = nlm_init_fh_by_vp(vp, &lm_fh, &vers);
+	if (error != 0)
+		return (error);
+
+	return (nlm_call_share(shr, hostp, &lm_fh, vers, 1));
+}
+
+/*
+ * Set local share information for some NFS server.
+ *
+ * Called after a share request (set or clear) succeeded. We record
+ * the details in the local lock manager. Note that since the remote
+ * server has granted the share, we can be sure that it doesn't
+ * conflict with any other shares we have in the local lock manager.
+ *
+ * Since it is possible that host may also make NLM client requests to
+ * our NLM server, we use a different sysid value to record our own
+ * client shares.
+ */
+int
+nlm_local_shrlock(vnode_t *vp, struct shrlock *shr, int cmd, int flags)
+{
+	return (fs_shrlock(vp, cmd, shr, flags, CRED(), NULL));
+}
+
+static void
+nlm_local_shrcancel(vnode_t *vp, struct shrlock *shr)
+{
+	(void) nlm_local_shrlock(vp, shr, F_UNSHARE, FREAD | FWRITE);
+	nlm_send_siglost(shr->s_pid);
+}
+
+/*
+ * Do NLM_SHARE call.
+ * Was: nlm_setshare()
+ */
+static int
+nlm_call_share(struct shrlock *shr, struct nlm_host *host,
+    struct netobj *fh, int vers, int reclaim)
+{
+	struct nlm4_shareargs args;
+	enum nlm4_stats nlm_err;
+	uint32_t xid;
+	int error;
+
+	bzero(&args, sizeof (args));
+	nlm_init_share(&args.share, shr, fh);
+
+	args.reclaim = reclaim;
+	xid = atomic_inc_32_nv(&nlm_xid);
+	args.cookie.n_len = sizeof (xid);
+	args.cookie.n_bytes = (char *)&xid;
+
+
+	for (;;) {
+		nlm_rpc_t *rpcp;
+		struct nlm4_shareres res;
+		enum clnt_stat stat;
+
+		error = nlm_host_get_rpc(host, vers, &rpcp);
+		if (error != 0)
+			return (ENOLCK);
+
+		bzero(&res, sizeof (res));
+		stat = nlm_share_rpc(&args, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(host, rpcp);
+
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			return (error);
+		}
+
+		DTRACE_PROBE1(share__res, enum nlm4_stats, res.stat);
+		nlm_err = res.stat;
+		xdr_free((xdrproc_t)xdr_nlm4_shareres, (void *)&res);
+		if (nlm_err == nlm4_denied_grace_period) {
+			if (args.reclaim)
+				return (ENOLCK);
+
+			error = nlm_host_wait_grace(host);
+			if (error != 0)
+				return (error);
+
+			continue;
+		}
+
+		break;
+	}
+
+	switch (nlm_err) {
+	case nlm4_granted:
+		error = 0;
+		break;
+	case nlm4_blocked:
+	case nlm4_denied:
+		error = EAGAIN;
+		break;
+	case nlm4_denied_nolocks:
+	case nlm4_deadlck:
+		error = ENOLCK;
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	return (error);
+}
+
+/*
+ * Do NLM_UNSHARE call.
+ */
+static int
+nlm_call_unshare(struct shrlock *shr, struct nlm_host *host,
+    struct netobj *fh, int vers)
+{
+	struct nlm4_shareargs args;
+	enum nlm4_stats nlm_err;
+	uint32_t xid;
+	int error;
+
+	bzero(&args, sizeof (args));
+	nlm_init_share(&args.share, shr, fh);
+
+	xid = atomic_inc_32_nv(&nlm_xid);
+	args.cookie.n_len = sizeof (xid);
+	args.cookie.n_bytes = (char *)&xid;
+
+	for (;;) {
+		nlm_rpc_t *rpcp;
+		struct nlm4_shareres res;
+		enum clnt_stat stat;
+
+		error = nlm_host_get_rpc(host, vers, &rpcp);
+		if (error != 0)
+			return (ENOLCK);
+
+		bzero(&res, sizeof (res));
+		stat = nlm_unshare_rpc(&args, &res, rpcp->nr_handle, vers);
+		nlm_host_rele_rpc(host, rpcp);
+
+		error = nlm_map_clnt_stat(stat);
+		if (error != 0) {
+			if (error == EAGAIN)
+				continue;
+
+			return (error);
+		}
+
+		DTRACE_PROBE1(unshare__res, enum nlm4_stats, res.stat);
+		nlm_err = res.stat;
+		xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
+		if (nlm_err == nlm4_denied_grace_period) {
+			error = nlm_host_wait_grace(host);
+			if (error != 0)
+				return (error);
+
+			continue;
+		}
+
+		break;
+	}
+
+	switch (nlm_err) {
+	case nlm4_granted:
+		error = 0;
+		break;
+	case nlm4_denied:
+		error = EAGAIN;
+		break;
+	case nlm4_denied_nolocks:
+		error = ENOLCK;
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+
+	return (error);
+}
+
+static void
+nlm_init_share(struct nlm4_share *args,
+	const struct shrlock *shr, struct netobj *fh)
+{
+
+	bzero(args, sizeof (*args));
+
+	args->caller_name = uts_nodename();
+	args->fh.n_len = fh->n_len;
+	args->fh.n_bytes = fh->n_bytes;
+	args->oh.n_len = shr->s_own_len;
+	args->oh.n_bytes = (void *)shr->s_owner;
+
+	switch (shr->s_deny) {
+	default:
+	case F_NODNY:
+		args->mode = fsm_DN;
+		break;
+	case F_RDDNY:
+		args->mode = fsm_DR;
+		break;
+	case F_WRDNY:
+		args->mode = fsm_DW;
+		break;
+	case F_RWDNY:
+		args->mode = fsm_DRW;
+		break;
+	}
+
+	switch (shr->s_access) {
+	default:
+	case 0:	/* seen with F_UNSHARE */
+		args->access = fsa_NONE;
+		break;
+	case F_RDACC:
+		args->access = fsa_R;
+		break;
+	case F_WRACC:
+		args->access = fsa_W;
+		break;
+	case F_RWACC:
+		args->access = fsa_RW;
+		break;
+	}
+}
+
+/*
+ * Initialize filehandle according to the version
+ * of NFS vnode was created on. The version of
+ * NLM that can be used with given NFS version
+ * is saved to lm_vers.
+ */
+static int
+nlm_init_fh_by_vp(vnode_t *vp, struct netobj *fh, rpcvers_t *lm_vers)
+{
+	mntinfo_t *mi = VTOMI(vp);
+
+	/*
+	 * Too bad the NFS code doesn't just carry the FH
+	 * in a netobj or a netbuf.
+	 */
+	switch (mi->mi_vers) {
+	case NFS_V3:
+		/* See nfs3_frlock() */
+		*lm_vers = NLM4_VERS;
+		fh->n_len = VTOFH3(vp)->fh3_length;
+		fh->n_bytes = (char *)&(VTOFH3(vp)->fh3_u.data);
+		break;
+
+	case NFS_VERSION:
+		/* See nfs_frlock() */
+		*lm_vers = NLM_VERS;
+		fh->n_len = sizeof (fhandle_t);
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		fh->n_bytes = (char *)VTOFH(vp);
+		break;
+	default:
+		return (ENOSYS);
+	}
+
+	return (0);
+}
+
+/*
+ * Send SIGLOST to the process identified by pid.
+ * NOTE: called when NLM decides to remove lock
+ * or share reservation ownder by the process
+ * by force.
+ */
+static void
+nlm_send_siglost(pid_t pid)
+{
+	proc_t *p;
+
+	mutex_enter(&pidlock);
+	p = prfind(pid);
+	if (p != NULL)
+		psignal(p, SIGLOST);
+
+	mutex_exit(&pidlock);
+}
+
+static int
+nlm_map_clnt_stat(enum clnt_stat stat)
+{
+	switch (stat) {
+	case RPC_SUCCESS:
+		return (0);
+
+	case RPC_TIMEDOUT:
+	case RPC_PROGUNAVAIL:
+		return (EAGAIN);
+
+	case RPC_INTR:
+		return (EINTR);
+
+	default:
+		return (EINVAL);
+	}
+}
+
+static int
+nlm_map_status(enum nlm4_stats stat)
+{
+	switch (stat) {
+	case nlm4_granted:
+		return (0);
+
+	case nlm4_denied:
+		return (EAGAIN);
+
+	case nlm4_denied_nolocks:
+		return (ENOLCK);
+
+	case nlm4_blocked:
+		return (EAGAIN);
+
+	case nlm4_denied_grace_period:
+		return (EAGAIN);
+
+	case nlm4_deadlck:
+		return (EDEADLK);
+
+	case nlm4_rofs:
+		return (EROFS);
+
+	case nlm4_stale_fh:
+		return (ESTALE);
+
+	case nlm4_fbig:
+		return (EFBIG);
+
+	case nlm4_failed:
+		return (EACCES);
+
+	default:
+		return (EINVAL);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_dispatch.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,755 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy is of the CDDL is also available via the Internet
+ * at http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager, server-side dispatch tables and
+ * dispatch programs: nlm_prog_3, nlm_prog4
+ *
+ * These are called by RPC framework after the RPC service
+ * endpoints setup done in nlm_impl.c: nlm_svc_add_ep().
+ *
+ * Originally from rpcgen, then reduced.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sdt.h>
+#include <rpcsvc/nlm_prot.h>
+#include "nlm_impl.h"
+
+/*
+ * Dispatch entry function pointers.
+ */
+typedef bool_t (*nlm_svc_func_t)(void *, void *, struct svc_req *);
+typedef void (*nlm_freeres_func_t)(void *);
+
+/*
+ * Entries in the dispatch tables below.
+ */
+struct dispatch_entry {
+	nlm_svc_func_t		de_svc;		/* service routine function */
+	xdrproc_t		de_xargs;	/* XDR args decode function */
+	xdrproc_t		de_xres;	/* XDR res encode function */
+	nlm_freeres_func_t	de_resfree;	/* free res function */
+	int			de_ressz;	/* size of result */
+	uint_t			de_flags;	/* flags */
+};
+
+/* Flag bits in de_flags */
+#define	NLM_DISP_NOREMOTE	1	/* Local calls only */
+
+/*
+ * Cast macros for dispatch table function pointers.
+ */
+#define	NLM_SVC_FUNC(func)	(nlm_svc_func_t)func
+#define	NLM_FREERES_FUNC(func)	(nlm_freeres_func_t)func
+
+/* ARGSUSED */
+static bool_t
+nlm_null_svc(void *args, void *resp, struct svc_req *sr)
+{
+	return (TRUE);
+}
+
+/*
+ * The common NLM service dispatch function, used by
+ * both: nlm_prog_3, nlm_prog_4
+ */
+void
+nlm_dispatch(
+	struct svc_req *rqstp,
+	SVCXPRT *transp,
+	const struct dispatch_entry *de)
+{
+	union {
+		/* All the arg types */
+		nlm_cancargs	au_cancargs;
+		nlm_lockargs	au_lockargs;
+		nlm_notify	au_notify;
+		nlm_res		au_res;
+		nlm_shareargs	au_shareargs;
+		nlm_sm_status	au_sm_status;
+		nlm_testargs	au_testargs;
+		nlm_testres	au_testres;
+		nlm_unlockargs	au_unlockargs;
+		nlm4_cancargs	au_cancargs4;
+		nlm4_lockargs	au_lockargs4;
+		nlm4_notify	au_notify4;
+		nlm4_res	au_res4;
+		nlm4_shareargs	au_shareargs4;
+		nlm4_testargs	au_testargs4;
+		nlm4_testres	au_testres4;
+		nlm4_unlockargs	au_unlockargs4;
+	} argu;
+	void *args = &argu;
+	union {
+		/* All the ret types */
+		int		ru_int;
+		nlm_res		ru_res;
+		nlm_shareres	ru_shareres;
+		nlm_testres	ru_testres;
+		nlm4_res	ru_res4;
+		nlm4_shareres	ru_shareres4;
+		nlm4_testres	ru_testres4;
+
+	} resu;
+	void *res = &resu;
+	nlm_svc_func_t func;
+	bool_t do_reply = FALSE;
+	bool_t dupcached = FALSE;
+	struct dupreq *dr;
+	int dupstat;
+
+	if ((func = de->de_svc) == NULL) {
+		svcerr_noproc(transp);
+		return;
+	}
+
+	if ((de->de_flags & NLM_DISP_NOREMOTE) &&
+	    !nlm_caller_is_local(transp)) {
+		svcerr_noproc(transp);
+		return;
+	}
+
+	/*
+	 * This section from rpcgen, and then modified slightly.
+	 *
+	 * Dispatch entries that should _never_ send a response
+	 * (i.e. all the _MSG and _RES entries) put NULL in the
+	 * de_xres field to indicate that.  For such entries, we
+	 * will NOT call svc_sendreply nor xdr_free().  Normal
+	 * dispatch entries skip svc_sendreply if the dispatch
+	 * function returns zero, but always call xdr_free().
+	 *
+	 * There are more complex cases where some dispatch
+	 * functions need to send their own reply.  We chose
+	 * to indicate those by returning false from the
+	 * service routine.
+	 */
+	bzero(&argu, sizeof (argu));
+	if (!SVC_GETARGS(transp, de->de_xargs, args)) {
+		svcerr_decode(transp);
+		return;
+	}
+
+	/*
+	 * Duplicate request cache.
+	 *
+	 * Since none of the NLM replies are very large we have simplified the
+	 * DRC by not distinguishing between idempotent and non-idempotent
+	 * requests.
+	 */
+	dupstat = SVC_DUP_EXT(transp, rqstp, res, de->de_ressz, &dr,
+	    &dupcached);
+
+	switch (dupstat) {
+	case DUP_ERROR:
+		svcerr_systemerr(transp);
+		break;
+	case DUP_INPROGRESS:
+		break;
+	case DUP_NEW:
+	case DUP_DROP:
+		/*
+		 * When UFS is quiescing it uses lockfs to block vnode
+		 * operations until it has finished quiescing.  Set the
+		 * thread's T_DONTPEND flag to prevent the service routine
+		 * from blocking due to a lockfs lock. (See ufs_check_lockfs)
+		 */
+		curthread->t_flag |= T_DONTPEND;
+
+		bzero(&resu, sizeof (resu));
+		do_reply = (*func)(args, res, rqstp);
+
+		curthread->t_flag &= ~T_DONTPEND;
+		if (curthread->t_flag & T_WOULDBLOCK) {
+			curthread->t_flag &= ~T_WOULDBLOCK;
+			SVC_DUPDONE_EXT(transp, dr, res, NULL,
+			    de->de_ressz, DUP_DROP);
+			do_reply = FALSE;
+			break;
+		}
+		SVC_DUPDONE_EXT(transp, dr, res, de->de_resfree,
+		    de->de_ressz, DUP_DONE);
+		dupcached = TRUE;
+		break;
+	case DUP_DONE:
+		/*
+		 * The service routine may have been responsible for sending
+		 * the reply for the original request but for a re-xmitted
+		 * request we don't invoke the service routine so we must
+		 * re-xmit the reply from the dispatch function.
+		 *
+		 * If de_xres is NULL this is a one-way message so no reply is
+		 * needed.
+		 */
+		if (de->de_xres != NULL_xdrproc_t) {
+			do_reply = TRUE;
+		}
+		break;
+	}
+
+	if (do_reply) {
+		ASSERT(de->de_xres != NULL_xdrproc_t);
+		DTRACE_PROBE3(sendreply, struct svc_req *, rqstp,
+		    SVCXPRT *, transp, struct dispatch_entry *, de);
+
+		if (!svc_sendreply(transp, de->de_xres, res)) {
+			svcerr_systemerr(transp);
+			NLM_ERR("nlm_dispatch(): svc_sendreply() failed!\n");
+		}
+
+		if (!dupcached) {
+			xdr_free(de->de_xres, res);
+		}
+	}
+
+	if (!SVC_FREEARGS(transp, de->de_xargs, args))
+		NLM_WARN("nlm_dispatch(): unable to free arguments");
+}
+
+/*
+ * Result free functions.  The functions are called by the RPC duplicate
+ * request cache code when an entry is being evicted from the cache.
+ */
+static void
+nlm_res_free(nlm_res *resp)
+{
+	xdr_free(xdr_nlm_res, (char *)resp);
+}
+
+static void
+nlm_shareres_free(nlm_shareres *resp)
+{
+	xdr_free(xdr_nlm_shareres, (char *)resp);
+}
+
+static void
+nlm_testres_free(nlm_testres *resp)
+{
+	xdr_free(xdr_nlm_testres, (char *)resp);
+}
+
+static void
+nlm4_res_free(nlm4_res *resp)
+{
+	xdr_free(xdr_nlm4_res, (char *)resp);
+}
+
+static void
+nlm4_shareres_free(nlm4_shareres *resp)
+{
+	xdr_free(xdr_nlm4_shareres, (char *)resp);
+}
+
+static void
+nlm4_testres_free(nlm4_testres *resp)
+{
+	xdr_free(xdr_nlm4_testres, (char *)resp);
+}
+
+/*
+ * Dispatch tables for each program version.
+ *
+ * The tables here were all originally from rpcgen,
+ * but then arg/resp sizes removed, flags added.
+ */
+
+/*
+ * Dispatch table for versions 1, 2, 3
+ * (NLM_VERS, NLM_SM, NLM_VERSX)
+ */
+static const struct dispatch_entry
+nlm_prog_3_dtable[] = {
+
+	/*
+	 * Version 1 (NLM_VERS) entries.
+	 */
+
+	{ /* 0: NULLPROC */
+	NLM_SVC_FUNC(nlm_null_svc),
+	(xdrproc_t)xdr_void,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	0 },
+
+	{ /* 1: NLM_TEST */
+	NLM_SVC_FUNC(nlm_test_1_svc),
+	(xdrproc_t)xdr_nlm_testargs,
+	(xdrproc_t)xdr_nlm_testres,
+	NLM_FREERES_FUNC(nlm_testres_free),
+	sizeof (nlm_testres),
+	0 },
+
+	{ /* 2: NLM_LOCK */
+	NLM_SVC_FUNC(nlm_lock_1_svc),
+	(xdrproc_t)xdr_nlm_lockargs,
+	(xdrproc_t)xdr_nlm_res,
+	NLM_FREERES_FUNC(nlm_res_free),
+	sizeof (nlm_res),
+	0 },
+
+	{ /* 3: NLM_CANCEL */
+	NLM_SVC_FUNC(nlm_cancel_1_svc),
+	(xdrproc_t)xdr_nlm_cancargs,
+	(xdrproc_t)xdr_nlm_res,
+	NLM_FREERES_FUNC(nlm_res_free),
+	sizeof (nlm_res),
+	0 },
+
+	{ /* 4: NLM_UNLOCK */
+	NLM_SVC_FUNC(nlm_unlock_1_svc),
+	(xdrproc_t)xdr_nlm_unlockargs,
+	(xdrproc_t)xdr_nlm_res,
+	NLM_FREERES_FUNC(nlm_res_free),
+	sizeof (nlm_res),
+	0 },
+
+	{ /* 5: NLM_GRANTED */
+	NLM_SVC_FUNC(nlm_granted_1_svc),
+	(xdrproc_t)xdr_nlm_testargs,
+	(xdrproc_t)xdr_nlm_res,
+	NLM_FREERES_FUNC(nlm_res_free),
+	sizeof (nlm_res),
+	0 },
+
+	/*
+	 * All the _MSG and _RES entries are "one way" calls that
+	 * skip the usual RPC reply.  We give them a null xdr_res
+	 * function so the dispatcher will not send a reply.
+	 */
+
+	{ /* 6: NLM_TEST_MSG */
+	NLM_SVC_FUNC(nlm_test_msg_1_svc),
+	(xdrproc_t)xdr_nlm_testargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 7: NLM_LOCK_MSG */
+	NLM_SVC_FUNC(nlm_lock_msg_1_svc),
+	(xdrproc_t)xdr_nlm_lockargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 8: NLM_CANCEL_MSG */
+	NLM_SVC_FUNC(nlm_cancel_msg_1_svc),
+	(xdrproc_t)xdr_nlm_cancargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 9: NLM_UNLOCK_MSG */
+	NLM_SVC_FUNC(nlm_unlock_msg_1_svc),
+	(xdrproc_t)xdr_nlm_unlockargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 10: NLM_GRANTED_MSG */
+	NLM_SVC_FUNC(nlm_granted_msg_1_svc),
+	(xdrproc_t)xdr_nlm_testargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 11: NLM_TEST_RES */
+	NLM_SVC_FUNC(nlm_test_res_1_svc),
+	(xdrproc_t)xdr_nlm_testres,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 12: NLM_LOCK_RES */
+	NLM_SVC_FUNC(nlm_lock_res_1_svc),
+	(xdrproc_t)xdr_nlm_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 13: NLM_CANCEL_RES */
+	NLM_SVC_FUNC(nlm_cancel_res_1_svc),
+	(xdrproc_t)xdr_nlm_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 14: NLM_UNLOCK_RES */
+	NLM_SVC_FUNC(nlm_unlock_res_1_svc),
+	(xdrproc_t)xdr_nlm_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 15: NLM_GRANTED_RES */
+	NLM_SVC_FUNC(nlm_granted_res_1_svc),
+	(xdrproc_t)xdr_nlm_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 16: not used */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 17: NLM_SM_NOTIFY1 */
+	NLM_SVC_FUNC(nlm_sm_notify1_2_svc),
+	(xdrproc_t)xdr_nlm_sm_status,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	NLM_DISP_NOREMOTE },
+
+	{ /* 18: NLM_SM_NOTIFY2 */
+	NLM_SVC_FUNC(nlm_sm_notify2_2_svc),
+	(xdrproc_t)xdr_nlm_sm_status,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	NLM_DISP_NOREMOTE },
+
+	/*
+	 * Version 3 (NLM_VERSX) entries.
+	 */
+
+	{ /* 19: not used */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 20: NLM_SHARE */
+	NLM_SVC_FUNC(nlm_share_3_svc),
+	(xdrproc_t)xdr_nlm_shareargs,
+	(xdrproc_t)xdr_nlm_shareres,
+	NLM_FREERES_FUNC(nlm_shareres_free),
+	sizeof (nlm_shareres),
+	0 },
+
+	{ /* 21: NLM_UNSHARE */
+	NLM_SVC_FUNC(nlm_unshare_3_svc),
+	(xdrproc_t)xdr_nlm_shareargs,
+	(xdrproc_t)xdr_nlm_shareres,
+	NLM_FREERES_FUNC(nlm_shareres_free),
+	sizeof (nlm_shareres),
+	0 },
+
+	{ /* 22: NLM_NM_LOCK */
+	NLM_SVC_FUNC(nlm_nm_lock_3_svc),
+	(xdrproc_t)xdr_nlm_lockargs,
+	(xdrproc_t)xdr_nlm_res,
+	NLM_FREERES_FUNC(nlm_res_free),
+	sizeof (nlm_res),
+	0 },
+
+	{ /* 23: NLM_FREE_ALL */
+	NLM_SVC_FUNC(nlm_free_all_3_svc),
+	(xdrproc_t)xdr_nlm_notify,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	0 },
+};
+static int nlm_prog_3_dtsize =
+	sizeof (nlm_prog_3_dtable) /
+	sizeof (nlm_prog_3_dtable[0]);
+
+/*
+ * RPC dispatch function for nlm_prot versions: 1,2,3
+ */
+void
+nlm_prog_3(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+	const struct dispatch_entry *de;
+	rpcproc_t max_proc;
+
+	switch (rqstp->rq_vers) {
+	case NLM_VERS:
+		max_proc = NLM_GRANTED_RES;
+		break;
+	case NLM_SM:
+		max_proc = NLM_SM_NOTIFY2;
+		break;
+	case NLM_VERSX:
+		max_proc = NLM_FREE_ALL;
+		break;
+	default:
+		/* Our svc registration should prevent this. */
+		ASSERT(0); /* paranoid */
+		svcerr_noprog(transp);
+		return;
+	}
+	ASSERT(max_proc < nlm_prog_3_dtsize);
+
+	if (rqstp->rq_proc > max_proc) {
+		svcerr_noproc(transp);
+		return;
+	}
+
+	de = &nlm_prog_3_dtable[rqstp->rq_proc];
+
+	nlm_dispatch(rqstp, transp, de);
+}
+
+/*
+ * Dispatch table for version 4 (NLM4_VERS)
+ */
+static const struct dispatch_entry
+nlm_prog_4_dtable[] = {
+
+	{ /* 0: NULLPROC */
+	NLM_SVC_FUNC(nlm_null_svc),
+	(xdrproc_t)xdr_void,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	0 },
+
+	{ /* 1: NLM4_TEST */
+	NLM_SVC_FUNC(nlm4_test_4_svc),
+	(xdrproc_t)xdr_nlm4_testargs,
+	(xdrproc_t)xdr_nlm4_testres,
+	NLM_FREERES_FUNC(nlm4_testres_free),
+	sizeof (nlm4_testres),
+	0 },
+
+	{ /* 2: NLM4_LOCK */
+	NLM_SVC_FUNC(nlm4_lock_4_svc),
+	(xdrproc_t)xdr_nlm4_lockargs,
+	(xdrproc_t)xdr_nlm4_res,
+	NLM_FREERES_FUNC(nlm4_res_free),
+	sizeof (nlm4_res),
+	0 },
+
+	{ /* 3: NLM4_CANCEL */
+	NLM_SVC_FUNC(nlm4_cancel_4_svc),
+	(xdrproc_t)xdr_nlm4_cancargs,
+	(xdrproc_t)xdr_nlm4_res,
+	NLM_FREERES_FUNC(nlm4_res_free),
+	sizeof (nlm4_res),
+	0 },
+
+	{ /* 4: NLM4_UNLOCK */
+	NLM_SVC_FUNC(nlm4_unlock_4_svc),
+	(xdrproc_t)xdr_nlm4_unlockargs,
+	(xdrproc_t)xdr_nlm4_res,
+	NLM_FREERES_FUNC(nlm4_res_free),
+	sizeof (nlm4_res),
+	0 },
+
+	{ /* 5: NLM4_GRANTED */
+	NLM_SVC_FUNC(nlm4_granted_4_svc),
+	(xdrproc_t)xdr_nlm4_testargs,
+	(xdrproc_t)xdr_nlm4_res,
+	NLM_FREERES_FUNC(nlm4_res_free),
+	sizeof (nlm4_res),
+	0 },
+
+	/*
+	 * All the _MSG and _RES entries are "one way" calls that
+	 * skip the usual RPC reply.  We give them a null xdr_res
+	 * function so the dispatcher will not send a reply.
+	 */
+
+	{ /* 6: NLM4_TEST_MSG */
+	NLM_SVC_FUNC(nlm4_test_msg_4_svc),
+	(xdrproc_t)xdr_nlm4_testargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 7: NLM4_LOCK_MSG */
+	NLM_SVC_FUNC(nlm4_lock_msg_4_svc),
+	(xdrproc_t)xdr_nlm4_lockargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 8: NLM4_CANCEL_MSG */
+	NLM_SVC_FUNC(nlm4_cancel_msg_4_svc),
+	(xdrproc_t)xdr_nlm4_cancargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 9: NLM4_UNLOCK_MSG */
+	NLM_SVC_FUNC(nlm4_unlock_msg_4_svc),
+	(xdrproc_t)xdr_nlm4_unlockargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 10: NLM4_GRANTED_MSG */
+	NLM_SVC_FUNC(nlm4_granted_msg_4_svc),
+	(xdrproc_t)xdr_nlm4_testargs,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 11: NLM4_TEST_RES */
+	NLM_SVC_FUNC(nlm4_test_res_4_svc),
+	(xdrproc_t)xdr_nlm4_testres,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 12: NLM4_LOCK_RES */
+	NLM_SVC_FUNC(nlm4_lock_res_4_svc),
+	(xdrproc_t)xdr_nlm4_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 13: NLM4_CANCEL_RES */
+	NLM_SVC_FUNC(nlm4_cancel_res_4_svc),
+	(xdrproc_t)xdr_nlm4_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 14: NLM4_UNLOCK_RES */
+	NLM_SVC_FUNC(nlm4_unlock_res_4_svc),
+	(xdrproc_t)xdr_nlm4_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 15: NLM4_GRANTED_RES */
+	NLM_SVC_FUNC(nlm4_granted_res_4_svc),
+	(xdrproc_t)xdr_nlm4_res,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 16: not used */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 17: NLM_SM_NOTIFY1 (not in v4) */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 18: NLM_SM_NOTIFY2 (not in v4) */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 19: not used */
+	NLM_SVC_FUNC(0),
+	(xdrproc_t)0,
+	(xdrproc_t)0,
+	NULL,
+	0,
+	0 },
+
+	{ /* 20: NLM4_SHARE */
+	NLM_SVC_FUNC(nlm4_share_4_svc),
+	(xdrproc_t)xdr_nlm4_shareargs,
+	(xdrproc_t)xdr_nlm4_shareres,
+	NLM_FREERES_FUNC(nlm4_shareres_free),
+	sizeof (nlm4_shareres),
+	0 },
+
+	{ /* 21: NLM4_UNSHARE */
+	NLM_SVC_FUNC(nlm4_unshare_4_svc),
+	(xdrproc_t)xdr_nlm4_shareargs,
+	(xdrproc_t)xdr_nlm4_shareres,
+	NLM_FREERES_FUNC(nlm4_shareres_free),
+	sizeof (nlm4_shareres),
+	0 },
+
+	{ /* 22: NLM4_NM_LOCK */
+	NLM_SVC_FUNC(nlm4_nm_lock_4_svc),
+	(xdrproc_t)xdr_nlm4_lockargs,
+	(xdrproc_t)xdr_nlm4_res,
+	NLM_FREERES_FUNC(nlm4_res_free),
+	sizeof (nlm4_res),
+	0 },
+
+	{ /* 23: NLM4_FREE_ALL */
+	NLM_SVC_FUNC(nlm4_free_all_4_svc),
+	(xdrproc_t)xdr_nlm4_notify,
+	(xdrproc_t)xdr_void,
+	NULL,
+	0,
+	0 },
+};
+static int nlm_prog_4_dtsize =
+	sizeof (nlm_prog_4_dtable) /
+	sizeof (nlm_prog_4_dtable[0]);
+
+/*
+ * RPC dispatch function for nlm_prot version 4.
+ */
+void
+nlm_prog_4(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+	const struct dispatch_entry *de;
+
+	if (rqstp->rq_vers != NLM4_VERS) {
+		/* Our svc registration should prevent this. */
+		ASSERT(0); /* paranoid */
+		svcerr_noprog(transp);
+		return;
+	}
+
+	if (rqstp->rq_proc >= nlm_prog_4_dtsize) {
+		svcerr_noproc(transp);
+		return;
+	}
+
+	de = &nlm_prog_4_dtable[rqstp->rq_proc];
+
+	nlm_dispatch(rqstp, transp, de);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_impl.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,2740 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * NFS LockManager, start/stop, support functions, etc.
+ * Most of the interesting code is here.
+ *
+ * Source code derived from FreeBSD nlm_prot_impl.c
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/mount.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/share.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/class.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/queue.h>
+#include <sys/bitmap.h>
+#include <sys/sdt.h>
+#include <netinet/in.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/rpcb_prot.h>
+
+#include <rpcsvc/nlm_prot.h>
+#include <rpcsvc/sm_inter.h>
+#include <rpcsvc/nsm_addr.h>
+
+#include <nfs/nfs.h>
+#include <nfs/nfs_clnt.h>
+#include <nfs/export.h>
+#include <nfs/rnode.h>
+#include <nfs/lm.h>
+
+#include "nlm_impl.h"
+
+struct nlm_knc {
+	struct knetconfig	n_knc;
+	const char		*n_netid;
+};
+
+/*
+ * Number of attempts NLM tries to obtain RPC binding
+ * of local statd.
+ */
+#define	NLM_NSM_RPCBIND_RETRIES 10
+
+/*
+ * Timeout (in seconds) NLM waits before making another
+ * attempt to obtain RPC binding of local statd.
+ */
+#define	NLM_NSM_RPCBIND_TIMEOUT 5
+
+/*
+ * Total number of sysids in NLM sysid bitmap
+ */
+#define	NLM_BMAP_NITEMS	(LM_SYSID_MAX + 1)
+
+/*
+ * Number of ulong_t words in bitmap that is used
+ * for allocation of sysid numbers.
+ */
+#define	NLM_BMAP_WORDS  (NLM_BMAP_NITEMS / BT_NBIPUL)
+
+/*
+ * Given an integer x, the macro returns
+ * -1 if x is negative,
+ *  0 if x is zero
+ *  1 if x is positive
+ */
+#define	SIGN(x) (((x) > 0) - ((x) < 0))
+
+#define	ARRSIZE(arr)	(sizeof (arr) / sizeof ((arr)[0]))
+#define	NLM_KNCS	ARRSIZE(nlm_netconfigs)
+
+krwlock_t lm_lck;
+
+/*
+ * Zero timeout for asynchronous NLM RPC operations
+ */
+static const struct timeval nlm_rpctv_zero = { 0,  0 };
+
+/*
+ * List of all Zone globals nlm_globals instences
+ * linked together.
+ */
+static struct nlm_globals_list nlm_zones_list; /* (g) */
+
+/*
+ * NLM kmem caches
+ */
+static struct kmem_cache *nlm_hosts_cache = NULL;
+static struct kmem_cache *nlm_vhold_cache = NULL;
+
+/*
+ * A bitmap for allocation of new sysids.
+ * Sysid is a unique number between LM_SYSID
+ * and LM_SYSID_MAX. Sysid represents unique remote
+ * host that does file locks on the given host.
+ */
+static ulong_t	nlm_sysid_bmap[NLM_BMAP_WORDS];	/* (g) */
+static int	nlm_sysid_nidx;			/* (g) */
+
+/*
+ * RPC service registration for all transports
+ */
+static SVC_CALLOUT nlm_svcs[] = {
+	{ NLM_PROG, 4, 4, nlm_prog_4 },	/* NLM4_VERS */
+	{ NLM_PROG, 1, 3, nlm_prog_3 }	/* NLM_VERS - NLM_VERSX */
+};
+
+static SVC_CALLOUT_TABLE nlm_sct = {
+	ARRSIZE(nlm_svcs),
+	FALSE,
+	nlm_svcs
+};
+
+/*
+ * Static table of all netid/knetconfig network
+ * lock manager can work with. nlm_netconfigs table
+ * is used when we need to get valid knetconfig by
+ * netid and vice versa.
+ *
+ * Knetconfigs are activated either by the call from
+ * user-space lockd daemon (server side) or by taking
+ * knetconfig from NFS mountinfo (client side)
+ */
+static struct nlm_knc nlm_netconfigs[] = { /* (g) */
+	/* UDP */
+	{
+		{ NC_TPI_CLTS, NC_INET, NC_UDP, NODEV },
+		"udp",
+	},
+	/* TCP */
+	{
+		{ NC_TPI_COTS_ORD, NC_INET, NC_TCP, NODEV },
+		"tcp",
+	},
+	/* UDP over IPv6 */
+	{
+		{ NC_TPI_CLTS, NC_INET6, NC_UDP, NODEV },
+		"udp6",
+	},
+	/* TCP over IPv6 */
+	{
+		{ NC_TPI_COTS_ORD, NC_INET6, NC_TCP, NODEV },
+		"tcp6",
+	},
+	/* ticlts (loopback over UDP) */
+	{
+		{ NC_TPI_CLTS, NC_LOOPBACK, NC_NOPROTO, NODEV },
+		"ticlts",
+	},
+	/* ticotsord (loopback over TCP) */
+	{
+		{ NC_TPI_COTS_ORD, NC_LOOPBACK, NC_NOPROTO, NODEV },
+		"ticotsord",
+	},
+};
+
+/*
+ * NLM misc. function
+ */
+static void nlm_copy_netbuf(struct netbuf *, struct netbuf *);
+static int nlm_netbuf_addrs_cmp(struct netbuf *, struct netbuf *);
+static void nlm_kmem_reclaim(void *);
+static void nlm_pool_shutdown(void);
+static void nlm_suspend_zone(struct nlm_globals *);
+static void nlm_resume_zone(struct nlm_globals *);
+static void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
+static void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
+
+/*
+ * NLM thread functions
+ */
+static void nlm_gc(struct nlm_globals *);
+static void nlm_reclaimer(struct nlm_host *);
+
+/*
+ * NLM NSM functions
+ */
+static int nlm_init_local_knc(struct knetconfig *);
+static int nlm_nsm_init_local(struct nlm_nsm *);
+static int nlm_nsm_init(struct nlm_nsm *, struct knetconfig *, struct netbuf *);
+static void nlm_nsm_fini(struct nlm_nsm *);
+static enum clnt_stat nlm_nsm_simu_crash(struct nlm_nsm *);
+static enum clnt_stat nlm_nsm_stat(struct nlm_nsm *, int32_t *);
+static enum clnt_stat nlm_nsm_mon(struct nlm_nsm *, char *, uint16_t);
+static enum clnt_stat nlm_nsm_unmon(struct nlm_nsm *, char *);
+
+/*
+ * NLM host functions
+ */
+static int nlm_host_ctor(void *, void *, int);
+static void nlm_host_dtor(void *, void *);
+static void nlm_host_destroy(struct nlm_host *);
+static struct nlm_host *nlm_host_create(char *, const char *,
+    struct knetconfig *, struct netbuf *);
+static struct nlm_host *nlm_host_find_locked(struct nlm_globals *,
+    const char *, struct netbuf *, avl_index_t *);
+static void nlm_host_unregister(struct nlm_globals *, struct nlm_host *);
+static void nlm_host_gc_vholds(struct nlm_host *);
+static bool_t nlm_host_has_srv_locks(struct nlm_host *);
+static bool_t nlm_host_has_cli_locks(struct nlm_host *);
+static bool_t nlm_host_has_locks(struct nlm_host *);
+
+/*
+ * NLM vhold functions
+ */
+static int nlm_vhold_ctor(void *, void *, int);
+static void nlm_vhold_dtor(void *, void *);
+static void nlm_vhold_destroy(struct nlm_host *,
+    struct nlm_vhold *);
+static bool_t nlm_vhold_busy(struct nlm_host *, struct nlm_vhold *);
+static void nlm_vhold_clean(struct nlm_vhold *, int);
+
+/*
+ * NLM client/server sleeping locks/share reservation functions
+ */
+struct nlm_slreq *nlm_slreq_find_locked(struct nlm_host *,
+    struct nlm_vhold *, struct flock64 *);
+static struct nlm_shres *nlm_shres_create_item(struct shrlock *, vnode_t *);
+static void nlm_shres_destroy_item(struct nlm_shres *);
+static bool_t nlm_shres_equal(struct shrlock *, struct shrlock *);
+
+/*
+ * NLM initialization functions.
+ */
+void
+nlm_init(void)
+{
+	nlm_hosts_cache = kmem_cache_create("nlm_host_cache",
+	    sizeof (struct nlm_host), 0, nlm_host_ctor, nlm_host_dtor,
+	    nlm_kmem_reclaim, NULL, NULL, 0);
+
+	nlm_vhold_cache = kmem_cache_create("nlm_vhold_cache",
+	    sizeof (struct nlm_vhold), 0, nlm_vhold_ctor, nlm_vhold_dtor,
+	    NULL, NULL, NULL, 0);
+
+	nlm_rpc_init();
+	TAILQ_INIT(&nlm_zones_list);
+
+	/* initialize sysids bitmap */
+	bzero(nlm_sysid_bmap, sizeof (nlm_sysid_bmap));
+	nlm_sysid_nidx = 1;
+
+	/*
+	 * Reserv the sysid #0, because it's associated
+	 * with local locks only. Don't let to allocate
+	 * it for remote locks.
+	 */
+	BT_SET(nlm_sysid_bmap, 0);
+}
+
+void
+nlm_globals_register(struct nlm_globals *g)
+{
+	rw_enter(&lm_lck, RW_WRITER);
+	TAILQ_INSERT_TAIL(&nlm_zones_list, g, nlm_link);
+	rw_exit(&lm_lck);
+}
+
+void
+nlm_globals_unregister(struct nlm_globals *g)
+{
+	rw_enter(&lm_lck, RW_WRITER);
+	TAILQ_REMOVE(&nlm_zones_list, g, nlm_link);
+	rw_exit(&lm_lck);
+}
+
+/* ARGSUSED */
+static void
+nlm_kmem_reclaim(void *cdrarg)
+{
+	struct nlm_globals *g;
+
+	rw_enter(&lm_lck, RW_READER);
+	TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
+		cv_broadcast(&g->nlm_gc_sched_cv);
+
+	rw_exit(&lm_lck);
+}
+
+/*
+ * NLM garbage collector thread (GC).
+ *
+ * NLM GC periodically checks whether there're any host objects
+ * that can be cleaned up. It also releases stale vnodes that
+ * live on the server side (under protection of vhold objects).
+ *
+ * NLM host objects are cleaned up from GC thread because
+ * operations helping us to determine whether given host has
+ * any locks can be quite expensive and it's not good to call
+ * them every time the very last reference to the host is dropped.
+ * Thus we use "lazy" approach for hosts cleanup.
+ *
+ * The work of GC is to release stale vnodes on the server side
+ * and destroy hosts that haven't any locks and any activity for
+ * some time (i.e. idle hosts).
+ */
+static void
+nlm_gc(struct nlm_globals *g)
+{
+	struct nlm_host *hostp;
+	clock_t now, idle_period;
+
+	idle_period = SEC_TO_TICK(g->cn_idle_tmo);
+	mutex_enter(&g->lock);
+	for (;;) {
+		/*
+		 * GC thread can be explicitly scheduled from
+		 * memory reclamation function.
+		 */
+		(void) cv_timedwait(&g->nlm_gc_sched_cv, &g->lock,
+		    ddi_get_lbolt() + idle_period);
+
+		/*
+		 * NLM is shutting down, time to die.
+		 */
+		if (g->run_status == NLM_ST_STOPPING)
+			break;
+
+		now = ddi_get_lbolt();
+		DTRACE_PROBE2(gc__start, struct nlm_globals *, g,
+		    clock_t, now);
+
+		/*
+		 * Handle all hosts that are unused at the moment
+		 * until we meet one with idle timeout in future.
+		 */
+		while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
+			bool_t has_locks = FALSE;
+
+			if (hostp->nh_idle_timeout > now)
+				break;
+
+			/*
+			 * Drop global lock while doing expensive work
+			 * on this host. We'll re-check any conditions
+			 * that might change after retaking the global
+			 * lock.
+			 */
+			mutex_exit(&g->lock);
+			mutex_enter(&hostp->nh_lock);
+
+			/*
+			 * nlm_globals lock was dropped earlier because
+			 * garbage collecting of vholds and checking whether
+			 * host has any locks/shares are expensive operations.
+			 */
+			nlm_host_gc_vholds(hostp);
+			has_locks = nlm_host_has_locks(hostp);
+
+			mutex_exit(&hostp->nh_lock);
+			mutex_enter(&g->lock);
+
+			/*
+			 * While we were doing expensive operations outside of
+			 * nlm_globals critical section, somebody could
+			 * take the host, add lock/share to one of its vnodes
+			 * and release the host back. If so, host's idle timeout
+			 * is renewed and our information about locks on the
+			 * given host is outdated.
+			 */
+			if (hostp->nh_idle_timeout > now)
+				continue;
+
+			/*
+			 * If either host has locks or somebody has began to
+			 * use it while we were outside the nlm_globals critical
+			 * section. In both cases we have to renew host's
+			 * timeout and put it to the end of LRU list.
+			 */
+			if (has_locks || hostp->nh_refs > 0) {
+				TAILQ_REMOVE(&g->nlm_idle_hosts,
+				    hostp, nh_link);
+				hostp->nh_idle_timeout = now + idle_period;
+				TAILQ_INSERT_TAIL(&g->nlm_idle_hosts,
+				    hostp, nh_link);
+				continue;
+			}
+
+			/*
+			 * We're here if all the following conditions hold:
+			 * 1) Host hasn't any locks or share reservations
+			 * 2) Host is unused
+			 * 3) Host wasn't touched by anyone at least for
+			 *    g->cn_idle_tmo seconds.
+			 *
+			 * So, now we can destroy it.
+			 */
+			nlm_host_unregister(g, hostp);
+			mutex_exit(&g->lock);
+
+			nlm_host_unmonitor(g, hostp);
+			nlm_host_destroy(hostp);
+			mutex_enter(&g->lock);
+			if (g->run_status == NLM_ST_STOPPING)
+				break;
+
+		}
+
+		DTRACE_PROBE(gc__end);
+	}
+
+	DTRACE_PROBE1(gc__exit, struct nlm_globals *, g);
+
+	/* Let others know that GC has died */
+	g->nlm_gc_thread = NULL;
+	mutex_exit(&g->lock);
+
+	cv_broadcast(&g->nlm_gc_finish_cv);
+	zthread_exit();
+}
+
+/*
+ * Thread reclaim locks/shares acquired by the client side
+ * on the given server represented by hostp.
+ */
+static void
+nlm_reclaimer(struct nlm_host *hostp)
+{
+	struct nlm_globals *g;
+
+	mutex_enter(&hostp->nh_lock);
+	hostp->nh_reclaimer = curthread;
+	mutex_exit(&hostp->nh_lock);
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	nlm_reclaim_client(g, hostp);
+
+	mutex_enter(&hostp->nh_lock);
+	hostp->nh_flags &= ~NLM_NH_RECLAIM;
+	hostp->nh_reclaimer = NULL;
+	cv_broadcast(&hostp->nh_recl_cv);
+	mutex_exit(&hostp->nh_lock);
+
+	/*
+	 * Host was explicitly referenced before
+	 * nlm_reclaim() was called, release it
+	 * here.
+	 */
+	nlm_host_release(g, hostp);
+	zthread_exit();
+}
+
+/*
+ * Copy a struct netobj.  (see xdr.h)
+ */
+void
+nlm_copy_netobj(struct netobj *dst, struct netobj *src)
+{
+	dst->n_len = src->n_len;
+	dst->n_bytes = kmem_alloc(src->n_len, KM_SLEEP);
+	bcopy(src->n_bytes, dst->n_bytes, src->n_len);
+}
+
+/*
+ * An NLM specificw replacement for clnt_call().
+ * nlm_clnt_call() is used by all RPC functions generated
+ * from nlm_prot.x specification. The function is aware
+ * about some pitfalls of NLM RPC procedures and has a logic
+ * that handles them properly.
+ */
+enum clnt_stat
+nlm_clnt_call(CLIENT *clnt, rpcproc_t procnum, xdrproc_t xdr_args,
+    caddr_t argsp, xdrproc_t xdr_result, caddr_t resultp, struct timeval wait)
+{
+	k_sigset_t oldmask;
+	enum clnt_stat stat;
+	bool_t sig_blocked = FALSE;
+
+	/*
+	 * If NLM RPC procnum is one of the NLM _RES procedures
+	 * that are used to reply to asynchronous NLM RPC
+	 * (MSG calls), explicitly set RPC timeout to zero.
+	 * Client doesn't send a reply to RES procedures, so
+	 * we don't need to wait anything.
+	 *
+	 * NOTE: we ignore NLM4_*_RES procnums because they are
+	 * equal to NLM_*_RES numbers.
+	 */
+	if (procnum >= NLM_TEST_RES && procnum <= NLM_GRANTED_RES)
+		wait = nlm_rpctv_zero;
+
+	/*
+	 * We need to block signals in case of NLM_CANCEL RPC
+	 * in order to prevent interruption of network RPC
+	 * calls.
+	 */
+	if (procnum == NLM_CANCEL) {
+		k_sigset_t newmask;
+
+		sigfillset(&newmask);
+		sigreplace(&newmask, &oldmask);
+		sig_blocked = TRUE;
+	}
+
+	stat = clnt_call(clnt, procnum, xdr_args,
+	    argsp, xdr_result, resultp, wait);
+
+	/*
+	 * Restore signal mask back if signals were blocked
+	 */
+	if (sig_blocked)
+		sigreplace(&oldmask, (k_sigset_t *)NULL);
+
+	return (stat);
+}
+
+/*
+ * Suspend NLM client/server in the given zone.
+ *
+ * During suspend operation we mark those hosts
+ * that have any locks with NLM_NH_SUSPEND flags,
+ * so that they can be checked later, when resume
+ * operation occurs.
+ */
+static void
+nlm_suspend_zone(struct nlm_globals *g)
+{
+	struct nlm_host *hostp;
+	struct nlm_host_list all_hosts;
+
+	/*
+	 * Note that while we're doing suspend, GC thread is active
+	 * and it can destroy some hosts while we're walking through
+	 * the hosts tree. To prevent that and make suspend logic
+	 * a bit more simple we put all hosts to local "all_hosts"
+	 * list and increment reference counter of each host.
+	 * This guaranties that no hosts will be released while
+	 * we're doing suspend.
+	 * NOTE: reference of each host must be dropped during
+	 * resume operation.
+	 */
+	TAILQ_INIT(&all_hosts);
+	mutex_enter(&g->lock);
+	for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
+	    hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
+		/*
+		 * If host is idle, remove it from idle list and
+		 * clear idle flag. That is done to prevent GC
+		 * from touching this host.
+		 */
+		if (hostp->nh_flags & NLM_NH_INIDLE) {
+			TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
+			hostp->nh_flags &= ~NLM_NH_INIDLE;
+		}
+
+		hostp->nh_refs++;
+		TAILQ_INSERT_TAIL(&all_hosts, hostp, nh_link);
+	}
+
+	/*
+	 * Now we can walk through all hosts on the system
+	 * with zone globals lock released. The fact the
+	 * we have taken a reference to each host guaranties
+	 * that no hosts can be destroyed during that process.
+	 */
+	mutex_exit(&g->lock);
+	while ((hostp = TAILQ_FIRST(&all_hosts)) != NULL) {
+		mutex_enter(&hostp->nh_lock);
+		if (nlm_host_has_locks(hostp))
+			hostp->nh_flags |= NLM_NH_SUSPEND;
+
+		mutex_exit(&hostp->nh_lock);
+		TAILQ_REMOVE(&all_hosts, hostp, nh_link);
+	}
+}
+
+/*
+ * Resume NLM hosts for the given zone.
+ *
+ * nlm_resume_zone() is called after hosts were suspended
+ * (see nlm_suspend_zone) and its main purpose to check
+ * whether remote locks owned by hosts are still in consistent
+ * state. If they aren't, resume function tries to reclaim
+ * reclaim locks (for client side hosts) and clean locks (for
+ * server side hosts).
+ */
+static void
+nlm_resume_zone(struct nlm_globals *g)
+{
+	struct nlm_host *hostp, *h_next;
+
+	mutex_enter(&g->lock);
+	hostp = avl_first(&g->nlm_hosts_tree);
+
+	/*
+	 * In nlm_suspend_zone() the reference counter of each
+	 * host was incremented, so we can safely iterate through
+	 * all hosts without worrying that any host we touch will
+	 * be removed at the moment.
+	 */
+	while (hostp != NULL) {
+		struct nlm_nsm nsm;
+		enum clnt_stat stat;
+		int32_t sm_state;
+		int error;
+		bool_t resume_failed = FALSE;
+
+		h_next = AVL_NEXT(&g->nlm_hosts_tree, hostp);
+		mutex_exit(&g->lock);
+
+		DTRACE_PROBE1(resume__host, struct nlm_host *, hostp);
+
+		/*
+		 * Suspend operation marked that the host doesn't
+		 * have any locks. Skip it.
+		 */
+		if (!(hostp->nh_flags & NLM_NH_SUSPEND))
+			goto cycle_end;
+
+		error = nlm_nsm_init(&nsm, &hostp->nh_knc, &hostp->nh_addr);
+		if (error != 0) {
+			NLM_ERR("Resume: Failed to contact to NSM of host %s "
+			    "[error=%d]\n", hostp->nh_name, error);
+			resume_failed = TRUE;
+			goto cycle_end;
+		}
+
+		stat = nlm_nsm_stat(&nsm, &sm_state);
+		if (stat != RPC_SUCCESS) {
+			NLM_ERR("Resume: Failed to call SM_STAT operation for "
+			    "host %s [stat=%d]\n", hostp->nh_name, stat);
+			resume_failed = TRUE;
+			nlm_nsm_fini(&nsm);
+			goto cycle_end;
+		}
+
+		if (sm_state != hostp->nh_state) {
+			/*
+			 * Current SM state of the host isn't equal
+			 * to the one host had when it was suspended.
+			 * Probably it was rebooted. Try to reclaim
+			 * locks if the host has any on its client side.
+			 * Also try to clean up its server side locks
+			 * (if the host has any).
+			 */
+			nlm_host_notify_client(hostp, sm_state);
+			nlm_host_notify_server(hostp, sm_state);
+		}
+
+		nlm_nsm_fini(&nsm);
+
+cycle_end:
+		if (resume_failed) {
+			/*
+			 * Resume failed for the given host.
+			 * Just clean up all resources it owns.
+			 */
+			nlm_host_notify_server(hostp, 0);
+			nlm_client_cancel_all(g, hostp);
+		}
+
+		hostp->nh_flags &= ~NLM_NH_SUSPEND;
+		nlm_host_release(g, hostp);
+		hostp = h_next;
+		mutex_enter(&g->lock);
+	}
+
+	mutex_exit(&g->lock);
+}
+
+/*
+ * NLM functions responsible for operations on NSM handle.
+ */
+
+/*
+ * Initialize knetconfig that is used for communication
+ * with local statd via loopback interface.
+ */
+static int
+nlm_init_local_knc(struct knetconfig *knc)
+{
+	int error;
+	vnode_t *vp;
+
+	bzero(knc, sizeof (*knc));
+	error = lookupname("/dev/tcp", UIO_SYSSPACE,
+	    FOLLOW, NULLVPP, &vp);
+	if (error != 0)
+		return (error);
+
+	knc->knc_semantics = NC_TPI_COTS;
+	knc->knc_protofmly = NC_INET;
+	knc->knc_proto = NC_TCP;
+	knc->knc_rdev = vp->v_rdev;
+	VN_RELE(vp);
+
+
+	return (0);
+}
+
+/*
+ * Initialize NSM handle that will be used to talk
+ * to local statd via loopback interface.
+ */
+static int
+nlm_nsm_init_local(struct nlm_nsm *nsm)
+{
+	int error;
+	struct knetconfig knc;
+	struct sockaddr_in sin;
+	struct netbuf nb;
+
+	error = nlm_init_local_knc(&knc);
+	if (error != 0)
+		return (error);
+
+	bzero(&sin, sizeof (sin));
+	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	sin.sin_family = AF_INET;
+
+	nb.buf = (char *)&sin;
+	nb.len = nb.maxlen = sizeof (sin);
+
+	return (nlm_nsm_init(nsm, &knc, &nb));
+}
+
+/*
+ * Initialize NSM handle used for talking to statd
+ */
+static int
+nlm_nsm_init(struct nlm_nsm *nsm, struct knetconfig *knc, struct netbuf *nb)
+{
+	enum clnt_stat stat;
+	int error, retries;
+
+	bzero(nsm, sizeof (*nsm));
+	nsm->ns_knc = *knc;
+	nlm_copy_netbuf(&nsm->ns_addr, nb);
+
+	/*
+	 * Try several times to get the port of statd service,
+	 * If rpcbind_getaddr returns  RPC_PROGNOTREGISTERED,
+	 * retry an attempt, but wait for NLM_NSM_RPCBIND_TIMEOUT
+	 * seconds berofore.
+	 */
+	for (retries = 0; retries < NLM_NSM_RPCBIND_RETRIES; retries++) {
+		stat = rpcbind_getaddr(&nsm->ns_knc, SM_PROG,
+		    SM_VERS, &nsm->ns_addr);
+		if (stat != RPC_SUCCESS) {
+			if (stat == RPC_PROGNOTREGISTERED) {
+				delay(SEC_TO_TICK(NLM_NSM_RPCBIND_TIMEOUT));
+				continue;
+			}
+		}
+
+		break;
+	}
+
+	if (stat != RPC_SUCCESS) {
+		DTRACE_PROBE2(rpcbind__error, enum clnt_stat, stat,
+		    int, retries);
+		error = ENOENT;
+		goto error;
+	}
+
+	/*
+	 * Create an RPC handle that'll be used for communication with local
+	 * statd using the status monitor protocol.
+	 */
+	error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, SM_PROG, SM_VERS,
+	    0, NLM_RPC_RETRIES, kcred, &nsm->ns_handle);
+	if (error != 0)
+		goto error;
+
+	/*
+	 * Create an RPC handle that'll be used for communication with the
+	 * local statd using the address registration protocol.
+	 */
+	error = clnt_tli_kcreate(&nsm->ns_knc, &nsm->ns_addr, NSM_ADDR_PROGRAM,
+	    NSM_ADDR_V1, 0, NLM_RPC_RETRIES, kcred, &nsm->ns_addr_handle);
+	if (error != 0)
+		goto error;
+
+	sema_init(&nsm->ns_sem, 1, NULL, SEMA_DEFAULT, NULL);
+	return (0);
+
+error:
+	kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
+	if (nsm->ns_handle)
+		CLNT_DESTROY(nsm->ns_handle);
+
+	return (error);
+}
+
+static void
+nlm_nsm_fini(struct nlm_nsm *nsm)
+{
+	kmem_free(nsm->ns_addr.buf, nsm->ns_addr.maxlen);
+	CLNT_DESTROY(nsm->ns_addr_handle);
+	nsm->ns_addr_handle = NULL;
+	CLNT_DESTROY(nsm->ns_handle);
+	nsm->ns_handle = NULL;
+	sema_destroy(&nsm->ns_sem);
+}
+
+static enum clnt_stat
+nlm_nsm_simu_crash(struct nlm_nsm *nsm)
+{
+	enum clnt_stat stat;
+
+	sema_p(&nsm->ns_sem);
+	nlm_nsm_clnt_init(nsm->ns_handle, nsm);
+	stat = sm_simu_crash_1(NULL, NULL, nsm->ns_handle);
+	sema_v(&nsm->ns_sem);
+
+	return (stat);
+}
+
+static enum clnt_stat
+nlm_nsm_stat(struct nlm_nsm *nsm, int32_t *out_stat)
+{
+	struct sm_name args;
+	struct sm_stat_res res;
+	enum clnt_stat stat;
+
+	args.mon_name = uts_nodename();
+	bzero(&res, sizeof (res));
+
+	sema_p(&nsm->ns_sem);
+	nlm_nsm_clnt_init(nsm->ns_handle, nsm);
+	stat = sm_stat_1(&args, &res, nsm->ns_handle);
+	sema_v(&nsm->ns_sem);
+
+	if (stat == RPC_SUCCESS)
+		*out_stat = res.state;
+
+	return (stat);
+}
+
+static enum clnt_stat
+nlm_nsm_mon(struct nlm_nsm *nsm, char *hostname, uint16_t priv)
+{
+	struct mon args;
+	struct sm_stat_res res;
+	enum clnt_stat stat;
+
+	bzero(&args, sizeof (args));
+	bzero(&res, sizeof (res));
+
+	args.mon_id.mon_name = hostname;
+	args.mon_id.my_id.my_name = uts_nodename();
+	args.mon_id.my_id.my_prog = NLM_PROG;
+	args.mon_id.my_id.my_vers = NLM_SM;
+	args.mon_id.my_id.my_proc = NLM_SM_NOTIFY1;
+	bcopy(&priv, args.priv, sizeof (priv));
+
+	sema_p(&nsm->ns_sem);
+	nlm_nsm_clnt_init(nsm->ns_handle, nsm);
+	stat = sm_mon_1(&args, &res, nsm->ns_handle);
+	sema_v(&nsm->ns_sem);
+
+	return (stat);
+}
+
+static enum clnt_stat
+nlm_nsm_unmon(struct nlm_nsm *nsm, char *hostname)
+{
+	struct mon_id args;
+	struct sm_stat res;
+	enum clnt_stat stat;
+
+	bzero(&args, sizeof (args));
+	bzero(&res, sizeof (res));
+
+	args.mon_name = hostname;
+	args.my_id.my_name = uts_nodename();
+	args.my_id.my_prog = NLM_PROG;
+	args.my_id.my_vers = NLM_SM;
+	args.my_id.my_proc = NLM_SM_NOTIFY1;
+
+	sema_p(&nsm->ns_sem);
+	nlm_nsm_clnt_init(nsm->ns_handle, nsm);
+	stat = sm_unmon_1(&args, &res, nsm->ns_handle);
+	sema_v(&nsm->ns_sem);
+
+	return (stat);
+}
+
+static enum clnt_stat
+nlm_nsmaddr_reg(struct nlm_nsm *nsm, char *name, int family, netobj *address)
+{
+	struct reg1args args = { 0 };
+	struct reg1res res = { 0 };
+	enum clnt_stat stat;
+
+	args.family = family;
+	args.name = name;
+	args.address = *address;
+
+	sema_p(&nsm->ns_sem);
+	nlm_nsm_clnt_init(nsm->ns_addr_handle, nsm);
+	stat = nsmaddrproc1_reg_1(&args, &res, nsm->ns_addr_handle);
+	sema_v(&nsm->ns_sem);
+
+	return (stat);
+}
+
+/*
+ * Get NLM vhold object corresponding to vnode "vp".
+ * If no such object was found, create a new one.
+ *
+ * The purpose of this function is to associate vhold
+ * object with given vnode, so that:
+ * 1) vnode is hold (VN_HOLD) while vhold object is alive.
+ * 2) host has a track of all vnodes it touched by lock
+ *    or share operations. These vnodes are accessible
+ *    via collection of vhold objects.
+ */
+struct nlm_vhold *
+nlm_vhold_get(struct nlm_host *hostp, vnode_t *vp)
+{
+	struct nlm_vhold *nvp, *new_nvp = NULL;
+
+	mutex_enter(&hostp->nh_lock);
+	nvp = nlm_vhold_find_locked(hostp, vp);
+	if (nvp != NULL)
+		goto out;
+
+	/* nlm_vhold wasn't found, then create a new one */
+	mutex_exit(&hostp->nh_lock);
+	new_nvp = kmem_cache_alloc(nlm_vhold_cache, KM_SLEEP);
+
+	/*
+	 * Check if another thread has already
+	 * created the same nlm_vhold.
+	 */
+	mutex_enter(&hostp->nh_lock);
+	nvp = nlm_vhold_find_locked(hostp, vp);
+	if (nvp == NULL) {
+		nvp = new_nvp;
+		new_nvp = NULL;
+
+		TAILQ_INIT(&nvp->nv_slreqs);
+		nvp->nv_vp = vp;
+		nvp->nv_refcnt = 1;
+		VN_HOLD(nvp->nv_vp);
+
+		VERIFY(mod_hash_insert(hostp->nh_vholds_by_vp,
+		    (mod_hash_key_t)vp, (mod_hash_val_t)nvp) == 0);
+		TAILQ_INSERT_TAIL(&hostp->nh_vholds_list, nvp, nv_link);
+	}
+
+out:
+	mutex_exit(&hostp->nh_lock);
+	if (new_nvp != NULL)
+		kmem_cache_free(nlm_vhold_cache, new_nvp);
+
+	return (nvp);
+}
+
+/*
+ * Drop a reference to vhold object nvp.
+ */
+void
+nlm_vhold_release(struct nlm_host *hostp, struct nlm_vhold *nvp)
+{
+	if (nvp == NULL)
+		return;
+
+	mutex_enter(&hostp->nh_lock);
+	ASSERT(nvp->nv_refcnt > 0);
+	nvp->nv_refcnt--;
+	mutex_exit(&hostp->nh_lock);
+}
+
+/*
+ * Clean all locks and share reservations on the
+ * given vhold object that were acquired by the
+ * given sysid
+ */
+static void
+nlm_vhold_clean(struct nlm_vhold *nvp, int sysid)
+{
+	cleanlocks(nvp->nv_vp, IGN_PID, sysid);
+	cleanshares_by_sysid(nvp->nv_vp, sysid);
+}
+
+static void
+nlm_vhold_destroy(struct nlm_host *hostp, struct nlm_vhold *nvp)
+{
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+
+	VERIFY(mod_hash_remove(hostp->nh_vholds_by_vp,
+	    (mod_hash_key_t)nvp->nv_vp,
+	    (mod_hash_val_t)&nvp) == 0);
+
+	TAILQ_REMOVE(&hostp->nh_vholds_list, nvp, nv_link);
+	VN_RELE(nvp->nv_vp);
+	nvp->nv_vp = NULL;
+
+	kmem_cache_free(nlm_vhold_cache, nvp);
+}
+
+/*
+ * Return TRUE if the given vhold is busy.
+ * Vhold object is considered to be "busy" when
+ * all the following conditions hold:
+ * 1) No one uses it at the moment;
+ * 2) It hasn't any locks;
+ * 3) It hasn't any share reservations;
+ */
+static bool_t
+nlm_vhold_busy(struct nlm_host *hostp, struct nlm_vhold *nvp)
+{
+	vnode_t *vp;
+	int sysid;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+
+	if (nvp->nv_refcnt > 0)
+		return (TRUE);
+
+	vp = nvp->nv_vp;
+	sysid = hostp->nh_sysid;
+	if (flk_has_remote_locks_for_sysid(vp, sysid) ||
+	    shr_has_remote_shares(vp, sysid))
+		return (TRUE);
+
+	return (FALSE);
+}
+
+/* ARGSUSED */
+static int
+nlm_vhold_ctor(void *datap, void *cdrarg, int kmflags)
+{
+	struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
+
+	bzero(nvp, sizeof (*nvp));
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+nlm_vhold_dtor(void *datap, void *cdrarg)
+{
+	struct nlm_vhold *nvp = (struct nlm_vhold *)datap;
+
+	ASSERT(nvp->nv_refcnt == 0);
+	ASSERT(TAILQ_EMPTY(&nvp->nv_slreqs));
+	ASSERT(nvp->nv_vp == NULL);
+}
+
+struct nlm_vhold *
+nlm_vhold_find_locked(struct nlm_host *hostp, const vnode_t *vp)
+{
+	struct nlm_vhold *nvp = NULL;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+	(void) mod_hash_find(hostp->nh_vholds_by_vp,
+	    (mod_hash_key_t)vp,
+	    (mod_hash_val_t)&nvp);
+
+	if (nvp != NULL)
+		nvp->nv_refcnt++;
+
+	return (nvp);
+}
+
+/*
+ * NLM host functions
+ */
+static void
+nlm_copy_netbuf(struct netbuf *dst, struct netbuf *src)
+{
+	ASSERT(src->len <= src->maxlen);
+
+	dst->maxlen = src->maxlen;
+	dst->len = src->len;
+	dst->buf = kmem_zalloc(src->maxlen, KM_SLEEP);
+	bcopy(src->buf, dst->buf, src->len);
+}
+
+/* ARGSUSED */
+static int
+nlm_host_ctor(void *datap, void *cdrarg, int kmflags)
+{
+	struct nlm_host *hostp = (struct nlm_host *)datap;
+
+	bzero(hostp, sizeof (*hostp));
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+nlm_host_dtor(void *datap, void *cdrarg)
+{
+	struct nlm_host *hostp = (struct nlm_host *)datap;
+	ASSERT(hostp->nh_refs == 0);
+}
+
+static void
+nlm_host_unregister(struct nlm_globals *g, struct nlm_host *hostp)
+{
+	ASSERT(hostp->nh_refs == 0);
+
+	avl_remove(&g->nlm_hosts_tree, hostp);
+	VERIFY(mod_hash_remove(g->nlm_hosts_hash,
+	    (mod_hash_key_t)(uintptr_t)hostp->nh_sysid,
+	    (mod_hash_val_t)&hostp) == 0);
+	TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
+	hostp->nh_flags &= ~NLM_NH_INIDLE;
+}
+
+/*
+ * Free resources used by a host. This is called after the reference
+ * count has reached zero so it doesn't need to worry about locks.
+ */
+static void
+nlm_host_destroy(struct nlm_host *hostp)
+{
+	ASSERT(hostp->nh_name != NULL);
+	ASSERT(hostp->nh_netid != NULL);
+	ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
+
+	strfree(hostp->nh_name);
+	strfree(hostp->nh_netid);
+	kmem_free(hostp->nh_addr.buf, hostp->nh_addr.maxlen);
+
+	if (hostp->nh_sysid != LM_NOSYSID)
+		nlm_sysid_free(hostp->nh_sysid);
+
+	nlm_rpc_cache_destroy(hostp);
+
+	ASSERT(TAILQ_EMPTY(&hostp->nh_vholds_list));
+	mod_hash_destroy_ptrhash(hostp->nh_vholds_by_vp);
+
+	mutex_destroy(&hostp->nh_lock);
+	cv_destroy(&hostp->nh_rpcb_cv);
+	cv_destroy(&hostp->nh_recl_cv);
+
+	kmem_cache_free(nlm_hosts_cache, hostp);
+}
+
+/*
+ * Cleanup SERVER-side state after a client restarts,
+ * or becomes unresponsive, or whatever.
+ *
+ * We unlock any active locks owned by the host.
+ * When rpc.lockd is shutting down,
+ * this function is called with newstate set to zero
+ * which allows us to cancel any pending async locks
+ * and clear the locking state.
+ *
+ * When "state" is 0, we don't update host's state,
+ * but cleanup all remote locks on the host.
+ * It's useful to call this function for resources
+ * cleanup.
+ */
+void
+nlm_host_notify_server(struct nlm_host *hostp, int32_t state)
+{
+	struct nlm_vhold *nvp;
+	struct nlm_slreq *slr;
+	struct nlm_slreq_list slreqs2free;
+
+	TAILQ_INIT(&slreqs2free);
+	mutex_enter(&hostp->nh_lock);
+	if (state != 0)
+		hostp->nh_state = state;
+
+	TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
+
+		/* cleanup sleeping requests at first */
+		while ((slr = TAILQ_FIRST(&nvp->nv_slreqs)) != NULL) {
+			TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
+
+			/*
+			 * Instead of freeing cancelled sleeping request
+			 * here, we add it to the linked list created
+			 * on the stack in order to do all frees outside
+			 * the critical section.
+			 */
+			TAILQ_INSERT_TAIL(&slreqs2free, slr, nsr_link);
+		}
+
+		nvp->nv_refcnt++;
+		mutex_exit(&hostp->nh_lock);
+
+		nlm_vhold_clean(nvp, hostp->nh_sysid);
+
+		mutex_enter(&hostp->nh_lock);
+		nvp->nv_refcnt--;
+	}
+
+	mutex_exit(&hostp->nh_lock);
+	while ((slr = TAILQ_FIRST(&slreqs2free)) != NULL) {
+		TAILQ_REMOVE(&slreqs2free, slr, nsr_link);
+		kmem_free(slr, sizeof (*slr));
+	}
+}
+
+/*
+ * Cleanup CLIENT-side state after a server restarts,
+ * or becomes unresponsive, or whatever.
+ *
+ * This is called by the local NFS statd when we receive a
+ * host state change notification.  (also nlm_svc_stopping)
+ *
+ * Deal with a server restart.  If we are stopping the
+ * NLM service, we'll have newstate == 0, and will just
+ * cancel all our client-side lock requests.  Otherwise,
+ * start the "recovery" process to reclaim any locks
+ * we hold on this server.
+ */
+void
+nlm_host_notify_client(struct nlm_host *hostp, int32_t state)
+{
+	mutex_enter(&hostp->nh_lock);
+	hostp->nh_state = state;
+	if (hostp->nh_flags & NLM_NH_RECLAIM) {
+		/*
+		 * Either host's state is up to date or
+		 * host is already in recovery.
+		 */
+		mutex_exit(&hostp->nh_lock);
+		return;
+	}
+
+	hostp->nh_flags |= NLM_NH_RECLAIM;
+
+	/*
+	 * Host will be released by the recovery thread,
+	 * thus we need to increment refcount.
+	 */
+	hostp->nh_refs++;
+	mutex_exit(&hostp->nh_lock);
+
+	(void) zthread_create(NULL, 0, nlm_reclaimer,
+	    hostp, 0, minclsyspri);
+}
+
+/*
+ * The function is called when NLM client detects that
+ * server has entered in grace period and client needs
+ * to wait until reclamation process (if any) does
+ * its job.
+ */
+int
+nlm_host_wait_grace(struct nlm_host *hostp)
+{
+	struct nlm_globals *g;
+	int error = 0;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	mutex_enter(&hostp->nh_lock);
+
+	do {
+		int rc;
+
+		rc = cv_timedwait_sig(&hostp->nh_recl_cv,
+		    &hostp->nh_lock, ddi_get_lbolt() +
+		    SEC_TO_TICK(g->retrans_tmo));
+
+		if (rc == 0) {
+			error = EINTR;
+			break;
+		}
+	} while (hostp->nh_flags & NLM_NH_RECLAIM);
+
+	mutex_exit(&hostp->nh_lock);
+	return (error);
+}
+
+/*
+ * Create a new NLM host.
+ *
+ * NOTE: The in-kernel RPC (kRPC) subsystem uses TLI/XTI,
+ * which needs both a knetconfig and an address when creating
+ * endpoints. Thus host object stores both knetconfig and
+ * netid.
+ */
+static struct nlm_host *
+nlm_host_create(char *name, const char *netid,
+    struct knetconfig *knc, struct netbuf *naddr)
+{
+	struct nlm_host *host;
+
+	host = kmem_cache_alloc(nlm_hosts_cache, KM_SLEEP);
+
+	mutex_init(&host->nh_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&host->nh_rpcb_cv, NULL, CV_DEFAULT, NULL);
+	cv_init(&host->nh_recl_cv, NULL, CV_DEFAULT, NULL);
+
+	host->nh_sysid = LM_NOSYSID;
+	host->nh_refs = 1;
+	host->nh_name = strdup(name);
+	host->nh_netid = strdup(netid);
+	host->nh_knc = *knc;
+	nlm_copy_netbuf(&host->nh_addr, naddr);
+
+	host->nh_state = 0;
+	host->nh_rpcb_state = NRPCB_NEED_UPDATE;
+	host->nh_flags = 0;
+
+	host->nh_vholds_by_vp = mod_hash_create_ptrhash("nlm vholds hash",
+	    32, mod_hash_null_valdtor, sizeof (vnode_t));
+
+	TAILQ_INIT(&host->nh_vholds_list);
+	TAILQ_INIT(&host->nh_rpchc);
+
+	return (host);
+}
+
+/*
+ * Cancel all client side sleeping locks owned by given host.
+ */
+void
+nlm_host_cancel_slocks(struct nlm_globals *g, struct nlm_host *hostp)
+{
+	struct nlm_slock *nslp;
+
+	mutex_enter(&g->lock);
+	TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
+		if (nslp->nsl_host == hostp) {
+			nslp->nsl_state = NLM_SL_CANCELLED;
+			cv_broadcast(&nslp->nsl_cond);
+		}
+	}
+
+	mutex_exit(&g->lock);
+}
+
+/*
+ * Garbage collect stale vhold objects.
+ *
+ * In other words check whether vnodes that are
+ * held by vhold objects still have any locks
+ * or shares or still in use. If they aren't,
+ * just destroy them.
+ */
+static void
+nlm_host_gc_vholds(struct nlm_host *hostp)
+{
+	struct nlm_vhold *nvp;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+
+	nvp = TAILQ_FIRST(&hostp->nh_vholds_list);
+	while (nvp != NULL) {
+		struct nlm_vhold *nvp_tmp;
+
+		if (nlm_vhold_busy(hostp, nvp)) {
+			nvp = TAILQ_NEXT(nvp, nv_link);
+			continue;
+		}
+
+		nvp_tmp = TAILQ_NEXT(nvp, nv_link);
+		nlm_vhold_destroy(hostp, nvp);
+		nvp = nvp_tmp;
+	}
+}
+
+/*
+ * Check whether the given host has any
+ * server side locks or share reservations.
+ */
+static bool_t
+nlm_host_has_srv_locks(struct nlm_host *hostp)
+{
+	/*
+	 * It's cheap and simple: if server has
+	 * any locks/shares there must be vhold
+	 * object storing the affected vnode.
+	 *
+	 * NOTE: We don't need to check sleeping
+	 * locks on the server side, because if
+	 * server side sleeping lock is alive,
+	 * there must be a vhold object corresponding
+	 * to target vnode.
+	 */
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+	if (!TAILQ_EMPTY(&hostp->nh_vholds_list))
+		return (TRUE);
+
+	return (FALSE);
+}
+
+/*
+ * Check whether the given host has any client side
+ * locks or share reservations.
+ */
+static bool_t
+nlm_host_has_cli_locks(struct nlm_host *hostp)
+{
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+
+	/*
+	 * XXX: It's not the way I'd like to do the check,
+	 * because flk_sysid_has_locks() can be very
+	 * expensive by design. Unfortunatelly it iterates
+	 * through all locks on the system, doesn't matter
+	 * were they made on remote system via NLM or
+	 * on local system via reclock. To understand the
+	 * problem, consider that there're dozens of thousands
+	 * of locks that are made on some ZFS dataset. And there's
+	 * another dataset shared by NFS where NLM client had locks
+	 * some time ago, but doesn't have them now.
+	 * In this case flk_sysid_has_locks() will iterate
+	 * thrught dozens of thousands locks until it returns us
+	 * FALSE.
+	 * Oh, I hope that in shiny future somebody will make
+	 * local lock manager (os/flock.c) better, so that
+	 * it'd be more friedly to remote locks and
+	 * flk_sysid_has_locks() wouldn't be so expensive.
+	 */
+	if (flk_sysid_has_locks(hostp->nh_sysid |
+	    LM_SYSID_CLIENT, FLK_QUERY_ACTIVE))
+		return (TRUE);
+
+	/*
+	 * Check whether host has any share reservations
+	 * registered on the client side.
+	 */
+	if (hostp->nh_shrlist != NULL)
+		return (TRUE);
+
+	return (FALSE);
+}
+
+/*
+ * Determine whether the given host owns any
+ * locks or share reservations.
+ */
+static bool_t
+nlm_host_has_locks(struct nlm_host *hostp)
+{
+	if (nlm_host_has_srv_locks(hostp))
+		return (TRUE);
+
+	return (nlm_host_has_cli_locks(hostp));
+}
+
+/*
+ * This function compares only addresses of two netbufs
+ * that belong to NC_TCP[6] or NC_UDP[6] protofamily.
+ * Port part of netbuf is ignored.
+ *
+ * Return values:
+ *  -1: nb1's address is "smaller" than nb2's
+ *   0: addresses are equal
+ *   1: nb1's address is "greater" than nb2's
+ */
+static int
+nlm_netbuf_addrs_cmp(struct netbuf *nb1, struct netbuf *nb2)
+{
+	union nlm_addr {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} *na1, *na2;
+	int res;
+
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	na1 = (union nlm_addr *)nb1->buf;
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	na2 = (union nlm_addr *)nb2->buf;
+
+	if (na1->sa.sa_family < na2->sa.sa_family)
+		return (-1);
+	if (na1->sa.sa_family > na2->sa.sa_family)
+		return (1);
+
+	switch (na1->sa.sa_family) {
+	case AF_INET:
+		res = memcmp(&na1->sin.sin_addr, &na2->sin.sin_addr,
+		    sizeof (na1->sin.sin_addr));
+		break;
+	case AF_INET6:
+		res = memcmp(&na1->sin6.sin6_addr, &na2->sin6.sin6_addr,
+		    sizeof (na1->sin6.sin6_addr));
+		break;
+	default:
+		VERIFY(0);
+		return (0);
+	}
+
+	return (SIGN(res));
+}
+
+/*
+ * Compare two nlm hosts.
+ * Return values:
+ * -1: host1 is "smaller" than host2
+ *  0: host1 is equal to host2
+ *  1: host1 is "greater" than host2
+ */
+int
+nlm_host_cmp(const void *p1, const void *p2)
+{
+	struct nlm_host *h1 = (struct nlm_host *)p1;
+	struct nlm_host *h2 = (struct nlm_host *)p2;
+	int res;
+
+	res = strcmp(h1->nh_netid, h2->nh_netid);
+	if (res != 0)
+		return (SIGN(res));
+
+	res = nlm_netbuf_addrs_cmp(&h1->nh_addr, &h2->nh_addr);
+	return (res);
+}
+
+/*
+ * Find the host specified by...  (see below)
+ * If found, increment the ref count.
+ */
+static struct nlm_host *
+nlm_host_find_locked(struct nlm_globals *g, const char *netid,
+    struct netbuf *naddr, avl_index_t *wherep)
+{
+	struct nlm_host *hostp, key;
+	avl_index_t pos;
+
+	ASSERT(MUTEX_HELD(&g->lock));
+
+	key.nh_netid = (char *)netid;
+	key.nh_addr.buf = naddr->buf;
+	key.nh_addr.len = naddr->len;
+	key.nh_addr.maxlen = naddr->maxlen;
+
+	hostp = avl_find(&g->nlm_hosts_tree, &key, &pos);
+
+	if (hostp != NULL) {
+		/*
+		 * Host is inuse now. Remove it from idle
+		 * hosts list if needed.
+		 */
+		if (hostp->nh_flags & NLM_NH_INIDLE) {
+			TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
+			hostp->nh_flags &= ~NLM_NH_INIDLE;
+		}
+
+		hostp->nh_refs++;
+	}
+	if (wherep != NULL)
+		*wherep = pos;
+
+	return (hostp);
+}
+
+/*
+ * Find NLM host for the given name and address.
+ */
+struct nlm_host *
+nlm_host_find(struct nlm_globals *g, const char *netid,
+    struct netbuf *addr)
+{
+	struct nlm_host *hostp = NULL;
+
+	mutex_enter(&g->lock);
+	if (g->run_status != NLM_ST_UP)
+		goto out;
+
+	hostp = nlm_host_find_locked(g, netid, addr, NULL);
+
+out:
+	mutex_exit(&g->lock);
+	return (hostp);
+}
+
+
+/*
+ * Find or create an NLM host for the given name and address.
+ *
+ * The remote host is determined by all of: name, netidd, address.
+ * Note that the netid is whatever nlm_svc_add_ep() gave to
+ * svc_tli_kcreate() for the service binding.  If any of these
+ * are different, allocate a new host (new sysid).
+ */
+struct nlm_host *
+nlm_host_findcreate(struct nlm_globals *g, char *name,
+    const char *netid, struct netbuf *addr)
+{
+	int err;
+	struct nlm_host *host, *newhost = NULL;
+	struct knetconfig knc;
+	avl_index_t where;
+
+	mutex_enter(&g->lock);
+	if (g->run_status != NLM_ST_UP) {
+		mutex_exit(&g->lock);
+		return (NULL);
+	}
+
+	host = nlm_host_find_locked(g, netid, addr, NULL);
+	mutex_exit(&g->lock);
+	if (host != NULL)
+		return (host);
+
+	err = nlm_knc_from_netid(netid, &knc);
+	if (err != 0)
+		return (NULL);
+	/*
+	 * Do allocations (etc.) outside of mutex,
+	 * and then check again before inserting.
+	 */
+	newhost = nlm_host_create(name, netid, &knc, addr);
+	newhost->nh_sysid = nlm_sysid_alloc();
+	if (newhost->nh_sysid == LM_NOSYSID)
+		goto out;
+
+	mutex_enter(&g->lock);
+	host = nlm_host_find_locked(g, netid, addr, &where);
+	if (host == NULL) {
+		host = newhost;
+		newhost = NULL;
+
+		/*
+		 * Insert host to the hosts AVL tree that is
+		 * used to lookup by <netid, address> pair.
+		 */
+		avl_insert(&g->nlm_hosts_tree, host, where);
+
+		/*
+		 * Insert host ot the hosts hash table that is
+		 * used to lookup host by sysid.
+		 */
+		VERIFY(mod_hash_insert(g->nlm_hosts_hash,
+		    (mod_hash_key_t)(uintptr_t)host->nh_sysid,
+		    (mod_hash_val_t)host) == 0);
+	}
+
+	mutex_exit(&g->lock);
+
+out:
+	if (newhost != NULL)
+		nlm_host_destroy(newhost);
+
+	return (host);
+}
+
+/*
+ * Find the NLM host that matches the value of 'sysid'.
+ * If found, return it with a new ref,
+ * else return NULL.
+ */
+struct nlm_host *
+nlm_host_find_by_sysid(struct nlm_globals *g, sysid_t sysid)
+{
+	struct nlm_host *hostp = NULL;
+
+	mutex_enter(&g->lock);
+	if (g->run_status != NLM_ST_UP)
+		goto out;
+
+	(void) mod_hash_find(g->nlm_hosts_hash,
+	    (mod_hash_key_t)(uintptr_t)sysid,
+	    (mod_hash_val_t)&hostp);
+
+	if (hostp == NULL)
+		goto out;
+
+	/*
+	 * Host is inuse now. Remove it
+	 * from idle hosts list if needed.
+	 */
+	if (hostp->nh_flags & NLM_NH_INIDLE) {
+		TAILQ_REMOVE(&g->nlm_idle_hosts, hostp, nh_link);
+		hostp->nh_flags &= ~NLM_NH_INIDLE;
+	}
+
+	hostp->nh_refs++;
+
+out:
+	mutex_exit(&g->lock);
+	return (hostp);
+}
+
+/*
+ * Release the given host.
+ * I.e. drop a reference that was taken earlier by one of
+ * the following functions: nlm_host_findcreate(), nlm_host_find(),
+ * nlm_host_find_by_sysid().
+ *
+ * When the very last reference is dropped, host is moved to
+ * so-called "idle state". All hosts that are in idle state
+ * have an idle timeout. If timeout is expired, GC thread
+ * checks whether hosts have any locks and if they heven't
+ * any, it removes them.
+ * NOTE: only unused hosts can be in idle state.
+ */
+void
+nlm_host_release(struct nlm_globals *g, struct nlm_host *hostp)
+{
+	if (hostp == NULL)
+		return;
+
+	mutex_enter(&g->lock);
+	ASSERT(hostp->nh_refs > 0);
+
+	hostp->nh_refs--;
+	if (hostp->nh_refs != 0) {
+		mutex_exit(&g->lock);
+		return;
+	}
+
+	/*
+	 * The very last reference to the host was dropped,
+	 * thus host is unused now. Set its idle timeout
+	 * and move it to the idle hosts LRU list.
+	 */
+	hostp->nh_idle_timeout = ddi_get_lbolt() +
+	    SEC_TO_TICK(g->cn_idle_tmo);
+
+	ASSERT((hostp->nh_flags & NLM_NH_INIDLE) == 0);
+	TAILQ_INSERT_TAIL(&g->nlm_idle_hosts, hostp, nh_link);
+	hostp->nh_flags |= NLM_NH_INIDLE;
+	mutex_exit(&g->lock);
+}
+
+/*
+ * Unregister this NLM host (NFS client) with the local statd
+ * due to idleness (no locks held for a while).
+ */
+void
+nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host)
+{
+	enum clnt_stat stat;
+
+	VERIFY(host->nh_refs == 0);
+	if (!(host->nh_flags & NLM_NH_MONITORED))
+		return;
+
+	host->nh_flags &= ~NLM_NH_MONITORED;
+	stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name);
+	if (stat != RPC_SUCCESS) {
+		NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat);
+		return;
+	}
+}
+
+/*
+ * Ask the local NFS statd to begin monitoring this host.
+ * It will call us back when that host restarts, using the
+ * prog,vers,proc specified below, i.e. NLM_SM_NOTIFY1,
+ * which is handled in nlm_do_notify1().
+ */
+void
+nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state)
+{
+	int family;
+	netobj obj;
+	enum clnt_stat stat;
+
+	if (state != 0 && host->nh_state == 0) {
+		/*
+		 * This is the first time we have seen an NSM state
+		 * Value for this host. We record it here to help
+		 * detect host reboots.
+		 */
+		host->nh_state = state;
+	}
+
+	mutex_enter(&host->nh_lock);
+	if (host->nh_flags & NLM_NH_MONITORED) {
+		mutex_exit(&host->nh_lock);
+		return;
+	}
+
+	host->nh_flags |= NLM_NH_MONITORED;
+	mutex_exit(&host->nh_lock);
+
+	/*
+	 * Before we begin monitoring the host register the network address
+	 * associated with this hostname.
+	 */
+	nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj);
+	stat = nlm_nsmaddr_reg(&g->nlm_nsm, host->nh_name, family, &obj);
+	if (stat != RPC_SUCCESS) {
+		NLM_WARN("Failed to register address, stat=%d\n", stat);
+		mutex_enter(&g->lock);
+		host->nh_flags &= ~NLM_NH_MONITORED;
+		mutex_exit(&g->lock);
+
+		return;
+	}
+
+	/*
+	 * Tell statd how to call us with status updates for
+	 * this host. Updates arrive via nlm_do_notify1().
+	 *
+	 * We put our assigned system ID value in the priv field to
+	 * make it simpler to find the host if we are notified of a
+	 * host restart.
+	 */
+	stat = nlm_nsm_mon(&g->nlm_nsm, host->nh_name, host->nh_sysid);
+	if (stat != RPC_SUCCESS) {
+		NLM_WARN("Failed to contact local NSM, stat=%d\n", stat);
+		mutex_enter(&g->lock);
+		host->nh_flags &= ~NLM_NH_MONITORED;
+		mutex_exit(&g->lock);
+
+		return;
+	}
+}
+
+int
+nlm_host_get_state(struct nlm_host *hostp)
+{
+
+	return (hostp->nh_state);
+}
+
+/*
+ * NLM client/server sleeping locks
+ */
+
+/*
+ * Register client side sleeping lock.
+ *
+ * Our client code calls this to keep information
+ * about sleeping lock somewhere. When it receives
+ * grant callback from server or when it just
+ * needs to remove all sleeping locks from vnode,
+ * it uses this information for remove/apply lock
+ * properly.
+ */
+struct nlm_slock *
+nlm_slock_register(
+	struct nlm_globals *g,
+	struct nlm_host *host,
+	struct nlm4_lock *lock,
+	struct vnode *vp)
+{
+	struct nlm_slock *nslp;
+
+	nslp = kmem_zalloc(sizeof (*nslp), KM_SLEEP);
+	cv_init(&nslp->nsl_cond, NULL, CV_DEFAULT, NULL);
+	nslp->nsl_lock = *lock;
+	nlm_copy_netobj(&nslp->nsl_fh, &nslp->nsl_lock.fh);
+	nslp->nsl_state = NLM_SL_BLOCKED;
+	nslp->nsl_host = host;
+	nslp->nsl_vp = vp;
+
+	mutex_enter(&g->lock);
+	TAILQ_INSERT_TAIL(&g->nlm_slocks, nslp, nsl_link);
+	mutex_exit(&g->lock);
+
+	return (nslp);
+}
+
+/*
+ * Remove this lock from the wait list and destroy it.
+ */
+void
+nlm_slock_unregister(struct nlm_globals *g, struct nlm_slock *nslp)
+{
+	mutex_enter(&g->lock);
+	TAILQ_REMOVE(&g->nlm_slocks, nslp, nsl_link);
+	mutex_exit(&g->lock);
+
+	kmem_free(nslp->nsl_fh.n_bytes, nslp->nsl_fh.n_len);
+	cv_destroy(&nslp->nsl_cond);
+	kmem_free(nslp, sizeof (*nslp));
+}
+
+/*
+ * Wait for a granted callback or cancellation event
+ * for a sleeping lock.
+ *
+ * If a signal interrupted the wait or if the lock
+ * was cancelled, return EINTR - the caller must arrange to send
+ * a cancellation to the server.
+ *
+ * If timeout occurred, return ETIMEDOUT - the caller must
+ * resend the lock request to the server.
+ *
+ * On success return 0.
+ */
+int
+nlm_slock_wait(struct nlm_globals *g,
+    struct nlm_slock *nslp, uint_t timeo_secs)
+{
+	clock_t timeo_ticks;
+	int cv_res, error;
+
+	/*
+	 * If the granted message arrived before we got here,
+	 * nw->nw_state will be GRANTED - in that case, don't sleep.
+	 */
+	cv_res = 1;
+	timeo_ticks = ddi_get_lbolt() + SEC_TO_TICK(timeo_secs);
+
+	mutex_enter(&g->lock);
+	if (nslp->nsl_state == NLM_SL_BLOCKED) {
+		cv_res = cv_timedwait_sig(&nslp->nsl_cond,
+		    &g->lock, timeo_ticks);
+	}
+
+	/*
+	 * No matter why we wake up, if the lock was
+	 * cancelled, let the function caller to know
+	 * about it by returning EINTR.
+	 */
+	if (nslp->nsl_state == NLM_SL_CANCELLED) {
+		error = EINTR;
+		goto out;
+	}
+
+	if (cv_res <= 0) {
+		/* We was woken up either by timeout or interrupt */
+		error = (cv_res < 0) ? ETIMEDOUT : EINTR;
+
+		/*
+		 * The granted message may arrive after the
+		 * interrupt/timeout but before we manage to lock the
+		 * mutex. Detect this by examining nslp.
+		 */
+		if (nslp->nsl_state == NLM_SL_GRANTED)
+			error = 0;
+	} else { /* awaken via cv_signal or didn't block */
+		error = 0;
+		VERIFY(nslp->nsl_state == NLM_SL_GRANTED);
+	}
+
+out:
+	mutex_exit(&g->lock);
+	return (error);
+}
+
+/*
+ * Mark client side sleeping lock as granted
+ * and wake up a process blocked on the lock.
+ * Called from server side NLM_GRANT handler.
+ *
+ * If sleeping lock is found return 0, otherwise
+ * return ENOENT.
+ */
+int
+nlm_slock_grant(struct nlm_globals *g,
+    struct nlm_host *hostp, struct nlm4_lock *alock)
+{
+	struct nlm_slock *nslp;
+	int error = ENOENT;
+
+	mutex_enter(&g->lock);
+	TAILQ_FOREACH(nslp, &g->nlm_slocks, nsl_link) {
+		if ((nslp->nsl_state != NLM_SL_BLOCKED) ||
+		    (nslp->nsl_host != hostp))
+			continue;
+
+		if (alock->svid		== nslp->nsl_lock.svid &&
+		    alock->l_offset	== nslp->nsl_lock.l_offset &&
+		    alock->l_len	== nslp->nsl_lock.l_len &&
+		    alock->fh.n_len	== nslp->nsl_lock.fh.n_len &&
+		    bcmp(alock->fh.n_bytes, nslp->nsl_lock.fh.n_bytes,
+		    nslp->nsl_lock.fh.n_len) == 0) {
+			nslp->nsl_state = NLM_SL_GRANTED;
+			cv_broadcast(&nslp->nsl_cond);
+			error = 0;
+			break;
+		}
+	}
+
+	mutex_exit(&g->lock);
+	return (error);
+}
+
+/*
+ * Register sleeping lock request corresponding to
+ * flp on the given vhold object.
+ * On success function returns 0, otherwise (if
+ * lock request with the same flp is already
+ * registered) function returns EEXIST.
+ */
+int
+nlm_slreq_register(struct nlm_host *hostp, struct nlm_vhold *nvp,
+	struct flock64 *flp)
+{
+	struct nlm_slreq *slr, *new_slr = NULL;
+	int ret = EEXIST;
+
+	mutex_enter(&hostp->nh_lock);
+	slr = nlm_slreq_find_locked(hostp, nvp, flp);
+	if (slr != NULL)
+		goto out;
+
+	mutex_exit(&hostp->nh_lock);
+	new_slr = kmem_zalloc(sizeof (*slr), KM_SLEEP);
+	bcopy(flp, &new_slr->nsr_fl, sizeof (*flp));
+
+	mutex_enter(&hostp->nh_lock);
+	slr = nlm_slreq_find_locked(hostp, nvp, flp);
+	if (slr == NULL) {
+		slr = new_slr;
+		new_slr = NULL;
+		ret = 0;
+
+		TAILQ_INSERT_TAIL(&nvp->nv_slreqs, slr, nsr_link);
+	}
+
+out:
+	mutex_exit(&hostp->nh_lock);
+	if (new_slr != NULL)
+		kmem_free(new_slr, sizeof (*new_slr));
+
+	return (ret);
+}
+
+/*
+ * Unregister sleeping lock request corresponding
+ * to flp from the given vhold object.
+ * On success function returns 0, otherwise (if
+ * lock request corresponding to flp isn't found
+ * on the given vhold) function returns ENOENT.
+ */
+int
+nlm_slreq_unregister(struct nlm_host *hostp, struct nlm_vhold *nvp,
+	struct flock64 *flp)
+{
+	struct nlm_slreq *slr;
+
+	mutex_enter(&hostp->nh_lock);
+	slr = nlm_slreq_find_locked(hostp, nvp, flp);
+	if (slr == NULL) {
+		mutex_exit(&hostp->nh_lock);
+		return (ENOENT);
+	}
+
+	TAILQ_REMOVE(&nvp->nv_slreqs, slr, nsr_link);
+	mutex_exit(&hostp->nh_lock);
+
+	kmem_free(slr, sizeof (*slr));
+	return (0);
+}
+
+/*
+ * Find sleeping lock request on the given vhold object by flp.
+ */
+struct nlm_slreq *
+nlm_slreq_find_locked(struct nlm_host *hostp, struct nlm_vhold *nvp,
+    struct flock64 *flp)
+{
+	struct nlm_slreq *slr = NULL;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+	TAILQ_FOREACH(slr, &nvp->nv_slreqs, nsr_link) {
+		if (slr->nsr_fl.l_start		== flp->l_start	&&
+		    slr->nsr_fl.l_len		== flp->l_len	&&
+		    slr->nsr_fl.l_pid		== flp->l_pid	&&
+		    slr->nsr_fl.l_type		== flp->l_type)
+			break;
+	}
+
+	return (slr);
+}
+
+/*
+ * NLM tracks active share reservations made on the client side.
+ * It needs to have a track of share reservations for two purposes
+ * 1) to determine if nlm_host is busy (if it has active locks and/or
+ *    share reservations, it is)
+ * 2) to recover active share reservations when NLM server reports
+ *    that it has rebooted.
+ *
+ * Unfortunately Illumos local share reservations manager (see os/share.c)
+ * doesn't have an ability to lookup all reservations on the system
+ * by sysid (like local lock manager) or get all reservations by sysid.
+ * It tracks reservations per vnode and is able to get/looup them
+ * on particular vnode. It's not what NLM needs. Thus it has that ugly
+ * share reservations tracking scheme.
+ */
+
+void
+nlm_shres_track(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
+{
+	struct nlm_shres *nsp, *nsp_new;
+
+	/*
+	 * NFS code must fill the s_owner, so that
+	 * s_own_len is never 0.
+	 */
+	ASSERT(shrp->s_own_len > 0);
+	nsp_new = nlm_shres_create_item(shrp, vp);
+
+	mutex_enter(&hostp->nh_lock);
+	for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next)
+		if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr))
+			break;
+
+	if (nsp != NULL) {
+		/*
+		 * Found a duplicate. Do nothing.
+		 */
+
+		goto out;
+	}
+
+	nsp = nsp_new;
+	nsp_new = NULL;
+	nsp->ns_next = hostp->nh_shrlist;
+	hostp->nh_shrlist = nsp;
+
+out:
+	mutex_exit(&hostp->nh_lock);
+	if (nsp_new != NULL)
+		nlm_shres_destroy_item(nsp_new);
+}
+
+void
+nlm_shres_untrack(struct nlm_host *hostp, vnode_t *vp, struct shrlock *shrp)
+{
+	struct nlm_shres *nsp, *nsp_prev = NULL;
+
+	mutex_enter(&hostp->nh_lock);
+	nsp = hostp->nh_shrlist;
+	while (nsp != NULL) {
+		if (nsp->ns_vp == vp && nlm_shres_equal(shrp, nsp->ns_shr)) {
+			struct nlm_shres *nsp_del;
+
+			nsp_del = nsp;
+			nsp = nsp->ns_next;
+			if (nsp_prev != NULL)
+				nsp_prev->ns_next = nsp;
+			else
+				hostp->nh_shrlist = nsp;
+
+			nlm_shres_destroy_item(nsp_del);
+			continue;
+		}
+
+		nsp_prev = nsp;
+		nsp = nsp->ns_next;
+	}
+
+	mutex_exit(&hostp->nh_lock);
+}
+
+/*
+ * Get a _copy_ of the list of all active share reservations
+ * made by the given host.
+ * NOTE: the list function returns _must_ be released using
+ *       nlm_free_shrlist().
+ */
+struct nlm_shres *
+nlm_get_active_shres(struct nlm_host *hostp)
+{
+	struct nlm_shres *nsp, *nslist = NULL;
+
+	mutex_enter(&hostp->nh_lock);
+	for (nsp = hostp->nh_shrlist; nsp != NULL; nsp = nsp->ns_next) {
+		struct nlm_shres *nsp_new;
+
+		nsp_new = nlm_shres_create_item(nsp->ns_shr, nsp->ns_vp);
+		nsp_new->ns_next = nslist;
+		nslist = nsp_new;
+	}
+
+	mutex_exit(&hostp->nh_lock);
+	return (nslist);
+}
+
+/*
+ * Free memory allocated for the active share reservations
+ * list created by nlm_get_active_shres() function.
+ */
+void
+nlm_free_shrlist(struct nlm_shres *nslist)
+{
+	struct nlm_shres *nsp;
+
+	while (nslist != NULL) {
+		nsp =  nslist;
+		nslist = nslist->ns_next;
+
+		nlm_shres_destroy_item(nsp);
+	}
+}
+
+static bool_t
+nlm_shres_equal(struct shrlock *shrp1, struct shrlock *shrp2)
+{
+	if (shrp1->s_sysid	== shrp2->s_sysid	&&
+	    shrp1->s_pid	== shrp2->s_pid		&&
+	    shrp1->s_own_len	== shrp2->s_own_len	&&
+	    bcmp(shrp1->s_owner, shrp2->s_owner,
+	    shrp1->s_own_len) == 0)
+		return (TRUE);
+
+	return (FALSE);
+}
+
+static struct nlm_shres *
+nlm_shres_create_item(struct shrlock *shrp, vnode_t *vp)
+{
+	struct nlm_shres *nsp;
+
+	nsp = kmem_alloc(sizeof (*nsp), KM_SLEEP);
+	nsp->ns_shr = kmem_alloc(sizeof (*shrp), KM_SLEEP);
+	bcopy(shrp, nsp->ns_shr, sizeof (*shrp));
+	nsp->ns_shr->s_owner = kmem_alloc(shrp->s_own_len, KM_SLEEP);
+	bcopy(shrp->s_owner, nsp->ns_shr->s_owner, shrp->s_own_len);
+	nsp->ns_vp = vp;
+
+	return (nsp);
+}
+
+static void
+nlm_shres_destroy_item(struct nlm_shres *nsp)
+{
+	kmem_free(nsp->ns_shr->s_owner,
+	    nsp->ns_shr->s_own_len);
+	kmem_free(nsp->ns_shr, sizeof (struct shrlock));
+	kmem_free(nsp, sizeof (*nsp));
+}
+
+/*
+ * Called by klmmod.c when lockd adds a network endpoint
+ * on which we should begin RPC services.
+ */
+int
+nlm_svc_add_ep(struct file *fp, const char *netid, struct knetconfig *knc)
+{
+	SVCMASTERXPRT *xprt = NULL;
+	int error;
+
+	error = svc_tli_kcreate(fp, 0, (char *)netid, NULL, &xprt,
+	    &nlm_sct, NULL, NLM_SVCPOOL_ID, FALSE);
+	if (error != 0)
+		return (error);
+
+	(void) nlm_knc_to_netid(knc);
+	return (0);
+}
+
+/*
+ * Start NLM service.
+ */
+int
+nlm_svc_starting(struct nlm_globals *g, struct file *fp,
+    const char *netid, struct knetconfig *knc)
+{
+	int error;
+	enum clnt_stat stat;
+
+	VERIFY(g->run_status == NLM_ST_STARTING);
+	VERIFY(g->nlm_gc_thread == NULL);
+
+	error = nlm_nsm_init_local(&g->nlm_nsm);
+	if (error != 0) {
+		NLM_ERR("Failed to initialize NSM handler "
+		    "(error=%d)\n", error);
+		g->run_status = NLM_ST_DOWN;
+		return (error);
+	}
+
+	error = EIO;
+
+	/*
+	 * Create an NLM garbage collector thread that will
+	 * clean up stale vholds and hosts objects.
+	 */
+	g->nlm_gc_thread = zthread_create(NULL, 0, nlm_gc,
+	    g, 0, minclsyspri);
+
+	/*
+	 * Send SIMU_CRASH to local statd to report that
+	 * NLM started, so that statd can report other hosts
+	 * about NLM state change.
+	 */
+
+	stat = nlm_nsm_simu_crash(&g->nlm_nsm);
+	if (stat != RPC_SUCCESS) {
+		NLM_ERR("Failed to connect to local statd "
+		    "(rpcerr=%d)\n", stat);
+		goto shutdown_lm;
+	}
+
+	stat = nlm_nsm_stat(&g->nlm_nsm, &g->nsm_state);
+	if (stat != RPC_SUCCESS) {
+		NLM_ERR("Failed to get the status of local statd "
+		    "(rpcerr=%d)\n", stat);
+		goto shutdown_lm;
+	}
+
+	g->grace_threshold = ddi_get_lbolt() +
+	    SEC_TO_TICK(g->grace_period);
+
+	/* Register endpoint used for communications with local NLM */
+	error = nlm_svc_add_ep(fp, netid, knc);
+	if (error != 0)
+		goto shutdown_lm;
+
+	(void) svc_pool_control(NLM_SVCPOOL_ID,
+	    SVCPSET_SHUTDOWN_PROC, (void *)nlm_pool_shutdown);
+	g->run_status = NLM_ST_UP;
+	return (0);
+
+shutdown_lm:
+	mutex_enter(&g->lock);
+	g->run_status = NLM_ST_STOPPING;
+	mutex_exit(&g->lock);
+
+	nlm_svc_stopping(g);
+	return (error);
+}
+
+/*
+ * Called when the server pool is destroyed, so that
+ * all transports are closed and no any server threads
+ * exist.
+ *
+ * Just call lm_shutdown() to shut NLM down properly.
+ */
+static void
+nlm_pool_shutdown(void)
+{
+	(void) lm_shutdown();
+}
+
+/*
+ * Stop NLM service, cleanup all resources
+ * NLM owns at the moment.
+ *
+ * NOTE: NFS code can call NLM while it's
+ * stopping or even if it's shut down. Any attempt
+ * to lock file either on client or on the server
+ * will fail if NLM isn't in NLM_ST_UP state.
+ */
+void
+nlm_svc_stopping(struct nlm_globals *g)
+{
+	mutex_enter(&g->lock);
+	ASSERT(g->run_status == NLM_ST_STOPPING);
+
+	/*
+	 * Ask NLM GC thread to exit and wait until it dies.
+	 */
+	cv_signal(&g->nlm_gc_sched_cv);
+	while (g->nlm_gc_thread != NULL)
+		cv_wait(&g->nlm_gc_finish_cv, &g->lock);
+
+	mutex_exit(&g->lock);
+
+	/*
+	 * Cleanup locks owned by NLM hosts.
+	 * NOTE: New hosts won't be created while
+	 * NLM is stopping.
+	 */
+	while (!avl_is_empty(&g->nlm_hosts_tree)) {
+		struct nlm_host *hostp;
+		int busy_hosts = 0;
+
+		/*
+		 * Iterate through all NLM hosts in the system
+		 * and drop the locks they own by force.
+		 */
+		hostp = avl_first(&g->nlm_hosts_tree);
+		while (hostp != NULL) {
+			/* Cleanup all client and server side locks */
+			nlm_client_cancel_all(g, hostp);
+			nlm_host_notify_server(hostp, 0);
+
+			mutex_enter(&hostp->nh_lock);
+			nlm_host_gc_vholds(hostp);
+			if (hostp->nh_refs > 0 || nlm_host_has_locks(hostp)) {
+				/*
+				 * Oh, it seems the host is still busy, let
+				 * it some time to release and go to the
+				 * next one.
+				 */
+
+				mutex_exit(&hostp->nh_lock);
+				hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
+				busy_hosts++;
+				continue;
+			}
+
+			mutex_exit(&hostp->nh_lock);
+			hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
+		}
+
+		/*
+		 * All hosts go to nlm_idle_hosts list after
+		 * all locks they own are cleaned up and last refereces
+		 * were dropped. Just destroy all hosts in nlm_idle_hosts
+		 * list, they can not be removed from there while we're
+		 * in stopping state.
+		 */
+		while ((hostp = TAILQ_FIRST(&g->nlm_idle_hosts)) != NULL) {
+			nlm_host_unregister(g, hostp);
+			nlm_host_destroy(hostp);
+		}
+
+		if (busy_hosts > 0) {
+			/*
+			 * There're some hosts that weren't cleaned
+			 * up. Probably they're in resource cleanup
+			 * process. Give them some time to do drop
+			 * references.
+			 */
+			delay(MSEC_TO_TICK(500));
+		}
+	}
+
+	ASSERT(TAILQ_EMPTY(&g->nlm_slocks));
+
+	nlm_nsm_fini(&g->nlm_nsm);
+	g->lockd_pid = 0;
+	g->run_status = NLM_ST_DOWN;
+}
+
+/*
+ * Returns TRUE if the given vnode has
+ * any active or sleeping locks.
+ */
+int
+nlm_vp_active(const vnode_t *vp)
+{
+	struct nlm_globals *g;
+	struct nlm_host *hostp;
+	struct nlm_vhold *nvp;
+	int active = 0;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+
+	/*
+	 * Server side NLM has locks on the given vnode
+	 * if there exist a vhold object that holds
+	 * the given vnode "vp" in one of NLM hosts.
+	 */
+	mutex_enter(&g->lock);
+	hostp = avl_first(&g->nlm_hosts_tree);
+	while (hostp != NULL) {
+		mutex_enter(&hostp->nh_lock);
+		nvp = nlm_vhold_find_locked(hostp, vp);
+		mutex_exit(&hostp->nh_lock);
+		if (nvp != NULL) {
+			active = 1;
+			break;
+		}
+
+		hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
+	}
+
+	mutex_exit(&g->lock);
+	return (active);
+}
+
+/*
+ * Called right before NFS export is going to
+ * dissapear. The function finds all vnodes
+ * belonging to the given export and cleans
+ * all remote locks and share reservations
+ * on them.
+ */
+void
+nlm_unexport(struct exportinfo *exi)
+{
+	struct nlm_globals *g;
+	struct nlm_host *hostp;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+
+	mutex_enter(&g->lock);
+	hostp = avl_first(&g->nlm_hosts_tree);
+	while (hostp != NULL) {
+		struct nlm_vhold *nvp;
+
+		mutex_enter(&hostp->nh_lock);
+		TAILQ_FOREACH(nvp, &hostp->nh_vholds_list, nv_link) {
+			vnode_t *vp;
+
+			nvp->nv_refcnt++;
+			mutex_exit(&hostp->nh_lock);
+
+			vp = nvp->nv_vp;
+
+			if (!EQFSID(&exi->exi_fsid, &vp->v_vfsp->vfs_fsid))
+				goto next_iter;
+
+			/*
+			 * Ok, it we found out that vnode vp is under
+			 * control by the exportinfo exi, now we need
+			 * to drop all locks from this vnode, let's
+			 * do it.
+			 */
+			nlm_vhold_clean(nvp, hostp->nh_sysid);
+
+		next_iter:
+			mutex_enter(&hostp->nh_lock);
+			nvp->nv_refcnt--;
+		}
+
+		mutex_exit(&hostp->nh_lock);
+		hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp);
+	}
+
+	mutex_exit(&g->lock);
+}
+
+/*
+ * Allocate new unique sysid.
+ * In case of failure (no available sysids)
+ * return LM_NOSYSID.
+ */
+sysid_t
+nlm_sysid_alloc(void)
+{
+	sysid_t ret_sysid = LM_NOSYSID;
+
+	rw_enter(&lm_lck, RW_WRITER);
+	if (nlm_sysid_nidx > LM_SYSID_MAX)
+		nlm_sysid_nidx = LM_SYSID;
+
+	if (!BT_TEST(nlm_sysid_bmap, nlm_sysid_nidx)) {
+		BT_SET(nlm_sysid_bmap, nlm_sysid_nidx);
+		ret_sysid = nlm_sysid_nidx++;
+	} else {
+		index_t id;
+
+		id = bt_availbit(nlm_sysid_bmap, NLM_BMAP_NITEMS);
+		if (id > 0) {
+			nlm_sysid_nidx = id + 1;
+			ret_sysid = id;
+			BT_SET(nlm_sysid_bmap, id);
+		}
+	}
+
+	rw_exit(&lm_lck);
+	return (ret_sysid);
+}
+
+void
+nlm_sysid_free(sysid_t sysid)
+{
+	ASSERT(sysid >= LM_SYSID && sysid <= LM_SYSID_MAX);
+
+	rw_enter(&lm_lck, RW_WRITER);
+	ASSERT(BT_TEST(nlm_sysid_bmap, sysid));
+	BT_CLEAR(nlm_sysid_bmap, sysid);
+	rw_exit(&lm_lck);
+}
+
+/*
+ * Return true if the request came from a local caller.
+ * By necessity, this "knows" the netid names invented
+ * in lm_svc() and nlm_netid_from_knetconfig().
+ */
+bool_t
+nlm_caller_is_local(SVCXPRT *transp)
+{
+	char *netid;
+	struct netbuf *rtaddr;
+
+	netid = svc_getnetid(transp);
+	rtaddr = svc_getrpccaller(transp);
+
+	if (netid == NULL)
+		return (FALSE);
+
+	if (strcmp(netid, "ticlts") == 0 ||
+	    strcmp(netid, "ticotsord") == 0)
+		return (TRUE);
+
+	if (strcmp(netid, "tcp") == 0 || strcmp(netid, "udp") == 0) {
+		struct sockaddr_in *sin = (void *)rtaddr->buf;
+		if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
+			return (TRUE);
+	}
+	if (strcmp(netid, "tcp6") == 0 || strcmp(netid, "udp6") == 0) {
+		struct sockaddr_in6 *sin6 = (void *)rtaddr->buf;
+		if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+			return (TRUE);
+	}
+
+	return (FALSE); /* unknown transport */
+}
+
+/*
+ * Get netid string correspondig to the given knetconfig.
+ * If not done already, save knc->knc_rdev in our table.
+ */
+const char *
+nlm_knc_to_netid(struct knetconfig *knc)
+{
+	int i;
+	dev_t rdev;
+	struct nlm_knc *nc;
+	const char *netid = NULL;
+
+	rw_enter(&lm_lck, RW_READER);
+	for (i = 0; i < NLM_KNCS; i++) {
+		nc = &nlm_netconfigs[i];
+
+		if (nc->n_knc.knc_semantics == knc->knc_semantics &&
+		    strcmp(nc->n_knc.knc_protofmly,
+		    knc->knc_protofmly) == 0) {
+			netid = nc->n_netid;
+			rdev = nc->n_knc.knc_rdev;
+			break;
+		}
+	}
+	rw_exit(&lm_lck);
+
+	if (netid != NULL && rdev == NODEV) {
+		rw_enter(&lm_lck, RW_WRITER);
+		if (nc->n_knc.knc_rdev == NODEV)
+			nc->n_knc.knc_rdev = knc->knc_rdev;
+		rw_exit(&lm_lck);
+	}
+
+	return (netid);
+}
+
+/*
+ * Get a knetconfig corresponding to the given netid.
+ * If there's no knetconfig for this netid, ENOENT
+ * is returned.
+ */
+int
+nlm_knc_from_netid(const char *netid, struct knetconfig *knc)
+{
+	int i, ret;
+
+	ret = ENOENT;
+	for (i = 0; i < NLM_KNCS; i++) {
+		struct nlm_knc *nknc;
+
+		nknc = &nlm_netconfigs[i];
+		if (strcmp(netid, nknc->n_netid) == 0 &&
+		    nknc->n_knc.knc_rdev != NODEV) {
+			*knc = nknc->n_knc;
+			ret = 0;
+			break;
+		}
+	}
+
+	return (ret);
+}
+
+void
+nlm_cprsuspend(void)
+{
+	struct nlm_globals *g;
+
+	rw_enter(&lm_lck, RW_READER);
+	TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
+		nlm_suspend_zone(g);
+
+	rw_exit(&lm_lck);
+}
+
+void
+nlm_cprresume(void)
+{
+	struct nlm_globals *g;
+
+	rw_enter(&lm_lck, RW_READER);
+	TAILQ_FOREACH(g, &nlm_zones_list, nlm_link)
+		nlm_resume_zone(g);
+
+	rw_exit(&lm_lck);
+}
+
+static void
+nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
+{
+	(void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
+	    NLM_RPC_RETRIES, kcred);
+}
+
+static void
+nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj)
+{
+	/* LINTED pointer alignment */
+	struct sockaddr *sa = (struct sockaddr *)addr->buf;
+
+	*family = sa->sa_family;
+
+	switch (sa->sa_family) {
+	case AF_INET: {
+		/* LINTED pointer alignment */
+		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+		obj->n_len = sizeof (sin->sin_addr);
+		obj->n_bytes = (char *)&sin->sin_addr;
+		break;
+	}
+
+	case AF_INET6: {
+		/* LINTED pointer alignment */
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+		obj->n_len = sizeof (sin6->sin6_addr);
+		obj->n_bytes = (char *)&sin6->sin6_addr;
+		break;
+	}
+
+	default:
+		VERIFY(0);
+		break;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_impl.h	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager (NLM) private declarations, etc.
+ *
+ * Source code derived from FreeBSD nlm.h
+ */
+
+#ifndef	_NLM_NLM_H_
+#define	_NLM_NLM_H_
+
+#include <sys/cmn_err.h>
+#include <sys/queue.h>
+#include <sys/modhash.h>
+#include <sys/avl.h>
+
+#define	RPC_MSGOUT(args...)	cmn_err(CE_NOTE, args)
+#define	NLM_ERR(...)		cmn_err(CE_NOTE, __VA_ARGS__)
+#define	NLM_WARN(...)		cmn_err(CE_WARN, __VA_ARGS__)
+
+#ifndef	SEEK_SET
+#define	SEEK_SET	0
+#endif
+#ifndef	SEEK_CUR
+#define	SEEK_CUR	1
+#endif
+#ifndef	SEEK_END
+#define	SEEK_END	2
+#endif
+
+/*
+ * Maximum offset supported by NLM calls using the older
+ * (32-bit) versions of the protocol.
+ */
+#define	MAX_UOFF32	0xffffffffULL
+
+struct nlm_host;
+struct vnode;
+struct exportinfo;
+struct shrlock;
+struct _kthread;
+
+/*
+ * How to read the code: probably the best point to start
+ * it the nlm_host structure that is sort of most major
+ * structure in klmmod. nlm_host is closely tied with all
+ * other NLM structures.
+ *
+ * There're three major locks we use inside NLM:
+ * 1) Global read-write lock (lm_lck) that is used to
+ *    protect operations with sysid allocation and
+ *    management of zone globals structures for each
+ *    zone.
+ * 2) Zone global lock: (nlm_globals->lock) is a mutex
+ *    used to protect all operations inside particular
+ *    zone.
+ * 3) Host's lock: (nlm_host->nh_lock) is per-host mutex
+ *    used to protect host's internal fields and all
+ *    operations with the given host.
+ *
+ * Locks order _must_ obey the following scheme:
+ *  lm_lck then nlm_globals->lock then nlm_host->nh_lock
+ *
+ * Locks:
+ * (g)          locked by lm_lck
+ * (z)          locked by nlm_globals->lock
+ * (l)		locked by host->nh_lock
+ * (c)		const until freeing
+ */
+
+/*
+ * Callback functions for nlm_do_lock() and others.
+ *
+ * Calls to nlm_do_lock are unusual, because it needs to handle
+ * the reply itself, instead of letting it happen the normal way.
+ * It also needs to make an RPC call _back_ to the client when a
+ * blocked lock request completes.
+ *
+ * We pass three callback functions to nlm_do_lock:
+ *    nlm_reply_cb: send a normal RPC reply
+ *      nlm_res_cb: do a _res (message style) RPC (call)
+ * nlm_testargs_cb: do a "granted" RPC call (after blocking)
+ * Only one of the 1st or 2nd is used.
+ * The 3rd is used only for blocking
+ *
+ * We also use callback functions for all the _msg variants
+ * of the NLM svc calls, where the reply is a reverse call.
+ * The nlm_testres_cb is used by the _test_msg svc calls.
+ * The nlm_res_cb type is used by the other _msg calls.
+ */
+typedef bool_t (*nlm_reply_cb)(SVCXPRT *, nlm4_res *);
+typedef enum clnt_stat (*nlm_res_cb)(nlm4_res *, void *, CLIENT *);
+typedef enum clnt_stat (*nlm_testargs_cb)(nlm4_testargs *, void *, CLIENT *);
+typedef enum clnt_stat (*nlm_testres_cb)(nlm4_testres *, void *, CLIENT *);
+
+/*
+ * NLM sleeping lock request.
+ *
+ * Sleeping lock requests are server side only objects
+ * that are created when client asks server to add new
+ * sleeping lock and when this lock needs to block.
+ * Server keeps a track of these requests in order to be
+ * able to cancel them or clean them up.
+ *
+ * Sleeping lock requests are closely tiled with particular
+ * vnode or, strictly speaking, NLM vhold object that holds
+ * the vnode.
+ *
+ * struct nlm_slreq:
+ *   nsr_fl: an information about file lock
+ *   nsr_link: a list node to store lock requests
+ *             in vhold object.
+ */
+struct nlm_slreq {
+	struct flock64		nsr_fl;
+	TAILQ_ENTRY(nlm_slreq)	nsr_link;
+};
+TAILQ_HEAD(nlm_slreq_list, nlm_slreq);
+
+/*
+ * NLM vhold object is a sort of wrapper on vnodes remote
+ * clients have locked (or added share reservation)
+ * on NLM server. Vhold keeps vnode held (by VN_HOLD())
+ * while vnode has any locks or shares made by parent host.
+ * Vholds are used for two purposes:
+ * 1) Hold vnode (with VN_HOLD) while it has any locks;
+ * 2) Keep a track of all vnodes remote host touched
+ *    with lock/share operations on NLM server, so that NLM
+ *    can know what vnodes are potentially locked;
+ *
+ * Vholds are used on server side only. For server side it's really
+ * important to keep vnodes held while they potentially have
+ * any locks/shares. In contrast, it's not important for clinet
+ * side at all. When particular vnode comes to the NLM client side
+ * code, it's already held (VN_HOLD) by the process calling
+ * lock/share function (it's referenced because client calls open()
+ * before making locks or shares).
+ *
+ * Each NLM host object has a collection of vholds associated
+ * with vnodes host touched earlier by adding locks or shares.
+ * Having this collection allows us to decide if host is still
+ * in use. When it has any vhold objects it's considered to be
+ * in use. Otherwise we're free to destroy it.
+ *
+ * Vholds are destroyed by the NLM garbage collecter thread that
+ * periodically checks whether they have any locks or shares.
+ * Checking occures when parent host is untouched by client
+ * or server for some period of time.
+ *
+ * struct nlm_vhold:
+ *   nv_vp: a pointer to vnode that is hold by given nlm_vhold
+ *   nv_refcnt: reference counter (non zero when vhold is inuse)
+ *   nv_slreqs: sleeping lock requests that were made on the nv_vp
+ *   nv_link: list node to store vholds in host's nh_vnodes_list
+ */
+struct nlm_vhold {
+	vnode_t			*nv_vp;    /* (c) */
+	int			nv_refcnt; /* (l) */
+	struct nlm_slreq_list	nv_slreqs; /* (l) */
+	TAILQ_ENTRY(nlm_vhold)	nv_link;   /* (l) */
+};
+TAILQ_HEAD(nlm_vhold_list, nlm_vhold);
+
+/*
+ * Client side sleeping lock state.
+ * - NLM_SL_BLOCKED: some thread is blocked on this lock
+ * - NLM_SL_GRANTED: server granted us the lock
+ * - NLM_SL_CANCELLED: the lock is cancelled (i.e. invalid/inactive)
+ */
+typedef enum nlm_slock_state {
+	NLM_SL_UNKNOWN = 0,
+	NLM_SL_BLOCKED,
+	NLM_SL_GRANTED,
+	NLM_SL_CANCELLED
+} nlm_slock_state_t;
+
+/*
+ * A client side sleeping lock request (set by F_SETLKW)
+ * stored in nlm_slocks collection of nlm_globals.
+ *
+ *  struct nlm_slock
+ *   nsl_state: Sleeping lock state.
+ *             (see nlm_slock_state for more information)
+ *   nsl_cond: Condvar that is used when sleeping lock
+ *            needs to wait for a GRANT callback
+ *            or cancellation event.
+ *   nsl_lock: nlm4_lock structure that is sent to the server
+ *   nsl_fh: Filehandle that corresponds to nw_vp
+ *   nsl_host: A host owning this sleeping lock
+ *   nsl_vp: A vnode sleeping lock is waiting on.
+ *   nsl_link: A list node for nlm_globals->nlm_slocks list.
+ */
+struct nlm_slock {
+	nlm_slock_state_t	nsl_state; /* (z) */
+	kcondvar_t		nsl_cond;  /* (z) */
+	nlm4_lock		nsl_lock;  /* (c) */
+	struct netobj		nsl_fh;    /* (c) */
+	struct nlm_host		*nsl_host; /* (c) */
+	struct vnode		*nsl_vp;   /* (c) */
+	TAILQ_ENTRY(nlm_slock)	nsl_link;  /* (z) */
+};
+TAILQ_HEAD(nlm_slock_list, nlm_slock);
+
+/*
+ * Share reservation description. NLM tracks all active
+ * share reservations made by the client side, so that
+ * they can be easily recovered if remote NLM server
+ * reboots. Share reservations tracking is also useful
+ * when NLM needs to determine whether host owns any
+ * resources on the system and can't be destroyed.
+ *
+ * nlm_shres:
+ *   ns_shr: share reservation description
+ *   ns_vp: a pointer to vnode where share reservation is located
+ *   ns_next: next nlm_shres instance (or NULL if next item isn't
+ *            present).
+ */
+struct nlm_shres {
+	struct shrlock		*ns_shr;
+	vnode_t			*ns_vp;
+	struct nlm_shres	*ns_next;
+};
+
+/*
+ * NLM RPC handle object.
+ *
+ * In kRPC subsystem it's unsafe to use one RPC handle by
+ * several threads simultaneously. It was designed so that
+ * each thread has to create an RPC handle that it'll use.
+ * RPC handle creation can be quite expensive operation, especially
+ * with session oriented protocols (such as TCP) that need to
+ * establish session at first. NLM RPC handle object is a sort of
+ * wrapper on kRPC handle object that can be cached and used in
+ * future. We store all created RPC handles for given host in a
+ * host's RPC handles cache, so that to make new requests threads
+ * can simply take ready objects from the cache. That improves
+ * NLM performance.
+ *
+ * nlm_rpc_t:
+ *   nr_handle: a kRPC handle itself.
+ *   nr_vers: a version of NLM protocol kRPC handle was
+ *            created for.
+ *   nr_link: a list node to store NLM RPC handles in the host
+ *            RPC handles cache.
+ */
+typedef struct nlm_rpc {
+	CLIENT	  *nr_handle;		/* (l) */
+	rpcvers_t  nr_vers;		/* (c) */
+	TAILQ_ENTRY(nlm_rpc) nr_link;	/* (l) */
+} nlm_rpc_t;
+TAILQ_HEAD(nlm_rpch_list, nlm_rpc);
+
+/*
+ * Describes the state of NLM host's RPC binding.
+ * RPC binding can be in one of three states:
+ * 1) NRPCB_NEED_UPDATE:
+ *    Binding is either not initialized or stale.
+ * 2) NRPCB_UPDATE_INPROGRESS:
+ *    When some thread updates host's RPC binding,
+ *    it sets binding's state to NRPCB_UPDATE_INPROGRESS
+ *    which denotes that other threads must wait until
+ *    update process is finished.
+ * 3) NRPCB_UPDATED:
+ *    Denotes that host's RPC binding is both initialized
+ *    and fresh.
+ */
+enum nlm_rpcb_state {
+	NRPCB_NEED_UPDATE = 0,
+	NRPCB_UPDATE_INPROGRESS,
+	NRPCB_UPDATED
+};
+
+/*
+ * NLM host flags
+ */
+#define	NLM_NH_MONITORED 0x01
+#define	NLM_NH_RECLAIM   0x02
+#define	NLM_NH_INIDLE    0x04
+#define	NLM_NH_SUSPEND   0x08
+
+/*
+ * NLM host object is the most major structure in NLM.
+ * It identifies remote client or remote server or both.
+ * NLM host object keep a track of all vnodes client/server
+ * locked and all sleeping locks it has. All lock/unlock
+ * operations are done using host object.
+ *
+ * nlm_host:
+ *   nh_lock: a mutex protecting host object fields
+ *   nh_refs: reference counter. Identifies how many threads
+ *            uses this host object.
+ *   nh_link: a list node for keeping host in zone-global list.
+ *   nh_by_addr: an AVL tree node for keeping host in zone-global tree.
+ *              Host can be looked up in the tree by <netid, address>
+ *              pair.
+ *   nh_name: host name.
+ *   nh_netid: netid string identifying type of transport host uses.
+ *   nh_knc: host's knetconfig (used by kRPC subsystem).
+ *   nh_addr: host's address (either IPv4 or IPv6).
+ *   nh_sysid: unique sysid associated with this host.
+ *   nh_state: last seen host's state reported by NSM.
+ *   nh_flags: ORed host flags.
+ *   nh_idle_timeout: host idle timeout. When expired host is freed.
+ *   nh_recl_cv: condition variable used for reporting that reclamation
+ *               process is finished.
+ *   nh_rpcb_cv: condition variable that is used to make sure
+ *               that only one thread renews host's RPC binding.
+ *   nh_rpcb_ustat: error code returned by RPC binding update operation.
+ *   nh_rpcb_state: host's RPC binding state (see enum nlm_rpcb_state
+ *                  for more details).
+ *   nh_rpchc: host's RPC handles cache.
+ *   nh_vholds_by_vp: a hash table of all vholds host owns. (used for lookup)
+ *   nh_vholds_list: a linked list of all vholds host owns. (used for iteration)
+ *   nh_shrlist: a list of all active share resevations on the client side.
+ *   nh_reclaimer: a pointer to reclamation thread (kthread_t)
+ *                 NULL if reclamation thread doesn't exist
+ */
+struct nlm_host {
+	kmutex_t		nh_lock;		/* (c) */
+	volatile uint_t		nh_refs;		/* (z) */
+	TAILQ_ENTRY(nlm_host)	nh_link;		/* (z) */
+	avl_node_t		nh_by_addr;		/* (z) */
+	char			*nh_name;		/* (c) */
+	char			*nh_netid;		/* (c) */
+	struct knetconfig	nh_knc;			/* (c) */
+	struct netbuf		nh_addr;		/* (c) */
+	sysid_t			nh_sysid;		/* (c) */
+	int32_t			nh_state;		/* (z) */
+	clock_t			nh_idle_timeout;	/* (z) */
+	uint8_t			nh_flags;		/* (z) */
+	kcondvar_t		nh_recl_cv;		/* (z) */
+	kcondvar_t		nh_rpcb_cv;		/* (l) */
+	enum clnt_stat		nh_rpcb_ustat;		/* (l) */
+	enum nlm_rpcb_state	nh_rpcb_state;		/* (l) */
+	struct nlm_rpch_list	nh_rpchc;		/* (l) */
+	mod_hash_t		*nh_vholds_by_vp;	/* (l) */
+	struct nlm_vhold_list	nh_vholds_list;		/* (l) */
+	struct nlm_shres	*nh_shrlist;		/* (l) */
+	kthread_t		*nh_reclaimer;		/* (l) */
+};
+TAILQ_HEAD(nlm_host_list, nlm_host);
+
+/*
+ * nlm_nsm structure describes RPC client handle that can be
+ * used to communicate with local NSM via kRPC.
+ *
+ * We need to wrap handle with nlm_nsm structure because kRPC
+ * can not share one handle between several threads. It's assumed
+ * that NLM uses only one NSM handle per zone, thus all RPC operations
+ * on NSM's handle are serialized using nlm_nsm->sem semaphore.
+ *
+ * nlm_nsm also contains refcnt field used for reference counting.
+ * It's used because there exist a possibility of simultaneous
+ * execution of NLM shutdown operation and host monitor/unmonitor
+ * operations.
+ *
+ * struct nlm_nsm:
+ *  ns_sem: a semaphore for serialization network operations to statd
+ *  ns_knc: a kneconfig describing transport that is used for communication
+ *  ns_addr: an address of local statd we're talking to
+ *  ns_handle: an RPC handle used for talking to local statd using the status
+ *      monitor protocol (SM_PROG)
+ *  ns_addr_handle: an RPC handle used for talking to local statd using the
+ *      address registration protocol (NSM_ADDR_PROGRAM)
+ */
+struct nlm_nsm {
+	ksema_t			ns_sem;
+	struct knetconfig	ns_knc;		 /* (c) */
+	struct netbuf		ns_addr;	 /* (c) */
+	CLIENT			*ns_handle;	 /* (c) */
+	CLIENT			*ns_addr_handle; /* (c) */
+};
+
+/*
+ * Could use flock.h flk_nlm_status_t instead, but
+ * prefer our own enum with initial zero...
+ */
+typedef enum {
+	NLM_ST_DOWN = 0,
+	NLM_ST_STOPPING,
+	NLM_ST_UP,
+	NLM_ST_STARTING
+} nlm_run_status_t;
+
+/*
+ * nlm_globals structure allows NLM be zone aware. The structure
+ * collects all "global variables" NLM has for each zone.
+ *
+ * struct nlm_globals:
+ * lock: mutex protecting all operations inside given zone
+ * grace_threshold: grace period expiration time (in ticks)
+ * lockd_pid: PID of lockd user space daemon
+ * run_status: run status of klmmod inside given zone
+ * nsm_state: state obtained from local statd during klmmod startup
+ * nlm_gc_thread: garbage collector thread
+ * nlm_gc_sched_cv: condvar that can be signalled to wakeup GC
+ * nlm_gc_finish_cv: condvar that is signalled just before GC thread exits
+ * nlm_nsm: an object describing RPC handle used for talking to local statd
+ * nlm_hosts_tree: an AVL tree of all hosts in the given zone
+ *                 (used for hosts lookup by <netid, address> pair)
+ * nlm_hosts_hash: a hash table of all hosts in the given zone
+ *                 (used for hosts lookup by sysid)
+ * nlm_idle_hosts: a list of all hosts that are idle state (i.e. unused)
+ * nlm_slocks: a list of all client-side sleeping locks in the zone
+ * cn_idle_tmo: a value of idle timeout (in seconds) obtained from lockd
+ * grace_period: a value of grace period (in seconds) obtained from lockd
+ * retrans_tmo: a value of retransmission timeout (in seconds) obtained
+ *              from lockd.
+ * clean_lock: mutex used to serialize clear_locks calls.
+ * nlm_link: a list node used for keeping all nlm_globals objects
+ *           in one global linked list.
+ */
+struct nlm_globals {
+	kmutex_t			lock;
+	clock_t				grace_threshold;	/* (z) */
+	pid_t				lockd_pid;		/* (z) */
+	nlm_run_status_t		run_status;		/* (z) */
+	int32_t				nsm_state;		/* (z) */
+	kthread_t			*nlm_gc_thread;		/* (z) */
+	kcondvar_t			nlm_gc_sched_cv;	/* (z) */
+	kcondvar_t			nlm_gc_finish_cv;	/* (z) */
+	struct nlm_nsm			nlm_nsm;		/* (z) */
+	avl_tree_t			nlm_hosts_tree;		/* (z) */
+	mod_hash_t			*nlm_hosts_hash;	/* (z) */
+	struct nlm_host_list		nlm_idle_hosts;		/* (z) */
+	struct nlm_slock_list		nlm_slocks;		/* (z) */
+	int				cn_idle_tmo;		/* (z) */
+	int				grace_period;		/* (z) */
+	int				retrans_tmo;		/* (z) */
+	kmutex_t			clean_lock;		/* (c) */
+	TAILQ_ENTRY(nlm_globals)	nlm_link;		/* (g) */
+};
+TAILQ_HEAD(nlm_globals_list, nlm_globals);
+
+
+/*
+ * This is what we pass as the "owner handle" for NLM_LOCK.
+ * This lets us find the blocked lock in NLM_GRANTED.
+ * It also exposes on the wire what we're using as the
+ * sysid for any server, which can be very helpful for
+ * problem diagnosis.  (Observability is good).
+ */
+struct nlm_owner_handle {
+	sysid_t oh_sysid;		/* of remote host */
+};
+
+/*
+ * Number retries NLM RPC call is repeatead in case of failure.
+ * (used in case of conectionless transport).
+ */
+#define	NLM_RPC_RETRIES 5
+
+/*
+ * Klmmod global variables
+ */
+extern krwlock_t lm_lck;
+extern zone_key_t nlm_zone_key;
+
+/*
+ * NLM interface functions (called directly by
+ * either klmmod or klmpos)
+ */
+extern int nlm_frlock(struct vnode *, int, struct flock64 *, int, u_offset_t,
+    struct cred *, struct netobj *, struct flk_callback *, int);
+extern int nlm_shrlock(struct vnode *, int, struct shrlock *, int,
+    struct netobj *, int);
+extern int nlm_safemap(const vnode_t *);
+extern int nlm_safelock(vnode_t *, const struct flock64 *, cred_t *);
+extern int nlm_has_sleep(const vnode_t *);
+extern void nlm_register_lock_locally(struct vnode *, struct nlm_host *,
+    struct flock64 *, int, u_offset_t);
+int nlm_vp_active(const vnode_t *vp);
+void nlm_sysid_free(sysid_t);
+int nlm_vp_active(const vnode_t *);
+void nlm_unexport(struct exportinfo *);
+
+/*
+ * NLM startup/shutdown
+ */
+int nlm_svc_starting(struct nlm_globals *, struct file *,
+    const char *, struct knetconfig *);
+void nlm_svc_stopping(struct nlm_globals *);
+int nlm_svc_add_ep(struct file *, const char *, struct knetconfig *);
+
+/*
+ * NLM suspend/resume
+ */
+void nlm_cprsuspend(void);
+void nlm_cprresume(void);
+
+/*
+ * NLM internal functions for initialization.
+ */
+void nlm_init(void);
+void nlm_rpc_init(void);
+void nlm_rpc_cache_destroy(struct nlm_host *);
+void nlm_globals_register(struct nlm_globals *);
+void nlm_globals_unregister(struct nlm_globals *);
+sysid_t nlm_sysid_alloc(void);
+
+/*
+ * Client reclamation/cancelation
+ */
+void nlm_reclaim_client(struct nlm_globals *, struct nlm_host *);
+void nlm_client_cancel_all(struct nlm_globals *, struct nlm_host *);
+
+/* (nlm_rpc_clnt.c) */
+enum clnt_stat nlm_null_rpc(CLIENT *, rpcvers_t);
+enum clnt_stat nlm_test_rpc(nlm4_testargs *, nlm4_testres *,
+    CLIENT *, rpcvers_t);
+enum clnt_stat nlm_lock_rpc(nlm4_lockargs *, nlm4_res *,
+    CLIENT *, rpcvers_t);
+enum clnt_stat nlm_cancel_rpc(nlm4_cancargs *, nlm4_res *,
+    CLIENT *, rpcvers_t);
+enum clnt_stat nlm_unlock_rpc(nlm4_unlockargs *, nlm4_res *,
+    CLIENT *, rpcvers_t);
+enum clnt_stat nlm_share_rpc(nlm4_shareargs *, nlm4_shareres *,
+    CLIENT *, rpcvers_t);
+enum clnt_stat nlm_unshare_rpc(nlm4_shareargs *, nlm4_shareres *,
+    CLIENT *, rpcvers_t);
+
+
+/*
+ * RPC service functions.
+ * nlm_dispatch.c
+ */
+void nlm_prog_3(struct svc_req *rqstp, SVCXPRT *transp);
+void nlm_prog_4(struct svc_req *rqstp, SVCXPRT *transp);
+
+/*
+ * Functions for working with knetconfigs (nlm_netconfig.c)
+ */
+const char *nlm_knc_to_netid(struct knetconfig *);
+int nlm_knc_from_netid(const char *, struct knetconfig *);
+
+/*
+ * NLM host functions (nlm_impl.c)
+ */
+struct nlm_host *nlm_host_findcreate(struct nlm_globals *, char *,
+    const char *, struct netbuf *);
+struct nlm_host *nlm_host_find(struct nlm_globals *,
+    const char *, struct netbuf *);
+struct nlm_host *nlm_host_find_by_sysid(struct nlm_globals *, sysid_t);
+void nlm_host_release(struct nlm_globals *, struct nlm_host *);
+
+void nlm_host_monitor(struct nlm_globals *, struct nlm_host *, int);
+void nlm_host_unmonitor(struct nlm_globals *, struct nlm_host *);
+
+void nlm_host_notify_server(struct nlm_host *, int32_t);
+void nlm_host_notify_client(struct nlm_host *, int32_t);
+
+int nlm_host_get_state(struct nlm_host *);
+
+struct nlm_vhold *nlm_vhold_get(struct nlm_host *, vnode_t *);
+void nlm_vhold_release(struct nlm_host *, struct nlm_vhold *);
+struct nlm_vhold *nlm_vhold_find_locked(struct nlm_host *, const vnode_t *);
+
+struct nlm_slock *nlm_slock_register(struct nlm_globals *,
+    struct nlm_host *, struct nlm4_lock *, struct vnode *);
+void nlm_slock_unregister(struct nlm_globals *, struct nlm_slock *);
+int nlm_slock_wait(struct nlm_globals *, struct nlm_slock *, uint_t);
+int nlm_slock_grant(struct nlm_globals *,
+    struct nlm_host *, struct nlm4_lock *);
+void nlm_host_cancel_slocks(struct nlm_globals *, struct nlm_host *);
+
+int nlm_slreq_register(struct nlm_host *,
+    struct nlm_vhold *, struct flock64 *);
+int nlm_slreq_unregister(struct nlm_host *,
+    struct nlm_vhold *, struct flock64 *);
+
+void nlm_shres_track(struct nlm_host *, vnode_t *, struct shrlock *);
+void nlm_shres_untrack(struct nlm_host *, vnode_t *, struct shrlock *);
+struct nlm_shres *nlm_get_active_shres(struct nlm_host *);
+void nlm_free_shrlist(struct nlm_shres *);
+
+int nlm_host_wait_grace(struct nlm_host *);
+int nlm_host_cmp(const void *, const void *);
+void nlm_copy_netobj(struct netobj *, struct netobj *);
+
+int nlm_host_get_rpc(struct nlm_host *, int, nlm_rpc_t **);
+void nlm_host_rele_rpc(struct nlm_host *, nlm_rpc_t *);
+
+/*
+ * NLM server functions (nlm_service.c)
+ */
+int nlm_vp_active(const vnode_t *vp);
+void nlm_do_notify1(nlm_sm_status *, void *, struct svc_req *);
+void nlm_do_notify2(nlm_sm_status *, void *, struct svc_req *);
+void nlm_do_test(nlm4_testargs *, nlm4_testres *,
+    struct svc_req *, nlm_testres_cb);
+void nlm_do_lock(nlm4_lockargs *, nlm4_res *, struct svc_req *,
+    nlm_reply_cb, nlm_res_cb, nlm_testargs_cb);
+void nlm_do_cancel(nlm4_cancargs *, nlm4_res *,
+    struct svc_req *, nlm_res_cb);
+void nlm_do_unlock(nlm4_unlockargs *, nlm4_res *,
+    struct svc_req *, nlm_res_cb);
+void nlm_do_granted(nlm4_testargs *, nlm4_res *,
+    struct svc_req *, nlm_res_cb);
+void nlm_do_share(nlm4_shareargs *, nlm4_shareres *, struct svc_req *);
+void nlm_do_unshare(nlm4_shareargs *, nlm4_shareres *, struct svc_req *);
+void nlm_do_free_all(nlm4_notify *, void *, struct svc_req *);
+
+/*
+ * NLM RPC functions
+ */
+enum clnt_stat nlm_clnt_call(CLIENT *, rpcproc_t, xdrproc_t,
+    caddr_t, xdrproc_t, caddr_t, struct timeval);
+bool_t nlm_caller_is_local(SVCXPRT *);
+
+#endif	/* _NLM_NLM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_prot_clnt.sed	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,31 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy is of the CDDL is also available via the Internet
+# at http://www.illumos.org/license/CDDL.
+#
+#
+# Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+#
+
+#
+# This sed script is run on the client code generated by rpcgen
+# from nlm_prot.x before it is compiled.
+#
+
+6{
+i\
+#include <sys/param.h>
+i\
+#include <sys/systm.h>
+i\
+#include <rpcsvc/nlm_prot.h>
+i\
+#include "nlm_impl.h"
+}
+/^.include/,/^.endif/d
+s/clnt_call/nlm_clnt_call/g
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_rpc_clnt.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * Client-side RPC wrappers (nlm_..._rpc)
+ * Called from nlm_client.c
+ *
+ * Source code derived from FreeBSD nlm_advlock.c
+ */
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/lock.h>
+#include <sys/flock.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+#include <sys/queue.h>
+
+#include <rpcsvc/nlm_prot.h>
+
+#include <nfs/nfs.h>
+#include <nfs/nfs_clnt.h>
+#include <nfs/export.h>
+#include <nfs/rnode.h>
+
+#include "nlm_impl.h"
+
+static void
+nlm_convert_to_nlm_lock(struct nlm_lock *dst, struct nlm4_lock *src)
+{
+	dst->caller_name = src->caller_name;
+	dst->fh = src->fh;
+	dst->oh = src->oh;
+	dst->svid = src->svid;
+	dst->l_offset = src->l_offset;
+	dst->l_len = src->l_len;
+}
+
+static void
+nlm_convert_to_nlm4_holder(struct nlm4_holder *dst, struct nlm_holder *src)
+{
+	dst->exclusive = src->exclusive;
+	dst->svid = src->svid;
+	dst->oh = src->oh;
+	dst->l_offset = src->l_offset;
+	dst->l_len = src->l_len;
+}
+
+static void
+nlm_convert_to_nlm4_res(struct nlm4_res *dst, struct nlm_res *src)
+{
+	dst->cookie = src->cookie;
+	dst->stat.stat = (enum nlm4_stats) src->stat.stat;
+}
+
+enum clnt_stat
+nlm_test_rpc(nlm4_testargs *args, nlm4_testres *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_test_4(args, res, client));
+	} else {
+		nlm_testargs args1;
+		nlm_testres res1;
+		enum clnt_stat stat;
+
+		args1.cookie = args->cookie;
+		args1.exclusive = args->exclusive;
+		nlm_convert_to_nlm_lock(&args1.alock, &args->alock);
+		(void) memset(&res1, 0, sizeof (res1));
+
+		stat = nlm_test_1(&args1, &res1, client);
+
+		if (stat == RPC_SUCCESS) {
+			res->cookie = res1.cookie;
+			res->stat.stat = (enum nlm4_stats) res1.stat.stat;
+			if (res1.stat.stat == nlm_denied)
+				nlm_convert_to_nlm4_holder(
+				    &res->stat.nlm4_testrply_u.holder,
+				    &res1.stat.nlm_testrply_u.holder);
+		}
+
+		return (stat);
+	}
+}
+
+enum clnt_stat
+nlm_lock_rpc(nlm4_lockargs *args, nlm4_res *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_lock_4(args, res, client));
+	} else {
+		nlm_lockargs args1;
+		nlm_res res1;
+		enum clnt_stat stat;
+
+		args1.cookie = args->cookie;
+		args1.block = args->block;
+		args1.exclusive = args->exclusive;
+		nlm_convert_to_nlm_lock(&args1.alock, &args->alock);
+		args1.reclaim = args->reclaim;
+		args1.state = args->state;
+		(void) memset(&res1, 0, sizeof (res1));
+
+		stat = nlm_lock_1(&args1, &res1, client);
+
+		if (stat == RPC_SUCCESS) {
+			nlm_convert_to_nlm4_res(res, &res1);
+		}
+
+		return (stat);
+	}
+}
+
+enum clnt_stat
+nlm_cancel_rpc(nlm4_cancargs *args, nlm4_res *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_cancel_4(args, res, client));
+	} else {
+		nlm_cancargs args1;
+		nlm_res res1;
+		enum clnt_stat stat;
+
+		args1.cookie = args->cookie;
+		args1.block = args->block;
+		args1.exclusive = args->exclusive;
+		nlm_convert_to_nlm_lock(&args1.alock, &args->alock);
+		(void) memset(&res1, 0, sizeof (res1));
+
+		stat = nlm_cancel_1(&args1, &res1, client);
+
+		if (stat == RPC_SUCCESS) {
+			nlm_convert_to_nlm4_res(res, &res1);
+		}
+
+		return (stat);
+	}
+}
+
+enum clnt_stat
+nlm_unlock_rpc(nlm4_unlockargs *args, nlm4_res *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_unlock_4(args, res, client));
+	} else {
+		nlm_unlockargs args1;
+		nlm_res res1;
+		enum clnt_stat stat;
+
+		args1.cookie = args->cookie;
+		nlm_convert_to_nlm_lock(&args1.alock, &args->alock);
+		(void) memset(&res1, 0, sizeof (res1));
+
+		stat = nlm_unlock_1(&args1, &res1, client);
+
+		if (stat == RPC_SUCCESS) {
+			nlm_convert_to_nlm4_res(res, &res1);
+		}
+
+		return (stat);
+	}
+}
+
+enum clnt_stat
+nlm_null_rpc(CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS)
+		return (nlm4_null_4(NULL, NULL, client));
+
+	return (nlm_null_1(NULL, NULL, client));
+}
+
+/*
+ * Share reservations
+ */
+
+static void
+nlm_convert_to_nlm_share(struct nlm_share *dst, struct nlm4_share *src)
+{
+
+	dst->caller_name = src->caller_name;
+	dst->fh = src->fh;
+	dst->oh = src->oh;
+	dst->mode = src->mode;
+	dst->access = src->access;
+}
+
+static void
+nlm_convert_to_nlm4_shres(struct nlm4_shareres *dst,
+	struct nlm_shareres *src)
+{
+	dst->cookie = src->cookie;
+	dst->stat = (enum nlm4_stats) src->stat;
+	dst->sequence = src->sequence;
+}
+
+
+enum clnt_stat
+nlm_share_rpc(nlm4_shareargs *args, nlm4_shareres *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_share_4(args, res, client));
+	} else {
+		nlm_shareargs args3;
+		nlm_shareres res3;
+		enum clnt_stat stat;
+
+		args3.cookie = args->cookie;
+		nlm_convert_to_nlm_share(&args3.share, &args->share);
+		args3.reclaim = args->reclaim;
+		(void) memset(&res3, 0, sizeof (res3));
+
+		stat = nlm_share_3(&args3, &res3, client);
+
+		if (stat == RPC_SUCCESS) {
+			nlm_convert_to_nlm4_shres(res, &res3);
+		}
+
+		return (stat);
+	}
+}
+
+enum clnt_stat
+nlm_unshare_rpc(nlm4_shareargs *args, nlm4_shareres *res,
+    CLIENT *client, rpcvers_t vers)
+{
+	if (vers == NLM4_VERS) {
+		return (nlm4_unshare_4(args, res, client));
+	} else {
+		nlm_shareargs args3;
+		nlm_shareres res3;
+		enum clnt_stat stat;
+
+		args3.cookie = args->cookie;
+		nlm_convert_to_nlm_share(&args3.share, &args->share);
+		args3.reclaim = args->reclaim;
+		(void) memset(&res3, 0, sizeof (res3));
+
+		stat = nlm_unshare_3(&args3, &res3, client);
+
+		if (stat == RPC_SUCCESS) {
+			nlm_convert_to_nlm4_shres(res, &res3);
+		}
+
+		return (stat);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_rpc_handle.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,362 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/unistd.h>
+#include <sys/queue.h>
+#include <sys/sdt.h>
+#include <netinet/in.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/rpcb_prot.h>
+
+#include <rpcsvc/nlm_prot.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "nlm_impl.h"
+
+/*
+ * The following errors codes from nlm_null_rpc indicate that the port we have
+ * cached for the client's NLM service is stale and that we need to establish
+ * a new RPC client.
+ */
+#define	NLM_STALE_CLNT(_status)			\
+	((_status) == RPC_PROGUNAVAIL ||	\
+	(_status) == RPC_PROGVERSMISMATCH ||	\
+	(_status) == RPC_PROCUNAVAIL ||		\
+	(_status) == RPC_CANTCONNECT ||		\
+	(_status) == RPC_XPRTFAILED)
+
+static struct kmem_cache *nlm_rpch_cache = NULL;
+
+static int nlm_rpch_ctor(void *, void *, int);
+static void nlm_rpch_dtor(void *, void *);
+static void destroy_rpch(nlm_rpc_t *);
+static nlm_rpc_t *get_nlm_rpc_fromcache(struct nlm_host *, int);
+static void update_host_rpcbinding(struct nlm_host *, int);
+static int refresh_nlm_rpc(struct nlm_host *, nlm_rpc_t *);
+static void nlm_host_rele_rpc_locked(struct nlm_host *, nlm_rpc_t *);
+
+static nlm_rpc_t *
+get_nlm_rpc_fromcache(struct nlm_host *hostp, int vers)
+{
+	nlm_rpc_t *rpcp;
+	bool_t found = FALSE;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+	if (TAILQ_EMPTY(&hostp->nh_rpchc))
+		return (NULL);
+
+	TAILQ_FOREACH(rpcp, &hostp->nh_rpchc, nr_link) {
+		if (rpcp->nr_vers == vers) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	if (!found)
+		return (NULL);
+
+	TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
+	return (rpcp);
+}
+
+/*
+ * Update host's RPC binding (host->nh_addr).
+ * The function is executed by only one thread at time.
+ */
+static void
+update_host_rpcbinding(struct nlm_host *hostp, int vers)
+{
+	enum clnt_stat stat;
+
+	ASSERT(MUTEX_HELD(&hostp->nh_lock));
+
+	/*
+	 * Mark RPC binding state as "update in progress" in order
+	 * to say other threads that they need to wait until binding
+	 * is fully updated.
+	 */
+	hostp->nh_rpcb_state = NRPCB_UPDATE_INPROGRESS;
+	hostp->nh_rpcb_ustat = RPC_SUCCESS;
+	mutex_exit(&hostp->nh_lock);
+
+	stat = rpcbind_getaddr(&hostp->nh_knc, NLM_PROG, vers, &hostp->nh_addr);
+	mutex_enter(&hostp->nh_lock);
+
+	hostp->nh_rpcb_state = ((stat == RPC_SUCCESS) ?
+	    NRPCB_UPDATED : NRPCB_NEED_UPDATE);
+
+	hostp->nh_rpcb_ustat = stat;
+	cv_broadcast(&hostp->nh_rpcb_cv);
+}
+
+/*
+ * Refresh RPC handle taken from host handles cache.
+ * This function is called when an RPC handle is either
+ * uninitialized or was initialized using a binding that's
+ * no longer current.
+ */
+static int
+refresh_nlm_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
+{
+	int ret;
+
+	if (rpcp->nr_handle == NULL) {
+		bool_t clset = TRUE;
+
+		ret = clnt_tli_kcreate(&hostp->nh_knc, &hostp->nh_addr,
+		    NLM_PROG, rpcp->nr_vers, 0, NLM_RPC_RETRIES,
+		    CRED(), &rpcp->nr_handle);
+
+		/*
+		 * Set the client's CLSET_NODELAYONERR option to true. The
+		 * RPC clnt_call interface creates an artificial delay for
+		 * certain call errors in order to prevent RPC consumers
+		 * from getting into tight retry loops. Since this function is
+		 * called by the NLM service routines we would like to avoid
+		 * this artificial delay when possible. We do not retry if the
+		 * NULL request fails so it is safe for us to turn this option
+		 * on.
+		 */
+		if (clnt_control(rpcp->nr_handle, CLSET_NODELAYONERR,
+		    (char *)&clset)) {
+			NLM_ERR("Unable to set CLSET_NODELAYONERR\n");
+		}
+	} else {
+		ret = clnt_tli_kinit(rpcp->nr_handle, &hostp->nh_knc,
+		    &hostp->nh_addr, 0, NLM_RPC_RETRIES, CRED());
+		if (ret == 0) {
+			enum clnt_stat stat;
+
+			/*
+			 * Check whether host's RPC binding is still
+			 * fresh, i.e. if remote program is still sits
+			 * on the same port we assume. Call NULL proc
+			 * to do it.
+			 *
+			 * Note: Even though we set no delay on error on the
+			 * client handle the call to nlm_null_rpc can still
+			 * delay for 10 seconds before returning an error. For
+			 * example the no delay on error option is not honored
+			 * for RPC_XPRTFAILED errors (see clnt_cots_kcallit).
+			 */
+			stat = nlm_null_rpc(rpcp->nr_handle, rpcp->nr_vers);
+			if (NLM_STALE_CLNT(stat)) {
+				ret = ESTALE;
+			}
+		}
+	}
+
+	return (ret);
+}
+
+/*
+ * Get RPC handle that can be used to talk to the NLM
+ * of given version running on given host.
+ * Saves obtained RPC handle to rpcpp argument.
+ *
+ * If error occures, return nonzero error code.
+ */
+int
+nlm_host_get_rpc(struct nlm_host *hostp, int vers, nlm_rpc_t **rpcpp)
+{
+	nlm_rpc_t *rpcp = NULL;
+	int rc;
+
+	mutex_enter(&hostp->nh_lock);
+
+	/*
+	 * If this handle is either uninitialized, or was
+	 * initialized using binding that's now stale
+	 * do the init or re-init.
+	 * See comments to enum nlm_rpcb_state for more
+	 * details.
+	 */
+again:
+	while (hostp->nh_rpcb_state != NRPCB_UPDATED) {
+		if (hostp->nh_rpcb_state == NRPCB_UPDATE_INPROGRESS) {
+			rc = cv_wait_sig(&hostp->nh_rpcb_cv, &hostp->nh_lock);
+			if (rc == 0) {
+				mutex_exit(&hostp->nh_lock);
+				return (EINTR);
+			}
+		}
+
+		/*
+		 * Check if RPC binding was marked for update.
+		 * If so, start RPC binding update operation.
+		 * NOTE: the operation can be executed by only
+		 * one thread at time.
+		 */
+		if (hostp->nh_rpcb_state == NRPCB_NEED_UPDATE)
+			update_host_rpcbinding(hostp, vers);
+
+		/*
+		 * Check if RPC error occured during RPC binding
+		 * update operation. If so, report a correspoding
+		 * error.
+		 */
+		if (hostp->nh_rpcb_ustat != RPC_SUCCESS) {
+			mutex_exit(&hostp->nh_lock);
+			return (ENOENT);
+		}
+	}
+
+	rpcp = get_nlm_rpc_fromcache(hostp, vers);
+	mutex_exit(&hostp->nh_lock);
+	if (rpcp == NULL) {
+		/*
+		 * There weren't any RPC handles in a host
+		 * cache. No luck, just create a new one.
+		 */
+		rpcp = kmem_cache_alloc(nlm_rpch_cache, KM_SLEEP);
+		rpcp->nr_vers = vers;
+	}
+
+	/*
+	 * Refresh RPC binding
+	 */
+	rc = refresh_nlm_rpc(hostp, rpcp);
+	if (rc != 0) {
+		if (rc == ESTALE) {
+			/*
+			 * Host's RPC binding is stale, we have
+			 * to update it. Put the RPC handle back
+			 * to the cache and mark the host as
+			 * "need update".
+			 */
+			mutex_enter(&hostp->nh_lock);
+			hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
+			nlm_host_rele_rpc_locked(hostp, rpcp);
+			goto again;
+		}
+
+		destroy_rpch(rpcp);
+		return (rc);
+	}
+
+	DTRACE_PROBE2(end, struct nlm_host *, hostp,
+	    nlm_rpc_t *, rpcp);
+
+	*rpcpp = rpcp;
+	return (0);
+}
+
+void
+nlm_host_rele_rpc(struct nlm_host *hostp, nlm_rpc_t *rpcp)
+{
+	mutex_enter(&hostp->nh_lock);
+	nlm_host_rele_rpc_locked(hostp, rpcp);
+	mutex_exit(&hostp->nh_lock);
+}
+
+static void
+nlm_host_rele_rpc_locked(struct nlm_host *hostp, nlm_rpc_t *rpcp)
+{
+	ASSERT(mutex_owned(&hostp->nh_lock));
+	TAILQ_INSERT_HEAD(&hostp->nh_rpchc, rpcp, nr_link);
+}
+
+/*
+ * The function invalidates host's RPC binding by marking it
+ * as not fresh. In this case another time thread tries to
+ * get RPC handle from host's handles cache, host's RPC binding
+ * will be updated.
+ *
+ * The function should be executed when RPC call invoked via
+ * handle taken from RPC cache returns RPC_PROCUNAVAIL.
+ */
+void
+nlm_host_invalidate_binding(struct nlm_host *hostp)
+{
+	mutex_enter(&hostp->nh_lock);
+	hostp->nh_rpcb_state = NRPCB_NEED_UPDATE;
+	mutex_exit(&hostp->nh_lock);
+}
+
+void
+nlm_rpc_init(void)
+{
+	nlm_rpch_cache = kmem_cache_create("nlm_rpch_cache",
+	    sizeof (nlm_rpc_t), 0, nlm_rpch_ctor, nlm_rpch_dtor,
+	    NULL, NULL, NULL, 0);
+}
+
+void
+nlm_rpc_cache_destroy(struct nlm_host *hostp)
+{
+	nlm_rpc_t *rpcp;
+
+	/*
+	 * There's no need to lock host's mutex here,
+	 * nlm_rpc_cache_destroy() should be called from
+	 * only one place: nlm_host_destroy, when all
+	 * resources host owns are already cleaned up.
+	 * So there shouldn't be any raises.
+	 */
+	while ((rpcp = TAILQ_FIRST(&hostp->nh_rpchc)) != NULL) {
+		TAILQ_REMOVE(&hostp->nh_rpchc, rpcp, nr_link);
+		destroy_rpch(rpcp);
+	}
+}
+
+/* ARGSUSED */
+static int
+nlm_rpch_ctor(void *datap, void *cdrarg, int kmflags)
+{
+	nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
+
+	bzero(rpcp, sizeof (*rpcp));
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+nlm_rpch_dtor(void *datap, void *cdrarg)
+{
+	nlm_rpc_t *rpcp = (nlm_rpc_t *)datap;
+	ASSERT(rpcp->nr_handle == NULL);
+}
+
+static void
+destroy_rpch(nlm_rpc_t *rpcp)
+{
+	if (rpcp->nr_handle != NULL) {
+		AUTH_DESTROY(rpcp->nr_handle->cl_auth);
+		CLNT_DESTROY(rpcp->nr_handle);
+		rpcp->nr_handle = NULL;
+	}
+
+	kmem_cache_free(nlm_rpch_cache, rpcp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_rpc_svc.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager, RPC service functions (nlm_..._svc)
+ * Called via nlm_dispatch.c tables.
+ *
+ * Source code derived from FreeBSD nlm_prot_server.c
+ *
+ * The real service functions all use nlm4_... args and return
+ * data types.  These wrappers convert older forms to and from
+ * the new forms and call the nlm_do_... service functions.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <rpcsvc/nlm_prot.h>
+#include "nlm_impl.h"
+
+/*
+ * Convert between various versions of the protocol structures.
+ */
+
+/*
+ * Down-convert, for granted_1 call
+ *
+ * This converts a 64-bit lock to 32-bit form for our granted
+ * call-back when we're dealing with a 32-bit NLM client.
+ * Our NLM_LOCK handler ensures that any lock we grant to a
+ * 32-bit client can be represented in 32-bits.  If the
+ * ASSERTs here fire, then the call to nlm_init_flock in
+ * nlm_do_lock has failed to restrict a 32-bit client to
+ * 32-bit lock ranges.
+ */
+static void
+nlm_convert_to_nlm_lock(struct nlm_lock *dst, struct nlm4_lock *src)
+{
+	dst->caller_name = src->caller_name;
+	dst->fh = src->fh;
+	dst->oh = src->oh;
+	dst->svid = src->svid;
+	ASSERT(src->l_offset <= MAX_UOFF32);
+	dst->l_offset = (uint32_t)src->l_offset;
+	ASSERT(src->l_len <= MAX_UOFF32);
+	dst->l_len = (uint32_t)src->l_len;
+}
+
+/*
+ * Up-convert for v1 svc functions with a 32-bit lock range arg.
+ * Note that lock range checks (like overflow) are done later,
+ * in nlm_init_flock().
+ */
+static void
+nlm_convert_to_nlm4_lock(struct nlm4_lock *dst, struct nlm_lock *src)
+{
+
+	dst->caller_name = src->caller_name;
+	dst->fh = src->fh;
+	dst->oh = src->oh;
+	dst->svid = src->svid;
+	dst->l_offset = src->l_offset;
+	dst->l_len = src->l_len;
+}
+
+static void
+nlm_convert_to_nlm4_share(struct nlm4_share *dst, struct nlm_share *src)
+{
+
+	dst->caller_name = src->caller_name;
+	dst->fh = src->fh;
+	dst->oh = src->oh;
+	dst->mode = src->mode;
+	dst->access = src->access;
+}
+
+/*
+ * Down-convert for v1 NLM_TEST or NLM_TEST_MSG response.
+ * Note that nlm_do_test is careful to give us lock ranges
+ * that can be represented with 32-bit values.  If the
+ * ASSERTs here fire, then the code in nlm_do_test that
+ * builds an nlm4_holder for a 32-bit client has failed to
+ * restrict the reported conflicting lock range so it's a
+ * valid 32-bit lock range.
+ */
+static void
+nlm_convert_to_nlm_holder(struct nlm_holder *dst, struct nlm4_holder *src)
+{
+	dst->exclusive = src->exclusive;
+	dst->svid = src->svid;
+	dst->oh = src->oh;
+	ASSERT(src->l_offset <= MAX_UOFF32);
+	dst->l_offset = (uint32_t)src->l_offset;
+	ASSERT(src->l_len <= MAX_UOFF32);
+	dst->l_len = (uint32_t)src->l_len;
+}
+
+static enum nlm_stats
+nlm_convert_to_nlm_stats(enum nlm4_stats src)
+{
+	if (src > nlm4_deadlck)
+		return (nlm_denied);
+	return ((enum nlm_stats)src);
+}
+
+static void
+nlm_convert_to_nlm_res(struct nlm_res *dst, struct nlm4_res *src)
+{
+	dst->cookie = src->cookie;
+	dst->stat.stat = nlm_convert_to_nlm_stats(src->stat.stat);
+}
+
+/* ******************************************************************** */
+
+/*
+ * Version 1 svc functions
+ */
+
+bool_t
+nlm_test_1_svc(struct nlm_testargs *argp, nlm_testres *resp,
+    struct svc_req *sr)
+{
+	nlm4_testargs args4;
+	nlm4_testres res4;
+
+	bzero(&args4, sizeof (args4));
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_test(&args4, &res4, sr, NULL);
+
+	resp->cookie = res4.cookie;
+	resp->stat.stat = nlm_convert_to_nlm_stats(res4.stat.stat);
+	if (resp->stat.stat == nlm_denied)
+		nlm_convert_to_nlm_holder(
+		    &resp->stat.nlm_testrply_u.holder,
+		    &res4.stat.nlm4_testrply_u.holder);
+
+	return (TRUE);
+}
+
+/*
+ * Callback functions for nlm_lock_1_svc
+ */
+static bool_t nlm_lock_1_reply(SVCXPRT *, nlm4_res *);
+static enum clnt_stat nlm_granted_1_cb(nlm4_testargs *, void *, CLIENT *);
+
+bool_t
+nlm_lock_1_svc(nlm_lockargs *argp, nlm_res *resp,
+    struct svc_req *sr)
+{
+	nlm4_lockargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.block = argp->block;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+	args4.reclaim = argp->reclaim;
+	args4.state = argp->state;
+
+	/* NLM_LOCK */
+	nlm_do_lock(&args4, &res4, sr,
+	    nlm_lock_1_reply, NULL,
+	    nlm_granted_1_cb);
+
+	/* for freeresult */
+	nlm_convert_to_nlm_res(resp, &res4);
+
+	/* above does its own reply */
+	return (FALSE);
+}
+
+static bool_t
+nlm_lock_1_reply(SVCXPRT *transp, nlm4_res *resp)
+{
+	nlm_res res1;
+
+	nlm_convert_to_nlm_res(&res1, resp);
+	return (svc_sendreply(transp, xdr_nlm_res, (char *)&res1));
+}
+
+static enum clnt_stat
+nlm_granted_1_cb(nlm4_testargs *argp, void *resp, CLIENT *clnt)
+{
+	nlm_testargs args1;
+	nlm_res res1;
+	int rv;
+
+	bzero(&res1, sizeof (res1));
+
+	args1.cookie = argp->cookie;
+	args1.exclusive = argp->exclusive;
+	nlm_convert_to_nlm_lock(&args1.alock, &argp->alock);
+
+	rv = nlm_granted_1(&args1, &res1, clnt);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm_res, (void *)&res1);
+	(void) resp;
+
+	return (rv);
+}
+
+bool_t
+nlm_cancel_1_svc(struct nlm_cancargs *argp, nlm_res *resp,
+    struct svc_req *sr)
+{
+	nlm4_cancargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.block = argp->block;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_cancel(&args4, &res4, sr, NULL);
+
+	nlm_convert_to_nlm_res(resp, &res4);
+
+	return (TRUE);
+}
+
+bool_t
+nlm_unlock_1_svc(struct nlm_unlockargs *argp, nlm_res *resp,
+    struct svc_req *sr)
+{
+	nlm4_unlockargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_unlock(&args4, &res4, sr, NULL);
+
+	nlm_convert_to_nlm_res(resp, &res4);
+
+	return (TRUE);
+}
+
+bool_t
+nlm_granted_1_svc(struct nlm_testargs *argp, nlm_res *resp,
+    struct svc_req *sr)
+{
+	nlm4_testargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_granted(&args4, &res4, sr, NULL);
+
+	nlm_convert_to_nlm_res(resp, &res4);
+
+	return (TRUE);
+}
+
+/*
+ * The _msg_ calls get no reply.  Instead, these callers
+ * expect an RPC call to the corresponding _res function.
+ * We pass this callback function to nlm_do_test so it will
+ * use it to do the RPC callback, with the correct res type.
+ *
+ * The callback functions have nearly the same arg signature
+ * as the client call functions so that many of those can be
+ * optimized to nothing by the compiler.  Also, passing the
+ * null result arg for these just to reduce warnings.
+ *
+ * See similar callbacks for other _msg functions below.
+ */
+
+static enum clnt_stat nlm_test_res_1_cb(nlm4_testres *, void *, CLIENT *);
+
+bool_t
+nlm_test_msg_1_svc(struct nlm_testargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_testargs args4;
+	nlm4_testres res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_test(&args4, &res4, sr,
+	    nlm_test_res_1_cb);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_testres, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+static enum clnt_stat
+nlm_test_res_1_cb(nlm4_testres *res4, void *null, CLIENT *clnt)
+{
+	nlm_testres res1;
+
+	res1.cookie = res4->cookie;
+	res1.stat.stat = nlm_convert_to_nlm_stats(res4->stat.stat);
+	if (res1.stat.stat == nlm_denied)
+		nlm_convert_to_nlm_holder(
+		    &res1.stat.nlm_testrply_u.holder,
+		    &res4->stat.nlm4_testrply_u.holder);
+
+	return (nlm_test_res_1(&res1, null, clnt));
+}
+
+/*
+ * Callback functions for nlm_lock_msg_1_svc
+ */
+static enum clnt_stat nlm_lock_res_1_cb(nlm4_res *, void *, CLIENT *);
+static enum clnt_stat nlm_granted_msg_1_cb(nlm4_testargs *, void *, CLIENT *);
+
+bool_t
+nlm_lock_msg_1_svc(nlm_lockargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_lockargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.block = argp->block;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+	args4.reclaim = argp->reclaim;
+	args4.state = argp->state;
+
+	/* NLM_LOCK_MSG */
+	nlm_do_lock(&args4, &res4, sr,
+	    NULL, nlm_lock_res_1_cb,
+	    nlm_granted_msg_1_cb);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+static enum clnt_stat
+nlm_lock_res_1_cb(nlm4_res *resp, void *null, CLIENT *clnt)
+{
+	nlm_res res1;
+
+	nlm_convert_to_nlm_res(&res1, resp);
+	return (nlm_lock_res_1(&res1, null, clnt));
+}
+
+static enum clnt_stat
+nlm_granted_msg_1_cb(nlm4_testargs *argp, void *null, CLIENT *clnt)
+{
+	nlm_testargs args1;
+
+	args1.cookie = argp->cookie;
+	args1.exclusive = argp->exclusive;
+	nlm_convert_to_nlm_lock(&args1.alock, &argp->alock);
+
+	return (nlm_granted_msg_1(&args1, null, clnt));
+
+}
+
+
+static enum clnt_stat nlm_cancel_res_1_cb(nlm4_res *, void *, CLIENT *);
+
+bool_t
+nlm_cancel_msg_1_svc(struct nlm_cancargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_cancargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.block = argp->block;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_cancel(&args4, &res4, sr,
+	    nlm_cancel_res_1_cb);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+static enum clnt_stat
+nlm_cancel_res_1_cb(nlm4_res *res4, void *null, CLIENT *clnt)
+{
+	nlm_res res1;
+
+	nlm_convert_to_nlm_res(&res1, res4);
+	return (nlm_cancel_res_1(&res1, null, clnt));
+}
+
+
+static enum clnt_stat nlm_unlock_res_1_cb(nlm4_res *, void *, CLIENT *);
+
+bool_t
+nlm_unlock_msg_1_svc(struct nlm_unlockargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_unlockargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_unlock(&args4, &res4, sr,
+	    nlm_unlock_res_1_cb);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+static enum clnt_stat
+nlm_unlock_res_1_cb(nlm4_res *res4, void *null, CLIENT *clnt)
+{
+	nlm_res res1;
+
+	nlm_convert_to_nlm_res(&res1, res4);
+	return (nlm_unlock_res_1(&res1, null, clnt));
+}
+
+
+static enum clnt_stat nlm_granted_res_1_cb(nlm4_res *, void *, CLIENT *);
+
+bool_t
+nlm_granted_msg_1_svc(struct nlm_testargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_testargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+
+	nlm_do_granted(&args4, &res4, sr,
+	    nlm_granted_res_1_cb);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+static enum clnt_stat
+nlm_granted_res_1_cb(nlm4_res *res4, void *null, CLIENT *clnt)
+{
+	nlm_res res1;
+
+	nlm_convert_to_nlm_res(&res1, res4);
+	return (nlm_granted_res_1(&res1, null, clnt));
+}
+
+/*
+ * The _res_ calls get no reply.  These RPC calls are
+ * "call backs" in response to RPC _msg_ calls.
+ * We don't care about these responses.
+ */
+
+/* ARGSUSED */
+bool_t
+nlm_test_res_1_svc(nlm_testres *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm_lock_res_1_svc(nlm_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm_cancel_res_1_svc(nlm_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm_unlock_res_1_svc(nlm_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm_granted_res_1_svc(nlm_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/*
+ * Version 2 svc functions (used by local statd)
+ */
+
+bool_t
+nlm_sm_notify1_2_svc(struct nlm_sm_status *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm_do_notify1(argp, resp, sr);
+	return (TRUE);
+}
+
+bool_t
+nlm_sm_notify2_2_svc(struct nlm_sm_status *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm_do_notify2(argp, resp, sr);
+	return (TRUE);
+}
+
+/*
+ * Version 3 svc functions
+ */
+
+bool_t
+nlm_share_3_svc(nlm_shareargs *argp, nlm_shareres *resp,
+    struct svc_req *sr)
+{
+	nlm4_shareargs args4;
+	nlm4_shareres res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	nlm_convert_to_nlm4_share(&args4.share, &argp->share);
+	args4.reclaim = argp->reclaim;
+
+	nlm_do_share(&args4, &res4, sr);
+
+	resp->cookie = res4.cookie;
+	resp->stat = nlm_convert_to_nlm_stats(res4.stat);
+	resp->sequence = res4.sequence;
+
+	return (TRUE);
+}
+
+bool_t
+nlm_unshare_3_svc(nlm_shareargs *argp, nlm_shareres *resp,
+    struct svc_req *sr)
+{
+	nlm4_shareargs args4;
+	nlm4_shareres res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	nlm_convert_to_nlm4_share(&args4.share, &argp->share);
+	args4.reclaim = argp->reclaim;
+
+	nlm_do_unshare(&args4, &res4, sr);
+
+	resp->cookie = res4.cookie;
+	resp->stat = nlm_convert_to_nlm_stats(res4.stat);
+	resp->sequence = res4.sequence;
+
+	return (TRUE);
+}
+
+bool_t
+nlm_nm_lock_3_svc(nlm_lockargs *argp, nlm_res *resp, struct svc_req *sr)
+{
+	nlm4_lockargs args4;
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+
+	args4.cookie = argp->cookie;
+	args4.block = argp->block;
+	args4.exclusive = argp->exclusive;
+	nlm_convert_to_nlm4_lock(&args4.alock, &argp->alock);
+	args4.reclaim = argp->reclaim;
+	args4.state = argp->state;
+
+	/*
+	 * Don't allow blocking for non-monitored (nm_lock) calls.
+	 * These clients don't handle any callbacks, including
+	 * the granted call we make after a blocking lock.
+	 * Same reply callback as nlm_lock_1_svc
+	 */
+	args4.block = FALSE;
+
+	/* NLM_NM_LOCK */
+	nlm_do_lock(&args4, &res4, sr,
+	    nlm_lock_1_reply, NULL,
+	    NULL); /* indicates non-monitored */
+
+	/* for freeresult */
+	nlm_convert_to_nlm_res(resp, &res4);
+
+	/* above does its own reply */
+	return (FALSE);
+}
+
+bool_t
+nlm_free_all_3_svc(nlm_notify *argp, void *resp, struct svc_req *sr)
+{
+	struct nlm4_notify args4;
+
+	args4.name = argp->name;
+	args4.state = argp->state;
+
+	nlm_do_free_all(&args4, resp, sr);
+
+	return (TRUE);
+}
+
+/*
+ * Version 4 svc functions
+ */
+
+bool_t
+nlm4_test_4_svc(nlm4_testargs *argp, nlm4_testres *resp, struct svc_req *sr)
+{
+	nlm_do_test(argp, resp, sr, NULL);
+	return (TRUE);
+}
+
+/*
+ * Callback functions for nlm4_lock_4_svc
+ */
+static bool_t nlm4_lock_4_reply(SVCXPRT *, nlm4_res *);
+static enum clnt_stat nlm4_granted_4_cb(nlm4_testargs *, void *, CLIENT *);
+
+bool_t
+nlm4_lock_4_svc(nlm4_lockargs *argp, nlm4_res *resp,
+    struct svc_req *sr)
+{
+
+	/* NLM4_LOCK */
+	nlm_do_lock(argp, resp, sr,
+	    nlm4_lock_4_reply, NULL,
+	    nlm4_granted_4_cb);
+
+	/* above does its own reply */
+	return (FALSE);
+}
+
+static bool_t
+nlm4_lock_4_reply(SVCXPRT *transp, nlm4_res *resp)
+{
+	return (svc_sendreply(transp, xdr_nlm4_res, (char *)resp));
+}
+
+static enum clnt_stat
+nlm4_granted_4_cb(nlm4_testargs *argp, void *resp, CLIENT *clnt)
+{
+	nlm4_res res4;
+	int rv;
+
+	bzero(&res4, sizeof (res4));
+	rv = nlm4_granted_4(argp, &res4, clnt);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	return (rv);
+}
+
+bool_t
+nlm4_cancel_4_svc(nlm4_cancargs *argp, nlm4_res *resp, struct svc_req *sr)
+{
+	nlm_do_cancel(argp, resp, sr, NULL);
+	return (TRUE);
+}
+
+bool_t
+nlm4_unlock_4_svc(nlm4_unlockargs *argp, nlm4_res *resp, struct svc_req *sr)
+{
+	nlm_do_unlock(argp, resp, sr, NULL);
+	return (TRUE);
+}
+
+bool_t
+nlm4_granted_4_svc(nlm4_testargs *argp, nlm4_res *resp, struct svc_req *sr)
+{
+	nlm_do_granted(argp, resp, sr, NULL);
+	return (TRUE);
+}
+
+bool_t
+nlm4_test_msg_4_svc(nlm4_testargs *argp, void *resp, struct svc_req *sr)
+{
+	nlm4_testres res4;
+
+	bzero(&res4, sizeof (res4));
+	nlm_do_test(argp, &res4, sr,
+	    nlm4_test_res_4);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_testres, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+/*
+ * Callback functions for nlm4_lock_msg_4_svc
+ * (using the RPC client stubs directly)
+ */
+
+bool_t
+nlm4_lock_msg_4_svc(nlm4_lockargs *argp, void *resp,
+    struct svc_req *sr)
+{
+	nlm4_res res4;
+
+	/* NLM4_LOCK_MSG */
+	bzero(&res4, sizeof (res4));
+	nlm_do_lock(argp, &res4, sr,
+	    NULL, nlm4_lock_res_4,
+	    nlm4_granted_msg_4);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+bool_t
+nlm4_cancel_msg_4_svc(nlm4_cancargs *argp, void *resp, struct svc_req *sr)
+{
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+	nlm_do_cancel(argp, &res4, sr,
+	    nlm4_cancel_res_4);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+bool_t
+nlm4_unlock_msg_4_svc(nlm4_unlockargs *argp, void *resp, struct svc_req *sr)
+{
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+	nlm_do_unlock(argp, &res4, sr,
+	    nlm4_unlock_res_4);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+bool_t
+nlm4_granted_msg_4_svc(nlm4_testargs *argp, void *resp, struct svc_req *sr)
+{
+	nlm4_res res4;
+
+	bzero(&res4, sizeof (res4));
+	nlm_do_granted(argp, &res4, sr,
+	    nlm4_granted_res_4);
+
+	/* NB: We have a result our caller will not free. */
+	xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res4);
+	(void) resp;
+
+	/* The _msg_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_test_res_4_svc(nlm4_testres *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_lock_res_4_svc(nlm4_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_cancel_res_4_svc(nlm4_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_unlock_res_4_svc(nlm4_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_granted_res_4_svc(nlm4_res *argp, void *resp, struct svc_req *sr)
+{
+	/* The _res_ calls get no reply. */
+	return (FALSE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_share_4_svc(nlm4_shareargs *argp, nlm4_shareres *resp,
+    struct svc_req *sr)
+{
+	nlm_do_share(argp, resp, sr);
+	return (TRUE);
+}
+
+/* ARGSUSED */
+bool_t
+nlm4_unshare_4_svc(nlm4_shareargs *argp, nlm4_shareres *resp,
+    struct svc_req *sr)
+{
+	nlm_do_unshare(argp, resp, sr);
+	return (TRUE);
+}
+
+bool_t
+nlm4_nm_lock_4_svc(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr)
+{
+
+	/*
+	 * Don't allow blocking for non-monitored (nm_lock) calls.
+	 * These clients don't handle any callbacks, including
+	 * the granted call we make after a blocking lock.
+	 * Same reply callback as nlm4_lock_4_svc
+	 */
+	argp->block = FALSE;
+
+	/* NLM4_NM_LOCK */
+	nlm_do_lock(argp, resp, sr,
+	    nlm4_lock_4_reply, NULL,
+	    NULL); /* indicates non-monitored */
+
+	/* above does its own reply */
+	return (FALSE);
+}
+
+bool_t
+nlm4_free_all_4_svc(nlm4_notify *argp, void *resp, struct svc_req *sr)
+{
+	nlm_do_free_all(argp, resp, sr);
+	return (TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/klm/nlm_service.c	Mon Aug 26 20:42:32 2013 -0800
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ */
+
+/*
+ * NFS Lock Manager service functions (nlm_do_...)
+ * Called from nlm_rpc_svc.c wrappers.
+ *
+ * Source code derived from FreeBSD nlm_prot_impl.c
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/mount.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/share.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/taskq.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/queue.h>
+#include <sys/sdt.h>
+#include <netinet/in.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/rpcb_prot.h>
+
+#include <rpcsvc/nlm_prot.h>
+#include <rpcsvc/sm_inter.h>
+
+#include <nfs/nfs.h>
+#include <nfs/nfs_clnt.h>
+#include <nfs/export.h>
+#include <nfs/rnode.h>
+
+#include "nlm_impl.h"
+
+#define	NLM_IN_GRACE(g) (ddi_get_lbolt() < (g)->grace_threshold)
+
+struct nlm_block_cb_data {
+	struct nlm_host		*hostp;
+	struct nlm_vhold	*nvp;
+	struct flock64		*flp;
+};
+
+/*
+ * Invoke an asyncronous RPC callbeck
+ * (used when NLM server needs to reply to MSG NLM procedure).
+ */
+#define	NLM_INVOKE_CALLBACK(descr, rpcp, resp, callb)			\
+	do {								\
+		enum clnt_stat _stat;					\
+									\
+		_stat = (*(callb))(resp, NULL, (rpcp)->nr_handle);	\
+		if (_stat != RPC_SUCCESS && _stat != RPC_TIMEDOUT) {	\
+			struct rpc_err _err;				\
+									\
+			CLNT_GETERR((rpcp)->nr_handle, &_err);		\
+			NLM_ERR("NLM: %s callback failed: "		\
+			    "stat %d, err %d\n", descr, _stat,		\
+			    _err.re_errno);				\
+		}							\
+									\
+	_NOTE(CONSTCOND) } while (0)
+
+static void nlm_block(
+	nlm4_lockargs *lockargs,
+	struct nlm_host *host,
+	struct nlm_vhold *nvp,
+	nlm_rpc_t *rpcp,
+	struct flock64 *fl,
+	nlm_testargs_cb grant_cb);
+
+static vnode_t *nlm_fh_to_vp(struct netobj *);
+static struct nlm_vhold *nlm_fh_to_vhold(struct nlm_host *, struct netobj *);
+static void nlm_init_shrlock(struct shrlock *, nlm4_share *, struct nlm_host *);
+static callb_cpr_t *nlm_block_callback(flk_cb_when_t, void *);
+static int nlm_vop_frlock(vnode_t *, int, flock64_t *, int, offset_t,
+    struct flk_callback *, cred_t *, caller_context_t *);
+
+/*
+ * Convert a lock from network to local form, and
+ * check for valid range (no overflow).
+ */
+static int
+nlm_init_flock(struct flock64 *fl, struct nlm4_lock *nl,
+	struct nlm_host *host, rpcvers_t vers, short type)
+{
+	uint64_t off, len;
+
+	bzero(fl, sizeof (*fl));
+	off = nl->l_offset;
+	len = nl->l_len;
+
+	if (vers < NLM4_VERS) {
+		if (off > MAX_UOFF32 || len > MAX_UOFF32)
+			return (EINVAL);
+		if (off + len > MAX_UOFF32 + 1)
+			return (EINVAL);
+	} else {
+		/*
+		 * Check range for 64-bit client (no overflow).
+		 * Again allow len == ~0 to mean lock to EOF.
+		 */
+		if (len == MAX_U_OFFSET_T)
+			len = 0;
+		if (len != 0 && off + (len - 1) < off)
+			return (EINVAL);
+	}
+
+	fl->l_type = type;
+	fl->l_whence = SEEK_SET;
+	fl->l_start = off;
+	fl->l_len = len;
+	fl->l_sysid = host->nh_sysid;
+	fl->l_pid = nl->svid;
+	/* l_pad */
+
+	return (0);
+}
+
+/*
+ * Gets vnode from client's filehandle
+ * NOTE: Holds vnode, it _must_ be explicitly
+ * released by VN_RELE().
+ */
+static vnode_t *
+nlm_fh_to_vp(struct netobj *fh)
+{
+	fhandle_t *fhp;
+
+	/*
+	 * Get a vnode pointer for the given NFS file handle.
+	 * Note that it could be an NFSv2 for NFSv3 handle,
+	 * which means the size might vary.  (don't copy)
+	 */
+	if (fh->n_len < sizeof (*fhp))
+		return (NULL);
+
+	/* We know this is aligned (kmem_alloc) */
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	fhp = (fhandle_t *)fh->n_bytes;
+	return (lm_fhtovp(fhp));
+}
+
+/*
+ * Get vhold from client's filehandle, but in contrast to
+ * The function tries to check some access rights as well.
+ *
+ * NOTE: vhold object _must_ be explicitly released by
+ * nlm_vhold_release().
+ */
+static struct nlm_vhold *
+nlm_fh_to_vhold(struct nlm_host *hostp, struct netobj *fh)
+{
+	vnode_t *vp;
+	struct nlm_vhold *nvp;
+
+	vp = nlm_fh_to_vp(fh);
+	if (vp == NULL)
+		return (NULL);
+
+
+	nvp = nlm_vhold_get(hostp, vp);
+
+	/*
+	 * Both nlm_fh_to_vp() and nlm_vhold_get()
+	 * do VN_HOLD(), so we need to drop one
+	 * reference on vnode.
+	 */
+	VN_RELE(vp);
+	return (nvp);
+}
+
+/* ******************************************************************* */
+
+/*
+ * NLM implementation details, called from the RPC svc code.
+ */
+
+/*
+ * Call-back from NFS statd, used to notify that one of our
+ * hosts had a status change. The host can be either an
+ * NFS client, NFS server or both.
+ * According to NSM protocol description, the state is a
+ * number that is increases monotonically each time the
+ * state of host changes. An even number indicates that
+ * the host is down, while an odd number indicates that
+ * the host is up.
+ *
+ * Here we ignore this even/odd difference of status number
+ * reported by the NSM, we launch notification handlers
+ * every time the state is changed. The reason we why do so
+ * is that client and server can talk to each other using
+ * connectionless transport and it's easy to lose packet
+ * containing NSM notification with status number update.
+ *
+ * In nlm_host_monitor(), we put the sysid in the private data
+ * that statd carries in this callback, so we can easliy find
+ * the host this call applies to.
+ */
+/* ARGSUSED */
+void
+nlm_do_notify1(nlm_sm_status *argp, void *res, struct svc_req *sr)
+{
+	struct nlm_globals *g;
+	struct nlm_host *host;
+	uint16_t sysid;
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	bcopy(&argp->priv, &sysid, sizeof (sysid));
+
+	DTRACE_PROBE2(nsm__notify, uint16_t, sysid,
+	    int, argp->state);
+
+	host = nlm_host_find_by_sysid(g, (sysid_t)sysid);
+	if (host == NULL)
+		return;
+
+	nlm_host_notify_server(host, argp->state);
+	nlm_host_notify_client(host, argp->state);
+	nlm_host_release(g, host);
+}
+
+/*
+ * Another available call-back for NFS statd.
+ * Not currently used.
+ */
+/* ARGSUSED */
+void
+nlm_do_notify2(nlm_sm_status *argp, void *res, struct svc_req *sr)
+{
+	ASSERT(0);
+}
+
+
+/*
+ * NLM_TEST, NLM_TEST_MSG,
+ * NLM4_TEST, NLM4_TEST_MSG,
+ * Client inquiry about locks, non-blocking.
+ */
+void
+nlm_do_test(nlm4_testargs *argp, nlm4_testres *resp,
+    struct svc_req *sr, nlm_testres_cb cb)
+{
+	struct nlm_globals *g;
+	struct nlm_host *host;
+	struct nlm4_holder *lh;
+	struct nlm_owner_handle *oh;
+	nlm_rpc_t *rpcp = NULL;
+	vnode_t *vp = NULL;
+	struct netbuf *addr;
+	char *netid;
+	char *name;
+	int error;
+	struct flock64 fl;
+
+	nlm_copy_netobj(&resp->cookie, &argp->cookie);
+
+	name = argp->alock.caller_name;
+	netid = svc_getnetid(sr->rq_xprt);
+	addr = svc_getrpccaller(sr->rq_xprt);
+
+	g = zone_getspecific(nlm_zone_key, curzone);
+	host = nlm_host_findcreate(g, name, netid, addr);
+	if (host == NULL) {
+		resp->stat.stat = nlm4_denied_nolocks;
+		return;
+	}
+	if (cb != NULL) {
+		error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
+		if (error != 0) {
+			resp->stat.stat = nlm4_denied_nolocks;
+			goto out;
+		}
+	}
+
+	vp = nlm_fh_to_vp(&argp->alock.fh);
+	if (vp == NULL) {
+		resp->stat.stat = nlm4_stale_fh;
+		goto out;
+	}
+
+	if (NLM_IN_GRACE(g)) {
+		resp->stat.stat = nlm4_denied_grace_period;
+		goto out;
+	}
+
+	/* Convert to local form. */
+	error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers,
+	    (argp->exclusive) ? F_WRLCK : F_RDLCK);
+	if (error) {
+		resp->stat.stat = nlm4_failed;
+		goto out;
+	}
+
+	/* BSD: VOP_ADVLOCK(nv->nv_vp, NULL, F_GETLK, &fl, F_REMOTE); */
+	error = nlm_vop_frlock(vp, F_GETLK, &fl,
+	    F_REMOTELOCK | FREAD | FWRITE,
+	    (u_offset_t)0, NULL, CRED(), NULL);
+	if (error) {
+		resp->stat.stat = nlm4_failed;
+		goto out;
+	}
+
+	if (fl.l_type == F_UNLCK) {
+		resp->stat.stat = nlm4_granted;
+		goto out;
+	}
+	resp->stat.stat = nlm4_denied;
+
+	/*
+	 * This lock "test" fails due to a conflicting lock.
+	 *
+	 * If this is a v1 client, make sure the conflicting
+	 * lock range we report can be expressed with 32-bit
+	 * offsets.  The lock range requested was expressed
+	 * as 32-bit offset and length, so at least part of
+	 * the conflicting lock should lie below MAX_UOFF32.
+	 * If the conflicting lock extends past that, we'll
+	 * trim the range to end at MAX_UOFF32 so this lock
+	 * can be represented in a 32-bit response.  Check
+	 * the start also (paranoid, but a low cost check).
+	 */
+	if (sr->rq_vers < NLM4_VERS) {
+		uint64 maxlen;
+		if (fl.l_start > MAX_UOFF32)
+			fl.l_start = MAX_UOFF32;
+		maxlen = MAX_UOFF32 + 1 - fl.l_start;
+		if (fl.l_len > maxlen)
+			fl.l_len = maxlen;
+	}
+
+	/*
+	 * Build the nlm4_holder result structure.
+	 *
+	 * Note that lh->oh is freed via xdr_free,
+	 * xdr_nlm4_holder, xdr_netobj, xdr_bytes.
+	 */
+	oh = kmem_zalloc(sizeof (*oh), KM_SLEEP);
+	oh->oh_sysid = (sysid_t)fl.l_sysid;
+	lh = &resp->stat.nlm4_testrply_u.holder;
+	lh->exclusive = (fl.l_type == F_WRLCK);
+	lh->svid = fl.l_pid;
+	lh->oh.n_len = sizeof (*oh);
+	lh->oh.n_bytes = (void *)oh;
+	lh->l_offset = fl.l_start;
+	lh->l_len = fl.l_len;
+
+out:
+	/*
+	 * If we have a callback funtion, use that to
+	 * deliver the response via another RPC call.
+	 */
+	if (cb != NULL && rpcp != NULL)
+		NLM_INVOKE_CALLBACK("test", rpcp, resp, cb);
+
+	if (vp != NULL)
+		VN_RELE(vp);
+	if (rpcp != NULL)
+		nlm_host_rele_rpc(host, rpcp);
+
+	nlm_host_release(g, host);
+}
+
+/*
+ * NLM_LOCK, NLM_LOCK_MSG, NLM_NM_LOCK
+ * NLM4_LOCK, NLM4_LOCK_MSG, NLM4_NM_LOCK
+ *
+ * Client request to set a lock, possibly blocking.
+ *
+ * If the lock needs to block, we return status blocked to
+ * this RPC call, and then later call back the client with
+ * a "