* Richard Underwood <richard@wuzz.demon.co.uk>
*
* Stir fried together from the IP multicast and CAP patches above
- * Alan Cox <Alan.Cox@linux.org>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
*
* Fixes:
* Alan Cox : Update the device on a real delete
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
+#include <net/net_namespace.h>
#include <net/ip.h>
#include <net/route.h>
#include <linux/skbuff.h>
{
int err;
- netif_tx_lock_bh(dev);
- err = __dev_addr_delete(&dev->mc_list, addr, alen, glbl);
+ netif_addr_lock_bh(dev);
+ err = __dev_addr_delete(&dev->mc_list, &dev->mc_count,
+ addr, alen, glbl);
if (!err) {
- dev->mc_count--;
-
/*
* We have altered the list, so the card
* loaded filter is now wrong. Fix it
__dev_set_rx_mode(dev);
}
- netif_tx_unlock_bh(dev);
+ netif_addr_unlock_bh(dev);
return err;
}
{
int err;
- netif_tx_lock_bh(dev);
- err = __dev_addr_add(&dev->mc_list, addr, alen, glbl);
- if (!err) {
- dev->mc_count++;
+ netif_addr_lock_bh(dev);
+ err = __dev_addr_add(&dev->mc_list, &dev->mc_count, addr, alen, glbl);
+ if (!err)
__dev_set_rx_mode(dev);
- }
- netif_tx_unlock_bh(dev);
+ netif_addr_unlock_bh(dev);
return err;
}
-/*
- * Discard multicast list when a device is downed
+/**
+ * dev_mc_sync - Synchronize device's multicast list to another device
+ * @to: destination device
+ * @from: source device
+ *
+ * Add newly added addresses to the destination device and release
+ * addresses that have no users left. The source device must be
+ * locked by netif_tx_lock_bh.
+ *
+ * This function is intended to be called from the dev->set_multicast_list
+ * or dev->set_rx_mode function of layered software devices.
*/
-
-void dev_mc_discard(struct net_device *dev)
+int dev_mc_sync(struct net_device *to, struct net_device *from)
{
- netif_tx_lock_bh(dev);
- __dev_addr_discard(&dev->mc_list);
- dev->mc_count = 0;
- netif_tx_unlock_bh(dev);
-}
+ int err = 0;
-#ifdef CONFIG_PROC_FS
-static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos)
-{
- struct net_device *dev;
- loff_t off = 0;
+ netif_addr_lock_bh(to);
+ err = __dev_addr_sync(&to->mc_list, &to->mc_count,
+ &from->mc_list, &from->mc_count);
+ if (!err)
+ __dev_set_rx_mode(to);
+ netif_addr_unlock_bh(to);
- read_lock(&dev_base_lock);
- for_each_netdev(dev) {
- if (off++ == *pos)
- return dev;
- }
- return NULL;
+ return err;
}
+EXPORT_SYMBOL(dev_mc_sync);
-static void *dev_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
-{
- ++*pos;
- return next_net_device((struct net_device *)v);
-}
-static void dev_mc_seq_stop(struct seq_file *seq, void *v)
+/**
+ * dev_mc_unsync - Remove synchronized addresses from the destination
+ * device
+ * @to: destination device
+ * @from: source device
+ *
+ * Remove all addresses that were added to the destination device by
+ * dev_mc_sync(). This function is intended to be called from the
+ * dev->stop function of layered software devices.
+ */
+void dev_mc_unsync(struct net_device *to, struct net_device *from)
{
- read_unlock(&dev_base_lock);
-}
+ netif_addr_lock_bh(from);
+ netif_addr_lock(to);
+ __dev_addr_unsync(&to->mc_list, &to->mc_count,
+ &from->mc_list, &from->mc_count);
+ __dev_set_rx_mode(to);
+
+ netif_addr_unlock(to);
+ netif_addr_unlock_bh(from);
+}
+EXPORT_SYMBOL(dev_mc_unsync);
+#ifdef CONFIG_PROC_FS
static int dev_mc_seq_show(struct seq_file *seq, void *v)
{
struct dev_addr_list *m;
struct net_device *dev = v;
- netif_tx_lock_bh(dev);
+ if (v == SEQ_START_TOKEN)
+ return 0;
+
+ netif_addr_lock_bh(dev);
for (m = dev->mc_list; m; m = m->next) {
int i;
seq_putc(seq, '\n');
}
- netif_tx_unlock_bh(dev);
+ netif_addr_unlock_bh(dev);
return 0;
}
static const struct seq_operations dev_mc_seq_ops = {
- .start = dev_mc_seq_start,
- .next = dev_mc_seq_next,
- .stop = dev_mc_seq_stop,
+ .start = dev_seq_start,
+ .next = dev_seq_next,
+ .stop = dev_seq_stop,
.show = dev_mc_seq_show,
};
static int dev_mc_seq_open(struct inode *inode, struct file *file)
{
- return seq_open(file, &dev_mc_seq_ops);
+ return seq_open_net(inode, file, &dev_mc_seq_ops,
+ sizeof(struct seq_net_private));
}
static const struct file_operations dev_mc_seq_fops = {
.open = dev_mc_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
#endif
+static int __net_init dev_mc_net_init(struct net *net)
+{
+ if (!proc_net_fops_create(net, "dev_mcast", 0, &dev_mc_seq_fops))
+ return -ENOMEM;
+ return 0;
+}
+
+static void __net_exit dev_mc_net_exit(struct net *net)
+{
+ proc_net_remove(net, "dev_mcast");
+}
+
+static struct pernet_operations __net_initdata dev_mc_net_ops = {
+ .init = dev_mc_net_init,
+ .exit = dev_mc_net_exit,
+};
+
void __init dev_mcast_init(void)
{
- proc_net_fops_create("dev_mcast", 0, &dev_mc_seq_fops);
+ register_pernet_subsys(&dev_mc_net_ops);
}
EXPORT_SYMBOL(dev_mc_add);