USB: f_qc_rndis: Add spinlock protection whereever required
Add spinlock protection in rndis_qc_command_complete() and rndis_qc_response_complete() to avoid races with disable and unbind() function calls. Otherwise it results in crash or using freed memory. Change-Id: Ida99de70a541ba12a8a8610b1c6fa717e42d865c Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org> Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
This commit is contained in:
parent
3162449f7d
commit
fc2fd4a788
1 changed files with 39 additions and 12 deletions
|
@ -472,16 +472,26 @@ static void rndis_qc_response_available(void *_rndis)
|
||||||
static void rndis_qc_response_complete(struct usb_ep *ep,
|
static void rndis_qc_response_complete(struct usb_ep *ep,
|
||||||
struct usb_request *req)
|
struct usb_request *req)
|
||||||
{
|
{
|
||||||
struct f_rndis_qc *rndis = req->context;
|
struct f_rndis_qc *rndis;
|
||||||
int status = req->status;
|
int status = req->status;
|
||||||
struct usb_composite_dev *cdev;
|
struct usb_composite_dev *cdev;
|
||||||
|
struct usb_ep *notify_ep;
|
||||||
|
|
||||||
|
spin_lock(&rndis_lock);
|
||||||
|
rndis = _rndis_qc;
|
||||||
|
if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rndis->func.config || !rndis->func.config->cdev) {
|
if (!rndis->func.config || !rndis->func.config->cdev) {
|
||||||
pr_err("%s(): cdev or config is NULL.\n", __func__);
|
pr_err("%s(): cdev or config is NULL.\n", __func__);
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cdev = rndis->func.config->cdev;
|
cdev = rndis->func.config->cdev;
|
||||||
|
|
||||||
/* after TX:
|
/* after TX:
|
||||||
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
|
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
|
||||||
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
|
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
|
||||||
|
@ -491,7 +501,7 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
|
||||||
case -ESHUTDOWN:
|
case -ESHUTDOWN:
|
||||||
/* connection gone */
|
/* connection gone */
|
||||||
atomic_set(&rndis->notify_count, 0);
|
atomic_set(&rndis->notify_count, 0);
|
||||||
break;
|
goto out;
|
||||||
default:
|
default:
|
||||||
pr_info("RNDIS %s response error %d, %d/%d\n",
|
pr_info("RNDIS %s response error %d, %d/%d\n",
|
||||||
ep->name, status,
|
ep->name, status,
|
||||||
|
@ -499,30 +509,47 @@ static void rndis_qc_response_complete(struct usb_ep *ep,
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
case 0:
|
case 0:
|
||||||
if (ep != rndis->notify)
|
if (ep != rndis->notify)
|
||||||
break;
|
goto out;
|
||||||
|
|
||||||
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
|
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
|
||||||
* notifications by resending until we're done
|
* notifications by resending until we're done
|
||||||
*/
|
*/
|
||||||
if (atomic_dec_and_test(&rndis->notify_count))
|
if (atomic_dec_and_test(&rndis->notify_count))
|
||||||
break;
|
goto out;
|
||||||
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
|
notify_ep = rndis->notify;
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
|
status = usb_ep_queue(notify_ep, req, GFP_ATOMIC);
|
||||||
if (status) {
|
if (status) {
|
||||||
atomic_dec(&rndis->notify_count);
|
spin_lock(&rndis_lock);
|
||||||
|
if (!_rndis_qc)
|
||||||
|
goto out;
|
||||||
|
atomic_dec(&_rndis_qc->notify_count);
|
||||||
DBG(cdev, "notify/1 --> %d\n", status);
|
DBG(cdev, "notify/1 --> %d\n", status);
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
out:
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rndis_qc_command_complete(struct usb_ep *ep,
|
static void rndis_qc_command_complete(struct usb_ep *ep,
|
||||||
struct usb_request *req)
|
struct usb_request *req)
|
||||||
{
|
{
|
||||||
struct f_rndis_qc *rndis = req->context;
|
struct f_rndis_qc *rndis;
|
||||||
int status;
|
int status;
|
||||||
rndis_init_msg_type *buf;
|
rndis_init_msg_type *buf;
|
||||||
u32 ul_max_xfer_size, dl_max_xfer_size;
|
u32 ul_max_xfer_size, dl_max_xfer_size;
|
||||||
|
|
||||||
|
spin_lock(&rndis_lock);
|
||||||
|
rndis = _rndis_qc;
|
||||||
|
if (!rndis || !rndis->notify || !rndis->notify->driver_data) {
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
|
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
|
||||||
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
|
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
|
@ -551,6 +578,7 @@ static void rndis_qc_command_complete(struct usb_ep *ep,
|
||||||
rndis_get_dl_max_xfer_size(rndis->params);
|
rndis_get_dl_max_xfer_size(rndis->params);
|
||||||
ipa_data_set_dl_max_xfer_size(dl_max_xfer_size);
|
ipa_data_set_dl_max_xfer_size(dl_max_xfer_size);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&rndis_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -749,13 +777,16 @@ static void rndis_qc_disable(struct usb_function *f)
|
||||||
{
|
{
|
||||||
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
|
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
|
||||||
struct usb_composite_dev *cdev = f->config->cdev;
|
struct usb_composite_dev *cdev = f->config->cdev;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (!rndis->notify->driver_data)
|
if (!rndis->notify->driver_data)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DBG(cdev, "rndis deactivated\n");
|
DBG(cdev, "rndis deactivated\n");
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rndis_lock, flags);
|
||||||
rndis_uninit(rndis->params);
|
rndis_uninit(rndis->params);
|
||||||
|
spin_unlock_irqrestore(&rndis_lock, flags);
|
||||||
ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS);
|
ipa_data_disconnect(&rndis->bam_port, USB_IPA_FUNC_RNDIS);
|
||||||
|
|
||||||
msm_ep_unconfig(rndis->bam_port.out);
|
msm_ep_unconfig(rndis->bam_port.out);
|
||||||
|
@ -1092,18 +1123,14 @@ rndis_qc_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||||
void rndis_ipa_reset_trigger(void)
|
void rndis_ipa_reset_trigger(void)
|
||||||
{
|
{
|
||||||
struct f_rndis_qc *rndis;
|
struct f_rndis_qc *rndis;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&rndis_lock, flags);
|
|
||||||
rndis = _rndis_qc;
|
rndis = _rndis_qc;
|
||||||
if (!rndis) {
|
if (!rndis) {
|
||||||
pr_err("%s: No RNDIS instance", __func__);
|
pr_err("%s: No RNDIS instance", __func__);
|
||||||
spin_unlock_irqrestore(&rndis_lock, flags);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rndis->net_ready_trigger = false;
|
rndis->net_ready_trigger = false;
|
||||||
spin_unlock_irqrestore(&rndis_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Reference in a new issue