[LinuxBIOS] filo ide speedup patch!
Peter Stuge
stuge-linuxbios at cdy.org
Sat Apr 7 05:34:17 CEST 2007
Hey,
On Fri, Apr 06, 2007 at 04:17:26PM -0400, Ward Vandewege wrote:
> On Fri, Apr 06, 2007 at 08:54:59PM +0200, Peter Stuge wrote:
> > Of course you are right. Sorry about doubting that.
>
> No problem at all. I really appreciate all your efforts.
I want to make this work well. :)
> So - it boots!! I've attached a boot log.
Beautiful! :)
> I'm going to try to time things a bit now, disabling debug etc.
On Fri, Apr 06, 2007 at 04:33:41PM -0400, Ward Vandewege wrote:
> On Fri, Apr 06, 2007 at 10:29:12PM +0200, Stefan Reinauer wrote:
> > Can you guys send a single complete patch when it's done?
Yes, here's a complete patch with signoff.
> Yes, for sure. But I think Peter probably wants to speed it up some
> more :)
Two things remain:
--8<-- minicom-20070406-file-speedup-patch2.log.gz
Mounted ext2fs
ext2fs_read_one: block 43935752 offset=0 len=1 ret=0
ext2fs_read_one: block 43935752 offset=1 len=1 ret=0
ext2fs_read_one: block 43935752 offset=2 len=1 ret=0
ext2fs_read_one: block 43935752 offset=3 len=1 ret=0
ext2fs_read_one: block 43935752 offset=4 len=1 ret=0
ext2fs_read_one: block 43935752 offset=5 len=1 ret=0
-->8--
This is GRUB code reading menu.lst one byte at a time because it is
looking for a newline.
There's a simple but good enough sector cache in read_sector() that
gets used here so this is not a problem.
The mdelay(50) that I was worried about is not executed for every IDE
command, but only for commands going to a different device than the
last command, so it is also a non-issue.
I think this patch is good to go in now but please feel free to test
further.
Thanks to Ward for patient help with remote debugging! :)
//Peter
-------------- next part --------------
Support reading up to 256 IDE sectors per IDE command.
Use multi-sector reads when loading kernel from ext2fs.
Do 32-bit IDE data transfers as suggested by Tom Sylla, enabled by default.
include/fs.h:
Added prototypes for ide_readmany() and devreadmany().
defconfig:
Added IDE_32BIT_PIO, default set
Added DEBUG_EXT2, default unset
fs/blockdev.c:
Added read_manysectors() that calls ide_readmany() as many times as needed.
Added devreadmany() for reading from the open partition in fs/fsys_*.c, calls read_manysectors().
fs/fsys_ext2.c:
In ext2fs_read(), call devreadmany() to read consecutive file blocks in one go.
drivers/ide.c:
Added controller.stat that holds the status read by status bit wait functions.
Made not_bsy() and bsy() status bit wait functions store status in ctrl->stat.
Added drq() status bit wait function.
In pio_data_in(), use logic similar to OpenBIOS ide.c:ob_ide_pio_data_in() for reading multiple blocks of data.
In pio_data_in(), add a 50ms timeout for devices that set DRQ after clearing BSY.
In pio_data_in() and pio_packet(), use insl() instead of insw() if IDE_32BIT_PIO is set.
Renamed ide_read_sector_{chs,lba,lba48} to ide_read_sectors_* and added num_sectors parameter.
Added ide_readmany() that reads up to 256 consecutive sectors with one command.
Changed ide_read() to simply call ide_readmany() with 1 sector.
Multi-sector read reduced load time for a 2.5Mb kernel on Extreme III CF from 3.2s to 2.0s (38% faster) on a 600MHz EPIA-MII.
32-bit IO reduced load time for the same kernel on the same flash from 0.76s to 0.4s (48% faster) on a 1GHz EPIA-CN.
Signed-off-by: Peter Stuge <peter at stuge.se>
Index: include/fs.h
===================================================================
--- include/fs.h (revision 34)
+++ include/fs.h (working copy)
@@ -7,6 +7,7 @@
#ifdef IDE_DISK
int ide_probe(int drive);
+int ide_readmany(int drive, sector_t sector, sector_t num_sectors, void *buffer);
int ide_read(int drive, sector_t sector, void *buffer);
#endif
@@ -22,6 +23,7 @@
int devopen(const char *name, int *reopen);
int devread(unsigned long sector, unsigned long byte_offset,
unsigned long byte_len, void *buf);
+int devreadmany(void *buf, unsigned long first_sector, unsigned long num_sectors);
int file_open(const char *filename);
int file_read(void *buf, unsigned long len);
Index: defconfig
===================================================================
--- defconfig (revision 34)
+++ defconfig (working copy)
@@ -25,6 +25,10 @@
# Driver for hard disk, CompactFlash, and CD-ROM on IDE bus
IDE_DISK = 1
+# Use 32-bit PIO for increased transfer speed
+# Not supported by all IDE controllers
+IDE_32BIT_PIO = 1
+
# Add a short delay when polling status registers
# (required on some broken SATA controllers)
# NOTE: Slows down access significantly, so disable
@@ -90,4 +94,5 @@
#DEBUG_IDE = 1
#DEBUG_USB = 1
#DEBUG_ELTORITO = 1
+#DEBUG_EXT2 = 1
Index: fs/fsys_ext2fs.c
===================================================================
--- fs/fsys_ext2fs.c (revision 34)
+++ fs/fsys_ext2fs.c (working copy)
@@ -1,6 +1,9 @@
/*
- * GRUB -- GRand Unified Bootloader
+ * FILO ext2fs driver
+ *
+ * Mostly code from GRUB -- GRand Unified Bootloader
* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2007 Peter Stuge <peter at stuge.se>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,6 +25,9 @@
#include "shared.h"
#include "filesys.h"
+#define DEBUG_THIS DEBUG_EXT2
+#include <debug.h>
+
static int mapblock1, mapblock2;
/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */
@@ -386,6 +392,8 @@
[logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)];
}
+#define E2_BSIZE (EXT2_BLOCK_SIZE (SUPERBLOCK))
+
/* preconditions: all preconds of ext2fs_block_map */
int
ext2fs_read (char *buf, int len)
@@ -395,26 +403,96 @@
int map;
int ret = 0;
int size = 0;
+ int manylen, manypos, blocks, firstmapped = 0;
-#ifdef E2DEBUG
- static char hexdigit[] = "0123456789abcdef";
- unsigned char *i;
- for (i = (unsigned char *) INODE;
- i < ((unsigned char *) INODE + sizeof (struct ext2_inode));
- i++)
+ /* read one full or partial block */
+ int ext2fs_read_one(void) {
+ int res;
+ debug ("block %d offset=%d len=%d ret=%d\n", map, offset, len, ret);
+ if (map < 0)
+ return 1;
+ size = E2_BSIZE;
+ size -= offset;
+ if (size > len)
+ size = len;
+ disk_read_func = disk_read_hook;
+ res = devread (map * (E2_BSIZE/DEV_BSIZE), offset, size, buf);
+ disk_read_func = NULL;
+ if (!res)
+ return 1;
+ buf += size;
+ len -= size;
+ filepos += size;
+ ret += size;
+ return 0;
+ }
+
+ /* read many fs blocks at once */
+ int ext2fs_read_many(int first_block, int num_blocks) {
+ int first_devblock, num_devblocks, res;
+ debug ("%3d block%c %d - %d len=%d ret=%d\n", num_blocks, 1 == num_blocks ? ' ' : 's', first_block, first_block + num_blocks - 1, len, ret);
+ if (offset)
+ debug ("can't read many blocks with non-zero offset %d\n", offset);
+ if (first_block < 0 || num_blocks < 0 || offset)
+ return 1;
+ if (!num_blocks)
+ return 0;
+ first_devblock = first_block * (E2_BSIZE/DEV_BSIZE);
+ num_devblocks = num_blocks * (E2_BSIZE/DEV_BSIZE);
+ disk_read_func = disk_read_hook;
+ res = devreadmany (buf, first_devblock, num_devblocks);
+ disk_read_func = NULL;
+ if (!res)
+ return 1;
+ size = num_blocks * E2_BSIZE;
+ buf += size;
+ len -= size;
+ filepos += size;
+ ret += size;
+ return 0;
+ }
+
+ /* read many blocks at once? */
+ if (len > E2_BSIZE)
{
- printf ("%c", hexdigit[*i >> 4]);
- printf ("%c", hexdigit[*i % 16]);
- if (!((i + 1 - (unsigned char *) INODE) % 16))
- {
- printf ("\n");
- }
- else
- {
- printf (" ");
- }
+ offset = filepos & (E2_BSIZE-1);
+ if (offset)
+ {
+ logical_block = filepos >> EXT2_BLOCK_SIZE_BITS(SUPERBLOCK);
+ map = ext2fs_block_map (logical_block);
+ if (ext2fs_read_one ())
+ goto err;
+ offset = 0;
+ }
+ while (len > 0)
+ {
+ manylen = len;
+ manypos = filepos;
+ for (blocks = 0; manylen > 0; blocks++)
+ {
+ map = ext2fs_block_map (manypos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK));
+ if (blocks > 0 && map != firstmapped + blocks)
+ break;
+ if (!blocks)
+ firstmapped = map;
+ if (manylen < E2_BSIZE)
+ continue;
+ manypos += E2_BSIZE;
+ manylen -= E2_BSIZE;
+ }
+ if (ext2fs_read_many (firstmapped, blocks))
+ goto err;
+ }
+ if (len < 0)
+ {
+ debug ("discarding %d surplus bytes\n", -len);
+ ret += len;
+ filepos += len;
+ len = 0;
+ }
+ debug ("done reading many len=%d ret=%d\n", len, ret);
}
-#endif /* E2DEBUG */
+
while (len > 0)
{
/* find the (logical) block component of our location */
@@ -424,27 +502,14 @@
#ifdef E2DEBUG
printf ("map=%d\n", map);
#endif /* E2DEBUG */
- if (map < 0)
- break;
+ if (ext2fs_read_one ())
+ break;
+ }
- size = EXT2_BLOCK_SIZE (SUPERBLOCK);
- size -= offset;
- if (size > len)
- size = len;
+err:
+ if (len)
+ debug ("returning %d with len=%d ret=%d errnum=%d\n", errnum ? 0 : ret, len, ret, errnum);
- disk_read_func = disk_read_hook;
-
- devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE),
- offset, size, buf);
-
- disk_read_func = NULL;
-
- buf += size;
- len -= size;
- filepos += size;
- ret += size;
- }
-
if (errnum)
ret = 0;
Index: fs/blockdev.c
===================================================================
--- fs/blockdev.c (revision 34)
+++ fs/blockdev.c (working copy)
@@ -335,6 +335,58 @@
return 0;
}
+/* reads many sectors from opened device into memory */
+static unsigned long read_manysectors(char *buf, unsigned long first_sector, unsigned long num_sectors)
+{
+ int n;
+
+ /* If reading memory, just copy it */
+ if (dev_type == DISK_MEM) {
+ unsigned long phys = first_sector << 9;
+ char *virt;
+ //debug("mem: %#lx\n", phys);
+ virt = phys_to_virt(phys);
+ memcpy(buf, virt, num_sectors * 512);
+ return num_sectors;
+ }
+
+ switch (dev_type) {
+#ifdef IDE_DISK
+ case DISK_IDE:
+ do {
+ n = num_sectors <= 256 ? num_sectors : 256;
+ if (ide_readmany(dev_drive, first_sector, n, buf) != 0)
+ goto readerr;
+ first_sector += n;
+ num_sectors -= n;
+ buf += 512 * n;
+ } while (num_sectors);
+ break;
+#endif
+#ifdef USB_DISK
+ case DISK_USB:
+ while (num_sectors) {
+ if (usb_read(dev_drive, first_sector, buf) != 0)
+ goto readerr;
+ first_sector++;
+ num_sectors--;
+ buf += 512;
+ }
+ break;
+#endif
+ default:
+ printf("read_manysectors: device not open\n");
+ return 0;
+ }
+ return 1;
+
+readerr:
+ printf("Disk readmany error dev=%d drive=%d first_sector=%lu num_sectors=%lu\n",
+ dev_type, dev_drive, first_sector, num_sectors);
+ dev_name[0] = '\0'; /* force re-open the device next time */
+ return 0;
+}
+
int devread(unsigned long sector, unsigned long byte_offset,
unsigned long byte_len, void *buf)
{
@@ -369,3 +421,22 @@
}
return 1;
}
+
+/* reads many sectors from opened partition into memory */
+int devreadmany(void *buf,unsigned long first_sector, unsigned long num_sectors)
+{
+ char *dest = buf;
+ unsigned long last_sector = first_sector + num_sectors - 1;
+
+ if (last_sector > part_length) {
+ printf("Attempt to readmany out of device/partition\n");
+ debug("part_length=%lu last_sector=%lu\n", part_length, last_sector);
+ return 0;
+ }
+
+ debug("sectors %lu - %lu (%3lu) to %p\n", first_sector, last_sector, num_sectors, buf);
+ if (!read_manysectors (dest, part_start + first_sector, num_sectors))
+ return 0;
+
+ return 1;
+}
Index: drivers/ide.c
===================================================================
--- drivers/ide.c (revision 34)
+++ drivers/ide.c (working copy)
@@ -1,4 +1,7 @@
-/* Derived from Etherboot 5.1 */
+/*
+ * FILO IDE driver
+ * Derived from Etherboot 5.1
+ */
#include <lib.h>
#include <fs.h>
@@ -19,6 +22,7 @@
* UBL, The Universal Talkware Boot Loader
* Copyright (C) 2000 Universal Talkware Inc.
* Copyright (C) 2002 Eric Biederman
+ * Copyright (C) 2007 Peter Stuge <peter at stuge.se>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,6 +43,7 @@
struct controller {
uint16_t cmd_base;
uint16_t ctrl_base;
+ uint8_t stat;
};
struct harddisk_info {
@@ -252,10 +257,12 @@
* So if any IDE commands takes this long we know we have problems.
*/
#define IDE_TIMEOUT (32*TICKS_PER_SEC)
+#define IDE_TIMEOUT_50ms (TICKS_PER_SEC/20)
static int not_bsy(struct controller *ctrl)
{
- return !(inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY);
+ ctrl->stat = inb(IDE_REG_ALTSTATUS(ctrl));
+ return !(ctrl->stat & IDE_STATUS_BSY);
}
/* IDE drives assert BSY bit within 400 nsec when SRST is set.
@@ -264,9 +271,16 @@
static int bsy(struct controller *ctrl)
{
- return inb(IDE_REG_STATUS(ctrl)) & IDE_STATUS_BSY;
+ ctrl->stat = inb(IDE_REG_STATUS(ctrl));
+ return ctrl->stat & IDE_STATUS_BSY;
}
+static int drq(struct controller *ctrl)
+{
+ ctrl->stat = inb(IDE_REG_STATUS(ctrl));
+ return ctrl->stat & IDE_STATUS_DRQ;
+}
+
#if !BSY_SET_DURING_SPINUP
static int timeout(struct controller *ctrl)
{
@@ -364,9 +378,16 @@
static int pio_data_in(struct controller *ctrl, const struct ide_pio_command *cmd,
void *buffer, size_t bytes)
{
+ int drive;
+ size_t count;
unsigned int status;
- /* FIXME handle commands with multiple blocks */
+ for(drive = 0; drive < IDE_MAX_DRIVES; drive++)
+ if(ctrl == harddisk_info[drive].ctrl)
+ break;
+ if(IDE_MAX_DRIVES == drive)
+ return -1;
+
/* Wait until the busy bit is clear */
if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
return -1;
@@ -375,20 +396,40 @@
/* How do I tell if INTRQ is asserted? */
pio_set_registers(ctrl, cmd);
ndelay(400);
- if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0) {
- return -1;
- }
- status = inb(IDE_REG_STATUS(ctrl));
- if (!(status & IDE_STATUS_DRQ)) {
- print_status(ctrl);
- return -1;
- }
- insw(IDE_REG_DATA(ctrl), buffer, bytes/2);
- status = inb(IDE_REG_STATUS(ctrl));
- if (status & IDE_STATUS_DRQ) {
- print_status(ctrl);
- return -1;
- }
+
+ /* multi block capable logic inspired by OpenBIOS ide.c
+ * ob_ide_pio_data_in() */
+ do {
+ count = bytes;
+ if (count > harddisk_info[drive].hw_sector_size)
+ count = harddisk_info[drive].hw_sector_size;
+ if (await_ide(not_bsy, ctrl, currticks() + IDE_TIMEOUT) < 0)
+ return -1;
+ if (ctrl->stat & (IDE_STATUS_WFT | IDE_STATUS_ERR)) {
+#ifdef IDE_32BIT_PIO
+ debug("IDE error before insl\n");
+#else
+ debug("IDE error before insw\n");
+#endif
+ print_status(ctrl);
+ return -1;
+ }
+ if (!(ctrl->stat & IDE_STATUS_DRQ))
+ if (await_ide(drq, ctrl, currticks() + IDE_TIMEOUT_50ms) < 0) {
+ debug("No error after BSY=0 but DRQ=0 even after 50ms\n");
+ print_status(ctrl);
+ return -1;
+ }
+#ifdef IDE_32BIT_PIO
+ insl(IDE_REG_DATA(ctrl), buffer, count/4);
+#else
+ insw(IDE_REG_DATA(ctrl), buffer, count/2);
+#endif
+ buffer += count;
+ bytes -= count;
+ ndelay(400);
+ } while(bytes);
+
return 0;
}
@@ -445,19 +486,27 @@
return -1;
}
+#ifdef IDE_32BIT_PIO
+ insl(IDE_REG_DATA(info->ctrl), buffer, buffer_len/4);
+#else
insw(IDE_REG_DATA(info->ctrl), buffer, buffer_len/2);
-
+#endif
status = inb(IDE_REG_STATUS(info->ctrl));
if (status & IDE_STATUS_DRQ) {
+#ifdef IDE_32BIT_PIO
+ debug("drq after insl\n");
+#else
debug("drq after insw\n");
+#endif
print_status(info->ctrl);
return -1;
}
return 0;
}
-static inline int ide_read_sector_chs(
- struct harddisk_info *info, void *buffer, unsigned long sector)
+static inline int ide_read_sectors_chs(
+ struct harddisk_info *info, void *buffer, unsigned long sector,
+ unsigned long num_sectors)
{
struct ide_pio_command cmd;
unsigned int track;
@@ -465,8 +514,11 @@
unsigned int cylinder;
memset(&cmd, 0, sizeof(cmd));
- cmd.sector_count = 1;
+ if (num_sectors > 256)
+ return -1;
+ cmd.sector_count = 256 == num_sectors ? 0 : num_sectors;
+
//debug("ide_read_sector_chs: sector= %ld.\n",sector);
track = sector / info->sectors_per_track;
@@ -481,16 +533,20 @@
info->slave |
IDE_DH_CHS;
cmd.command = IDE_CMD_READ_SECTORS;
- return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
+ return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors);
}
-static inline int ide_read_sector_lba(
- struct harddisk_info *info, void *buffer, unsigned long sector)
+static inline int ide_read_sectors_lba(
+ struct harddisk_info *info, void *buffer, unsigned long sector,
+ unsigned long num_sectors)
{
struct ide_pio_command cmd;
memset(&cmd, 0, sizeof(cmd));
- cmd.sector_count = 1;
+ if (num_sectors > 256)
+ return -1;
+ cmd.sector_count = 256 == num_sectors ? 0 : num_sectors;
+
cmd.lba_low = sector & 0xff;
cmd.lba_mid = (sector >> 8) & 0xff;
cmd.lba_high = (sector >> 16) & 0xff;
@@ -500,17 +556,20 @@
IDE_DH_LBA;
cmd.command = IDE_CMD_READ_SECTORS;
//debug("%s: sector= %ld, device command= 0x%x.\n",__FUNCTION__,(unsigned long) sector, cmd.device);
- return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
+ return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors);
}
-static inline int ide_read_sector_lba48(
- struct harddisk_info *info, void *buffer, sector_t sector)
+static inline int ide_read_sectors_lba48(
+ struct harddisk_info *info, void *buffer, sector_t sector, sector_t num_sectors)
{
struct ide_pio_command cmd;
memset(&cmd, 0, sizeof(cmd));
//debug("ide_read_sector_lba48: sector= %ld.\n",(unsigned long) sector);
- cmd.sector_count = 1;
+ if (num_sectors > 256)
+ return -1;
+ cmd.sector_count = 256 == num_sectors ? 0 : num_sectors;
+
cmd.lba_low = sector & 0xff;
cmd.lba_mid = (sector >> 8) & 0xff;
cmd.lba_high = (sector >> 16) & 0xff;
@@ -519,7 +578,7 @@
cmd.lba_high2 = (sector >> 40) & 0xff;
cmd.device = info->slave | IDE_DH_LBA;
cmd.command = IDE_CMD_READ_SECTORS_EXT;
- return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE);
+ return pio_data_in(info->ctrl, &cmd, buffer, IDE_SECTOR_SIZE * num_sectors);
}
static inline int ide_read_sector_packet(
@@ -567,27 +626,39 @@
return 0;
}
-int ide_read(int drive, sector_t sector, void *buffer)
+int ide_readmany(int drive, sector_t first_sector, sector_t num_sectors, void *buffer)
{
struct harddisk_info *info = &harddisk_info[drive];
int result;
+ sector_t i, last_sector = first_sector + num_sectors - 1;
- //debug("drive=%d, sector=%ld\n",drive,(unsigned long) sector);
- /* Report the buffer is empty */
- if (sector > info->sectors) {
+ if (0 == num_sectors) {
+ debug("0 sectors requested, nothing read\n");
+ return 0;
+ }
+ if (1 == num_sectors)
+ debug("sector %Lu to %p\n", first_sector, buffer);
+ else
+ debug("sectors %Lu - %Lu (%3Lu) to %p\n", first_sector, last_sector, num_sectors, buffer);
+ if (last_sector > info->sectors) {
+ debug("attempt to read past end of device");
return -1;
}
if (info->address_mode == ADDRESS_MODE_CHS) {
- result = ide_read_sector_chs(info, buffer, sector);
+ result = ide_read_sectors_chs(info, buffer, first_sector, num_sectors);
}
else if (info->address_mode == ADDRESS_MODE_LBA) {
- result = ide_read_sector_lba(info, buffer, sector);
+ result = ide_read_sectors_lba(info, buffer, first_sector, num_sectors);
}
else if (info->address_mode == ADDRESS_MODE_LBA48) {
- result = ide_read_sector_lba48(info, buffer, sector);
+ result = ide_read_sectors_lba48(info, buffer, first_sector, num_sectors);
}
else if (info->address_mode == ADDRESS_MODE_PACKET) {
- result = ide_read_sector_packet(info, buffer, sector);
+ for(i = 0; i < num_sectors; i++) {
+ result = ide_read_sector_packet(info, buffer, first_sector + i);
+ if (-1 == result)
+ return result;
+ }
}
else {
result = -1;
@@ -595,6 +666,11 @@
return result;
}
+int ide_read(int drive, sector_t sector, void *buffer)
+{
+ return ide_readmany(drive, sector, 1, buffer);
+}
+
static int init_drive(struct harddisk_info *info, struct controller *ctrl,
int slave, int drive, unsigned char *buffer, int ident_command)
{
More information about the coreboot
mailing list