diff options
Diffstat (limited to 'net/ipv4/multipath_random.c')
-rw-r--r-- | net/ipv4/multipath_random.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/net/ipv4/multipath_random.c b/net/ipv4/multipath_random.c new file mode 100644 index 00000000000..805a16e47de --- /dev/null +++ b/net/ipv4/multipath_random.c @@ -0,0 +1,128 @@ +/* + * Random policy for multipath. + * + * + * Version: $Id: multipath_random.c,v 1.1.2.3 2004/09/21 08:42:11 elueck Exp $ + * + * Authors: Einar Lueck <elueck@de.ibm.com><lkml@einar-lueck.de> + * + * 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/config.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/fcntl.h> +#include <linux/stat.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/igmp.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/mroute.h> +#include <linux/init.h> +#include <net/ip.h> +#include <net/protocol.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <net/icmp.h> +#include <net/udp.h> +#include <net/raw.h> +#include <linux/notifier.h> +#include <linux/if_arp.h> +#include <linux/netfilter_ipv4.h> +#include <net/ipip.h> +#include <net/checksum.h> +#include <net/ip_mp_alg.h> + +#define MULTIPATH_MAX_CANDIDATES 40 + +/* interface to random number generation */ +static unsigned int RANDOM_SEED = 93186752; + +static inline unsigned int random(unsigned int ubound) +{ + static unsigned int a = 1588635695, + q = 2, + r = 1117695901; + + RANDOM_SEED = a*(RANDOM_SEED % q) - r*(RANDOM_SEED / q); + + return RANDOM_SEED % ubound; +} + + +static void random_select_route(const struct flowi *flp, + struct rtable *first, + struct rtable **rp) +{ + struct rtable *rt; + struct rtable *decision; + unsigned char candidate_count = 0; + + /* count all candidate */ + for (rt = rcu_dereference(first); rt; + rt = rcu_dereference(rt->u.rt_next)) { + if ((rt->u.dst.flags & DST_BALANCED) != 0 && + multipath_comparekeys(&rt->fl, flp)) + ++candidate_count; + } + + /* choose a random candidate */ + decision = first; + if (candidate_count > 1) { + unsigned char i = 0; + unsigned char candidate_no = (unsigned char) + random(candidate_count); + + /* find chosen candidate and adjust GC data for all candidates + * to ensure they stay in cache + */ + for (rt = first; rt; rt = rt->u.rt_next) { + if ((rt->u.dst.flags & DST_BALANCED) != 0 && + multipath_comparekeys(&rt->fl, flp)) { + rt->u.dst.lastuse = jiffies; + + if (i == candidate_no) + decision = rt; + + if (i >= candidate_count) + break; + + i++; + } + } + } + + decision->u.dst.__use++; + *rp = decision; +} + +static struct ip_mp_alg_ops random_ops = { + .mp_alg_select_route = random_select_route, +}; + +static int __init random_init(void) +{ + return multipath_alg_register(&random_ops, IP_MP_ALG_RANDOM); +} + +static void __exit random_exit(void) +{ + multipath_alg_unregister(&random_ops, IP_MP_ALG_RANDOM); +} + +module_init(random_init); +module_exit(random_exit); |