Skip to contentSkip to conversation

VFAT performance.

DISCUSSION Rogier Wolff 2 participants 2 messages Jun 5, 2003 VFAT/FAT/msdos filesystem

lore
Share & tools
lore
#0 lore raw

Hi,

For the performance freaks: We're copying some data off a VFAT32 partition. We've opened the drive. (Yes I know, you're not supposed to do that. "Don't do this at home folks!" :-)

When copying /dev/hda, we were able to achieve 11Mbyte per second: Our 100mpbs ethernet throughput.

When copying large files off /mnt, we see a performance of about 7Mb per second. We see the head seek to the FAT about twice per second. This fits in with:

4K bytes of FAT contains 1024 fat entries.
with a 4K clustersize, that would describe about 4Mbytes worth of data. So, at 7Mbytes per second we require a new FAT block twice per second.

I think that we're loosing the 4Mbytes per second of performance due to the 4 seeks per second that the drive has to perform.

The way to fix this would be to be able to assign a higher cache priority (*) to the blocks in the FAT, and to read more than just 4k per seek to the FAT.

Just something to keep in mind when fiddling with the code again....

Roger.

(*) i.e. expire them from the buffer cache less easily than normal blocks.

Signature · 5 lines
** [email protected] ** http://www.BitWizard.nl/ ** +31-15-2600998 **
*-- BitWizard writes Linux device drivers for any device you may have! --*
* The Worlds Ecosystem is a stable system. Stable systems may experience *
* excursions from the stable situation. We are currently in such an      * 
* excursion: The stable situation does not include humans. ***************
maintainer
#1 lore raw
Quoted 3 lines · Rogier Wolff

The way to fix this would be to be able to assign a higher cache priority (*) to the blocks in the FAT, and to read more than just 4k per seek to the FAT.

I tried it by attached *stupid* patch.

copying 500M data

	root@devron (x)[1014]# time dd if=/dev/hda6 bs=1M count=500 > /dev/null
	500+0 records in
	500+0 records out
	524288000 bytes transferred in 44.988916 seconds (11653715 bytes/sec)
	
	real    0m45.011s
	user    0m0.008s
	sys     0m8.723s

2.5.70-bk9

	root@devron (a)[1032]# time dd if=file bs=1M count=500 > /dev/null
	500+0 records in
	500+0 records out
	524288000 bytes transferred in 75.510951 seconds (6943205 bytes/sec)
	
	real    1m15.538s
	user    0m0.015s
	sys     0m16.493s

2.5.70-bk9+patch

	root@devron (a)[1024]# time dd if=file bs=1M count=500 > /dev/null
	500+0 records in
	500+0 records out
	524288000 bytes transferred in 52.034399 seconds (10075796 bytes/sec)
	
	real    0m52.041s
	user    0m0.006s
	sys     0m17.594s

You're right. It seems that that optimization has sufficient effect to non fragment file. Thanks.

Signature · 1 line
OGAWA Hirofumi <[email protected]>
 fs/fat/cache.c              |   22 +++++++++++++++++-----
 fs/fat/inode.c              |    4 +++-
 include/linux/msdos_fs_sb.h |    1 +
 3 files changed, 21 insertions(+), 6 deletions(-)
fs/fat/cache.c renamed from fs/fat/cache.c~fat_test +17 -5
diff -puN fs/fat/cache.c~fat_test fs/fat/cache.c
@@ -17,7 +17,7 @@ int __fat_access(struct super_block *sb,
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
 	unsigned char *p_first, *p_last;
-	int copy, first, last, next, b;
+	int copy, first, last, next, b, i;
 
 	if (sbi->fat_bits == 32) {
 		first = last = nr*4;
@@ -28,10 +28,22 @@ int __fat_access(struct super_block *sb,
 		last = first+1;
 	}
 	b = sbi->fat_start + (first >> sb->s_blocksize_bits);
-	if (!(bh = sb_bread(sb, b))) {
-		printk(KERN_ERR "FAT: bread(block %d) in"
-		       " fat_access failed\n", b);
-		return -EIO;
+	bh = NULL;
+	for (i = 0; i < 16; i++) {
+		if (sbi->fat_bh[i]->b_blocknr == b) {
+			bh = sbi->fat_bh[i];
+			break;
+		}
+	}
+	if (bh == NULL) {
+		for (i = 0; i < 16; i++) {
+			brelse(sbi->fat_bh[i]);
+			sbi->fat_bh[i] = sb_bread(sb, b + i);
+			if (sbi->fat_bh[i] == NULL)
+				return -EIO;
+			get_bh(sbi->fat_bh[i]);
+		}
+		bh = sbi->fat_bh[0];
 	}
 	if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
 		bh2 = bh;
fs/fat/inode.c renamed from fs/fat/inode.c~fat_test +3 -1
diff -puN fs/fat/inode.c~fat_test fs/fat/inode.c
@@ -158,7 +158,9 @@ void fat_clear_inode(struct inode *inode
 void fat_put_super(struct super_block *sb)
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
-
+	int i;
+	for (i = 0; i < 16; i++)
+		brelse(sbi->fat_bh[i]);
 	fat_clusters_flush(sb);
 	if (sbi->nls_disk) {
 		unload_nls(sbi->nls_disk);
include/linux/msdos_fs_sb.h renamed from include/linux/msdos_fs_sb.h~fat_test +1 -0
diff -puN include/linux/msdos_fs_sb.h~fat_test include/linux/msdos_fs_sb.h
@@ -59,6 +59,7 @@ struct msdos_sb_info {
 
 	spinlock_t cache_lock;
 	struct fat_cache cache_array[FAT_CACHE_NR], *cache;
+	struct buffer_head *fat_bh[16];
 };
 
 #endif

_