diff options
author | David S. Miller <davem@sunset.davemloft.net> | 2007-07-20 05:30:25 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-20 20:14:30 -0400 |
commit | 028ebff26915df18ab0cda664e2f0582650af155 (patch) | |
tree | 9b15fa56de4c8956479b14a8b150b10eeaac1f8c /drivers/net | |
parent | 5fc986100cb253897b4e16992e805343d30a819e (diff) |
[SPARC64]: Add proper multicast support to VNET driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/sunvnet.c | 137 | ||||
-rw-r--r-- | drivers/net/sunvnet.h | 11 |
2 files changed, 146 insertions, 2 deletions
diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index ef0066bab2cf..61f98251feab 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c | |||
@@ -459,6 +459,22 @@ static int vnet_nack(struct vnet_port *port, void *msgbuf) | |||
459 | return 0; | 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 | static void maybe_tx_wakeup(struct vnet *vp) | 478 | static void maybe_tx_wakeup(struct vnet *vp) |
463 | { | 479 | { |
464 | struct net_device *dev = vp->dev; | 480 | struct net_device *dev = vp->dev; |
@@ -544,7 +560,10 @@ static void vnet_event(void *arg, int event) | |||
544 | err = vnet_nack(port, &msgbuf); | 560 | err = vnet_nack(port, &msgbuf); |
545 | } | 561 | } |
546 | } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { | 562 | } else if (msgbuf.tag.type == VIO_TYPE_CTRL) { |
547 | err = vio_control_pkt_engine(vio, &msgbuf); | 563 | if (msgbuf.tag.stype_env == VNET_MCAST_INFO) |
564 | err = handle_mcast(port, &msgbuf); | ||
565 | else | ||
566 | err = vio_control_pkt_engine(vio, &msgbuf); | ||
548 | if (err) | 567 | if (err) |
549 | break; | 568 | break; |
550 | } else { | 569 | } else { |
@@ -731,9 +750,122 @@ static int vnet_close(struct net_device *dev) | |||
731 | return 0; | 750 | return 0; |
732 | } | 751 | } |
733 | 752 | ||
753 | static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr) | ||
754 | { | ||
755 | struct vnet_mcast_entry *m; | ||
756 | |||
757 | for (m = vp->mcast_list; m; m = m->next) { | ||
758 | if (!memcmp(m->addr, addr, ETH_ALEN)) | ||
759 | return m; | ||
760 | } | ||
761 | return NULL; | ||
762 | } | ||
763 | |||
764 | static void __update_mc_list(struct vnet *vp, struct net_device *dev) | ||
765 | { | ||
766 | struct dev_addr_list *p; | ||
767 | |||
768 | for (p = dev->mc_list; p; p = p->next) { | ||
769 | struct vnet_mcast_entry *m; | ||
770 | |||
771 | m = __vnet_mc_find(vp, p->dmi_addr); | ||
772 | if (m) { | ||
773 | m->hit = 1; | ||
774 | continue; | ||
775 | } | ||
776 | |||
777 | if (!m) { | ||
778 | m = kzalloc(sizeof(*m), GFP_ATOMIC); | ||
779 | if (!m) | ||
780 | continue; | ||
781 | memcpy(m->addr, p->dmi_addr, ETH_ALEN); | ||
782 | m->hit = 1; | ||
783 | |||
784 | m->next = vp->mcast_list; | ||
785 | vp->mcast_list = m; | ||
786 | } | ||
787 | } | ||
788 | } | ||
789 | |||
790 | static void __send_mc_list(struct vnet *vp, struct vnet_port *port) | ||
791 | { | ||
792 | struct vio_net_mcast_info info; | ||
793 | struct vnet_mcast_entry *m, **pp; | ||
794 | int n_addrs; | ||
795 | |||
796 | memset(&info, 0, sizeof(info)); | ||
797 | |||
798 | info.tag.type = VIO_TYPE_CTRL; | ||
799 | info.tag.stype = VIO_SUBTYPE_INFO; | ||
800 | info.tag.stype_env = VNET_MCAST_INFO; | ||
801 | info.tag.sid = vio_send_sid(&port->vio); | ||
802 | info.set = 1; | ||
803 | |||
804 | n_addrs = 0; | ||
805 | for (m = vp->mcast_list; m; m = m->next) { | ||
806 | if (m->sent) | ||
807 | continue; | ||
808 | m->sent = 1; | ||
809 | memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], | ||
810 | m->addr, ETH_ALEN); | ||
811 | if (++n_addrs == VNET_NUM_MCAST) { | ||
812 | info.count = n_addrs; | ||
813 | |||
814 | (void) vio_ldc_send(&port->vio, &info, | ||
815 | sizeof(info)); | ||
816 | n_addrs = 0; | ||
817 | } | ||
818 | } | ||
819 | if (n_addrs) { | ||
820 | info.count = n_addrs; | ||
821 | (void) vio_ldc_send(&port->vio, &info, sizeof(info)); | ||
822 | } | ||
823 | |||
824 | info.set = 0; | ||
825 | |||
826 | n_addrs = 0; | ||
827 | pp = &vp->mcast_list; | ||
828 | while ((m = *pp) != NULL) { | ||
829 | if (m->hit) { | ||
830 | m->hit = 0; | ||
831 | pp = &m->next; | ||
832 | continue; | ||
833 | } | ||
834 | |||
835 | memcpy(&info.mcast_addr[n_addrs * ETH_ALEN], | ||
836 | m->addr, ETH_ALEN); | ||
837 | if (++n_addrs == VNET_NUM_MCAST) { | ||
838 | info.count = n_addrs; | ||
839 | (void) vio_ldc_send(&port->vio, &info, | ||
840 | sizeof(info)); | ||
841 | n_addrs = 0; | ||
842 | } | ||
843 | |||
844 | *pp = m->next; | ||
845 | kfree(m); | ||
846 | } | ||
847 | if (n_addrs) { | ||
848 | info.count = n_addrs; | ||
849 | (void) vio_ldc_send(&port->vio, &info, sizeof(info)); | ||
850 | } | ||
851 | } | ||
852 | |||
734 | static void vnet_set_rx_mode(struct net_device *dev) | 853 | static void vnet_set_rx_mode(struct net_device *dev) |
735 | { | 854 | { |
736 | /* XXX Implement multicast support XXX */ | 855 | struct vnet *vp = netdev_priv(dev); |
856 | struct vnet_port *port; | ||
857 | unsigned long flags; | ||
858 | |||
859 | spin_lock_irqsave(&vp->lock, flags); | ||
860 | if (!list_empty(&vp->port_list)) { | ||
861 | port = list_entry(vp->port_list.next, struct vnet_port, list); | ||
862 | |||
863 | if (port->switch_port) { | ||
864 | __update_mc_list(vp, dev); | ||
865 | __send_mc_list(vp, port); | ||
866 | } | ||
867 | } | ||
868 | spin_unlock_irqrestore(&vp->lock, flags); | ||
737 | } | 869 | } |
738 | 870 | ||
739 | static int vnet_change_mtu(struct net_device *dev, int new_mtu) | 871 | static int vnet_change_mtu(struct net_device *dev, int new_mtu) |
@@ -1070,6 +1202,7 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, | |||
1070 | switch_port = 0; | 1202 | switch_port = 0; |
1071 | if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) | 1203 | if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) |
1072 | switch_port = 1; | 1204 | switch_port = 1; |
1205 | port->switch_port = switch_port; | ||
1073 | 1206 | ||
1074 | spin_lock_irqsave(&vp->lock, flags); | 1207 | spin_lock_irqsave(&vp->lock, flags); |
1075 | if (switch_port) | 1208 | if (switch_port) |
diff --git a/drivers/net/sunvnet.h b/drivers/net/sunvnet.h index 7d3a0cac727b..d347a5bf24b0 100644 --- a/drivers/net/sunvnet.h +++ b/drivers/net/sunvnet.h | |||
@@ -30,6 +30,8 @@ struct vnet_port { | |||
30 | 30 | ||
31 | struct hlist_node hash; | 31 | struct hlist_node hash; |
32 | u8 raddr[ETH_ALEN]; | 32 | u8 raddr[ETH_ALEN]; |
33 | u8 switch_port; | ||
34 | u8 __pad; | ||
33 | 35 | ||
34 | struct vnet *vp; | 36 | struct vnet *vp; |
35 | 37 | ||
@@ -53,6 +55,13 @@ static inline unsigned int vnet_hashfn(u8 *mac) | |||
53 | return val & (VNET_PORT_HASH_MASK); | 55 | return val & (VNET_PORT_HASH_MASK); |
54 | } | 56 | } |
55 | 57 | ||
58 | struct vnet_mcast_entry { | ||
59 | u8 addr[ETH_ALEN]; | ||
60 | u8 sent; | ||
61 | u8 hit; | ||
62 | struct vnet_mcast_entry *next; | ||
63 | }; | ||
64 | |||
56 | struct vnet { | 65 | struct vnet { |
57 | /* Protects port_list and port_hash. */ | 66 | /* Protects port_list and port_hash. */ |
58 | spinlock_t lock; | 67 | spinlock_t lock; |
@@ -65,6 +74,8 @@ struct vnet { | |||
65 | 74 | ||
66 | struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; | 75 | struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; |
67 | 76 | ||
77 | struct vnet_mcast_entry *mcast_list; | ||
78 | |||
68 | struct list_head list; | 79 | struct list_head list; |
69 | u64 local_mac; | 80 | u64 local_mac; |
70 | }; | 81 | }; |