Skip to content
Open
154 changes: 88 additions & 66 deletions fs/eventpoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ struct eventpoll {
*/
refcount_t refcount;

/* used to defer freeing past ep_get_upwards_depth_proc() RCU walk */
struct rcu_head rcu;

#ifdef CONFIG_NET_RX_BUSY_POLL
/* used to track busy poll napi_id */
unsigned int napi_id;
Expand Down Expand Up @@ -708,41 +711,54 @@ static void ep_free(struct eventpoll *ep)
mutex_destroy(&ep->mtx);
free_uid(ep->user);
wakeup_source_unregister(ep->ws);
kfree(ep);
/* ep_get_upwards_depth_proc() may still hold epi->ep under RCU */
kfree_rcu(ep, rcu);
}

/*
* Removes a "struct epitem" from the eventpoll RB tree and deallocates
* all the associated resources. Must be called with "mtx" held.
* If the dying flag is set, do the removal only if force is true.
* This prevents ep_clear_and_put() from dropping all the ep references
* while running concurrently with eventpoll_release_file().
* Returns true if the eventpoll can be disposed.
* The ffd.file pointer may be in the process of being torn down due to
* being closed, but we may not have finished eventpoll_release() yet.
*
* Normally, even with the atomic_long_inc_not_zero, the file may have
* been free'd and then gotten re-allocated to something else (since
* files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU).
*
* But for epoll, users hold the ep->mtx mutex, and as such any file in
* the process of being free'd will block in eventpoll_release_file()
* and thus the underlying file allocation will not be free'd, and the
* file re-use cannot happen.
*
* For the same reason we can avoid a rcu_read_lock() around the
* operation - 'ffd.file' cannot go away even if the refcount has
* reached zero (but we must still not call out to ->poll() functions
* etc).
*/
static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
static struct file *epi_fget(const struct epitem *epi)
{
struct file *file = epi->ffd.file;
struct epitems_head *to_free;
struct hlist_head *head;
struct file *file;

lockdep_assert_irqs_enabled();
file = epi->ffd.file;
if (!atomic_long_inc_not_zero(&file->f_count))
file = NULL;
return file;
}

