Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 899edae6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'perf-fixes-for-linus' of...

Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  perf, x86: Try to handle unknown nmis with an enabled PMU
  perf, x86: Fix handle_irq return values
  perf, x86: Fix accidentally ack'ing a second event on intel perf counter
  oprofile, x86: fix init_sysfs() function stub
  lockup_detector: Sync touch_*_watchdog back to old semantics
  tracing: Fix a race in function profile
  oprofile, x86: fix init_sysfs error handling
  perf_events: Fix time tracking for events with pid != -1 and cpu != -1
  perf: Initialize callchains roots's childen hits
  oprofile: fix crash when accessing freed task structs
parents c8c727db 4177c42a
Loading
Loading
Loading
Loading
+46 −13
Original line number Diff line number Diff line
@@ -1154,7 +1154,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs)
		/*
		 * event overflow
		 */
		handled		= 1;
		handled++;
		data.period	= event->hw.last_period;

		if (!x86_perf_event_set_period(event))
@@ -1200,12 +1200,20 @@ void perf_events_lapic_init(void)
	apic_write(APIC_LVTPC, APIC_DM_NMI);
}

struct pmu_nmi_state {
	unsigned int	marked;
	int		handled;
};

static DEFINE_PER_CPU(struct pmu_nmi_state, pmu_nmi);

static int __kprobes
perf_event_nmi_handler(struct notifier_block *self,
			 unsigned long cmd, void *__args)
{
	struct die_args *args = __args;
	struct pt_regs *regs;
	unsigned int this_nmi;
	int handled;

	if (!atomic_read(&active_events))
		return NOTIFY_DONE;
@@ -1214,22 +1222,47 @@ perf_event_nmi_handler(struct notifier_block *self,
	case DIE_NMI:
	case DIE_NMI_IPI:
		break;

	case DIE_NMIUNKNOWN:
		this_nmi = percpu_read(irq_stat.__nmi_count);
		if (this_nmi != __get_cpu_var(pmu_nmi).marked)
			/* let the kernel handle the unknown nmi */
			return NOTIFY_DONE;
		/*
		 * This one is a PMU back-to-back nmi. Two events
		 * trigger 'simultaneously' raising two back-to-back
		 * NMIs. If the first NMI handles both, the latter
		 * will be empty and daze the CPU. So, we drop it to
		 * avoid false-positive 'unknown nmi' messages.
		 */
		return NOTIFY_STOP;
	default:
		return NOTIFY_DONE;
	}

	regs = args->regs;

	apic_write(APIC_LVTPC, APIC_DM_NMI);

	handled = x86_pmu.handle_irq(args->regs);
	if (!handled)
		return NOTIFY_DONE;

	this_nmi = percpu_read(irq_stat.__nmi_count);
	if ((handled > 1) ||
		/* the next nmi could be a back-to-back nmi */
	    ((__get_cpu_var(pmu_nmi).marked == this_nmi) &&
	     (__get_cpu_var(pmu_nmi).handled > 1))) {
		/*
	 * Can't rely on the handled return value to say it was our NMI, two
	 * events could trigger 'simultaneously' raising two back-to-back NMIs.
		 * We could have two subsequent back-to-back nmis: The
		 * first handles more than one counter, the 2nd
		 * handles only one counter and the 3rd handles no
		 * counter.
		 *
	 * If the first NMI handles both, the latter will be empty and daze
	 * the CPU.
		 * This is the 2nd nmi because the previous was
		 * handling more than one counter. We will mark the
		 * next (3rd) and then drop it if unhandled.
		 */
	x86_pmu.handle_irq(regs);
		__get_cpu_var(pmu_nmi).marked	= this_nmi + 1;
		__get_cpu_var(pmu_nmi).handled	= handled;
	}

	return NOTIFY_STOP;
}
+9 −6
Original line number Diff line number Diff line
@@ -712,7 +712,8 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
	struct perf_sample_data data;
	struct cpu_hw_events *cpuc;
	int bit, loops;
	u64 ack, status;
	u64 status;
	int handled = 0;

	perf_sample_data_init(&data, 0);

@@ -728,6 +729,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)

	loops = 0;
