mei: me: avoid hw access in polling when not active

Polling thread should not access hardware when not active.
Check for active state under device lock to avoid such
access when hardware is powered down.

Reviewed-by: Menachem Adin <menachem.adin@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Link: https://patch.msgid.link/20260831-cri_polling-v1-3-d927315e6598@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Alexander Usyskin 2026-08-31 14:38:33 +03:00 committed by Greg Kroah-Hartman
parent 155c978d1b
commit 528f61bdc5

View File

@ -5,6 +5,7 @@
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
@ -1290,28 +1291,15 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
}
EXPORT_SYMBOL_GPL(mei_me_irq_quick_handler);
/**
* mei_me_irq_thread_handler - function called after ISR to handle the interrupt
* processing.
*
* @irq: The irq number
* @dev_id: pointer to the device structure
*
* Return: irqreturn_t
*
*/
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
static irqreturn_t __mei_me_irq_thread_handler(struct mei_device *dev)
{
struct mei_device *dev = (struct mei_device *) dev_id;
struct list_head cmpl_list;
bool pg_blocked;
s32 slots;
u32 hcsr;
int rets = 0;
dev_dbg(&dev->dev, "function called after ISR to handle the interrupt processing.\n");
/* initialize our complete list */
mutex_lock(&dev->device_lock);
hcsr = mei_hcsr_read(dev);
me_intr_clear(dev, hcsr);
@ -1406,13 +1394,33 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
end:
dev_dbg(&dev->dev, "interrupt thread end ret = %d\n", rets);
mei_me_intr_enable(dev);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
/**
* mei_me_irq_thread_handler - function called after ISR to handle the interrupt
* processing.
*
* @irq: The irq number
* @dev_id: pointer to the device structure
*
* Return: irqreturn_t
*
*/
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
{
struct mei_device *dev = (struct mei_device *) dev_id;
dev_dbg(&dev->dev, "function called after ISR to handle the interrupt processing.\n");
guard(mutex)(&dev->device_lock);
return __mei_me_irq_thread_handler(dev);
}
EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
#define MEI_POLLING_TIMEOUT_ACTIVE 100
#define MEI_POLLING_TIMEOUT_IDLE 500
#define MEI_POLL_ACTIVE_MS 100
#define MEI_POLL_IDLE_MS 500
/**
* mei_me_polling_thread - interrupt register polling thread
@ -1420,48 +1428,59 @@ EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
* @_dev: mei device
*
* The thread monitors the interrupt source register and calls
* mei_me_irq_thread_handler() to handle the firmware
* __mei_me_irq_thread_handler() to handle the firmware
* input.
*
* The function polls in MEI_POLLING_TIMEOUT_ACTIVE timeout
* The function polls in MEI_POLL_ACTIVE_MS timeout
* in case there was an event, in idle case the polling
* time increases yet again by MEI_POLLING_TIMEOUT_ACTIVE
* up to MEI_POLLING_TIMEOUT_IDLE.
* time increases yet again by MEI_POLL_ACTIVE_MS
* up to MEI_POLL_IDLE_MS.
*
* Return: always 0
*/
int mei_me_polling_thread(void *_dev)
{
struct mei_device *dev = _dev;
irqreturn_t irq_ret;
long polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
long polling_timeout = MEI_POLL_ACTIVE_MS;
bool have_interrupt;
dev_dbg(&dev->dev, "kernel thread is running\n");
while (!kthread_should_stop()) {
struct mei_me_hw *hw = to_me_hw(dev);
u32 hcsr;
wait_event_interruptible_timeout(hw->wait_active,
hw->is_active || kthread_should_stop(),
msecs_to_jiffies(MEI_POLLING_TIMEOUT_IDLE));
if (!hw->is_active)
wait_event_interruptible(hw->wait_active,
hw->is_active || kthread_should_stop());
else
wait_event_interruptible_timeout(hw->wait_active,
hw->is_active || kthread_should_stop(),
msecs_to_jiffies(MEI_POLL_IDLE_MS));
if (kthread_should_stop())
break;
hcsr = mei_hcsr_read(dev);
if (me_intr_src(hcsr)) {
polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
irq_ret = mei_me_irq_thread_handler(1, dev);
if (irq_ret != IRQ_HANDLED)
dev_err(&dev->dev, "irq_ret %d\n", irq_ret);
} else {
/*
* Increase timeout by MEI_POLLING_TIMEOUT_ACTIVE
* up to MEI_POLLING_TIMEOUT_IDLE
*/
polling_timeout = clamp_val(polling_timeout + MEI_POLLING_TIMEOUT_ACTIVE,
MEI_POLLING_TIMEOUT_ACTIVE,
MEI_POLLING_TIMEOUT_IDLE);
scoped_guard(mutex, &dev->device_lock) {
if (!hw->is_active) {
have_interrupt = false;
} else {
hcsr = mei_hcsr_read(dev);
have_interrupt = !!me_intr_src(hcsr);
}
if (have_interrupt) {
polling_timeout = MEI_POLL_ACTIVE_MS;
dev_dbg(&dev->dev, "polling interrupt processing\n");
__mei_me_irq_thread_handler(dev);
} else {
/*
* Increase timeout by MEI_POLL_ACTIVE_MS
* up to MEI_POLL_IDLE_MS
*/
polling_timeout = clamp_val(polling_timeout + MEI_POLL_ACTIVE_MS,
MEI_POLL_ACTIVE_MS,
MEI_POLL_IDLE_MS);
}
}
schedule_timeout_interruptible(msecs_to_jiffies(polling_timeout));