linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
@ 2016-07-26 14:25 Craig Gallek
  2016-08-10 15:26 ` Craig Gallek
                   ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Craig Gallek @ 2016-07-26 14:25 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Add struct kobject to struct irq_desc to allow for easy export
to sysfs.  This allows for much simpler userspace-parsing of
the information contained in struct irq_desc.

Note that sysfs is not available at the time of early irq initialization.
These interrupts are accounted for using a postcore_initcall callback.

Signed-off-by: Craig Gallek <kraig@google.com>
---
 include/linux/irqdesc.h |   2 +
 kernel/irq/irqdesc.c    | 203 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 203 insertions(+), 2 deletions(-)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..a528efe5fff8 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -92,6 +93,7 @@ struct irq_desc {
 	int			parent_irq;
 	struct module		*owner;
 	const char		*name;
+	struct kobject		kobj;
 } ____cacheline_internodealigned_in_smp;
 
 #ifdef CONFIG_SPARSE_IRQ
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..ac07470adcac 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -121,6 +122,145 @@ EXPORT_SYMBOL_GPL(nr_irqs);
 static DEFINE_MUTEX(sparse_irq_lock);
 static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
+struct kobject *irq_kobj;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int i;
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	for_each_online_cpu(i) {
+		unsigned int c = kstat_irqs_cpu(desc->irq_data.irq, i);
+		if (!ret)
+			ret = sprintf(buf, "%u", c);
+		else
+			ret += scnprintf(buf+ret, PAGE_SIZE-ret, ",%u", c);
+	}
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	if (ret)
+		ret += scnprintf(buf+ret, PAGE_SIZE-ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	if (desc->irq_data.chip->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			       struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	unsigned long flags;
+	ssize_t ret = 0;
+
+	raw_spin_lock_irqsave(&desc->lock, flags);
+	for (action = desc->action; action != NULL; action = action->next) {
+		if (!ret)
+			ret = scnprintf(buf, PAGE_SIZE, "%s", action->name);
+		else
+			ret += scnprintf(buf+ret, PAGE_SIZE-ret, ",%s",
+					 action->name);
+	}
+	raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+	if (ret)
+		ret += scnprintf(buf+ret, PAGE_SIZE-ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static void irq_kobj_release(struct kobject *kobj);
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
 #ifdef CONFIG_SPARSE_IRQ
 
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
@@ -187,6 +327,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +338,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +410,12 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		if (irq_kobj) {
+			if (kobject_add(&desc->kobj, irq_kobj, "%d", start + i))
+				printk(KERN_WARNING
+				       "Fail to add kobject for irq %d\n",
+				       start + i);
+		}
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
@@ -339,6 +494,7 @@ int __init early_irq_init(void)
 		raw_spin_lock_init(&desc[i].lock);
 		lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
 		desc_set_defaults(i, &desc[i], node, NULL, NULL);
+		kobject_init(&desc[i].kobj, &irq_kobj_type);
 	}
 	return arch_early_irq_init();
 }
@@ -349,14 +505,27 @@ struct irq_desc *irq_to_desc(unsigned int irq)
 }
 EXPORT_SYMBOL(irq_to_desc);
 
+static void irq_kobj_release(struct kobject *kobj)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+
+	/*
+	 * This irq_desc is statically allocated.  Simply zero its kobject
+	 * rather than kfree it.
+	 */
+	memset(&desc->kobj, 0, sizeof(desc->kobj));
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	unsigned long flags;
 
+	kobject_del(&desc->kobj);
 	raw_spin_lock_irqsave(&desc->lock, flags);
 	desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
+	kobject_put(&desc->kobj);
 }
 
 static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
@@ -369,6 +538,11 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
 		struct irq_desc *desc = irq_to_desc(start + i);
 
 		desc->owner = owner;
+		if (irq_kobj)
+			if (kobject_add(&desc->kobj, irq_kobj, "%d", start + i))
+				printk(KERN_WARNING
+				       "Fail to add kobject for irq %d\n",
+				       start + i);
 	}
 	return start;
 }
@@ -730,3 +904,28 @@ unsigned int kstat_irqs_usr(unsigned int irq)
 	irq_unlock_sparse();
 	return sum;
 }
+
+static int __init irq_sysfs_init(void)
+{
+	int i;
+
+	irq_kobj = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj)
+		return -ENOMEM;
+
+	irq_lock_sparse();
+	for (i = 0; i < nr_irqs; ++i) {
+		struct irq_desc *desc = irq_to_desc(i);
+
+		if (!desc)
+			continue;
+
+		if (kobject_add(&desc->kobj, irq_kobj, "%d", i))
+			printk(KERN_WARNING
+			       "Fail to add kobject for irq %d\n", i);
+	}
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
  2016-07-26 14:25 [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts Craig Gallek
@ 2016-08-10 15:26 ` Craig Gallek
  2016-09-02 13:52   ` Thomas Gleixner
  2016-09-02 14:59 ` Thomas Gleixner
  2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
  2 siblings, 1 reply; 20+ messages in thread
From: Craig Gallek @ 2016-08-10 15:26 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, LKML

On Tue, Jul 26, 2016 at 10:25 AM, Craig Gallek <kraigatgoog@gmail.com> wrote:
> From: Craig Gallek <kraig@google.com>
>
> Add struct kobject to struct irq_desc to allow for easy export
> to sysfs.  This allows for much simpler userspace-parsing of
> the information contained in struct irq_desc.
>
> Note that sysfs is not available at the time of early irq initialization.
> These interrupts are accounted for using a postcore_initcall callback.
>
> Signed-off-by: Craig Gallek <kraig@google.com>
Bump... Thomas, are you still the maintainer for irq/core?

Thanks,
Craig

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
  2016-08-10 15:26 ` Craig Gallek
@ 2016-09-02 13:52   ` Thomas Gleixner
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-02 13:52 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, LKML

On Wed, 10 Aug 2016, Craig Gallek wrote:

> On Tue, Jul 26, 2016 at 10:25 AM, Craig Gallek <kraigatgoog@gmail.com> wrote:
> > From: Craig Gallek <kraig@google.com>
> >
> > Add struct kobject to struct irq_desc to allow for easy export
> > to sysfs.  This allows for much simpler userspace-parsing of
> > the information contained in struct irq_desc.
> >
> > Note that sysfs is not available at the time of early irq initialization.
> > These interrupts are accounted for using a postcore_initcall callback.
> >
> > Signed-off-by: Craig Gallek <kraig@google.com>
> Bump... Thomas, are you still the maintainer for irq/core?

