svn commit: r197953 - head/sys/fs/tmpfs
Xin LI
delphij at FreeBSD.org
Sun Oct 11 07:03:57 UTC 2009
Author: delphij
Date: Sun Oct 11 07:03:56 2009
New Revision: 197953
URL: http://svn.freebsd.org/changeset/base/197953
Log:
Add locking around access to parent node, and bail out when the parent
node is already freed rather than panicking the system.
PR: kern/122038
Submitted by: gk
Tested by: pho
MFC after: 1 week
Modified:
head/sys/fs/tmpfs/tmpfs.h
head/sys/fs/tmpfs/tmpfs_subr.c
head/sys/fs/tmpfs/tmpfs_vnops.c
Modified: head/sys/fs/tmpfs/tmpfs.h
==============================================================================
--- head/sys/fs/tmpfs/tmpfs.h Sun Oct 11 05:59:43 2009 (r197952)
+++ head/sys/fs/tmpfs/tmpfs.h Sun Oct 11 07:03:56 2009 (r197953)
@@ -303,10 +303,30 @@ LIST_HEAD(tmpfs_node_list, tmpfs_node);
#define TMPFS_NODE_LOCK(node) mtx_lock(&(node)->tn_interlock)
#define TMPFS_NODE_UNLOCK(node) mtx_unlock(&(node)->tn_interlock)
-#define TMPFS_NODE_MTX(node) (&(node)->tn_interlock)
+#define TMPFS_NODE_MTX(node) (&(node)->tn_interlock)
+
+#ifdef INVARIANTS
+#define TMPFS_ASSERT_LOCKED(node) do { \
+ MPASS(node != NULL); \
+ MPASS(node->tn_vnode != NULL); \
+ if (!VOP_ISLOCKED(node->tn_vnode) && \
+ !mtx_owned(TMPFS_NODE_MTX(node))) \
+ panic("tmpfs: node is not locked: %p", node); \
+ } while (0)
+#define TMPFS_ASSERT_ELOCKED(node) do { \
+ MPASS((node) != NULL); \
+ MPASS((node)->tn_vnode != NULL); \
+ mtx_assert(TMPFS_NODE_MTX(node), MA_OWNED); \
+ ASSERT_VOP_LOCKED((node)->tn_vnode, "tmpfs"); \
+ } while (0)
+#else
+#define TMPFS_ASSERT_LOCKED(node) (void)0
+#define TMPFS_ASSERT_ELOCKED(node) (void)0
+#endif
#define TMPFS_VNODE_ALLOCATING 1
#define TMPFS_VNODE_WANT 2
+#define TMPFS_VNODE_DOOMED 4
/* --------------------------------------------------------------------- */
/*
Modified: head/sys/fs/tmpfs/tmpfs_subr.c
==============================================================================
--- head/sys/fs/tmpfs/tmpfs_subr.c Sun Oct 11 05:59:43 2009 (r197952)
+++ head/sys/fs/tmpfs/tmpfs_subr.c Sun Oct 11 07:03:56 2009 (r197953)
@@ -124,7 +124,9 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp
nnode->tn_dir.tn_readdir_lastn = 0;
nnode->tn_dir.tn_readdir_lastp = NULL;
nnode->tn_links++;
+ TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
nnode->tn_dir.tn_parent->tn_links++;
+ TMPFS_NODE_UNLOCK(nnode->tn_dir.tn_parent);
break;
case VFIFO:
@@ -187,6 +189,7 @@ tmpfs_free_node(struct tmpfs_mount *tmp,
#ifdef INVARIANTS
TMPFS_NODE_LOCK(node);
MPASS(node->tn_vnode == NULL);
+ MPASS((node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0);
TMPFS_NODE_UNLOCK(node);
#endif
@@ -312,6 +315,7 @@ tmpfs_alloc_vp(struct mount *mp, struct
loop:
TMPFS_NODE_LOCK(node);
if ((vp = node->tn_vnode) != NULL) {
+ MPASS((node->tn_vpstate & TMPFS_VNODE_DOOMED) == 0);
VI_LOCK(vp);
TMPFS_NODE_UNLOCK(node);
vholdl(vp);
@@ -330,6 +334,14 @@ loop:
goto out;
}
+ if ((node->tn_vpstate & TMPFS_VNODE_DOOMED) ||
+ (node->tn_type == VDIR && node->tn_dir.tn_parent == NULL)) {
+ TMPFS_NODE_UNLOCK(node);
+ error = ENOENT;
+ vp = NULL;
+ goto out;
+ }
+
/*
* otherwise lock the vp list while we call getnewvnode
* since that can block.
@@ -375,6 +387,7 @@ loop:
vp->v_op = &tmpfs_fifoop_entries;
break;
case VDIR:
+ MPASS(node->tn_dir.tn_parent != NULL);
if (node->tn_dir.tn_parent == node)
vp->v_vflag |= VV_ROOT;
break;
@@ -428,10 +441,9 @@ tmpfs_free_vp(struct vnode *vp)
node = VP_TO_TMPFS_NODE(vp);
- TMPFS_NODE_LOCK(node);
+ mtx_assert(TMPFS_NODE_MTX(node), MA_OWNED);
node->tn_vnode = NULL;
vp->v_data = NULL;
- TMPFS_NODE_UNLOCK(node);
}
/* --------------------------------------------------------------------- */
@@ -653,7 +665,18 @@ tmpfs_dir_getdotdotdent(struct tmpfs_nod
TMPFS_VALIDATE_DIR(node);
MPASS(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT);
+ /*
+ * Return ENOENT if the current node is already removed.
+ */
+ TMPFS_ASSERT_LOCKED(node);
+ if (node->tn_dir.tn_parent == NULL) {
+ return (ENOENT);
+ }
+
+ TMPFS_NODE_LOCK(node->tn_dir.tn_parent);
dent.d_fileno = node->tn_dir.tn_parent->tn_id;
+ TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent);
+
dent.d_type = DT_DIR;
dent.d_namlen = 2;
dent.d_name[0] = '.';
Modified: head/sys/fs/tmpfs/tmpfs_vnops.c
==============================================================================
--- head/sys/fs/tmpfs/tmpfs_vnops.c Sun Oct 11 05:59:43 2009 (r197952)
+++ head/sys/fs/tmpfs/tmpfs_vnops.c Sun Oct 11 07:03:56 2009 (r197953)
@@ -87,6 +87,11 @@ tmpfs_lookup(struct vop_cachedlookup_arg
dnode->tn_dir.tn_parent == dnode,
!(cnp->cn_flags & ISDOTDOT)));
+ TMPFS_ASSERT_LOCKED(dnode);
+ if (dnode->tn_dir.tn_parent == NULL) {
+ error = ENOENT;
+ goto out;
+ }
if (cnp->cn_flags & ISDOTDOT) {
int ltype = 0;
@@ -914,6 +919,7 @@ tmpfs_rename(struct vop_rename_args *v)
char *newname;
int error;
struct tmpfs_dirent *de;
+ struct tmpfs_mount *tmp;
struct tmpfs_node *fdnode;
struct tmpfs_node *fnode;
struct tmpfs_node *tnode;
@@ -934,6 +940,7 @@ tmpfs_rename(struct vop_rename_args *v)
goto out;
}
+ tmp = VFS_TO_TMPFS(tdvp->v_mount);
tdnode = VP_TO_TMPFS_DIR(tdvp);
/* If source and target are the same file, there is nothing to do. */
@@ -1018,25 +1025,63 @@ tmpfs_rename(struct vop_rename_args *v)
* directory being moved. Otherwise, we'd end up
* with stale nodes. */
n = tdnode;
+ /* TMPFS_LOCK garanties that no nodes are freed while
+ * traversing the list. Nodes can only be marked as
+ * removed: tn_parent == NULL. */
+ TMPFS_LOCK(tmp);
+ TMPFS_NODE_LOCK(n);
while (n != n->tn_dir.tn_parent) {
+ struct tmpfs_node *parent;
+
if (n == fnode) {
+ TMPFS_NODE_UNLOCK(n);
+ TMPFS_UNLOCK(tmp);
error = EINVAL;
if (newname != NULL)
free(newname, M_TMPFSNAME);
goto out_locked;
}
- n = n->tn_dir.tn_parent;
+ parent = n->tn_dir.tn_parent;
+ TMPFS_NODE_UNLOCK(n);
+ if (parent == NULL) {
+ n = NULL;
+ break;
+ }
+ TMPFS_NODE_LOCK(parent);
+ if (parent->tn_dir.tn_parent == NULL) {
+ TMPFS_NODE_UNLOCK(parent);
+ n = NULL;
+ break;
+ }
+ n = parent;
+ }
+ TMPFS_UNLOCK(tmp);
+ if (n == NULL) {
+ error = EINVAL;
+ if (newname != NULL)
+ free(newname, M_TMPFSNAME);
+ goto out_locked;
}
+ TMPFS_NODE_UNLOCK(n);
/* Adjust the parent pointer. */
TMPFS_VALIDATE_DIR(fnode);
+ TMPFS_NODE_LOCK(de->td_node);
de->td_node->tn_dir.tn_parent = tdnode;
+ TMPFS_NODE_UNLOCK(de->td_node);
/* As a result of changing the target of the '..'
* entry, the link count of the source and target
* directories has to be adjusted. */
- fdnode->tn_links--;
+ TMPFS_NODE_LOCK(tdnode);
+ TMPFS_ASSERT_LOCKED(tdnode);
tdnode->tn_links++;
+ TMPFS_NODE_UNLOCK(tdnode);
+
+ TMPFS_NODE_LOCK(fdnode);
+ TMPFS_ASSERT_LOCKED(fdnode);
+ fdnode->tn_links--;
+ TMPFS_NODE_UNLOCK(fdnode);
}
/* Do the move: just remove the entry from the source directory
@@ -1163,15 +1208,26 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
goto out;
}
+
/* Detach the directory entry from the directory (dnode). */
tmpfs_dir_detach(dvp, de);
+ /* No vnode should be allocated for this entry from this point */
+ TMPFS_NODE_LOCK(node);
+ TMPFS_ASSERT_ELOCKED(node);
node->tn_links--;
+ node->tn_dir.tn_parent = NULL;
node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \
TMPFS_NODE_MODIFIED;
- node->tn_dir.tn_parent->tn_links--;
- node->tn_dir.tn_parent->tn_status |= TMPFS_NODE_ACCESSED | \
+
+ TMPFS_NODE_UNLOCK(node);
+
+ TMPFS_NODE_LOCK(dnode);
+ TMPFS_ASSERT_ELOCKED(dnode);
+ dnode->tn_links--;
+ dnode->tn_status |= TMPFS_NODE_ACCESSED | \
TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED;
+ TMPFS_NODE_UNLOCK(dnode);
cache_purge(dvp);
cache_purge(vp);
@@ -1359,13 +1415,21 @@ tmpfs_reclaim(struct vop_reclaim_args *v
vnode_destroy_vobject(vp);
cache_purge(vp);
+
+ TMPFS_NODE_LOCK(node);
+ TMPFS_ASSERT_ELOCKED(node);
tmpfs_free_vp(vp);
/* If the node referenced by this vnode was deleted by the user,
* we must free its associated data structures (now that the vnode
* is being reclaimed). */
- if (node->tn_links == 0)
+ if (node->tn_links == 0 &&
+ (node->tn_vpstate & TMPFS_VNODE_ALLOCATING) == 0) {
+ node->tn_vpstate = TMPFS_VNODE_DOOMED;
+ TMPFS_NODE_UNLOCK(node);
tmpfs_free_node(tmp, node);
+ } else
+ TMPFS_NODE_UNLOCK(node);
MPASS(vp->v_data == NULL);
return 0;
More information about the svn-src-head
mailing list