diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /net/ipv4/fib_hash.c | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'net/ipv4/fib_hash.c')
-rw-r--r-- | net/ipv4/fib_hash.c | 62 |
1 files changed, 44 insertions, 18 deletions
diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index d9e029cef..0472f6118 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -5,7 +5,7 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $ + * Version: $Id: fib_hash.c,v 1.10 1999/06/09 10:10:45 davem Exp $ * * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * @@ -145,13 +145,16 @@ extern __inline__ int fn_key_leq(fn_key_t a, fn_key_t b) return a.datum <= b.datum; } +static rwlock_t fib_hash_lock = RW_LOCK_UNLOCKED; + #define FZ_MAX_DIVISOR 1024 #ifdef CONFIG_IP_ROUTE_LARGE_TABLES +/* The fib hash lock must be held when this is called. */ static __inline__ void fn_rebuild_zone(struct fn_zone *fz, - struct fib_node **old_ht, - int old_divisor) + struct fib_node **old_ht, + int old_divisor) { int i; struct fib_node *f, **fp, *next; @@ -198,13 +201,13 @@ static void fn_rehash_zone(struct fn_zone *fz) if (ht) { memset(ht, 0, new_divisor*sizeof(struct fib_node*)); - start_bh_atomic(); + write_lock_bh(&fib_hash_lock); old_ht = fz->fz_hash; fz->fz_hash = ht; fz->fz_hashmask = new_hashmask; fz->fz_divisor = new_divisor; fn_rebuild_zone(fz, old_ht, old_divisor); - end_bh_atomic(); + write_unlock_bh(&fib_hash_lock); kfree(old_ht); } } @@ -246,6 +249,7 @@ fn_new_zone(struct fn_hash *table, int z) for (i=z+1; i<=32; i++) if (table->fn_zones[i]) break; + write_lock_bh(&fib_hash_lock); if (i>32) { /* No more specific masks, we are the first. */ fz->fz_next = table->fn_zone_list; @@ -255,6 +259,7 @@ fn_new_zone(struct fn_hash *table, int z) table->fn_zones[i]->fz_next = fz; } table->fn_zones[z] = fz; + write_unlock_bh(&fib_hash_lock); return fz; } @@ -265,6 +270,7 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result struct fn_zone *fz; struct fn_hash *t = (struct fn_hash*)tb->tb_data; + read_lock(&fib_hash_lock); for (fz = t->fn_zone_list; fz; fz = fz->fz_next) { struct fib_node *f; fn_key_t k = fz_key(key->dst, fz); @@ -293,13 +299,16 @@ fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result res->scope = f->fn_scope; res->prefixlen = fz->fz_order; res->prefix = &fz_prefix(f->fn_key, fz); - return 0; + goto out; } if (err < 0) - return err; + goto out; } } - return 1; + err = 1; +out: + read_unlock(&fib_hash_lock); + return err; } static int fn_hash_last_dflt=-1; @@ -344,6 +353,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi last_resort = NULL; order = -1; + read_lock(&fib_hash_lock); for (f = fz->fz_hash[0]; f; f = f->fn_next) { struct fib_info *next_fi = FIB_INFO(f); @@ -364,7 +374,7 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi } else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { res->fi = fi; fn_hash_last_dflt = order; - return; + goto out; } fi = next_fi; order++; @@ -372,18 +382,20 @@ fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fi if (order<=0 || fi==NULL) { fn_hash_last_dflt = -1; - return; + goto out; } if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { res->fi = fi; fn_hash_last_dflt = order; - return; + goto out; } if (last_idx >= 0) res->fi = last_resort; fn_hash_last_dflt = last_idx; +out: + read_unlock(&fib_hash_lock); } #define FIB_SCAN(f, fp) \ @@ -457,6 +469,7 @@ rta->rta_prefsrc ? *(u32*)rta->rta_prefsrc : 0); fp = fz_chain_p(key, fz); + /* * Scan list to find the first route with the same destination */ @@ -560,14 +573,17 @@ replace: */ new_f->fn_next = f; + write_lock_bh(&fib_hash_lock); *fp = new_f; + write_unlock_bh(&fib_hash_lock); fz->fz_nent++; if (del_fp) { f = *del_fp; /* Unlink replaced node */ + write_lock_bh(&fib_hash_lock); *del_fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); if (!(f->fn_state&FN_S_ZOMBIE)) rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); @@ -619,11 +635,13 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? fp = fz_chain_p(key, fz); + FIB_SCAN(f, fp) { if (fn_key_eq(f->fn_key, key)) break; - if (fn_key_leq(key, f->fn_key)) + if (fn_key_leq(key, f->fn_key)) { return -ESRCH; + } } #ifdef CONFIG_IP_ROUTE_TOS FIB_SCAN_KEY(f, fp, key) { @@ -637,9 +655,9 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? FIB_SCAN_TOS(f, fp, key, tos) { struct fib_info * fi = FIB_INFO(f); - if (f->fn_state&FN_S_ZOMBIE) + if (f->fn_state&FN_S_ZOMBIE) { return -ESRCH; - + } matched++; if (del_fp == NULL && @@ -655,8 +673,9 @@ FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); if (matched != 1) { + write_lock_bh(&fib_hash_lock); *del_fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); if (f->fn_state&FN_S_ACCESSED) rt_cache_flush(-1); @@ -687,8 +706,9 @@ fn_flush_list(struct fib_node ** fp, int z, struct fn_hash *table) struct fib_info *fi = FIB_INFO(f); if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) { + write_lock_bh(&fib_hash_lock); *fp = f->fn_next; - synchronize_bh(); + write_unlock_bh(&fib_hash_lock); fn_free_node(f); found++; @@ -727,6 +747,7 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c int pos = 0; int n = 0; + read_lock(&fib_hash_lock); for (fz=table->fn_zone_list; fz; fz = fz->fz_next) { int i; struct fib_node *f; @@ -752,10 +773,12 @@ static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int c FZ_MASK(fz), buffer); buffer += 128; if (++n >= count) - return n; + goto out; } } } +out: + read_unlock(&fib_hash_lock); return n; } #endif @@ -818,15 +841,18 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin struct fn_hash *table = (struct fn_hash*)tb->tb_data; s_m = cb->args[1]; + read_lock(&fib_hash_lock); for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) { if (m < s_m) continue; if (m > s_m) memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0])); if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) { cb->args[1] = m; + read_unlock(&fib_hash_lock); return -1; } } + read_unlock(&fib_hash_lock); cb->args[1] = m; return skb->len; } |