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 <aprathi@codeaurora.org>
tirimbino
Ajay Prathi 5 years ago committed by Gerrit - the friendly Code Review server
parent 81e1f8864e
commit 62ebe11053
  1. 122
      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;

Loading…
Cancel
Save