/*
* Removes poll wait queue hooks.
*/
ep_unregister_pollwait(ep, epi);
/*
* Called with &file->f_lock held,
* returns with it released
*/
static void ep_remove_file(struct eventpoll *ep, struct epitem *epi,
struct file *file)
{
struct epitems_head *to_free = NULL;
struct hlist_head *head = file->f_ep;

/* Remove the current item from the list of epoll hooks */
spin_lock(&file->f_lock);
if (epi->dying && !force) {
spin_unlock(&file->f_lock);
return false;
}
lockdep_assert_held(&ep->mtx);
lockdep_assert_held(&file->f_lock);

to_free = NULL;
head = file->f_ep;
if (head->first == &epi->fllink && !epi->fllink.next) {
file->f_ep = NULL;
if (hlist_is_singular_node(&epi->fllink, head)) {
/* See eventpoll_release() for details. */
WRITE_ONCE(file->f_ep, NULL);
if (!is_file_epoll(file)) {
struct epitems_head *v;
v = container_of(head, struct epitems_head, epitems);
Expand All @@ -753,6 +769,11 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
hlist_del_rcu(&epi->fllink);
spin_unlock(&file->f_lock);
free_ephead(to_free);
}

static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
{
lockdep_assert_held(&ep->mtx);

rb_erase_cached(&epi->rbn, &ep->rbr);

Expand All @@ -778,9 +799,31 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
/*
* ep_remove variant for callers owing an additional reference to the ep
*/
static void ep_remove_safe(struct eventpoll *ep, struct epitem *epi)
static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
if (__ep_remove(ep, epi, false))
struct file *file __free(fput) = NULL;

lockdep_assert_irqs_enabled();
lockdep_assert_held(&ep->mtx);

ep_unregister_pollwait(ep, epi);

/* cheap sync with eventpoll_release_file() */
if (unlikely(READ_ONCE(epi->dying)))
return;

/*
* If we manage to grab a reference it means we're not in
* eventpoll_release_file() and aren't going to be.
*/
file = epi_fget(epi);
if (!file)
return;

spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);

if (ep_remove_epi(ep, epi))
WARN_ON_ONCE(ep_refcount_dec_and_test(ep));
}

Expand All @@ -807,7 +850,7 @@ static void ep_clear_and_put(struct eventpoll *ep)

/*
* Walks through the whole tree and try to free each "struct epitem".
* Note that ep_remove_safe() will not remove the epitem in case of a
* Note that ep_remove() will not remove the epitem in case of a
* racing eventpoll_release_file(); the latter will do the removal.
* At this point we are sure no poll callbacks will be lingering around.
* Since we still own a reference to the eventpoll struct, the loop can't
Expand All @@ -816,7 +859,7 @@ static void ep_clear_and_put(struct eventpoll *ep)
for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = next) {
next = rb_next(rbp);
epi = rb_entry(rbp, struct epitem, rbn);
ep_remove_safe(ep, epi);
ep_remove(ep, epi);
cond_resched();
}

Expand Down Expand Up @@ -875,34 +918,6 @@ static __poll_t __ep_eventpoll_poll(struct file *file, poll_table *wait, int dep
return res;
}

/*
* The ffd.file pointer may be in the process of being torn down due to
* being closed, but we may not have finished eventpoll_release() yet.
*
* Normally, even with the atomic_long_inc_not_zero, the file may have
* been free'd and then gotten re-allocated to something else (since
* files are not RCU-delayed, they are SLAB_TYPESAFE_BY_RCU).
*
* But for epoll, users hold the ep->mtx mutex, and as such any file in
* the process of being free'd will block in eventpoll_release_file()
* and thus the underlying file allocation will not be free'd, and the
* file re-use cannot happen.
*
* For the same reason we can avoid a rcu_read_lock() around the
* operation - 'ffd.file' cannot go away even if the refcount has
* reached zero (but we must still not call out to ->poll() functions
* etc).
*/
static struct file *epi_fget(const struct epitem *epi)
{
struct file *file;

file = epi->ffd.file;
if (!atomic_long_inc_not_zero(&file->f_count))
file = NULL;
return file;
}

/*
* Differs from ep_eventpoll_poll() in that internal callers already have
* the ep->mtx so we need to start from depth=1, such that mutex_lock_nested()
Expand Down Expand Up @@ -989,7 +1004,7 @@ void eventpoll_release_file(struct file *file)
spin_lock(&file->f_lock);
if (file->f_ep && file->f_ep->first) {
epi = hlist_entry(file->f_ep->first, struct epitem, fllink);
epi->dying = true;
WRITE_ONCE(epi->dying, true);
spin_unlock(&file->f_lock);

/*
Expand All @@ -998,7 +1013,13 @@ void eventpoll_release_file(struct file *file)
*/
ep = epi->ep;
mutex_lock(&ep->mtx);
dispose = __ep_remove(ep, epi, true);

ep_unregister_pollwait(ep, epi);

spin_lock(&file->f_lock);
ep_remove_file(ep, epi, file);
dispose = ep_remove_epi(ep, epi);

mutex_unlock(&ep->mtx);

if (dispose && ep_refcount_dec_and_test(ep))
Expand Down Expand Up @@ -1505,7 +1526,8 @@ static int attach_epitem(struct file *file, struct epitem *epi)
spin_unlock(&file->f_lock);
goto allocate;
}
file->f_ep = head;
/* See eventpoll_release() for details. */
WRITE_ONCE(file->f_ep, head);
to_free = NULL;
}
hlist_add_head_rcu(&epi->fllink, file->f_ep);
Expand Down Expand Up @@ -1571,21 +1593,21 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
mutex_unlock(&tep->mtx);

