[Ocfs2-commits] mfasheh commits r2563 - branches/readonly-operation/fs/ocfs2

svn-commits at oss.oracle.com svn-commits at oss.oracle.com
Mon Sep 12 21:45:48 CDT 2005


Author: mfasheh
Date: 2005-09-12 21:45:42 -0500 (Mon, 12 Sep 2005)
New Revision: 2563

Modified:
   branches/readonly-operation/fs/ocfs2/buffer_head_io.c
   branches/readonly-operation/fs/ocfs2/dlmglue.c
   branches/readonly-operation/fs/ocfs2/journal.c
   branches/readonly-operation/fs/ocfs2/journal.h
   branches/readonly-operation/fs/ocfs2/namei.c
   branches/readonly-operation/fs/ocfs2/ocfs2.h
   branches/readonly-operation/fs/ocfs2/ocfs2_fs.h
   branches/readonly-operation/fs/ocfs2/super.c
Log:
* Support readonly mount in "soft" and "hard" modes.
                           
* Detect readonly devices and do a hard readonly mount there.

* Update heartbeat mount options so they understand the meaning between no
  heartbeat (only valid for readonly device) and local heartbeat.

* errors=panic / errors=ro mount options. these will be used in a future
  commit.

* turn osb->s_next_gen_lock into osb->osb_lock and use it for guarding osb  
  flags as well. s_next_gen_lock is hardly used to there seems no point in
  having two spinlocks on osb.



Modified: branches/readonly-operation/fs/ocfs2/buffer_head_io.c
===================================================================
--- branches/readonly-operation/fs/ocfs2/buffer_head_io.c	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/buffer_head_io.c	2005-09-13 02:45:42 UTC (rev 2563)
@@ -57,7 +57,6 @@
 {
 	int status = 0;
 	int i;
-	struct super_block *sb;
 	struct buffer_head *bh;
 
 	mlog_entry("(bh[0]->b_blocknr = %llu, nr=%d, inode=%p)\n",
@@ -69,7 +68,13 @@
 		goto bail;
 	}
 
-	sb = osb->sb;
+	/* No need to check for a soft readonly file system here. non
+	 * journalled writes are only ever done on system files which
+	 * can get modified during recovery even if read-only. */
+	if (ocfs2_is_hard_readonly(osb)) {
+		status = -EROFS;
+		goto bail;
+	}
 
 	if (inode)
 		down(&OCFS2_I(inode)->ip_io_sem);

Modified: branches/readonly-operation/fs/ocfs2/dlmglue.c
===================================================================
--- branches/readonly-operation/fs/ocfs2/dlmglue.c	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/dlmglue.c	2005-09-13 02:45:42 UTC (rev 2563)
@@ -1163,7 +1163,7 @@
 int ocfs2_data_lock(struct inode *inode,
 		    int write)
 {
-	int status, level;
+	int status = 0, level;
 	struct ocfs2_lock_res *lockres;
 
 	BUG_ON(!inode);
@@ -1174,6 +1174,16 @@
 	     OCFS2_I(inode)->ip_blkno,
 	     write ? "EXMODE" : "PRMODE");
 
+	/* We'll allow faking a readonly data lock for
+	 * rodevices. */
+	if (ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb))) {
+		if (write) {
+			status = -EROFS;
+			mlog_errno(status);
+		}
+		goto out;
+	}
+
 	lockres = &OCFS2_I(inode)->ip_data_lockres;
 
 	level = write ? LKM_EXMODE : LKM_PRMODE;
@@ -1183,6 +1193,7 @@
 	if (status < 0)
 		mlog_errno(status);
 
+out:
 	mlog_exit(status);
 	return status;
 }
@@ -1230,7 +1241,8 @@
 	     OCFS2_I(inode)->ip_blkno,
 	     write ? "EXMODE" : "PRMODE");
 
-	ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
+	if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb)))
+		ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
 
 	mlog_exit_void();
 }
@@ -1612,6 +1624,14 @@
 	status = 0;
 	acquired = 0;
 
+	/* We'll allow faking a readonly metadata lock for
+	 * rodevices. */
+	if (ocfs2_is_hard_readonly(osb)) {
+		if (ex)
+			status = -EROFS;
+		goto bail;
+	}
+
 	if (!(flags & OCFS2_META_LOCK_RECOVERY))
 		status = wait_event_interruptible(osb->recovery_event,
 						  ocfs2_node_map_is_empty(osb,
@@ -1702,7 +1722,8 @@
 	     OCFS2_I(inode)->ip_blkno,
 	     ex ? "EXMODE" : "PRMODE");
 
-	ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
+	if (!ocfs2_is_hard_readonly(OCFS2_SB(inode->i_sb)))
+		ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
 
 	mlog_exit_void();
 }
@@ -1718,6 +1739,9 @@
 
 	mlog_entry_void();
 
