[Cluster-devel] cluster/gfs2 fsck/fs_recovery.c fsck/fs_recove ...

rpeterso at sourceware.org rpeterso at sourceware.org
Fri Oct 19 15:07:59 UTC 2007


CVSROOT:	/cvs/cluster
Module name:	cluster
Branch: 	RHEL5
Changes by:	rpeterso at sourceware.org	2007-10-19 15:07:59

Modified files:
	gfs2/fsck      : fs_recovery.c fs_recovery.h initialize.c 
	gfs2/libgfs2   : Makefile libgfs2.h 
Added files:
	gfs2/libgfs2   : recovery.c 

Log message:
	Resolves: bz 291551: gfs2_fsck clears journals without asking.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/fsck/fs_recovery.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.4.2.1&r2=1.4.2.2
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/fsck/fs_recovery.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.2&r2=1.2.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/fsck/initialize.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.6.2.3&r2=1.6.2.4
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/libgfs2/recovery.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/libgfs2/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.4&r2=1.4.2.1
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/libgfs2/libgfs2.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.7.2.13&r2=1.7.2.14

--- cluster/gfs2/fsck/fs_recovery.c	2007/02/12 19:29:45	1.4.2.1
+++ cluster/gfs2/fsck/fs_recovery.c	2007/10/19 15:07:58	1.4.2.2
@@ -11,6 +11,7 @@
 *******************************************************************************
 ******************************************************************************/
 
+#include <errno.h>
 #include <inttypes.h>
 #include <linux_endian.h>
 #include <stdlib.h>
@@ -18,36 +19,421 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "fsck.h"
+#include "fs_recovery.h"
 #include "libgfs2.h"
 #include "util.h"
-#include "fs_recovery.h"
 
 #define RANDOM(values) ((values) * (random() / (RAND_MAX + 1.0)))
 