/*
* ep_remove_safe() calls in the later error paths can't lead to
* ep_remove() calls in the later error paths can't lead to
* ep_free() as the ep file itself still holds an ep reference.
*/
ep_get(ep);

/* now check if we've created too many backpaths */
if (unlikely(full_check && reverse_path_check())) {
ep_remove_safe(ep, epi);
ep_remove(ep, epi);
return -EINVAL;
}

if (epi->event.events & EPOLLWAKEUP) {
error = ep_create_wakeup_source(epi);
if (error) {
ep_remove_safe(ep, epi);
ep_remove(ep, epi);
return error;
}
}
Expand All @@ -1609,7 +1631,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
* high memory pressure.
*/
if (unlikely(!epq.epi)) {
ep_remove_safe(ep, epi);
ep_remove(ep, epi);
return -ENOMEM;
}

Expand Down Expand Up @@ -2262,7 +2284,7 @@ int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
* The eventpoll itself is still alive: the refcount
* can't go to zero here.
*/
ep_remove_safe(ep, epi);
ep_remove(ep, epi);
error = 0;
} else {
error = -ENOENT;
Expand Down
2 changes: 1 addition & 1 deletion include/linux/eventpoll.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ static inline void eventpoll_release(struct file *file)
* because the file in on the way to be removed and nobody ( but
* eventpoll ) has still a reference to this file.
*/
if (likely(!file->f_ep))
if (likely(!READ_ONCE(file->f_ep)))
return;

/*
Expand Down
16 changes: 11 additions & 5 deletions kernel/locking/rtmutex.c
Original file line number Diff line number Diff line change
Expand Up @@ -1534,20 +1534,26 @@ static bool rtmutex_spin_on_owner(struct rt_mutex_base *lock,
*
* Must be called with lock->wait_lock held and interrupts disabled. It must
* have just failed to try_to_take_rt_mutex().
*
* When invoked from rt_mutex_start_proxy_lock() waiter::task != current !
*/
static void __sched remove_waiter(struct rt_mutex_base *lock,
struct rt_mutex_waiter *waiter)
{
bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock));
struct task_struct *owner = rt_mutex_owner(lock);
struct task_struct *waiter_task = waiter->task;
struct rt_mutex_base *next_lock;

lockdep_assert_held(&lock->wait_lock);

raw_spin_lock(&current->pi_lock);
rt_mutex_dequeue(lock, waiter);
current->pi_blocked_on = NULL;
raw_spin_unlock(&current->pi_lock);
if (!waiter_task) /* never enqueued */
return;

scoped_guard(raw_spinlock, &waiter_task->pi_lock) {
rt_mutex_dequeue(lock, waiter);
waiter_task->pi_blocked_on = NULL;
}

/*
* Only update priority if the waiter was the highest priority
Expand Down Expand Up @@ -1583,7 +1589,7 @@ static void __sched remove_waiter(struct rt_mutex_base *lock,
raw_spin_unlock_irq(&lock->wait_lock);

rt_mutex_adjust_prio_chain(owner, RT_MUTEX_MIN_CHAINWALK, lock,
next_lock, NULL, current);
next_lock, NULL, waiter_task);

raw_spin_lock_irq(&lock->wait_lock);
}
Expand Down
2 changes: 1 addition & 1 deletion kernel/locking/rtmutex_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ int __sched rt_mutex_start_proxy_lock(struct rt_mutex_base *lock,

raw_spin_lock_irq(&lock->wait_lock);
ret = __rt_mutex_start_proxy_lock(lock, waiter, task, &wake_q);
if (unlikely(ret))
if (unlikely(ret < 0))
remove_waiter(lock, waiter);
preempt_disable();
raw_spin_unlock_irq(&lock->wait_lock);
Expand Down
Loading