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:
Linus Torvalds
2019-07-08 11:01:13 -07:00
32 changed files with 1530 additions and 194 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {}

View File

@@ -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)

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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;
}