This patch speeds up loading a TGA image on my test system from 29
seconds to approximately 1 second.

I noticed that on my 1 GHz test system running from an IDE CompactFlash
drive, loading a certain TGA image in GRUB takes about 29 seconds.
This seems pretty outrageous, so after looking at the code I hacked the
TGA reader code to read the entire TGA image file into memory with a
single grub_file_read() call.  This made it much faster.  Therefore, I
concluded that the problem was that each of the one- to four-byte
reads done by the TGA reader was causing an actual device read.  It
seems that it is the file subsystem's responsibility to do file
buffering, so I implemented it in `kern/file.c'.

It turns out that when booting GRUB from IDE, if file buffering is
used, GRUB hangs right after the "GRUB" message, early in the boot
process.  So I added a flag that allows grub_main() to enable buffering
when it is safe to do so.  It always worked file from an ISO image
(generated with grub-mkrescue) in VMware and QEMU, but when I actually
installed GRUB to my CompactFlash drive and booted it, it hung after
"GRUB" if file buffering was enabled at the start.

It may be poor style to expose `grub_file_buffering_enabled' as a
global variable in file.h, but after I refactored it as a function, I
wondered whether the overhead of creating a whole new function as a
'setter' was wasteful since it would be called only once.  Please
advise if you prefer it as a function, or if there is a better solution.

Regards,
Colin
=== modified file 'ChangeLog'
--- ChangeLog	2008-07-21 09:40:01 +0000
+++ ChangeLog	2008-07-22 15:42:00 +0000
@@ -1,3 +1,17 @@
+2008-07-22  Colin D Bennett <[EMAIL PROTECTED]>
+
+	* include/grub/file.h (grub_file_buffering_enabled): New global
+	variable.
+
+	* kern/file.c (BUFFER_SIZE): New constant.
+	(grub_file_buffering_enabled): New global variable.
+	(grub_file_open): Allocate file readahead buffer.
+	(grub_file_close): Free file readahead buffer.
+	(grub_file_read): Use file readahead buffer.
+
+	* kern/main.c (grub_main): Enable file readahead buffering when it is
+	safe to do so.
+
 2008-07-21  Bean  <[EMAIL PROTECTED]>
 
 	* kern/i386/pc/startup.S (gate_a20_try_bios): Change test order for

=== modified file 'include/grub/file.h'
--- include/grub/file.h	2007-08-02 17:40:37 +0000
+++ include/grub/file.h	2008-07-22 15:23:31 +0000
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB 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 +39,19 @@
   /* The file size.  */
   grub_off_t size;
 
+  /* The file offset when `buffer' starts, or -1 to indicate that
+     buffer has no data.  */
+  grub_off_t buffer_offset;
+
+  /* The number of bytes in `buffer'.  */
+  grub_size_t buffer_length;
+
+  /* The number of bytes allocated for `buffer'.  */
+  grub_size_t buffer_capacity;
+
+  /* Readahead buffer.  */
+  char *buffer;
+
   /* Filesystem-specific data.  */
   void *data;
 
@@ -48,6 +61,11 @@
 };
 typedef struct grub_file *grub_file_t;
 
+/* Enable file readahead buffering if nonzero.  Buffering is disabled by
+   default, since it seems to cause problems during the early GRUB boot
+   process.  The grub_main function enables it.  */
+extern int grub_file_buffering_enabled;
+
 /* Get a device name from NAME.  */
 char *EXPORT_FUNC(grub_file_get_device_name) (const char *name);
 

=== modified file 'kern/file.c'
--- kern/file.c	2008-01-25 20:57:40 +0000
+++ kern/file.c	2008-07-22 15:23:31 +0000
@@ -1,7 +1,7 @@
 /* file.c - file I/O functions */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2006,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2006,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,6 +24,12 @@
 #include <grub/fs.h>
 #include <grub/device.h>
 