Yup and I'm looking at it right now. Sorry for the delay.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
  2016-07-26 14:25 [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts Craig Gallek
  2016-08-10 15:26 ` Craig Gallek
@ 2016-09-02 14:59 ` Thomas Gleixner
  2016-09-06 20:55   ` Craig Gallek
  2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
  2 siblings, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-02 14:59 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, LKML

On Tue, 26 Jul 2016, Craig Gallek wrote:
>  /*
>   * Core internal functions to deal with irq descriptors
> @@ -92,6 +93,7 @@ struct irq_desc {
>  	int			parent_irq;
>  	struct module		*owner;
>  	const char		*name;
> +	struct kobject		kobj;

Lacks a docbook comment.

> @@ -121,6 +122,145 @@ EXPORT_SYMBOL_GPL(nr_irqs);
>  static DEFINE_MUTEX(sparse_irq_lock);
>  static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
>  
> +struct kobject *irq_kobj;

That wants to be static. Aside of that the variable name
sucks. irq_kobj_base or such would be self explaining.

> +#define IRQ_ATTR_RO(_name) \
> +static struct kobj_attribute _name##_attr = __ATTR_RO(_name)

All these show() functions want to be conditional on CONFIG_SYSFS

> +static ssize_t per_cpu_count_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr, char *buf)
> +{
> +	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
> +	int i;
> +	unsigned long flags;
> +	ssize_t ret = 0;

Can you please order the variables as we do everywhere else?

+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	unsigned long flags;
+	ssize_t ret = 0;
+	int i;

Hmm?

> +
> +	raw_spin_lock_irqsave(&desc->lock, flags);

Why would we need locking for this? The count is unsigned int, so you can
access it lockless.

> +	for_each_online_cpu(i) {
> +		unsigned int c = kstat_irqs_cpu(desc->irq_data.irq, i);

Missing new line between variable declaration and code.

> +		if (!ret)
> +			ret = sprintf(buf, "%u", c);
> +		else
> +			ret += scnprintf(buf+ret, PAGE_SIZE-ret, ",%u", c);

  			'buf + ret', PAGE_SIZE - ret,
Please.

> +	}
> +	raw_spin_unlock_irqrestore(&desc->lock, flags);
> +
> +	if (ret)

This conditional is pointless. There is always at least one cpu online
otherwise you would not be executing this function ....

> +		ret += scnprintf(buf+ret, PAGE_SIZE-ret, "\n");

And the whole thing can be simplified to:

	int cpu, irq = desc->irq_data.irq;
	ssize_t ret = 0;
	char *p = "";

	for_each_online_cpu(cpu) {
		unsigned int c = kstat_irqs_cpu(irq, cpu);

		ret += scnprintf(buf + ret , PAGE_SIZE - ret, "%s%u", p, c);
		p = ",";
	}

	ret += scnprintf(buf + ret , PAGE_SIZE - ret, "\n");
	return ret;

Hmm?

> +IRQ_ATTR_RO(per_cpu_count);
> +
> +static ssize_t chip_name_show(struct kobject *kobj,
> +			      struct kobj_attribute *attr, char *buf)
> +{
> +	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
> +	unsigned long flags;
> +	ssize_t ret = 0;
> +
> +	raw_spin_lock_irqsave(&desc->lock, flags);

raw_spin_lock_irq() because this is always called from non interrupt
disabled code. No need for flags.

> +	if (desc->irq_data.chip->name)

chip can be NULL .....

> +		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
> +				desc->irq_data.chip->name);

If you break the line of the conditional statement, please add 
a pair of curly braces.

> +static ssize_t actions_show(struct kobject *kobj,
> +			    struct kobj_attribute *attr, char *buf)
> +{
> +	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
> +	struct irqaction *action;
> +	unsigned long flags;
> +	ssize_t ret = 0;
> +
> +	raw_spin_lock_irqsave(&desc->lock, flags);
> +	for (action = desc->action; action != NULL; action = action->next) {
> +		if (!ret)
> +			ret = scnprintf(buf, PAGE_SIZE, "%s", action->name);
> +		else
> +			ret += scnprintf(buf+ret, PAGE_SIZE-ret, ",%s",
> +					 action->name);
> +	}
> +	raw_spin_unlock_irqrestore(&desc->lock, flags);
> +
> +	if (ret)
> +		ret += scnprintf(buf+ret, PAGE_SIZE-ret, "\n");

Can be deuglified as well.

> @@ -261,6 +410,12 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
>  			goto err;
>  		mutex_lock(&sparse_irq_lock);
>  		irq_insert_desc(start + i, desc);
> +		if (irq_kobj) {
> +			if (kobject_add(&desc->kobj, irq_kobj, "%d", start + i))
> +				printk(KERN_WARNING
> +				       "Fail to add kobject for irq %d\n",
> +				       start + i);

pr_warn please

> +		}

Please split that out into a a helper function. irq_sysfs_add() or such so
you avoid that mess in the code and you can reuse that for the init stuff.

> +static void irq_kobj_release(struct kobject *kobj)
> +{
> +	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
> +
> +	/*
> +	 * This irq_desc is statically allocated.  Simply zero its kobject
> +	 * rather than kfree it.
> +	 */
> +	memset(&desc->kobj, 0, sizeof(desc->kobj));

This will nicely explode when the irq descriptor is handed out again before
this function is called.

There is no point to make this work for !SPARSE_IRQ. Just ignore it.

> +static int __init irq_sysfs_init(void)

Again: CONFIG_SYSFS ....

> +{
> +	int i;
> +
> +	irq_kobj = kobject_create_and_add("irq", kernel_kobj);
> +	if (!irq_kobj)
> +		return -ENOMEM;
> +
> +	irq_lock_sparse();
> +	for (i = 0; i < nr_irqs; ++i) {

  for_each_irq_desc() ....

> +		struct irq_desc *desc = irq_to_desc(i);
> +
> +		if (!desc)
> +			continue;
> +
> +		if (kobject_add(&desc->kobj, irq_kobj, "%d", i))
> +			printk(KERN_WARNING
> +			       "Fail to add kobject for irq %d\n", i);

So you managed to copy the same thing 3 times ....

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
  2016-09-02 14:59 ` Thomas Gleixner
@ 2016-09-06 20:55   ` Craig Gallek
  2016-09-09  6:15     ` Thomas Gleixner
  0 siblings, 1 reply; 20+ messages in thread
From: Craig Gallek @ 2016-09-06 20:55 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, LKML

On Fri, Sep 2, 2016 at 10:59 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Tue, 26 Jul 2016, Craig Gallek wrote:
>> +static void irq_kobj_release(struct kobject *kobj)
>> +{
>> +     struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
>> +
>> +     /*
>> +      * This irq_desc is statically allocated.  Simply zero its kobject
>> +      * rather than kfree it.
>> +      */
>> +     memset(&desc->kobj, 0, sizeof(desc->kobj));
>
> This will nicely explode when the irq descriptor is handed out again before
> this function is called.
>
> There is no point to make this work for !SPARSE_IRQ. Just ignore it.
Thank you for the review.  Just one clarification before I send v2:
Are you suggesting getting rid of this feature entirely for the
non-sparse version or just this kobject cleanup code?

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2] genirq: Machine-parsable version of /proc/interrupts
  2016-07-26 14:25 [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts Craig Gallek
  2016-08-10 15:26 ` Craig Gallek
  2016-09-02 14:59 ` Thomas Gleixner
@ 2016-09-08 17:34 ` Craig Gallek
  2016-09-08 17:57   ` Randy Dunlap
                     ` (2 more replies)
  2 siblings, 3 replies; 20+ messages in thread
From: Craig Gallek @ 2016-09-08 17:34 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Add struct kobject to struct irq_desc to allow for easy export
to sysfs.  This allows for much simpler userspace-parsing of
the information contained in struct irq_desc.

Note that sysfs is not available at the time of early irq initialization.
These interrupts are accounted for using a postcore_initcall callback.

Signed-off-by: Craig Gallek <kraig@google.com>
---
v2 changes:
 * Several suggested stylistic changes.
 * Fix one NULL pointer dereference possibility.
 * Do not destroy kobjects in non-SPARSE_IRQ configuration.
 * Include #define CONFIG_SYSFS guards to selectively enable feature.

 include/linux/irqdesc.h |   3 +
 kernel/irq/irqdesc.c    | 189 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 190 insertions(+), 2 deletions(-)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..aa41d39803b2 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -45,6 +46,7 @@ struct pt_regs;
  * @rcu:		rcu head for delayed free
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
+ * @kobj:		kobject used to represent this struct in sysfs
  */
 struct irq_desc {
 	struct irq_common_data	irq_common_data;
@@ -92,6 +94,7 @@ struct irq_desc {
 	int			parent_irq;
 	struct module		*owner;
 	const char		*name;
+	struct kobject		kobj;
 } ____cacheline_internodealigned_in_smp;
 
 #ifdef CONFIG_SPARSE_IRQ
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..110d5aadc755 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -121,6 +122,154 @@ EXPORT_SYMBOL_GPL(nr_irqs);
 static DEFINE_MUTEX(sparse_irq_lock);
 static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_online_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Fail to add kobject for irq %d\n", irq);
+	}
+}
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 #ifdef CONFIG_SPARSE_IRQ
 
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
@@ -187,6 +336,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +347,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +419,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
@@ -339,6 +498,7 @@ int __init early_irq_init(void)
 		raw_spin_lock_init(&desc[i].lock);
 		lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
 		desc_set_defaults(i, &desc[i], node, NULL, NULL);
+		kobject_init(&desc[i].kobj, &irq_kobj_type);
 	}
 	return arch_early_irq_init();
 }
@@ -349,11 +509,15 @@ struct irq_desc *irq_to_desc(unsigned int irq)
 }
 EXPORT_SYMBOL(irq_to_desc);
 
+/* Statically allocated irq_desc is never deallocated */
+static void irq_kobj_release(struct kobject *kobj) {}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	unsigned long flags;
 
+	kobject_del(&desc->kobj);
 	raw_spin_lock_irqsave(&desc->lock, flags);
 	desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -369,6 +533,7 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
 		struct irq_desc *desc = irq_to_desc(start + i);
 
 		desc->owner = owner;
+		irq_sysfs_add(start + i, desc);
 	}
 	return start;
 }
@@ -730,3 +895,23 @@ unsigned int kstat_irqs_usr(unsigned int irq)
 	irq_unlock_sparse();
 	return sum;
 }
+
+#ifdef CONFIG_SYSFS
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base)
+		return -ENOMEM;
+
+	irq_lock_sparse();
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+#endif /* CONFIG_SYSFS */
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
@ 2016-09-08 17:57   ` Randy Dunlap
  2016-09-08 20:25   ` [PATCH v3] " Craig Gallek
  2016-09-09 16:28   ` [PATCH v4] " Craig Gallek
  2 siblings, 0 replies; 20+ messages in thread
From: Randy Dunlap @ 2016-09-08 17:57 UTC (permalink / raw)
  To: Craig Gallek, Thomas Gleixner; +Cc: David Decotigny, linux-kernel

On 09/08/16 10:34, Craig Gallek wrote:
> From: Craig Gallek <kraig@google.com>
> 
> Add struct kobject to struct irq_desc to allow for easy export
> to sysfs.  This allows for much simpler userspace-parsing of
> the information contained in struct irq_desc.

Please give example(s) of the sysfs (output) format.

Also add the interface info to Documentation/ABI/.

> +static void irq_sysfs_add(int irq, struct irq_desc *desc)
> +{
> +	if (irq_kobj_base) {
> +		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
> +			pr_warn("Fail to add kobject for irq %d\n", irq);

			         Failed

> +	}
> +}

Thanks.
-- 
~Randy

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v3] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
  2016-09-08 17:57   ` Randy Dunlap
@ 2016-09-08 20:25   ` Craig Gallek
  2016-09-08 22:00     ` Randy Dunlap
  2016-09-09 16:28   ` [PATCH v4] " Craig Gallek
  2 siblings, 1 reply; 20+ messages in thread
From: Craig Gallek @ 2016-09-08 20:25 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Add struct kobject to struct irq_desc to allow for easy export
to sysfs.  This allows for much simpler userspace-parsing of
the information contained in struct irq_desc.

Note that sysfs is not available at the time of early irq initialization.
These interrupts are accounted for using a postcore_initcall callback.

Examples:
  /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
  /sys/kernel/irq/18/chip_name:	IR-IO-APIC
  /sys/kernel/irq/18/hwirq:		18
  /sys/kernel/irq/18/name:		fasteoi
  /sys/kernel/irq/18/per_cpu_count:	0,0
  /sys/kernel/irq/18/type:		level

  /sys/kernel/irq/25/actions:	ahci0
  /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
  /sys/kernel/irq/25/hwirq:		512000
  /sys/kernel/irq/25/name:		edge
  /sys/kernel/irq/25/per_cpu_count:	29036,0
  /sys/kernel/irq/25/type:		edge

Signed-off-by: Craig Gallek <kraig@google.com>
---
v3 changes:
 * Add some examples to the commit message and an ABI documentation file.
 * Fix spelling mistake.

v2 changes:
 * Several suggested stylistic changes.
 * Fix one NULL pointer dereference possibility.
 * Do not destroy kobjects in non-SPARSE_IRQ configuration.
 * Include #define CONFIG_SYSFS guards to selectively enable feature.

 Documentation/ABI/testing/sysfs-kernel-irq |  51 ++++++++
 include/linux/irqdesc.h                    |   3 +
 kernel/irq/irqdesc.c                       | 189 ++++++++++++++++++++++++++++-
 3 files changed, 241 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-irq

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 000000000000..f2ba24640b2b
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,51 @@
+What:		/sys/kernel/irq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Directory containing information about the system's IRQs.
+		Specifically, data from the associated struct irq_desc.
+		The information here is similar to that in /proc/interrupts
+		but in a more machine-friendly format.  This directory contains
+		one subdirectory for each Linux IRQ number.
+
+What:		/sys/kernel/irq/<irq>/actions
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The IRQ action chain.  A comma-separated list of zero or more
+		device names associated with this interrupt.
+
+What:		/sys/kernel/irq/<irq>/chip_name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable chip name supplied by the associated device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/hwirq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	When interrupt translation domains are used, this file contains
+		the underlying hardware IRQ number used for this Linux IRQ.
+
+What:		/sys/kernel/irq/<irq>/name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable flow handler name as defined by the device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/per_cpu_count
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The number of times the interrupt has fired since boot.  This
+		is a comma-separated list of counters; one per online CPU in
+		CPU id order.
+
+What:		/sys/kernel/irq/<irq>/type
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The type of the interrupt.  Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..aa41d39803b2 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -45,6 +46,7 @@ struct pt_regs;
  * @rcu:		rcu head for delayed free
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
+ * @kobj:		kobject used to represent this struct in sysfs
  */
 struct irq_desc {
 	struct irq_common_data	irq_common_data;
@@ -92,6 +94,7 @@ struct irq_desc {
 	int			parent_irq;
 	struct module		*owner;
 	const char		*name;
+	struct kobject		kobj;
 } ____cacheline_internodealigned_in_smp;
 
 #ifdef CONFIG_SPARSE_IRQ
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..2db12c4822f5 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -121,6 +122,154 @@ EXPORT_SYMBOL_GPL(nr_irqs);
 static DEFINE_MUTEX(sparse_irq_lock);
 static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_online_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Failed to add kobject for irq %d\n", irq);
+	}
+}
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 #ifdef CONFIG_SPARSE_IRQ
 
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
@@ -187,6 +336,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +347,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +419,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
@@ -339,6 +498,7 @@ int __init early_irq_init(void)
 		raw_spin_lock_init(&desc[i].lock);
 		lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
 		desc_set_defaults(i, &desc[i], node, NULL, NULL);
