[char-misc-next 3/3 V2] mei: disconnect on connection request timeout

From: Tomas Winkler
Date: Wed Jul 29 2015 - 08:01:33 EST


From: Alexander Usyskin <alexander.usyskin@xxxxxxxxx>

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
sufficient to send a disconnect request to bring the device to the
stable state.

Signed-off-by: Alexander Usyskin <alexander.usyskin@xxxxxxxxx>
Signed-off-by: Tomas Winkler <tomas.winkler@xxxxxxxxx>
---
V2: 1. Remove bougous check on pm_runtime_active that prevented
disconnection from a client
2. Fix the commit message

drivers/misc/mei/client.c | 84 ++++++++++++++++++++++++++++++--------------
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, 93 insertions(+), 30 deletions(-)

diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 9dacea7a9a60..5c1b0cf92e23 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -836,45 +836,22 @@ 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;
- }
-
cl->state = MEI_FILE_DISCONNECTING;

cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
@@ -910,11 +887,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 +1077,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 4b3283ebf239..e9b0d7fc716c 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -776,6 +776,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 1185ec56ee20..b4012b4e0003 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;
--
2.4.3

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