diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-10-05 11:43:42 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-10-05 11:43:42 -0600 |
commit | a39acc0c7228605ee2d98de8384085883dadaa9b (patch) | |
tree | 74b0bff47e50607d875d4cd41e646cf49a46b27e /nuttx | |
parent | b118c15d61500248ddd9e69e8e2d480f3ea317ae (diff) | |
download | px4-nuttx-a39acc0c7228605ee2d98de8384085883dadaa9b.tar.gz px4-nuttx-a39acc0c7228605ee2d98de8384085883dadaa9b.tar.bz2 px4-nuttx-a39acc0c7228605ee2d98de8384085883dadaa9b.zip |
Initiail implementation of lio_listio() and rethinking of signal logic
Diffstat (limited to 'nuttx')
-rw-r--r-- | nuttx/include/aio.h | 8 | ||||
-rw-r--r-- | nuttx/libc/aio/Make.defs | 6 | ||||
-rw-r--r-- | nuttx/libc/aio/aio_fsync.c | 227 | ||||
-rw-r--r-- | nuttx/libc/aio/aio_signal.c | 35 | ||||
-rw-r--r-- | nuttx/libc/aio/aio_suspend.c | 25 | ||||
-rw-r--r-- | nuttx/libc/aio/lio_listio.c | 514 |
6 files changed, 747 insertions, 68 deletions
diff --git a/nuttx/include/aio.h b/nuttx/include/aio.h index c0c4af1f6..5de5bb713 100644 --- a/nuttx/include/aio.h +++ b/nuttx/include/aio.h @@ -134,8 +134,8 @@ struct aiocb off_t aio_offset; /* File offset */ size_t aio_nbytes; /* Length of transfer */ int aio_fildes; /* File descriptor */ - int aio_reqprio; /* Request priority offset */ - int aio_lio_opcode; /* Operation to be performed */ + int8_t aio_reqprio; /* Request priority offset */ + uint8_t aio_lio_opcode; /* Operation to be performed */ /* Non-standard, implementation-dependent data. For portability reasons, * application code should never reference these elements. @@ -144,6 +144,7 @@ struct aiocb struct work_s aio_work; /* Used to defer I/O to the work thread */ pid_t aio_pid; /* ID of client to be notify at completion */ volatile ssize_t aio_result; /* Support for aio_error() and aio_return() */ + FAR void *aio_priv; /* Used by signal handlers */ }; /**************************************************************************** @@ -170,11 +171,8 @@ ssize_t aio_return(FAR struct aiocb *aiocbp); int aio_suspend(FAR const struct aiocb *const list[], int nent, FAR const struct timespec *timeout); int aio_write(FAR struct aiocb *aiocbp); - -#ifndef CONFIG_PTHREAD_DISABLE /* Depends on pthread support */ int lio_listio(int mode, FAR struct aiocb *const list[], int nent, FAR struct sigevent *sig); -#endif #undef EXTERN #ifdef __cplusplus diff --git a/nuttx/libc/aio/Make.defs b/nuttx/libc/aio/Make.defs index 9df5d1f57..5af09c03c 100644 --- a/nuttx/libc/aio/Make.defs +++ b/nuttx/libc/aio/Make.defs @@ -38,11 +38,7 @@ ifeq ($(CONFIG_LIBC_AIO),y) # Add the asynchronous I/O C files to the build CSRCS += aio_cancel.c aio_error.c aio_fsync.c aio_read.c aio_return.c -CSRCS += aio_signal.c aio_suspend.c aio_write.c - -ifneq ($(CONFIG_PTHREAD_DISABLE),y) -CSRCS += lio_listio.c -endif +CSRCS += aio_signal.c aio_suspend.c aio_write.c lio_listio.c # Add the asynchronous I/O directory to the build diff --git a/nuttx/libc/aio/aio_fsync.c b/nuttx/libc/aio/aio_fsync.c new file mode 100644 index 000000000..b354ab479 --- /dev/null +++ b/nuttx/libc/aio/aio_fsync.c @@ -0,0 +1,227 @@ +/**************************************************************************** + * libc/aio/aio_fsync.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * 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 <nuttx/config.h> + +#include <unistd.h> +#include <aio.h> +#include <assert.h> +#include <errno.h> + +#include <nuttx/wqueue.h> + +#include "lib_internal.h" +#include "aio/aio.h" + +#ifndef CONFIG_LIBC_AIO + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: aio_fsync_worker + * + * Description: + * This function executes on the worker thread and performs the + * asynchronous I/O operation. + * + * Input Parameters: + * arg - Worker argument. In this case, a pointer to an instance of + * struct aiocb cast to void *. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void aio_fsync_worker(FAR void *arg) +{ + FAR struct aiocb *aiocbp = (FAR struct aiocb *)arg; + DEBASSERT(arg); + int ret; + + /* Perform the fsync using aio_fildes */ + + ret = fsync(aiocbp->aio_fildes); + if (ret < 0) + { + int errcode = get_errno(); + fdbg("ERROR: fsync failed: %d\n", errode); + DEBUGASSERT(errcode > 0); + aicbp->result = -errcode; + } + else + { + aicbp->result = OK; + } + + /* Signal the client */ + + (void)aio_signal(aiocbp); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: aio_fsync + * + * Description: + * The aio_fsync() function asynchronously forces all I/O operations + * associated with the file indicated by the file descriptor aio_fildes + * member of the aiocb structure referenced by the aiocbp argument and + * queued at the time of the call to aio_fsync() to the synchronized + * I/O completion state. The function call will return when the + * synchronization request has been initiated or queued to the file or + * device (even when the data cannot be synchronized immediately). + * + * If op is O_DSYNC, all currently queued I/O operations will be + * completed as if by a call to fdatasync(); that is, as defined for + * synchronized I/O data integrity completion. If op is O_SYNC, all + * currently queued I/O operations will be completed as if by a call to + * fsync(); that is, as defined for synchronized I/O file integrity + * completion. If the aio_fsync() function fails, or if the operation + * queued by aio_fsync() fails, then, as for fsync() and fdatasync(), + * outstanding I/O operations are not guaranteed to have been completed. + * [See "POSIX Compliance" below] + * + * If aio_fsync() succeeds, then it is only the I/O that was queued at + * the time of the call to aio_fsync() that is guaranteed to be forced + * to the relevant completion state. The completion of subsequent I/O + * on the file descriptor is not guaranteed to be completed in a + * synchronized fashion. + * + * The aiocbp argument refers to an asynchronous I/O control block. The + * aiocbp value may be used as an argument to aio_error() and aio_return() + * in order to determine the error status and return status, respectively, + * of the asynchronous operation while it is proceeding. When the request + * is queued, the error status for the operation is [EINPROGRESS]. When + * all data has been successfully transferred, the error status will be + * reset to reflect the success or failure of the operation. If the + * operation does not complete successfully, the error status for the + * operation will be set to indicate the error. The aio_sigevent member + * determines the asynchronous notification to occur when all operations + * have achieved synchronized I/O completion. All other members of the + * structure referenced by aiocbp are ignored. If the control block + * referenced by aiocbp becomes an illegal address prior to asynchronous + * I/O completion, then the behavior is undefined. + * + * If the aio_fsync() function fails or aiocbp indicates an error condition, + * data is not guaranteed to have been successfully transferred. + * + * Input Parameters: + * op - Should be either O_SYNC or O_DSYNC. Ignored in this implementation. + * aiocbp - A pointer to an instance of struct aiocb + * + * Returned Value: + * The aio_fsync() function will return the value 0 if the I/O operation is + * successfully queued; otherwise, the function will return the value -1 and + * set errno to indicate the error. + * + * The aio_fsync() function will fail if: + * + * EAGAIN - The requested asynchronous operation was not queued due to + * temporary resource limitations. + * EBADF - The aio_fildes member of the aiocb structure referenced by + * the aiocbp argument is not a valid file descriptor open for writing. + * EINVAL - This implementation does not support synchronized I/O for + * this file. + * EINVAL - A value of op other than O_DSYNC or O_SYNC was specified. + * + * In the event that any of the queued I/O operations fail, aio_fsync() + * will return the error condition defined for read() and write(). The + * error is returned in the error status for the asynchronous fsync() + * operation, which can be retrieved using aio_error(). + * + * POSIX Compliance + * - NuttX does not currently make any distinction between O_DYSNC and O_SYNC. + * Hence, the 'op' argument is ignored altogether. + * - Most errors required in the standard are not detected at this point. + * There are no pre-queuing checks for the validity of the operation. + * + ****************************************************************************/ + +int aio_fsync(int op, FAR struct aiocb *aiocbp) +{ + int ret; + + DEBUGASSERT(op == O_SYNC /* || op == O_DSYNC */); + DEBUGASSERT(aiocbp); + + /* The result -EINPROGRESS means that the transfer has not yet completed */ + + aiocbp->aio_result = -EINPROGRESS; + + /* Save the ID of the calling, client thread */ + + aiocbp->aio_pid = getpid(); + + /* Defer the work to the worker thread */ + + ret = work_queue(AIO_QUEUE, &aiocbp->aio_work, aio_fsync_worker, aiocbp, 0); + if (ret < 0) + { + aio->aio_result = ret; + set_errno(ret); + return ERROR; + } + + return OK; +} + +#endif /* CONFIG_LIBC_AIO */ diff --git a/nuttx/libc/aio/aio_signal.c b/nuttx/libc/aio/aio_signal.c index 6029c7145..75c1a3dd7 100644 --- a/nuttx/libc/aio/aio_signal.c +++ b/nuttx/libc/aio/aio_signal.c @@ -92,33 +92,54 @@ int aio_signal(FAR struct aiocb *aiocbp) { + int errcode; + int status; int ret; DEBUGASSERT(aiocbp); + ret = OK; /* Assume success */ + /* Signal the client */ if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL) { #ifdef CONFIG_CAN_PASS_STRUCTS - ret = sigqueue(aiocbp->aio_pid, aiocbp->aio_sigevent.sigev_signo, - aiocbp->aio_sigevent.sigev_value); + status = sigqueue(aiocbp->aio_pid, aiocbp->aio_sigevent.sigev_signo, + aiocbp->aio_sigevent.sigev_value); #else - ret = sigqueue(aiocbp->aio_pid, aiocbp->aio_sigevent.sigev_sign, - aiocbp->aio_sigevent.sigev_value.sival_ptr); + status = sigqueue(aiocbp->aio_pid, aiocbp->aio_sigevent.sigev_sign, + aiocbp->aio_sigevent.sigev_value.sival_ptr); #endif + if (ret < 0) + { + errcode = get_errno(); + fdbg("ERROR: sigqueue failed: %d\n", errcode); + ret = ERROR; + } } /* Send the poll signal in any event in case the caller is waiting * on sig_suspend(); */ - else + status = kill(aiocbp->aio_pid, SIGPOLL); + if (status && ret == OK) + { + errcode = get_errno(); + fdbg("ERROR: kill failed: %d\n", errcode); + ret = ERROR; + } + + /* Make sure that errno is set correctly on return */ + + if (ret < 0) { - ret = kill(aiocbp->aio_pid, SIGPOLL); + set_errno(errcode); + return ERROR; } - return ret; + return OK; } #endif /* CONFIG_LIBC_AIO */ diff --git a/nuttx/libc/aio/aio_suspend.c b/nuttx/libc/aio/aio_suspend.c index 4683ab24a..51fdf7e4a 100644 --- a/nuttx/libc/aio/aio_suspend.c +++ b/nuttx/libc/aio/aio_suspend.c @@ -125,11 +125,6 @@ int aio_suspend(FAR const struct aiocb *const list[], int nent, DEBUGASSERT(aiocbp); - /* Create the signal set */ - - sigemptyset(&set); - sigaddset(&set, SIGPOLL); - /* Lock the scheduler so that no I/O events can complete on the worker * thread until we set our wait set up. Pre-emption will, of course, be * re-enabled while we are waiting for the signal. @@ -152,21 +147,19 @@ int aio_suspend(FAR const struct aiocb *const list[], int nent, sched_unlock(); return OK; } - - /* If the caller has a signal notification setup, add that to the - * signals we will wait for. - */ - - if (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL) - { - sigaddset(&set, aiocbp->aio_sigevent.sigev_signo); - } } - /* Then wait. sigtimedwait() will return the signal number that cause the - * error. It will set errno appropriately for this function on errors. + /* Then wait for SIGPOLL. On success sigtimedwait() will return the + * signal number that cause the error (SIGPOLL). It will set errno + * appropriately for this function on errors. + * + * NOTE: If completion of the I/O causes other signals to be generated + * first, then this will wake up and return EINTR instead of success. */ + sigemptyset(&set); + sigaddset(&set, SIGPOLL); + ret = sigtimedwait(&set, NULL, &timeout) sched_unlock(); return ret >= 0 ? OK : ERROR; diff --git a/nuttx/libc/aio/lio_listio.c b/nuttx/libc/aio/lio_listio.c index c5f6e424c..abdd46f73 100644 --- a/nuttx/libc/aio/lio_listio.c +++ b/nuttx/libc/aio/lio_listio.c @@ -39,15 +39,16 @@ #include <nuttx/config.h> -#include <pthread.h> +#include <unistd.h> #include <signal.h> #include <aio.h> #include <assert.h> #include <errno.h> #include "lib_internal.h" +#include "aio/aio.h" -#if defined(CONFIG_LIBC_AIO) && !defined(CONFIG_PTHREAD_DISABLE) +#ifdef CONFIG_LIBC_AIO /**************************************************************************** * Pre-processor Definitions @@ -57,16 +58,15 @@ /**************************************************************************** * Private Types ****************************************************************************/ -/* This structure is passed to lio_thread() when the it is started by - * lio_listio(). - */ -struct lio_threadparm_s +struct lio_sighand_s { - FAR struct sigevent *sig; /* Used to notify the caller */ - FAR struct aiocb *const *list; /* The list of I/O operations to be performed */ - pid_t pid; /* ID of caller to be notified */ - int nent; /* The number of elements in the list */ + FAR struct aiocb * const *list; /* List of I/O operations */ + FAR struct sigevent *sig; /* Describes how to signal the caller */ + int nent; /* Number or elements in list[] */ + pid_t pid; /* ID of client */ + sigset_t oprocmask; /* sigprocmask to restore */ + struct sigaction oact; /* Signal handler to restore */ }; /**************************************************************************** @@ -82,53 +82,310 @@ struct lio_threadparm_s ****************************************************************************/ /**************************************************************************** - * Name: lio_dolistio + * Name: lio_checkio * * Description: - * This function executes the I/O list. If lio_listio() is call with mode - * equal to LIO_WAIT, then this function will be called directly from - * lio_listio(). If lio_listio() is called with mode equal to LIO_NOWAIT, - * this function will be called from lio_thread. In either case, this - * function will not return until all of the I/O is the list has completed. + * Check if all I/O operations in the list are complete. * * Input Parameters: * list - The list of I/O operations to be performed * nent - The number of elements in the list * * Returned Value: - * Zero if the I/O list was complete successfully. Otherwise, a negated - * errno value is returned. NOTE that the errno is not set here because - * we may be running asynchronously on a different thread from the caller. + * Zero (OK) is returned if all I/O completed successfully. + * -EINPROGRESS is returned if one or more I/Os have not yet completed. + * The negated errno value if first error noted in the case where all I/O + * completed but one or more I/Os completed with an error. + * + * Assumptions: + * The scheduler is locked and no I/O can complete asynchronously with + * the logic in this function. + * + ****************************************************************************/ + +static int lio_checkio(FAR struct aiocb * const *list, int nent) +{ + FAR struct aiocb *aiocbp; + int ret; + int i; + + ret = OK; /* Assume success */ + + /* Check each entry in the list. Break out of the loop if any entry + * has not completed. + */ + + for (i = 0; i < nent; i++) + { + /* Skip over NULL entries */ + + aiocbp = list[i]; + if (aiocbp) + { + /* Check if the I/O has completed */ + + if (aiocbp->aio_result == -EINPROGRESS) + { + /* No.. return -EINPROGRESS */ + + return -EINPROGRESS; + } + + /* Check for an I/O error */ + + else if (aiocbp->aio_result < 0 && ret == OK) + { + /* Some other error other than -EINPROGRESS */ + + ret = aiocbp->aio_result; + } + } + } + + /* All of the I/Os have completed */ + + return ret; +} + +/**************************************************************************** + * Name: lio_sighandler + * + * Description: + * Handle the SIGPOLL signal. + * + * Input Parameters: + * signo - The number of the signal that we caught (SIGPOLL) + * info - Information accompanying the signal + * context - Not used in NuttX + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void lio_sighandler(int signo, siginfo_t *info, void *ucontext) +{ + FAR struct aiocb *aiocbp; + FAR struct lio_sighand_s *sighand; + int ret; + + DEBUGASSERT(signo == SIGPOLL && info); + + /* The info structure should contain a pointer to the AIO control block */ + + aiocbp = (FAR struct aiocb *)info->si_value.sival_ptr; + DEBUGASSERT(aiocbp && aiocbp->aio_result != -EINPROGRESS); + + /* Recover our private data from the AIO control block */ + + sighand = (FAR struct lio_sighand_s *)aiocbp->aio_priv; + DEBUGASSERT(sighand && sighand->list); + aiocbp->aio_priv = NULL; + + /* Prevent any asynchronous I/O completions while the signal handler runs */ + + sched_lock(); + + /* Check if all of the pending I/O has completed */ + + ret = lio_checkio(sighand->list, sighand->nent); + if (ret != -EINPROGRESS) + { + /* All pending I/O has completed */ + /* Restore the signal handler */ + + (void)sigaction(SIGPOLL, &sighand->oact, NULL); + + /* Restore the sigprocmask */ + + (void)sigprocmask(SIG_SETMASK, &sighand->oprocmask, NULL); + + /* Signal the client */ + + if (sighand->sig->sigev_notify == SIGEV_SIGNAL) + { +#ifdef CONFIG_CAN_PASS_STRUCTS + (void)sigqueue(sighand->pid, sighand->sig->sigev_signo, + sighand->sig->sigev_value); +#else + (void)sigqueue(sighand->aio_pid, sighand->sig.sigev_sign, + sighand->sig->sigev_value.sival_ptr); +#endif + } + + /* And free the container */ + + lib_free(sighand); + } + + sched_unlock(); +} + +/**************************************************************************** + * Name: lio_sigsetup + * + * Description: + * Setup a signal handler to detect when until all I/O completes. + * + * Input Parameters: + * list - The list of I/O operations to be performed + * nent - The number of elements in the list + * + * Returned Value: + * Zero (OK) is returned if all I/O completed successfully; Otherwise, a + * negated errno value is returned corresponding to the first error + * detected. + * + * Assumptions: + * The scheduler is locked and no I/O can complete asynchronously with + * the logic in this function. * ****************************************************************************/ -static int lio_dolistio(FAR struct aiocb *const *list, int nent) +static int lio_sigsetup(FAR struct aiocb * const *list, int nent, + FAR struct sigevent *sig) { -#warning Missing logic - return -ENOSYS; + FAR struct aiocb *aiocbp; + FAR struct lio_sighand_s *sighand; + sigset_t sigset; + struct sigaction act; + int status; + int i; + + /* Allocate a structure to pass data to the signal handler */ + + sighand = (FAR struct lio_sighand_s *)lib_zalloc(sizeof(struct lio_sighand_s)); + if (!sighand) + { + fdbg("ERROR: lib_zalloc failed\n"); + return -ENOMEM; + } + + /* Initialize the allocated structure */ + + sighand->list = list; + sighand->sig = sig; + sighand->nent = nent; + sighand->pid = getpid(); + + /* Save this structure as the private data attached to each aiocb */ + + for (i = 0; i < nent; i++) + { + /* Skip over NULL entries in the list */ + + aiocbp = list[i]; + if (aiocbp) + { + FAR void *priv = NULL; + + /* Check if I/O is pending for this entry */ + + if (aiocbp->aio_result == -EINPROGRESS) + { + priv = (FAR void *)sighand; + } + + aiocbp->aio_priv = priv; + } + } + + /* Make sure that SIGPOLL is not blocked */ + + (void)sigemptyset(&sigset); + (void)sigaddset(&sigset, SIGPOLL); + status = sigprocmask(SIG_UNBLOCK, &sigset, &sighand->oprocmask); + if (status != OK) + { + int errcode = get_errno(); + fdbg("ERROR sigprocmask failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + /* Attach our signal handler */ + + printf("waiter_main: Registering signal handler\n" ); + act.sa_sigaction = lio_sighandler; + act.sa_flags = SA_SIGINFO; + + (void)sigfillset(&act.sa_mask); + (void)sigdelset(&act.sa_mask, SIGPOLL); + + status = sigaction(SIGPOLL, &act, &sighand->oact); + if (status != OK) + { + int errcode = get_errno(); + fdbg("ERROR sigaction failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + + return OK; } /**************************************************************************** - * Name: lio_thread + * Name: lio_waitall * * Description: - * When lio_listio() is called with LIO_NOWAIT, list I/O processing will - * occur on this thread. + * Wait for all I/O operations in the list to be complete. * * Input Parameters: - * arg - An instance of struct lio_thread_parm_s cast to pthread_addr_.t + * list - The list of I/O operations to be performed + * nent - The number of elements in the list * * Returned Value: - * NULL is returned. + * Zero (OK) is returned if all I/O completed successfully; Otherwise, a + * negated errno value is returned corresponding to the first error + * detected. + * + * Assumptions: + * The scheduler is locked and no I/O can complete asynchronously with + * the logic in this function. * ****************************************************************************/ -static pthread_addr_t lio_thread(pthread_addr_t arg) +static int lio_waitall(FAR struct aiocb * const *list, int nent) { - FAR struct lio_threadparm_s *parms = (FAR struct lio_threadparm_s *)arg; - DEBUGASSERT(parms && parms->list); -#warning Missing logic - return NULL; + sigset_t set; + int ret; + + /* Loop until all I/O completes */ + + for (;;) + { + /* Check if all I/O has completed */ + + ret = lio_checkio(list, nent); + if (ret != -EINPROGRESS) + { + /* All I/O has completed.. We are finished. */ + + return ret; + } + + /* Then wait for SIGPOLL -- indefinitely. + * + * NOTE: If completion of the I/O causes other signals to be generated + * first, then this will wake up and return EINTR instead of success. + */ + + sigemptyset(&set); + sigaddset(&set, SIGPOLL); + + ret = sigwaitinfo(&set, NULL); + if (ret < 0) + { + /* The most likely reason that we would get here is because some + * unrelated signal has been received. + */ + + int errcode = get_errno(); + fdbg("ERROR: sigwaitinfo failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + return -errcode; + } + } } /**************************************************************************** @@ -279,8 +536,195 @@ static pthread_addr_t lio_thread(pthread_addr_t arg) int lio_listio(int mode, FAR struct aiocb *const list[], int nent, FAR struct sigevent *sig) { -#warning Missing logic - return -ENOSYS; + FAR struct aiocb *aiocbp; + int nqueued; + int errcode; + int retcode; + int status; + int ret; + int i; + + DEBUGASSERT(mode == LIO_WAIT || mode == LIO_NOWAIT); + DEBUGASSERT(list); + + nqueued = 0; /* No I/O operations yet queued */ + ret = OK; /* Assume success */ + + /* Lock the scheduler so that no I/O events can complete on the worker + * thread until we set our wait set up. Pre-emption will, of course, be + * re-enabled while we are waiting for the signal. + */ + + sched_lock(); + + /* Submit each asynchronous I/O operation in the list, skipping over NULL + * entries. + */ + + for (i = 0; i < nent; i++) + { + /* Skip over NULL entries */ + + aiocbp = list[i]; + if (aiocbp) + { + /* Submit the operation according to its opcode */ + + status = OK; + switch (aiocbp->aio_lio_opcode) + { + + case LIO_NOP: + { + /* Mark the do-nothing operation complete */ + + aiocbp->aio_result = OK; + } + break; + + case LIO_READ: + case LIO_WRITE: + { + if (aiocbp->aio_lio_opcode == LIO_READ) + { + /* Submit the asynchronous read operation */ + + status = aio_read(aiocbp); + } + else + { + /* Submit the asynchronous write operation */ + + status = aio_write(aiocbp); + } + + if (status < 0) + { + /* Failed to queue the I/O. Set up the error return. */ + + errcode = get_errno(); + fdbg("ERROR: aio_read/write failed: %d\n", errcode); + DEBUGASSERT(errocode > 0); + aiocbp->aio_result = -errcode; + ret = ERROR; + } + else + { + /* Increment the count of successfully queue operations */ + + nqueued++; + } + } + break; + + default: + { + /* Make the invalid operation complete with an error */ + + fdbg("ERROR: Unrecognized opcode: %d\n", aiocbp->aio_lio_opcode); + aiocbp->aio_result = -EINVAL; + ret = ERROR; + } + break; + } + } + } + + /* If there was any failure in queuing the I/O, EIO will be returned */ + + retcode = EIO; + + /* Now what? Three possibilities: + * + * Case 1: mode == LIO_WAIT + * + * Ignore the sig argument; Do no return until all I/O completes. + */ + + if (mode == LIO_WAIT) + { + /* Don't wait if all if no I/O was queue */ + + if (nqueued > 0) + { + /* Wait until all I/O completes. The scheduler will be unlocked + * while we are waiting. + */ + + status = lio_waitall(list, nent); + if (status < 0 && ret != OK) + { + /* Something bad happened while waiting and this is the first + * error to be reported. + */ + + retcode = -status; + ret = ERROR; + } + } + } + + /* Case 2: mode == LIO_NOWAIT and sig != NULL + * + * If any I/O was queued, then setup to signal the caller when all of + * the transfers complete. + * + * If no I/O was queue, then we I suppose that we need to signal the + * caller ourself? + */ + + else if (sig && sig->sigev_notify == SIGEV_SIGNAL) + { + if (nqueued > 0) + { + /* Setup a signal handler to detect when until all I/O completes. */ + + status = lio_sigsetup(list, nent, sig); + if (status < 0 && ret != OK) + { + /* Something bad happened while setting up the signal and this + * is the first error to be reported. + */ + + retcode = -status; + ret = ERROR; + } + } + else + { +#ifdef CONFIG_CAN_PASS_STRUCTS + status = sigqueue(getpid(), sig->sigev_signo, + sig->sigev_value); +#else + status = sigqueue(getpid(), sig->sigev_signo, + sig->sigev_value.sival_ptr); +#endif + + if (status < 0 && ret != OK) + { + /* Something bad happened while signalling ourself and this is + * the first error to be reported. + */ + + retcode = get_errno(); + ret = ERROR; + } + } + } + + /* Case 3: mode == LIO_NOWAIT and sig == NULL + * + * Just return now. + */ + + sched_unlock(); + if (ret < 0) + { + set_errno(retcode); + return ERROR; + } + + return OK; } -#endif /* CONFIG_LIBC_AIO && !CONFIG_PTHREAD_DISABLE */ +#endif /* CONFIG_LIBC_AIO */ |