[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20250923134954.3016697-1-crajank@nvidia.com>
Date: Tue, 23 Sep 2025 16:49:54 +0300
From: Ciju Rajan K <crajank@...dia.com>
To: <hdegoede@...hat.com>, <ilpo.jarvinen@...ux.intel.com>,
<tglx@...utronix.de>, <andriy.shevchenko@...ux.intel.com>,
<linux-kernel@...r.kernel.org>
CC: <christophe.jaillet@...adoo.fr>, <platform-driver-x86@...r.kernel.org>,
<vadimp@...dia.com>, Ciju Rajan K <crajank@...dia.com>
Subject: [PATCH platform-next v3 1/1] [PATCH platform-next] platform/mellanox: mlxreg-hotplug: Add support for handling interrupt storm
In case of broken hardware, it is possible that broken device will
flood interrupt handler with false events. For example, if fan or
power supply has damaged presence pin, it will cause permanent
generation of plugged in / plugged out events. As a result, interrupt
handler will consume a lot of CPU resources and will keep raising
"UDEV" events to the user space.
This patch provides a mechanism to detect device causing interrupt
flooding and mask interrupt for this specific device, to isolate
from interrupt handling flow. Use the following criteria: if the
specific interrupt was generated 'N' times during 'T' seconds,
such device is to be considered as broken and will be closed for
getting interrupts. User will be notified through the log error
and will be instructed to replace broken device.
Add fields for interrupt storm handling.
Extend structure mlxreg_core_data with the following fields:
'wmark_cntr' - interrupt storm counter.
'wmark_window' - time window to count interrupts to check for storm.
Extend structure mlxreg_core_item with the following field:
'storming_bits' - interrupt storming bits mask.
Reviewed-by: Vadim Pasternak <vadimp@...dia.com>
Signed-off-by: Ciju Rajan K <crajank@...dia.com>
--
v2->v3
Comments pointed out by Ilpo:
- Merged variable declarations and implementation to a single patch.
- Moved the wmark_cntr increments to else block to allow recalculation
of the time window.
v0->v2
Comments pointed out by Ilpo:
- Renamed the macro MLXREG_HOTPLUG_WM_WINDOW to MLXREG_HOTPLUG_WM_WINDOW_MS to indicate milli seconds
- Removed the timestamp stored in wmark_high_ts and used jiffies directly
- Calculating the time window and storing it in the new variable wmark_window.
- Removed the variables wmark_low_ts and wmark_high_ts
- Used continue statement inside the time window check
Changes added by Ciju:
- Renamed variable wmark_low_cntr to wmark_cntr
- Fixed the time window to check for interrupt storm
---
drivers/platform/mellanox/mlxreg-hotplug.c | 33 ++++++++++++++++++++--
include/linux/platform_data/mlxreg.h | 6 ++++
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c
index d246772aafd6..f72eaaa14aba 100644
--- a/drivers/platform/mellanox/mlxreg-hotplug.c
+++ b/drivers/platform/mellanox/mlxreg-hotplug.c
@@ -11,6 +11,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/platform_data/mlxreg.h>
#include <linux/platform_device.h>
@@ -30,6 +31,11 @@
#define MLXREG_HOTPLUG_ATTRS_MAX 128
#define MLXREG_HOTPLUG_NOT_ASSERT 3
+/* Interrupt storm definitions */
+#define MLXREG_HOTPLUG_WM_COUNTER 100
+/* Time window in milliseconds */
+#define MLXREG_HOTPLUG_WM_WINDOW_MS 3000
+
/**
* struct mlxreg_hotplug_priv_data - platform private data:
* @irq: platform device interrupt number;
@@ -366,11 +372,34 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
for_each_set_bit(bit, &asserted, 8) {
int pos;
+ /* Skip already marked storming bit. */
+ if (item->storming_bits & BIT(bit))
+ continue;
+
pos = mlxreg_hotplug_item_label_index_get(item->mask, bit);
if (pos < 0)
goto out;
data = item->data + pos;
+
+ /* Interrupt storm handling logic. */
+ if (data->wmark_cntr == 0) {
+ data->wmark_window = jiffies +
+ msecs_to_jiffies(MLXREG_HOTPLUG_WM_WINDOW_MS);
+ }
+ if (data->wmark_cntr >= MLXREG_HOTPLUG_WM_COUNTER - 1) {
+ if (time_after(data->wmark_window, jiffies)) {
+ dev_err(priv->dev,
+ "Storming bit %d (label: %s) - interrupt masked permanently. Replace broken HW.",
+ bit, data->label);
+ /* Mark bit as storming. */
+ item->storming_bits |= BIT(bit);
+ continue;
+ }
+ data->wmark_cntr = 0;
+ } else {
+ data->wmark_cntr++;
+ }
if (regval & BIT(bit)) {
if (item->inversed)
mlxreg_hotplug_device_destroy(priv, data, item->kind);
@@ -390,9 +419,9 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
if (ret)
goto out;
- /* Unmask event. */
+ /* Unmask event, exclude storming bits. */
ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_MASK_OFF,
- item->mask);
+ item->mask & ~item->storming_bits);
out:
if (ret)
diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h
index f6cca7a035c7..453c8dfd7eb9 100644
--- a/include/linux/platform_data/mlxreg.h
+++ b/include/linux/platform_data/mlxreg.h
@@ -131,6 +131,8 @@ struct mlxreg_hotplug_device {
* @regnum: number of registers occupied by multi-register attribute;
* @slot: slot number, at which device is located;
* @secured: if set indicates that entry access is secured;
+ * @wmark_cntr: interrupt storm counter;
+ * @wmark_window: time window to count interrupts to check for storm;
*/
struct mlxreg_core_data {
char label[MLXREG_CORE_LABEL_MAX_SIZE];
@@ -151,6 +153,8 @@ struct mlxreg_core_data {
u8 regnum;
u8 slot;
u8 secured;
+ unsigned int wmark_cntr;
+ unsigned long wmark_window;
};
/**
@@ -167,6 +171,7 @@ struct mlxreg_core_data {
* @ind: element's index inside the group;
* @inversed: if 0: 0 for signal status is OK, if 1 - 1 is OK;
* @health: true if device has health indication, false in other case;
+ * @storming_bits: interrupt storming bits mask;
*/
struct mlxreg_core_item {
struct mlxreg_core_data *data;
@@ -180,6 +185,7 @@ struct mlxreg_core_item {
u8 ind;
u8 inversed;
u8 health;
+ u32 storming_bits;
};
/**
--
2.47.2
Powered by blists - more mailing lists