svn commit: r248561 - in head/sys: kern sys ufs/ufs

Kirk McKusick mckusick at FreeBSD.org
Wed Mar 20 17:57:01 UTC 2013


Author: mckusick
Date: Wed Mar 20 17:57:00 2013
New Revision: 248561
URL: http://svnweb.freebsd.org/changeset/base/248561

Log:
  When renaming a directory from one parent directory to another,
  we need to call ufs_checkpath() to walk from our new location to
  the root of the filesystem to ensure that we do not encounter
  ourselves along the way. Until now, we accomplished this by reading
  the ".." entries of each directory in our path until we reached
  the root (or encountered an error). This change tries to avoid the
  I/O of reading the ".." entries by first looking them up in the
  name cache and only doing the I/O when the name cache lookup fails.
  
  Reviewed by: kib
  Tested by:   Peter Holm
  MFC after:   4 weeks

Modified:
  head/sys/kern/vfs_cache.c
  head/sys/sys/vnode.h
  head/sys/ufs/ufs/ufs_lookup.c

Modified: head/sys/kern/vfs_cache.c
==============================================================================
--- head/sys/kern/vfs_cache.c	Wed Mar 20 16:30:17 2013	(r248560)
+++ head/sys/kern/vfs_cache.c	Wed Mar 20 17:57:00 2013	(r248561)
@@ -1359,6 +1359,28 @@ vn_fullpath1(struct thread *td, struct v
 	return (0);
 }
 
+struct vnode *
+vn_dir_dd_ino(struct vnode *vp)
+{
+	struct namecache *ncp;
+	struct vnode *ddvp;
+
+	ASSERT_VOP_LOCKED(vp, "vn_dir_dd_ino");
+	CACHE_RLOCK();
+	TAILQ_FOREACH(ncp, &(vp->v_cache_dst), nc_dst) {
+		if ((ncp->nc_flag & NCF_ISDOTDOT) != 0)
+			continue;
+		ddvp = ncp->nc_dvp;
+		VI_LOCK(ddvp);
+		CACHE_RUNLOCK();
+		if (vget(ddvp, LK_INTERLOCK | LK_SHARED | LK_NOWAIT, curthread))
+			return (NULL);
+		return (ddvp);
+	}
+	CACHE_RUNLOCK();
+	return (NULL);
+}
+
 int
 vn_commname(struct vnode *vp, char *buf, u_int buflen)
 {

Modified: head/sys/sys/vnode.h
==============================================================================
--- head/sys/sys/vnode.h	Wed Mar 20 16:30:17 2013	(r248560)
+++ head/sys/sys/vnode.h	Wed Mar 20 17:57:00 2013	(r248561)
@@ -622,6 +622,8 @@ int	vn_fullpath(struct thread *td, struc
 	    char **retbuf, char **freebuf);
 int	vn_fullpath_global(struct thread *td, struct vnode *vn,
 	    char **retbuf, char **freebuf);
+struct vnode *
+	vn_dir_dd_ino(struct vnode *vp);
 int	vn_commname(struct vnode *vn, char *buf, u_int buflen);
 int	vn_path_to_global_path(struct thread *td, struct vnode *vp,
 	    char *path, u_int pathlen);

Modified: head/sys/ufs/ufs/ufs_lookup.c
==============================================================================
--- head/sys/ufs/ufs/ufs_lookup.c	Wed Mar 20 16:30:17 2013	(r248560)
+++ head/sys/ufs/ufs/ufs_lookup.c	Wed Mar 20 17:57:00 2013	(r248561)
@@ -1387,13 +1387,29 @@ ufs_dirempty(ip, parentino, cred)
 }
 
 static int
-ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino)
+ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino,
+    struct vnode **dd_vp)
 {
 	struct dirtemplate dirbuf;
+	struct vnode *ddvp;
 	int error, namlen;
 
+	ASSERT_VOP_LOCKED(vp, "ufs_dir_dd_ino");
 	if (vp->v_type != VDIR)
 		return (ENOTDIR);
+	/*
+	 * First check to see if we have it in the name cache.
+	 */
+	if ((ddvp = vn_dir_dd_ino(vp)) != NULL) {
+		KASSERT(ddvp->v_mount == vp->v_mount,
+		    ("ufs_dir_dd_ino: Unexpected mount point crossing"));
+		*dd_ino = VTOI(ddvp)->i_number;
+		*dd_vp = ddvp;
+		return (0);
+	}
+	/*
+	 * Have to read the directory.
+	 */
 	error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
 	    sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
 	    IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, NULL);
@@ -1411,6 +1427,7 @@ ufs_dir_dd_ino(struct vnode *vp, struct 
 	    dirbuf.dotdot_name[1] != '.')
 		return (ENOTDIR);
 	*dd_ino = dirbuf.dotdot_ino;
+	*dd_vp = NULL;
 	return (0);
 }
 
@@ -1435,7 +1452,7 @@ ufs_checkpath(ino_t source_ino, ino_t pa
 	if (target->i_number == ROOTINO)
 		return (0);
 	for (;;) {
-		error = ufs_dir_dd_ino(vp, cred, &dd_ino);
+		error = ufs_dir_dd_ino(vp, cred, &dd_ino, &vp1);
 		if (error != 0)
 			break;
 		if (dd_ino == source_ino) {
@@ -1446,22 +1463,16 @@ ufs_checkpath(ino_t source_ino, ino_t pa
 			break;
 		if (dd_ino == parent_ino)
 			break;
-		error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT, &vp1);
-		if (error != 0) {
-			*wait_ino = dd_ino;
-			break;
-		}
-		/* Recheck that ".." still points to vp1 after relock of vp */
-		error = ufs_dir_dd_ino(vp, cred, &dd_ino);
-		if (error != 0) {
-			vput(vp1);
-			break;
-		}
-		/* Redo the check of ".." if directory was reparented */
-		if (dd_ino != VTOI(vp1)->i_number) {
-			vput(vp1);
-			continue;
+		if (vp1 == NULL) {
+			error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT,
+			    &vp1);
+			if (error != 0) {
+				*wait_ino = dd_ino;
+				break;
+			}
 		}
+		KASSERT(dd_ino == VTOI(vp1)->i_number,
+		    ("directory %d reparented\n", VTOI(vp1)->i_number));
 		if (vp != tvp)
 			vput(vp);
 		vp = vp1;
@@ -1469,6 +1480,8 @@ ufs_checkpath(ino_t source_ino, ino_t pa
 
 	if (error == ENOTDIR)
 		panic("checkpath: .. not a directory\n");
+	if (vp1 != NULL)
+		vput(vp1);
 	if (vp != tvp)
 		vput(vp);
 	return (error);


More information about the svn-src-head mailing list