]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blobdiff - arch/s390/kernel/perf_cpum_sf.c
s390/cpum_sf: Avoid SBD overflow condition in irq handler
[mirror_ubuntu-eoan-kernel.git] / arch / s390 / kernel / perf_cpum_sf.c
index 1266194afb026fb04d626ff8f0a598b7cb2f4cbe..c874575be88f4ceef7faf467aed43a04947678b7 100644 (file)
@@ -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 */
@@ -728,6 +741,12 @@ static int __hw_perf_event_init(struct perf_event *event)
                goto out;
        }
 
+       if (si.ribm & CPU_MF_SF_RIBM_NOTAV) {
+               pr_warn("CPU Measurement Facility sampling is temporarily not available\n");
+               err = -EBUSY;
+               goto out;
+       }
+
        /* Always enable basic sampling */
        SAMPL_FLAGS(hwc) = PERF_CPUM_SF_BASIC_MODE;
 
@@ -1248,18 +1267,28 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
                 */
                if (flush_all && done)
                        break;
-
-               /* If an event overflow happened, discard samples by
-                * processing any remaining sample-data-blocks.
-                */
-               if (event_overflow)
-                       flush_all = 1;
        }
 
        /* Account sample overflows in the event hardware structure */
        if (sampl_overflow)
                OVERFLOW_REG(hwc) = DIV_ROUND_UP(OVERFLOW_REG(hwc) +
                                                 sampl_overflow, 1 + num_sdb);
+
+       /* Perf_event_overflow() and perf_event_account_interrupt() limit
+        * the interrupt rate to an upper limit. Roughly 1000 samples per
+        * task tick.
+        * Hitting this limit results in a large number
+        * of throttled REF_REPORT_THROTTLE entries and the samples
+        * are dropped.
+        * Slightly increase the interval to avoid hitting this limit.
+        */
+       if (event_overflow) {
+               SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10);
+               debug_sprintf_event(sfdbg, 1, "%s: rate adjustment %ld\n",
+                                   __func__,
+                                   DIV_ROUND_UP(SAMPL_RATE(hwc), 10));
+       }
+
        if (sampl_overflow || event_overflow)
                debug_sprintf_event(sfdbg, 4, "hw_perf_event_update: "
                                    "overflow stats: sample=%llu event=%llu\n",