+		kobject_init(&desc[i].kobj, &irq_kobj_type);
 	}
 	return arch_early_irq_init();
 }
@@ -349,11 +509,15 @@ struct irq_desc *irq_to_desc(unsigned int irq)
 }
 EXPORT_SYMBOL(irq_to_desc);
 
+/* Statically allocated irq_desc is never deallocated */
+static void irq_kobj_release(struct kobject *kobj) {}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	unsigned long flags;
 
+	kobject_del(&desc->kobj);
 	raw_spin_lock_irqsave(&desc->lock, flags);
 	desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -369,6 +533,7 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
 		struct irq_desc *desc = irq_to_desc(start + i);
 
 		desc->owner = owner;
+		irq_sysfs_add(start + i, desc);
 	}
 	return start;
 }
@@ -730,3 +895,23 @@ unsigned int kstat_irqs_usr(unsigned int irq)
 	irq_unlock_sparse();
 	return sum;
 }
+
+#ifdef CONFIG_SYSFS
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base)
+		return -ENOMEM;
+
+	irq_lock_sparse();
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+#endif /* CONFIG_SYSFS */
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v3] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 20:25   ` [PATCH v3] " Craig Gallek
@ 2016-09-08 22:00     ` Randy Dunlap
  2016-09-08 22:15       ` Craig Gallek
  0 siblings, 1 reply; 20+ messages in thread
From: Randy Dunlap @ 2016-09-08 22:00 UTC (permalink / raw)
  To: Craig Gallek, Thomas Gleixner; +Cc: David Decotigny, linux-kernel

On 09/08/16 13:25, Craig Gallek wrote:
> From: Craig Gallek <kraig@google.com>
> 
> Add struct kobject to struct irq_desc to allow for easy export
> to sysfs.  This allows for much simpler userspace-parsing of
> the information contained in struct irq_desc.
> 
> Note that sysfs is not available at the time of early irq initialization.
> These interrupts are accounted for using a postcore_initcall callback.
> 
> Examples:
>   /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
>   /sys/kernel/irq/18/chip_name:	IR-IO-APIC
>   /sys/kernel/irq/18/hwirq:		18
>   /sys/kernel/irq/18/name:		fasteoi
>   /sys/kernel/irq/18/per_cpu_count:	0,0
>   /sys/kernel/irq/18/type:		level
> 
>   /sys/kernel/irq/25/actions:	ahci0
>   /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
>   /sys/kernel/irq/25/hwirq:		512000
>   /sys/kernel/irq/25/name:		edge
>   /sys/kernel/irq/25/per_cpu_count:	29036,0
>   /sys/kernel/irq/25/type:		edge

Thanks for the update.

One concern:

This per_cpu_count is for online CPUs only.
How does this help when the online CPUs change?

E.g., above could be for CPUs 1 and 5.
The next time that it is read it could be for CPUs 0 and 3.
Seems that it could be confusing even for software reading it.


-- 
~Randy

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v3] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 22:00     ` Randy Dunlap
@ 2016-09-08 22:15       ` Craig Gallek
  2016-09-09 12:39         ` Thomas Gleixner
  0 siblings, 1 reply; 20+ messages in thread
