]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.31/patches.suse/ocfs2-Implement-quota-recovery.patch
Imported linux-2.6.27.39 suse/xen patches.
[people/teissler/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.suse / ocfs2-Implement-quota-recovery.patch
diff --git a/src/patches/suse-2.6.27.31/patches.suse/ocfs2-Implement-quota-recovery.patch b/src/patches/suse-2.6.27.31/patches.suse/ocfs2-Implement-quota-recovery.patch
deleted file mode 100644 (file)
index 8370cb2..0000000
+++ /dev/null
@@ -1,819 +0,0 @@
-From: Jan Kara <jack@suse.cz>
-References: fate#302681
-Subject: [PATCH 28/28] ocfs2: Implement quota recovery
-Patch-mainline: 2.6.29?
-
-Implement functions for recovery after a crash. Functions just
-read local quota file and sync info to global quota file.
-
-Signed-off-by: Jan Kara <jack@suse.cz>
----
- fs/ocfs2/journal.c     |  103 +++++++++--
- fs/ocfs2/journal.h     |    1 
- fs/ocfs2/ocfs2.h       |    4 
- fs/ocfs2/quota.h       |   21 ++
- fs/ocfs2/quota_local.c |  428 ++++++++++++++++++++++++++++++++++++++++++++++++-
- fs/ocfs2/super.c       |    3 
- 6 files changed, 530 insertions(+), 30 deletions(-)
-
---- a/fs/ocfs2/journal.c
-+++ b/fs/ocfs2/journal.c
-@@ -45,6 +45,7 @@
- #include "slot_map.h"
- #include "super.h"
- #include "sysfile.h"
-+#include "quota.h"
- #include "buffer_head_io.h"
-@@ -52,7 +53,7 @@ DEFINE_SPINLOCK(trans_inc_lock);
- static int ocfs2_force_read_journal(struct inode *inode);
- static int ocfs2_recover_node(struct ocfs2_super *osb,
--                            int node_num);
-+                            int node_num, int slot_num);
- static int __ocfs2_recovery_thread(void *arg);
- static int ocfs2_commit_cache(struct ocfs2_super *osb);
- static int __ocfs2_wait_on_mount(struct ocfs2_super *osb, int quota);
-@@ -889,6 +890,7 @@ struct ocfs2_la_recovery_item {
-       int                     lri_slot;
-       struct ocfs2_dinode     *lri_la_dinode;
-       struct ocfs2_dinode     *lri_tl_dinode;
-+      struct ocfs2_quota_recovery *lri_qrec;
- };
- /* Does the second half of the recovery process. By this point, the
-@@ -909,6 +911,7 @@ void ocfs2_complete_recovery(struct work
-       struct ocfs2_super *osb = journal->j_osb;
-       struct ocfs2_dinode *la_dinode, *tl_dinode;
-       struct ocfs2_la_recovery_item *item, *n;
-+      struct ocfs2_quota_recovery *qrec;
-       LIST_HEAD(tmp_la_list);
-       mlog_entry_void();
-@@ -956,6 +959,16 @@ void ocfs2_complete_recovery(struct work
-               if (ret < 0)
-                       mlog_errno(ret);
-+              qrec = item->lri_qrec;
-+              if (qrec) {
-+                      mlog(0, "Recovering quota files");
-+                      ret = ocfs2_finish_quota_recovery(osb, qrec,
-+                                                        item->lri_slot);
-+                      if (ret < 0)
-+                              mlog_errno(ret);
-+                      /* Recovery info is already freed now */
-+              }
-+
-               kfree(item);
-       }
-@@ -969,7 +982,8 @@ void ocfs2_complete_recovery(struct work
- static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
-                                           int slot_num,
-                                           struct ocfs2_dinode *la_dinode,
--                                          struct ocfs2_dinode *tl_dinode)
-+                                          struct ocfs2_dinode *tl_dinode,
-+                                          struct ocfs2_quota_recovery *qrec)
- {
-       struct ocfs2_la_recovery_item *item;
-@@ -984,6 +998,9 @@ static void ocfs2_queue_recovery_complet
-               if (tl_dinode)
-                       kfree(tl_dinode);
-+              if (qrec)
-+                      ocfs2_free_quota_recovery(qrec);
-+
-               mlog_errno(-ENOMEM);
-               return;
-       }
-@@ -992,6 +1009,7 @@ static void ocfs2_queue_recovery_complet
-       item->lri_la_dinode = la_dinode;
-       item->lri_slot = slot_num;
-       item->lri_tl_dinode = tl_dinode;
-+      item->lri_qrec = qrec;
-       spin_lock(&journal->j_lock);
-       list_add_tail(&item->lri_list, &journal->j_la_cleanups);
-@@ -1011,6 +1029,7 @@ void ocfs2_complete_mount_recovery(struc
-               ocfs2_queue_recovery_completion(journal,
-                                               osb->slot_num,
-                                               osb->local_alloc_copy,
-+                                              NULL,
-                                               NULL);
-               ocfs2_schedule_truncate_log_flush(osb, 0);
-@@ -1019,11 +1038,26 @@ void ocfs2_complete_mount_recovery(struc
-       }
- }
-+void ocfs2_complete_quota_recovery(struct ocfs2_super *osb)
-+{
-+      if (osb->quota_rec) {
-+              ocfs2_queue_recovery_completion(osb->journal,
-+                                              osb->slot_num,
-+                                              NULL,
-+                                              NULL,
-+                                              osb->quota_rec);
-+              osb->quota_rec = NULL;
-+      }
-+}
-+
- static int __ocfs2_recovery_thread(void *arg)
- {
--      int status, node_num;
-+      int status, node_num, slot_num;
-       struct ocfs2_super *osb = arg;
-       struct ocfs2_recovery_map *rm = osb->recovery_map;
-+      int *rm_quota = NULL;
-+      int rm_quota_used = 0, i;
-+      struct ocfs2_quota_recovery *qrec;
-       mlog_entry_void();
-@@ -1032,6 +1066,11 @@ static int __ocfs2_recovery_thread(void 
-               goto bail;
-       }
-+      rm_quota = kzalloc(osb->max_slots * sizeof(int), GFP_NOFS);
-+      if (!rm_quota) {
-+              status = -ENOMEM;
-+              goto bail;
-+      }
- restart:
-       status = ocfs2_super_lock(osb, 1);
-       if (status < 0) {
-@@ -1045,8 +1084,28 @@ restart:
-                * clear it until ocfs2_recover_node() has succeeded. */
-               node_num = rm->rm_entries[0];
-               spin_unlock(&osb->osb_lock);
-+              mlog(0, "checking node %d\n", node_num);
-+              slot_num = ocfs2_node_num_to_slot(osb, node_num);
-+              if (slot_num == -ENOENT) {
-+                      status = 0;
-+                      mlog(0, "no slot for this node, so no recovery"
-+                           "required.\n");
-+                      goto skip_recovery;
-+              }
-+              mlog(0, "node %d was using slot %d\n", node_num, slot_num);
--              status = ocfs2_recover_node(osb, node_num);
-+              /* It is a bit subtle with quota recovery. We cannot do it
-+               * immediately because we have to obtain cluster locks from
-+               * quota files and we also don't want to just skip it because
-+               * then quota usage would be out of sync until some node takes
-+               * the slot. So we remember which nodes need quota recovery
-+               * and when everything else is done, we recover quotas. */
-+              for (i = 0; i < rm_quota_used && rm_quota[i] != slot_num; i++);
-+              if (i == rm_quota_used)
-+                      rm_quota[rm_quota_used++] = slot_num;
-+
-+              status = ocfs2_recover_node(osb, node_num, slot_num);
-+skip_recovery:
-               if (!status) {
-                       ocfs2_recovery_map_clear(osb, node_num);
-               } else {
-@@ -1070,11 +1129,22 @@ restart:
-       ocfs2_super_unlock(osb, 1);
-+      /* Now it is right time to recover quotas... */
-+      for (i = 0; i < rm_quota_used; i++) {
-+              qrec = ocfs2_begin_quota_recovery(osb, rm_quota[i]);
-+              if (IS_ERR(qrec)) {
-+                      status = PTR_ERR(qrec);
-+                      mlog_errno(status);
-+              }
-+              ocfs2_queue_recovery_completion(osb->journal, rm_quota[i],
-+                                              NULL, NULL, qrec);
-+      }
-+
-       /* We always run recovery on our own orphan dir - the dead
-        * node(s) may have disallowd a previos inode delete. Re-processing
-        * is therefore required. */
-       ocfs2_queue_recovery_completion(osb->journal, osb->slot_num, NULL,
--                                      NULL);
-+                                      NULL, NULL);
- bail:
-       mutex_lock(&osb->recovery_lock);
-@@ -1089,6 +1159,9 @@ bail:
-       mutex_unlock(&osb->recovery_lock);
-+      if (rm_quota)
-+              kfree(rm_quota);
-+
-       mlog_exit(status);
-       /* no one is callint kthread_stop() for us so the kthread() api
-        * requires that we call do_exit().  And it isn't exported, but
-@@ -1317,31 +1390,19 @@ done:
-  * far less concerning.
-  */
- static int ocfs2_recover_node(struct ocfs2_super *osb,
--                            int node_num)
-+                            int node_num, int slot_num)
- {
-       int status = 0;
--      int slot_num;
-       struct ocfs2_dinode *la_copy = NULL;
-       struct ocfs2_dinode *tl_copy = NULL;
--      mlog_entry("(node_num=%d, osb->node_num = %d)\n",
--                 node_num, osb->node_num);
--
--      mlog(0, "checking node %d\n", node_num);
-+      mlog_entry("(node_num=%d, slot_num=%d, osb->node_num = %d)\n",
-+                 node_num, slot_num, osb->node_num);
-       /* Should not ever be called to recover ourselves -- in that
-        * case we should've called ocfs2_journal_load instead. */
-       BUG_ON(osb->node_num == node_num);
--      slot_num = ocfs2_node_num_to_slot(osb, node_num);
--      if (slot_num == -ENOENT) {
--              status = 0;
--              mlog(0, "no slot for this node, so no recovery required.\n");
--              goto done;
--      }
--
--      mlog(0, "node %d was using slot %d\n", node_num, slot_num);
--
-       status = ocfs2_replay_journal(osb, node_num, slot_num);
-       if (status < 0) {
-               if (status == -EBUSY) {
-@@ -1377,7 +1438,7 @@ static int ocfs2_recover_node(struct ocf
-       /* This will kfree the memory pointed to by la_copy and tl_copy */
-       ocfs2_queue_recovery_completion(osb->journal, slot_num, la_copy,
--                                      tl_copy);
-+                                      tl_copy, NULL);
-       status = 0;
- done:
---- a/fs/ocfs2/journal.h
-+++ b/fs/ocfs2/journal.h
-@@ -173,6 +173,7 @@ void   ocfs2_recovery_thread(struct ocfs
-                            int node_num);
- int    ocfs2_mark_dead_nodes(struct ocfs2_super *osb);
- void   ocfs2_complete_mount_recovery(struct ocfs2_super *osb);
-+void ocfs2_complete_quota_recovery(struct ocfs2_super *osb);
- static inline void ocfs2_start_checkpoint(struct ocfs2_super *osb)
- {
---- a/fs/ocfs2/ocfs2.h
-+++ b/fs/ocfs2/ocfs2.h
-@@ -209,6 +209,7 @@ enum ocfs2_mount_options
- struct ocfs2_journal;
- struct ocfs2_slot_info;
- struct ocfs2_recovery_map;
-+struct ocfs2_quota_recovery;
- struct ocfs2_super
- {
-       struct task_struct *commit_task;
-@@ -290,10 +291,11 @@ struct ocfs2_super
-       char *local_alloc_debug_buf;
- #endif
--      /* Next two fields are for local node slot recovery during
-+      /* Next three fields are for local node slot recovery during
-        * mount. */
-       int dirty;
-       struct ocfs2_dinode *local_alloc_copy;
-+      struct ocfs2_quota_recovery *quota_rec;
-       struct ocfs2_alloc_stats alloc_stats;
-       char dev_str[20];               /* "major,minor" of the device */
---- a/fs/ocfs2/quota.h
-+++ b/fs/ocfs2/quota.h
-@@ -38,6 +38,17 @@ struct ocfs2_dquot {
-       s64 dq_originodes;      /* Last globally synced inode usage */
- };
-+/* Description of one chunk to recover in memory */
-+struct ocfs2_recovery_chunk {
-+      struct list_head rc_list;       /* List of chunks */
-+      int rc_chunk;                   /* Chunk number */
-+      unsigned long *rc_bitmap;       /* Bitmap of entries to recover */
-+};
-+
-+struct ocfs2_quota_recovery {
-+      struct list_head r_list[MAXQUOTAS];     /* List of chunks to recover */
-+};
-+
- /* In-memory structure with quota header information */
- struct ocfs2_mem_dqinfo {
-       unsigned int dqi_type;          /* Quota type this structure describes */
-@@ -54,6 +65,10 @@ struct ocfs2_mem_dqinfo {
-       struct buffer_head *dqi_ibh;    /* Buffer with information header */
-       struct qtree_mem_dqinfo dqi_gi; /* Info about global file */
-       struct timer_list dqi_sync_timer;       /* Timer for syncing dquots */
-+      struct ocfs2_quota_recovery *dqi_rec;   /* Pointer to recovery
-+                                               * information, in case we
-+                                               * enable quotas on file
-+                                               * needing it */
- };
- static inline struct ocfs2_dquot *OCFS2_DQUOT(struct dquot *dquot)
-@@ -72,6 +87,12 @@ extern struct kmem_cache *ocfs2_qf_chunk
- extern struct qtree_fmt_operations ocfs2_global_ops;
-+struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
-+                              struct ocfs2_super *osb, int slot_num);
-+int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
-+                              struct ocfs2_quota_recovery *rec,
-+                              int slot_num);
-+void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec);
- ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data,
-                        size_t len, loff_t off);
- ssize_t ocfs2_quota_write(struct super_block *sb, int type,
---- a/fs/ocfs2/quota_local.c
-+++ b/fs/ocfs2/quota_local.c
-@@ -49,14 +49,25 @@ static unsigned int ol_quota_chunk_block
-       return 1 + (ol_chunk_blocks(sb) + 1) * c;
- }
--/* Offset of the dquot structure in the quota file */
--static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
-+static unsigned int ol_dqblk_block(struct super_block *sb, int c, int off)
-+{
-+      int epb = ol_quota_entries_per_block(sb);
-+
-+      return ol_quota_chunk_block(sb, c) + 1 + off / epb;
-+}
-+
-+static unsigned int ol_dqblk_block_off(struct super_block *sb, int c, int off)
- {
-       int epb = ol_quota_entries_per_block(sb);
--      return ((ol_quota_chunk_block(sb, c) + 1 + off / epb)
--              << sb->s_blocksize_bits) +
--              (off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
-+      return (off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
-+}
-+
-+/* Offset of the dquot structure in the quota file */
-+static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
-+{
-+      return (ol_dqblk_block(sb, c, off) << sb->s_blocksize_bits) +
-+             ol_dqblk_block_off(sb, c, off);
- }
- /* Compute block number from given offset */
-@@ -253,6 +264,382 @@ static void olq_update_info(struct buffe
-       spin_unlock(&dq_data_lock);
- }
-+static int ocfs2_add_recovery_chunk(struct super_block *sb,
-+                                  struct ocfs2_local_disk_chunk *dchunk,
-+                                  int chunk,
-+                                  struct list_head *head)
-+{
-+      struct ocfs2_recovery_chunk *rc;
-+
-+      rc = kmalloc(sizeof(struct ocfs2_recovery_chunk), GFP_NOFS);
-+      if (!rc)
-+              return -ENOMEM;
-+      rc->rc_chunk = chunk;
-+      rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
-+      if (!rc->rc_bitmap) {
-+              kfree(rc);
-+              return -ENOMEM;
-+      }
-+      memcpy(rc->rc_bitmap, dchunk->dqc_bitmap,
-+             (ol_chunk_entries(sb) + 7) >> 3);
-+      list_add_tail(&rc->rc_list, head);
-+      return 0;
-+}
-+
-+static void free_recovery_list(struct list_head *head)
-+{
-+      struct ocfs2_recovery_chunk *next;
-+      struct ocfs2_recovery_chunk *rchunk;
-+
-+      list_for_each_entry_safe(rchunk, next, head, rc_list) {
-+              list_del(&rchunk->rc_list);
-+              kfree(rchunk->rc_bitmap);
-+              kfree(rchunk);
-+      }
-+}
-+
-+void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec)
-+{
-+      int type;
-+
-+      for (type = 0; type < MAXQUOTAS; type++)
-+              free_recovery_list(&(rec->r_list[type]));
-+      kfree(rec);
-+}
-+
-+/* Load entries in our quota file we have to recover*/
-+static int ocfs2_recovery_load_quota(struct inode *lqinode,
-+                                   struct ocfs2_local_disk_dqinfo *ldinfo,
-+                                   int type,
-+                                   struct list_head *head)
-+{
-+      struct super_block *sb = lqinode->i_sb;
-+      struct buffer_head *hbh;
-+      struct ocfs2_local_disk_chunk *dchunk;
-+      int i, chunks = le32_to_cpu(ldinfo->dqi_chunks);
-+      int status = 0;
-+
-+      for (i = 0; i < chunks; i++) {
-+              hbh = ocfs2_bread(lqinode, ol_quota_chunk_block(sb, i),
-+                                &status, 0);
-+              if (!hbh) {
-+                      mlog_errno(status);
-+                      break;
-+              }
-+              dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
-+              if (le32_to_cpu(dchunk->dqc_free) < ol_chunk_entries(sb))
-+                      status = ocfs2_add_recovery_chunk(sb, dchunk, i, head);
-+              brelse(hbh);
-+              if (status < 0)
-+                      break;
-+      }
-+      if (status < 0)
-+              free_recovery_list(head);
-+      return status;
-+}
-+
-+static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void)
-+{
-+      int type;
-+      struct ocfs2_quota_recovery *rec;
-+
-+      rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS);
-+      if (!rec)
-+              return NULL;
-+      for (type = 0; type < MAXQUOTAS; type++)
-+              INIT_LIST_HEAD(&(rec->r_list[type]));
-+      return rec;
-+}
-+
-+/* Load information we need for quota recovery into memory */
-+struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
-+                                              struct ocfs2_super *osb,
-+                                              int slot_num)
-+{
-+      unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
-+                                          OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
-+      unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
-+                                      LOCAL_GROUP_QUOTA_SYSTEM_INODE };
-+      struct super_block *sb = osb->sb;
-+      struct ocfs2_local_disk_dqinfo *ldinfo;
-+      struct inode *lqinode;
-+      struct buffer_head *bh;
-+      int type;
-+      int status;
-+      struct ocfs2_quota_recovery *rec;
-+
-+      mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num);
-+      rec = ocfs2_alloc_quota_recovery();
-+      if (!rec)
-+              return ERR_PTR(-ENOMEM);
-+      /* First init... */
-+
-+      for (type = 0; type < MAXQUOTAS; type++) {
-+              if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
-+                      continue;
-+              lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
-+              if (!lqinode) {
-+                      status = -ENOENT;
-+                      goto out;
-+              }
-+              status = ocfs2_inode_lock_full(lqinode, NULL, 1,
-+                                                     OCFS2_META_LOCK_NOQUEUE);
-+              /* Someone else is holding the lock? Then he must be
-+               * doing the recovery. Just skip the file... */
-+              if (status == -EAGAIN) {
-+                      mlog(ML_NOTICE, "skipping quota recovery for slot %d "
-+                           "because quota file is locked.\n", slot_num);
-+                      status = 0;
-+                      goto out_put;
-+              } else if (status < 0) {
-+                      mlog_errno(status);
-+                      goto out_put;
-+              }
-+              /* Now read local header */
-+              bh = ocfs2_bread(lqinode, 0, &status, 0);
-+              if (!bh) {
-+                      mlog_errno(status);
-+                      mlog(ML_ERROR, "failed to read quota file info header "
-+                              "(slot=%d type=%d)\n", slot_num, type);
-+                      goto out_lock;
-+              }
-+              ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
-+                                                      OCFS2_LOCAL_INFO_OFF);
-+              status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
-+                                                 &rec->r_list[type]);
-+              brelse(bh);
-+out_lock:
-+              ocfs2_inode_unlock(lqinode, 1);
-+out_put:
-+              iput(lqinode);
-+              if (status < 0)
-+                      break;
-+      }
-+out:
-+      if (status < 0) {
-+              ocfs2_free_quota_recovery(rec);
-+              rec = ERR_PTR(status);
-+      }
-+      return rec;
-+}
-+
-+/* Sync changes in local quota file into global quota file and
-+ * reinitialize local quota file.
-+ * The function expects local quota file to be already locked and
-+ * dqonoff_mutex locked. */
-+static int ocfs2_recover_local_quota_file(struct inode *lqinode,
-+                                        int type,
-+                                        struct ocfs2_quota_recovery *rec)
-+{
-+      struct super_block *sb = lqinode->i_sb;
-+      struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
-+      struct ocfs2_local_disk_chunk *dchunk;
-+      struct ocfs2_local_disk_dqblk *dqblk;
-+      struct dquot *dquot;
-+      handle_t *handle;
-+      struct buffer_head *hbh = NULL, *qbh = NULL;
-+      int status = 0;
-+      int bit, chunk;
-+      struct ocfs2_recovery_chunk *rchunk, *next;
-+      qsize_t spacechange, inodechange;
-+
-+      mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type);
-+
-+      status = ocfs2_lock_global_qf(oinfo, 1);
-+      if (status < 0)
-+              goto out;
-+
-+      list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) {
-+              chunk = rchunk->rc_chunk;
-+              hbh = ocfs2_bread(lqinode, ol_quota_chunk_block(sb, chunk),
-+                                &status, 0);
-+              if (!hbh) {
-+                      mlog_errno(status);
-+                      break;
-+              }
-+              dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
-+              for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
-+                      qbh = ocfs2_bread(lqinode,
-+                                        ol_dqblk_block(sb, chunk, bit),
-+                                        &status, 0);
-+                      if (!qbh) {
-+                              mlog_errno(status);
-+                              break;
-+                      }
-+                      dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
-+                              ol_dqblk_block_off(sb, chunk, bit));
-+                      dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
-+                      if (!dquot) {
-+                              status = -EIO;
-+                              mlog(ML_ERROR, "Failed to get quota structure "
-+                                   "for id %u, type %d. Cannot finish quota "
-+                                   "file recovery.\n",
-+                                   (unsigned)le64_to_cpu(dqblk->dqb_id),
-+                                   type);
-+                              goto out_put_bh;
-+                      }
-+                      handle = ocfs2_start_trans(OCFS2_SB(sb),
-+                                                 OCFS2_QSYNC_CREDITS);
-+                      if (IS_ERR(handle)) {
-+                              status = PTR_ERR(handle);
-+                              mlog_errno(status);
-+                              goto out_put_dquot;
-+                      }
-+                      mutex_lock(&sb_dqopt(sb)->dqio_mutex);
-+                      spin_lock(&dq_data_lock);
-+                      /* Add usage from quota entry into quota changes
-+                       * of our node. Auxiliary variables are important
-+                       * due to signedness */
-+                      spacechange = le64_to_cpu(dqblk->dqb_spacemod);
-+                      inodechange = le64_to_cpu(dqblk->dqb_inodemod);
-+                      dquot->dq_dqb.dqb_curspace += spacechange;
-+                      dquot->dq_dqb.dqb_curinodes += inodechange;
-+                      spin_unlock(&dq_data_lock);
-+                      /* We want to drop reference held by the crashed
-+                       * node. Since we have our own reference we know
-+                       * global structure actually won't be freed. */
-+                      status = ocfs2_global_release_dquot(dquot);
-+                      if (status < 0) {
-+                              mlog_errno(status);
-+                              goto out_commit;
-+                      }
-+                      /* Release local quota file entry */
-+                      status = ocfs2_journal_access(handle, lqinode,
-+                                      qbh, OCFS2_JOURNAL_ACCESS_WRITE);
-+                      if (status < 0) {
-+                              mlog_errno(status);
-+                              goto out_commit;
-+                      }
-+                      lock_buffer(qbh);
-+                      WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap));
-+                      ocfs2_clear_bit(bit, dchunk->dqc_bitmap);
-+                      le32_add_cpu(&dchunk->dqc_free, 1);
-+                      unlock_buffer(qbh);
-+                      status = ocfs2_journal_dirty(handle, qbh);
-+                      if (status < 0)
-+                              mlog_errno(status);
-+out_commit:
-+                      mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
-+                      ocfs2_commit_trans(OCFS2_SB(sb), handle);
-+out_put_dquot:
-+                      dqput(dquot);
-+out_put_bh:
-+                      brelse(qbh);
-+                      if (status < 0)
-+                              break;
-+              }
-+              brelse(hbh);
-+              list_del(&rchunk->rc_list);
-+              kfree(rchunk->rc_bitmap);
-+              kfree(rchunk);
-+              if (status < 0)
-+                      break;
-+      }
-+      ocfs2_unlock_global_qf(oinfo, 1);
-+out:
-+      if (status < 0)
-+              free_recovery_list(&(rec->r_list[type]));
-+      mlog_exit(status);
-+      return status;
-+}
-+
-+/* Recover local quota files for given node different from us */
-+int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
-+                              struct ocfs2_quota_recovery *rec,
-+                              int slot_num)
-+{
-+      unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
-+                                      LOCAL_GROUP_QUOTA_SYSTEM_INODE };
-+      struct super_block *sb = osb->sb;
-+      struct ocfs2_local_disk_dqinfo *ldinfo;
-+      struct buffer_head *bh;
-+      handle_t *handle;
-+      int type;
-+      int status = 0;
-+      struct inode *lqinode;
-+      unsigned int flags;
-+
-+      mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num);
-+      mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
-+      for (type = 0; type < MAXQUOTAS; type++) {
-+              if (list_empty(&(rec->r_list[type])))
-+                      continue;
-+              mlog(0, "Recovering quota in slot %d\n", slot_num);
-+              lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
-+              if (!lqinode) {
-+                      status = -ENOENT;
-+                      goto out;
-+              }
-+              status = ocfs2_inode_lock_full(lqinode, NULL, 1,
-+                                                     OCFS2_META_LOCK_NOQUEUE);
-+              /* Someone else is holding the lock? Then he must be
-+               * doing the recovery. Just skip the file... */
-+              if (status == -EAGAIN) {
-+                      mlog(ML_NOTICE, "skipping quota recovery for slot %d "
-+                           "because quota file is locked.\n", slot_num);
-+                      status = 0;
-+                      goto out_put;
-+              } else if (status < 0) {
-+                      mlog_errno(status);
-+                      goto out_put;
-+              }
-+              /* Now read local header */
-+              bh = ocfs2_bread(lqinode, 0, &status, 0);
-+              if (!bh) {
-+                      mlog_errno(status);
-+                      mlog(ML_ERROR, "failed to read quota file info header "
-+                              "(slot=%d type=%d)\n", slot_num, type);
-+                      goto out_lock;
-+              }
-+              ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
-+                                                      OCFS2_LOCAL_INFO_OFF);
-+              /* Is recovery still needed? */
-+              flags = le32_to_cpu(ldinfo->dqi_flags);
-+              if (!(flags & OLQF_CLEAN))
-+                      status = ocfs2_recover_local_quota_file(lqinode,
-+                                                              type,
-+                                                              rec);
-+              /* We don't want to mark file as clean when it is actually
-+               * active */
-+              if (slot_num == osb->slot_num)
-+                      goto out_bh;
-+              /* Mark quota file as clean if we are recovering quota file of
-+               * some other node. */
-+              handle = ocfs2_start_trans(osb, 1);
-+              if (IS_ERR(handle)) {
-+                      status = PTR_ERR(handle);
-+                      mlog_errno(status);
-+                      goto out_bh;
-+              }
-+              status = ocfs2_journal_access(handle, lqinode, bh,
-+                                            OCFS2_JOURNAL_ACCESS_WRITE);
-+              if (status < 0) {
-+                      mlog_errno(status);
-+                      goto out_trans;
-+              }
-+              lock_buffer(bh);
-+              ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN);
-+              unlock_buffer(bh);
-+              status = ocfs2_journal_dirty(handle, bh);
-+              if (status < 0)
-+                      mlog_errno(status);
-+out_trans:
-+              ocfs2_commit_trans(osb, handle);
-+out_bh:
-+              brelse(bh);
-+out_lock:
-+              ocfs2_inode_unlock(lqinode, 1);
-+out_put:
-+              iput(lqinode);
-+              if (status < 0)
-+                      break;
-+      }
-+out:
-+      mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
-+      kfree(rec);
-+      return status;
-+}
-+
- /* Read information header from quota file */
- static int ocfs2_local_read_info(struct super_block *sb, int type)
- {
-@@ -262,6 +649,7 @@ static int ocfs2_local_read_info(struct 
-       struct inode *lqinode = sb_dqopt(sb)->files[type];
-       int status;
-       struct buffer_head *bh = NULL;
-+      struct ocfs2_quota_recovery *rec;
-       int locked = 0;
-       info->dqi_maxblimit = 0x7fffffffffffffffLL;
-@@ -275,6 +663,7 @@ static int ocfs2_local_read_info(struct 
-       info->dqi_priv = oinfo;
-       oinfo->dqi_type = type;
-       INIT_LIST_HEAD(&oinfo->dqi_chunk);
-+      oinfo->dqi_rec = NULL;
-       oinfo->dqi_lqi_bh = NULL;
-       oinfo->dqi_ibh = NULL;
-@@ -305,10 +694,27 @@ static int ocfs2_local_read_info(struct 
-       oinfo->dqi_ibh = bh;
-       /* We crashed when using local quota file? */
--      if (!(info->dqi_flags & OLQF_CLEAN))
--              goto out_err;   /* So far we just bail out. Later we should resync here */
-+      if (!(info->dqi_flags & OLQF_CLEAN)) {
-+              rec = OCFS2_SB(sb)->quota_rec;
-+              if (!rec) {
-+                      rec = ocfs2_alloc_quota_recovery();
-+                      if (!rec) {
-+                              status = -ENOMEM;
-+                              mlog_errno(status);
-+                              goto out_err;
-+                      }
-+                      OCFS2_SB(sb)->quota_rec = rec;
-+              }
--      status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type],
-+              status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
-+                                                   &rec->r_list[type]);
-+              if (status < 0) {
-+                      mlog_errno(status);
-+                      goto out_err;
-+              }
-+      }
-+
-+      status = ocfs2_load_local_quota_bitmaps(lqinode,
-                                               ldinfo,
-                                               &oinfo->dqi_chunk);
-       if (status < 0) {
-@@ -394,6 +800,12 @@ static int ocfs2_local_free_info(struct 
-       }
-       ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
-+      /* dqonoff_mutex protects us against racing with recovery thread... */
-+      if (oinfo->dqi_rec) {
-+              ocfs2_free_quota_recovery(oinfo->dqi_rec);
-+              mark_clean = 0;
-+      }
-+
-       if (!mark_clean)
-               goto out;
---- a/fs/ocfs2/super.c
-+++ b/fs/ocfs2/super.c
-@@ -973,6 +973,9 @@ static int ocfs2_fill_super(struct super
-                       goto read_super_error;
-               }
-       }
-+
-+      ocfs2_complete_quota_recovery(osb);
-+
-       /* Now we wake up again for processes waiting for quotas */
-       atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
-       wake_up(&osb->osb_mount_event);