fs_of: Add an ino_seek() method.
authorTony Breeds <tony@bakeyournoodle.com>
Fri, 21 Sep 2012 10:06:39 +0000 (20:06 +1000)
committerTony Breeds <tony@bakeyournoodle.com>
Sat, 11 May 2013 05:29:47 +0000 (15:29 +1000)
When booting from DVD we fallback to using fs_of functions to read the
from the DVD.  In the case where we need to boot a kernel and initrd
we may run into a situation where we start loading the initrd below
'real-base' and as we grow the buffer 1MB at a time we eventually hit
real-base and get a claim fail.

If we could detect in advance the size of the initrd (as we can for ext*
and netboot) we wouldn't have tis issue as we'd claim the buffer in one
chunk.

So implement an ino_size() method for fs_of.

There is a small complication.  OF doesn't correctly support 'seek' so
we cannot just seek to the end of the file and get the size, then seek
to the begining.  So we need to read the file to determine it's size.

Ideally we'd then seek to the begining of the file ready to read it
again. ... See previous comment about seek :(

So we have to close and re-open the file to get the fp back to 0.

In order to make this a little less disturbing, move the ino_size()
checks so that if the re-open() fails (it /really/ shouldn't) we can at
least handle the failure somewhat gracefully.

Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
include/file.h
include/fs.h
second/fs_ext2.c
second/fs_of.c
second/yaboot.c

index e2ccc13d2265b8d4c665c0a4b7bc45072852e956..e1c1d095371fba2c20ec1a68b87da48c604636d9 100644 (file)
@@ -69,6 +69,7 @@ struct boot_file_t {
        ino_t           inode;
        __u64           pos;
        unsigned char*  buffer;
+       char*           devspec_cache;
        __u64           len;
 //     unsigned int    dev_blk_size;
 //     unsigned int    part_start;
index 7f7847cde4ac91be3c2252c92d3edb625551e182..6800c05c04ac87a5cb8a2dfa71fa01d79dd89186 100644 (file)
@@ -45,7 +45,7 @@ struct fs_t {
 
        int (*close)(   struct boot_file_t*     file);
 
-       unsigned int (*ino_size)(struct boot_file_t *file);
+       int (*ino_size)(struct boot_file_t *file, unsigned int *size);
 };
 
 extern const struct fs_t *fs_of;
index a85958f0323f54e4d73c00ce158d8bc70208f8d1..41a3c79cce2096ac513e9135ccb677b072a67f4f 100644 (file)
@@ -54,7 +54,7 @@ static int ext2_read( struct boot_file_t*     file,
 static int ext2_seek(  struct boot_file_t*     file,
                        unsigned int            newpos);
 static int ext2_close( struct boot_file_t*     file);
-static unsigned int ext2_ino_size(struct boot_file_t *file);
+static int ext2_ino_size(struct boot_file_t *file, unsigned int *size);
 
 struct fs_t ext2_filesystem =
 {
@@ -567,14 +567,15 @@ ext2_close(       struct boot_file_t*     file)
      return 0;
 }
 
-static unsigned int ext2_ino_size(struct boot_file_t *file)
+static int ext2_ino_size(struct boot_file_t *file, unsigned int *size)
 {
     struct ext2_inode ei;
 
     if (ext2fs_read_inode(fs, file->inode, &ei))
-       return 0;
+       return FILE_IOERR;
 
-    return ei.i_size;
+    *size = ei.i_size;
+    return FILE_ERR_OK;
 }
 
 static errcode_t linux_open (const char *name, int flags, io_channel * channel)
index 3a8819c4b83f82c9342848712edb7fd5f311ec49..36bd354d533913aa61e022c7976725575c9fd97e 100644 (file)
@@ -51,13 +51,14 @@ static int of_open(struct boot_file_t* file,
 static int of_read(struct boot_file_t* file, unsigned int size, void* buffer);
 static int of_seek(struct boot_file_t* file, unsigned int newpos);
 static int of_close(struct boot_file_t* file);
+static int of_ino_size(struct boot_file_t* file, unsigned int *size);
 
 
 static int of_net_open(struct boot_file_t* file,
                       struct partition_t* part, struct boot_fspec_t* fspec);
 static int of_net_read(struct boot_file_t* file, unsigned int size, void* buffer);
 static int of_net_seek(struct boot_file_t* file, unsigned int newpos);
-static unsigned int of_net_ino_size(struct boot_file_t* file);
+static int of_net_ino_size(struct boot_file_t* file, unsigned int *size);
 
 
 struct fs_t of_filesystem =
@@ -66,7 +67,8 @@ struct fs_t of_filesystem =
      of_open,
      of_read,
      of_seek,
-     of_close
+     of_close,
+     of_ino_size,
 };
 
 struct fs_t of_net_filesystem =
@@ -122,6 +124,8 @@ of_open(struct boot_file_t* file,
 
      file->pos = 0;
      file->buffer = NULL;
+     file->devspec_cache = strdup(buffer);
+
      if ((file->of_device == PROM_INVALID_HANDLE) || (file->of_device == 0))
      {
          DEBUG_LEAVE(FILE_ERR_BAD_FSYS);
@@ -290,10 +294,45 @@ of_close(struct boot_file_t* file)
      return 0;
 }
 
-static unsigned int
-of_net_ino_size(struct boot_file_t* file)
+static int
+of_ino_size(struct boot_file_t* file, unsigned int *size)
+{
+     static char buffer[1<<20];
+     int read_count = 0;
+
+     if (file->len == 0) {
+          DEBUG_F("Estimating size of: %p\n", file->of_device);
+          while (prom_read(file->of_device, (void *)&buffer, sizeof(buffer))
+                 != 0) {
+               read_count++;
+               DEBUG_F("read_count == %d\n", read_count);
+          }
+          file->pos = 0;
+          file->len = read_count * sizeof(buffer);
+          /* sigh:
+           * prom_seek(file->of_device, file->pos);
+           * doen't work */
+          prom_close(file->of_device);
+          DEBUG_F("Re-Opening: \"%s\"\n", file->devspec_cache);
+          file->of_device = prom_open(file->devspec_cache);
+          DEBUG_F("file->of_device = %p\n", file->of_device);
+          if (file->of_device == 0) {
+               file->len = 0;
+               return FILE_IOERR;
+          }
+     }
+
+     DEBUG_F("Estimated size is: %Lu(%d)\n", (unsigned long long)file->len,
+             read_count);
+     *size = file->len;
+     return FILE_ERR_OK;
+}
+
+static int
+of_net_ino_size(struct boot_file_t* file, unsigned int *size)
 {
-       return file->len;
+       *size = file->len;
+       return FILE_ERR_OK;
 }
 
 /*
index 9b66ab44e1be0ee82b88e386a5d0358428766e73..469b1afa0adddbb292dd0dbdc1befaee6927824a 100644 (file)
@@ -76,6 +76,8 @@
 #define KERNEL_LINK_ADDR_PPC32 0xC0000000UL
 #define KERNEL_LINK_ADDR_PPC64 0xC000000000000000ULL
 
+#define INITRD_CHUNKSIZE 0x100000
+
 typedef struct {
      union {
          Elf32_Ehdr  elf32hdr;
@@ -1023,6 +1025,7 @@ yaboot_text_ui(void)
      loadinfo_t          loadinfo;
      void                *initrd_more,*initrd_want;
      unsigned long       initrd_read;
+     unsigned int        len = INITRD_CHUNKSIZE;
 
      loadinfo.load_loc = 0;
 
@@ -1112,19 +1115,18 @@ yaboot_text_ui(void)
               }
               prom_printf("Loading ramdisk...\n");
               result = open_file(&params.rd, &file);
+              if (result == FILE_ERR_OK && file.fs->ino_size) {
+                   result = file.fs->ino_size(&file, &len);
+               }
               if (result != FILE_ERR_OK) {
                    prom_printf("%s:%d,", params.rd.dev, params.rd.part);
                    prom_perror(result, params.rd.file);
               }
               else {
-#define INITRD_CHUNKSIZE 0x100000
-                   unsigned int len = INITRD_CHUNKSIZE;
-
-                   /* We add a bit to the actual size so the loop below doesn't think
-                    * there is more to load.
+                   /* We add a bit to the actual size so the loop below
+                    * doesn't think there is more to load.
                     */
-                   if (file.fs->ino_size && file.fs->ino_size(&file) > 0)
-                        len = file.fs->ino_size(&file) + 0x1000;
+                    len += 0x1000;
 
                    initrd_base = prom_claim_chunk(loadinfo.base+loadinfo.memsize, len, 0);
                    if (initrd_base == (void *)-1) {