quota: decouple fs reserved space from quota reservation

Currently inode_reservation is managed by fs itself and this
reservation is transfered on dquot_transfer(). This means what
inode_reservation must always be in sync with
dquot->dq_dqb.dqb_rsvspace. Otherwise dquot_transfer() will result
in incorrect quota(WARN_ON in dquot_claim_reserved_space() will be
triggered)
This is not easy because of complex locking order issues
for example http://bugzilla.kernel.org/show_bug.cgi?id=14739

The patch introduce quota reservation field for each fs-inode
(fs specific inode is used in order to prevent bloating generic
vfs inode). This reservation is managed by quota code internally
similar to i_blocks/i_bytes and may not be always in sync with
internal fs reservation.

Also perform some code rearrangement:
- Unify dquot_reserve_space() and dquot_reserve_space()
- Unify dquot_release_reserved_space() and dquot_free_space()
- Also this patch add missing warning update to release_rsv()
  dquot_release_reserved_space() must call flush_warnings() as
  dquot_free_space() does.

Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Dmitry Monakhov 2009-12-14 15:21:13 +03:00 committed by Jan Kara
parent b462707e7c
commit fd8fbfc170
2 changed files with 125 additions and 99 deletions

View file

@ -1318,6 +1318,67 @@ void vfs_dq_drop(struct inode *inode)
} }
EXPORT_SYMBOL(vfs_dq_drop); EXPORT_SYMBOL(vfs_dq_drop);
/*
* inode_reserved_space is managed internally by quota, and protected by
* i_lock similar to i_blocks+i_bytes.
*/
static qsize_t *inode_reserved_space(struct inode * inode)
{
/* Filesystem must explicitly define it's own method in order to use
* quota reservation interface */
BUG_ON(!inode->i_sb->dq_op->get_reserved_space);
return inode->i_sb->dq_op->get_reserved_space(inode);
}
static void inode_add_rsv_space(struct inode *inode, qsize_t number)
{
spin_lock(&inode->i_lock);
*inode_reserved_space(inode) += number;
spin_unlock(&inode->i_lock);
}
static void inode_claim_rsv_space(struct inode *inode, qsize_t number)
{
spin_lock(&inode->i_lock);
*inode_reserved_space(inode) -= number;
__inode_add_bytes(inode, number);
spin_unlock(&inode->i_lock);
}
static void inode_sub_rsv_space(struct inode *inode, qsize_t number)
{
spin_lock(&inode->i_lock);
*inode_reserved_space(inode) -= number;
spin_unlock(&inode->i_lock);
}
static qsize_t inode_get_rsv_space(struct inode *inode)
{
qsize_t ret;
spin_lock(&inode->i_lock);
ret = *inode_reserved_space(inode);
spin_unlock(&inode->i_lock);
return ret;
}
static void inode_incr_space(struct inode *inode, qsize_t number,
int reserve)
{
if (reserve)
inode_add_rsv_space(inode, number);
else
inode_add_bytes(inode, number);
}
static void inode_decr_space(struct inode *inode, qsize_t number, int reserve)
{
if (reserve)
inode_sub_rsv_space(inode, number);
else
inode_sub_bytes(inode, number);
}
/* /*
* Following four functions update i_blocks+i_bytes fields and * Following four functions update i_blocks+i_bytes fields and
* quota information (together with appropriate checks) * quota information (together with appropriate checks)
@ -1336,6 +1397,21 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
int cnt, ret = QUOTA_OK; int cnt, ret = QUOTA_OK;
char warntype[MAXQUOTAS]; char warntype[MAXQUOTAS];
/*
* First test before acquiring mutex - solves deadlocks when we
* re-enter the quota code and are already holding the mutex
*/
if (IS_NOQUOTA(inode)) {
inode_incr_space(inode, number, reserve);
goto out;
}
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode)) {
inode_incr_space(inode, number, reserve);
goto out_unlock;
}
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
warntype[cnt] = QUOTA_NL_NOWARN; warntype[cnt] = QUOTA_NL_NOWARN;
@ -1346,7 +1422,8 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt) if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt)
== NO_QUOTA) { == NO_QUOTA) {
ret = NO_QUOTA; ret = NO_QUOTA;
goto out_unlock; spin_unlock(&dq_data_lock);
goto out_flush_warn;
} }
} }
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
@ -1357,64 +1434,32 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
else else
dquot_incr_space(inode->i_dquot[cnt], number); dquot_incr_space(inode->i_dquot[cnt], number);
} }
if (!reserve) inode_incr_space(inode, number, reserve);
inode_add_bytes(inode, number);
out_unlock:
spin_unlock(&dq_data_lock); spin_unlock(&dq_data_lock);
if (reserve)
goto out_flush_warn;
/* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (inode->i_dquot[cnt])
mark_dquot_dirty(inode->i_dquot[cnt]);
out_flush_warn:
flush_warnings(inode->i_dquot, warntype); flush_warnings(inode->i_dquot, warntype);
out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
out:
return ret; return ret;
} }
int dquot_alloc_space(struct inode *inode, qsize_t number, int warn) int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
{ {
int cnt, ret = QUOTA_OK; return __dquot_alloc_space(inode, number, warn, 0);
/*
* First test before acquiring mutex - solves deadlocks when we
* re-enter the quota code and are already holding the mutex
*/
if (IS_NOQUOTA(inode)) {
inode_add_bytes(inode, number);
goto out;
}
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode)) {
inode_add_bytes(inode, number);
goto out_unlock;
}
ret = __dquot_alloc_space(inode, number, warn, 0);
if (ret == NO_QUOTA)
goto out_unlock;
/* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (inode->i_dquot[cnt])
mark_dquot_dirty(inode->i_dquot[cnt]);
out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
out:
return ret;
} }
EXPORT_SYMBOL(dquot_alloc_space); EXPORT_SYMBOL(dquot_alloc_space);
int dquot_reserve_space(struct inode *inode, qsize_t number, int warn) int dquot_reserve_space(struct inode *inode, qsize_t number, int warn)
{ {
int ret = QUOTA_OK; return __dquot_alloc_space(inode, number, warn, 1);
if (IS_NOQUOTA(inode))
goto out;
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode))
goto out_unlock;
ret = __dquot_alloc_space(inode, number, warn, 1);
out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
out:
return ret;
} }
EXPORT_SYMBOL(dquot_reserve_space); EXPORT_SYMBOL(dquot_reserve_space);
@ -1471,14 +1516,14 @@ int dquot_claim_space(struct inode *inode, qsize_t number)
int ret = QUOTA_OK; int ret = QUOTA_OK;
if (IS_NOQUOTA(inode)) { if (IS_NOQUOTA(inode)) {
inode_add_bytes(inode, number); inode_claim_rsv_space(inode, number);
goto out; goto out;
} }
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode)) { if (IS_NOQUOTA(inode)) {
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
inode_add_bytes(inode, number); inode_claim_rsv_space(inode, number);
goto out; goto out;
} }
@ -1490,7 +1535,7 @@ int dquot_claim_space(struct inode *inode, qsize_t number)
number); number);
} }
/* Update inode bytes */ /* Update inode bytes */
inode_add_bytes(inode, number); inode_claim_rsv_space(inode, number);
spin_unlock(&dq_data_lock); spin_unlock(&dq_data_lock);
/* Dirtify all the dquots - this can block when journalling */ /* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
@ -1502,39 +1547,10 @@ out:
} }
EXPORT_SYMBOL(dquot_claim_space); EXPORT_SYMBOL(dquot_claim_space);
/*
* Release reserved quota space
*/
void dquot_release_reserved_space(struct inode *inode, qsize_t number)
{
int cnt;
if (IS_NOQUOTA(inode))
goto out;
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
if (IS_NOQUOTA(inode))
goto out_unlock;
spin_lock(&dq_data_lock);
/* Release reserved dquots */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (inode->i_dquot[cnt])
dquot_free_reserved_space(inode->i_dquot[cnt], number);
}
spin_unlock(&dq_data_lock);
out_unlock:
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
out:
return;
}
EXPORT_SYMBOL(dquot_release_reserved_space);
/* /*
* This operation can block, but only after everything is updated * This operation can block, but only after everything is updated
*/ */
int dquot_free_space(struct inode *inode, qsize_t number) int __dquot_free_space(struct inode *inode, qsize_t number, int reserve)
{ {
unsigned int cnt; unsigned int cnt;
char warntype[MAXQUOTAS]; char warntype[MAXQUOTAS];
@ -1543,7 +1559,7 @@ int dquot_free_space(struct inode *inode, qsize_t number)
* re-enter the quota code and are already holding the mutex */ * re-enter the quota code and are already holding the mutex */
if (IS_NOQUOTA(inode)) { if (IS_NOQUOTA(inode)) {
out_sub: out_sub:
inode_sub_bytes(inode, number); inode_decr_space(inode, number, reserve);
return QUOTA_OK; return QUOTA_OK;
} }
@ -1558,20 +1574,42 @@ out_sub:
if (!inode->i_dquot[cnt]) if (!inode->i_dquot[cnt])
continue; continue;
warntype[cnt] = info_bdq_free(inode->i_dquot[cnt], number); warntype[cnt] = info_bdq_free(inode->i_dquot[cnt], number);
dquot_decr_space(inode->i_dquot[cnt], number); if (reserve)
dquot_free_reserved_space(inode->i_dquot[cnt], number);
else
dquot_decr_space(inode->i_dquot[cnt], number);
} }
inode_sub_bytes(inode, number); inode_decr_space(inode, number, reserve);
spin_unlock(&dq_data_lock); spin_unlock(&dq_data_lock);
if (reserve)
goto out_unlock;
/* Dirtify all the dquots - this can block when journalling */ /* Dirtify all the dquots - this can block when journalling */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) for (cnt = 0; cnt < MAXQUOTAS; cnt++)
if (inode->i_dquot[cnt]) if (inode->i_dquot[cnt])
mark_dquot_dirty(inode->i_dquot[cnt]); mark_dquot_dirty(inode->i_dquot[cnt]);
out_unlock:
flush_warnings(inode->i_dquot, warntype); flush_warnings(inode->i_dquot, warntype);
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
return QUOTA_OK; return QUOTA_OK;
} }
int dquot_free_space(struct inode *inode, qsize_t number)
{
return __dquot_free_space(inode, number, 0);
}
EXPORT_SYMBOL(dquot_free_space); EXPORT_SYMBOL(dquot_free_space);
/*
* Release reserved quota space
*/
void dquot_release_reserved_space(struct inode *inode, qsize_t number)
{
__dquot_free_space(inode, number, 1);
}
EXPORT_SYMBOL(dquot_release_reserved_space);
/* /*
* This operation can block, but only after everything is updated * This operation can block, but only after everything is updated
*/ */
@ -1609,19 +1647,6 @@ int dquot_free_inode(const struct inode *inode, qsize_t number)
} }
EXPORT_SYMBOL(dquot_free_inode); EXPORT_SYMBOL(dquot_free_inode);
/*
* call back function, get reserved quota space from underlying fs
*/
qsize_t dquot_get_reserved_space(struct inode *inode)
{
qsize_t reserved_space = 0;
if (sb_any_quota_active(inode->i_sb) &&
inode->i_sb->dq_op->get_reserved_space)
reserved_space = inode->i_sb->dq_op->get_reserved_space(inode);
return reserved_space;
}
/* /*
* Transfer the number of inode and blocks from one diskquota to an other. * Transfer the number of inode and blocks from one diskquota to an other.
* *
@ -1665,7 +1690,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
} }
spin_lock(&dq_data_lock); spin_lock(&dq_data_lock);
cur_space = inode_get_bytes(inode); cur_space = inode_get_bytes(inode);
rsv_space = dquot_get_reserved_space(inode); rsv_space = inode_get_rsv_space(inode);
space = cur_space + rsv_space; space = cur_space + rsv_space;
/* Build the transfer_from list and check the limits */ /* Build the transfer_from list and check the limits */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {

View file

@ -315,8 +315,9 @@ struct dquot_operations {
int (*claim_space) (struct inode *, qsize_t); int (*claim_space) (struct inode *, qsize_t);
/* release rsved quota for delayed alloc */ /* release rsved quota for delayed alloc */
void (*release_rsv) (struct inode *, qsize_t); void (*release_rsv) (struct inode *, qsize_t);
/* get reserved quota for delayed alloc */ /* get reserved quota for delayed alloc, value returned is managed by
qsize_t (*get_reserved_space) (struct inode *); * quota code only */
qsize_t *(*get_reserved_space) (struct inode *);
}; };
/* Operations handling requests from userspace */ /* Operations handling requests from userspace */