+unsigned int sd_found_jblocks = 0, sd_replayed_jblocks = 0;
+unsigned int sd_found_metablocks = 0, sd_replayed_metablocks = 0;
+unsigned int sd_found_revokes = 0;
+osi_list_t sd_revoke_list;
+unsigned int sd_replay_tail;
+
+struct gfs2_revoke_replay {
+	osi_list_t rr_list;
+	uint64_t rr_blkno;
+	unsigned int rr_where;
+};
+
+int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
+{
+	osi_list_t *tmp, *head = &sd_revoke_list;
+	struct gfs2_revoke_replay *rr;
+	int found = 0;
+
+	osi_list_foreach(tmp, head) {
+		rr = osi_list_entry(tmp, struct gfs2_revoke_replay, rr_list);
+		if (rr->rr_blkno == blkno) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		rr->rr_where = where;
+		return 0;
+	}
+
+	rr = malloc(sizeof(struct gfs2_revoke_replay));
+	if (!rr)
+		return -ENOMEM;
+
+	rr->rr_blkno = blkno;
+	rr->rr_where = where;
+	osi_list_add(&rr->rr_list, head);
+	return 1;
+}
+
+int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
+{
+	osi_list_t *tmp;
+	struct gfs2_revoke_replay *rr;
+	int wrap, a, b, revoke;
+	int found = 0;
+
+	osi_list_foreach(tmp, &sd_revoke_list) {
+		rr = osi_list_entry(tmp, struct gfs2_revoke_replay, rr_list);
+		if (rr->rr_blkno == blkno) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return 0;
+
+	wrap = (rr->rr_where < sd_replay_tail);
+	a = (sd_replay_tail < where);
+	b = (where < rr->rr_where);
+	revoke = (wrap) ? (a || b) : (a && b);
+	return revoke;
+}
+
+void gfs2_revoke_clean(struct gfs2_sbd *sdp)
+{
+	osi_list_t *head = &sd_revoke_list;
+	struct gfs2_revoke_replay *rr;
+
+	while (!osi_list_empty(head)) {
+		rr = osi_list_entry(head->next, struct gfs2_revoke_replay, rr_list);
+		osi_list_del(&rr->rr_list);
+		free(rr);
+	}
+}
+
+static int buf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				struct gfs2_log_descriptor *ld, __be64 *ptr,
+				int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh_log, *bh_ip;
+	uint64_t blkno;
+	int error = 0;
+	enum update_flags if_modified;
+
+	if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_METADATA)
+		return 0;
+
+	gfs2_replay_incr_blk(ip, &start);
+
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		uint32_t check_magic;
+
+		sd_found_metablocks++;
+
+		blkno = be64_to_cpu(*ptr++);
+		if (gfs2_revoke_check(sdp, blkno, start))
+			continue;
+
+		error = gfs2_replay_read_block(ip, start, &bh_log);
+		if (error)
+			return error;
+
+		bh_ip = bget(sdp, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+
+		check_magic = ((struct gfs2_meta_header *)
+			       (bh_ip->b_data))->mh_magic;
+		check_magic = be32_to_cpu(check_magic);
+		if (check_magic != GFS2_MAGIC) {
+			if_modified = not_updated;
+			error = -EIO;
+		} else
+			if_modified = updated;
+
+		brelse(bh_log, not_updated);
+		brelse(bh_ip, if_modified);
+		if (error)
+			break;
+
+		sd_replayed_metablocks++;
+	}
+	return error;
+}
+
+static int revoke_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				   struct gfs2_log_descriptor *ld, __be64 *ptr,
+				   int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_length);
+	unsigned int revokes = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh;
+	unsigned int offset;
+	uint64_t blkno;
+	int first = 1;
+	int error;
+
+	if (pass != 0 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_REVOKE)
+		return 0;
+
+	offset = sizeof(struct gfs2_log_descriptor);
+
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		error = gfs2_replay_read_block(ip, start, &bh);
+		if (error)
+			return error;
+
+		if (!first) {
+			if (gfs2_check_meta(bh, GFS2_METATYPE_LB))
+				continue;
+		}
+		while (offset + sizeof(uint64_t) <= sdp->sd_sb.sb_bsize) {
+			blkno = be64_to_cpu(*(__be64 *)(bh->b_data + offset));
+			error = gfs2_revoke_add(sdp, blkno, start);
+			if (error < 0)
+				return error;
+			else if (error)
+				sd_found_revokes++;
+
+			if (!--revokes)
+				break;
+			offset += sizeof(uint64_t);
+		}
+
+		brelse(bh, updated);
+		offset = sizeof(struct gfs2_meta_header);
+		first = 0;
+	}
+	return 0;
+}
+
+static int databuf_lo_scan_elements(struct gfs2_inode *ip, unsigned int start,
+				    struct gfs2_log_descriptor *ld,
+				    __be64 *ptr, int pass)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	unsigned int blks = be32_to_cpu(ld->ld_data1);
+	struct gfs2_buffer_head *bh_log, *bh_ip;
+	uint64_t blkno;
+	uint64_t esc;
+	int error = 0;
+
+	if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_JDATA)
+		return 0;
+
+	gfs2_replay_incr_blk(ip, &start);
+	for (; blks; gfs2_replay_incr_blk(ip, &start), blks--) {
+		blkno = be64_to_cpu(*ptr++);
+		esc = be64_to_cpu(*ptr++);
+
+		sd_found_jblocks++;
+
+		if (gfs2_revoke_check(sdp, blkno, start))
+			continue;
+
+		error = gfs2_replay_read_block(ip, start, &bh_log);
+		if (error)
+			return error;
+
+		bh_ip = bget(sdp, blkno);
+		memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
+
+		/* Unescape */
+		if (esc) {
+			__be32 *eptr = (__be32 *)bh_ip->b_data;
+			*eptr = cpu_to_be32(GFS2_MAGIC);
+		}
+
+		brelse(bh_log, not_updated);
+		brelse(bh_ip, updated);
+
+		sd_replayed_jblocks++;
+	}
+	return error;
+}
+
+/**
+ * foreach_descriptor - go through the active part of the log
+ * @ip: the journal incore inode
+ * @start: the first log header in the active region
+ * @end: the last log header (don't process the contents of this entry))
+ *
+ * Call a given function once for every log descriptor in the active
+ * portion of the log.
+ *
+ * Returns: errno
+ */
+
+int foreach_descriptor(struct gfs2_inode *ip, unsigned int start,
+		       unsigned int end, int pass)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_log_descriptor *ld;
+	int error = 0;
+	uint32_t length;
+	__be64 *ptr;
+	unsigned int offset = sizeof(struct gfs2_log_descriptor);
+	offset += sizeof(__be64) - 1;
+	offset &= ~(sizeof(__be64) - 1);
+
+	while (start != end) {
+		uint32_t check_magic;
+
+		error = gfs2_replay_read_block(ip, start, &bh);
+		if (error)
+			return error;
+		check_magic = ((struct gfs2_meta_header *)
+			       (bh->b_data))->mh_magic;
+		check_magic = be32_to_cpu(check_magic);
+		if (check_magic != GFS2_MAGIC) {
+			brelse(bh, updated);
+			return -EIO;
+		}
+		ld = (struct gfs2_log_descriptor *)bh->b_data;
+		length = be32_to_cpu(ld->ld_length);
+
+		if (be32_to_cpu(ld->ld_header.mh_type) == GFS2_METATYPE_LH) {
+			struct gfs2_log_header lh;
+
+			error = get_log_header(ip, start, &lh);
+			if (!error) {
+				gfs2_replay_incr_blk(ip, &start);
+				brelse(bh, updated);
+				continue;
+			}
+			if (error == 1)
+				error = -EIO;
+			brelse(bh, updated);
+			return error;
+		} else if (gfs2_check_meta(bh, GFS2_METATYPE_LD)) {
+			brelse(bh, updated);
+			return -EIO;
+		}
+		ptr = (__be64 *)(bh->b_data + offset);
+		error = databuf_lo_scan_elements(ip, start, ld, ptr, pass);
+		if (error) {
+			brelse(bh, updated);
+			return error;
+		}
+		error = buf_lo_scan_elements(ip, start, ld, ptr, pass);
+		if (error) {
+			brelse(bh, updated);
+			return error;
+		}
+		error = revoke_lo_scan_elements(ip, start, ld, ptr, pass);
+		if (error) {
+			brelse(bh, updated);
+			return error;
+		}
+
+		while (length--)
+			gfs2_replay_incr_blk(ip, &start);
+
+		brelse(bh, updated);
+	}
+
+	return 0;
+}
+
+/**
+ * gfs2_recover_journal - recovery a given journal
+ * @ip: the journal incore inode
+ *
+ * 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_inode *ip, int j)
+{
+	struct gfs2_sbd *sdp = ip->i_sbd;
+	struct gfs2_log_header head;
+	unsigned int pass;
+	int error;
+
+	log_info("jid=%u: Looking at journal...\n", j);
+
+	osi_list_init(&sd_revoke_list);
+	error = gfs2_find_jhead(ip, &head);
+	if (error)
+		goto out;
+
+	if (head.lh_flags & GFS2_LOG_HEAD_UNMOUNT) {
+		log_info("jid=%u: Journal is clean.\n", j);
+		return 0;
+	}
+	if (query(&opts, "\nJournal #%d (\"journal%d\") is dirty.  Okay to replay it? (y/n)",
+		    j+1, j)) {
+		log_info("jid=%u: Replaying journal...\n", j);
+
+		sd_found_jblocks = sd_replayed_jblocks = 0;
+		sd_found_metablocks = sd_replayed_metablocks = 0;
+		sd_found_revokes = 0;
+		sd_replay_tail = head.lh_tail;
+		for (pass = 0; pass < 2; pass++) {
+			error = foreach_descriptor(ip, head.lh_tail,
+						   head.lh_blkno, pass);
+			if (error)
+				goto out;
+		}
+		log_info("jid=%u: Found %u revoke tags\n", j,
+			 sd_found_revokes);
+		gfs2_revoke_clean(sdp);
+		error = clean_journal(ip, &head);
+		if (error)
+			goto out;
+		log_err("jid=%u: Replayed %u of %u journaled data blocks\n",
+			j, sd_replayed_jblocks, sd_found_jblocks);
+		log_err("jid=%u: Replayed %u of %u metadata blocks\n",
+			j, sd_replayed_metablocks, sd_found_metablocks);
+	} else {
+		if (query(&opts, "Do you want to clear the dirty journal instead? (y/n)")) {
+			write_journal(sdp, sdp->md.journal[j], j,
+				      sdp->md.journal[j]->i_di.di_size /
+				      sdp->sd_sb.sb_bsize);
+			
+		} else
+			log_err("jid=%u: Dirty journal not replayed or cleared.\n", j);
+	}
+
+out:
+	log_info("jid=%u: %s\n", j, (error) ? "Failed" : "Done");
+	return error;
+}
+
 /*
- * reconstruct_journals - write fresh journals
+ * replay_journals - replay the journals
  * sdp: the super block
  *
- * FIXME: it would be nice to get this to actually replay the journals
- * - there should be a flag to the fsck to enable/disable this
- * feature, and the fsck should probably fall back to clearing the
- * journal if an inconsitancy is found, but only for the bad journal
+ * There should be a flag to the fsck to enable/disable this
+ * feature.  The fsck falls back to clearing the journal if an 
+ * inconsistency is found, but only for the bad journal.
  *
  * Returns: 0 on success, -1 on failure
  */
