Users using Go have reported an issue to us: When performing read/write operations with goroutines, since fuse's file->f_ops->poll is not empty, read/write operations are conducted via epoll. Additionally, goroutines use the EPOLLET wake-up mode. Currently, the implementation of fuse_file_poll has the following problem: After receiving EAGAIN during read/write operations, the goroutine calls epoll_wait again, but then results in permanent blocking. This is because a wake-up event is required in EPOLLET mode. The modification idea of this patch is based on the implementation of epoll_wait: After epoll_wait calls ->poll() for EPOLLET events, it does not reinsert them into the ready list. In this case, ep_poll_callback() needs to be used to reinsert them into the ready list, so that the behavior of EPOLLET is consistent with that of non-EPOLLET. Reported-by: Geng Xueyu Reported-by: Wang Jian Suggested-by: Xie Yongji Signed-off-by: Zhang Tianci --- Here is the reproducer for the bug: 1. Change libfuse/example/passthrough.c's xmp_read() to always return -EAGAIN and mount it on /mnt_fuse. 2. Then create test file: dd if=/dev/zero of=/mnt_fuse/test_poll bs=1M count=10 3. compile and run the following test case. #include #include #include #include #include #include #include #define MAX_EVENTS 10 #define BUF_SIZE 4096 int main() { int epfd, nfds, fd; struct epoll_event ev, events[MAX_EVENTS]; char buffer[BUF_SIZE]; fd = open("/mnt_fuse/test_poll", O_RDWR|O_NONBLOCK|O_CREAT, 0644); if (fd == -1) { perror("open"); return -1; } epfd = epoll_create1(0); if (epfd == -1) { perror("epoll_create1"); return -1; } ev.data.fd = fd; ev.events = EPOLLIN | EPOLLET; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { perror("epoll_ctl: fd"); return -1; } printf("Epoll is monitoring fd=%d.\n", fd); while (1) { nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); if (nfds == -1) { if (errno == EINTR) { continue; } perror("epoll_wait"); return -1; } for (int i = 0; i < nfds; ++i) { int current_fd = events[i].data.fd; if (current_fd == fd) { printf("[Notification] Data arrived on fd\n", fd); while (1) { ssize_t count = read(current_fd, buffer, BUF_SIZE); if (count == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { printf("[Info] Buffer is empty, waiting for next event...\n"); break; } else { perror("read"); close(current_fd); return -1; } } else if (count == 0) { printf("[Info] EOF detected. Closing.\n"); close(current_fd); return 0; } printf(">>> Read %zd bytes\n", count); } } } } close(epfd); return 0; } fs/fuse/file.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 01bc894e9c2ba..025eea58232c2 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2735,10 +2735,11 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait) FUSE_ARGS(args); int err; + poll_wait(file, &ff->poll_wait, wait); + if (fm->fc->no_poll) - return DEFAULT_POLLMASK; + goto no_poll; - poll_wait(file, &ff->poll_wait, wait); inarg.events = mangle_poll(poll_requested_events(wait)); /* @@ -2764,9 +2765,13 @@ __poll_t fuse_file_poll(struct file *file, poll_table *wait) return demangle_poll(outarg.revents); if (err == -ENOSYS) { fm->fc->no_poll = 1; - return DEFAULT_POLLMASK; + goto no_poll; } return EPOLLERR; + +no_poll: + wake_up_interruptible_sync(&ff->poll_wait); + return DEFAULT_POLLMASK; } EXPORT_SYMBOL_GPL(fuse_file_poll); -- 2.39.5