From: Matthew Dillon Date: Sat, 13 Feb 2010 07:57:53 +0000 (-0800) Subject: kernel - TMPFS - Bug fixing pass - vinitvmio(), umount, readdir X-Git-Tag: v2.7.1~168 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/22d3b3946bbeccc18eb6c307b2d8075024cfbd18 kernel - TMPFS - Bug fixing pass - vinitvmio(), umount, readdir * Call vinitvmio() with a properly bufcache-aligned number of pages * Do not call vinitvmio() for symlinks or directories (we don't use the buffer cache for those vnode types). * Adjust tmpfs_dir_attach() and tmpfs_dir_detach() so the detach code can be called from umount, and call it from umount to fix a panic. * tmpfs_dir_getdotdotdent() needed to be adjusted to handle the root vnode's NULL parent pointer. Fixed empty root directories for the mount. * Acquire TMPFS_NODE_LOCK() around opreations on dnode->tn_dir.tn_dirhead. --- diff --git a/sys/vfs/tmpfs/tmpfs.h b/sys/vfs/tmpfs/tmpfs.h index 647c0ddf6b..4c1b03da53 100644 --- a/sys/vfs/tmpfs/tmpfs.h +++ b/sys/vfs/tmpfs/tmpfs.h @@ -431,13 +431,14 @@ int tmpfs_alloc_vp(struct mount *, struct tmpfs_node *, int, void tmpfs_free_vp(struct vnode *); int tmpfs_alloc_file(struct vnode *, struct vnode **, struct vattr *, struct namecache *, struct ucred *, char *); -void tmpfs_dir_attach(struct vnode *, struct tmpfs_dirent *); -void tmpfs_dir_detach(struct vnode *, struct tmpfs_dirent *); +void tmpfs_dir_attach(struct tmpfs_node *, struct tmpfs_dirent *); +void tmpfs_dir_detach(struct tmpfs_node *, struct tmpfs_dirent *); struct tmpfs_dirent * tmpfs_dir_lookup(struct tmpfs_node *node, struct tmpfs_node *f, struct namecache *ncp); int tmpfs_dir_getdotdent(struct tmpfs_node *, struct uio *); -int tmpfs_dir_getdotdotdent(struct tmpfs_node *, struct uio *); +int tmpfs_dir_getdotdotdent(struct tmpfs_mount *, + struct tmpfs_node *, struct uio *); struct tmpfs_dirent * tmpfs_dir_lookupbycookie(struct tmpfs_node *, off_t); int tmpfs_dir_getdents(struct tmpfs_node *, struct uio *, off_t *); int tmpfs_reg_resize(struct vnode *, off_t, int); diff --git a/sys/vfs/tmpfs/tmpfs_subr.c b/sys/vfs/tmpfs/tmpfs_subr.c index 8e7005b312..e575d990be 100644 --- a/sys/vfs/tmpfs/tmpfs_subr.c +++ b/sys/vfs/tmpfs/tmpfs_subr.c @@ -444,17 +444,14 @@ loop: case VSOCK: break; case VREG: - vinitvmio(vp, node->tn_size); + vinitvmio(vp, (node->tn_size + BMASK) & ~(off_t)BMASK); break; case VLNK: - if (node->tn_size >= vp->v_mount->mnt_maxsymlinklen) - vinitvmio(vp, node->tn_size); break; case VFIFO: vp->v_ops = &mp->mnt_vn_fifo_ops; break; case VDIR: - vinitvmio(vp, node->tn_size); break; default: @@ -580,7 +577,7 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, /* Now that all required items are allocated, we can proceed to * insert the new node into the directory, an operation that * cannot fail. */ - tmpfs_dir_attach(dvp, de); + tmpfs_dir_attach(dnode, de); TMPFS_NODE_UNLOCK(node); return error; @@ -594,19 +591,15 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, * the directory entry, as this is done by tmpfs_alloc_dirent. */ void -tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de) +tmpfs_dir_attach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) { - struct tmpfs_node *dnode; - - dnode = VP_TO_TMPFS_DIR(vp); - + TMPFS_NODE_LOCK(dnode); TAILQ_INSERT_TAIL(&dnode->tn_dir.tn_dirhead, de, td_entries); - TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); dnode->tn_size += sizeof(struct tmpfs_dirent); - dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ - TMPFS_NODE_MODIFIED; + dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | + TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(dnode); } @@ -618,24 +611,19 @@ tmpfs_dir_attach(struct vnode *vp, struct tmpfs_dirent *de) * the directory entry, as this is done by tmpfs_free_dirent. */ void -tmpfs_dir_detach(struct vnode *vp, struct tmpfs_dirent *de) +tmpfs_dir_detach(struct tmpfs_node *dnode, struct tmpfs_dirent *de) { - struct tmpfs_node *dnode; - - dnode = VP_TO_TMPFS_DIR(vp); - - + TMPFS_NODE_LOCK(dnode); if (dnode->tn_dir.tn_readdir_lastp == de) { dnode->tn_dir.tn_readdir_lastn = 0; dnode->tn_dir.tn_readdir_lastp = NULL; } TAILQ_REMOVE(&dnode->tn_dir.tn_dirhead, de, td_entries); - TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); dnode->tn_size -= sizeof(struct tmpfs_dirent); - dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ - TMPFS_NODE_MODIFIED; + dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | + TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(dnode); } @@ -723,7 +711,8 @@ tmpfs_dir_getdotdent(struct tmpfs_node *node, struct uio *uio) * error happens. */ int -tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) +tmpfs_dir_getdotdotdent(struct tmpfs_mount *tmp, struct tmpfs_node *node, + struct uio *uio) { int error; struct dirent dent; @@ -732,17 +721,14 @@ tmpfs_dir_getdotdotdent(struct tmpfs_node *node, struct uio *uio) TMPFS_VALIDATE_DIR(node); KKASSERT(uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT); - /* - * Return ENOENT if the current node is already removed. - */ - if (node->tn_dir.tn_parent == NULL) { - return (ENOENT); + if (node->tn_dir.tn_parent) { + TMPFS_NODE_LOCK(node->tn_dir.tn_parent); + dent.d_ino = node->tn_dir.tn_parent->tn_id; + TMPFS_NODE_UNLOCK(node->tn_dir.tn_parent); + } else { + dent.d_ino = tmp->tm_root->tn_id; } - TMPFS_NODE_LOCK(node->tn_dir.tn_parent); - dent.d_ino = 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] = '.'; @@ -961,19 +947,24 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize, int trivial) * When adjusting the vnode filesize and its VM object we must * also adjust our backing VM object (aobj). The blocksize * used must match the block sized we use for the buffer cache. + * + * The backing VM object contains no VM pages, only swap + * assignments. */ if (newsize < oldsize) { vm_pindex_t osize; + vm_pindex_t nsize; vm_object_t aobj; error = nvtruncbuf(vp, newsize, BSIZE, -1); aobj = node->tn_reg.tn_aobj; if (aobj) { osize = aobj->size; - if (osize > vp->v_object->size) { + nsize = vp->v_object->size; + if (nsize < osize) { aobj->size = osize; - vm_object_page_remove(aobj, vp->v_object->size, - osize, FALSE); + swap_pager_freespace(aobj, nsize, + osize - nsize); } } } else { diff --git a/sys/vfs/tmpfs/tmpfs_vfsops.c b/sys/vfs/tmpfs/tmpfs_vfsops.c index ce0165da23..338ede673a 100644 --- a/sys/vfs/tmpfs/tmpfs_vfsops.c +++ b/sys/vfs/tmpfs/tmpfs_vfsops.c @@ -337,6 +337,7 @@ tmpfs_unmount(struct mount *mp, int mntflags) while (!TAILQ_EMPTY(&node->tn_dir.tn_dirhead)) { de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); + tmpfs_dir_detach(node, de); tmpfs_free_dirent(tmp, de); node->tn_size -= sizeof(struct tmpfs_dirent); } diff --git a/sys/vfs/tmpfs/tmpfs_vnops.c b/sys/vfs/tmpfs/tmpfs_vnops.c index f3f92c7259..10663d48ba 100644 --- a/sys/vfs/tmpfs/tmpfs_vnops.c +++ b/sys/vfs/tmpfs/tmpfs_vnops.c @@ -786,7 +786,7 @@ tmpfs_nremove(struct vop_nremove_args *v) /* Remove the entry from the directory; as it is a file, we do not * have to change the number of hard links of the directory. */ - tmpfs_dir_detach(dvp, de); + tmpfs_dir_detach(dnode, de); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really @@ -819,14 +819,15 @@ tmpfs_nlink(struct vop_nlink_args *v) struct vnode *dvp = v->a_dvp; struct vnode *vp = v->a_vp; struct namecache *ncp = v->a_nch->ncp; - - int error; struct tmpfs_dirent *de; struct tmpfs_node *node; + struct tmpfs_node *dnode; + int error; KKASSERT(dvp != vp); /* XXX When can this be false? */ node = VP_TO_TMPFS_NODE(vp); + dnode = VP_TO_TMPFS_NODE(dvp); /* XXX: Why aren't the following two tests done by the caller? */ @@ -863,7 +864,7 @@ tmpfs_nlink(struct vop_nlink_args *v) goto out; /* Insert the new directory entry into the appropriate directory. */ - tmpfs_dir_attach(dvp, de); + tmpfs_dir_attach(dnode, de); /* vp link count has changed, so update node times. */ @@ -891,15 +892,14 @@ tmpfs_nrename(struct vop_nrename_args *v) struct vnode *tdvp = v->a_tdvp; struct namecache *tncp = v->a_tnch->ncp; struct vnode *tvp = tncp->nc_vp; - - char *newname; - int error; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *fdnode; struct tmpfs_node *fnode; struct tmpfs_node *tnode; struct tmpfs_node *tdnode; + char *newname; + int error; tnode = (tvp == NULL) ? NULL : VP_TO_TMPFS_NODE(tvp); @@ -1049,8 +1049,8 @@ tmpfs_nrename(struct vop_nrename_args *v) /* Do the move: just remove the entry from the source directory * and insert it into the target one. */ - tmpfs_dir_detach(fdvp, de); - tmpfs_dir_attach(tdvp, de); + tmpfs_dir_detach(fdnode, de); + tmpfs_dir_attach(tdnode, de); } /* If the name has changed, we need to make it effective by changing @@ -1078,7 +1078,7 @@ tmpfs_nrename(struct vop_nrename_args *v) if (tvp != NULL) { /* Remove the old entry from the target directory. */ de = tmpfs_dir_lookup(tdnode, tnode, tncp); - tmpfs_dir_detach(tdvp, de); + tmpfs_dir_detach(tdnode, de); /* Free the directory entry we just deleted. Note that the * node referred by it will not be removed until the vnode is @@ -1187,7 +1187,7 @@ tmpfs_nrmdir(struct vop_nrmdir_args *v) /* Detach the directory entry from the directory (dnode). */ - tmpfs_dir_detach(dvp, de); + tmpfs_dir_detach(dnode, de); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(node); @@ -1272,7 +1272,7 @@ tmpfs_readdir(struct vop_readdir_args *v) int *eofflag = v->a_eofflag; off_t **cookies = v->a_cookies; int *ncookies = v->a_ncookies; - + struct tmpfs_mount *tmp; int error; off_t startoff; off_t cnt = 0; @@ -1282,6 +1282,7 @@ tmpfs_readdir(struct vop_readdir_args *v) if (vp->v_type != VDIR) return ENOTDIR; + tmp = VFS_TO_TMPFS(vp->v_mount); node = VP_TO_TMPFS_DIR(vp); startoff = uio->uio_offset; @@ -1293,7 +1294,7 @@ tmpfs_readdir(struct vop_readdir_args *v) } if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) { - error = tmpfs_dir_getdotdotdent(node, uio); + error = tmpfs_dir_getdotdotdent(tmp, node, uio); if (error != 0) goto outok; cnt++;