ipw2x00: fix rtnl mutex deadlock

This fix regression introduced by:

commit: ecb4433550
Author: Stanislaw Gruszka <sgruszka@redhat.com>
Date:   Fri Aug 12 14:00:59 2011 +0200

    mac80211: fix suspend/resume races with unregister hw

Above commit add rtnl_lock() into wiphy_register(), what cause deadlock
when initializing ipw2x00 driver, which itself call wiphy_register()
from register_netdev() internal callback with rtnl mutex taken.

To fix move wiphy_register() outside register_netdev(). This solution
have side effect of not creating /sys/class/net/wlanX/phy80211 link,
but that's a minor issue we can live with.

Bisected-by: Witold Baryluk <baryluk@smp.if.uj.edu.pl>
Bisected-by: Michael Witten <mfwitten@gmail.com>
Tested-by: Witold Baryluk <baryluk@smp.if.uj.edu.pl>
Tested-by: Michael Witten <mfwitten@gmail.com>
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Stanislaw Gruszka 2011-09-14 16:47:50 +02:00 committed by John W. Linville
parent aa3d7eef39
commit 7cabafcea7
2 changed files with 40 additions and 20 deletions

View File

@ -1901,17 +1901,19 @@ static void ipw2100_down(struct ipw2100_priv *priv)
/* Called by register_netdev() */ /* Called by register_netdev() */
static int ipw2100_net_init(struct net_device *dev) static int ipw2100_net_init(struct net_device *dev)
{
struct ipw2100_priv *priv = libipw_priv(dev);
return ipw2100_up(priv, 1);
}
static int ipw2100_wdev_init(struct net_device *dev)
{ {
struct ipw2100_priv *priv = libipw_priv(dev); struct ipw2100_priv *priv = libipw_priv(dev);
const struct libipw_geo *geo = libipw_get_geo(priv->ieee); const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
struct wireless_dev *wdev = &priv->ieee->wdev; struct wireless_dev *wdev = &priv->ieee->wdev;
int ret;
int i; int i;
ret = ipw2100_up(priv, 1);
if (ret)
return ret;
memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
/* fill-out priv->ieee->bg_band */ /* fill-out priv->ieee->bg_band */
@ -6350,9 +6352,13 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
"Error calling register_netdev.\n"); "Error calling register_netdev.\n");
goto fail; goto fail;
} }
registered = 1;
err = ipw2100_wdev_init(dev);
if (err)
goto fail;
mutex_lock(&priv->action_mutex); mutex_lock(&priv->action_mutex);
registered = 1;
IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev)); IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
@ -6389,7 +6395,8 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
fail_unlock: fail_unlock:
mutex_unlock(&priv->action_mutex); mutex_unlock(&priv->action_mutex);
wiphy_unregister(priv->ieee->wdev.wiphy);
kfree(priv->ieee->bg_band.channels);
fail: fail:
if (dev) { if (dev) {
if (registered) if (registered)

View File

@ -11424,17 +11424,24 @@ static void ipw_bg_down(struct work_struct *work)
/* Called by register_netdev() */ /* Called by register_netdev() */
static int ipw_net_init(struct net_device *dev) static int ipw_net_init(struct net_device *dev)
{
int rc = 0;
struct ipw_priv *priv = libipw_priv(dev);
mutex_lock(&priv->mutex);
if (ipw_up(priv))
rc = -EIO;
mutex_unlock(&priv->mutex);
return rc;
}
static int ipw_wdev_init(struct net_device *dev)
{ {
int i, rc = 0; int i, rc = 0;
struct ipw_priv *priv = libipw_priv(dev); struct ipw_priv *priv = libipw_priv(dev);
const struct libipw_geo *geo = libipw_get_geo(priv->ieee); const struct libipw_geo *geo = libipw_get_geo(priv->ieee);
struct wireless_dev *wdev = &priv->ieee->wdev; struct wireless_dev *wdev = &priv->ieee->wdev;
mutex_lock(&priv->mutex);
if (ipw_up(priv)) {
rc = -EIO;
goto out;
}
memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN);
@ -11519,13 +11526,9 @@ static int ipw_net_init(struct net_device *dev)
set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev);
/* With that information in place, we can now register the wiphy... */ /* With that information in place, we can now register the wiphy... */
if (wiphy_register(wdev->wiphy)) { if (wiphy_register(wdev->wiphy))
rc = -EIO; rc = -EIO;
goto out;
}
out: out:
mutex_unlock(&priv->mutex);
return rc; return rc;
} }
@ -11832,14 +11835,22 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
goto out_remove_sysfs; goto out_remove_sysfs;
} }
err = ipw_wdev_init(net_dev);
if (err) {
IPW_ERROR("failed to register wireless device\n");
goto out_unregister_netdev;
}
#ifdef CONFIG_IPW2200_PROMISCUOUS #ifdef CONFIG_IPW2200_PROMISCUOUS
if (rtap_iface) { if (rtap_iface) {
err = ipw_prom_alloc(priv); err = ipw_prom_alloc(priv);
if (err) { if (err) {
IPW_ERROR("Failed to register promiscuous network " IPW_ERROR("Failed to register promiscuous network "
"device (error %d).\n", err); "device (error %d).\n", err);
unregister_netdev(priv->net_dev); wiphy_unregister(priv->ieee->wdev.wiphy);
goto out_remove_sysfs; kfree(priv->ieee->a_band.channels);
kfree(priv->ieee->bg_band.channels);
goto out_unregister_netdev;
} }
} }
#endif #endif
@ -11851,6 +11862,8 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev,
return 0; return 0;
out_unregister_netdev:
unregister_netdev(priv->net_dev);
out_remove_sysfs: out_remove_sysfs:
sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group); sysfs_remove_group(&pdev->dev.kobj, &ipw_attribute_group);
out_release_irq: out_release_irq: