1 /****************************************************************************
2 * fs/vfs/fs_poll.c
3 *
4 * Copyright (C) 2008-2009, 2012-2019 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include "los_hwi.h"
41 #include "vfs_config.h"
42 #include "stdint.h"
43 #include "poll.h"
44 #include "assert.h"
45 #include "errno.h"
46 #include "vnode.h"
47 #include "stdlib.h"
48 #include "stdio.h"
49 #include "console.h"
50 #include "unistd.h"
51 #include "linux/wait.h"
52 #ifdef LOSCFG_NET_LWIP_SACK
53 #include "lwip/sockets.h"
54 #endif
55 #ifndef CONFIG_DISABLE_POLL
56
57 /****************************************************************************
58 * Pre-processor Definitions
59 ****************************************************************************/
60
61 #ifndef MSEC_PER_SEC
62 #define MSEC_PER_SEC 1000L
63 #endif
64
65 #ifndef NSEC_PER_MSEC
66 #define NSEC_PER_MSEC 1000000L
67 #endif
68
69 #define N_POLL_ITEMS 5
70 #define poll_semgive(sem) sem_post(sem)
71
72 /****************************************************************************
73 * Data Structures & Macros
74 ****************************************************************************/
75
76 typedef wait_queue_head_t * poll_wait_queue;
77
78 typedef struct tag_poll_wait_node
79 {
80 LOS_DL_LIST queue_node;
81 pollevent_t key;
82 struct tag_poll_wait_entry *entry;
83 poll_wait_queue wait_queue;
84 } poll_wait_node;
85
86 typedef struct tag_poll_wait_entry_table
87 {
88 struct tag_poll_wait_entry_table *next;
89 UINT32 index;
90 poll_wait_node items[N_POLL_ITEMS];
91 } poll_wait_entry_table;
92
93 typedef struct tag_poll_wait_entry
94 {
95 bool add_queue_flag;
96 sem_t sem;
97 UINT32 inline_index;
98 poll_wait_node inline_items[N_POLL_ITEMS];
99 poll_wait_entry_table *table;
100 } poll_wait_entry;
101
102 /****************************************************************************
103 * Private Functions
104 ****************************************************************************/
105
106 /****************************************************************************
107 * Name: poll_semtake
108 ****************************************************************************/
109
poll_semtake(sem_t * sem)110 static int poll_semtake(sem_t *sem)
111 {
112 /* Take the semaphore (perhaps waiting) */
113
114 if (sem_wait(sem) < 0)
115 {
116 int err = get_errno();
117
118 /* The only case that an error should occur here is if the wait were
119 * awakened by a signal.
120 */
121
122 DEBUGASSERT(err == EINTR);
123 return -err;
124 }
125
126 return OK;
127 }
128
set_add_poll_wait_flag(poll_wait_head wait,bool add_queue_flag)129 static void set_add_poll_wait_flag(poll_wait_head wait, bool add_queue_flag)
130 {
131 wait->add_queue_flag = add_queue_flag;
132 }
133
destroy_poll_wait(poll_wait_head wait)134 static int destroy_poll_wait(poll_wait_head wait)
135 {
136 unsigned int i;
137 unsigned long int_save;
138 poll_wait_node *wait_node = NULL;
139 poll_wait_entry_table *curr_table = NULL;
140
141 for (i = 0; i < wait->inline_index; ++i)
142 {
143 wait_node = &wait->inline_items[i];
144 spin_lock_irqsave(&wait_node->wait_queue->lock, int_save);
145 LOS_ListDelete(&wait_node->queue_node);
146 spin_unlock_irqrestore(&wait_node->wait_queue->lock, int_save);
147 }
148
149 while (wait->table)
150 {
151 curr_table = wait->table;
152 wait->table = curr_table->next;
153
154 for (i = 0; i < curr_table->index; ++i)
155 {
156 wait_node = &curr_table->items[i];
157 spin_lock_irqsave(&wait_node->wait_queue->lock, int_save);
158 LOS_ListDelete(&wait_node->queue_node);
159 spin_unlock_irqrestore(&wait_node->wait_queue->lock, int_save);
160 }
161 free(curr_table);
162 }
163
164 if (sem_destroy(&wait->sem) < 0)
165 {
166 PRINT_ERR("[%s] sem_destroy failed\n", __FUNCTION__);
167 return -1;
168 }
169
170 return 0;
171 }
172
get_poll_item(poll_wait_head wait)173 static poll_wait_node *get_poll_item(poll_wait_head wait)
174 {
175 if (wait->inline_index < N_POLL_ITEMS)
176 {
177 return wait->inline_items + wait->inline_index++;
178 }
179 if (!wait->table || (wait->table && wait->table->index >= N_POLL_ITEMS))
180 {
181 poll_wait_entry_table *new_entry_table = NULL;
182
183 new_entry_table = (poll_wait_entry_table *)malloc(sizeof(poll_wait_entry_table));
184 if (new_entry_table == NULL)
185 {
186 return (poll_wait_node *)NULL;
187 }
188 new_entry_table->index = 0;
189
190 new_entry_table->next = wait->table;
191 wait->table = new_entry_table;
192 }
193
194 return wait->table->items + wait->table->index++;
195 }
196
add_pollwait_queue(poll_wait_queue queue,poll_table * p)197 static void add_pollwait_queue(poll_wait_queue queue, poll_table *p)
198 {
199 unsigned long int_save;
200 poll_wait_head wait = p->wait;
201 poll_wait_node *new_node = get_poll_item(wait);
202 if (new_node != NULL)
203 {
204 new_node->entry = wait;
205 new_node->key = p->key;
206 new_node->wait_queue = queue;
207 spin_lock_irqsave(&queue->lock, int_save);
208 LOS_ListAdd(&queue->poll_queue, &new_node->queue_node);
209 spin_unlock_irqrestore(&queue->lock, int_save);
210 }
211 }
212
wait_sem_time(poll_wait_head wait,const struct timespec * time_ptr)213 static int wait_sem_time(poll_wait_head wait, const struct timespec *time_ptr)
214 {
215 if (time_ptr != NULL)
216 {
217 return sem_timedwait(&wait->sem, time_ptr);
218 }
219 else
220 {
221 return poll_semtake(&wait->sem);
222 }
223 }
224
file_poll(struct file * filep,poll_table * wait)225 static int file_poll(struct file *filep, poll_table *wait)
226 {
227 int ret = -ENOSYS;
228
229 if (filep->ops != NULL && filep->ops->poll != NULL)
230 {
231 ret = filep->ops->poll(filep, wait);
232 }
233
234 return ret;
235 }
236
fdesc_poll(int fd,poll_table * wait)237 static int fdesc_poll(int fd, poll_table *wait)
238 {
239 struct file *filep = NULL;
240
241 if (fd <= STDERR_FILENO && fd >= STDIN_FILENO) /* fd : [0,2] */
242 {
243 fd = ConsoleUpdateFd();
244 if (fd < 0)
245 {
246 set_errno(EBADF);
247 return VFS_ERROR;
248 }
249 }
250
251 /* Get the file pointer corresponding to this file descriptor */
252
253 int ret = fs_getfilep(fd, &filep);
254 if (ret < 0)
255 {
256 /* The errno value has already been set */
257 int errorcode = get_errno();
258 return -errorcode;
259 }
260
261 /* Let file_poll() do the rest */
262
263 return file_poll(filep, wait);
264 }
265
query_fd(int fd,poll_table * wait)266 static int query_fd(int fd, poll_table *wait)
267 {
268 /* Check for a valid file descriptor */
269
270 if (fd >= CONFIG_NFILE_DESCRIPTORS)
271 {
272 /* Perform the socket ioctl */
273
274 #if defined(LOSCFG_NET_LWIP_SACK)
275 if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS))
276 {
277 return socks_poll(fd, wait);
278 }
279 else
280 #endif
281 {
282 return -EBADF;
283 }
284 }
285
286 return fdesc_poll(fd, wait);
287 }
288
query_fds(struct pollfd * fds,nfds_t nfds,poll_table * wait)289 static int query_fds(struct pollfd *fds, nfds_t nfds, poll_table *wait)
290 {
291 unsigned int i;
292 int ret;
293 int count = 0;
294
295 if ((nfds != 0 && fds == NULL) || wait == NULL || wait->wait == NULL)
296 {
297 set_errno(EINVAL);
298 return -1;
299 }
300
301 for (i = 0; i < nfds; ++i)
302 {
303 struct pollfd *p_fds = &fds[i];
304 if (p_fds->fd < 0)
305 {
306 set_errno(EBADF);
307 return -1;
308 }
309
310 wait->key = p_fds->events | POLLERR | POLLHUP;
311
312 ret = query_fd(p_fds->fd, wait);
313 if (ret < 0)
314 {
315 set_errno(-ret);
316 return -1;
317 }
318
319 p_fds->revents = (p_fds->events | POLLERR | POLLHUP) & (pollevent_t)ret;
320 if (p_fds->revents)
321 {
322 ++count;
323 set_add_poll_wait_flag(wait->wait, false);
324 }
325 }
326
327 return count;
328 }
329
330 /****************************************************************************
331 * Public Functions
332 ****************************************************************************/
333
notify_poll_with_key(wait_queue_head_t * wait_address,pollevent_t key)334 void notify_poll_with_key(wait_queue_head_t *wait_address, pollevent_t key)
335 {
336 unsigned long int_save;
337 int failed_count = 0;
338 poll_wait_node *curr = NULL;
339
340 spin_lock_irqsave(&wait_address->lock, int_save);
341 LOS_DL_LIST_FOR_EACH_ENTRY(curr, &(wait_address->poll_queue), poll_wait_node, queue_node)
342 {
343 poll_wait_entry *curr_entry = curr->entry;
344 if (!key || (key & curr->key))
345 {
346 if (poll_semgive(&curr_entry->sem) < 0)
347 {
348 failed_count++;
349 }
350 }
351 }
352 spin_unlock_irqrestore(&wait_address->lock, int_save);
353
354 if (failed_count != 0)
355 {
356 PRINT_ERR("[%s] sem_post failed %d times\n", __FUNCTION__, failed_count);
357 }
358 }
359
360 /* just for compatible */
361
notify_poll(wait_queue_head_t * wait_address)362 void notify_poll(wait_queue_head_t *wait_address)
363 {
364 notify_poll_with_key(wait_address, 0);
365 }
366
poll_wait(struct file * filp,wait_queue_head_t * wait_address,poll_table * p)367 void poll_wait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
368 {
369 if (!wait_address || !p || !p->wait)
370 {
371 return;
372 }
373
374 if (p->wait->add_queue_flag)
375 {
376 add_pollwait_queue(wait_address, p);
377 }
378 }
379
380 /****************************************************************************
381 * Name: poll
382 *
383 * Description:
384 * poll() waits for one of a set of file descriptors to become ready to
385 * perform I/O. If none of the events requested (and no error) has
386 * occurred for any of the file descriptors, then poll() blocks until
387 * one of the events occurs.
388 *
389 * Input Parameters:
390 * fds - List of structures describing file descriptors to be monitored
391 * nfds - The number of entries in the list
392 * timeout - Specifies an upper limit on the time for which poll() will
393 * block in milliseconds. A negative value of timeout means an infinite
394 * timeout.
395 *
396 * Returned Value:
397 * On success, the number of structures that have non-zero revents fields.
398 * A value of 0 indicates that the call timed out and no file descriptors
399 * were ready. On error, -1 is returned, and errno is set appropriately:
400 *
401 * EBADF - An invalid file descriptor was given in one of the sets.
402 * EFAULT - The fds address is invalid
403 * EINTR - A signal occurred before any requested event.
404 * EINVAL - The nfds value exceeds a system limit.
405 * ENOMEM - There was no space to allocate internal data structures.
406 * ENOSYS - One or more of the drivers supporting the file descriptor
407 * does not support the poll method.
408 *
409 ****************************************************************************/
410
poll(struct pollfd * fds,nfds_t nfds,int timeout)411 int poll(struct pollfd *fds, nfds_t nfds, int timeout)
412 {
413 poll_table wait_table;
414 poll_wait_entry wait_entry;
415 size_t start_ticks = 0;
416 int millisecs_left;
417 int ret = OK;
418 int count = 0;
419 int err = 0;
420 int temp = 0;
421
422 wait_table.wait = &wait_entry;
423 wait_table.wait->table = NULL;
424 wait_table.wait->inline_index = 0;
425 if (sem_init(&wait_table.wait->sem, 0, 0) < 0)
426 {
427 set_errno(ENOMEM);
428 return -1;
429 }
430
431 /* set wait flag */
432
433 set_add_poll_wait_flag(wait_table.wait, ((timeout == 0) ? false : true));
434
435 count = query_fds(fds, nfds, &wait_table);
436 if (count > 0)
437 {
438 ret = OK;
439 goto out;
440 }
441 else if (count < 0)
442 {
443 goto out;
444 }
445
446 /* clear wait flag if no fd got on first query */
447
448 set_add_poll_wait_flag(wait_table.wait, false);
449
450 if (timeout > 0)
451 {
452 start_ticks = LOS_TickCountGet();
453 }
454
455 millisecs_left = timeout;
456 while (count == 0)
457 {
458 if (timeout < 0)
459 {
460 ret = wait_sem_time(wait_table.wait, (const struct timespec *)NULL);
461 }
462 else if (timeout == 0 || millisecs_left <= 0)
463 {
464 ret = OK;
465 goto out;
466 }
467 else if (millisecs_left > 0)
468 {
469 struct timespec wait_time;
470 UINT64 curr_ticks;
471 int millisecs_last;
472
473 curr_ticks = LOS_TickCountGet();
474 millisecs_last = (curr_ticks - start_ticks) * MSEC_PER_SEC / LOSCFG_BASE_CORE_TICK_PER_SECOND;
475 if (millisecs_last >= timeout)
476 {
477 ret = OK;
478 goto out;
479 }
480 else
481 {
482 millisecs_left = timeout - millisecs_last;
483 }
484
485 wait_time.tv_sec = millisecs_left / MSEC_PER_SEC;
486 wait_time.tv_nsec = (millisecs_left - MSEC_PER_SEC * wait_time.tv_sec) * NSEC_PER_MSEC;
487
488 ret = wait_sem_time(wait_table.wait, &wait_time);
489 if (ret < 0)
490 {
491 err = get_errno();
492
493 if (err == ETIMEDOUT)
494 {
495 ret = OK;
496 }
497 else
498 {
499 ret = -err;
500 }
501 }
502 }
503
504 if (ret < 0)
505 {
506 goto out;
507 }
508 count = query_fds(fds, nfds, &wait_table);
509 if (err == ETIMEDOUT)
510 {
511 break;
512 }
513 }
514
515 out:
516 temp = get_errno();
517 if (destroy_poll_wait(wait_table.wait) < 0)
518 {
519 temp = get_errno();
520 }
521
522 if (temp != 0)
523 {
524 set_errno(temp);
525 }
526 if (ret < 0)
527 {
528 set_errno(-ret);
529 return -1;
530 }
531
532 return count;
533 }
534
535 #endif /* CONFIG_DISABLE_POLL */
536