-int reconstruct_journals(struct gfs2_sbd *sdp){
+int replay_journals(struct gfs2_sbd *sdp){
 	int i;
 
-	log_notice("Clearing journals (this may take a while)");
-	for(i=0; i < sdp->md.journals; i++) {
-		/* Journal replay seems to have slowed down quite a bit in
-		 * the gfs2_fsck */
+	log_notice("Recovering journals (this may take a while)");
+
+	/* Get master dinode */
+	sdp->master_dir = gfs2_load_inode(sdp,
+					  sdp->sd_sb.sb_master_dir.no_addr);
+	gfs2_lookupi(sdp->master_dir, "jindex", 6, &sdp->md.jiinode);
+
+	/* read in the journal index data */
+	if (ji_update(sdp)){
+		log_err("Unable to read in jindex inode.\n");
+		return -1;
+	}
+
+	for(i = 0; i < sdp->md.journals; i++) {
 		if((i % 2) == 0)
 			log_at_notice(".");
-		write_journal(sdp, sdp->md.journal[i], i,
-					  sdp->md.journal[i]->i_di.di_size / sdp->sd_sb.sb_bsize);
-		/* Can't use d_di.di_blocks because that also includes metadata. */
+		gfs2_recover_journal(sdp->md.journal[i], i);
+		inode_put(sdp->md.journal[i],
+			  (opts.no ? not_updated : updated));
 	}
-	log_notice("\nJournals cleared.\n");
+	log_notice("\nJournal recovery complete.\n");
+	inode_put(sdp->master_dir, not_updated);
+	inode_put(sdp->md.jiinode, not_updated);
+	/* Sync the buffers to disk so we get a fresh start. */
+	bsync(sdp);
 	return 0;
 }
--- cluster/gfs2/fsck/fs_recovery.h	2006/06/06 14:49:31	1.2
+++ cluster/gfs2/fsck/fs_recovery.h	2007/10/19 15:07:58	1.2.2.1
@@ -16,7 +16,7 @@
 
 #include "libgfs2.h"
 
-int reconstruct_journals(struct gfs2_sbd *sdp);
+int replay_journals(struct gfs2_sbd *sdp);
 
 #endif /* __FS_RECOVERY_H__ */
 
--- cluster/gfs2/fsck/initialize.c	2007/06/26 01:51:44	1.6.2.3
+++ cluster/gfs2/fsck/initialize.c	2007/10/19 15:07:58	1.6.2.4
@@ -42,8 +42,7 @@
 int init_journals(struct gfs2_sbd *sbp)
 {
 	if(!opts.no) {
-		/* ATTENTION -- Journal replay is not supported */
-		if(reconstruct_journals(sbp))
+		if(replay_journals(sbp))
 			return 1;
 	}
 	return 0;
@@ -192,51 +191,18 @@
 }
 
 /**
- * fill_super_block
- * @sdp:
+ * init_system_inodes
  *
  * Returns: 0 on success, -1 on failure
  */
-static int fill_super_block(struct gfs2_sbd *sdp)
+static int init_system_inodes(struct gfs2_sbd *sdp)
 {
-	uint32_t i;
-	char *buf;
 	uint64_t inumbuf;
+	char *buf;
 	struct gfs2_statfs_change sc;
 	int rgcount;
-	uint64_t addl_mem_needed;
 	enum rgindex_trust_level trust_lvl;
-
-	sync();
-
-	/********************************************************************
-	 ***************** First, initialize all lists **********************
-	 ********************************************************************/
-	log_info("Initializing lists...\n");
-	osi_list_init(&sdp->rglist);
-	osi_list_init(&sdp->buf_list);
-	for(i = 0; i < BUF_HASH_SIZE; i++) {
-		osi_list_init(&dir_hash[i]);
-		osi_list_init(&inode_hash[i]);
-		osi_list_init(&sdp->buf_hash[i]);
-	}
-
-	/********************************************************************
-	 ************  next, read in on-disk SB and set constants  **********
-	 ********************************************************************/
-	sdp->sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
-	sdp->bsize = sdp->sd_sb.sb_bsize;
-
-	if(sizeof(struct gfs2_sb) > sdp->sd_sb.sb_bsize){
-		log_crit("GFS superblock is larger than the blocksize!\n");
-		log_debug("sizeof(struct gfs2_sb) > sdp->sd_sb.sb_bsize\n");
-		return -1;
-	}
-
-	compute_constants(sdp);
-	if(read_sb(sdp) < 0){
-		return -1;
-	}
+	uint64_t addl_mem_needed;
 
 	/*******************************************************************
 	 ******************  Initialize important inodes  ******************
@@ -298,9 +264,10 @@
 	}
 	if (trust_lvl > distrust) {
 		log_err("RG recovery impossible; I can't fix this file system.\n");
-		goto fail;
+		return -1;
 	}
 	log_info("%u resource groups found.\n", rgcount);
+
 	/*******************************************************************
 	 *******  Now, set boundary fields in the super block  *************
 	 *******************************************************************/
@@ -318,7 +285,6 @@
 		goto fail;
 	}
 	return 0;
-
  fail:
 	empty_super_block(sdp);
 
@@ -326,6 +292,50 @@
 }
 
 /**
+ * fill_super_block
+ * @sdp:
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int fill_super_block(struct gfs2_sbd *sdp)
+{
+	uint32_t i;
+
+	sync();
+
+	/********************************************************************
+	 ***************** First, initialize all lists **********************
+	 ********************************************************************/
+	log_info("Initializing lists...\n");
+	osi_list_init(&sdp->rglist);
+	osi_list_init(&sdp->buf_list);
+	for(i = 0; i < BUF_HASH_SIZE; i++) {
+		osi_list_init(&dir_hash[i]);
+		osi_list_init(&inode_hash[i]);
+		osi_list_init(&sdp->buf_hash[i]);
+	}
+
+	/********************************************************************
+	 ************  next, read in on-disk SB and set constants  **********
+	 ********************************************************************/
+	sdp->sd_sb.sb_bsize = GFS2_DEFAULT_BSIZE;
+	sdp->bsize = sdp->sd_sb.sb_bsize;
+
+	if(sizeof(struct gfs2_sb) > sdp->sd_sb.sb_bsize){
+		log_crit("GFS superblock is larger than the blocksize!\n");
+		log_debug("sizeof(struct gfs2_sb) > sdp->sd_sb.sb_bsize\n");
+		return -1;
+	}
+
+	compute_constants(sdp);
+	if(read_sb(sdp) < 0){
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
  * init_sbp - initialize superblock pointer
  *
  */
@@ -365,6 +375,9 @@
 		return -1;
 	}
 
+	if (init_system_inodes(sbp))
+		return -1;
+
 	return 0;
 }
 