+	if (ocfs2_is_hard_readonly(osb))
+		return -EROFS;
+
 	status = ocfs2_cluster_lock(osb, lockres, level, 0, NULL, 0);
 	if (status < 0) {
 		mlog_errno(status);
@@ -1764,6 +1788,9 @@
 	int status;
 	struct ocfs2_lock_res *lockres = &osb->osb_rename_lockres;
 
+	if (ocfs2_is_hard_readonly(osb))
+		return -EROFS;
+
 	status = ocfs2_cluster_lock(osb, lockres, LKM_EXMODE, 0, NULL, 0);
 	if (status < 0)
 		mlog_errno(status);

Modified: branches/readonly-operation/fs/ocfs2/journal.c
===================================================================
--- branches/readonly-operation/fs/ocfs2/journal.c	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/journal.c	2005-09-13 02:45:42 UTC (rev 2563)
@@ -150,6 +150,11 @@
 	if (!osb || !osb->journal->j_journal)
 		BUG();
 
+	if (ocfs2_is_hard_readonly(osb)) {
+		/* Ugh. Can't we just return ERR_PTR */
+		goto done_free;
+	}
+
 	BUG_ON(osb->journal->j_state == OCFS2_JOURNAL_FREE);
 	BUG_ON(max_buffs <= 0);
 
@@ -485,6 +490,19 @@
 
 #define OCFS2_DEFAULT_COMMIT_INTERVAL 	(HZ * 5)
 
+void ocfs2_set_journal_params(ocfs2_super *osb)
+{
+	journal_t *journal = osb->journal->j_journal;
+
+	spin_lock(&journal->j_state_lock);
+	journal->j_commit_interval = OCFS2_DEFAULT_COMMIT_INTERVAL;
+	if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER)
+		journal->j_flags |= JFS_BARRIER;
+	else
+		journal->j_flags &= ~JFS_BARRIER;
+	spin_unlock(&journal->j_state_lock);
+}
+
 int ocfs2_journal_init(ocfs2_journal *journal, int *dirty)
 {
 	int status = -1;
@@ -551,15 +569,7 @@
 
 	mlog(0, "Returned from journal_init_inode\n");
 	mlog(0, "j_journal->j_maxlen = %u\n", j_journal->j_maxlen);
-	j_journal->j_commit_interval = OCFS2_DEFAULT_COMMIT_INTERVAL;
 
-	spin_lock(&j_journal->j_state_lock);
-	if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER)
-		j_journal->j_flags |= JFS_BARRIER;
-	else
-		j_journal->j_flags &= ~JFS_BARRIER;
-	spin_unlock(&j_journal->j_state_lock);
-
 	*dirty = (le32_to_cpu(di->id1.journal1.ij_flags) &
 		  OCFS2_JOURNAL_DIRTY_FL);
 
@@ -567,6 +577,8 @@
 	journal->j_inode = inode;
 	journal->j_bh = bh;
 
+	ocfs2_set_journal_params(osb);
+
 	journal->j_state = OCFS2_JOURNAL_LOADED;
 
 	status = 0;
@@ -909,7 +921,6 @@
 	}
 
 	mlog(0, "Recovery completion\n");
-
 	mlog_exit_void();
 }
 
@@ -962,6 +973,8 @@
 						osb->slot_num,
 						osb->local_alloc_copy,
 						NULL);
+		ocfs2_schedule_truncate_log_flush(osb, 0);
+
 		osb->local_alloc_copy = NULL;
 		osb->dirty = 0;
 	}
@@ -1182,7 +1195,6 @@
 
 	mlog_exit(status);
 	return status;
-
 }
 
 /*
@@ -1372,7 +1384,7 @@
 	if  (!orphan_dir_inode) {
 		status = -ENOENT;
 		mlog_errno(status);
-		goto bail;
+		goto out;
 	}
 
 	down(&orphan_dir_inode->i_sem);
@@ -1380,7 +1392,7 @@
 	if (status < 0) {
 		up(&orphan_dir_inode->i_sem);
 		mlog_errno(status);
-		goto bail;
+		goto out;
 	}
 	have_disk_lock = 1;
 
@@ -1397,7 +1409,7 @@
 			if (bh)
 				brelse(bh);
 			mlog_errno(status);
-			goto bail;
+			goto out;
 		}
 
 		local = 0;
@@ -1411,7 +1423,7 @@
 				status = -EINVAL;
 				mlog_errno(status);
 				brelse(bh);
-				goto bail;
+				goto out;
 			}
 
 			local += le16_to_cpu(de->rec_len);
@@ -1483,7 +1495,7 @@
 		inode = iter;
 	}
 
-bail:
+out:
 	if (have_disk_lock)
 		ocfs2_meta_unlock(orphan_dir_inode, 0);
 
@@ -1545,3 +1557,50 @@
 
 	return 0;
 }
+
+/* Look for a dirty journal without taking any cluster locks. Used for
+ * hard readonly access to determine whether the file system journals
+ * require recovery. */
+int ocfs2_check_journals_nolocks(ocfs2_super *osb)
+{
+	int ret = 0;
+	unsigned int slot;
+	struct buffer_head *di_bh;
+	ocfs2_dinode *di;
+	struct inode *journal = NULL;
+
+	for(slot = 0; slot < osb->max_slots; slot++) {
+		journal = ocfs2_get_system_file_inode(osb,
+						      JOURNAL_SYSTEM_INODE,
+						      slot);
+		if (!journal || is_bad_inode(journal)) {
+			ret = -EACCES;
+			mlog_errno(ret);
+			goto out;
+		}
+
+		di_bh = NULL;
+		ret = ocfs2_read_block(osb, OCFS2_I(journal)->ip_blkno, &di_bh,
+				       0, journal);
+		if (ret < 0) {
+			mlog_errno(ret);
+			goto out;
+		}
+
+		di = (ocfs2_dinode *) di_bh->b_data;
+
+		if (le32_to_cpu(di->id1.journal1.ij_flags) &
+		    OCFS2_JOURNAL_DIRTY_FL)
+			ret = -EROFS;
+
+		brelse(di_bh);
+		if (ret)
+			break;
+	}
+
+out:
+	if (journal)
+		iput(journal);
+
+	return ret;
+}

