svn commit: r189737 - in head/sys/ufs: ffs ufs

Konstantin Belousov kib at FreeBSD.org
Thu Mar 12 05:43:57 PDT 2009


Author: kib
Date: Thu Mar 12 12:43:56 2009
New Revision: 189737
URL: http://svn.freebsd.org/changeset/base/189737

Log:
  The non-modifying EA VOPs are executed with only shared vnode lock taken.
  Provide a custom lock around initializing and tearing down EA area,
  to prevent both memory leaks and double-free of it. Count the number
  of EA area accessors.
  
  Lock protocol requires either holding exclusive vnode lock to modify
  i_ea_area, or shared vnode lock and owning IN_EA_LOCKED flag in i_flag.
  
  Noted by:	YAMAMOTO, Taku <taku tackymt homeip net>
  Tested by:	pho (previous version)
  MFC after:	2 weeks

Modified:
  head/sys/ufs/ffs/ffs_vfsops.c
  head/sys/ufs/ffs/ffs_vnops.c
  head/sys/ufs/ufs/inode.h

Modified: head/sys/ufs/ffs/ffs_vfsops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vfsops.c	Thu Mar 12 10:36:39 2009	(r189736)
+++ head/sys/ufs/ffs/ffs_vfsops.c	Thu Mar 12 12:43:56 2009	(r189737)
@@ -1451,6 +1451,7 @@ ffs_vgetf(mp, ino, flags, vpp, ffs_flags
 	ip->i_fs = fs;
 	ip->i_dev = dev;
 	ip->i_number = ino;
+	ip->i_ea_refs = 0;
 #ifdef QUOTA
 	{
 		int i;

Modified: head/sys/ufs/ffs/ffs_vnops.c
==============================================================================
--- head/sys/ufs/ffs/ffs_vnops.c	Thu Mar 12 10:36:39 2009	(r189736)
+++ head/sys/ufs/ffs/ffs_vnops.c	Thu Mar 12 12:43:56 2009	(r189737)
@@ -1225,6 +1225,35 @@ ffs_rdextattr(u_char **p, struct vnode *
 	return (0);
 }
 
+static void
+ffs_lock_ea(struct vnode *vp)
+{
+	struct inode *ip;
+
+	ip = VTOI(vp);
+	VI_LOCK(vp);
+	while (ip->i_flag & IN_EA_LOCKED) {
+		ip->i_flag |= IN_EA_LOCKWAIT;
+		msleep(&ip->i_ea_refs, &vp->v_interlock, PINOD + 2, "ufs_ea",
+		    0);
+	}
+	ip->i_flag |= IN_EA_LOCKED;
+	VI_UNLOCK(vp);
+}
+
+static void
+ffs_unlock_ea(struct vnode *vp)
+{
+	struct inode *ip;
+
+	ip = VTOI(vp);
+	VI_LOCK(vp);
+	if (ip->i_flag & IN_EA_LOCKWAIT)
+		wakeup(&ip->i_ea_refs);
+	ip->i_flag &= ~(IN_EA_LOCKED | IN_EA_LOCKWAIT);
+	VI_UNLOCK(vp);
+}
+
 static int
 ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td)
 {
@@ -1234,14 +1263,22 @@ ffs_open_ea(struct vnode *vp, struct ucr
 
 	ip = VTOI(vp);
 
-	if (ip->i_ea_area != NULL)
-		return (EBUSY);
+	ffs_lock_ea(vp);
+	if (ip->i_ea_area != NULL) {
+		ip->i_ea_refs++;
+		ffs_unlock_ea(vp);
+		return (0);
+	}
 	dp = ip->i_din2;
 	error = ffs_rdextattr(&ip->i_ea_area, vp, td, 0);
-	if (error)
+	if (error) {
+		ffs_unlock_ea(vp);
 		return (error);
+	}
 	ip->i_ea_len = dp->di_extsize;
 	ip->i_ea_error = 0;
+	ip->i_ea_refs++;
+	ffs_unlock_ea(vp);
 	return (0);
 }
 
@@ -1258,11 +1295,16 @@ ffs_close_ea(struct vnode *vp, int commi
 	struct ufs2_dinode *dp;
 
 	ip = VTOI(vp);
-	if (ip->i_ea_area == NULL)
+
+	ffs_lock_ea(vp);
+	if (ip->i_ea_area == NULL) {
+		ffs_unlock_ea(vp);
 		return (EINVAL);
+	}
 	dp = ip->i_din2;
 	error = ip->i_ea_error;
 	if (commit && error == 0) {
+		ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit");
 		if (cred == NOCRED)
 			cred =  vp->v_mount->mnt_cred;
 		liovec.iov_base = ip->i_ea_area;
@@ -1279,10 +1321,13 @@ ffs_close_ea(struct vnode *vp, int commi
 			error = ffs_truncate(vp, 0, IO_EXT, cred, td);
 		error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred);
 	}
-	free(ip->i_ea_area, M_TEMP);
-	ip->i_ea_area = NULL;
-	ip->i_ea_len = 0;
-	ip->i_ea_error = 0;
+	if (--ip->i_ea_refs == 0) {
+		free(ip->i_ea_area, M_TEMP);
+		ip->i_ea_area = NULL;
+		ip->i_ea_len = 0;
+		ip->i_ea_error = 0;
+	}
+	ffs_unlock_ea(vp);
 	return (error);
 }
 
@@ -1392,7 +1437,6 @@ vop_deleteextattr {
 	uint32_t ealength, ul;
 	int ealen, olen, eapad1, eapad2, error, i, easize;
 	u_char *eae, *p;
-	int stand_alone;
 
 	ip = VTOI(ap->a_vp);
 	fs = ip->i_fs;
@@ -1409,19 +1453,19 @@ vop_deleteextattr {
 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
 	    ap->a_cred, ap->a_td, VWRITE);
 	if (error) {
+
+		/*
+		 * ffs_lock_ea is not needed there, because the vnode
+		 * must be exlusively locked.
+		 */
 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
 			ip->i_ea_error = error;
 		return (error);
 	}
 
-	if (ip->i_ea_area == NULL) {
-		error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
-		if (error)
-			return (error);
-		stand_alone = 1;
-	} else {
-		stand_alone = 0;
-	}
+	error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+	if (error)
+		return (error);
 
 	ealength = eapad1 = ealen = eapad2 = 0;
 
@@ -1434,8 +1478,7 @@ vop_deleteextattr {
 	if (olen == -1) {
 		/* delete but nonexistent */
 		free(eae, M_TEMP);
-		if (stand_alone)
-			ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
 		return(ENOATTR);
 	}
 	bcopy(p, &ul, sizeof ul);
@@ -1446,9 +1489,8 @@ vop_deleteextattr {
 	}
 	if (easize > NXADDR * fs->fs_bsize) {
 		free(eae, M_TEMP);
-		if (stand_alone)
-			ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
-		else if (ip->i_ea_error == 0)
+		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
 			ip->i_ea_error = ENOSPC;
 		return(ENOSPC);
 	}
@@ -1456,8 +1498,7 @@ vop_deleteextattr {
 	ip->i_ea_area = eae;
 	ip->i_ea_len = easize;
 	free(p, M_TEMP);
-	if (stand_alone)
-		error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
+	error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
 	return(error);
 }
 
@@ -1482,7 +1523,7 @@ vop_getextattr {
 	struct fs *fs;
 	u_char *eae, *p;
 	unsigned easize;
-	int error, ealen, stand_alone;
+	int error, ealen;
 
 	ip = VTOI(ap->a_vp);
 	fs = ip->i_fs;
@@ -1495,14 +1536,10 @@ vop_getextattr {
 	if (error)
 		return (error);
 
-	if (ip->i_ea_area == NULL) {
-		error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
-		if (error)
-			return (error);
-		stand_alone = 1;
-	} else {
-		stand_alone = 0;
-	}
+	error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+	if (error)
+		return (error);
+
 	eae = ip->i_ea_area;
 	easize = ip->i_ea_len;
 
@@ -1516,8 +1553,8 @@ vop_getextattr {
 			error = uiomove(p, ealen, ap->a_uio);
 	} else
 		error = ENOATTR;
-	if (stand_alone)
-		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+
+	ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
 	return(error);
 }
 
@@ -1542,7 +1579,7 @@ vop_listextattr {
 	u_char *eae, *p, *pe, *pn;
 	unsigned easize;
 	uint32_t ul;
-	int error, ealen, stand_alone;
+	int error, ealen;
 
 	ip = VTOI(ap->a_vp);
 	fs = ip->i_fs;
@@ -1555,14 +1592,9 @@ vop_listextattr {
 	if (error)
 		return (error);
 
-	if (ip->i_ea_area == NULL) {
-		error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
-		if (error)
-			return (error);
-		stand_alone = 1;
-	} else {
-		stand_alone = 0;
-	}
+	error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+	if (error)
+		return (error);
 	eae = ip->i_ea_area;
 	easize = ip->i_ea_len;
 
@@ -1586,8 +1618,7 @@ vop_listextattr {
 			error = uiomove(p, ealen + 1, ap->a_uio);
 		}
 	}
-	if (stand_alone)
-		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+	ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
 	return(error);
 }
 
@@ -1612,7 +1643,6 @@ vop_setextattr {
 	uint32_t ealength, ul;
 	int ealen, olen, eapad1, eapad2, error, i, easize;
 	u_char *eae, *p;
-	int stand_alone;
 
 	ip = VTOI(ap->a_vp);
 	fs = ip->i_fs;
@@ -1633,19 +1663,19 @@ vop_setextattr {
 	error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
 	    ap->a_cred, ap->a_td, VWRITE);
 	if (error) {
+
+		/*
+		 * ffs_lock_ea is not needed there, because the vnode
+		 * must be exlusively locked.
+		 */
 		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
 			ip->i_ea_error = error;
 		return (error);
 	}
 
-	if (ip->i_ea_area == NULL) {
-		error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
-		if (error)
-			return (error);
-		stand_alone = 1;
-	} else {
-		stand_alone = 0;
-	}
+	error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+	if (error)
+		return (error);
 
 	ealen = ap->a_uio->uio_resid;
 	ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name);
@@ -1677,9 +1707,8 @@ vop_setextattr {
 	}
 	if (easize > NXADDR * fs->fs_bsize) {
 		free(eae, M_TEMP);
-		if (stand_alone)
-			ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
-		else if (ip->i_ea_error == 0)
+		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
 			ip->i_ea_error = ENOSPC;
 		return(ENOSPC);
 	}
@@ -1695,9 +1724,8 @@ vop_setextattr {
 	error = uiomove(p, ealen, ap->a_uio);
 	if (error) {
 		free(eae, M_TEMP);
-		if (stand_alone)
-			ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
-		else if (ip->i_ea_error == 0)
+		ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+		if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
 			ip->i_ea_error = error;
 		return(error);
 	}
@@ -1708,8 +1736,7 @@ vop_setextattr {
 	ip->i_ea_area = eae;
 	ip->i_ea_len = easize;
 	free(p, M_TEMP);
-	if (stand_alone)
-		error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
+	error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
 	return(error);
 }
 

Modified: head/sys/ufs/ufs/inode.h
==============================================================================
--- head/sys/ufs/ufs/inode.h	Thu Mar 12 10:36:39 2009	(r189736)
+++ head/sys/ufs/ufs/inode.h	Thu Mar 12 12:43:56 2009	(r189737)
@@ -94,6 +94,7 @@ struct inode {
 	u_char	  *i_ea_area;	/* Pointer to malloced copy of EA area */
 	unsigned  i_ea_len;	/* Length of i_ea_area */
 	int	  i_ea_error;	/* First errno in transaction */
+	int	  i_ea_refs;	/* Number of users of EA area */
 
 	/*
 	 * Copies from the on-disk dinode itself.
@@ -125,6 +126,8 @@ struct inode {
 #define	IN_SPACECOUNTED	0x0080		/* Blocks to be freed in free count. */
 #define	IN_LAZYACCESS	0x0100		/* Process IN_ACCESS after the
 					   suspension finished */
+#define	IN_EA_LOCKED	0x0200
+#define	IN_EA_LOCKWAIT	0x0400
 
 #define i_devvp i_ump->um_devvp
 #define i_umbufobj i_ump->um_bo


More information about the svn-src-all mailing list