|
@@ -65,6 +65,11 @@ static int ocfs2_trylock_journal(struct ocfs2_super *osb,
|
|
|
static int ocfs2_recover_orphans(struct ocfs2_super *osb,
|
|
|
int slot);
|
|
|
static int ocfs2_commit_thread(void *arg);
|
|
|
+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_quota_recovery *qrec);
|
|
|
|
|
|
static inline int ocfs2_wait_on_mount(struct ocfs2_super *osb)
|
|
|
{
|
|
@@ -76,6 +81,97 @@ static inline int ocfs2_wait_on_quotas(struct ocfs2_super *osb)
|
|
|
return __ocfs2_wait_on_mount(osb, 1);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This replay_map is to track online/offline slots, so we could recover
|
|
|
+ * offline slots during recovery and mount
|
|
|
+ */
|
|
|
+
|
|
|
+enum ocfs2_replay_state {
|
|
|
+ REPLAY_UNNEEDED = 0, /* Replay is not needed, so ignore this map */
|
|
|
+ REPLAY_NEEDED, /* Replay slots marked in rm_replay_slots */
|
|
|
+ REPLAY_DONE /* Replay was already queued */
|
|
|
+};
|
|
|
+
|
|
|
+struct ocfs2_replay_map {
|
|
|
+ unsigned int rm_slots;
|
|
|
+ enum ocfs2_replay_state rm_state;
|
|
|
+ unsigned char rm_replay_slots[0];
|
|
|
+};
|
|
|
+
|
|
|
+void ocfs2_replay_map_set_state(struct ocfs2_super *osb, int state)
|
|
|
+{
|
|
|
+ if (!osb->replay_map)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* If we've already queued the replay, we don't have any more to do */
|
|
|
+ if (osb->replay_map->rm_state == REPLAY_DONE)
|
|
|
+ return;
|
|
|
+
|
|
|
+ osb->replay_map->rm_state = state;
|
|
|
+}
|
|
|
+
|
|
|
+int ocfs2_compute_replay_slots(struct ocfs2_super *osb)
|
|
|
+{
|
|
|
+ struct ocfs2_replay_map *replay_map;
|
|
|
+ int i, node_num;
|
|
|
+
|
|
|
+ /* If replay map is already set, we don't do it again */
|
|
|
+ if (osb->replay_map)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ replay_map = kzalloc(sizeof(struct ocfs2_replay_map) +
|
|
|
+ (osb->max_slots * sizeof(char)), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!replay_map) {
|
|
|
+ mlog_errno(-ENOMEM);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock(&osb->osb_lock);
|
|
|
+
|
|
|
+ replay_map->rm_slots = osb->max_slots;
|
|
|
+ replay_map->rm_state = REPLAY_UNNEEDED;
|
|
|
+
|
|
|
+ /* set rm_replay_slots for offline slot(s) */
|
|
|
+ for (i = 0; i < replay_map->rm_slots; i++) {
|
|
|
+ if (ocfs2_slot_to_node_num_locked(osb, i, &node_num) == -ENOENT)
|
|
|
+ replay_map->rm_replay_slots[i] = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ osb->replay_map = replay_map;
|
|
|
+ spin_unlock(&osb->osb_lock);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+void ocfs2_queue_replay_slots(struct ocfs2_super *osb)
|
|
|
+{
|
|
|
+ struct ocfs2_replay_map *replay_map = osb->replay_map;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!replay_map)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (replay_map->rm_state != REPLAY_NEEDED)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (i = 0; i < replay_map->rm_slots; i++)
|
|
|
+ if (replay_map->rm_replay_slots[i])
|
|
|
+ ocfs2_queue_recovery_completion(osb->journal, i, NULL,
|
|
|
+ NULL, NULL);
|
|
|
+ replay_map->rm_state = REPLAY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+void ocfs2_free_replay_slots(struct ocfs2_super *osb)
|
|
|
+{
|
|
|
+ struct ocfs2_replay_map *replay_map = osb->replay_map;
|
|
|
+
|
|
|
+ if (!osb->replay_map)
|
|
|
+ return;
|
|
|
+
|
|
|
+ kfree(replay_map);
|
|
|
+ osb->replay_map = NULL;
|
|
|
+}
|
|
|
+
|
|
|
int ocfs2_recovery_init(struct ocfs2_super *osb)
|
|
|
{
|
|
|
struct ocfs2_recovery_map *rm;
|
|
@@ -1194,24 +1290,24 @@ static void ocfs2_queue_recovery_completion(struct ocfs2_journal *journal,
|
|
|
}
|
|
|
|
|
|
/* Called by the mount code to queue recovery the last part of
|
|
|
- * recovery for it's own slot. */
|
|
|
+ * recovery for it's own and offline slot(s). */
|
|
|
void ocfs2_complete_mount_recovery(struct ocfs2_super *osb)
|
|
|
{
|
|
|
struct ocfs2_journal *journal = osb->journal;
|
|
|
|
|
|
- if (osb->dirty) {
|
|
|
- /* No need to queue up our truncate_log as regular
|
|
|
- * cleanup will catch that. */
|
|
|
- ocfs2_queue_recovery_completion(journal,
|
|
|
- osb->slot_num,
|
|
|
- osb->local_alloc_copy,
|
|
|
- NULL,
|
|
|
- NULL);
|
|
|
- ocfs2_schedule_truncate_log_flush(osb, 0);
|
|
|
+ /* No need to queue up our truncate_log as regular cleanup will catch
|
|
|
+ * that */
|
|
|
+ ocfs2_queue_recovery_completion(journal, osb->slot_num,
|
|
|
+ osb->local_alloc_copy, NULL, NULL);
|
|
|
+ ocfs2_schedule_truncate_log_flush(osb, 0);
|
|
|
|
|
|
- osb->local_alloc_copy = NULL;
|
|
|
- osb->dirty = 0;
|
|
|
- }
|
|
|
+ osb->local_alloc_copy = NULL;
|
|
|
+ osb->dirty = 0;
|
|
|
+
|
|
|
+ /* queue to recover orphan slots for all offline slots */
|
|
|
+ ocfs2_replay_map_set_state(osb, REPLAY_NEEDED);
|
|
|
+ ocfs2_queue_replay_slots(osb);
|
|
|
+ ocfs2_free_replay_slots(osb);
|
|
|
}
|
|
|
|
|
|
void ocfs2_complete_quota_recovery(struct ocfs2_super *osb)
|
|
@@ -1254,6 +1350,14 @@ restart:
|
|
|
goto bail;
|
|
|
}
|
|
|
|
|
|
+ status = ocfs2_compute_replay_slots(osb);
|
|
|
+ if (status < 0)
|
|
|
+ mlog_errno(status);
|
|
|
+
|
|
|
+ /* queue recovery for our own slot */
|
|
|
+ ocfs2_queue_recovery_completion(osb->journal, osb->slot_num, NULL,
|
|
|
+ NULL, NULL);
|
|
|
+
|
|
|
spin_lock(&osb->osb_lock);
|
|
|
while (rm->rm_used) {
|
|
|
/* It's always safe to remove entry zero, as we won't
|
|
@@ -1319,11 +1423,8 @@ skip_recovery:
|
|
|
|
|
|
ocfs2_super_unlock(osb, 1);
|
|
|
|
|
|
- /* 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);
|
|
|
+ /* queue recovery for offline slots */
|
|
|
+ ocfs2_queue_replay_slots(osb);
|
|
|
|
|
|
bail:
|
|
|
mutex_lock(&osb->recovery_lock);
|
|
@@ -1332,6 +1433,7 @@ bail:
|
|
|
goto restart;
|
|
|
}
|
|
|
|
|
|
+ ocfs2_free_replay_slots(osb);
|
|
|
osb->recovery_thread_task = NULL;
|
|
|
mb(); /* sync with ocfs2_recovery_thread_running */
|
|
|
wake_up(&osb->recovery_event);
|
|
@@ -1483,6 +1585,9 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb,
|
|
|
goto done;
|
|
|
}
|
|
|
|
|
|
+ /* we need to run complete recovery for offline orphan slots */
|
|
|
+ ocfs2_replay_map_set_state(osb, REPLAY_NEEDED);
|
|
|
+
|
|
|
mlog(ML_NOTICE, "Recovering node %d from slot %d on device (%u,%u)\n",
|
|
|
node_num, slot_num,
|
|
|
MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev));
|