Disable cache for huge files (> 1MB) When reading a file GRUB2 always reads one block of the maximum cache size (4KB by default), then it copies the data into the cache and then it reads the next block. For each block "Int 13h" is used to read the data from the disk, but since GRUB2 runs as 32bit program in protected mode it needs to switch into real mode, call the interrupt and switch back to protected mode for each block. But somehow this back-and-forth-switching takes a lot of time on some PCs... So I've written this patch that disables the cache for huge files (like kernel and initrd, which don't actually need to be cached anyway) and reads directly with full block size (512KB). As a result significantly fewer total blocks need to be read and thus the back-and-forth between real and protected mode is done more than a hundred times less often. Tests show that with this patch loading the kernel and initrd is about as fast as with isolinux (which runs completly in 16bit real mode and thus the mode-switching is not needed) on PCs that were way slower without this patch. Author: Andreas Loibl diff -Naur grub/include/grub/disk.h grub/include/grub/disk.h --- grub/include/grub/disk.h 2011-03-08 23:30:19.033192285 +0100 +++ grub/include/grub/disk.h 2011-03-08 23:29:30.153195171 +0100 @@ -138,8 +138,8 @@ #define GRUB_DISK_CACHE_NUM 1021 /* The size of a disk cache in sector units. */ -#define GRUB_DISK_CACHE_SIZE 8 -#define GRUB_DISK_CACHE_BITS 3 +#define GRUB_DISK_CACHE_SIZE 32 /*8*/ +#define GRUB_DISK_CACHE_BITS 5 /*3*/ /* This is called from the memory manager. */ void grub_disk_cache_invalidate_all (void); diff -Naur grub/grub-core/kern/disk.c grub/grub-core/kern/disk.c --- grub/grub-core/kern/disk.c 2011-03-08 23:29:45.113192284 +0100 +++ grub/grub-core/kern/disk.c 2011-03-08 23:34:35.283192206 +0100 @@ -408,6 +408,17 @@ { char *tmp_buf; unsigned real_offset; + grub_size_t read_size = GRUB_DISK_CACHE_SIZE; + grub_size_t read_bits = GRUB_DISK_CACHE_BITS; + + char use_cache = 1; + /* disable cache for huge files (>1MB) */ + if (size > 0x100000) + { + use_cache = 0; + read_size = GRUB_DISK_SECTOR_SIZE; + read_bits = GRUB_DISK_SECTOR_BITS; + } /* First of all, check if the region is within the disk. */ if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) @@ -422,14 +433,14 @@ real_offset = offset; /* Allocate a temporary buffer. */ - tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); + tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << read_bits); if (! tmp_buf) return grub_errno; /* Until SIZE is zero... */ while (size) { - char *data; + char *data = NULL; grub_disk_addr_t start_sector; grub_size_t len; grub_size_t pos; @@ -437,13 +448,14 @@ /* For reading bulk data. */ start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1); pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS; - len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS) + len = ((GRUB_DISK_SECTOR_SIZE << read_bits) - pos - real_offset); if (len > size) len = size; /* Fetch the cache. */ - data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector); + if (use_cache) + data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector); if (data) { /* Just copy it! */ @@ -453,9 +465,9 @@ else { /* Otherwise read data from the disk actually. */ - if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors + if (start_sector + read_size > disk->total_sectors || (disk->dev->read) (disk, start_sector, - GRUB_DISK_CACHE_SIZE, tmp_buf) + read_size, tmp_buf) != GRUB_ERR_NONE) { /* Uggh... Failed. Instead, just read necessary data. */ @@ -508,7 +520,8 @@ /* Copy it and store it in the disk cache. */ grub_memcpy (buf, tmp_buf + pos + real_offset, len); - grub_disk_cache_store (disk->dev->id, disk->id, + if (use_cache) + grub_disk_cache_store (disk->dev->id, disk->id, start_sector, tmp_buf); } @@ -534,7 +547,7 @@ } } - sector = start_sector + GRUB_DISK_CACHE_SIZE; + sector = start_sector + read_size; buf = (char *) buf + len; size -= len; real_offset = 0;