[PATCH 2/6] quota: Add proper error handling on quota initialization.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently dqget_initialize() is a black-box so IO errors are simply
ignored. In order to pass to the caller real error codes quota
interface must being redesigned in following way.

- return real error code from dqget()
- return real error code from dquot_initialize()

Now filesystem it is filesystem's responsibility to dquot_initialize()
return value.

Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx>
---
 fs/ocfs2/file.c          |   18 ++++++--
 fs/ocfs2/quota_local.c   |   10 ++--
 fs/quota/dquot.c         |  101 +++++++++++++++++++++++++++++++---------------
 include/linux/quotaops.h |    5 +-
 4 files changed, 90 insertions(+), 44 deletions(-)

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index b26df86..3c32f40 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1032,10 +1032,15 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 		    OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
 			transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
 						      USRQUOTA);
+			if (IS_ERR(transfer_to[USRQUOTA])) {
+				status = PTR_ERR(transfer_to[USRQUOTA]);
+				goto bail_unlock;
+			}
+
 			transfer_from[USRQUOTA] = dqget(sb, inode->i_uid,
 							USRQUOTA);
-			if (!transfer_to[USRQUOTA] || !transfer_from[USRQUOTA]) {
-				status = -ESRCH;
+			if (IS_ERR(transfer_from[USRQUOTA])) {
+				status = PTR_ERR(transfer_from[USRQUOTA]);
 				goto bail_unlock;
 			}
 		}
@@ -1044,10 +1049,15 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
 		    OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
 			transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
 						      GRPQUOTA);
+			if (IS_ERR(transfer_to[GRPQUOTA])) {
+				status = PTR_ERR(transfer_to[GRPQUOTA]);
+				goto bail_unlock;
+			}
+
 			transfer_from[GRPQUOTA] = dqget(sb, inode->i_gid,
 							GRPQUOTA);
-			if (!transfer_to[GRPQUOTA] || !transfer_from[GRPQUOTA]) {
-				status = -ESRCH;
+			if (IS_ERR(transfer_from[GRPQUOTA])) {
+				status = PTR_ERR(transfer_from[GRPQUOTA]);
 				goto bail_unlock;
 			}
 		}
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 21f9e71..6827ff6 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -469,13 +469,13 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
 			dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
 				ol_dqblk_block_off(sb, chunk, bit));
 			dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
-			if (!dquot) {
-				status = -EIO;
+			if (IS_ERR(dquot)) {
+				status = PTR_ERR(dquot);
 				mlog(ML_ERROR, "Failed to get quota structure "
-				     "for id %u, type %d. Cannot finish quota "
-				     "file recovery.\n",
+				     "for id %u, type %d err %d. Cannot finish"
+				     " quota file recovery.\n",
 				     (unsigned)le64_to_cpu(dqblk->dqb_id),
-				     type);
+				     type, status);
 				goto out_put_bh;
 			}
 			status = ocfs2_lock_global_qf(oinfo, 1);
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index a168189..3f4541e 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -230,7 +230,7 @@ struct dqstats dqstats;
 EXPORT_SYMBOL(dqstats);
 
 static qsize_t inode_get_rsv_space(struct inode *inode);
-static void __dquot_initialize(struct inode *inode, int type);
+static int __dquot_initialize(struct inode *inode, int type);
 
 static inline unsigned int
 hashfn(const struct super_block *sb, unsigned int id, int type)
@@ -702,7 +702,7 @@ void dqput(struct dquot *dquot)
 {
 	int ret;
 
-	if (!dquot)
+	if (!dquot || IS_ERR(dquot))
 		return;
 #ifdef __DQUOT_PARANOIA
 	if (!atomic_read(&dquot->dq_count)) {
@@ -800,14 +800,16 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
  * destroying our dquot by:
  *   a) checking for quota flags under dq_list_lock and
  *   b) getting a reference to dquot before we release dq_list_lock
+ * Return: valid pointer on success, ERR_PTR otherwise.
  */
 struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
 {
 	unsigned int hashent = hashfn(sb, id, type);
-	struct dquot *dquot = NULL, *empty = NULL;
+	struct dquot *dquot = ERR_PTR(-ESRCH);
+	struct dquot *empty = NULL;
 
         if (!sb_has_quota_active(sb, type))
-		return NULL;
+		goto out;
 we_slept:
 	spin_lock(&dq_list_lock);
 	spin_lock(&dq_state_lock);
@@ -848,11 +850,13 @@ we_slept:
 	 * already finished or it will be canceled due to dq_count > 1 test */
 	wait_on_dquot(dquot);
 	/* Read the dquot / allocate space in quota file */
-	if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) &&
-	    sb->dq_op->acquire_dquot(dquot) < 0) {
-		dqput(dquot);
-		dquot = NULL;
-		goto out;
+	if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
+		int ret = sb->dq_op->acquire_dquot(dquot);
+		if (ret < 0) {
+			dqput(dquot);
+			dquot = ERR_PTR(ret);
+			goto out;
+		}
 	}
 #ifdef __DQUOT_PARANOIA
 	BUG_ON(!dquot->dq_sb);	/* Has somebody invalidated entry under us? */
@@ -1311,18 +1315,19 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space)
  * It is better to call this function outside of any transaction as it
  * might need a lot of space in journal for dquot structure allocation.
  */
-static void __dquot_initialize(struct inode *inode, int type)
+static int __dquot_initialize(struct inode *inode, int type)
 {
 	unsigned int id = 0;
-	int cnt;
+	int cnt, err = 0;
 	struct dquot *got[MAXQUOTAS];
 	struct super_block *sb = inode->i_sb;
 	qsize_t rsv;
 
+repeat:
 	/* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
 	if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode))
-		return;
+		return 0;
 
 	/* First get references to structures we might need. */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@@ -1341,35 +1346,50 @@ static void __dquot_initialize(struct inode *inode, int type)
 	}
 
 	down_write(&sb_dqopt(sb)->dqptr_sem);
