Commit 0a5a9c72 authored by Jan Kara's avatar Jan Kara

quota: Fix warning when a delayed write happens before quota is enabled

If a delayed-allocation write happens before quota is enabled, the
kernel spits out a warning:
WARNING: at fs/quota/dquot.c:988 dquot_claim_space+0x77/0x112()

because the fact that user has some delayed allocation is not recorded
in quota structure.

Make dquot_initialize() update amount of reserved space for user if it sees
inode has some space reserved. Also make sure that reserved quota space does
not go negative and we warn about the filesystem bug just once.
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent c469070a
...@@ -229,6 +229,8 @@ static struct hlist_head *dquot_hash; ...@@ -229,6 +229,8 @@ static struct hlist_head *dquot_hash;
struct dqstats dqstats; struct dqstats dqstats;
EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dqstats);
static qsize_t inode_get_rsv_space(struct inode *inode);
static inline unsigned int static inline unsigned int
hashfn(const struct super_block *sb, unsigned int id, int type) hashfn(const struct super_block *sb, unsigned int id, int type)
{ {
...@@ -844,11 +846,14 @@ static int dqinit_needed(struct inode *inode, int type) ...@@ -844,11 +846,14 @@ static int dqinit_needed(struct inode *inode, int type)
static void add_dquot_ref(struct super_block *sb, int type) static void add_dquot_ref(struct super_block *sb, int type)
{ {
struct inode *inode, *old_inode = NULL; struct inode *inode, *old_inode = NULL;
int reserved = 0;
spin_lock(&inode_lock); spin_lock(&inode_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW)) if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW))
continue; continue;
if (unlikely(inode_get_rsv_space(inode) > 0))
reserved = 1;
if (!atomic_read(&inode->i_writecount)) if (!atomic_read(&inode->i_writecount))
continue; continue;
if (!dqinit_needed(inode, type)) if (!dqinit_needed(inode, type))
...@@ -869,6 +874,12 @@ static void add_dquot_ref(struct super_block *sb, int type) ...@@ -869,6 +874,12 @@ static void add_dquot_ref(struct super_block *sb, int type)
} }
spin_unlock(&inode_lock); spin_unlock(&inode_lock);
iput(old_inode); 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);
}
} }
/* /*
...@@ -982,10 +993,12 @@ static inline void dquot_resv_space(struct dquot *dquot, qsize_t number) ...@@ -982,10 +993,12 @@ static inline void dquot_resv_space(struct dquot *dquot, qsize_t number)
/* /*
* Claim reserved quota space * Claim reserved quota space
*/ */
static void dquot_claim_reserved_space(struct dquot *dquot, static void dquot_claim_reserved_space(struct dquot *dquot, qsize_t number)
qsize_t number)
{ {
WARN_ON(dquot->dq_dqb.dqb_rsvspace < number); if (dquot->dq_dqb.dqb_rsvspace < number) {
WARN_ON_ONCE(1);
number = dquot->dq_dqb.dqb_rsvspace;
}
dquot->dq_dqb.dqb_curspace += number; dquot->dq_dqb.dqb_curspace += number;
dquot->dq_dqb.dqb_rsvspace -= number; dquot->dq_dqb.dqb_rsvspace -= number;
} }
...@@ -993,7 +1006,12 @@ static void dquot_claim_reserved_space(struct dquot *dquot, ...@@ -993,7 +1006,12 @@ static void dquot_claim_reserved_space(struct dquot *dquot,
static inline static inline
void dquot_free_reserved_space(struct dquot *dquot, qsize_t number) void dquot_free_reserved_space(struct dquot *dquot, qsize_t number)
{ {
if (dquot->dq_dqb.dqb_rsvspace >= number)
dquot->dq_dqb.dqb_rsvspace -= number; dquot->dq_dqb.dqb_rsvspace -= number;
else {
WARN_ON_ONCE(1);
dquot->dq_dqb.dqb_rsvspace = 0;
}
} }
static void dquot_decr_inodes(struct dquot *dquot, qsize_t number) static void dquot_decr_inodes(struct dquot *dquot, qsize_t number)
...@@ -1246,6 +1264,7 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space) ...@@ -1246,6 +1264,7 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space)
return QUOTA_NL_BHARDBELOW; return QUOTA_NL_BHARDBELOW;
return QUOTA_NL_NOWARN; return QUOTA_NL_NOWARN;
} }
/* /*
* Initialize quota pointers in inode * Initialize quota pointers in inode
* We do things in a bit complicated way but by that we avoid calling * We do things in a bit complicated way but by that we avoid calling
...@@ -1257,6 +1276,7 @@ int dquot_initialize(struct inode *inode, int type) ...@@ -1257,6 +1276,7 @@ int dquot_initialize(struct inode *inode, int type)
int cnt, ret = 0; int cnt, ret = 0;
struct dquot *got[MAXQUOTAS] = { NULL, NULL }; struct dquot *got[MAXQUOTAS] = { NULL, NULL };
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
qsize_t rsv;
/* First test before acquiring mutex - solves deadlocks when we /* First test before acquiring mutex - solves deadlocks when we
* re-enter the quota code and are already holding the mutex */ * re-enter the quota code and are already holding the mutex */
...@@ -1290,6 +1310,13 @@ int dquot_initialize(struct inode *inode, int type) ...@@ -1290,6 +1310,13 @@ int dquot_initialize(struct inode *inode, int type)
if (!inode->i_dquot[cnt]) { if (!inode->i_dquot[cnt]) {
inode->i_dquot[cnt] = got[cnt]; inode->i_dquot[cnt] = got[cnt];
got[cnt] = NULL; 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: out_err:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment