svn commit: r212660 - head/sys/fs/devfs
Jaakko Heinonen
jh at FreeBSD.org
Wed Sep 15 14:23:55 UTC 2010
Author: jh
Date: Wed Sep 15 14:23:55 2010
New Revision: 212660
URL: http://svn.freebsd.org/changeset/base/212660
Log:
Remove empty devfs directories automatically.
devfs_delete() now recursively removes empty parent directories unless
the DEVFS_DEL_NORECURSE flag is specified. devfs_delete() can't be
called anymore with a parent directory vnode lock held because the
possible parent directory deletion needs to lock the vnode. Thus we
unlock the parent directory vnode in devfs_remove() before calling
devfs_delete().
Call devfs_populate_vp() from devfs_symlink() and devfs_vptocnp() as now
directories can get removed.
Add a check for DE_DOOMED flag to devfs_populate_vp() because
devfs_delete() drops dm_lock before the VI_DOOMED vnode flag gets set.
This ensures that devfs_populate_vp() returns an error for directories
which are in progress of deletion.
Reviewed by: kib
Discussed on: freebsd-current (mostly silence)
Modified:
head/sys/fs/devfs/devfs.h
head/sys/fs/devfs/devfs_devs.c
head/sys/fs/devfs/devfs_vnops.c
Modified: head/sys/fs/devfs/devfs.h
==============================================================================
--- head/sys/fs/devfs/devfs.h Wed Sep 15 13:45:06 2010 (r212659)
+++ head/sys/fs/devfs/devfs.h Wed Sep 15 14:23:55 2010 (r212660)
@@ -170,12 +170,15 @@ extern unsigned devfs_rule_depth;
#define DEVFS_DMP_HOLD(dmp) ((dmp)->dm_holdcnt++)
#define DEVFS_DMP_DROP(dmp) (--(dmp)->dm_holdcnt == 0)
+#define DEVFS_DEL_VNLOCKED 0x01
+#define DEVFS_DEL_NORECURSE 0x02
+
void devfs_rules_apply(struct devfs_mount *dm, struct devfs_dirent *de);
void devfs_rules_cleanup (struct devfs_mount *dm);
int devfs_rules_ioctl(struct devfs_mount *dm, u_long cmd, caddr_t data, struct thread *td);
int devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode,
struct vnode **vpp);
-void devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int vp_locked);
+void devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int flags);
void devfs_dirent_free(struct devfs_dirent *de);
void devfs_populate (struct devfs_mount *dm);
void devfs_cleanup (struct devfs_mount *dm);
Modified: head/sys/fs/devfs/devfs_devs.c
==============================================================================
--- head/sys/fs/devfs/devfs_devs.c Wed Sep 15 13:45:06 2010 (r212659)
+++ head/sys/fs/devfs/devfs_devs.c Wed Sep 15 14:23:55 2010 (r212660)
@@ -275,17 +275,68 @@ devfs_dirent_free(struct devfs_dirent *d
}
/*
+ * Removes a directory if it is empty. Also empty parent directories are
+ * removed recursively.
+ */
+static void
+devfs_rmdir_empty(struct devfs_mount *dm, struct devfs_dirent *de)
+{
+ struct devfs_dirent *dd, *de_dot, *de_dotdot;
+
+ sx_assert(&dm->dm_lock, SX_XLOCKED);
+
+ for (;;) {
+ KASSERT(de->de_dirent->d_type == DT_DIR,
+ ("devfs_rmdir_empty: de is not a directory"));
+
+ if ((de->de_flags & DE_DOOMED) != 0 || de == dm->dm_rootdir)
+ return;
+
+ de_dot = TAILQ_FIRST(&de->de_dlist);
+ KASSERT(de_dot != NULL, ("devfs_rmdir_empty: . missing"));
+ de_dotdot = TAILQ_NEXT(de_dot, de_list);
+ KASSERT(de_dotdot != NULL, ("devfs_rmdir_empty: .. missing"));
+ /* Return if the directory is not empty. */
+ if (TAILQ_NEXT(de_dotdot, de_list) != NULL)
+ return;
+
+ dd = devfs_parent_dirent(de);
+ KASSERT(dd != NULL, ("devfs_rmdir_empty: NULL dd"));
+ TAILQ_REMOVE(&dd->de_dlist, de, de_list);
+ DEVFS_DE_HOLD(dd);
+ devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
+ devfs_delete(dm, de_dot, DEVFS_DEL_NORECURSE);
+ devfs_delete(dm, de_dotdot, DEVFS_DEL_NORECURSE);
+ if (DEVFS_DE_DROP(dd)) {
+ devfs_dirent_free(dd);
+ return;
+ }
+
+ de = dd;
+ }
+}
+
+/*
* The caller needs to hold the dm for the duration of the call since
* dm->dm_lock may be temporary dropped.
*/
void
-devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int vp_locked)
+devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int flags)
{
+ struct devfs_dirent *dd;
struct vnode *vp;
KASSERT((de->de_flags & DE_DOOMED) == 0,
("devfs_delete doomed dirent"));
de->de_flags |= DE_DOOMED;
+
+ if ((flags & DEVFS_DEL_NORECURSE) == 0) {
+ dd = devfs_parent_dirent(de);
+ if (dd != NULL)
+ DEVFS_DE_HOLD(dd);
+ } else
+ dd = NULL;
+
mtx_lock(&devfs_de_interlock);
vp = de->de_vnode;
if (vp != NULL) {
@@ -293,12 +344,12 @@ devfs_delete(struct devfs_mount *dm, str
mtx_unlock(&devfs_de_interlock);
vholdl(vp);
sx_unlock(&dm->dm_lock);
- if (!vp_locked)
+ if ((flags & DEVFS_DEL_VNLOCKED) == 0)
vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
else
VI_UNLOCK(vp);
vgone(vp);
- if (!vp_locked)
+ if ((flags & DEVFS_DEL_VNLOCKED) == 0)
VOP_UNLOCK(vp, 0);
vdrop(vp);
sx_xlock(&dm->dm_lock);
@@ -317,6 +368,13 @@ devfs_delete(struct devfs_mount *dm, str
}
if (DEVFS_DE_DROP(de))
devfs_dirent_free(de);
+
+ if (dd != NULL) {
+ if (DEVFS_DE_DROP(dd))
+ devfs_dirent_free(dd);
+ else
+ devfs_rmdir_empty(dm, dd);
+ }
}
/*
@@ -331,19 +389,24 @@ devfs_purge(struct devfs_mount *dm, stru
struct devfs_dirent *de;
sx_assert(&dm->dm_lock, SX_XLOCKED);
+
+ DEVFS_DE_HOLD(dd);
for (;;) {
de = TAILQ_FIRST(&dd->de_dlist);
if (de == NULL)
break;
TAILQ_REMOVE(&dd->de_dlist, de, de_list);
- if (de->de_flags & (DE_DOT|DE_DOTDOT))
- devfs_delete(dm, de, 0);
+ if (de->de_flags & (DE_DOT | DE_DOTDOT))
+ devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
else if (de->de_dirent->d_type == DT_DIR)
devfs_purge(dm, de);
- else
- devfs_delete(dm, de, 0);
+ else
+ devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
}
- devfs_delete(dm, dd, 0);
+ if (DEVFS_DE_DROP(dd))
+ devfs_dirent_free(dd);
+ else if ((dd->de_flags & DE_DOOMED) == 0)
+ devfs_delete(dm, dd, DEVFS_DEL_NORECURSE);
}
/*
Modified: head/sys/fs/devfs/devfs_vnops.c
==============================================================================
--- head/sys/fs/devfs/devfs_vnops.c Wed Sep 15 13:45:06 2010 (r212659)
+++ head/sys/fs/devfs/devfs_vnops.c Wed Sep 15 14:23:55 2010 (r212660)
@@ -36,7 +36,6 @@
/*
* TODO:
- * remove empty directories
* mkdir: want it ?
*/
@@ -191,6 +190,7 @@ devfs_clear_cdevpriv(void)
static int
devfs_populate_vp(struct vnode *vp)
{
+ struct devfs_dirent *de;
struct devfs_mount *dmp;
int locked;
@@ -214,7 +214,14 @@ devfs_populate_vp(struct vnode *vp)
devfs_unmount_final(dmp);
return (EBADF);
}
- if (vp->v_iflag & VI_DOOMED) {
+ if ((vp->v_iflag & VI_DOOMED) != 0) {
+ sx_xunlock(&dmp->dm_lock);
+ return (EBADF);
+ }
+ de = vp->v_data;
+ KASSERT(de != NULL,
+ ("devfs_populate_vp: vp->v_data == NULL but vnode not doomed"));
+ if ((de->de_flags & DE_DOOMED) != 0) {
sx_xunlock(&dmp->dm_lock);
return (EBADF);
}
@@ -234,11 +241,13 @@ devfs_vptocnp(struct vop_vptocnp_args *a
int i, error;
dmp = VFSTODEVFS(vp->v_mount);
+
+ error = devfs_populate_vp(vp);
+ if (error != 0)
+ return (error);
+
i = *buflen;
dd = vp->v_data;
- error = 0;
-
- sx_xlock(&dmp->dm_lock);
if (vp->v_type == VCHR) {
i -= strlen(dd->de_cdp->cdp_c.si_name);
@@ -1271,11 +1280,15 @@ devfs_reclaim(struct vop_reclaim_args *a
static int
devfs_remove(struct vop_remove_args *ap)
{
+ struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
struct devfs_dirent *dd;
struct devfs_dirent *de, *de_covered;
struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
+ ASSERT_VOP_ELOCKED(dvp, "devfs_remove");
+ ASSERT_VOP_ELOCKED(vp, "devfs_remove");
+
sx_xlock(&dmp->dm_lock);
dd = ap->a_dvp->v_data;
de = vp->v_data;
@@ -1287,11 +1300,19 @@ devfs_remove(struct vop_remove_args *ap)
if (de_covered != NULL)
de_covered->de_flags &= ~DE_COVERED;
}
- devfs_delete(dmp, de, 1);
+ /* We need to unlock dvp because devfs_delete() may lock it. */
+ VOP_UNLOCK(vp, 0);
+ if (dvp != vp)
+ VOP_UNLOCK(dvp, 0);
+ devfs_delete(dmp, de, 0);
+ sx_xunlock(&dmp->dm_lock);
+ if (dvp != vp)
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
} else {
de->de_flags |= DE_WHITEOUT;
+ sx_xunlock(&dmp->dm_lock);
}
- sx_xunlock(&dmp->dm_lock);
return (0);
}
@@ -1533,6 +1554,9 @@ devfs_symlink(struct vop_symlink_args *a
if (error)
return(error);
dmp = VFSTODEVFS(ap->a_dvp->v_mount);
+ if (devfs_populate_vp(ap->a_dvp) != 0)
+ return (ENOENT);
+
dd = ap->a_dvp->v_data;
de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
de->de_uid = 0;
@@ -1544,7 +1568,6 @@ devfs_symlink(struct vop_symlink_args *a
i = strlen(ap->a_target) + 1;
de->de_symlink = malloc(i, M_DEVFS, M_WAITOK);
bcopy(ap->a_target, de->de_symlink, i);
- sx_xlock(&dmp->dm_lock);
#ifdef MAC
mac_devfs_create_symlink(ap->a_cnp->cn_cred, dmp->dm_mount, dd, de);
#endif
More information about the svn-src-head
mailing list