[PATCH 1/2] RFC quota: add generic quota error handlers. v2

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

 



Quota may becomes inconsistent or corrupted due to number of reasons.
If so, we have to print unified error message (unified means that it
may be grepped from dmesg easily) and allow corresponding filesystem
to know about possible quota inconsistency. Later filesystem will
make a decision about forced fsck or quotacheck.

IMHO it is reasonable to consider a possibility to add new field to
on_disk quota header where error_state may be stored to signal
that quota-file's data is probably incorrect.

changes from v1:

- Add dump manipulation flag to prevent spam due to massive quota
  corruption.
- Add ->notify_error() callback. It is responsible to signal
  fs about dquot inconsistency. It has much in common with taint flags.
  This approach is better than:
  1) explicit error provoking (return EIO from most of quota calls after
     quota goes corrupted) because even after quota corruption, fs is
     still usable.
 2) Passive quota state flags check (as it was done in previous version).
    Because host may goes down without correct filesystem umount procedure.

- Minor fixes according to Jan's comments.

Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx>
---
 fs/quota/dquot.c      |   64 ++++++++++++++++++++++++----------
 fs/quota/netlink.c    |    8 ++--
 fs/quota/quota.c      |   50 ++++++++++++++++++++++++++
 fs/quota/quota_tree.c |   92 +++++++++++++++++++++++++++---------------------
 fs/quota/quota_v1.c   |   10 +++---
 fs/quota/quota_v2.c   |   14 +++----
 include/linux/quota.h |   22 ++++++++++--
 7 files changed, 181 insertions(+), 79 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 7ccef66..21235a4 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -134,7 +134,6 @@ static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_state_lock);
 __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_data_lock);
 EXPORT_SYMBOL(dq_data_lock);
 
-static char *quotatypes[] = INITQFNAMES;
 static struct quota_format_type *quota_formats;	/* List of registered formats */
 static struct quota_module_name module_names[] = INIT_QUOTA_MODULE_NAMES;
 
@@ -706,11 +705,9 @@ void dqput(struct dquot *dquot)
 		return;
 #ifdef __DQUOT_PARANOIA
 	if (!atomic_read(&dquot->dq_count)) {
-		printk("VFS: dqput: trying to free free dquot\n");
-		printk("VFS: device %s, dquot of %s %d\n",
-			dquot->dq_sb->s_id,
-			quotatypes[dquot->dq_type],
-			dquot->dq_id);
+		quota_error(dquot->dq_sb, dquot->dq_type, 1,
+			"Trying to free free dquot type:%d with count:%d",
+			dquot->dq_id, atomic_read(&dquot->dq_count));
 		BUG();
 	}
 #endif
@@ -736,9 +733,9 @@ we_slept:
 		/* Commit dquot before releasing */
 		ret = dquot->dq_sb->dq_op->write_dquot(dquot);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: cannot write quota structure on "
