Subject: boot code bloat with 64bit sector numbers
To: None <tech-kern@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: tech-kern
Date: 04/09/2003 17:25:35
When I try to compile some of the boot code with a 64bit daddr_t
I find that it exceeds the available space.

This can be rectified by ensuring that nothing calls the 64bit
divide routine (which is over 1k on x86) - probably larger on
other architectures.

The only place it seems to be used is in the filesystem code where
it works out which of the indirect blocks needs to be inspected.

For ufs the divide/remainder can definitely be replaced by a shift/mask
(see diff below).

Is the same true for lfs?
(I don't have an LFS filesystem to check).

Note that 'f_nindir' and 'mult' need to be daddr_t (not int)
because the 3rd level indirects on a normal filesystem are for
files with >2^33 blocks.

Maybe the ln2(NINDIR(fs)) value can be extracted directly from the
superblock?

	David

Index: ufs.c
===================================================================
RCS file: /cvsroot/src/sys/lib/libsa/ufs.c,v
retrieving revision 1.34
diff -u -p -r1.34 ufs.c
--- ufs.c	2003/04/02 19:47:25	1.34
+++ ufs.c	2003/04/09 16:11:22
@@ -112,9 +112,10 @@ struct file {
 	off_t		f_seekp;	/* seek pointer */
 	struct fs	*f_fs;		/* pointer to super-block */
 	union dinode	f_di;		/* copy of on-disk inode */
-	unsigned int	f_nindir[NIADDR];
+	daddr_t		f_nindir[NIADDR];
 					/* number of blocks mapped by
 					   indirect block at level i */
+	int		f_l2indir[NIADDR]; /* log2(f_nindir) */
 	char		*f_blk[NIADDR];	/* buffer for indirect block at
 					   level i */
 	size_t		f_blksize[NIADDR];
@@ -301,8 +302,8 @@ block_map(f, file_block, disk_block_p)
 			ind_p32 = (int32_t *)fp->f_blk[level];
 
 		if (level > 0) {
-			idx = file_block / fp->f_nindir[level - 1];
-			file_block %= fp->f_nindir[level - 1];
+			idx = file_block >> fp->f_l2indir[level - 1];
+			file_block &= fp->f_nindir[level - 1] - 1;
 		} else
 			idx = file_block;
 
@@ -530,13 +531,30 @@ ufs_open(path, f)
 	 * Calculate indirect block levels.
 	 */
 	{
-		int mult;
+		daddr_t mult;
 		int level;
+		int ln2;
 
-		mult = 1;
+		mult = NINDIR(fs);
+		/*
+		 * We note that the number of indirect blocks is always
+		 * a power of 2.  This lets us use shifts and masks instead
+		 * of divide and remainder and avoinds pulling in the
+		 * 64bit division routine into the boot code.
+		 */
+#ifdef DEBUG
+		if (mult & (mult - 1)) {
+			/* Hummm was't a power of 2 */
+			rc = EINVAL;
+			goto out;
+		}
+#endif
+		for (ln2 = 0; mult != 1; ln2++)
+			mult >>= 1;
+
 		for (level = 0; level < NIADDR; level++) {
 			mult *= NINDIR(fs);
 			fp->f_nindir[level] = mult;
+			fp->f_l2indir[level] = ln2 * (level + 1);
 		}
 	}
 

-- 
David Laight: david@l8s.co.uk