get rid of S_BIAS
[safe/jmp/linux-2.6] / fs / ocfs2 / quota_global.c
index 444aa5a..2bb35fe 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include <linux/spinlock.h>
 #include <linux/fs.h>
+#include <linux/slab.h>
 #include <linux/quota.h>
 #include <linux/quotaops.h>
 #include <linux/dqblk_qtree.h>
 #include "sysfile.h"
 #include "dlmglue.h"
 #include "uptodate.h"
+#include "super.h"
+#include "buffer_head_io.h"
 #include "quota.h"
 
+/*
+ * Locking of quotas with OCFS2 is rather complex. Here are rules that
+ * should be obeyed by all the functions:
+ * - any write of quota structure (either to local or global file) is protected
+ *   by dqio_mutex or dquot->dq_lock.
+ * - any modification of global quota file holds inode cluster lock, i_mutex,
+ *   and ip_alloc_sem of the global quota file (achieved by
+ *   ocfs2_lock_global_qf). It also has to hold qinfo_lock.
+ * - an allocation of new blocks for local quota file is protected by
+ *   its ip_alloc_sem
+ *
+ * A rough sketch of locking dependencies (lf = local file, gf = global file):
+ * Normal filesystem operation:
+ *   start_trans -> dqio_mutex -> write to lf
+ * Syncing of local and global file:
+ *   ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock ->
+ *     write to gf
+ *                                                    -> write to lf
+ * Acquire dquot for the first time:
+ *   dq_lock -> ocfs2_lock_global_qf -> qinfo_lock -> read from gf
+ *                                  -> alloc space for gf
+ *                                  -> start_trans -> qinfo_lock -> write to gf
+ *          -> ip_alloc_sem of lf -> alloc space for lf
+ *          -> write to lf
+ * Release last reference to dquot:
+ *   dq_lock -> ocfs2_lock_global_qf -> start_trans -> qinfo_lock -> write to gf
+ *          -> write to lf
+ * Note that all the above operations also hold the inode cluster lock of lf.
+ * Recovery:
+ *   inode cluster lock of recovered lf
+ *     -> read bitmaps -> ip_alloc_sem of lf
+ *     -> ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock ->
+ *        write to gf
+ */
+
 static struct workqueue_struct *ocfs2_quota_wq = NULL;
 
 static void qsync_work_fn(struct work_struct *work);
@@ -69,6 +107,7 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
        d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
        d->dqb_btime = cpu_to_le64(m->dqb_btime);
        d->dqb_itime = cpu_to_le64(m->dqb_itime);
+       d->dqb_pad1 = d->dqb_pad2 = 0;
 }
 
 static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
@@ -88,8 +127,7 @@ struct qtree_fmt_operations ocfs2_global_ops = {
        .is_id = ocfs2_global_is_id,
 };
 
-static int ocfs2_validate_quota_block(struct super_block *sb,
-                                     struct buffer_head *bh)
+int ocfs2_validate_quota_block(struct super_block *sb, struct buffer_head *bh)
 {
        struct ocfs2_disk_dqtrailer *dqt =
                ocfs2_block_dqtrailer(sb->s_blocksize, bh->b_data);
@@ -107,45 +145,19 @@ static int ocfs2_validate_quota_block(struct super_block *sb,
        return ocfs2_validate_meta_ecc(sb, bh->b_data, &dqt->dq_check);
 }
 
-int ocfs2_read_quota_block(struct inode *inode, u64 v_block,
-                          struct buffer_head **bh)
+int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block,
+                               struct buffer_head **bhp)
 {
-       int rc = 0;
-       struct buffer_head *tmp = *bh;
+       int rc;
 
-       rc = ocfs2_read_virt_blocks(inode, v_block, 1, &tmp, 0,
-                                   ocfs2_validate_quota_block);
+       *bhp = NULL;
+       rc = ocfs2_read_blocks(INODE_CACHE(inode), p_block, 1, bhp, 0,
+                              ocfs2_validate_quota_block);
        if (rc)
                mlog_errno(rc);
-
-       /* If ocfs2_read_virt_blocks() got us a new bh, pass it up. */
-       if (!rc && !*bh)
-               *bh = tmp;
-
        return rc;
 }
 
