summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-07-28 01:23:59 +0000
committerkib <kib@FreeBSD.org>2014-07-28 01:23:59 +0000
commit12dac89c4e7bb75b1406bae66a8d87489c9f4f98 (patch)
tree7d9b9fe708c6dbd530a728bc3b84533759ac16a5
parent20a66511817c069bc8532bba59681731e3bdb85f (diff)
downloadFreeBSD-src-12dac89c4e7bb75b1406bae66a8d87489c9f4f98.zip
FreeBSD-src-12dac89c4e7bb75b1406bae66a8d87489c9f4f98.tar.gz
MFC r268617:
Rework the tmpfs unmount.
-rw-r--r--sys/fs/tmpfs/tmpfs.h2
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c25
-rw-r--r--sys/fs/tmpfs/tmpfs_vfsops.c74
3 files changed, 74 insertions, 27 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
index 644f5a9..445bf61 100644
--- a/sys/fs/tmpfs/tmpfs.h
+++ b/sys/fs/tmpfs/tmpfs.h
@@ -384,7 +384,7 @@ struct tmpfs_fid {
* Prototypes for tmpfs_subr.c.
*/
-int tmpfs_alloc_node(struct tmpfs_mount *, enum vtype,
+int tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *, enum vtype,
uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *,
char *, dev_t, struct tmpfs_node **);
void tmpfs_free_node(struct tmpfs_mount *, struct tmpfs_node *);
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index 1770d0b..04ccd18 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -159,7 +159,7 @@ tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages)
* Returns zero on success or an appropriate error code on failure.
*/
int
-tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
+tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type,
uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent,
char *target, dev_t rdev, struct tmpfs_node **node)
{
@@ -169,6 +169,8 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
/* If the root directory of the 'tmp' file system is not yet
* allocated, this must be the request to do it. */
MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR));
+ KASSERT(tmp->tm_root == NULL || mp->mnt_writeopcount > 0,
+ ("creating node not under vn_start_write"));
MPASS(IFF(type == VLNK, target != NULL));
MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL));
@@ -178,6 +180,24 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
if (tmpfs_pages_check_avail(tmp, 1) == 0)
return (ENOSPC);
+ if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0) {
+ /*
+ * When a new tmpfs node is created for fully
+ * constructed mount point, there must be a parent
+ * node, which vnode is locked exclusively. As
+ * consequence, if the unmount is executing in
+ * parallel, vflush() cannot reclaim the parent vnode.
+ * Due to this, the check for MNTK_UNMOUNT flag is not
+ * racy: if we did not see MNTK_UNMOUNT flag, then tmp
+ * cannot be destroyed until node construction is
+ * finished and the parent vnode unlocked.
+ *
+ * Tmpfs does not need to instantiate new nodes during
+ * unmount.
+ */
+ return (EBUSY);
+ }
+
nnode = (struct tmpfs_node *)uma_zalloc_arg(
tmp->tm_node_pool, tmp, M_WAITOK);
@@ -687,7 +707,8 @@ tmpfs_alloc_file(struct vnode *dvp, struct vnode **vpp, struct vattr *vap,
parent = NULL;
/* Allocate a node that represents the new file. */
- error = tmpfs_alloc_node(tmp, vap->va_type, cnp->cn_cred->cr_uid,
+ error = tmpfs_alloc_node(dvp->v_mount, tmp, vap->va_type,
+ cnp->cn_cred->cr_uid,
dnode->tn_gid, vap->va_mode, parent, target, vap->va_rdev, &node);
if (error != 0)
return (error);
diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c
index 1773f71..cb4a32b 100644
--- a/sys/fs/tmpfs/tmpfs_vfsops.c
+++ b/sys/fs/tmpfs/tmpfs_vfsops.c
@@ -238,7 +238,7 @@ tmpfs_mount(struct mount *mp)
tmp->tm_ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
/* Allocate the root node. */
- error = tmpfs_alloc_node(tmp, VDIR, root_uid,
+ error = tmpfs_alloc_node(mp, tmp, VDIR, root_uid,
root_gid, root_mode & ALLPERMS, NULL, NULL,
VNOVAL, &root);
@@ -269,38 +269,49 @@ tmpfs_mount(struct mount *mp)
static int
tmpfs_unmount(struct mount *mp, int mntflags)
{
- int error;
- int flags = 0;
struct tmpfs_mount *tmp;
struct tmpfs_node *node;
+ int error, flags;
- /* Handle forced unmounts. */
- if (mntflags & MNT_FORCE)
- flags |= FORCECLOSE;
-
- /* Finalize all pending I/O. */
- error = vflush(mp, 0, flags, curthread);
- if (error != 0)
- return error;
-
+ flags = (mntflags & MNT_FORCE) != 0 ? FORCECLOSE : 0;
tmp = VFS_TO_TMPFS(mp);
- /* Free all associated data. The loop iterates over the linked list
- * we have containing all used nodes. For each of them that is
- * a directory, we free all its directory entries. Note that after
- * freeing a node, it will automatically go to the available list,
- * so we will later have to iterate over it to release its items. */
- node = LIST_FIRST(&tmp->tm_nodes_used);
- while (node != NULL) {
- struct tmpfs_node *next;
+ /* Stop writers */
+ error = vfs_write_suspend_umnt(mp);
+ if (error != 0)
+ return (error);
+ /*
+ * At this point, nodes cannot be destroyed by any other
+ * thread because write suspension is started.
+ */
+
+ for (;;) {
+ error = vflush(mp, 0, flags, curthread);
+ if (error != 0) {
+ vfs_write_resume(mp, VR_START_WRITE);
+ return (error);
+ }
+ MNT_ILOCK(mp);
+ if (mp->mnt_nvnodelistsize == 0) {
+ MNT_IUNLOCK(mp);
+ break;
+ }
+ MNT_IUNLOCK(mp);
+ if ((mntflags & MNT_FORCE) == 0) {
+ vfs_write_resume(mp, VR_START_WRITE);
+ return (EBUSY);
+ }
+ }
+ TMPFS_LOCK(tmp);
+ while ((node = LIST_FIRST(&tmp->tm_nodes_used)) != NULL) {
+ TMPFS_UNLOCK(tmp);
if (node->tn_type == VDIR)
tmpfs_dir_destroy(tmp, node);
-
- next = LIST_NEXT(node, tn_entries);
tmpfs_free_node(tmp, node);
- node = next;
+ TMPFS_LOCK(tmp);
}
+ TMPFS_UNLOCK(tmp);
uma_zdestroy(tmp->tm_dirent_pool);
uma_zdestroy(tmp->tm_node_pool);
@@ -313,11 +324,13 @@ tmpfs_unmount(struct mount *mp, int mntflags)
/* Throw away the tmpfs_mount structure. */
free(mp->mnt_data, M_TMPFSMNT);
mp->mnt_data = NULL;
+ vfs_write_resume(mp, VR_START_WRITE);
MNT_ILOCK(mp);
mp->mnt_flag &= ~MNT_LOCAL;
MNT_IUNLOCK(mp);
- return 0;
+
+ return (0);
}
static int
@@ -401,6 +414,18 @@ tmpfs_statfs(struct mount *mp, struct statfs *sbp)
return 0;
}
+static int
+tmpfs_sync(struct mount *mp, int waitfor)
+{
+
+ if (waitfor == MNT_SUSPEND) {
+ MNT_ILOCK(mp);
+ mp->mnt_kern_flag |= MNTK_SUSPEND2 | MNTK_SUSPENDED;
+ MNT_IUNLOCK(mp);
+ }
+ return (0);
+}
+
/*
* tmpfs vfs operations.
*/
@@ -411,5 +436,6 @@ struct vfsops tmpfs_vfsops = {
.vfs_root = tmpfs_root,
.vfs_statfs = tmpfs_statfs,
.vfs_fhtovp = tmpfs_fhtovp,
+ .vfs_sync = tmpfs_sync,
};
VFS_SET(tmpfs_vfsops, tmpfs, VFCF_JAIL);
OpenPOWER on IntegriCloud