summaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch')
-rw-r--r--recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch164
1 files changed, 164 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch b/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch
new file mode 100644
index 00000000..a1ed8d1f
--- /dev/null
+++ b/recipes-kernel/linux/linux-ti33x-psp-3.2/3.2.9/0068-epoll-introduce-POLLFREE-to-flush-signalfd_wqh-befor.patch
@@ -0,0 +1,164 @@
1From 967aa5fe959f1110ace296ef2d2f90fd6a5c431b Mon Sep 17 00:00:00 2001
2From: Oleg Nesterov <oleg@redhat.com>
3Date: Fri, 24 Feb 2012 20:07:11 +0100
4Subject: [PATCH 68/72] epoll: introduce POLLFREE to flush ->signalfd_wqh
5 before kfree()
6
7commit d80e731ecab420ddcb79ee9d0ac427acbc187b4b upstream.
8
9This patch is intentionally incomplete to simplify the review.
10It ignores ep_unregister_pollwait() which plays with the same wqh.
11See the next change.
12
13epoll assumes that the EPOLL_CTL_ADD'ed file controls everything
14f_op->poll() needs. In particular it assumes that the wait queue
15can't go away until eventpoll_release(). This is not true in case
16of signalfd, the task which does EPOLL_CTL_ADD uses its ->sighand
17which is not connected to the file.
18
19This patch adds the special event, POLLFREE, currently only for
20epoll. It expects that init_poll_funcptr()'ed hook should do the
21necessary cleanup. Perhaps it should be defined as EPOLLFREE in
22eventpoll.
23
24__cleanup_sighand() is changed to do wake_up_poll(POLLFREE) if
25->signalfd_wqh is not empty, we add the new signalfd_cleanup()
26helper.
27
28ep_poll_callback(POLLFREE) simply does list_del_init(task_list).
29This make this poll entry inconsistent, but we don't care. If you
30share epoll fd which contains our sigfd with another process you
31should blame yourself. signalfd is "really special". I simply do
32not know how we can define the "right" semantics if it used with
33epoll.
34
35The main problem is, epoll calls signalfd_poll() once to establish
36the connection with the wait queue, after that signalfd_poll(NULL)
37returns the different/inconsistent results depending on who does
38EPOLL_CTL_MOD/signalfd_read/etc. IOW: apart from sigmask, signalfd
39has nothing to do with the file, it works with the current thread.
40
41In short: this patch is the hack which tries to fix the symptoms.
42It also assumes that nobody can take tasklist_lock under epoll
43locks, this seems to be true.
44
45Note:
46
47 - we do not have wake_up_all_poll() but wake_up_poll()
48 is fine, poll/epoll doesn't use WQ_FLAG_EXCLUSIVE.
49
50 - signalfd_cleanup() uses POLLHUP along with POLLFREE,
51 we need a couple of simple changes in eventpoll.c to
52 make sure it can't be "lost".
53
54Reported-by: Maxime Bizon <mbizon@freebox.fr>
55Signed-off-by: Oleg Nesterov <oleg@redhat.com>
56Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
57Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
58---
59 fs/eventpoll.c | 4 ++++
60 fs/signalfd.c | 11 +++++++++++
61 include/asm-generic/poll.h | 2 ++
62 include/linux/signalfd.h | 5 ++++-
63 kernel/fork.c | 5 ++++-
64 5 files changed, 25 insertions(+), 2 deletions(-)
65
66diff --git a/fs/eventpoll.c b/fs/eventpoll.c
67index 828e750..ede66ad 100644
68--- a/fs/eventpoll.c
69+++ b/fs/eventpoll.c
70@@ -827,6 +827,10 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k
71 struct epitem *epi = ep_item_from_wait(wait);
72 struct eventpoll *ep = epi->ep;
73
74+ /* the caller holds eppoll_entry->whead->lock */
75+ if ((unsigned long)key & POLLFREE)
76+ list_del_init(&wait->task_list);
77+
78 spin_lock_irqsave(&ep->lock, flags);
79
80 /*
81diff --git a/fs/signalfd.c b/fs/signalfd.c
82index 492465b..79c1eea 100644
83--- a/fs/signalfd.c
84+++ b/fs/signalfd.c
85@@ -30,6 +30,17 @@
86 #include <linux/signalfd.h>
87 #include <linux/syscalls.h>
88
89+void signalfd_cleanup(struct sighand_struct *sighand)
90+{
91+ wait_queue_head_t *wqh = &sighand->signalfd_wqh;
92+
93+ if (likely(!waitqueue_active(wqh)))
94+ return;
95+
96+ /* wait_queue_t->func(POLLFREE) should do remove_wait_queue() */
97+ wake_up_poll(wqh, POLLHUP | POLLFREE);
98+}
99+
100 struct signalfd_ctx {
101 sigset_t sigmask;
102 };
103diff --git a/include/asm-generic/poll.h b/include/asm-generic/poll.h
104index 44bce83..9ce7f44 100644
105--- a/include/asm-generic/poll.h
106+++ b/include/asm-generic/poll.h
107@@ -28,6 +28,8 @@
108 #define POLLRDHUP 0x2000
109 #endif
110
111+#define POLLFREE 0x4000 /* currently only for epoll */
112+
113 struct pollfd {
114 int fd;
115 short events;
116diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h
117index 3ff4961..247399b 100644
118--- a/include/linux/signalfd.h
119+++ b/include/linux/signalfd.h
120@@ -61,13 +61,16 @@ static inline void signalfd_notify(struct task_struct *tsk, int sig)
121 wake_up(&tsk->sighand->signalfd_wqh);
122 }
123
124+extern void signalfd_cleanup(struct sighand_struct *sighand);
125+
126 #else /* CONFIG_SIGNALFD */
127
128 static inline void signalfd_notify(struct task_struct *tsk, int sig) { }
129
130+static inline void signalfd_cleanup(struct sighand_struct *sighand) { }
131+
132 #endif /* CONFIG_SIGNALFD */
133
134 #endif /* __KERNEL__ */
135
136 #endif /* _LINUX_SIGNALFD_H */
137-
138diff --git a/kernel/fork.c b/kernel/fork.c
139index da4a6a1..0acf42c0 100644
140--- a/kernel/fork.c
141+++ b/kernel/fork.c
142@@ -66,6 +66,7 @@
143 #include <linux/user-return-notifier.h>
144 #include <linux/oom.h>
145 #include <linux/khugepaged.h>
146+#include <linux/signalfd.h>
147
148 #include <asm/pgtable.h>
149 #include <asm/pgalloc.h>
150@@ -910,8 +911,10 @@ static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
151
152 void __cleanup_sighand(struct sighand_struct *sighand)
153 {
154- if (atomic_dec_and_test(&sighand->count))
155+ if (atomic_dec_and_test(&sighand->count)) {
156+ signalfd_cleanup(sighand);
157 kmem_cache_free(sighand_cachep, sighand);
158+ }
159 }
160
161
162--
1631.7.9.4
164