quota: Push dqio_sem down to ->write_dqblk()
[sfrench/cifs-2.6.git] / fs / quota / dquot.c
index 48813aeaab8067e921d67b110e28e2a8dd94a3c0..562f5978488ff1d944affa6d381799eddf5ba578 100644 (file)
  * sure they cannot race with quotaon which first sets S_NOQUOTA flag and
  * then drops all pointers to dquots from an inode.
  *
- * 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).
- * Currently dquot is locked only when it is being read to memory (or space for
- * it is being allocated) on the first dqget() and when it is being released on
- * the last dqput(). The allocation and release oparations are serialized by
- * the dq_lock and by checking the use count in dquot_release().  Write
- * operations on dquots don't hold dq_lock as they copy data under dq_data_lock
- * spinlock to internal buffers before writing.
+ * Each dquot has its dq_lock mutex.  Dquot is locked when it is being read to
+ * memory (or space for it is being allocated) on the first dqget(), when it is
+ * being written out, and when it is being released on the last dqput(). The
+ * allocation and release operations are serialized by the dq_lock and by
+ * checking the use count in dquot_release().
  *
  * Lock ordering (including related VFS locks) is the following:
- *   s_umount > i_mutex > journal_lock > dquot->dq_lock > dqio_mutex
+ *   s_umount > i_mutex > journal_lock > dquot->dq_lock > dqio_sem
  */
 
 static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_list_lock);
@@ -406,7 +403,6 @@ int dquot_acquire(struct dquot *dquot)
        struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 
        mutex_lock(&dquot->dq_lock);
-       mutex_lock(&dqopt->dqio_mutex);
        if (!test_bit(DQ_READ_B, &dquot->dq_flags))
                ret = dqopt->ops[dquot->dq_id.type]->read_dqblk(dquot);
        if (ret < 0)
@@ -419,8 +415,10 @@ int dquot_acquire(struct dquot *dquot)
                ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
                /* Write the info if needed */
                if (info_dirty(&dqopt->info[dquot->dq_id.type])) {
+                       down_write(&dqopt->dqio_sem);
                        ret2 = dqopt->ops[dquot->dq_id.type]->write_file_info(
                                        dquot->dq_sb, dquot->dq_id.type);
+                       up_write(&dqopt->dqio_sem);
                }
                if (ret < 0)
                        goto out_iolock;
@@ -436,7 +434,6 @@ int dquot_acquire(struct dquot *dquot)
        smp_mb__before_atomic();
        set_bit(DQ_ACTIVE_B, &dquot->dq_flags);
 out_iolock:
-       mutex_unlock(&dqopt->dqio_mutex);
        mutex_unlock(&dquot->dq_lock);
        return ret;
 }
@@ -450,11 +447,11 @@ int dquot_commit(struct dquot *dquot)
        int ret = 0;
        struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 
-       mutex_lock(&dqopt->dqio_mutex);
+       mutex_lock(&dquot->dq_lock);
        spin_lock(&dq_list_lock);
        if (!clear_dquot_dirty(dquot)) {
                spin_unlock(&dq_list_lock);
-               goto out_sem;
+               goto out_lock;
        }
        spin_unlock(&dq_list_lock);
        /* Inactive dquot can be only if there was error during read/init
@@ -463,8 +460,8 @@ int dquot_commit(struct dquot *dquot)
                ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
        else
                ret = -EIO;
-out_sem:
-       mutex_unlock(&dqopt->dqio_mutex);
+out_lock:
+       mutex_unlock(&dquot->dq_lock);
        return ret;
 }
 EXPORT_SYMBOL(dquot_commit);
@@ -481,7 +478,7 @@ int dquot_release(struct dquot *dquot)
        /* Check whether we are not racing with some other dqget() */
        if (atomic_read(&dquot->dq_count) > 1)
                goto out_dqlock;
-       mutex_lock(&dqopt->dqio_mutex);
+       down_write(&dqopt->dqio_sem);
        if (dqopt->ops[dquot->dq_id.type]->release_dqblk) {
                ret = dqopt->ops[dquot->dq_id.type]->release_dqblk(dquot);
                /* Write the info */
@@ -493,7 +490,7 @@ int dquot_release(struct dquot *dquot)
                        ret = ret2;
        }
        clear_bit(DQ_ACTIVE_B, &dquot->dq_flags);
-       mutex_unlock(&dqopt->dqio_mutex);
+       up_write(&dqopt->dqio_sem);
 out_dqlock:
        mutex_unlock(&dquot->dq_lock);
        return ret;
