patch-2.1.93 linux/fs/ext2/balloc.c

Next file: linux/fs/ext2/dir.c
Previous file: linux/fs/exec.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.92/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
@@ -38,27 +38,33 @@
 
 #define in_range(b, first, len)		((b) >= (first) && (b) <= (first) + (len) - 1)
 
-static struct ext2_group_desc * get_group_desc (struct super_block * sb,
-						unsigned int block_group,
-						struct buffer_head ** bh)
+struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+					     unsigned int block_group,
+					     struct buffer_head ** bh)
 {
 	unsigned long group_desc;
 	unsigned long desc;
 	struct ext2_group_desc * gdp;
 
-	if (block_group >= sb->u.ext2_sb.s_groups_count)
-		ext2_panic (sb, "get_group_desc",
+	if (block_group >= sb->u.ext2_sb.s_groups_count) {
+		ext2_error (sb, "ext2_get_group_desc",
 			    "block_group >= groups_count - "
 			    "block_group = %d, groups_count = %lu",
 			    block_group, sb->u.ext2_sb.s_groups_count);
 
+		return NULL;
+	}
+	
 	group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
 	desc = block_group % EXT2_DESC_PER_BLOCK(sb);
-	if (!sb->u.ext2_sb.s_group_desc[group_desc])
-		ext2_panic (sb, "get_group_desc",
+	if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+		ext2_error (sb, "ext2_get_group_desc",
 			    "Group descriptor not loaded - "
 			    "block_group = %d, group_desc = %lu, desc = %lu",
 			     block_group, group_desc, desc);
+		return NULL;
+	}
+	
 	gdp = (struct ext2_group_desc *) 
 	      sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
 	if (bh)
@@ -66,22 +72,40 @@
 	return gdp + desc;
 }
 
-static void read_block_bitmap (struct super_block * sb,
+/*
+ * Read the bitmap for a given block_group, reading into the specified 
+ * slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
 			       unsigned int block_group,
 			       unsigned long bitmap_nr)
 {
 	struct ext2_group_desc * gdp;
-	struct buffer_head * bh;
+	struct buffer_head * bh = NULL;
+	int retval = 0;
 	
-	gdp = get_group_desc (sb, block_group, NULL);
+	gdp = ext2_get_group_desc (sb, block_group, NULL);
+	if (!gdp)
+		goto error_out;
 	bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
-	if (!bh)
-		ext2_panic (sb, "read_block_bitmap",
+	if (!bh) {
+		ext2_error (sb, "read_block_bitmap",
 			    "Cannot read block bitmap - "
 			    "block_group = %d, block_bitmap = %lu",
-			    block_group, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
+			    block_group, (unsigned long) gdp->bg_block_bitmap);
+		retval = -EIO;
+	}
+	/*
+	 * On IO error, just leave a zero in the superblock's block pointer for
+	 * this group.  The IO will be retried next time.
+	 */
+error_out:
 	sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
 	sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
+	return retval;
 }
 
 /*
@@ -94,11 +118,13 @@
  * 1/ There is one cache per mounted file system.
  * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
  *    this function reads the bitmap without maintaining a LRU cache.
+ * 
+ * Return the slot used to store the bitmap, or a -ve error code.
  */
 static int load__block_bitmap (struct super_block * sb,
 			       unsigned int block_group)
 {
-	int i, j;
+	int i, j, retval = 0;
 	unsigned long block_bitmap_number;
 	struct buffer_head * block_bitmap;
 
@@ -110,16 +136,16 @@
 
 	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
 		if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
-			if (sb->u.ext2_sb.s_block_bitmap_number[block_group] !=
+			if (sb->u.ext2_sb.s_block_bitmap_number[block_group] ==
 			    block_group)
-				ext2_panic (sb, "load_block_bitmap",
-					    "block_group != block_bitmap_number");
-			else
 				return block_group;
-		} else {
-			read_block_bitmap (sb, block_group, block_group);
-			return block_group;
+			ext2_error (sb, "load_block_bitmap",
+				    "block_group != block_bitmap_number");
 		}
+		retval = read_block_bitmap (sb, block_group, block_group);
+		if (retval < 0)
+			return retval;
+		return block_group;
 	}
 
 	for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
@@ -137,6 +163,14 @@
 		}
 		sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
 		sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
+
+		/*
+		 * There's still one special case here --- if block_bitmap == 0
+		 * then our last attempt to read the bitmap failed and we have
+		 * just ended up caching that failure.  Try again to read it.
+		 */
+		if (!block_bitmap)
+			retval = read_block_bitmap (sb, block_group, 0);
 	} else {
 		if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
 			sb->u.ext2_sb.s_loaded_block_bitmaps++;
@@ -148,24 +182,71 @@
 			sb->u.ext2_sb.s_block_bitmap[j] =
 				sb->u.ext2_sb.s_block_bitmap[j - 1];
 		}
-		read_block_bitmap (sb, block_group, 0);
+		retval = read_block_bitmap (sb, block_group, 0);
 	}
-	return 0;
+	return retval;
 }
 
+/*
+ * Load the block bitmap for a given block group.  First of all do a couple
+ * of fast lookups for common cases and then pass the request onto the guts
+ * of the bitmap loader.
+ *
+ * Return the slot number of the group in the superblock bitmap cache's on
+ * success, or a -ve error code.
+ *
+ * There is still one inconsistancy here --- if the number of groups in this
+ * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of 
+ * differentiating between a group for which we have never performed a bitmap
+ * IO request, and a group for which the last bitmap read request failed.
+ */
 static inline int load_block_bitmap (struct super_block * sb,
 				     unsigned int block_group)
 {
-	if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
-	    sb->u.ext2_sb.s_block_bitmap_number[0] == block_group)
-		return 0;
+	int slot;
 	
