| From 260068c5b37b48d8b910c22301984b4913b36481 Mon Sep 17 00:00:00 2001 |
| From: Thomas Richter <tmricht@linux.ibm.com> |
| Date: Fri, 22 Nov 2019 16:43:15 +0100 |
| Subject: [PATCH] s390/cpum_sf: Check for SDBT and SDB consistency |
| |
| commit 247f265fa502e7b17a0cb0cc330e055a36aafce4 upstream. |
| |
| Each SBDT is located at a 4KB page and contains 512 entries. |
| Each entry of a SDBT points to a SDB, a 4KB page containing |
| sampled data. The last entry is a link to another SDBT page. |
| |
| When an event is created the function sequence executed is: |
| |
| __hw_perf_event_init() |
| +--> allocate_buffers() |
| +--> realloc_sampling_buffers() |
| +---> alloc_sample_data_block() |
| |
| Both functions realloc_sampling_buffers() and |
| alloc_sample_data_block() allocate pages and the allocation |
| can fail. This is handled correctly and all allocated |
| pages are freed and error -ENOMEM is returned to the |
| top calling function. Finally the event is not created. |
| |
| Once the event has been created, the amount of initially |
| allocated SDBT and SDB can be too low. This is detected |
| during measurement interrupt handling, where the amount |
| of lost samples is calculated. If the number of lost samples |
| is too high considering sampling frequency and already allocated |
| SBDs, the number of SDBs is enlarged during the next execution |
| of cpumsf_pmu_enable(). |
| |
| If more SBDs need to be allocated, functions |
| |
| realloc_sampling_buffers() |
| +---> alloc-sample_data_block() |
| |
| are called to allocate more pages. Page allocation may fail |
| and the returned error is ignored. A SDBT and SDB setup |
| already exists. |
| |
| However the modified SDBTs and SDBs might end up in a situation |
| where the first entry of an SDBT does not point to an SDB, |
| but another SDBT, basicly an SBDT without payload. |
| This can not be handled by the interrupt handler, where an SDBT |
| must have at least one entry pointing to an SBD. |
| |
| Add a check to avoid SDBTs with out payload (SDBs) when enlarging |
| the buffer setup. |
| |
| Signed-off-by: Thomas Richter <tmricht@linux.ibm.com> |
| Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c |
| index 1266194afb02..de38fa5bbedf 100644 |
| --- a/arch/s390/kernel/perf_cpum_sf.c |
| +++ b/arch/s390/kernel/perf_cpum_sf.c |
| @@ -193,7 +193,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb, |
| unsigned long num_sdb, gfp_t gfp_flags) |
| { |
| int i, rc; |
| - unsigned long *new, *tail; |
| + unsigned long *new, *tail, *tail_prev = NULL; |
| |
| if (!sfb->sdbt || !sfb->tail) |
| return -EINVAL; |
| @@ -232,6 +232,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb, |
| sfb->num_sdbt++; |
| /* Link current page to tail of chain */ |
| *tail = (unsigned long)(void *) new + 1; |
| + tail_prev = tail; |
| tail = new; |
| } |
| |
| @@ -241,10 +242,22 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb, |
| * issue, a new realloc call (if required) might succeed. |
| */ |
| rc = alloc_sample_data_block(tail, gfp_flags); |
| - if (rc) |
| + if (rc) { |
| + /* Undo last SDBT. An SDBT with no SDB at its first |
| + * entry but with an SDBT entry instead can not be |
| + * handled by the interrupt handler code. |
| + * Avoid this situation. |
| + */ |
| + if (tail_prev) { |
| + sfb->num_sdbt--; |
| + free_page((unsigned long) new); |
| + tail = tail_prev; |
| + } |
| break; |
| + } |
| sfb->num_sdb++; |
| tail++; |
| + tail_prev = new = NULL; /* Allocated at least one SBD */ |
| } |
| |
| /* Link sampling buffer to its origin */ |
| -- |
| 2.7.4 |
| |