-static int ocfs2_get_quota_block(struct inode *inode, int block,
-                                struct buffer_head **bh)
-{
-       u64 pblock, pcount;
-       int err;
-
-       down_read(&OCFS2_I(inode)->ip_alloc_sem);
-       err = ocfs2_extent_map_get_blocks(inode, block, &pblock, &pcount, NULL);
-       up_read(&OCFS2_I(inode)->ip_alloc_sem);
-       if (err) {
-               mlog_errno(err);
-               return err;
-       }
-       *bh = sb_getblk(inode->i_sb, pblock);
-       if (!*bh) {
-               err = -EIO;
-               mlog_errno(err);
-       }
-       return err;;
-}
-
 /* Read data from global quotafile - avoid pagecache and such because we cannot
  * afford acquiring the locks... We use quota cluster lock to serialize
  * operations. Caller is responsible for acquiring it. */
@@ -160,6 +172,7 @@ ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
        int err = 0;
        struct buffer_head *bh;
        size_t toread, tocopy;
+       u64 pblock = 0, pcount = 0;
 
        if (off > i_size)
                return 0;
@@ -167,9 +180,20 @@ ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
                len = i_size - off;
        toread = len;
        while (toread > 0) {
-               tocopy = min((size_t)(sb->s_blocksize - offset), toread);
+               tocopy = min_t(size_t, (sb->s_blocksize - offset), toread);
+               if (!pcount) {
+                       err = ocfs2_extent_map_get_blocks(gqinode, blk, &pblock,
+                                                         &pcount, NULL);
+                       if (err) {
+                               mlog_errno(err);
+                               return err;
+                       }
+               } else {
+                       pcount--;
+                       pblock++;
+               }
                bh = NULL;
-               err = ocfs2_read_quota_block(gqinode, blk, &bh);
+               err = ocfs2_read_quota_phys_block(gqinode, pblock, &bh);
                if (err) {
                        mlog_errno(err);
                        return err;
@@ -197,6 +221,7 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
        int err = 0, new = 0, ja_type;
        struct buffer_head *bh = NULL;
        handle_t *handle = journal_current_handle();
+       u64 pblock, pcount;
 
        if (!handle) {
                mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled "
@@ -209,32 +234,37 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
                len = sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE - offset;
        }
 
-       mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA);
        if (gqinode->i_size < off + len) {
-               down_write(&OCFS2_I(gqinode)->ip_alloc_sem);
-               err = ocfs2_extend_no_holes(gqinode, off + len, off);
-               up_write(&OCFS2_I(gqinode)->ip_alloc_sem);
-               if (err < 0)
-                       goto out;
+               loff_t rounded_end =
+                               ocfs2_align_bytes_to_blocks(sb, off + len);
+
+               /* Space is already allocated in ocfs2_acquire_dquot() */
                err = ocfs2_simple_size_update(gqinode,
                                               oinfo->dqi_gqi_bh,
-                                              off + len);
+                                              rounded_end);
                if (err < 0)
                        goto out;
                new = 1;
        }
+       err = ocfs2_extent_map_get_blocks(gqinode, blk, &pblock, &pcount, NULL);
+       if (err) {
+               mlog_errno(err);
+               goto out;
+       }
        /* Not rewriting whole block? */
        if ((offset || len < sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) &&
            !new) {
-               err = ocfs2_read_quota_block(gqinode, blk, &bh);
+               err = ocfs2_read_quota_phys_block(gqinode, pblock, &bh);
                ja_type = OCFS2_JOURNAL_ACCESS_WRITE;
        } else {
-               err = ocfs2_get_quota_block(gqinode, blk, &bh);
+               bh = sb_getblk(sb, pblock);
+               if (!bh)
+                       err = -ENOMEM;
                ja_type = OCFS2_JOURNAL_ACCESS_CREATE;
        }
        if (err) {
                mlog_errno(err);
-               return err;
+               goto out;
        }
        lock_buffer(bh);
        if (new)
@@ -243,25 +273,22 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
        flush_dcache_page(bh->b_page);
        set_buffer_uptodate(bh);
        unlock_buffer(bh);
