aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.com>2015-09-07 08:38:37 -0400
committerSteven Rostedt <rostedt@goodmis.org>2015-11-03 16:03:24 -0500
commit8b46ff6938d2c78a16520a37086944ecbaa8ab8b (patch)
treebc6dedd4617eee7a290a23d1db7b59be05a86e4c
parentfb8c2293e1a3c4a35a571b82cc2efae0c9e59b2b (diff)
ring_buffer: Do no not complete benchmark reader too early
It seems that complete(&read_done) might be called too early in some situations. 1st scenario: ------------- CPU0 CPU1 ring_buffer_producer_thread() wake_up_process(consumer); wait_for_completion(&read_start); ring_buffer_consumer_thread() complete(&read_start); ring_buffer_producer() # producing data in # the do-while cycle ring_buffer_consumer(); # reading data # got error # set kill_test = 1; set_current_state( TASK_INTERRUPTIBLE); if (reader_finish) # false schedule(); # producer still in the middle of # do-while cycle if (consumer && !(cnt % wakeup_interval)) wake_up_process(consumer); # spurious wakeup while (!reader_finish && !kill_test) # leaving because # kill_test == 1 reader_finish = 0; complete(&read_done); 1st BANG: We might access uninitialized "read_done" if this is the the first round. # producer finally leaving # the do-while cycle because kill_test == 1; if (consumer) { reader_finish = 1; wake_up_process(consumer); wait_for_completion(&read_done); 2nd BANG: This will never complete because consumer already did the completion. 2nd scenario: ------------- CPU0 CPU1 ring_buffer_producer_thread() wake_up_process(consumer); wait_for_completion(&read_start); ring_buffer_consumer_thread() complete(&read_start); ring_buffer_producer() # CPU3 removes the module <--- difference from # and stops producer <--- the 1st scenario if (kthread_should_stop()) kill_test = 1; ring_buffer_consumer(); while (!reader_finish && !kill_test) # kill_test == 1 => we never go # into the top level while() reader_finish = 0; complete(&read_done); # producer still in the middle of # do-while cycle if (consumer && !(cnt % wakeup_interval)) wake_up_process(consumer); # spurious wakeup while (!reader_finish && !kill_test) # leaving because kill_test == 1 reader_finish = 0; complete(&read_done); BANG: We are in the same "bang" situations as in the 1st scenario. Root of the problem: -------------------- ring_buffer_consumer() must complete "read_done" only when "reader_finish" variable is set. It must not be skipped due to other conditions. Note that we still must keep the check for "reader_finish" in a loop because there might be spurious wakeups as described in the above scenarios. Solution: ---------- The top level cycle in ring_buffer_consumer() will finish only when "reader_finish" is set. The data will be read in "while-do" cycle so that they are not read after an error (kill_test == 1) or a spurious wake up. In addition, "reader_finish" is manipulated by the producer thread. Therefore we add READ_ONCE() to make sure that the fresh value is read in each cycle. Also we add the corresponding barrier to synchronize the sleep check. Next we set the state back to TASK_RUNNING for the situation where we did not sleep. Just from paranoid reasons, we initialize both completions statically. This is safer, in case there are other races that we are unaware of. As a side effect we could remove the memory barrier from ring_buffer_producer_thread(). IMHO, this was the reason for the barrier. ring_buffer_reset() uses spin locks that should provide the needed memory barrier for using the buffer. Link: http://lkml.kernel.org/r/1441629518-32712-2-git-send-email-pmladek@suse.com Signed-off-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--kernel/trace/ring_buffer_benchmark.c25
1 files changed, 16 insertions, 9 deletions
diff --git a/kernel/trace/ring_buffer_benchmark.c b/kernel/trace/ring_buffer_benchmark.c
index a1503a027ee2..9ea7949366b3 100644
--- a/kernel/trace/ring_buffer_benchmark.c
+++ b/kernel/trace/ring_buffer_benchmark.c
@@ -24,8 +24,8 @@ struct rb_page {
24static int wakeup_interval = 100; 24static int wakeup_interval = 100;
25 25
26static int reader_finish; 26static int reader_finish;
27static struct completion read_start; 27static DECLARE_COMPLETION(read_start);
28static struct completion read_done; 28static DECLARE_COMPLETION(read_done);
29 29
30static struct ring_buffer *buffer; 30static struct ring_buffer *buffer;
31static struct task_struct *producer; 31static struct task_struct *producer;
@@ -178,10 +178,14 @@ static void ring_buffer_consumer(void)
178 read_events ^= 1; 178 read_events ^= 1;
179 179
180 read = 0; 180 read = 0;
181 while (!reader_finish && !kill_test) { 181 /*
182 int found; 182 * Continue running until the producer specifically asks to stop
183 * and is ready for the completion.
184 */
185 while (!READ_ONCE(reader_finish)) {
186 int found = 1;
183 187
184 do { 188 while (found && !kill_test) {
185 int cpu; 189 int cpu;
186 190
187 found = 0; 191 found = 0;
@@ -195,17 +199,23 @@ static void ring_buffer_consumer(void)
195 199
196 if (kill_test) 200 if (kill_test)
197 break; 201 break;
202
198 if (stat == EVENT_FOUND) 203 if (stat == EVENT_FOUND)
199 found = 1; 204 found = 1;
205
200 } 206 }
201 } while (found && !kill_test); 207 }
202 208
209 /* Wait till the producer wakes us up when there is more data
210 * available or when the producer wants us to finish reading.
211 */
203 set_current_state(TASK_INTERRUPTIBLE); 212 set_current_state(TASK_INTERRUPTIBLE);
204 if (reader_finish) 213 if (reader_finish)
205 break; 214 break;
206 215
207 schedule(); 216 schedule();
208 } 217 }
218 __set_current_state(TASK_RUNNING);
209 reader_finish = 0; 219 reader_finish = 0;
210 complete(&read_done); 220 complete(&read_done);
211} 221}
@@ -389,13 +399,10 @@ static int ring_buffer_consumer_thread(void *arg)
389 399
390static int ring_buffer_producer_thread(void *arg) 400static int ring_buffer_producer_thread(void *arg)
391{ 401{
392 init_completion(&read_start);
393
394 while (!kthread_should_stop() && !kill_test) { 402 while (!kthread_should_stop() && !kill_test) {
395 ring_buffer_reset(buffer); 403 ring_buffer_reset(buffer);
396 404
397 if (consumer) { 405 if (consumer) {
398 smp_wmb();
399 wake_up_process(consumer); 406 wake_up_process(consumer);
400 wait_for_completion(&read_start); 407 wait_for_completion(&read_start);
401 } 408 }