aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2007-07-20 05:30:25 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-07-20 20:14:30 -0400
commit028ebff26915df18ab0cda664e2f0582650af155 (patch)
tree9b15fa56de4c8956479b14a8b150b10eeaac1f8c /drivers
parent5fc986100cb253897b4e16992e805343d30a819e (diff)
[SPARC64]: Add proper multicast support to VNET driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/sunvnet.c137
-rw-r--r--drivers/net/sunvnet.h11
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
462static 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
462static void maybe_tx_wakeup(struct vnet *vp) 478static 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
753static 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
764static 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
790static 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
734static void vnet_set_rx_mode(struct net_device *dev) 853static 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
739static int vnet_change_mtu(struct net_device *dev, int new_mtu) 871static 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
58struct vnet_mcast_entry {
59 u8 addr[ETH_ALEN];
60 u8 sent;
61 u8 hit;
62 struct vnet_mcast_entry *next;
63};
64
56struct vnet { 65struct 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};