patch-2.1.15 linux/net/core/neighbour.c

Next file: linux/net/core/net_alias.c
Previous file: linux/net/core/iovec.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/net/core/neighbour.c linux/net/core/neighbour.c
@@ -0,0 +1,293 @@
+/*
+ *	Generic address resultion entity
+ *
+ *	Authors:
+ *	Pedro Roque	<roque@di.fc.ul.pt>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <net/neighbour.h>
+
+
+static void neigh_purge_send_q(struct neighbour *neigh);
+
+void neigh_table_init(struct neigh_table *tbl, struct neigh_ops *ops, int size)
+{
+	int bmemlen;
+
+	memset(tbl, 0, sizeof(struct neigh_table));
+	
+	tbl->tbl_size = size;
+	tbl->neigh_ops = ops;
+	
+	/*
+	 *	This should only be called on initialization
+	 *	And interrupts should be on
+	 */
+
+	bmemlen = size * sizeof(struct neighbour *);
+	tbl->hash_buckets = kmalloc(bmemlen, GFP_KERNEL);
+
+	if (tbl->hash_buckets == NULL)
+	{
+		panic("unable to initialize neigh_table");
+	}
+
+	memset(tbl->hash_buckets, 0, bmemlen);
+}
+
+struct neighbour *neigh_alloc(int size, int priority)
+{
+	struct neighbour *neigh;
+	
+	neigh = kmalloc(size, priority);
+	if (neigh == NULL)
+	{
+		return NULL;
+	}
+
+	memset(neigh, 0, size);
+	
+	skb_queue_head_init(&neigh->arp_queue);
+
+	return neigh;
+}
+
+void neigh_queue_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+	struct neighbour *entry, **head;
+	entry = tbl->request_queue;
+
+	head = &tbl->request_queue;
+	
+	for (; entry; entry = entry->next)
+	{
+		head = &entry->next;
+	}
+
+	*head = neigh;
+	neigh->next = neigh->prev = NULL;
+}
+
+static struct neighbour *neigh_dequeue(struct neigh_table *tbl)
+{
+	struct neighbour *neigh;
+
+	if ((neigh = tbl->request_queue))
+	{
+		tbl->request_queue = neigh->next;
+	}
+	return neigh;
+}
+
+void neigh_table_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+        unsigned int hash_val;
+	struct neighbour **head;
+	
+	hash_val = tbl->neigh_ops->hash(neigh->primary_key) % tbl->tbl_size;
+	
+	neigh->tbl = tbl;
+	neigh->ops = tbl->neigh_ops;
+	
+	head = &tbl->hash_buckets[hash_val];
+	
+	if (!(*head))
+	{
+		neigh->next = neigh;
+		neigh->prev = neigh;
+	}
+	else
+	{
+		struct neighbour *prev;
+		struct neighbour *next;
+	
+		next = *head;
+		prev = next->prev;
+		
+
+		neigh->next = next;
+		neigh->prev = prev;
+		next->prev = neigh;
+		prev->next = neigh;
+	}
+	
+	*head = neigh;
+}
+
+struct neighbour * neigh_lookup(struct neigh_table *tbl, void *pkey,
+				int key_len, struct device *dev)
+{
+	struct neighbour *neigh, *head;
+	unsigned int hash_val;
+	
+	hash_val = tbl->neigh_ops->hash(pkey) % tbl->tbl_size;
+	head = tbl->hash_buckets[hash_val];
+
+	neigh = head;
+
+	if (neigh)
+	{
+		do {
+			if (memcmp(&neigh->primary_key, pkey, key_len) == 0)
+			{
+				if (!dev || dev == neigh->dev)
+					break;
+			}
+			neigh = neigh->next;
+			
+		} while (neigh != head);
+	}
+
+	return neigh;
+}
+
+/*
+ *	neighbour must already be out of the table;
+ *
+ */
+void neigh_destroy(struct neighbour *neigh)
+{	
+	unsigned long flags;
+
+	if (neigh->tbl)
+	{
+		printk(KERN_DEBUG "neigh_destroy: neighbour still in table. "
+		       "called from %p\n", __builtin_return_address(0));
+	}
+
+	if (neigh->ops->destructor)
+	{
+		(neigh->ops->destructor)(neigh);
+	}
+
+	neigh_purge_send_q(neigh);
+
+	save_flags(flags);
+	cli();
+	restore_flags(flags);
+	
+	kfree(neigh);
+}
+
+void neigh_unlink(struct neighbour *neigh)
+{
+	struct neigh_table *tbl;
+	struct neighbour **head;
+	unsigned int hash_val;
+	struct neighbour *next, *prev;
+	
+	tbl = neigh->tbl;	
+	neigh->tbl = NULL;
+	
+	hash_val = neigh->ops->hash(neigh->primary_key) % tbl->tbl_size;
+
+	head = &tbl->hash_buckets[hash_val];
+	tbl->tbl_entries--;
+	
+	next = neigh->next;
+	if (neigh == (*head))
+	{
+		if (next == neigh)
+		{
+			*head = NULL;
+			goto out;
+		}
+		*head = next;
+	}
+	
+	prev = neigh->prev;
+	next->prev = prev;
+	prev->next = next;
+  out:	
+	neigh->next = neigh->prev = NULL;
+}
+
+/*
+ *	Must only be called with an exclusive lock and bh disabled
+ *
+ */
+
+void ntbl_walk_table(struct neigh_table *tbl, ntbl_examine_t func,
+		     unsigned long filter, int max, void *args)
+{
+	int i;
+	
+	if (max == 0)
+		max = tbl->tbl_size;
+	
+	for (i=0; i < max; i++)
+	{
+		struct neighbour **head;
+		struct neighbour *entry;
+		
+		head = &tbl->hash_buckets[i];
+		entry = *head;
+
+		if (!entry)
+			continue;
+		
+		do {
+			if (entry->flags & (~filter))
+			{
+				int ret;
+				ret = (*func)(entry, args);
+
+				if (ret)
+				{
+					struct neighbour *curp;
+
+					curp = entry;
+					entry = curp->next;
+					
+					neigh_unlink(curp);
+					neigh_destroy(curp);
+					
+					if ((*head) == NULL)
+						break;
+					continue;
+				}
+			}
+			entry = entry->next;
+
+		} while (entry != *head);
+	}
+}
+
+void neigh_tbl_run_bh(struct neigh_table *tbl)
+{       
+	if ((tbl->tbl_bh_mask & NT_MASK_QUEUE))
+	{
+		struct neighbour *neigh;
+
+		while((neigh = neigh_dequeue(tbl)))
+		{
+			neigh_table_ins(tbl, neigh);
+		}
+		tbl->tbl_bh_mask &= ~NT_MASK_QUEUE;
+	}
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void neigh_purge_send_q(struct neighbour *neigh)
+{
+	struct sk_buff *skb;
+
+	/* Release the list of `skb' pointers. */
+	while ((skb = skb_dequeue(&neigh->arp_queue)))
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+	return;
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov