[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251225110318.46261-1-zhangtianci.1997@bytedance.com>
Date: Thu, 25 Dec 2025 19:03:18 +0800
From: Zhang Tianci <zhangtianci.1997@...edance.com>
To: miklos@...redi.hu
Cc: linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org,
Zhang Tianci <zhangtianci.1997@...edance.com>,
Geng Xueyu <gengxueyu.520@...edance.com>,
Wang Jian <wangjian.pg@...edance.com>,
Xie Yongji <xieyongji@...edance.com>
Subject: [PATCH] fuse: fix the bug of missing EPOLLET event wakeup
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 <gengxueyu.520@...edance.com>
Reported-by: Wang Jian <wangjian.pg@...edance.com>
Suggested-by: Xie Yongji <xieyongji@...edance.com>
Signed-off-by: Zhang Tianci <zhangtianci.1997@...edance.com>
---
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#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
Powered by blists - more mailing lists