patch-2.2.14 linux/fs/dquot.c

Next file: linux/fs/ext2/balloc.c
Previous file: linux/fs/devices.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.13/linux/fs/dquot.c linux/fs/dquot.c
@@ -31,6 +31,9 @@
  *		add_dquot_ref() restarts after blocking
  *		Added check for bogus uid and fixed check for group in quotactl.
  *		Jan Kara, <jack@atrey.karlin.mff.cuni.cz>, 4-6/99
+ *		Definitely (hopefully) fixed deadlock in write_dquot(). We no
+ *		longer account quota files.
+ *		Jan Kara, <jack@suse.cz>, 11/99, sposored by SuSE CR
  *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
@@ -245,6 +248,9 @@
 	wake_up(&dquot->dq_wait);
 }
 
+/*
+ *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
 static void write_dquot(struct dquot *dquot)
 {
 	short type = dquot->dq_type;
@@ -252,20 +258,14 @@
 	mm_segment_t fs;
 	loff_t offset;
 	ssize_t ret;
-	struct dqblk data;
 	struct semaphore *sem = &dquot->dq_mnt->mnt_dquot.dqio_sem;
 
-	/*
-	 * We copy our data to preserve consistency when dquot is not locked.
-	 * We can't lock it because it can cause deadlocks - think about
-	 * growing the quota file...
-	 */
-	memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk));
-	down(sem);
+	lock_dquot(dquot);
 	if (!dquot->dq_mnt) {	/* Invalidated quota? */
-		up(sem);
+		unlock_dquot(dquot);
 		return;
 	}
+	down(sem);
 	filp = dquot->dq_mnt->mnt_dquot.files[type];
 	offset = dqoff(dquot->dq_id);
 	fs = get_fs();
@@ -278,18 +278,15 @@
 	dquot->dq_flags &= ~DQ_MOD;
 	ret = 0;
 	if (filp)
-		ret = filp->f_op->write(filp, (char *)&data, 
+		ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, 
 					sizeof(struct dqblk), &offset);
 	if (ret != sizeof(struct dqblk))
 		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
 			kdevname(dquot->dq_dev));
 
-	/*
-	 * Be sure that anybody didn't invalidated the dquot in the mean time...
-	 * Nasty but I don't see other choice when we don't want to lock dquot.
-	 */
-	up(sem);
 	set_fs(fs);
+	up(sem);
+	unlock_dquot(dquot);
 
 	dqstats.writes++;
 }
@@ -572,10 +569,9 @@
 	/*
 	 * Try pruning the dcache to free up some dquots ...
 	 */
-	count = select_dcache(128, 0);
-	if (count) {
-		printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
-		prune_dcache(count);
+	printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
+	if (prune_dcache(0, 128))
+	{
 		free_inode_memory(count);
 		goto repeat;
 	}
@@ -652,12 +648,31 @@
 	return dquot;
 }
 
+/* Check whether this inode is quota file */
+static inline int is_quotafile(struct inode *inode)
+{
+	int cnt;
+	struct vfsmount *vfsmnt;
+	struct file **files;
+
+	vfsmnt = lookup_vfsmnt(inode->i_dev);
+	if (!vfsmnt)
+		return 0;
+	files = vfsmnt->mnt_dquot.files;
+	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+		if (files[cnt] && files[cnt]->f_dentry->d_inode == inode)
+			return 1;
+	return 0;
+}
+
 static int dqinit_needed(struct inode *inode, short type)
 {
 	int cnt;
 
         if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
                 return 0;
+	if (is_quotafile(inode))
+		return 0;
 	if (type != -1)
 		return inode->i_dquot[type] == NODQUOT;
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++)
@@ -717,7 +732,7 @@
 	}
 	inode->i_flags &= ~S_QUOTA;
 put_it:
-	if (dquot != NODQUOT)
+	if (dquot != NODQUOT) {
 		if (dqput_blocks(dquot)) {
 			spin_unlock(&inode_lock);	/* We may block so drop the lock... */
 			dqput(dquot);
@@ -727,6 +742,8 @@
 		}
 		else
 			dqput(dquot);	/* dqput() won't block so we can hold locks... */
+	}
+
 	return 0;
 }
 
@@ -1070,6 +1087,9 @@
 	if (S_ISREG(inode->i_mode) ||
             S_ISDIR(inode->i_mode) ||
             S_ISLNK(inode->i_mode)) {
+		/* We don't want to have quotas on quota files - nasty deadlocks possible */
+		if (is_quotafile(inode))
+			return;
 		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 			if (type != -1 && cnt != type)
 				continue;
@@ -1341,7 +1361,6 @@
 		dquot_incr_blocks(transfer_to[cnt], blocks);
 
 		unlock_dquot(transfer_from[cnt]);
-		dqput(transfer_from[cnt]);
 		if (inode->i_dquot[cnt] != NODQUOT) {
 			struct dquot *temp = inode->i_dquot[cnt];
 			inode->i_dquot[cnt] = transfer_to[cnt];
@@ -1351,20 +1370,20 @@
 			unlock_dquot(transfer_to[cnt]);
 			dqput(transfer_to[cnt]);
 		}
+		dqput(transfer_from[cnt]);
 	}
 
 	return 0;
 put_all:
 	for (disc = 0; disc < cnt; disc++) {
 		/* There should be none or both pointers set but... */
-		if (transfer_to[disc] != NODQUOT) {
+		if (transfer_to[disc] != NODQUOT)
 			unlock_dquot(transfer_to[disc]);
-			dqput(transfer_to[disc]);
-		}
-		if (transfer_from[disc] != NODQUOT) {
+		if (transfer_from[disc] != NODQUOT)
 			unlock_dquot(transfer_from[disc]);
-			dqput(transfer_from[disc]);
-		}
+		/* dqput() tests for NODQUOT itself... */
+		dqput(transfer_from[disc]);
+		dqput(transfer_to[disc]);
 	}
 	return error;
 }
@@ -1513,6 +1532,7 @@
 	error = -EINVAL;
 	if (inode->i_size == 0 || (inode->i_size % sizeof(struct dqblk)) != 0)
 		goto out_f;
+	dquot_drop(inode);	/* We don't want quota on quota files */
 
 	set_enable_flags(vfsmnt, type);
 	mnt_dquot->files[type] = f;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)