From 6ff92c14b7a65f64dea4e0d4e8d19b2dcf3f57b3 Mon Sep 17 00:00:00 2001 From: msmith Date: Tue, 5 Sep 2000 09:52:50 +0000 Subject: Implement readahead buffering for non-raw files. This drastically improves the efficiency of byte-by-byte read operations on filesystems not already supported by the block cache (especially NFS). This should be a welcome change for users booting via PXE, as the loader now reads its startup files almost instantly, instead of taking tens of seconds. --- lib/libstand/close.c | 50 ++++++++++++++++++----------------- lib/libstand/lseek.c | 65 ++++++++++++++++++++++++++------------------- lib/libstand/open.c | 9 +++++++ lib/libstand/read.c | 75 +++++++++++++++++++++++++++++++++++++--------------- lib/libstand/stand.h | 6 ++++- 5 files changed, 132 insertions(+), 73 deletions(-) (limited to 'lib/libstand') diff --git a/lib/libstand/close.c b/lib/libstand/close.c index aca6a65..bf81094 100644 --- a/lib/libstand/close.c +++ b/lib/libstand/close.c @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* $NetBSD: close.c,v 1.7 1997/01/22 00:38:09 cgd Exp $ */ /*- @@ -67,30 +68,31 @@ #include "stand.h" int -close(fd) - int fd; +close(int fd) { - register struct open_file *f = &files[fd]; - int err1 = 0, err2 = 0; + struct open_file *f = &files[fd]; + int err1 = 0, err2 = 0; - if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) { - errno = EBADF; - return (-1); - } - if (!(f->f_flags & F_RAW) && f->f_ops) - err1 = (f->f_ops->fo_close)(f); - if (!(f->f_flags & F_NODEV) && f->f_dev) - err2 = (f->f_dev->dv_close)(f); - if (f->f_devdata != NULL) - devclose(f); - f->f_flags = 0; - if (err1) { - errno = err1; - return (-1); - } - if (err2) { - errno = err2; - return (-1); - } - return (0); + if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) { + errno = EBADF; + return (-1); + } + if (f->f_rabuf != NULL) + free(f->f_rabuf); + if (!(f->f_flags & F_RAW) && f->f_ops) + err1 = (f->f_ops->fo_close)(f); + if (!(f->f_flags & F_NODEV) && f->f_dev) + err2 = (f->f_dev->dv_close)(f); + if (f->f_devdata != NULL) + devclose(f); + f->f_flags = 0; + if (err1) { + errno = err1; + return (-1); + } + if (err2) { + errno = err2; + return (-1); + } + return (0); } diff --git a/lib/libstand/lseek.c b/lib/libstand/lseek.c index 0894912..91f8612 100644 --- a/lib/libstand/lseek.c +++ b/lib/libstand/lseek.c @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* $NetBSD: lseek.c,v 1.4 1997/01/22 00:38:10 cgd Exp $ */ /*- @@ -67,36 +68,46 @@ #include "stand.h" off_t -lseek(fd, offset, where) - int fd; - off_t offset; - int where; +lseek(int fd, off_t offset, int where) { - register struct open_file *f = &files[fd]; + struct open_file *f = &files[fd]; - if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) { - errno = EBADF; - return (-1); - } + if ((unsigned)fd >= SOPEN_MAX || f->f_flags == 0) { + errno = EBADF; + return (-1); + } - if (f->f_flags & F_RAW) { - /* - * On RAW devices, update internal offset. - */ - switch (where) { - case SEEK_SET: - f->f_offset = offset; - break; - case SEEK_CUR: - f->f_offset += offset; - break; - case SEEK_END: - default: - errno = EOFFSET; - return (-1); - } - return (f->f_offset); + if (f->f_flags & F_RAW) { + /* + * On RAW devices, update internal offset. + */ + switch (where) { + case SEEK_SET: + f->f_offset = offset; + break; + case SEEK_CUR: + f->f_offset += offset; + break; + case SEEK_END: + default: + errno = EOFFSET; + return (-1); } + return (f->f_offset); + } + + /* + * If this is a relative seek, we need to correct the offset for + * bytes that we have already read but the caller doesn't know + * about. + */ + if (where == SEEK_CUR) + offset -= f->f_ralen; + + /* + * Invalidate the readahead buffer. + */ + f->f_ralen = 0; - return (f->f_ops->fo_seek)(f, offset, where); + return (f->f_ops->fo_seek)(f, offset, where); } diff --git a/lib/libstand/open.c b/lib/libstand/open.c index 3a9127c..471f937 100644 --- a/lib/libstand/open.c +++ b/lib/libstand/open.c @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* $NetBSD: open.c,v 1.16 1997/01/28 09:41:03 pk Exp $ */ /*- @@ -79,6 +80,13 @@ o_gethandle(void) return(-1); } +static void +o_rainit(struct open_file *f) +{ + f->f_rabuf = malloc(SOPEN_RASIZE); + f->f_ralen = 0; + f->f_raoffset = 0; +} int open(const char *fname, int mode) @@ -118,6 +126,7 @@ open(const char *fname, int mode) if (error == 0) { f->f_ops = file_system[i]; + o_rainit(f); return (fd); } if (error != EINVAL) diff --git a/lib/libstand/read.c b/lib/libstand/read.c index 5b14a75..28ff47e 100644 --- a/lib/libstand/read.c +++ b/lib/libstand/read.c @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* $NetBSD: read.c,v 1.8 1997/01/22 00:38:12 cgd Exp $ */ /*- @@ -68,29 +69,61 @@ #include "stand.h" ssize_t -read(fd, dest, bcount) - int fd; - void *dest; - size_t bcount; +read(int fd, void *dest, size_t bcount) { - register struct open_file *f = &files[fd]; - size_t resid; + struct open_file *f = &files[fd]; + size_t resid; - if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) { - errno = EBADF; - return (-1); + if ((unsigned)fd >= SOPEN_MAX || !(f->f_flags & F_READ)) { + errno = EBADF; + return (-1); + } + if (f->f_flags & F_RAW) { + twiddle(); + errno = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, + btodb(f->f_offset), bcount, dest, &resid); + if (errno) + return (-1); + f->f_offset += resid; + return (resid); + } + + /* + * Optimise reads from regular files using a readahead buffer. + * If the request can't be satisfied from the current buffer contents, + * check to see if it should be bypassed, or refill the buffer and complete + * the request. + */ + resid = bcount; + for (;;) { + size_t ccount, cresid; + /* how much can we supply? */ + ccount = imin(f->f_ralen, resid); + if (ccount > 0) { + bcopy(f->f_rabuf + f->f_raoffset, dest, ccount); + f->f_raoffset += ccount; + f->f_ralen -= ccount; + resid -= ccount; + if (resid == 0) + return(bcount); + dest += ccount; } - if (f->f_flags & F_RAW) { - twiddle(); - errno = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, - btodb(f->f_offset), bcount, dest, &resid); - if (errno) - return (-1); - f->f_offset += resid; - return (resid); + + /* will filling the readahead buffer again not help? */ + if (resid >= SOPEN_RASIZE) { + /* bypass the rest of the request and leave the buffer empty */ + if ((errno = (f->f_ops->fo_read)(f, dest, resid, &cresid))) + return(bcount - resid); + return(bcount - cresid); } - resid = bcount; - if ((errno = (f->f_ops->fo_read)(f, dest, bcount, &resid))) - return (-1); - return (ssize_t)(bcount - resid); + + /* fetch more data */ + if ((errno = (f->f_ops->fo_read)(f, f->f_rabuf, SOPEN_RASIZE, &cresid))) + return(bcount - resid); /* behave like fread() */ + f->f_raoffset = 0; + f->f_ralen = SOPEN_RASIZE - cresid; + /* no more data, return what we had */ + if (f->f_ralen == 0) + return(bcount - resid); + } } diff --git a/lib/libstand/stand.h b/lib/libstand/stand.h index aa8e65f..e48565a 100644 --- a/lib/libstand/stand.h +++ b/lib/libstand/stand.h @@ -159,7 +159,11 @@ struct open_file { void *f_devdata; /* device specific data */ struct fs_ops *f_ops; /* pointer to file system operations */ void *f_fsdata; /* file system specific data */ - off_t f_offset; /* current file offset (F_RAW) */ + off_t f_offset; /* current file offset */ + char *f_rabuf; /* readahead buffer pointer */ + size_t f_ralen; /* valid data in readahead buffer */ + off_t f_raoffset; /* consumer offset in readahead buffer */ +#define SOPEN_RASIZE 512 }; #define SOPEN_MAX 8 -- cgit v1.1