+/* File readahead buffer size in bytes.  */
+#define BUFFER_SIZE 1024
+
+/* Use readahead buffering for file reads?  */
+int grub_file_buffering_enabled = 0;
+
 /* Get the device part of the filename NAME. It is enclosed by parentheses.  */
 char *
 grub_file_get_device_name (const char *name)
@@ -81,6 +87,10 @@
   
   file->device = device;
   file->offset = 0;
+  file->buffer_offset = -1;
+  file->buffer_length = 0;
+  file->buffer_capacity = 0;
+  file->buffer = 0;
   file->data = 0;
   file->read_hook = 0;
     
@@ -97,6 +107,13 @@
   if ((file->fs->open) (file, file_name) != GRUB_ERR_NONE)
     goto fail;
 
+  if (grub_file_buffering_enabled)
+    {
+      file->buffer = grub_malloc (BUFFER_SIZE);
+      if (file->buffer)
+        file->buffer_capacity = BUFFER_SIZE;
+    }
+
   return file;
 
  fail:
@@ -110,6 +127,15 @@
   return 0;
 }
 
+static inline grub_size_t
+grub_min (grub_size_t a, grub_size_t b)
+{
+  if (a < b)
+    return a;
+  else
+    return b;
+}
+
 grub_ssize_t
 grub_file_read (grub_file_t file, char *buf, grub_size_t len)
 {
@@ -124,10 +150,52 @@
   
   if (len == 0)
     return 0;
-  
-  res = (file->fs->read) (file, buf, len);
-  if (res > 0)
-    file->offset += res;
+
+  if (grub_file_buffering_enabled && file->buffer)
+    {
+      res = 0;   /* The while loop uses res to accumulate the # bytes read. */
+      while (len)
+        {
+          /* If the start of the chunk requested is not in the buffer,
+             then fill the buffer starting at the requested offset.  */
+          if (file->buffer_offset == -1
+              || file->offset < file->buffer_offset
+              || file->offset >= file->buffer_offset + file->buffer_length)
+            {
+              grub_ssize_t res2;
+
+              /* Read into the buffer, and then use that data.  */
+              file->buffer_offset = -1;
+              file->buffer_length = grub_min (file->buffer_capacity,
+                                              file->size - file->offset);
+              res2 = (file->fs->read) (file, file->buffer, file->buffer_length);
+              if (res2 > 0)
+                {
+                  file->buffer_length = res2;
+                  file->buffer_offset = file->offset;
+                }
+              else
+                return res;
+            }
+
+          /* Use as much buffered data as possible.  */
+          grub_size_t bufstart = file->offset - file->buffer_offset;
+          grub_size_t buflen = len;
+          if (bufstart + len > file->buffer_length)
+            buflen = file->buffer_length - bufstart;
+          grub_memcpy (buf, file->buffer + bufstart, buflen);
+          buf += buflen;
+          res += buflen;
+          len -= buflen;
+          file->offset += buflen;
+        }
+    }
+  else
+    {
+      res = (file->fs->read) (file, buf, len);
+      if (res > 0)
+        file->offset += res;
+    }
 
   return res;
 }
@@ -135,6 +203,9 @@
 grub_err_t
 grub_file_close (grub_file_t file)
 {
+  grub_free (file->buffer);
+  file->buffer = 0;
+
   if (file->fs->close)
     (file->fs->close) (file);
 

=== modified file 'kern/main.c'
--- kern/main.c	2008-06-19 19:08:57 +0000
+++ kern/main.c	2008-07-22 15:23:31 +0000
@@ -128,6 +128,9 @@
   grub_env_export ("prefix");
   grub_set_root_dev ();
 
+  /* Enable file readahead buffering, now that it is safe to do so.  */
+  grub_file_buffering_enabled = 1;
+
   /* Load the normal mode module.  */
   grub_load_normal_mode ();
   

Attachment: signature.asc
Description: PGP signature

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to