Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers: net: usb: rtl8150: concurrent URB bugfix

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>

authored by

Petko Manolov and committed by
David S. Miller
4d12997a 25dff94f

+46 -54
+46 -54
drivers/net/usb/rtl8150.c
··· 130 130 struct usb_device *udev; 131 131 struct tasklet_struct tl; 132 132 struct net_device *netdev; 133 - struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; 133 + struct urb *rx_urb, *tx_urb, *intr_urb; 134 134 struct sk_buff *tx_skb, *rx_skb; 135 135 struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE]; 136 136 spinlock_t rx_pool_lock; 137 137 struct usb_ctrlrequest dr; 138 138 int intr_interval; 139 - __le16 rx_creg; 140 139 u8 *intr_buff; 141 140 u8 phy; 142 141 }; 143 142 144 143 typedef struct rtl8150 rtl8150_t; 144 + 145 + struct async_req { 146 + struct usb_ctrlrequest dr; 147 + u16 rx_creg; 148 + }; 145 149 146 150 static const char driver_name [] = "rtl8150"; 147 151 ··· 168 164 indx, 0, data, size, 500); 169 165 } 170 166 171 - static void ctrl_callback(struct urb *urb) 167 + static void async_set_reg_cb(struct urb *urb) 172 168 { 173 - rtl8150_t *dev; 169 + struct async_req *req = (struct async_req *)urb->context; 174 170 int status = urb->status; 175 171 176 - switch (status) { 177 - case 0: 178 - break; 179 - case -EINPROGRESS: 180 - break; 181 - case -ENOENT: 182 - break; 183 - default: 184 - if (printk_ratelimit()) 185 - dev_warn(&urb->dev->dev, "ctrl urb status %d\n", status); 186 - } 187 - dev = urb->context; 188 - clear_bit(RX_REG_SET, &dev->flags); 172 + if (status < 0) 173 + dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); 174 + kfree(req); 175 + usb_free_urb(urb); 189 176 } 190 177 191 - static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size) 178 + static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, u16 reg) 192 179 { 193 - int ret; 180 + int res = -ENOMEM; 181 + struct urb *async_urb; 182 + struct async_req *req; 194 183 195 - if (test_bit(RX_REG_SET, &dev->flags)) 196 - return -EAGAIN; 197 - 198 - dev->dr.bRequestType = RTL8150_REQT_WRITE; 199 - dev->dr.bRequest = RTL8150_REQ_SET_REGS; 200 - dev->dr.wValue = cpu_to_le16(indx); 201 - dev->dr.wIndex = 0; 202 - dev->dr.wLength = cpu_to_le16(size); 203 - dev->ctrl_urb->transfer_buffer_length = size; 204 - usb_fill_control_urb(dev->ctrl_urb, dev->udev, 205 - usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr, 206 - &dev->rx_creg, size, ctrl_callback, dev); 207 - if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) { 208 - if (ret == -ENODEV) 184 + req = kmalloc(sizeof(struct async_req), GFP_ATOMIC); 185 + if (req == NULL) 186 + return res; 187 + async_urb = usb_alloc_urb(0, GFP_ATOMIC); 188 + if (async_urb == NULL) { 189 + kfree(req); 190 + return res; 191 + } 192 + req->rx_creg = cpu_to_le16(reg); 193 + req->dr.bRequestType = RTL8150_REQT_WRITE; 194 + req->dr.bRequest = RTL8150_REQ_SET_REGS; 195 + req->dr.wIndex = 0; 196 + req->dr.wValue = cpu_to_le16(indx); 197 + req->dr.wLength = cpu_to_le16(size); 198 + usb_fill_control_urb(async_urb, dev->udev, 199 + usb_sndctrlpipe(dev->udev, 0), (void *)&req->dr, 200 + &req->rx_creg, size, async_set_reg_cb, req); 201 + res = usb_submit_urb(async_urb, GFP_ATOMIC); 202 + if (res) { 203 + if (res == -ENODEV) 209 204 netif_device_detach(dev->netdev); 210 - dev_err(&dev->udev->dev, 211 - "control request submission failed: %d\n", ret); 212 - } else 213 - set_bit(RX_REG_SET, &dev->flags); 214 - 215 - return ret; 205 + dev_err(&dev->udev->dev, "%s failed with %d\n", __func__, res); 206 + } 207 + return res; 216 208 } 217 209 218 210 static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg) ··· 330 330 usb_free_urb(dev->tx_urb); 331 331 return 0; 332 332 } 333 - dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); 334 - if (!dev->ctrl_urb) { 335 - usb_free_urb(dev->rx_urb); 336 - usb_free_urb(dev->tx_urb); 337 - usb_free_urb(dev->intr_urb); 338 - return 0; 339 - } 340 333 341 334 return 1; 342 335 } ··· 339 346 usb_free_urb(dev->rx_urb); 340 347 usb_free_urb(dev->tx_urb); 341 348 usb_free_urb(dev->intr_urb); 342 - usb_free_urb(dev->ctrl_urb); 343 349 } 344 350 345 351 static void unlink_all_urbs(rtl8150_t * dev) ··· 346 354 usb_kill_urb(dev->rx_urb); 347 355 usb_kill_urb(dev->tx_urb); 348 356 usb_kill_urb(dev->intr_urb); 349 - usb_kill_urb(dev->ctrl_urb); 350 357 } 351 358 352 359 static inline struct sk_buff *pull_skb(rtl8150_t *dev) ··· 620 629 } 621 630 /* RCR bit7=1 attach Rx info at the end; =0 HW CRC (which is broken) */ 622 631 rcr = 0x9e; 623 - dev->rx_creg = cpu_to_le16(rcr); 624 632 tcr = 0xd8; 625 633 cr = 0x0c; 626 634 if (!(rcr & 0x80)) ··· 652 662 static void rtl8150_set_multicast(struct net_device *netdev) 653 663 { 654 664 rtl8150_t *dev = netdev_priv(netdev); 665 + u16 rx_creg = 0x9e; 666 + 655 667 netif_stop_queue(netdev); 656 668 if (netdev->flags & IFF_PROMISC) { 657 - dev->rx_creg |= cpu_to_le16(0x0001); 669 + rx_creg |= 0x0001; 658 670 dev_info(&netdev->dev, "%s: promiscuous mode\n", netdev->name); 659 671 } else if (!netdev_mc_empty(netdev) || 660 672 (netdev->flags & IFF_ALLMULTI)) { 661 - dev->rx_creg &= cpu_to_le16(0xfffe); 662 - dev->rx_creg |= cpu_to_le16(0x0002); 673 + rx_creg &= 0xfffe; 674 + rx_creg |= 0x0002; 663 675 dev_info(&netdev->dev, "%s: allmulti set\n", netdev->name); 664 676 } else { 665 677 /* ~RX_MULTICAST, ~RX_PROMISCUOUS */ 666 - dev->rx_creg &= cpu_to_le16(0x00fc); 678 + rx_creg &= 0x00fc; 667 679 } 668 - async_set_registers(dev, RCR, 2); 680 + async_set_registers(dev, RCR, sizeof(rx_creg), rx_creg); 669 681 netif_wake_queue(netdev); 670 682 } 671 683