* [RFC] ACPI interrupt statistics
@ 2007-11-03 4:40 Len Brown
2007-11-19 19:42 ` Carlos Corbacho
0 siblings, 1 reply; 3+ messages in thread
From: Len Brown @ 2007-11-03 4:40 UTC (permalink / raw)
To: Linux-acpi
For your review and testing...
The patch below exports interrupt counters from within ACPI
so that developers can better understand the cause of ACPI interrupts.
My intent is to have something like this built in by default
starting in 2.6.25. The version below applies to 2.6.21
and is originally based on a patch by Luming Yu.
I'll probably move most of the code to system.c from osl.c,
and I'll port it to 2.6.24;
but otherwise, it seems to be doing what I wanted it to do...
One idea is that a user-space application could
select() an individual GPE file in order to notice
when a particular GPE fires. This might be useful
for user-space platform specific code -- say allowing
it to recognize a GPE without requiring any platform-specific
kernel code...
$ cd /sys/firmware/acpi/interrupts/
$ ls
gpe0 gpe11 gpe14 gpe17 gpe2 gpe22 gpe25 gpe28 gpe30 gpe5 gpe8
gpe1 gpe12 gpe15 gpe18 gpe20 gpe23 gpe26 gpe29 gpe31 gpe6 gpe9
gpe10 gpe13 gpe16 gpe19 gpe21 gpe24 gpe27 gpe3 gpe4 gpe7 summary
$ cat summary
pm_timer 0
glbl_lock 0
power_btn 0
sleep_btn 0
rtc 0
gpe0 0
gpe1 0
gpe2 0
gpe3 0
gpe4 0
gpe5 0
gpe6 0
gpe7 0
gpe8 0
gpe9 2
gpe10 0
gpe11 0
gpe12 0
gpe13 0
gpe14 0
gpe15 0
gpe16 0
gpe17 60
gpe18 0
gpe19 0
gpe20 0
gpe21 0
gpe22 0
gpe23 0
gpe24 0
gpe25 0
gpe26 0
gpe27 0
gpe28 0
gpe29 0
gpe30 0
gpe31 0
gpe_hi 0
gpe_total 62
acpi_irq 62
$ cat gpe17
60
# echo 0 > summary
# cat summary
(all zeros)
Signed-off-by: Len Brown <len.brown@intel.com>
---
drivers/acpi/bus.c | 44 +++
drivers/acpi/events/evevent.c | 2
drivers/acpi/events/evgpe.c | 2
drivers/acpi/osl.c | 232 +++++++++++++++++-
drivers/acpi/utilities/utglobal.c | 2
include/acpi/acglobal.h | 1
include/acpi/aclocal.h | 1
include/acpi/acpiosxf.h | 3
include/linux/acpi.h | 6
9 files changed, 290 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index dd49ea0..b1a8b24 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -735,6 +735,47 @@ static int __init acpi_bus_init(void)
decl_subsys(acpi, NULL, NULL);
+struct acpi_subsys_attr {
+ struct attribute attr;
+ int type;
+ int value;
+ ssize_t(*show) (struct attribute *, char *);
+ ssize_t(*store) (struct attribute *, const char *, size_t count);
+};
+#define to_acpi_attr(k) container_of(k,struct acpi_subsys_attr,attr)
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct acpi_subsys_attr *aattr = to_acpi_attr(attr);
+ if (aattr->show)
+ return aattr->show(attr, buf);
+ else
+ return -EIO;
+}
+
+static ssize_t store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_subsys_attr *aattr = to_acpi_attr(attr);
+ if (aattr->store)
+ return aattr->store(attr, buf, count);
+ else
+ return -EIO;
+}
+static struct sysfs_ops sysfs_ops = {
+ .show = show,
+ .store = store,
+};
+
+static struct kobj_type ktype_acpi = {
+ .sysfs_ops = &sysfs_ops,
+};
+
+void acpi_subsys_set_sysfs_ops(void)
+{
+ acpi_subsys.kset.kobj.ktype = &ktype_acpi;
+ acpi_subsys.kset.ktype = &ktype_acpi;
+}
+
static int __init acpi_init(void)
{
int result = 0;
@@ -749,7 +790,7 @@ static int __init acpi_init(void)
if (result < 0
printk(KERN_WARNING "%s: firmware_register error: %d\n",
__FUNCTION__, result);
-
+ acpi_subsys_set_sysfs_ops();
result = acpi_bus_init();
if (!result) {
@@ -769,4 +810,5 @@ static int __init acpi_init(void)
return result;
}
+EXPORT_SYMBOL(acpi_subsys);
subsys_initcall(acpi_init);
diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c
index a1f87b5..8bf4dbe 100644
--- a/drivers/acpi/events/evevent.c
+++ b/drivers/acpi/events/evevent.c
@@ -261,7 +261,7 @@ u32 acpi_ev_fixed_event_detect(void)
enable_bit_mask)) {
/* Found an active (signalled) event */
-
+ acpi_fixed_event_count[i]++;
int_status |= acpi_ev_fixed_event_dispatch((u32) i);
}
}
diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c
index 635ba44..76a91f4 100644
--- a/drivers/acpi/events/evgpe.c
+++ b/drivers/acpi/events/evgpe.c
@@ -621,6 +621,8 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number)
acpi_gpe_count++;
+ acpi_os_gpe_count(gpe_number);
+
/*
* If edge-triggered, clear the GPE status bit now. Note that
* level-triggered events are cleared after the GPE is serviced.
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 971eca4..21290c0 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -71,7 +71,9 @@ extern char line_buf[80];
static unsigned int acpi_irq_irq;
static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
+static u32 acpi_irq_handled;
static struct workqueue_struct *kacpid_wq;
+static void acpi_irq_stats_init(void);
static void __init acpi_request_region (struct acpi_generic_address *addr,
unsigned int length, char *desc)
@@ -276,15 +278,26 @@ acpi_os_table_override(struct acpi_table_header * existing_table,
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
- return (*acpi_irq_handler) (acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE;
+ u32 handled;
+
+ handled = (*acpi_irq_handler) (acpi_irq_context);
+
+ if (handled) {
+ acpi_irq_handled++;
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
}
+
acpi_status
acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
void *context)
{
unsigned int irq;
+ acpi_irq_stats_init();
+
/*
* Ignore the GSI from the core, and use the value in our copy of the
* FADT. It may not be the same if an interrupt source override exists
@@ -1019,6 +1032,223 @@ void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
spin_unlock_irqrestore(lockp, flags);
}
+/*
+ * ACPI IRQ counters
+ *
+ * /sys/firmware/acpi/interrupts
+ * summary -- IRQ, Fixed Event, and GPE counters
+ * gpe* -- broken-out counters for up to 32 GPEs
+ */
+
+static u32 *gpe_counters;
+static u32 gpe_counter_high; /* bucket for GPE's >= 32 */
+static u32 number_of_gpes;
+static struct attribute **all_attrs;
+
+static struct attribute_group gpe_stats_attr_group = {
+ .name = "interrupts",
+};
+static struct gpe_attr *gpe_attrs;
+
+static int count_num_gpes(void) {
+ int count = 0;
+ struct acpi_gpe_xrupt_info *gpe_xrupt_info;
+ struct acpi_gpe_block_info *gpe_block;
+ acpi_cpu_flags flags;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
+ while (gpe_xrupt_info) {
+ gpe_block = gpe_xrupt_info->gpe_block_list_head;
+ while (gpe_block) {
+ count += gpe_block->register_count *
+ ACPI_GPE_REGISTER_WIDTH;
+ gpe_block = gpe_block->next;
+ }
+ gpe_xrupt_info = gpe_xrupt_info->next;
+ }
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+
+ return count;
+}
+
+static void delete_gpe_attr_array(void) {
+
+ u32 *tmp = gpe_counters;
+
+ gpe_counters = NULL;
+ kfree (tmp);
+
+ if (gpe_attrs) {
+ int i;
+
+ for (i = 0; i < number_of_gpes; i++)
+ kfree(gpe_attrs[i].attr.name);
+
+ kfree(gpe_attrs);
+ }
+ kfree(all_attrs);
+
+ return;
+}
+
+static ssize_t interrupt_summary_show(struct attribute *not_used, char *buf)
+{
+ int i;
+ ssize_t count = 0;
+
+ /*
+ * Fixed event counters
+ */
+ count += sprintf(buf + count, "pm_timer %4d\n",
+ acpi_fixed_event_count[ACPI_EVENT_PMTIMER]);
+
+ count += sprintf(buf + count, "glbl_lock %4d\n",
+ acpi_fixed_event_count[ACPI_EVENT_GLOBAL]);
+
+ count += sprintf(buf + count, "power_btn %4d\n",
+ acpi_fixed_event_count[ACPI_EVENT_POWER_BUTTON]);
+
+ count += sprintf(buf + count, "sleep_btn %4d\n",
+ acpi_fixed_event_count[ACPI_EVENT_SLEEP_BUTTON]);
+
+ count += sprintf(buf + count, "rtc %4d\n",
+ acpi_fixed_event_count[ACPI_EVENT_RTC]);
+
+ /*
+ * General Purpose Events
+ */
+ for (i = 0; i < number_of_gpes; i++) {
+ count += sprintf(buf + count, "gpe%-3d %4d\n",
+ i, gpe_counters[i]);
+ }
+
+ count += sprintf(buf + count, "gpe_hi %4d\n",
+ gpe_counter_high);
+
+ count += sprintf(buf + count, "gpe_total %4d\n",
+ acpi_gpe_count);
+
+ count += sprintf(buf + count, "acpi_irq %4d\n",
+ acpi_irq_handled);
+
+ return count;
+}
+
+/*
+ * write anything to summary file: resets all counters
+ */
+static ssize_t interrupt_summary_reset(struct attribute *not_used, const char *buf, size_t count)
+{
+ int i;
+
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; ++i)
+ acpi_fixed_event_count[i] = 0;
+
+ for (i = 0; i < number_of_gpes; ++i)
+ gpe_counters[i] = 0;
+
+ acpi_gpe_count = 0;
+ gpe_counter_high = 0;
+ acpi_irq_handled = 0;
+
+ return count;
+}
+
+extern struct subsystem acpi_subsys;
+
+static ssize_t gpe_count_show(struct attribute *attr, char *buf) {
+
+ int index = container_of(attr, struct gpe_attr, attr) - gpe_attrs;
+
+ return sprintf(buf, "%d\n", gpe_counters[index]);
+}
+
+static ssize_t gpe_count_reset(struct attribute *attr, const char *buf, size_t size) {
+
+ int index = container_of(attr, struct gpe_attr, attr) - gpe_attrs;
+
+ gpe_counters[index] = strtoul(buf, NULL, 0);
+
+ return size;
+}
+
+static struct gpe_attr summary_attr = {
+ .attr = {.name = "summary", .mode = 0644},
+ .show = interrupt_summary_show,
+ .store = interrupt_summary_reset
+};
+
+void acpi_irq_stats_init(void) {
+
+ int i;
+
+ number_of_gpes = count_num_gpes();
+
+ all_attrs = kzalloc(sizeof(struct attribute *) * (number_of_gpes + 2),
+ GFP_KERNEL);
+ if (all_attrs == NULL)
+ return;
+
+ gpe_counters = kzalloc(sizeof(u32) * (number_of_gpes), GFP_KERNEL);
+ if (gpe_counters == NULL)
+ goto fail;
+
+ gpe_attrs = kzalloc(sizeof(struct gpe_attr) * (number_of_gpes),
+ GFP_KERNEL);
+ if (gpe_attrs == NULL)
+ goto fail;
+
+ for (i = 0; i < number_of_gpes; ++i) {
+ char buffer[10];
+ char *name;
+
+ sprintf(buffer, "gpe%d", i);
+ name = kzalloc(strlen(buffer) + 1, GFP_KERNEL);
+ if (na)me == NULL)
+ goto fail;
+ strncpy(name, buffer, strlen(buffer) + 1);
+
+ gpe_attrs[i].attr.name = name;
+ gpe_attrs[i].attr.mode = 0644;
+ gpe_attrs[i].show = gpe_count_show;
+ gpe_attrs[i].store = gpe_count_reset;
+
+ all_attrs[i] = &gpe_attrs[i].attr;
+ }
+ all_attrs[number_of_gpes] = &summary_attr.attr;
+
+ gpe_stats_attr_group.attrs = all_attrs;
+ sysfs_create_group(&cpi_subsys.kset.kobj, &gpe_stats_attr_group);
+ return;
+
+ fail:
+ delete_gpe_attr_array();
+ return;
+}
+
+static void __exit gpe_stats_exit(void) {
+ sysfs_remove_group(&acpi_subsys.kset.kobj, &gpe_stats_attr_group);
+
+ delete_gpe_attr_array();
+
+ return;
+}
+
+void acpi_os_gpe_count (u32 gpe_number) {
+
+ if (!gpe_counters)
+ return;
+
+ if (gpe_number < number_of_gpes) {
+ gpe_counters[gpe_number]++;
+ } else {
+ gpe_counter_high++;
+ }
+ return;
+}
+
#ifndef ACPI_USE_LOCAL_CACHE
/*******************************************************************************
diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c
index af33358..fbb9bec 100644
--- a/drivers/acpi/utilities/utglobal.c
+++ b/drivers/acpi/utilities/utglobal.c
@@ -673,6 +673,8 @@ void acpi_ut_init_globals(void)
/* GPE support */
acpi_gpe_count = 0;
+ for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++)
+ acpi_fixed_event_count[i] = 0;
acpi_gbl_gpe_xrupt_list_head = NULL;
acpi_gbl_gpe_fadt_blocks[0] = NULL;
acpi_gbl_gpe_fadt_blocks[1] = NULL;
diff --git a/include/acpi/acglobal.h b/include/acpi/acglobal.h
index 24c3f05..a27ba06 100644
--- a/include/acpi/acglobal.h
+++ b/include/acpi/acglobal.h
@@ -120,6 +120,7 @@ extern u32 acpi_gbl_nesting_level;
/* Event counters */
ACPI_EXTERN u32 acpi_gpe_count;
+ACPI_EXTERN u32 acpi_fixed_event_count[ACPI_NUM_FIXED_EVENTS];
/* Support for dynamic control method tracing mechanism */
diff --git a/include/acpi/aclocal.h b/include/acpi/aclocal.h
index 6f83ddb..380a8a8 100644
--- a/include/acpi/aclocal.h
+++ b/include/acpi/aclocal.h
@@ -368,6 +368,7 @@ struct acpi_gpe_event_info {
struct acpi_gpe_register_info *register_info; /* Backpointer to register info */
u8 flags; /* Misc info about this GPE */
u8 gpe_number; /* This GPE */
+ u32 count;
};
/* Information about a GPE register pair, one per each status/enable pair in an array */
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h
index 2785058..0d82437 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -180,6 +180,9 @@ acpi_os_install_interrupt_handler(u32 gsi,
acpi_status
acpi_os_remove_interrupt_handler(u32 gsi, acpi_osd_handler service_routine);
+void
+acpi_os_gpe_count (u32 gpe_number);
+
/*
* Threads and Scheduling
*/
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 8bcfaa4..5bc7c84 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -158,6 +158,12 @@ struct acpi_prt_list {
struct list_head entries;
};
+struct gpe_attr {
+ struct attribute attr;
+ int value;
+ ssize_t(*show) (struct attribute *, char *);
+ ssize_t(*store) (struct attribute *, const char *, size_t count);
+};
struct pci_dev;
int acpi_pci_irq_enable (struct pci_dev *dev);
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [RFC] ACPI interrupt statistics
2007-11-03 4:40 [RFC] ACPI interrupt statistics Len Brown
@ 2007-11-19 19:42 ` Carlos Corbacho
2007-11-24 5:34 ` Len Brown
0 siblings, 1 reply; 3+ messages in thread
From: Carlos Corbacho @ 2007-11-19 19:42 UTC (permalink / raw)
To: Len Brown; +Cc: Linux-acpi
On Saturday 03 November 2007 04:40:53 Len Brown wrote:
> For your review and testing...
>
> The patch below exports interrupt counters from within ACPI
> so that developers can better understand the cause of ACPI interrupts.
>
> My intent is to have something like this built in by default
> starting in 2.6.25. The version below applies to 2.6.21
> and is originally based on a patch by Luming Yu.
There have been a fair few changes since then, and this patch will probably
need a fair bit of work.
1) Back in 2.6.21, acpi_subsys was defined as a 'struct subsystem' in
acpi_bus.h - it's now a 'struct kset'
2) Greg KH has an RFC patch lined up that does even more work in this area -
http://lkml.org/lkml/2007/11/2
3) From my limited understanding of the area and intent, this patch seems to
be a combination of kset work + the interrupt support - could it be split
into two functionally separate patches to cover these instead?
(Basically, the only bit I'm really interested in is the
EXPORT_SYMBOL(acpi_subsys), as acpi_subsys can currently only be used from
built-in drivers, which is preventing me from converting wmi.c to a module).
-Carlos
--
E-Mail: carlos@strangeworlds.co.uk
Web: strangeworlds.co.uk
GPG Key ID: 0x23EE722D
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFC] ACPI interrupt statistics
2007-11-19 19:42 ` Carlos Corbacho
@ 2007-11-24 5:34 ` Len Brown
0 siblings, 0 replies; 3+ messages in thread
From: Len Brown @ 2007-11-24 5:34 UTC (permalink / raw)
To: Carlos Corbacho; +Cc: Linux-acpi
On Monday 19 November 2007 14:42, Carlos Corbacho wrote:
> On Saturday 03 November 2007 04:40:53 Len Brown wrote:
> > For your review and testing...
> >
> > The patch below exports interrupt counters from within ACPI
> > so that developers can better understand the cause of ACPI interrupts.
> >
> > My intent is to have something like this built in by default
> > starting in 2.6.25. The version below applies to 2.6.21
> > and is originally based on a patch by Luming Yu.
>
> There have been a fair few changes since then, and this patch will probably
> need a fair bit of work.
>
> 1) Back in 2.6.21, acpi_subsys was defined as a 'struct subsystem' in
> acpi_bus.h - it's now a 'struct kset'
yep, that is what broke it.
> 2) Greg KH has an RFC patch lined up that does even more work in this area -
> http://lkml.org/lkml/2007/11/2
yeah, i noticed that too -- we already ran amuck of them with some
other patches in the acpi tree... akpm patched it up in mm.
> 3) From my limited understanding of the area and intent, this patch seems to
> be a combination of kset work + the interrupt support - could it be split
> into two functionally separate patches to cover these instead?
the patch I sent to the list is actually a roll-up of a patch series,
so more patches will appear in the tree when it hits 2.6.25.
> (Basically, the only bit I'm really interested in is the
> EXPORT_SYMBOL(acpi_subsys), as acpi_subsys can currently only be used from
> built-in drivers, which is preventing me from converting wmi.c to a module).
hmm, okay, i didn't know that.
but feel free to pre-pend a non-wmi patch to your wmi series if you need it.
thanks,
-Len
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2007-11-24 5:34 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-03 4:40 [RFC] ACPI interrupt statistics Len Brown
2007-11-19 19:42 ` Carlos Corbacho
2007-11-24 5:34 ` Len Brown
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.