-       ocfs2_set_buffer_uptodate(gqinode, bh);
-       err = ocfs2_journal_access_dq(handle, gqinode, bh, ja_type);
+       ocfs2_set_buffer_uptodate(INODE_CACHE(gqinode), bh);
+       err = ocfs2_journal_access_dq(handle, INODE_CACHE(gqinode), bh,
+                                     ja_type);
        if (err < 0) {
                brelse(bh);
                goto out;
        }
-       err = ocfs2_journal_dirty(handle, bh);
+       ocfs2_journal_dirty(handle, bh);
        brelse(bh);
-       if (err < 0)
-               goto out;
 out:
        if (err) {
-               mutex_unlock(&gqinode->i_mutex);
                mlog_errno(err);
                return err;
        }
        gqinode->i_version++;
        ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh);
-       mutex_unlock(&gqinode->i_mutex);
        return len;
 }
 
@@ -279,11 +306,23 @@ int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
        else
                WARN_ON(bh != oinfo->dqi_gqi_bh);
        spin_unlock(&dq_data_lock);
+       if (ex) {
+               mutex_lock(&oinfo->dqi_gqinode->i_mutex);
+               down_write(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem);
+       } else {
+               down_read(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem);
+       }
        return 0;
 }
 
 void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex)
 {
+       if (ex) {
+               up_write(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem);
+               mutex_unlock(&oinfo->dqi_gqinode->i_mutex);
+       } else {
+               up_read(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem);
+       }
        ocfs2_inode_unlock(oinfo->dqi_gqinode, ex);
        brelse(oinfo->dqi_gqi_bh);
        spin_lock(&dq_data_lock);
@@ -301,6 +340,7 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
        struct ocfs2_global_disk_dqinfo dinfo;
        struct mem_dqinfo *info = sb_dqinfo(sb, type);
        struct ocfs2_mem_dqinfo *oinfo = info->dqi_priv;
+       u64 pcount;
        int status;
 
        mlog_entry_void();
@@ -327,9 +367,19 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
                mlog_errno(status);
                goto out_err;
        }
+
+       status = ocfs2_extent_map_get_blocks(gqinode, 0, &oinfo->dqi_giblk,
+                                            &pcount, NULL);
+       if (status < 0)
+               goto out_unlock;
+
+       status = ocfs2_qinfo_lock(oinfo, 0);
+       if (status < 0)
+               goto out_unlock;
        status = sb->s_op->quota_read(sb, type, (char *)&dinfo,
                                      sizeof(struct ocfs2_global_disk_dqinfo),
                                      OCFS2_GLOBAL_INFO_OFF);
+       ocfs2_qinfo_unlock(oinfo, 0);
        ocfs2_unlock_global_qf(oinfo, 0);
        if (status != sizeof(struct ocfs2_global_disk_dqinfo)) {
                mlog(ML_ERROR, "Cannot read global quota info (%d).\n",
@@ -342,7 +392,6 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
        info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
        info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
        oinfo->dqi_syncms = le32_to_cpu(dinfo.dqi_syncms);
-       oinfo->dqi_syncjiff = msecs_to_jiffies(oinfo->dqi_syncms);
        oinfo->dqi_gi.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
        oinfo->dqi_gi.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
        oinfo->dqi_gi.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
@@ -352,11 +401,15 @@ int ocfs2_global_read_info(struct super_block *sb, int type)
        oinfo->dqi_gi.dqi_qtree_depth = qtree_depth(&oinfo->dqi_gi);
        INIT_DELAYED_WORK(&oinfo->dqi_sync_work, qsync_work_fn);
        queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work,
-                          oinfo->dqi_syncjiff);
+                          msecs_to_jiffies(oinfo->dqi_syncms));
 
 out_err:
        mlog_exit(status);
        return status;
