Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[SPARC64]: Add proper multicast support to VNET driver.

Signed-off-by: David S. Miller <davem@davemloft.net>

+146 -2
+135 -2
drivers/net/sunvnet.c
··· 459 459 return 0; 460 460 } 461 461 462 + static int handle_mcast(struct vnet_port *port, void *msgbuf) 463 + { 464 + struct vio_net_mcast_info *pkt = msgbuf; 465 + 466 + if (pkt->tag.stype != VIO_SUBTYPE_ACK) 467 + printk(KERN_ERR PFX "%s: Got unexpected MCAST reply " 468 + "[%02x:%02x:%04x:%08x]\n", 469 + port->vp->dev->name, 470 + pkt->tag.type, 471 + pkt->tag.stype, 472 + pkt->tag.stype_env, 473 + pkt->tag.sid); 474 + 475 + return 0; 476 + } 477 + 462 478 static void maybe_tx_wakeup(struct vnet *vp) 463 479 { 464 480 struct net_device *dev = vp->dev; ··· 560 544 err = vnet_nack(port, &msgbuf); 561 545 } 562 546 } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { 563 - err = vio_control_pkt_engine(vio, &msgbuf); 547 + if (msgbuf.tag.stype_env == VNET_MCAST_INFO) 548 + err = handle_mcast(port, &msgbuf); 549 + else 550 + err = vio_control_pkt_engine(vio, &msgbuf); 564 551 if (err) 565 552 break; 566 553 } else { ··· 750 731 return 0; 751 732 } 752 733 734 + static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) 735 + { 736 + struct vnet_mcast_entry *m; 737 + 738 + for (m = vp->mcast_list; m; m = m->next) { 739 + if (!memcmp(m->addr, addr, ETH_ALEN)) 740 + return m; 741 + } 742 + return NULL; 743 + } 744 + 745 + static void __update_mc_list(struct vnet *vp, struct net_device *dev) 746 + { 747 + struct dev_addr_list *p; 748 + 749 + for (p = dev->mc_list; p; p = p->next) { 750 + struct vnet_mcast_entry *m; 751 + 752 + m = __vnet_mc_find(vp, p->dmi_addr); 753 + if (m) { 754 + m->hit = 1; 755 + continue; 756 + } 757 + 758 + if (!m) { 759 + m = kzalloc(sizeof(*m), GFP_ATOMIC); 760 + if (!m) 761 + continue; 762 + memcpy(m->addr, p->dmi_addr, ETH_ALEN); 763 + m->hit = 1; 764 + 765 + m->next = vp->mcast_list; 766 + vp->mcast_list = m; 767 + } 768 + } 769 + } 770 + 771 + static void __send_mc_list(struct vnet *vp, struct vnet_port *port) 772 + { 773 + struct vio_net_mcast_info info; 774 + struct vnet_mcast_entry *m, **pp; 775 + int n_addrs; 776 + 777 + memset(&info, 0, sizeof(info)); 778 + 779 + info.tag.type = VIO_TYPE_CTRL; 780 + info.tag.stype = VIO_SUBTYPE_INFO; 781 + info.tag.stype_env = VNET_MCAST_INFO; 782 + info.tag.sid = vio_send_sid(&port->vio); 783 + info.set = 1; 784 + 785 + n_addrs = 0; 786 + for (m = vp->mcast_list; m; m = m->next) { 787 + if (m->sent) 788 + continue; 789 + m->sent = 1; 790 + memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 791 + m->addr, ETH_ALEN); 792 + if (++n_addrs == VNET_NUM_MCAST) { 793 + info.count = n_addrs; 794 + 795 + (void) vio_ldc_send(&port->vio, &info, 796 + sizeof(info)); 797 + n_addrs = 0; 798 + } 799 + } 800 + if (n_addrs) { 801 + info.count = n_addrs; 802 + (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 803 + } 804 + 805 + info.set = 0; 806 + 807 + n_addrs = 0; 808 + pp = &vp->mcast_list; 809 + while ((m = *pp) != NULL) { 810 + if (m->hit) { 811 + m->hit = 0; 812 + pp = &m->next; 813 + continue; 814 + } 815 + 816 + memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], 817 + m->addr, ETH_ALEN); 818 + if (++n_addrs == VNET_NUM_MCAST) { 819 + info.count = n_addrs; 820 + (void) vio_ldc_send(&port->vio, &info, 821 + sizeof(info)); 822 + n_addrs = 0; 823 + } 824 + 825 + *pp = m->next; 826 + kfree(m); 827 + } 828 + if (n_addrs) { 829 + info.count = n_addrs; 830 + (void) vio_ldc_send(&port->vio, &info, sizeof(info)); 831 + } 832 + } 833 + 753 834 static void vnet_set_rx_mode(struct net_device *dev) 754 835 { 755 - /* XXX Implement multicast support XXX */ 836 + struct vnet *vp = netdev_priv(dev); 837 + struct vnet_port *port; 838 + unsigned long flags; 839 + 840 + spin_lock_irqsave(&vp->lock, flags); 841 + if (!list_empty(&vp->port_list)) { 842 + port = list_entry(vp->port_list.next, struct vnet_port, list); 843 + 844 + if (port->switch_port) { 845 + __update_mc_list(vp, dev); 846 + __send_mc_list(vp, port); 847 + } 848 + } 849 + spin_unlock_irqrestore(&vp->lock, flags); 756 850 } 757 851 758 852 static int vnet_change_mtu(struct net_device *dev, int new_mtu) ··· 1202 1070 switch_port = 0; 1203 1071 if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) 1204 1072 switch_port = 1; 1073 + port->switch_port = switch_port; 1205 1074 1206 1075 spin_lock_irqsave(&vp->lock, flags); 1207 1076 if (switch_port)
+11
drivers/net/sunvnet.h
··· 30 30 31 31 struct hlist_node hash; 32 32 u8 raddr[ETH_ALEN]; 33 + u8 switch_port; 34 + u8 __pad; 33 35 34 36 struct vnet *vp; 35 37 ··· 55 53 return val & (VNET_PORT_HASH_MASK); 56 54 } 57 55 56 + struct vnet_mcast_entry { 57 + u8 addr[ETH_ALEN]; 58 + u8 sent; 59 + u8 hit; 60 + struct vnet_mcast_entry *next; 61 + }; 62 + 58 63 struct vnet { 59 64 /* Protects port_list and port_hash. */ 60 65 spinlock_t lock; ··· 73 64 struct list_head port_list; 74 65 75 66 struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; 67 + 68 + struct vnet_mcast_entry *mcast_list; 76 69 77 70 struct list_head list; 78 71 u64 local_mac;