0d3f54257d
There is currently no way to evaluate the effective affinity mask of a given interrupt. Many irq chips allow only a single target CPU or a subset of CPUs in the affinity mask. Updating the mask at the time of setting the affinity to the subset would be counterproductive because information for cpu hotplug about assigned interrupt affinities gets lost. On CPU hotplug it's also pointless to force migrate an interrupt, which is not targeted at the CPU effectively. But currently the information is not available. Provide a seperate mask to be updated by the irq_chip->irq_set_affinity() implementations. Implement the read only proc files so the user can see the effective mask as well w/o trying to deduce it from /proc/interrupts. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Jens Axboe <axboe@kernel.dk> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Keith Busch <keith.busch@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Christoph Hellwig <hch@lst.de> Link: http://lkml.kernel.org/r/20170619235446.247834245@linutronix.de
220 lines
5.9 KiB
C
220 lines
5.9 KiB
C
/*
|
|
* Copyright 2017 Thomas Gleixner <tglx@linutronix.de>
|
|
*
|
|
* This file is licensed under the GPL V2.
|
|
*/
|
|
#include <linux/debugfs.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include "internals.h"
|
|
|
|
static struct dentry *irq_dir;
|
|
|
|
struct irq_bit_descr {
|
|
unsigned int mask;
|
|
char *name;
|
|
};
|
|
#define BIT_MASK_DESCR(m) { .mask = m, .name = #m }
|
|
|
|
static void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state,
|
|
const struct irq_bit_descr *sd, int size)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++, sd++) {
|
|
if (state & sd->mask)
|
|
seq_printf(m, "%*s%s\n", ind + 12, "", sd->name);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SMP
|
|
static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc)
|
|
{
|
|
struct irq_data *data = irq_desc_get_irq_data(desc);
|
|
struct cpumask *msk;
|
|
|
|
msk = irq_data_get_affinity_mask(data);
|
|
seq_printf(m, "affinity: %*pbl\n", cpumask_pr_args(msk));
|
|
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
|
msk = irq_data_get_effective_affinity_mask(data);
|
|
seq_printf(m, "effectiv: %*pbl\n", cpumask_pr_args(msk));
|
|
#endif
|
|
#ifdef CONFIG_GENERIC_PENDING_IRQ
|
|
msk = desc->pending_mask;
|
|
seq_printf(m, "pending: %*pbl\n", cpumask_pr_args(msk));
|
|
#endif
|
|
}
|
|
#else
|
|
static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { }
|
|
#endif
|
|
|
|
static const struct irq_bit_descr irqchip_flags[] = {
|
|
BIT_MASK_DESCR(IRQCHIP_SET_TYPE_MASKED),
|
|
BIT_MASK_DESCR(IRQCHIP_EOI_IF_HANDLED),
|
|
BIT_MASK_DESCR(IRQCHIP_MASK_ON_SUSPEND),
|
|
BIT_MASK_DESCR(IRQCHIP_ONOFFLINE_ENABLED),
|
|
BIT_MASK_DESCR(IRQCHIP_SKIP_SET_WAKE),
|
|
BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE),
|
|
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
|
|
};
|
|
|
|
static void
|
|
irq_debug_show_chip(struct seq_file *m, struct irq_data *data, int ind)
|
|
{
|
|
struct irq_chip *chip = data->chip;
|
|
|
|
if (!chip) {
|
|
seq_printf(m, "chip: None\n");
|
|
return;
|
|
}
|
|
seq_printf(m, "%*schip: %s\n", ind, "", chip->name);
|
|
seq_printf(m, "%*sflags: 0x%lx\n", ind + 1, "", chip->flags);
|
|
irq_debug_show_bits(m, ind, chip->flags, irqchip_flags,
|
|
ARRAY_SIZE(irqchip_flags));
|
|
}
|
|
|
|
static void
|
|
irq_debug_show_data(struct seq_file *m, struct irq_data *data, int ind)
|
|
{
|
|
seq_printf(m, "%*sdomain: %s\n", ind, "",
|
|
data->domain ? data->domain->name : "");
|
|
seq_printf(m, "%*shwirq: 0x%lx\n", ind + 1, "", data->hwirq);
|
|
irq_debug_show_chip(m, data, ind + 1);
|
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
|
if (!data->parent_data)
|
|
return;
|
|
seq_printf(m, "%*sparent:\n", ind + 1, "");
|
|
irq_debug_show_data(m, data->parent_data, ind + 4);
|
|
#endif
|
|
}
|
|
|
|
static const struct irq_bit_descr irqdata_states[] = {
|
|
BIT_MASK_DESCR(IRQ_TYPE_EDGE_RISING),
|
|
BIT_MASK_DESCR(IRQ_TYPE_EDGE_FALLING),
|
|
BIT_MASK_DESCR(IRQ_TYPE_LEVEL_HIGH),
|
|
BIT_MASK_DESCR(IRQ_TYPE_LEVEL_LOW),
|
|
BIT_MASK_DESCR(IRQD_LEVEL),
|
|
|
|
BIT_MASK_DESCR(IRQD_ACTIVATED),
|
|
BIT_MASK_DESCR(IRQD_IRQ_STARTED),
|
|
BIT_MASK_DESCR(IRQD_IRQ_DISABLED),
|
|
BIT_MASK_DESCR(IRQD_IRQ_MASKED),
|
|
BIT_MASK_DESCR(IRQD_IRQ_INPROGRESS),
|
|
|
|
BIT_MASK_DESCR(IRQD_PER_CPU),
|
|
BIT_MASK_DESCR(IRQD_NO_BALANCING),
|
|
|
|
BIT_MASK_DESCR(IRQD_MOVE_PCNTXT),
|
|
BIT_MASK_DESCR(IRQD_AFFINITY_SET),
|
|
BIT_MASK_DESCR(IRQD_SETAFFINITY_PENDING),
|
|
BIT_MASK_DESCR(IRQD_AFFINITY_MANAGED),
|
|
BIT_MASK_DESCR(IRQD_MANAGED_SHUTDOWN),
|
|
|
|
BIT_MASK_DESCR(IRQD_FORWARDED_TO_VCPU),
|
|
|
|
BIT_MASK_DESCR(IRQD_WAKEUP_STATE),
|
|
BIT_MASK_DESCR(IRQD_WAKEUP_ARMED),
|
|
};
|
|
|
|
static const struct irq_bit_descr irqdesc_states[] = {
|
|
BIT_MASK_DESCR(_IRQ_NOPROBE),
|
|
BIT_MASK_DESCR(_IRQ_NOREQUEST),
|
|
BIT_MASK_DESCR(_IRQ_NOTHREAD),
|
|
BIT_MASK_DESCR(_IRQ_NOAUTOEN),
|
|
BIT_MASK_DESCR(_IRQ_NESTED_THREAD),
|
|
BIT_MASK_DESCR(_IRQ_PER_CPU_DEVID),
|
|
BIT_MASK_DESCR(_IRQ_IS_POLLED),
|
|
BIT_MASK_DESCR(_IRQ_DISABLE_UNLAZY),
|
|
};
|
|
|
|
static const struct irq_bit_descr irqdesc_istates[] = {
|
|
BIT_MASK_DESCR(IRQS_AUTODETECT),
|
|
BIT_MASK_DESCR(IRQS_SPURIOUS_DISABLED),
|
|
BIT_MASK_DESCR(IRQS_POLL_INPROGRESS),
|
|
BIT_MASK_DESCR(IRQS_ONESHOT),
|
|
BIT_MASK_DESCR(IRQS_REPLAY),
|
|
BIT_MASK_DESCR(IRQS_WAITING),
|
|
BIT_MASK_DESCR(IRQS_PENDING),
|
|
BIT_MASK_DESCR(IRQS_SUSPENDED),
|
|
};
|
|
|
|
|
|
static int irq_debug_show(struct seq_file *m, void *p)
|
|
{
|
|
struct irq_desc *desc = m->private;
|
|
struct irq_data *data;
|
|
|
|
raw_spin_lock_irq(&desc->lock);
|
|
data = irq_desc_get_irq_data(desc);
|
|
seq_printf(m, "handler: %pf\n", desc->handle_irq);
|
|
seq_printf(m, "status: 0x%08x\n", desc->status_use_accessors);
|
|
irq_debug_show_bits(m, 0, desc->status_use_accessors, irqdesc_states,
|
|
ARRAY_SIZE(irqdesc_states));
|
|
seq_printf(m, "istate: 0x%08x\n", desc->istate);
|
|
irq_debug_show_bits(m, 0, desc->istate, irqdesc_istates,
|
|
ARRAY_SIZE(irqdesc_istates));
|
|
seq_printf(m, "ddepth: %u\n", desc->depth);
|
|
seq_printf(m, "wdepth: %u\n", desc->wake_depth);
|
|
seq_printf(m, "dstate: 0x%08x\n", irqd_get(data));
|
|
irq_debug_show_bits(m, 0, irqd_get(data), irqdata_states,
|
|
ARRAY_SIZE(irqdata_states));
|
|
seq_printf(m, "node: %d\n", irq_data_get_node(data));
|
|
irq_debug_show_masks(m, desc);
|
|
irq_debug_show_data(m, data, 0);
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
return 0;
|
|
}
|
|
|
|
static int irq_debug_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, irq_debug_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations dfs_irq_ops = {
|
|
.open = irq_debug_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
char name [10];
|
|
|
|
if (!irq_dir || !desc || desc->debugfs_file)
|
|
return;
|
|
|
|
sprintf(name, "%d", irq);
|
|
desc->debugfs_file = debugfs_create_file(name, 0444, irq_dir, desc,
|
|
&dfs_irq_ops);
|
|
}
|
|
|
|
void irq_remove_debugfs_entry(struct irq_desc *desc)
|
|
{
|
|
if (desc->debugfs_file)
|
|
debugfs_remove(desc->debugfs_file);
|
|
}
|
|
|
|
static int __init irq_debugfs_init(void)
|
|
{
|
|
struct dentry *root_dir;
|
|
int irq;
|
|
|
|
root_dir = debugfs_create_dir("irq", NULL);
|
|
if (!root_dir)
|
|
return -ENOMEM;
|
|
|
|
irq_domain_debugfs_init(root_dir);
|
|
|
|
irq_dir = debugfs_create_dir("irqs", root_dir);
|
|
|
|
irq_lock_sparse();
|
|
for_each_active_irq(irq)
|
|
irq_add_debugfs_entry(irq, irq_to_desc(irq));
|
|
irq_unlock_sparse();
|
|
|
|
return 0;
|
|
}
|
|
__initcall(irq_debugfs_init);
|