qeth_arp_constructor(struct neighbour *neigh)
{
struct net_device *dev = neigh->dev;
- struct in_device *in_dev = in_dev_get(dev);
+ struct in_device *in_dev;
+ struct neigh_parms *parms;
- if (in_dev == NULL)
- return -EINVAL;
if (!qeth_verify_dev(dev)) {
- in_dev_put(in_dev);
return qeth_old_arp_constructor(neigh);
}
+ rcu_read_lock();
+ in_dev = __in_dev_get(dev);
+ if (in_dev == NULL) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ parms = in_dev->arp_parms;
+ if (parms) {
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ }
+ rcu_read_unlock();
+
neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
- if (in_dev->arp_parms)
- neigh->parms = in_dev->arp_parms;
- in_dev_put(in_dev);
neigh->nud_state = NUD_NOARP;
neigh->ops = arp_direct_ops;
neigh->output = neigh->ops->queue_xmit;
void *sysctl_table;
+ int dead;
+ atomic_t refcnt;
struct rcu_head rcu_head;
int base_reachable_time;
extern struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table *tbl);
extern void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms);
+extern void neigh_parms_destroy(struct neigh_parms *parms);
extern unsigned long neigh_rand_reach_time(unsigned long base);
extern void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
proc_handler *proc_handler);
extern void neigh_sysctl_unregister(struct neigh_parms *p);
+static inline void __neigh_parms_put(struct neigh_parms *parms)
+{
+ atomic_dec(&parms->refcnt);
+}
+
+static inline void neigh_parms_put(struct neigh_parms *parms)
+{
+ if (atomic_dec_and_test(&parms->refcnt))
+ neigh_parms_destroy(parms);
+}
+
+static inline struct neigh_parms *neigh_parms_clone(struct neigh_parms *parms)
+{
+ atomic_inc(&parms->refcnt);
+ return parms;
+}
+
/*
* Neighbour references
*/
#include <linux/bitops.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.h>
#include <net/route.h> /* for struct rtable and routing */
#include <net/icmp.h> /* icmp_send */
#include <asm/param.h> /* for HZ */
{
struct atmarp_entry *entry = NEIGH2ENTRY(neigh);
struct net_device *dev = neigh->dev;
- struct in_device *in_dev = dev->ip_ptr;
+ struct in_device *in_dev;
+ struct neigh_parms *parms;
DPRINTK("clip_constructor (neigh %p, entry %p)\n",neigh,entry);
- if (!in_dev) return -EINVAL;
neigh->type = inet_addr_type(entry->ip);
if (neigh->type != RTN_UNICAST) return -EINVAL;
- if (in_dev->arp_parms) neigh->parms = in_dev->arp_parms;
+
+ rcu_read_lock();
+ in_dev = __in_dev_get(dev);
+ if (!in_dev) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ parms = in_dev->arp_parms;
+ if (parms) {
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ }
+ rcu_read_unlock();
+
neigh->ops = &clip_neigh_ops;
neigh->output = neigh->nud_state & NUD_VALID ?
neigh->ops->connected_output : neigh->ops->output;
we must kill timers etc. and move
it to safe state.
*/
- n->parms = &tbl->parms;
skb_queue_purge(&n->arp_queue);
n->output = neigh_blackhole;
if (n->nud_state & NUD_VALID)
n->updated = n->used = now;
n->nud_state = NUD_NONE;
n->output = neigh_blackhole;
- n->parms = &tbl->parms;
+ n->parms = neigh_parms_clone(&tbl->parms);
init_timer(&n->timer);
n->timer.function = neigh_timer_handler;
n->timer.data = (unsigned long)n;
hash_val = tbl->hash(pkey, dev);
write_lock_bh(&tbl->lock);
+ if (n->parms->dead) {
+ rc = ERR_PTR(-EINVAL);
+ goto out_tbl_unlock;
+ }
+
for (n1 = tbl->hash_buckets[hash_val]; n1; n1 = n1->next) {
if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
neigh_hold(n1);
- write_unlock_bh(&tbl->lock);
rc = n1;
- goto out_neigh_release;
+ goto out_tbl_unlock;
}
}
rc = n;
out:
return rc;
+out_tbl_unlock:
+ write_unlock_bh(&tbl->lock);
out_neigh_release:
neigh_release(n);
goto out;
skb_queue_purge(&neigh->arp_queue);
dev_put(neigh->dev);
+ neigh_parms_put(neigh->parms);
NEIGH_PRINTK2("neigh %p is destroyed.\n", neigh);
if (p) {
memcpy(p, &tbl->parms, sizeof(*p));
p->tbl = tbl;
+ atomic_set(&p->refcnt, 1);
INIT_RCU_HEAD(&p->rcu_head);
p->reachable_time =
neigh_rand_reach_time(p->base_reachable_time);
struct neigh_parms *parms =
container_of(head, struct neigh_parms, rcu_head);
- kfree(parms);
+ neigh_parms_put(parms);
}
void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
for (p = &tbl->parms.next; *p; p = &(*p)->next) {
if (*p == parms) {
*p = parms->next;
+ parms->dead = 1;
write_unlock_bh(&tbl->lock);
call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
return;
NEIGH_PRINTK1("neigh_parms_release: not found\n");
}
+void neigh_parms_destroy(struct neigh_parms *parms)
+{
+ kfree(parms);
+}
+
void neigh_table_init(struct neigh_table *tbl)
{
unsigned long now = jiffies;
+ atomic_set(&tbl->parms.refcnt, 1);
INIT_RCU_HEAD(&tbl->parms.rcu_head);
tbl->parms.reachable_time =
neigh_rand_reach_time(tbl->parms.base_reachable_time);
dev->dn_ptr = NULL;
neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
+ neigh_ifdown(&dn_neigh_table, dev);
if (dn_db->router)
neigh_release(dn_db->router);
#include <linux/netfilter_decnet.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
+#include <linux/rcupdate.h>
#include <asm/atomic.h>
#include <net/neighbour.h>
#include <net/dst.h>
{
struct net_device *dev = neigh->dev;
struct dn_neigh *dn = (struct dn_neigh *)neigh;
- struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+ struct dn_dev *dn_db;
+ struct neigh_parms *parms;
- if (dn_db == NULL)
+ rcu_read_lock();
+ dn_db = dev->dn_ptr;
+ if (dn_db == NULL) {
+ rcu_read_unlock();
return -EINVAL;
+ }
- if (dn_db->neigh_parms)
- neigh->parms = dn_db->neigh_parms;
+ parms = dn_db->neigh_parms;
+ if (parms) {
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ }
+ rcu_read_unlock();
if (dn_db->use_long)
neigh->ops = &dn_long_ops;
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/net.h>
+#include <linux/rcupdate.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
{
u32 addr = *(u32*)neigh->primary_key;
struct net_device *dev = neigh->dev;
- struct in_device *in_dev = in_dev_get(dev);
-
- if (in_dev == NULL)
- return -EINVAL;
+ struct in_device *in_dev;
+ struct neigh_parms *parms;
neigh->type = inet_addr_type(addr);
- if (in_dev->arp_parms)
- neigh->parms = in_dev->arp_parms;
- in_dev_put(in_dev);
+ rcu_read_lock();
+ in_dev = __in_dev_get(dev);
+ if (in_dev == NULL) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ parms = in_dev->arp_parms;
+ if (parms) {
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ }
+ rcu_read_unlock();
if (dev->hard_header == NULL) {
neigh->nud_state = NUD_NOARP;
static void inetdev_destroy(struct in_device *in_dev)
{
struct in_ifaddr *ifa;
+ struct net_device *dev;
ASSERT_RTNL();
devinet_sysctl_unregister(&in_dev->cnf);
#endif
- in_dev->dev->ip_ptr = NULL;
+ dev = in_dev->dev;
+ dev->ip_ptr = NULL;
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(in_dev->arp_parms);
#endif
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+ arp_ifdown(dev);
+
call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
}
neigh_sysctl_unregister(idev->nd_parms);
#endif
neigh_parms_release(&nd_tbl, idev->nd_parms);
+ neigh_ifdown(&nd_tbl, dev);
in6_dev_put(idev);
}
return 0;
#include <linux/in6.h>
#include <linux/route.h>
#include <linux/init.h>
+#include <linux/rcupdate.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
{
struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
struct net_device *dev = neigh->dev;
- struct inet6_dev *in6_dev = in6_dev_get(dev);
+ struct inet6_dev *in6_dev;
+ struct neigh_parms *parms;
int is_multicast = ipv6_addr_is_multicast(addr);
- if (in6_dev == NULL)
+ rcu_read_lock();
+ in6_dev = in6_dev_get(dev);
+ if (in6_dev == NULL) {
+ rcu_read_unlock();
return -EINVAL;
+ }
- if (in6_dev->nd_parms)
- neigh->parms = in6_dev->nd_parms;
+ parms = in6_dev->nd_parms;
+ if (parms) {
+ __neigh_parms_put(neigh->parms);
+ neigh->parms = neigh_parms_clone(parms);
+ }
+ rcu_read_unlock();
neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
if (dev->hard_header == NULL) {