summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/eventpoll.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 5ee4398a6cb8..0f785c0a1544 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -912,22 +912,26 @@ static bool ep_remove_epi(struct eventpoll *ep, struct epitem *epi)
*/
static void ep_remove(struct eventpoll *ep, struct epitem *epi)
{
- struct file *file = epi->ffd.file;
+ struct file *file __free(fput) = NULL;
lockdep_assert_irqs_enabled();
lockdep_assert_held(&ep->mtx);
ep_unregister_pollwait(ep, epi);
- /* sync with eventpoll_release_file() */
+ /* cheap sync with eventpoll_release_file() */
if (unlikely(READ_ONCE(epi->dying)))
return;
- spin_lock(&file->f_lock);
- if (epi->dying) {
- spin_unlock(&file->f_lock);
+ /*
+ * 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))