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:
Родитель
b462707e7c
Коммит
fd8fbfc170
217
fs/quota/dquot.c
217
fs/quota/dquot.c
|
@ -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);
|
||||||
|
if (reserve)
|
||||||
|
dquot_free_reserved_space(inode->i_dquot[cnt], number);
|
||||||
|
else
|
||||||
dquot_decr_space(inode->i_dquot[cnt], number);
|
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++) {
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче