diff options
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.patch | 164 |
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 @@ | |||
1 | From 967aa5fe959f1110ace296ef2d2f90fd6a5c431b Mon Sep 17 00:00:00 2001 | ||
2 | From: Oleg Nesterov <oleg@redhat.com> | ||
3 | Date: Fri, 24 Feb 2012 20:07:11 +0100 | ||
4 | Subject: [PATCH 68/72] epoll: introduce POLLFREE to flush ->signalfd_wqh | ||
5 | before kfree() | ||
6 | |||
7 | commit d80e731ecab420ddcb79ee9d0ac427acbc187b4b upstream. | ||
8 | |||
9 | This patch is intentionally incomplete to simplify the review. | ||
10 | It ignores ep_unregister_pollwait() which plays with the same wqh. | ||
11 | See the next change. | ||
12 | |||
13 | epoll assumes that the EPOLL_CTL_ADD'ed file controls everything | ||
14 | f_op->poll() needs. In particular it assumes that the wait queue | ||
15 | can't go away until eventpoll_release(). This is not true in case | ||
16 | of signalfd, the task which does EPOLL_CTL_ADD uses its ->sighand | ||
17 | which is not connected to the file. | ||
18 | |||
19 | This patch adds the special event, POLLFREE, currently only for | ||
20 | epoll. It expects that init_poll_funcptr()'ed hook should do the | ||
21 | necessary cleanup. Perhaps it should be defined as EPOLLFREE in | ||
22 | eventpoll. | ||
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() | ||
26 | helper. | ||
27 | |||
28 | ep_poll_callback(POLLFREE) simply does list_del_init(task_list). | ||
29 | This make this poll entry inconsistent, but we don't care. If you | ||
30 | share epoll fd which contains our sigfd with another process you | ||
31 | should blame yourself. signalfd is "really special". I simply do | ||
32 | not know how we can define the "right" semantics if it used with | ||
33 | epoll. | ||
34 | |||
35 | The main problem is, epoll calls signalfd_poll() once to establish | ||
36 | the connection with the wait queue, after that signalfd_poll(NULL) | ||
37 | returns the different/inconsistent results depending on who does | ||
38 | EPOLL_CTL_MOD/signalfd_read/etc. IOW: apart from sigmask, signalfd | ||
39 | has nothing to do with the file, it works with the current thread. | ||
40 | |||
41 | In short: this patch is the hack which tries to fix the symptoms. | ||
42 | It also assumes that nobody can take tasklist_lock under epoll | ||
43 | locks, this seems to be true. | ||
44 | |||
45 | Note: | ||
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 | |||
54 | Reported-by: Maxime Bizon <mbizon@freebox.fr> | ||
55 | Signed-off-by: Oleg Nesterov <oleg@redhat.com> | ||
56 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | ||
57 | Signed-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 | |||
66 | diff --git a/fs/eventpoll.c b/fs/eventpoll.c | ||
67 | index 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 | /* | ||
81 | diff --git a/fs/signalfd.c b/fs/signalfd.c | ||
82 | index 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 | }; | ||
103 | diff --git a/include/asm-generic/poll.h b/include/asm-generic/poll.h | ||
104 | index 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; | ||
116 | diff --git a/include/linux/signalfd.h b/include/linux/signalfd.h | ||
117 | index 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 | - | ||
138 | diff --git a/kernel/fork.c b/kernel/fork.c | ||
139 | index 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 | -- | ||
163 | 1.7.9.4 | ||
164 | |||