-				"device %s (error %d). Quota may get out of "
-				"sync!\n", dquot->dq_sb->s_id, ret);
+			quota_error(dquot->dq_sb, dquot->dq_type, 1,
+				"Cannot write quota structure (errror :%d)"
+				"Quota may get out of sync!\n", ret);
 			/*
 			 * We clear dirty bit anyway, so that we avoid
 			 * infinite loop here
@@ -913,9 +910,10 @@ static void add_dquot_ref(struct super_block *sb, int type)
 	iput(old_inode);
 
 	if (reserved) {
-		printk(KERN_WARNING "VFS (%s): Writes happened before quota"
-			" was turned on thus quota information is probably "
-			"inconsistent. Please run quotacheck(8).\n", sb->s_id);
+		quota_error(sb, type == -1 ? 0 : type, 1,
+			"Writes happened before quota was turned on thus quota "
+			"information is probably inconsistent. "
+			"Please run quotacheck(8)");
 	}
 }
 
@@ -945,7 +943,10 @@ static int remove_inode_dquot_ref(struct inode *inode, int type,
 		if (dqput_blocks(dquot)) {
 #ifdef __DQUOT_PARANOIA
 			if (atomic_read(&dquot->dq_count) != 1)
-				printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", atomic_read(&dquot->dq_count));
+				quota_warning(dquot->dq_sb, dquot->dq_type, 1,
+					"Adding dquot with dq_count %d to "
+					"dispose list.\n",
+					atomic_read(&dquot->dq_count));
 #endif
 			spin_lock(&dq_list_lock);
 			/* As dquot must have currently users it can't be on
@@ -1032,8 +1033,12 @@ static inline void dquot_resv_space(struct dquot *dquot, qsize_t number)
  */
 static void dquot_claim_reserved_space(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_dqb.dqb_rsvspace < number) {
-		WARN_ON_ONCE(1);
+	if (unlikely(dquot->dq_dqb.dqb_rsvspace < number)) {
+		quota_error(dquot->dq_sb, dquot->dq_type, 0,
+			"Incorrect quota reservation for quota id:%d, "
+			"rsvspace:%lld, claim:%lld, thus quota information "
+			"is probably inconsistent. Please run quotacheck(8).",
+			dquot->dq_id, dquot->dq_dqb.dqb_rsvspace, number);
 		number = dquot->dq_dqb.dqb_rsvspace;
 	}
 	dquot->dq_dqb.dqb_curspace += number;
@@ -1043,10 +1048,14 @@ static void dquot_claim_reserved_space(struct dquot *dquot, qsize_t number)
 static inline
 void dquot_free_reserved_space(struct dquot *dquot, qsize_t number)
 {
-	if (dquot->dq_dqb.dqb_rsvspace >= number)
+	if (unlikely(dquot->dq_dqb.dqb_rsvspace >= number))
 		dquot->dq_dqb.dqb_rsvspace -= number;
 	else {
-		WARN_ON_ONCE(1);
+		quota_error(dquot->dq_sb, dquot->dq_type, 0,
+			"Incorrect quota reservation for quota id:%d, "
+			"rsvspace:%lld, claim:%lld, thus quota information "
+			"is probably inconsistent. Please run quotacheck(8).",
+			dquot->dq_id, dquot->dq_dqb.dqb_rsvspace, number);
 		dquot->dq_dqb.dqb_rsvspace = 0;
 	}
 }
@@ -1437,7 +1446,16 @@ EXPORT_SYMBOL(inode_add_rsv_space);
 void inode_claim_rsv_space(struct inode *inode, qsize_t number)
 {
 	spin_lock(&inode->i_lock);
-	*inode_reserved_space(inode) -= number;
+	if (unlikely(*inode_reserved_space(inode) < number)) {
+		
+		quota_error(inode->i_sb, 0, 0, "Incorrect quota "
+			"reservation for inode:%ld, rsvspace:%lld, claim:%lld,"
+			"thus quota information is probably inconsistent. "
+			"Please run quotacheck(8).", inode->i_ino,
+			*inode_reserved_space(inode), number);
+		*inode_reserved_space(inode) = 0;
+	} else
+		*inode_reserved_space(inode) -= number;
 	__inode_add_bytes(inode, number);
 	spin_unlock(&inode->i_lock);
 }
@@ -1446,7 +1464,15 @@ EXPORT_SYMBOL(inode_claim_rsv_space);
 void inode_sub_rsv_space(struct inode *inode, qsize_t number)
 {
 	spin_lock(&inode->i_lock);
-	*inode_reserved_space(inode) -= number;
+	if (unlikely(*inode_reserved_space(inode) < number)) {
+		quota_error(inode->i_sb, 0, 0, "Incorrect quota "
+			"reservation for inode:%ld, rsvspace:%lld, claim:%lld,"
+			"thus quota information is probably inconsistent. "
+			"Please run quotacheck(8).", inode->i_ino,
+			*inode_reserved_space(inode), number);
+		*inode_reserved_space(inode) = 0;
+	} else
+		*inode_reserved_space(inode) -= number;
 	spin_unlock(&inode->i_lock);
 }
 EXPORT_SYMBOL(inode_sub_rsv_space);
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index 2663ed9..0865ae3 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -45,14 +45,14 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
 	skb = genlmsg_new(msg_size, GFP_NOFS);
 	if (!skb) {
 		printk(KERN_ERR
-		  "VFS: Not enough memory to send quota warning.\n");
+		  "QUOTA: Not enough memory to send quota warning.\n");
 		return;
 	}
 	msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq),
 			&quota_genl_family, 0, QUOTA_NL_C_WARNING);
 	if (!msg_head) {
 		printk(KERN_ERR
-		  "VFS: Cannot store netlink header in quota warning.\n");
+		  "QUOTA: Cannot store netlink header in quota warning.\n");
 		goto err_out;
 	}
 	ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
