Commit fdab4b10 authored by David S. Miller's avatar David S. Miller Committed by David S. Miller

[NET]: Abstract neigh table walking.

This way no code actually needs to traverse the
neigh hash tables outside of net/core/neighbour.c
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 559b540d
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/seq_file.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
...@@ -224,6 +225,24 @@ extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); ...@@ -224,6 +225,24 @@ extern int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg); extern int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
extern void neigh_app_ns(struct neighbour *n); extern void neigh_app_ns(struct neighbour *n);
extern void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie);
extern void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *));
extern void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *));
struct neigh_seq_state {
struct neigh_table *tbl;
void *(*neigh_sub_iter)(struct neigh_seq_state *state,
struct neighbour *n, loff_t *pos);
unsigned int bucket;
unsigned int flags;
#define NEIGH_SEQ_NEIGH_ONLY 0x00000001
#define NEIGH_SEQ_IS_PNEIGH 0x00000002
#define NEIGH_SEQ_SKIP_NOARP 0x00000004
};
extern void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *, unsigned int);
extern void *neigh_seq_next(struct seq_file *, void *, loff_t *);
extern void neigh_seq_stop(struct seq_file *, void *);
extern int neigh_sysctl_register(struct net_device *dev, extern int neigh_sysctl_register(struct net_device *dev,
struct neigh_parms *p, struct neigh_parms *p,
int p_id, int pdev_id, int p_id, int pdev_id,
......
...@@ -123,64 +123,49 @@ static void unlink_clip_vcc(struct clip_vcc *clip_vcc) ...@@ -123,64 +123,49 @@ static void unlink_clip_vcc(struct clip_vcc *clip_vcc)
spin_unlock_bh(&entry->neigh->dev->xmit_lock); spin_unlock_bh(&entry->neigh->dev->xmit_lock);
} }
/* The neighbour entry n->lock is held. */
static void idle_timer_check(unsigned long dummy) static int neigh_check_cb(struct neighbour *n)
{ {
int i; struct atmarp_entry *entry = NEIGH2ENTRY(n);
struct clip_vcc *cv;
/*DPRINTK("idle_timer_check\n");*/ for (cv = entry->vccs; cv; cv = cv->next) {
write_lock(&clip_tbl.lock); unsigned long exp = cv->last_use + cv->idle_timeout;
for (i = 0; i <= NEIGH_HASHMASK; i++) {
struct neighbour **np; if (cv->idle_timeout && time_after(jiffies, exp)) {
DPRINTK("releasing vcc %p->%p of entry %p\n",
for (np = &clip_tbl.hash_buckets[i]; *np;) { cv, cv->vcc, entry);
struct neighbour *n = *np; vcc_release_async(cv->vcc, -ETIMEDOUT);
struct atmarp_entry *entry = NEIGH2ENTRY(n);
struct clip_vcc *clip_vcc;
write_lock(&n->lock);
for (clip_vcc = entry->vccs; clip_vcc;
clip_vcc = clip_vcc->next)
if (clip_vcc->idle_timeout &&
time_after(jiffies, clip_vcc->last_use+
clip_vcc->idle_timeout)) {
DPRINTK("releasing vcc %p->%p of "
"entry %p\n",clip_vcc,clip_vcc->vcc,
entry);
vcc_release_async(clip_vcc->vcc,
-ETIMEDOUT);
}
if (entry->vccs ||
time_before(jiffies, entry->expires)) {
np = &n->next;
write_unlock(&n->lock);
continue;
}
if (atomic_read(&n->refcnt) > 1) {
struct sk_buff *skb;
DPRINTK("destruction postponed with ref %d\n",
atomic_read(&n->refcnt));
while ((skb = skb_dequeue(&n->arp_queue)) !=
NULL)
dev_kfree_skb(skb);
np = &n->next;
write_unlock(&n->lock);
continue;
}
*np = n->next;
DPRINTK("expired neigh %p\n",n);
n->dead = 1;
write_unlock(&n->lock);
neigh_release(n);
} }
} }
if (entry->vccs || time_before(jiffies, entry->expires))
return 0;
if (atomic_read(&n->refcnt) > 1) {
struct sk_buff *skb;
DPRINTK("destruction postponed with ref %d\n",
atomic_read(&n->refcnt));
while ((skb = skb_dequeue(&n->arp_queue)) != NULL)
dev_kfree_skb(skb);
return 0;
}
DPRINTK("expired neigh %p\n",n);
return 1;
}
static void idle_timer_check(unsigned long dummy)
{
write_lock(&clip_tbl.lock);
__neigh_for_each_release(&clip_tbl, neigh_check_cb);
mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ); mod_timer(&idle_timer, jiffies+CLIP_CHECK_INTERVAL*HZ);
write_unlock(&clip_tbl.lock); write_unlock(&clip_tbl.lock);
} }
static int clip_arp_rcv(struct sk_buff *skb) static int clip_arp_rcv(struct sk_buff *skb)
{ {
struct atm_vcc *vcc; struct atm_vcc *vcc;
...@@ -833,120 +818,126 @@ static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr) ...@@ -833,120 +818,126 @@ static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr)
} }
} }
/* This means the neighbour entry has no attached VCC objects. */
#define SEQ_NO_VCC_TOKEN ((void *) 2)
static void atmarp_info(struct seq_file *seq, struct net_device *dev, static void atmarp_info(struct seq_file *seq, struct net_device *dev,
struct atmarp_entry *entry, struct clip_vcc *clip_vcc) struct atmarp_entry *entry, struct clip_vcc *clip_vcc)
{ {
unsigned long exp;
char buf[17]; char buf[17];
int svc, off; int svc, llc, off;
svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
(clip_vcc->vcc->sk->sk_family == AF_ATMSVC));
llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
clip_vcc->encap);
if (clip_vcc == SEQ_NO_VCC_TOKEN)
exp = entry->neigh->used;
else
exp = clip_vcc->last_use;
svc = !clip_vcc || clip_vcc->vcc->sk->sk_family == AF_ATMSVC; exp = (jiffies - exp) / HZ;
seq_printf(seq, "%-6s%-4s%-4s%5ld ", dev->name, svc ? "SVC" : "PVC",
!clip_vcc || clip_vcc->encap ? "LLC" : "NULL",
(jiffies-(clip_vcc ? clip_vcc->last_use : entry->neigh->used))/HZ);
off = scnprintf(buf, sizeof(buf) - 1, "%d.%d.%d.%d", NIPQUAD(entry->ip)); seq_printf(seq, "%-6s%-4s%-4s%5ld ",
dev->name,
svc ? "SVC" : "PVC",
llc ? "LLC" : "NULL",
exp);
off = scnprintf(buf, sizeof(buf) - 1, "%d.%d.%d.%d",
NIPQUAD(entry->ip));
while (off < 16) while (off < 16)
buf[off++] = ' '; buf[off++] = ' ';
buf[off] = '\0'; buf[off] = '\0';
seq_printf(seq, "%s", buf); seq_printf(seq, "%s", buf);
if (!clip_vcc) { if (clip_vcc == SEQ_NO_VCC_TOKEN) {
if (time_before(jiffies, entry->expires)) if (time_before(jiffies, entry->expires))
seq_printf(seq, "(resolving)\n"); seq_printf(seq, "(resolving)\n");
else else
seq_printf(seq, "(expired, ref %d)\n", seq_printf(seq, "(expired, ref %d)\n",
atomic_read(&entry->neigh->refcnt)); atomic_read(&entry->neigh->refcnt));
} else if (!svc) { } else if (!svc) {
seq_printf(seq, "%d.%d.%d\n", clip_vcc->vcc->dev->number, seq_printf(seq, "%d.%d.%d\n",
clip_vcc->vcc->vpi, clip_vcc->vcc->vci); clip_vcc->vcc->dev->number,
clip_vcc->vcc->vpi,
clip_vcc->vcc->vci);
} else { } else {
svc_addr(seq, &clip_vcc->vcc->remote); svc_addr(seq, &clip_vcc->vcc->remote);
seq_putc(seq, '\n'); seq_putc(seq, '\n');
} }
} }
struct arp_state { struct clip_seq_state {
int bucket; /* This member must be first. */
struct neighbour *n; struct neigh_seq_state ns;
/* Local to clip specific iteration. */
struct clip_vcc *vcc; struct clip_vcc *vcc;
}; };
static void *arp_vcc_walk(struct arp_state *state,
struct atmarp_entry *e, loff_t *l)
{
struct clip_vcc *vcc = state->vcc;
if (!vcc) static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e,
vcc = e->vccs; struct clip_vcc *curr)
if (vcc == (void *)1) {
vcc = e->vccs;
--*l;
}
for (; vcc; vcc = vcc->next) {
if (--*l < 0)
break;
}
state->vcc = vcc;
return (*l < 0) ? state : NULL;
}
static void *arp_get_idx(struct arp_state *state, loff_t l)
{ {
void *v = NULL; if (!curr) {
curr = e->vccs;
for (; state->bucket <= NEIGH_HASHMASK; state->bucket++) { if (!curr)
for (; state->n; state->n = state->n->next) { return SEQ_NO_VCC_TOKEN;
v = arp_vcc_walk(state, NEIGH2ENTRY(state->n), &l); return curr;
if (v)
goto done;
}
state->n = clip_tbl.hash_buckets[state->bucket + 1];
} }
done: if (curr == SEQ_NO_VCC_TOKEN)
return v; return NULL;
curr = curr->next;
return curr;
} }
static void *arp_seq_start(struct seq_file *seq, loff_t *pos) static void *clip_seq_vcc_walk(struct clip_seq_state *state,
struct atmarp_entry *e, loff_t *pos)
{ {
struct arp_state *state = seq->private; struct clip_vcc *vcc = state->vcc;
void *ret = (void *)1;
read_lock_bh(&clip_tbl.lock);
state->bucket = 0;
state->n = clip_tbl.hash_buckets[0];
state->vcc = (void *)1;
if (*pos)
ret = arp_get_idx(state, *pos);
return ret;
}
static void arp_seq_stop(struct seq_file *seq, void *v) vcc = clip_seq_next_vcc(e, vcc);
if (vcc && pos != NULL) {
while (*pos) {
vcc = clip_seq_next_vcc(e, vcc);
if (!vcc)
break;
--(*pos);
}
}
state->vcc = vcc;
return vcc;
}
static void *clip_seq_sub_iter(struct neigh_seq_state *_state,
struct neighbour *n, loff_t *pos)
{ {
struct arp_state *state = seq->private; struct clip_seq_state *state = (struct clip_seq_state *) _state;
if (state->bucket != -1) return clip_seq_vcc_walk(state, NEIGH2ENTRY(n), pos);
read_unlock_bh(&clip_tbl.lock);
} }
static void *arp_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void *clip_seq_start(struct seq_file *seq, loff_t *pos)
{ {
struct arp_state *state = seq->private; return neigh_seq_start(seq, pos, &clip_tbl, NEIGH_SEQ_NEIGH_ONLY);
v = arp_get_idx(state, 1);
*pos += !!PTR_ERR(v);
return v;
} }
static int arp_seq_show(struct seq_file *seq, void *v) static int clip_seq_show(struct seq_file *seq, void *v)
{ {
static char atm_arp_banner[] = static char atm_arp_banner[] =
"IPitf TypeEncp Idle IP address ATM address\n"; "IPitf TypeEncp Idle IP address ATM address\n";
if (v == (void *)1) if (v == SEQ_START_TOKEN) {
seq_puts(seq, atm_arp_banner); seq_puts(seq, atm_arp_banner);
else { } else {
struct arp_state *state = seq->private; struct clip_seq_state *state = seq->private;
struct neighbour *n = state->n; struct neighbour *n = v;
struct clip_vcc *vcc = state->vcc; struct clip_vcc *vcc = state->vcc;
atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc); atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc);
...@@ -955,15 +946,15 @@ static int arp_seq_show(struct seq_file *seq, void *v) ...@@ -955,15 +946,15 @@ static int arp_seq_show(struct seq_file *seq, void *v)
} }
static struct seq_operations arp_seq_ops = { static struct seq_operations arp_seq_ops = {
.start = arp_seq_start, .start = clip_seq_start,
.next = arp_seq_next, .next = neigh_seq_next,
.stop = arp_seq_stop, .stop = neigh_seq_stop,
.show = arp_seq_show, .show = clip_seq_show,
}; };
static int arp_seq_open(struct inode *inode, struct file *file) static int arp_seq_open(struct inode *inode, struct file *file)
{ {
struct arp_state *state; struct clip_seq_state *state;
struct seq_file *seq; struct seq_file *seq;
int rc = -EAGAIN; int rc = -EAGAIN;
...@@ -972,6 +963,8 @@ static int arp_seq_open(struct inode *inode, struct file *file) ...@@ -972,6 +963,8 @@ static int arp_seq_open(struct inode *inode, struct file *file)
rc = -ENOMEM; rc = -ENOMEM;
goto out_kfree; goto out_kfree;
} }
memset(state, 0, sizeof(*state));
state->ns.neigh_sub_iter = clip_seq_sub_iter;
rc = seq_open(file, &arp_seq_ops); rc = seq_open(file, &arp_seq_ops);
if (rc) if (rc)
...@@ -987,16 +980,11 @@ static int arp_seq_open(struct inode *inode, struct file *file) ...@@ -987,16 +980,11 @@ static int arp_seq_open(struct inode *inode, struct file *file)
goto out; goto out;
} }
static int arp_seq_release(struct inode *inode, struct file *file)
{
return seq_release_private(inode, file);
}
static struct file_operations arp_seq_fops = { static struct file_operations arp_seq_fops = {
.open = arp_seq_open, .open = arp_seq_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = arp_seq_release, .release = seq_release_private,
.owner = THIS_MODULE .owner = THIS_MODULE
}; };
#endif #endif
......
...@@ -1489,6 +1489,266 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1489,6 +1489,266 @@ int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len; return skb->len;
} }
void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie)
{
int chain;
read_lock_bh(&tbl->lock);
for (chain = 0; chain <= NEIGH_HASHMASK; chain++) {
struct neighbour *n;
for (n = tbl->hash_buckets[chain]; n; n = n->next)
cb(n, cookie);
}
read_unlock_bh(&tbl->lock);
}
EXPORT_SYMBOL(neigh_for_each);
/* The tbl->lock must be held as a writer and BH disabled. */
void __neigh_for_each_release(struct neigh_table *tbl,
int (*cb)(struct neighbour *))
{
int chain;
for (chain = 0; chain <= NEIGH_HASHMASK; chain++) {
struct neighbour *n, **np;
np = &tbl->hash_buckets[chain];
while ((n = *np) != NULL) {
int release;
write_lock(&n->lock);
release = cb(n);
if (release) {
*np = n->next;
n->dead = 1;
} else
np = &n->next;
write_unlock(&n->lock);
if (release)
neigh_release(n);
}
}
}
EXPORT_SYMBOL(__neigh_for_each_release);
#ifdef CONFIG_PROC_FS
static struct neighbour *neigh_get_first(struct seq_file *seq)
{
struct neigh_seq_state *state = seq->private;
struct neigh_table *tbl = state->tbl;
struct neighbour *n = NULL;
int bucket = state->bucket;
state->flags &= ~NEIGH_SEQ_IS_PNEIGH;
for (bucket = 0; bucket <= NEIGH_HASHMASK; bucket++) {
n = tbl->hash_buckets[bucket];
while (n) {
if (state->neigh_sub_iter) {
loff_t fakep = 0;
void *v;
v = state->neigh_sub_iter(state, n, &fakep);
if (!v)
goto next;
}
if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
break;
if (n->nud_state & ~NUD_NOARP)
break;
next:
n = n->next;
}
if (n)
break;
}
state->bucket = bucket;
return n;
}
static struct neighbour *neigh_get_next(struct seq_file *seq,
struct neighbour *n,
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
struct neigh_table *tbl = state->tbl;
if (state->neigh_sub_iter) {
void *v = state->neigh_sub_iter(state, n, pos);
if (v)
return n;
}
n = n->next;
while (1) {
while (n) {
if (state->neigh_sub_iter) {
void *v = state->neigh_sub_iter(state, n, pos);
if (v)
return n;
goto next;
}
if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
break;
if (n->nud_state & ~NUD_NOARP)
break;
next:
n = n->next;
}
if (n)
break;
if (++state->bucket > NEIGH_HASHMASK)
break;
n = tbl->hash_buckets[state->bucket];
}
if (n && pos)
--(*pos);
return n;
}
static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
{
struct neighbour *n = neigh_get_first(seq);
if (n) {
while (*pos) {
n = neigh_get_next(seq, n, pos);
if (!n)
break;
}
}
return *pos ? NULL : n;
}
static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
{
struct neigh_seq_state *state = seq->private;
struct neigh_table *tbl = state->tbl;
struct pneigh_entry *pn = NULL;
int bucket = state->bucket;
state->flags |= NEIGH_SEQ_IS_PNEIGH;
for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
pn = tbl->phash_buckets[bucket];
if (pn)
break;
}
state->bucket = bucket;
return pn;
}
static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
struct pneigh_entry *pn,
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
struct neigh_table *tbl = state->tbl;
pn = pn->next;
while (!pn) {
if (++state->bucket > PNEIGH_HASHMASK)
break;
pn = tbl->phash_buckets[state->bucket];
if (pn)
break;
}
if (pn && pos)
--(*pos);
return pn;
}
static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos)
{
struct pneigh_entry *pn = pneigh_get_first(seq);
if (pn) {
while (*pos) {
pn = pneigh_get_next(seq, pn, pos);
if (!pn)
break;
}
}
return *pos ? NULL : pn;
}
static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
void *rc;
rc = neigh_get_idx(seq, pos);
if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY))
rc = pneigh_get_idx(seq, pos);
return rc;
}
void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
{
struct neigh_seq_state *state = seq->private;
loff_t pos_minus_one;
state->tbl = tbl;
state->bucket = 0;
state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH);
read_lock_bh(&tbl->lock);
pos_minus_one = *pos - 1;
return *pos ? neigh_get_idx_any(seq, &pos_minus_one) : SEQ_START_TOKEN;
}
EXPORT_SYMBOL(neigh_seq_start);
void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct neigh_seq_state *state;
void *rc;
if (v == SEQ_START_TOKEN) {
rc = neigh_get_idx(seq, pos);
goto out;
}
state = seq->private;
if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) {
rc = neigh_get_next(seq, v, NULL);
if (rc)
goto out;
if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY))
rc = pneigh_get_first(seq);
} else {
BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY);
rc = pneigh_get_next(seq, v, NULL);
}
out:
++(*pos);
return rc;
}
EXPORT_SYMBOL(neigh_seq_next);
void neigh_seq_stop(struct seq_file *seq, void *v)
{
struct neigh_seq_state *state = seq->private;
struct neigh_table *tbl = state->tbl;
read_unlock_bh(&tbl->lock);
}
EXPORT_SYMBOL(neigh_seq_stop);
#endif /* CONFIG_PROC_FS */
#ifdef CONFIG_ARPD #ifdef CONFIG_ARPD
void neigh_app_ns(struct neighbour *n) void neigh_app_ns(struct neighbour *n)
{ {
......
...@@ -514,141 +514,66 @@ static char *dn_find_slot(char *base, int max, int priority) ...@@ -514,141 +514,66 @@ static char *dn_find_slot(char *base, int max, int priority)
return (*min < priority) ? (min - 6) : NULL; return (*min < priority) ? (min - 6) : NULL;
} }
int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n) struct elist_cb_state {
{ struct net_device *dev;
int t = 0; unsigned char *ptr;
int i; unsigned char *rs;
struct neighbour *neigh; int t, n;
struct dn_neigh *dn;
struct neigh_table *tbl = &dn_neigh_table;
unsigned char *rs = ptr;
struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
read_lock_bh(&tbl->lock);
for(i = 0; i < NEIGH_HASHMASK; i++) {
for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
if (neigh->dev != dev)
continue;
dn = (struct dn_neigh *)neigh;
if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
continue;
if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
continue;
if (t == n)
rs = dn_find_slot(ptr, n, dn->priority);
else
t++;
if (rs == NULL)
continue;
dn_dn2eth(rs, dn->addr);
rs += 6;
*rs = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
*rs |= dn->priority;
rs++;
}
}
read_unlock_bh(&tbl->lock);
return t;
}
#ifdef CONFIG_PROC_FS
struct dn_neigh_iter_state {
int bucket;
}; };
static struct neighbour *neigh_get_first(struct seq_file *seq) static void neigh_elist_cb(struct neighbour *neigh, void *_info)
{
struct dn_neigh_iter_state *state = seq->private;
struct neighbour *n = NULL;
for(state->bucket = 0;
state->bucket <= NEIGH_HASHMASK;
++state->bucket) {
n = dn_neigh_table.hash_buckets[state->bucket];
if (n)
break;
}
return n;
}
static struct neighbour *neigh_get_next(struct seq_file *seq,
struct neighbour *n)
{ {
struct dn_neigh_iter_state *state = seq->private; struct elist_cb_state *s = _info;
struct dn_dev *dn_db;
n = n->next; struct dn_neigh *dn;
try_again:
if (n)
goto out;
if (++state->bucket > NEIGH_HASHMASK)
goto out;
n = dn_neigh_table.hash_buckets[state->bucket];
goto try_again;
out:
return n;
}
static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) if (neigh->dev != s->dev)
{ return;
struct neighbour *n = neigh_get_first(seq);
if (n) dn = (struct dn_neigh *) neigh;
while(*pos && (n = neigh_get_next(seq, n))) if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
--*pos; return;
return *pos ? NULL : n;
}
static void *dn_neigh_get_idx(struct seq_file *seq, loff_t pos) dn_db = (struct dn_dev *) s->dev->dn_ptr;
{ if (dn_db->parms.forwarding == 1 && (dn->flags & DN_NDFLAG_R2))
void *rc; return;
read_lock_bh(&dn_neigh_table.lock);
rc = neigh_get_idx(seq, &pos);
if (!rc) {
read_unlock_bh(&dn_neigh_table.lock);
}
return rc;
}
static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos) if (s->t == s->n)
{ s->rs = dn_find_slot(s->ptr, s->n, dn->priority);
return *pos ? dn_neigh_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; else
s->t++;
if (s->rs == NULL)
return;
dn_dn2eth(s->rs, dn->addr);
s->rs += 6;
*(s->rs) = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
*(s->rs) |= dn->priority;
s->rs++;
} }
static void *dn_neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n)
{ {
void *rc; struct elist_cb_state state;
state.dev = dev;
state.t = 0;
state.n = n;
state.ptr = ptr;
state.rs = ptr;
if (v == SEQ_START_TOKEN) { neigh_for_each(&dn_neigh_table, neigh_elist_cb, &state);
rc = dn_neigh_get_idx(seq, 0);
goto out;
}
rc = neigh_get_next(seq, v); return state.t;
if (rc)
goto out;
read_unlock_bh(&dn_neigh_table.lock);
out:
++*pos;
return rc;
} }
static void dn_neigh_seq_stop(struct seq_file *seq, void *v)
{ #ifdef CONFIG_PROC_FS
if (v && v != SEQ_START_TOKEN)
read_unlock_bh(&dn_neigh_table.lock);
}
static inline void dn_neigh_format_entry(struct seq_file *seq, static inline void dn_neigh_format_entry(struct seq_file *seq,
struct neighbour *n) struct neighbour *n)
{ {
struct dn_neigh *dn = (struct dn_neigh *)n; struct dn_neigh *dn = (struct dn_neigh *) n;
char buf[DN_ASCBUF_LEN]; char buf[DN_ASCBUF_LEN];
read_lock(&n->lock); read_lock(&n->lock);
...@@ -675,10 +600,16 @@ static int dn_neigh_seq_show(struct seq_file *seq, void *v) ...@@ -675,10 +600,16 @@ static int dn_neigh_seq_show(struct seq_file *seq, void *v)
return 0; return 0;
} }
static void *dn_neigh_seq_start(struct seq_file *seq, loff_t *pos)
{
return neigh_seq_start(seq, pos, &dn_neigh_table,
NEIGH_SEQ_NEIGH_ONLY);
}
static struct seq_operations dn_neigh_seq_ops = { static struct seq_operations dn_neigh_seq_ops = {
.start = dn_neigh_seq_start, .start = dn_neigh_seq_start,
.next = dn_neigh_seq_next, .next = neigh_seq_next,
.stop = dn_neigh_seq_stop, .stop = neigh_seq_stop,
.show = dn_neigh_seq_show, .show = dn_neigh_seq_show,
}; };
...@@ -686,11 +617,12 @@ static int dn_neigh_seq_open(struct inode *inode, struct file *file) ...@@ -686,11 +617,12 @@ static int dn_neigh_seq_open(struct inode *inode, struct file *file)
{ {
struct seq_file *seq; struct seq_file *seq;
int rc = -ENOMEM; int rc = -ENOMEM;
struct dn_neigh_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) if (!s)
goto out; goto out;
memset(s, 0, sizeof(*s));
rc = seq_open(file, &dn_neigh_seq_ops); rc = seq_open(file, &dn_neigh_seq_ops);
if (rc) if (rc)
goto out_kfree; goto out_kfree;
......
...@@ -1269,162 +1269,10 @@ static char *ax2asc2(ax25_address *a, char *buf) ...@@ -1269,162 +1269,10 @@ static char *ax2asc2(ax25_address *a, char *buf)
} }
#endif /* CONFIG_AX25 */ #endif /* CONFIG_AX25 */
struct arp_iter_state {
int is_pneigh, bucket;
};
static struct neighbour *neigh_get_first(struct seq_file *seq)
{
struct arp_iter_state* state = seq->private;
struct neighbour *n = NULL;
state->is_pneigh = 0;
for (state->bucket = 0;
state->bucket <= NEIGH_HASHMASK;
++state->bucket) {
n = arp_tbl.hash_buckets[state->bucket];
while (n && !(n->nud_state & ~NUD_NOARP))
n = n->next;
if (n)
break;
}
return n;
}
static struct neighbour *neigh_get_next(struct seq_file *seq,
struct neighbour *n)
{
struct arp_iter_state* state = seq->private;
do {
n = n->next;
/* Don't confuse "arp -a" w/ magic entries */
try_again:
;
} while (n && !(n->nud_state & ~NUD_NOARP));
if (n)
goto out;
if (++state->bucket > NEIGH_HASHMASK)
goto out;
n = arp_tbl.hash_buckets[state->bucket];
goto try_again;
out:
return n;
}
static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
{
struct neighbour *n = neigh_get_first(seq);
if (n)
while (*pos && (n = neigh_get_next(seq, n)))
--*pos;
return *pos ? NULL : n;
}
static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
{
struct arp_iter_state* state = seq->private;
struct pneigh_entry *pn;
state->is_pneigh = 1;
for (state->bucket = 0;
state->bucket <= PNEIGH_HASHMASK;
++state->bucket) {
pn = arp_tbl.phash_buckets[state->bucket];
if (pn)
break;
}
return pn;
}
static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
struct pneigh_entry *pn)
{
struct arp_iter_state* state = seq->private;
pn = pn->next;
while (!pn) {
if (++state->bucket > PNEIGH_HASHMASK)
break;
pn = arp_tbl.phash_buckets[state->bucket];
}
return pn;
}
static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t pos)
{
struct pneigh_entry *pn = pneigh_get_first(seq);
if (pn)
while (pos && (pn = pneigh_get_next(seq, pn)))
--pos;
return pos ? NULL : pn;
}
static void *arp_get_idx(struct seq_file *seq, loff_t pos)
{
void *rc;
read_lock_bh(&arp_tbl.lock);
rc = neigh_get_idx(seq, &pos);
if (!rc) {
read_unlock_bh(&arp_tbl.lock);
rc = pneigh_get_idx(seq, pos);
}
return rc;
}
static void *arp_seq_start(struct seq_file *seq, loff_t *pos)
{
struct arp_iter_state* state = seq->private;
state->is_pneigh = 0;
state->bucket = 0;
return *pos ? arp_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
}
static void *arp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
void *rc;
struct arp_iter_state* state;
if (v == SEQ_START_TOKEN) {
rc = arp_get_idx(seq, 0);
goto out;
}
state = seq->private;
if (!state->is_pneigh) {
rc = neigh_get_next(seq, v);
if (rc)
goto out;
read_unlock_bh(&arp_tbl.lock);
rc = pneigh_get_first(seq);
} else
rc = pneigh_get_next(seq, v);
out:
++*pos;
return rc;
}
static void arp_seq_stop(struct seq_file *seq, void *v)
{
struct arp_iter_state* state = seq->private;
if (!state->is_pneigh && v != SEQ_START_TOKEN)
read_unlock_bh(&arp_tbl.lock);
}
#define HBUFFERLEN 30 #define HBUFFERLEN 30
static __inline__ void arp_format_neigh_entry(struct seq_file *seq, static void arp_format_neigh_entry(struct seq_file *seq,
struct neighbour *n) struct neighbour *n)
{ {
char hbuffer[HBUFFERLEN]; char hbuffer[HBUFFERLEN];
const char hexbuf[] = "0123456789ABCDEF"; const char hexbuf[] = "0123456789ABCDEF";
...@@ -1455,8 +1303,8 @@ static __inline__ void arp_format_neigh_entry(struct seq_file *seq, ...@@ -1455,8 +1303,8 @@ static __inline__ void arp_format_neigh_entry(struct seq_file *seq,
read_unlock(&n->lock); read_unlock(&n->lock);
} }
static __inline__ void arp_format_pneigh_entry(struct seq_file *seq, static void arp_format_pneigh_entry(struct seq_file *seq,
struct pneigh_entry *n) struct pneigh_entry *n)
{ {
struct net_device *dev = n->dev; struct net_device *dev = n->dev;
int hatype = dev ? dev->type : 0; int hatype = dev ? dev->type : 0;
...@@ -1470,13 +1318,13 @@ static __inline__ void arp_format_pneigh_entry(struct seq_file *seq, ...@@ -1470,13 +1318,13 @@ static __inline__ void arp_format_pneigh_entry(struct seq_file *seq,
static int arp_seq_show(struct seq_file *seq, void *v) static int arp_seq_show(struct seq_file *seq, void *v)
{ {
if (v == SEQ_START_TOKEN) if (v == SEQ_START_TOKEN) {
seq_puts(seq, "IP address HW type Flags " seq_puts(seq, "IP address HW type Flags "
"HW address Mask Device\n"); "HW address Mask Device\n");
else { } else {
struct arp_iter_state* state = seq->private; struct neigh_seq_state *state = seq->private;
if (state->is_pneigh) if (state->flags & NEIGH_SEQ_IS_PNEIGH)
arp_format_pneigh_entry(seq, v); arp_format_pneigh_entry(seq, v);
else else
arp_format_neigh_entry(seq, v); arp_format_neigh_entry(seq, v);
...@@ -1485,12 +1333,20 @@ static int arp_seq_show(struct seq_file *seq, void *v) ...@@ -1485,12 +1333,20 @@ static int arp_seq_show(struct seq_file *seq, void *v)
return 0; return 0;
} }
static void *arp_seq_start(struct seq_file *seq, loff_t *pos)
{
/* Don't want to confuse "arp -a" w/ magic entries,
* so we tell the generic iterator to skip NUD_NOARP.
*/
return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_SKIP_NOARP);
}
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
static struct seq_operations arp_seq_ops = { static struct seq_operations arp_seq_ops = {
.start = arp_seq_start, .start = arp_seq_start,
.next = arp_seq_next, .next = neigh_seq_next,
.stop = arp_seq_stop, .stop = neigh_seq_stop,
.show = arp_seq_show, .show = arp_seq_show,
}; };
...@@ -1498,11 +1354,12 @@ static int arp_seq_open(struct inode *inode, struct file *file) ...@@ -1498,11 +1354,12 @@ static int arp_seq_open(struct inode *inode, struct file *file)
{ {
struct seq_file *seq; struct seq_file *seq;
int rc = -ENOMEM; int rc = -ENOMEM;
struct arp_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL); struct neigh_seq_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s) if (!s)
goto out; goto out;
memset(s, 0, sizeof(*s));
rc = seq_open(file, &arp_seq_ops); rc = seq_open(file, &arp_seq_ops);
if (rc) if (rc)
goto out_kfree; goto out_kfree;
......
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