diff options
Diffstat (limited to 'kernel/events')
| -rw-r--r-- | kernel/events/ring_buffer.c | 42 |
1 files changed, 26 insertions, 16 deletions
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index e8b168af135b..146a5792b1d2 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c | |||
| @@ -61,19 +61,20 @@ again: | |||
| 61 | * | 61 | * |
| 62 | * kernel user | 62 | * kernel user |
| 63 | * | 63 | * |
| 64 | * READ ->data_tail READ ->data_head | 64 | * if (LOAD ->data_tail) { LOAD ->data_head |
| 65 | * smp_mb() (A) smp_rmb() (C) | 65 | * (A) smp_rmb() (C) |
| 66 | * WRITE $data READ $data | 66 | * STORE $data LOAD $data |
| 67 | * smp_wmb() (B) smp_mb() (D) | 67 | * smp_wmb() (B) smp_mb() (D) |
| 68 | * STORE ->data_head WRITE ->data_tail | 68 | * STORE ->data_head STORE ->data_tail |
| 69 | * } | ||
| 69 | * | 70 | * |
| 70 | * Where A pairs with D, and B pairs with C. | 71 | * Where A pairs with D, and B pairs with C. |
| 71 | * | 72 | * |
| 72 | * I don't think A needs to be a full barrier because we won't in fact | 73 | * In our case (A) is a control dependency that separates the load of |
| 73 | * write data until we see the store from userspace. So we simply don't | 74 | * the ->data_tail and the stores of $data. In case ->data_tail |
| 74 | * issue the data WRITE until we observe it. Be conservative for now. | 75 | * indicates there is no room in the buffer to store $data we do not. |
| 75 | * | 76 | * |
| 76 | * OTOH, D needs to be a full barrier since it separates the data READ | 77 | * D needs to be a full barrier since it separates the data READ |
| 77 | * from the tail WRITE. | 78 | * from the tail WRITE. |
| 78 | * | 79 | * |
| 79 | * For B a WMB is sufficient since it separates two WRITEs, and for C | 80 | * For B a WMB is sufficient since it separates two WRITEs, and for C |
| @@ -81,7 +82,7 @@ again: | |||
| 81 | * | 82 | * |
| 82 | * See perf_output_begin(). | 83 | * See perf_output_begin(). |
| 83 | */ | 84 | */ |
| 84 | smp_wmb(); | 85 | smp_wmb(); /* B, matches C */ |
| 85 | rb->user_page->data_head = head; | 86 | rb->user_page->data_head = head; |
| 86 | 87 | ||
| 87 | /* | 88 | /* |
| @@ -144,17 +145,26 @@ int perf_output_begin(struct perf_output_handle *handle, | |||
| 144 | if (!rb->overwrite && | 145 | if (!rb->overwrite && |
| 145 | unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) | 146 | unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) |
| 146 | goto fail; | 147 | goto fail; |
| 148 | |||
| 149 | /* | ||
| 150 | * The above forms a control dependency barrier separating the | ||
| 151 | * @tail load above from the data stores below. Since the @tail | ||
| 152 | * load is required to compute the branch to fail below. | ||
| 153 | * | ||
| 154 | * A, matches D; the full memory barrier userspace SHOULD issue | ||
| 155 | * after reading the data and before storing the new tail | ||
| 156 | * position. | ||
| 157 | * | ||
| 158 | * See perf_output_put_handle(). | ||
| 159 | */ | ||
| 160 | |||
| 147 | head += size; | 161 | head += size; |
| 148 | } while (local_cmpxchg(&rb->head, offset, head) != offset); | 162 | } while (local_cmpxchg(&rb->head, offset, head) != offset); |
| 149 | 163 | ||
| 150 | /* | 164 | /* |
| 151 | * Separate the userpage->tail read from the data stores below. | 165 | * We rely on the implied barrier() by local_cmpxchg() to ensure |
| 152 | * Matches the MB userspace SHOULD issue after reading the data | 166 | * none of the data stores below can be lifted up by the compiler. |
| 153 | * and before storing the new tail position. | ||
| 154 | * | ||
| 155 | * See perf_output_put_handle(). | ||
| 156 | */ | 167 | */ |
| 157 | smp_mb(); | ||
| 158 | 168 | ||
| 159 | if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) | 169 | if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) |
| 160 | local_add(rb->watermark, &rb->wakeup); | 170 | local_add(rb->watermark, &rb->wakeup); |