From: Craig Gallek @ 2016-09-08 22:15 UTC (permalink / raw)
  To: Randy Dunlap; +Cc: Thomas Gleixner, David Decotigny, LKML

On Thu, Sep 8, 2016 at 6:00 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
> On 09/08/16 13:25, Craig Gallek wrote:
>> From: Craig Gallek <kraig@google.com>
>>
>> Add struct kobject to struct irq_desc to allow for easy export
>> to sysfs.  This allows for much simpler userspace-parsing of
>> the information contained in struct irq_desc.
>>
>> Note that sysfs is not available at the time of early irq initialization.
>> These interrupts are accounted for using a postcore_initcall callback.
>>
>> Examples:
>>   /sys/kernel/irq/18/actions: i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
>>   /sys/kernel/irq/18/chip_name:       IR-IO-APIC
>>   /sys/kernel/irq/18/hwirq:           18
>>   /sys/kernel/irq/18/name:            fasteoi
>>   /sys/kernel/irq/18/per_cpu_count:   0,0
>>   /sys/kernel/irq/18/type:            level
>>
>>   /sys/kernel/irq/25/actions: ahci0
>>   /sys/kernel/irq/25/chip_name:       IR-PCI-MSI
>>   /sys/kernel/irq/25/hwirq:           512000
>>   /sys/kernel/irq/25/name:            edge
>>   /sys/kernel/irq/25/per_cpu_count:   29036,0
>>   /sys/kernel/irq/25/type:            edge
>
> Thanks for the update.
>
> One concern:
>
> This per_cpu_count is for online CPUs only.
> How does this help when the online CPUs change?
>
> E.g., above could be for CPUs 1 and 5.
> The next time that it is read it could be for CPUs 0 and 3.
> Seems that it could be confusing even for software reading it.

Thanks for the feedback. I imagine most use cases for this will simply
add up all the values to obtain a total.  There's not a lot of use for
the individual elements unless you additionally know something about
the CPU id layout, interrupt pinning, and/or CPU online state.  The
/proc/interrupts interface has this same issue, but additionally uses
column headers.  There's really know way to give a similar consistent
view of all of this data using multiple sysfs files.  Given this lack
of atomicity, across files, I don't think it's unreasonable for the
counter order to change when the system's online CPUs change.

The only alternative I could imagine would be to include a header row
in this file listing the CPU ids as a parallel list.  I think this
goes against the standard sysfs file format, though...

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts
  2016-09-06 20:55   ` Craig Gallek
@ 2016-09-09  6:15     ` Thomas Gleixner
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-09  6:15 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, LKML

On Tue, 6 Sep 2016, Craig Gallek wrote:

> On Fri, Sep 2, 2016 at 10:59 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> > On Tue, 26 Jul 2016, Craig Gallek wrote:
> >> +static void irq_kobj_release(struct kobject *kobj)
> >> +{
> >> +     struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
> >> +
> >> +     /*
> >> +      * This irq_desc is statically allocated.  Simply zero its kobject
> >> +      * rather than kfree it.
> >> +      */
> >> +     memset(&desc->kobj, 0, sizeof(desc->kobj));
> >
> > This will nicely explode when the irq descriptor is handed out again before
> > this function is called.
> >
> > There is no point to make this work for !SPARSE_IRQ. Just ignore it.
> Thank you for the review.  Just one clarification before I send v2:
> Are you suggesting getting rid of this feature entirely for the
> non-sparse version or just this kobject cleanup code?

There is no point in supporting that !SPARSE stuff. It should die anyway.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v3] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 22:15       ` Craig Gallek
@ 2016-09-09 12:39         ` Thomas Gleixner
  0 siblings, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-09 12:39 UTC (permalink / raw)
  To: Craig Gallek; +Cc: Randy Dunlap, David Decotigny, LKML

On Thu, 8 Sep 2016, Craig Gallek wrote:
> On Thu, Sep 8, 2016 at 6:00 PM, Randy Dunlap <rdunlap@infradead.org> wrote:
> > On 09/08/16 13:25, Craig Gallek wrote:
> >> From: Craig Gallek <kraig@google.com>
> >>
> >> Add struct kobject to struct irq_desc to allow for easy export
> >> to sysfs.  This allows for much simpler userspace-parsing of
> >> the information contained in struct irq_desc.
> >>
> >> Note that sysfs is not available at the time of early irq initialization.
> >> These interrupts are accounted for using a postcore_initcall callback.
> >>
> >> Examples:
> >>   /sys/kernel/irq/18/actions: i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
> >>   /sys/kernel/irq/18/chip_name:       IR-IO-APIC
> >>   /sys/kernel/irq/18/hwirq:           18
> >>   /sys/kernel/irq/18/name:            fasteoi
> >>   /sys/kernel/irq/18/per_cpu_count:   0,0
> >>   /sys/kernel/irq/18/type:            level
> >>
> >>   /sys/kernel/irq/25/actions: ahci0
> >>   /sys/kernel/irq/25/chip_name:       IR-PCI-MSI
> >>   /sys/kernel/irq/25/hwirq:           512000
> >>   /sys/kernel/irq/25/name:            edge
> >>   /sys/kernel/irq/25/per_cpu_count:   29036,0
> >>   /sys/kernel/irq/25/type:            edge
> >
> > Thanks for the update.
> >
> > One concern:
> >
> > This per_cpu_count is for online CPUs only.
> > How does this help when the online CPUs change?
> >
> > E.g., above could be for CPUs 1 and 5.
> > The next time that it is read it could be for CPUs 0 and 3.
> > Seems that it could be confusing even for software reading it.
> 
> Thanks for the feedback. I imagine most use cases for this will simply
> add up all the values to obtain a total.  There's not a lot of use for
> the individual elements unless you additionally know something about
> the CPU id layout, interrupt pinning, and/or CPU online state.  The
> /proc/interrupts interface has this same issue, but additionally uses
> column headers.  There's really know way to give a similar consistent
> view of all of this data using multiple sysfs files.  Given this lack
> of atomicity, across files, I don't think it's unreasonable for the
> counter order to change when the system's online CPUs change.

Can you please spell that out in the documentation?

Thanks,

	tglx

 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v4] genirq: Machine-parsable version of /proc/interrupts
  2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
  2016-09-08 17:57   ` Randy Dunlap
  2016-09-08 20:25   ` [PATCH v3] " Craig Gallek
