Skip to content

Commit

Permalink
drivers: net: usb: rtl8150: concurrent URB bugfix
Browse files Browse the repository at this point in the history
This patch fixes a potential race with concurrently running asynchronous
write requests.  The values for device's RX control register are now
stored in dynamically allocated buffers so each URB submission has it's
own copy.  Doing it the old way is data clobbering prone.

This patch is against latest 'net' tree.

Signed-off-by: Petko Manolov <petkan@nucleusys.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Petko Manolov authored and David S. Miller committed May 20, 2013
1 parent 25dff94 commit 4d12997
Showing 1 changed file with 46 additions and 54 deletions.
100 changes: 46 additions & 54 deletions drivers/net/usb/rtl8150.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,23 @@ struct rtl8150 {
struct usb_device *udev;
struct tasklet_struct tl;
struct net_device *netdev;
struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb;
struct urb *rx_urb, *tx_urb, *intr_urb;
struct sk_buff *tx_skb, *rx_skb;
struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE];
spinlock_t rx_pool_lock;
struct usb_ctrlrequest dr;
int intr_interval;
__le16 rx_creg;
u8 *intr_buff;
u8 phy;
};

typedef struct rtl8150 rtl8150_t;

struct async_req {
struct usb_ctrlrequest dr;
u16 rx_creg;
};

static const char driver_name [] = "rtl8150";

/*
Expand All @@ -164,51 +168,47 @@ static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
indx, 0, data, size, 500);
}

static void ctrl_callback(struct urb *urb)
static void async_set_reg_cb(struct urb *urb)
{
rtl8150_t *dev;
struct async_req *req = (struct async_req *)urb->context;
int status = urb->status;

switch (status) {
case 0:
break;
case -EINPROGRESS:
break;
case -ENOENT:
break;
default:
if (printk_ratelimit())
dev_warn(&urb->dev->dev, "ctrl urb status %d\n", status);
}
dev = urb->context;
clear_bit(RX_REG_SET, &dev->flags);
if (status < 0)
dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status);
kfree(req);
usb_free_urb(urb);
}

static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size)
static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg)
{
int ret;

if (test_bit(RX_REG_SET, &dev->flags))
return -EAGAIN;
int res = -ENOMEM;
struct urb *async_urb;
struct async_req *req;

dev->dr.bRequestType = RTL8150_REQT_WRITE;
dev->dr.bRequest = RTL8150_REQ_SET_REGS;
dev->dr.wValue = cpu_to_le16(indx);
dev->dr.wIndex = 0;
dev->dr.wLength = cpu_to_le16(size);
dev->ctrl_urb->transfer_buffer_length = size;
usb_fill_control_urb(dev->ctrl_urb, dev->udev,
usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr,
&dev->rx_creg, size, ctrl_callback, dev);
if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) {
if (ret == -ENODEV)
req = kmalloc(sizeof(struct async_req), GFP_ATOMIC);
if (req == NULL)
return res;
async_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (async_urb == NULL) {
kfree(req);
return res;
}
req->rx_creg = cpu_to_le16(reg);
req->dr.bRequestType = RTL8150_REQT_WRITE;
req->dr.bRequest = RTL8150_REQ_SET_REGS;
req->dr.wIndex = 0;
req->dr.wValue = cpu_to_le16(indx);
req->dr.wLength = cpu_to_le16(size);
usb_fill_control_urb(async_urb, dev->udev,
usb_sndctrlpipe(dev->udev, 0), (void *)&req->dr,
&req->rx_creg, size, async_set_reg_cb, req);
res = usb_submit_urb(async_urb, GFP_ATOMIC);
if (res) {
if (res == -ENODEV)
netif_device_detach(dev->netdev);
dev_err(&dev->udev->dev,
"control request submission failed: %d\n", ret);
} else
set_bit(RX_REG_SET, &dev->flags);

return ret;
dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res);
}
return res;
}

static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg)
Expand Down Expand Up @@ -330,13 +330,6 @@ static int alloc_all_urbs(rtl8150_t * dev)
usb_free_urb(dev->tx_urb);
return 0;
}
dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->ctrl_urb) {
usb_free_urb(dev->rx_urb);
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->intr_urb);
return 0;
}

return 1;
}
Expand All @@ -346,15 +339,13 @@ static void free_all_urbs(rtl8150_t * dev)
usb_free_urb(dev->rx_urb);
usb_free_urb(dev->tx_urb);
usb_free_urb(dev->intr_urb);
usb_free_urb(dev->ctrl_urb);
}

static void unlink_all_urbs(rtl8150_t * dev)
{
usb_kill_urb(dev->rx_urb);
usb_kill_urb(dev->tx_urb);
usb_kill_urb(dev->intr_urb);
usb_kill_urb(dev->ctrl_urb);
}

static inline struct sk_buff *pull_skb(rtl8150_t *dev)
Expand Down Expand Up @@ -629,7 +620,6 @@ static int enable_net_traffic(rtl8150_t * dev)
}
/* RCR bit7=1 attach Rx info at the end; =0 HW CRC (which is broken) */
rcr = 0x9e;
dev->rx_creg = cpu_to_le16(rcr);
tcr = 0xd8;
cr = 0x0c;
if (!(rcr & 0x80))
Expand Down Expand Up @@ -662,20 +652,22 @@ static void rtl8150_tx_timeout(struct net_device *netdev)
static void rtl8150_set_multicast(struct net_device *netdev)
{
rtl8150_t *dev = netdev_priv(netdev);
u16 rx_creg = 0x9e;

netif_stop_queue(netdev);
if (netdev->flags & IFF_PROMISC) {
dev->rx_creg |= cpu_to_le16(0x0001);
rx_creg |= 0x0001;
dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name);
} else if (!netdev_mc_empty(netdev) ||
(netdev->flags & IFF_ALLMULTI)) {
dev->rx_creg &= cpu_to_le16(0xfffe);
dev->rx_creg |= cpu_to_le16(0x0002);
rx_creg &= 0xfffe;
rx_creg |= 0x0002;
dev_info(&netdev->dev, "%s: allmulti set\n", netdev->name);
} else {
/* ~RX_MULTICAST, ~RX_PROMISCUOUS */
dev->rx_creg &= cpu_to_le16(0x00fc);
rx_creg &= 0x00fc;
}
async_set_registers(dev, RCR, 2);
async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg);
netif_wake_queue(netdev);
}

Expand Down

0 comments on commit 4d12997

Please sign in to comment.