From b2bb7e626fd9d3ca2a0a53a179712a30f6f2ab3a Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sat, 28 Sep 2013 14:47:49 -0600 Subject: Optimized sendfile() from Max Holtzberg --- nuttx/ChangeLog | 3 + nuttx/configs/sim/include/board.h | 3 + nuttx/configs/sim/nsh/defconfig | 107 +++++-- nuttx/fs/Makefile | 6 + nuttx/fs/fs_fcntl.c | 51 ++-- nuttx/fs/fs_filedup.c | 7 +- nuttx/fs/fs_files.c | 54 ++-- nuttx/fs/fs_fsync.c | 20 +- nuttx/fs/fs_ioctl.c | 17 +- nuttx/fs/fs_lseek.c | 150 +++++----- nuttx/fs/fs_open.c | 10 +- nuttx/fs/fs_poll.c | 13 +- nuttx/fs/fs_read.c | 114 +++++--- nuttx/fs/fs_sendfile.c | 154 ++++++++++ nuttx/fs/fs_write.c | 19 +- nuttx/include/nuttx/fs/fs.h | 45 ++- nuttx/include/nuttx/net/net.h | 8 + nuttx/include/sys/sendfile.h | 2 +- nuttx/include/sys/syscall.h | 11 +- nuttx/libc/misc/lib_sendfile.c | 8 +- nuttx/net/Kconfig | 14 +- nuttx/net/Makefile | 6 + nuttx/net/net_sendfile.c | 561 +++++++++++++++++++++++++++++++++++++ nuttx/net/send.c | 1 + nuttx/syscall/syscall.csv | 1 + nuttx/syscall/syscall_lookup.h | 6 +- nuttx/syscall/syscall_stublookup.c | 2 + 27 files changed, 1151 insertions(+), 242 deletions(-) create mode 100644 nuttx/configs/sim/include/board.h create mode 100644 nuttx/fs/fs_sendfile.c create mode 100644 nuttx/net/net_sendfile.c (limited to 'nuttx') diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 841d44d99..c8cf633a4 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -5663,4 +5663,7 @@ tools (2013-9-27). * drivers/net/encx24j500.c: Use separate pools for RX and TX descriptors. From Max Holtzberg (2013-9-28). + * nuttx/fs/fs_sendfile.c, nuttx/net/net_sendfile.c, and other file: + Integrate an optimized sendfile() operation from Max Holtzberg + (2013-9-28). diff --git a/nuttx/configs/sim/include/board.h b/nuttx/configs/sim/include/board.h new file mode 100644 index 000000000..327ad6925 --- /dev/null +++ b/nuttx/configs/sim/include/board.h @@ -0,0 +1,3 @@ +/************************************************************************ + * configs/sim/include/board.h + ************************************************************************/ diff --git a/nuttx/configs/sim/nsh/defconfig b/nuttx/configs/sim/nsh/defconfig index 545469eef..01c5342b0 100644 --- a/nuttx/configs/sim/nsh/defconfig +++ b/nuttx/configs/sim/nsh/defconfig @@ -39,6 +39,7 @@ CONFIG_HOST_LINUX=y # Debug Options # # CONFIG_DEBUG is not set +# CONFIG_ARCH_HAVE_STACKCHECK is not set CONFIG_DEBUG_SYMBOLS=y # @@ -56,7 +57,6 @@ CONFIG_ARCH_SIM=y # CONFIG_ARCH_Z16 is not set # CONFIG_ARCH_Z80 is not set CONFIG_ARCH="sim" -CONFIG_BOARD_LOOPSPERMSEC=0 # # Simulation Configuration Options @@ -64,10 +64,6 @@ CONFIG_BOARD_LOOPSPERMSEC=0 # CONFIG_SIM_M32 is not set # CONFIG_SIM_WALLTIME is not set -# -# External Memory Configuration -# - # # Architecture Options # @@ -81,12 +77,13 @@ CONFIG_BOARD_LOOPSPERMSEC=0 # CONFIG_ARCH_STACKDUMP is not set # CONFIG_ENDIAN_BIG is not set # CONFIG_ARCH_HAVE_RAMFUNCS is not set +# CONFIG_ARCH_HAVE_RAMVECTORS is not set # # Board Settings # -CONFIG_RAM_START=0x0 -CONFIG_RAM_SIZE=0 +CONFIG_BOARD_LOOPSPERMSEC=0 +# CONFIG_ARCH_CALIBRATION is not set # # Boot options @@ -97,6 +94,12 @@ CONFIG_BOOT_RUNFROMEXTSRAM=y # CONFIG_BOOT_RUNFROMSDRAM is not set # CONFIG_BOOT_COPYTORAM is not set +# +# Boot Memory Configuration +# +CONFIG_RAM_START=0x0 +CONFIG_RAM_SIZE=0 + # # Board Selection # @@ -116,6 +119,7 @@ CONFIG_NSH_MMCSDMINOR=0 # # RTOS Features # +# CONFIG_BOARD_INITIALIZE is not set CONFIG_MSEC_PER_TICK=10 CONFIG_RR_INTERVAL=0 # CONFIG_SCHED_INSTRUMENTATION is not set @@ -132,7 +136,6 @@ CONFIG_DEV_CONSOLE=y # CONFIG_FDCLONE_DISABLE is not set # CONFIG_FDCLONE_STDIO is not set CONFIG_SDCLONE_DISABLE=y -# CONFIG_SCHED_WORKQUEUE is not set CONFIG_SCHED_WAITPID=y # CONFIG_SCHED_STARTHOOK is not set # CONFIG_SCHED_ATEXIT is not set @@ -194,6 +197,7 @@ CONFIG_DEV_NULL=y # CONFIG_RTC is not set # CONFIG_WATCHDOG is not set # CONFIG_ANALOG is not set +# CONFIG_AUDIO_DEVICES is not set # CONFIG_BCH is not set # CONFIG_INPUT is not set # CONFIG_LCD is not set @@ -203,11 +207,16 @@ CONFIG_DEV_NULL=y # CONFIG_PM is not set # CONFIG_POWER is not set # CONFIG_SENSORS is not set -# CONFIG_SERCOMM_CONSOLE is not set CONFIG_SERIAL=y # CONFIG_DEV_LOWCONSOLE is not set # CONFIG_16550_UART is not set + +# +# USART Configuration +# # CONFIG_STANDARD_SERIAL is not set +# CONFIG_SERIAL_IFLOWCONTROL is not set +# CONFIG_SERIAL_OFLOWCONTROL is not set # CONFIG_USBDEV is not set # CONFIG_USBHOST is not set # CONFIG_WIRELESS is not set @@ -243,11 +252,13 @@ CONFIG_FAT_MAXFNAME=32 # CONFIG_FAT_DMAMEMORY is not set # CONFIG_FS_NXFFS is not set CONFIG_FS_ROMFS=y +# CONFIG_FS_SMARTFS is not set CONFIG_FS_BINFS=y # # System Logging # +# CONFIG_SYSLOG_ENABLE is not set # CONFIG_SYSLOG is not set # @@ -258,10 +269,16 @@ CONFIG_FS_BINFS=y # # Memory Management # +# CONFIG_MM_MULTIHEAP is not set # CONFIG_MM_SMALL is not set CONFIG_MM_REGIONS=1 # CONFIG_GRAN is not set +# +# Audio Support +# +# CONFIG_AUDIO is not set + # # Binary Formats # @@ -288,6 +305,7 @@ CONFIG_LIB_HOMEDIR="/" # CONFIG_LIBM is not set # CONFIG_NOPRINTF_FIELDWIDTH is not set # CONFIG_LIBC_FLOATINGPOINT is not set +CONFIG_LIB_RAND_ORDER=1 # CONFIG_EOL_IS_CR is not set # CONFIG_EOL_IS_LF is not set # CONFIG_EOL_IS_BOTH_CRLF is not set @@ -296,6 +314,7 @@ CONFIG_LIBC_EXECFUNCS=y CONFIG_EXECFUNCS_SYMTAB="g_symtab" CONFIG_EXECFUNCS_NSYMBOLS=0 CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=1024 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=2048 # CONFIG_LIBC_STRERROR is not set # CONFIG_LIBC_PERROR_STDOUT is not set CONFIG_ARCH_LOWPUTC=y @@ -304,9 +323,11 @@ CONFIG_LIB_SENDFILE_BUFSIZE=512 # CONFIG_ARCH_OPTIMIZED_FUNCTIONS is not set # -# Non-standard Helper Functions +# Non-standard Library Support # +# CONFIG_SCHED_WORKQUEUE is not set # CONFIG_LIB_KBDCODEC is not set +# CONFIG_LIB_SLCDCODEC is not set # # Basic CXX Support @@ -328,8 +349,6 @@ CONFIG_BUILTIN_PROXY_STACKSIZE=1024 # # CONFIG_EXAMPLES_BUTTONS is not set # CONFIG_EXAMPLES_CAN is not set -# CONFIG_SYSTEM_CDCACM is not set -# CONFIG_SYSTEM_COMPOSITE is not set # CONFIG_EXAMPLES_DHCPD is not set # CONFIG_EXAMPLES_ELF is not set # CONFIG_EXAMPLES_FTPC is not set @@ -342,8 +361,9 @@ CONFIG_EXAMPLES_HELLO=y # CONFIG_EXAMPLES_IGMP is not set # CONFIG_EXAMPLES_LCDRW is not set # CONFIG_EXAMPLES_MM is not set -# CONFIG_EXAMPLES_MOUNT is not set # CONFIG_EXAMPLES_MODBUS is not set +# CONFIG_EXAMPLES_MOUNT is not set +# CONFIG_EXAMPLES_NRF24L01TERM is not set CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_NULL is not set # CONFIG_EXAMPLES_NX is not set @@ -363,6 +383,10 @@ CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_ROMFS is not set # CONFIG_EXAMPLES_SENDMAIL is not set # CONFIG_EXAMPLES_SERLOOP is not set +# CONFIG_EXAMPLES_SLCD is not set +# CONFIG_EXAMPLES_SMART_TEST is not set +# CONFIG_EXAMPLES_SMART is not set +# CONFIG_EXAMPLES_TCPECHO is not set # CONFIG_EXAMPLES_TELNETD is not set # CONFIG_EXAMPLES_THTTPD is not set # CONFIG_EXAMPLES_TIFF is not set @@ -370,13 +394,13 @@ CONFIG_EXAMPLES_NSH=y # CONFIG_EXAMPLES_UDP is not set # CONFIG_EXAMPLES_UIP is not set # CONFIG_EXAMPLES_USBSERIAL is not set -# CONFIG_SYSTEM_USBMSC is not set # CONFIG_EXAMPLES_USBTERM is not set # CONFIG_EXAMPLES_WATCHDOG is not set # -# Interpreters +# Graphics Support # +# CONFIG_TIFF is not set # # Interpreters @@ -406,11 +430,7 @@ CONFIG_EXAMPLES_NSH=y # CONFIG_NETUTILS_WEBCLIENT is not set # -# ModBus -# - -# -# FreeModbus +# FreeModBus # # CONFIG_MODBUS is not set @@ -427,6 +447,7 @@ CONFIG_NSH_FILE_APPS=y # CONFIG_NSH_DISABLE_CAT is not set # CONFIG_NSH_DISABLE_CD is not set # CONFIG_NSH_DISABLE_CP is not set +# CONFIG_NSH_DISABLE_CMP is not set # CONFIG_NSH_DISABLE_DD is not set # CONFIG_NSH_DISABLE_ECHO is not set # CONFIG_NSH_DISABLE_EXEC is not set @@ -463,22 +484,34 @@ CONFIG_NSH_FILE_APPS=y # CONFIG_NSH_DISABLE_USLEEP is not set # CONFIG_NSH_DISABLE_WGET is not set # CONFIG_NSH_DISABLE_XD is not set + +# +# Configure Command Options +# +# CONFIG_NSH_CMDOPT_DF_H is not set CONFIG_NSH_CODECS_BUFSIZE=128 CONFIG_NSH_FILEIOSIZE=1024 CONFIG_NSH_LINELEN=80 +CONFIG_NSH_MAXARGUMENTS=6 CONFIG_NSH_NESTDEPTH=3 # CONFIG_NSH_DISABLESCRIPT is not set # CONFIG_NSH_DISABLEBG is not set CONFIG_NSH_ROMFSETC=y +# CONFIG_NSH_ROMFSRC is not set CONFIG_NSH_ROMFSMOUNTPT="/etc" CONFIG_NSH_INITSCRIPT="init.d/rcS" CONFIG_NSH_ROMFSDEVNO=1 CONFIG_NSH_ROMFSSECTSIZE=64 +# CONFIG_NSH_ARCHROMFS is not set CONFIG_NSH_FATDEVNO=2 CONFIG_NSH_FATSECTSIZE=512 CONFIG_NSH_FATNSECTORS=1024 CONFIG_NSH_FATMOUNTPT="/tmp" CONFIG_NSH_CONSOLE=y + +# +# USB Trace Support +# # CONFIG_NSH_CONDEV is not set # CONFIG_NSH_ARCHINIT is not set @@ -490,6 +523,14 @@ CONFIG_NSH_CONSOLE=y # System NSH Add-Ons # +# +# USB CDC/ACM Device Commands +# + +# +# USB Composite Device Commands +# + # # Custom Free Memory Command # @@ -504,6 +545,15 @@ CONFIG_NSH_CONSOLE=y # # CONFIG_SYSTEM_INSTALL is not set +# +# FLASH Erase-all Command +# + +# +# RAM test +# +# CONFIG_SYSTEM_RAMTEST is not set + # # readline() # @@ -529,3 +579,20 @@ CONFIG_READLINE_ECHO=y # Sysinfo # # CONFIG_SYSTEM_SYSINFO is not set + +# +# USB Monitor +# + +# +# Stack Monitor +# + +# +# USB Mass Storage Device Commands +# + +# +# Zmodem Commands +# +# CONFIG_SYSTEM_ZMODEM is not set diff --git a/nuttx/fs/Makefile b/nuttx/fs/Makefile index d9e0900fa..deb98bd44 100644 --- a/nuttx/fs/Makefile +++ b/nuttx/fs/Makefile @@ -83,6 +83,12 @@ ifneq ($(CONFIG_NFILE_STREAMS),0) CSRCS += fs_fdopen.c endif +# Support for sendfile() + +ifeq ($(CONFIG_NET_SENDFILE),y) +CSRCS += fs_sendfile.c +endif + # System logging to a character device (or file) ifeq ($(CONFIG_SYSLOG),y) diff --git a/nuttx/fs/fs_fcntl.c b/nuttx/fs/fs_fcntl.c index 20c05a439..db814f210 100644 --- a/nuttx/fs/fs_fcntl.c +++ b/nuttx/fs/fs_fcntl.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -62,26 +63,22 @@ ****************************************************************************/ #if CONFIG_NFILE_DESCRIPTORS > 0 -static inline int file_vfcntl(int fildes, int cmd, va_list ap) +static inline int file_vfcntl(int fd, int cmd, va_list ap) { FAR struct filelist *list; - FAR struct file *this_file; + FAR struct file *filep; int err = 0; int ret = OK; /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - err = EMFILE; - goto errout; - } + DEBUGASSERT(list); /* Was this file opened ? */ - this_file = &list->fl_files[fildes]; - if (!this_file->f_inode) + filep = &list->fl_files[fd]; + if (!filep->f_inode) { err = EBADF; goto errout; @@ -101,20 +98,20 @@ static inline int file_vfcntl(int fildes, int cmd, va_list ap) */ { - ret = file_dup(fildes, va_arg(ap, int)); + ret = file_dup(fd, va_arg(ap, int)); } break; case F_GETFD: /* Get the file descriptor flags defined in that are associated - * with the file descriptor fildes. File descriptor flags are associated + * with the file descriptor fd. File descriptor flags are associated * with a single file descriptor and do not affect other file descriptors * that refer to the same file. */ case F_SETFD: /* Set the file descriptor flags defined in , that are associated - * with fildes, to the third argument, arg, taken as type int. If the + * with fd, to the third argument, arg, taken as type int. If the * FD_CLOEXEC flag in the third argument is 0, the file shall remain open * across the exec functions; otherwise, the file shall be closed upon * successful execution of one of the exec functions. @@ -125,7 +122,7 @@ static inline int file_vfcntl(int fildes, int cmd, va_list ap) case F_GETFL: /* Get the file status flags and file access modes, defined in , - * for the file description associated with fildes. The file access modes + * for the file description associated with fd. The file access modes * can be extracted from the return value using the mask O_ACCMODE, which is * defined in . File status flags and file access modes are associated * with the file description and do not affect other file descriptors that @@ -133,13 +130,13 @@ static inline int file_vfcntl(int fildes, int cmd, va_list ap) */ { - ret = this_file->f_oflags; + ret = filep->f_oflags; } break; case F_SETFL: /* Set the file status flags, defined in , for the file description - * associated with fildes from the corresponding bits in the third argument, + * associated with fd from the corresponding bits in the third argument, * arg, taken as type int. Bits corresponding to the file access mode and * the file creation flags, as defined in , that are set in arg shall * be ignored. If any bits in arg other than those mentioned here are changed @@ -149,25 +146,25 @@ static inline int file_vfcntl(int fildes, int cmd, va_list ap) { int oflags = va_arg(ap, int); - oflags &= FFCNTL; - this_file->f_oflags &= ~FFCNTL; - this_file->f_oflags |= oflags; + oflags &= FFCNTL; + filep->f_oflags &= ~FFCNTL; + filep->f_oflags |= oflags; } break; case F_GETOWN: - /* If fildes refers to a socket, get the process or process group ID specified + /* If fd refers to a socket, get the process or process group ID specified * to receive SIGURG signals when out-of-band data is available. Positive values * indicate a process ID; negative values, other than -1, indicate a process group - * ID. If fildes does not refer to a socket, the results are unspecified. + * ID. If fd does not refer to a socket, the results are unspecified. */ case F_SETOWN: - /* If fildes refers to a socket, set the process or process group ID specified + /* If fd refers to a socket, set the process or process group ID specified * to receive SIGURG signals when out-of-band data is available, using the value * of the third argument, arg, taken as type int. Positive values indicate a * process ID; negative values, other than -1, indicate a process group ID. If - * fildes does not refer to a socket, the results are unspecified. + * fd does not refer to a socket, the results are unspecified. */ err = EBADF; /* Only valid on socket descriptors */ @@ -227,7 +224,7 @@ errout: * Name: fcntl ****************************************************************************/ -int fcntl(int fildes, int cmd, ...) +int fcntl(int fd, int cmd, ...) { va_list ap; int ret; @@ -239,11 +236,11 @@ int fcntl(int fildes, int cmd, ...) /* Did we get a valid file descriptor? */ #if CONFIG_NFILE_DESCRIPTORS > 0 - if ((unsigned int)fildes < CONFIG_NFILE_DESCRIPTORS) + if ((unsigned int)fd < CONFIG_NFILE_DESCRIPTORS) { /* Yes.. defer file operations to file_vfcntl() */ - ret = file_vfcntl(fildes, cmd, ap); + ret = file_vfcntl(fd, cmd, ap); } else #endif @@ -251,11 +248,11 @@ int fcntl(int fildes, int cmd, ...) /* No... check for operations on a socket descriptor */ #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 - if ((unsigned int)fildes < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS)) + if ((unsigned int)fd < (CONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS)) { /* Yes.. defer socket descriptor operations to net_vfcntl() */ - ret = net_vfcntl(fildes, cmd, ap); + ret = net_vfcntl(fd, cmd, ap); } else #endif diff --git a/nuttx/fs/fs_filedup.c b/nuttx/fs/fs_filedup.c index b7eeb7add..dcc229ab3 100644 --- a/nuttx/fs/fs_filedup.c +++ b/nuttx/fs/fs_filedup.c @@ -41,6 +41,7 @@ #include #include +#include #include @@ -84,11 +85,7 @@ int file_dup(int fildes, int minfd) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - set_errno(EMFILE); - return ERROR; - } + DEBUGASSERT(list); /* Verify that fildes is a valid, open file descriptor */ diff --git a/nuttx/fs/fs_files.c b/nuttx/fs/fs_files.c index c68ec7e73..739947a7d 100644 --- a/nuttx/fs/fs_files.c +++ b/nuttx/fs/fs_files.c @@ -222,11 +222,7 @@ int files_dup(FAR struct file *filep1, FAR struct file *filep2) } list = sched_getfiles(); - if (!list) - { - err = EMFILE; - goto errout; - } + DEBUGASSERT(list); _files_semtake(list); @@ -317,25 +313,23 @@ int files_allocate(FAR struct inode *inode, int oflags, off_t pos, int minfd) int i; list = sched_getfiles(); - if (list) + DEBUGASSERT(list); + + _files_semtake(list); + for (i = minfd; i < CONFIG_NFILE_DESCRIPTORS; i++) { - _files_semtake(list); - for (i = minfd; i < CONFIG_NFILE_DESCRIPTORS; i++) + if (!list->fl_files[i].f_inode) { - if (!list->fl_files[i].f_inode) - { - list->fl_files[i].f_oflags = oflags; - list->fl_files[i].f_pos = pos; - list->fl_files[i].f_inode = inode; - list->fl_files[i].f_priv = NULL; - _files_semgive(list); - return i; - } + list->fl_files[i].f_oflags = oflags; + list->fl_files[i].f_pos = pos; + list->fl_files[i].f_inode = inode; + list->fl_files[i].f_priv = NULL; + _files_semgive(list); + return i; } - - _files_semgive(list); } + _files_semgive(list); return ERROR; } @@ -358,10 +352,7 @@ int files_close(int filedes) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - return -EMFILE; - } + DEBUGASSERT(list); /* If the file was properly opened, there should be an inode assigned */ @@ -392,16 +383,15 @@ void files_release(int filedes) FAR struct filelist *list; list = sched_getfiles(); - if (list) + DEBUGASSERT(list); + + if (filedes >=0 && filedes < CONFIG_NFILE_DESCRIPTORS) { - if (filedes >=0 && filedes < CONFIG_NFILE_DESCRIPTORS) - { - _files_semtake(list); - list->fl_files[filedes].f_oflags = 0; - list->fl_files[filedes].f_pos = 0; - list->fl_files[filedes].f_inode = NULL; - _files_semgive(list); - } + _files_semtake(list); + list->fl_files[filedes].f_oflags = 0; + list->fl_files[filedes].f_pos = 0; + list->fl_files[filedes].f_inode = NULL; + _files_semgive(list); } } diff --git a/nuttx/fs/fs_fsync.c b/nuttx/fs/fs_fsync.c index 877c33a83..49104ed02 100644 --- a/nuttx/fs/fs_fsync.c +++ b/nuttx/fs/fs_fsync.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fs_fsync.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,8 @@ #include #include #include +#include + #include #include @@ -78,18 +80,14 @@ int fsync(int fd) { FAR struct filelist *list; - FAR struct file *this_file; + FAR struct file *filep; struct inode *inode; int ret; /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - ret = EMFILE; - goto errout; - } + DEBUGASSERT(list); /* Did we get a valid file descriptor? */ @@ -101,8 +99,8 @@ int fsync(int fd) /* Was this file opened for write access? */ - this_file = &list->fl_files[fd]; - if ((this_file->f_oflags & O_WROK) == 0) + filep = &list->fl_files[fd]; + if ((filep->f_oflags & O_WROK) == 0) { ret = EBADF; goto errout; @@ -113,7 +111,7 @@ int fsync(int fd) * the mountpoint operations vtable contains a sync method. */ - inode = this_file->f_inode; + inode = filep->f_inode; if (!inode || !INODE_IS_MOUNTPT(inode) || !inode->u.i_mops || !inode->u.i_mops->sync) { @@ -123,7 +121,7 @@ int fsync(int fd) /* Yes, then tell the mountpoint to sync this file */ - ret = inode->u.i_mops->sync(this_file); + ret = inode->u.i_mops->sync(filep); if (ret >= 0) { return OK; diff --git a/nuttx/fs/fs_ioctl.c b/nuttx/fs/fs_ioctl.c index 3440bc0d0..89b7b3f51 100644 --- a/nuttx/fs/fs_ioctl.c +++ b/nuttx/fs/fs_ioctl.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fs_ioctl.c * - * Copyright (C) 2007-2010, 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2010, 2012-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -89,7 +90,7 @@ int ioctl(int fd, int req, unsigned long arg) int err; #if CONFIG_NFILE_DESCRIPTORS > 0 FAR struct filelist *list; - FAR struct file *this_file; + FAR struct file *filep; FAR struct inode *inode; int ret = OK; @@ -117,22 +118,18 @@ int ioctl(int fd, int req, unsigned long arg) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - err = EMFILE; - goto errout; - } + DEBUGASSERT(list); /* Is a driver registered? Does it support the ioctl method? */ - this_file = &list->fl_files[fd]; - inode = this_file->f_inode; + filep = &list->fl_files[fd]; + inode = filep->f_inode; if (inode && inode->u.i_ops && inode->u.i_ops->ioctl) { /* Yes, then let it perform the ioctl */ - ret = (int)inode->u.i_ops->ioctl(this_file, req, arg); + ret = (int)inode->u.i_ops->ioctl(filep, req, arg); if (ret < 0) { err = -ret; diff --git a/nuttx/fs/fs_lseek.c b/nuttx/fs/fs_lseek.c index c57658434..bcf66f51b 100644 --- a/nuttx/fs/fs_lseek.c +++ b/nuttx/fs/fs_lseek.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "fs_internal.h" @@ -52,6 +53,86 @@ * Global Functions ****************************************************************************/ +/**************************************************************************** + * Name: file_seek + * + * Description: + * This is the internal implementation of lseek. See the comments in + * lseek() for further information. + * + * Parameters: + * file File structure instance + * offset Defines the offset to position to + * whence Defines how to use offset + * + * Return: + * The resulting offset on success. -1 on failure withi errno set + * properly (see lseek comments). + * + ****************************************************************************/ + +#ifndef CONFIG_NET_SENDFILE +static inline +#endif +off_t file_seek(FAR struct file *filep, off_t offset, int whence) +{ + FAR struct inode *inode; + int ret; + int err = OK; + + DEBUGASSERT(seekfile); + inode = filep->f_inode; + + /* Invoke the file seek method if available */ + + if (inode && inode->u.i_ops && inode->u.i_ops->seek) + { + ret = (int)inode->u.i_ops->seek(filep, offset, whence); + if (ret < 0) + { + err = -ret; + goto errout; + } + } + else + { + /* No... Just set the common file position value */ + + switch (whence) + { + case SEEK_CUR: + offset += filep->f_pos; + + case SEEK_SET: + if (offset >= 0) + { + filep->f_pos = offset; /* Might be beyond the end-of-file */ + break; + } + else + { + err = EINVAL; + goto errout; + } + break; + + case SEEK_END: + err = ENOSYS; + goto errout; + + default: + err = EINVAL; + goto errout; + } + } + + return filep->f_pos; + +errout: + set_errno(err); + return (off_t)ERROR; +} + /**************************************************************************** * Name: lseek * @@ -100,77 +181,18 @@ off_t lseek(int fd, off_t offset, int whence) if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) { - err = EBADF; - goto errout; + set_errno(EBADF); + return (off_t)ERROR; } /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - err = EMFILE; - goto errout; - } - - /* Is a driver registered? */ + DEBUGASSERT(list); - filep = &list->fl_files[fd]; - inode = filep->f_inode; - - if (inode && inode->u.i_ops) - { - /* Does it support the seek method */ + /* Then let file_seek do the real work */ - if (inode->u.i_ops->seek) - { - /* Yes, then let it perform the seek */ - - err = (int)inode->u.i_ops->seek(filep, offset, whence); - if (err < 0) - { - err = -err; - goto errout; - } - } - else - { - /* No... there are a couple of default actions we can take */ - - switch (whence) - { - case SEEK_CUR: - offset += filep->f_pos; - - case SEEK_SET: - if (offset >= 0) - { - filep->f_pos = offset; /* Might be beyond the end-of-file */ - break; - } - else - { - err = EINVAL; - goto errout; - } - break; - - case SEEK_END: - err = ENOSYS; - goto errout; - - default: - err = EINVAL; - goto errout; - } - } - } - - return filep->f_pos; - -errout: - set_errno(err); - return (off_t)ERROR; + return file_seek(&list->fl_files[fd], offset, whence); } #endif diff --git a/nuttx/fs/fs_open.c b/nuttx/fs/fs_open.c index 5b318b702..aead99f8f 100644 --- a/nuttx/fs/fs_open.c +++ b/nuttx/fs/fs_open.c @@ -38,14 +38,18 @@ ****************************************************************************/ #include + #include #include #include #include +#include #ifdef CONFIG_FILE_MODE #include #endif + #include + #include "fs_internal.h" /**************************************************************************** @@ -99,11 +103,7 @@ int open(const char *path, int oflags, ...) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - ret = EMFILE; - goto errout; - } + DEBUGASSERT(list); #ifdef CONFIG_FILE_MODE # ifdef CONFIG_CPP_HAVE_WARNING diff --git a/nuttx/fs/fs_poll.c b/nuttx/fs/fs_poll.c index 52d7f94a8..b86d6933e 100644 --- a/nuttx/fs/fs_poll.c +++ b/nuttx/fs/fs_poll.c @@ -97,7 +97,7 @@ static void poll_semtake(FAR sem_t *sem) static int poll_fdsetup(int fd, FAR struct pollfd *fds, bool setup) { FAR struct filelist *list; - FAR struct file *this_file; + FAR struct file *filep; FAR struct inode *inode; int ret = -ENOSYS; @@ -122,23 +122,20 @@ static int poll_fdsetup(int fd, FAR struct pollfd *fds, bool setup) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - return -EMFILE; - } + DEBUGASSERT(list); /* Is a driver registered? Does it support the poll method? * If not, return -ENOSYS */ - this_file = &list->fl_files[fd]; - inode = this_file->f_inode; + filep = &list->fl_files[fd]; + inode = filep->f_inode; if (inode && inode->u.i_ops && inode->u.i_ops->poll) { /* Yes, then setup the poll */ - ret = (int)inode->u.i_ops->poll(this_file, fds, setup); + ret = (int)inode->u.i_ops->poll(filep, fds, setup); } return ret; diff --git a/nuttx/fs/fs_read.c b/nuttx/fs/fs_read.c index 30dd4af03..5a7e6cb18 100644 --- a/nuttx/fs/fs_read.c +++ b/nuttx/fs/fs_read.c @@ -1,7 +1,7 @@ /**************************************************************************** - * fs_read.c + * fs/fs_read.c * - * Copyright (C) 2007-2009, 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2012-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -52,51 +52,60 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_read + * + * Description: + * This is the internal implementation of read(). + * + * Parameters: + * file File structure instance + * buf User-provided to save the data + * nbytes The maximum size of the user-provided buffer + * + * Return: + * The positive non-zero number of bytes read on success, 0 on if an + * end-of-file condition, or -1 on failure withi errno set appropriately. + * + ****************************************************************************/ + #if CONFIG_NFILE_DESCRIPTORS > 0 -static inline ssize_t file_read(int fd, FAR void *buf, size_t nbytes) +#ifndef CONFIG_NET_SENDFILE +static inline +#endif +ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes) { - FAR struct filelist *list; + FAR struct inode *inode; int ret = -EBADF; - /* Get the thread-specific file list */ + DEBUGASSERT(filep); + inode = filep->f_inode; + + /* Was this file opened for read access? */ - list = sched_getfiles(); - if (!list) + if ((filep->f_oflags & O_RDOK) == 0) { - /* Failed to get the file list */ + /* No.. File is not read-able */ - ret = -EMFILE; + ret = -EACCES; } - /* Were we given a valid file descriptor? */ + /* Is a driver or mountpoint registered? If so, does it support the read + * method? + */ - else if ((unsigned int)fd < CONFIG_NFILE_DESCRIPTORS) + else if (inode && inode->u.i_ops && inode->u.i_ops->read) { - FAR struct file *this_file = &list->fl_files[fd]; - FAR struct inode *inode = this_file->f_inode; - - /* Yes.. Was this file opened for read access? */ - - if ((this_file->f_oflags & O_RDOK) == 0) - { - /* No.. File is not read-able */ - - ret = -EACCES; - } - - /* Is a driver or mountpoint registered? If so, does it support - * the read method? + /* Yes.. then let it perform the read. NOTE that for the case of the + * mountpoint, we depend on the read methods bing identical in + * signature and position in the operations vtable. */ - else if (inode && inode->u.i_ops && inode->u.i_ops->read) - { - /* Yes.. then let it perform the read. NOTE that for the case - * of the mountpoint, we depend on the read methods bing - * identical in signature and position in the operations vtable. - */ - - ret = (int)inode->u.i_ops->read(this_file, (char*)buf, (size_t)nbytes); - } + ret = (int)inode->u.i_ops->read(filep, (char*)buf, (size_t)nbytes); } /* If an error occurred, set errno and return -1 (ERROR) */ @@ -111,14 +120,31 @@ static inline ssize_t file_read(int fd, FAR void *buf, size_t nbytes) return ret; } -#endif +#endif /* CONFIG_NFILE_DESCRIPTORS > 0 */ /**************************************************************************** - * Public Functions + * Name: read + * + * Description: + * The standard, POSIX read interface. + * + * Parameters: + * file File structure instance + * buf User-provided to save the data + * nbytes The maximum size of the user-provided buffer + * + * Return: + * The positive non-zero number of bytes read on success, 0 on if an + * end-of-file condition, or -1 on failure withi errno set appropriately. + * ****************************************************************************/ ssize_t read(int fd, FAR void *buf, size_t nbytes) { +#if CONFIG_NFILE_DESCRIPTORS > 0 + FAR struct filelist *list; +#endif + /* Did we get a valid file descriptor? */ #if CONFIG_NFILE_DESCRIPTORS > 0 @@ -139,9 +165,19 @@ ssize_t read(int fd, FAR void *buf, size_t nbytes) #endif } - /* The descriptor is in a valid range to file descriptor... do the read */ - #if CONFIG_NFILE_DESCRIPTORS > 0 - return file_read(fd, buf, nbytes); + else + { + /* Thee descriptor is in a valid range to file descriptor... do the + * read. Get the thread-specific file list. + */ + + list = sched_getfiles(); + DEBUGASSERT(list); + + /* Then let file_read do all of the work */ + + return file_read(&list->fl_files[fd], buf, nbytes); + } #endif } diff --git a/nuttx/fs/fs_sendfile.c b/nuttx/fs/fs_sendfile.c new file mode 100644 index 000000000..39533a4f2 --- /dev/null +++ b/nuttx/fs/fs_sendfile.c @@ -0,0 +1,154 @@ +/************************************************************************ + * fs/fs_sendfile.c + * + * Copyright (C) 2007, 2009, 2011, 2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************/ + +/************************************************************************ + * Included Files + ************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NET_SENDFILE + +/************************************************************************ + * Private types + ************************************************************************/ + +/************************************************************************ + * Private Variables + ************************************************************************/ + +/************************************************************************ + * Public Variables + ************************************************************************/ + +/************************************************************************ + * Private Functions + ************************************************************************/ + +/************************************************************************ + * Public Functions + ************************************************************************/ + +/************************************************************************ + * Name: sendfile + * + * Description: + * sendfile() copies data between one file descriptor and another. + * Used with file descriptors it basically just wraps a sequence of + * reads() and writes() to perform a copy. + * + * If the destination descriptor is a socket, it gives a better + * performance than simple reds() and writes(). The data is read directly + * into the net buffer and the whole tcp window is filled if possible. + * + * NOTE: This interface is *not* specified in POSIX.1-2001, or other + * standards. The implementation here is very similar to the Linux + * sendfile interface. Other UNIX systems implement sendfile() with + * different semantics and prototypes. sendfile() should not be used + * in portable programs. + * + * Input Parmeters: + * infd - A file (or socket) descriptor opened for reading + * outfd - A descriptor opened for writing. + * offset - If 'offset' is not NULL, then it points to a variable + * holding the file offset from which sendfile() will start + * reading data from 'infd'. When sendfile() returns, this + * variable will be set to the offset of the byte following + * the last byte that was read. If 'offset' is not NULL, + * then sendfile() does not modify the current file offset of + * 'infd'; otherwise the current file offset is adjusted to + * reflect the number of bytes read from 'infd.' + * + * If 'offset' is NULL, then data will be read from 'infd' + * starting at the current file offset, and the file offset + * will be updated by the call. + * count - The number of bytes to copy between the file descriptors. + * + * Returned Value: + * If the transfer was successful, the number of bytes written to outfd is + * returned. On error, -1 is returned, and errno is set appropriately. + * There error values are those returned by read() or write() plus: + * + * EINVAL - Bad input parameters. + * ENOMEM - Could not allocated an I/O buffer + * + ************************************************************************/ + +ssize_t sendfile(int outfd, int infd, off_t *offset, size_t count) +{ +#if defined(CONFIG_NET_TCP) && CONFIG_NSOCKET_DESCRIPTORS > 0 + + /* Check the destination file descriptor: Is it a (probable) file + * descriptor? Check the source file: Is it a normal file? + */ + + if ((unsigned int)outfd >= CONFIG_NFILE_DESCRIPTORS && + (unsigned int)infd < CONFIG_NFILE_DESCRIPTORS) + { + FAR struct filelist *list; + + /* This appears to be a file-to-socket transfer. Get the thread- + * specific file list. + */ + + list = sched_getfiles(); + DEBUGASSERT(list); + + /* Then let net_sendfile do the work. */ + + return net_sendfile(outfd, &list->fl_files[infd], offset, count); + } + else +#endif + { + /* No... then this is probably a file-to-file transfer. The generic + * lib_sendfile() can handle that case. + */ + + return lib_sendfile(outfd, infd, offset, count); + } +} + +#endif /* CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NET_SENDFILE */ diff --git a/nuttx/fs/fs_write.c b/nuttx/fs/fs_write.c index 93dad92c4..7b6a8172f 100644 --- a/nuttx/fs/fs_write.c +++ b/nuttx/fs/fs_write.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fs_write.c * - * Copyright (C) 2007-2009, 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2012-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include #include #include +#include #if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 # include @@ -58,7 +59,7 @@ static inline ssize_t file_write(int fd, FAR const void *buf, size_t nbytes) { FAR struct filelist *list; - FAR struct file *this_file; + FAR struct file *filep; FAR struct inode *inode; int ret; int err; @@ -66,16 +67,12 @@ static inline ssize_t file_write(int fd, FAR const void *buf, size_t nbytes) /* Get the thread-specific file list */ list = sched_getfiles(); - if (!list) - { - err = EMFILE; - goto errout; - } + DEBUGASSERT(list); /* Was this file opened for write access? */ - this_file = &list->fl_files[fd]; - if ((this_file->f_oflags & O_WROK) == 0) + filep = &list->fl_files[fd]; + if ((filep->f_oflags & O_WROK) == 0) { err = EBADF; goto errout; @@ -83,7 +80,7 @@ static inline ssize_t file_write(int fd, FAR const void *buf, size_t nbytes) /* Is a driver registered? Does it support the write method? */ - inode = this_file->f_inode; + inode = filep->f_inode; if (!inode || !inode->u.i_ops || !inode->u.i_ops->write) { err = EBADF; @@ -92,7 +89,7 @@ static inline ssize_t file_write(int fd, FAR const void *buf, size_t nbytes) /* Yes, then let the driver perform the write */ - ret = inode->u.i_ops->write(this_file, buf, nbytes); + ret = inode->u.i_ops->write(filep, buf, nbytes); if (ret < 0) { err = -ret; diff --git a/nuttx/include/nuttx/fs/fs.h b/nuttx/include/nuttx/fs/fs.h index c11084dee..a1678b603 100644 --- a/nuttx/include/nuttx/fs/fs.h +++ b/nuttx/include/nuttx/fs/fs.h @@ -594,7 +594,7 @@ struct tcb_s; /* Forward reference */ FAR struct file_struct *fs_fdopen(int fd, int oflags, FAR struct tcb_s *tcb); #endif -/* lib/stdio/lib_fflush.c **************************************************/ +/* libc/stdio/lib_fflush.c *************************************************/ /**************************************************************************** * Name: lib_flushall * @@ -608,6 +608,49 @@ FAR struct file_struct *fs_fdopen(int fd, int oflags, FAR struct tcb_s *tcb); int lib_flushall(FAR struct streamlist *list); #endif +/* libc/misc/lib_sendfile.c *************************************************/ +/**************************************************************************** + * Name: lib_sendfile + * + * Description: + * Transfer a file + * + ****************************************************************************/ + +#ifdef CONFIG_NET_SENDFILE +ssize_t lib_sendfile(int outfd, int infd, off_t *offset, size_t count); +#endif + +/* fs/fs_fileread.c *********************************************************/ +/**************************************************************************** + * Name: file_read + * + * Description: + * Equivalent to the standard read() function except that is accepts a + * struct file instance instead of a file descriptor. Currently used + * only by net_sendfile() + * + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_NET_SENDFILE) +ssize_t file_read(FAR struct file *filep, FAR void *buf, size_t nbytes); +#endif + +/* fs/fs_fileread.c *********************************************************/ +/**************************************************************************** + * Name: file_seek + * + * Description: + * Equivalent to the standard lseek() function except that is accepts a + * struct file instance instead of a file descriptor. Currently used + * only by net_sendfile() + * + ****************************************************************************/ + +#if CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_NET_SENDFILE) +off_t file_seek(FAR struct file *filep, off_t offset, int whence); +#endif + /* drivers/dev_null.c *******************************************************/ /**************************************************************************** * Name: devnull_register diff --git a/nuttx/include/nuttx/net/net.h b/nuttx/include/nuttx/net/net.h index d9ba5b9f4..402be5d5f 100644 --- a/nuttx/include/nuttx/net/net.h +++ b/nuttx/include/nuttx/net/net.h @@ -261,6 +261,14 @@ int net_dup2(int sockfd1, int sockfd2); int net_clone(FAR struct socket *psock1, FAR struct socket *psock2); +/* net_sendfile.c ************************************************************/ +/* Send files via a TCP connections */ + +#ifdef CONFIG_NET_SENDFILE +struct file; +ssize_t net_sendfile(int outfd, struct file *infile, off_t *offset, size_t count); +#endif + /* net_vfcntl.c **************************************************************/ /* Performs fcntl operations on socket */ diff --git a/nuttx/include/sys/sendfile.h b/nuttx/include/sys/sendfile.h index 0f3c05444..831c7b3d5 100644 --- a/nuttx/include/sys/sendfile.h +++ b/nuttx/include/sys/sendfile.h @@ -113,7 +113,7 @@ extern "C" { * ************************************************************************/ -EXTERN ssize_t sendfile (int outfd, int infd, FAR off_t *offset, size_t count); +ssize_t sendfile(int outfd, int infd, FAR off_t *offset, size_t count); #undef EXTERN #if defined(__cplusplus) diff --git a/nuttx/include/sys/syscall.h b/nuttx/include/sys/syscall.h index e6d458eae..24743679a 100644 --- a/nuttx/include/sys/syscall.h +++ b/nuttx/include/sys/syscall.h @@ -245,9 +245,16 @@ # if CONFIG_NFILE_STREAMS > 0 # define SYS_fs_fdopen (__SYS_filedesc+16) # define SYS_sched_getstreams (__SYS_filedesc+17) -# define __SYS_mountpoint (__SYS_filedesc+18) +# define __SYS_sendfile (__SYS_filedesc+18) # else -# define __SYS_mountpoint (__SYS_filedesc+16) +# define __SYS_sendfile (__SYS_filedesc+16) +# endif + +# if defined(CONFIG_NET_SENDFILE) +# define SYS_sendfile, __SYS_sendfile +# define __SYS_mountpoint (__SYS_sendfile+1) +# else +# define __SYS_mountpoint __SYS_sendfile # endif # if !defined(CONFIG_DISABLE_MOUNTPOINT) diff --git a/nuttx/libc/misc/lib_sendfile.c b/nuttx/libc/misc/lib_sendfile.c index f66c30918..181fd9395 100644 --- a/nuttx/libc/misc/lib_sendfile.c +++ b/nuttx/libc/misc/lib_sendfile.c @@ -1,5 +1,5 @@ /************************************************************************ - * libc/misc/lib_streamsem.c + * libc/misc/lib_sendfile.c * * Copyright (C) 2007, 2009, 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -70,7 +70,7 @@ ************************************************************************/ /************************************************************************ - * Name: sendfile + * Name: sendfile / lib_sendfile * * Description: * sendfile() copies data between one file descriptor and another. @@ -113,7 +113,11 @@ * ************************************************************************/ +#ifdef CONFIG_NET_SENDFILE +ssize_t lib_sendfile(int outfd, int infd, off_t *offset, size_t count) +#else ssize_t sendfile(int outfd, int infd, off_t *offset, size_t count) +#endif { FAR uint8_t *iobuffer; FAR uint8_t *wrbuffer; diff --git a/nuttx/net/Kconfig b/nuttx/net/Kconfig index bddd9d56e..c16bbafaf 100644 --- a/nuttx/net/Kconfig +++ b/nuttx/net/Kconfig @@ -174,9 +174,17 @@ config NET_TCP_SPLIT_SIZE ---help--- Packets of this size or smaller than this will not be split. -endif -endif -endmenu +endif # NET_TCP_SPLIT + +config NET_SENDFILE + bool "Optimized network sendfile()" + default n + ---help--- + Support larger, high performance sendfile() from transferring + files out a TCP connection. + +endif # NET_TCP +endmenu # TCP/IP Networking menu "UDP Networking" diff --git a/nuttx/net/Makefile b/nuttx/net/Makefile index 46ad34afa..234434496 100644 --- a/nuttx/net/Makefile +++ b/nuttx/net/Makefile @@ -68,6 +68,12 @@ ifneq ($(CONFIG_NFILE_STREAMS),0) SOCK_CSRCS += net_checksd.c endif +# Support for sendfile() + +ifeq ($(CONFIG_NET_SENDFILE),y) +SOCK_CSRCS += net_sendfile.c +endif + # Support for operations on network devices NETDEV_ASRCS = diff --git a/nuttx/net/net_sendfile.c b/nuttx/net/net_sendfile.c new file mode 100644 index 000000000..a5b2fcddf --- /dev/null +++ b/nuttx/net/net_sendfile.c @@ -0,0 +1,561 @@ +/**************************************************************************** + * net/net_sendfile.c + * + * Copyright (C) 2013 UVC Ingenieure. All rights reserved. + * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. + * Authors: Gregory Nutt + * Max Holtzberg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_TCP) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "net_internal.h" +#include "uip/uip_internal.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#if defined(CONFIG_NET_TCP_SPLIT) && !defined(CONFIG_NET_TCP_SPLIT_SIZE) +# define CONFIG_NET_TCP_SPLIT_SIZE 40 +#endif + +#define TCPBUF ((struct uip_tcpip_hdr *)&dev->d_buf[UIP_LLH_LEN]) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure holds the state of the send operation until it can be + * operated upon from the interrupt level. + */ + +struct sendfile_s +{ + FAR struct socket *snd_sock; /* Points to the parent socket structure */ + FAR struct uip_callback_s *snd_datacb; /* Data callback */ + FAR struct uip_callback_s *snd_ackcb; /* ACK callback */ + FAR struct file *snd_file; /* File structure of the input file */ + sem_t snd_sem; /* Used to wake up the waiting thread */ + off_t snd_foffset; /* Input file offset */ + size_t snd_flen; /* File length */ + ssize_t snd_sent; /* The number of bytes sent */ + uint32_t snd_isn; /* Initial sequence number */ + uint32_t snd_acked; /* The number of bytes acked */ +#if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) + uint32_t snd_time; /* Last send time for determining timeout */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: sendfile_timeout + * + * Description: + * Check for send timeout. + * + * Parameters: + * pstate send state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) +static inline int sendfile_timeout(FAR struct sendfile_s *pstate) +{ + FAR struct socket *psock = 0; + + /* Check for a timeout configured via setsockopts(SO_SNDTIMEO). + * If none... we well let the send wait forever. + */ + + psock = pstate->snd_sock; + if (psock && psock->s_sndtimeo != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->snd_time, psock->s_sndtimeo); + } + + /* No timeout */ + + return FALSE; +} +#endif /* CONFIG_NET_SOCKOPTS && !CONFIG_DISABLE_CLOCK */ + +static uint16_t ack_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct sendfile_s *pstate = (FAR struct sendfile_s *)pvpriv; + + if ((flags & UIP_ACKDATA) != 0) + { + /* The current acknowledgement number number is the (relative) offset + * of the of the next byte needed by the receiver. The snd_isn is the + * offset of the first byte to send to the receiver. The difference + * is the number of bytes to be acknowledged. + */ + + pstate->snd_acked = uip_tcpgetsequence(TCPBUF->ackno) - pstate->snd_isn; + nllvdbg("ACK: acked=%d sent=%d flen=%d\n", + pstate->snd_acked, pstate->snd_sent, pstate->snd_flen); + + dev->d_sndlen = 0; + } + + return OK; +} + +/**************************************************************************** + * Function: sendfile_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * send operation when polled by the uIP layer. + * + * Parameters: + * dev The sructure of the network driver that caused the interrupt + * conn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +static uint16_t sendfile_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct uip_conn *conn = (FAR struct uip_conn*)pvconn; + FAR struct sendfile_s *pstate = (FAR struct sendfile_s *)pvpriv; + int ret; + + nllvdbg("flags: %04x acked: %d sent: %d\n", + flags, pstate->snd_acked, pstate->snd_sent); + + if ((flags & UIP_REXMIT) != 0) + { + /* Yes.. in this case, reset the number of bytes that have been sent + * to the number of bytes that have been ACKed. + */ + + pstate->snd_sent = pstate->snd_acked; + + /* Fall through to re-send data from the last that was ACKed */ + } + + /* Check for a loss of connection */ + + else if ((flags & (UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT)) != 0) + { + /* Report not connected */ + + nlldbg("Lost connection\n"); + + net_lostconnection(pstate->snd_sock, flags); + pstate->snd_sent = -ENOTCONN; + goto end_wait; + } + + /* We get here if (1) not all of the data has been ACKed, (2) we have been + * asked to retransmit data, (3) the connection is still healthy, and (4) + * the outgoing packet is available for our use. In this case, we are + * now free to send more data to receiver -- UNLESS the buffer contains + * unprocessing incoming data. In that event, we will have to wait for the + * next polling cycle. + */ + + if ((flags & UIP_NEWDATA) == 0 && pstate->snd_sent < pstate->snd_flen) + { + /* Get the amount of data that we can send in the next packet */ + + uint32_t sndlen = pstate->snd_flen - pstate->snd_sent; + + if (sndlen > uip_mss(conn)) + { + sndlen = uip_mss(conn); + } + + /* Check if we have "space" in the window */ +#warning REVISIT +#if 0 /* Where is wndsize defined? */ + if ((pstate->snd_sent - pstate->snd_acked + sndlen) < conn->wndsize) +#endif + { + uint32_t seqno; + + /* Then set-up to send that amount of data. (this won't actually + * happen until the polling cycle completes). + */ + + ret = file_seek(pstate->snd_file, + pstate->snd_foffset + pstate->snd_sent, SEEK_SET); + if (ret < 0) + { + int errcode = errno; + nlldbg("failed to lseek: %d\n", errcode); + pstate->snd_sent = -errcode; + goto end_wait; + } + + ret = file_read(pstate->snd_file, dev->d_snddata, sndlen); + if (ret < 0) + { + int errcode = errno; + nlldbg("failed to read from input file: %d\n", errcode); + pstate->snd_sent = -errcode; + goto end_wait; + } + + dev->d_sndlen = sndlen; + + /* Set the sequence number for this packet. NOTE: uIP updates + * sndseq on recept of ACK *before* this function is called. In that + * case sndseq will point to the next unacknowledge byte (which might + * have already been sent). We will overwrite the value of sndseq + * here before the packet is sent. + */ + + seqno = pstate->snd_sent + pstate->snd_isn; + nllvdbg("SEND: sndseq %08x->%08x len: %d\n", conn->sndseq, seqno, ret); + + uip_tcpsetsequence(conn->sndseq, seqno); + + /* Check if the destination IP address is in the ARP table. If not, + * then the send won't actually make it out... it will be replaced with + * an ARP request. + * + * NOTE 1: This could be an expensive check if there are a lot of entries + * in the ARP table. Hence, we only check on the first packet -- when + * snd_sent is zero. + * + * NOTE 2: If we are actually harvesting IP addresses on incomming IP + * packets, then this check should not be necessary; the MAC mapping + * should already be in the ARP table. + */ + +#if defined(CONFIG_NET_ETHERNET) && !defined (CONFIG_NET_ARP_IPIN) + if (pstate->snd_sent != 0 || uip_arp_find(conn->ripaddr) != NULL) +#endif + { + /* Update the amount of data sent (but not necessarily ACKed) */ + + pstate->snd_sent += sndlen; + nllvdbg("pid: %d SEND: acked=%d sent=%d flen=%d\n", getpid(), + pstate->snd_acked, pstate->snd_sent, pstate->snd_flen); + + /* Update the send time */ + +#if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) + pstate->snd_time = clock_systimer(); +#endif + } + } +#warning REVISIT +#if 0 /* Where is wndsize defined? */ + else + { + nlldbg("window full, wait for ack\n"); + goto wait; + } +#endif + } + + /* All data has been send and we are just waiting for ACK or re-transmit + * indications to complete the send. Check for a timeout. + */ + +#if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) + else if (sendfile_timeout(pstate)) + { + /* Yes.. report the timeout */ + + nlldbg("SEND timeout\n"); + pstate->snd_sent = -ETIMEDOUT; + goto end_wait; + } +#endif /* CONFIG_NET_SOCKOPTS && !CONFIG_DISABLE_CLOCK */ + +end_wait: + + /* Do not allow any further callbacks */ + + pstate->snd_datacb->flags = 0; + pstate->snd_datacb->priv = NULL; + pstate->snd_datacb->event = NULL; + + /* Wake up the waiting thread */ + + sem_post(&pstate->snd_sem); + +wait: + return flags; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: net_sendfile + * + * Description: + * The send() call may be used only when the socket is in a connected state + * (so that the intended recipient is known). The only difference between + * send() and write() is the presence of flags. With zero flags parameter, + * send() is equivalent to write(). Also, send(sockfd,buf,len,flags) is + * equivalent to sendto(sockfd,buf,len,flags,NULL,0). + * + * Parameters: + * psock An instance of the internal socket structure. + * buf Data to send + * len Length of data to send + * flags Send flags + * + * Returned Value: + * On success, returns the number of characters sent. On error, + * -1 is returned, and errno is set appropriately: + * + * EAGAIN or EWOULDBLOCK + * The socket is marked non-blocking and the requested operation + * would block. + * EBADF + * An invalid descriptor was specified. + * ECONNRESET + * Connection reset by peer. + * EDESTADDRREQ + * The socket is not connection-mode, and no peer address is set. + * EFAULT + * An invalid user space address was specified for a parameter. + * EINTR + * A signal occurred before any data was transmitted. + * EINVAL + * Invalid argument passed. + * EISCONN + * The connection-mode socket was connected already but a recipient + * was specified. (Now either this error is returned, or the recipient + * specification is ignored.) + * EMSGSIZE + * The socket type requires that message be sent atomically, and the + * size of the message to be sent made this impossible. + * ENOBUFS + * The output queue for a network interface was full. This generally + * indicates that the interface has stopped sending, but may be + * caused by transient congestion. + * ENOMEM + * No memory available. + * ENOTCONN + * The socket is not connected, and no target has been given. + * ENOTSOCK + * The argument s is not a socket. + * EOPNOTSUPP + * Some bit in the flags argument is inappropriate for the socket + * type. + * EPIPE + * The local end has been shut down on a connection oriented socket. + * In this case the process will also receive a SIGPIPE unless + * MSG_NOSIGNAL is set. + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t net_sendfile(int outfd, struct file *infile, off_t *offset, + size_t count) +{ + FAR struct socket *psock = sockfd_socket(outfd); + FAR struct uip_conn *conn = (FAR struct uip_conn*)psock->s_conn; + struct sendfile_s state; + uip_lock_t save; + int err; + + nlldbg("pid: %d\n", getpid()); + + /* Verify that the sockfd corresponds to valid, allocated socket */ + + if (!psock || psock->s_crefs <= 0) + { + err = EBADF; + goto errout; + } + + /* If this is an un-connected socket, then return ENOTCONN */ + + if (psock->s_type != SOCK_STREAM || !_SS_ISCONNECTED(psock->s_flags)) + { + err = ENOTCONN; + goto errout; + } + + /* Set the socket state to sending */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND); + + /* Initialize the state structure. This is done with interrupts + * disabled because we don't want anything to happen until we + * are ready. + */ + + save = uip_lock(); + + memset(&state, 0, sizeof(struct sendfile_s)); + sem_init(&state. snd_sem, 0, 0); /* Doesn't really fail */ + state.snd_sock = psock; /* Socket descriptor to use */ + state.snd_foffset = offset ? *offset : 0; /* Input file offset */ + state.snd_flen = count; /* Number of bytes to send */ + state.snd_file = infile; /* File to read from */ + + /* Allocate resources to receive a callback */ + + state.snd_datacb = uip_tcpcallbackalloc(conn); + + if (state.snd_datacb == NULL) + { + nlldbg("Failed to allocate data callback\n"); + err = ENOMEM; + goto errout_locked; + } + + state.snd_ackcb = uip_tcpcallbackalloc(conn); + + if (state.snd_ackcb == NULL) + { + nlldbg("Failed to allocate ack callback\n"); + err = ENOMEM; + goto errout_datacb; + } + + /* Get the initial sequence number that will be used */ + + state.snd_isn = uip_tcpgetsequence(conn->sndseq); + + /* There is no outstanding, unacknowledged data after this + * initial sequence number. + */ + + conn->unacked = 0; + + /* Set the initial time for calculating timeouts */ + +#if defined(CONFIG_NET_SOCKOPTS) && !defined(CONFIG_DISABLE_CLOCK) + state.snd_time = clock_systimer(); +#endif + + /* Set up the ACK callback in the connection */ + + state.snd_ackcb->flags = UIP_ACKDATA; + state.snd_ackcb->priv = (void*)&state; + state.snd_ackcb->event = ack_interrupt; + + /* Perform the TCP send operation */ + + do + { + state.snd_datacb->flags = UIP_REXMIT|UIP_POLL|UIP_CLOSE|UIP_ABORT|UIP_TIMEDOUT; + state.snd_datacb->priv = (void*)&state; + state.snd_datacb->event = sendfile_interrupt; + + /* Notify the device driver of the availaibilty of TX data */ + + netdev_txnotify(&conn->ripaddr); + + uip_lockedwait(&state.snd_sem); + } + while (state.snd_sent > 0 && state.snd_acked < state.snd_flen); + + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); + + uip_tcpcallbackfree(conn, state.snd_ackcb); + + errout_datacb: + uip_tcpcallbackfree(conn, state.snd_datacb); + + errout_locked: + + sem_destroy(&state. snd_sem); + uip_unlock(save); + + errout: + + if (err) + { + set_errno(err); + return ERROR; + } + else if (state.snd_sent < 0) + { + set_errno(-state.snd_sent); + return ERROR; + } + else + { + return state.snd_sent; + } +} + +#endif /* CONFIG_NET && CONFIG_NET_TCP */ diff --git a/nuttx/net/send.c b/nuttx/net/send.c index 2d66f26f0..3d18ecde5 100644 --- a/nuttx/net/send.c +++ b/nuttx/net/send.c @@ -51,6 +51,7 @@ #include #include +#include #include #ifdef CONFIG_NET_ARP_IPIN diff --git a/nuttx/syscall/syscall.csv b/nuttx/syscall/syscall.csv index 71d800950..e470b91a7 100644 --- a/nuttx/syscall/syscall.csv +++ b/nuttx/syscall/syscall.csv @@ -106,6 +106,7 @@ "sem_unlink","semaphore.h","","int","FAR const char*" "sem_wait","semaphore.h","","int","FAR sem_t*" "send","sys/socket.h","CONFIG_NSOCKET_DESCRIPTORS > 0 && defined(CONFIG_NET)","ssize_t","int","FAR const void*","size_t","int" +"sendfile","sys/sendfile.h","CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_NET_SENDFILE)","ssize_t","int","int","FAR off_t*","size_t" "sendto","sys/socket.h","CONFIG_NSOCKET_DESCRIPTORS > 0 && defined(CONFIG_NET)","ssize_t","int","FAR const void*","size_t","int","FAR const struct sockaddr*","socklen_t" "set_errno","errno.h","","void","int" "setenv","stdlib.h","!defined(CONFIG_DISABLE_ENVIRON)","int","const char*","const char*","int" diff --git a/nuttx/syscall/syscall_lookup.h b/nuttx/syscall/syscall_lookup.h index d87385f0a..b370bdd5b 100644 --- a/nuttx/syscall/syscall_lookup.h +++ b/nuttx/syscall/syscall_lookup.h @@ -182,7 +182,11 @@ SYSCALL_LOOKUP(up_assert, 2, STUB_up_assert) # if CONFIG_NFILE_STREAMS > 0 SYSCALL_LOOKUP(fdopen, 3, STUB_fs_fdopen) SYSCALL_LOOKUP(sched_getstreams, 0, STUB_sched_getstreams) -#endif +# endif + +# if defined(CONFIG_NET_SENDFILE) + SYSCALL_LOOKUP(sendfile, 4, STUB_fs_sendifile) +# endif # if !defined(CONFIG_DISABLE_MOUNTPOINT) SYSCALL_LOOKUP(fsync, 1, STUB_fsync) diff --git a/nuttx/syscall/syscall_stublookup.c b/nuttx/syscall/syscall_stublookup.c index 517c15af3..436637c9e 100644 --- a/nuttx/syscall/syscall_stublookup.c +++ b/nuttx/syscall/syscall_stublookup.c @@ -199,6 +199,8 @@ uintptr_t STUB_fs_fdopen(int nbr, uintptr_t parm1, uintptr_t parm2, uintptr_t parm3); uintptr_t STUB_sched_getstreams(int nbr); +ssize_t sendfile(int outfd, int infd, FAR off_t *offset, size_t count); + uintptr_t STUB_fsync(int nbr, uintptr_t parm1); uintptr_t STUB_mkdir(int nbr, uintptr_t parm1, uintptr_t parm2); uintptr_t STUB_mount(int nbr, uintptr_t parm1, uintptr_t parm2, -- cgit v1.2.3