xref: /nrf52832-nimble/rt-thread/components/libc/aio/posix_aio.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017/12/30     Bernard      The first version.
9  */
10 
11 #include <stdint.h>
12 #include <stdio.h>
13 
14 #include <rthw.h>
15 #include <rtdevice.h>
16 #include <rtthread.h>
17 
18 #include <dfs_posix.h>
19 
20 #include "posix_aio.h"
21 
22 struct rt_workqueue* aio_queue = NULL;
23 
24 /**
25  * The aio_cancel() function shall attempt to cancel one or more asynchronous I/O
26  * requests currently outstanding against file descriptor fildes. The aiocbp
27  * argument points to the asynchronous I/O control block for a particular request
28  * to be canceled. If aiocbp is NULL, then all outstanding cancelable asynchronous
29  * I/O requests against fildes shall be canceled.
30  *
31  * Normal asynchronous notification shall occur for asynchronous I/O operations
32  * that are successfully canceled. If there are requests that cannot be canceled,
33  * then the normal asynchronous completion process shall take place for those
34  * requests when they are completed.
35  *
36  * For requested operations that are successfully canceled, the associated error
37  * status shall be set to [ECANCELED] and the return status shall be -1. For
38  * requested operations that are not successfully canceled, the aiocbp shall not
39  * be modified by aio_cancel().
40  *
41  * If aiocbp is not NULL, then if fildes does not have the same value as the file
42  * descriptor with which the asynchronous operation was initiated, unspecified results occur.
43  *
44  * Which operations are cancelable is implementation-defined.
45  */
aio_cancel(int fd,struct aiocb * cb)46 int aio_cancel(int fd, struct aiocb *cb)
47 {
48     rt_err_t ret;
49 
50     if (!cb) return -EINVAL;
51     if (cb->aio_fildes != fd) return -EINVAL;
52 
53     ret = rt_workqueue_cancel_work_sync(aio_queue, &(cb->aio_work));
54     if (ret == RT_EOK)
55     {
56         errno = -ECANCELED;
57         return -1;
58     }
59 
60     return 0;
61 }
62 
63 /**
64  * The aio_error() function shall return the error status associated with the
65  * aiocb structure referenced by the aiocbp argument. The error status for an
66  * asynchronous I/O operation is the errno value that would be set by the corresponding
67  * read(), write(),
68  */
aio_error(const struct aiocb * cb)69 int aio_error (const struct aiocb *cb)
70 {
71     if (cb)
72     {
73         return cb->aio_result;
74     }
75 
76     return -EINVAL;
77 }
78 
79 /**
80  * The aio_fsync() function shall asynchronously perform a file synchronization
81  * operation, as specified by the op argument, for I/O operations associated with
82  * the file indicated by the file descriptor aio_fildes member of the aiocb
83  * structure referenced by the aiocbp argument and queued at the time of the
84  * call to aio_fsync(). The function call shall return when the synchronization
85  * request has been initiated or queued to the file or device (even when the data
86  * cannot be synchronized immediately).
87  *
88  * option: If op is O_DSYNC, all currently queued I/O operations shall be completed
89  * as if by a call to fdatasync(); that is, as defined for synchronized I/O data
90  * integrity completion.
91  *
92  * option: If op is O_SYNC, all currently queued I/O operations shall be completed
93  * as if by a call to fsync(); that is, as defined for synchronized I/O file integrity
94  * completion. If the aio_fsync() function fails, or if the operation queued by
95  * aio_fsync() fails, then outstanding I/O operations are not guaranteed to have
96  * been completed.
97  *
98  * If aio_fsync() succeeds, then it is only the I/O that was queued at the time
99  * of the call to aio_fsync() that is guaranteed to be forced to the relevant
100  * completion state. The completion of subsequent I/O on the file descriptor is
101  * not guaranteed to be completed in a synchronized fashion.
102  *
103  * The aiocbp argument refers to an asynchronous I/O control block. The aiocbp
104  * value may be used as an argument to aio_error() and aio_return() in order to
105  * determine the error status and return status, respectively, of the asynchronous
106  * operation while it is proceeding. When the request is queued, the error status
107  * for the operation is [EINPROGRESS]. When all data has been successfully transferred,
108  * the error status shall be reset to reflect the success or failure of the operation.
109  * If the operation does not complete successfully, the error status for the
110  * operation shall be set to indicate the error. The aio_sigevent member determines
111  * the asynchronous notification to occur as specified in Signal Generation and
112  * Delivery when all operations have achieved synchronized I/O completion. All
113  * other members of the structure referenced by aiocbp are ignored. If the control
114  * block referenced by aiocbp becomes an illegal address prior to asynchronous
115  * I/O completion, then the behavior is undefined.
116  *
117  * If the aio_fsync() function fails or aiocbp indicates an error condition,
118  * data is not guaranteed to have been successfully transferred.
119  */
aio_fync_work(struct rt_work * work,void * work_data)120 static void aio_fync_work(struct rt_work* work, void* work_data)
121 {
122     int result;
123     rt_ubase_t level;
124     struct aiocb *cb = (struct aiocb*)work_data;
125 
126     RT_ASSERT(cb != RT_NULL);
127 
128     result = fsync(cb->aio_fildes);
129     /* modify result */
130     level = rt_hw_interrupt_disable();
131     if (result < 0)
132         cb->aio_result = errno;
133     else
134         cb->aio_result = 0;
135     rt_hw_interrupt_enable(level);
136 
137     return ;
138 }
139 
aio_fsync(int op,struct aiocb * cb)140 int aio_fsync(int op, struct aiocb *cb)
141 {
142     rt_ubase_t level;
143     if (!cb) return -EINVAL;
144 
145     level = rt_hw_interrupt_disable();
146     cb->aio_result = -EINPROGRESS;
147     rt_hw_interrupt_enable(level);
148 
149     rt_work_init(&(cb->aio_work), aio_fync_work, cb);
150     rt_workqueue_dowork(aio_queue, &(cb->aio_work));
151 
152     return 0;
153 }
154 
aio_read_work(struct rt_work * work,void * work_data)155 static void aio_read_work(struct rt_work* work, void* work_data)
156 {
157     int len;
158     rt_ubase_t level;
159     uint8_t *buf_ptr;
160     struct aiocb *cb = (struct aiocb*)work_data;
161 
162     buf_ptr = (uint8_t*)cb->aio_buf;
163 
164     /* seek to offset */
165     lseek(cb->aio_fildes, cb->aio_offset, SEEK_SET);
166     len = read(cb->aio_fildes, &buf_ptr[cb->aio_offset], cb->aio_nbytes);
167 
168     /* modify result */
169     level = rt_hw_interrupt_disable();
170     if (len <= 0)
171         cb->aio_result = errno;
172     else
173         cb->aio_result = len;
174     rt_hw_interrupt_enable(level);
175 
176     return ;
177 }
178 
179 /**
180  * The aio_read() function shall read aiocbp->aio_nbytes from the file associated
181  * with aiocbp->aio_fildes into the buffer pointed to by aiocbp->aio_buf. The
182  * function call shall return when the read request has been initiated or queued
183  * to the file or device (even when the data cannot be delivered immediately).
184  *
185  * If prioritized I/O is supported for this file, then the asynchronous operation
186  * shall be submitted at a priority equal to a base scheduling priority minus
187  * aiocbp->aio_reqprio. If Thread Execution Scheduling is not supported, then
188  * the base scheduling priority is that of the calling process;
189  *
190  * otherwise, the base scheduling priority is that of the calling thread.
191  *
192  * The aiocbp value may be used as an argument to aio_error() and aio_return()
193  * in order to determine the error status and return status, respectively, of
194  * the asynchronous operation while it is proceeding. If an error condition is
195  * encountered during queuing, the function call shall return without having
196  * initiated or queued the request. The requested operation takes place at the
197  * absolute position in the file as given by aio_offset, as if lseek() were called
198  * immediately prior to the operation with an offset equal to aio_offset and a
199  * whence equal to SEEK_SET. After a successful call to enqueue an asynchronous
200  * I/O operation, the value of the file offset for the file is unspecified.
201  *
202  * The aio_sigevent member specifies the notification which occurs when the
203  * request is completed.
204  *
205  * The aiocbp->aio_lio_opcode field shall be ignored by aio_read().
206  *
207  * The aiocbp argument points to an aiocb structure. If the buffer pointed to by
208  * aiocbp->aio_buf or the control block pointed to by aiocbp becomes an illegal
209  * address prior to asynchronous I/O completion, then the behavior is undefined.
210  *
211  * Simultaneous asynchronous operations using the same aiocbp produce undefined
212  * results.
213  *
214  * If synchronized I/O is enabled on the file associated with aiocbp->aio_fildes,
215  * the behavior of this function shall be according to the definitions of synchronized
216  * I/O data integrity completion and synchronized I/O file integrity completion.
217  *
218  * For any system action that changes the process memory space while an asynchronous
219  * I/O is outstanding to the address range being changed, the result of that action
220  * is undefined.
221  *
222  * For regular files, no data transfer shall occur past the offset maximum
223  * established in the open file description associated with aiocbp->aio_fildes.
224  *
225  */
aio_read(struct aiocb * cb)226 int aio_read(struct aiocb *cb)
227 {
228     rt_ubase_t level;
229 
230     if (!cb) return -EINVAL;
231     if (cb->aio_offset < 0) return -EINVAL;
232 
233     level = rt_hw_interrupt_disable();
234     cb->aio_result = -EINPROGRESS;
235     rt_hw_interrupt_enable(level);
236 
237     /* en-queue read work */
238     rt_work_init(&(cb->aio_work), aio_read_work, cb);
239     rt_workqueue_dowork(aio_queue, &(cb->aio_work));
240 
241     return 0;
242 }
243 
244 /**
245  * The aio_return() function shall return the return status associated with the
246  * aiocb structure referenced by the aiocbp argument. The return status for an
247  * asynchronous I/O operation is the value that would be returned by the corresponding
248  * read(), write(), or fsync() function call. If the error status for the operation
249  * is equal to [EINPROGRESS], then the return status for the operation is undefined.
250  * The aio_return() function may be called exactly once to retrieve the return
251  * status of a given asynchronous operation; thereafter, if the same aiocb structure
252  * is used in a call to aio_return() or aio_error(), an error may be returned.
253  * When the aiocb structure referred to by aiocbp is used to submit another asynchronous
254  * operation, then aio_return() may be successfully used to retrieve the return
255  * status of that operation.
256  */
aio_return(struct aiocb * cb)257 ssize_t  aio_return(struct aiocb *cb)
258 {
259     if (cb)
260     {
261         if (cb->aio_result < 0)
262             rt_set_errno(cb->aio_result);
263 
264         return cb->aio_result;
265     }
266 
267     return -EINVAL;
268 }
269 
270 /**
271  * The aio_suspend() function shall suspend the calling thread until at least
272  * one of the asynchronous I/O operations referenced by the list argument has
273  * completed, until a signal interrupts the function, or, if timeout is not NULL,
274  * until the time interval specified by timeout has passed. If any of the aiocb
275  * structures in the list correspond to completed asynchronous I/O operations
276  * (that is, the error status for the operation is not equal to [EINPROGRESS])
277  * at the time of the call, the function shall return without suspending the
278  * calling thread. The list argument is an array of pointers to asynchronous I/O
279  * control blocks. The nent argument indicates the number of elements in the
280  * array. Each aiocb structure pointed to has been used in initiating an asynchronous
281  * I/O request via aio_read(), aio_write(), or lio_listio(). This array may
282  * contain null pointers, which are ignored. If this array contains pointers
283  * that refer to aiocb structures that have not been used in submitting asynchronous
284  * I/O, the effect is undefined.
285  *
286  * If the time interval indicated in the timespec structure pointed to by timeout
287  * passes before any of the I/O operations referenced by list are completed, then
288  * aio_suspend() shall return with an error.
289  */
aio_suspend(const struct aiocb * const list[],int nent,const struct timespec * timeout)290 int aio_suspend(const struct aiocb *const list[], int nent,
291              const struct timespec *timeout)
292 {
293     return -ENOSYS;
294 }
295 
aio_write_work(struct rt_work * work,void * work_data)296 static void aio_write_work(struct rt_work* work, void* work_data)
297 {
298     int len, oflags, level;
299     uint8_t *buf_ptr;
300     struct aiocb *cb = (struct aiocb*)work_data;
301 
302     buf_ptr = (uint8_t*)cb->aio_buf;
303 
304     /* whether seek offset */
305     oflags = fcntl(cb->aio_fildes, F_GETFL, 0);
306     if ((oflags & O_APPEND) == 0)
307     {
308         lseek(cb->aio_fildes, SEEK_SET, cb->aio_offset);
309     }
310 
311     /* write data */
312     len = write(cb->aio_fildes, buf_ptr, cb->aio_nbytes);
313 
314     /* modify result */
315     level = rt_hw_interrupt_disable();
316     if (len <= 0)
317         cb->aio_result = errno;
318     else
319         cb->aio_result = len;
320     rt_hw_interrupt_enable(level);
321 
322     return;
323 }
324 
325 /**
326  * The aio_write() function shall write aiocbp->aio_nbytes to the file associated
327  * with aiocbp->aio_fildes from the buffer pointed to by aiocbp->aio_buf. The
328  * function shall return when the write request has been initiated or, at a minimum,
329  * queued to the file or device.
330  *
331  * The aiocbp argument may be used as an argument to aio_error() and aio_return()
332  * in order to determine the error status and return status, respectively, of the
333  * asynchronous operation while it is proceeding.
334  *
335  * The aiocbp argument points to an aiocb structure. If the buffer pointed to by
336  * aiocbp->aio_buf or the control block pointed to by aiocbp becomes an illegal
337  * address prior to asynchronous I/O completion, then the behavior is undefined.
338  *
339  * If O_APPEND is not set for the file descriptor aio_fildes, then the requested
340  * operation shall take place at the absolute position in the file as given by
341  * aio_offset, as if lseek() were called immediately prior to the operation with
342  * an offset equal to aio_offset and a whence equal to SEEK_SET. If O_APPEND is
343  * set for the file descriptor, or if aio_fildes is associated with a device that
344  * is incapable of seeking, write operations append to the file in the same order
345  * as the calls were made, except under circumstances described in Asynchronous
346  * I/O. After a successful call to enqueue an asynchronous I/O operation, the value
347  * of the file offset for the file is unspecified.
348  *
349  * The aio_sigevent member specifies the notification which occurs when the request
350  * is completed.
351  *
352  * The aiocbp->aio_lio_opcode field shall be ignored by aio_write().
353  *
354  * Simultaneous asynchronous operations using the same aiocbp produce undefined
355  * results.
356  *
357  * If synchronized I/O is enabled on the file associated with aiocbp->aio_fildes,
358  * the behavior of this function shall be according to the definitions of synchronized
359  * I/O data integrity completion, and synchronized I/O file integrity completion.
360  *
361  * For regular files, no data transfer shall occur past the offset maximum established
362  * in the open file description associated with aiocbp->aio_fildes.
363  */
aio_write(struct aiocb * cb)364 int aio_write(struct aiocb *cb)
365 {
366     int oflags;
367     rt_ubase_t level;
368 
369     if (!cb || (cb->aio_buf == NULL)) return -EINVAL;
370 
371     /* check access mode */
372     oflags = fcntl(cb->aio_fildes, F_GETFL, 0);
373     if ((oflags & O_ACCMODE) != O_WRONLY ||
374         (oflags & O_ACCMODE) != O_RDWR)
375         return -EINVAL;
376 
377     level = rt_hw_interrupt_disable();
378     cb->aio_result = -EINPROGRESS;
379     rt_hw_interrupt_enable(level);
380 
381     rt_work_init(&(cb->aio_work), aio_write_work, cb);
382     rt_workqueue_dowork(aio_queue, &(cb->aio_work));
383 
384     return 0;
385 }
386 
387 /**
388  * The lio_listio() function shall initiate a list of I/O requests with a single
389  * function call.
390  *
391  * The mode argument takes one of the values LIO_WAIT or LIO_NOWAIT declared in
392  * <aio.h> and determines whether the function returns when the I/O operations
393  * have been completed, or as soon as the operations have been queued. If the
394  * mode argument is LIO_WAIT, the function shall wait until all I/O is complete
395  * and the sig argument shall be ignored.
396  *
397  * If the mode argument is LIO_NOWAIT, the function shall return immediately, and
398  * asynchronous notification shall occur, according to the sig argument, when all
399  * the I/O operations complete. If sig is NULL, then no asynchronous notification
400  * shall occur. If sig is not NULL, asynchronous notification occurs as specified
401  * in Signal Generation and Delivery when all the requests in list have completed.
402  *
403  * The I/O requests enumerated by list are submitted in an unspecified order.
404  *
405  * The list argument is an array of pointers to aiocb structures. The array contains
406  * nent elements. The array may contain NULL elements, which shall be ignored.
407  *
408  * If the buffer pointed to by list or the aiocb structures pointed to by the
409  * elements of the array list become illegal addresses before all asynchronous I/O
410  * completed and, if necessary, the notification is sent, then the behavior is
411  * undefined. If the buffers pointed to by the aio_buf member of the aiocb structure
412  * pointed to by the elements of the array list become illegal addresses prior to
413  * the asynchronous I/O associated with that aiocb structure being completed, the
414  * behavior is undefined.
415  *
416  * The aio_lio_opcode field of each aiocb structure specifies the operation to be
417  * performed. The supported operations are LIO_READ, LIO_WRITE, and LIO_NOP; these
418  * symbols are defined in <aio.h>. The LIO_NOP operation causes the list entry to
419  * be ignored. If the aio_lio_opcode element is equal to LIO_READ, then an I/O operation
420  * is submitted as if by a call to aio_read() with the aiocbp equal to the address
421  * of the aiocb structure. If the aio_lio_opcode element is equal to LIO_WRITE, then
422  * an I/O operation is submitted as if by a call to aio_write() with the aiocbp equal
423  * to the address of the aiocb structure.
424  *
425  * The aio_fildes member specifies the file descriptor on which the operation is to
426  * be performed.
427  *
428  * The aio_buf member specifies the address of the buffer to or from which the data
429  * is transferred.
430  *
431  * The aio_nbytes member specifies the number of bytes of data to be transferred.
432  *
433  * The members of the aiocb structure further describe the I/O operation to be
434  * performed, in a manner identical to that of the corresponding aiocb structure
435  * when used by the aio_read() and aio_write() functions.
436  *
437  * The nent argument specifies how many elements are members of the list; that is,
438  * the length of the array.
439  *
440  * The behavior of this function is altered according to the definitions of synchronized
441  * I/O data integrity completion and synchronized I/O file integrity completion if
442  * synchronized I/O is enabled on the file associated with aio_fildes.
443  *
444  * For regular files, no data transfer shall occur past the offset maximum established
445  * in the open file description associated with aiocbp->aio_fildes.
446  *
447  * If sig->sigev_notify is SIGEV_THREAD and sig->sigev_notify_attributes is a
448  * non-null pointer and the block pointed to by this pointer becomes an illegal
449  * address prior to all asynchronous I/O being completed, then the behavior is
450  * undefined.
451  */
lio_listio(int mode,struct aiocb * const list[],int nent,struct sigevent * sig)452 int lio_listio(int mode, struct aiocb * const list[], int nent,
453             struct sigevent *sig)
454 {
455     return -ENOSYS;
456 }
457 
aio_system_init(void)458 int aio_system_init(void)
459 {
460     aio_queue = rt_workqueue_create("aio", 2048, RT_THREAD_PRIORITY_MAX/2);
461     RT_ASSERT(aio_queue != NULL);
462 
463     return 0;
464 }
465 INIT_COMPONENT_EXPORT(aio_system_init);
466