From c9db62c69f26684eb5b00c620d0e182f3e336412 Mon Sep 17 00:00:00 2001 From: Kozlov Dmitry Date: Sat, 21 Aug 2010 12:53:18 +0400 Subject: * use rcu + spin_lock in gre module for 2.6 kernel * use rcu + spin_lock instead of rcu + semaphone in pptp module * fixed incorrect rcu_synchronize() place * use static bitmap (DECLARE_BITMAP) instead of dynamically allocated * use vmalloc for callid_sock memory allocation instead of BITS_PER_LONG + __get_free_pages * avoid copying writable clones (thanks to theMIROn) --- kernel/driver/gre.c | 107 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 22 deletions(-) (limited to 'kernel/driver/gre.c') diff --git a/kernel/driver/gre.c b/kernel/driver/gre.c index cda0576b..485da85d 100644 --- a/kernel/driver/gre.c +++ b/kernel/driver/gre.c @@ -10,50 +10,83 @@ #include "gre.h" - struct gre_protocol *gre_proto[GREPROTO_MAX] ____cacheline_aligned_in_smp; -static DEFINE_RWLOCK(gre_proto_lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static rwlock_t chan_lock=RW_LOCK_UNLOCKED; +#else +static DEFINE_SPINLOCK(gre_proto_lock); +#endif int gre_add_protocol(struct gre_protocol *proto, u8 version) { int ret; if (version >= GREPROTO_MAX) - return -1; + return -EINVAL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_lock_bh(&gre_proto_lock); +#else + spin_lock(&gre_proto_lock); +#endif if (gre_proto[version]) { - ret = -1; + ret = -EAGAIN; } else { - gre_proto[version]=proto; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + gre_proto[version] = proto; +#else + rcu_assign_pointer(gre_proto[version], proto); +#endif ret = 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); +#endif return ret; } int gre_del_protocol(struct gre_protocol *proto, u8 version) { - int ret; - if (version >= GREPROTO_MAX) - return -1; + goto out_err; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_lock_bh(&gre_proto_lock); - if (gre_proto[version] == proto) { +#else + spin_lock(&gre_proto_lock); +#endif + if (gre_proto[version] == proto) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) gre_proto[version] = NULL; - ret = 0; - } else { - ret = -1; - } +#else + rcu_assign_pointer(gre_proto[version], NULL); +#endif + else + goto out_err_unlock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); + synchronize_rcu(); +#endif + return 0; - return ret; +out_err_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + write_unlock_bh(&gre_proto_lock); +#else + spin_unlock(&gre_proto_lock); +#endif +out_err: + return -EINVAL; } static int gre_rcv(struct sk_buff *skb) { u8 ver; int ret; + struct gre_protocol *proto; if (!pskb_may_pull(skb, 12)) goto drop_nolock; @@ -62,17 +95,32 @@ static int gre_rcv(struct sk_buff *skb) if (ver >= GREPROTO_MAX) goto drop_nolock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_lock(&gre_proto_lock); - if (!gre_proto[ver] || !gre_proto[ver]->handler) + proto = gre_proto[ver]; +#else + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); +#endif + if (!proto || !proto->handler) goto drop; - ret = gre_proto[ver]->handler(skb); + ret = proto->handler(skb); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif return ret; drop: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif drop_nolock: kfree_skb(skb); return NET_RX_DROP; @@ -80,27 +128,40 @@ drop_nolock: static void gre_err(struct sk_buff *skb, u32 info) { u8 ver; - - printk("err\n"); + struct gre_protocol *proto; if (!pskb_may_pull(skb, 12)) goto drop_nolock; - ver=skb->data[1]; + ver=skb->data[1]&0x7f; if (ver>=GREPROTO_MAX) goto drop_nolock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_lock(&gre_proto_lock); - if (!gre_proto[ver] || !gre_proto[ver]->err_handler) + proto = gre_proto[ver]; +#else + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); +#endif + if (!proto || !proto->err_handler) goto drop; - gre_proto[ver]->err_handler(skb,info); + proto->err_handler(skb, info); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif return; drop: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) read_unlock(&gre_proto_lock); +#else + rcu_read_unlock(); +#endif drop_nolock: kfree_skb(skb); } @@ -109,7 +170,9 @@ drop_nolock: static struct net_protocol net_gre_protocol = { .handler = gre_rcv, .err_handler = gre_err, -// .netns_ok=1, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24) + .netns_ok=1, +#endif }; static int __init gre_init(void) -- cgit v1.2.3