From 18901357e70ae29e3fd1c58712a6847c2ae52eae Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 23 Jul 2015 21:37:13 +0300 Subject: mei: disconnect on connection request timeout For the FW with HBM version >= 2.0 we don't need to reset the whole device in case of a particular client failing to connect, it is enough to send disconnect a request to bring the device to the stable state. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 85 ++++++++++++++++++++++++++++++-------------- drivers/misc/mei/debugfs.c | 2 ++ drivers/misc/mei/hbm.c | 4 +++ drivers/misc/mei/hw.h | 6 ++++ drivers/misc/mei/interrupt.c | 20 ++++++++++- drivers/misc/mei/mei_dev.h | 7 ++-- 6 files changed, 95 insertions(+), 29 deletions(-) (limited to 'drivers/misc') diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 9dacea7a9a60..40285e02b612 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -836,44 +836,24 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, return ret; } - - /** - * mei_cl_disconnect - disconnect host client from the me one + * __mei_cl_disconnect - disconnect host client from the me one + * internal function runtime pm has to be already acquired * * @cl: host client * - * Locking: called under "dev->device_lock" lock - * * Return: 0 on success, <0 on failure. */ -int mei_cl_disconnect(struct mei_cl *cl) +static int __mei_cl_disconnect(struct mei_cl *cl) { struct mei_device *dev; struct mei_cl_cb *cb; int rets; - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - dev = cl->dev; - cl_dbg(dev, cl, "disconnecting"); - - if (!mei_cl_is_connected(cl)) - return 0; - - if (mei_cl_is_fixed_address(cl)) { - mei_cl_set_disconnected(cl); - return 0; - } - - rets = pm_runtime_get(dev->dev); - if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(dev->dev); - cl_err(dev, cl, "rpm: get failed %d\n", rets); - return rets; - } + if (WARN_ON(!pm_runtime_active(dev->dev))) + return -EFAULT; cl->state = MEI_FILE_DISCONNECTING; @@ -910,11 +890,52 @@ out: if (!rets) cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); + mei_io_cb_free(cb); + return rets; +} + +/** + * mei_cl_disconnect - disconnect host client from the me one + * + * @cl: host client + * + * Locking: called under "dev->device_lock" lock + * + * Return: 0 on success, <0 on failure. + */ +int mei_cl_disconnect(struct mei_cl *cl) +{ + struct mei_device *dev; + int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + cl_dbg(dev, cl, "disconnecting"); + + if (!mei_cl_is_connected(cl)) + return 0; + + if (mei_cl_is_fixed_address(cl)) { + mei_cl_set_disconnected(cl); + return 0; + } + + rets = pm_runtime_get(dev->dev); + if (rets < 0 && rets != -EINPROGRESS) { + pm_runtime_put_noidle(dev->dev); + cl_err(dev, cl, "rpm: get failed %d\n", rets); + return rets; + } + + rets = __mei_cl_disconnect(cl); + cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); - mei_io_cb_free(cb); return rets; } @@ -1059,11 +1080,23 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, mutex_unlock(&dev->device_lock); wait_event_timeout(cl->wait, (cl->state == MEI_FILE_CONNECTED || + cl->state == MEI_FILE_DISCONNECT_REQUIRED || cl->state == MEI_FILE_DISCONNECT_REPLY), mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); if (!mei_cl_is_connected(cl)) { + if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { + mei_io_list_flush(&dev->ctrl_rd_list, cl); + mei_io_list_flush(&dev->ctrl_wr_list, cl); + /* ignore disconnect return valuue; + * in case of failure reset will be invoked + */ + __mei_cl_disconnect(cl); + rets = -EFAULT; + goto out; + } + /* timeout or something went really wrong */ if (!cl->status) cl->status = -EFAULT; diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index a65a1e6f386f..e39cfe6bc5bc 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -156,6 +156,8 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, dev->hbm_f_pg_supported); pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", dev->hbm_f_dc_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", + dev->hbm_f_dot_supported); } pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index d4dba639db37..07a8ea8362a3 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -774,6 +774,10 @@ static void mei_hbm_config_features(struct mei_device *dev) if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) dev->hbm_f_dc_supported = 1; + + /* disconnect on connect timeout instead of link reset */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) + dev->hbm_f_dot_supported = 1; } /** diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 815f40a604b9..e961be392fae 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -52,6 +52,12 @@ #define HBM_MINOR_VERSION_DC 0 #define HBM_MAJOR_VERSION_DC 2 +/* + * MEI version with disconnect on connection timeout support + */ +#define HBM_MINOR_VERSION_DOT 0 +#define HBM_MAJOR_VERSION_DOT 2 + /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f /* Host bus message command RESPONSE */ diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3f3405269c39..89d8e1304077 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -424,6 +424,24 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) EXPORT_SYMBOL_GPL(mei_irq_write_handler); +/** + * mei_connect_timeout - connect/disconnect timeouts + * + * @cl: host client + */ +static void mei_connect_timeout(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + + if (cl->state == MEI_FILE_CONNECTING) { + if (dev->hbm_f_dot_supported) { + cl->state = MEI_FILE_DISCONNECT_REQUIRED; + wake_up(&cl->wait); + return; + } + } + mei_reset(dev); +} /** * mei_timer - timer function. @@ -464,7 +482,7 @@ void mei_timer(struct work_struct *work) if (cl->timer_count) { if (--cl->timer_count == 0) { dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); - mei_reset(dev); + mei_connect_timeout(cl); goto out; } } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 33823f4a1cf2..8bd46cd95b7a 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -89,6 +89,7 @@ enum file_state { MEI_FILE_CONNECTED, MEI_FILE_DISCONNECTING, MEI_FILE_DISCONNECT_REPLY, + MEI_FILE_DISCONNECT_REQUIRED, MEI_FILE_DISCONNECTED, }; @@ -407,8 +408,9 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @wr_msg : the buffer for hbm control messages * * @version : HBM protocol version in use - * @hbm_f_pg_supported : hbm feature pgi protocol - * @hbm_f_dc_supported : hbm feature dynamic clients + * @hbm_f_pg_supported : hbm feature pgi protocol + * @hbm_f_dc_supported : hbm feature dynamic clients + * @hbm_f_dot_supported : hbm feature disconnect on timeout * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -503,6 +505,7 @@ struct mei_device { struct hbm_version version; unsigned int hbm_f_pg_supported:1; unsigned int hbm_f_dc_supported:1; + unsigned int hbm_f_dot_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; -- cgit v1.2.3