/cvs/cluster/cluster/gfs2/libgfs2/recovery.c,v  -->  standard output
revision 1.1.2.1
--- cluster/gfs2/libgfs2/recovery.c
+++ -	2007-10-19 15:07:59.613200000 +0000
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2007 Red Hat, Inc.  All rights reserved.
+ *
+ * This copyrighted material is made available to anyone wishing to use,
+ * modify, copy, or redistribute it subject to the terms and conditions
+ * of the GNU General Public License version 2.
+ *
+ */
+
+/*
+ * NOTE:
+ *
+ * This code was pilfered from the gfs2 kernel and adapted to userland.
+ * If you change this part, you should evaluate whether the upstream kernel
+ * version of recovery.c should be changed as well.  Likewise, if the
+ * upstream version changes, this part should be kept in sync.
+ * 
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "libgfs2.h"
+
+void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk)
+{
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+        if (++*blk == jd_blocks)
+                *blk = 0;
+}
+
+int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+			   struct gfs2_buffer_head **bh)
+{
+	int new = 0;
+	uint64_t dblock;
+	uint32_t extlen;
+
+	block_map(ip, blk, &new, &dblock, &extlen, FALSE, not_updated);
+	if (!dblock)
+		return -EIO;
+
+	*bh = bread(ip->i_sbd, dblock);
+	return 0;
+}
+
+/**
+ * get_log_header - read the log header for a given segment
+ * @ip: the journal incore inode
+ * @blk: the block to look at
+ * @lh: the log header to return
+ *
+ * Read the log header for a given segement in a given journal.  Do a few
+ * sanity checks on it.
+ *
+ * Returns: 0 on success,
+ *          1 if the header was invalid or incomplete,
+ *          errno on error
+ */
+
+int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+		   struct gfs2_log_header *head)
+{
+	struct gfs2_buffer_head *bh;
+	struct gfs2_log_header lh, *tmp;
+	uint32_t hash, saved_hash;
+	int error;
+
+	error = gfs2_replay_read_block(ip, blk, &bh);
+	if (error)
+		return error;
+
+	tmp = (struct gfs2_log_header *)bh->b_data;
+	saved_hash = tmp->lh_hash;
+	tmp->lh_hash = 0;
+	hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header));
+	tmp->lh_hash = saved_hash;
+	gfs2_log_header_in(&lh, bh->b_data);
+	brelse(bh, not_updated);
+
+	if (error || lh.lh_blkno != blk || lh.lh_hash != hash)
+		return 1;
+
+	*head = lh;
+
+	return 0;
+}
+
+/**
+ * find_good_lh - find a good log header
+ * @ip: the journal incore inode
+ * @blk: the segment to start searching from
+ * @lh: the log header to fill in
+ * @forward: if true search forward in the log, else search backward
+ *
+ * Call get_log_header() to get a log header for a segment, but if the
+ * segment is bad, either scan forward or backward until we find a good one.
+ *
+ * Returns: errno
+ */
+
+int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+		 struct gfs2_log_header *head)
+{
+	unsigned int orig_blk = *blk;
+	int error;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+
+	for (;;) {
+		error = get_log_header(ip, *blk, head);
+		if (error <= 0)
+			return error;
+
+		if (++*blk == jd_blocks)
+			*blk = 0;
+
+		if (*blk == orig_blk)
+			return -EIO;
+	}
+}
+
+/**
+ * jhead_scan - make sure we've found the head of the log
+ * @jd: the journal
+ * @head: this is filled in with the log descriptor of the head
+ *
+ * At this point, seg and lh should be either the head of the log or just
+ * before.  Scan forward until we find the head.
+ *
+ * Returns: errno
+ */
+
+int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int blk = head->lh_blkno;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	struct gfs2_log_header lh;
+	int error;
+
+	for (;;) {
+		if (++blk == jd_blocks)
+			blk = 0;
+
+		error = get_log_header(ip, blk, &lh);
+		if (error < 0)
+			return error;
+		if (error == 1)
+			continue;
+
+		if (lh.lh_sequence == head->lh_sequence)
+			return -EIO;
+		if (lh.lh_sequence < head->lh_sequence)
+			break;
+
+		*head = lh;
+	}
+
+	return 0;
+}
+
+/**
+ * gfs2_find_jhead - find the head of a log
+ * @jd: the journal
+ * @head: the log descriptor for the head of the log is returned here
+ *
+ * Do a binary search of a journal and find the valid log entry with the
+ * highest sequence number.  (i.e. the log head)
+ *
+ * Returns: errno
+ */
+
+int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	struct gfs2_log_header lh_1, lh_m;
+	uint32_t blk_1, blk_2, blk_m;
+	uint32_t jd_blocks = ip->i_di.di_size / ip->i_sbd->sd_sb.sb_bsize;
+	int error;
+
+	blk_1 = 0;
+	blk_2 = jd_blocks - 1;
+
+	for (;;) {
+		blk_m = (blk_1 + blk_2) / 2;
+
+		error = find_good_lh(ip, &blk_1, &lh_1);
+		if (error)
+			return error;
+
+		error = find_good_lh(ip, &blk_m, &lh_m);
+		if (error)
+			return error;
+
+		if (blk_1 == blk_m || blk_m == blk_2)
+			break;
+
+		if (lh_1.lh_sequence <= lh_m.lh_sequence)
+			blk_1 = blk_m;
+		else
+			blk_2 = blk_m;
+	}
+
+	error = jhead_scan(ip, &lh_1);
+	if (error)
+		return error;
+
+	*head = lh_1;
+
+	return error;
+}
+
+/**
+ * clean_journal - mark a dirty journal as being clean
+ * @sdp: the filesystem
+ * @jd: the journal
+ * @head: the head journal to start from
+ *
+ * Returns: errno
+ */
+
+int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head)
+{
+	unsigned int lblock;
+	struct gfs2_log_header *lh;
+	uint32_t hash, extlen;
+	struct gfs2_buffer_head *bh;
+	int new = 0;
+	uint64_t dblock;
+
+	lblock = head->lh_blkno;
+	gfs2_replay_incr_blk(ip, &lblock);
+	block_map(ip, lblock, &new, &dblock, &extlen, 0, not_updated);
+	if (!dblock)
+		return -EIO;
+
+	bh = bread(ip->i_sbd, dblock);
+	memset(bh->b_data, 0, bh->b_size);
+
+	lh = (struct gfs2_log_header *)bh->b_data;
+	memset(lh, 0, sizeof(struct gfs2_log_header));
+	lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
+	lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH);
+	lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH);
+	lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1);
+	lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT);
+	lh->lh_blkno = cpu_to_be32(lblock);
+	hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header));
+	lh->lh_hash = cpu_to_be32(hash);
+
+	brelse(bh, updated);
+
+	return 0;
+}
+
--- cluster/gfs2/libgfs2/Makefile	2006/08/11 15:18:13	1.4
+++ cluster/gfs2/libgfs2/Makefile	2007/10/19 15:07:59	1.4.2.1
@@ -27,7 +27,7 @@
        -D_GNU_SOURCE -DGFS2_RELEASE_NAME=\"2\" ${INCLUDEPATH}
 
 H=gfs2_disk_hash.h libgfs2.h linux_endian.h ondisk.h osi_list.h
