diff options
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 59 |
1 files changed, 46 insertions, 13 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index f2da20fda02d..3efdf2870a35 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
| @@ -1154,7 +1154,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) | |||
| 1154 | /* | 1154 | /* |
| 1155 | * event overflow | 1155 | * event overflow |
| 1156 | */ | 1156 | */ |
| 1157 | handled = 1; | 1157 | handled++; |
| 1158 | data.period = event->hw.last_period; | 1158 | data.period = event->hw.last_period; |
| 1159 | 1159 | ||
| 1160 | if (!x86_perf_event_set_period(event)) | 1160 | if (!x86_perf_event_set_period(event)) |
| @@ -1200,12 +1200,20 @@ void perf_events_lapic_init(void) | |||
| 1200 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1200 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
| 1201 | } | 1201 | } |
| 1202 | 1202 | ||
| 1203 | struct pmu_nmi_state { | ||
| 1204 | unsigned int marked; | ||
| 1205 | int handled; | ||
| 1206 | }; | ||
| 1207 | |||
| 1208 | static DEFINE_PER_CPU(struct pmu_nmi_state, pmu_nmi); | ||
| 1209 | |||
| 1203 | static int __kprobes | 1210 | static int __kprobes |
| 1204 | perf_event_nmi_handler(struct notifier_block *self, | 1211 | perf_event_nmi_handler(struct notifier_block *self, |
| 1205 | unsigned long cmd, void *__args) | 1212 | unsigned long cmd, void *__args) |
| 1206 | { | 1213 | { |
| 1207 | struct die_args *args = __args; | 1214 | struct die_args *args = __args; |
| 1208 | struct pt_regs *regs; | 1215 | unsigned int this_nmi; |
| 1216 | int handled; | ||
| 1209 | 1217 | ||
| 1210 | if (!atomic_read(&active_events)) | 1218 | if (!atomic_read(&active_events)) |
| 1211 | return NOTIFY_DONE; | 1219 | return NOTIFY_DONE; |
| @@ -1214,22 +1222,47 @@ perf_event_nmi_handler(struct notifier_block *self, | |||
| 1214 | case DIE_NMI: | 1222 | case DIE_NMI: |
| 1215 | case DIE_NMI_IPI: | 1223 | case DIE_NMI_IPI: |
| 1216 | break; | 1224 | break; |
| 1217 | 1225 | case DIE_NMIUNKNOWN: | |
| 1226 | this_nmi = percpu_read(irq_stat.__nmi_count); | ||
| 1227 | if (this_nmi != __get_cpu_var(pmu_nmi).marked) | ||
| 1228 | /* let the kernel handle the unknown nmi */ | ||
| 1229 | return NOTIFY_DONE; | ||
| 1230 | /* | ||
| 1231 | * This one is a PMU back-to-back nmi. Two events | ||
| 1232 | * trigger 'simultaneously' raising two back-to-back | ||
| 1233 | * NMIs. If the first NMI handles both, the latter | ||
| 1234 | * will be empty and daze the CPU. So, we drop it to | ||
| 1235 | * avoid false-positive 'unknown nmi' messages. | ||
| 1236 | */ | ||
| 1237 | return NOTIFY_STOP; | ||
| 1218 | default: | 1238 | default: |
| 1219 | return NOTIFY_DONE; | 1239 | return NOTIFY_DONE; |
| 1220 | } | 1240 | } |
| 1221 | 1241 | ||
| 1222 | regs = args->regs; | ||
| 1223 | |||
| 1224 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1242 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
| 1225 | /* | 1243 | |
| 1226 | * Can't rely on the handled return value to say it was our NMI, two | 1244 | handled = x86_pmu.handle_irq(args->regs); |
| 1227 | * events could trigger 'simultaneously' raising two back-to-back NMIs. | 1245 | if (!handled) |
| 1228 | * | 1246 | return NOTIFY_DONE; |
| 1229 | * If the first NMI handles both, the latter will be empty and daze | 1247 | |
| 1230 | * the CPU. | 1248 | this_nmi = percpu_read(irq_stat.__nmi_count); |
| 1231 | */ | 1249 | if ((handled > 1) || |
| 1232 | x86_pmu.handle_irq(regs); | 1250 | /* the next nmi could be a back-to-back nmi */ |
| 1251 | ((__get_cpu_var(pmu_nmi).marked == this_nmi) && | ||
| 1252 | (__get_cpu_var(pmu_nmi).handled > 1))) { | ||
| 1253 | /* | ||
| 1254 | * We could have two subsequent back-to-back nmis: The | ||
| 1255 | * first handles more than one counter, the 2nd | ||
| 1256 | * handles only one counter and the 3rd handles no | ||
| 1257 | * counter. | ||
| 1258 | * | ||
| 1259 | * This is the 2nd nmi because the previous was | ||
| 1260 | * handling more than one counter. We will mark the | ||
| 1261 | * next (3rd) and then drop it if unhandled. | ||
| 1262 | */ | ||
| 1263 | __get_cpu_var(pmu_nmi).marked = this_nmi + 1; | ||
| 1264 | __get_cpu_var(pmu_nmi).handled = handled; | ||
| 1265 | } | ||
| 1233 | 1266 | ||
| 1234 | return NOTIFY_STOP; | 1267 | return NOTIFY_STOP; |
| 1235 | } | 1268 | } |
