Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / fs / gfs2 / recovery.c
index 8bc182c..09fa319 100644 (file)
@@ -7,20 +7,20 @@
  * of the GNU General Public License version 2.
  */
 
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/crc32.h>
-#include <linux/lm_interface.h>
+#include <linux/slow-work.h>
 
 #include "gfs2.h"
 #include "incore.h"
 #include "bmap.h"
 #include "glock.h"
 #include "glops.h"
-#include "lm.h"
 #include "lops.h"
 #include "meta_io.h"
 #include "recovery.h"
@@ -69,7 +69,7 @@ int gfs2_revoke_add(struct gfs2_sbd *sdp, u64 blkno, unsigned int where)
                return 0;
        }
 
-       rr = kmalloc(sizeof(struct gfs2_revoke_replay), GFP_KERNEL);
+       rr = kmalloc(sizeof(struct gfs2_revoke_replay), GFP_NOFS);
        if (!rr)
                return -ENOMEM;
 
@@ -116,6 +116,22 @@ void gfs2_revoke_clean(struct gfs2_sbd *sdp)
        }
 }
 
+static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf)
+{
+       const struct gfs2_log_header *str = buf;
+
+       if (str->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) ||
+           str->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH))
+               return 1;
+
+       lh->lh_sequence = be64_to_cpu(str->lh_sequence);
+       lh->lh_flags = be32_to_cpu(str->lh_flags);
+       lh->lh_tail = be32_to_cpu(str->lh_tail);
+       lh->lh_blkno = be32_to_cpu(str->lh_blkno);
+       lh->lh_hash = be32_to_cpu(str->lh_hash);
+       return 0;
+}
+
 /**
  * get_log_header - read the log header for a given segment
  * @jd: the journal
@@ -134,7 +150,7 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
                          struct gfs2_log_header_host *head)
 {
        struct buffer_head *bh;
-       struct gfs2_log_header_host lh;
+       struct gfs2_log_header_host uninitialized_var(lh);
        const u32 nothing = 0;
        u32 hash;
        int error;
@@ -147,12 +163,10 @@ static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
                                             sizeof(u32));
        hash = crc32_le(hash, (unsigned char const *)&nothing, sizeof(nothing));
        hash ^= (u32)~0;
-       gfs2_log_header_in(&lh, bh->b_data);
+       error = gfs2_log_header_in(&lh, bh->b_data);
        brelse(bh);
 
-       if (lh.lh_header.mh_magic != GFS2_MAGIC ||
-           lh.lh_header.mh_type != GFS2_METATYPE_LH ||
-           lh.lh_blkno != blk || lh.lh_hash != hash)
+       if (error || lh.lh_blkno != blk || lh.lh_hash != hash)
                return 1;
 
        *head = lh;
@@ -377,7 +391,7 @@ static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header_host *hea
        lblock = head->lh_blkno;
        gfs2_replay_incr_blk(sdp, &lblock);
        bh_map.b_size = 1 << ip->i_inode.i_blkbits;
-       error = gfs2_block_map(&ip->i_inode, lblock, 0, &bh_map);
+       error = gfs2_block_map(&ip->i_inode, lblock, &bh_map, 0);
        if (error)
                return error;
        if (!bh_map.b_blocknr) {
@@ -411,18 +425,41 @@ static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header_host *hea
        return error;
 }
 
-/**
- * gfs2_recover_journal - recovery a given journal
- * @jd: the struct gfs2_jdesc describing the journal
- *
- * Acquire the journal's lock, check to see if the journal is clean, and
- * do recovery if necessary.
- *
- * Returns: errno
- */
 
-int gfs2_recover_journal(struct gfs2_jdesc *jd)
+static void gfs2_recovery_done(struct gfs2_sbd *sdp, unsigned int jid,
+                               unsigned int message)
+{
+       char env_jid[20];
+       char env_status[20];
+       char *envp[] = { env_jid, env_status, NULL };
+       struct lm_lockstruct *ls = &sdp->sd_lockstruct;
+        ls->ls_recover_jid_done = jid;
+        ls->ls_recover_jid_status = message;
+       sprintf(env_jid, "JID=%d", jid);
+       sprintf(env_status, "RECOVERY=%s",
+               message == LM_RD_SUCCESS ? "Done" : "Failed");
+        kobject_uevent_env(&sdp->sd_kobj, KOBJ_CHANGE, envp);
+}
+
+static int gfs2_recover_get_ref(struct slow_work *work)
+{
+       struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work);
+       if (test_and_set_bit(JDF_RECOVERY, &jd->jd_flags))
+               return -EBUSY;
+       return 0;
+}
+
+static void gfs2_recover_put_ref(struct slow_work *work)
 {
+       struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work);
+       clear_bit(JDF_RECOVERY, &jd->jd_flags);
+       smp_mb__after_clear_bit();
+       wake_up_bit(&jd->jd_flags, JDF_RECOVERY);
+}
+
+static void gfs2_recover_work(struct slow_work *work)
+{
+       struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work);
        struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
        struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
        struct gfs2_log_header_host head;