-C=bitmap.c block_list.c buf.c device_geometry.c fs_bits.c fs_geometry.c fs_ops.c locking.c gfs2_log.c misc.c ondisk.c size.c structures.c super.c rgrp.c
+C=bitmap.c block_list.c buf.c device_geometry.c fs_bits.c fs_geometry.c fs_ops.c locking.c gfs2_log.c misc.c ondisk.c recovery.c size.c structures.c super.c rgrp.c
 O=$(subst .c,.o,${C})
 
 all: ${LIBGFS2}
--- cluster/gfs2/libgfs2/libgfs2.h	2007/10/17 20:16:44	1.7.2.13
+++ cluster/gfs2/libgfs2/libgfs2.h	2007/10/19 15:07:59	1.7.2.14
@@ -515,6 +515,22 @@
 void lock_for_admin(struct gfs2_sbd *sdp);
 void cleanup_metafs(struct gfs2_sbd *sdp);
 
+/* recovery.c */
+void gfs2_replay_incr_blk(struct gfs2_inode *ip, unsigned int *blk);
+int gfs2_replay_read_block(struct gfs2_inode *ip, unsigned int blk,
+			   struct gfs2_buffer_head **bh);
+int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where);
+int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno,
+		      unsigned int where);
+void gfs2_revoke_clean(struct gfs2_sbd *sdp);
+int get_log_header(struct gfs2_inode *ip, unsigned int blk,
+		   struct gfs2_log_header *head);
+int find_good_lh(struct gfs2_inode *ip, unsigned int *blk,
+		 struct gfs2_log_header *head);
+int jhead_scan(struct gfs2_inode *ip, struct gfs2_log_header *head);
+int gfs2_find_jhead(struct gfs2_inode *ip, struct gfs2_log_header *head);
+int clean_journal(struct gfs2_inode *ip, struct gfs2_log_header *head);
+
 /* rgrp.c */
 int gfs2_compute_bitstructs(struct gfs2_sbd *sdp, struct rgrp_list *rgd);
 struct rgrp_list *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk);




More information about the Cluster-devel mailing list