USB: gadget: u_data_ipa: Handle usb requests allocation/free correctly

Currently USB requests are allocated during connect work for starting
endless TX and RX in BAM2BAM over IPA. But these requests are not freed
during disconnect which leads to memory leak and could result in memory
allocation failure in function drivers during next connect. Hence free USB
requests allocated during disconnect to fix memleak issue and also move
allocation of RX/TX usb requests to gbam_connect to avoid crashes due
to NULL pointer dereference.

Also extend spinlock protection to avoid the crashes during connect and
disconnect functions.

Change-Id: I4362fde2928857253d2150e4d9531cada876cd58
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
Signed-off-by: Ajay Agarwal <ajaya@codeaurora.org>
This commit is contained in:
Chandana Kishori Chiluveru 2016-12-13 14:24:22 +05:30 committed by Ajay Agarwal
parent 3162449f7d
commit 025b721004

View file

@ -93,12 +93,17 @@ static void ipa_data_endless_complete(struct usb_ep *ep,
*/
static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in)
{
unsigned long flags;
int status;
if (!port->port_usb) {
pr_err("%s(): port_usb is NULL.\n", __func__);
spin_lock_irqsave(&port->port_lock, flags);
if (!port->port_usb || (in && !port->tx_req)
|| (!in && !port->rx_req)) {
spin_unlock_irqrestore(&port->port_lock, flags);
pr_err("%s(): port_usb/req is NULL.\n", __func__);
return;
}
spin_unlock_irqrestore(&port->port_lock, flags);
if (in) {
pr_debug("%s: enqueue endless TX_REQ(IN)\n", __func__);
@ -125,12 +130,17 @@ static void ipa_data_start_endless_xfer(struct ipa_data_ch_info *port, bool in)
*/
static void ipa_data_stop_endless_xfer(struct ipa_data_ch_info *port, bool in)
{
unsigned long flags;
int status;
if (!port->port_usb) {
pr_err("%s(): port_usb is NULL.\n", __func__);
spin_lock_irqsave(&port->port_lock, flags);
if (!port->port_usb || (in && !port->tx_req)
|| (!in && !port->rx_req)) {
spin_unlock_irqrestore(&port->port_lock, flags);
pr_err("%s(): port_usb/req is NULL.\n", __func__);
return;
}
spin_unlock_irqrestore(&port->port_lock, flags);
if (in) {
pr_debug("%s: dequeue endless TX_REQ(IN)\n", __func__);
@ -253,6 +263,8 @@ static void ipa_data_disconnect_work(struct work_struct *w)
usb_bam_free_fifos(port->usb_bam_type,
port->src_connection_idx);
if (port->func_type == USB_IPA_FUNC_RMNET)
teth_bridge_disconnect(port->ipa_params.src_client);
/*
* Decrement usage count which was incremented
* upon cable connect or cable disconnect in suspended state.
@ -313,6 +325,11 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func)
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->in);
spin_lock_irqsave(&port->port_lock, flags);
if (port->tx_req) {
usb_ep_free_request(port->port_usb->in,
port->tx_req);
port->tx_req = NULL;
}
port->port_usb->in->endless = false;
}
@ -321,6 +338,11 @@ void ipa_data_disconnect(struct gadget_ipa_port *gp, enum ipa_func_type func)
spin_unlock_irqrestore(&port->port_lock, flags);
usb_ep_disable(port->port_usb->out);
spin_lock_irqsave(&port->port_lock, flags);
if (port->rx_req) {
usb_ep_free_request(port->port_usb->out,
port->rx_req);
port->rx_req = NULL;
}
port->port_usb->out->endless = false;
}
@ -367,6 +389,8 @@ static void ipa_data_connect_work(struct work_struct *w)
connect_w);
struct gadget_ipa_port *gport;
struct usb_gadget *gadget = NULL;
struct teth_bridge_connect_params connect_params;
struct teth_bridge_init_params teth_bridge_params;
u32 sps_params;
int ret;
unsigned long flags;
@ -407,34 +431,7 @@ static void ipa_data_connect_work(struct work_struct *w)
gport->ipa_consumer_ep = -1;
gport->ipa_producer_ep = -1;
if (gport->out) {
port->rx_req = usb_ep_alloc_request(gport->out, GFP_ATOMIC);
if (!port->rx_req) {
spin_unlock_irqrestore(&port->port_lock, flags);
pr_err("%s: failed to allocate rx_req\n", __func__);
return;
}
port->rx_req->context = port;
port->rx_req->complete = ipa_data_endless_complete;
port->rx_req->length = 0;
port->rx_req->no_interrupt = 1;
}
if (gport->in) {
port->tx_req = usb_ep_alloc_request(gport->in, GFP_ATOMIC);
if (!port->tx_req) {
spin_unlock_irqrestore(&port->port_lock, flags);
pr_err("%s: failed to allocate tx_req\n", __func__);
goto free_rx_req;
}
port->tx_req->context = port;
port->tx_req->complete = ipa_data_endless_complete;
port->tx_req->length = 0;
port->tx_req->no_interrupt = 1;
}
port->is_connected = true;
spin_unlock_irqrestore(&port->port_lock, flags);
/* update IPA Parameteres here. */
port->ipa_params.usb_connection_speed = gadget->speed;
@ -445,11 +442,12 @@ static void ipa_data_connect_work(struct work_struct *w)
port->ipa_params.cons_clnt_hdl = -1;
port->ipa_params.prod_clnt_hdl = -1;
if (gport->out) {
spin_unlock_irqrestore(&port->port_lock, flags);
usb_bam_alloc_fifos(port->usb_bam_type,
port->src_connection_idx);
spin_lock_irqsave(&port->port_lock, flags);
sps_params = MSM_SPS_MODE | MSM_DISABLE_WB
| MSM_PRODUCER | port->src_pipe_idx;
port->rx_req->length = 32*1024;
@ -460,15 +458,18 @@ static void ipa_data_connect_work(struct work_struct *w)
ret = msm_ep_config(gport->out, port->rx_req, GFP_ATOMIC);
if (ret) {
pr_err("msm_ep_config() failed for OUT EP\n");
spin_unlock_irqrestore(&port->port_lock, flags);
usb_bam_free_fifos(port->usb_bam_type,
port->src_connection_idx);
goto free_rx_tx_req;
goto out;
}
}
if (gport->in) {
spin_unlock_irqrestore(&port->port_lock, flags);
usb_bam_alloc_fifos(port->usb_bam_type,
port->dst_connection_idx);
spin_lock_irqsave(&port->port_lock, flags);
sps_params = MSM_SPS_MODE | MSM_DISABLE_WB |
port->dst_pipe_idx;
port->tx_req->length = 32*1024;
@ -478,10 +479,21 @@ static void ipa_data_connect_work(struct work_struct *w)
ret = msm_ep_config(gport->in, port->tx_req, GFP_ATOMIC);
if (ret) {
pr_err("msm_ep_config() failed for IN EP\n");
spin_unlock_irqrestore(&port->port_lock, flags);
goto unconfig_msm_ep_out;
}
}
if (port->func_type == USB_IPA_FUNC_RMNET) {
teth_bridge_params.client = port->ipa_params.src_client;
ret = teth_bridge_init(&teth_bridge_params);
if (ret) {
pr_err("%s:teth_bridge_init() failed\n", __func__);
spin_unlock_irqrestore(&port->port_lock, flags);
goto unconfig_msm_ep_in;
}
}
/*
* Perform below operations for Tx from Device (OUT transfer)
* 1. Connect with pipe of USB BAM with IPA BAM pipe
@ -498,17 +510,35 @@ static void ipa_data_connect_work(struct work_struct *w)
port->ipa_params.priv = rndis_qc_get_ipa_priv();
port->ipa_params.skip_ep_cfg =
rndis_qc_get_skip_ep_config();
} else if (port->func_type == USB_IPA_FUNC_RMNET) {
port->ipa_params.notify =
teth_bridge_params.usb_notify_cb;
port->ipa_params.priv =
teth_bridge_params.private_data;
port->ipa_params.reset_pipe_after_lpm =
msm_dwc3_reset_ep_after_lpm(gadget);
port->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
port->ipa_params.skip_ep_cfg =
teth_bridge_params.skip_ep_cfg;
}
spin_unlock_irqrestore(&port->port_lock, flags);
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa out failed err:%d\n", ret);
goto unconfig_msm_ep_in;
goto disconnect_usb_bam_ipa_out;
}
gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx;
spin_lock_irqsave(&port->port_lock, flags);
is_ipa_disconnected = false;
/* check if USB cable is disconnected or not */
if (!port->port_usb) {
pr_debug("%s:%d: cable is disconnected.\n",
__func__, __LINE__);
spin_unlock_irqrestore(&port->port_lock, flags);
goto disconnect_usb_bam_ipa_out;
}
gport->ipa_consumer_ep = port->ipa_params.ipa_cons_ep_idx;
}
if (gport->in) {
@ -520,22 +550,41 @@ static void ipa_data_connect_work(struct work_struct *w)
port->ipa_params.priv = rndis_qc_get_ipa_priv();
port->ipa_params.skip_ep_cfg =
rndis_qc_get_skip_ep_config();
} else if (port->func_type == USB_IPA_FUNC_RMNET) {
port->ipa_params.notify =
teth_bridge_params.usb_notify_cb;
port->ipa_params.priv =
teth_bridge_params.private_data;
port->ipa_params.reset_pipe_after_lpm =
msm_dwc3_reset_ep_after_lpm(gadget);
port->ipa_params.ipa_ep_cfg.mode.mode = IPA_BASIC;
port->ipa_params.skip_ep_cfg =
teth_bridge_params.skip_ep_cfg;
}
if (port->func_type == USB_IPA_FUNC_DPL)
port->ipa_params.dst_client = IPA_CLIENT_USB_DPL_CONS;
spin_unlock_irqrestore(&port->port_lock, flags);
ret = usb_bam_connect_ipa(port->usb_bam_type,
&port->ipa_params);
if (ret) {
pr_err("usb_bam_connect_ipa IN failed err:%d\n", ret);
goto disconnect_usb_bam_ipa_out;
}
spin_lock_irqsave(&port->port_lock, flags);
/* check if USB cable is disconnected or not */
if (!port->port_usb) {
pr_debug("%s:%d: cable is disconnected.\n",
__func__, __LINE__);
spin_unlock_irqrestore(&port->port_lock, flags);
goto disconnect_usb_bam_ipa_out;
}
gport->ipa_producer_ep = port->ipa_params.ipa_prod_ep_idx;
is_ipa_disconnected = false;
}
/* For DPL need to update_ipa_pipes to qti */
spin_unlock_irqrestore(&port->port_lock, flags);
if (port->func_type == USB_IPA_FUNC_RNDIS) {
rndis_data->prod_clnt_hdl =
port->ipa_params.prod_clnt_hdl;
@ -563,11 +612,28 @@ static void ipa_data_connect_work(struct work_struct *w)
return;
}
atomic_set(&port->pipe_connect_notified, 1);
} else {
/* For RmNet and DPL need to update_ipa_pipes to qti */
enum qti_port_type qti_port_type = port->func_type ==
USB_IPA_FUNC_RMNET ? QTI_PORT_RMNET : QTI_PORT_DPL;
gqti_ctrl_update_ipa_pipes(port->port_usb, qti_port_type,
gport->ipa_producer_ep, gport->ipa_consumer_ep);
}
if (port->func_type == USB_IPA_FUNC_RMNET) {
gqti_ctrl_update_ipa_pipes(port->port_usb, QTI_PORT_RMNET,
gport->ipa_producer_ep, gport->ipa_consumer_ep);
connect_params.ipa_usb_pipe_hdl =
port->ipa_params.prod_clnt_hdl;
connect_params.usb_ipa_pipe_hdl =
port->ipa_params.cons_clnt_hdl;
connect_params.tethering_mode =
TETH_TETHERING_MODE_RMNET;
connect_params.client_type =
port->ipa_params.src_client;
ret = teth_bridge_connect(&connect_params);
if (ret) {
pr_err("%s:teth_bridge_connect() failed\n", __func__);
goto disconnect_usb_bam_ipa_out;
}
}
pr_debug("ipa_producer_ep:%d ipa_consumer_ep:%d\n",
@ -598,26 +664,27 @@ disconnect_usb_bam_ipa_out:
is_ipa_disconnected = true;
}
unconfig_msm_ep_in:
if (gport->in)
spin_lock_irqsave(&port->port_lock, flags);
/* check if USB cable is disconnected or not */
if (port->port_usb && gport->in)
msm_ep_unconfig(port->port_usb->in);
spin_unlock_irqrestore(&port->port_lock, flags);
unconfig_msm_ep_out:
if (gport->in)
usb_bam_free_fifos(port->usb_bam_type,
port->dst_connection_idx);
if (gport->out) {
spin_lock_irqsave(&port->port_lock, flags);
/* check if USB cable is disconnected or not */
if (port->port_usb && gport->out) {
msm_ep_unconfig(port->port_usb->out);
usb_bam_free_fifos(port->usb_bam_type,
port->src_connection_idx);
}
free_rx_tx_req:
spin_unlock_irqrestore(&port->port_lock, flags);
out:
spin_lock_irqsave(&port->port_lock, flags);
port->is_connected = false;
spin_unlock_irqrestore(&port->port_lock, flags);
if (gport->in && port->tx_req)
usb_ep_free_request(gport->in, port->tx_req);
free_rx_req:
if (gport->out && port->rx_req)
usb_ep_free_request(gport->out, port->rx_req);
}
/**
@ -658,6 +725,31 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = gp;
port->gadget = gp->cdev->gadget;
if (gp->out) {
port->rx_req = usb_ep_alloc_request(gp->out, GFP_ATOMIC);
if (!port->rx_req) {
spin_unlock_irqrestore(&port->port_lock, flags);
pr_err("%s: failed to allocate rx_req\n", __func__);
goto err;
}
port->rx_req->context = port;
port->rx_req->complete = ipa_data_endless_complete;
port->rx_req->length = 0;
port->rx_req->no_interrupt = 1;
}
if (gp->in) {
port->tx_req = usb_ep_alloc_request(gp->in, GFP_ATOMIC);
if (!port->tx_req) {
pr_err("%s: failed to allocate tx_req\n", __func__);
goto free_rx_req;
}
port->tx_req->context = port;
port->tx_req->complete = ipa_data_endless_complete;
port->tx_req->length = 0;
port->tx_req->no_interrupt = 1;
}
port->src_connection_idx = src_connection_idx;
port->dst_connection_idx = dst_connection_idx;
port->usb_bam_type = usb_bam_get_bam_type(gp->cdev->gadget->name);
@ -679,6 +771,8 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
if (ret) {
pr_err("usb_ep_enable failed eptype:IN ep:%p",
port->port_usb->in);
usb_ep_free_request(port->port_usb->in, port->tx_req);
port->tx_req = NULL;
port->port_usb->in->endless = false;
goto err_usb_in;
}
@ -690,21 +784,18 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
if (ret) {
pr_err("usb_ep_enable failed eptype:OUT ep:%p",
port->port_usb->out);
usb_ep_free_request(port->port_usb->out, port->rx_req);
port->rx_req = NULL;
port->port_usb->out->endless = false;
goto err_usb_out;
}
}
if (!port->port_usb->out && !port->port_usb->in) {
pr_err("%s(): No USB endpoint enabled.\n", __func__);
ret = -EINVAL;
goto err_usb_in;
}
/* Wait for host to enable flow_control */
if (port->func_type == USB_IPA_FUNC_RNDIS) {
spin_unlock_irqrestore(&port->port_lock, flags);
ret = 0;
goto err_usb_in;
return ret;
}
/*
@ -720,9 +811,20 @@ int ipa_data_connect(struct gadget_ipa_port *gp, enum ipa_func_type func,
return ret;
err_usb_out:
if (port->port_usb->in)
if (port->port_usb->in) {
usb_ep_disable(port->port_usb->in);
port->port_usb->in->endless = false;
}
err_usb_in:
if (gp->in && port->tx_req) {
usb_ep_free_request(gp->in, port->tx_req);
port->tx_req = NULL;
}
free_rx_req:
if (gp->out && port->rx_req) {
usb_ep_free_request(gp->out, port->rx_req);
port->rx_req = NULL;
}
spin_unlock_irqrestore(&port->port_lock, flags);
err:
pr_debug("%s(): failed with error:%d\n", __func__, ret);