From 07f84566d5c24f3f52f19fa17505392dec5c869d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 4 Aug 2007 01:22:52 +0100 Subject: [PATCH] Make read_user() non-blocking, and add select() call. --- src/core/posix_io.c | 94 ++++++++++++++++++++++-------------- src/include/gpxe/posix_io.h | 54 +++++++++++++++++++++ src/interface/pxe/pxe_tftp.c | 53 ++++++++++++++------ 3 files changed, 151 insertions(+), 50 deletions(-) diff --git a/src/core/posix_io.c b/src/core/posix_io.c index 21f818bfa..530ce6511 100644 --- a/src/core/posix_io.c +++ b/src/core/posix_io.c @@ -60,12 +60,6 @@ struct posix_file { /** List of open files */ static LIST_HEAD ( posix_files ); -/** Minimum file descriptor that will ever be allocated */ -#define POSIX_FD_MIN ( 1 ) - -/** Maximum file descriptor that will ever be allocated */ -#define POSIX_FD_MAX ( 255 ) - /** * Free open file * @@ -251,6 +245,38 @@ int open ( const char *uri_string ) { return rc; } +/** + * Check file descriptors for readiness + * + * @v readfds File descriptors to check + * @v wait Wait until data is ready + * @ret nready Number of ready file descriptors + */ +int select ( fd_set *readfds, int wait ) { + struct posix_file *file; + int fd; + + do { + for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { + if ( ! FD_ISSET ( fd, readfds ) ) + continue; + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + if ( ( list_empty ( &file->data ) ) && + ( file->rc != -EINPROGRESS ) ) + continue; + /* Data is available or status has changed */ + FD_ZERO ( readfds ); + FD_SET ( fd, readfds ); + return 1; + } + step(); + } while ( wait ); + + return 0; +} + /** * Read data from file * @@ -258,47 +284,45 @@ int open ( const char *uri_string ) { * @v offset Starting offset within data buffer * @v len Maximum length to read * @ret len Actual length read, or negative error number + * + * This call is non-blocking; if no data is available to read then + * -EWOULDBLOCK will be returned. */ ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) { struct posix_file *file; struct io_buffer *iobuf; - size_t frag_len; - ssize_t len = 0; + size_t len; /* Identify file */ file = posix_fd_to_file ( fd ); if ( ! file ) return -EBADF; - while ( 1 ) { - /* Try to fetch more data if none available */ - if ( list_empty ( &file->data ) ) - step(); - /* Dequeue at most one received I/O buffer into user buffer */ - list_for_each_entry ( iobuf, &file->data, list ) { - frag_len = iob_len ( iobuf ); - if ( frag_len > max_len ) - frag_len = max_len; - copy_to_user ( buffer, offset, iobuf->data, - frag_len ); - iob_pull ( iobuf, frag_len ); - if ( ! iob_len ( iobuf ) ) { - list_del ( &iobuf-> list ); - free_iob ( iobuf ); - } - file->pos += frag_len; - len += frag_len; - offset += frag_len; - max_len -= frag_len; - break; + /* Try to fetch more data if none available */ + if ( list_empty ( &file->data ) ) + step(); + + /* Dequeue at most one received I/O buffer into user buffer */ + list_for_each_entry ( iobuf, &file->data, list ) { + len = iob_len ( iobuf ); + if ( len > max_len ) + len = max_len; + copy_to_user ( buffer, offset, iobuf->data, len ); + iob_pull ( iobuf, len ); + if ( ! iob_len ( iobuf ) ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); } - /* If buffer is full, return */ - if ( ! max_len ) - return len; - /* If file has completed, return */ - if ( file->rc != -EINPROGRESS ) - return ( file->rc ? file->rc : len ); + file->pos += len; + return len; } + + /* If file has completed, return (after returning all data) */ + if ( file->rc != -EINPROGRESS ) + return file->rc; + + /* No data ready and file still in progress; return -WOULDBLOCK */ + return -EWOULDBLOCK; } /** diff --git a/src/include/gpxe/posix_io.h b/src/include/gpxe/posix_io.h index a5cf0c753..9984db005 100644 --- a/src/include/gpxe/posix_io.h +++ b/src/include/gpxe/posix_io.h @@ -10,12 +10,66 @@ #include #include +/** Minimum file descriptor that will ever be allocated */ +#define POSIX_FD_MIN ( 1 ) + +/** Maximum file descriptor that will ever be allocated */ +#define POSIX_FD_MAX ( 31 ) + +/** File descriptor set as used for select() */ +typedef uint32_t fd_set; + extern int open ( const char *uri_string ); extern ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t len ); +extern int select ( fd_set *readfds, int wait ); extern ssize_t fsize ( int fd ); extern int close ( int fd ); +/** + * Zero a file descriptor set + * + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_ZERO ( fd_set *set ) { + *set = 0; +} + +/** + * Set a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_SET ( int fd, fd_set *set ) { + *set |= ( 1 << fd ); +} + +/** + * Clear a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_CLR ( int fd, fd_set *set ) { + *set &= ~( 1 << fd ); +} + +/** + * Test a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + * @ret is_set Corresponding bit is set + */ +static inline __attribute__ (( always_inline )) int +FD_ISSET ( int fd, fd_set *set ) { + return ( *set & ( 1 << fd ) ); +} + /** * Read data from file * diff --git a/src/interface/pxe/pxe_tftp.c b/src/interface/pxe/pxe_tftp.c index 5197a6310..64f7ffd51 100644 --- a/src/interface/pxe/pxe_tftp.c +++ b/src/interface/pxe/pxe_tftp.c @@ -78,6 +78,37 @@ static void pxe_tftp_build_uri ( char *uri_string, ( ( filename[0] == '/' ) ? "" : "/" ), filename ); } +/** + * Read as much as possible from file + * + * @v fd File descriptor + * @v buffer Data buffer + * @v max_len Maximum length to read + * @ret len Actual length read, or negative error + */ +static ssize_t pxe_tftp_read_all ( int fd, userptr_t buffer, + size_t max_len ) { + fd_set fdset; + off_t offset = 0; + int ready; + ssize_t len; + + do { + FD_ZERO ( &fdset ); + FD_SET ( fd, &fdset ); + ready = select ( &fdset, 1 ); + if ( ready < 0 ) + return ready; + len = read_user ( fd, buffer, offset, max_len ); + if ( len < 0 ) + return len; + offset += len; + max_len -= len; + } while ( max_len && len ); + + return offset; +} + /** * TFTP OPEN * @@ -251,11 +282,12 @@ PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) { buffer = real_to_user ( tftp_read->Buffer.segment, tftp_read->Buffer.offset ); - len = read_user ( pxe_single_fd, buffer, 0, pxe_single_blksize ); + len = pxe_tftp_read_all ( pxe_single_fd, buffer, pxe_single_blksize ); if ( len < 0 ) { tftp_read->Status = PXENV_STATUS ( len ); return PXENV_EXIT_FAILURE; } + tftp_read->BufferSize = len; tftp_read->PacketNumber = ++pxe_single_blkidx; @@ -359,10 +391,8 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE char uri_string[PXE_URI_LEN]; int fd; userptr_t buffer; - size_t max_len; - ssize_t frag_len; - size_t len = 0; - int rc = -ENOBUFS; + ssize_t len; + int rc = 0; DBG ( "PXENV_TFTP_READ_FILE" ); @@ -384,16 +414,9 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE /* Read file */ buffer = phys_to_user ( tftp_read_file->Buffer ); - max_len = tftp_read_file->BufferSize; - while ( max_len ) { - frag_len = read_user ( fd, buffer, len, max_len ); - if ( frag_len <= 0 ) { - rc = frag_len; - break; - } - len += frag_len; - max_len -= frag_len; - } + len = pxe_tftp_read_all ( fd, buffer, tftp_read_file->BufferSize ); + if ( len < 0 ) + rc = len; close ( fd ); tftp_read_file->BufferSize = len;