+out_unlock:
+       ocfs2_unlock_global_qf(oinfo, 0);
+       mlog_errno(status);
+       goto out_err;
 }
 
 /* Write information to global quota file. Expects exlusive lock on quota
@@ -402,44 +455,23 @@ int ocfs2_global_write_info(struct super_block *sb, int type)
        return err;
 }
 
-/* Read in information from global quota file and acquire a reference to it.
- * dquot_acquire() has already started the transaction and locked quota file */
-int ocfs2_global_read_dquot(struct dquot *dquot)
+static int ocfs2_global_qinit_alloc(struct super_block *sb, int type)
 {
-       int err, err2, ex = 0;
-       struct ocfs2_mem_dqinfo *info =
-                       sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+       struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
 
-       err = ocfs2_qinfo_lock(info, 0);
-       if (err < 0)
-               goto out;
-       err = qtree_read_dquot(&info->dqi_gi, dquot);
-       if (err < 0)
-               goto out_qlock;
-       OCFS2_DQUOT(dquot)->dq_use_count++;
-       OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
-       OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
-       if (!dquot->dq_off) {   /* No real quota entry? */
-               /* Upgrade to exclusive lock for allocation */
-               err = ocfs2_qinfo_lock(info, 1);
-               if (err < 0)
-                       goto out_qlock;
-               ex = 1;
-       }
-       err = qtree_write_dquot(&info->dqi_gi, dquot);
-       if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) {
-               err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type);
-               if (!err)
-                       err = err2;
-       }
-out_qlock:
-       if (ex)
-               ocfs2_qinfo_unlock(info, 1);
-       ocfs2_qinfo_unlock(info, 0);
-out:
-       if (err < 0)
-               mlog_errno(err);
-       return err;
+       /*
+        * We may need to allocate tree blocks and a leaf block but not the
+        * root block
+        */
+       return oinfo->dqi_gi.dqi_qtree_depth;
+}
+
+static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type)
+{
+       /* We modify all the allocated blocks, tree root, info block and
+        * the inode */
+       return (ocfs2_global_qinit_alloc(sb, type) + 2) *
+                       OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + 1;
 }
 
 /* Sync local information about quota modifications with global quota file.
@@ -580,14 +612,13 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
        }
        mutex_lock(&sb_dqopt(sb)->dqio_mutex);
        status = ocfs2_sync_dquot(dquot);
-       mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
        if (status < 0)
                mlog_errno(status);
        /* We have to write local structure as well... */
-       dquot_mark_dquot_dirty(dquot);
-       status = dquot_commit(dquot);
+       status = ocfs2_local_write_dquot(dquot);
        if (status < 0)
                mlog_errno(status);
+       mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
        ocfs2_commit_trans(osb, handle);
 out_ilock:
        ocfs2_unlock_global_qf(oinfo, 1);
@@ -605,7 +636,7 @@ static void qsync_work_fn(struct work_struct *work)
 
        dquot_scan_active(sb, ocfs2_sync_dquot_helper, oinfo->dqi_type);
        queue_delayed_work(ocfs2_quota_wq, &oinfo->dqi_sync_work,
-                          oinfo->dqi_syncjiff);
+                          msecs_to_jiffies(oinfo->dqi_syncms));
 }
 
 /*
@@ -626,27 +657,27 @@ static int ocfs2_write_dquot(struct dquot *dquot)
                mlog_errno(status);
                goto out;
        }
-       status = dquot_commit(dquot);
+       mutex_lock(&sb_dqopt(dquot->dq_sb)->dqio_mutex);
+       status = ocfs2_local_write_dquot(dquot);
+       mutex_unlock(&sb_dqopt(dquot->dq_sb)->dqio_mutex);
        ocfs2_commit_trans(osb, handle);
 out:
        mlog_exit(status);
        return status;
 }
 
-int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
+static int ocfs2_calc_qdel_credits(struct super_block *sb, int type)
 {
-       struct ocfs2_mem_dqinfo *oinfo;
-       int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                   OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
-
-       if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
-               return 0;
-
-       oinfo = sb_dqinfo(sb, type)->dqi_priv;
-       /* We modify tree, leaf block, global info, local chunk header,
-        * global and local inode */
-       return oinfo->dqi_gi.dqi_qtree_depth + 2 + 1 +
-              2 * OCFS2_INODE_UPDATE_CREDITS;
+       struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
+       /*
+        * We modify tree, leaf block, global info, local chunk header,
+        * global and local inode; OCFS2_QINFO_WRITE_CREDITS already
+        * accounts for inode update
+        */
+       return (oinfo->dqi_gi.dqi_qtree_depth + 2) *
+              OCFS2_QUOTA_BLOCK_UPDATE_CREDITS +
+              OCFS2_QINFO_WRITE_CREDITS +
+              OCFS2_INODE_UPDATE_CREDITS;
 }
 
 static int ocfs2_release_dquot(struct dquot *dquot)
