From: Tony Breeds Date: Fri, 21 Sep 2012 10:06:39 +0000 (+1000) Subject: fs_of: Add an ino_seek() method. X-Git-Url: http://git.ozlabs.org/?p=yaboot.git;a=commitdiff_plain;h=57a24d7b07ceef22166ac508dd478926e9172f4b fs_of: Add an ino_seek() method. 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 --- diff --git a/include/file.h b/include/file.h index e2ccc13..e1c1d09 100644 --- a/include/file.h +++ b/include/file.h @@ -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; diff --git a/include/fs.h b/include/fs.h index 7f7847c..6800c05 100644 --- a/include/fs.h +++ b/include/fs.h @@ -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; diff --git a/second/fs_ext2.c b/second/fs_ext2.c index a85958f..41a3c79 100644 --- a/second/fs_ext2.c +++ b/second/fs_ext2.c @@ -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) diff --git a/second/fs_of.c b/second/fs_of.c index 3a8819c..36bd354 100644 --- a/second/fs_of.c +++ b/second/fs_of.c @@ -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; } /* diff --git a/second/yaboot.c b/second/yaboot.c index 9b66ab4..469b1af 100644 --- a/second/yaboot.c +++ b/second/yaboot.c @@ -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(¶ms.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) {