aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiri Pirko <jpirko@redhat.com>2010-03-04 06:32:16 -0500
committerDavid S. Miller <davem@davemloft.net>2010-03-04 06:32:16 -0500
commit12c3400a84742f8bb0e4edc822e9ccba58781e0c (patch)
tree0d4d98975822024b6d7414739b6a88f83b553da4
parent4c32531324b83672f100692354b680625bcd7fba (diff)
rndis_wlan: correct multicast_list handling V3
My previous patch (655ffee284dfcf9a24ac0343f3e5ee6db85b85c5) added locking in a bad way. Because rndis_set_oid can sleep, there is need to prepare multicast addresses into local buffer under netif_addr_lock first, then call rndis_set_oid outside. This caused reorganizing of the whole function. Signed-off-by: Jiri Pirko <jpirko@redhat.com> Reported-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/wireless/rndis_wlan.c66
1 files changed, 41 insertions, 25 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 9f6d6bf06b8e..2887047069f2 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1496,51 +1496,67 @@ static void set_multicast_list(struct usbnet *usbdev)
1496{ 1496{
1497 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); 1497 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
1498 struct dev_mc_list *mclist; 1498 struct dev_mc_list *mclist;
1499 __le32 filter; 1499 __le32 filter, basefilter;
1500 int ret, i, size; 1500 int ret;
1501 char *buf; 1501 char *mc_addrs = NULL;
1502 int mc_count;
1502 1503
1503 filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST; 1504 basefilter = filter = RNDIS_PACKET_TYPE_DIRECTED |
1505 RNDIS_PACKET_TYPE_BROADCAST;
1504 1506
1505 netif_addr_lock_bh(usbdev->net);
1506 if (usbdev->net->flags & IFF_PROMISC) { 1507 if (usbdev->net->flags & IFF_PROMISC) {
1507 filter |= RNDIS_PACKET_TYPE_PROMISCUOUS | 1508 filter |= RNDIS_PACKET_TYPE_PROMISCUOUS |
1508 RNDIS_PACKET_TYPE_ALL_LOCAL; 1509 RNDIS_PACKET_TYPE_ALL_LOCAL;
1509 } else if (usbdev->net->flags & IFF_ALLMULTI || 1510 } else if (usbdev->net->flags & IFF_ALLMULTI) {
1510 netdev_mc_count(usbdev->net) > priv->multicast_size) { 1511 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1512 }
1513
1514 if (filter != basefilter)
1515 goto set_filter;
1516
1517 /*
1518 * mc_list should be accessed holding the lock, so copy addresses to
1519 * local buffer first.
1520 */
1521 netif_addr_lock_bh(usbdev->net);
1522 mc_count = netdev_mc_count(usbdev->net);
1523 if (mc_count > priv->multicast_size) {
1511 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST; 1524 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1512 } else if (!netdev_mc_empty(usbdev->net)) { 1525 } else if (mc_count) {
1513 size = min(priv->multicast_size, netdev_mc_count(usbdev->net)); 1526 int i = 0;
1514 buf = kmalloc(size * ETH_ALEN, GFP_KERNEL); 1527
1515 if (!buf) { 1528 mc_addrs = kmalloc(mc_count * ETH_ALEN, GFP_ATOMIC);
1529 if (!mc_addrs) {
1516 netdev_warn(usbdev->net, 1530 netdev_warn(usbdev->net,
1517 "couldn't alloc %d bytes of memory\n", 1531 "couldn't alloc %d bytes of memory\n",
1518 size * ETH_ALEN); 1532 mc_count * ETH_ALEN);
1519 netif_addr_unlock_bh(usbdev->net); 1533 netif_addr_unlock_bh(usbdev->net);
1520 return; 1534 return;
1521 } 1535 }
1522 1536
1523 i = 0; 1537 netdev_for_each_mc_addr(mclist, usbdev->net)
1524 netdev_for_each_mc_addr(mclist, usbdev->net) { 1538 memcpy(mc_addrs + i++ * ETH_ALEN,
1525 if (i == size) 1539 mclist->dmi_addr, ETH_ALEN);
1526 break; 1540 }
1527 memcpy(buf + i++ * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); 1541 netif_addr_unlock_bh(usbdev->net);
1528 }
1529 1542
1530 ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, buf, 1543 if (filter != basefilter)
1531 i * ETH_ALEN); 1544 goto set_filter;
1532 if (ret == 0 && i > 0) 1545
1546 if (mc_count) {
1547 ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, mc_addrs,
1548 mc_count * ETH_ALEN);
1549 kfree(mc_addrs);
1550 if (ret == 0)
1533 filter |= RNDIS_PACKET_TYPE_MULTICAST; 1551 filter |= RNDIS_PACKET_TYPE_MULTICAST;
1534 else 1552 else
1535 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST; 1553 filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
1536 1554
1537 netdev_dbg(usbdev->net, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n", 1555 netdev_dbg(usbdev->net, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d\n",
1538 i, priv->multicast_size, ret); 1556 mc_count, priv->multicast_size, ret);
1539
1540 kfree(buf);
1541 } 1557 }
1542 netif_addr_unlock_bh(usbdev->net);
1543 1558
1559set_filter:
1544 ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter, 1560 ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
1545 sizeof(filter)); 1561 sizeof(filter));
1546 if (ret < 0) { 1562 if (ret < 0) {