mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
Merge patch series "io_uring related epoll cleanups"
Jens Axboe <axboe@kernel.dk> says: One of the nastier things about epoll is how it allows nesting contexts inside each other, leading to the necessity of loop detection and the issues that have come with that. I don't believe there's any reason to support nesting on the io_uring side, in fact IORING_OP_EPOLL_CTL is a historical mistake, imho. But let's at least try and contain the damage and disallow nested contexts from our side. Christian Brauner <brauner@kernel.org> says: Bring in the eventpoll specific io_uring changes together with the eventpoll cleanup I did this cycle. The io_uring changes can go on top of both through the block tree. * patches from https://patch.msgid.link/20260514140817.623026-1-axboe@kernel.dk: eventpoll: rename struct epoll_filefd to epoll_key eventpoll: add file based control interface eventpoll: export is_file_epoll() eventpoll: pass struct epoll_filefd through ep_find() and ep_insert() Link: https://patch.msgid.link/20260514140817.623026-1-axboe@kernel.dk Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
commit
6ece1a31c5
|
|
@ -224,11 +224,6 @@
|
|||
|
||||
#define EP_ITEM_COST (sizeof(struct epitem) + sizeof(struct eppoll_entry))
|
||||
|
||||
struct epoll_filefd {
|
||||
struct file *file;
|
||||
int fd;
|
||||
} __packed;
|
||||
|
||||
/* Wait structure used by the poll hooks */
|
||||
struct eppoll_entry {
|
||||
/* List header used to link this structure to the "struct epitem" */
|
||||
|
|
@ -273,7 +268,7 @@ struct epitem {
|
|||
struct epitem *ovflist_next;
|
||||
|
||||
/* The file descriptor information this item refers to */
|
||||
struct epoll_filefd ffd;
|
||||
struct epoll_key ffd;
|
||||
|
||||
/* List containing poll wait queues */
|
||||
struct eppoll_entry *pwqlist;
|
||||
|
|
@ -526,22 +521,13 @@ static void __init epoll_sysctls_init(void)
|
|||
|
||||
static const struct file_operations eventpoll_fops;
|
||||
|
||||
static inline bool is_file_epoll(struct file *f)
|
||||
bool is_file_epoll(struct file *f)
|
||||
{
|
||||
return f->f_op == &eventpoll_fops;
|
||||
}
|
||||
|
||||
/* Setup the structure that is used as key for the RB tree */
|
||||
static inline void ep_set_ffd(struct epoll_filefd *ffd,
|
||||
struct file *file, int fd)
|
||||
{
|
||||
ffd->file = file;
|
||||
ffd->fd = fd;
|
||||
}
|
||||
|
||||
/* Compare RB tree keys */
|
||||
static inline int ep_cmp_ffd(struct epoll_filefd *p1,
|
||||
struct epoll_filefd *p2)
|
||||
static inline int ep_cmp_ffd(struct epoll_key *p1, struct epoll_key *p2)
|
||||
{
|
||||
return (p1->file > p2->file ? +1:
|
||||
(p1->file < p2->file ? -1 : p1->fd - p2->fd));
|
||||
|
|
@ -1437,17 +1423,15 @@ static int ep_alloc(struct eventpoll **pep)
|
|||
* are protected by the "mtx" mutex, and ep_find() must be called with
|
||||
* "mtx" held.
|
||||
*/
|
||||
static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd)
|
||||
static struct epitem *ep_find(struct eventpoll *ep, struct epoll_key *tf)
|
||||
{
|
||||
int kcmp;
|
||||
struct rb_node *rbp;
|
||||
struct epitem *epi, *epir = NULL;
|
||||
struct epoll_filefd ffd;
|
||||
|
||||
ep_set_ffd(&ffd, file, fd);
|
||||
for (rbp = ep->rbr.rb_root.rb_node; rbp; ) {
|
||||
epi = rb_entry(rbp, struct epitem, rbn);
|
||||
kcmp = ep_cmp_ffd(&ffd, &epi->ffd);
|
||||
kcmp = ep_cmp_ffd(tf, &epi->ffd);
|
||||
if (kcmp > 0)
|
||||
rbp = rbp->rb_right;
|
||||
else if (kcmp < 0)
|
||||
|
|
@ -1787,8 +1771,8 @@ static int ep_attach_file(struct file *file, struct epitem *epi)
|
|||
|
||||
/*
|
||||
* Charge the user's epoll_watches quota, allocate a fresh epitem for
|
||||
* @tfile/@fd, and initialize its fields. The returned item is not yet
|
||||
* linked into any data structure; the caller must install it via
|
||||
* @tf, and initialize its fields. The returned item is not yet linked
|
||||
* into any data structure; the caller must install it via
|
||||
* ep_register_epitem() (which takes over on success) or kmem_cache_free()
|
||||
* it and decrement epoll_watches on its own.
|
||||
*
|
||||
|
|
@ -1797,7 +1781,7 @@ static int ep_attach_file(struct file *file, struct epitem *epi)
|
|||
*/
|
||||
static struct epitem *ep_alloc_epitem(struct eventpoll *ep,
|
||||
const struct epoll_event *event,
|
||||
struct file *tfile, int fd)
|
||||
struct epoll_key *tf)
|
||||
{
|
||||
struct epitem *epi;
|
||||
|
||||
|
|
@ -1814,7 +1798,7 @@ static struct epitem *ep_alloc_epitem(struct eventpoll *ep,
|
|||
|
||||
INIT_LIST_HEAD(&epi->rdllink);
|
||||
epi->ep = ep;
|
||||
ep_set_ffd(&epi->ffd, tfile, fd);
|
||||
epi->ffd = *tf;
|
||||
epi->event = *event;
|
||||
epi_clear_ovflist(epi);
|
||||
|
||||
|
|
@ -1871,8 +1855,8 @@ static int ep_register_epitem(struct ep_ctl_ctx *ctx, struct eventpoll *ep,
|
|||
* Must be called with "mtx" held.
|
||||
*/
|
||||
static int ep_insert(struct ep_ctl_ctx *ctx, struct eventpoll *ep,
|
||||
const struct epoll_event *event, struct file *tfile,
|
||||
int fd, int full_check)
|
||||
const struct epoll_event *event, struct epoll_key *tf,
|
||||
int full_check)
|
||||
{
|
||||
int error, pwake = 0;
|
||||
__poll_t revents;
|
||||
|
|
@ -1880,12 +1864,12 @@ static int ep_insert(struct ep_ctl_ctx *ctx, struct eventpoll *ep,
|
|||
struct ep_pqueue epq;
|
||||
struct eventpoll *tep = NULL;
|
||||
|
||||
if (is_file_epoll(tfile))
|
||||
tep = tfile->private_data;
|
||||
if (is_file_epoll(tf->file))
|
||||
tep = tf->file->private_data;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
|
||||
epi = ep_alloc_epitem(ep, event, tfile, fd);
|
||||
epi = ep_alloc_epitem(ep, event, tf);
|
||||
if (IS_ERR(epi))
|
||||
return PTR_ERR(epi);
|
||||
|
||||
|
|
@ -2610,8 +2594,8 @@ static void ep_ctl_unlock(struct ep_ctl_ctx *ctx, struct eventpoll *ep,
|
|||
}
|
||||
}
|
||||
|
||||
int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
||||
bool nonblock)
|
||||
int do_epoll_ctl_file(struct file *f, int op, struct epoll_key *tf,
|
||||
struct epoll_event *epds, bool nonblock)
|
||||
{
|
||||
int error;
|
||||
int full_check;
|
||||
|
|
@ -2619,17 +2603,8 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
|||
struct epitem *epi;
|
||||
struct ep_ctl_ctx ctx = { };
|
||||
|
||||
CLASS(fd, f)(epfd);
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
|
||||
/* Get the "struct file *" for the target file */
|
||||
CLASS(fd, tf)(fd);
|
||||
if (fd_empty(tf))
|
||||
return -EBADF;
|
||||
|
||||
/* The target file descriptor must support poll */
|
||||
if (!file_can_poll(fd_file(tf)))
|
||||
if (!file_can_poll(tf->file))
|
||||
return -EPERM;
|
||||
|
||||
/* Check if EPOLLWAKEUP is allowed */
|
||||
|
|
@ -2637,10 +2612,10 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
|||
ep_take_care_of_epollwakeup(epds);
|
||||
|
||||
/*
|
||||
* The @epfd file must itself be an eventpoll, and we do not permit
|
||||
* The @f file must itself be an eventpoll, and we do not permit
|
||||
* adding an epoll file descriptor inside itself.
|
||||
*/
|
||||
if (fd_file(f) == fd_file(tf) || !is_file_epoll(fd_file(f)))
|
||||
if (f == tf->file || !is_file_epoll(f))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
|
@ -2651,15 +2626,14 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
|||
if (ep_op_has_event(op) && (epds->events & EPOLLEXCLUSIVE)) {
|
||||
if (op == EPOLL_CTL_MOD)
|
||||
return -EINVAL;
|
||||
if (op == EPOLL_CTL_ADD && (is_file_epoll(fd_file(tf)) ||
|
||||
if (op == EPOLL_CTL_ADD && (is_file_epoll(tf->file) ||
|
||||
(epds->events & ~EPOLLEXCLUSIVE_OK_BITS)))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ep = fd_file(f)->private_data;
|
||||
ep = f->private_data;
|
||||
|
||||
full_check = ep_ctl_lock(&ctx, ep, op, fd_file(f), fd_file(tf),
|
||||
nonblock);
|
||||
full_check = ep_ctl_lock(&ctx, ep, op, f, tf->file, nonblock);
|
||||
if (full_check < 0)
|
||||
return full_check;
|
||||
|
||||
|
|
@ -2667,15 +2641,14 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
|||
* Look the target up in ep's RB tree. We hold ep->mtx, so the
|
||||
* item stays valid until we release.
|
||||
*/
|
||||
epi = ep_find(ep, fd_file(tf), fd);
|
||||
epi = ep_find(ep, tf);
|
||||
|
||||
error = -EINVAL;
|
||||
switch (op) {
|
||||
case EPOLL_CTL_ADD:
|
||||
if (!epi) {
|
||||
epds->events |= EPOLLERR | EPOLLHUP;
|
||||
error = ep_insert(&ctx, ep, epds, fd_file(tf), fd,
|
||||
full_check);
|
||||
error = ep_insert(&ctx, ep, epds, tf, full_check);
|
||||
} else
|
||||
error = -EEXIST;
|
||||
break;
|
||||
|
|
@ -2706,6 +2679,25 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
|||
return error;
|
||||
}
|
||||
|
||||
int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
||||
bool nonblock)
|
||||
{
|
||||
struct epoll_key efd;
|
||||
|
||||
CLASS(fd, f)(epfd);
|
||||
if (fd_empty(f))
|
||||
return -EBADF;
|
||||
|
||||
/* Get the "struct file *" for the target file */
|
||||
CLASS(fd, tf)(fd);
|
||||
if (fd_empty(tf))
|
||||
return -EBADF;
|
||||
|
||||
efd.file = fd_file(tf);
|
||||
efd.fd = fd;
|
||||
return do_epoll_ctl_file(fd_file(f), op, &efd, epds, nonblock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The following function implements the controller interface for
|
||||
* the eventpoll file that enables the insertion/removal/change of
|
||||
|
|
|
|||
|
|
@ -61,8 +61,16 @@ static inline void eventpoll_release(struct file *file)
|
|||
eventpoll_release_file(file);
|
||||
}
|
||||
|
||||
struct epoll_key {
|
||||
struct file *file;
|
||||
int fd;
|
||||
} __packed;
|
||||
|
||||
int do_epoll_ctl_file(struct file *f, int op, struct epoll_key *tf,
|
||||
struct epoll_event *epds, bool nonblock);
|
||||
int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
|
||||
bool nonblock);
|
||||
bool is_file_epoll(struct file *f);
|
||||
|
||||
/* Tells if the epoll_ctl(2) operation needs an event copy from userspace */
|
||||
static inline int ep_op_has_event(int op)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user