Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner: "The irq departement provides the usual mixed bag: Core: - Further improvements to the irq timings code which aims to predict the next interrupt for power state selection to achieve better latency/power balance - Add interrupt statistics to the core NMI handlers - The usual small fixes and cleanups Drivers: - Support for Renesas RZ/A1, Annapurna Labs FIC, Meson-G12A SoC and Amazon Gravition AMR/GIC interrupt controllers. - Rework of the Renesas INTC controller driver - ACPI support for Socionext SoCs - Enhancements to the CSKY interrupt controller - The usual small fixes and cleanups" * 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (39 commits) irq/irqdomain: Fix comment typo genirq: Update irq stats from NMI handlers irqchip/gic-pm: Remove PM_CLK dependency irqchip/al-fic: Introduce Amazon's Annapurna Labs Fabric Interrupt Controller Driver dt-bindings: interrupt-controller: Add Amazon's Annapurna Labs FIC softirq: Use __this_cpu_write() in takeover_tasklets() irqchip/mbigen: Stop printing kernel addresses irqchip/gic: Add dependency for ARM_GIC_MAX_NR genirq/affinity: Remove unused argument from [__]irq_build_affinity_masks() genirq/timings: Add selftest for next event computation genirq/timings: Add selftest for irqs circular buffer genirq/timings: Add selftest for circular array genirq/timings: Encapsulate storing function genirq/timings: Encapsulate timings push genirq/timings: Optimize the period detection speed genirq/timings: Fix timings buffer inspection genirq/timings: Fix next event index function irqchip/qcom: Use struct_size() in devm_kzalloc() irqchip/irq-csky-mpintc: Remove unnecessary loop in interrupt handler dt-bindings: interrupt-controller: Update csky mpintc ...
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
|
||||
obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
|
||||
obj-$(CONFIG_IRQ_TIMINGS) += timings.o
|
||||
ifeq ($(CONFIG_TEST_IRQ_TIMINGS),y)
|
||||
CFLAGS_timings.o += -DDEBUG
|
||||
endif
|
||||
obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
|
||||
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
|
||||
obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
|
||||
|
@@ -94,8 +94,7 @@ static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask,
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static int __irq_build_affinity_masks(const struct irq_affinity *affd,
|
||||
unsigned int startvec,
|
||||
static int __irq_build_affinity_masks(unsigned int startvec,
|
||||
unsigned int numvecs,
|
||||
unsigned int firstvec,
|
||||
cpumask_var_t *node_to_cpumask,
|
||||
@@ -171,8 +170,7 @@ static int __irq_build_affinity_masks(const struct irq_affinity *affd,
|
||||
* 1) spread present CPU on these vectors
|
||||
* 2) spread other possible CPUs on these vectors
|
||||
*/
|
||||
static int irq_build_affinity_masks(const struct irq_affinity *affd,
|
||||
unsigned int startvec, unsigned int numvecs,
|
||||
static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
||||
unsigned int firstvec,
|
||||
struct irq_affinity_desc *masks)
|
||||
{
|
||||
@@ -197,7 +195,7 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd,
|
||||
build_node_to_cpumask(node_to_cpumask);
|
||||
|
||||
/* Spread on present CPUs starting from affd->pre_vectors */
|
||||
nr_present = __irq_build_affinity_masks(affd, curvec, numvecs,
|
||||
nr_present = __irq_build_affinity_masks(curvec, numvecs,
|
||||
firstvec, node_to_cpumask,
|
||||
cpu_present_mask, nmsk, masks);
|
||||
|
||||
@@ -212,7 +210,7 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd,
|
||||
else
|
||||
curvec = firstvec + nr_present;
|
||||
cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask);
|
||||
nr_others = __irq_build_affinity_masks(affd, curvec, numvecs,
|
||||
nr_others = __irq_build_affinity_masks(curvec, numvecs,
|
||||
firstvec, node_to_cpumask,
|
||||
npresmsk, nmsk, masks);
|
||||
put_online_cpus();
|
||||
@@ -295,7 +293,7 @@ irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
|
||||
unsigned int this_vecs = affd->set_size[i];
|
||||
int ret;
|
||||
|
||||
ret = irq_build_affinity_masks(affd, curvec, this_vecs,
|
||||
ret = irq_build_affinity_masks(curvec, this_vecs,
|
||||
curvec, masks);
|
||||
if (ret) {
|
||||
kfree(masks);
|
||||
|
@@ -748,6 +748,8 @@ void handle_fasteoi_nmi(struct irq_desc *desc)
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
irqreturn_t res;
|
||||
|
||||
__kstat_incr_irqs_this_cpu(desc);
|
||||
|
||||
trace_irq_handler_entry(irq, action);
|
||||
/*
|
||||
* NMIs cannot be shared, there is only one action.
|
||||
@@ -962,6 +964,8 @@ void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc)
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
irqreturn_t res;
|
||||
|
||||
__kstat_incr_irqs_this_cpu(desc);
|
||||
|
||||
trace_irq_handler_entry(irq, action);
|
||||
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
|
||||
trace_irq_handler_exit(irq, action, res);
|
||||
|
@@ -354,6 +354,16 @@ static inline int irq_timing_decode(u64 value, u64 *timestamp)
|
||||
return value & U16_MAX;
|
||||
}
|
||||
|
||||
static __always_inline void irq_timings_push(u64 ts, int irq)
|
||||
{
|
||||
struct irq_timings *timings = this_cpu_ptr(&irq_timings);
|
||||
|
||||
timings->values[timings->count & IRQ_TIMINGS_MASK] =
|
||||
irq_timing_encode(ts, irq);
|
||||
|
||||
timings->count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The function record_irq_time is only called in one place in the
|
||||
* interrupts handler. We want this function always inline so the code
|
||||
@@ -367,15 +377,8 @@ static __always_inline void record_irq_time(struct irq_desc *desc)
|
||||
if (!static_branch_likely(&irq_timing_enabled))
|
||||
return;
|
||||
|
||||
if (desc->istate & IRQS_TIMINGS) {
|
||||
struct irq_timings *timings = this_cpu_ptr(&irq_timings);
|
||||
|
||||
timings->values[timings->count & IRQ_TIMINGS_MASK] =
|
||||
irq_timing_encode(local_clock(),
|
||||
irq_desc_get_irq(desc));
|
||||
|
||||
timings->count++;
|
||||
}
|
||||
if (desc->istate & IRQS_TIMINGS)
|
||||
irq_timings_push(local_clock(), irq_desc_get_irq(desc));
|
||||
}
|
||||
#else
|
||||
static inline void irq_remove_timings(struct irq_desc *desc) {}
|
||||
|
@@ -950,6 +950,11 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu)
|
||||
*per_cpu_ptr(desc->kstat_irqs, cpu) : 0;
|
||||
}
|
||||
|
||||
static bool irq_is_nmi(struct irq_desc *desc)
|
||||
{
|
||||
return desc->istate & IRQS_NMI;
|
||||
}
|
||||
|
||||
/**
|
||||
* kstat_irqs - Get the statistics for an interrupt
|
||||
* @irq: The interrupt number
|
||||
@@ -967,7 +972,8 @@ unsigned int kstat_irqs(unsigned int irq)
|
||||
if (!desc || !desc->kstat_irqs)
|
||||
return 0;
|
||||
if (!irq_settings_is_per_cpu_devid(desc) &&
|
||||
!irq_settings_is_per_cpu(desc))
|
||||
!irq_settings_is_per_cpu(desc) &&
|
||||
!irq_is_nmi(desc))
|
||||
return desc->tot_count;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
|
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(irq_domain_free_fwnode);
|
||||
* @ops: domain callbacks
|
||||
* @host_data: Controller private data pointer
|
||||
*
|
||||
* Allocates and initialize and irq_domain structure.
|
||||
* Allocates and initializes an irq_domain structure.
|
||||
* Returns pointer to IRQ domain, or NULL on failure.
|
||||
*/
|
||||
struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
|
||||
@@ -139,7 +139,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
|
||||
|
||||
domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
|
||||
GFP_KERNEL, of_node_to_nid(of_node));
|
||||
if (WARN_ON(!domain))
|
||||
if (!domain)
|
||||
return NULL;
|
||||
|
||||
if (fwnode && is_fwnode_irqchip(fwnode)) {
|
||||
|
@@ -1,10 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
#define pr_fmt(fmt) "irq_timings: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/irq.h>
|
||||
@@ -261,12 +263,29 @@ void irq_timings_disable(void)
|
||||
#define EMA_ALPHA_VAL 64
|
||||
#define EMA_ALPHA_SHIFT 7
|
||||
|
||||
#define PREDICTION_PERIOD_MIN 2
|
||||
#define PREDICTION_PERIOD_MIN 3
|
||||
#define PREDICTION_PERIOD_MAX 5
|
||||
#define PREDICTION_FACTOR 4
|
||||
#define PREDICTION_MAX 10 /* 2 ^ PREDICTION_MAX useconds */
|
||||
#define PREDICTION_BUFFER_SIZE 16 /* slots for EMAs, hardly more than 16 */
|
||||
|
||||
/*
|
||||
* Number of elements in the circular buffer: If it happens it was
|
||||
* flushed before, then the number of elements could be smaller than
|
||||
* IRQ_TIMINGS_SIZE, so the count is used, otherwise the array size is
|
||||
* used as we wrapped. The index begins from zero when we did not
|
||||
* wrap. That could be done in a nicer way with the proper circular
|
||||
* array structure type but with the cost of extra computation in the
|
||||
* interrupt handler hot path. We choose efficiency.
|
||||
*/
|
||||
#define for_each_irqts(i, irqts) \
|
||||
for (i = irqts->count < IRQ_TIMINGS_SIZE ? \
|
||||
0 : irqts->count & IRQ_TIMINGS_MASK, \
|
||||
irqts->count = min(IRQ_TIMINGS_SIZE, \
|
||||
irqts->count); \
|
||||
irqts->count > 0; irqts->count--, \
|
||||
i = (i + 1) & IRQ_TIMINGS_MASK)
|
||||
|
||||
struct irqt_stat {
|
||||
u64 last_ts;
|
||||
u64 ema_time[PREDICTION_BUFFER_SIZE];
|
||||
@@ -297,7 +316,16 @@ static u64 irq_timings_ema_new(u64 value, u64 ema_old)
|
||||
|
||||
static int irq_timings_next_event_index(int *buffer, size_t len, int period_max)
|
||||
{
|
||||
int i;
|
||||
int period;
|
||||
|
||||
/*
|
||||
* Move the beginning pointer to the end minus the max period x 3.
|
||||
* We are at the point we can begin searching the pattern
|
||||
*/
|
||||
buffer = &buffer[len - (period_max * 3)];
|
||||
|
||||
/* Adjust the length to the maximum allowed period x 3 */
|
||||
len = period_max * 3;
|
||||
|
||||
/*
|
||||
* The buffer contains the suite of intervals, in a ilog2
|
||||
@@ -306,21 +334,45 @@ static int irq_timings_next_event_index(int *buffer, size_t len, int period_max)
|
||||
* period beginning at the end of the buffer. We do that for
|
||||
* each suffix.
|
||||
*/
|
||||
for (i = period_max; i >= PREDICTION_PERIOD_MIN ; i--) {
|
||||
for (period = period_max; period >= PREDICTION_PERIOD_MIN; period--) {
|
||||
|
||||
int *begin = &buffer[len - (i * 3)];
|
||||
int *ptr = begin;
|
||||
/*
|
||||
* The first comparison always succeed because the
|
||||
* suffix is deduced from the first n-period bytes of
|
||||
* the buffer and we compare the initial suffix with
|
||||
* itself, so we can skip the first iteration.
|
||||
*/
|
||||
int idx = period;
|
||||
size_t size = period;
|
||||
|
||||
/*
|
||||
* We look if the suite with period 'i' repeat
|
||||
* itself. If it is truncated at the end, as it
|
||||
* repeats we can use the period to find out the next
|
||||
* element.
|
||||
* element with the modulo.
|
||||
*/
|
||||
while (!memcmp(ptr, begin, i * sizeof(*ptr))) {
|
||||
ptr += i;
|
||||
if (ptr >= &buffer[len])
|
||||
return begin[((i * 3) % i)];
|
||||
while (!memcmp(buffer, &buffer[idx], size * sizeof(int))) {
|
||||
|
||||
/*
|
||||
* Move the index in a period basis
|
||||
*/
|
||||
idx += size;
|
||||
|
||||
/*
|
||||
* If this condition is reached, all previous
|
||||
* memcmp were successful, so the period is
|
||||
* found.
|
||||
*/
|
||||
if (idx == len)
|
||||
return buffer[len % period];
|
||||
|
||||
/*
|
||||
* If the remaining elements to compare are
|
||||
* smaller than the period, readjust the size
|
||||
* of the comparison for the last iteration.
|
||||
*/
|
||||
if (len - idx < period)
|
||||
size = len - idx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,11 +432,43 @@ static u64 __irq_timings_next_event(struct irqt_stat *irqs, int irq, u64 now)
|
||||
return irqs->last_ts + irqs->ema_time[index];
|
||||
}
|
||||
|
||||
static __always_inline int irq_timings_interval_index(u64 interval)
|
||||
{
|
||||
/*
|
||||
* The PREDICTION_FACTOR increase the interval size for the
|
||||
* array of exponential average.
|
||||
*/
|
||||
u64 interval_us = (interval >> 10) / PREDICTION_FACTOR;
|
||||
|
||||
return likely(interval_us) ? ilog2(interval_us) : 0;
|
||||
}
|
||||
|
||||
static __always_inline void __irq_timings_store(int irq, struct irqt_stat *irqs,
|
||||
u64 interval)
|
||||
{
|
||||
int index;
|
||||
|
||||
/*
|
||||
* Get the index in the ema table for this interrupt.
|
||||
*/
|
||||
index = irq_timings_interval_index(interval);
|
||||
|
||||
/*
|
||||
* Store the index as an element of the pattern in another
|
||||
* circular array.
|
||||
*/
|
||||
irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index;
|
||||
|
||||
irqs->ema_time[index] = irq_timings_ema_new(interval,
|
||||
irqs->ema_time[index]);
|
||||
|
||||
irqs->count++;
|
||||
}
|
||||
|
||||
static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts)
|
||||
{
|
||||
u64 old_ts = irqs->last_ts;
|
||||
u64 interval;
|
||||
int index;
|
||||
|
||||
/*
|
||||
* The timestamps are absolute time values, we need to compute
|
||||
@@ -415,24 +499,7 @@ static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the index in the ema table for this interrupt. The
|
||||
* PREDICTION_FACTOR increase the interval size for the array
|
||||
* of exponential average.
|
||||
*/
|
||||
index = likely(interval) ?
|
||||
ilog2((interval >> 10) / PREDICTION_FACTOR) : 0;
|
||||
|
||||
/*
|
||||
* Store the index as an element of the pattern in another
|
||||
* circular array.
|
||||
*/
|
||||
irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index;
|
||||
|
||||
irqs->ema_time[index] = irq_timings_ema_new(interval,
|
||||
irqs->ema_time[index]);
|
||||
|
||||
irqs->count++;
|
||||
__irq_timings_store(irq, irqs, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,11 +560,7 @@ u64 irq_timings_next_event(u64 now)
|
||||
* model while decrementing the counter because we consume the
|
||||
* data from our circular buffer.
|
||||
*/
|
||||
|
||||
i = (irqts->count & IRQ_TIMINGS_MASK) - 1;
|
||||
irqts->count = min(IRQ_TIMINGS_SIZE, irqts->count);
|
||||
|
||||
for (; irqts->count > 0; irqts->count--, i = (i + 1) & IRQ_TIMINGS_MASK) {
|
||||
for_each_irqts(i, irqts) {
|
||||
irq = irq_timing_decode(irqts->values[i], &ts);
|
||||
s = idr_find(&irqt_stats, irq);
|
||||
if (s)
|
||||
@@ -564,3 +627,325 @@ int irq_timings_alloc(int irq)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEST_IRQ_TIMINGS
|
||||
struct timings_intervals {
|
||||
u64 *intervals;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
/*
|
||||
* Intervals are given in nanosecond base
|
||||
*/
|
||||
static u64 intervals0[] __initdata = {
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000, 500000,
|
||||
10000, 50000, 200000,
|
||||
};
|
||||
|
||||
static u64 intervals1[] __initdata = {
|
||||
223947000, 1240000, 1384000, 1386000, 1386000,
|
||||
217416000, 1236000, 1384000, 1386000, 1387000,
|
||||
214719000, 1241000, 1386000, 1387000, 1384000,
|
||||
213696000, 1234000, 1384000, 1386000, 1388000,
|
||||
219904000, 1240000, 1385000, 1389000, 1385000,
|
||||
212240000, 1240000, 1386000, 1386000, 1386000,
|
||||
214415000, 1236000, 1384000, 1386000, 1387000,
|
||||
214276000, 1234000,
|
||||
};
|
||||
|
||||
static u64 intervals2[] __initdata = {
|
||||
4000, 3000, 5000, 100000,
|
||||
3000, 3000, 5000, 117000,
|
||||
4000, 4000, 5000, 112000,
|
||||
4000, 3000, 4000, 110000,
|
||||
3000, 5000, 3000, 117000,
|
||||
4000, 4000, 5000, 112000,
|
||||
4000, 3000, 4000, 110000,
|
||||
3000, 4000, 5000, 112000,
|
||||
4000,
|
||||
};
|
||||
|
||||
static u64 intervals3[] __initdata = {
|
||||
1385000, 212240000, 1240000,
|
||||
1386000, 214415000, 1236000,
|
||||
1384000, 214276000, 1234000,
|
||||
1386000, 214415000, 1236000,
|
||||
1385000, 212240000, 1240000,
|
||||
1386000, 214415000, 1236000,
|
||||
1384000, 214276000, 1234000,
|
||||
1386000, 214415000, 1236000,
|
||||
1385000, 212240000, 1240000,
|
||||
};
|
||||
|
||||
static u64 intervals4[] __initdata = {
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000, 50000, 10000, 50000,
|
||||
10000,
|
||||
};
|
||||
|
||||
static struct timings_intervals tis[] __initdata = {
|
||||
{ intervals0, ARRAY_SIZE(intervals0) },
|
||||
{ intervals1, ARRAY_SIZE(intervals1) },
|
||||
{ intervals2, ARRAY_SIZE(intervals2) },
|
||||
{ intervals3, ARRAY_SIZE(intervals3) },
|
||||
{ intervals4, ARRAY_SIZE(intervals4) },
|
||||
};
|
||||
|
||||
static int __init irq_timings_test_next_index(struct timings_intervals *ti)
|
||||
{
|
||||
int _buffer[IRQ_TIMINGS_SIZE];
|
||||
int buffer[IRQ_TIMINGS_SIZE];
|
||||
int index, start, i, count, period_max;
|
||||
|
||||
count = ti->count - 1;
|
||||
|
||||
period_max = count > (3 * PREDICTION_PERIOD_MAX) ?
|
||||
PREDICTION_PERIOD_MAX : count / 3;
|
||||
|
||||
/*
|
||||
* Inject all values except the last one which will be used
|
||||
* to compare with the next index result.
|
||||
*/
|
||||
pr_debug("index suite: ");
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
index = irq_timings_interval_index(ti->intervals[i]);
|
||||
_buffer[i & IRQ_TIMINGS_MASK] = index;
|
||||
pr_cont("%d ", index);
|
||||
}
|
||||
|
||||
start = count < IRQ_TIMINGS_SIZE ? 0 :
|
||||
count & IRQ_TIMINGS_MASK;
|
||||
|
||||
count = min_t(int, count, IRQ_TIMINGS_SIZE);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
int index = (start + i) & IRQ_TIMINGS_MASK;
|
||||
buffer[i] = _buffer[index];
|
||||
}
|
||||
|
||||
index = irq_timings_next_event_index(buffer, count, period_max);
|
||||
i = irq_timings_interval_index(ti->intervals[ti->count - 1]);
|
||||
|
||||
if (index != i) {
|
||||
pr_err("Expected (%d) and computed (%d) next indexes differ\n",
|
||||
i, index);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init irq_timings_next_index_selftest(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tis); i++) {
|
||||
|
||||
pr_info("---> Injecting intervals number #%d (count=%zd)\n",
|
||||
i, tis[i].count);
|
||||
|
||||
ret = irq_timings_test_next_index(&tis[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init irq_timings_test_irqs(struct timings_intervals *ti)
|
||||
{
|
||||
struct irqt_stat __percpu *s;
|
||||
struct irqt_stat *irqs;
|
||||
int i, index, ret, irq = 0xACE5;
|
||||
|
||||
ret = irq_timings_alloc(irq);
|
||||
if (ret) {
|
||||
pr_err("Failed to allocate irq timings\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
s = idr_find(&irqt_stats, irq);
|
||||
if (!s) {
|
||||
ret = -EIDRM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
irqs = this_cpu_ptr(s);
|
||||
|
||||
for (i = 0; i < ti->count; i++) {
|
||||
|
||||
index = irq_timings_interval_index(ti->intervals[i]);
|
||||
pr_debug("%d: interval=%llu ema_index=%d\n",
|
||||
i, ti->intervals[i], index);
|
||||
|
||||
__irq_timings_store(irq, irqs, ti->intervals[i]);
|
||||
if (irqs->circ_timings[i & IRQ_TIMINGS_MASK] != index) {
|
||||
pr_err("Failed to store in the circular buffer\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (irqs->count != ti->count) {
|
||||
pr_err("Count differs\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
irq_timings_free(irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init irq_timings_irqs_selftest(void)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tis); i++) {
|
||||
pr_info("---> Injecting intervals number #%d (count=%zd)\n",
|
||||
i, tis[i].count);
|
||||
ret = irq_timings_test_irqs(&tis[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init irq_timings_test_irqts(struct irq_timings *irqts,
|
||||
unsigned count)
|
||||
{
|
||||
int start = count >= IRQ_TIMINGS_SIZE ? count - IRQ_TIMINGS_SIZE : 0;
|
||||
int i, irq, oirq = 0xBEEF;
|
||||
u64 ots = 0xDEAD, ts;
|
||||
|
||||
/*
|
||||
* Fill the circular buffer by using the dedicated function.
|
||||
*/
|
||||
for (i = 0; i < count; i++) {
|
||||
pr_debug("%d: index=%d, ts=%llX irq=%X\n",
|
||||
i, i & IRQ_TIMINGS_MASK, ots + i, oirq + i);
|
||||
|
||||
irq_timings_push(ots + i, oirq + i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the first elements values after the index wrapped
|
||||
* up or not.
|
||||
*/
|
||||
ots += start;
|
||||
oirq += start;
|
||||
|
||||
/*
|
||||
* Test the circular buffer count is correct.
|
||||
*/
|
||||
pr_debug("---> Checking timings array count (%d) is right\n", count);
|
||||
if (WARN_ON(irqts->count != count))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Test the macro allowing to browse all the irqts.
|
||||
*/
|
||||
pr_debug("---> Checking the for_each_irqts() macro\n");
|
||||
for_each_irqts(i, irqts) {
|
||||
|
||||
irq = irq_timing_decode(irqts->values[i], &ts);
|
||||
|
||||
pr_debug("index=%d, ts=%llX / %llX, irq=%X / %X\n",
|
||||
i, ts, ots, irq, oirq);
|
||||
|
||||
if (WARN_ON(ts != ots || irq != oirq))
|
||||
return -EINVAL;
|
||||
|
||||
ots++; oirq++;
|
||||
}
|
||||
|
||||
/*
|
||||
* The circular buffer should have be flushed when browsed
|
||||
* with for_each_irqts
|
||||
*/
|
||||
pr_debug("---> Checking timings array is empty after browsing it\n");
|
||||
if (WARN_ON(irqts->count))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init irq_timings_irqts_selftest(void)
|
||||
{
|
||||
struct irq_timings *irqts = this_cpu_ptr(&irq_timings);
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* Test the circular buffer with different number of
|
||||
* elements. The purpose is to test at the limits (empty, half
|
||||
* full, full, wrapped with the cursor at the boundaries,
|
||||
* wrapped several times, etc ...
|
||||
*/
|
||||
int count[] = { 0,
|
||||
IRQ_TIMINGS_SIZE >> 1,
|
||||
IRQ_TIMINGS_SIZE,
|
||||
IRQ_TIMINGS_SIZE + (IRQ_TIMINGS_SIZE >> 1),
|
||||
2 * IRQ_TIMINGS_SIZE,
|
||||
(2 * IRQ_TIMINGS_SIZE) + 3,
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(count); i++) {
|
||||
|
||||
pr_info("---> Checking the timings with %d/%d values\n",
|
||||
count[i], IRQ_TIMINGS_SIZE);
|
||||
|
||||
ret = irq_timings_test_irqts(irqts, count[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init irq_timings_selftest(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("------------------- selftest start -----------------\n");
|
||||
|
||||
/*
|
||||
* At this point, we don't except any subsystem to use the irq
|
||||
* timings but us, so it should not be enabled.
|
||||
*/
|
||||
if (static_branch_unlikely(&irq_timing_enabled)) {
|
||||
pr_warn("irq timings already initialized, skipping selftest\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = irq_timings_irqts_selftest();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = irq_timings_irqs_selftest();
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = irq_timings_next_index_selftest();
|
||||
out:
|
||||
pr_info("---------- selftest end with %s -----------\n",
|
||||
ret ? "failure" : "success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
early_initcall(irq_timings_selftest);
|
||||
#endif
|
||||
|
@@ -649,7 +649,7 @@ static int takeover_tasklets(unsigned int cpu)
|
||||
/* Find end, append list for that CPU. */
|
||||
if (&per_cpu(tasklet_vec, cpu).head != per_cpu(tasklet_vec, cpu).tail) {
|
||||
*__this_cpu_read(tasklet_vec.tail) = per_cpu(tasklet_vec, cpu).head;
|
||||
this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail);
|
||||
__this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail);
|
||||
per_cpu(tasklet_vec, cpu).head = NULL;
|
||||
per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head;
|
||||
}
|
||||
|
Reference in New Issue
Block a user