@@ -1910,6 +1907,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
 {
        qsize_t space, cur_space;
        qsize_t rsv_space = 0;
+       qsize_t inode_usage = 1;
        struct dquot *transfer_from[MAXQUOTAS] = {};
        int cnt, ret = 0;
        char is_valid[MAXQUOTAS] = {};
@@ -1919,6 +1917,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
 
        if (IS_NOQUOTA(inode))
                return 0;
+
+       if (inode->i_sb->dq_op->get_inode_usage) {
+               ret = inode->i_sb->dq_op->get_inode_usage(inode, &inode_usage);
+               if (ret)
+                       return ret;
+       }
+
        /* Initialize the arrays */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
                warn_to[cnt].w_type = QUOTA_NL_NOWARN;
@@ -1946,7 +1951,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
                        continue;
                is_valid[cnt] = 1;
                transfer_from[cnt] = i_dquot(inode)[cnt];
-               ret = check_idq(transfer_to[cnt], 1, &warn_to[cnt]);
+               ret = check_idq(transfer_to[cnt], inode_usage, &warn_to[cnt]);
                if (ret)
                        goto over_quota;
                ret = check_bdq(transfer_to[cnt], space, 0, &warn_to[cnt]);
@@ -1963,7 +1968,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
                /* Due to IO error we might not have transfer_from[] structure */
                if (transfer_from[cnt]) {
                        int wtype;
-                       wtype = info_idq_free(transfer_from[cnt], 1);
+                       wtype = info_idq_free(transfer_from[cnt], inode_usage);
                        if (wtype != QUOTA_NL_NOWARN)
                                prepare_warning(&warn_from_inodes[cnt],
                                                transfer_from[cnt], wtype);
@@ -1971,13 +1976,13 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
                        if (wtype != QUOTA_NL_NOWARN)
                                prepare_warning(&warn_from_space[cnt],
                                                transfer_from[cnt], wtype);
-                       dquot_decr_inodes(transfer_from[cnt], 1);
+                       dquot_decr_inodes(transfer_from[cnt], inode_usage);
                        dquot_decr_space(transfer_from[cnt], cur_space);
                        dquot_free_reserved_space(transfer_from[cnt],
                                                  rsv_space);
                }
 
-               dquot_incr_inodes(transfer_to[cnt], 1);
+               dquot_incr_inodes(transfer_to[cnt], inode_usage);
                dquot_incr_space(transfer_to[cnt], cur_space);
                dquot_resv_space(transfer_to[cnt], rsv_space);
 
@@ -2052,9 +2057,9 @@ int dquot_commit_info(struct super_block *sb, int type)
        int ret;
        struct quota_info *dqopt = sb_dqopt(sb);
 
-       mutex_lock(&dqopt->dqio_mutex);
+       down_write(&dqopt->dqio_sem);
        ret = dqopt->ops[type]->write_file_info(sb, type);
-       mutex_unlock(&dqopt->dqio_mutex);
+       up_write(&dqopt->dqio_sem);
        return ret;
 }
 EXPORT_SYMBOL(dquot_commit_info);
@@ -2068,9 +2073,9 @@ int dquot_get_next_id(struct super_block *sb, struct kqid *qid)
                return -ESRCH;
        if (!dqopt->ops[qid->type]->get_next_id)
                return -ENOSYS;
-       mutex_lock(&dqopt->dqio_mutex);
+       down_read(&dqopt->dqio_sem);
        err = dqopt->ops[qid->type]->get_next_id(sb, qid);
-       mutex_unlock(&dqopt->dqio_mutex);
+       up_read(&dqopt->dqio_sem);
        return err;
 }
 EXPORT_SYMBOL(dquot_get_next_id);
@@ -2320,15 +2325,15 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
        dqopt->info[type].dqi_format = fmt;
        dqopt->info[type].dqi_fmt_id = format_id;
        INIT_LIST_HEAD(&dqopt->info[type].dqi_dirty_list);
-       mutex_lock(&dqopt->dqio_mutex);
+       down_read(&dqopt->dqio_sem);
        error = dqopt->ops[type]->read_file_info(sb, type);
        if (error < 0) {
-               mutex_unlock(&dqopt->dqio_mutex);
+               up_read(&dqopt->dqio_sem);
                goto out_file_init;
        }
        if (dqopt->flags & DQUOT_QUOTA_SYS_FILE)
                dqopt->info[type].dqi_flags |= DQF_SYS_FILE;
-       mutex_unlock(&dqopt->dqio_mutex);
+       up_read(&dqopt->dqio_sem);
        spin_lock(&dq_state_lock);
        dqopt->flags |= dquot_state_flag(flags, type);
        spin_unlock(&dq_state_lock);