| From a7959c1394d4126a70a53b914ce4105f5173d0aa Mon Sep 17 00:00:00 2001 |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| Date: Mon, 19 Mar 2012 15:44:31 -0500 |
| Subject: rtlwifi: Preallocate USB read buffers and eliminate kalloc in read routine |
| |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| |
| commit a7959c1394d4126a70a53b914ce4105f5173d0aa upstream. |
| |
| The current version of rtlwifi for USB operations uses kmalloc to |
| acquire a 32-bit buffer for each read of the device. When |
| _usb_read_sync() is called with the rcu_lock held, the result is |
| a "sleeping function called from invalid context" BUG. This is |
| reported for two cases in https://bugzilla.kernel.org/show_bug.cgi?id=42775. |
| The first case has the lock originating from within rtlwifi and could |
| be fixed by rearranging the locking; however, the second originates from |
| within mac80211. The kmalloc() call is removed from _usb_read_sync() |
| by creating a ring buffer pointer in the private area and |
| allocating the buffer data in the probe routine. |
| |
| Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> |
| Signed-off-by: John W. Linville <linville@tuxdriver.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/rtlwifi/usb.c | 34 ++++++++++++++++------------------ |
| drivers/net/wireless/rtlwifi/wifi.h | 6 +++++- |
| 2 files changed, 21 insertions(+), 19 deletions(-) |
| |
| --- a/drivers/net/wireless/rtlwifi/usb.c |
| +++ b/drivers/net/wireless/rtlwifi/usb.c |
| @@ -127,46 +127,38 @@ static int _usbctrl_vendorreq_sync_read( |
| return status; |
| } |
| |
| -static u32 _usb_read_sync(struct usb_device *udev, u32 addr, u16 len) |
| +static u32 _usb_read_sync(struct rtl_priv *rtlpriv, u32 addr, u16 len) |
| { |
| + struct device *dev = rtlpriv->io.dev; |
| + struct usb_device *udev = to_usb_device(dev); |
| u8 request; |
| u16 wvalue; |
| u16 index; |
| - u32 *data; |
| - u32 ret; |
| + __le32 *data = &rtlpriv->usb_data[rtlpriv->usb_data_index]; |
| |
| - data = kmalloc(sizeof(u32), GFP_KERNEL); |
| - if (!data) |
| - return -ENOMEM; |
| request = REALTEK_USB_VENQT_CMD_REQ; |
| index = REALTEK_USB_VENQT_CMD_IDX; /* n/a */ |
| |
| wvalue = (u16)addr; |
| _usbctrl_vendorreq_sync_read(udev, request, wvalue, index, data, len); |
| - ret = le32_to_cpu(*data); |
| - kfree(data); |
| - return ret; |
| + if (++rtlpriv->usb_data_index >= RTL_USB_MAX_RX_COUNT) |
| + rtlpriv->usb_data_index = 0; |
| + return le32_to_cpu(*data); |
| } |
| |
| static u8 _usb_read8_sync(struct rtl_priv *rtlpriv, u32 addr) |
| { |
| - struct device *dev = rtlpriv->io.dev; |
| - |
| - return (u8)_usb_read_sync(to_usb_device(dev), addr, 1); |
| + return (u8)_usb_read_sync(rtlpriv, addr, 1); |
| } |
| |
| static u16 _usb_read16_sync(struct rtl_priv *rtlpriv, u32 addr) |
| { |
| - struct device *dev = rtlpriv->io.dev; |
| - |
| - return (u16)_usb_read_sync(to_usb_device(dev), addr, 2); |
| + return (u16)_usb_read_sync(rtlpriv, addr, 2); |
| } |
| |
| static u32 _usb_read32_sync(struct rtl_priv *rtlpriv, u32 addr) |
| { |
| - struct device *dev = rtlpriv->io.dev; |
| - |
| - return _usb_read_sync(to_usb_device(dev), addr, 4); |
| + return _usb_read_sync(rtlpriv, addr, 4); |
| } |
| |
| static void _usb_write_async(struct usb_device *udev, u32 addr, u32 val, |
| @@ -954,6 +946,11 @@ int __devinit rtl_usb_probe(struct usb_i |
| return -ENOMEM; |
| } |
| rtlpriv = hw->priv; |
| + rtlpriv->usb_data = kzalloc(RTL_USB_MAX_RX_COUNT * sizeof(u32), |
| + GFP_KERNEL); |
| + if (!rtlpriv->usb_data) |
| + return -ENOMEM; |
| + rtlpriv->usb_data_index = 0; |
| init_completion(&rtlpriv->firmware_loading_complete); |
| SET_IEEE80211_DEV(hw, &intf->dev); |
| udev = interface_to_usbdev(intf); |
| @@ -1023,6 +1020,7 @@ void rtl_usb_disconnect(struct usb_inter |
| /* rtl_deinit_rfkill(hw); */ |
| rtl_usb_deinit(hw); |
| rtl_deinit_core(hw); |
| + kfree(rtlpriv->usb_data); |
| rtlpriv->cfg->ops->deinit_sw_leds(hw); |
| rtlpriv->cfg->ops->deinit_sw_vars(hw); |
| _rtl_usb_io_handler_release(hw); |
| --- a/drivers/net/wireless/rtlwifi/wifi.h |
| +++ b/drivers/net/wireless/rtlwifi/wifi.h |
| @@ -65,7 +65,7 @@ |
| #define QOS_QUEUE_NUM 4 |
| #define RTL_MAC80211_NUM_QUEUE 5 |
| #define REALTEK_USB_VENQT_MAX_BUF_SIZE 254 |
| - |
| +#define RTL_USB_MAX_RX_COUNT 100 |
| #define QBSS_LOAD_SIZE 5 |
| #define MAX_WMMELE_LENGTH 64 |
| |
| @@ -1627,6 +1627,10 @@ struct rtl_priv { |
| interface or hardware */ |
| unsigned long status; |
| |
| + /* data buffer pointer for USB reads */ |
| + __le32 *usb_data; |
| + int usb_data_index; |
| + |
| /*This must be the last item so |
| that it points to the data allocated |
| beyond this structure like: |