-	if (IS_NOQUOTA(inode))
+	if (IS_NOQUOTA(inode)) {
+		err = 0;
 		goto out_err;
+	}
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
 		/* Avoid races with quotaoff() */
 		if (!sb_has_quota_active(sb, cnt))
 			continue;
-		if (!inode->i_dquot[cnt]) {
-			inode->i_dquot[cnt] = got[cnt];
-			got[cnt] = NULL;
-			/*
-			 * Make quota reservation system happy if someone
-			 * did a write before quota was turned on
-			 */
-			rsv = inode_get_rsv_space(inode);
-			if (unlikely(rsv))
-				dquot_resv_space(inode->i_dquot[cnt], rsv);
+		if (inode->i_dquot[cnt])
+			continue;
+		if (unlikely(IS_ERR(got[cnt]))) {
+			err = (int)PTR_ERR(got[cnt]);
+			/* If dqget() was raced with quotaon() then we have to
+			 * repeat lookup. */
+			if (err == -ESRCH) {
+				err = 0;
+				up_write(&sb_dqopt(sb)->dqptr_sem);
+				dqput_all(got);
+				goto repeat;
+			}
+			goto out_err;
 		}
+		inode->i_dquot[cnt] = got[cnt];
+		got[cnt] = NULL;
+		/*
+		 * Make quota reservation system happy if someone
+		 * did a write before quota was turned on
+		 */
+		rsv = inode_get_rsv_space(inode);
+		if (unlikely(rsv))
+			dquot_resv_space(inode->i_dquot[cnt], rsv);
 	}
 out_err:
 	up_write(&sb_dqopt(sb)->dqptr_sem);
 	/* Drop unused references */
 	dqput_all(got);
+	return err;
 }
 
-void dquot_initialize(struct inode *inode)
+int dquot_initialize(struct inode *inode)
 {
-	__dquot_initialize(inode, -1);
+	return __dquot_initialize(inode, -1);
 }
 EXPORT_SYMBOL(dquot_initialize);
 
@@ -1696,6 +1716,7 @@ static int __dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask
 	char warntype_to[MAXQUOTAS];
 	char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
 
+repeat:
 	/* First test before acquiring mutex - solves deadlocks when we
          * re-enter the quota code and are already holding the mutex */
 	if (IS_NOQUOTA(inode))
@@ -1721,15 +1742,29 @@ static int __dquot_transfer(struct inode *inode, qid_t *chid, unsigned long mask
 	space = cur_space + rsv_space;
 	/* Build the transfer_from list and check the limits */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-		if (!transfer_to[cnt])
+		if (!(mask & (1 << cnt)))
 			continue;
+		if (unlikely(IS_ERR(transfer_to[cnt]))) {
+			ret = (int)PTR_ERR(transfer_to[cnt]);
+			/* If dqget() was raced with quotaon() then we have to
+			 * repeat lookup. */
+			if (ret == -ESRCH) {
+				ret = 0;
+				spin_unlock(&dq_data_lock);
+				up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
+				dqput_all(transfer_to);
+				goto repeat;
+			}
+			goto bad_quota;
+		}
+
 		transfer_from[cnt] = inode->i_dquot[cnt];
 		ret = check_idq(transfer_to[cnt], 1, warntype_to + cnt);
 		if (ret)
-			goto over_quota;
+			goto bad_quota;
 		ret = check_bdq(transfer_to[cnt], space, 0, warntype_to + cnt);
 		if (ret)
-			goto over_quota;
+			goto bad_quota;
 	}
 
 	/*
@@ -1776,7 +1811,7 @@ put_all:
 	dqput_all(transfer_from);
 	dqput_all(transfer_to);
 	return ret;
-over_quota:
+bad_quota:
 	spin_unlock(&dq_data_lock);
 	up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
 	/* Clear dquot pointers we don't want to dqput() */
@@ -2302,8 +2337,8 @@ int vfs_get_dqblk(struct super_block *sb, int type, qid_t id,
 	struct dquot *dquot;
 
 	dquot = dqget(sb, id, type);
-	if (!dquot)
-		return -ESRCH;
+	if (IS_ERR(dquot))
+		return PTR_ERR(dquot);
 	do_get_dqblk(dquot, di);
 	dqput(dquot);
 
@@ -2396,8 +2431,8 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id,
 	int rc;
 
 	dquot = dqget(sb, id, type);
-	if (!dquot) {
-		rc = -ESRCH;
+	if (IS_ERR(dquot)) {
+		rc = PTR_ERR(dquot);
 		goto out;
 	}
 	rc = do_set_dqblk(dquot, di);
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index fd8f70b..6b50f98 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -31,7 +31,7 @@ void inode_add_rsv_space(struct inode *inode, qsize_t number);
 void inode_claim_rsv_space(struct inode *inode, qsize_t number);
 void inode_sub_rsv_space(struct inode *inode, qsize_t number);
 
-void dquot_initialize(struct inode *inode);
+int dquot_initialize(struct inode *inode);
 void dquot_drop(struct inode *inode);
 struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
 void dqput(struct dquot *dquot);
@@ -206,8 +206,9 @@ static inline int sb_any_quota_active(struct super_block *sb)
 #define sb_dquot_ops				(NULL)
 #define sb_quotactl_ops				(NULL)
 
-static inline void dquot_initialize(struct inode *inode)
+static inline int dquot_initialize(struct inode *inode)
 {
+	return 0;
 }
 
 static inline void dquot_drop(struct inode *inode)
-- 
1.6.6.1

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux