Print this page
*** 19,29 ****
* CDDL HEADER END
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
! * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
--- 19,29 ----
* CDDL HEADER END
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
! * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
*** 64,75 ****
#include <sys/nbmlock.h>
#include <sys/fcntl.h>
#include <fs/fs_subr.h>
#include <sys/taskq.h>
#include <fs/fs_reparse.h>
- #include <sys/time.h>
- #include <sys/sdt.h>
/* Determine if this vnode is a file that is read-only */
#define ISROFILE(vp) \
((vp)->v_type != VCHR && (vp)->v_type != VBLK && \
(vp)->v_type != VFIFO && vn_is_readonly(vp))
--- 64,73 ----
*** 102,114 ****
kmutex_t vskstat_tree_lock;
/* Global variable which enables/disables the vopstats collection */
int vopstats_enabled = 1;
- /* Global used for empty/invalid v_path */
- char *vn_vpath_empty = "";
-
/*
* forward declarations for internal vnode specific data (vsd)
*/
static void *vsd_realloc(void *, size_t, size_t);
--- 100,109 ----
*** 206,216 ****
}
#define VOP_LATENCY_10MS 10000000
#define VOP_LATENCY_100MS 100000000
#define VOP_LATENCY_1S 1000000000
- #define VOP_LATENCY_10S 10000000000
/*
* Convert stat(2) formats to vnode types and vice versa. (Knows about
* numerical order of S_IFMT and vnode types.)
*/
--- 201,210 ----
*** 2292,2303 ****
mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&vp->v_vsd_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&vp->v_cv, NULL, CV_DEFAULT, NULL);
rw_init(&vp->v_nbllock, NULL, RW_DEFAULT, NULL);
vp->v_femhead = NULL; /* Must be done before vn_reinit() */
! vp->v_path = vn_vpath_empty;
! vp->v_path_stamp = 0;
vp->v_mpssdata = NULL;
vp->v_vsd = NULL;
vp->v_fopdata = NULL;
return (0);
--- 2286,2296 ----
mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&vp->v_vsd_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&vp->v_cv, NULL, CV_DEFAULT, NULL);
rw_init(&vp->v_nbllock, NULL, RW_DEFAULT, NULL);
vp->v_femhead = NULL; /* Must be done before vn_reinit() */
! vp->v_path = NULL;
vp->v_mpssdata = NULL;
vp->v_vsd = NULL;
vp->v_fopdata = NULL;
return (0);
*** 2340,2350 ****
*/
void
vn_recycle(vnode_t *vp)
{
ASSERT(vp->v_pages == NULL);
- VERIFY(vp->v_path != NULL);
/*
* XXX - This really belongs in vn_reinit(), but we have some issues
* with the counts. Best to have it here for clean initialization.
*/
--- 2333,2342 ----
*** 2363,2377 ****
ASSERT(vp->v_femhead->femh_list == NULL);
mutex_destroy(&vp->v_femhead->femh_lock);
kmem_free(vp->v_femhead, sizeof (*(vp->v_femhead)));
vp->v_femhead = NULL;
}
! if (vp->v_path != vn_vpath_empty) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
! vp->v_path = vn_vpath_empty;
}
- vp->v_path_stamp = 0;
if (vp->v_fopdata != NULL) {
free_fopdata(vp);
}
vp->v_mpssdata = NULL;
--- 2355,2368 ----
ASSERT(vp->v_femhead->femh_list == NULL);
mutex_destroy(&vp->v_femhead->femh_lock);
kmem_free(vp->v_femhead, sizeof (*(vp->v_femhead)));
vp->v_femhead = NULL;
}
! if (vp->v_path) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
! vp->v_path = NULL;
}
if (vp->v_fopdata != NULL) {
free_fopdata(vp);
}
vp->v_mpssdata = NULL;
*** 2438,2451 ****
* some with v_count of 1. In any case, the value should
* never be anything else.
*/
ASSERT((vp->v_count == 0) || (vp->v_count == 1));
ASSERT(vp->v_count_dnlc == 0);
! VERIFY(vp->v_path != NULL);
! if (vp->v_path != vn_vpath_empty) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
! vp->v_path = vn_vpath_empty;
}
/* If FEM was in use, make sure everything gets cleaned up */
if (vp->v_femhead) {
/* XXX - There should be a free_femhead() that does all this */
--- 2429,2441 ----
* some with v_count of 1. In any case, the value should
* never be anything else.
*/
ASSERT((vp->v_count == 0) || (vp->v_count == 1));
ASSERT(vp->v_count_dnlc == 0);
! if (vp->v_path != NULL) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
! vp->v_path = NULL;
}
/* If FEM was in use, make sure everything gets cleaned up */
if (vp->v_femhead) {
/* XXX - There should be a free_femhead() that does all this */
*** 2975,3217 ****
return ((u_longlong_t)atomic_inc_64_nv(&next_caller_id));
}
/*
! * The value stored in v_path is relative to rootdir, located in the global
! * zone. Zones or chroot environments which reside deeper inside the VFS
! * hierarchy will have a relative view of MAXPATHLEN since they are unaware of
! * what lies below their perceived root. In order to keep v_path usable for
! * these child environments, its allocations are allowed to exceed MAXPATHLEN.
! *
! * An upper bound of max_vnode_path is placed upon v_path allocations to
! * prevent the system from going too wild at the behest of pathological
! * behavior from the operator.
*/
size_t max_vnode_path = 4 * MAXPATHLEN;
-
void
! vn_clearpath(vnode_t *vp, hrtime_t compare_stamp)
{
! char *buf;
! mutex_enter(&vp->v_lock);
! /*
! * If the snapshot of v_path_stamp passed in via compare_stamp does not
! * match the present value on the vnode, it indicates that subsequent
! * changes have occurred. The v_path value is not cleared in this case
! * since the new value may be valid.
! */
! if (compare_stamp != 0 && vp->v_path_stamp != compare_stamp) {
! mutex_exit(&vp->v_lock);
! return;
}
- buf = vp->v_path;
- vp->v_path = vn_vpath_empty;
- vp->v_path_stamp = 0;
- mutex_exit(&vp->v_lock);
- if (buf != vn_vpath_empty) {
- kmem_free(buf, strlen(buf) + 1);
- }
- }
- static void
- vn_setpath_common(vnode_t *pvp, vnode_t *vp, const char *name, size_t len,
- boolean_t is_rename)
- {
- char *buf, *oldbuf;
- hrtime_t pstamp;
- size_t baselen, buflen = 0;
-
- /* Handle the vn_setpath_str case. */
- if (pvp == NULL) {
- if (len + 1 > max_vnode_path) {
- DTRACE_PROBE4(vn__setpath__too__long, vnode_t *, pvp,
- vnode_t *, vp, char *, name, size_t, len + 1);
- return;
- }
- buf = kmem_alloc(len + 1, KM_SLEEP);
- bcopy(name, buf, len);
- buf[len] = '\0';
-
- mutex_enter(&vp->v_lock);
- oldbuf = vp->v_path;
- vp->v_path = buf;
- vp->v_path_stamp = gethrtime();
- mutex_exit(&vp->v_lock);
- if (oldbuf != vn_vpath_empty) {
- kmem_free(oldbuf, strlen(oldbuf) + 1);
- }
- return;
- }
-
- /* Take snapshot of parent dir */
- mutex_enter(&pvp->v_lock);
- retrybuf:
- if (pvp->v_path == vn_vpath_empty) {
/*
! * Without v_path from the parent directory, generating a child
! * path from the name is impossible.
*/
! if (len > 0) {
! pstamp = pvp->v_path_stamp;
! mutex_exit(&pvp->v_lock);
! vn_clearpath(vp, pstamp);
return;
}
/*
! * The only feasible case here is where a NUL lookup is being
! * performed on rootdir prior to its v_path being populated.
*/
! ASSERT(pvp->v_path_stamp = 0);
! baselen = 0;
! pstamp = 0;
! } else {
! pstamp = pvp->v_path_stamp;
! baselen = strlen(pvp->v_path);
! /* ignore a trailing slash if present */
! if (pvp->v_path[baselen - 1] == '/') {
! /* This should only the be case for rootdir */
! ASSERT(baselen == 1 && pvp == rootdir);
! baselen--;
! }
! }
! mutex_exit(&pvp->v_lock);
! if (buflen != 0) {
! /* Free the existing (mis-sized) buffer in case of retry */
! kmem_free(buf, buflen);
! }
! /* base, '/', name and trailing NUL */
! buflen = baselen + len + 2;
! if (buflen > max_vnode_path) {
! DTRACE_PROBE4(vn__setpath_too__long, vnode_t *, pvp,
! vnode_t *, vp, char *, name, size_t, buflen);
return;
- }
- buf = kmem_alloc(buflen, KM_SLEEP);
! mutex_enter(&pvp->v_lock);
! if (pvp->v_path_stamp != pstamp) {
! size_t vlen;
! /*
! * Since v_path_stamp changed on the parent, it is likely that
! * v_path has been altered as well. If the length does not
! * exactly match what was previously measured, the buffer
! * allocation must be repeated for proper sizing.
! */
! if (pvp->v_path == vn_vpath_empty) {
! /* Give up if parent lack v_path */
! mutex_exit(&pvp->v_lock);
! kmem_free(buf, buflen);
return;
}
! vlen = strlen(pvp->v_path);
! if (pvp->v_path[vlen - 1] == '/') {
! vlen--;
! }
! if (vlen != baselen) {
! goto retrybuf;
! }
! }
! bcopy(pvp->v_path, buf, baselen);
! mutex_exit(&pvp->v_lock);
! buf[baselen] = '/';
! baselen++;
! bcopy(name, &buf[baselen], len + 1);
mutex_enter(&vp->v_lock);
! if (vp->v_path_stamp == 0) {
! /* never-visited vnode can inherit stamp from parent */
! ASSERT(vp->v_path == vn_vpath_empty);
! vp->v_path_stamp = pstamp;
! vp->v_path = buf;
mutex_exit(&vp->v_lock);
! } else if (vp->v_path_stamp < pstamp || is_rename) {
! /*
! * Install the updated path and stamp, ensuring that the v_path
! * pointer is valid at all times for dtrace.
! */
! oldbuf = vp->v_path;
! vp->v_path = buf;
! vp->v_path_stamp = gethrtime();
! mutex_exit(&vp->v_lock);
! kmem_free(oldbuf, strlen(oldbuf) + 1);
} else {
! /*
! * If the timestamp matches or is greater, it means another
! * thread performed the update first while locks were dropped
! * here to make the allocation. We defer to the newer value.
! */
mutex_exit(&vp->v_lock);
- kmem_free(buf, buflen);
}
- ASSERT(MUTEX_NOT_HELD(&vp->v_lock));
}
void
! vn_updatepath(vnode_t *pvp, vnode_t *vp, const char *name)
{
! size_t len;
! /*
! * If the parent is older or empty, there's nothing further to do.
! */
! if (pvp->v_path == vn_vpath_empty ||
! pvp->v_path_stamp <= vp->v_path_stamp) {
return;
}
! /*
! * Given the lack of appropriate context, meaningful updates to v_path
! * cannot be made for during lookups for the '.' or '..' entries.
! */
! len = strlen(name);
! if (len == 0 || (len == 1 && name[0] == '.') ||
! (len == 2 && name[0] == '.' && name[1] == '.')) {
! return;
! }
! vn_setpath_common(pvp, vp, name, len, B_FALSE);
}
/*
- * Given a starting vnode and a path, updates the path in the target vnode in
- * a safe manner. If the vnode already has path information embedded, then the
- * cached path is left untouched.
- */
- /* ARGSUSED */
- void
- vn_setpath(vnode_t *rootvp, vnode_t *pvp, vnode_t *vp, const char *name,
- size_t len)
- {
- vn_setpath_common(pvp, vp, name, len, B_FALSE);
- }
-
- /*
- * Sets the path to the vnode to be the given string, regardless of current
- * context. The string must be a complete path from rootdir. This is only used
- * by fsop_root() for setting the path based on the mountpoint.
- */
- void
- vn_setpath_str(vnode_t *vp, const char *str, size_t len)
- {
- vn_setpath_common(NULL, vp, str, len, B_FALSE);
- }
-
- /*
* Called from within filesystem's vop_rename() to handle renames once the
* target vnode is available.
*/
void
! vn_renamepath(vnode_t *pvp, vnode_t *vp, const char *name, size_t len)
{
! vn_setpath_common(pvp, vp, name, len, B_TRUE);
}
/*
* Similar to vn_setpath_str(), this function sets the path of the destination
* vnode to the be the same as the source vnode.
--- 2965,3093 ----
return ((u_longlong_t)atomic_inc_64_nv(&next_caller_id));
}
/*
! * Given a starting vnode and a path, updates the path in the target vnode in
! * a safe manner. If the vnode already has path information embedded, then the
! * cached path is left untouched.
*/
+
size_t max_vnode_path = 4 * MAXPATHLEN;
void
! vn_setpath(vnode_t *rootvp, struct vnode *startvp, struct vnode *vp,
! const char *path, size_t plen)
{
! char *rpath;
! vnode_t *base;
! size_t rpathlen, rpathalloc;
! int doslash = 1;
! if (*path == '/') {
! base = rootvp;
! path++;
! plen--;
! } else {
! base = startvp;
}
/*
! * We cannot grab base->v_lock while we hold vp->v_lock because of
! * the potential for deadlock.
*/
! mutex_enter(&base->v_lock);
! if (base->v_path == NULL) {
! mutex_exit(&base->v_lock);
return;
}
+ rpathlen = strlen(base->v_path);
+ rpathalloc = rpathlen + plen + 1;
+ /* Avoid adding a slash if there's already one there */
+ if (base->v_path[rpathlen-1] == '/')
+ doslash = 0;
+ else
+ rpathalloc++;
+
/*
! * We don't want to call kmem_alloc(KM_SLEEP) with kernel locks held,
! * so we must do this dance. If, by chance, something changes the path,
! * just give up since there is no real harm.
*/
! mutex_exit(&base->v_lock);
! /* Paths should stay within reason */
! if (rpathalloc > max_vnode_path)
return;
! rpath = kmem_alloc(rpathalloc, KM_SLEEP);
! mutex_enter(&base->v_lock);
! if (base->v_path == NULL || strlen(base->v_path) != rpathlen) {
! mutex_exit(&base->v_lock);
! kmem_free(rpath, rpathalloc);
return;
}
! bcopy(base->v_path, rpath, rpathlen);
! mutex_exit(&base->v_lock);
! if (doslash)
! rpath[rpathlen++] = '/';
! bcopy(path, rpath + rpathlen, plen);
! rpath[rpathlen + plen] = '\0';
mutex_enter(&vp->v_lock);
! if (vp->v_path != NULL) {
mutex_exit(&vp->v_lock);
! kmem_free(rpath, rpathalloc);
} else {
! vp->v_path = rpath;
mutex_exit(&vp->v_lock);
}
}
+ /*
+ * Sets the path to the vnode to be the given string, regardless of current
+ * context. The string must be a complete path from rootdir. This is only used
+ * by fsop_root() for setting the path based on the mountpoint.
+ */
void
! vn_setpath_str(struct vnode *vp, const char *str, size_t len)
{
! char *buf = kmem_alloc(len + 1, KM_SLEEP);
! mutex_enter(&vp->v_lock);
! if (vp->v_path != NULL) {
! mutex_exit(&vp->v_lock);
! kmem_free(buf, len + 1);
return;
}
! vp->v_path = buf;
! bcopy(str, vp->v_path, len);
! vp->v_path[len] = '\0';
! mutex_exit(&vp->v_lock);
}
/*
* Called from within filesystem's vop_rename() to handle renames once the
* target vnode is available.
*/
void
! vn_renamepath(vnode_t *dvp, vnode_t *vp, const char *nm, size_t len)
{
! char *tmp;
!
! mutex_enter(&vp->v_lock);
! tmp = vp->v_path;
! vp->v_path = NULL;
! mutex_exit(&vp->v_lock);
! vn_setpath(rootdir, dvp, vp, nm, len);
! if (tmp != NULL)
! kmem_free(tmp, strlen(tmp) + 1);
}
/*
* Similar to vn_setpath_str(), this function sets the path of the destination
* vnode to the be the same as the source vnode.
*** 3218,3263 ****
*/
void
vn_copypath(struct vnode *src, struct vnode *dst)
{
char *buf;
! hrtime_t stamp;
! size_t buflen;
mutex_enter(&src->v_lock);
! if (src->v_path == vn_vpath_empty) {
mutex_exit(&src->v_lock);
return;
}
! buflen = strlen(src->v_path) + 1;
! mutex_exit(&src->v_lock);
! buf = kmem_alloc(buflen, KM_SLEEP);
!
mutex_enter(&src->v_lock);
! if (src->v_path == vn_vpath_empty ||
! strlen(src->v_path) + 1 != buflen) {
mutex_exit(&src->v_lock);
! kmem_free(buf, buflen);
return;
}
! bcopy(src->v_path, buf, buflen);
! stamp = src->v_path_stamp;
mutex_exit(&src->v_lock);
mutex_enter(&dst->v_lock);
! if (dst->v_path != vn_vpath_empty) {
mutex_exit(&dst->v_lock);
! kmem_free(buf, buflen);
return;
}
dst->v_path = buf;
- dst->v_path_stamp = stamp;
mutex_exit(&dst->v_lock);
}
-
/*
* XXX Private interface for segvn routines that handle vnode
* large page segments.
*
* return 1 if vp's file system VOP_PAGEIO() implementation
--- 3094,3134 ----
*/
void
vn_copypath(struct vnode *src, struct vnode *dst)
{
char *buf;
! int alloc;
mutex_enter(&src->v_lock);
! if (src->v_path == NULL) {
mutex_exit(&src->v_lock);
return;
}
! alloc = strlen(src->v_path) + 1;
! /* avoid kmem_alloc() with lock held */
! mutex_exit(&src->v_lock);
! buf = kmem_alloc(alloc, KM_SLEEP);
mutex_enter(&src->v_lock);
! if (src->v_path == NULL || strlen(src->v_path) + 1 != alloc) {
mutex_exit(&src->v_lock);
! kmem_free(buf, alloc);
return;
}
! bcopy(src->v_path, buf, alloc);
mutex_exit(&src->v_lock);
mutex_enter(&dst->v_lock);
! if (dst->v_path != NULL) {
mutex_exit(&dst->v_lock);
! kmem_free(buf, alloc);
return;
}
dst->v_path = buf;
mutex_exit(&dst->v_lock);
}
/*
* XXX Private interface for segvn routines that handle vnode
* large page segments.
*
* return 1 if vp's file system VOP_PAGEIO() implementation
*** 3442,3460 ****
if (lat < VOP_LATENCY_100MS)
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
else if (lat < VOP_LATENCY_1S) {
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
- } else if (lat < VOP_LATENCY_10S) {
- atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
- atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
- atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
} else {
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
- atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
}
}
}
return (err);
--- 3313,3326 ----
*** 3510,3528 ****
if (lat < VOP_LATENCY_100MS)
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
else if (lat < VOP_LATENCY_1S) {
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
- } else if (lat < VOP_LATENCY_10S) {
- atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
- atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
- atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
} else {
atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
- atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
}
}
}
return (err);
--- 3376,3389 ----
*** 3686,3697 ****
ret = (*(dvp)->v_op->vop_lookup)
(dvp, nm, vpp, pnp, flags, rdir, cr, ct, deflags, ppnp);
}
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, lookup);
! vn_updatepath(dvp, *vpp, nm);
}
return (ret);
}
int
--- 3547,3560 ----
ret = (*(dvp)->v_op->vop_lookup)
(dvp, nm, vpp, pnp, flags, rdir, cr, ct, deflags, ppnp);
}
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, lookup);
! if ((*vpp)->v_path == NULL) {
! vn_setpath(rootdir, dvp, *vpp, nm, strlen(nm));
}
+ }
return (ret);
}
int
*** 3726,3737 ****
ret = (*(dvp)->v_op->vop_create)
(dvp, name, vap, excl, mode, vpp, cr, flags, ct, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, create);
! vn_updatepath(dvp, *vpp, name);
}
return (ret);
}
int
--- 3589,3602 ----
ret = (*(dvp)->v_op->vop_create)
(dvp, name, vap, excl, mode, vpp, cr, flags, ct, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, create);
! if ((*vpp)->v_path == NULL) {
! vn_setpath(rootdir, dvp, *vpp, name, strlen(name));
}
+ }
return (ret);
}
int
*** 3846,3857 ****
ret = (*(dvp)->v_op->vop_mkdir)
(dvp, dirname, vap, vpp, cr, ct, flags, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, mkdir);
! vn_updatepath(dvp, *vpp, dirname);
}
return (ret);
}
int
--- 3711,3725 ----
ret = (*(dvp)->v_op->vop_mkdir)
(dvp, dirname, vap, vpp, cr, ct, flags, vsecp);
if (ret == 0 && *vpp) {
VOPSTATS_UPDATE(*vpp, mkdir);
! if ((*vpp)->v_path == NULL) {
! vn_setpath(rootdir, dvp, *vpp, dirname,
! strlen(dirname));
}
+ }
return (ret);
}
int