@ 2016-09-09 16:28   ` Craig Gallek
  2016-09-09 23:19     ` Thomas Gleixner
  2016-09-12 14:12     ` [PATCH v5] " Craig Gallek
  2 siblings, 2 replies; 20+ messages in thread
From: Craig Gallek @ 2016-09-09 16:28 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Add struct kobject to struct irq_desc to allow for easy export
to sysfs.  This allows for much simpler userspace-parsing of
the information contained in struct irq_desc.

Note that sysfs is not available at the time of early irq initialization.
These interrupts are accounted for using a postcore_initcall callback.

This feature is only available when both CONFIG_SPARSE_IRQ and
CONFIG_SYSFS are enabled.

Examples:
  /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
  /sys/kernel/irq/18/chip_name:	IR-IO-APIC
  /sys/kernel/irq/18/hwirq:		18
  /sys/kernel/irq/18/name:		fasteoi
  /sys/kernel/irq/18/per_cpu_count:	0,0
  /sys/kernel/irq/18/type:		level

  /sys/kernel/irq/25/actions:	ahci0
  /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
  /sys/kernel/irq/25/hwirq:		512000
  /sys/kernel/irq/25/name:		edge
  /sys/kernel/irq/25/per_cpu_count:	29036,0
  /sys/kernel/irq/25/type:		edge

Signed-off-by: Craig Gallek <kraig@google.com>
---
v4 changes:
 * Remove modifications to the !CONFIG_SPARSE_IRQ.
 * Include note in documentation wrt per-cpu interrupt counters and
   offline CPUs.

v3 changes:
 * Add some examples to the commit message and an ABI documentation file.
 * Fix spelling mistake.

v2 changes:
 * Several suggested stylistic changes.
 * Fix one NULL pointer dereference possibility.
 * Do not destroy kobjects in non-SPARSE_IRQ configuration.
 * Include #define CONFIG_SYSFS guards to selectively enable feature.

 Documentation/ABI/testing/sysfs-kernel-irq |  53 +++++++++
 include/linux/irqdesc.h                    |   3 +
 kernel/irq/irqdesc.c                       | 185 ++++++++++++++++++++++++++++-
 3 files changed, 239 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-irq

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 000000000000..b0437a7f8e1e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,53 @@
+What:		/sys/kernel/irq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Directory containing information about the system's IRQs.
+		Specifically, data from the associated struct irq_desc.
+		The information here is similar to that in /proc/interrupts
+		but in a more machine-friendly format.  This directory contains
+		one subdirectory for each Linux IRQ number.
+
+What:		/sys/kernel/irq/<irq>/actions
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The IRQ action chain.  A comma-separated list of zero or more
+		device names associated with this interrupt.
+
+What:		/sys/kernel/irq/<irq>/chip_name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable chip name supplied by the associated device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/hwirq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	When interrupt translation domains are used, this file contains
+		the underlying hardware IRQ number used for this Linux IRQ.
+
+What:		/sys/kernel/irq/<irq>/name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable flow handler name as defined by the device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/per_cpu_count
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The number of times the interrupt has fired since boot.  This
+		is a comma-separated list of counters; one per online CPU in
+		CPU id order.  NOTE: If the set of online CPUs changes, the
+		counter positions in this list will also change and counters
+		for offline CPUs will not be available.
+
+What:		/sys/kernel/irq/<irq>/type
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The type of the interrupt.  Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..aa41d39803b2 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -45,6 +46,7 @@ struct pt_regs;
  * @rcu:		rcu head for delayed free
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
+ * @kobj:		kobject used to represent this struct in sysfs
  */
 struct irq_desc {
 	struct irq_common_data	irq_common_data;
@@ -92,6 +94,7 @@ struct irq_desc {
 	int			parent_irq;
 	struct module		*owner;
 	const char		*name;
+	struct kobject		kobj;
 } ____cacheline_internodealigned_in_smp;
 
 #ifdef CONFIG_SPARSE_IRQ
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..f944f30ed23e 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -123,6 +124,154 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
 #ifdef CONFIG_SPARSE_IRQ
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_online_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Failed to add kobject for irq %d\n", irq);
+	}
+}
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +336,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +347,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +419,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
@@ -730,3 +889,25 @@ unsigned int kstat_irqs_usr(unsigned int irq)
 	irq_unlock_sparse();
 	return sum;
 }
+
+#ifdef CONFIG_SPARSE_IRQ
+#ifdef CONFIG_SYSFS
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base)
+		return -ENOMEM;
+
+	irq_lock_sparse();
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+#endif /* CONFIG_SYSFS */
+#endif /* CONFIG_SPARSE_IRQ */
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v4] genirq: Machine-parsable version of /proc/interrupts
  2016-09-09 16:28   ` [PATCH v4] " Craig Gallek
@ 2016-09-09 23:19     ` Thomas Gleixner
  2016-09-12 14:12     ` [PATCH v5] " Craig Gallek
  1 sibling, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-09 23:19 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, linux-kernel

On Fri, 9 Sep 2016, Craig Gallek wrote:
> +What:		/sys/kernel/irq/<irq>/per_cpu_count
> +Date:		September 2016
> +KernelVersion:	4.9
> +Contact:	Craig Gallek <kraig@google.com>
> +Description:	The number of times the interrupt has fired since boot.  This
> +		is a comma-separated list of counters; one per online CPU in
> +		CPU id order.  NOTE: If the set of online CPUs changes, the
> +		counter positions in this list will also change and counters
> +		for offline CPUs will not be available.

More thoughts:

We could easily print all possible cpus here instead of restricting it to
online cpus. The per cpu counters are still available.

That way you can even observe the affinity change after a online or offline
and a tool which is interrested in more than the sum wouldn't have to cope
with changing positions.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v5] genirq: Machine-parsable version of /proc/interrupts
  2016-09-09 16:28   ` [PATCH v4] " Craig Gallek
  2016-09-09 23:19     ` Thomas Gleixner
@ 2016-09-12 14:12     ` Craig Gallek
  2016-09-13 14:50       ` Thomas Gleixner
  2016-09-13 16:14       ` [PATCH v6] " Craig Gallek
  1 sibling, 2 replies; 20+ messages in thread
From: Craig Gallek @ 2016-09-12 14:12 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Add struct kobject to struct irq_desc to allow for easy export
to sysfs.  This allows for much simpler userspace-parsing of
the information contained in struct irq_desc.

Note that sysfs is not available at the time of early irq initialization.
These interrupts are accounted for using a postcore_initcall callback.

This feature is only available when both CONFIG_SPARSE_IRQ and
CONFIG_SYSFS are enabled.

Examples:
  /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
  /sys/kernel/irq/18/chip_name:	IR-IO-APIC
  /sys/kernel/irq/18/hwirq:		18
  /sys/kernel/irq/18/name:		fasteoi
  /sys/kernel/irq/18/per_cpu_count:	0,0
  /sys/kernel/irq/18/type:		level

  /sys/kernel/irq/25/actions:	ahci0
  /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
  /sys/kernel/irq/25/hwirq:		512000
  /sys/kernel/irq/25/name:		edge
  /sys/kernel/irq/25/per_cpu_count:	29036,0
  /sys/kernel/irq/25/type:		edge

Signed-off-by: Craig Gallek <kraig@google.com>
---
v5 changes:
 * Change per_cpu_count: for_each_online_cpu -> for_each_possible_cpu.

v4 changes:
 * Remove modifications to the !CONFIG_SPARSE_IRQ.
 * Include note in documentation wrt per-cpu interrupt counters and
   offline CPUs.

v3 changes:
 * Add some examples to the commit message and an ABI documentation file.
 * Fix spelling mistake.

v2 changes:
 * Several suggested stylistic changes.
 * Fix one NULL pointer dereference possibility.
 * Do not destroy kobjects in non-SPARSE_IRQ configuration.
 * Include #define CONFIG_SYSFS guards to selectively enable feature.

 Documentation/ABI/testing/sysfs-kernel-irq |  53 +++++++++
 include/linux/irqdesc.h                    |   3 +
 kernel/irq/irqdesc.c                       | 185 ++++++++++++++++++++++++++++-
 3 files changed, 239 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-irq

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 000000000000..f96a8f271b6e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,53 @@
+What:		/sys/kernel/irq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Directory containing information about the system's IRQs.
+		Specifically, data from the associated struct irq_desc.
+		The information here is similar to that in /proc/interrupts
+		but in a more machine-friendly format.  This directory contains
+		one subdirectory for each Linux IRQ number.
+
+What:		/sys/kernel/irq/<irq>/actions
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The IRQ action chain.  A comma-separated list of zero or more
+		device names associated with this interrupt.
+
+What:		/sys/kernel/irq/<irq>/chip_name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable chip name supplied by the associated device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/hwirq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	When interrupt translation domains are used, this file contains
+		the underlying hardware IRQ number used for this Linux IRQ.
+
+What:		/sys/kernel/irq/<irq>/name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable flow handler name as defined by the device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/per_cpu_count
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The number of times the interrupt has fired since boot.  This
+		is a comma-separated list of counters; one per CPU in CPU id
+		order.  NOTE: This file consistently shows counters for all
+		CPU ids.  This differs from the behavior of /proc/interrupts
+		which only shows counters for online CPUs.
+
+What:		/sys/kernel/irq/<irq>/type
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The type of the interrupt.  Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..aa41d39803b2 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -45,6 +46,7 @@ struct pt_regs;
  * @rcu:		rcu head for delayed free
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
+ * @kobj:		kobject used to represent this struct in sysfs
  */
 struct irq_desc {
 	struct irq_common_data	irq_common_data;
@@ -92,6 +94,7 @@ struct irq_desc {
 	int			parent_irq;
 	struct module		*owner;
 	const char		*name;
+	struct kobject		kobj;
 } ____cacheline_internodealigned_in_smp;
 
 #ifdef CONFIG_SPARSE_IRQ
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..2bc903505f6b 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -123,6 +124,154 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
 #ifdef CONFIG_SPARSE_IRQ
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_possible_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Failed to add kobject for irq %d\n", irq);
+	}
+}
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +336,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +347,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +419,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
@@ -730,3 +889,25 @@ unsigned int kstat_irqs_usr(unsigned int irq)
 	irq_unlock_sparse();
 	return sum;
 }
+
+#ifdef CONFIG_SPARSE_IRQ
+#ifdef CONFIG_SYSFS
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base)
+		return -ENOMEM;
+
+	irq_lock_sparse();
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+#endif /* CONFIG_SYSFS */
+#endif /* CONFIG_SPARSE_IRQ */
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v5] genirq: Machine-parsable version of /proc/interrupts
  2016-09-12 14:12     ` [PATCH v5] " Craig Gallek
