[PATCH for review] ACPI: Create /sys/firmware/acpi/interrupts/ counters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Len Brown <len.brown@xxxxxxxxx>

Here is the ACPI GPE statistics patch, forward ported to grok 2.6.25's kobject changes.
(Greg, let me know if I was able to unleash the inner beauty of the kobj interface)
(yes, I know you don't like sysfs files with more than 1 value,
 but I violate that rule for only one of the 33 files:-)

thanks,
-Len
--

# ls /sys/firmware/acpi/interrupts/
gpe00  gpe02  gpe04  gpe06  gpe08  gpe0A  gpe0C  gpe0E  gpe10  gpe12  gpe14  gpe16  gpe18  gpe1A  gpe1C  gpe1E  summary
gpe01  gpe03  gpe05  gpe07  gpe09  gpe0B  gpe0D  gpe0F  gpe11  gpe13  gpe15  gpe17  gpe19  gpe1B  gpe1D  gpe1F

# cat /sys/firmware/acpi/interrupts/summary
pm_timer     0
glbl_lock    0
power_btn    0
sleep_btn    0
rtc          0
gpe00    0
gpe01    0
gpe02    0
gpe03    0
gpe04    0
gpe05    0
gpe06    0
gpe07    0
gpe08    0
gpe09    2
gpe0A    0
gpe0B    0
gpe0C    0
gpe0D    0
gpe0E    0
gpe0F    0
gpe10    0
gpe11   60
gpe12    0
gpe13    0
gpe14    0
gpe15    0
gpe16    0
gpe17    0
gpe18    0
gpe19    1
gpe1A    0
gpe1B    0
gpe1C    0
gpe1D    0
gpe1E    0
gpe1F    0
gpe_hi    0
gpe_total   63
acpi_irq    63

Inspired-by-original-patch-by: Luming Yu <luming.yu@xxxxxxxxx>
Signed-off-by: Len Brown <len.brown@xxxxxxxxx>
---
 drivers/acpi/events/evevent.c     |    2 +-
 drivers/acpi/events/evgpe.c       |    2 +
 drivers/acpi/osl.c                |   12 ++-
 drivers/acpi/system.c             |  218 +++++++++++++++++++++++++++++++++++++
 drivers/acpi/utilities/utglobal.c |    2 +
 include/acpi/acglobal.h           |    1 +
 include/acpi/aclocal.h            |    1 +
 include/acpi/acpiosxf.h           |    2 +
 include/linux/acpi.h              |    2 +
 9 files changed, 240 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c
index e412878..18514bf 100644
--- a/drivers/acpi/events/evevent.c
+++ b/drivers/acpi/events/evevent.c
@@ -259,7 +259,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 e22f4a9..515128f 100644
--- a/drivers/acpi/events/evgpe.c
+++ b/drivers/acpi/events/evgpe.c
@@ -620,6 +620,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 e53fb51..1087efe 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -332,7 +332,15 @@ 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
@@ -341,6 +349,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
 {
 	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
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index 5ffe0ea..538d154 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -40,6 +40,8 @@ ACPI_MODULE_NAME("system");
 #define ACPI_SYSTEM_CLASS		"system"
 #define ACPI_SYSTEM_DEVICE_NAME		"System"
 
+u32 acpi_irq_handled;
+
 /*
  * Make ACPICA version work as module param
  */
@@ -166,6 +168,222 @@ static int acpi_system_sysfs_init(void)
 	return 0;
 }
 
+/*
+ * ACPI IRQ counters
+ *
+ * /sys/firmware/acpi/interrupts/
+ *   summary -- IRQ, Fixed Event, and GPE counters
+ *   gpeXX -- broken-out counters for up to 32 GPEs
+ */
+
+static u32 *acpi_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 interrupt_stats_attr_group = {
+	.name = "interrupts",
+};
+static struct kobj_attribute *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 = acpi_gpe_counters;
+
+	acpi_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 kobject *kobj,
+	struct kobj_attribute *attribute, 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%02X %4d\n",
+			i, acpi_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 kobject *kobj,
+	struct kobj_attribute *attribute, 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)
+		acpi_gpe_counters[i] = 0;
+
+	acpi_gpe_count = 0;
+	gpe_counter_high = 0;
+	acpi_irq_handled = 0;
+
+	return count;
+}
+void acpi_os_gpe_count(u32 gpe_number)
+{
+
+	if (!acpi_gpe_counters)
+		return;
+
+	if (gpe_number < number_of_gpes)
+		acpi_gpe_counters[gpe_number]++;
+	else
+		gpe_counter_high++;
+
+	return;
+}
+
+static struct kobj_attribute summary_attribute =
+	__ATTR(summary, 0644, interrupt_summary_show, interrupt_summary_reset);
+
+
+static ssize_t gpe_count_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n", acpi_gpe_counters[attr - gpe_attrs]);
+}
+
+static ssize_t gpe_count_reset(struct kobject *kobj,
+	struct kobj_attribute *attr, const char *buf, size_t size)
+{
+	acpi_gpe_counters[attr - gpe_attrs] = strtoul(buf, NULL, 0);
+
+	return size;
+}
+
+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;
+
+	acpi_gpe_counters = kzalloc(sizeof(u32) * (number_of_gpes), GFP_KERNEL);
+	if (acpi_gpe_counters == NULL)
+		goto fail;
+
+	gpe_attrs = kzalloc(sizeof(struct kobj_attribute) * (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%02X", i);
+		name = kzalloc(strlen(buffer) + 1, GFP_KERNEL);
+		if (name == 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_attribute.attr;
+
+	interrupt_stats_attr_group.attrs = all_attrs;
+	sysfs_create_group(acpi_kobj, &interrupt_stats_attr_group);
+	return;
+
+fail:
+	delete_gpe_attr_array();
+	return;
+}
+
+static void __exit interrupt_stats_exit(void)
+{
+	sysfs_remove_group(acpi_kobj, &interrupt_stats_attr_group);
+
+	delete_gpe_attr_array();
+
+	return;
+}
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c
index 93ea829..9cc1c3a 100644
--- a/drivers/acpi/utilities/utglobal.c
+++ b/drivers/acpi/utilities/utglobal.c
@@ -672,6 +672,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 347a911..7b3a2ba 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 202cd42..cabdf12 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 ca882b8..4a64da5 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -181,6 +181,8 @@ 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 63f2e6e..cb911f3 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -115,7 +115,9 @@ int acpi_unmap_lsapic(int cpu);
 
 int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base);
 int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base);
+void acpi_irq_stats_init(void);
 
+extern u32 acpi_irq_handled;
 extern int acpi_mp_config;
 
 extern struct acpi_mcfg_allocation *pci_mmcfg_config;
-- 
1.5.4.18.gd0b8

-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux