summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMichael Hsu <mhsu@nvidia.com>2012-02-23 21:18:10 -0800
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-04-25 08:46:39 -0700
commit077c1bf346608626a5573313a041b0e86f58f0b6 (patch)
tree0b878960a8a9818d3d163b73b7fa1665de65ad99 /drivers
parent6b9bd6f6e4315a4413359a479d6bc3c0236600d4 (diff)
net: usb: raw-ip: Fix autopm / system suspend issues.
Add system suspend count and avoid urb activity during system suspend. Use async autopm to avoid deadlock with system suspend. Do not allocate rx urb's constantly - allocate once upon init, free rx urb upon exit. Bug 929408, 952748, 957354 Change-Id: I4ea050fc881528cf44d2039d42891e21c9df8c4e Signed-off-by: Michael Hsu <mhsu@nvidia.com> (cherry picked from commit 8bd7322127ccf6727d949f4bc1b2a4eac4b6814e) Reviewed-on: http://git-master/r/95166 Reviewed-by: Vinayak Pane <vpane@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/usb/raw_ip_net.c169
1 files changed, 146 insertions, 23 deletions
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c
index b8cc5fd8a1bd..1b3b89b3c274 100644
--- a/drivers/net/usb/raw_ip_net.c
+++ b/drivers/net/usb/raw_ip_net.c
@@ -67,8 +67,13 @@ module_param(usb_net_raw_ip_tx_debug, ulong, 0644);
MODULE_PARM_DESC(usb_net_raw_ip_tx_debug, "usb net (raw-ip) - tx debug");
struct baseband_usb {
+ /* semaphore between disconnect/suspend/resume */
+ struct semaphore sem;
+ /* instance */
int baseband_index;
+ /* network statistics */
struct net_device_stats stats;
+ /* usb context */
struct {
struct usb_driver *driver;
struct usb_device *device;
@@ -87,8 +92,11 @@ struct baseband_usb {
struct workqueue_struct *tx_workqueue;
struct work_struct tx_work;
} usb;
+ /* re-usable rx urb */
struct urb *urb_r;
void *buff;
+ /* suspend count */
+ int susp_count;
};
static struct baseband_usb *baseband_usb_net[MAX_INTFS] = { 0, 0, 0};
@@ -158,6 +166,11 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf)
baseband_usb_net[i]->usb.interface, intf);
continue;
}
+ /* acquire semaphore */
+ if (down_interruptible(&baseband_usb_net[i]->sem)) {
+ pr_err("%s: cannot acquire semaphore\n", __func__);
+ continue;
+ }
/* kill usb tx */
while ((urb = usb_get_from_anchor(&baseband_usb_net[i]->
usb.tx_urb_deferred)) != (struct urb *) 0) {
@@ -192,6 +205,8 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf)
/* mark interface as disconnected */
baseband_usb_net[i]->usb.interface
= (struct usb_interface *) 0;
+ /* release semaphore */
+ up(&baseband_usb_net[i]->sem);
}
}
@@ -200,10 +215,14 @@ static void baseband_usb_driver_disconnect(struct usb_interface *intf)
static int baseband_usb_driver_suspend(struct usb_interface *intf,
pm_message_t message)
{
- int i;
+ int i, susp_count;
pr_debug("%s intf %p\n", __func__, intf);
+ pr_debug("%s: cnt %d intf=%p &intf->dev=%p kobj=%s\n",
+ __func__, atomic_read(&intf->dev.power.usage_count),
+ intf, &intf->dev, kobject_name(&intf->dev.kobj));
+
for (i = 0; i < max_intfs; i++) {
pr_debug("[%d]\n", i);
if (!baseband_usb_net[i])
@@ -213,13 +232,47 @@ static int baseband_usb_driver_suspend(struct usb_interface *intf,
baseband_usb_net[i]->usb.interface, intf);
continue;
}
+ /* increment suspend count */
+ susp_count = (baseband_usb_net[i]->susp_count)++;
+ if (susp_count > 0) {
+ pr_debug("%s: susp_count %d > 0 (already suspended)\n",
+ __func__, susp_count);
+ continue;
+ }
+ if (susp_count < 0) {
+ pr_debug("%s: susp_count %d < 0 (ILLEGAL VALUE)\n",
+ __func__, susp_count);
+ baseband_usb_net[i]->susp_count = 0;
+ continue;
+ }
+ pr_debug("%s: susp_count = %d (suspending...)\n",
+ __func__, susp_count);
+ /* acquire semaphore */
+ if (down_interruptible(&baseband_usb_net[i]->sem)) {
+ pr_err("%s: cannot acquire semaphore\n", __func__);
+ continue;
+ }
/* kill usb rx */
if (!baseband_usb_net[i]->usb.rx_urb) {
pr_debug("rx_usb already killed\n");
+ up(&baseband_usb_net[i]->sem);
continue;
}
+ pr_debug("%s: kill rx_urb {\n",__func__);
usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ pr_debug("%s: kill rx_urb }\n",__func__);
baseband_usb_net[i]->usb.rx_urb = (struct urb *) 0;
+ /* cancel tx urb work (will restart after resume) */
+ if (!baseband_usb_net[i]->usb.tx_workqueue) {
+ pr_err("%s: !tx_workqueue\n", __func__);
+ up(&baseband_usb_net[i]->sem);
+ continue;
+ }
+ pr_debug("%s: cancel_work_sync {\n",__func__);
+ cancel_work_sync(&baseband_usb_net[i]->usb.tx_work);
+ pr_debug("%s: cancel_work_sync }\n",__func__);
+ /* release semaphore */
+ up(&baseband_usb_net[i]->sem);
}
return 0;
@@ -227,10 +280,14 @@ static int baseband_usb_driver_suspend(struct usb_interface *intf,
static int baseband_usb_driver_resume(struct usb_interface *intf)
{
- int i, err;
+ int i, err, susp_count;
pr_debug("%s intf %p\n", __func__, intf);
+ pr_debug("%s: cnt %d intf=%p &intf->dev=%p kobj=%s\n",
+ __func__, atomic_read(&intf->dev.power.usage_count),
+ intf, &intf->dev, kobject_name(&intf->dev.kobj));
+
for (i = 0; i < max_intfs; i++) {
pr_debug("[%d]\n", i);
if (!baseband_usb_net[i])
@@ -240,16 +297,48 @@ static int baseband_usb_driver_resume(struct usb_interface *intf)
baseband_usb_net[i]->usb.interface, intf);
continue;
}
+ /* decrement suspend count */
+ susp_count = --(baseband_usb_net[i]->susp_count);
+ if (susp_count > 0) {
+ pr_debug("%s: susp_count %d > 0 (not resuming yet)\n",
+ __func__, susp_count);
+ continue;
+ }
+ if (susp_count < 0) {
+ pr_debug("%s: susp_count %d < 0 (ILLEGAL VALUE)\n",
+ __func__, susp_count);
+ baseband_usb_net[i]->susp_count = 0;
+ continue;
+ }
+ pr_debug("%s: susp_count = %d (resuming...)\n",
+ __func__, susp_count);
+ /* acquire semaphore */
+ if (down_interruptible(&baseband_usb_net[i]->sem)) {
+ pr_err("%s: cannot acquire semaphore\n", __func__);
+ continue;
+ }
/* start usb rx */
if (baseband_usb_net[i]->usb.rx_urb) {
pr_debug("rx_usb already exists\n");
+ up(&baseband_usb_net[i]->sem);
continue;
}
err = usb_net_raw_ip_rx_urb_submit(baseband_usb_net[i]);
if (err < 0) {
pr_err("submit rx failed - err %d\n", err);
+ up(&baseband_usb_net[i]->sem);
continue;
}
+ /* restart tx urb work (cancelled in suspend) */
+ if (!baseband_usb_net[i]->usb.tx_workqueue) {
+ pr_err("%s: !tx_workqueue\n", __func__);
+ up(&baseband_usb_net[i]->sem);
+ continue;
+ }
+ queue_work(baseband_usb_net[i]->usb.tx_workqueue,
+ &baseband_usb_net[i]->usb.tx_work);
+ /* release semaphore */
+ up(&baseband_usb_net[i]->sem);
}
return 0;
@@ -361,6 +450,9 @@ struct baseband_usb *baseband_usb_open(int index,
if (!usb)
return (struct baseband_usb *) 0;
+ /* create semaphores */
+ sema_init(&usb->sem, 1);
+
/* open usb driver */
sprintf(baseband_usb_driver_name[index],
"baseband_usb_%x_%x_%x",
@@ -424,6 +516,9 @@ void baseband_usb_close(struct baseband_usb *usb)
pr_debug("close usb driver }\n");
}
+ /* destroy semaphores */
+ memset(&usb->sem, 0, sizeof(usb->sem));
+
/* free baseband usb structure */
kfree(usb);
@@ -485,12 +580,22 @@ static netdev_tx_t baseband_usb_netdev_start_xmit(
}
usb = baseband_usb_net[i];
+ /* autoresume if suspended */
+ if (usb->usb.interface) {
+ usb_autopm_get_interface_async(usb->usb.interface);
+ } else {
+ pr_err("%s: tx get interface error\n", __func__);
+ netif_stop_queue(dev);
+ usb->stats.tx_errors++;
+ return NETDEV_TX_BUSY;
+ }
+
/* submit tx urb */
err = usb_net_raw_ip_tx_urb_submit(usb, skb);
if (err < 0) {
- pr_err("tx urb submit error\n");
- usb->stats.tx_errors++;
+ pr_err("%s: tx urb submit error\n", __func__);
netif_stop_queue(dev);
+ usb->stats.tx_errors++;
return NETDEV_TX_BUSY;
}
@@ -600,14 +705,15 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
switch (urb->status) {
case 0:
break;
- case -ENOENT:
- /* fall through */
case -ESHUTDOWN:
/* fall through */
- case -EPROTO:
pr_info("%s: rx urb %p - link shutdown %d\n",
__func__, urb, urb->status);
goto err_exit;
+ case -EPROTO:
+ pr_info("%s: rx urb %p - link shutdown %d EPROTO\n",
+ __func__, urb, urb->status);
+ goto err_exit;
default:
pr_info("%s: rx urb %p - status %d\n",
__func__, urb, urb->status);
@@ -615,7 +721,7 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
}
/* put rx urb data in rx buffer */
- if (urb->actual_length) {
+ if (urb->actual_length > 0) {
pr_debug("usb_net_raw_ip_rx_urb_comp - "
"urb->actual_length %d\n", urb->actual_length);
/* allocate skb with space for
@@ -664,6 +770,10 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
/* mark rx urb complete */
usb->usb.rx_urb = (struct urb *) 0;
+ /* do not submit urb if interface is suspending */
+ if (urb->status == -ENOENT)
+ return;
+
/* submit next rx urb */
usb_net_raw_ip_rx_urb_submit(usb);
return;
@@ -745,25 +855,28 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
pr_err("%s: !usb\n", __func__);
return -EINVAL;
}
- if (!skb) {
- pr_err("%s: !skb\n", __func__);
- return -EINVAL;
- }
if (!usb->usb.interface) {
pr_err("usb interface disconnected - not submitting tx urb\n");
return -EINVAL;
}
+ if (!skb) {
+ pr_err("%s: !skb\n", __func__);
+ usb_autopm_put_interface_async(usb->usb.interface);
+ return -EINVAL;
+ }
/* allocate urb */
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
pr_err("usb_alloc_urb() failed\n");
+ usb_autopm_put_interface_async(usb->usb.interface);
return -ENOMEM;
}
buf = kzalloc(skb->len - 14, GFP_ATOMIC);
if (!buf) {
pr_err("usb buffer kzalloc() failed\n");
usb_free_urb(urb);
+ usb_autopm_put_interface_async(usb->usb.interface);
return -ENOMEM;
}
err = skb_copy_bits(skb, 14, buf, skb->len - 14);
@@ -771,6 +884,7 @@ static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
pr_err("skb_copy_bits() failed - %d\n", err);
kfree(buf);
usb_free_urb(urb);
+ usb_autopm_put_interface_async(usb->usb.interface);
return err;
}
usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
@@ -814,18 +928,23 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work)
return;
}
+ /* check if suspended */
+ if (usb->susp_count > 0) {
+ pr_info("%s: usb->susp_count %d > 0 (suspended)\n",
+ __func__, usb->susp_count);
+ return;
+ }
+
/* submit queued tx urb(s) */
while ((urb = usb_get_from_anchor(&usb->usb.tx_urb_deferred))
!= (struct urb *) 0) {
/* decrement count from usb_get_from_anchor() */
usb_free_urb(urb);
-
- /* autoresume before tx */
- usb_mark_last_busy(usb->usb.device);
- err = usb_autopm_get_interface(usb->usb.interface);
- if (err < 0) {
- pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
- __func__, usb->usb.interface, err);
+ /* check if usb interface disconnected */
+ if (!usb->usb.interface) {
+ pr_err("%s: not submitting tx urb %p"
+ " - interface disconnected\n",
+ __func__, urb);
if (urb->transfer_buffer) {
kfree(urb->transfer_buffer);
urb->transfer_buffer = (void *) 0;
@@ -834,17 +953,20 @@ static void usb_net_raw_ip_tx_urb_work(struct work_struct *work)
usb->stats.tx_errors++;
continue;
}
+ /* autoresume before tx */
+ usb_mark_last_busy(usb->usb.device);
/* submit tx urb */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
pr_err("%s: usb_submit_urb(%p) failed - err %d\n",
__func__, urb, err);
- usb_autopm_put_interface(usb->usb.interface);
+ usb_autopm_put_interface_async(usb->usb.interface);
if (urb->transfer_buffer) {
kfree(urb->transfer_buffer);
urb->transfer_buffer = (void *) 0;
}
usb_free_urb(urb);
+ usb->stats.tx_errors++;
continue;
}
/* free tx urb
@@ -882,6 +1004,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
case -EPROTO:
pr_info("%s: tx urb %p - link shutdown %d\n",
__func__, urb, urb->status);
+ usb_autopm_put_interface_async(usb->usb.interface);
goto err_exit;
default:
pr_info("%s: tx urb %p - status %d\n",
@@ -894,6 +1017,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
usb->stats.tx_packets++;
usb->stats.tx_bytes += urb->transfer_buffer_length;
}
+
/* autosuspend after tx completed */
if (!usb->usb.interface) {
pr_err("%s: usb interface disconnected"
@@ -901,7 +1025,7 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
__func__);
goto err_exit;
}
- usb_autopm_put_interface(usb->usb.interface);
+ usb_autopm_put_interface_async(usb->usb.interface);
err_exit:
/* free tx urb transfer buffer */
@@ -909,7 +1033,6 @@ err_exit:
kfree(urb->transfer_buffer);
urb->transfer_buffer = (void *) 0;
}
-
pr_debug("usb_net_raw_ip_tx_urb_comp }\n");
}
@@ -932,6 +1055,7 @@ static int usb_net_raw_ip_init(void)
err = -1;
goto error_exit;
}
+ init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred);
/* register network device */
usb_net_raw_ip_dev[i] = alloc_netdev(0,
BASEBAND_USB_NET_DEV_NAME,
@@ -964,7 +1088,6 @@ static int usb_net_raw_ip_init(void)
goto error_exit;
}
/* start usb tx */
- init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred);
sprintf(name, "raw_ip_tx_wq-%d",
baseband_usb_net[i]->baseband_index);
baseband_usb_net[i]->usb.tx_workqueue