1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-07-20   zmq810150896   first version
9  * 2024-03-28   TroyMitchell   Add comments for all functions
10  */
11 
12 #include <rtthread.h>
13 #include <fcntl.h>
14 #include <rtdevice.h>
15 #include <stdint.h>
16 #include <unistd.h>
17 #include <dfs_file.h>
18 #include <dfs.h>
19 #include "poll.h"
20 #include "eventfd.h"
21 
22 #define EFD_SEMAPHORE (1 << 0)
23 #define EFD_CLOEXEC O_CLOEXEC
24 #define EFD_NONBLOCK O_NONBLOCK
25 
26 #define EFD_SHARED_FCNTL_FLAGS (O_CLOEXEC | O_NONBLOCK)
27 #define EFD_FLAGS_SET (EFD_SHARED_FCNTL_FLAGS | EFD_SEMAPHORE)
28 
29 #define EFD_ULLONG_MAX  (~0ULL)
30 
31 #define EVENTFD_MUTEX_NAME "eventfd"
32 
33 struct eventfd_ctx
34 {
35     rt_wqueue_t reader_queue;
36     rt_wqueue_t writer_queue;
37     rt_uint64_t count;
38     unsigned int flags;
39     struct rt_mutex lock;
40 };
41 
42 #ifndef RT_USING_DFS_V2
43 static int eventfd_close(struct dfs_file *file);
44 static int eventfd_poll(struct dfs_file *file, struct rt_pollreq *req);
45 static ssize_t eventfd_read(struct dfs_file *file, void *buf, size_t count);
46 static ssize_t eventfd_write(struct dfs_file *file, const void *buf, size_t count);
47 #else
48 static int eventfd_close(struct dfs_file *file);
49 static int eventfd_poll(struct dfs_file *file, struct rt_pollreq *req);
50 static ssize_t eventfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos);
51 static ssize_t eventfd_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos);
52 #endif
53 
54 static const struct dfs_file_ops eventfd_fops =
55 {
56     .close      = eventfd_close,
57     .poll       = eventfd_poll,
58     .read       = eventfd_read,
59     .write      = eventfd_write,
60 };
61 
62 /**
63  * @brief   Closes an event file descriptor.
64  * @param   file Pointer to the file descriptor structure.
65  * @return  0 on success, otherwise an error code.
66  */
eventfd_close(struct dfs_file * file)67 static int eventfd_close(struct dfs_file *file)
68 {
69     struct eventfd_ctx *ctx = file->vnode->data;
70 
71     if (file->vnode->ref_count == 1)
72     {
73         rt_mutex_detach(&ctx->lock);
74         rt_free(ctx);
75     }
76 
77     return 0;
78 }
79 
80 /**
81  * @brief   Polls an event file descriptor for events.
82  * @param   file Pointer to the file descriptor structure.
83  * @param   req Pointer to the poll request structure.
84  * @return  Events that occurred on the file descriptor.
85  */
eventfd_poll(struct dfs_file * file,struct rt_pollreq * req)86 static int eventfd_poll(struct dfs_file *file, struct rt_pollreq *req)
87 {
88     struct eventfd_ctx *ctx = (struct eventfd_ctx *)file->vnode->data;
89     int events = 0;
90     rt_uint64_t count;
91 
92     count = ctx->count;
93 
94     rt_poll_add(&ctx->reader_queue, req);
95 
96     if (count > 0)
97         events |= POLLIN;
98 
99     if (count == EFD_ULLONG_MAX)
100         events |= POLLERR;
101 
102     if ((EFD_ULLONG_MAX - 1) > count)
103         events |= POLLOUT;
104 
105     return events;
106 }
107 
108 #ifndef RT_USING_DFS_V2
109 /**
110  * @brief   Reads data from an event file descriptor.
111  * @param   file Pointer to the file descriptor structure.
112  * @param   buf Pointer to the buffer to read data into.
113  * @param   count Maximum number of bytes to read.
114  * @return  Number of bytes read on success, otherwise an error code.
115  */
eventfd_read(struct dfs_file * file,void * buf,size_t count)116 static ssize_t eventfd_read(struct dfs_file *file, void *buf, size_t count)
117 #else
118 /**
119  * @brief   Reads data from an event file descriptor.
120  * @param   file Pointer to the file descriptor structure.
121  * @param   buf Pointer to the buffer to read data into.
122  * @param   count Maximum number of bytes to read.
123  * @param   pos Pointer to the file position (not used).
124  * @return  Number of bytes read on success, otherwise an error code.
125  */
126 static ssize_t eventfd_read(struct dfs_file *file, void *buf, size_t count, off_t *pos)
127 #endif
128 {
129     struct eventfd_ctx *ctx = (struct eventfd_ctx *)file->vnode->data;
130     rt_uint64_t counter_num = 0;
131     rt_uint64_t *buffer;
132 
133     if (count < sizeof(counter_num))
134         return -EINVAL;
135 
136     buffer = (rt_uint64_t *)buf;
137 
138     rt_mutex_take(&ctx->lock, RT_WAITING_FOREVER);
139 
140     if (ctx->count <= 0)
141     {
142         if (file->flags & O_NONBLOCK)
143         {
144             rt_wqueue_wakeup(&ctx->writer_queue, (void*)POLLOUT);
145             rt_mutex_release(&ctx->lock);
146             return -EAGAIN;
147         }
148         else
149         {
150             /* In this case, when the data is read in blocked mode, when ctx->count is 0, the mutex needs to be released and wait for writing */
151             rt_mutex_release(&ctx->lock);
152             rt_wqueue_wakeup(&ctx->writer_queue, (void*)POLLOUT);
153             rt_wqueue_wait(&ctx->reader_queue, 0, RT_WAITING_FOREVER);
154             rt_mutex_take(&ctx->lock, RT_WAITING_FOREVER);
155         }
156     }
157 
158     if (ctx->flags & EFD_SEMAPHORE)
159     {
160         counter_num = 1;
161     }
162     else
163     {
164         counter_num = ctx->count;
165     }
166 
167     ctx->count -= counter_num;
168     (*buffer) = counter_num;
169 
170     rt_mutex_release(&ctx->lock);
171 
172     return sizeof(counter_num);
173 }
174 
175 #ifndef RT_USING_DFS_V2
176 /**
177  * @brief   Writes data to an event file descriptor.
178  * @param   file Pointer to the file descriptor structure.
179  * @param   buf Pointer to the buffer containing data to write.
180  * @param   count Number of bytes to write.
181  * @return  Number of bytes written on success, otherwise an error code.
182  */
eventfd_write(struct dfs_file * file,const void * buf,size_t count)183 static ssize_t eventfd_write(struct dfs_file *file, const void *buf, size_t count)
184 #else
185 /**
186  * @brief   Writes data to an event file descriptor.
187  * @param   file Pointer to the file descriptor structure.
188  * @param   buf Pointer to the buffer containing data to write.
189  * @param   count Number of bytes to write.
190  * @param   pos Pointer to the file position (not used).
191  * @return  Number of bytes written on success, otherwise an error code.
192  */
193 static ssize_t eventfd_write(struct dfs_file *file, const void *buf, size_t count, off_t *pos)
194 #endif
195 {
196     struct eventfd_ctx *ctx = (struct eventfd_ctx *)file->vnode->data;
197     rt_ssize_t ret = 0;
198 
199     rt_uint64_t counter_num;
200 
201     if (count < sizeof(counter_num))
202         return -EINVAL;
203 
204     counter_num = *(rt_uint64_t *)buf;
205 
206     if (counter_num == EFD_ULLONG_MAX)
207         return -EINVAL;
208 
209     ret = -EAGAIN;
210 
211     rt_mutex_take(&ctx->lock, RT_WAITING_FOREVER);
212 
213     if ((EFD_ULLONG_MAX - ctx->count) > counter_num)
214     {
215         ret = sizeof(counter_num);
216     }
217     else if (!(file->flags & O_NONBLOCK))
218     {
219         for (;;)
220         {
221             if ((EFD_ULLONG_MAX - ctx->count) >= counter_num)
222             {
223                 ret = sizeof(counter_num);
224                 break;
225             }
226             /* Release the mutex to avoid a deadlock */
227             rt_mutex_release(&ctx->lock);
228             rt_wqueue_wait(&ctx->writer_queue, 0, RT_WAITING_FOREVER);
229             rt_mutex_take(&ctx->lock, RT_WAITING_FOREVER);
230         }
231     }
232 
233     if (ret > 0)
234     {
235         ctx->count += counter_num;
236         rt_wqueue_wakeup(&ctx->reader_queue, (void *)POLLIN);
237     }
238 
239     rt_mutex_release(&ctx->lock);
240 
241     return ret;
242 }
243 /**
244  * @brief   Creates an event file descriptor.
245  * @param   df Pointer to the file descriptor structure.
246  * @param   count Initial value of the event counter.
247  * @param   flags Flags for the event file descriptor.
248  * @return  0 on success, otherwise an error code.
249  */
rt_eventfd_create(struct dfs_file * df,unsigned int count,int flags)250 static int rt_eventfd_create(struct dfs_file *df, unsigned int count, int flags)
251 {
252     struct eventfd_ctx *ctx = RT_NULL;
253     rt_err_t ret = 0;
254 
255     ctx = (struct eventfd_ctx *)rt_malloc(sizeof(struct eventfd_ctx));
256     if (ctx == RT_NULL)
257     {
258         ret = -ENOMEM;
259     }
260     else
261     {
262         ctx->count = count;
263         ctx->flags = flags;
264         flags &= EFD_SHARED_FCNTL_FLAGS;
265         flags |= O_RDWR;
266 
267         rt_mutex_init(&ctx->lock, EVENTFD_MUTEX_NAME, RT_IPC_FLAG_FIFO);
268         rt_wqueue_init(&ctx->reader_queue);
269         rt_wqueue_init(&ctx->writer_queue);
270 
271         df->vnode = (struct dfs_vnode *)rt_malloc(sizeof(struct dfs_vnode));
272         if (df->vnode)
273         {
274             dfs_vnode_init(df->vnode, FT_NONLOCK, &eventfd_fops);
275             df->vnode->data = ctx;
276             df->flags = flags;
277         }
278         else
279         {
280             rt_mutex_detach(&ctx->lock);
281             rt_free(ctx);
282             ret = -ENOMEM;
283         }
284 
285         #ifdef RT_USING_DFS_V2
286         df->fops = &eventfd_fops;
287         #endif
288 
289     }
290 
291     return ret;
292 }
293 
294 /**
295  * @brief   Internal function to create an event file descriptor.
296  * @param   count Initial value of the event counter.
297  * @param   flags Flags for the event file descriptor.
298  * @return  File descriptor on success, otherwise an error code.
299  */
do_eventfd(unsigned int count,int flags)300 static int do_eventfd(unsigned int count, int flags)
301 {
302     struct dfs_file *file;
303     int fd;
304     int status;
305     rt_ssize_t ret = 0;
306 
307     if (flags & ~EFD_FLAGS_SET)
308     {
309         rt_set_errno(EINVAL);
310         return -1;
311     }
312 
313     fd = fd_new();
314     if (fd >= 0)
315     {
316         ret = fd;
317         file = fd_get(fd);
318 
319         status = rt_eventfd_create(file, count, flags);
320         if (status < 0)
321         {
322             fd_release(fd);
323             rt_set_errno(-status);
324             ret = -1;
325         }
326     }
327     else
328     {
329         rt_set_errno(-fd);
330         ret = -1;
331     }
332 
333     return ret;
334 }
335 
336 /**
337  * @brief   Creates an event file descriptor with the specified count and flags.
338  * @param   count Initial value of the event counter.
339  * @param   flags Flags for the event file descriptor.
340  * @return  File descriptor on success, otherwise an error code.
341  */
eventfd(unsigned int count,int flags)342 int eventfd(unsigned int count, int flags)
343 {
344     return do_eventfd(count, flags);
345 }
346