@@ -659,6 +690,10 @@ static int ocfs2_release_dquot(struct dquot *dquot)
 
        mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
 
+       mutex_lock(&dquot->dq_lock);
+       /* Check whether we are not racing with some other dqget() */
+       if (atomic_read(&dquot->dq_count) > 1)
+               goto out;
        status = ocfs2_lock_global_qf(oinfo, 1);
        if (status < 0)
                goto out;
@@ -669,62 +704,113 @@ static int ocfs2_release_dquot(struct dquot *dquot)
                mlog_errno(status);
                goto out_ilock;
        }
-       status = dquot_release(dquot);
+
+       status = ocfs2_global_release_dquot(dquot);
+       if (status < 0) {
+               mlog_errno(status);
+               goto out_trans;
+       }
+       status = ocfs2_local_release_dquot(handle, dquot);
+       /*
+        * If we fail here, we cannot do much as global structure is
+        * already released. So just complain...
+        */
+       if (status < 0)
+               mlog_errno(status);
+       clear_bit(DQ_ACTIVE_B, &dquot->dq_flags);
+out_trans:
        ocfs2_commit_trans(osb, handle);
 out_ilock:
        ocfs2_unlock_global_qf(oinfo, 1);
 out:
+       mutex_unlock(&dquot->dq_lock);
        mlog_exit(status);
        return status;
 }
 
-int ocfs2_calc_qinit_credits(struct super_block *sb, int type)
-{
-       struct ocfs2_mem_dqinfo *oinfo;
-       int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-                                   OCFS2_FEATURE_RO_COMPAT_GRPQUOTA };
-       struct ocfs2_dinode *lfe, *gfe;
-
-       if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type]))
-               return 0;
-
-       oinfo = sb_dqinfo(sb, type)->dqi_priv;
-       gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data;
-       lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data;
-       /* We can extend local file + global file. In local file we
-        * can modify info, chunk header block and dquot block. In
-        * global file we can modify info, tree and leaf block */
-       return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) +
-              ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) +
-              3 + oinfo->dqi_gi.dqi_qtree_depth + 2;
-}
-
+/*
+ * Read global dquot structure from disk or create it if it does
+ * not exist. Also update use count of the global structure and
+ * create structure in node-local quota file.
+ */
 static int ocfs2_acquire_dquot(struct dquot *dquot)
 {
+       int status = 0, err;
+       int ex = 0;
+       struct super_block *sb = dquot->dq_sb;
+       struct ocfs2_super *osb = OCFS2_SB(sb);
+       int type = dquot->dq_type;
+       struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
+       struct inode *gqinode = info->dqi_gqinode;
+       int need_alloc = ocfs2_global_qinit_alloc(sb, type);
        handle_t *handle;
-       struct ocfs2_mem_dqinfo *oinfo =
-                       sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
-       struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
-       int status = 0;
 
-       mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type);
-       /* We need an exclusive lock, because we're going to update use count
-        * and instantiate possibly new dquot structure */
-       status = ocfs2_lock_global_qf(oinfo, 1);
+       mlog_entry("id=%u, type=%d", dquot->dq_id, type);
+       mutex_lock(&dquot->dq_lock);
+       /*
+        * We need an exclusive lock, because we're going to update use count
+        * and instantiate possibly new dquot structure
+        */
+       status = ocfs2_lock_global_qf(info, 1);
        if (status < 0)
                goto out;