again:
	intel_pmu_ack_status(status);
	if (++loops > 100) {
		WARN_ONCE(1, "perfevents: irq loop stuck!\n");
		perf_event_print_debug();
@@ -736,19 +738,22 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
	}

	inc_irq_stat(apic_perf_irqs);
	ack = status;

	intel_pmu_lbr_read();

	/*
	 * PEBS overflow sets bit 62 in the global status register
	 */
	if (__test_and_clear_bit(62, (unsigned long *)&status))
	if (__test_and_clear_bit(62, (unsigned long *)&status)) {
		handled++;
		x86_pmu.drain_pebs(regs);
	}

	for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
		struct perf_event *event = cpuc->events[bit];

		handled++;

		if (!test_bit(bit, cpuc->active_mask))
			continue;

@@ -761,8 +766,6 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
			x86_pmu_stop(event);
	}

	intel_pmu_ack_status(ack);

	/*
	 * Repeat if there is more work to be done:
	 */
@@ -772,7 +775,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)

done:
	intel_pmu_enable_all(0);
	return 1;
	return handled;
}

static struct event_constraint *
+1 −1
Original line number Diff line number Diff line
@@ -692,7 +692,7 @@ static int p4_pmu_handle_irq(struct pt_regs *regs)
		inc_irq_stat(apic_perf_irqs);
	}

	return handled > 0;
	return handled;
}

/*
+17 −5
Original line number Diff line number Diff line
@@ -568,8 +568,13 @@ static int __init init_sysfs(void)
	int error;

	error = sysdev_class_register(&oprofile_sysclass);
	if (!error)
	if (error)
		return error;

	error = sysdev_register(&device_oprofile);
	if (error)
		sysdev_class_unregister(&oprofile_sysclass);

	return error;
}

@@ -580,8 +585,10 @@ static void exit_sysfs(void)
}

#else
#define init_sysfs() do { } while (0)
#define exit_sysfs() do { } while (0)

static inline int  init_sysfs(void) { return 0; }
static inline void exit_sysfs(void) { }

#endif /* CONFIG_PM */

static int __init p4_init(char **cpu_type)
@@ -695,6 +702,8 @@ int __init op_nmi_init(struct oprofile_operations *ops)
	char *cpu_type = NULL;
	int ret = 0;

	using_nmi = 0;

	if (!cpu_has_apic)
		return -ENODEV;

@@ -774,7 +783,10 @@ int __init op_nmi_init(struct oprofile_operations *ops)

	mux_init(ops);

	init_sysfs();
	ret = init_sysfs();
	if (ret)
		return ret;

	using_nmi = 1;
	printk(KERN_INFO "oprofile: using NMI interrupt.\n");
	return 0;
+14 −13
Original line number Diff line number Diff line
@@ -141,16 +141,6 @@ static struct notifier_block module_load_nb = {
	.notifier_call = module_load_notify,
};


static void end_sync(void)
{
	end_cpu_work();
	/* make sure we don't leak task structs */
	process_task_mortuary();
	process_task_mortuary();
}


int sync_start(void)
{
	int err;
@@ -158,7 +148,7 @@ int sync_start(void)
	if (!zalloc_cpumask_var(&marked_cpus, GFP_KERNEL))
		return -ENOMEM;

	start_cpu_work();
	mutex_lock(&buffer_mutex);

	err = task_handoff_register(&task_free_nb);
	if (err)
@@ -173,7 +163,10 @@ int sync_start(void)
	if (err)
		goto out4;

	start_cpu_work();

out:
	mutex_unlock(&buffer_mutex);
	return err;
out4:
	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
@@ -182,7 +175,6 @@ int sync_start(void)
out2:
	task_handoff_unregister(&task_free_nb);
out1:
	end_sync();
	free_cpumask_var(marked_cpus);
	goto out;
}
@@ -190,11 +182,20 @@ int sync_start(void)

void sync_stop(void)
{
	/* flush buffers */
	mutex_lock(&buffer_mutex);
	end_cpu_work();
	unregister_module_notifier(&module_load_nb);
	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
	profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
	task_handoff_unregister(&task_free_nb);
	end_sync();
	mutex_unlock(&buffer_mutex);
	flush_scheduled_work();

	/* make sure we don't leak task structs */
	process_task_mortuary();
	process_task_mortuary();

	free_cpumask_var(marked_cpus);
}

Loading