@ 2016-09-13 14:50       ` Thomas Gleixner
  2016-09-13 16:14       ` [PATCH v6] " Craig Gallek
  1 sibling, 0 replies; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-13 14:50 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, linux-kernel

On Mon, 12 Sep 2016, Craig Gallek wrote:

> From: Craig Gallek <kraig@google.com>
 
> Add struct kobject to struct irq_desc to allow for easy export
> to sysfs.  This allows for much simpler userspace-parsing of
> the information contained in struct irq_desc.

This lacks a rationale WHY we want to add this. Something like:

"/proc/interrupts is hard to parse by tools because <add content>."

Then add some blurb why and how a sysfs based interface solves the above
problem.

The fact that you add a kobject is an implementation detail and completely
irrelevant for the changelog. We can see that from the patch.
 
> Note that sysfs is not available at the time of early irq initialization.
> These interrupts are accounted for using a postcore_initcall callback.

That want's to be documented in the code .
 
>  /*
>   * Core internal functions to deal with irq descriptors
> @@ -45,6 +46,7 @@ struct pt_regs;
>   * @rcu:		rcu head for delayed free
>   * @dir:		/proc/irq/ procfs entry
>   * @name:		flow handler name for /proc/interrupts output
> + * @kobj:		kobject used to represent this struct in sysfs
>   */
>  struct irq_desc {
>  	struct irq_common_data	irq_common_data;
> @@ -92,6 +94,7 @@ struct irq_desc {
>  	int			parent_irq;
>  	struct module		*owner;
>  	const char		*name;
> +	struct kobject		kobj;

Can you please move that into the CONFIG_SPARSE_IRQ conditional section
where we have the rcu head ?

> +
> +#ifdef CONFIG_SPARSE_IRQ
> +#ifdef CONFIG_SYSFS

We use 

#if defined(A) && defined(B)

but please move it into the #ifdef SYSFS section which you add anyway.

> +static int __init irq_sysfs_init(void)
> +{
> +	struct irq_desc *desc;
> +	int irq;
> +
> +	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
> +	if (!irq_kobj_base)
> +		return -ENOMEM;

This is racy versus a concurrent interrupt setup. You need to move that
into the sparse locked section.

> +
> +	irq_lock_sparse();
> +	for_each_irq_desc(irq, desc)
> +		irq_sysfs_add(irq, desc);
> +	irq_unlock_sparse();

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v6] genirq: Machine-parsable version of /proc/interrupts
  2016-09-12 14:12     ` [PATCH v5] " Craig Gallek
  2016-09-13 14:50       ` Thomas Gleixner
@ 2016-09-13 16:14       ` Craig Gallek
  2016-09-14 13:34         ` [tip:irq/core] genirq: Expose interrupt information through sysfs tip-bot for Craig Gallek
  2016-09-14 13:36         ` [PATCH v6] genirq: Machine-parsable version of /proc/interrupts Thomas Gleixner
  1 sibling, 2 replies; 20+ messages in thread
From: Craig Gallek @ 2016-09-13 16:14 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, linux-kernel

From: Craig Gallek <kraig@google.com>

Export struct irq_desc information through sysfs.  The same information
is available in /proc/interrupts but the format of that file has changed
over kernel versions and differs across architectures.  The procfs version
also has varying column numbers depending on hardware.  This sysfs version
exposes each irq attribute in a separate file in a consistent, machine
parsable way.

This feature is only available when both CONFIG_SPARSE_IRQ and
CONFIG_SYSFS are enabled.

Examples:
  /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
  /sys/kernel/irq/18/chip_name:	IR-IO-APIC
  /sys/kernel/irq/18/hwirq:		18
  /sys/kernel/irq/18/name:		fasteoi
  /sys/kernel/irq/18/per_cpu_count:	0,0
  /sys/kernel/irq/18/type:		level

  /sys/kernel/irq/25/actions:	ahci0
  /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
  /sys/kernel/irq/25/hwirq:		512000
  /sys/kernel/irq/25/name:		edge
  /sys/kernel/irq/25/per_cpu_count:	29036,0
  /sys/kernel/irq/25/type:		edge

Signed-off-by: Craig Gallek <kraig@google.com>
---
v6 changes:
 * Apply suggestions to commit message (moved some of it to code comments).
 * Move kobject in irq_desc to CONFIG_SPARSE_IRQ guard.
 * Consolidate irq_sysfs_init CONFIG_SYSFS guard with earlier guard.
 * Use sparse_irq_lock to protect initial creation/read of irq sysfs
   directory.

v5 changes:
 * Change per_cpu_count: for_each_online_cpu -> for_each_possible_cpu.

v4 changes:
 * Remove modifications to the !CONFIG_SPARSE_IRQ.
 * Include note in documentation wrt per-cpu interrupt counters and
   offline CPUs.

v3 changes:
 * Add some examples to the commit message and an ABI documentation file.
 * Fix spelling mistake.

v2 changes:
 * Several suggested stylistic changes.
 * Fix one NULL pointer dereference possibility.
 * Do not destroy kobjects in non-SPARSE_IRQ configuration.
 * Include #define CONFIG_SYSFS guards to selectively enable feature.

 Documentation/ABI/testing/sysfs-kernel-irq |  53 ++++++++
 include/linux/irqdesc.h                    |   3 +
 kernel/irq/irqdesc.c                       | 188 ++++++++++++++++++++++++++++-
 3 files changed, 242 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-irq

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 000000000000..f96a8f271b6e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,53 @@
+What:		/sys/kernel/irq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Directory containing information about the system's IRQs.
+		Specifically, data from the associated struct irq_desc.
+		The information here is similar to that in /proc/interrupts
+		but in a more machine-friendly format.  This directory contains
+		one subdirectory for each Linux IRQ number.
+
+What:		/sys/kernel/irq/<irq>/actions
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The IRQ action chain.  A comma-separated list of zero or more
+		device names associated with this interrupt.
+
+What:		/sys/kernel/irq/<irq>/chip_name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable chip name supplied by the associated device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/hwirq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	When interrupt translation domains are used, this file contains
+		the underlying hardware IRQ number used for this Linux IRQ.
+
+What:		/sys/kernel/irq/<irq>/name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable flow handler name as defined by the device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/per_cpu_count
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The number of times the interrupt has fired since boot.  This
+		is a comma-separated list of counters; one per CPU in CPU id
+		order.  NOTE: This file consistently shows counters for all
+		CPU ids.  This differs from the behavior of /proc/interrupts
+		which only shows counters for online CPUs.
+
+What:		/sys/kernel/irq/<irq>/type
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The type of the interrupt.  Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beebf9804..c9be57931b58 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -43,6 +44,7 @@ struct pt_regs;
  * @force_resume_depth:	number of irqactions on a irq descriptor with
  *			IRQF_FORCE_RESUME set
  * @rcu:		rcu head for delayed free
+ * @kobj:		kobject used to represent this struct in sysfs
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
  */
@@ -88,6 +90,7 @@ struct irq_desc {
 #endif
 #ifdef CONFIG_SPARSE_IRQ
 	struct rcu_head		rcu;
+	struct kobject		kobj;
 #endif
 	int			parent_irq;
 	struct module		*owner;
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44f2d4b..64bbd696686f 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -123,6 +124,179 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
 #ifdef CONFIG_SPARSE_IRQ
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_possible_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Failed to add kobject for irq %d\n", irq);
+	}
+}
+
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	irq_lock_sparse();
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base) {
+		irq_unlock_sparse();
+		return -ENOMEM;
+	}
+
+	/*
+	 * Here we catch any interrupts which were added during early_irq_init
+	 * before the sysfs subsystem was available and we add them to our
+	 * newly created sysfs irq directory.
+	 */
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +361,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,20 +372,28 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	unregister_irq_proc(irq, desc);
+	kobject_del(&desc->kobj);
 
 	/*
 	 * sparse_irq_lock protects also show_interrupts() and
@@ -261,6 +444,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;
-- 
2.8.0.rc3.226.g39d4020

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [tip:irq/core] genirq: Expose interrupt information through sysfs
  2016-09-13 16:14       ` [PATCH v6] " Craig Gallek
@ 2016-09-14 13:34         ` tip-bot for Craig Gallek
  2016-09-14 13:36         ` [PATCH v6] genirq: Machine-parsable version of /proc/interrupts Thomas Gleixner
  1 sibling, 0 replies; 20+ messages in thread
From: tip-bot for Craig Gallek @ 2016-09-14 13:34 UTC (permalink / raw)
  To: linux-tip-commits; +Cc: tglx, linux-kernel, kraig, mingo, hpa, decot

Commit-ID:  ecb3f394c5dba897d215a5422f1b363e93e2ce4e
Gitweb:     http://git.kernel.org/tip/ecb3f394c5dba897d215a5422f1b363e93e2ce4e
Author:     Craig Gallek <kraig@google.com>
AuthorDate: Tue, 13 Sep 2016 12:14:51 -0400
Committer:  Thomas Gleixner <tglx@linutronix.de>
CommitDate: Wed, 14 Sep 2016 15:28:15 +0200

genirq: Expose interrupt information through sysfs

Information about interrupts is exposed via /proc/interrupts, but the
format of that file has changed over kernel versions and differs across
architectures. It also has varying column numbers depending on hardware.

That all makes it hard for tools to parse.

To solve this, expose the information through sysfs so each irq attribute
is in a separate file in a consistent, machine parsable way.

This feature is only available when both CONFIG_SPARSE_IRQ and
CONFIG_SYSFS are enabled.

Examples:
  /sys/kernel/irq/18/actions:	i801_smbus,ehci_hcd:usb1,uhci_hcd:usb7
  /sys/kernel/irq/18/chip_name:	IR-IO-APIC
  /sys/kernel/irq/18/hwirq:		18
  /sys/kernel/irq/18/name:		fasteoi
  /sys/kernel/irq/18/per_cpu_count:	0,0
  /sys/kernel/irq/18/type:		level

  /sys/kernel/irq/25/actions:	ahci0
  /sys/kernel/irq/25/chip_name:	IR-PCI-MSI
  /sys/kernel/irq/25/hwirq:		512000
  /sys/kernel/irq/25/name:		edge
  /sys/kernel/irq/25/per_cpu_count:	29036,0
  /sys/kernel/irq/25/type:		edge

[ tglx: Moved kobject_del() under sparse_irq_lock, massaged code comments
  	and changelog ]

Signed-off-by: Craig Gallek <kraig@google.com>
Cc: David Decotigny <decot@google.com>
Link: http://lkml.kernel.org/r/1473783291-122873-1-git-send-email-kraigatgoog@gmail.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 Documentation/ABI/testing/sysfs-kernel-irq |  53 ++++++++
 include/linux/irqdesc.h                    |   3 +
 kernel/irq/irqdesc.c                       | 193 ++++++++++++++++++++++++++++-
 3 files changed, 247 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-kernel-irq b/Documentation/ABI/testing/sysfs-kernel-irq
new file mode 100644
index 0000000..eb074b1
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-irq
@@ -0,0 +1,53 @@
+What:		/sys/kernel/irq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Directory containing information about the system's IRQs.
+		Specifically, data from the associated struct irq_desc.
+		The information here is similar to that in /proc/interrupts
+		but in a more machine-friendly format.  This directory contains
+		one subdirectory for each Linux IRQ number.
+
+What:		/sys/kernel/irq/<irq>/actions
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The IRQ action chain.  A comma-separated list of zero or more
+		device names associated with this interrupt.
+
+What:		/sys/kernel/irq/<irq>/chip_name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable chip name supplied by the associated device
+		driver.
+
+What:		/sys/kernel/irq/<irq>/hwirq
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	When interrupt translation domains are used, this file contains
+		the underlying hardware IRQ number used for this Linux IRQ.
+
+What:		/sys/kernel/irq/<irq>/name
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	Human-readable flow handler name as defined by the irq chip
+		driver.
+
+What:		/sys/kernel/irq/<irq>/per_cpu_count
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The number of times the interrupt has fired since boot.  This
+		is a comma-separated list of counters; one per CPU in CPU id
+		order.  NOTE: This file consistently shows counters for all
+		CPU ids.  This differs from the behavior of /proc/interrupts
+		which only shows counters for online CPUs.
+
+What:		/sys/kernel/irq/<irq>/type
+Date:		September 2016
+KernelVersion:	4.9
+Contact:	Craig Gallek <kraig@google.com>
+Description:	The type of the interrupt.  Either the string 'level' or 'edge'.
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index b51beeb..c9be579 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -2,6 +2,7 @@
 #define _LINUX_IRQDESC_H
 
 #include <linux/rcupdate.h>
+#include <linux/kobject.h>
 
 /*
  * Core internal functions to deal with irq descriptors
@@ -43,6 +44,7 @@ struct pt_regs;
  * @force_resume_depth:	number of irqactions on a irq descriptor with
  *			IRQF_FORCE_RESUME set
  * @rcu:		rcu head for delayed free
+ * @kobj:		kobject used to represent this struct in sysfs
  * @dir:		/proc/irq/ procfs entry
  * @name:		flow handler name for /proc/interrupts output
  */
@@ -88,6 +90,7 @@ struct irq_desc {
 #endif
 #ifdef CONFIG_SPARSE_IRQ
 	struct rcu_head		rcu;
+	struct kobject		kobj;
 #endif
 	int			parent_irq;
 	struct module		*owner;
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index a623b44..93b5172 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -15,6 +15,7 @@
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
+#include <linux/sysfs.h>
 
 #include "internals.h"
 
@@ -123,6 +124,181 @@ static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS);
 
 #ifdef CONFIG_SPARSE_IRQ
 
+static void irq_kobj_release(struct kobject *kobj);
+
+#ifdef CONFIG_SYSFS
+static struct kobject *irq_kobj_base;
+
+#define IRQ_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+static ssize_t per_cpu_count_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	int cpu, irq = desc->irq_data.irq;
+	ssize_t ret = 0;
+	char *p = "";
+
+	for_each_possible_cpu(cpu) {
+		unsigned int c = kstat_irqs_cpu(irq, cpu);
+
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
+		p = ",";
+	}
+
+	ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+	return ret;
+}
+IRQ_ATTR_RO(per_cpu_count);
+
+static ssize_t chip_name_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.chip && desc->irq_data.chip->name) {
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+				desc->irq_data.chip->name);
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(chip_name);
+
+static ssize_t hwirq_show(struct kobject *kobj,
+			  struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->irq_data.domain)
+		ret = sprintf(buf, "%d\n", (int)desc->irq_data.hwirq);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(hwirq);
+
+static ssize_t type_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	ret = sprintf(buf, "%s\n",
+		      irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+
+}
+IRQ_ATTR_RO(type);
+
+static ssize_t name_show(struct kobject *kobj,
+			 struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	ssize_t ret = 0;
+
+	raw_spin_lock_irq(&desc->lock);
+	if (desc->name)
+		ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
+	raw_spin_unlock_irq(&desc->lock);
+
+	return ret;
+}
+IRQ_ATTR_RO(name);
+
+static ssize_t actions_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
+	struct irqaction *action;
+	ssize_t ret = 0;
+	char *p = "";
+
+	raw_spin_lock_irq(&desc->lock);
+	for (action = desc->action; action != NULL; action = action->next) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
+				 p, action->name);
+		p = ",";
+	}
+	raw_spin_unlock_irq(&desc->lock);
+
+	if (ret)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
+	return ret;
+}
+IRQ_ATTR_RO(actions);
+
+static struct attribute *irq_attrs[] = {
+	&per_cpu_count_attr.attr,
+	&chip_name_attr.attr,
+	&hwirq_attr.attr,
+	&type_attr.attr,
+	&name_attr.attr,
+	&actions_attr.attr,
+	NULL
+};
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+	.sysfs_ops	= &kobj_sysfs_ops,
+	.default_attrs	= irq_attrs,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc)
+{
+	if (irq_kobj_base) {
+		/*
+		 * Continue even in case of failure as this is nothing
+		 * crucial.
+		 */
+		if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq))
+			pr_warn("Failed to add kobject for irq %d\n", irq);
+	}
+}
+
+static int __init irq_sysfs_init(void)
+{
+	struct irq_desc *desc;
+	int irq;
+
+	/* Prevent concurrent irq alloc/free */
+	irq_lock_sparse();
+
+	irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
+	if (!irq_kobj_base) {
+		irq_unlock_sparse();
+		return -ENOMEM;
+	}
+
+	/* Add the already allocated interrupts */
+	for_each_irq_desc(irq, desc)
+		irq_sysfs_add(irq, desc);
+	irq_unlock_sparse();
+
+	return 0;
+}
+postcore_initcall(irq_sysfs_init);
+
+#else /* !CONFIG_SYSFS */
+
+static struct kobj_type irq_kobj_type = {
+	.release	= irq_kobj_release,
+};
+
+static void irq_sysfs_add(int irq, struct irq_desc *desc) {}
+
+#endif /* CONFIG_SYSFS */
+
 static RADIX_TREE(irq_desc_tree, GFP_KERNEL);
 
 static void irq_insert_desc(unsigned int irq, struct irq_desc *desc)
