Make read_user() non-blocking, and add select() call.
authorMichael Brown <mcb30@etherboot.org>
Sat, 4 Aug 2007 00:22:52 +0000 (01:22 +0100)
committerMichael Brown <mcb30@etherboot.org>
Sat, 4 Aug 2007 00:22:52 +0000 (01:22 +0100)
src/core/posix_io.c
src/include/gpxe/posix_io.h
src/interface/pxe/pxe_tftp.c

index 21f818b..530ce65 100644 (file)
@@ -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;
 }
 
 /**
index a5cf0c7..9984db0 100644 (file)
 #include <stdint.h>
 #include <gpxe/uaccess.h>
 
+/** 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
  *
index 5197a63..64f7ffd 100644 (file)
@@ -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;