mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-20 08:38:24 +08:00
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-quota-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-quota-2.6: ocfs2: Remove ocfs2_dquot_initialize() and ocfs2_dquot_drop() quota: Improve locking
This commit is contained in:
commit
cc597bc3d3
222
fs/dquot.c
222
fs/dquot.c
@ -87,14 +87,17 @@
|
|||||||
#define __DQUOT_PARANOIA
|
#define __DQUOT_PARANOIA
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two quota SMP locks. dq_list_lock protects all lists with quotas
|
* There are three quota SMP locks. dq_list_lock protects all lists with quotas
|
||||||
* and quota formats and also dqstats structure containing statistics about the
|
* and quota formats, dqstats structure containing statistics about the lists
|
||||||
* lists. dq_data_lock protects data from dq_dqb and also mem_dqinfo structures
|
* dq_data_lock protects data from dq_dqb and also mem_dqinfo structures and
|
||||||
* and also guards consistency of dquot->dq_dqb with inode->i_blocks, i_bytes.
|
* also guards consistency of dquot->dq_dqb with inode->i_blocks, i_bytes.
|
||||||
* i_blocks and i_bytes updates itself are guarded by i_lock acquired directly
|
* i_blocks and i_bytes updates itself are guarded by i_lock acquired directly
|
||||||
* in inode_add_bytes() and inode_sub_bytes().
|
* in inode_add_bytes() and inode_sub_bytes(). dq_state_lock protects
|
||||||
|
* modifications of quota state (on quotaon and quotaoff) and readers who care
|
||||||
|
* about latest values take it as well.
|
||||||
*
|
*
|
||||||
* The spinlock ordering is hence: dq_data_lock > dq_list_lock > i_lock
|
* The spinlock ordering is hence: dq_data_lock > dq_list_lock > i_lock,
|
||||||
|
* dq_list_lock > dq_state_lock
|
||||||
*
|
*
|
||||||
* Note that some things (eg. sb pointer, type, id) doesn't change during
|
* Note that some things (eg. sb pointer, type, id) doesn't change during
|
||||||
* the life of the dquot structure and so needn't to be protected by a lock
|
* the life of the dquot structure and so needn't to be protected by a lock
|
||||||
@ -103,12 +106,7 @@
|
|||||||
* operation is just reading pointers from inode (or not using them at all) the
|
* operation is just reading pointers from inode (or not using them at all) the
|
||||||
* read lock is enough. If pointers are altered function must hold write lock
|
* read lock is enough. If pointers are altered function must hold write lock
|
||||||
* (these locking rules also apply for S_NOQUOTA flag in the inode - note that
|
* (these locking rules also apply for S_NOQUOTA flag in the inode - note that
|
||||||
* for altering the flag i_mutex is also needed). If operation is holding
|
* for altering the flag i_mutex is also needed).
|
||||||
* reference to dquot in other way (e.g. quotactl ops) it must be guarded by
|
|
||||||
* dqonoff_mutex.
|
|
||||||
* This locking assures that:
|
|
||||||
* a) update/access to dquot pointers in inode is serialized
|
|
||||||
* b) everyone is guarded against invalidate_dquots()
|
|
||||||
*
|
*
|
||||||
* Each dquot has its dq_lock mutex. Locked dquots might not be referenced
|
* Each dquot has its dq_lock mutex. Locked dquots might not be referenced
|
||||||
* from inodes (dquot_alloc_space() and such don't check the dq_lock).
|
* from inodes (dquot_alloc_space() and such don't check the dq_lock).
|
||||||
@ -122,10 +120,17 @@
|
|||||||
* Lock ordering (including related VFS locks) is the following:
|
* Lock ordering (including related VFS locks) is the following:
|
||||||
* i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock >
|
* i_mutex > dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock >
|
||||||
* dqio_mutex
|
* dqio_mutex
|
||||||
|
* The lock ordering of dqptr_sem imposed by quota code is only dqonoff_sem >
|
||||||
|
* dqptr_sem. But filesystem has to count with the fact that functions such as
|
||||||
|
* dquot_alloc_space() acquire dqptr_sem and they usually have to be called
|
||||||
|
* from inside a transaction to keep filesystem consistency after a crash. Also
|
||||||
|
* filesystems usually want to do some IO on dquot from ->mark_dirty which is
|
||||||
|
* called with dqptr_sem held.
|
||||||
* i_mutex on quota files is special (it's below dqio_mutex)
|
* i_mutex on quota files is special (it's below dqio_mutex)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(dq_list_lock);
|
static DEFINE_SPINLOCK(dq_list_lock);
|
||||||
|
static DEFINE_SPINLOCK(dq_state_lock);
|
||||||
DEFINE_SPINLOCK(dq_data_lock);
|
DEFINE_SPINLOCK(dq_data_lock);
|
||||||
|
|
||||||
static char *quotatypes[] = INITQFNAMES;
|
static char *quotatypes[] = INITQFNAMES;
|
||||||
@ -428,7 +433,7 @@ static inline void do_destroy_dquot(struct dquot *dquot)
|
|||||||
* quota is disabled and pointers from inodes removed so there cannot be new
|
* quota is disabled and pointers from inodes removed so there cannot be new
|
||||||
* quota users. There can still be some users of quotas due to inodes being
|
* quota users. There can still be some users of quotas due to inodes being
|
||||||
* just deleted or pruned by prune_icache() (those are not attached to any
|
* just deleted or pruned by prune_icache() (those are not attached to any
|
||||||
* list). We have to wait for such users.
|
* list) or parallel quotactl call. We have to wait for such users.
|
||||||
*/
|
*/
|
||||||
static void invalidate_dquots(struct super_block *sb, int type)
|
static void invalidate_dquots(struct super_block *sb, int type)
|
||||||
{
|
{
|
||||||
@ -600,7 +605,6 @@ static struct shrinker dqcache_shrinker = {
|
|||||||
/*
|
/*
|
||||||
* Put reference to dquot
|
* Put reference to dquot
|
||||||
* NOTE: If you change this function please check whether dqput_blocks() works right...
|
* NOTE: If you change this function please check whether dqput_blocks() works right...
|
||||||
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
||||||
*/
|
*/
|
||||||
void dqput(struct dquot *dquot)
|
void dqput(struct dquot *dquot)
|
||||||
{
|
{
|
||||||
@ -696,37 +700,31 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
|
|||||||
return dquot;
|
return dquot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Check whether dquot is in memory.
|
|
||||||
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
|
||||||
*/
|
|
||||||
int dquot_is_cached(struct super_block *sb, unsigned int id, int type)
|
|
||||||
{
|
|
||||||
unsigned int hashent = hashfn(sb, id, type);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
if (!sb_has_quota_active(sb, type))
|
|
||||||
return 0;
|
|
||||||
spin_lock(&dq_list_lock);
|
|
||||||
if (find_dquot(hashent, sb, id, type) != NODQUOT)
|
|
||||||
ret = 1;
|
|
||||||
spin_unlock(&dq_list_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get reference to dquot
|
* Get reference to dquot
|
||||||
* MUST be called with either dqptr_sem or dqonoff_mutex held
|
*
|
||||||
|
* Locking is slightly tricky here. We are guarded from parallel quotaoff()
|
||||||
|
* 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
|
||||||
*/
|
*/
|
||||||
struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
|
struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
|
||||||
{
|
{
|
||||||
unsigned int hashent = hashfn(sb, id, type);
|
unsigned int hashent = hashfn(sb, id, type);
|
||||||
struct dquot *dquot, *empty = NODQUOT;
|
struct dquot *dquot = NODQUOT, *empty = NODQUOT;
|
||||||
|
|
||||||
if (!sb_has_quota_active(sb, type))
|
if (!sb_has_quota_active(sb, type))
|
||||||
return NODQUOT;
|
return NODQUOT;
|
||||||
we_slept:
|
we_slept:
|
||||||
spin_lock(&dq_list_lock);
|
spin_lock(&dq_list_lock);
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
|
if (!sb_has_quota_active(sb, type)) {
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
|
spin_unlock(&dq_list_lock);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
|
|
||||||
if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
|
if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
|
||||||
if (empty == NODQUOT) {
|
if (empty == NODQUOT) {
|
||||||
spin_unlock(&dq_list_lock);
|
spin_unlock(&dq_list_lock);
|
||||||
@ -735,6 +733,7 @@ we_slept:
|
|||||||
goto we_slept;
|
goto we_slept;
|
||||||
}
|
}
|
||||||
dquot = empty;
|
dquot = empty;
|
||||||
|
empty = NODQUOT;
|
||||||
dquot->dq_id = id;
|
dquot->dq_id = id;
|
||||||
/* all dquots go on the inuse_list */
|
/* all dquots go on the inuse_list */
|
||||||
put_inuse(dquot);
|
put_inuse(dquot);
|
||||||
@ -749,8 +748,6 @@ we_slept:
|
|||||||
dqstats.cache_hits++;
|
dqstats.cache_hits++;
|
||||||
dqstats.lookups++;
|
dqstats.lookups++;
|
||||||
spin_unlock(&dq_list_lock);
|
spin_unlock(&dq_list_lock);
|
||||||
if (empty)
|
|
||||||
do_destroy_dquot(empty);
|
|
||||||
}
|
}
|
||||||
/* Wait for dq_lock - after this we know that either dquot_release() is already
|
/* Wait for dq_lock - after this we know that either dquot_release() is already
|
||||||
* finished or it will be canceled due to dq_count > 1 test */
|
* finished or it will be canceled due to dq_count > 1 test */
|
||||||
@ -758,11 +755,15 @@ we_slept:
|
|||||||
/* Read the dquot and instantiate it (everything done only if needed) */
|
/* Read the dquot and instantiate it (everything done only if needed) */
|
||||||
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) {
|
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) {
|
||||||
dqput(dquot);
|
dqput(dquot);
|
||||||
return NODQUOT;
|
dquot = NODQUOT;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
#ifdef __DQUOT_PARANOIA
|
#ifdef __DQUOT_PARANOIA
|
||||||
BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
|
BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
|
||||||
#endif
|
#endif
|
||||||
|
out:
|
||||||
|
if (empty)
|
||||||
|
do_destroy_dquot(empty);
|
||||||
|
|
||||||
return dquot;
|
return dquot;
|
||||||
}
|
}
|
||||||
@ -1198,63 +1199,76 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space)
|
|||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Initialize quota pointers in inode
|
* Initialize quota pointers in inode
|
||||||
* Transaction must be started at entry
|
* We do things in a bit complicated way but by that we avoid calling
|
||||||
|
* dqget() and thus filesystem callbacks under dqptr_sem.
|
||||||
*/
|
*/
|
||||||
int dquot_initialize(struct inode *inode, int type)
|
int dquot_initialize(struct inode *inode, int type)
|
||||||
{
|
{
|
||||||
unsigned int id = 0;
|
unsigned int id = 0;
|
||||||
int cnt, ret = 0;
|
int cnt, ret = 0;
|
||||||
|
struct dquot *got[MAXQUOTAS] = { NODQUOT, NODQUOT };
|
||||||
|
struct super_block *sb = inode->i_sb;
|
||||||
|
|
||||||
/* 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 */
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
return 0;
|
return 0;
|
||||||
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
|
/* First get references to structures we might need. */
|
||||||
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
|
if (type != -1 && cnt != type)
|
||||||
|
continue;
|
||||||
|
switch (cnt) {
|
||||||
|
case USRQUOTA:
|
||||||
|
id = inode->i_uid;
|
||||||
|
break;
|
||||||
|
case GRPQUOTA:
|
||||||
|
id = inode->i_gid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
got[cnt] = dqget(sb, id, cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
down_write(&sb_dqopt(sb)->dqptr_sem);
|
||||||
/* Having dqptr_sem we know NOQUOTA flags can't be altered... */
|
/* Having dqptr_sem we know NOQUOTA flags can't be altered... */
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
goto out_err;
|
goto out_err;
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (type != -1 && cnt != type)
|
if (type != -1 && cnt != type)
|
||||||
continue;
|
continue;
|
||||||
|
/* Avoid races with quotaoff() */
|
||||||
|
if (!sb_has_quota_active(sb, cnt))
|
||||||
|
continue;
|
||||||
if (inode->i_dquot[cnt] == NODQUOT) {
|
if (inode->i_dquot[cnt] == NODQUOT) {
|
||||||
switch (cnt) {
|
inode->i_dquot[cnt] = got[cnt];
|
||||||
case USRQUOTA:
|
got[cnt] = NODQUOT;
|
||||||
id = inode->i_uid;
|
|
||||||
break;
|
|
||||||
case GRPQUOTA:
|
|
||||||
id = inode->i_gid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
inode->i_dquot[cnt] = dqget(inode->i_sb, id, cnt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_err:
|
out_err:
|
||||||
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
up_write(&sb_dqopt(sb)->dqptr_sem);
|
||||||
|
/* Drop unused references */
|
||||||
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
|
dqput(got[cnt]);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release all quotas referenced by inode
|
* Release all quotas referenced by inode
|
||||||
* Transaction must be started at an entry
|
|
||||||
*/
|
*/
|
||||||
int dquot_drop_locked(struct inode *inode)
|
|
||||||
{
|
|
||||||
int cnt;
|
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
if (inode->i_dquot[cnt] != NODQUOT) {
|
|
||||||
dqput(inode->i_dquot[cnt]);
|
|
||||||
inode->i_dquot[cnt] = NODQUOT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dquot_drop(struct inode *inode)
|
int dquot_drop(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
int cnt;
|
||||||
|
struct dquot *put[MAXQUOTAS];
|
||||||
|
|
||||||
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
dquot_drop_locked(inode);
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
|
put[cnt] = inode->i_dquot[cnt];
|
||||||
|
inode->i_dquot[cnt] = NODQUOT;
|
||||||
|
}
|
||||||
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
|
|
||||||
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
|
dqput(put[cnt]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1470,8 +1484,9 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
|||||||
qsize_t space;
|
qsize_t space;
|
||||||
struct dquot *transfer_from[MAXQUOTAS];
|
struct dquot *transfer_from[MAXQUOTAS];
|
||||||
struct dquot *transfer_to[MAXQUOTAS];
|
struct dquot *transfer_to[MAXQUOTAS];
|
||||||
int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
|
int cnt, ret = QUOTA_OK;
|
||||||
chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
|
int chuid = iattr->ia_valid & ATTR_UID && inode->i_uid != iattr->ia_uid,
|
||||||
|
chgid = iattr->ia_valid & ATTR_GID && inode->i_gid != iattr->ia_gid;
|
||||||
char warntype_to[MAXQUOTAS];
|
char warntype_to[MAXQUOTAS];
|
||||||
char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
|
char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
|
||||||
|
|
||||||
@ -1479,21 +1494,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
|||||||
* 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))
|
||||||
return QUOTA_OK;
|
return QUOTA_OK;
|
||||||
/* Clear the arrays */
|
/* Initialize the arrays */
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
|
transfer_from[cnt] = NODQUOT;
|
||||||
|
transfer_to[cnt] = NODQUOT;
|
||||||
warntype_to[cnt] = QUOTA_NL_NOWARN;
|
warntype_to[cnt] = QUOTA_NL_NOWARN;
|
||||||
}
|
|
||||||
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
/* Now recheck reliably when holding dqptr_sem */
|
|
||||||
if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
|
|
||||||
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
return QUOTA_OK;
|
|
||||||
}
|
|
||||||
/* First build the transfer_to list - here we can block on
|
|
||||||
* reading/instantiating of dquots. We know that the transaction for
|
|
||||||
* us was already started so we don't violate lock ranking here */
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
switch (cnt) {
|
switch (cnt) {
|
||||||
case USRQUOTA:
|
case USRQUOTA:
|
||||||
if (!chuid)
|
if (!chuid)
|
||||||
@ -1507,6 +1512,13 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
|
/* Now recheck reliably when holding dqptr_sem */
|
||||||
|
if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
|
||||||
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
|
goto put_all;
|
||||||
|
}
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
space = inode_get_bytes(inode);
|
space = inode_get_bytes(inode);
|
||||||
/* Build the transfer_from list and check the limits */
|
/* Build the transfer_from list and check the limits */
|
||||||
@ -1517,7 +1529,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
|||||||
if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) ==
|
if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) ==
|
||||||
NO_QUOTA || check_bdq(transfer_to[cnt], space, 0,
|
NO_QUOTA || check_bdq(transfer_to[cnt], space, 0,
|
||||||
warntype_to + cnt) == NO_QUOTA)
|
warntype_to + cnt) == NO_QUOTA)
|
||||||
goto warn_put_all;
|
goto over_quota;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1545,28 +1557,37 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
|||||||
|
|
||||||
inode->i_dquot[cnt] = transfer_to[cnt];
|
inode->i_dquot[cnt] = transfer_to[cnt];
|
||||||
}
|
}
|
||||||
ret = QUOTA_OK;
|
|
||||||
warn_put_all:
|
|
||||||
spin_unlock(&dq_data_lock);
|
spin_unlock(&dq_data_lock);
|
||||||
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
|
|
||||||
/* 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 (transfer_from[cnt])
|
if (transfer_from[cnt])
|
||||||
mark_dquot_dirty(transfer_from[cnt]);
|
mark_dquot_dirty(transfer_from[cnt]);
|
||||||
if (transfer_to[cnt])
|
if (transfer_to[cnt]) {
|
||||||
mark_dquot_dirty(transfer_to[cnt]);
|
mark_dquot_dirty(transfer_to[cnt]);
|
||||||
|
/* The reference we got is transferred to the inode */
|
||||||
|
transfer_to[cnt] = NODQUOT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
warn_put_all:
|
||||||
flush_warnings(transfer_to, warntype_to);
|
flush_warnings(transfer_to, warntype_to);
|
||||||
flush_warnings(transfer_from, warntype_from_inodes);
|
flush_warnings(transfer_from, warntype_from_inodes);
|
||||||
flush_warnings(transfer_from, warntype_from_space);
|
flush_warnings(transfer_from, warntype_from_space);
|
||||||
|
put_all:
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (ret == QUOTA_OK && transfer_from[cnt] != NODQUOT)
|
dqput(transfer_from[cnt]);
|
||||||
dqput(transfer_from[cnt]);
|
dqput(transfer_to[cnt]);
|
||||||
if (ret == NO_QUOTA && transfer_to[cnt] != NODQUOT)
|
|
||||||
dqput(transfer_to[cnt]);
|
|
||||||
}
|
}
|
||||||
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
return ret;
|
return ret;
|
||||||
|
over_quota:
|
||||||
|
spin_unlock(&dq_data_lock);
|
||||||
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
|
/* Clear dquot pointers we don't want to dqput() */
|
||||||
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
|
transfer_from[cnt] = NODQUOT;
|
||||||
|
ret = NO_QUOTA;
|
||||||
|
goto warn_put_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wrapper for transferring ownership of an inode */
|
/* Wrapper for transferring ownership of an inode */
|
||||||
@ -1651,19 +1672,24 @@ int vfs_quota_disable(struct super_block *sb, int type, unsigned int flags)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (flags & DQUOT_SUSPENDED) {
|
if (flags & DQUOT_SUSPENDED) {
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
dqopt->flags |=
|
dqopt->flags |=
|
||||||
dquot_state_flag(DQUOT_SUSPENDED, cnt);
|
dquot_state_flag(DQUOT_SUSPENDED, cnt);
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
} else {
|
} else {
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
dqopt->flags &= ~dquot_state_flag(flags, cnt);
|
dqopt->flags &= ~dquot_state_flag(flags, cnt);
|
||||||
/* Turning off suspended quotas? */
|
/* Turning off suspended quotas? */
|
||||||
if (!sb_has_quota_loaded(sb, cnt) &&
|
if (!sb_has_quota_loaded(sb, cnt) &&
|
||||||
sb_has_quota_suspended(sb, cnt)) {
|
sb_has_quota_suspended(sb, cnt)) {
|
||||||
dqopt->flags &= ~dquot_state_flag(
|
dqopt->flags &= ~dquot_state_flag(
|
||||||
DQUOT_SUSPENDED, cnt);
|
DQUOT_SUSPENDED, cnt);
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
iput(dqopt->files[cnt]);
|
iput(dqopt->files[cnt]);
|
||||||
dqopt->files[cnt] = NULL;
|
dqopt->files[cnt] = NULL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We still have to keep quota loaded? */
|
/* We still have to keep quota loaded? */
|
||||||
@ -1830,7 +1856,9 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
|
|||||||
}
|
}
|
||||||
mutex_unlock(&dqopt->dqio_mutex);
|
mutex_unlock(&dqopt->dqio_mutex);
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
dqopt->flags |= dquot_state_flag(flags, type);
|
dqopt->flags |= dquot_state_flag(flags, type);
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
|
|
||||||
add_dquot_ref(sb, type);
|
add_dquot_ref(sb, type);
|
||||||
mutex_unlock(&dqopt->dqonoff_mutex);
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
||||||
@ -1872,9 +1900,11 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
|
|||||||
}
|
}
|
||||||
inode = dqopt->files[type];
|
inode = dqopt->files[type];
|
||||||
dqopt->files[type] = NULL;
|
dqopt->files[type] = NULL;
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
|
flags = dqopt->flags & dquot_state_flag(DQUOT_USAGE_ENABLED |
|
||||||
DQUOT_LIMITS_ENABLED, type);
|
DQUOT_LIMITS_ENABLED, type);
|
||||||
dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
|
dqopt->flags &= ~dquot_state_flag(DQUOT_STATE_FLAGS, type);
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
mutex_unlock(&dqopt->dqonoff_mutex);
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
||||||
|
|
||||||
flags = dquot_generic_flag(flags, type);
|
flags = dquot_generic_flag(flags, type);
|
||||||
@ -1952,7 +1982,9 @@ int vfs_quota_enable(struct inode *inode, int type, int format_id,
|
|||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto out_lock;
|
goto out_lock;
|
||||||
}
|
}
|
||||||
|
spin_lock(&dq_state_lock);
|
||||||
sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
|
sb_dqopt(sb)->flags |= dquot_state_flag(flags, type);
|
||||||
|
spin_unlock(&dq_state_lock);
|
||||||
out_lock:
|
out_lock:
|
||||||
mutex_unlock(&dqopt->dqonoff_mutex);
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
@ -2039,14 +2071,12 @@ int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
|
|||||||
{
|
{
|
||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
|
|
||||||
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
dquot = dqget(sb, id, type);
|
||||||
if (!(dquot = dqget(sb, id, type))) {
|
if (dquot == NODQUOT)
|
||||||
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
}
|
|
||||||
do_get_dqblk(dquot, di);
|
do_get_dqblk(dquot, di);
|
||||||
dqput(dquot);
|
dqput(dquot);
|
||||||
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2130,7 +2160,6 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
|
|||||||
struct dquot *dquot;
|
struct dquot *dquot;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
||||||
dquot = dqget(sb, id, type);
|
dquot = dqget(sb, id, type);
|
||||||
if (!dquot) {
|
if (!dquot) {
|
||||||
rc = -ESRCH;
|
rc = -ESRCH;
|
||||||
@ -2139,7 +2168,6 @@ int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *d
|
|||||||
rc = do_set_dqblk(dquot, di);
|
rc = do_set_dqblk(dquot, di);
|
||||||
dqput(dquot);
|
dqput(dquot);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2370,11 +2398,9 @@ EXPORT_SYMBOL(dquot_release);
|
|||||||
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
|
||||||
EXPORT_SYMBOL(dquot_initialize);
|
EXPORT_SYMBOL(dquot_initialize);
|
||||||
EXPORT_SYMBOL(dquot_drop);
|
EXPORT_SYMBOL(dquot_drop);
|
||||||
EXPORT_SYMBOL(dquot_drop_locked);
|
|
||||||
EXPORT_SYMBOL(vfs_dq_drop);
|
EXPORT_SYMBOL(vfs_dq_drop);
|
||||||
EXPORT_SYMBOL(dqget);
|
EXPORT_SYMBOL(dqget);
|
||||||
EXPORT_SYMBOL(dqput);
|
EXPORT_SYMBOL(dqput);
|
||||||
EXPORT_SYMBOL(dquot_is_cached);
|
|
||||||
EXPORT_SYMBOL(dquot_alloc_space);
|
EXPORT_SYMBOL(dquot_alloc_space);
|
||||||
EXPORT_SYMBOL(dquot_alloc_inode);
|
EXPORT_SYMBOL(dquot_alloc_inode);
|
||||||
EXPORT_SYMBOL(dquot_free_space);
|
EXPORT_SYMBOL(dquot_free_space);
|
||||||
|
@ -810,171 +810,6 @@ out:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is difficult. We have to lock quota inode and start transaction
|
|
||||||
* in this function but we don't want to take the penalty of exlusive
|
|
||||||
* quota file lock when we are just going to use cached structures. So
|
|
||||||
* we just take read lock check whether we have dquot cached and if so,
|
|
||||||
* we don't have to take the write lock... */
|
|
||||||
static int ocfs2_dquot_initialize(struct inode *inode, int type)
|
|
||||||
{
|
|
||||||
handle_t *handle = NULL;
|
|
||||||
int status = 0;
|
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
struct ocfs2_mem_dqinfo *oinfo;
|
|
||||||
int exclusive = 0;
|
|
||||||
int cnt;
|
|
||||||
qid_t id;
|
|
||||||
|
|
||||||
mlog_entry_void();
|
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
if (type != -1 && cnt != type)
|
|
||||||
continue;
|
|
||||||
if (!sb_has_quota_active(sb, cnt))
|
|
||||||
continue;
|
|
||||||
oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
|
|
||||||
status = ocfs2_lock_global_qf(oinfo, 0);
|
|
||||||
if (status < 0)
|
|
||||||
goto out;
|
|
||||||
/* This is just a performance optimization not a reliable test.
|
|
||||||
* Since we hold an inode lock, noone can actually release
|
|
||||||
* the structure until we are finished with initialization. */
|
|
||||||
if (inode->i_dquot[cnt] != NODQUOT) {
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* When we have inode lock, we know that no dquot_release() can
|
|
||||||
* run and thus we can safely check whether we need to
|
|
||||||
* read+modify global file to get quota information or whether
|
|
||||||
* our node already has it. */
|
|
||||||
if (cnt == USRQUOTA)
|
|
||||||
id = inode->i_uid;
|
|
||||||
else if (cnt == GRPQUOTA)
|
|
||||||
id = inode->i_gid;
|
|
||||||
else
|
|
||||||
BUG();
|
|
||||||
/* Obtain exclusion from quota off... */
|
|
||||||
down_write(&sb_dqopt(sb)->dqptr_sem);
|
|
||||||
exclusive = !dquot_is_cached(sb, id, cnt);
|
|
||||||
up_write(&sb_dqopt(sb)->dqptr_sem);
|
|
||||||
if (exclusive) {
|
|
||||||
status = ocfs2_lock_global_qf(oinfo, 1);
|
|
||||||
if (status < 0) {
|
|
||||||
exclusive = 0;
|
|
||||||
mlog_errno(status);
|
|
||||||
goto out_ilock;
|
|
||||||
}
|
|
||||||
handle = ocfs2_start_trans(OCFS2_SB(sb),
|
|
||||||
ocfs2_calc_qinit_credits(sb, cnt));
|
|
||||||
if (IS_ERR(handle)) {
|
|
||||||
status = PTR_ERR(handle);
|
|
||||||
mlog_errno(status);
|
|
||||||
goto out_ilock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dquot_initialize(inode, cnt);
|
|
||||||
if (exclusive) {
|
|
||||||
ocfs2_commit_trans(OCFS2_SB(sb), handle);
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 1);
|
|
||||||
}
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 0);
|
|
||||||
}
|
|
||||||
mlog_exit(0);
|
|
||||||
return 0;
|
|
||||||
out_ilock:
|
|
||||||
if (exclusive)
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 1);
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 0);
|
|
||||||
out:
|
|
||||||
mlog_exit(status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ocfs2_dquot_drop_slow(struct inode *inode)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
int cnt;
|
|
||||||
int got_lock[MAXQUOTAS] = {0, 0};
|
|
||||||
handle_t *handle;
|
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
struct ocfs2_mem_dqinfo *oinfo;
|
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
if (!sb_has_quota_active(sb, cnt))
|
|
||||||
continue;
|
|
||||||
oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
|
|
||||||
status = ocfs2_lock_global_qf(oinfo, 1);
|
|
||||||
if (status < 0)
|
|
||||||
goto out;
|
|
||||||
got_lock[cnt] = 1;
|
|
||||||
}
|
|
||||||
handle = ocfs2_start_trans(OCFS2_SB(sb),
|
|
||||||
ocfs2_calc_qinit_credits(sb, USRQUOTA) +
|
|
||||||
ocfs2_calc_qinit_credits(sb, GRPQUOTA));
|
|
||||||
if (IS_ERR(handle)) {
|
|
||||||
status = PTR_ERR(handle);
|
|
||||||
mlog_errno(status);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
dquot_drop(inode);
|
|
||||||
ocfs2_commit_trans(OCFS2_SB(sb), handle);
|
|
||||||
out:
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
|
||||||
if (got_lock[cnt]) {
|
|
||||||
oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 1);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See the comment before ocfs2_dquot_initialize. */
|
|
||||||
static int ocfs2_dquot_drop(struct inode *inode)
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
struct super_block *sb = inode->i_sb;
|
|
||||||
struct ocfs2_mem_dqinfo *oinfo;
|
|
||||||
int exclusive = 0;
|
|
||||||
int cnt;
|
|
||||||
int got_lock[MAXQUOTAS] = {0, 0};
|
|
||||||
|
|
||||||
mlog_entry_void();
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
if (!sb_has_quota_active(sb, cnt))
|
|
||||||
continue;
|
|
||||||
oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
|
|
||||||
status = ocfs2_lock_global_qf(oinfo, 0);
|
|
||||||
if (status < 0)
|
|
||||||
goto out;
|
|
||||||
got_lock[cnt] = 1;
|
|
||||||
}
|
|
||||||
/* Lock against anyone releasing references so that when when we check
|
|
||||||
* we know we are not going to be last ones to release dquot */
|
|
||||||
down_write(&sb_dqopt(sb)->dqptr_sem);
|
|
||||||
/* Urgh, this is a terrible hack :( */
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
|
||||||
if (inode->i_dquot[cnt] != NODQUOT &&
|
|
||||||
atomic_read(&inode->i_dquot[cnt]->dq_count) > 1) {
|
|
||||||
exclusive = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exclusive)
|
|
||||||
dquot_drop_locked(inode);
|
|
||||||
up_write(&sb_dqopt(sb)->dqptr_sem);
|
|
||||||
out:
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
|
||||||
if (got_lock[cnt]) {
|
|
||||||
oinfo = sb_dqinfo(sb, cnt)->dqi_priv;
|
|
||||||
ocfs2_unlock_global_qf(oinfo, 0);
|
|
||||||
}
|
|
||||||
/* In case we bailed out because we had to do expensive locking
|
|
||||||
* do it now... */
|
|
||||||
if (exclusive)
|
|
||||||
status = ocfs2_dquot_drop_slow(inode);
|
|
||||||
mlog_exit(status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type)
|
static struct dquot *ocfs2_alloc_dquot(struct super_block *sb, int type)
|
||||||
{
|
{
|
||||||
struct ocfs2_dquot *dquot =
|
struct ocfs2_dquot *dquot =
|
||||||
@ -991,8 +826,8 @@ static void ocfs2_destroy_dquot(struct dquot *dquot)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct dquot_operations ocfs2_quota_operations = {
|
struct dquot_operations ocfs2_quota_operations = {
|
||||||
.initialize = ocfs2_dquot_initialize,
|
.initialize = dquot_initialize,
|
||||||
.drop = ocfs2_dquot_drop,
|
.drop = dquot_drop,
|
||||||
.alloc_space = dquot_alloc_space,
|
.alloc_space = dquot_alloc_space,
|
||||||
.alloc_inode = dquot_alloc_inode,
|
.alloc_inode = dquot_alloc_inode,
|
||||||
.free_space = dquot_free_space,
|
.free_space = dquot_free_space,
|
||||||
|
@ -24,10 +24,8 @@ void sync_dquots(struct super_block *sb, int type);
|
|||||||
|
|
||||||
int dquot_initialize(struct inode *inode, int type);
|
int dquot_initialize(struct inode *inode, int type);
|
||||||
int dquot_drop(struct inode *inode);
|
int dquot_drop(struct inode *inode);
|
||||||
int dquot_drop_locked(struct inode *inode);
|
|
||||||
struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
|
struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
|
||||||
void dqput(struct dquot *dquot);
|
void dqput(struct dquot *dquot);
|
||||||
int dquot_is_cached(struct super_block *sb, unsigned int id, int type);
|
|
||||||
int dquot_scan_active(struct super_block *sb,
|
int dquot_scan_active(struct super_block *sb,
|
||||||
int (*fn)(struct dquot *dquot, unsigned long priv),
|
int (*fn)(struct dquot *dquot, unsigned long priv),
|
||||||
unsigned long priv);
|
unsigned long priv);
|
||||||
|
Loading…
Reference in New Issue
Block a user