diff options
Diffstat (limited to 'drivers/net/usb/usbnet.c')
| -rw-r--r-- | drivers/net/usb/usbnet.c | 23 | 
1 files changed, 16 insertions, 7 deletions
| diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index fae0fbd8bc88..b7b3f5b0d406 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -328,13 +328,13 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)  	unsigned long		lockflags;  	size_t			size = dev->rx_urb_size; -	if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) { +	skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); +	if (!skb) {  		netif_dbg(dev, rx_err, dev->net, "no rx skb\n");  		usbnet_defer_kevent (dev, EVENT_RX_MEMORY);  		usb_free_urb (urb);  		return -ENOMEM;  	} -	skb_reserve (skb, NET_IP_ALIGN);  	entry = (struct skb_data *) skb->cb;  	entry->urb = urb; @@ -493,6 +493,7 @@ block:  		if (netif_running (dev->net) &&  		    !test_bit (EVENT_RX_HALT, &dev->flags)) {  			rx_submit (dev, urb, GFP_ATOMIC); +			usb_mark_last_busy(dev->udev);  			return;  		}  		usb_free_urb (urb); @@ -589,6 +590,15 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)  		entry = (struct skb_data *) skb->cb;  		urb = entry->urb; +		/* +		 * Get reference count of the URB to avoid it to be +		 * freed during usb_unlink_urb, which may trigger +		 * use-after-free problem inside usb_unlink_urb since +		 * usb_unlink_urb is always racing with .complete +		 * handler(include defer_bh). +		 */ +		usb_get_urb(urb); +		spin_unlock_irqrestore(&q->lock, flags);  		// during some PM-driven resume scenarios,  		// these (async) unlinks complete immediately  		retval = usb_unlink_urb (urb); @@ -596,6 +606,8 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)  			netdev_dbg(dev->net, "unlink urb err, %d\n", retval);  		else  			count++; +		usb_put_urb(urb); +		spin_lock_irqsave(&q->lock, flags);  	}  	spin_unlock_irqrestore (&q->lock, flags);  	return count; @@ -1026,7 +1038,6 @@ static void tx_complete (struct urb *urb)  	}  	usb_autopm_put_interface_async(dev->intf); -	urb->dev = NULL;  	entry->state = tx_done;  	defer_bh(dev, skb, &dev->txq);  } @@ -1334,10 +1345,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	// set up our own records  	net = alloc_etherdev(sizeof(*dev)); -	if (!net) { -		dbg ("can't kmalloc dev"); +	if (!net)  		goto out; -	}  	/* netdev_printk() needs this so do it as early as possible */  	SET_NETDEV_DEV(net, &udev->dev); @@ -1535,7 +1544,7 @@ int usbnet_resume (struct usb_interface *intf)  		if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {  			if (!(dev->txq.qlen >= TX_QLEN(dev))) -				netif_start_queue(dev->net); +				netif_tx_wake_all_queues(dev->net);  			tasklet_schedule (&dev->bh);  		}  	} | 