Modified: branches/readonly-operation/fs/ocfs2/journal.h
===================================================================
--- branches/readonly-operation/fs/ocfs2/journal.h	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/journal.h	2005-09-13 02:45:42 UTC (rev 2563)
@@ -194,12 +194,14 @@
  *                          event on.
  *  ocfs2_start_checkpoint - Kick the commit thread to do a checkpoint.
  */
+void   ocfs2_set_journal_params(ocfs2_super *osb);
 int    ocfs2_journal_init(ocfs2_journal *journal,
 			  int *dirty);
 void   ocfs2_journal_shutdown(struct _ocfs2_super *osb);
 int    ocfs2_journal_wipe(ocfs2_journal *journal,
 			  int full);
 int    ocfs2_journal_load(ocfs2_journal *journal);
+int    ocfs2_check_journals_nolocks(ocfs2_super *osb);
 void   ocfs2_recovery_thread(struct _ocfs2_super *osb,
 			     int node_num);
 int    ocfs2_mark_dead_nodes(ocfs2_super *osb);

Modified: branches/readonly-operation/fs/ocfs2/namei.c
===================================================================
--- branches/readonly-operation/fs/ocfs2/namei.c	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/namei.c	2005-09-13 02:45:42 UTC (rev 2563)
@@ -496,9 +496,9 @@
 	else
 		inode->i_nlink = 1;
 	inode->i_mode = mode;
-	spin_lock(&osb->s_next_gen_lock);
+	spin_lock(&osb->osb_lock);
 	inode->i_generation = osb->s_next_generation++;
-	spin_unlock(&osb->s_next_gen_lock);
+	spin_unlock(&osb->osb_lock);
 
 	*new_fe_bh = sb_getblk(osb->sb, fe_blkno);
 	if (!*new_fe_bh) {

Modified: branches/readonly-operation/fs/ocfs2/ocfs2.h
===================================================================
--- branches/readonly-operation/fs/ocfs2/ocfs2.h	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/ocfs2.h	2005-09-13 02:45:42 UTC (rev 2563)
@@ -171,13 +171,18 @@
 
 enum ocfs2_mount_options
 {
-	OCFS2_MOUNT_HB_OK   = 1 << 0,	/* Heartbeat started */
+	OCFS2_MOUNT_HB_LOCAL   = 1 << 0, /* Heartbeat started in local mode */
 	OCFS2_MOUNT_BARRIER = 1 << 1,	/* Use block barriers */
+	OCFS2_MOUNT_ERRORS_PANIC = 1 << 2,
 #ifdef OCFS2_ORACORE_WORKAROUNDS
 	OCFS2_MOUNT_COMPAT_OCFS = 1 << 30, /* ocfs1 compatibility mode */
 #endif
 };
 
+#define OCFS2_OSB_SOFT_RO	0x0001
+#define OCFS2_OSB_HARD_RO	0x0002
+#define OCFS2_OSB_ERROR_FS	0x0003
+
 struct _ocfs2_journal;
 typedef struct _ocfs2_journal_handle ocfs2_journal_handle;
 
@@ -212,8 +217,11 @@
 	u32 s_feature_incompat;
 	u32 s_feature_ro_compat;
 
-	spinlock_t s_next_gen_lock;
+	/* Protects s_next_generaion, osb_flags. Could protect more on
+	 * osb as it's very short lived. */
+	spinlock_t osb_lock;
 	u32 s_next_generation;
+	unsigned long osb_flags;
 
 	unsigned long s_mount_opt;
 
@@ -291,6 +299,44 @@
 #define OCFS2_SB(sb)	    ((ocfs2_super *)(sb)->s_fs_info)
 #define OCFS2_MAX_OSB_ID             65536
 
+/* set / clear functions because cluster events can make these happen
+ * in parallel so we want the transitions to be atomic. this also
+ * means that any future flags osb_flags must be protected by spinlock
+ * too! */
+static inline void ocfs2_set_ro_flag(ocfs2_super *osb,
+				     int hard)
+{
+	spin_lock(&osb->osb_lock);
+	osb->osb_flags &= ~(OCFS2_OSB_SOFT_RO|OCFS2_OSB_HARD_RO);
+	if (hard)
+		osb->osb_flags |= OCFS2_OSB_HARD_RO;
+	else
+		osb->osb_flags |= OCFS2_OSB_SOFT_RO;
+	spin_unlock(&osb->osb_lock);
+}
+
+static inline int ocfs2_is_hard_readonly(ocfs2_super *osb)
+{
+	int ret;
+
+	spin_lock(&osb->osb_lock);
+	ret = osb->osb_flags & OCFS2_OSB_HARD_RO;
+	spin_unlock(&osb->osb_lock);
+
+	return ret;
+}
+
+static inline int ocfs2_is_soft_readonly(ocfs2_super *osb)
+{
+	int ret;
+
+	spin_lock(&osb->osb_lock);
+	ret = osb->osb_flags & OCFS2_OSB_SOFT_RO;
+	spin_unlock(&osb->osb_lock);
+
+	return ret;
+}
+
 /* Helps document which BUG's should really just force the file system
  * to go readonly */
 #define OCFS2_BUG_ON_RO(x) BUG_ON((x))

Modified: branches/readonly-operation/fs/ocfs2/ocfs2_fs.h
===================================================================
--- branches/readonly-operation/fs/ocfs2/ocfs2_fs.h	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/ocfs2_fs.h	2005-09-13 02:45:42 UTC (rev 2563)
@@ -185,7 +185,9 @@
 };
 
 /* Parameter passed from mount.ocfs2 to module */
