From e589cdf9015a48ce9515b0d8bb5d5a6f2413ee8e Mon Sep 17 00:00:00 2001 From: Vijayavardhan Vennapusa Date: Wed, 11 May 2016 13:06:37 +0530 Subject: [PATCH] USB: dwc3-msm: Perform DBM config/unconfig under spinlock protection There is a possibility of dwc3_msm_ep_queue() and msm_ep_unconfig() racing each other if suspend happens right after configured. This scenario will result in NOC error if start_xfer command gets queued after msm_ep_unconfig(). Hence fix the issue by adding spinlock protection for DBM endpoint configuration and unconfiguration. Change-Id: I3fd007647370250017c97faebffadb35afb7fc4d Signed-off-by: Vijayavardhan Vennapusa Signed-off-by: Chandana Kishori Chiluveru --- drivers/usb/dwc3/dwc3-msm.c | 40 ++++++++++++++++++------ drivers/usb/gadget/function/f_gsi.c | 4 +-- drivers/usb/gadget/function/u_bam.c | 4 +-- drivers/usb/gadget/function/u_bam_data.c | 4 +-- drivers/usb/gadget/function/u_data_ipa.c | 4 +-- drivers/usb/gadget/function/u_qdss.c | 2 +- include/linux/usb/msm_hsusb.h | 6 ++-- 7 files changed, 41 insertions(+), 23 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 7447efebed7a..beff34569392 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -640,6 +640,14 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep, return -EPERM; } + if (!mdwc->original_ep_ops[dep->number]) { + dev_err(mdwc->dev, + "ep [%s,%d] was unconfigured as msm endpoint\n", + ep->name, dep->number); + spin_unlock_irqrestore(&dwc->lock, flags); + return -EINVAL; + } + if (!request) { dev_err(mdwc->dev, "%s: request is NULL\n", __func__); spin_unlock_irqrestore(&dwc->lock, flags); @@ -647,14 +655,11 @@ static int dwc3_msm_ep_queue(struct usb_ep *ep, } if (!(request->udc_priv & MSM_SPS_MODE)) { - /* Not SPS mode, call original queue */ - dev_vdbg(mdwc->dev, "%s: not sps mode, use regular queue\n", + dev_err(mdwc->dev, "%s: sps mode is not set\n", __func__); spin_unlock_irqrestore(&dwc->lock, flags); - return (mdwc->original_ep_ops[dep->number])->queue(ep, - request, - gfp_flags); + return -EINVAL; } /* HW restriction regarding TRB size (8KB) */ @@ -1331,8 +1336,7 @@ static int dwc3_msm_gsi_ep_op(struct usb_ep *ep, * * @return int - 0 on success, negetive on error. */ -int msm_ep_config(struct usb_ep *ep, struct usb_request *request, - gfp_t gfp_flags) +int msm_ep_config(struct usb_ep *ep, struct usb_request *request) { struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; @@ -1344,23 +1348,27 @@ int msm_ep_config(struct usb_ep *ep, struct usb_request *request, bool disable_wb; bool internal_mem; bool ioc; + unsigned long flags; + spin_lock_irqsave(&dwc->lock, flags); /* Save original ep ops for future restore*/ if (mdwc->original_ep_ops[dep->number]) { dev_err(mdwc->dev, "ep [%s,%d] already configured as msm endpoint\n", ep->name, dep->number); + spin_unlock_irqrestore(&dwc->lock, flags); return -EPERM; } mdwc->original_ep_ops[dep->number] = ep->ops; /* Set new usb ops as we like */ - new_ep_ops = kzalloc(sizeof(struct usb_ep_ops), gfp_flags); + new_ep_ops = kzalloc(sizeof(struct usb_ep_ops), GFP_ATOMIC); if (!new_ep_ops) { dev_err(mdwc->dev, "%s: unable to allocate mem for new usb ep ops\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); return -ENOMEM; } (*new_ep_ops) = (*ep->ops); @@ -1368,8 +1376,10 @@ int msm_ep_config(struct usb_ep *ep, struct usb_request *request, new_ep_ops->gsi_ep_op = dwc3_msm_gsi_ep_op; ep->ops = new_ep_ops; - if (!mdwc->dbm || !request || (dep->endpoint.ep_type == EP_TYPE_GSI)) + if (!mdwc->dbm || !request || (dep->endpoint.ep_type == EP_TYPE_GSI)) { + spin_unlock_irqrestore(&dwc->lock, flags); return 0; + } /* * Configure the DBM endpoint if required. @@ -1385,9 +1395,12 @@ int msm_ep_config(struct usb_ep *ep, struct usb_request *request, if (ret < 0) { dev_err(mdwc->dev, "error %d after calling dbm_ep_config\n", ret); + spin_unlock_irqrestore(&dwc->lock, flags); return ret; } + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; } EXPORT_SYMBOL(msm_ep_config); @@ -1407,12 +1420,15 @@ int msm_ep_unconfig(struct usb_ep *ep) struct dwc3 *dwc = dep->dwc; struct dwc3_msm *mdwc = dev_get_drvdata(dwc->dev->parent); struct usb_ep_ops *old_ep_ops; + unsigned long flags; + spin_lock_irqsave(&dwc->lock, flags); /* Restore original ep ops */ if (!mdwc->original_ep_ops[dep->number]) { dev_err(mdwc->dev, "ep [%s,%d] was not configured as msm endpoint\n", ep->name, dep->number); + spin_unlock_irqrestore(&dwc->lock, flags); return -EINVAL; } old_ep_ops = (struct usb_ep_ops *)ep->ops; @@ -1424,8 +1440,10 @@ int msm_ep_unconfig(struct usb_ep *ep) * Do HERE more usb endpoint un-configurations * which are specific to MSM. */ - if (!mdwc->dbm || (dep->endpoint.ep_type == EP_TYPE_GSI)) + if (!mdwc->dbm || (dep->endpoint.ep_type == EP_TYPE_GSI)) { + spin_unlock_irqrestore(&dwc->lock, flags); return 0; + } if (dep->busy_slot == dep->free_slot && list_empty(&dep->request_list) && list_empty(&dep->req_queued)) { @@ -1446,6 +1464,8 @@ int msm_ep_unconfig(struct usb_ep *ep) dbm_event_buffer_config(mdwc->dbm, 0, 0, 0); } + spin_unlock_irqrestore(&dwc->lock, flags); + return 0; } EXPORT_SYMBOL(msm_ep_unconfig); diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 84b60ec771a4..d3e01f71fa2f 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -2245,7 +2245,7 @@ skip_string_id_alloc: if (!ep) goto fail; gsi->d_port.in_ep = ep; - msm_ep_config(gsi->d_port.in_ep, NULL, GFP_KERNEL); + msm_ep_config(gsi->d_port.in_ep, NULL); ep->driver_data = cdev; /* claim */ } @@ -2255,7 +2255,7 @@ skip_string_id_alloc: if (!ep) goto fail; gsi->d_port.out_ep = ep; - msm_ep_config(gsi->d_port.out_ep, NULL, GFP_KERNEL); + msm_ep_config(gsi->d_port.out_ep, NULL); ep->driver_data = cdev; /* claim */ } diff --git a/drivers/usb/gadget/function/u_bam.c b/drivers/usb/gadget/function/u_bam.c index bc3dd69dcb00..bbb744b33c3a 100644 --- a/drivers/usb/gadget/function/u_bam.c +++ b/drivers/usb/gadget/function/u_bam.c @@ -1395,7 +1395,7 @@ static void gbam2bam_connect_work(struct work_struct *w) d->src_pipe_idx; d->rx_req->length = 32*1024; d->rx_req->udc_priv = sps_params; - msm_ep_config(port->port_usb->out, d->rx_req, GFP_ATOMIC); + msm_ep_config(port->port_usb->out, d->rx_req); /* Configure for TX */ configure_data_fifo(d->usb_bam_type, d->dst_connection_idx, @@ -1403,7 +1403,7 @@ static void gbam2bam_connect_work(struct work_struct *w) sps_params = MSM_SPS_MODE | MSM_DISABLE_WB | d->dst_pipe_idx; d->tx_req->length = 32*1024; d->tx_req->udc_priv = sps_params; - msm_ep_config(port->port_usb->in, d->tx_req, GFP_ATOMIC); + msm_ep_config(port->port_usb->in, d->tx_req); } else { /* Configure for RX */ diff --git a/drivers/usb/gadget/function/u_bam_data.c b/drivers/usb/gadget/function/u_bam_data.c index 226af4bf1595..e479907e93ef 100644 --- a/drivers/usb/gadget/function/u_bam_data.c +++ b/drivers/usb/gadget/function/u_bam_data.c @@ -935,7 +935,7 @@ static void bam2bam_data_connect_work(struct work_struct *w) | MSM_PRODUCER | d->src_pipe_idx; d->rx_req->length = 32*1024; d->rx_req->udc_priv = sps_params; - msm_ep_config(port->port_usb->out, d->rx_req, GFP_ATOMIC); + msm_ep_config(port->port_usb->out, d->rx_req); /* Configure TX */ configure_usb_data_fifo(d->usb_bam_type, @@ -945,7 +945,7 @@ static void bam2bam_data_connect_work(struct work_struct *w) | d->dst_pipe_idx; d->tx_req->length = 32*1024; d->tx_req->udc_priv = sps_params; - msm_ep_config(port->port_usb->in, d->tx_req, GFP_ATOMIC); + msm_ep_config(port->port_usb->in, d->tx_req); } else { /* Configure RX */ diff --git a/drivers/usb/gadget/function/u_data_ipa.c b/drivers/usb/gadget/function/u_data_ipa.c index 1850aa6ea19d..51d95f6c01d4 100644 --- a/drivers/usb/gadget/function/u_data_ipa.c +++ b/drivers/usb/gadget/function/u_data_ipa.c @@ -457,7 +457,7 @@ static void ipa_data_connect_work(struct work_struct *w) configure_fifo(port->usb_bam_type, port->src_connection_idx, port->port_usb->out); - ret = msm_ep_config(gport->out, port->rx_req, GFP_ATOMIC); + ret = msm_ep_config(gport->out, port->rx_req); if (ret) { pr_err("msm_ep_config() failed for OUT EP\n"); usb_bam_free_fifos(port->usb_bam_type, @@ -475,7 +475,7 @@ static void ipa_data_connect_work(struct work_struct *w) port->tx_req->udc_priv = sps_params; configure_fifo(port->usb_bam_type, port->dst_connection_idx, gport->in); - ret = msm_ep_config(gport->in, port->tx_req, GFP_ATOMIC); + ret = msm_ep_config(gport->in, port->tx_req); if (ret) { pr_err("msm_ep_config() failed for IN EP\n"); goto unconfig_msm_ep_out; diff --git a/drivers/usb/gadget/function/u_qdss.c b/drivers/usb/gadget/function/u_qdss.c index e26f0977b9b7..42a9cda68659 100644 --- a/drivers/usb/gadget/function/u_qdss.c +++ b/drivers/usb/gadget/function/u_qdss.c @@ -99,7 +99,7 @@ static int init_data(struct usb_ep *ep) pr_debug("init_data\n"); - res = msm_ep_config(ep, qdss->endless_req, GFP_ATOMIC); + res = msm_ep_config(ep, qdss->endless_req); if (res) pr_err("msm_ep_config failed\n"); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 1eb442f8dc6c..21fddf0cbf09 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -296,8 +296,7 @@ static inline void msm_usb_irq_disable(bool disable) #endif #ifdef CONFIG_USB_DWC3_QCOM -int msm_ep_config(struct usb_ep *ep, struct usb_request *request, - gfp_t gfp_flags); +int msm_ep_config(struct usb_ep *ep, struct usb_request *request); int msm_ep_unconfig(struct usb_ep *ep); void dwc3_tx_fifo_resize_request(struct usb_ep *ep, bool qdss_enable); int msm_data_fifo_config(struct usb_ep *ep, phys_addr_t addr, u32 size, @@ -312,8 +311,7 @@ static inline int msm_data_fifo_config(struct usb_ep *ep, phys_addr_t addr, return -ENODEV; } -static inline int msm_ep_config(struct usb_ep *ep, struct usb_request *request, - gfp_t gfp_flags) +static inline int msm_ep_config(struct usb_ep *ep, struct usb_request *request) { return -ENODEV; }