changeset 12679:30c794f87c0d

6930814 share/unshare issues 6931194 exportfs() uses exportinfo_t after dropping exported_lock and without doing exi_count++ 6836258 exportinfo 16-way hash is too small 6953045 Put implementation of the Pearson's hashing algorithm into a separate file
author Pavel Filipensky <Pavel.Filipensky@Sun.COM>
date Wed, 23 Jun 2010 15:54:15 +0200
parents 79fad257c978
children 84a6540d6278
files usr/src/lib/libshare/common/libshare.c usr/src/lib/libshare/common/libshare.h usr/src/lib/libshare/nfs/libshare_nfs.c usr/src/pkg/manifests/system-header.mf usr/src/uts/common/Makefile.files usr/src/uts/common/fs/nfs/nfs4_idmap.c usr/src/uts/common/fs/nfs/nfs4_srv_ns.c usr/src/uts/common/fs/nfs/nfs_auth.c usr/src/uts/common/fs/nfs/nfs_export.c usr/src/uts/common/fs/pkp_hash.c usr/src/uts/common/fs/sharefs/sharetab.c usr/src/uts/common/nfs/export.h usr/src/uts/common/nfs/nfs4_idmap_impl.h usr/src/uts/common/sharefs/sharetab.h usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/pkp_hash.h
diffstat 16 files changed, 320 insertions(+), 405 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libshare/common/libshare.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/lib/libshare/common/libshare.c	Wed Jun 23 15:54:15 2010 +0200
@@ -221,6 +221,9 @@
 	case SA_PASSWORD_ENC:
 		ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
 		break;
+	case SA_SHARE_EXISTS:
+		ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
+		break;
 	default:
 		(void) snprintf(errstr, sizeof (errstr),
 		    dgettext(TEXT_DOMAIN, "unknown %d"), err);
--- a/usr/src/lib/libshare/common/libshare.h	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/lib/libshare/common/libshare.h	Wed Jun 23 15:54:15 2010 +0200
@@ -87,6 +87,7 @@
 #define	SA_NO_SUCH_SECTION	30	/* no section found */
 #define	SA_NO_PROPERTIES	31	/* no properties found */
 #define	SA_PASSWORD_ENC		32	/* passwords must be encrypted */
+#define	SA_SHARE_EXISTS		33	/* path or file is already shared */
 
 /* API Initialization */
 #define	SA_INIT_SHARE_API	0x0001	/* init share specific interface */
--- a/usr/src/lib/libshare/nfs/libshare_nfs.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.c	Wed Jun 23 15:54:15 2010 +0200
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -1952,7 +1951,10 @@
 				break;
 			}
 			err = SA_NO_PERMISSION;
-			/* FALLTHROUGH */
+			break;
+		case EEXIST:
+			err = SA_SHARE_EXISTS;
+			break;
 		default:
 			break;
 		}
--- a/usr/src/pkg/manifests/system-header.mf	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/pkg/manifests/system-header.mf	Wed Jun 23 15:54:15 2010 +0200
@@ -1283,6 +1283,7 @@
 file path=usr/include/sys/physmem.h
 $(i386_ONLY)file path=usr/include/sys/pic.h
 $(i386_ONLY)file path=usr/include/sys/pit.h
+file path=usr/include/sys/pkp_hash.h
 file path=usr/include/sys/pm.h
 $(i386_ONLY)file path=usr/include/sys/pmem.h
 file path=usr/include/sys/policy.h
--- a/usr/src/uts/common/Makefile.files	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/Makefile.files	Wed Jun 23 15:54:15 2010 +0200
@@ -255,6 +255,7 @@
 		pgrp.o		\
 		pgrpsys.o	\
 		pid.o		\
+		pkp_hash.o	\
 		policy.o	\
 		poll.o		\
 		pool.o		\
--- a/usr/src/uts/common/fs/nfs/nfs4_idmap.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/fs/nfs/nfs4_idmap.c	Wed Jun 23 15:54:15 2010 +0200
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 
@@ -124,6 +123,7 @@
 #include <sys/sunddi.h>
 #include <sys/dnlc.h>
 #include <sys/sdt.h>
+#include <sys/pkp_hash.h>
 #include <nfs/nfs4.h>
 #include <nfs/rnode4.h>
 #include <nfs/nfsid_map.h>
@@ -137,7 +137,6 @@
 static list_t			nfsidmap_globals_list;
 static kmutex_t			nfsidmap_globals_lock;
 static kmem_cache_t		*nfsidmap_cache;
-static uint_t			pkp_tab[NFSID_CACHE_ANCHORS];
 static int			nfs4_idcache_tout;
 
 /*
@@ -147,31 +146,11 @@
 #define		_CACHE_TOUT		(60*60)		/* secs in 1 hour */
 #define		TIMEOUT(x)		(gethrestime_sec() > \
 					((x) + nfs4_idcache_tout))
-
 /*
  * Max length of valid id string including the trailing null
  */
 #define		_MAXIDSTRLEN		11
 
-/*
- * Pearson's string hash
- *
- * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
- * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
- */
-#define		PS_HASH(msg, hash, len)					\
-{                                                                       \
-	uint_t		key = 0x12345678;	/* arbitrary value */	\
-	int		i;						\
-                                                                        \
-	(hash) = MOD2((key + (len)), NFSID_CACHE_ANCHORS);		\
-                                                                        \
-	for (i = 0; i < (len); i++) {					\
-		(hash) = MOD2(((hash) + (msg)[i]), NFSID_CACHE_ANCHORS); \
-		(hash) = pkp_tab[(hash)];				\
-	}                                                               \
-}
-
 #define		ID_HASH(id, hash)					\
 {									\
 	(hash) = MOD2(((id) ^ NFSID_CACHE_ANCHORS), NFSID_CACHE_ANCHORS); \
@@ -185,7 +164,6 @@
 static void	 nfs_idmap_fini_zone(zoneid_t, void *);
 
 static int	 is_stringified_id(utf8string *);
-static void	 init_pkp_tab(void);
 static void	 nfs_idmap_i2s_literal(uid_t, utf8string *);
 static int	 nfs_idmap_s2i_literal(utf8string *, uid_t *, int);
 static void	 nfs_idmap_reclaim(void *);
@@ -215,10 +193,6 @@
 nfs_idmap_init(void)
 {
 	/*
-	 * Initialize Pearson's Table
-	 */
-	init_pkp_tab();
-	/*
 	 * Initialize the kmem cache
 	 */
 	nfsidmap_cache = kmem_cache_create("NFS_idmap_cache",
@@ -1303,7 +1277,6 @@
 	nfsidmap_t	*p;
 	nfsidmap_t	*pnext;
 	nfsidhq_t	*hq;
-	uint_t		 hash;
 	char		*rqst_c_str;
 	uint_t		 rqst_len;
 	uint_t		 found_stat = 0;
@@ -1318,9 +1291,8 @@
 	/*
 	 * Compute hash queue
 	 */
-	PS_HASH(rqst_c_str, hash, rqst_len - 1);
-	*hashno = hash;
-	hq = &cip->table[hash];
+	*hashno = pkp_tab_hash(rqst_c_str, rqst_len - 1);
+	hq = &cip->table[*hashno];
 
 	/*
 	 * Look for the entry in the HQ
@@ -1407,7 +1379,7 @@
 
 		case HQ_HASH_FIND:
 		default:
-			PS_HASH(c_str, hashno, c_len - 1);
+			hashno = pkp_tab_hash(c_str, c_len - 1);
 			break;
 	}
 	hq = &cip->table[hashno];
@@ -1704,33 +1676,6 @@
 
 /* -- Utility functions -- */
 
-/*
- * Initialize table in pseudo-random fashion
- * for use in Pearson's string hash algorithm.
- *
- * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
- * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
- */
-static void
-init_pkp_tab(void)
-{
-	int		i;
-	int		j;
-	int		k = 7;
-	uint_t		s;
-
-	for (i = 0; i < NFSID_CACHE_ANCHORS; i++)
-		pkp_tab[i] = i;
-
-	for (j = 0; j < 4; j++)
-		for (i = 0; i < NFSID_CACHE_ANCHORS; i++) {
-			s = pkp_tab[i];
-			k = MOD2((k + s), NFSID_CACHE_ANCHORS);
-			pkp_tab[i] = pkp_tab[k];
-			pkp_tab[k] = s;
-		}
-}
-
 char *
 utf8_strchr(utf8string *u8s, const char c)
 {
--- a/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/fs/nfs/nfs4_srv_ns.c	Wed Jun 23 15:54:15 2010 +0200
@@ -137,38 +137,21 @@
  * a) its export root is VROOT
  * b) a descendant of the export root is shared
  */
-int
-pseudo_exportfs(vnode_t *vp, struct exp_visible *vis_head,
-	    struct exportdata *exdata, struct exportinfo **exi_retp)
+struct exportinfo *
+pseudo_exportfs(vnode_t *vp, fid_t *fid, struct exp_visible *vis_head,
+	    struct exportdata *exdata)
 {
 	struct exportinfo *exi;
 	struct exportdata *kex;
-	fid_t fid;
 	fsid_t fsid;
-	int error, vpathlen;
+	int vpathlen;
 
 	ASSERT(RW_WRITE_HELD(&exported_lock));
 
-	/*
-	 * Get the vfs id
-	 */
-	bzero(&fid, sizeof (fid));
-	fid.fid_len = MAXFIDSZ;
-	error = vop_fid_pseudo(vp, &fid);
-	if (error) {
-		/*
-		 * If VOP_FID returns ENOSPC then the fid supplied
-		 * is too small.  For now we simply return EREMOTE.
-		 */
-		if (error == ENOSPC)
-			error = EREMOTE;
-		return (error);
-	}
-
 	fsid = vp->v_vfsp->vfs_fsid;
 	exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
 	exi->exi_fsid = fsid;
-	exi->exi_fid = fid;
+	exi->exi_fid = *fid;
 	exi->exi_vp = vp;
 	VN_HOLD(exi->exi_vp);
 	exi->exi_visible = vis_head;
@@ -212,14 +195,7 @@
 	 */
 	export_link(exi);
 
-	/*
-	 * If exi_retp is non-NULL return a pointer to the new
-	 * exportinfo structure.
-	 */
-	if (exi_retp)
-		*exi_retp = exi;
-
-	return (0);
+	return (exi);
 }
 
 /*
@@ -339,7 +315,7 @@
  * share  /x/y/a/b
  *
  * When more_visible() is called during the second share,
- * the existing namespace is folowing:
+ * the existing namespace is following:
  *                                   exp_visible_t
  *   treenode_t       exportinfo_t      v0     v1
  * ns_root+---+        +------------+  +---+  +---+
@@ -381,7 +357,7 @@
  * - add t4, t5, t6 as a child of t1 (t4 will become sibling of t2)
  * - add v3 to the end of E0->exi_visible
  *
- * Note that v4 and v5 were already proccesed in pseudo_exportfs() and
+ * Note that v4 and v5 were already processed in pseudo_exportfs() and
  * added to E2. The outer loop of more_visible() will loop only over v2
  * and v3. The inner loop of more_visible() always loops over v0 and v1.
  *
@@ -681,10 +657,8 @@
 				 * this as a pseudo export so that an NFS v4
 				 * client can do lookups in it.
 				 */
-				error = pseudo_exportfs(vp, vis_head, NULL,
-				    &new_exi);
-				if (error)
-					break;
+				new_exi = pseudo_exportfs(vp, &fid, vis_head,
+				    NULL);
 				vis_head = NULL;
 			}
 
@@ -765,9 +739,8 @@
 	/*
 	 * We can have set error due to error in:
 	 * 1. vop_fid_pseudo()
-	 * 2. pseudo_exportfs() which can fail only in vop_fid_pseudo()
-	 * 3. VOP_GETATTR()
-	 * 4. VOP_LOOKUP()
+	 * 2. VOP_GETATTR()
+	 * 3. VOP_LOOKUP()
 	 * We must free pseudo exportinfos, visibles and treenodes.
 	 * Visibles are referenced from treenode_t::tree_vis and
 	 * exportinfo_t::exi_visible. To avoid double freeing, only
@@ -787,8 +760,7 @@
 			exportinfo_t *e  = tree_head->tree_exi;
 			/* exip will be freed in exportfs() */
 			if (e && e != exip) {
-				(void) export_unlink(&e->exi_fsid, &e->exi_fid,
-				    e->exi_vp, NULL);
+				export_unlink(e);
 				exi_rele(e);
 			}
 			tree_head = tree_head->tree_child_first;
@@ -814,7 +786,6 @@
 void
 treeclimb_unexport(struct exportinfo *exip)
 {
-	struct exportinfo *exi;
 	treenode_t *tnode, *old_nd;
 
 	ASSERT(RW_WRITE_HELD(&exported_lock));
@@ -839,17 +810,13 @@
 		/* Release pseudo export if it has no child */
 		if (TREE_ROOT(tnode) && !TREE_EXPORTED(tnode) &&
 		    tnode->tree_child_first == 0) {
-			exi = tnode->tree_exi;
-			(void) export_unlink(&exi->exi_fsid, &exi->exi_fid,
-			    exi->exi_vp, NULL);
+			export_unlink(tnode->tree_exi);
 			exi_rele(tnode->tree_exi);
 		}
 
 		/* Release visible in parent's exportinfo */
-		if (tnode->tree_vis) {
-			exi = vis2exi(tnode);
-			less_visible(exi, tnode->tree_vis);
-		}
+		if (tnode->tree_vis)
+			less_visible(vis2exi(tnode), tnode->tree_vis);
 
 		/* Continue with parent */
 		old_nd = tnode;
--- a/usr/src/uts/common/fs/nfs/nfs_auth.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/fs/nfs/nfs_auth.c	Wed Jun 23 15:54:15 2010 +0200
@@ -1145,7 +1145,7 @@
 	rw_enter(&exported_lock, RW_READER);
 
 	for (i = 0; i < EXPTABLESIZE; i++) {
-		for (exi = exptable[i]; exi; exi = exi->exi_hash) {
+		for (exi = exptable[i]; exi; exi = exi->fid_hash.next) {
 			exi_cache_trim(exi);
 		}
 	}
--- a/usr/src/uts/common/fs/nfs/nfs_export.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/fs/nfs/nfs_export.c	Wed Jun 23 15:54:15 2010 +0200
@@ -62,12 +62,14 @@
 #include <nfs/nfs_log.h>
 #include <nfs/lm.h>
 #include <sys/sunddi.h>
+#include <sys/pkp_hash.h>
 
 treenode_t *ns_root;
 
+struct exportinfo *exptable_path_hash[PKP_HASH_SIZE];
 struct exportinfo *exptable[EXPTABLESIZE];
 
-static int	unexport(fsid_t *, fid_t *, vnode_t *);
+static int	unexport(exportinfo_t *);
 static void	exportfree(exportinfo_t *);
 static int	loadindex(exportdata_t *);
 
@@ -112,61 +114,36 @@
 
 #define	exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1))
 
+static uint8_t
+xor_hash(uint8_t *data, int len)
+{
+	uint8_t h = 0;
+
+	while (len--)
+		h ^= *data++;
+
+	return (h);
+}
+
 /*
- * File handle hash function, good for producing hash values 16 bits wide.
+ * File handle hash function, XOR over all bytes in fsid and fid.
  */
-int
+static unsigned
 nfs_fhhash(fsid_t *fsid, fid_t *fid)
 {
-	short *data;
-	int i, len;
-	short h;
-
-	ASSERT(fid != NULL);
-
-	data = (short *)fid->fid_data;
-
-	/* fid_data must be aligned on a short */
-	ASSERT((((uintptr_t)data) & (sizeof (short) - 1)) == 0);
+	int len;
+	uint8_t h;
 
-	if (fid->fid_len == 10) {
-		/*
-		 * probably ufs: hash on bytes 4,5 and 8,9
-		 */
-		return (fsid->val[0] ^ data[2] ^ data[4]);
-	}
-
-	if (fid->fid_len == 6) {
-		/*
-		 * probably hsfs: hash on bytes 0,1 and 4,5
-		 */
-		return ((fsid->val[0] ^ data[0] ^ data[2]));
-	}
-
-	/*
-	 * Some other file system. Assume that every byte is
-	 * worth hashing.
-	 */
-	h = (short)fsid->val[0];
+	h = xor_hash((uint8_t *)fsid, sizeof (fsid_t));
 
 	/*
 	 * Sanity check the length before using it
 	 * blindly in case the client trashed it.
 	 */
-	if (fid->fid_len > NFS_FHMAXDATA)
-		len = 0;
-	else
-		len = fid->fid_len / sizeof (short);
+	len = fid->fid_len > NFS_FH4MAXDATA ? 0 : fid->fid_len;
+	h ^= xor_hash((uint8_t *)fid->fid_data, len);
 
-	/*
-	 * This will ignore one byte if len is not a multiple of
-	 * of sizeof (short). No big deal since we at least get some
-	 * variation with fsid->val[0];
-	 */
-	for (i = 0; i < len; i++)
-		h ^= data[i];
-
-	return ((int)h);
+	return ((unsigned)h);
 }
 
 /*
@@ -189,7 +166,6 @@
 		    sizeof (rpc_gss_OID_desc));
 		secp->s_secinfo.sc_gss_mech_type = NULL;
 	}
-
 }
 
 /*
@@ -783,13 +759,35 @@
 	}
 }
 
-void
-export_link(exportinfo_t *exi) {
-	int exporthash;
+/* hash_name is a text substitution for either fid_hash or path_hash */
+#define	exp_hash_unlink(exi, hash_name) \
+	if (*(exi)->hash_name.bckt == (exi)) \
+		*(exi)->hash_name.bckt = (exi)->hash_name.next; \
+	if ((exi)->hash_name.prev) \
+		(exi)->hash_name.prev->hash_name.next = (exi)->hash_name.next; \
+	if ((exi)->hash_name.next) \
+		(exi)->hash_name.next->hash_name.prev = (exi)->hash_name.prev; \
+	(exi)->hash_name.bckt = NULL;
 
-	exporthash = exptablehash(&exi->exi_fsid, &exi->exi_fid);
-	exi->exi_hash = exptable[exporthash];
-	exptable[exporthash] = exi;
+#define	exp_hash_link(exi, hash_name, bucket) \
+	(exi)->hash_name.bckt = (bucket); \
+	(exi)->hash_name.prev = NULL; \
+	(exi)->hash_name.next = *(bucket); \
+	if ((exi)->hash_name.next) \
+		(exi)->hash_name.next->hash_name.prev = (exi); \
+	*(bucket) = (exi);
+
+void
+export_link(exportinfo_t *exi)
+{
+	exportinfo_t **bckt;
+
+	bckt = &exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)];
+	exp_hash_link(exi, fid_hash, bckt);
+
+	bckt = &exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path,
+	    strlen(exi->exi_export.ex_path))];
+	exp_hash_link(exi, path_hash, bckt);
 }
 
 /*
@@ -948,7 +946,7 @@
 					}
 				}
 			}
-			exi = exi->exi_hash;
+			exi = exi->fid_hash.next;
 		}
 	}
 done:
@@ -998,7 +996,7 @@
 	vnode_t *dvp;
 	struct exportdata *kex;
 	struct exportinfo *exi = NULL;
-	struct exportinfo *ex, *prev;
+	struct exportinfo *ex, *ex1, *ex2;
 	fid_t fid;
 	fsid_t fsid;
 	int error;
@@ -1018,9 +1016,39 @@
 	struct secinfo oldsec[MAX_FLAVORS];
 	int oldcnt;
 	int i;
+	struct pathname lookpn;
 
 	STRUCT_SET_HANDLE(uap, model, args);
 
+	/* Read in pathname from userspace */
+	if (error = pn_get(STRUCT_FGETP(uap, dname), UIO_USERSPACE, &lookpn))
+		return (error);
+
+	/* Walk the export list looking for that pathname */
+	rw_enter(&exported_lock, RW_READER);
+	DTRACE_PROBE(nfss__i__exported_lock1_start);
+	for (ex1 = exptable_path_hash[pkp_tab_hash(lookpn.pn_path,
+	    strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) {
+		if (ex1 != exi_root && 0 ==
+		    strcmp(ex1->exi_export.ex_path, lookpn.pn_path)) {
+			exi_hold(ex1);
+			break;
+		}
+	}
+	DTRACE_PROBE(nfss__i__exported_lock1_stop);
+	rw_exit(&exported_lock);
+
+	/* Is this an unshare? */
+	if (STRUCT_FGETP(uap, uex) == NULL) {
+		pn_free(&lookpn);
+		if (ex1 == NULL)
+			return (EINVAL);
+		error = unexport(ex1);
+		exi_rele(ex1);
+		return (error);
+	}
+
+	/* It is a share or a re-share */
 	error = lookupname(STRUCT_FGETP(uap, dname), UIO_USERSPACE,
 	    FOLLOW, &dvp, &vp);
 	if (error == EINVAL) {
@@ -1034,87 +1062,18 @@
 		dvp = NULL;
 	}
 	if (!error && vp == NULL) {
-		/*
-		 * Last component of fname not found
-		 */
-		if (dvp != NULL) {
+		/* Last component of fname not found */
+		if (dvp != NULL)
 			VN_RELE(dvp);
-		}
 		error = ENOENT;
 	}
-
 	if (error) {
-		/*
-		 * If this is a request to unexport, indicated by the
-		 * uex pointer being NULL, it is possible that the
-		 * directory has already been removed or shared filesystem
-		 * could have been forcibly unmounted. In which case
-		 * we scan the export list which records the pathname
-		 * originally exported.
-		 */
-		if (STRUCT_FGETP(uap, uex) == NULL) {
-			char namebuf[TYPICALMAXPATHLEN];
-			struct pathname lookpn;
-			int i;
-
-			/* Read in pathname from userspace */
-			error = pn_get_buf(STRUCT_FGETP(uap, dname),
-			    UIO_USERSPACE, &lookpn, namebuf, sizeof (namebuf));
-			if (error == ENAMETOOLONG) {
-				/*
-				 * pathname > TYPICALMAXPATHLEN, use
-				 * pn_get() instead. Remember to
-				 * pn_free() afterwards.
-				 */
-				error = pn_get(STRUCT_FGETP(uap, dname),
-				    UIO_USERSPACE, &lookpn);
-			}
-
-			if (error)
-				return (error);
-
-			/* Walk the export list looking for that pathname */
-			rw_enter(&exported_lock, RW_READER);
-			for (i = 0; i < EXPTABLESIZE; i++) {
-				exi = exptable[i];
-				while (exi) {
-					if (strcmp(exi->exi_export.ex_path,
-					    lookpn.pn_path) == 0) {
-						goto exi_scan_end;
-					}
-					exi = exi->exi_hash;
-				}
-			}
-exi_scan_end:
-			rw_exit(&exported_lock);
-			if (exi) {
-				/* Found a match, use it. */
-				vp = exi->exi_vp;
-				dvp = exi->exi_dvp;
-				DTRACE_PROBE2(nfss__i__nmspc__tree,
-				    char *,
-				    "unsharing removed dir/unmounted fs",
-				    char *, lookpn.pn_path);
-				VN_HOLD(vp);
-				VN_HOLD(dvp);
-				error = 0;
-			} else {
-				/* Still no match, set error */
-				error = ENOENT;
-			}
-			if (lookpn.pn_buf != namebuf) {
-				/*
-				 * We didn't use namebuf, so make
-				 * sure we free the allocated memory
-				 */
-				pn_free(&lookpn);
-			}
-		}
+		pn_free(&lookpn);
+		if (ex1)
+			exi_rele(ex1);
+		return (error);
 	}
 
-	if (error)
-		return (error);
-
 	/*
 	 * 'vp' may be an AUTOFS node, so we perform a
 	 * VOP_ACCESS() to trigger the mount of the
@@ -1135,10 +1094,22 @@
 			VN_RELE(vp);
 			if (dvp != NULL)
 				VN_RELE(dvp);
+			pn_free(&lookpn);
+			if (ex1)
+				exi_rele(ex1);
 			return (error);
 		}
 	}
 
+	/* Do not allow sharing another vnode for already shared path */
+	if (ex1 && !PSEUDO(ex1) && !VN_CMP(ex1->exi_vp, vp)) {
+		pn_free(&lookpn);
+		exi_rele(ex1);
+		return (EEXIST);
+	}
+	if (ex1)
+		exi_rele(ex1);
+
 	/*
 	 * Get the vfs id
 	 */
@@ -1147,13 +1118,7 @@
 	error = VOP_FID(vp, &fid, NULL);
 	fsid = vp->v_vfsp->vfs_fsid;
 
-	/*
-	 * Allow unshare request for forcibly unmounted shared filesystem.
-	 */
-	if (error == EIO && exi) {
-		fid = exi->exi_fid;
-		fsid = exi->exi_fsid;
-	} else if (error) {
+	if (error) {
 		VN_RELE(vp);
 		if (dvp != NULL)
 			VN_RELE(dvp);
@@ -1163,16 +1128,30 @@
 		 */
 		if (error == ENOSPC)
 			error = EREMOTE;
+		pn_free(&lookpn);
 		return (error);
 	}
 
-	if (STRUCT_FGETP(uap, uex) == NULL) {
-		error = unexport(&fsid, &fid, vp);
-		VN_RELE(vp);
-		if (dvp != NULL)
-		VN_RELE(dvp);
-		return (error);
+	/*
+	 * Do not allow re-sharing a shared vnode under a different path
+	 * PSEUDO export has ex_path fabricated, e.g. "/tmp (pseudo)", skip it.
+	 */
+	rw_enter(&exported_lock, RW_READER);
+	DTRACE_PROBE(nfss__i__exported_lock2_start);
+	for (ex2 = exptable[exptablehash(&fsid, &fid)]; ex2;
+	    ex2 = ex2->fid_hash.next) {
+		if (ex2 != exi_root && !PSEUDO(ex2) &&
+		    VN_CMP(ex2->exi_vp, vp) &&
+		    strcmp(ex2->exi_export.ex_path, lookpn.pn_path) != 0) {
+			DTRACE_PROBE(nfss__i__exported_lock2_stop);
+			rw_exit(&exported_lock);
+			pn_free(&lookpn);
+			return (EEXIST);
+		}
 	}
+	DTRACE_PROBE(nfss__i__exported_lock2_stop);
+	rw_exit(&exported_lock);
+	pn_free(&lookpn);
 
 	exi = kmem_zalloc(sizeof (*exi), KM_SLEEP);
 	exi->exi_fsid = fsid;
@@ -1447,6 +1426,7 @@
 	 * Insert the new entry at the front of the export list
 	 */
 	rw_enter(&exported_lock, RW_WRITER);
+	DTRACE_PROBE(nfss__i__exported_lock3_start);
 
 	export_link(exi);
 
@@ -1455,10 +1435,9 @@
 	 * If one is found then unlink it, wait until this is the
 	 * only reference and then free it.
 	 */
-	prev = exi;
-	for (ex = prev->exi_hash; ex != NULL; prev = ex, ex = ex->exi_hash) {
+	for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) {
 		if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) {
-			prev->exi_hash = ex->exi_hash;
+			export_unlink(ex);
 			break;
 		}
 	}
@@ -1553,6 +1532,7 @@
 		ex->exi_visible = NULL;
 	}
 
+	DTRACE_PROBE(nfss__i__exported_lock3_stop);
 	rw_exit(&exported_lock);
 
 	if (exi_public == exi || kex->ex_flags & EX_LOG) {
@@ -1569,7 +1549,8 @@
 
 out7:
 	/* Unlink the new export in exptable. */
-	(void) export_unlink(&exi->exi_fsid, &exi->exi_fid, exi->exi_vp, NULL);
+	export_unlink(exi);
+	DTRACE_PROBE(nfss__i__exported_lock3_stop);
 	rw_exit(&exported_lock);
 out6:
 	if (kex->ex_flags & EX_INDEX)
@@ -1607,71 +1588,34 @@
 /*
  * Remove the exportinfo from the export list
  */
-int
-export_unlink(fsid_t *fsid, fid_t *fid, vnode_t *vp, struct exportinfo **exip)
+void
+export_unlink(struct exportinfo *exi)
 {
-	struct exportinfo **tail;
-
 	ASSERT(RW_WRITE_HELD(&exported_lock));
 
-	tail = &exptable[exptablehash(fsid, fid)];
-	while (*tail != NULL) {
-		if (exportmatch(*tail, fsid, fid)) {
-			/*
-			 * If vp is given, check if vp is the
-			 * same vnode as the exported node.
-			 *
-			 * Since VOP_FID of a lofs node returns the
-			 * fid of its real node (ufs), the exported
-			 * node for lofs and (pseudo) ufs may have
-			 * the same fsid and fid.
-			 */
-			if (vp == NULL || vp == (*tail)->exi_vp) {
-
-				if (exip != NULL)
-					*exip = *tail;
-				*tail = (*tail)->exi_hash;
-
-				return (0);
-			}
-		}
-		tail = &(*tail)->exi_hash;
-	}
-
-	return (EINVAL);
+	exp_hash_unlink(exi, fid_hash);
+	exp_hash_unlink(exi, path_hash);
 }
 
 /*
  * Unexport an exported filesystem
  */
-int
-unexport(fsid_t *fsid, fid_t *fid, vnode_t *vp)
+static int
+unexport(struct exportinfo *exi)
 {
-	struct exportinfo *exi = NULL;
-	int error;
 	struct secinfo cursec[MAX_FLAVORS];
 	int curcnt;
 
 	rw_enter(&exported_lock, RW_WRITER);
 
-	error = export_unlink(fsid, fid, vp, &exi);
-
-	if (error) {
-		rw_exit(&exported_lock);
-		return (error);
-	}
-
-	/* pseudo node is not a real exported filesystem */
-	if (PSEUDO(exi)) {
-		/*
-		 * Put the pseudo node back into the export table
-		 * before erroring out.
-		 */
-		export_link(exi);
+	/* Check if exi is still linked in the export table */
+	if (!EXP_LINKED(exi) || PSEUDO(exi)) {
 		rw_exit(&exported_lock);
 		return (EINVAL);
 	}
 
+	export_unlink(exi);
+
 	/*
 	 * Remove security flavors before treeclimb_unexport() is called
 	 * because srv_secinfo_treeclimb needs the namespace tree
@@ -1688,19 +1632,13 @@
 	if (exi->exi_visible) {
 		struct exportinfo *newexi;
 
-		error = pseudo_exportfs(exi->exi_vp, exi->exi_visible,
-		    &exi->exi_export, &newexi);
-		if (error)
-			goto done;
+		newexi = pseudo_exportfs(exi->exi_vp, &exi->exi_fid,
+		    exi->exi_visible, &exi->exi_export);
+		exi->exi_visible = NULL;
 
-		exi->exi_visible = NULL;
-		/*
-		 * pseudo_exportfs() has allocated new exportinfo,
-		 * update the treenode.
-		 */
+		/* interconnect the existing treenode with the new exportinfo */
 		newexi->exi_tree = exi->exi_tree;
 		newexi->exi_tree->tree_exi = newexi;
-
 	} else {
 		treeclimb_unexport(exi);
 	}
@@ -1736,12 +1674,7 @@
 	}
 
 	exi_rele(exi);
-	return (error);
-
-done:
-	rw_exit(&exported_lock);
-	exi_rele(exi);
-	return (error);
+	return (0);
 }
 
 /*
@@ -2525,7 +2458,7 @@
 	rw_enter(&exported_lock, RW_READER);
 	for (exi = exptable[exptablehash(fsid, fid)];
 	    exi != NULL;
-	    exi = exi->exi_hash) {
+	    exi = exi->fid_hash.next) {
 		if (exportmatch(exi, fsid, fid)) {
 			/*
 			 * If this is the place holder for the
@@ -2563,7 +2496,7 @@
 
 	for (exi = exptable[exptablehash(fsid, fid)];
 	    exi != NULL;
-	    exi = exi->exi_hash) {
+	    exi = exi->fid_hash.next) {
 		if (exportmatch(exi, fsid, fid)) {
 			/*
 			 * If this is the place holder for the
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/fs/pkp_hash.c	Wed Jun 23 15:54:15 2010 +0200
@@ -0,0 +1,70 @@
+/*
+ * 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) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <sys/pkp_hash.h>
+
+/*
+ * Pearson's string hash
+ *
+ * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
+ * http://portal.acm.org/citation.cfm?doid=78973.78978
+ */
+
+#define		MOD2(a, pow_of_2)	((a) & ((pow_of_2) - 1))
+
+static uint_t	pkp_tab[PKP_HASH_SIZE] = {
+1, 87, 49, 12, 176, 178, 102, 166, 121, 193, 6, 84, 249, 230, 44, 163,
+14, 197, 213, 181, 161, 85, 218, 80, 64, 239, 24, 226, 236, 142, 38, 200,
+110, 177, 104, 103, 141, 253, 255, 50, 77, 101, 81, 18, 45, 96, 31, 222,
+25, 107, 190, 70, 86, 237, 240, 34, 72, 242, 20, 214, 244, 227, 149, 235,
+97, 234, 57, 22, 60, 250, 82, 175, 208, 5, 127, 199, 111, 62, 135, 248,
+174, 169, 211, 58, 66, 154, 106, 195, 245, 171, 17, 187, 182, 179, 0, 243,
+132, 56, 148, 75, 128, 133, 158, 100, 130, 126, 91, 13, 153, 246, 216, 219,
+119, 68, 223, 78, 83, 88, 201, 99, 122, 11, 92, 32, 136, 114, 52, 10,
+138, 30, 48, 183, 156, 35, 61, 26, 143, 74, 251, 94, 129, 162, 63, 152,
+170, 7, 115, 167, 241, 206, 3, 150, 55, 59, 151, 220, 90, 53, 23, 131,
+125, 173, 15, 238, 79, 95, 89, 16, 105, 137, 225, 224, 217, 160, 37, 123,
+118, 73, 2, 157, 46, 116, 9, 145, 134, 228, 207, 212, 202, 215, 69, 229,
+27, 188, 67, 124, 168, 252, 42, 4, 29, 108, 21, 247, 19, 205, 39, 203,
+233, 40, 186, 147, 198, 192, 155, 33, 164, 191, 98, 204, 165, 180, 117, 76,
+140, 36, 210, 172, 41, 54, 159, 8, 185, 232, 113, 196, 231, 47, 146, 120,
+51, 65, 28, 144, 254, 221, 93, 189, 194, 139, 112, 43, 71, 109, 184, 209
+};
+
+uint_t
+pkp_tab_hash(char *str, int len)
+{
+	uint_t	key = 0x12345678;	/* arbitrary value */
+	uint_t	hash;
+	int	i;
+
+	hash = MOD2((key + len), PKP_HASH_SIZE);
+
+	for (i = 0; i < len; i++) {
+		hash = MOD2((hash + str[i]), PKP_HASH_SIZE);
+		hash = pkp_tab[hash];
+	}
+
+	return (hash);
+}
--- a/usr/src/uts/common/fs/sharefs/sharetab.c	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/fs/sharefs/sharetab.c	Wed Jun 23 15:54:15 2010 +0200
@@ -20,12 +20,9 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/types32.h>
 #include <sys/param.h>
@@ -86,36 +83,6 @@
 
 uint_t		sharetab_generation;	/* Only increments and wraps! */
 
-static uint_t	pkp_tab[SHARETAB_HASHES];
-
-/*
- * Initialize table in pseudo-random fashion
- * for use in Pearson's string hash algorithm.
- *
- * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
- * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
- */
-static void
-init_pkp_tab(void)
-{
-	int	i;
-	int	j;
-	int	k = 7;
-	uint_t	s;
-
-	for (i = 0; i < SHARETAB_HASHES; i++)
-		pkp_tab[i] = i;
-
-	for (j = 0; j < 4; j++) {
-		for (i = 0; i < SHARETAB_HASHES; i++) {
-			s = pkp_tab[i];
-			k = MOD2((k + s), SHARETAB_HASHES);
-			pkp_tab[i] = pkp_tab[k];
-			pkp_tab[k] = s;
-		}
-	}
-}
-
 /*
  * Take care of cleaning up a share.
  * If passed in a length array, use it to determine how much
@@ -168,7 +135,7 @@
 	}
 
 	iPath = shl ? shl->shl_path : strlen(sh->sh_path);
-	SHARETAB_HASH_IT(iHash, sh->sh_path);
+	iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 
 	/*
 	 * Now walk down the hash table and find the entry to free!
@@ -263,7 +230,7 @@
 	/*
 	 * Now we need to find where we have to add the entry.
 	 */
-	SHARETAB_HASH_IT(iHash, sh->sh_path);
+	iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 
 	iPath = shl ? shl->shl_path : strlen(sh->sh_path);
 
@@ -347,8 +314,6 @@
 void
 sharefs_sharetab_init(void)
 {
-	init_pkp_tab();
-
 	rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL);
 	rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL);
 
--- a/usr/src/uts/common/nfs/export.h	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/nfs/export.h	Wed Jun 23 15:54:15 2010 +0200
@@ -419,7 +419,13 @@
 /* Root of nfs pseudo namespace */
 extern treenode_t *ns_root;
 
-#define	EXPTABLESIZE	16
+#define	EXPTABLESIZE   256
+
+struct exp_hash {
+	struct exportinfo	*prev;  /* ptr to the previous exportinfo */
+	struct exportinfo	*next;  /* ptr to the next exportinfo */
+	struct exportinfo	**bckt; /* backpointer to the hash bucket */
+};
 
 /*
  * A node associated with an export entry on the
@@ -441,7 +447,8 @@
 	struct exportdata	exi_export;
 	fsid_t			exi_fsid;
 	struct fid		exi_fid;
-	struct exportinfo	*exi_hash;
+	struct exp_hash		fid_hash;
+	struct exp_hash		path_hash;
 	struct treenode		*exi_tree;
 	fhandle_t		exi_fh;
 	krwlock_t		exi_cache_lock;
@@ -505,6 +512,7 @@
 typedef struct exp_visible exp_visible_t;
 
 #define	PSEUDO(exi)	((exi)->exi_export.ex_flags & EX_PSEUDO)
+#define	EXP_LINKED(exi)	((exi)->fid_hash.bckt != NULL)
 
 #define	EQFSID(fsidp1, fsidp2)	\
 	(((fsidp1)->val[0] == (fsidp2)->val[0]) && \
@@ -533,7 +541,6 @@
 				struct svc_req *);
 extern int	nfsauth4_secinfo_access(struct exportinfo *,
 				struct svc_req *, int, int);
-extern int	nfs_fhhash(fsid_t *, fid_t *);
 extern int	nfs_fhbcmp(char *, char *, int);
 extern int	nfs_exportinit(void);
 extern void	nfs_exportfini(void);
@@ -555,8 +562,7 @@
 extern int	nfs_check_vpexi(vnode_t *, vnode_t *, cred_t *,
 			struct exportinfo **);
 extern void	export_link(struct exportinfo *);
-extern int	export_unlink(fsid_t *, fid_t *, vnode_t *,
-			struct exportinfo **);
+extern void	export_unlink(struct exportinfo *);
 extern vnode_t *untraverse(vnode_t *);
 extern int	vn_is_nfs_reparse(vnode_t *, cred_t *);
 extern int	client_is_downrev(struct svc_req *);
@@ -573,8 +579,8 @@
 extern int	has_visible(struct exportinfo *, vnode_t *);
 extern void	free_visible(struct exp_visible *);
 extern int	nfs_exported(struct exportinfo *, vnode_t *);
-extern int	pseudo_exportfs(vnode_t *, struct exp_visible *,
-    struct exportdata *, struct exportinfo **);
+extern struct exportinfo *pseudo_exportfs(vnode_t *, fid_t *,
+    struct exp_visible *, struct exportdata *);
 extern int	vop_fid_pseudo(vnode_t *, fid_t *fidp);
 extern int	nfs4_vget_pseudo(struct exportinfo *, vnode_t **, fid_t *);
 /*
--- a/usr/src/uts/common/nfs/nfs4_idmap_impl.h	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/nfs/nfs4_idmap_impl.h	Wed Jun 23 15:54:15 2010 +0200
@@ -2,9 +2,8 @@
  * 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.
+ * 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.
@@ -20,17 +19,15 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _NFS4_IDMAP_IMPL_H
 #define	_NFS4_IDMAP_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/list.h>
 #include <sys/door.h>
+#include <sys/pkp_hash.h>
 
 /*
  * This is a private header file.  Applications should not directly include
@@ -44,7 +41,7 @@
 /*
  * Cache Entry Definitions
  */
-#define	NFSID_CACHE_ANCHORS	256
+#define	NFSID_CACHE_ANCHORS	PKP_HASH_SIZE
 
 typedef struct nfsidmap {
 	struct nfsidmap *id_chain[2];	/* must be first */
--- a/usr/src/uts/common/sharefs/sharetab.h	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/sharefs/sharetab.h	Wed Jun 23 15:54:15 2010 +0200
@@ -18,16 +18,14 @@
  *
  * CDDL HEADER END
  */
-
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _SHAREFS_SHARETAB_H
 #define	_SHAREFS_SHARETAB_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#include <sys/pkp_hash.h>
 
 /*
  * This header defines the glue to keeping a sharetab in memory.
@@ -54,7 +52,7 @@
 	uint_t		ssh_count;
 } sharefs_hash_head_t;
 
-#define	SHARETAB_HASHES		256
+#define	SHARETAB_HASHES		PKP_HASH_SIZE
 
 typedef struct sharetab {
 	sharefs_hash_head_t	s_buckets[SHARETAB_HASHES];
@@ -63,29 +61,6 @@
 	uint_t			s_count;
 } sharetab_t;
 
-#define	MOD2(a, pow_of_2)	(a) & ((pow_of_2) - 1)
-
-/*
- * Pearson's string hash
- *
- * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
- * http://www.acm.org/pubs/citations/journals/cacm/1990-33-6/p677-pearson
- */
-#define	SHARETAB_HASH_IT(hash, path)					\
-{									\
-	uint_t		key = 0x12345678;	/* arbitrary value */	\
-	int		i, len;						\
-									\
-	len = strlen((path));						\
-									\
-	(hash) = MOD2((key + len), SHARETAB_HASHES);			\
-									\
-	for (i = 0; i < len; i++) {					\
-		(hash) = MOD2(((hash) + (path)[i]), SHARETAB_HASHES);	\
-		(hash) = pkp_tab[(hash)];				\
-	}								\
-}
-
 #ifdef __cplusplus
 }
 #endif
--- a/usr/src/uts/common/sys/Makefile	Wed Jun 23 10:37:46 2010 +0100
+++ b/usr/src/uts/common/sys/Makefile	Wed Jun 23 15:54:15 2010 +0200
@@ -431,6 +431,7 @@
 	pg.h			\
 	pghw.h			\
 	physmem.h		\
+	pkp_hash.h		\
 	pm.h			\
 	policy.h		\
 	poll.h			\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/pkp_hash.h	Wed Jun 23 15:54:15 2010 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#ifndef	_PKP_HASH_H_
+#define	_PKP_HASH_H_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Pearson's string hash
+ *
+ * See: Communications of the ACM, June 1990 Vol 33 pp 677-680
+ * http://portal.acm.org/citation.cfm?doid=78973.78978
+ */
+#define	PKP_HASH_SIZE		256
+
+extern uint_t pkp_tab_hash(char *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKP_HASH_H_ */