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-all mailing list