Commit 028ebff2 authored by David S. Miller's avatar David S. Miller

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

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5fc98610
...@@ -459,6 +459,22 @@ static int vnet_nack(struct vnet_port *port, void *msgbuf) ...@@ -459,6 +459,22 @@ static int vnet_nack(struct vnet_port *port, void *msgbuf)
return 0; return 0;
} }
static int handle_mcast(struct vnet_port *port, void *msgbuf)
{
struct vio_net_mcast_info *pkt = msgbuf;
if (pkt->tag.stype != VIO_SUBTYPE_ACK)
printk(KERN_ERR PFX "%s: Got unexpected MCAST reply "
"[%02x:%02x:%04x:%08x]\n",
port->vp->dev->name,
pkt->tag.type,
pkt->tag.stype,
pkt->tag.stype_env,
pkt->tag.sid);
return 0;
}
static void maybe_tx_wakeup(struct vnet *vp) static void maybe_tx_wakeup(struct vnet *vp)
{ {
struct net_device *dev = vp->dev; struct net_device *dev = vp->dev;
...@@ -544,6 +560,9 @@ static void vnet_event(void *arg, int event) ...@@ -544,6 +560,9 @@ static void vnet_event(void *arg, int event)
err = vnet_nack(port, &msgbuf); err = vnet_nack(port, &msgbuf);
} }
} else if (msgbuf.tag.type == VIO_TYPE_CTRL) { } else if (msgbuf.tag.type == VIO_TYPE_CTRL) {
if (msgbuf.tag.stype_env == VNET_MCAST_INFO)
err = handle_mcast(port, &msgbuf);
else
err = vio_control_pkt_engine(vio, &msgbuf); err = vio_control_pkt_engine(vio, &msgbuf);
if (err) if (err)
break; break;
...@@ -731,9 +750,122 @@ static int vnet_close(struct net_device *dev) ...@@ -731,9 +750,122 @@ static int vnet_close(struct net_device *dev)
return 0; return 0;
} }
static struct vnet_mcast_entry *__vnet_mc_find(struct vnet *vp, u8 *addr)
{
struct vnet_mcast_entry *m;
for (m = vp->mcast_list; m; m = m->next) {
if (!memcmp(m->addr, addr, ETH_ALEN))
return m;
}
return NULL;
}
static void __update_mc_list(struct vnet *vp, struct net_device *dev)
{
struct dev_addr_list *p;
for (p = dev->mc_list; p; p = p->next) {
struct vnet_mcast_entry *m;
m = __vnet_mc_find(vp, p->dmi_addr);
if (m) {
m->hit = 1;
continue;
}
if (!m) {
m = kzalloc(sizeof(*m), GFP_ATOMIC);
if (!m)
continue;
memcpy(m->addr, p->dmi_addr, ETH_ALEN);
m->hit = 1;
m->next = vp->mcast_list;
vp->mcast_list = m;
}
}
}
static void __send_mc_list(struct vnet *vp, struct vnet_port *port)
{
struct vio_net_mcast_info info;
struct vnet_mcast_entry *m, **pp;
int n_addrs;
memset(&info, 0, sizeof(info));
info.tag.type = VIO_TYPE_CTRL;
info.tag.stype = VIO_SUBTYPE_INFO;
info.tag.stype_env = VNET_MCAST_INFO;
info.tag.sid = vio_send_sid(&port->vio);
info.set = 1;
n_addrs = 0;
for (m = vp->mcast_list; m; m = m->next) {
if (m->sent)
continue;
m->sent = 1;
memcpy(&info.mcast_addr[n_addrs * ETH_ALEN],
m->addr, ETH_ALEN);
if (++n_addrs == VNET_NUM_MCAST) {
info.count = n_addrs;
(void) vio_ldc_send(&port->vio, &info,
sizeof(info));
n_addrs = 0;
}
}
if (n_addrs) {
info.count = n_addrs;
(void) vio_ldc_send(&port->vio, &info, sizeof(info));
}
info.set = 0;
n_addrs = 0;
pp = &vp->mcast_list;
while ((m = *pp) != NULL) {
if (m->hit) {
m->hit = 0;
pp = &m->next;
continue;
}
memcpy(&info.mcast_addr[n_addrs * ETH_ALEN],
m->addr, ETH_ALEN);
if (++n_addrs == VNET_NUM_MCAST) {
info.count = n_addrs;
(void) vio_ldc_send(&port->vio, &info,
sizeof(info));
n_addrs = 0;
}
*pp = m->next;
kfree(m);
}
if (n_addrs) {
info.count = n_addrs;
(void) vio_ldc_send(&port->vio, &info, sizeof(info));
}
}
static void vnet_set_rx_mode(struct net_device *dev) static void vnet_set_rx_mode(struct net_device *dev)
{ {
/* XXX Implement multicast support XXX */ struct vnet *vp = netdev_priv(dev);
struct vnet_port *port;
unsigned long flags;
spin_lock_irqsave(&vp->lock, flags);
if (!list_empty(&vp->port_list)) {
port = list_entry(vp->port_list.next, struct vnet_port, list);
if (port->switch_port) {
__update_mc_list(vp, dev);
__send_mc_list(vp, port);
}
}
spin_unlock_irqrestore(&vp->lock, flags);
} }
static int vnet_change_mtu(struct net_device *dev, int new_mtu) 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,6 +1202,7 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev,
switch_port = 0; switch_port = 0;
if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL) if (mdesc_get_property(hp, vdev->mp, "switch-port", NULL) != NULL)
switch_port = 1; switch_port = 1;
port->switch_port = switch_port;
spin_lock_irqsave(&vp->lock, flags); spin_lock_irqsave(&vp->lock, flags);
if (switch_port) if (switch_port)
......
...@@ -30,6 +30,8 @@ struct vnet_port { ...@@ -30,6 +30,8 @@ struct vnet_port {
struct hlist_node hash; struct hlist_node hash;
u8 raddr[ETH_ALEN]; u8 raddr[ETH_ALEN];
u8 switch_port;
u8 __pad;
struct vnet *vp; struct vnet *vp;
...@@ -53,6 +55,13 @@ static inline unsigned int vnet_hashfn(u8 *mac) ...@@ -53,6 +55,13 @@ static inline unsigned int vnet_hashfn(u8 *mac)
return val & (VNET_PORT_HASH_MASK); return val & (VNET_PORT_HASH_MASK);
} }
struct vnet_mcast_entry {
u8 addr[ETH_ALEN];
u8 sent;
u8 hit;
struct vnet_mcast_entry *next;
};
struct vnet { struct vnet {
/* Protects port_list and port_hash. */ /* Protects port_list and port_hash. */
spinlock_t lock; spinlock_t lock;
...@@ -65,6 +74,8 @@ struct vnet { ...@@ -65,6 +74,8 @@ struct vnet {
struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; struct hlist_head port_hash[VNET_PORT_HASH_SIZE];
struct vnet_mcast_entry *mcast_list;
struct list_head list; struct list_head list;
u64 local_mac; u64 local_mac;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment