summaryrefslogtreecommitdiff
path: root/kernel/irq/timings.c
blob: e6f1d61925f02fdc35dfa58099ec21f5e1d0d964 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
 * linux/kernel/irq/timings.c
 *
 * Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/percpu.h>
#include <linux/static_key.h>

#include "internals.h"

DEFINE_STATIC_KEY_FALSE(irq_timing_enabled);

void irq_timings_enable(void)
{
	static_branch_inc(&irq_timing_enabled);
}

void irq_timings_disable(void)
{
	static_branch_dec(&irq_timing_enabled);
}

/**
 * irqtiming_get_next - return the next irq timing
 *
 * @irq: a pointer to an integer representing the interrupt number
 *
 * Must be called under rcu_read_lock().
 *
 * This function allows to browse safely the interrupt descriptors in order
 * to retrieve the interrupts timings. The parameter gives the interrupt
 * number to begin with and will return the interrupt timings for the next
 * allocated irq. This approach gives us the possibility to go through the
 * different interrupts without having to handle the sparse irq.
 *
 * The function changes @irq to the next allocated irq + 1, it should be
 * passed back again and again until NULL is returned. Usually this function
 * is called the first time with @irq = 0.
 *
 * Returns a struct irq_timings, NULL if we reach the end of the interrupts
 * list.
 */
struct irq_timings *irq_timings_get_next(int *irq)
{
	struct irq_desc *desc;
	int next;

again:
	/* Do a racy lookup of the next allocated irq */
	next = irq_get_next_irq(*irq);
	if (next >= nr_irqs)
		return NULL;

	*irq = next + 1;

	/*
	 * Now lookup the descriptor. It's RCU protected. This
	 * descriptor might belong to an uninteresting interrupt or
	 * one that is not measured. Look for the next interrupt in
	 * that case.
	 */
	desc = irq_to_desc(next);
	if (!desc || !(desc->istate & IRQS_TIMINGS))
		goto again;

	return this_cpu_ptr(desc->timings);
}