-#define OCFS2_HB_OK		"hbok"
+#define OCFS2_COMPAT_HB_OK		"hbok"
+#define OCFS2_HB_NONE			"heartbeat=none"
+#define OCFS2_HB_LOCAL			"heartbeat=local"
 
 /*
  * OCFS2 directory file types.  Only the low 3 bits are used.  The

Modified: branches/readonly-operation/fs/ocfs2/super.c
===================================================================
--- branches/readonly-operation/fs/ocfs2/super.c	2005-09-13 02:21:45 UTC (rev 2562)
+++ branches/readonly-operation/fs/ocfs2/super.c	2005-09-13 02:45:42 UTC (rev 2563)
@@ -89,9 +89,11 @@
 MODULE_AUTHOR("Oracle");
 MODULE_LICENSE("GPL");
 
-static int ocfs2_parse_options(char *options, unsigned long *mount_opt);
+static int ocfs2_parse_options(struct super_block *sb, char *options,
+			       unsigned long *mount_opt, int is_remount);
 static void ocfs2_put_super(struct super_block *sb);
-static int ocfs2_mount_volume(struct super_block *sb, unsigned long mount_opt);
+static int ocfs2_mount_volume(struct super_block *sb);
+static int ocfs2_remount(struct super_block *sb, int *flags, char *data);
 static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err);
 static int ocfs2_initialize_mem_caches(void);
 static void ocfs2_free_mem_caches(void);
@@ -109,7 +111,9 @@
 static int ocfs2_verify_volume(ocfs2_dinode *di,
 			       struct buffer_head *bh,
 			       u32 sectsize);
-static int ocfs2_initialize_osb(ocfs2_super *osb, struct buffer_head *bh);
+static int ocfs2_initialize_super(struct super_block *sb,
+				  struct buffer_head *bh,
+				  int sector_size);
 static int ocfs2_get_sector(struct super_block *sb,
 			    struct buffer_head **bh,
 			    int block,
@@ -130,6 +134,7 @@
 	.sync_fs	= ocfs2_sync_fs,
 	.write_super	= ocfs2_write_super,
 	.put_super	= ocfs2_put_super,
+	.remount_fs	= ocfs2_remount,
 };
 
 #ifdef OCFS2_ORACORE_WORKAROUNDS
@@ -139,18 +144,26 @@
 enum {
 	Opt_hbok,
 	Opt_barrier,
+	Opt_err_panic,
+	Opt_err_ro,
 #ifdef OCFS2_ORACORE_WORKAROUNDS
 	Opt_datavolume,
 #endif
+	Opt_hb_none,
+	Opt_hb_local,
 	Opt_err,
 };
 
 static match_table_t tokens = {
-	{Opt_hbok, OCFS2_HB_OK},
+	{Opt_hbok, OCFS2_COMPAT_HB_OK},
 	{Opt_barrier, "barrier=%u"},
+	{Opt_err_panic, "errors=panic"},
+	{Opt_err_ro, "errors=remount-ro"},
 #ifdef OCFS2_ORACORE_WORKAROUNDS
 	{Opt_datavolume, "datavolume"},
 #endif
+	{Opt_hb_none, OCFS2_HB_NONE},
+	{Opt_hb_local, OCFS2_HB_LOCAL},
 	{Opt_err, NULL}
 };
 
@@ -172,6 +185,9 @@
 
 	sb->s_dirt = 0;
 
+	if (ocfs2_is_hard_readonly(osb))
+		return 0;
+
 	if (wait) {
 		status = ocfs2_flush_truncate_log(osb);
 		if (status < 0)
@@ -344,13 +360,152 @@
 	return (((unsigned long long)pagefactor) << bitshift) - 1;
 }
 
+static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
+{
+	int incompat_features;
+	int ret = 0;
+	unsigned long parsed_options;
+	ocfs2_super *osb = OCFS2_SB(sb);
+
+	if (!ocfs2_parse_options(sb, data, &parsed_options, 1)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* We're going to/from readonly mode. */
+	if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+		/* Lock here so the check of HARD_RO and the potential
+		 * setting of SOFT_RO is atomic. */
+		spin_lock(&osb->osb_lock);
+		if (osb->osb_flags & OCFS2_OSB_HARD_RO) {
+			mlog(ML_ERROR, "Remount on readonly device is forbidden.\n");
+			ret = -EROFS;
+			goto unlock_osb;
+		}
+
+		if (*flags & MS_RDONLY) {
+			mlog(0, "Going to ro mode.\n");
+			sb->s_flags |= MS_RDONLY;
+			osb->osb_flags |= OCFS2_OSB_SOFT_RO;
+		} else {
+			mlog(0, "Making ro filesystem writeable.\n");
+			incompat_features = OCFS2_HAS_RO_COMPAT_FEATURE(sb, ~OCFS2_FEATURE_RO_COMPAT_SUPP);
+			if (incompat_features) {
+				mlog(ML_ERROR, "couldn't mount RDWR because "
+				     "of unsupported optional features "
+				     "(%x).\n", incompat_features);
+				ret = -EINVAL;
+				goto unlock_osb;
+			}
+			sb->s_flags &= ~MS_RDONLY;
+			osb->osb_flags &= ~OCFS2_OSB_SOFT_RO;
+		}
+unlock_osb:
+		spin_unlock(&osb->osb_lock);
+	}
+
+	if (!ret) {
+		if (!ocfs2_is_hard_readonly(osb))
+			ocfs2_set_journal_params(osb);
+
+		/* Only save off the new mount options in case of a successful
+		 * remount. */
+		osb->s_mount_opt = parsed_options;
+	}
+
+out:
+	return ret;
+}
+
+static int ocfs2_sb_probe(struct super_block *sb,
+			  struct buffer_head **bh,
+			  int *sector_size)
+{
+	int status = 0, tmpstat;
+	ocfs1_vol_disk_hdr *hdr;
+	ocfs2_dinode *di;
+	int blksize;
+
+	*bh = NULL;
+
+	/* may be > 512 */
+	*sector_size = bdev_hardsect_size(sb->s_bdev);
+	if (*sector_size > OCFS2_MAX_BLOCKSIZE) {
+		mlog(ML_ERROR, "Hardware sector size too large: %d (max=%d)\n",
+		     *sector_size, OCFS2_MAX_BLOCKSIZE);
+		status = -EINVAL;
+		goto bail;
+	}
+
+	/* Can this really happen? */
+	if (*sector_size < OCFS2_MIN_BLOCKSIZE)
+		*sector_size = OCFS2_MIN_BLOCKSIZE;
+
+	/* check block zero for old format */
+	status = ocfs2_get_sector(sb, bh, 0, *sector_size);
+	if (status < 0) {
+		mlog_errno(status);
+		goto bail;
+	}
+	hdr = (ocfs1_vol_disk_hdr *) (*bh)->b_data;
+	if (hdr->major_version == OCFS1_MAJOR_VERSION) {
+		mlog(ML_ERROR, "incompatible version: %u.%u\n",
+		     hdr->major_version, hdr->minor_version);
+		status = -EINVAL;
+	}
+	if (memcmp(hdr->signature, OCFS1_VOLUME_SIGNATURE,
+		   strlen(OCFS1_VOLUME_SIGNATURE)) == 0) {
+		mlog(ML_ERROR, "incompatible volume signature: %8s\n",
+		     hdr->signature);
+		status = -EINVAL;
+	}
+	brelse(*bh);
+	*bh = NULL;
+	if (status < 0) {
+		mlog(ML_ERROR, "This is an ocfs v1 filesystem which must be "
+		     "upgraded before mounting with ocfs v2\n");
+		goto bail;
+	}
+
+	/*
+	 * Now check at magic offset for 512, 1024, 2048, 4096
+	 * blocksizes.  4096 is the maximum blocksize because it is
+	 * the minimum clustersize.
+	 */
+	status = -EINVAL;
+	for (blksize = *sector_size;
+	     blksize <= OCFS2_MAX_BLOCKSIZE;
+	     blksize <<= 1) {
+		tmpstat = ocfs2_get_sector(sb, bh,
+					   OCFS2_SUPER_BLOCK_BLKNO,
+					   blksize);
+		if (tmpstat < 0) {
+			status = tmpstat;
+			mlog_errno(status);
+			goto bail;
+		}
+		di = (ocfs2_dinode *) (*bh)->b_data;
+		status = ocfs2_verify_volume(di, *bh, blksize);
+		if (status >= 0)
+			goto bail;
+		brelse(*bh);
+		*bh = NULL;
+		if (status != -EAGAIN)
+			break;
+	}
+
+bail:
+	return status;
+}
+
 static int ocfs2_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct dentry *root;
