Commit 544f9460 authored by Tomas Winkler's avatar Tomas Winkler Committed by Greg Kroah-Hartman

mei: do not run reset flow from the interrupt thread

This fixes a potential deadlock in case of a firmware
initiated reset

mei_reset has a dialog with the interrupt thread hence
it has to be run from an another work item

Most of the mei_resets were called from mei_hbm_dispatch
which is called in interrupt thread context so this
function underwent major revamp. The error code is
propagated to the interrupt thread and if needed
the reset is scheduled from there.
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 634608f2
This diff is collapsed.
......@@ -32,13 +32,13 @@ struct mei_cl;
enum mei_hbm_state {
MEI_HBM_IDLE = 0,
MEI_HBM_START,
MEI_HBM_STARTED,
MEI_HBM_ENUM_CLIENTS,
MEI_HBM_CLIENT_PROPERTIES,
MEI_HBM_STARTED,
MEI_HBM_STOP,
MEI_HBM_STOPPED,
};
void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);
static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
{
......
......@@ -469,7 +469,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
struct mei_device *dev = (struct mei_device *) dev_id;
struct mei_cl_cb complete_list;
s32 slots;
int rets;
int rets = 0;
dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
/* initialize our complete list */
......@@ -487,10 +487,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
dev->dev_state != MEI_DEV_INITIALIZING &&
dev->dev_state != MEI_DEV_POWER_DOWN &&
dev->dev_state != MEI_DEV_POWER_UP) {
dev_dbg(&dev->pdev->dev, "FW not ready.\n");
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
schedule_work(&dev->reset_work);
goto end;
}
/* check if we need to start the dev */
......@@ -500,15 +499,12 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
dev->recvd_hw_ready = true;
wake_up_interruptible(&dev->wait_hw_ready);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
} else {
dev_dbg(&dev->pdev->dev, "Reset Completed.\n");
mei_me_hw_reset_release(dev);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
goto end;
}
/* check slots available for reading */
slots = mei_count_full_read_slots(dev);
......@@ -516,21 +512,23 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* we have urgent data to send so break the read */
if (dev->wr_ext_msg.hdr.length)
break;
dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
dev_dbg(&dev->pdev->dev, "call mei_irq_read_handler.\n");
dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
rets = mei_irq_read_handler(dev, &complete_list, &slots);
if (rets)
if (rets) {
schedule_work(&dev->reset_work);
goto end;
}
}
rets = mei_irq_write_handler(dev, &complete_list);
end:
dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
mutex_unlock(&dev->device_lock);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
mei_irq_compl_handler(dev, &complete_list);
end:
dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
static const struct mei_hw_ops mei_me_hw_ops = {
......
......@@ -43,42 +43,6 @@ const char *mei_dev_state_str(int state)
#undef MEI_DEV_STATE
}
void mei_device_init(struct mei_device *dev)
{
/* setup our list array */
INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
mei_io_list_init(&dev->read_list);
mei_io_list_init(&dev->write_list);
mei_io_list_init(&dev->write_waiting_list);
mei_io_list_init(&dev->ctrl_wr_list);
mei_io_list_init(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->init_work, mei_host_client_init);
INIT_LIST_HEAD(&dev->wd_cl.link);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
mei_io_list_init(&dev->amthif_rd_complete_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;
/*
* Reserving the first client ID
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
}
EXPORT_SYMBOL_GPL(mei_device_init);
/**
* mei_start - initializes host and fw to start work.
*
......@@ -131,10 +95,15 @@ int mei_start(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_start);
/**
* mei_cancel_work. Cancel mei background jobs
*
* @dev: the device structure
*/
void mei_cancel_work(struct mei_device *dev)
{
cancel_work_sync(&dev->init_work);
cancel_work_sync(&dev->reset_work);
cancel_delayed_work(&dev->timer_work);
}
......@@ -215,11 +184,27 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->dev_state = MEI_DEV_INIT_CLIENTS;
mei_hbm_start_req(dev);
ret = mei_hbm_start_req(dev);
if (ret) {
dev_err(&dev->pdev->dev, "hbm_start failed disabling the device\n");
dev->dev_state = MEI_DEV_DISABLED;
return;
}
}
EXPORT_SYMBOL_GPL(mei_reset);
static void mei_reset_work(struct work_struct *work)
{
struct mei_device *dev =
container_of(work, struct mei_device, reset_work);
mutex_lock(&dev->device_lock);
mei_reset(dev, true);
mutex_unlock(&dev->device_lock);
}
void mei_stop(struct mei_device *dev)
{
dev_dbg(&dev->pdev->dev, "stopping the device.\n");
......@@ -243,3 +228,40 @@ EXPORT_SYMBOL_GPL(mei_stop);
void mei_device_init(struct mei_device *dev)
{
/* setup our list array */
INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
mei_io_list_init(&dev->read_list);
mei_io_list_init(&dev->write_list);
mei_io_list_init(&dev->write_waiting_list);
mei_io_list_init(&dev->ctrl_wr_list);
mei_io_list_init(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->init_work, mei_host_client_init);
INIT_WORK(&dev->reset_work, mei_reset_work);
INIT_LIST_HEAD(&dev->wd_cl.link);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
mei_io_list_init(&dev->amthif_rd_complete_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;
/*
* Reserving the first client ID
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
}
EXPORT_SYMBOL_GPL(mei_device_init);
......@@ -329,9 +329,12 @@ int mei_irq_read_handler(struct mei_device *dev,
/* HBM message */
if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) {
mei_hbm_dispatch(dev, mei_hdr);
ret = 0;
dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch.\n");
ret = mei_hbm_dispatch(dev, mei_hdr);
if (ret) {
dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch failed ret = %d\n",
ret);
goto end;
}
goto reset_slots;
}
......
......@@ -428,6 +428,7 @@ struct mei_device {
bool iamthif_canceled;
struct work_struct init_work;
struct work_struct reset_work;
/* List of bus devices */
struct list_head device_list;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment