*
* Any operation working on dquots via inode pointers must hold dqptr_sem. If
* 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
- * (these locking rules also apply for S_NOQUOTA flag in the inode - note that
- * for altering the flag i_sem is also needed). If operation is holding
- * reference to dquot in other way (e.g. quotactl ops) it must be guarded by
- * dqonoff_sem.
+ * read lock is enough. If pointers are altered function must hold write lock.
+ * If operation is holding reference to dquot in other way (e.g. quotactl ops)
+ * it must be guarded by dqonoff_sem.
* This locking assures that:
* a) update/access to dquot pointers in inode is serialized
* b) everyone is guarded against invalidate_dquots()
* operations on dquots don't hold dq_lock as they copy data under dq_data_lock
* spinlock to internal buffers before writing.
*
- * Lock ordering (including related VFS locks) is following:
- * i_sem > dqonoff_sem > iprune_sem > journal_lock > dqptr_sem >
- * > dquot->dq_lock > dqio_sem
- * i_sem on quota files is special (it's below dqio_sem)
+ * Lock ordering (including journal_lock) is following:
+ * dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem
*/
spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED;
static void dqput(struct dquot *dquot);
-static inline unsigned int
-hashfn(const struct super_block *sb, unsigned int id, int type)
+static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
{
- unsigned long tmp;
-
- tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
+ unsigned long tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
}
int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
{
struct dquot *dquot = inode->i_dquot[type];
+ int cnt;
inode->i_dquot[type] = NODQUOT;
+ /* any other quota in use? */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] != NODQUOT)
+ goto put_it;
+ }
+ inode->i_flags &= ~S_QUOTA;
+put_it:
if (dquot != NODQUOT) {
if (dqput_blocks(dquot)) {
#ifdef __DQUOT_PARANOIA
}
}
+/* Function in inode.c - remove pointers to dquots in icache */
+extern void remove_dquot_ref(struct super_block *, int, struct list_head *);
+
/* Gather all references from inodes and drop them */
static void drop_dquot_ref(struct super_block *sb, int type)
{
LIST_HEAD(tofree_head);
- /* We need to be guarded against prune_icache to reach all the
- * inodes - otherwise some can be on the local list of prune_icache */
- down(&iprune_sem);
down_write(&sb_dqopt(sb)->dqptr_sem);
remove_dquot_ref(sb, type, &tofree_head);
up_write(&sb_dqopt(sb)->dqptr_sem);
- up(&iprune_sem);
put_dquot_list(&tofree_head);
}
break;
}
inode->i_dquot[cnt] = dqget(inode->i_sb, id, cnt);
+ if (inode->i_dquot[cnt])
+ inode->i_flags |= S_QUOTA;
}
}
out_err:
int cnt;
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
+ inode->i_flags &= ~S_QUOTA;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (inode->i_dquot[cnt] != NODQUOT) {
dqput(inode->i_dquot[cnt]);
struct quota_info *dqopt = sb_dqopt(sb);
struct dquot *to_drop[MAXQUOTAS];
int error, cnt;
- unsigned int oldflags = -1;
+ unsigned int oldflags;
if (!fmt)
return -ESRCH;
if (!S_ISREG(inode->i_mode))
goto out_fmt;
- down(&inode->i_sem);
down(&dqopt->dqonoff_sem);
if (sb_has_quota_enabled(sb, type)) {
- up(&inode->i_sem);
error = -EBUSY;
goto out_lock;
}
+ oldflags = inode->i_flags;
+ dqopt->files[type] = f;
+ error = -EINVAL;
+ if (!fmt->qf_ops->check_quota_file(sb, type))
+ goto out_file_init;
/* We don't want quota and atime on quota files (deadlocks possible)
* We also need to set GFP mask differently because we cannot recurse
* into filesystem when allocating page for quota inode */
down_write(&dqopt->dqptr_sem);
- oldflags = inode->i_flags & (S_NOATIME | S_NOQUOTA);
inode->i_flags |= S_NOQUOTA | S_NOATIME;
- up_write(&dqopt->dqptr_sem);
- up(&inode->i_sem);
- dqopt->files[type] = f;
- error = -EINVAL;
- if (!fmt->qf_ops->check_quota_file(sb, type))
- goto out_file_init;
/*
* We write to quota files deep within filesystem code. We don't want
* the VFS to reenter filesystem code when it tries to allocate a
mapping_set_gfp_mask(inode->i_mapping,
mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS);
- down_write(&dqopt->dqptr_sem);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
to_drop[cnt] = inode->i_dquot[cnt];
inode->i_dquot[cnt] = NODQUOT;
}
+ inode->i_flags &= ~S_QUOTA;
up_write(&dqopt->dqptr_sem);
/* We must put dquots outside of dqptr_sem because we may need to
* start transaction for dquot_release() */
return 0;
out_file_init:
+ inode->i_flags = oldflags;
dqopt->files[type] = NULL;
out_lock:
+ up_write(&dqopt->dqptr_sem);
up(&dqopt->dqonoff_sem);
- if (oldflags != -1) {
- down(&inode->i_sem);
- down_write(&dqopt->dqptr_sem);
- /* Reset the NOATIME flag back. I know it could change in the
- * mean time but playing with NOATIME flags on a quota file is
- * never a good idea */
- inode->i_flags &= ~(S_NOATIME | S_NOQUOTA);
- inode->i_flags |= oldflags;
- up_write(&dqopt->dqptr_sem);
- up(&inode->i_sem);
- }
out_fmt:
put_quota_format(fmt);