diff --git a/fs/eventpoll.c b/fs/eventpoll.c index b839cc02eb0e..a569e98d4a99 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -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 diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 728fb5dee5ed..de1c738aa8ad 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -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)