diff options
author | Alexander Usyskin <alexander.usyskin@intel.com> | 2015-07-23 14:37:13 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-08-03 20:25:51 -0400 |
commit | 18901357e70ae29e3fd1c58712a6847c2ae52eae (patch) | |
tree | d0aa7e5a5ace95881a146f85c3f41047b5dd79e0 | |
parent | 70ef835c84b3b88e274a53bf80a70940ae178a91 (diff) |
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 <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/misc/mei/client.c | 85 | ||||
-rw-r--r-- | drivers/misc/mei/debugfs.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/hbm.c | 4 | ||||
-rw-r--r-- | drivers/misc/mei/hw.h | 6 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 20 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 7 |
6 files changed, 95 insertions, 29 deletions
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, | |||
836 | return ret; | 836 | return ret; |
837 | } | 837 | } |
838 | 838 | ||
839 | |||
840 | |||
841 | /** | 839 | /** |
842 | * mei_cl_disconnect - disconnect host client from the me one | 840 | * __mei_cl_disconnect - disconnect host client from the me one |
841 | * internal function runtime pm has to be already acquired | ||
843 | * | 842 | * |
844 | * @cl: host client | 843 | * @cl: host client |
845 | * | 844 | * |
846 | * Locking: called under "dev->device_lock" lock | ||
847 | * | ||
848 | * Return: 0 on success, <0 on failure. | 845 | * Return: 0 on success, <0 on failure. |
849 | */ | 846 | */ |
850 | int mei_cl_disconnect(struct mei_cl *cl) | 847 | static int __mei_cl_disconnect(struct mei_cl *cl) |
851 | { | 848 | { |
852 | struct mei_device *dev; | 849 | struct mei_device *dev; |
853 | struct mei_cl_cb *cb; | 850 | struct mei_cl_cb *cb; |
854 | int rets; | 851 | int rets; |
855 | 852 | ||
856 | if (WARN_ON(!cl || !cl->dev)) | ||
857 | return -ENODEV; | ||
858 | |||
859 | dev = cl->dev; | 853 | dev = cl->dev; |
860 | 854 | ||
861 | cl_dbg(dev, cl, "disconnecting"); | 855 | if (WARN_ON(!pm_runtime_active(dev->dev))) |
862 | 856 | return -EFAULT; | |
863 | if (!mei_cl_is_connected(cl)) | ||
864 | return 0; | ||
865 | |||
866 | if (mei_cl_is_fixed_address(cl)) { | ||
867 | mei_cl_set_disconnected(cl); | ||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | rets = pm_runtime_get(dev->dev); | ||
872 | if (rets < 0 && rets != -EINPROGRESS) { | ||
873 | pm_runtime_put_noidle(dev->dev); | ||
874 | cl_err(dev, cl, "rpm: get failed %d\n", rets); | ||
875 | return rets; | ||
876 | } | ||
877 | 857 | ||
878 | cl->state = MEI_FILE_DISCONNECTING; | 858 | cl->state = MEI_FILE_DISCONNECTING; |
879 | 859 | ||
@@ -910,11 +890,52 @@ out: | |||
910 | if (!rets) | 890 | if (!rets) |
911 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); | 891 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); |
912 | 892 | ||
893 | mei_io_cb_free(cb); | ||
894 | return rets; | ||
895 | } | ||
896 | |||
897 | /** | ||
898 | * mei_cl_disconnect - disconnect host client from the me one | ||
899 | * | ||
900 | * @cl: host client | ||
901 | * | ||
902 | * Locking: called under "dev->device_lock" lock | ||
903 | * | ||
904 | * Return: 0 on success, <0 on failure. | ||
905 | */ | ||
906 | int mei_cl_disconnect(struct mei_cl *cl) | ||
907 | { | ||
908 | struct mei_device *dev; | ||
909 | int rets; | ||
910 | |||
911 | if (WARN_ON(!cl || !cl->dev)) | ||
912 | return -ENODEV; | ||
913 | |||
914 | dev = cl->dev; | ||
915 | |||
916 | cl_dbg(dev, cl, "disconnecting"); | ||
917 | |||
918 | if (!mei_cl_is_connected(cl)) | ||
919 | return 0; | ||
920 | |||
921 | if (mei_cl_is_fixed_address(cl)) { | ||
922 | mei_cl_set_disconnected(cl); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | rets = pm_runtime_get(dev->dev); | ||
927 | if (rets < 0 && rets != -EINPROGRESS) { | ||
928 | pm_runtime_put_noidle(dev->dev); | ||
929 | cl_err(dev, cl, "rpm: get failed %d\n", rets); | ||
930 | return rets; | ||
931 | } | ||
932 | |||
933 | rets = __mei_cl_disconnect(cl); | ||
934 | |||
913 | cl_dbg(dev, cl, "rpm: autosuspend\n"); | 935 | cl_dbg(dev, cl, "rpm: autosuspend\n"); |
914 | pm_runtime_mark_last_busy(dev->dev); | 936 | pm_runtime_mark_last_busy(dev->dev); |
915 | pm_runtime_put_autosuspend(dev->dev); | 937 | pm_runtime_put_autosuspend(dev->dev); |
916 | 938 | ||
917 | mei_io_cb_free(cb); | ||
918 | return rets; | 939 | return rets; |
919 | } | 940 | } |
920 | 941 | ||
@@ -1059,11 +1080,23 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, | |||
1059 | mutex_unlock(&dev->device_lock); | 1080 | mutex_unlock(&dev->device_lock); |
1060 | wait_event_timeout(cl->wait, | 1081 | wait_event_timeout(cl->wait, |
1061 | (cl->state == MEI_FILE_CONNECTED || | 1082 | (cl->state == MEI_FILE_CONNECTED || |
1083 | cl->state == MEI_FILE_DISCONNECT_REQUIRED || | ||
1062 | cl->state == MEI_FILE_DISCONNECT_REPLY), | 1084 | cl->state == MEI_FILE_DISCONNECT_REPLY), |
1063 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); | 1085 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); |
1064 | mutex_lock(&dev->device_lock); | 1086 | mutex_lock(&dev->device_lock); |
1065 | 1087 | ||
1066 | if (!mei_cl_is_connected(cl)) { | 1088 | if (!mei_cl_is_connected(cl)) { |
1089 | if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { | ||
1090 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | ||
1091 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | ||
1092 | /* ignore disconnect return valuue; | ||
1093 | * in case of failure reset will be invoked | ||
1094 | */ | ||
1095 | __mei_cl_disconnect(cl); | ||
1096 | rets = -EFAULT; | ||
1097 | goto out; | ||
1098 | } | ||
1099 | |||
1067 | /* timeout or something went really wrong */ | 1100 | /* timeout or something went really wrong */ |
1068 | if (!cl->status) | 1101 | if (!cl->status) |
1069 | cl->status = -EFAULT; | 1102 | 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, | |||
156 | dev->hbm_f_pg_supported); | 156 | dev->hbm_f_pg_supported); |
157 | pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", | 157 | pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", |
158 | dev->hbm_f_dc_supported); | 158 | dev->hbm_f_dc_supported); |
159 | pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", | ||
160 | dev->hbm_f_dot_supported); | ||
159 | } | 161 | } |
160 | 162 | ||
161 | pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", | 163 | 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) | |||
774 | 774 | ||
775 | if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) | 775 | if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) |
776 | dev->hbm_f_dc_supported = 1; | 776 | dev->hbm_f_dc_supported = 1; |
777 | |||
778 | /* disconnect on connect timeout instead of link reset */ | ||
779 | if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) | ||
780 | dev->hbm_f_dot_supported = 1; | ||
777 | } | 781 | } |
778 | 782 | ||
779 | /** | 783 | /** |
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 @@ | |||
52 | #define HBM_MINOR_VERSION_DC 0 | 52 | #define HBM_MINOR_VERSION_DC 0 |
53 | #define HBM_MAJOR_VERSION_DC 2 | 53 | #define HBM_MAJOR_VERSION_DC 2 |
54 | 54 | ||
55 | /* | ||
56 | * MEI version with disconnect on connection timeout support | ||
57 | */ | ||
58 | #define HBM_MINOR_VERSION_DOT 0 | ||
59 | #define HBM_MAJOR_VERSION_DOT 2 | ||
60 | |||
55 | /* Host bus message command opcode */ | 61 | /* Host bus message command opcode */ |
56 | #define MEI_HBM_CMD_OP_MSK 0x7f | 62 | #define MEI_HBM_CMD_OP_MSK 0x7f |
57 | /* Host bus message command RESPONSE */ | 63 | /* 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) | |||
424 | EXPORT_SYMBOL_GPL(mei_irq_write_handler); | 424 | EXPORT_SYMBOL_GPL(mei_irq_write_handler); |
425 | 425 | ||
426 | 426 | ||
427 | /** | ||
428 | * mei_connect_timeout - connect/disconnect timeouts | ||
429 | * | ||
430 | * @cl: host client | ||
431 | */ | ||
432 | static void mei_connect_timeout(struct mei_cl *cl) | ||
433 | { | ||
434 | struct mei_device *dev = cl->dev; | ||
435 | |||
436 | if (cl->state == MEI_FILE_CONNECTING) { | ||
437 | if (dev->hbm_f_dot_supported) { | ||
438 | cl->state = MEI_FILE_DISCONNECT_REQUIRED; | ||
439 | wake_up(&cl->wait); | ||
440 | return; | ||
441 | } | ||
442 | } | ||
443 | mei_reset(dev); | ||
444 | } | ||
427 | 445 | ||
428 | /** | 446 | /** |
429 | * mei_timer - timer function. | 447 | * mei_timer - timer function. |
@@ -464,7 +482,7 @@ void mei_timer(struct work_struct *work) | |||
464 | if (cl->timer_count) { | 482 | if (cl->timer_count) { |
465 | if (--cl->timer_count == 0) { | 483 | if (--cl->timer_count == 0) { |
466 | dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); | 484 | dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); |
467 | mei_reset(dev); | 485 | mei_connect_timeout(cl); |
468 | goto out; | 486 | goto out; |
469 | } | 487 | } |
470 | } | 488 | } |
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 { | |||
89 | MEI_FILE_CONNECTED, | 89 | MEI_FILE_CONNECTED, |
90 | MEI_FILE_DISCONNECTING, | 90 | MEI_FILE_DISCONNECTING, |
91 | MEI_FILE_DISCONNECT_REPLY, | 91 | MEI_FILE_DISCONNECT_REPLY, |
92 | MEI_FILE_DISCONNECT_REQUIRED, | ||
92 | MEI_FILE_DISCONNECTED, | 93 | MEI_FILE_DISCONNECTED, |
93 | }; | 94 | }; |
94 | 95 | ||
@@ -407,8 +408,9 @@ const char *mei_pg_state_str(enum mei_pg_state state); | |||
407 | * @wr_msg : the buffer for hbm control messages | 408 | * @wr_msg : the buffer for hbm control messages |
408 | * | 409 | * |
409 | * @version : HBM protocol version in use | 410 | * @version : HBM protocol version in use |
410 | * @hbm_f_pg_supported : hbm feature pgi protocol | 411 | * @hbm_f_pg_supported : hbm feature pgi protocol |
411 | * @hbm_f_dc_supported : hbm feature dynamic clients | 412 | * @hbm_f_dc_supported : hbm feature dynamic clients |
413 | * @hbm_f_dot_supported : hbm feature disconnect on timeout | ||
412 | * | 414 | * |
413 | * @me_clients_rwsem: rw lock over me_clients list | 415 | * @me_clients_rwsem: rw lock over me_clients list |
414 | * @me_clients : list of FW clients | 416 | * @me_clients : list of FW clients |
@@ -503,6 +505,7 @@ struct mei_device { | |||
503 | struct hbm_version version; | 505 | struct hbm_version version; |
504 | unsigned int hbm_f_pg_supported:1; | 506 | unsigned int hbm_f_pg_supported:1; |
505 | unsigned int hbm_f_dc_supported:1; | 507 | unsigned int hbm_f_dc_supported:1; |
508 | unsigned int hbm_f_dot_supported:1; | ||
506 | 509 | ||
507 | struct rw_semaphore me_clients_rwsem; | 510 | struct rw_semaphore me_clients_rwsem; |
508 | struct list_head me_clients; | 511 | struct list_head me_clients; |