From 62ebe11053ba953be2a9ff24cecd95175df554b2 Mon Sep 17 00:00:00 2001 From: Ajay Prathi Date: Fri, 24 Apr 2020 18:37:44 +0530 Subject: [PATCH] mhi: netdev: Open mhi channels based on state notifications from host MHI channels are opened at mhi start up, before host opens the channels which results in race conditions, when mhi device is switching on or off, and if mhi netdev uses the channel before the channel is initialised from host results in crash. Add code to register for mhi channel state change, open mhi channels and create network interface when the state change notification comes from host to fix the race condition. Change-Id: Id497d8fe6721709369aa57802c399bcd154b19b6 Signed-off-by: Ajay Prathi --- drivers/platform/msm/mhi_dev/mhi_dev_net.c | 122 +++++++++++++++++++-- 1 file changed, 112 insertions(+), 10 deletions(-) diff --git a/drivers/platform/msm/mhi_dev/mhi_dev_net.c b/drivers/platform/msm/mhi_dev/mhi_dev_net.c index 194b3ddde22e..d01e15711e18 100644 --- a/drivers/platform/msm/mhi_dev/mhi_dev_net.c +++ b/drivers/platform/msm/mhi_dev/mhi_dev_net.c @@ -516,7 +516,7 @@ net_dev_alloc_fail: return -ENOMEM; } -static int mhi_dev_net_open_channels(struct mhi_dev_net_client *client) +static int mhi_dev_net_open_chan_create_netif(struct mhi_dev_net_client *client) { int rc = 0; int ret = 0; @@ -626,16 +626,79 @@ static int mhi_dev_net_rgstr_client(struct mhi_dev_net_client *client, int idx) spin_lock_init(&client->rd_lock); mhi_dev_net_log(MHI_INFO, "Registering out %d, In %d channels\n", client->out_chan, client->in_chan); + return 0; +} + +static int mhi_dev_net_dergstr_client + (struct mhi_dev_net_client *client) +{ + mutex_destroy(&client->in_chan_lock); + mutex_destroy(&client->out_chan_lock); - /* Open IN and OUT channels for Network client*/ - mhi_dev_net_open_channels(client); return 0; } +static void mhi_dev_net_state_cb(struct mhi_dev_client_cb_data *cb_data) +{ + struct mhi_dev_net_client *mhi_client; + uint32_t info_in_ch = 0, info_out_ch = 0; + int ret; + + if (!cb_data || !cb_data->user_data) { + mhi_dev_net_log(MHI_ERROR, "invalid input received\n"); + return; + } + mhi_client = cb_data->user_data; + + ret = mhi_ctrl_state_info(mhi_client->in_chan, &info_in_ch); + if (ret) { + mhi_dev_net_log(MHI_ERROR, + "Failed to obtain in_channel %d state\n", + mhi_client->in_chan); + return; + } + ret = mhi_ctrl_state_info(mhi_client->out_chan, &info_out_ch); + if (ret) { + mhi_dev_net_log(MHI_ERROR, + "Failed to obtain out_channel %d state\n", + mhi_client->out_chan); + return; + } + mhi_dev_net_log(MHI_MSG_VERBOSE, "in_channel :%d, state :%d\n", + mhi_client->in_chan, info_in_ch); + mhi_dev_net_log(MHI_MSG_VERBOSE, "out_channel :%d, state :%d\n", + mhi_client->out_chan, info_out_ch); + if (info_in_ch == MHI_STATE_CONNECTED && + info_out_ch == MHI_STATE_CONNECTED) { + /** + * Open IN and OUT channels for Network client + * and create Network Interface. + */ + ret = mhi_dev_net_open_chan_create_netif(mhi_client); + if (ret) { + mhi_dev_net_log(MHI_ERROR, + "Failed to open channels\n"); + return; + } + } else if (info_in_ch == MHI_STATE_DISCONNECTED || + info_out_ch == MHI_STATE_DISCONNECTED) { + if (mhi_client->dev != NULL) { + netif_stop_queue(mhi_client->dev); + unregister_netdev(mhi_client->dev); + mhi_dev_close_channel(mhi_client->out_handle); + mhi_dev_close_channel(mhi_client->in_handle); + atomic_set(&mhi_client->tx_enabled, 0); + atomic_set(&mhi_client->rx_enabled, 0); + free_netdev(mhi_client->dev); + mhi_client->dev = NULL; + } + } +} + int mhi_dev_net_interface_init(void) { - int ret_val = 0; - int index = 0; + int ret_val = 0, index = 0; + bool out_channel_started = false; struct mhi_dev_net_client *mhi_net_client = NULL; if (mhi_net_ctxt.client_handle) { @@ -650,9 +713,12 @@ int mhi_dev_net_interface_init(void) mhi_net_ipc_log = ipc_log_context_create(MHI_NET_IPC_PAGES, "mhi-net", 0); - if (mhi_net_ipc_log == NULL) + if (!mhi_net_ipc_log) { mhi_dev_net_log(MHI_DBG, "Failed to create IPC logging for mhi_dev_net\n"); + kfree(mhi_net_client); + return -ENOMEM; + } mhi_net_ctxt.client_handle = mhi_net_client; if (mhi_net_ctxt.pdev) @@ -685,13 +751,49 @@ int mhi_dev_net_interface_init(void) "Failed to reg client %d ret 0\n", ret_val); goto client_register_fail; } - return ret_val; + ret_val = mhi_register_state_cb(mhi_dev_net_state_cb, + mhi_net_client, MHI_CLIENT_IP_SW_4_OUT); + /* -EEXIST indicates success and channel is already open */ + if (ret_val == -EEXIST) + out_channel_started = true; + else if (ret_val < 0) + goto register_state_cb_fail; + + ret_val = mhi_register_state_cb(mhi_dev_net_state_cb, + mhi_net_client, MHI_CLIENT_IP_SW_4_IN); + /* -EEXIST indicates success and channel is already open */ + if (ret_val == -EEXIST) { + /** + * If both in and out channels were opened by host at the + * time of registration proceed with opening channels and + * create network interface from device side. + * if the channels are not opened at the time of registration + * we will get a call back notification mhi_dev_net_state_cb() + * and proceed to open channels and create network interface + * with mhi_dev_net_open_chan_create_netif(). + */ + ret_val = 0; + if (out_channel_started) { + ret_val = mhi_dev_net_open_chan_create_netif + (mhi_net_client); + if (ret_val < 0) { + mhi_dev_net_log(MHI_ERROR, + "Failed to open channels\n"); + goto channel_open_fail; + } + } + } else if (ret_val < 0) { + goto register_state_cb_fail; + } -channel_init_fail: - kfree(mhi_net_client); - kfree(mhi_net_ipc_log); return ret_val; + +channel_open_fail: +register_state_cb_fail: + mhi_dev_net_dergstr_client(mhi_net_client); client_register_fail: +channel_init_fail: + destroy_workqueue(mhi_net_client->pending_pckt_wq); kfree(mhi_net_client); kfree(mhi_net_ipc_log); return ret_val;