Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mei: revamp client disconnection flow

Split disconnected state into two parts first reception disconnect
response from the firmware and second actually setting of disconnected
state. Book keeping data are needed for processing and after firmware
disconnected the client and are cleaned when setting the disconnected
state in mei_cl_set_disconneted() function.
Add mei_cl_send_disconnect to reduce code duplication.

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tomas Winkler and committed by
Greg Kroah-Hartman
3c666182 fe292283

+134 -96
-2
drivers/misc/mei/bus.c
··· 480 480 goto out; 481 481 } 482 482 483 - cl->state = MEI_FILE_DISCONNECTING; 484 - 485 483 err = mei_cl_disconnect(cl); 486 484 if (err < 0) { 487 485 dev_err(dev->dev, "Could not disconnect from the ME client");
+121 -38
drivers/misc/mei/client.c
··· 546 546 INIT_LIST_HEAD(&cl->link); 547 547 INIT_LIST_HEAD(&cl->device_link); 548 548 cl->writing_state = MEI_IDLE; 549 + cl->state = MEI_FILE_INITIALIZING; 549 550 cl->dev = dev; 550 551 } 551 552 ··· 716 715 } 717 716 718 717 /** 718 + * mei_cl_set_disconnected - set disconnected state and clear 719 + * associated states and resources 720 + * 721 + * @cl: host client 722 + */ 723 + void mei_cl_set_disconnected(struct mei_cl *cl) 724 + { 725 + struct mei_device *dev = cl->dev; 726 + 727 + if (cl->state == MEI_FILE_DISCONNECTED || 728 + cl->state == MEI_FILE_INITIALIZING) 729 + return; 730 + 731 + cl->state = MEI_FILE_DISCONNECTED; 732 + mei_io_list_flush(&dev->ctrl_rd_list, cl); 733 + mei_io_list_flush(&dev->ctrl_wr_list, cl); 734 + cl->mei_flow_ctrl_creds = 0; 735 + cl->timer_count = 0; 736 + } 737 + 738 + /* 739 + * mei_cl_send_disconnect - send disconnect request 740 + * 741 + * @cl: host client 742 + * @cb: callback block 743 + * 744 + * Return: 0, OK; otherwise, error. 745 + */ 746 + static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) 747 + { 748 + struct mei_device *dev; 749 + int ret; 750 + 751 + dev = cl->dev; 752 + 753 + ret = mei_hbm_cl_disconnect_req(dev, cl); 754 + cl->status = ret; 755 + if (ret) { 756 + cl->state = MEI_FILE_DISCONNECT_REPLY; 757 + return ret; 758 + } 759 + 760 + list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 761 + cl->timer_count = MEI_CONNECT_TIMEOUT; 762 + 763 + return 0; 764 + } 765 + 766 + /** 767 + * mei_cl_irq_disconnect - processes close related operation from 768 + * interrupt thread context - send disconnect request 769 + * 770 + * @cl: client 771 + * @cb: callback block. 772 + * @cmpl_list: complete list. 773 + * 774 + * Return: 0, OK; otherwise, error. 775 + */ 776 + int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 777 + struct mei_cl_cb *cmpl_list) 778 + { 779 + struct mei_device *dev = cl->dev; 780 + u32 msg_slots; 781 + int slots; 782 + int ret; 783 + 784 + msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 785 + slots = mei_hbuf_empty_slots(dev); 786 + 787 + if (slots < msg_slots) 788 + return -EMSGSIZE; 789 + 790 + ret = mei_cl_send_disconnect(cl, cb); 791 + if (ret) 792 + list_move_tail(&cb->list, &cmpl_list->list); 793 + 794 + return ret; 795 + } 796 + 797 + 798 + 799 + /** 719 800 * mei_cl_disconnect - disconnect host client from the me one 720 801 * 721 802 * @cl: host client ··· 819 736 820 737 cl_dbg(dev, cl, "disconnecting"); 821 738 822 - if (cl->state != MEI_FILE_DISCONNECTING) 739 + if (!mei_cl_is_connected(cl)) 823 740 return 0; 824 741 825 742 rets = pm_runtime_get(dev->dev); ··· 829 746 return rets; 830 747 } 831 748 749 + cl->state = MEI_FILE_DISCONNECTING; 750 + 832 751 cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); 833 752 rets = cb ? 0 : -ENOMEM; 834 753 if (rets) 835 - goto free; 754 + goto out; 755 + 756 + cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 757 + list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 836 758 837 759 if (mei_hbuf_acquire(dev)) { 838 - if (mei_hbm_cl_disconnect_req(dev, cl)) { 839 - rets = -ENODEV; 760 + rets = mei_cl_send_disconnect(cl, cb); 761 + if (rets) { 840 762 cl_err(dev, cl, "failed to disconnect.\n"); 841 - goto free; 763 + goto out; 842 764 } 843 - cl->timer_count = MEI_CONNECT_TIMEOUT; 844 - mdelay(10); /* Wait for hardware disconnection ready */ 845 - list_add_tail(&cb->list, &dev->ctrl_rd_list.list); 846 - } else { 847 - cl_dbg(dev, cl, "add disconnect cb to control write list\n"); 848 - list_add_tail(&cb->list, &dev->ctrl_wr_list.list); 849 - 850 765 } 766 + 851 767 mutex_unlock(&dev->device_lock); 852 - 853 - wait_event_timeout(cl->wait, 854 - MEI_FILE_DISCONNECTED == cl->state, 855 - mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 856 - 768 + wait_event_timeout(cl->wait, cl->state == MEI_FILE_DISCONNECT_REPLY, 769 + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 857 770 mutex_lock(&dev->device_lock); 858 771 859 - if (MEI_FILE_DISCONNECTED == cl->state) { 860 - rets = 0; 861 - cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 862 - } else { 772 + rets = cl->status; 773 + if (cl->state != MEI_FILE_DISCONNECT_REPLY) { 863 774 cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); 864 775 rets = -ETIME; 865 776 } 866 777 867 - mei_io_list_flush(&dev->ctrl_rd_list, cl); 868 - mei_io_list_flush(&dev->ctrl_wr_list, cl); 869 - free: 778 + out: 779 + /* we disconnect also on error */ 780 + mei_cl_set_disconnected(cl); 781 + if (!rets) 782 + cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); 783 + 870 784 cl_dbg(dev, cl, "rpm: autosuspend\n"); 871 785 pm_runtime_mark_last_busy(dev->dev); 872 786 pm_runtime_put_autosuspend(dev->dev); ··· 952 872 mutex_unlock(&dev->device_lock); 953 873 wait_event_timeout(cl->wait, 954 874 (cl->state == MEI_FILE_CONNECTED || 955 - cl->state == MEI_FILE_DISCONNECTED), 875 + cl->state == MEI_FILE_DISCONNECT_REPLY), 956 876 mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); 957 877 mutex_lock(&dev->device_lock); 958 878 959 879 if (!mei_cl_is_connected(cl)) { 960 - cl->state = MEI_FILE_DISCONNECTED; 961 880 /* something went really wrong */ 962 881 if (!cl->status) 963 882 cl->status = -EFAULT; 964 - 965 - mei_io_list_flush(&dev->ctrl_rd_list, cl); 966 - mei_io_list_flush(&dev->ctrl_wr_list, cl); 883 + mei_cl_set_disconnected(cl); 967 884 } 968 885 969 886 rets = cl->status; ··· 1366 1289 */ 1367 1290 void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) 1368 1291 { 1369 - if (cb->fop_type == MEI_FOP_WRITE) { 1292 + switch (cb->fop_type) { 1293 + case MEI_FOP_WRITE: 1370 1294 mei_io_cb_free(cb); 1371 - cb = NULL; 1372 1295 cl->writing_state = MEI_WRITE_COMPLETE; 1373 1296 if (waitqueue_active(&cl->tx_wait)) 1374 1297 wake_up_interruptible(&cl->tx_wait); 1298 + break; 1375 1299 1376 - } else if (cb->fop_type == MEI_FOP_READ) { 1300 + case MEI_FOP_READ: 1377 1301 list_add_tail(&cb->list, &cl->rd_completed); 1378 1302 if (waitqueue_active(&cl->rx_wait)) 1379 1303 wake_up_interruptible_all(&cl->rx_wait); 1380 1304 else 1381 1305 mei_cl_bus_rx_event(cl); 1306 + break; 1382 1307 1308 + case MEI_FOP_CONNECT: 1309 + case MEI_FOP_DISCONNECT: 1310 + if (waitqueue_active(&cl->wait)) 1311 + wake_up(&cl->wait); 1312 + 1313 + break; 1314 + default: 1315 + BUG_ON(0); 1383 1316 } 1384 1317 } 1385 1318 ··· 1399 1312 * 1400 1313 * @dev: mei device 1401 1314 */ 1402 - 1403 1315 void mei_cl_all_disconnect(struct mei_device *dev) 1404 1316 { 1405 1317 struct mei_cl *cl; 1406 1318 1407 - list_for_each_entry(cl, &dev->file_list, link) { 1408 - cl->state = MEI_FILE_DISCONNECTED; 1409 - cl->mei_flow_ctrl_creds = 0; 1410 - cl->timer_count = 0; 1411 - } 1319 + list_for_each_entry(cl, &dev->file_list, link) 1320 + mei_cl_set_disconnected(cl); 1412 1321 } 1413 1322 1414 1323
+3
drivers/misc/mei/client.h
··· 105 105 106 106 bool mei_cl_is_other_connecting(struct mei_cl *cl); 107 107 int mei_cl_disconnect(struct mei_cl *cl); 108 + void mei_cl_set_disconnected(struct mei_cl *cl); 109 + int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 110 + struct mei_cl_cb *cmpl_list); 108 111 int mei_cl_connect(struct mei_cl *cl, struct file *file); 109 112 int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp); 110 113 int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
+4 -4
drivers/misc/mei/hbm.c
··· 572 572 cl_dbg(dev, cl, "hbm: disconnect response status=%d\n", rs->status); 573 573 574 574 if (rs->status == MEI_CL_DISCONN_SUCCESS) 575 - cl->state = MEI_FILE_DISCONNECTED; 575 + cl->state = MEI_FILE_DISCONNECT_REPLY; 576 576 cl->status = 0; 577 577 } 578 578 ··· 611 611 if (rs->status == MEI_CL_CONN_SUCCESS) 612 612 cl->state = MEI_FILE_CONNECTED; 613 613 else 614 - cl->state = MEI_FILE_DISCONNECTED; 614 + cl->state = MEI_FILE_DISCONNECT_REPLY; 615 615 cl->status = mei_cl_conn_status_to_errno(rs->status); 616 616 } 617 617 ··· 680 680 681 681 cl = mei_hbm_cl_find_by_cmd(dev, disconnect_req); 682 682 if (cl) { 683 - cl_dbg(dev, cl, "disconnect request received\n"); 684 - cl->state = MEI_FILE_DISCONNECTED; 683 + cl_dbg(dev, cl, "fw disconnect request received\n"); 684 + cl->state = MEI_FILE_DISCONNECTING; 685 685 cl->timer_count = 0; 686 686 687 687 cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
+1 -45
drivers/misc/mei/interrupt.c
··· 180 180 return -EMSGSIZE; 181 181 182 182 ret = mei_hbm_cl_disconnect_rsp(dev, cl); 183 - 184 - cl->state = MEI_FILE_DISCONNECTED; 185 - cl->status = 0; 183 + mei_cl_set_disconnected(cl); 186 184 mei_io_cb_free(cb); 187 185 188 186 return ret; 189 187 } 190 - 191 - 192 - 193 - /** 194 - * mei_cl_irq_disconnect - processes close related operation from 195 - * interrupt thread context - send disconnect request 196 - * 197 - * @cl: client 198 - * @cb: callback block. 199 - * @cmpl_list: complete list. 200 - * 201 - * Return: 0, OK; otherwise, error. 202 - */ 203 - static int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, 204 - struct mei_cl_cb *cmpl_list) 205 - { 206 - struct mei_device *dev = cl->dev; 207 - u32 msg_slots; 208 - int slots; 209 - 210 - msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); 211 - slots = mei_hbuf_empty_slots(dev); 212 - 213 - if (slots < msg_slots) 214 - return -EMSGSIZE; 215 - 216 - if (mei_hbm_cl_disconnect_req(dev, cl)) { 217 - cl->status = 0; 218 - cb->buf_idx = 0; 219 - list_move_tail(&cb->list, &cmpl_list->list); 220 - return -EIO; 221 - } 222 - 223 - cl->state = MEI_FILE_DISCONNECTING; 224 - cl->status = 0; 225 - cb->buf_idx = 0; 226 - list_move_tail(&cb->list, &dev->ctrl_rd_list.list); 227 - cl->timer_count = MEI_CONNECT_TIMEOUT; 228 - 229 - return 0; 230 - } 231 - 232 188 233 189 /** 234 190 * mei_cl_irq_read - processes client read related operation from the
+3 -6
drivers/misc/mei/main.c
··· 94 94 { 95 95 struct mei_cl *cl = file->private_data; 96 96 struct mei_device *dev; 97 - int rets = 0; 97 + int rets; 98 98 99 99 if (WARN_ON(!cl || !cl->dev)) 100 100 return -ENODEV; ··· 106 106 rets = mei_amthif_release(dev, file); 107 107 goto out; 108 108 } 109 - if (mei_cl_is_connected(cl)) { 110 - cl->state = MEI_FILE_DISCONNECTING; 111 - cl_dbg(dev, cl, "disconnecting\n"); 112 - rets = mei_cl_disconnect(cl); 113 - } 109 + rets = mei_cl_disconnect(cl); 110 + 114 111 mei_cl_flush_queues(cl, file); 115 112 cl_dbg(dev, cl, "removing\n"); 116 113
+2 -1
drivers/misc/mei/mei_dev.h
··· 88 88 MEI_FILE_CONNECTING, 89 89 MEI_FILE_CONNECTED, 90 90 MEI_FILE_DISCONNECTING, 91 - MEI_FILE_DISCONNECTED 91 + MEI_FILE_DISCONNECT_REPLY, 92 + MEI_FILE_DISCONNECTED, 92 93 }; 93 94 94 95 /* MEI device states */