@@ -187,6 +363,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
 
 	desc_set_defaults(irq, desc, node, affinity, owner);
 	irqd_set(&desc->irq_data, flags);
+	kobject_init(&desc->kobj, &irq_kobj_type);
 
 	return desc;
 
@@ -197,15 +374,22 @@ err_desc:
 	return NULL;
 }
 
-static void delayed_free_desc(struct rcu_head *rhp)
+static void irq_kobj_release(struct kobject *kobj)
 {
-	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+	struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
 
 	free_masks(desc);
 	free_percpu(desc->kstat_irqs);
 	kfree(desc);
 }
 
+static void delayed_free_desc(struct rcu_head *rhp)
+{
+	struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu);
+
+	kobject_put(&desc->kobj);
+}
+
 static void free_desc(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
@@ -217,8 +401,12 @@ static void free_desc(unsigned int irq)
 	 * kstat_irq_usr(). Once we deleted the descriptor from the
 	 * sparse tree we can free it. Access in proc will fail to
 	 * lookup the descriptor.
+	 *
+	 * The sysfs entry must be serialized against a concurrent
+	 * irq_sysfs_init() as well.
 	 */
 	mutex_lock(&sparse_irq_lock);
+	kobject_del(&desc->kobj);
 	delete_irq_desc(irq);
 	mutex_unlock(&sparse_irq_lock);
 
@@ -261,6 +449,7 @@ static int alloc_descs(unsigned int start, unsigned int cnt, int node,
 			goto err;
 		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
+		irq_sysfs_add(start + i, desc);
 		mutex_unlock(&sparse_irq_lock);
 	}
 	return start;

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v6] genirq: Machine-parsable version of /proc/interrupts
  2016-09-13 16:14       ` [PATCH v6] " Craig Gallek
  2016-09-14 13:34         ` [tip:irq/core] genirq: Expose interrupt information through sysfs tip-bot for Craig Gallek