@@ -78,7 +78,7 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
 	genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
 	return;
 attr_err_out:
-	printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
+	printk(KERN_ERR "QUOTA: Not enough space to compose quota message!\n");
 err_out:
 	kfree_skb(skb);
 }
@@ -88,7 +88,7 @@ static int __init quota_init(void)
 {
 	if (genl_register_family(&quota_genl_family) != 0)
 		printk(KERN_ERR
-		       "VFS: Failed to create quota netlink interface.\n");
+		       "QUOTA: Failed to create quota netlink interface.\n");
 	return 0;
 };
 
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 95388f9..cdeab3e 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -11,6 +11,7 @@
 #include <asm/current.h>
 #include <asm/uaccess.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/buffer_head.h>
@@ -19,6 +20,55 @@
 #include <linux/types.h>
 #include <linux/writeback.h>
 
+static char *quotatypes[] = INITQFNAMES;
+static int quota_handle_error(struct super_block *sb, int type, int force)
+{
+
+	int was_set;
+
+	was_set = test_and_set_bit(_DQUOT_ERROR + _DQUOT_STATE_FLAGS
+					* type,	&sb_dqopt(sb)->flags);
+	if (!was_set || force) {
+		if (sb->dq_op->notify_error)
+			sb->dq_op->notify_error(sb,type);
+		return 1;
+	}
+	return 0;
+}
+
+void __quota_error(struct super_block *sb, int type, int force_dump,
+		const char *function, const char *fmt, ...)
+{
+	va_list args;
+
+	if (!quota_handle_error(sb, type, force_dump))
+		return;
+	va_start(args, fmt);
+	printk(KERN_CRIT "QUOTA: error (device:%s, type:%s): %s: ",
+		sb->s_id, quotatypes[type], function);
+	vprintk(fmt, args);
+	printk("\n");
+	va_end(args);
+}
+EXPORT_SYMBOL(__quota_error);
+
+void __quota_warning(struct super_block *sb, int type, int force_dump,
+		const char *func, const char *fmt, ...)
+{
+	va_list args;
+	if (test_bit(_DQUOT_ERROR + _DQUOT_STATE_FLAGS	* type,
+			&sb_dqopt(sb)->flags) && !force_dump)
+		return;
+
+	va_start(args, fmt);
+	printk(KERN_WARNING "QUOTA: warning (device:%s, type:%s): %s: ",
+			sb->s_id, quotatypes[type], func);
+	vprintk(fmt, args);
+	printk("\n");
+	va_end(args);
+}
+EXPORT_SYMBOL(__quota_warning);
+
 static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
 				     qid_t id)
 {
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index f81f4bc..c0730d8 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -152,8 +152,8 @@ static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
 	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
 	/* No matter whether write succeeds block is out of list */
 	if (write_blk(info, blk, buf) < 0)
-		printk(KERN_ERR
-		       "VFS: Can't write block (%u) with free entries.\n",
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't write block (%u) with free entries.",
 		       blk);
 	return 0;
 out_buf:
@@ -244,9 +244,9 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
 	if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
 		*err = remove_free_dqentry(info, buf, blk);
 		if (*err < 0) {
-			printk(KERN_ERR "VFS: find_free_dqentry(): Can't "
-			       "remove block (%u) from entry free list.\n",
-			       blk);
+			quota_error(info->dqi_sb, info->dqi_type, 1,
+				"Can't remove block (%u) from entry free list.",
+				blk);
 			goto out_buf;
 		}
 	}
@@ -260,16 +260,17 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
 	}
 #ifdef __QUOTA_QT_PARANOIA
 	if (i == qtree_dqstr_in_blk(info)) {
-		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full "
-				"but it shouldn't.\n");
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Data block full but it shouldn't.");
 		*err = -EIO;
 		goto out_buf;
 	}
 #endif
 	*err = write_blk(info, blk, buf);
 	if (*err < 0) {
-		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota "
-				"data block %u.\n", blk);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't write quota data block %u.",
+			blk);
 		goto out_buf;
 	}
 	dquot->dq_off = (blk << info->dqi_blocksize_bits) +
