/* * Copyright (c) 2017-2018 Linaro Limited * Copyright (c) 2021 Nordic Semiconductor * Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved. * Copyright (c) 2024 Tenstorrent AI ULC * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) bool net_socket_is_tls(void *obj); #else #define net_socket_is_tls(obj) false #endif int zvfs_poll_internal(struct zvfs_pollfd *fds, int nfds, k_timeout_t timeout) { bool retry; int ret = 0; int i; struct zvfs_pollfd *pfd; struct k_poll_event poll_events[CONFIG_ZVFS_POLL_MAX]; struct k_poll_event *pev; struct k_poll_event *pev_end = poll_events + ARRAY_SIZE(poll_events); const struct fd_op_vtable *vtable; struct k_mutex *lock; k_timepoint_t end; bool offload = false; const struct fd_op_vtable *offl_vtable = NULL; void *offl_ctx = NULL; end = sys_timepoint_calc(timeout); pev = poll_events; for (pfd = fds, i = nfds; i--; pfd++) { void *ctx; int result; /* Per POSIX, negative fd's are just ignored */ if (pfd->fd < 0) { continue; } ctx = zvfs_get_fd_obj_and_vtable(pfd->fd, &vtable, &lock); if (ctx == NULL) { /* Will set POLLNVAL in return loop */ continue; } (void)k_mutex_lock(lock, K_FOREVER); result = zvfs_fdtable_call_ioctl(vtable, ctx, ZFD_IOCTL_POLL_PREPARE, pfd, &pev, pev_end); if (result == -EALREADY) { /* If POLL_PREPARE returned with EALREADY, it means * it already detected that some socket is ready. In * this case, we still perform a k_poll to pick up * as many events as possible, but without any wait. */ timeout = K_NO_WAIT; end = sys_timepoint_calc(timeout); result = 0; } else if (result == -EXDEV) { /* If POLL_PREPARE returned EXDEV, it means * it detected an offloaded socket. * If offloaded socket is used with native TLS, the TLS * wrapper for the offloaded poll will be used. * In case the fds array contains a mixup of offloaded * and non-offloaded sockets, the offloaded poll handler * shall return an error. */ offload = true; if (offl_vtable == NULL || net_socket_is_tls(ctx)) { offl_vtable = vtable; offl_ctx = ctx; } result = 0; } k_mutex_unlock(lock); if (result < 0) { errno = -result; return -1; } } if (offload) { int poll_timeout; if (K_TIMEOUT_EQ(timeout, K_FOREVER)) { poll_timeout = SYS_FOREVER_MS; } else { poll_timeout = k_ticks_to_ms_floor32(timeout.ticks); } return zvfs_fdtable_call_ioctl(offl_vtable, offl_ctx, ZFD_IOCTL_POLL_OFFLOAD, fds, nfds, poll_timeout); } timeout = sys_timepoint_timeout(end); do { ret = k_poll(poll_events, pev - poll_events, timeout); /* EAGAIN when timeout expired, EINTR when cancelled (i.e. EOF) */ if (ret != 0 && ret != -EAGAIN && ret != -EINTR) { errno = -ret; return -1; } retry = false; ret = 0; pev = poll_events; for (pfd = fds, i = nfds; i--; pfd++) { void *ctx; int result; pfd->revents = 0; if (pfd->fd < 0) { continue; } ctx = zvfs_get_fd_obj_and_vtable(pfd->fd, &vtable, &lock); if (ctx == NULL) { pfd->revents = ZVFS_POLLNVAL; ret++; continue; } (void)k_mutex_lock(lock, K_FOREVER); result = zvfs_fdtable_call_ioctl(vtable, ctx, ZFD_IOCTL_POLL_UPDATE, pfd, &pev); k_mutex_unlock(lock); if (result == -EAGAIN) { retry = true; continue; } else if (result != 0) { errno = -result; return -1; } if (pfd->revents != 0) { ret++; } } if (retry) { if (ret > 0) { break; } timeout = sys_timepoint_timeout(end); if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { break; } } } while (retry); return ret; } int z_impl_zvfs_poll(struct zvfs_pollfd *fds, int nfds, int poll_timeout) { k_timeout_t timeout; if (poll_timeout < 0) { timeout = K_FOREVER; } else { timeout = K_MSEC(poll_timeout); } return zvfs_poll_internal(fds, nfds, timeout); } #ifdef CONFIG_USERSPACE static inline int z_vrfy_zvfs_poll(struct zvfs_pollfd *fds, int nfds, int timeout) { struct zvfs_pollfd *fds_copy; size_t fds_size; int ret; /* Copy fds array from user mode */ if (size_mul_overflow(nfds, sizeof(struct zvfs_pollfd), &fds_size)) { errno = EFAULT; return -1; } fds_copy = k_usermode_alloc_from_copy((void *)fds, fds_size); if (!fds_copy) { errno = ENOMEM; return -1; } ret = z_impl_zvfs_poll(fds_copy, nfds, timeout); if (ret >= 0) { k_usermode_to_copy((void *)fds, fds_copy, fds_size); } k_free(fds_copy); return ret; } #include #endif