mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-12 20:31:49 +00:00
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: Larry Finger <Larry.Finger@lwfinger.net> Tested-by: Hin-Tak Leung <htl10@users.sourceforge.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
388cdf31db
commit
c1db52b9d2
@ -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 {
|
|||||||
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[] = {
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user