@@ -436,7 +473,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd)
                fs_info(sdp, "jid=%u: Trying to acquire journal lock...\n",
                        jd->jd_jid);
 
-               /* Aquire the journal lock so we can do recovery */
+               /* Acquire the journal lock so we can do recovery */
 
                error = gfs2_glock_nq_num(sdp, jd->jd_jid, &gfs2_journal_glops,
                                          LM_ST_EXCLUSIVE,
@@ -455,7 +492,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd)
                };
 
                error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED,
-                                          LM_FLAG_NOEXP, &ji_gh);
+                                          LM_FLAG_NOEXP | GL_NOCACHE, &ji_gh);
                if (error)
                        goto fail_gunlock_j;
        } else {
@@ -482,7 +519,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd)
 
                error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED,
                                           LM_FLAG_NOEXP | LM_FLAG_PRIORITY |
-                                          GL_NOCANCEL | GL_NOCACHE, &t_gh);
+                                          GL_NOCACHE, &t_gh);
                if (error)
                        goto fail_gunlock_ji;
 
@@ -490,13 +527,21 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd)
                        if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
                                ro = 1;
                } else {
-                       if (sdp->sd_vfs->s_flags & MS_RDONLY)
-                               ro = 1;
+                       if (sdp->sd_vfs->s_flags & MS_RDONLY) {
+                               /* check if device itself is read-only */
+                               ro = bdev_read_only(sdp->sd_vfs->s_bdev);
+                               if (!ro) {
+                                       fs_info(sdp, "recovery required on "
+                                               "read-only filesystem.\n");
+                                       fs_info(sdp, "write access will be "
+                                               "enabled during recovery.\n");
+                               }
+                       }
                }
 
                if (ro) {
-                       fs_warn(sdp, "jid=%u: Can't replay: read-only FS\n",
-                               jd->jd_jid);
+                       fs_warn(sdp, "jid=%u: Can't replay: read-only block "
+                               "device\n", jd->jd_jid);
                        error = -EROFS;
                        goto fail_gunlock_tr;
                }
@@ -525,13 +570,13 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd)
        if (jd->jd_jid != sdp->sd_lockstruct.ls_jid)
                gfs2_glock_dq_uninit(&ji_gh);
 
-       gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS);
+       gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS);
 
        if (jd->jd_jid != sdp->sd_lockstruct.ls_jid)
                gfs2_glock_dq_uninit(&j_gh);
 
        fs_info(sdp, "jid=%u: Done\n", jd->jd_jid);
-       return 0;
+       return;
 
 fail_gunlock_tr:
        gfs2_glock_dq_uninit(&t_gh);
@@ -545,27 +590,30 @@ fail_gunlock_j:
        fs_info(sdp, "jid=%u: %s\n", jd->jd_jid, (error) ? "Failed" : "Done");
 
 fail:
-       gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP);
-       return error;
+       gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP);
 }
 
-/**
- * gfs2_check_journals - Recover any dirty journals
- * @sdp: the filesystem
- *
- */
+struct slow_work_ops gfs2_recover_ops = {
+       .owner   = THIS_MODULE,
+       .get_ref = gfs2_recover_get_ref,
+       .put_ref = gfs2_recover_put_ref,
+       .execute = gfs2_recover_work,
+};
 
-void gfs2_check_journals(struct gfs2_sbd *sdp)
-{
-       struct gfs2_jdesc *jd;
 
-       for (;;) {
-               jd = gfs2_jdesc_find_dirty(sdp);
-               if (!jd)
-                       break;
+static int gfs2_recovery_wait(void *word)
+{
+       schedule();
+       return 0;
+}
 
-               if (jd != sdp->sd_jdesc)
-                       gfs2_recover_journal(jd);
-       }
+int gfs2_recover_journal(struct gfs2_jdesc *jd)
+{
+       int rv;
+       rv = slow_work_enqueue(&jd->jd_work);
+       if (rv)
+               return rv;
+       wait_on_bit(&jd->jd_flags, JDF_RECOVERY, gfs2_recovery_wait, TASK_UNINTERRUPTIBLE);
+       return 0;
 }