ring-buffer: Fix waking up ring buffer readers
[linux-2.6-block.git] / kernel / trace / ring_buffer.c
index 0699027b4f4c92d3a6ed7c62ee64491d7cd89046..3400f11286e35a3c482ba193873add238be85b53 100644 (file)
@@ -384,7 +384,6 @@ struct rb_irq_work {
        struct irq_work                 work;
        wait_queue_head_t               waiters;
        wait_queue_head_t               full_waiters;
-       long                            wait_index;
        bool                            waiters_pending;
        bool                            full_waiters_pending;
        bool                            wakeup_full;
@@ -798,14 +797,40 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu)
                rbwork = &cpu_buffer->irq_work;
        }
 
-       rbwork->wait_index++;
-       /* make sure the waiters see the new index */
-       smp_wmb();
-
        /* This can be called in any context */
        irq_work_queue(&rbwork->work);
 }
 
+static bool rb_watermark_hit(struct trace_buffer *buffer, int cpu, int full)
+{
+       struct ring_buffer_per_cpu *cpu_buffer;
+       bool ret = false;
+
+       /* Reads of all CPUs always waits for any data */
+       if (cpu == RING_BUFFER_ALL_CPUS)
+               return !ring_buffer_empty(buffer);
+
+       cpu_buffer = buffer->buffers[cpu];
+
+       if (!ring_buffer_empty_cpu(buffer, cpu)) {
+               unsigned long flags;
+               bool pagebusy;
+
+               if (!full)
+                       return true;
+
+               raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
+               pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page;
+               ret = !pagebusy && full_hit(buffer, cpu, full);
+
+               if (!cpu_buffer->shortest_full ||
+                   cpu_buffer->shortest_full > full)
+                       cpu_buffer->shortest_full = full;
+               raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
+       }
+       return ret;
+}
+
 /**
  * ring_buffer_wait - wait for input to the ring buffer
  * @buffer: buffer to wait on
@@ -821,7 +846,6 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)
        struct ring_buffer_per_cpu *cpu_buffer;
        DEFINE_WAIT(wait);
        struct rb_irq_work *work;
-       long wait_index;
        int ret = 0;
 
        /*
@@ -840,81 +864,54 @@ int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full)
                work = &cpu_buffer->irq_work;
        }
 
-       wait_index = READ_ONCE(work->wait_index);
-
-       while (true) {
-               if (full)
-                       prepare_to_wait(&work->full_waiters, &wait, TASK_INTERRUPTIBLE);
-               else
-                       prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
-
-               /*
-                * The events can happen in critical sections where
-                * checking a work queue can cause deadlocks.
-                * After adding a task to the queue, this flag is set
-                * only to notify events to try to wake up the queue
-                * using irq_work.
-                *
-                * We don't clear it even if the buffer is no longer
-                * empty. The flag only causes the next event to run
-                * irq_work to do the work queue wake up. The worse
-                * that can happen if we race with !trace_empty() is that
-                * an event will cause an irq_work to try to wake up
-                * an empty queue.
-                *
-                * There's no reason to protect this flag either, as
-                * the work queue and irq_work logic will do the necessary
-                * synchronization for the wake ups. The only thing
-                * that is necessary is that the wake up happens after
-                * a task has been queued. It's OK for spurious wake ups.
-                */
-               if (full)
-                       work->full_waiters_pending = true;
-               else
-                       work->waiters_pending = true;
-
-               if (signal_pending(current)) {
-                       ret = -EINTR;
-                       break;
-               }
-
-               if (cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer))
-                       break;
-
-               if (cpu != RING_BUFFER_ALL_CPUS &&
-                   !ring_buffer_empty_cpu(buffer, cpu)) {
-                       unsigned long flags;
-                       bool pagebusy;
-                       bool done;
-
-                       if (!full)
-                               break;
-
-                       raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
-                       pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page;
-                       done = !pagebusy && full_hit(buffer, cpu, full);
+       if (full)
+               prepare_to_wait(&work->full_waiters, &wait, TASK_INTERRUPTIBLE);
+       else
+               prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
 
-                       if (!cpu_buffer->shortest_full ||
-                           cpu_buffer->shortest_full > full)
-                               cpu_buffer->shortest_full = full;
-                       raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
-                       if (done)
-                               break;
-               }
+       /*
+        * The events can happen in critical sections where
+        * checking a work queue can cause deadlocks.
+        * After adding a task to the queue, this flag is set
+        * only to notify events to try to wake up the queue
+        * using irq_work.
+        *
+        * We don't clear it even if the buffer is no longer
+        * empty. The flag only causes the next event to run
+        * irq_work to do the work queue wake up. The worse
+        * that can happen if we race with !trace_empty() is that
+        * an event will cause an irq_work to try to wake up
+        * an empty queue.
+        *
+        * There's no reason to protect this flag either, as
+        * the work queue and irq_work logic will do the necessary
+        * synchronization for the wake ups. The only thing
+        * that is necessary is that the wake up happens after
+        * a task has been queued. It's OK for spurious wake ups.
+        */
+       if (full)
+               work->full_waiters_pending = true;
+       else
+               work->waiters_pending = true;
 
-               schedule();
+       if (rb_watermark_hit(buffer, cpu, full))
+               goto out;
 
-               /* Make sure to see the new wait index */
-               smp_rmb();
-               if (wait_index != work->wait_index)
-                       break;
+       if (signal_pending(current)) {
+               ret = -EINTR;
+               goto out;
        }
 
+       schedule();
+ out:
        if (full)
                finish_wait(&work->full_waiters, &wait);
        else
                finish_wait(&work->waiters, &wait);
 
+       if (!ret && !rb_watermark_hit(buffer, cpu, full) && signal_pending(current))
+               ret = -EINTR;
+
        return ret;
 }