ipv4: fix redirect handling
[linux-2.6.git] / net / phonet / pn_dev.c
index d5ad794..d2df8f3 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/kernel.h>
 #include <linux/net.h>
+#include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/phonet.h>
 #include <linux/proc_fs.h>
@@ -45,9 +46,16 @@ struct phonet_net {
 
 int phonet_net_id __read_mostly;
 
+static struct phonet_net *phonet_pernet(struct net *net)
+{
+       BUG_ON(!net);
+
+       return net_generic(net, phonet_net_id);
+}
+
 struct phonet_device_list *phonet_device_list(struct net *net)
 {
-       struct phonet_net *pnn = net_generic(net, phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(net);
        return &pnn->pndevs;
 }
 
@@ -61,7 +69,8 @@ static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
        pnd->netdev = dev;
        bitmap_zero(pnd->addrs, 64);
 
-       list_add(&pnd->list, &pndevs->list);
+       BUG_ON(!mutex_is_locked(&pndevs->lock));
+       list_add_rcu(&pnd->list, &pndevs->list);
        return pnd;
 }
 
@@ -70,6 +79,7 @@ static struct phonet_device *__phonet_get(struct net_device *dev)
        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
        struct phonet_device *pnd;
 
+       BUG_ON(!mutex_is_locked(&pndevs->lock));
        list_for_each_entry(pnd, &pndevs->list, list) {
                if (pnd->netdev == dev)
                        return pnd;
@@ -77,6 +87,18 @@ static struct phonet_device *__phonet_get(struct net_device *dev)
        return NULL;
 }
 
+static struct phonet_device *__phonet_get_rcu(struct net_device *dev)
+{
+       struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
+       struct phonet_device *pnd;
+
+       list_for_each_entry_rcu(pnd, &pndevs->list, list) {
+               if (pnd->netdev == dev)
+                       return pnd;
+       }
+       return NULL;
+}
+
 static void phonet_device_destroy(struct net_device *dev)
 {
        struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
@@ -84,17 +106,16 @@ static void phonet_device_destroy(struct net_device *dev)
 
        ASSERT_RTNL();
 
-       spin_lock_bh(&pndevs->lock);
+       mutex_lock(&pndevs->lock);
        pnd = __phonet_get(dev);
        if (pnd)
-               list_del(&pnd->list);
-       spin_unlock_bh(&pndevs->lock);
+               list_del_rcu(&pnd->list);
+       mutex_unlock(&pndevs->lock);
 
        if (pnd) {
                u8 addr;
 
-               for (addr = find_first_bit(pnd->addrs, 64); addr < 64;
-                       addr = find_next_bit(pnd->addrs, 64, 1+addr))
+               for_each_set_bit(addr, pnd->addrs, 64)
                        phonet_address_notify(RTM_DELADDR, dev, addr);
                kfree(pnd);
        }
@@ -106,8 +127,8 @@ struct net_device *phonet_device_get(struct net *net)
        struct phonet_device *pnd;
        struct net_device *dev = NULL;
 
-       spin_lock_bh(&pndevs->lock);
-       list_for_each_entry(pnd, &pndevs->list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(pnd, &pndevs->list, list) {
                dev = pnd->netdev;
                BUG_ON(!dev);
 
@@ -118,7 +139,7 @@ struct net_device *phonet_device_get(struct net *net)
        }
        if (dev)
                dev_hold(dev);
-       spin_unlock_bh(&pndevs->lock);
+       rcu_read_unlock();
        return dev;
 }
 
@@ -128,7 +149,7 @@ int phonet_address_add(struct net_device *dev, u8 addr)
        struct phonet_device *pnd;
        int err = 0;
 
-       spin_lock_bh(&pndevs->lock);
+       mutex_lock(&pndevs->lock);
        /* Find or create Phonet-specific device data */
        pnd = __phonet_get(dev);
        if (pnd == NULL)
@@ -137,7 +158,7 @@ int phonet_address_add(struct net_device *dev, u8 addr)
                err = -ENOMEM;
        else if (test_and_set_bit(addr >> 2, pnd->addrs))
                err = -EEXIST;
-       spin_unlock_bh(&pndevs->lock);
+       mutex_unlock(&pndevs->lock);
        return err;
 }
 
@@ -147,27 +168,31 @@ int phonet_address_del(struct net_device *dev, u8 addr)
        struct phonet_device *pnd;
        int err = 0;
 
-       spin_lock_bh(&pndevs->lock);
+       mutex_lock(&pndevs->lock);
        pnd = __phonet_get(dev);
-       if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs))
+       if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
                err = -EADDRNOTAVAIL;
-       else if (bitmap_empty(pnd->addrs, 64)) {
-               list_del(&pnd->list);
-               kfree(pnd);
-       }
-       spin_unlock_bh(&pndevs->lock);
+               pnd = NULL;
+       } else if (bitmap_empty(pnd->addrs, 64))
+               list_del_rcu(&pnd->list);
+       else
+               pnd = NULL;
+       mutex_unlock(&pndevs->lock);
+
+       if (pnd)
+               kfree_rcu(pnd, rcu);
+
        return err;
 }
 
 /* Gets a source address toward a destination, through a interface. */
 u8 phonet_address_get(struct net_device *dev, u8 daddr)
 {
-       struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
        struct phonet_device *pnd;
        u8 saddr;
 
-       spin_lock_bh(&pndevs->lock);
-       pnd = __phonet_get(dev);
+       rcu_read_lock();
+       pnd = __phonet_get_rcu(dev);
        if (pnd) {
                BUG_ON(bitmap_empty(pnd->addrs, 64));
 
@@ -178,7 +203,7 @@ u8 phonet_address_get(struct net_device *dev, u8 daddr)
                        saddr = find_first_bit(pnd->addrs, 64) << 2;
        } else
                saddr = PN_NO_ADDR;
-       spin_unlock_bh(&pndevs->lock);
+       rcu_read_unlock();
 
        if (saddr == PN_NO_ADDR) {
                /* Fallback to another device */
@@ -200,8 +225,8 @@ int phonet_address_lookup(struct net *net, u8 addr)
        struct phonet_device *pnd;
        int err = -EADDRNOTAVAIL;
 
-       spin_lock_bh(&pndevs->lock);
-       list_for_each_entry(pnd, &pndevs->list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(pnd, &pndevs->list, list) {
                /* Don't allow unregistering devices! */
                if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
                                ((pnd->netdev->flags & IFF_UP)) != IFF_UP)
@@ -213,7 +238,7 @@ int phonet_address_lookup(struct net *net, u8 addr)
                }
        }
 found:
-       spin_unlock_bh(&pndevs->lock);
+       rcu_read_unlock();
        return err;
 }
 
