summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_route.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
commit95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch)
tree27a92a942821cde1edda9a1b088718d436b3efe4 /net/decnet/dn_route.c
parent45b27b0a0652331d104c953a5b192d843fff88f8 (diff)
Merge with Linux 2.3.40.
Diffstat (limited to 'net/decnet/dn_route.c')
-rw-r--r--net/decnet/dn_route.c289
1 files changed, 211 insertions, 78 deletions
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index c4f834157..6f842d465 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -25,6 +25,11 @@
* Steve Whitehouse : Dave Miller's dynamic hash table sizing and
* Alexey Kuznetsov's finer grained locking
* from ipv4/route.c.
+ * Steve Whitehouse : Routing is now starting to look like a
+ * sensible set of code now, mainly due to
+ * my copying the IPv4 routing code. The
+ * hooks here are modified and will continue
+ * to evolve for a while.
*/
/******************************************************************************
@@ -82,17 +87,23 @@ extern struct neigh_table dn_neigh_table;
static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+int dn_rt_min_delay = 2*HZ;
+int dn_rt_max_delay = 10*HZ;
+static unsigned long dn_rt_deadline = 0;
+
static int dn_dst_gc(void);
static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
static struct dst_entry *dn_dst_reroute(struct dst_entry *, struct sk_buff *skb);
static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
static void dn_dst_link_failure(struct sk_buff *);
static int dn_route_input(struct sk_buff *);
+static void dn_run_flush(unsigned long dummy);
static struct dn_rt_hash_bucket *dn_rt_hash_table;
static unsigned dn_rt_hash_mask;
static struct timer_list dn_route_timer = { NULL, NULL, 0, 0L, NULL };
+static struct timer_list dn_rt_flush_timer = { NULL, NULL, 0, 0L, dn_run_flush };
int decnet_dst_gc_interval = 2;
static struct dst_ops dn_dst_ops = {
@@ -109,12 +120,12 @@ static struct dst_ops dn_dst_ops = {
ATOMIC_INIT(0)
};
-static __inline__ unsigned dn_hash(unsigned short dest)
+static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst)
{
- unsigned short tmp = dest;
- tmp ^= (dest >> 3);
- tmp ^= (dest >> 5);
- tmp ^= (dest >> 10);
+ unsigned short tmp = src ^ dst;
+ tmp ^= (tmp >> 3);
+ tmp ^= (tmp >> 5);
+ tmp ^= (tmp >> 10);
return dn_rt_hash_mask & (unsigned)tmp;
}
@@ -155,8 +166,10 @@ static int dn_dst_gc(void)
unsigned long expire = 10 * HZ;
for(i = 0; i <= dn_rt_hash_mask; i++) {
+
write_lock_bh(&dn_rt_hash_table[i].lock);
rtp = &dn_rt_hash_table[i].chain;
+
for(; (rt=*rtp); rtp = &rt->u.rt_next) {
if (atomic_read(&rt->u.dst.__refcnt) ||
(now - rt->u.dst.lastuse) < expire)
@@ -199,9 +212,8 @@ static void dn_dst_link_failure(struct sk_buff *skb)
return;
}
-static void dn_insert_route(struct dn_route *rt)
+static void dn_insert_route(struct dn_route *rt, unsigned hash)
{
- unsigned hash = dn_hash(rt->rt_daddr);
unsigned long now = jiffies;
write_lock_bh(&dn_rt_hash_table[hash].lock);
@@ -237,6 +249,43 @@ nothing_to_declare:
}
}
+static spinlock_t dn_rt_flush_lock = SPIN_LOCK_UNLOCKED;
+
+void dn_rt_cache_flush(int delay)
+{
+ unsigned long now = jiffies;
+ int user_mode = !in_interrupt();
+
+ if (delay < 0)
+ delay = dn_rt_min_delay;
+
+ spin_lock_bh(&dn_rt_flush_lock);
+
+ if (del_timer(&dn_rt_flush_timer) && delay > 0 && dn_rt_deadline) {
+ long tmo = (long)(dn_rt_deadline - now);
+
+ if (user_mode && tmo < dn_rt_max_delay - dn_rt_min_delay)
+ tmo = 0;
+
+ if (delay > tmo)
+ delay = tmo;
+ }
+
+ if (delay <= 0) {
+ spin_unlock_bh(&dn_rt_flush_lock);
+ dn_run_flush(0);
+ return;
+ }
+
+ if (dn_rt_deadline == 0)
+ dn_rt_deadline = now + dn_rt_max_delay;
+
+ dn_rt_flush_timer.expires = now + delay;
+ add_timer(&dn_rt_flush_timer);
+ spin_unlock_bh(&dn_rt_flush_lock);
+}
+
+
static int dn_route_rx_packet(struct sk_buff *skb)
{
int err;
@@ -531,33 +580,49 @@ static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_addr
struct net_device *dev = decnet_default_device;
struct neighbour *neigh = NULL;
struct dn_dev *dn_db;
-
-
+ unsigned hash;
#ifdef CONFIG_DECNET_ROUTER
- if (decnet_node_type != DN_RT_INFO_ENDN) {
- struct dn_fib_res res;
- int err = 0;
-
- res.res_addr = dst;
- res.res_mask = 0;
- res.res_ifindex = 0;
- res.res_proto = RTN_UNSPEC;
- res.res_cost = 0;
-
- if ((err = dn_fib_resolve(&res)) == 0) {
-
- if (!res.res_fa || (res.res_fa->fa_type != RTN_UNICAST))
- return -EPROTO;
-
- if ((neigh = neigh_clone(res.res_fa->fa_neigh)) != NULL)
- goto got_route;
+ struct dn_fib_key key;
+ struct dn_fib_res res;
+ int err;
- return -ENOBUFS;
+ key.src = src;
+ key.dst = dst;
+ key.iif = 0;
+ key.oif = 0;
+ key.fwmark = 0;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+ if ((err = dn_fib_lookup(&key, &res)) == 0) {
+ switch(res.type) {
+ case RTN_UNICAST:
+ /*
+ * This method of handling multipath
+ * routes is a hack and will change.
+ * It works for now though.
+ */
+ if (res.fi->fib_nhs)
+ dn_fib_select_multipath(&key, &res);
+ neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
+ err = -ENOBUFS;
+ if (!neigh)
+ break;
+ err = 0;
+ break;
+ case RTN_UNREACHABLE:
+ err = -EHOSTUNREACH;
+ break;
+ default:
+ err = -EINVAL;
}
-
- if (err != -ENOENT)
+ dn_fib_res_put(&res);
+ if (err < 0)
return err;
+ goto got_route;
}
+
+ if (err != -ESRCH)
+ return err;
#endif
/* Look in On-Ethernet cache first */
@@ -594,13 +659,16 @@ got_route:
dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
- rt->rt_saddr = src;
- rt->rt_daddr = dst;
- rt->rt_oif = neigh->dev->ifindex;
- rt->rt_iif = 0;
+ rt->key.saddr = src;
+ rt->rt_saddr = src;
+ rt->key.daddr = dst;
+ rt->rt_daddr = dst;
+ rt->key.oif = neigh ? neigh->dev->ifindex : -1;
+ rt->key.iif = 0;
+ rt->key.fwmark = 0;
rt->u.dst.neighbour = neigh;
- rt->u.dst.dev = neigh->dev;
+ rt->u.dst.dev = neigh ? neigh->dev : NULL;
rt->u.dst.lastuse = jiffies;
rt->u.dst.output = dn_output;
rt->u.dst.input = dn_rt_bug;
@@ -608,7 +676,8 @@ got_route:
if (dn_dev_islocal(neigh->dev, rt->rt_daddr))
rt->u.dst.input = dn_nsp_rx;
- dn_insert_route(rt);
+ hash = dn_hash(rt->key.saddr, rt->key.daddr);
+ dn_insert_route(rt, hash);
*pprt = &rt->u.dst;
return 0;
@@ -616,16 +685,16 @@ got_route:
int dn_route_output(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
{
- unsigned hash = dn_hash(dst);
+ unsigned hash = dn_hash(src, dst);
struct dn_route *rt = NULL;
if (!(flags & MSG_TRYHARD)) {
read_lock_bh(&dn_rt_hash_table[hash].lock);
for(rt = dn_rt_hash_table[hash].chain; rt; rt = rt->u.rt_next) {
- if ((dst == rt->rt_daddr) &&
- (src == rt->rt_saddr) &&
- (rt->rt_iif == 0) &&
- (rt->rt_oif != 0)) {
+ if ((dst == rt->key.daddr) &&
+ (src == rt->key.saddr) &&
+ (rt->key.iif == 0) &&
+ (rt->key.oif != 0)) {
rt->u.dst.lastuse = jiffies;
dst_hold(&rt->u.dst);
rt->u.dst.__use++;
@@ -649,6 +718,15 @@ static int dn_route_input_slow(struct sk_buff *skb)
struct neighbour *neigh = NULL;
int (*dnrt_input)(struct sk_buff *skb);
int (*dnrt_output)(struct sk_buff *skb);
+ u32 fwmark = 0;
+ unsigned hash;
+ dn_address saddr = cb->src;
+ dn_address daddr = cb->dst;
+#ifdef CONFIG_DECNET_ROUTER
+ struct dn_fib_key key;
+ struct dn_fib_res res;
+ int err;
+#endif
if (dev == NULL)
return -EINVAL;
@@ -683,6 +761,8 @@ static int dn_route_input_slow(struct sk_buff *skb)
*/
dnrt_input = dn_nsp_rx;
dnrt_output = dn_output;
+ saddr = cb->dst;
+ daddr = cb->src;
if ((neigh = neigh_lookup(&dn_neigh_table, &cb->src, dev)) != NULL)
goto add_entry;
@@ -705,9 +785,49 @@ non_local_input:
* Destination is another node... find next hop in
* routing table here.
*/
- if (decnet_node_type == DN_RT_INFO_ENDN)
+
+ key.src = cb->src;
+ key.dst = cb->dst;
+ key.iif = dev->ifindex;
+ key.oif = 0;
+ key.scope = RT_SCOPE_UNIVERSE;
+
+#ifdef CONFIG_DECNET_ROUTE_FWMASK
+ if (skb->nfreason == NF_REASON_FOR_ROUTING)
+ key.fwmark = skb->fwmark;
+ else
+ key.fwmark = 0;
+#else
+ key.fwmark = 0;
+#endif
+
+ if ((err = dn_fib_lookup(&key, &res)) == 0) {
+ switch(res.type) {
+ case RTN_UNICAST:
+ if (res.fi->fib_nhs)
+ dn_fib_select_multipath(&key, &res);
+ neigh = __neigh_lookup(&dn_neigh_table, &DN_FIB_RES_GW(res), DN_FIB_RES_DEV(res), 1);
+ err = -ENOBUFS;
+ if (!neigh)
+ break;
+ err = 0;
+ dnrt_input = dn_forward;
+ fwmark = key.fwmark;
+ break;
+ case RTN_UNREACHABLE:
+ dnrt_input = dn_blackhole;
+ fwmark = key.fwmark;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ dn_fib_res_put(&res);
+ if (err < 0)
+ return err;
goto add_entry;
+ }
+ return err;
#endif /* CONFIG_DECNET_ROUTER */
@@ -718,18 +838,22 @@ add_entry:
return -EINVAL;
}
- rt->rt_saddr = cb->dst;
- rt->rt_daddr = cb->src;
- rt->rt_oif = 0;
- rt->rt_iif = dev->ifindex;
+ rt->key.saddr = cb->src;
+ rt->rt_saddr = saddr;
+ rt->key.daddr = cb->dst;
+ rt->rt_daddr = daddr;
+ rt->key.oif = 0;
+ rt->key.iif = dev->ifindex;
+ rt->key.fwmark = fwmark;
rt->u.dst.neighbour = neigh;
- rt->u.dst.dev = dev;
+ rt->u.dst.dev = neigh ? neigh->dev : NULL;
rt->u.dst.lastuse = jiffies;
rt->u.dst.output = dnrt_output;
rt->u.dst.input = dnrt_input;
- dn_insert_route(rt);
+ hash = dn_hash(rt->key.saddr, rt->key.daddr);
+ dn_insert_route(rt, hash);
skb->dst = (struct dst_entry *)rt;
return 0;
@@ -739,17 +863,22 @@ int dn_route_input(struct sk_buff *skb)
{
struct dn_route *rt;
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- unsigned hash = dn_hash(cb->src);
+ unsigned hash = dn_hash(cb->src, cb->dst);
if (skb->dst)
return 0;
read_lock(&dn_rt_hash_table[hash].lock);
for(rt = dn_rt_hash_table[hash].chain; rt != NULL; rt = rt->u.rt_next) {
- if ((rt->rt_saddr == cb->dst) &&
- (rt->rt_daddr == cb->src) &&
- (rt->rt_oif == 0) &&
- (rt->rt_iif == cb->iif)) {
+ if ((rt->key.saddr == cb->src) &&
+ (rt->key.daddr == cb->dst) &&
+ (rt->key.oif == 0) &&
+#ifdef CONFIG_DECNET_ROUTE_FWMASK
+ (rt->key.fwmark == (skb->nfreason ==
+ NF_REASON_FOR_ROUTING
+ ? skb->nfmark : 0)) &&
+#endif
+ (rt->key.iif == cb->iif)) {
rt->u.dst.lastuse = jiffies;
dst_hold(&rt->u.dst);
rt->u.dst.__use++;
@@ -953,6 +1082,7 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
);
+
pos = begin + len;
if (pos < offset) {
@@ -998,48 +1128,51 @@ void __init dn_route_init(void)
for(order = 0; (1UL << order) < goal; order++)
/* NOTHING */;
- /*
- * Only want 1024 entries max, since the table is very, very unlikely
- * to be larger than that.
- */
- while(order && ((((1UL << order) * PAGE_SIZE) /
- sizeof(struct dn_rt_hash_bucket)) >= 2048))
- order--;
-
- do {
- dn_rt_hash_mask = (1UL << order) * PAGE_SIZE /
- sizeof(struct dn_rt_hash_bucket);
- while(dn_rt_hash_mask & (dn_rt_hash_mask - 1))
- dn_rt_hash_mask--;
- dn_rt_hash_table = (struct dn_rt_hash_bucket *)
- __get_free_pages(GFP_ATOMIC, order);
- } while (dn_rt_hash_table == NULL && --order > 0);
+ /*
+ * Only want 1024 entries max, since the table is very, very unlikely
+ * to be larger than that.
+ */
+ while(order && ((((1UL << order) * PAGE_SIZE) /
+ sizeof(struct dn_rt_hash_bucket)) >= 2048))
+ order--;
+
+ do {
+ dn_rt_hash_mask = (1UL << order) * PAGE_SIZE /
+ sizeof(struct dn_rt_hash_bucket);
+ while(dn_rt_hash_mask & (dn_rt_hash_mask - 1))
+ dn_rt_hash_mask--;
+ dn_rt_hash_table = (struct dn_rt_hash_bucket *)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (dn_rt_hash_table == NULL && --order > 0);
if (!dn_rt_hash_table)
- panic("Failed to allocate DECnet route cache hash table\n");
+ panic("Failed to allocate DECnet route cache hash table\n");
- printk(KERN_INFO "DECnet: Routing cache hash table of %u buckets, %dKbytes\n", dn_rt_hash_mask, (dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
+ printk(KERN_INFO
+ "DECnet: Routing cache hash table of %u buckets, %dKbytes\n",
+ dn_rt_hash_mask,
+ (dn_rt_hash_mask*sizeof(struct dn_rt_hash_bucket))/1024);
dn_rt_hash_mask--;
- for(i = 0; i <= dn_rt_hash_mask; i++) {
- dn_rt_hash_table[i].lock = RW_LOCK_UNLOCKED;
- dn_rt_hash_table[i].chain = NULL;
- }
+ for(i = 0; i <= dn_rt_hash_mask; i++) {
+ dn_rt_hash_table[i].lock = RW_LOCK_UNLOCKED;
+ dn_rt_hash_table[i].chain = NULL;
+ }
- dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
+ dn_dst_ops.gc_thresh = (dn_rt_hash_mask + 1);
#ifdef CONFIG_PROC_FS
proc_net_create("decnet_cache",0,decnet_cache_get_info);
#endif /* CONFIG_PROC_FS */
}
-#ifdef CONFIG_DECNET_MODULE
-void dn_route_cleanup(void)
+void __exit dn_route_cleanup(void)
{
del_timer(&dn_route_timer);
dn_run_flush(0);
+
#ifdef CONFIG_PROC_FS
proc_net_remove("decnet_cache");
#endif /* CONFIG_PROC_FS */
}
-#endif /* CONFIG_DECNET_MODULE */
+