+       if (!test_bit(DQ_READ_B, &dquot->dq_flags)) {
+               status = ocfs2_qinfo_lock(info, 0);
+               if (status < 0)
+                       goto out_dq;
+               status = qtree_read_dquot(&info->dqi_gi, dquot);
+               ocfs2_qinfo_unlock(info, 0);
+               if (status < 0)
+                       goto out_dq;
+       }
+       set_bit(DQ_READ_B, &dquot->dq_flags);
+
+       OCFS2_DQUOT(dquot)->dq_use_count++;
+       OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
+       OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
+       if (!dquot->dq_off) {   /* No real quota entry? */
+               ex = 1;
+               /*
+                * Add blocks to quota file before we start a transaction since
+                * locking allocators ranks above a transaction start
+                */
+               WARN_ON(journal_current_handle());
+               status = ocfs2_extend_no_holes(gqinode,
+                       gqinode->i_size + (need_alloc << sb->s_blocksize_bits),
+                       gqinode->i_size);
+               if (status < 0)
+                       goto out_dq;
+       }
+
        handle = ocfs2_start_trans(osb,
-               ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type));
+                                  ocfs2_calc_global_qinit_credits(sb, type));
        if (IS_ERR(handle)) {
                status = PTR_ERR(handle);
-               mlog_errno(status);
-               goto out_ilock;
+               goto out_dq;
+       }
+       status = ocfs2_qinfo_lock(info, ex);
+       if (status < 0)
+               goto out_trans;
+       status = qtree_write_dquot(&info->dqi_gi, dquot);
+       if (ex && info_dirty(sb_dqinfo(sb, type))) {
+               err = __ocfs2_global_write_info(sb, type);
+               if (!status)
+                       status = err;
        }
-       status = dquot_acquire(dquot);
+       ocfs2_qinfo_unlock(info, ex);
+out_trans:
        ocfs2_commit_trans(osb, handle);
-out_ilock:
-       ocfs2_unlock_global_qf(oinfo, 1);
+out_dq:
+       ocfs2_unlock_global_qf(info, 1);
+       if (status < 0)
+               goto out;
+
+       status = ocfs2_create_local_dquot(dquot);
+       if (status < 0)
+               goto out;
+       set_bit(DQ_ACTIVE_B, &dquot->dq_flags);
 out:
+       mutex_unlock(&dquot->dq_lock);
        mlog_exit(status);
        return status;
 }
@@ -746,7 +832,6 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
        struct ocfs2_super *osb = OCFS2_SB(sb);
 
        mlog_entry("id=%u, type=%d", dquot->dq_id, type);
-       dquot_mark_dquot_dirty(dquot);
 
        /* In case user set some limits, sync dquot immediately to global
         * quota file so that information propagates quicker */
@@ -754,7 +839,9 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
        if (dquot->dq_flags & mask)
                sync = 1;
        spin_unlock(&dq_data_lock);
-       if (!sync) {
+       /* This is a slight hack but we can't afford getting global quota
+        * lock if we already have a transaction started. */
+       if (!sync || journal_current_handle()) {
                status = ocfs2_write_dquot(dquot);
                goto out;
        }
@@ -767,14 +854,16 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
                mlog_errno(status);
                goto out_ilock;
        }
+       mutex_lock(&sb_dqopt(sb)->dqio_mutex);
        status = ocfs2_sync_dquot(dquot);
        if (status < 0) {
                mlog_errno(status);
-               goto out_trans;
+               goto out_dlock;
        }
        /* Now write updated local dquot structure */
-       status = dquot_commit(dquot);
-out_trans:
+       status = ocfs2_local_write_dquot(dquot);
+out_dlock:
+       mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
        ocfs2_commit_trans(osb, handle);
 out_ilock:
        ocfs2_unlock_global_qf(oinfo, 1);
@@ -810,171 +899,6 @@ out:
        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)
 {
        struct ocfs2_dquot *dquot =
@@ -990,15 +914,8 @@ static void ocfs2_destroy_dquot(struct dquot *dquot)
        kmem_cache_free(ocfs2_dquot_cachep, dquot);
 }
 
-struct dquot_operations ocfs2_quota_operations = {
-       .initialize     = ocfs2_dquot_initialize,
-       .drop           = ocfs2_dquot_drop,
-       .alloc_space    = dquot_alloc_space,
-       .alloc_inode    = dquot_alloc_inode,
-       .free_space     = dquot_free_space,
-       .free_inode     = dquot_free_inode,
-       .transfer       = dquot_transfer,
-       .write_dquot    = ocfs2_write_dquot,
+const struct dquot_operations ocfs2_quota_operations = {
+       /* We never make dquot dirty so .write_dquot is never called */
        .acquire_dquot  = ocfs2_acquire_dquot,
        .release_dquot  = ocfs2_release_dquot,
        .mark_dirty     = ocfs2_mark_dquot_dirty,