@ 2016-09-14 13:36         ` Thomas Gleixner
  2016-09-14 13:59           ` Craig Gallek
  1 sibling, 1 reply; 20+ messages in thread
From: Thomas Gleixner @ 2016-09-14 13:36 UTC (permalink / raw)
  To: Craig Gallek; +Cc: David Decotigny, linux-kernel

On Tue, 13 Sep 2016, Craig Gallek wrote:
> +What:		/sys/kernel/irq/<irq>/name
> +Date:		September 2016
> +KernelVersion:	4.9
> +Contact:	Craig Gallek <kraig@google.com>
> +Description:	Human-readable flow handler name as defined by the device
> +		driver.

That's ambigous. device driver reads like a the driver of the device which
uses this irq. I fixed that up.

>  static void free_desc(unsigned int irq)
>  {
>  	struct irq_desc *desc = irq_to_desc(irq);
>  
>  	unregister_irq_proc(irq, desc);
> +	kobject_del(&desc->kobj);

You forgot to move this under the sparse lock protection as well. There is
the same race against a concurrent initcall.

Thanks,

	tglx

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v6] genirq: Machine-parsable version of /proc/interrupts
  2016-09-14 13:36         ` [PATCH v6] genirq: Machine-parsable version of /proc/interrupts Thomas Gleixner
@ 2016-09-14 13:59           ` Craig Gallek
  0 siblings, 0 replies; 20+ messages in thread
From: Craig Gallek @ 2016-09-14 13:59 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: David Decotigny, LKML

On Wed, Sep 14, 2016 at 9:36 AM, Thomas Gleixner <tglx@linutronix.de> wrote:
> On Tue, 13 Sep 2016, Craig Gallek wrote:
>> +What:                /sys/kernel/irq/<irq>/name
>> +Date:                September 2016
>> +KernelVersion:       4.9
>> +Contact:     Craig Gallek <kraig@google.com>
>> +Description: Human-readable flow handler name as defined by the device
>> +             driver.
>
> That's ambigous. device driver reads like a the driver of the device which
> uses this irq. I fixed that up.
>
>>  static void free_desc(unsigned int irq)
>>  {
>>       struct irq_desc *desc = irq_to_desc(irq);
>>
>>       unregister_irq_proc(irq, desc);
>> +     kobject_del(&desc->kobj);
>
> You forgot to move this under the sparse lock protection as well. There is
> the same race against a concurrent initcall.
Good catch, thank you for the review and accepting this code.

Craig

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2016-09-14 13:59 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-26 14:25 [PATCH RESEND] genirq: Machine-parsable version of /proc/interrupts Craig Gallek
2016-08-10 15:26 ` Craig Gallek
2016-09-02 13:52   ` Thomas Gleixner
2016-09-02 14:59 ` Thomas Gleixner
2016-09-06 20:55   ` Craig Gallek
2016-09-09  6:15     ` Thomas Gleixner
2016-09-08 17:34 ` [PATCH v2] " Craig Gallek
2016-09-08 17:57   ` Randy Dunlap
2016-09-08 20:25   ` [PATCH v3] " Craig Gallek
2016-09-08 22:00     ` Randy Dunlap
2016-09-08 22:15       ` Craig Gallek
2016-09-09 12:39         ` Thomas Gleixner
2016-09-09 16:28   ` [PATCH v4] " Craig Gallek
2016-09-09 23:19     ` Thomas Gleixner
2016-09-12 14:12     ` [PATCH v5] " Craig Gallek
2016-09-13 14:50       ` Thomas Gleixner
2016-09-13 16:14       ` [PATCH v6] " Craig Gallek
2016-09-14 13:34         ` [tip:irq/core] genirq: Expose interrupt information through sysfs tip-bot for Craig Gallek
2016-09-14 13:36         ` [PATCH v6] genirq: Machine-parsable version of /proc/interrupts Thomas Gleixner
2016-09-14 13:59           ` Craig Gallek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).