-	if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && 
-	    sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
-	    sb->u.ext2_sb.s_block_bitmap[block_group]) 
-		return block_group;
+	/*
+	 * Do the lookup for the slot.  First of all, check if we're asking
+	 * for the same slot as last time, and did we succeed that last time?
+	 */
+	if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
+	    sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
+	    sb->u.ext2_sb.s_block_bitmap[block_group]) {
+		slot = 0;
+	}
+	/*
+	 * Or can we do a fast lookup based on a loaded group on a filesystem
+	 * small enough to be mapped directly into the superblock?
+	 */
+	else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED && 
+		 sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
+		 sb->u.ext2_sb.s_block_bitmap[block_group]) {
+		slot = block_group;
+	}
+	/*
+	 * If not, then do a full lookup for this block group.
+	 */
+	else {
+		slot = load__block_bitmap (sb, block_group);
+	}
 
-	return load__block_bitmap (sb, block_group);
+	/*
+	 * <0 means we just got an error
+	 */
+	if (slot < 0)
+		return slot;
+	
+	/*
+	 * If it's a valid slot, we may still have cached a previous IO error,
+	 * in which case the bh in the superblock cache will be zero.
+	 */
+	if (!sb->u.ext2_sb.s_block_bitmap[slot])
+		return -EIO;
+	
+	/*
+	 * Must have been read in OK to get this far.
+	 */
+	return slot;
 }
 
 void ext2_free_blocks (const struct inode * inode, unsigned long block,
@@ -194,8 +275,7 @@
 		ext2_error (sb, "ext2_free_blocks",
 			    "Freeing blocks not in datazone - "
 			    "block = %lu, count = %lu", block, count);
-		unlock_super (sb);
-		return;
+		goto error_return;
 	}
 
 	ext2_debug ("freeing block %lu\n", block);
@@ -215,8 +295,13 @@
 		count -= overflow;
 	}
 	bitmap_nr = load_block_bitmap (sb, block_group);
+	if (bitmap_nr < 0)
+		goto error_return;
+	
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
-	gdp = get_group_desc (sb, block_group, &bh2);
+	gdp = ext2_get_group_desc (sb, block_group, &bh2);
+	if (!gdp)
+		goto error_return;
 
 	if (test_opt (sb, CHECK_STRICT) &&
 	    (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
@@ -259,6 +344,7 @@
 		goto do_more;
 	}
 	sb->s_dirt = 1;
+error_return:
 	unlock_super (sb);
 	return;
 }
@@ -312,7 +398,10 @@
 	    goal >= le32_to_cpu(es->s_blocks_count))
 		goal = le32_to_cpu(es->s_first_data_block);
 	i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb);
-	gdp = get_group_desc (sb, i, &bh2);
+	gdp = ext2_get_group_desc (sb, i, &bh2);
+	if (!gdp)
+		goto io_error;
+
 	if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
 		j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb));
 #ifdef EXT2FS_DEBUG
@@ -320,6 +409,9 @@
 			goal_attempts++;
 #endif
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			goto io_error;
+		
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
 		ext2_debug ("goal is at %d:%d.\n", i, j);
@@ -384,7 +476,12 @@
 		i++;
 		if (i >= sb->u.ext2_sb.s_groups_count)
 			i = 0;
-		gdp = get_group_desc (sb, i, &bh2);
+		gdp = ext2_get_group_desc (sb, i, &bh2);
+		if (!gdp) {
+			*err = -EIO;
+			unlock_super (sb);
+			return 0;
+		}
 		if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
 			break;
 	}
@@ -393,6 +490,9 @@
 		return 0;
 	}
 	bitmap_nr = load_block_bitmap (sb, i);
+	if (bitmap_nr < 0)
+		goto io_error;
+	
 	bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 	r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
 	j = (r - bh->b_data) << 3;
@@ -455,10 +555,16 @@
 	 */
 #ifdef EXT2_PREALLOCATE
 	if (prealloc_block) {
+		int	prealloc_goal;
+
+		prealloc_goal = es->s_prealloc_blocks ?
+			es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS;
+
 		*prealloc_count = 0;
 		*prealloc_block = tmp + 1;
 		for (k = 1;
-		     k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
+		     k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
+		     k++) {
 			if (sb->dq_op)
 				if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, sb->s_blocksize)))
 					break;
@@ -516,6 +622,12 @@
 	unlock_super (sb);
 	*err = 0;
 	return j;
+	
+io_error:
+	*err = -EIO;
+	unlock_super (sb);
+	return 0;
+	
 }
 
 unsigned long ext2_count_free_blocks (struct super_block * sb)
@@ -533,9 +645,14 @@
 	bitmap_count = 0;
 	gdp = NULL;
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
-		gdp = get_group_desc (sb, i, NULL);
+		gdp = ext2_get_group_desc (sb, i, NULL);
+		if (!gdp)
+			continue;
 		desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+		
 		x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
 				     sb->s_blocksize);
 		printk ("group %d: stored = %d, counted = %lu\n",
@@ -590,12 +707,17 @@
 	desc_blocks = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
 		      EXT2_DESC_PER_BLOCK(sb);
 	for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
-		gdp = get_group_desc (sb, i, NULL);
+		gdp = ext2_get_group_desc (sb, i, NULL);
+		if (!gdp)
+			continue;
 		desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
 		bitmap_nr = load_block_bitmap (sb, i);
+		if (bitmap_nr < 0)
+			continue;
+		
 		bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
 
-		if (!(sb->u.ext2_sb.s_feature_ro_compat &
+		if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
 		     EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
 		    (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) {
 			if (!ext2_test_bit (0, bh->b_data))

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov