diff --git a/fs/aio/CMakeLists.txt b/fs/aio/CMakeLists.txt index 835a379438d9c..1ac0d3b484f7d 100644 --- a/fs/aio/CMakeLists.txt +++ b/fs/aio/CMakeLists.txt @@ -31,6 +31,7 @@ if(CONFIG_FS_AIO) aio_queue.c aio_read.c aio_signal.c - aio_write.c) + aio_write.c + lio_listio.c) endif() diff --git a/fs/aio/Kconfig b/fs/aio/Kconfig index fa09b9a38cc97..d3feb4a58c2ee 100644 --- a/fs/aio/Kconfig +++ b/fs/aio/Kconfig @@ -11,6 +11,18 @@ config FS_AIO Enable support for asynchronous I/O. This selection enables the interfaces declared in include/aio.h. +config FS_AIO_LISTIO_MAX + int "Maximum number of AIO operations in listio" + default 256 + ---help--- + This option sets the maximum number of asynchronous I/O (AIO) operations + that can be submitted in a single call to lio_listio(). + + This value defines the upper limit for the 'nent' parameter in + lio_listio(mode, aiocb_list, nent, sevp). Increasing this value allows + more operations to be queued. + Default: 10 + if FS_AIO config FS_NAIOC diff --git a/fs/aio/Make.defs b/fs/aio/Make.defs index 48862477e499c..aae95a5a76f69 100644 --- a/fs/aio/Make.defs +++ b/fs/aio/Make.defs @@ -25,7 +25,7 @@ ifeq ($(CONFIG_FS_AIO),y) # Add the asynchronous I/O C files to the build CSRCS += aio_cancel.c aioc_contain.c aio_fsync.c aio_initialize.c -CSRCS += aio_queue.c aio_read.c aio_signal.c aio_write.c +CSRCS += aio_queue.c aio_read.c aio_signal.c aio_write.c lio_listio.c # Add the asynchronous I/O directory to the build diff --git a/fs/aio/aio.h b/fs/aio/aio.h index dbe0b2fbb905b..b0caf9e0cf8f4 100644 --- a/fs/aio/aio.h +++ b/fs/aio/aio.h @@ -239,6 +239,9 @@ int aio_queue(FAR struct aio_container_s *aioc, worker_t worker); int aio_signal(pid_t pid, FAR struct aiocb *aiocbp); +int aio_read_internal(FAR struct aiocb *aiocbp); +int aio_write_internal(FAR struct aiocb *aiocbp); + #undef EXTERN #if defined(__cplusplus) } diff --git a/fs/aio/aio_cancel.c b/fs/aio/aio_cancel.c index ceb31dd4a4106..ca26f5a1d66b8 100644 --- a/fs/aio/aio_cancel.c +++ b/fs/aio/aio_cancel.c @@ -32,6 +32,7 @@ #include #include +#include #include "aio/aio.h" @@ -83,18 +84,23 @@ int aio_cancel(int fildes, FAR struct aiocb *aiocbp) { - if (fildes < 0) - { - set_errno(EBADF); - return ERROR; - } - FAR struct aio_container_s *aioc; FAR struct aio_container_s *next; + FAR struct file *filep; + pid_t pid; int status; int ret; + ret = file_get(fildes, &filep); + if (ret < 0) + { + set_errno(-ret); + return ERROR; + } + + file_put(filep); + /* Check if a non-NULL aiocbp was provided */ /* Lock the scheduler so that no I/O events can complete on the worker @@ -165,14 +171,16 @@ int aio_cancel(int fildes, FAR struct aiocb *aiocbp) { /* No aiocbp.. cancel all outstanding I/O for the fildes */ - next = (FAR struct aio_container_s *)g_aio_pending.head; - do + for (aioc = (FAR struct aio_container_s *)g_aio_pending.head; + aioc; + aioc = next) { - /* Find the next container with this AIO control block */ + next = (FAR struct aio_container_s *)aioc->aioc_link.flink; - for (aioc = next; - aioc && aioc->aioc_aiocbp->aio_fildes != fildes; - aioc = (FAR struct aio_container_s *)aioc->aioc_link.flink); + if (aioc->aioc_aiocbp->aio_fildes != fildes) + { + continue; + } /* Did we find the container? We should; the aio_result says * that the transfer is pending. If not we return AIO_ALLDONE. @@ -195,8 +203,6 @@ int aio_cancel(int fildes, FAR struct aiocb *aiocbp) * transfers */ - next = - (FAR struct aio_container_s *)aioc->aioc_link.flink; pid = aioc->aioc_pid; aiocbp = aioc_decant(aioc); DEBUGASSERT(aiocbp); @@ -217,7 +223,6 @@ int aio_cancel(int fildes, FAR struct aiocb *aiocbp) } } } - while (aioc); } aio_unlock(); diff --git a/fs/aio/aio_fsync.c b/fs/aio/aio_fsync.c index cc4ad40dbbbe7..6fbc200786d27 100644 --- a/fs/aio/aio_fsync.c +++ b/fs/aio/aio_fsync.c @@ -79,7 +79,7 @@ static void aio_fsync_worker(FAR void *arg) #ifdef CONFIG_PRIORITY_INHERITANCE prio = aioc->aioc_prio; #endif - aiocbp = aioc_decant(aioc); + aiocbp = aioc->aioc_aiocbp; /* Perform the fsync using aioc_filep */ @@ -97,6 +97,7 @@ static void aio_fsync_worker(FAR void *arg) /* Signal the client */ aio_signal(pid, aiocbp); + aioc_decant(aioc); #ifdef CONFIG_PRIORITY_INHERITANCE /* Restore the low priority worker thread default priority */ @@ -193,19 +194,29 @@ int aio_fsync(int op, FAR struct aiocb *aiocbp) FAR struct aio_container_s *aioc; int ret; - if (op != O_SYNC) + /* SUSv2 / POSIX Issue 5 specified that a NULL aiocbp produces no + * status through aiocbp and no completion signal. POSIX Issue 6 removed + * that special case, so reject NULL defensively. + */ + + if (op != O_SYNC || aiocbp == NULL) { set_errno(EINVAL); return ERROR; } - DEBUGASSERT(aiocbp); - /* The result -EINPROGRESS means that the transfer has not yet completed */ sigwork_init(&aiocbp->aio_sigwork); aiocbp->aio_result = -EINPROGRESS; - aiocbp->aio_priv = NULL; + + /* Clear lio_link so list_in_list() returns false and aio_signal() skips + * the lio_listio path; list_initialize() would leave prev non-NULL, so + * list_in_list() wrongly returns true and aio_signal() notifies through + * the uninitialized lio_sigevent/lio_sigwork. + */ + + list_clear_node(&aiocbp->lio_link); /* Create a container for the AIO control block. This may cause us to * block if there are insufficient resources to satisfy the request. diff --git a/fs/aio/aio_read.c b/fs/aio/aio_read.c index b3e366215e24b..28b6f12f1cde4 100644 --- a/fs/aio/aio_read.c +++ b/fs/aio/aio_read.c @@ -79,7 +79,7 @@ static void aio_read_worker(FAR void *arg) #ifdef CONFIG_PRIORITY_INHERITANCE prio = aioc->aioc_prio; #endif - aiocbp = aioc_decant(aioc); + aiocbp = aioc->aioc_aiocbp; /* Perform the file read using: * @@ -106,6 +106,7 @@ static void aio_read_worker(FAR void *arg) /* Signal the client */ aio_signal(pid, aiocbp); + aioc_decant(aioc); #ifdef CONFIG_PRIORITY_INHERITANCE /* Restore the low priority worker thread default priority */ @@ -214,46 +215,30 @@ static void aio_read_worker(FAR void *arg) * ****************************************************************************/ -int aio_read(FAR struct aiocb *aiocbp) +int aio_read_internal(FAR struct aiocb *aiocbp) { FAR struct aio_container_s *aioc; int ret; DEBUGASSERT(aiocbp); - if (aiocbp->aio_reqprio < 0) - { - set_errno(EINVAL); - return ERROR; - } - - if (aiocbp->aio_fildes < 0) - { - /* the EBADF should be collected by aio_error(), we need return OK at - * here - */ - - aiocbp->aio_result = -EBADF; - return OK; - } - /* for aio_read, the aio_offset should be large or equal than 0 */ - if (aiocbp->aio_offset < 0) + if (aiocbp->aio_offset < 0 || aiocbp->aio_reqprio < 0) { - /* the EINVAL should be collected by aio_error(), we need to return OK - * here + /* the EINVAL should be collected by aio_error(), we need to return + * ERROR here */ aiocbp->aio_result = -EINVAL; - return OK; + set_errno(EINVAL); + return ERROR; } /* The result -EINPROGRESS means that the transfer has not yet completed */ sigwork_init(&aiocbp->aio_sigwork); aiocbp->aio_result = -EINPROGRESS; - aiocbp->aio_priv = NULL; /* Create a container for the AIO control block. This may cause us to * block if there are insufficient resources to satisfy the request. @@ -265,7 +250,7 @@ int aio_read(FAR struct aiocb *aiocbp) /* The errno has already been set (probably EBADF) */ aiocbp->aio_result = -get_errno(); - return ERROR; + return OK; } /* Defer the work to the worker thread */ @@ -282,4 +267,20 @@ int aio_read(FAR struct aiocb *aiocbp) return OK; } +int aio_read(FAR struct aiocb *aiocbp) +{ + if (aiocbp == NULL) + { + set_errno(EINVAL); + return ERROR; + } + + /* Clear lio_link so aio_signal() skips the lio_listio path (see + * aio_fsync.c); list_initialize() would wrongly leave prev non-NULL. + */ + + list_clear_node(&aiocbp->lio_link); + return aio_read_internal(aiocbp); +} + #endif /* CONFIG_FS_AIO */ diff --git a/fs/aio/aio_signal.c b/fs/aio/aio_signal.c index 65ca20484ce85..c8c734d12dd15 100644 --- a/fs/aio/aio_signal.c +++ b/fs/aio/aio_signal.c @@ -98,6 +98,32 @@ int aio_signal(pid_t pid, FAR struct aiocb *aiocbp) } } + if (list_in_list(&aiocbp->lio_link)) + { + /* This I/O is queued by lio_listio, remove this I/O from the list, + * signal the client when all I/O is completed + */ + + aio_lock(); + status = list_is_empty(&aiocbp->lio_link); + list_delete(&aiocbp->lio_link); + aio_unlock(); + + if (status) + { + status = nxsig_notification(pid, &aiocbp->lio_sigevent, SI_ASYNCIO, + &aiocbp->lio_sigwork); + if (status < 0) + { + ferr("ERROR: nxsig_notification failed: %d\n", status); + if (ret >= OK) + { + ret = status; + } + } + } + } + /* Make sure that errno is set correctly on return */ if (ret < 0) diff --git a/fs/aio/aio_write.c b/fs/aio/aio_write.c index 0ba11dc357ea9..81fcd37f8b25b 100644 --- a/fs/aio/aio_write.c +++ b/fs/aio/aio_write.c @@ -82,7 +82,7 @@ static void aio_write_worker(FAR void *arg) #ifdef CONFIG_PRIORITY_INHERITANCE prio = aioc->aioc_prio; #endif - aiocbp = aioc_decant(aioc); + aiocbp = aioc->aioc_aiocbp; /* Call fcntl(F_GETFL) to get the file open mode. */ @@ -134,6 +134,7 @@ static void aio_write_worker(FAR void *arg) /* Signal the client */ aio_signal(pid, aiocbp); + aioc_decant(aioc); #ifdef CONFIG_PRIORITY_INHERITANCE /* Restore the low priority worker thread default priority */ @@ -244,7 +245,7 @@ static void aio_write_worker(FAR void *arg) * ****************************************************************************/ -int aio_write(FAR struct aiocb *aiocbp) +int aio_write_internal(FAR struct aiocb *aiocbp) { FAR struct aio_container_s *aioc; int ret; @@ -252,35 +253,19 @@ int aio_write(FAR struct aiocb *aiocbp) DEBUGASSERT(aiocbp); - if (aiocbp->aio_reqprio < 0) + if (aiocbp->aio_offset < 0 || aiocbp->aio_reqprio < 0) { + aiocbp->aio_result = -EINVAL; set_errno(EINVAL); return ERROR; } - if (aiocbp->aio_offset < 0) - { - aiocbp->aio_result = -EINVAL; - return OK; - } - - if (aiocbp->aio_fildes < 0) - { - /* for EBADF, the aio_write do not return error directly, but using - * aio_error to return this error code - */ - - aiocbp->aio_result = -EBADF; - return OK; - } - /* the aio_fildes that transferred in may be opened with O_RDONLY, for this - * case, we need to return OK directly, and using the aio_error to collect - * the EBADF error code + * case, we need to return OK directly, and set the EBADF error code */ flags = fcntl(aiocbp->aio_fildes, F_GETFL); - if ((flags & O_ACCMODE) == O_RDONLY) + if (flags == ERROR || (flags & O_ACCMODE) == O_RDONLY) { aiocbp->aio_result = -EBADF; return OK; @@ -290,7 +275,6 @@ int aio_write(FAR struct aiocb *aiocbp) sigwork_init(&aiocbp->aio_sigwork); aiocbp->aio_result = -EINPROGRESS; - aiocbp->aio_priv = NULL; /* Create a container for the AIO control block. This may cause us to * block if there are insufficient resources to satisfy the request. @@ -302,7 +286,7 @@ int aio_write(FAR struct aiocb *aiocbp) /* The errno has already been set (probably EBADF) */ aiocbp->aio_result = -get_errno(); - return ERROR; + return OK; } /* Defer the work to the worker thread */ @@ -319,4 +303,20 @@ int aio_write(FAR struct aiocb *aiocbp) return OK; } +int aio_write(FAR struct aiocb *aiocbp) +{ + if (aiocbp == NULL) + { + set_errno(EINVAL); + return ERROR; + } + + /* Clear lio_link so aio_signal() skips the lio_listio path (see + * aio_fsync.c); list_initialize() would wrongly leave prev non-NULL. + */ + + list_clear_node(&aiocbp->lio_link); + return aio_write_internal(aiocbp); +} + #endif /* CONFIG_FS_AIO */ diff --git a/libs/libc/aio/lio_listio.c b/fs/aio/lio_listio.c similarity index 66% rename from libs/libc/aio/lio_listio.c rename to fs/aio/lio_listio.c index 42b6f3cfa9c03..f64a8aa30c653 100644 --- a/libs/libc/aio/lio_listio.c +++ b/fs/aio/lio_listio.c @@ -1,5 +1,5 @@ /**************************************************************************** - * libs/libc/aio/lio_listio.c + * fs/aio/lio_listio.c * * SPDX-License-Identifier: Apache-2.0 * @@ -36,25 +36,10 @@ #include #include -#include "libc.h" #include "aio/aio.h" #ifdef CONFIG_FS_AIO -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct lio_sighand_s -{ - 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 */ -}; - /**************************************************************************** * Private Functions ****************************************************************************/ @@ -81,7 +66,8 @@ struct lio_sighand_s * ****************************************************************************/ -static int lio_checkio(FAR struct aiocb * const *list, int nent) +static int lio_checkio(FAR struct aiocb *restrict const *restrict list, + int nent) { FAR struct aiocb *aiocbp; int ret; @@ -125,173 +111,6 @@ static int lio_checkio(FAR struct aiocb * const *list, int nent) 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; - - /* 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 */ - - sigaction(SIGPOLL, &sighand->oact, NULL); - - /* Restore the sigprocmask */ - - sigprocmask(SIG_SETMASK, &sighand->oprocmask, NULL); - - /* Signal the client */ - - DEBUGVERIFY(nxsig_notification(sighand->pid, &sighand->sig, - SI_ASYNCIO, &aiocbp->aio_sigwork)); - - /* And free the container */ - - lib_free(sighand); - } -} - -/**************************************************************************** - * 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_sigsetup(FAR struct aiocb * const *list, int nent, - FAR struct sigevent *sig) -{ - FAR struct aiocb *aiocbp; - struct lio_sighand_s sighand; - sigset_t set; - struct sigaction act; - int status; - int i; - - /* Initialize the allocated structure */ - - memset(&sighand, 0, sizeof(struct lio_sighand_s)); - sighand.list = list; - sighand.sig = *sig; - sighand.nent = nent; - sighand.pid = _SCHED_GETPID(); - - /* Make sure that SIGPOLL is not blocked */ - - sigemptyset(&set); - sigaddset(&set, SIGPOLL); - status = sigprocmask(SIG_UNBLOCK, &set, &sighand.oprocmask); - if (status != OK) - { - int errcode = get_errno(); - ferr("ERROR sigprocmask failed: %d\n", errcode); - DEBUGASSERT(errcode > 0); - return -errcode; - } - - /* Attach our signal handler */ - - finfo("Registering signal handler\n"); - - act.sa_sigaction = lio_sighandler; - act.sa_flags = SA_SIGINFO; - - sigfillset(&act.sa_mask); - sigdelset(&act.sa_mask, SIGPOLL); - - status = sigaction(SIGPOLL, &act, &sighand.oact); - if (status != OK) - { - int errcode = get_errno(); - - ferr("ERROR sigaction failed: %d\n", errcode); - - DEBUGASSERT(errcode > 0); - return -errcode; - } - - /* 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 = lib_zalloc(sizeof(struct lio_sighand_s)); - if (!priv) - { - ferr("ERROR: lib_zalloc failed\n"); - return -ENOMEM; - } - - memcpy(priv, (FAR void *)&sighand, - sizeof(struct lio_sighand_s)); - } - - aiocbp->aio_priv = priv; - } - } - - return OK; -} - /**************************************************************************** * Name: lio_waitall * @@ -313,7 +132,8 @@ static int lio_sigsetup(FAR struct aiocb * const *list, int nent, * ****************************************************************************/ -static int lio_waitall(FAR struct aiocb * const *list, int nent) +static int lio_waitall(FAR struct aiocb *restrict const *restrict list, + int nent) { sigset_t set; int ret; @@ -502,10 +322,11 @@ static int lio_waitall(FAR struct aiocb * const *list, int nent) * ****************************************************************************/ -int lio_listio(int mode, FAR struct aiocb * const list[], int nent, - FAR struct sigevent *sig) +int lio_listio(int mode, FAR struct aiocb *restrict const list[restrict], + int nent, FAR struct sigevent *restrict sig) { FAR struct aiocb *aiocbp = NULL; + struct list_node head; int nqueued; int errcode; int retcode; @@ -513,7 +334,8 @@ int lio_listio(int mode, FAR struct aiocb * const list[], int nent, int ret; int i; - if (mode != LIO_WAIT && mode != LIO_NOWAIT) + if (nent < 0 || nent > AIO_LISTIO_MAX || + (mode != LIO_WAIT && mode != LIO_NOWAIT)) { set_errno(EINVAL); return ERROR; @@ -524,12 +346,31 @@ int lio_listio(int mode, FAR struct aiocb * const list[], int nent, 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. - */ + if (mode == LIO_NOWAIT && sig) + { + list_initialize(&head); + } + + for (i = 0; i < nent; i++) + { + aiocbp = list[i]; + if (aiocbp && aiocbp->aio_lio_opcode != LIO_NOP) + { + if (mode == LIO_NOWAIT && sig) + { + list_add_head(&head, &(aiocbp->lio_link)); + aiocbp->lio_sigevent = *sig; + } + else + { + /* Not part of a lio_listio batch: clear lio_link so that + * aio_signal() skips the lio_listio completion path. + */ - sched_lock(); + list_clear_node(&aiocbp->lio_link); + } + } + } /* Submit each asynchronous I/O operation in the list, skipping over NULL * entries. @@ -540,67 +381,80 @@ int lio_listio(int mode, FAR struct aiocb * const list[], int nent, /* Skip over NULL entries */ aiocbp = list[i]; - if (aiocbp) + if (!aiocbp) { - /* Submit the operation according to its opcode */ + continue; + } + + /* Submit the operation according to its opcode */ - status = OK; - switch (aiocbp->aio_lio_opcode) + status = OK; + switch (aiocbp->aio_lio_opcode) + { + case LIO_NOP: { - 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(); - ferr("ERROR: aio_read/write failed: %d\n", errcode); - DEBUGASSERT(errcode > 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 */ - - ferr("ERROR: Unrecognized opcode: %d\n", - aiocbp->aio_lio_opcode); - aiocbp->aio_result = -EINVAL; - ret = ERROR; - } - break; + /* 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_internal(aiocbp); + } + else + { + /* Submit the asynchronous write operation */ + + status = aio_write_internal(aiocbp); + } + + if (status < 0) + { + /* Failed to queue the I/O. Set up the error return. */ + + errcode = get_errno(); + ferr("ERROR: aio_read/write failed: %d\n", errcode); + DEBUGASSERT(errcode > 0); + aiocbp->aio_result = -errcode; + ret = ERROR; + } + + if (status < 0 || aiocbp->aio_result == -EBADF || + aiocbp->aio_result == -EINVAL) + { + if (mode == LIO_NOWAIT && sig) + { + aio_lock(); + list_delete(&aiocbp->lio_link); + aio_unlock(); + } + } + else + { + /* Increment the count of successfully queue operations */ + + nqueued++; + } + } + break; + + default: + { + /* Make the invalid operation complete with an error */ + + ferr("ERROR: Unrecognized opcode: %d\n", + aiocbp->aio_lio_opcode); + aiocbp->aio_result = -EINVAL; + ret = ERROR; + } + break; } } @@ -649,33 +503,48 @@ int lio_listio(int mode, FAR struct aiocb * const list[], int nent, else if (sig != NULL) { - if (nqueued > 0) + aio_lock(); + status = list_is_empty(&head); + list_delete(&head); + aio_unlock(); + + if (status) { - /* Setup a signal handler to detect when until all I/O completes. */ + /* head is empty meaning all I/O completed before head was + * removed, so manually signal the client + */ - status = lio_sigsetup(list, nent, sig); - if (status < 0 && ret == OK) + /* Find a non-NULL aiocbp */ + + if (aiocbp == NULL) { - /* Something bad happened while setting up the signal and this - * is the first error to be reported. - */ + for (i = 0; i < nent; i++) + { + if (list[i]) + { + aiocbp = list[i]; + break; + } + } - retcode = -status; - ret = ERROR; + if (aiocbp == NULL) + { + goto out; + } } - } - else - { - status = nxsig_notification(_SCHED_GETPID(), sig, - SI_ASYNCIO, &aiocbp->aio_sigwork); + + status = nxsig_notification(nxsched_getpid(), + &aiocbp->lio_sigevent, + SI_ASYNCIO, + &aiocbp->lio_sigwork); if (status < 0 && ret == OK) { - /* Something bad happened while performing the notification - * and this is the first error to be reported. + /* Something bad happened while signal the client and + * this is the first error to be reported. */ - retcode = -status; - ret = ERROR; + retcode = -status; + ret = ERROR; } } } @@ -685,7 +554,7 @@ int lio_listio(int mode, FAR struct aiocb * const list[], int nent, * Just return now. */ - sched_unlock(); +out: if (ret < 0) { set_errno(retcode); diff --git a/include/aio.h b/include/aio.h index 5f508028eeca8..81dcddd962b71 100644 --- a/include/aio.h +++ b/include/aio.h @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -133,7 +134,9 @@ struct aiocb struct sigwork_s aio_sigwork; /* Signal work */ volatile ssize_t aio_result; /* Support for aio_error() and aio_return() */ - FAR void *aio_priv; /* Used by signal handlers */ + struct list_node lio_link; /* Make list of aiocb for lio_listio() */ + struct sigevent lio_sigevent; /* Sigevent for lio_listio() */ + struct sigwork_s lio_sigwork; /* Signal work for lio_listio() */ }; /**************************************************************************** @@ -152,16 +155,16 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -int aio_cancel(int fildes, FAR struct aiocb *aiocbp); -int aio_error(FAR const struct aiocb *aiocbp); -int aio_fsync(int op, FAR struct aiocb *aiocbp); -int aio_read(FAR struct aiocb *aiocbp); -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); -int lio_listio(int mode, FAR struct aiocb * const list[], int nent, - FAR struct sigevent *sig); +int aio_cancel(int, FAR struct aiocb *); +int aio_error(FAR const struct aiocb *); +int aio_fsync(int, FAR struct aiocb *); +int aio_read(FAR struct aiocb *); +ssize_t aio_return(FAR struct aiocb *); +int aio_suspend(FAR const struct aiocb * const[], int, + FAR const struct timespec *); +int aio_write(FAR struct aiocb *); +int lio_listio(int, FAR struct aiocb *restrict const[restrict], int, + FAR struct sigevent *restrict); #undef EXTERN #ifdef __cplusplus diff --git a/include/limits.h b/include/limits.h index 59357fe83f3c6..a2da21497b2ec 100644 --- a/include/limits.h +++ b/include/limits.h @@ -188,7 +188,7 @@ /* Required for asynchronous I/O */ -#define _POSIX_AIO_LISTIO_MAX 2 +#define _POSIX_AIO_LISTIO_MAX CONFIG_FS_AIO_LISTIO_MAX #define _POSIX_AIO_MAX 1 /* Required for POSIX message passing */ diff --git a/libs/libc/aio/CMakeLists.txt b/libs/libc/aio/CMakeLists.txt index dd1ce6fc06ac0..29ab724ac1907 100644 --- a/libs/libc/aio/CMakeLists.txt +++ b/libs/libc/aio/CMakeLists.txt @@ -21,5 +21,5 @@ # ############################################################################## if(CONFIG_FS_AIO) - target_sources(c PRIVATE aio_error.c aio_return.c aio_suspend.c lio_listio.c) + target_sources(c PRIVATE aio_error.c aio_return.c aio_suspend.c) endif() diff --git a/libs/libc/aio/Make.defs b/libs/libc/aio/Make.defs index de770cf04abd5..01714ed47a9fd 100644 --- a/libs/libc/aio/Make.defs +++ b/libs/libc/aio/Make.defs @@ -24,7 +24,7 @@ ifeq ($(CONFIG_FS_AIO),y) # Add the asynchronous I/O C files to the build -CSRCS += aio_error.c aio_return.c aio_suspend.c lio_listio.c +CSRCS += aio_error.c aio_return.c aio_suspend.c # Add the asynchronous I/O directory to the build diff --git a/libs/libc/aio/aio_error.c b/libs/libc/aio/aio_error.c index 8f3a856b22eb1..dced059ba8763 100644 --- a/libs/libc/aio/aio_error.c +++ b/libs/libc/aio/aio_error.c @@ -98,6 +98,17 @@ int aio_error(FAR const struct aiocb *aiocbp) return EINVAL; } + if (aiocbp->aio_offset < 0) + { + return -aiocbp->aio_result; + } + + if (aiocbp->aio_result == -EINVAL) + { + set_errno(EINVAL); + return ERROR; + } + if (aiocbp->aio_result < 0) { return -aiocbp->aio_result; diff --git a/libs/libc/aio/aio_suspend.c b/libs/libc/aio/aio_suspend.c index d15b546f140b9..9b3d8f0e6c97a 100644 --- a/libs/libc/aio/aio_suspend.c +++ b/libs/libc/aio/aio_suspend.c @@ -86,41 +86,65 @@ int aio_suspend(FAR const struct aiocb * const list[], int nent, FAR const struct timespec *timeout) { + struct timespec end; + struct timespec rem; sigset_t set; int ret; int i; DEBUGASSERT(list); - /* Check each entry in the list. Break out of the loop if any entry - * has completed. - */ + if (timeout) + { + clock_gettime(CLOCK_MONOTONIC, &end); + clock_timespec_add(&end, timeout, &end); + timeout = &rem; + } + + sigemptyset(&set); + sigaddset(&set, SIGPOLL); - for (i = 0; i < nent; i++) + for (; ; ) { - /* Check if the I/O has completed */ + /* Check each entry in the list. Break out of the loop if any entry + * has completed. + */ - if (list[i] && list[i]->aio_result != -EINPROGRESS) + for (i = 0; i < nent; i++) { - /* Yes, return success */ + /* Check if the I/O has completed */ + + if (list[i] && list[i]->aio_result != -EINPROGRESS) + { + /* Yes, return success */ - return OK; + return OK; + } } - } - /* 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. - */ + /* 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); + if (timeout) + { + clock_gettime(CLOCK_MONOTONIC, &rem); + clock_timespec_subtract(&end, &rem, &rem); + } + + ret = sigtimedwait(&set, NULL, timeout); + + if (ret < 0) + { + return ERROR; + } + } - ret = sigtimedwait(&set, NULL, timeout); - return ret >= 0 ? OK : ERROR; + return OK; } #endif /* CONFIG_FS_AIO */ diff --git a/libs/libc/libc.csv b/libs/libc/libc.csv index 2e2d13506cfc9..2cf17ac4b1526 100644 --- a/libs/libc/libc.csv +++ b/libs/libc/libc.csv @@ -157,7 +157,7 @@ "labs","stdlib.h","","long int","long int" "lib_dumpbuffer","debug.h","","void","FAR const char *","FAR const uint8_t *","unsigned int" "lib_get_stream","nuttx/tls.h","","FAR struct file_struct *","int" -"lio_listio","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb * const []|FAR struct aiocb * const *","int","FAR struct sigevent *" +"lio_listio","aio.h","defined(CONFIG_FS_AIO)","int","int","FAR struct aiocb *restrict const [restrict]|FAR struct aiocb *restrict const *restrict","int","FAR struct sigevent *restrict" "llabs","stdlib.h","","long long int","long long int" "localtime","time.h","","struct tm *","const time_t *" "localtime_r","time.h","","FAR struct tm *","FAR const time_t *","FAR struct tm *" diff --git a/libs/libc/unistd/lib_sysconf.c b/libs/libc/unistd/lib_sysconf.c index 428482aea52d5..20655721c1bb2 100644 --- a/libs/libc/unistd/lib_sysconf.c +++ b/libs/libc/unistd/lib_sysconf.c @@ -266,6 +266,9 @@ long sysconf(int name) case _SC_THREAD_THREADS_MAX: return UINT8_MAX; + case _SC_AIO_LISTIO_MAX: + return AIO_LISTIO_MAX; + default: #if 0 /* Assume valid but not implemented for the time being */ errcode = EINVAL;