Commit c1db52b9 authored by Larry Finger's avatar Larry Finger Committed by John W. Linville

rtl8187: Use usb anchor facilities to manage urbs

When SLUB debugging is enabled in the kernel, and the boot command includes
the option "slub_debug=P", rtl8187 encounters a GPF due to a read-after-free
of a urb.

Following the example of changes in p54usb to fix the same problem, the code
has been modified to use the usb_anchor_urb() method. With this change, the
USB core handles the freeing of urb's.

This patch fixes the problem reported in Kernel Bugzilla #12185
(http://bugzilla.kernel.org/show_bug.cgi?id=12185).
Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Tested-by: default avatarHin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 388cdf31
...@@ -99,6 +99,7 @@ struct rtl8187_priv { ...@@ -99,6 +99,7 @@ struct rtl8187_priv {
struct ieee80211_supported_band band; struct ieee80211_supported_band band;
struct usb_device *udev; struct usb_device *udev;
u32 rx_conf; u32 rx_conf;
struct usb_anchor anchored;
u16 txpwr_base; u16 txpwr_base;
u8 asic_rev; u8 asic_rev;
u8 is_rtl8187b; u8 is_rtl8187b;
...@@ -115,7 +116,6 @@ struct rtl8187_priv { ...@@ -115,7 +116,6 @@ struct rtl8187_priv {
u8 aifsn[4]; u8 aifsn[4];
struct { struct {
__le64 buf; __le64 buf;
struct urb *urb;
struct sk_buff_head queue; struct sk_buff_head queue;
} b_tx_status; } b_tx_status;
}; };
......
...@@ -99,7 +99,6 @@ static const struct ieee80211_channel rtl818x_channels[] = { ...@@ -99,7 +99,6 @@ static const struct ieee80211_channel rtl818x_channels[] = {
static void rtl8187_iowrite_async_cb(struct urb *urb) static void rtl8187_iowrite_async_cb(struct urb *urb)
{ {
kfree(urb->context); kfree(urb->context);
usb_free_urb(urb);
} }
static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
...@@ -136,11 +135,13 @@ static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, ...@@ -136,11 +135,13 @@ static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr,
usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0),
(unsigned char *)dr, buf, len, (unsigned char *)dr, buf, len,
rtl8187_iowrite_async_cb, buf); rtl8187_iowrite_async_cb, buf);
usb_anchor_urb(urb, &priv->anchored);
rc = usb_submit_urb(urb, GFP_ATOMIC); rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc < 0) { if (rc < 0) {
kfree(buf); kfree(buf);
usb_free_urb(urb); usb_unanchor_urb(urb);
} }
usb_free_urb(urb);
} }
static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv,
...@@ -172,7 +173,6 @@ static void rtl8187_tx_cb(struct urb *urb) ...@@ -172,7 +173,6 @@ static void rtl8187_tx_cb(struct urb *urb)
struct ieee80211_hw *hw = info->rate_driver_data[0]; struct ieee80211_hw *hw = info->rate_driver_data[0];
struct rtl8187_priv *priv = hw->priv; struct rtl8187_priv *priv = hw->priv;
usb_free_urb(info->rate_driver_data[1]);
skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
sizeof(struct rtl8187_tx_hdr)); sizeof(struct rtl8187_tx_hdr));
ieee80211_tx_info_clear_status(info); ieee80211_tx_info_clear_status(info);
...@@ -273,11 +273,13 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) ...@@ -273,11 +273,13 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep),
buf, skb->len, rtl8187_tx_cb, skb); buf, skb->len, rtl8187_tx_cb, skb);
usb_anchor_urb(urb, &priv->anchored);
rc = usb_submit_urb(urb, GFP_ATOMIC); rc = usb_submit_urb(urb, GFP_ATOMIC);
if (rc < 0) { if (rc < 0) {
usb_free_urb(urb); usb_unanchor_urb(urb);
kfree_skb(skb); kfree_skb(skb);
} }
usb_free_urb(urb);
return 0; return 0;
} }
...@@ -301,14 +303,13 @@ static void rtl8187_rx_cb(struct urb *urb) ...@@ -301,14 +303,13 @@ static void rtl8187_rx_cb(struct urb *urb)
return; return;
} }
spin_unlock(&priv->rx_queue.lock); spin_unlock(&priv->rx_queue.lock);
skb_put(skb, urb->actual_length);
if (unlikely(urb->status)) { if (unlikely(urb->status)) {
usb_free_urb(urb);
dev_kfree_skb_irq(skb); dev_kfree_skb_irq(skb);
return; return;
} }
skb_put(skb, urb->actual_length);
if (!priv->is_rtl8187b) { if (!priv->is_rtl8187b) {
struct rtl8187_rx_hdr *hdr = struct rtl8187_rx_hdr *hdr =
(typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
...@@ -361,7 +362,6 @@ static void rtl8187_rx_cb(struct urb *urb) ...@@ -361,7 +362,6 @@ static void rtl8187_rx_cb(struct urb *urb)
skb = dev_alloc_skb(RTL8187_MAX_RX); skb = dev_alloc_skb(RTL8187_MAX_RX);
if (unlikely(!skb)) { if (unlikely(!skb)) {
usb_free_urb(urb);
/* TODO check rx queue length and refill *somewhere* */ /* TODO check rx queue length and refill *somewhere* */
return; return;
} }
...@@ -373,24 +373,32 @@ static void rtl8187_rx_cb(struct urb *urb) ...@@ -373,24 +373,32 @@ static void rtl8187_rx_cb(struct urb *urb)
urb->context = skb; urb->context = skb;
skb_queue_tail(&priv->rx_queue, skb); skb_queue_tail(&priv->rx_queue, skb);
usb_submit_urb(urb, GFP_ATOMIC); usb_anchor_urb(urb, &priv->anchored);
if (usb_submit_urb(urb, GFP_ATOMIC)) {
usb_unanchor_urb(urb);
skb_unlink(skb, &priv->rx_queue);
dev_kfree_skb_irq(skb);
}
} }
static int rtl8187_init_urbs(struct ieee80211_hw *dev) static int rtl8187_init_urbs(struct ieee80211_hw *dev)
{ {
struct rtl8187_priv *priv = dev->priv; struct rtl8187_priv *priv = dev->priv;
struct urb *entry; struct urb *entry = NULL;
struct sk_buff *skb; struct sk_buff *skb;
struct rtl8187_rx_info *info; struct rtl8187_rx_info *info;
int ret = 0;
while (skb_queue_len(&priv->rx_queue) < 8) { while (skb_queue_len(&priv->rx_queue) < 8) {
skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL);
if (!skb) if (!skb) {
break; ret = -ENOMEM;
goto err;
}
entry = usb_alloc_urb(0, GFP_KERNEL); entry = usb_alloc_urb(0, GFP_KERNEL);
if (!entry) { if (!entry) {
kfree_skb(skb); ret = -ENOMEM;
break; goto err;
} }
usb_fill_bulk_urb(entry, priv->udev, usb_fill_bulk_urb(entry, priv->udev,
usb_rcvbulkpipe(priv->udev, usb_rcvbulkpipe(priv->udev,
...@@ -401,10 +409,22 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev) ...@@ -401,10 +409,22 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
info->urb = entry; info->urb = entry;
info->dev = dev; info->dev = dev;
skb_queue_tail(&priv->rx_queue, skb); skb_queue_tail(&priv->rx_queue, skb);
usb_submit_urb(entry, GFP_KERNEL); usb_anchor_urb(entry, &priv->anchored);
ret = usb_submit_urb(entry, GFP_KERNEL);
if (ret) {
skb_unlink(skb, &priv->rx_queue);
usb_unanchor_urb(entry);
goto err;
}
usb_free_urb(entry);
} }
return ret;
return 0; err:
usb_free_urb(entry);
kfree_skb(skb);
usb_kill_anchored_urbs(&priv->anchored);
return ret;
} }
static void rtl8187b_status_cb(struct urb *urb) static void rtl8187b_status_cb(struct urb *urb)
...@@ -414,10 +434,8 @@ static void rtl8187b_status_cb(struct urb *urb) ...@@ -414,10 +434,8 @@ static void rtl8187b_status_cb(struct urb *urb)
u64 val; u64 val;
unsigned int cmd_type; unsigned int cmd_type;
if (unlikely(urb->status)) { if (unlikely(urb->status))
usb_free_urb(urb);
return; return;
}
/* /*
* Read from status buffer: * Read from status buffer:
...@@ -488,26 +506,32 @@ static void rtl8187b_status_cb(struct urb *urb) ...@@ -488,26 +506,32 @@ static void rtl8187b_status_cb(struct urb *urb)
spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags);
} }
usb_submit_urb(urb, GFP_ATOMIC); usb_anchor_urb(urb, &priv->anchored);
if (usb_submit_urb(urb, GFP_ATOMIC))
usb_unanchor_urb(urb);
} }
static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) static int rtl8187b_init_status_urb(struct ieee80211_hw *dev)
{ {
struct rtl8187_priv *priv = dev->priv; struct rtl8187_priv *priv = dev->priv;
struct urb *entry; struct urb *entry;
int ret = 0;
entry = usb_alloc_urb(0, GFP_KERNEL); entry = usb_alloc_urb(0, GFP_KERNEL);
if (!entry) if (!entry)
return -ENOMEM; return -ENOMEM;
priv->b_tx_status.urb = entry;
usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9),
&priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf),
rtl8187b_status_cb, dev); rtl8187b_status_cb, dev);
usb_submit_urb(entry, GFP_KERNEL); usb_anchor_urb(entry, &priv->anchored);
ret = usb_submit_urb(entry, GFP_KERNEL);
if (ret)
usb_unanchor_urb(entry);
usb_free_urb(entry);
return 0; return ret;
} }
static int rtl8187_cmd_reset(struct ieee80211_hw *dev) static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
...@@ -841,6 +865,9 @@ static int rtl8187_start(struct ieee80211_hw *dev) ...@@ -841,6 +865,9 @@ static int rtl8187_start(struct ieee80211_hw *dev)
return ret; return ret;
mutex_lock(&priv->conf_mutex); mutex_lock(&priv->conf_mutex);
init_usb_anchor(&priv->anchored);
if (priv->is_rtl8187b) { if (priv->is_rtl8187b) {
reg = RTL818X_RX_CONF_MGMT | reg = RTL818X_RX_CONF_MGMT |
RTL818X_RX_CONF_DATA | RTL818X_RX_CONF_DATA |
...@@ -936,12 +963,12 @@ static void rtl8187_stop(struct ieee80211_hw *dev) ...@@ -936,12 +963,12 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
while ((skb = skb_dequeue(&priv->rx_queue))) { while ((skb = skb_dequeue(&priv->rx_queue))) {
info = (struct rtl8187_rx_info *)skb->cb; info = (struct rtl8187_rx_info *)skb->cb;
usb_kill_urb(info->urb);
kfree_skb(skb); kfree_skb(skb);
} }
while ((skb = skb_dequeue(&priv->b_tx_status.queue))) while ((skb = skb_dequeue(&priv->b_tx_status.queue)))
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
usb_kill_urb(priv->b_tx_status.urb);
usb_kill_anchored_urbs(&priv->anchored);
mutex_unlock(&priv->conf_mutex); mutex_unlock(&priv->conf_mutex);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment