summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/fs/dev/sdev_subr.c13
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vtops.c105
2 files changed, 77 insertions, 41 deletions
diff --git a/usr/src/uts/common/fs/dev/sdev_subr.c b/usr/src/uts/common/fs/dev/sdev_subr.c
index 3fced7bb9c..a47493ab55 100644
--- a/usr/src/uts/common/fs/dev/sdev_subr.c
+++ b/usr/src/uts/common/fs/dev/sdev_subr.c
@@ -2573,6 +2573,15 @@ get_cache:
/*
* Check validity of node
+ * Drop invalid and nodes to be skipped.
+ * A node the validator indicates as stale needs
+ * to be returned as presumably the node name itself
+ * is valid and the node data itself will be refreshed
+ * on lookup. An application performing a readdir then
+ * stat on each entry should thus always see consistent
+ * data. In any case, it is not possible to synchronize
+ * with dynamic kernel state, and any view we return can
+ * never be anything more than a snapshot at a point in time.
*/
if (vtor) {
switch (vtor(dv)) {
@@ -2581,6 +2590,10 @@ get_cache:
case SDEV_VTOR_INVALID:
case SDEV_VTOR_SKIP:
continue;
+ case SDEV_VTOR_STALE:
+ sdcmn_err3(("sdev_readir: %s stale\n",
+ dv->sdev_name));
+ break;
default:
cmn_err(CE_PANIC,
"dev fs: validator failed: %s(%p)\n",
diff --git a/usr/src/uts/common/fs/dev/sdev_vtops.c b/usr/src/uts/common/fs/dev/sdev_vtops.c
index a249110639..39b82c4285 100644
--- a/usr/src/uts/common/fs/dev/sdev_vtops.c
+++ b/usr/src/uts/common/fs/dev/sdev_vtops.c
@@ -88,15 +88,22 @@ devvt_str2minor(const char *nm, minor_t *mp)
return (0);
}
-/*ARGSUSED*/
+/*
+ * Validate that a node is up-to-date and correct.
+ * A validator may not update the node state or
+ * contents as a read lock permits entry by
+ * multiple threads.
+ */
int
devvt_validate(struct sdev_node *dv)
{
minor_t min;
char *nm = dv->sdev_name;
+ int rval;
ASSERT(!(dv->sdev_flags & SDEV_STALE));
ASSERT(dv->sdev_state == SDEV_READY);
+ ASSERT(RW_LOCK_HELD(&(dv->sdev_dotdot)->sdev_contents));
/* validate only READY nodes */
if (dv->sdev_state != SDEV_READY) {
@@ -110,28 +117,20 @@ devvt_validate(struct sdev_node *dv)
if (strcmp(nm, DEVVT_ACTIVE_NAME) == 0) {
char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
-
(void) vt_getactive(link, MAXPATHLEN);
- if (strcmp(link, dv->sdev_symlink) != 0) {
- strfree(dv->sdev_symlink);
- dv->sdev_symlink = strdup(link);
- dv->sdev_attr->va_size = strlen(link);
- }
+ rval = (strcmp(link, dv->sdev_symlink) == 0) ?
+ SDEV_VTOR_VALID : SDEV_VTOR_STALE;
kmem_free(link, MAXPATHLEN);
- return (SDEV_VTOR_VALID);
+ return (rval);
}
if (strcmp(nm, DEVVT_CONSUSER_NAME) == 0) {
char *link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
-
(void) vt_getconsuser(link, MAXPATHLEN);
- if (strcmp(link, dv->sdev_symlink) != 0) {
- strfree(dv->sdev_symlink);
- dv->sdev_symlink = strdup(link);
- dv->sdev_attr->va_size = strlen(link);
- }
+ rval = (strcmp(link, dv->sdev_symlink) == 0) ?
+ SDEV_VTOR_VALID : SDEV_VTOR_STALE;
kmem_free(link, MAXPATHLEN);
- return (SDEV_VTOR_VALID);
+ return (rval);
}
if (devvt_str2minor(nm, &min) != 0) {
@@ -238,6 +237,8 @@ devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
major_t maj;
minor_t min;
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+
if ((maj = vt_wc_attached()) == (major_t)-1)
return;
@@ -279,6 +280,36 @@ devvt_create_snode(struct sdev_node *ddv, char *nm, struct cred *cred, int type)
}
static void
+devvt_rebuild_stale_link(struct sdev_node *ddv, struct sdev_node *dv)
+{
+ char *link;
+
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+
+ ASSERT((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) ||
+ (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0));
+
+ link = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
+ if (strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == 0) {
+ (void) vt_getactive(link, MAXPATHLEN);
+ } else if (strcmp(dv->sdev_name, DEVVT_CONSUSER_NAME) == 0) {
+ (void) vt_getconsuser(link, MAXPATHLEN);
+ }
+
+ if (strcmp(link, dv->sdev_symlink) != 0) {
+ strfree(dv->sdev_symlink);
+ dv->sdev_symlink = strdup(link);
+ dv->sdev_attr->va_size = strlen(link);
+ }
+ kmem_free(link, MAXPATHLEN);
+}
+
+/*
+ * First step in refreshing directory contents.
+ * Remove each invalid entry and rebuild the link
+ * reference for each stale entry.
+ */
+static void
devvt_prunedir(struct sdev_node *ddv)
{
struct vnode *vp;
@@ -293,31 +324,24 @@ devvt_prunedir(struct sdev_node *ddv)
for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
next = SDEV_NEXT_ENTRY(ddv, dv);
- /* skip stale nodes */
- if (dv->sdev_flags & SDEV_STALE)
- continue;
-
- /* validate and prune only ready nodes */
- if (dv->sdev_state != SDEV_READY)
- continue;
-
switch (vtor(dv)) {
case SDEV_VTOR_VALID:
+ break;
case SDEV_VTOR_SKIP:
- continue;
+ break;
case SDEV_VTOR_INVALID:
+ vp = SDEVTOV(dv);
+ if (vp->v_count != 0)
+ break;
+ /* remove the cached node */
+ SDEV_HOLD(dv);
+ (void) sdev_cache_update(ddv, &dv,
+ dv->sdev_name, SDEV_CACHE_DELETE);
+ break;
case SDEV_VTOR_STALE:
- sdcmn_err7(("destroy invalid "
- "node: %s(%p)\n", dv->sdev_name, (void *)dv));
+ devvt_rebuild_stale_link(ddv, dv);
break;
}
- vp = SDEVTOV(dv);
- if (vp->v_count > 0)
- continue;
- SDEV_HOLD(dv);
- /* remove the cache node */
- (void) sdev_cache_update(ddv, &dv, dv->sdev_name,
- SDEV_CACHE_DELETE);
}
}
@@ -343,7 +367,10 @@ devvt_cleandir(struct vnode *dvp, struct cred *cred)
rw_enter(&sdvp->sdev_contents, RW_WRITER);
#endif
- /* 1. create missed nodes */
+ /* 1. prune invalid nodes and rebuild stale symlinks */
+ devvt_prunedir(sdvp);
+
+ /* 2. create missing nodes */
for (min = 0; min < cnt; min++) {
char nm[16];
@@ -358,7 +385,7 @@ devvt_cleandir(struct vnode *dvp, struct cred *cred)
/* skip stale nodes */
if (dv->sdev_flags & SDEV_STALE)
continue;
- /* validate and prune only ready nodes */
+ /* validate only ready nodes */
if (dv->sdev_state != SDEV_READY)
continue;
if (strcmp(nm, dv->sdev_name) == 0) {
@@ -371,7 +398,7 @@ devvt_cleandir(struct vnode *dvp, struct cred *cred)
}
}
- /* 2. create active link node and console user link node */
+ /* 3. create active link node and console user link node */
found = 0;
for (dv = SDEV_FIRST_ENTRY(sdvp); dv; dv = next) {
next = SDEV_NEXT_ENTRY(sdvp, dv);
@@ -379,7 +406,7 @@ devvt_cleandir(struct vnode *dvp, struct cred *cred)
/* skip stale nodes */
if (dv->sdev_flags & SDEV_STALE)
continue;
- /* validate and prune only ready nodes */
+ /* validate only ready nodes */
if (dv->sdev_state != SDEV_READY)
continue;
if ((strcmp(dv->sdev_name, DEVVT_ACTIVE_NAME) == NULL))
@@ -392,13 +419,9 @@ devvt_cleandir(struct vnode *dvp, struct cred *cred)
}
if (!(found & 0x01))
devvt_create_snode(sdvp, DEVVT_ACTIVE_NAME, cred, SDEV_VLINK);
-
if (!(found & 0x02))
devvt_create_snode(sdvp, DEVVT_CONSUSER_NAME, cred, SDEV_VLINK);
- /* 3. cleanup invalid nodes */
- devvt_prunedir(sdvp);
-
#ifndef __lock_lint
rw_downgrade(&sdvp->sdev_contents);
#else