@@ -242,7 +267,7 @@ static int phonet_device_autoconf(struct net_device *dev)
 
 static void phonet_route_autodel(struct net_device *dev)
 {
-       struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(dev_net(dev));
        unsigned i;
        DECLARE_BITMAP(deleted, 64);
 
@@ -259,8 +284,7 @@ static void phonet_route_autodel(struct net_device *dev)
        if (bitmap_empty(deleted, 64))
                return; /* short-circuit RCU */
        synchronize_rcu();
-       for (i = find_first_bit(deleted, 64); i < 64;
-                       i = find_next_bit(deleted, 64, i + 1)) {
+       for_each_set_bit(i, deleted, 64) {
                rtm_phonet_notify(RTM_DELROUTE, dev, i);
                dev_put(dev);
        }
@@ -292,27 +316,22 @@ static struct notifier_block phonet_device_notifier = {
 };
 
 /* Per-namespace Phonet devices handling */
-static int phonet_init_net(struct net *net)
+static int __net_init phonet_init_net(struct net *net)
 {
-       struct phonet_net *pnn = kzalloc(sizeof(*pnn), GFP_KERNEL);
-       if (!pnn)
-               return -ENOMEM;
+       struct phonet_net *pnn = phonet_pernet(net);
 
-       if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) {
-               kfree(pnn);
+       if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops))
                return -ENOMEM;
-       }
 
        INIT_LIST_HEAD(&pnn->pndevs.list);
-       spin_lock_init(&pnn->pndevs.lock);
+       mutex_init(&pnn->pndevs.lock);
        mutex_init(&pnn->routes.lock);
-       net_assign_generic(net, phonet_net_id, pnn);
        return 0;
 }
 
-static void phonet_exit_net(struct net *net)
+static void __net_exit phonet_exit_net(struct net *net)
 {
-       struct phonet_net *pnn = net_generic(net, phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(net);
        struct net_device *dev;
        unsigned i;
 
@@ -330,21 +349,23 @@ static void phonet_exit_net(struct net *net)
        rtnl_unlock();
 
        proc_net_remove(net, "phonet");
-       kfree(pnn);
 }
 
 static struct pernet_operations phonet_net_ops = {
        .init = phonet_init_net,
        .exit = phonet_exit_net,
+       .id   = &phonet_net_id,
+       .size = sizeof(struct phonet_net),
 };
 
 /* Initialize Phonet devices list */
 int __init phonet_device_init(void)
 {
-       int err = register_pernet_gen_device(&phonet_net_id, &phonet_net_ops);
+       int err = register_pernet_device(&phonet_net_ops);
        if (err)
                return err;
 
+       proc_net_fops_create(&init_net, "pnresource", 0, &pn_res_seq_fops);
        register_netdevice_notifier(&phonet_device_notifier);
        err = phonet_netlink_register();
        if (err)
@@ -356,12 +377,13 @@ void phonet_device_exit(void)
 {
        rtnl_unregister_all(PF_PHONET);
        unregister_netdevice_notifier(&phonet_device_notifier);
-       unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops);
+       unregister_pernet_device(&phonet_net_ops);
+       proc_net_remove(&init_net, "pnresource");
 }
 
 int phonet_route_add(struct net_device *dev, u8 daddr)
 {
-       struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(dev_net(dev));
        struct phonet_routes *routes = &pnn->routes;
        int err = -EEXIST;
 
@@ -378,7 +400,7 @@ int phonet_route_add(struct net_device *dev, u8 daddr)
 
 int phonet_route_del(struct net_device *dev, u8 daddr)
 {
-       struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(dev_net(dev));
        struct phonet_routes *routes = &pnn->routes;
 
        daddr = daddr >> 2;
@@ -396,24 +418,20 @@ int phonet_route_del(struct net_device *dev, u8 daddr)
        return 0;
 }
 
-struct net_device *phonet_route_get(struct net *net, u8 daddr)
+struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr)
 {
-       struct phonet_net *pnn = net_generic(net, phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(net);
        struct phonet_routes *routes = &pnn->routes;
        struct net_device *dev;
 
-       ASSERT_RTNL(); /* no need to hold the device */
-
        daddr >>= 2;
-       rcu_read_lock();
        dev = rcu_dereference(routes->table[daddr]);
-       rcu_read_unlock();
        return dev;
 }
 
 struct net_device *phonet_route_output(struct net *net, u8 daddr)
 {
-       struct phonet_net *pnn = net_generic(net, phonet_net_id);
+       struct phonet_net *pnn = phonet_pernet(net);
        struct phonet_routes *routes = &pnn->routes;
        struct net_device *dev;