Print this page
NEX-19996 exi_id_get_next() calls should be WRITER locked
NEX-20014 NFS v4 state lock mutex exited before entered (on error path)
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-9275 Got "bad mutex" panic when run IO to nfs share from clients
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
NEX-6778 NFS kstats leak and cause system to hang
Revert "NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats"
This reverts commit 586c3ab1927647487f01c337ddc011c642575a52.
Revert "NEX-5354 Aggregated IOPS, bandwidth, and latency kstats for NFS server"
This reverts commit c91d7614da8618ef48018102b077f60ecbbac8c2.
Revert "NEX-5667 nfssrv_stats_flags does not work for aggregated kstats"
This reverts commit 3dcf42618be7dd5f408c327f429c81e07ca08e74.
Revert "NEX-5750 Time values for aggregated NFS server kstats should be normalized"
This reverts commit 1f4d4f901153b0191027969fa4a8064f9d3b9ee1.
Revert "NEX-5942 Panic in rfs4_minorvers_mismatch() with NFSv4.1 client"
This reverts commit 40766417094a162f5e4cc8786c0fa0a7e5871cd9.
Revert "NEX-5752 NFS server: namespace collision in kstats"
This reverts commit ae81e668db86050da8e483264acb0cce0444a132.
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-4261 Per-client NFS server IOPS, bandwidth, and latency kstats
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-3097 IOPS, bandwidth, and latency kstats for NFS server
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
NEX-2345 nfsauth_cache_get() could spend a lot of time walking exi_cache
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>

*** 18,36 **** * * CDDL HEADER END */ /* - * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. * All rights reserved. */ #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> #include <sys/vfs.h> --- 18,38 ---- * * CDDL HEADER END */ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. * All rights reserved. */ + /* + * Copyright 2018 Nexenta Systems, Inc. + */ #include <sys/types.h> #include <sys/param.h> #include <sys/time.h> #include <sys/vfs.h>
*** 63,80 **** #include <nfs/nfs_clnt.h> #include <nfs/nfs_acl.h> #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(exportinfo_t *); static void exportfree(exportinfo_t *); static int loadindex(exportdata_t *); extern void nfsauth_cache_free(exportinfo_t *); extern int sec_svc_loadrootnames(int, int, caddr_t **, model_t); --- 65,95 ---- #include <nfs/nfs_clnt.h> #include <nfs/nfs_acl.h> #include <nfs/nfs_log.h> #include <nfs/lm.h> #include <sys/sunddi.h> ! static zone_key_t nfs_export_key; ! /* ! * exi_id support ! * ! * exi_id_next The next exi_id available. ! * exi_id_overflow The exi_id_next already overflowed, so we should ! * thoroughly check for duplicates. ! * exi_id_tree AVL tree indexed by exi_id. ! * nfs_exi_id_lock Lock to protect the export ID list ! * ! * All exi_id_next, exi_id_overflow, and exi_id_tree are protected by ! * nfs_exi_id_lock. ! */ ! static int exi_id_next; ! static bool_t exi_id_overflow; ! avl_tree_t exi_id_tree; ! kmutex_t nfs_exi_id_lock; ! static int unexport(nfs_export_t *, exportinfo_t *); static void exportfree(exportinfo_t *); static int loadindex(exportdata_t *); extern void nfsauth_cache_free(exportinfo_t *); extern int sec_svc_loadrootnames(int, int, caddr_t **, model_t);
*** 81,112 **** extern void sec_svc_freerootnames(int, int, caddr_t *); static int build_seclist_nodups(exportdata_t *, secinfo_t *, int); static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int); static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int); ! static void srv_secinfo_treeclimb(exportinfo_t *, secinfo_t *, int, bool_t); #ifdef VOLATILE_FH_TEST static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *); static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *); static void free_volrnm_list(exportinfo_t *); #endif /* VOLATILE_FH_TEST */ - /* - * exported_lock Read/Write lock that protects the exportinfo list. - * This lock must be held when searching or modifiying - * the exportinfo list. - */ - krwlock_t exported_lock; - - /* - * "public" and default (root) location for public filehandle - */ - struct exportinfo *exi_public, *exi_root; - - fid_t exi_rootfid; /* for checking the default public file handle */ - fhandle_t nullfh2; /* for comparing V2 filehandles */ /* * macro for static dtrace probes to trace server namespace ref count mods. */ --- 96,114 ---- extern void sec_svc_freerootnames(int, int, caddr_t *); static int build_seclist_nodups(exportdata_t *, secinfo_t *, int); static void srv_secinfo_add(secinfo_t **, int *, secinfo_t *, int, int); static void srv_secinfo_remove(secinfo_t **, int *, secinfo_t *, int); ! static void srv_secinfo_treeclimb(nfs_export_t *, exportinfo_t *, ! secinfo_t *, int, bool_t); #ifdef VOLATILE_FH_TEST static struct ex_vol_rename *find_volrnm_fh(exportinfo_t *, nfs_fh4 *); static uint32_t find_volrnm_fh_id(exportinfo_t *, nfs_fh4 *); static void free_volrnm_list(exportinfo_t *); #endif /* VOLATILE_FH_TEST */ fhandle_t nullfh2; /* for comparing V2 filehandles */ /* * macro for static dtrace probes to trace server namespace ref count mods. */
*** 115,124 **** --- 117,132 ---- char *, (tag), int, (int)(flav), int, (int)(aftcnt)) #define exptablehash(fsid, fid) (nfs_fhhash((fsid), (fid)) & (EXPTABLESIZE - 1)) + extern nfs_export_t * + nfs_get_export(void) + { + return (zone_getspecific(nfs_export_key, curzone)); + } + static uint8_t xor_hash(uint8_t *data, int len) { uint8_t h = 0;
*** 701,716 **** * For NFS V4. * Add or remove the newly exported or unexported security flavors of the * given exportinfo from its ancestors upto the system root. */ void ! srv_secinfo_treeclimb(exportinfo_t *exip, secinfo_t *sec, int seccnt, ! bool_t isadd) { treenode_t *tnode = exip->exi_tree; ! ASSERT(RW_WRITE_HELD(&exported_lock)); ASSERT(tnode != NULL); if (seccnt == 0) return; --- 709,724 ---- * For NFS V4. * Add or remove the newly exported or unexported security flavors of the * given exportinfo from its ancestors upto the system root. */ void ! srv_secinfo_treeclimb(nfs_export_t *ne, exportinfo_t *exip, secinfo_t *sec, ! int seccnt, bool_t isadd) { treenode_t *tnode = exip->exi_tree; ! ASSERT(RW_WRITE_HELD(&ne->exported_lock)); ASSERT(tnode != NULL); if (seccnt == 0) return;
*** 780,894 **** 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); } /* ! * Initialization routine for export routines. Should only be called once. */ int ! nfs_exportinit(void) { ! int error; int i; ! rw_init(&exported_lock, NULL, RW_DEFAULT, NULL); /* * Allocate the place holder for the public file handle, which * is all zeroes. It is initially set to the root filesystem. */ ! exi_root = kmem_zalloc(sizeof (*exi_root), KM_SLEEP); ! exi_public = exi_root; ! exi_root->exi_export.ex_flags = EX_PUBLIC; ! exi_root->exi_export.ex_pathlen = 1; /* length of "/" */ ! exi_root->exi_export.ex_path = ! kmem_alloc(exi_root->exi_export.ex_pathlen + 1, KM_SLEEP); ! exi_root->exi_export.ex_path[0] = '/'; ! exi_root->exi_export.ex_path[1] = '\0'; ! exi_root->exi_count = 1; ! mutex_init(&exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL); ! exi_root->exi_vp = rootdir; ! exi_rootfid.fid_len = MAXFIDSZ; ! error = vop_fid_pseudo(exi_root->exi_vp, &exi_rootfid); ! if (error) { ! mutex_destroy(&exi_root->exi_lock); ! kmem_free(exi_root, sizeof (*exi_root)); ! return (error); } ! /* ! * Initialize auth cache and auth cache lock ! */ for (i = 0; i < AUTH_TABLESIZE; i++) { ! exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP); ! avl_create(exi_root->exi_cache[i], nfsauth_cache_clnt_compar, ! sizeof (struct auth_cache_clnt), offsetof(struct auth_cache_clnt, authc_link)); } ! rw_init(&exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL); ! /* setup the fhandle template */ ! exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid; ! exi_root->exi_fh.fh_xlen = exi_rootfid.fid_len; ! bcopy(exi_rootfid.fid_data, exi_root->exi_fh.fh_xdata, ! exi_rootfid.fid_len); ! exi_root->exi_fh.fh_len = sizeof (exi_root->exi_fh.fh_data); ! /* ! * Publish the exportinfo in the hash table ! */ ! export_link(exi_root); ! nfslog_init(); ! ns_root = NULL; ! return (0); } ! /* ! * Finalization routine for export routines. Called to cleanup previously ! * initialization work when the NFS server module could not be loaded correctly. ! */ ! void ! nfs_exportfini(void) { int i; ! /* ! * Deallocate the place holder for the public file handle. ! */ ! srv_secinfo_list_free(exi_root->exi_export.ex_secinfo, ! exi_root->exi_export.ex_seccnt); ! mutex_destroy(&exi_root->exi_lock); ! rw_destroy(&exi_root->exi_cache_lock); for (i = 0; i < AUTH_TABLESIZE; i++) { ! avl_destroy(exi_root->exi_cache[i]); ! kmem_free(exi_root->exi_cache[i], sizeof (avl_tree_t)); } - kmem_free(exi_root, sizeof (*exi_root)); ! rw_destroy(&exported_lock); } /* * Check if 2 gss mechanism identifiers are the same. * * return FALSE if not the same. * return TRUE if the same. */ --- 788,1007 ---- if ((exi)->hash_name.next) \ (exi)->hash_name.next->hash_name.prev = (exi); \ *(bucket) = (exi); void ! export_link(nfs_export_t *ne, exportinfo_t *exi) { exportinfo_t **bckt; ! ASSERT(RW_WRITE_HELD(&ne->exported_lock)); ! ! bckt = &ne->exptable[exptablehash(&exi->exi_fsid, &exi->exi_fid)]; exp_hash_link(exi, fid_hash, bckt); ! bckt = &ne->exptable_path_hash[pkp_tab_hash(exi->exi_export.ex_path, strlen(exi->exi_export.ex_path))]; exp_hash_link(exi, path_hash, bckt); } /* ! * Helper functions for exi_id handling */ + static int + exi_id_compar(const void *v1, const void *v2) + { + const struct exportinfo *e1 = v1; + const struct exportinfo *e2 = v2; + + if (e1->exi_id < e2->exi_id) + return (-1); + if (e1->exi_id > e2->exi_id) + return (1); + + return (0); + } + int ! exi_id_get_next() { ! struct exportinfo e; ! int ret = exi_id_next; ! ! ASSERT(MUTEX_HELD(&nfs_exi_id_lock)); ! ! do { ! exi_id_next++; ! if (exi_id_next == 0) ! exi_id_overflow = TRUE; ! ! if (!exi_id_overflow) ! break; ! ! if (exi_id_next == ret) ! cmn_err(CE_PANIC, "exi_id exhausted"); ! ! e.exi_id = exi_id_next; ! } while (avl_find(&exi_id_tree, &e, NULL) != NULL); ! ! return (ret); ! } ! ! /*ARGSUSED*/ ! static void * ! nfs_export_zone_init(zoneid_t zoneid) ! { int i; + nfs_export_t *ne; ! ne = kmem_zalloc(sizeof (*ne), KM_SLEEP); + rw_init(&ne->exported_lock, NULL, RW_DEFAULT, NULL); + /* * Allocate the place holder for the public file handle, which * is all zeroes. It is initially set to the root filesystem. */ ! ne->exi_root = kmem_zalloc(sizeof (*ne->exi_root), KM_SLEEP); ! ne->exi_public = ne->exi_root; ! ne->exi_root->exi_export.ex_flags = EX_PUBLIC; ! ne->exi_root->exi_export.ex_pathlen = 1; /* length of "/" */ ! ne->exi_root->exi_export.ex_path = ! kmem_alloc(ne->exi_root->exi_export.ex_pathlen + 1, KM_SLEEP); ! ne->exi_root->exi_export.ex_path[0] = '/'; ! ne->exi_root->exi_export.ex_path[1] = '\0'; ! ne->exi_root->exi_count = 1; ! mutex_init(&ne->exi_root->exi_lock, NULL, MUTEX_DEFAULT, NULL); ! ne->exi_root->exi_vp = ZONE_ROOTVP(); ! ne->exi_rootfid.fid_len = MAXFIDSZ; ! if (vop_fid_pseudo(ne->exi_root->exi_vp, &ne->exi_rootfid) != 0) { ! mutex_destroy(&ne->exi_root->exi_lock); ! kmem_free(ne->exi_root->exi_export.ex_path, ! ne->exi_root->exi_export.ex_pathlen + 1); ! kmem_free(ne->exi_root, sizeof (*ne->exi_root)); ! return (NULL); } ! /* Initialize auth cache and auth cache lock */ for (i = 0; i < AUTH_TABLESIZE; i++) { ! ne->exi_root->exi_cache[i] = kmem_alloc(sizeof (avl_tree_t), KM_SLEEP); ! avl_create(ne->exi_root->exi_cache[i], ! nfsauth_cache_clnt_compar, sizeof (struct auth_cache_clnt), offsetof(struct auth_cache_clnt, authc_link)); } ! rw_init(&ne->exi_root->exi_cache_lock, NULL, RW_DEFAULT, NULL); ! /* Setup the fhandle template */ ! ne->exi_root->exi_fh.fh_fsid = rootdir->v_vfsp->vfs_fsid; ! ne->exi_root->exi_fh.fh_xlen = ne->exi_rootfid.fid_len; ! bcopy(ne->exi_rootfid.fid_data, ne->exi_root->exi_fh.fh_xdata, ! ne->exi_rootfid.fid_len); ! ne->exi_root->exi_fh.fh_len = sizeof (ne->exi_root->exi_fh.fh_data); ! rw_enter(&ne->exported_lock, RW_WRITER); ! /* Publish the exportinfo in the hash table */ ! export_link(ne, ne->exi_root); ! /* Initialize exi_id and exi_kstats */ ! mutex_enter(&nfs_exi_id_lock); ! ne->exi_root->exi_id = exi_id_get_next(); ! avl_add(&exi_id_tree, ne->exi_root); ! mutex_exit(&nfs_exi_id_lock); ! ne->exi_root->exi_kstats = exp_kstats_init(zoneid, ! ne->exi_root->exi_id, ne->exi_root->exi_export.ex_path, ! ne->exi_root->exi_export.ex_pathlen, FALSE); ! ! rw_exit(&ne->exported_lock); ! ne->ns_root = NULL; ! ! return (ne); } ! /*ARGSUSED*/ ! static void ! nfs_export_zone_fini(zoneid_t zoneid, void *data) { int i; + nfs_export_t *ne = data; + struct exportinfo *exi; ! rw_enter(&ne->exported_lock, RW_WRITER); ! mutex_enter(&nfs_exi_id_lock); ! ! exp_kstats_delete(ne->exi_root->exi_kstats); ! avl_remove(&exi_id_tree, ne->exi_root); ! export_unlink(ne, ne->exi_root); ! ! mutex_exit(&nfs_exi_id_lock); ! rw_exit(&ne->exported_lock); ! ! /* Deallocate the place holder for the public file handle */ ! srv_secinfo_list_free(ne->exi_root->exi_export.ex_secinfo, ! ne->exi_root->exi_export.ex_seccnt); ! mutex_destroy(&ne->exi_root->exi_lock); ! ! rw_destroy(&ne->exi_root->exi_cache_lock); for (i = 0; i < AUTH_TABLESIZE; i++) { ! avl_destroy(ne->exi_root->exi_cache[i]); ! kmem_free(ne->exi_root->exi_cache[i], sizeof (avl_tree_t)); } ! exp_kstats_fini(ne->exi_root->exi_kstats); ! kmem_free(ne->exi_root->exi_export.ex_path, ! ne->exi_root->exi_export.ex_pathlen + 1); ! kmem_free(ne->exi_root, sizeof (*ne->exi_root)); ! ! exi = avl_first(&exi_id_tree); ! while (exi != NULL) { ! struct exportinfo *nexi = AVL_NEXT(&exi_id_tree, exi); ! if (zoneid == exi->exi_zoneid) ! (void) unexport(ne, exi); ! exi = nexi; ! } ! ! rw_destroy(&ne->exported_lock); ! kmem_free(ne, sizeof (*ne)); } /* + * Initialization routine for export routines. + * Should only be called once. + */ + void + nfs_exportinit(void) + { + mutex_init(&nfs_exi_id_lock, NULL, MUTEX_DEFAULT, NULL); + + /* exi_id handling initialization */ + exi_id_next = 0; + exi_id_overflow = FALSE; + avl_create(&exi_id_tree, exi_id_compar, sizeof (struct exportinfo), + offsetof(struct exportinfo, exi_id_link)); + + zone_key_create(&nfs_export_key, nfs_export_zone_init, + NULL, nfs_export_zone_fini); + + nfslog_init(); + } + + /* + * Finalization routine for export routines. + */ + void + nfs_exportfini(void) + { + (void) zone_key_delete(nfs_export_key); + avl_destroy(&exi_id_tree); + mutex_destroy(&nfs_exi_id_lock); + } + + /* * Check if 2 gss mechanism identifiers are the same. * * return FALSE if not the same. * return TRUE if the same. */
*** 920,929 **** --- 1033,1043 ---- rpc_gss_lock_t *lock, void **cookie) { int i, j; rpc_gss_rawcred_t *raw_cred; struct exportinfo *exi; + nfs_export_t *ne = nfs_get_export(); /* * We don't deal with delegated credentials. */ if (deleg != GSS_C_NO_CREDENTIAL)
*** 930,942 **** return (FALSE); raw_cred = lock->raw_cred; *cookie = NULL; ! rw_enter(&exported_lock, RW_READER); for (i = 0; i < EXPTABLESIZE; i++) { ! exi = exptable[i]; while (exi) { if (exi->exi_export.ex_seccnt > 0) { struct secinfo *secp; seconfig_t *se; int seccnt; --- 1044,1057 ---- return (FALSE); raw_cred = lock->raw_cred; *cookie = NULL; ! rw_enter(&ne->exported_lock, RW_READER); ! for (i = 0; i < EXPTABLESIZE; i++) { ! exi = ne->exptable[i]; while (exi) { if (exi->exi_export.ex_seccnt > 0) { struct secinfo *secp; seconfig_t *se; int seccnt;
*** 972,982 **** } exi = exi->fid_hash.next; } } done: ! rw_exit(&exported_lock); /* * If no nfs pseudo number mapping can be found in the export * table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may * recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC). --- 1087,1097 ---- } exi = exi->fid_hash.next; } } done: ! rw_exit(&ne->exported_lock); /* * If no nfs pseudo number mapping can be found in the export * table, assign the nfsflavor to NFS_FLAVOR_NOMAP. In V4, we may * recover the flavor mismatch from NFS layer (NFS4ERR_WRONGSEC).
*** 1039,1076 **** int newcnt; 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, --- 1154,1192 ---- int newcnt; struct secinfo oldsec[MAX_FLAVORS]; int oldcnt; int i; struct pathname lookpn; + nfs_export_t *ne = nfs_get_export(); 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(&ne->exported_lock, RW_READER); DTRACE_PROBE(nfss__i__exported_lock1_start); ! for (ex1 = ne->exptable_path_hash[pkp_tab_hash(lookpn.pn_path, strlen(lookpn.pn_path))]; ex1; ex1 = ex1->path_hash.next) { ! if (ex1 != ne->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(&ne->exported_lock); /* Is this an unshare? */ if (STRUCT_FGETP(uap, uex) == NULL) { pn_free(&lookpn); if (ex1 == NULL) return (EINVAL); ! error = unexport(ne, ex1); ! exi_rele(&ex1); return (error); } /* It is a share or a re-share */ error = lookupname(STRUCT_FGETP(uap, dname), UIO_USERSPACE,
*** 1092,1102 **** error = ENOENT; } if (error) { pn_free(&lookpn); if (ex1) ! exi_rele(ex1); return (error); } /* * 'vp' may be an AUTOFS node, so we perform a --- 1208,1218 ---- error = ENOENT; } if (error) { pn_free(&lookpn); if (ex1) ! exi_rele(&ex1); return (error); } /* * 'vp' may be an AUTOFS node, so we perform a
*** 1118,1128 **** 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 */ --- 1234,1244 ---- 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 */
*** 1129,1143 **** if (ex1 && !PSEUDO(ex1) && !VN_CMP(ex1->exi_vp, vp)) { VN_RELE(vp); if (dvp != NULL) VN_RELE(dvp); pn_free(&lookpn); ! exi_rele(ex1); return (EEXIST); } if (ex1) ! exi_rele(ex1); /* * Get the vfs id */ bzero(&fid, sizeof (fid)); --- 1245,1259 ---- if (ex1 && !PSEUDO(ex1) && !VN_CMP(ex1->exi_vp, vp)) { VN_RELE(vp); if (dvp != NULL) VN_RELE(dvp); pn_free(&lookpn); ! exi_rele(&ex1); return (EEXIST); } if (ex1) ! exi_rele(&ex1); /* * Get the vfs id */ bzero(&fid, sizeof (fid));
*** 1161,1195 **** /* * 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); VN_RELE(vp); if (dvp != NULL) VN_RELE(dvp); 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; exi->exi_fid = fid; exi->exi_vp = vp; exi->exi_count = 1; exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag & VSW_VOLATILEDEV) ? 1 : 0; mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL); exi->exi_dvp = dvp; --- 1277,1312 ---- /* * 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(&ne->exported_lock, RW_READER); DTRACE_PROBE(nfss__i__exported_lock2_start); ! for (ex2 = ne->exptable[exptablehash(&fsid, &fid)]; ex2; ex2 = ex2->fid_hash.next) { ! if (ex2 != ne->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(&ne->exported_lock); VN_RELE(vp); if (dvp != NULL) VN_RELE(dvp); pn_free(&lookpn); return (EEXIST); } } DTRACE_PROBE(nfss__i__exported_lock2_stop); ! rw_exit(&ne->exported_lock); pn_free(&lookpn); exi = kmem_zalloc(sizeof (*exi), KM_SLEEP); exi->exi_fsid = fsid; exi->exi_fid = fid; exi->exi_vp = vp; exi->exi_count = 1; + exi->exi_zoneid = crgetzoneid(cr); exi->exi_volatile_dev = (vfssw[vp->v_vfsp->vfs_fstype].vsw_flag & VSW_VOLATILEDEV) ? 1 : 0; mutex_init(&exi->exi_lock, NULL, MUTEX_DEFAULT, NULL); exi->exi_dvp = dvp;
*** 1459,1499 **** } /* * 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); /* * Check the rest of the list for an old entry for the fs. * If one is found then unlink it, wait until this is the * only reference and then free it. */ for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) { ! if (ex != exi_root && VN_CMP(ex->exi_vp, vp)) { ! export_unlink(ex); break; } } /* * If the public filehandle is pointing at the * old entry, then point it back at the root. */ ! if (ex != NULL && ex == exi_public) ! exi_public = exi_root; /* * If the public flag is on, make the global exi_public * point to this entry and turn off the public bit so that * we can distinguish it from the place holder export. */ if (kex->ex_flags & EX_PUBLIC) { ! exi_public = exi; kex->ex_flags &= ~EX_PUBLIC; } #ifdef VOLATILE_FH_TEST /* --- 1576,1619 ---- } /* * Insert the new entry at the front of the export list */ ! rw_enter(&ne->exported_lock, RW_WRITER); DTRACE_PROBE(nfss__i__exported_lock3_start); ! export_link(ne, exi); /* * Check the rest of the list for an old entry for the fs. * If one is found then unlink it, wait until this is the * only reference and then free it. */ for (ex = exi->fid_hash.next; ex != NULL; ex = ex->fid_hash.next) { ! if (ex != ne->exi_root && VN_CMP(ex->exi_vp, vp)) { ! mutex_enter(&nfs_exi_id_lock); ! avl_remove(&exi_id_tree, ex); ! mutex_exit(&nfs_exi_id_lock); ! export_unlink(ne, ex); break; } } /* * If the public filehandle is pointing at the * old entry, then point it back at the root. */ ! if (ex != NULL && ex == ne->exi_public) ! ne->exi_public = ne->exi_root; /* * If the public flag is on, make the global exi_public * point to this entry and turn off the public bit so that * we can distinguish it from the place holder export. */ if (kex->ex_flags & EX_PUBLIC) { ! ne->exi_public = exi; kex->ex_flags &= ~EX_PUBLIC; } #ifdef VOLATILE_FH_TEST /*
*** 1521,1541 **** /* If it's a re-export update namespace tree */ exi->exi_tree = ex->exi_tree; exi->exi_tree->tree_exi = exi; /* Update the change timestamp */ ! tree_update_change(exi->exi_tree, NULL); } /* * build a unique flavor list from the flavors specified * in the share cmd. unique means that each flavor only * appears once in the secinfo list -- no duplicates allowed. */ newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE); ! srv_secinfo_treeclimb(exi, newsec, newcnt, TRUE); /* * If re-sharing an old export entry, update the secinfo data * depending on if the old entry is a pseudo node or not. */ --- 1641,1661 ---- /* If it's a re-export update namespace tree */ exi->exi_tree = ex->exi_tree; exi->exi_tree->tree_exi = exi; /* Update the change timestamp */ ! tree_update_change(ne, exi->exi_tree, NULL); } /* * build a unique flavor list from the flavors specified * in the share cmd. unique means that each flavor only * appears once in the secinfo list -- no duplicates allowed. */ newcnt = build_seclist_nodups(&exi->exi_export, newsec, FALSE); ! srv_secinfo_treeclimb(ne, exi, newsec, newcnt, TRUE); /* * If re-sharing an old export entry, update the secinfo data * depending on if the old entry is a pseudo node or not. */
*** 1556,1566 **** /* * First transfer implicit flavor refs to new export. * Remove old flavor refs last. */ srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt); ! srv_secinfo_treeclimb(ex, oldsec, oldcnt, FALSE); } } /* * If it's a re-export and the old entry has a pseudonode list, --- 1676,1686 ---- /* * First transfer implicit flavor refs to new export. * Remove old flavor refs last. */ srv_secinfo_exp2exp(&exi->exi_export, oldsec, oldcnt); ! srv_secinfo_treeclimb(ne, ex, oldsec, oldcnt, FALSE); } } /* * If it's a re-export and the old entry has a pseudonode list,
*** 1569,1598 **** if (ex != NULL && (ex->exi_visible != NULL)) { exi->exi_visible = ex->exi_visible; ex->exi_visible = NULL; } DTRACE_PROBE(nfss__i__exported_lock3_stop); ! rw_exit(&exported_lock); ! if (exi_public == exi || kex->ex_flags & EX_LOG) { /* * Log share operation to this buffer only. */ nfslog_share_record(exi, cr); } if (ex != NULL) ! exi_rele(ex); return (0); out7: /* Unlink the new export in exptable. */ ! export_unlink(exi); DTRACE_PROBE(nfss__i__exported_lock3_stop); ! rw_exit(&exported_lock); out6: if (kex->ex_flags & EX_INDEX) kmem_free(kex->ex_index, strlen(kex->ex_index) + 1); out5: /* free partially completed allocation */ --- 1689,1738 ---- if (ex != NULL && (ex->exi_visible != NULL)) { exi->exi_visible = ex->exi_visible; ex->exi_visible = NULL; } + /* + * Initialize exi_id and exi_kstats + */ + if (ex != NULL) { + exi->exi_id = ex->exi_id; + exi->exi_kstats = ex->exi_kstats; + ex->exi_kstats = NULL; + exp_kstats_reset(exi->exi_kstats, kex->ex_path, + kex->ex_pathlen, FALSE); + } else { + mutex_enter(&nfs_exi_id_lock); + exi->exi_id = exi_id_get_next(); + mutex_exit(&nfs_exi_id_lock); + exi->exi_kstats = exp_kstats_init(crgetzoneid(cr), exi->exi_id, + kex->ex_path, kex->ex_pathlen, FALSE); + } + mutex_enter(&nfs_exi_id_lock); + avl_add(&exi_id_tree, exi); + mutex_exit(&nfs_exi_id_lock); + DTRACE_PROBE(nfss__i__exported_lock3_stop); ! rw_exit(&ne->exported_lock); ! if (ne->exi_public == exi || kex->ex_flags & EX_LOG) { /* * Log share operation to this buffer only. */ nfslog_share_record(exi, cr); } if (ex != NULL) ! exi_rele(&ex); return (0); out7: /* Unlink the new export in exptable. */ ! export_unlink(ne, exi); DTRACE_PROBE(nfss__i__exported_lock3_stop); ! rw_exit(&ne->exported_lock); out6: if (kex->ex_flags & EX_INDEX) kmem_free(kex->ex_index, strlen(kex->ex_index) + 1); out5: /* free partially completed allocation */
*** 1632,1699 **** /* * Remove the exportinfo from the export list */ void ! export_unlink(struct exportinfo *exi) { ! ASSERT(RW_WRITE_HELD(&exported_lock)); exp_hash_unlink(exi, fid_hash); exp_hash_unlink(exi, path_hash); } /* * Unexport an exported filesystem */ static int ! unexport(struct exportinfo *exi) { struct secinfo cursec[MAX_FLAVORS]; int curcnt; ! rw_enter(&exported_lock, RW_WRITER); /* 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 */ curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE); - srv_secinfo_treeclimb(exi, cursec, curcnt, FALSE); - /* * If there's a visible list, then need to leave * a pseudo export here to retain the visible list * for paths to exports below. */ if (exi->exi_visible != NULL) { struct exportinfo *newexi; ! newexi = pseudo_exportfs(exi->exi_vp, &exi->exi_fid, exi->exi_visible, &exi->exi_export); exi->exi_visible = NULL; /* interconnect the existing treenode with the new exportinfo */ newexi->exi_tree = exi->exi_tree; newexi->exi_tree->tree_exi = newexi; /* Update the change timestamp */ ! tree_update_change(exi->exi_tree, NULL); } else { ! treeclimb_unexport(exi); } ! rw_exit(&exported_lock); /* * Need to call into the NFSv4 server and release all data * held on this particular export. This is important since * the v4 server may be holding file locks or vnodes under --- 1772,1842 ---- /* * Remove the exportinfo from the export list */ void ! export_unlink(nfs_export_t *ne, struct exportinfo *exi) { ! ASSERT(RW_WRITE_HELD(&ne->exported_lock)); exp_hash_unlink(exi, fid_hash); exp_hash_unlink(exi, path_hash); } /* * Unexport an exported filesystem */ static int ! unexport(nfs_export_t *ne, struct exportinfo *exi) { struct secinfo cursec[MAX_FLAVORS]; int curcnt; ! rw_enter(&ne->exported_lock, RW_WRITER); /* Check if exi is still linked in the export table */ if (!EXP_LINKED(exi) || PSEUDO(exi)) { ! rw_exit(&ne->exported_lock); return (EINVAL); } ! exp_kstats_delete(exi->exi_kstats); ! mutex_enter(&nfs_exi_id_lock); ! avl_remove(&exi_id_tree, exi); ! mutex_exit(&nfs_exi_id_lock); ! export_unlink(ne, exi); /* * Remove security flavors before treeclimb_unexport() is called * because srv_secinfo_treeclimb needs the namespace tree */ curcnt = build_seclist_nodups(&exi->exi_export, cursec, TRUE); + srv_secinfo_treeclimb(ne, exi, cursec, curcnt, FALSE); /* * If there's a visible list, then need to leave * a pseudo export here to retain the visible list * for paths to exports below. */ if (exi->exi_visible != NULL) { struct exportinfo *newexi; ! newexi = pseudo_exportfs(ne, exi->exi_vp, &exi->exi_fid, exi->exi_visible, &exi->exi_export); exi->exi_visible = NULL; /* interconnect the existing treenode with the new exportinfo */ newexi->exi_tree = exi->exi_tree; newexi->exi_tree->tree_exi = newexi; /* Update the change timestamp */ ! tree_update_change(ne, exi->exi_tree, NULL); } else { ! treeclimb_unexport(ne, exi); } ! rw_exit(&ne->exported_lock); /* * Need to call into the NFSv4 server and release all data * held on this particular export. This is important since * the v4 server may be holding file locks or vnodes under
*** 1709,1729 **** /* * If this was a public export, restore * the public filehandle to the root. */ ! if (exi == exi_public) { ! exi_public = exi_root; ! nfslog_share_record(exi_public, CRED()); } ! if (exi->exi_export.ex_flags & EX_LOG) { nfslog_unshare_record(exi, CRED()); - } ! exi_rele(exi); return (0); } /* * Get file handle system call. --- 1852,1871 ---- /* * If this was a public export, restore * the public filehandle to the root. */ ! if (exi == ne->exi_public) { ! ne->exi_public = ne->exi_root; ! nfslog_share_record(ne->exi_public, CRED()); } ! if (exi->exi_export.ex_flags & EX_LOG) nfslog_unshare_record(exi, CRED()); ! exi_rele(&exi); return (0); } /* * Get file handle system call.
*** 1875,1885 **** } if (!error && exi->exi_export.ex_flags & EX_LOG) { nfslog_getfh(exi, (fhandle_t *)logptr, STRUCT_FGETP(uap, fname), UIO_USERSPACE, cr); } ! exi_rele(exi); if (!error) { if (copyout(&l, STRUCT_FGETP(uap, lenp), sizeof (int))) error = EFAULT; if (copyout(buf, STRUCT_FGETP(uap, fhp), l)) error = EFAULT; --- 2017,2027 ---- } if (!error && exi->exi_export.ex_flags & EX_LOG) { nfslog_getfh(exi, (fhandle_t *)logptr, STRUCT_FGETP(uap, fname), UIO_USERSPACE, cr); } ! exi_rele(&exi); if (!error) { if (copyout(&l, STRUCT_FGETP(uap, lenp), sizeof (int))) error = EFAULT; if (copyout(buf, STRUCT_FGETP(uap, fhp), l)) error = EFAULT;
*** 2444,2456 **** */ struct exportinfo * checkexport(fsid_t *fsid, fid_t *fid) { struct exportinfo *exi; ! rw_enter(&exported_lock, RW_READER); ! for (exi = exptable[exptablehash(fsid, fid)]; exi != NULL; exi = exi->fid_hash.next) { if (exportmatch(exi, fsid, fid)) { /* * If this is the place holder for the --- 2586,2599 ---- */ struct exportinfo * checkexport(fsid_t *fsid, fid_t *fid) { struct exportinfo *exi; + nfs_export_t *ne = nfs_get_export(); ! rw_enter(&ne->exported_lock, RW_READER); ! for (exi = ne->exptable[exptablehash(fsid, fid)]; exi != NULL; exi = exi->fid_hash.next) { if (exportmatch(exi, fsid, fid)) { /* * If this is the place holder for the
*** 2457,2475 **** * public file handle, then return the * real export entry for the public file * handle. */ if (exi->exi_export.ex_flags & EX_PUBLIC) { ! exi = exi_public; } exi_hold(exi); ! rw_exit(&exported_lock); return (exi); } } ! rw_exit(&exported_lock); return (NULL); } /* --- 2600,2618 ---- * public file handle, then return the * real export entry for the public file * handle. */ if (exi->exi_export.ex_flags & EX_PUBLIC) { ! exi = ne->exi_public; } exi_hold(exi); ! rw_exit(&ne->exported_lock); return (exi); } } ! rw_exit(&ne->exported_lock); return (NULL); } /*
*** 2481,2494 **** */ struct exportinfo * checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp) { struct exportinfo *exi; ! ASSERT(RW_LOCK_HELD(&exported_lock)); ! for (exi = exptable[exptablehash(fsid, fid)]; exi != NULL; exi = exi->fid_hash.next) { if (exportmatch(exi, fsid, fid)) { /* * If this is the place holder for the --- 2624,2638 ---- */ struct exportinfo * checkexport4(fsid_t *fsid, fid_t *fid, vnode_t *vp) { struct exportinfo *exi; + nfs_export_t *ne = nfs_get_export(); ! ASSERT(RW_LOCK_HELD(&ne->exported_lock)); ! for (exi = ne->exptable[exptablehash(fsid, fid)]; exi != NULL; exi = exi->fid_hash.next) { if (exportmatch(exi, fsid, fid)) { /* * If this is the place holder for the
*** 2495,2505 **** * public file handle, then return the * real export entry for the public file * handle. */ if (exi->exi_export.ex_flags & EX_PUBLIC) { ! exi = exi_public; } /* * If vp is given, check if vp is the * same vnode as the exported node. --- 2639,2649 ---- * public file handle, then return the * real export entry for the public file * handle. */ if (exi->exi_export.ex_flags & EX_PUBLIC) { ! exi = ne->exi_public; } /* * If vp is given, check if vp is the * same vnode as the exported node.
*** 2518,2528 **** } /* * Free an entire export list node */ ! void exportfree(struct exportinfo *exi) { struct exportdata *ex; struct charset_cache *cache; int i; --- 2662,2672 ---- } /* * Free an entire export list node */ ! static void exportfree(struct exportinfo *exi) { struct exportdata *ex; struct charset_cache *cache; int i;
*** 2581,2590 **** --- 2725,2736 ---- for (i = 0; i < AUTH_TABLESIZE; i++) { avl_destroy(exi->exi_cache[i]); kmem_free(exi->exi_cache[i], sizeof (avl_tree_t)); } + exp_kstats_fini(exi->exi_kstats); + kmem_free(exi, sizeof (*exi)); } /* * load the index file from user space into kernel space.
*** 2622,2640 **** * When a thread completes using exi, it should call exi_rele(). * exi_rele() decrements exi_count. It releases exi if exi_count == 0, i.e. * if this is the last user of exi and exi is not on exportinfo list anymore */ void ! exi_rele(struct exportinfo *exi) { ! mutex_enter(&exi->exi_lock); ! exi->exi_count--; ! if (exi->exi_count == 0) { ! mutex_exit(&exi->exi_lock); ! exportfree(exi); } else ! mutex_exit(&exi->exi_lock); } #ifdef VOLATILE_FH_TEST /* * Test for volatile fh's - add file handle to list and set its volatile id --- 2768,2795 ---- * When a thread completes using exi, it should call exi_rele(). * exi_rele() decrements exi_count. It releases exi if exi_count == 0, i.e. * if this is the last user of exi and exi is not on exportinfo list anymore */ void ! exi_rele(struct exportinfo **exi) { ! struct exportinfo *exip = *exi; ! mutex_enter(&exip->exi_lock); ! exip->exi_count--; ! if (exip->exi_count == 0) { ! mutex_exit(&exip->exi_lock); ! /* ! * The exportinfo structure needs to be cleared here ! * since the control point, for when we free the structure, ! * is in this function and is triggered by the reference ! * count. The caller does not necessarily know when that ! * will be the case. ! */ ! *exi = NULL; ! exportfree(exip); } else ! mutex_exit(&exip->exi_lock); } #ifdef VOLATILE_FH_TEST /* * Test for volatile fh's - add file handle to list and set its volatile id