-	int status;
+	int status, sector_size;
+	unsigned long parsed_opt;
 	struct inode *inode = NULL;
 	ocfs2_super *osb = NULL;
-	unsigned long mount_opt = 0;
+	struct buffer_head *bh = NULL;
 
 	mlog_entry("%p, %p, %i", sb, data, silent);
 
@@ -361,47 +516,86 @@
 		goto read_super_error;
 	}
 
-	if (!ocfs2_parse_options(data, &mount_opt)) {
-		status = -EINVAL;
+	/* probe for superblock */
+	status = ocfs2_sb_probe(sb, &bh, &sector_size);
+	if (status < 0) {
+		mlog(ML_ERROR, "superblock probe failed!\n");
 		goto read_super_error;
 	}
 
-	/* Stopgap check to ensure that mount.ocfs2 mounted the volume */
-	if (!(mount_opt & OCFS2_MOUNT_HB_OK)) {
-		  mlog(ML_ERROR, "No heartbeat for device (%s)\n", sb->s_id);
-		  status = -EINVAL;
-		  goto read_super_error;
+	status = ocfs2_initialize_super(sb, bh, sector_size);
+	osb = OCFS2_SB(sb);
+	if (status < 0) {
+		mlog_errno(status);
+		goto read_super_error;
 	}
+	brelse(bh);
+	bh = NULL;
 
+	if (!ocfs2_parse_options(sb, data, &parsed_opt, 1)) {
+		status = -EINVAL;
+		goto read_super_error;
+	}
+	osb->s_mount_opt = parsed_opt;
+
 #ifdef OCFS2_ORACORE_WORKAROUNDS
-	if (mount_opt & OCFS2_MOUNT_COMPAT_OCFS)
+	if (osb->s_mount_opt & OCFS2_MOUNT_COMPAT_OCFS)
 		sb->s_magic = OCFS_SUPER_MAGIC;
 	else
 #endif
 		sb->s_magic = OCFS2_SUPER_MAGIC;
-	sb->s_op = &ocfs2_sops;
-	sb->s_export_op = &ocfs2_export_ops;
-	sb->s_flags |= MS_NOATIME;
-	sb->s_fs_info = NULL;
 
-	status = ocfs2_mount_volume(sb, mount_opt);
-	/* ocfs2_mount_volume may set osb even on error so we want to
-	 * pull it off for proper cleanup. */
-	osb = OCFS2_SB(sb);
-	if (osb && osb->root_inode)
+	/* Hard readonly mode only if: bdev_read_only, MS_RDONLY,
+	 * heartbeat=none */
+	if (bdev_read_only(sb->s_bdev)) {
+		if (!(sb->s_flags & MS_RDONLY)) {
+			status = -EACCES;
+			mlog(ML_ERROR, "Readonly device detected but readonly "
+			     "mount was not specified.\n");
+			goto read_super_error;
+		}
+
+		/* You should not be able to start a local heartbeat
+		 * on a readonly device. */
+		if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) {
+			status = -EROFS;
+			mlog(ML_ERROR, "Local heartbeat specified on readonly "
+			     "device.\n");
+			goto read_super_error;
+		}
+
+		status = ocfs2_check_journals_nolocks(osb);
+		if (status < 0) {
+			if (status == -EROFS)
+				mlog(ML_ERROR, "Recovery required on readonly "
+				     "file system, but write access is "
+				     "unavailable.\n");
+			else
+				mlog_errno(status);			
+			goto read_super_error;
+		}
+
+		ocfs2_set_ro_flag(osb, 1);
+
+		printk(KERN_NOTICE "Readonly device detected. No cluster "
+		       "services will be utilized for this mount. Recovery "
+		       "will be skipped.\n");
+	}
+
+	if (!ocfs2_is_hard_readonly(osb) &&
+	    !(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) {
+		  mlog(ML_ERROR, "No heartbeat for device (%s)\n", sb->s_id);
+		  status = -EINVAL;
+		  goto read_super_error;
+	}
+
+	status = ocfs2_mount_volume(sb);
+	if (osb->root_inode)
 		inode = igrab(osb->root_inode);
 
 	if (status < 0)
 		goto read_super_error;
 
-	/* this is needed to support O_LARGEFILE */
-	sb->s_maxbytes = ocfs2_max_file_offset(sb->s_blocksize_bits);
-
-	if (!osb) {
-		status = -EINVAL;
-		goto read_super_error;
-	}
-
 	if (!inode) {
 		status = -EIO;
 		mlog_errno(status);
@@ -430,6 +624,9 @@
 	return status;
 
 read_super_error:
+	if (bh != NULL)
+		brelse(bh);
+
 	if (inode)
 		iput(inode);
 
@@ -463,13 +660,19 @@
 	.next           = NULL
 };
 
-static int ocfs2_parse_options(char *options, unsigned long *mount_opt)
+static int ocfs2_parse_options(struct super_block *sb,
+			       char *options,
+			       unsigned long *mount_opt,
+			       int is_remount)
 {
 	int status;
 	char *p;
 
-	mlog_entry("options: \"%s\"\n", options ? options : "(none)");
+	mlog_entry("remount: %d, options: \"%s\"\n", is_remount,
+		   options ? options : "(none)");
 
+	*mount_opt = 0;
+
 	if (!options) {
 		status = 1;
 		goto bail;
@@ -485,8 +688,12 @@
 		token = match_token(p, tokens, args);
 		switch (token) {
 		case Opt_hbok:
-			*mount_opt |= OCFS2_MOUNT_HB_OK;
+		case Opt_hb_local:
+			*mount_opt |= OCFS2_MOUNT_HB_LOCAL;
 			break;
+		case Opt_hb_none:
+			*mount_opt &= ~OCFS2_MOUNT_HB_LOCAL;
+			break;
 		case Opt_barrier:
 			if (match_int(&args[0], &option)) {
 				status = 0;
@@ -497,8 +704,20 @@
 			else
 				*mount_opt &= ~OCFS2_MOUNT_BARRIER;
 			break;
+		case Opt_err_panic:
+			*mount_opt |= OCFS2_MOUNT_ERRORS_PANIC;
+			break;
+		case Opt_err_ro:
+			*mount_opt &= ~OCFS2_MOUNT_ERRORS_PANIC;
+			break;
 #ifdef OCFS2_ORACORE_WORKAROUNDS
 		case Opt_datavolume:
+			if (is_remount) {
+				mlog(ML_ERROR, "Cannot specifiy datavolume "
+				     "on remount.\n");
+				status = 0;
+				goto bail;
+			}
 			*mount_opt |= OCFS2_MOUNT_COMPAT_OCFS;
 			break;
 #endif
@@ -729,87 +948,6 @@
 	ocfs2_lock_cache = NULL;
 }
 
-static int ocfs2_sb_probe(struct super_block *sb,
-			  struct buffer_head **bh,
-			  int *sector_size)
-{
-	int status = 0, tmpstat;
-	ocfs1_vol_disk_hdr *hdr;
-	ocfs2_dinode *di;
-	int blksize;
-
-	*bh = NULL;
-
-	/* may be > 512 */
-	*sector_size = bdev_hardsect_size(sb->s_bdev);
-	if (*sector_size > OCFS2_MAX_BLOCKSIZE) {
-		mlog(ML_ERROR, "Hardware sector size too large: %d (max=%d)\n",
-		     *sector_size, OCFS2_MAX_BLOCKSIZE);
-		status = -EINVAL;
-		goto bail;
-	}
-
-	/* Can this really happen? */
-	if (*sector_size < OCFS2_MIN_BLOCKSIZE)
-		*sector_size = OCFS2_MIN_BLOCKSIZE;
-
-	/* check block zero for old format */
-	status = ocfs2_get_sector(sb, bh, 0, *sector_size);
-	if (status < 0) {
-		mlog_errno(status);
-		goto bail;
-	}
-	hdr = (ocfs1_vol_disk_hdr *) (*bh)->b_data;
-	if (hdr->major_version == OCFS1_MAJOR_VERSION) {
-		mlog(ML_ERROR, "incompatible version: %u.%u\n",
-		     hdr->major_version, hdr->minor_version);
-		status = -EINVAL;
-	}
-	if (memcmp(hdr->signature, OCFS1_VOLUME_SIGNATURE,
-		   strlen(OCFS1_VOLUME_SIGNATURE)) == 0) {
-		mlog(ML_ERROR, "incompatible volume signature: %8s\n",
-		     hdr->signature);
-		status = -EINVAL;
-	}
-	brelse(*bh);
-	*bh = NULL;
-	if (status < 0) {
-		mlog(ML_ERROR, "This is an ocfs v1 filesystem which must be "
-		     "upgraded before mounting with ocfs v2\n");
-		goto bail;
-	}
-
-	/*
-	 * Now check at magic offset for 512, 1024, 2048, 4096
-	 * blocksizes.  4096 is the maximum blocksize because it is
-	 * the minimum clustersize.
-	 */
-	status = -EINVAL;
-	for (blksize = *sector_size;
-	     blksize <= OCFS2_MAX_BLOCKSIZE;
-	     blksize <<= 1) {
-		tmpstat = ocfs2_get_sector(sb, bh,
-					   OCFS2_SUPER_BLOCK_BLKNO,
-					   blksize);
-		if (tmpstat < 0) {
-			status = tmpstat;
-			mlog_errno(status);
-			goto bail;
-		}
-		di = (ocfs2_dinode *) (*bh)->b_data;
-		status = ocfs2_verify_volume(di, *bh, blksize);
-		if (status >= 0)
-			goto bail;
-		brelse(*bh);
-		*bh = NULL;
-		if (status != -EAGAIN)
-			break;
-	}
-
-bail:
-	return status;
-}
-
 static int ocfs2_get_sector(struct super_block *sb,
 			    struct buffer_head **bh,
 			    int block,
@@ -855,46 +993,17 @@
 	return status;
 }
 
-static int ocfs2_mount_volume(struct super_block *sb, unsigned long mount_opt)
+static int ocfs2_mount_volume(struct super_block *sb)
 {
-	int status, sector_size;
+	int status = 0;
 	int unlock_super = 0;
-	ocfs2_super *osb = NULL;
-	struct buffer_head *bh = NULL;
+	ocfs2_super *osb = OCFS2_SB(sb);
 
 	mlog_entry_void();
 
-	/* probe for superblock */
-	status = ocfs2_sb_probe(sb, &bh, &sector_size);
-	if (status < 0) {
-		mlog(ML_ERROR, "superblock probe failed!\n");
-		goto leave;
-	}
+	if (ocfs2_is_hard_readonly(osb))
+		goto out_add_proc;
 
-	osb = kcalloc(1, sizeof(ocfs2_super), GFP_KERNEL);
-	if (!osb) {
-		status = -ENOMEM;
-		mlog_errno(status);
-		goto leave;
-	}
-
-	sb->s_fs_info = osb;
-	osb->sb = sb;
-
-	osb->s_mount_opt = mount_opt;
-
-	/* Save off for ocfs2_rw_direct */
-	osb->s_sectsize_bits = blksize_bits(sector_size);
-	if (!osb->s_sectsize_bits)
-		BUG();
-
-	/* s_blocksize was set in the probe */
-	status = ocfs2_initialize_osb(osb, bh);
-	if (status < 0) {
-		mlog_errno(status);
-		goto leave;
-	}
-
 	status = ocfs2_fill_local_node_info(osb);
 	if (status < 0) {
 		mlog_errno(status);
@@ -943,10 +1052,6 @@
 		goto leave;
 	}
 
-	/* Add proc entry for this volume */
-	ocfs2_proc_add_volume(osb);
-
-	mlog(0, "ocfs2_check_volume...\n");
 	status = ocfs2_check_volume(osb);
 	if (status < 0) {
 		mlog_errno(status);
@@ -968,12 +1073,14 @@
 	if (status < 0)
 		mlog_errno(status);
 
+out_add_proc:
+	/* Add proc entry for this volume */
+	ocfs2_proc_add_volume(osb);
+
 leave:
 	if (unlock_super)
 		ocfs2_super_unlock(osb, 1);
 
-	if (bh != NULL)
-		brelse(bh);
 	mlog_exit(status);
 	return status;
 }
@@ -1092,7 +1199,9 @@
 	return 0;
 }
 
-static int ocfs2_initialize_osb(ocfs2_super *osb, struct buffer_head *bh)
+static int ocfs2_initialize_super(struct super_block *sb,
+				  struct buffer_head *bh,
+				  int sector_size)
 {
 	int status = 0;
 	int i;
@@ -1101,9 +1210,30 @@
 	struct buffer_head *bitmap_bh = NULL;
 	ocfs2_journal *journal;
 	__le32 uuid_net_key;
+	ocfs2_super *osb;
 
 	mlog_entry_void();
 
+	osb = kcalloc(1, sizeof(ocfs2_super), GFP_KERNEL);
+	if (!osb) {
+		status = -ENOMEM;
+		mlog_errno(status);
+		goto bail;
+	}
+
+	sb->s_fs_info = osb;
+	sb->s_op = &ocfs2_sops;
+	sb->s_export_op = &ocfs2_export_ops;
+	sb->s_flags |= MS_NOATIME;
+	/* this is needed to support O_LARGEFILE */
+	sb->s_maxbytes = ocfs2_max_file_offset(sb->s_blocksize_bits);
+
+	osb->sb = sb;
+	/* Save off for ocfs2_rw_direct */
+	osb->s_sectsize_bits = blksize_bits(sector_size);
+	if (!osb->s_sectsize_bits)
+		BUG();
+
 	osb->net_response_ids = 0;
 	spin_lock_init(&osb->net_response_lock);
 	INIT_LIST_HEAD(&osb->net_response_list);
@@ -1117,7 +1247,7 @@
 	INIT_LIST_HEAD(&osb->blocked_lock_list);
 	osb->blocked_lock_count = 0;
 	INIT_LIST_HEAD(&osb->vote_list);
-	spin_lock_init(&osb->s_next_gen_lock);
+	spin_lock_init(&osb->osb_lock);
 
 	osb->osb_okp_teardown_next = NULL;
 	atomic_set(&osb->osb_okp_pending, 0);
@@ -1469,7 +1599,6 @@
 	return status;
 }
 
-
 /*
  * The routine gets called from dismount or close whenever a dismount on
  * volume is requested and the osb open count becomes 1.



More information about the Ocfs2-commits mailing list