OSDN Git Service

s390/cpum_sf: Avoid SBD overflow condition in irq handler
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / arch / s390 / kernel / perf_cpum_sf.c
index 3d8da1e..4a76b38 100644 (file)
@@ -185,7 +185,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;
@@ -224,6 +224,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;
                }
 
@@ -233,10 +234,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 */
@@ -744,6 +757,10 @@ static int __hw_perf_event_init(struct perf_event *event)
         */
        rate = 0;
        if (attr->freq) {
+               if (!attr->sample_freq) {
+                       err = -EINVAL;
+                       goto out;
+               }
                rate = freq_to_sample_rate(&si, attr->sample_freq);
                rate = hw_limit_rate(&si, rate);
                attr->freq = 0;
@@ -1277,18 +1294,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",
@@ -1612,14 +1639,17 @@ static int __init init_cpum_sampling_pmu(void)
        }
 
        sfdbg = debug_register(KMSG_COMPONENT, 2, 1, 80);
-       if (!sfdbg)
+       if (!sfdbg) {
                pr_err("Registering for s390dbf failed\n");
+               return -ENOMEM;
+       }
        debug_register_view(sfdbg, &debug_sprintf_view);
 
        err = register_external_irq(EXT_IRQ_MEASURE_ALERT,
                                    cpumf_measurement_alert);
        if (err) {
                pr_cpumsf_err(RS_INIT_FAILURE_ALRT);
+               debug_unregister(sfdbg);
                goto out;
        }
 
@@ -1628,6 +1658,7 @@ static int __init init_cpum_sampling_pmu(void)
                pr_cpumsf_err(RS_INIT_FAILURE_PERF);
                unregister_external_irq(EXT_IRQ_MEASURE_ALERT,
                                        cpumf_measurement_alert);
+               debug_unregister(sfdbg);
                goto out;
        }
        perf_cpu_notifier(cpumf_pmu_notifier);