@@ -303,8 +304,8 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 	} else {
 		ret = read_blk(info, *treeblk, buf);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Can't read tree quota block "
-					"%u.\n", *treeblk);
+			quota_error(info->dqi_sb, info->dqi_type, 1,
+				"Can't read tree quota block %u.", *treeblk);
 			goto out_buf;
 		}
 	}
@@ -315,9 +316,9 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 	if (depth == info->dqi_qtree_depth - 1) {
 #ifdef __QUOTA_QT_PARANOIA
 		if (newblk) {
-			printk(KERN_ERR "VFS: Inserting already present quota "
-					"entry (block %u).\n",
-			       le32_to_cpu(ref[get_index(info,
+			quota_error(info->dqi_sb, info->dqi_type, 1,
+				"Inserting already present quota entry (block %u).",
+				le32_to_cpu(ref[get_index(info,
 						dquot->dq_id, depth)]));
 			ret = -EIO;
 			goto out_buf;
@@ -365,8 +366,9 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 	if (!dquot->dq_off) {
 		ret = dq_insert_tree(info, dquot);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Error %zd occurred while "
-					"creating quota.\n", ret);
+			quota_error(info->dqi_sb, info->dqi_type, 1,
+				"Error %zd occurred while creating quota.",
+				ret);
 			kfree(ddquot);
 			return ret;
 		}
@@ -377,8 +379,8 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 	ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
 				    dquot->dq_off);
 	if (ret != info->dqi_entry_size) {
-		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
-		       sb->s_id);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Dquota write failed");
 		if (ret >= 0)
 			ret = -ENOSPC;
 	} else {
@@ -402,14 +404,15 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 	if (!buf)
 		return -ENOMEM;
 	if (dquot->dq_off >> info->dqi_blocksize_bits != blk) {
-		printk(KERN_ERR "VFS: Quota structure has offset to other "
-		  "block (%u) than it should (%u).\n", blk,
-		  (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
+		quota_error(info->dqi_sb, info->dqi_type, 1, "Quota structure "
+			"has offset to other block (%u) than it should (%u).",
+			blk, (uint)(dquot->dq_off >> info->dqi_blocksize_bits));
 		goto out_buf;
 	}
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't read quota data block %u", blk);
 		goto out_buf;
 	}
 	dh = (struct qt_disk_dqdbheader *)buf;
@@ -419,8 +422,8 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 		if (ret >= 0)
 			ret = put_free_dqblk(info, buf, blk);
 		if (ret < 0) {
-			printk(KERN_ERR "VFS: Can't move quota data block (%u) "
-			  "to free list.\n", blk);
+			quota_error(info->dqi_sb, info->dqi_type, 1,
+				"Can't move quota data block (%u) to free list.", blk);
 			goto out_buf;
 		}
 	} else {
@@ -432,15 +435,16 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 			/* Insert will write block itself */
 			ret = insert_free_dqentry(info, buf, blk);
 			if (ret < 0) {
-				printk(KERN_ERR "VFS: Can't insert quota data "
-				       "block (%u) to free entry list.\n", blk);
+				quota_error(info->dqi_sb, info->dqi_type, 1,
+					"Can't insert quota data block (%u) "
+					"to free list.", blk);
 				goto out_buf;
 			}
 		} else {
 			ret = write_blk(info, blk, buf);
 			if (ret < 0) {
-				printk(KERN_ERR "VFS: Can't write quota data "
-				  "block %u\n", blk);
+				quota_error(info->dqi_sb, info->dqi_type, 1,
+					"Can't write quota data block %u", blk);
 				goto out_buf;
 			}
 		}
@@ -464,7 +468,8 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 		return -ENOMEM;
 	ret = read_blk(info, *blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't read quota data block %u", *blk);
 		goto out_buf;
 	}
 	newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
@@ -488,8 +493,9 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 		} else {
 			ret = write_blk(info, *blk, buf);
 			if (ret < 0)
-				printk(KERN_ERR "VFS: Can't write quota tree "
-				  "block %u.\n", *blk);
+				quota_error(info->dqi_sb, info->dqi_type, 1,
+					"Can't write quota tree block %u.",
+					*blk);
 		}
 	}
 out_buf:
@@ -521,7 +527,8 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
 		return -ENOMEM;
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't read quota tree block %u.", blk);
 		goto out_buf;
 	}
 	ddquot = buf + sizeof(struct qt_disk_dqdbheader);
@@ -531,8 +538,9 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
 		ddquot += info->dqi_entry_size;
 	}
 	if (i == qtree_dqstr_in_blk(info)) {
-		printk(KERN_ERR "VFS: Quota for id %u referenced "
-		  "but not present.\n", dquot->dq_id);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Quota for id %u referenced but not present.",
+			dquot->dq_id);
 		ret = -EIO;
 		goto out_buf;
 	} else {
@@ -556,7 +564,8 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
 		return -ENOMEM;
 	ret = read_blk(info, blk, buf);
 	if (ret < 0) {
-		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Can't read quota tree block %u.", blk);
 		goto out_buf;
 	}
 	ret = 0;
@@ -590,7 +599,8 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 #ifdef __QUOTA_QT_PARANOIA
 	/* Invalidated quota? */
 	if (!sb_dqopt(dquot->dq_sb)->files[type]) {
-		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Quota invalidated while reading!");
 		return -EIO;
 	}
 #endif
@@ -599,8 +609,9 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 		offset = find_dqentry(info, dquot);
 		if (offset <= 0) {	/* Entry not present? */
 			if (offset < 0)
-				printk(KERN_ERR "VFS: Can't read quota "
-				  "structure for id %u.\n", dquot->dq_id);
+				quota_error(info->dqi_sb, info->dqi_type, 1,
+					"Can't read quota structure for id %u.",
+					dquot->dq_id);
 			dquot->dq_off = 0;
 			set_bit(DQ_FAKE_B, &dquot->dq_flags);
 			memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
@@ -617,8 +628,9 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 	if (ret != info->dqi_entry_size) {
 		if (ret >= 0)
 			ret = -EIO;
-		printk(KERN_ERR "VFS: Error while reading quota "
-				"structure for id %u.\n", dquot->dq_id);
+		quota_error(info->dqi_sb, info->dqi_type, 1,
+			"Error while reading quota structure for id %u.",
+			dquot->dq_id);
 		set_bit(DQ_FAKE_B, &dquot->dq_flags);
 		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
 		kfree(ddquot);
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index 2ae757e..4ed455c 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -95,8 +95,8 @@ static int v1_commit_dqblk(struct dquot *dquot)
 			(char *)&dqblk, sizeof(struct v1_disk_dqblk),
 			v1_dqoff(dquot->dq_id));
 	if (ret != sizeof(struct v1_disk_dqblk)) {
-		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
-			dquot->dq_sb->s_id);
+		quota_error(dquot->dq_sb, dquot->dq_type, 1,
+			"dquota write failed");
 		if (ret >= 0)
 			ret = -EIO;
 		goto out;
@@ -147,9 +147,9 @@ static int v1_check_quota_file(struct super_block *sb, int type)
 		return 1;	/* Probably not new format */
 	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
 		return 1;	/* Definitely not new format */
-	printk(KERN_INFO
-	       "VFS: %s: Refusing to turn on old quota format on given file."
-	       " It probably contains newer quota format.\n", sb->s_id);
+	quota_warning(sb, type, 1,
+	       "Refusing to turn on old quota format on given file."
+	       " It probably contains newer quota format.\n");
         return 0;		/* Seems like a new format file -> refuse it */
 }
 
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index e3da02f..2b14ad8 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -63,8 +63,8 @@ static int v2_read_header(struct super_block *sb, int type,
 	size = sb->s_op->quota_read(sb, type, (char *)dqhead,
 				    sizeof(struct v2_disk_dqheader), 0);
 	if (size != sizeof(struct v2_disk_dqheader)) {
-		printk(KERN_WARNING "quota_v2: Failed header read:"
-		       " expected=%zd got=%zd\n",
+		quota_error(sb, type, 1,
+			"Failed header read: expected=%zd got=%zd",
 			sizeof(struct v2_disk_dqheader), size);
 		return 0;
 	}
@@ -106,14 +106,13 @@ static int v2_read_file_info(struct super_block *sb, int type)
 	size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
-		printk(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n",
-			sb->s_id);
+		quota_error(sb, type, 1, "Can't read info structure");
 		return -1;
 	}
 	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
 	if (!info->dqi_priv) {
-		printk(KERN_WARNING
-		       "Not enough memory for quota information structure.\n");
+		quota_error(sb, type, 1,
+			"Not enough memory for quota information structure.");
 		return -1;
 	}
 	qinfo = info->dqi_priv;
@@ -167,8 +166,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
-		printk(KERN_WARNING "Can't write info structure on device %s.\n",
-			sb->s_id);
+		quota_error(sb, type, 1, "Can't write info structure");
 		return -1;
 	}
 	return 0;
diff --git a/include/linux/quota.h b/include/linux/quota.h
index b462916..d459e43 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -257,7 +257,8 @@ extern struct dqstats dqstats;
 #define DQ_FAKE_B	3	/* no limits only usage */
 #define DQ_READ_B	4	/* dquot was read into memory */
 #define DQ_ACTIVE_B	5	/* dquot is active (dquot_release not called) */
-#define DQ_LASTSET_B	6	/* Following 6 bits (see QIF_) are reserved\
+#define DQ_ERROR_B	6	/* quota corrupted or inconsistent */
+#define DQ_LASTSET_B	7	/* Following 6 bits (see QIF_) are reserved\
 				 * for the mask of entries set via SETQUOTA\
 				 * quotactl. They are set under dq_data_lock\
 				 * and the quota format handling dquot can\
@@ -302,6 +303,7 @@ struct dquot_operations {
 	/* get reserved quota for delayed alloc, value returned is managed by
 	 * quota code only */
 	qsize_t *(*get_reserved_space) (struct inode *);
+	void (*notify_error)(struct super_block *sb, int type); 	/* Called when quota becomes inconsistent or corrupted */
 };
 
 /* Operations handling requests from userspace */
@@ -333,7 +335,8 @@ enum {
 	_DQUOT_SUSPENDED,			/* User diskquotas are off, but
 						 * we have necessary info in
 						 * memory to turn them on */
-	_DQUOT_STATE_FLAGS
+	_DQUOT_ERROR, 				/* Quota is inconsistent or corrupted */
+	_DQUOT_STATE_FLAGS,
 };
 #define DQUOT_USAGE_ENABLED	(1 << _DQUOT_USAGE_ENABLED)
 #define DQUOT_LIMITS_ENABLED	(1 << _DQUOT_LIMITS_ENABLED)
@@ -362,6 +365,19 @@ static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
 	return (flags >> _DQUOT_STATE_FLAGS * type) & DQUOT_STATE_FLAGS;
 }
 
+extern void __quota_error(struct super_block *, int , int,
+			const char *, const char *, ...)
+	__attribute__ ((format (printf, 5, 6)));
+
+extern void __quota_warning(struct super_block *, int, int, const char *,
+			const char *, ...)
+	__attribute__ ((format (printf, 5, 6)));
+
+#define quota_error(sb, type, force_dump, fmt, arg...)			\
+	__quota_error(sb, type, force_dump, __func__, fmt, ##arg)
+#define quota_warning(sb, type, force_dump, fmt, arg...)	\
+	__quota_warning(sb, type, force_dump, __func__, fmt, ##arg)
+
 #ifdef CONFIG_QUOTA_NETLINK_INTERFACE
 extern void quota_send_warning(short type, unsigned int id, dev_t dev,
 			       const char warntype);
@@ -374,7 +390,7 @@ static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
 #endif /* CONFIG_QUOTA_NETLINK_INTERFACE */
 
 struct quota_info {
-	unsigned int flags;			/* Flags for diskquotas on this device */
+	unsigned long flags;			/* Flags for diskquotas on this device */
 	struct mutex dqio_mutex;		/* lock device while I/O in progress */
 	struct mutex dqonoff_mutex;		/* Serialize quotaon & quotaoff */
 	struct rw_semaphore dqptr_sem;		/* serialize ops using quota_info struct, pointers from inode to dquots */
-- 
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