diff options
| author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2010-05-23 03:14:08 -0400 |
|---|---|---|
| committer | Reinette Chatre <reinette.chatre@intel.com> | 2010-06-06 02:02:39 -0400 |
| commit | 7d47618a2ade0cb6d8a0b2597029c383c1662fa0 (patch) | |
| tree | 6d1faddd085fc0ab444affaed93c92bde1ff0ff8 | |
| parent | 9edc71b746efeaadc40e668964b76cda81fef386 (diff) | |
iwlwifi: move sysfs_create_group to post request firmware
Move the sysfs_create_group to iwl_ucode_callback after we
have safely got the firmware.
The motivation to do this comes from a warning from lockdep which detected
that we request priv->mutex while holding s_active during a sysfs request
(show_statistics in the example copy pasted). The reverse order exists upon
request_firmware: request_firmware which is a sysfs operation
that requires s_active is run under priv->mutex.
This ensures that we don't get sysfs request before we finish to request
the firmware, avoiding this deadlock.
=======================================================
[ INFO: possible circular locking dependency detected ]
-------------------------------------------------------
cat/2595 is trying to acquire lock:
(&priv->mutex){+.+.+.}, at: [<facfa598>] show_statistics+0x48/0x100 [iwlagn]
but task is already holding lock:
(s_active){++++.+}, at: [<c0580ebd>] sysfs_get_active_two+0x1d/0x50
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (s_active){++++.+}:
[<c0489b74>] __lock_acquire+0xc44/0x1230
[<c048a1ed>] lock_acquire+0x8d/0x110
[<c0581499>] sysfs_addrm_finish+0xe9/0x180
[<c057f64a>] sysfs_hash_and_remove+0x4a/0x80
[<c05829d4>] sysfs_remove_group+0x44/0xd0
[<c0714b75>] dpm_sysfs_remove+0x15/0x20
[<c070dac8>] device_del+0x38/0x170
[<c070dc1e>] device_unregister+0x1e/0x60
[<c071838d>] _request_firmware+0x29d/0x550
[<c07186c7>] request_firmware+0x17/0x20
[<fad01bf1>] iwl_mac_start+0xb1/0x1230 [iwlagn]
[<fa46ba06>] ieee80211_open+0x436/0x6f0 [mac80211]
[<c0808cd2>] dev_open+0x92/0xf0
[<c0808b2b>] dev_change_flags+0x7b/0x190
[<c08148e8>] do_setlink+0x178/0x3b0
[<c0815169>] rtnl_setlink+0xf9/0x130
[<c081453b>] rtnetlink_rcv_msg+0x1bb/0x1f0
[<c0827ce6>] netlink_rcv_skb+0x86/0xa0
[<c081436c>] rtnetlink_rcv+0x1c/0x30
[<c08279c3>] netlink_unicast+0x263/0x290
[<c0828768>] netlink_sendmsg+0x1c8/0x2a0
[<c07f85fd>] sock_sendmsg+0xcd/0x100
[<c07f964d>] sys_sendmsg+0x15d/0x290
[<c07f9e6b>] sys_socketcall+0xeb/0x2a0
[<c040ad9f>] sysenter_do_call+0x12/0x38
-> #0 (&priv->mutex){+.+.+.}:
[<c0489f84>] __lock_acquire+0x1054/0x1230
[<c048a1ed>] lock_acquire+0x8d/0x110
[<c08bb358>] __mutex_lock_common+0x58/0x470
[<c08bb84a>] mutex_lock_nested+0x3a/0x50
[<facfa598>] show_statistics+0x48/0x100 [iwlagn]
[<c070d219>] dev_attr_show+0x29/0x50
[<c057fecd>] sysfs_read_file+0xdd/0x190
[<c052880f>] vfs_read+0x9f/0x190
[<c0528d22>] sys_read+0x42/0x70
[<c040ad9f>] sysenter_do_call+0x12/0x38
other info that might help us debug this:
3 locks held by cat/2595:
#0: (&buffer->mutex){+.+.+.}, at: [<c057fe25>] sysfs_read_file+0x35/0x190
#1: (s_active){++++.+}, at: [<c0580ecd>] sysfs_get_active_two+0x2d/0x50
#2: (s_active){++++.+}, at: [<c0580ebd>] sysfs_get_active_two+0x1d/0x50
stack backtrace:
Pid: 2595, comm: cat Not tainted 2.6.33-tp-rc4 #2
Call Trace:
[<c08b99ab>] ? printk+0x1d/0x22
[<c0487752>] print_circular_bug+0xc2/0xd0
[<c0489f84>] __lock_acquire+0x1054/0x1230
[<c0478d81>] ? sched_clock_cpu+0x121/0x180
[<c048a1ed>] lock_acquire+0x8d/0x110
[<facfa598>] ? show_statistics+0x48/0x100 [iwlagn]
[<c08bb358>] __mutex_lock_common+0x58/0x470
[<facfa598>] ? show_statistics+0x48/0x100 [iwlagn]
[<c08bb84a>] mutex_lock_nested+0x3a/0x50
[<facfa598>] ? show_statistics+0x48/0x100 [iwlagn]
[<facfa598>] show_statistics+0x48/0x100 [iwlagn]
[<c0580cf9>] ? sysfs_get_active+0x69/0xb0
[<facfa550>] ? show_statistics+0x0/0x100 [iwlagn]
[<c070d219>] dev_attr_show+0x29/0x50
[<c057fecd>] sysfs_read_file+0xdd/0x190
[<c05ff314>] ? security_file_permission+0x14/0x20
[<c0528242>] ? rw_verify_area+0x62/0xd0
[<c052880f>] vfs_read+0x9f/0x190
[<c047745b>] ? up_read+0x1b/0x30
[<c057fdf0>] ? sysfs_read_file+0x0/0x190
[<c04af3b4>] ? audit_syscall_entry+0x1f4/0x220
[<c0528d22>] sys_read+0x42/0x70
[<c040ad9f>] sysenter_do_call+0x12/0x38
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
| -rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 318 |
1 files changed, 159 insertions, 159 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index aef4f71f1981..7726e67044c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c | |||
| @@ -1484,6 +1484,156 @@ bool iwl_good_ack_health(struct iwl_priv *priv, | |||
| 1484 | } | 1484 | } |
| 1485 | 1485 | ||
| 1486 | 1486 | ||
| 1487 | /***************************************************************************** | ||
| 1488 | * | ||
| 1489 | * sysfs attributes | ||
| 1490 | * | ||
| 1491 | *****************************************************************************/ | ||
| 1492 | |||
| 1493 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
| 1494 | |||
| 1495 | /* | ||
| 1496 | * The following adds a new attribute to the sysfs representation | ||
| 1497 | * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) | ||
| 1498 | * used for controlling the debug level. | ||
| 1499 | * | ||
| 1500 | * See the level definitions in iwl for details. | ||
| 1501 | * | ||
| 1502 | * The debug_level being managed using sysfs below is a per device debug | ||
| 1503 | * level that is used instead of the global debug level if it (the per | ||
| 1504 | * device debug level) is set. | ||
| 1505 | */ | ||
| 1506 | static ssize_t show_debug_level(struct device *d, | ||
| 1507 | struct device_attribute *attr, char *buf) | ||
| 1508 | { | ||
| 1509 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1510 | return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); | ||
| 1511 | } | ||
| 1512 | static ssize_t store_debug_level(struct device *d, | ||
| 1513 | struct device_attribute *attr, | ||
| 1514 | const char *buf, size_t count) | ||
| 1515 | { | ||
| 1516 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1517 | unsigned long val; | ||
| 1518 | int ret; | ||
| 1519 | |||
| 1520 | ret = strict_strtoul(buf, 0, &val); | ||
| 1521 | if (ret) | ||
| 1522 | IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); | ||
| 1523 | else { | ||
| 1524 | priv->debug_level = val; | ||
| 1525 | if (iwl_alloc_traffic_mem(priv)) | ||
| 1526 | IWL_ERR(priv, | ||
| 1527 | "Not enough memory to generate traffic log\n"); | ||
| 1528 | } | ||
| 1529 | return strnlen(buf, count); | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, | ||
| 1533 | show_debug_level, store_debug_level); | ||
| 1534 | |||
| 1535 | |||
| 1536 | #endif /* CONFIG_IWLWIFI_DEBUG */ | ||
| 1537 | |||
| 1538 | |||
| 1539 | static ssize_t show_temperature(struct device *d, | ||
| 1540 | struct device_attribute *attr, char *buf) | ||
| 1541 | { | ||
| 1542 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1543 | |||
| 1544 | if (!iwl_is_alive(priv)) | ||
| 1545 | return -EAGAIN; | ||
| 1546 | |||
| 1547 | return sprintf(buf, "%d\n", priv->temperature); | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); | ||
| 1551 | |||
| 1552 | static ssize_t show_tx_power(struct device *d, | ||
| 1553 | struct device_attribute *attr, char *buf) | ||
| 1554 | { | ||
| 1555 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1556 | |||
| 1557 | if (!iwl_is_ready_rf(priv)) | ||
| 1558 | return sprintf(buf, "off\n"); | ||
| 1559 | else | ||
| 1560 | return sprintf(buf, "%d\n", priv->tx_power_user_lmt); | ||
| 1561 | } | ||
| 1562 | |||
| 1563 | static ssize_t store_tx_power(struct device *d, | ||
| 1564 | struct device_attribute *attr, | ||
| 1565 | const char *buf, size_t count) | ||
| 1566 | { | ||
| 1567 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1568 | unsigned long val; | ||
| 1569 | int ret; | ||
| 1570 | |||
| 1571 | ret = strict_strtoul(buf, 10, &val); | ||
| 1572 | if (ret) | ||
| 1573 | IWL_INFO(priv, "%s is not in decimal form.\n", buf); | ||
| 1574 | else { | ||
| 1575 | ret = iwl_set_tx_power(priv, val, false); | ||
| 1576 | if (ret) | ||
| 1577 | IWL_ERR(priv, "failed setting tx power (0x%d).\n", | ||
| 1578 | ret); | ||
| 1579 | else | ||
| 1580 | ret = count; | ||
| 1581 | } | ||
| 1582 | return ret; | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); | ||
| 1586 | |||
| 1587 | static ssize_t show_rts_ht_protection(struct device *d, | ||
| 1588 | struct device_attribute *attr, char *buf) | ||
| 1589 | { | ||
| 1590 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1591 | |||
| 1592 | return sprintf(buf, "%s\n", | ||
| 1593 | priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self"); | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | static ssize_t store_rts_ht_protection(struct device *d, | ||
| 1597 | struct device_attribute *attr, | ||
| 1598 | const char *buf, size_t count) | ||
| 1599 | { | ||
| 1600 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 1601 | unsigned long val; | ||
| 1602 | int ret; | ||
| 1603 | |||
| 1604 | ret = strict_strtoul(buf, 10, &val); | ||
| 1605 | if (ret) | ||
| 1606 | IWL_INFO(priv, "Input is not in decimal form.\n"); | ||
| 1607 | else { | ||
| 1608 | if (!iwl_is_associated(priv)) | ||
| 1609 | priv->cfg->use_rts_for_ht = val ? true : false; | ||
| 1610 | else | ||
| 1611 | IWL_ERR(priv, "Sta associated with AP - " | ||
| 1612 | "Change protection mechanism is not allowed\n"); | ||
| 1613 | ret = count; | ||
| 1614 | } | ||
| 1615 | return ret; | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO, | ||
| 1619 | show_rts_ht_protection, store_rts_ht_protection); | ||
| 1620 | |||
| 1621 | |||
| 1622 | static struct attribute *iwl_sysfs_entries[] = { | ||
| 1623 | &dev_attr_temperature.attr, | ||
| 1624 | &dev_attr_tx_power.attr, | ||
| 1625 | &dev_attr_rts_ht_protection.attr, | ||
| 1626 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
| 1627 | &dev_attr_debug_level.attr, | ||
| 1628 | #endif | ||
| 1629 | NULL | ||
| 1630 | }; | ||
| 1631 | |||
| 1632 | static struct attribute_group iwl_attribute_group = { | ||
| 1633 | .name = NULL, /* put in device directory */ | ||
| 1634 | .attrs = iwl_sysfs_entries, | ||
| 1635 | }; | ||
| 1636 | |||
| 1487 | /****************************************************************************** | 1637 | /****************************************************************************** |
| 1488 | * | 1638 | * |
| 1489 | * uCode download functions | 1639 | * uCode download functions |
| @@ -1965,6 +2115,13 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) | |||
| 1965 | if (err) | 2115 | if (err) |
| 1966 | IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); | 2116 | IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); |
| 1967 | 2117 | ||
| 2118 | err = sysfs_create_group(&priv->pci_dev->dev.kobj, | ||
| 2119 | &iwl_attribute_group); | ||
| 2120 | if (err) { | ||
| 2121 | IWL_ERR(priv, "failed to create sysfs device attributes\n"); | ||
| 2122 | goto out_unbind; | ||
| 2123 | } | ||
| 2124 | |||
| 1968 | /* We have our copies now, allow OS release its copies */ | 2125 | /* We have our copies now, allow OS release its copies */ |
| 1969 | release_firmware(ucode_raw); | 2126 | release_firmware(ucode_raw); |
| 1970 | complete(&priv->_agn.firmware_loading_complete); | 2127 | complete(&priv->_agn.firmware_loading_complete); |
| @@ -3264,141 +3421,6 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, | |||
| 3264 | 3421 | ||
| 3265 | /***************************************************************************** | 3422 | /***************************************************************************** |
| 3266 | * | 3423 | * |
| 3267 | * sysfs attributes | ||
| 3268 | * | ||
| 3269 | *****************************************************************************/ | ||
| 3270 | |||
| 3271 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
| 3272 | |||
| 3273 | /* | ||
| 3274 | * The following adds a new attribute to the sysfs representation | ||
| 3275 | * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) | ||
| 3276 | * used for controlling the debug level. | ||
| 3277 | * | ||
| 3278 | * See the level definitions in iwl for details. | ||
| 3279 | * | ||
| 3280 | * The debug_level being managed using sysfs below is a per device debug | ||
| 3281 | * level that is used instead of the global debug level if it (the per | ||
| 3282 | * device debug level) is set. | ||
| 3283 | */ | ||
| 3284 | static ssize_t show_debug_level(struct device *d, | ||
| 3285 | struct device_attribute *attr, char *buf) | ||
| 3286 | { | ||
| 3287 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3288 | return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); | ||
| 3289 | } | ||
| 3290 | static ssize_t store_debug_level(struct device *d, | ||
| 3291 | struct device_attribute *attr, | ||
| 3292 | const char *buf, size_t count) | ||
| 3293 | { | ||
| 3294 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3295 | unsigned long val; | ||
| 3296 | int ret; | ||
| 3297 | |||
| 3298 | ret = strict_strtoul(buf, 0, &val); | ||
| 3299 | if (ret) | ||
| 3300 | IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); | ||
| 3301 | else { | ||
| 3302 | priv->debug_level = val; | ||
| 3303 | if (iwl_alloc_traffic_mem(priv)) | ||
| 3304 | IWL_ERR(priv, | ||
| 3305 | "Not enough memory to generate traffic log\n"); | ||
| 3306 | } | ||
| 3307 | return strnlen(buf, count); | ||
| 3308 | } | ||
| 3309 | |||
| 3310 | static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, | ||
| 3311 | show_debug_level, store_debug_level); | ||
| 3312 | |||
| 3313 | |||
| 3314 | #endif /* CONFIG_IWLWIFI_DEBUG */ | ||
| 3315 | |||
| 3316 | |||
| 3317 | static ssize_t show_temperature(struct device *d, | ||
| 3318 | struct device_attribute *attr, char *buf) | ||
| 3319 | { | ||
| 3320 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3321 | |||
| 3322 | if (!iwl_is_alive(priv)) | ||
| 3323 | return -EAGAIN; | ||
| 3324 | |||
| 3325 | return sprintf(buf, "%d\n", priv->temperature); | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); | ||
| 3329 | |||
| 3330 | static ssize_t show_tx_power(struct device *d, | ||
| 3331 | struct device_attribute *attr, char *buf) | ||
| 3332 | { | ||
| 3333 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3334 | |||
| 3335 | if (!iwl_is_ready_rf(priv)) | ||
| 3336 | return sprintf(buf, "off\n"); | ||
| 3337 | else | ||
| 3338 | return sprintf(buf, "%d\n", priv->tx_power_user_lmt); | ||
| 3339 | } | ||
| 3340 | |||
| 3341 | static ssize_t store_tx_power(struct device *d, | ||
| 3342 | struct device_attribute *attr, | ||
| 3343 | const char *buf, size_t count) | ||
| 3344 | { | ||
| 3345 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3346 | unsigned long val; | ||
| 3347 | int ret; | ||
| 3348 | |||
| 3349 | ret = strict_strtoul(buf, 10, &val); | ||
| 3350 | if (ret) | ||
| 3351 | IWL_INFO(priv, "%s is not in decimal form.\n", buf); | ||
| 3352 | else { | ||
| 3353 | ret = iwl_set_tx_power(priv, val, false); | ||
| 3354 | if (ret) | ||
| 3355 | IWL_ERR(priv, "failed setting tx power (0x%d).\n", | ||
| 3356 | ret); | ||
| 3357 | else | ||
| 3358 | ret = count; | ||
| 3359 | } | ||
| 3360 | return ret; | ||
| 3361 | } | ||
| 3362 | |||
| 3363 | static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); | ||
| 3364 | |||
| 3365 | static ssize_t show_rts_ht_protection(struct device *d, | ||
| 3366 | struct device_attribute *attr, char *buf) | ||
| 3367 | { | ||
| 3368 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3369 | |||
| 3370 | return sprintf(buf, "%s\n", | ||
| 3371 | priv->cfg->use_rts_for_ht ? "RTS/CTS" : "CTS-to-self"); | ||
| 3372 | } | ||
| 3373 | |||
| 3374 | static ssize_t store_rts_ht_protection(struct device *d, | ||
| 3375 | struct device_attribute *attr, | ||
| 3376 | const char *buf, size_t count) | ||
| 3377 | { | ||
| 3378 | struct iwl_priv *priv = dev_get_drvdata(d); | ||
| 3379 | unsigned long val; | ||
| 3380 | int ret; | ||
| 3381 | |||
| 3382 | ret = strict_strtoul(buf, 10, &val); | ||
| 3383 | if (ret) | ||
| 3384 | IWL_INFO(priv, "Input is not in decimal form.\n"); | ||
| 3385 | else { | ||
| 3386 | if (!iwl_is_associated(priv)) | ||
| 3387 | priv->cfg->use_rts_for_ht = val ? true : false; | ||
| 3388 | else | ||
| 3389 | IWL_ERR(priv, "Sta associated with AP - " | ||
| 3390 | "Change protection mechanism is not allowed\n"); | ||
| 3391 | ret = count; | ||
| 3392 | } | ||
| 3393 | return ret; | ||
| 3394 | } | ||
| 3395 | |||
| 3396 | static DEVICE_ATTR(rts_ht_protection, S_IWUSR | S_IRUGO, | ||
| 3397 | show_rts_ht_protection, store_rts_ht_protection); | ||
| 3398 | |||
| 3399 | |||
| 3400 | /***************************************************************************** | ||
| 3401 | * | ||
| 3402 | * driver setup and teardown | 3424 | * driver setup and teardown |
| 3403 | * | 3425 | * |
| 3404 | *****************************************************************************/ | 3426 | *****************************************************************************/ |
| @@ -3550,21 +3572,6 @@ static void iwl_uninit_drv(struct iwl_priv *priv) | |||
| 3550 | kfree(priv->scan_cmd); | 3572 | kfree(priv->scan_cmd); |
| 3551 | } | 3573 | } |
| 3552 | 3574 | ||
| 3553 | static struct attribute *iwl_sysfs_entries[] = { | ||
| 3554 | &dev_attr_temperature.attr, | ||
| 3555 | &dev_attr_tx_power.attr, | ||
| 3556 | &dev_attr_rts_ht_protection.attr, | ||
| 3557 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
| 3558 | &dev_attr_debug_level.attr, | ||
| 3559 | #endif | ||
| 3560 | NULL | ||
| 3561 | }; | ||
| 3562 | |||
| 3563 | static struct attribute_group iwl_attribute_group = { | ||
| 3564 | .name = NULL, /* put in device directory */ | ||
| 3565 | .attrs = iwl_sysfs_entries, | ||
| 3566 | }; | ||
| 3567 | |||
| 3568 | static struct ieee80211_ops iwl_hw_ops = { | 3575 | static struct ieee80211_ops iwl_hw_ops = { |
| 3569 | .tx = iwl_mac_tx, | 3576 | .tx = iwl_mac_tx, |
| 3570 | .start = iwl_mac_start, | 3577 | .start = iwl_mac_start, |
| @@ -3750,11 +3757,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 3750 | IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq); | 3757 | IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq); |
| 3751 | goto out_disable_msi; | 3758 | goto out_disable_msi; |
| 3752 | } | 3759 | } |
| 3753 | err = sysfs_create_group(&pdev->dev.kobj, &iwl_attribute_group); | ||
| 3754 | if (err) { | ||
| 3755 | IWL_ERR(priv, "failed to create sysfs device attributes\n"); | ||
| 3756 | goto out_free_irq; | ||
| 3757 | } | ||
| 3758 | 3760 | ||
| 3759 | iwl_setup_deferred_work(priv); | 3761 | iwl_setup_deferred_work(priv); |
| 3760 | iwl_setup_rx_handlers(priv); | 3762 | iwl_setup_rx_handlers(priv); |
| @@ -3788,15 +3790,13 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 3788 | 3790 | ||
| 3789 | err = iwl_request_firmware(priv, true); | 3791 | err = iwl_request_firmware(priv, true); |
| 3790 | if (err) | 3792 | if (err) |
| 3791 | goto out_remove_sysfs; | 3793 | goto out_destroy_workqueue; |
| 3792 | 3794 | ||
| 3793 | return 0; | 3795 | return 0; |
| 3794 | 3796 | ||
| 3795 | out_remove_sysfs: | 3797 | out_destroy_workqueue: |
| 3796 | destroy_workqueue(priv->workqueue); | 3798 | destroy_workqueue(priv->workqueue); |
| 3797 | priv->workqueue = NULL; | 3799 | priv->workqueue = NULL; |
| 3798 | sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group); | ||
| 3799 | out_free_irq: | ||
| 3800 | free_irq(priv->pci_dev->irq, priv); | 3800 | free_irq(priv->pci_dev->irq, priv); |
| 3801 | iwl_free_isr_ict(priv); | 3801 | iwl_free_isr_ict(priv); |
| 3802 | out_disable_msi: | 3802 | out_disable_msi: |
