You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
6.1 KiB
292 lines
6.1 KiB
/* |
|
* Copyright (c) 2018 Linaro Limited |
|
* Copyright (c) 2024 Tenstorrent AI ULC |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <stdbool.h> |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/internal/syscall_handler.h> |
|
#include <zephyr/sys/math_extras.h> |
|
#include <zephyr/net/socket.h> |
|
|
|
/* Get size, in elements, of an array within a struct. */ |
|
#define STRUCT_MEMBER_ARRAY_SIZE(type, field) ARRAY_SIZE(((type *)0)->field) |
|
|
|
/* Returns results in word_idx and bit_mask "output" params */ |
|
#define FD_SET_CALC_OFFSETS(set, word_idx, bit_mask) { \ |
|
unsigned int b_idx = fd % (sizeof(set->bitset[0]) * 8); \ |
|
word_idx = fd / (sizeof(set->bitset[0]) * 8); \ |
|
bit_mask = 1UL << b_idx; \ |
|
} |
|
|
|
int zvfs_poll_internal(struct zvfs_pollfd *fds, int nfds, k_timeout_t timeout); |
|
|
|
void ZVFS_FD_ZERO(struct zvfs_fd_set *set) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(set->bitset); i++) { |
|
set->bitset[i] = 0U; |
|
} |
|
} |
|
|
|
int ZVFS_FD_ISSET(int fd, struct zvfs_fd_set *set) |
|
{ |
|
uint32_t word_idx, bit_mask; |
|
|
|
if (fd < 0 || fd >= ZVFS_FD_SETSIZE) { |
|
return 0; |
|
} |
|
|
|
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask); |
|
|
|
return (set->bitset[word_idx] & bit_mask) != 0U; |
|
} |
|
|
|
void ZVFS_FD_CLR(int fd, struct zvfs_fd_set *set) |
|
{ |
|
uint32_t word_idx, bit_mask; |
|
|
|
if (fd < 0 || fd >= ZVFS_FD_SETSIZE) { |
|
return; |
|
} |
|
|
|
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask); |
|
|
|
set->bitset[word_idx] &= ~bit_mask; |
|
} |
|
|
|
void ZVFS_FD_SET(int fd, struct zvfs_fd_set *set) |
|
{ |
|
uint32_t word_idx, bit_mask; |
|
|
|
if (fd < 0 || fd >= ZVFS_FD_SETSIZE) { |
|
return; |
|
} |
|
|
|
FD_SET_CALC_OFFSETS(set, word_idx, bit_mask); |
|
|
|
set->bitset[word_idx] |= bit_mask; |
|
} |
|
|
|
int z_impl_zvfs_select(int nfds, struct zvfs_fd_set *ZRESTRICT readfds, |
|
struct zvfs_fd_set *ZRESTRICT writefds, |
|
struct zvfs_fd_set *ZRESTRICT exceptfds, |
|
const struct timespec *ZRESTRICT timeout, const void *ZRESTRICT sigmask) |
|
{ |
|
struct zvfs_pollfd pfds[CONFIG_ZVFS_POLL_MAX]; |
|
k_timeout_t poll_timeout; |
|
int i, res; |
|
int num_pfds = 0; |
|
int num_selects = 0; |
|
int fd_no = 0; |
|
|
|
for (i = 0; i < STRUCT_MEMBER_ARRAY_SIZE(struct zvfs_fd_set, bitset); i++) { |
|
uint32_t bit_mask = 1U; |
|
uint32_t read_mask = 0U, write_mask = 0U, except_mask = 0U; |
|
uint32_t ored_mask; |
|
|
|
if (readfds != NULL) { |
|
read_mask = readfds->bitset[i]; |
|
} |
|
|
|
if (writefds != NULL) { |
|
write_mask = writefds->bitset[i]; |
|
} |
|
|
|
if (exceptfds != NULL) { |
|
except_mask = exceptfds->bitset[i]; |
|
} |
|
|
|
ored_mask = read_mask | write_mask | except_mask; |
|
if (ored_mask == 0U) { |
|
fd_no += sizeof(ored_mask) * 8; |
|
continue; |
|
} |
|
|
|
do { |
|
if (ored_mask & bit_mask) { |
|
int events = 0; |
|
|
|
if (num_pfds >= ARRAY_SIZE(pfds)) { |
|
errno = ENOMEM; |
|
return -1; |
|
} |
|
|
|
if (read_mask & bit_mask) { |
|
events |= ZVFS_POLLIN; |
|
} |
|
|
|
if (write_mask & bit_mask) { |
|
events |= ZVFS_POLLOUT; |
|
} |
|
|
|
if (except_mask & bit_mask) { |
|
events |= ZVFS_POLLPRI; |
|
} |
|
|
|
pfds[num_pfds].fd = fd_no; |
|
pfds[num_pfds++].events = events; |
|
} |
|
|
|
bit_mask <<= 1; |
|
fd_no++; |
|
} while (bit_mask != 0U); |
|
} |
|
|
|
if (timeout == NULL) { |
|
poll_timeout = K_FOREVER; |
|
} else { |
|
poll_timeout = |
|
K_USEC(timeout->tv_sec * USEC_PER_SEC + timeout->tv_nsec / NSEC_PER_USEC); |
|
} |
|
|
|
res = zvfs_poll_internal(pfds, num_pfds, poll_timeout); |
|
if (res == -1) { |
|
return -1; |
|
} |
|
|
|
if (readfds != NULL) { |
|
ZVFS_FD_ZERO(readfds); |
|
} |
|
|
|
if (writefds != NULL) { |
|
ZVFS_FD_ZERO(writefds); |
|
} |
|
|
|
if (exceptfds != NULL) { |
|
ZVFS_FD_ZERO(exceptfds); |
|
} |
|
|
|
for (i = 0; i < num_pfds && res > 0; i++) { |
|
short revents = pfds[i].revents; |
|
int fd = pfds[i].fd; |
|
|
|
if (revents == 0) { |
|
continue; |
|
} |
|
|
|
/* POSIX: "EBADF: One or more of the file descriptor sets |
|
* specified a file descriptor that is not a valid open |
|
* file descriptor." |
|
* So, unlike poll(), a single invalid fd aborts the entire |
|
* select(). |
|
*/ |
|
if (revents & ZVFS_POLLNVAL) { |
|
errno = EBADF; |
|
return -1; |
|
} |
|
|
|
if (revents & ZVFS_POLLIN) { |
|
if (readfds != NULL) { |
|
ZVFS_FD_SET(fd, readfds); |
|
num_selects++; |
|
} |
|
} |
|
|
|
if (revents & ZVFS_POLLOUT) { |
|
if (writefds != NULL) { |
|
ZVFS_FD_SET(fd, writefds); |
|
num_selects++; |
|
} |
|
} |
|
|
|
/* It's unclear if HUP/ERR belong here. At least not ignore |
|
* them. Zephyr doesn't use HUP and barely use ERR so far. |
|
*/ |
|
if (revents & (ZVFS_POLLPRI | ZVFS_POLLHUP | ZVFS_POLLERR)) { |
|
if (exceptfds != NULL) { |
|
ZVFS_FD_SET(fd, exceptfds); |
|
num_selects++; |
|
} |
|
|
|
if (writefds != NULL) { |
|
ZVFS_FD_SET(fd, writefds); |
|
num_selects++; |
|
} |
|
} |
|
|
|
res--; |
|
} |
|
|
|
return num_selects; |
|
} |
|
|
|
#ifdef CONFIG_USERSPACE |
|
static int z_vrfy_zvfs_select(int nfds, struct zvfs_fd_set *ZRESTRICT readfds, |
|
struct zvfs_fd_set *ZRESTRICT writefds, |
|
struct zvfs_fd_set *ZRESTRICT exceptfds, |
|
const struct timespec *ZRESTRICT timeout, |
|
const void *ZRESTRICT sigmask) |
|
{ |
|
struct zvfs_fd_set *readfds_copy = NULL, *writefds_copy = NULL, *exceptfds_copy = NULL; |
|
struct timespec *to = NULL; |
|
int ret = -1; |
|
|
|
if (readfds) { |
|
readfds_copy = |
|
k_usermode_alloc_from_copy((void *)readfds, sizeof(struct zvfs_fd_set)); |
|
if (!readfds_copy) { |
|
errno = ENOMEM; |
|
goto out; |
|
} |
|
} |
|
|
|
if (writefds) { |
|
writefds_copy = |
|
k_usermode_alloc_from_copy((void *)writefds, sizeof(struct zvfs_fd_set)); |
|
if (!writefds_copy) { |
|
errno = ENOMEM; |
|
goto out; |
|
} |
|
} |
|
|
|
if (exceptfds) { |
|
exceptfds_copy = |
|
k_usermode_alloc_from_copy((void *)exceptfds, sizeof(struct zvfs_fd_set)); |
|
if (!exceptfds_copy) { |
|
errno = ENOMEM; |
|
goto out; |
|
} |
|
} |
|
|
|
if (timeout) { |
|
to = k_usermode_alloc_from_copy((void *)timeout, sizeof(*to)); |
|
if (!to) { |
|
errno = ENOMEM; |
|
goto out; |
|
} |
|
} |
|
|
|
ret = z_impl_zvfs_select(nfds, readfds_copy, writefds_copy, exceptfds_copy, to, sigmask); |
|
|
|
if (ret >= 0) { |
|
if (readfds_copy) { |
|
k_usermode_to_copy((void *)readfds, readfds_copy, |
|
sizeof(struct zvfs_fd_set)); |
|
} |
|
|
|
if (writefds_copy) { |
|
k_usermode_to_copy((void *)writefds, writefds_copy, |
|
sizeof(struct zvfs_fd_set)); |
|
} |
|
|
|
if (exceptfds_copy) { |
|
k_usermode_to_copy((void *)exceptfds, exceptfds_copy, |
|
sizeof(struct zvfs_fd_set)); |
|
} |
|
} |
|
|
|
out: |
|
k_free(to); |
|
k_free(readfds_copy); |
|
k_free(writefds_copy); |
|
k_free(exceptfds_copy); |
|
|
|
return ret; |
|
} |
|
#include <zephyr/syscalls/zvfs_select_mrsh.c> |
|
#endif
|
|
|