From 9cbf322c9aa226e013e712c30086d78fcc31c5cf Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Sat, 8 Jul 2006 14:19:30 -0400 Subject: [PATCH] Reduce stack usage by allocating ixs and irs structures. Prior to this change, on i386, ipsec_tunnel_start_xmit() consummed 856 bytes of stack, while ipsec_rcv() used 324 bytes of stack. Receive and transmit paths will now allocate the state buffer from two kmem_cache allocators, respectively, instead of chewing up stack space. Some additional cleanup was done to error handling in ipsec_klips_init(). --- include/openswan/ipsec_rcv.h | 3 + include/openswan/ipsec_tunnel.h | 3 + net/ipsec/ipsec_init.c | 52 +++++++++++++++++++ net/ipsec/ipsec_rcv.c | 106 +++++++++++++++++++++++++++++++++++++-- net/ipsec/ipsec_tunnel.c | 99 ++++++++++++++++++++++++++++++++---- 5 files changed, 247 insertions(+), 16 deletions(-) diff --git a/include/openswan/ipsec_rcv.h b/include/openswan/ipsec_rcv.h index df3d92a..45e4265 100644 --- a/include/openswan/ipsec_rcv.h +++ b/include/openswan/ipsec_rcv.h @@ -137,6 +137,9 @@ #endif /* __KERNEL__ */ extern int klips26_rcv_encap(struct sk_buff *skb, __u16 encap_type); +// manage ipsec rcv state objects +extern int ipsec_rcv_state_cache_init (void); +extern void ipsec_rcv_state_cache_cleanup (void); #endif /* IPSEC_RCV_H */ diff --git a/include/openswan/ipsec_tunnel.h b/include/openswan/ipsec_tunnel.h index f01e922..8a5cf96 100644 --- a/include/openswan/ipsec_tunnel.h +++ b/include/openswan/ipsec_tunnel.h @@ -128,6 +128,9 @@ #define DB_TN_REVEC 0x0100 #define DB_TN_ENCAP 0x0200 #endif /* CONFIG_KLIPS_DEBUG */ +// manage ipsec xmit state objects +extern int ipsec_xmit_state_cache_init (void); +extern void ipsec_xmit_state_cache_cleanup (void); /* * $Log: ipsec_tunnel.h,v $ * Revision 1.33 2005/06/04 16:06:05 mcr diff --git a/net/ipsec/ipsec_init.c b/net/ipsec/ipsec_init.c index a74c6f9..2f930ef 100644 --- a/net/ipsec/ipsec_init.c +++ b/net/ipsec/ipsec_init.c @@ -189,7 +189,17 @@ #endif /* CONFIG_KLIPS_ENC_3DES */ "KLIPS startup, Openswan KLIPS IPsec stack version: %s\n", ipsec_version_code()); + error = ipsec_xmit_state_cache_init (); + if (error) + goto error_xmit_state_cache; + + error = ipsec_rcv_state_cache_init (); + if (error) + goto error_rcv_state_cache; + error |= ipsec_proc_init(); + if (error) + goto error_proc_init; #ifdef SPINLOCK ipsec_sadb.sadb_lock = SPIN_LOCK_UNLOCKED; @@ -203,11 +213,20 @@ #ifndef SPINLOCK #endif /* !SPINLOCK */ error |= ipsec_sadb_init(); + if (error) + goto error_sadb_init; + error |= ipsec_radijinit(); + if (error) + goto error_radijinit; error |= pfkey_init(); + if (error) + goto error_pfkey_init; error |= register_netdevice_notifier(&ipsec_dev_notifier); + if (error) + goto error_netdev_notifier; #ifdef CONFIG_KLIPS_ESP openswan_inet_add_protocol(&esp_protocol, IPPROTO_ESP); @@ -225,6 +244,8 @@ #endif /* CONFIG_KLIPS_IPCOMP */ #endif error |= ipsec_tunnel_init_devices(); + if (error) + goto error_tunnel_init_devices; #if defined(NET_26) && defined(CONFIG_IPSEC_NAT_TRAVERSAL) /* register our ESP-UDP handler */ @@ -237,6 +258,8 @@ #endif #ifdef CONFIG_SYSCTL error |= ipsec_sysctl_register(); + if (error) + goto error_sysctl_register; #endif #ifdef CONFIG_KLIPS_ALG @@ -247,6 +270,29 @@ #endif prng_init(&ipsec_prng, seed, sizeof(seed)); return error; + + // undo ipsec_sysctl_register +error_sysctl_register: + ipsec_tunnel_cleanup_devices(); +error_tunnel_init_devices: + unregister_netdevice_notifier(&ipsec_dev_notifier); +error_netdev_notifier: + pfkey_cleanup(); +error_pfkey_init: + ipsec_radijcleanup(); +error_radijinit: + ipsec_sadb_cleanup(0); + ipsec_sadb_free(); +error_sadb_init: +error_proc_init: + // ipsec_proc_init() does not cleanup after itself, so we have to do it here + // TODO: ipsec_proc_init() should roll back what it chaned on failure + ipsec_proc_cleanup(); + ipsec_rcv_state_cache_cleanup (); +error_rcv_state_cache: + ipsec_xmit_state_cache_cleanup (); +error_xmit_state_cache: + return error; } @@ -311,6 +357,12 @@ #endif /* CONFIG_KLIPS_ESP */ "calling pfkey_cleanup.\n"); error |= pfkey_cleanup(); + ipsec_rcv_state_cache_cleanup (); + ipsec_xmit_state_cache_cleanup (); + + ipsec_rcv_state_cache_cleanup (); + ipsec_xmit_state_cache_cleanup (); + ipsec_proc_cleanup(); prng_final(&ipsec_prng); diff --git a/net/ipsec/ipsec_rcv.c b/net/ipsec/ipsec_rcv.c index aa54728..1a4dd65 100644 --- a/net/ipsec/ipsec_rcv.c +++ b/net/ipsec/ipsec_rcv.c @@ -1331,6 +1331,9 @@ #endif } #endif +/* management of buffers */ +static struct ipsec_rcv_state * ipsec_rcv_state_new (void); +static void ipsec_rcv_state_delete (struct ipsec_rcv_state *irs); int ipsec_rcv(struct sk_buff *skb @@ -1346,7 +1349,7 @@ #endif /* CONFIG_KLIPS_DEBUG */ struct net_device_stats *stats = NULL; /* This device's statistics */ struct net_device *ipsecdev = NULL, *prvdev; struct ipsecpriv *prv; - struct ipsec_rcv_state nirs, *irs = &nirs; + struct ipsec_rcv_state *irs = NULL; struct iphdr *ipp; char name[9]; int i; @@ -1354,7 +1357,13 @@ #endif /* CONFIG_KLIPS_DEBUG */ /* Don't unlink in the middle of a turnaround */ KLIPS_INC_USE; - memset(&nirs, 0, sizeof(struct ipsec_rcv_state)); + irs = ipsec_rcv_state_new (); + if (unlikely (! irs)) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "failled to allocate a rcv state object\n"); + goto error_alloc; + } if (skb == NULL) { KLIPS_PRINT(debug_rcv, @@ -1578,6 +1587,8 @@ #endif irs->skb = skb; ipsec_rcv_decap(irs); + + ipsec_rcv_state_delete (irs); KLIPS_DEC_USE; return(0); @@ -1585,7 +1596,9 @@ #endif if(skb) { ipsec_kfree_skb(skb); } + ipsec_rcv_state_delete (irs); KLIPS_DEC_USE; + error_alloc: return(0); } @@ -1605,14 +1618,20 @@ #ifdef NET_26 */ int klips26_rcv_encap(struct sk_buff *skb, __u16 encap_type) { - struct ipsec_rcv_state nirs, *irs = &nirs; + struct ipsec_rcv_state *irs = NULL; struct iphdr *ipp; + irs = ipsec_rcv_state_new (); + if (unlikely (! irs)) { + KLIPS_PRINT(debug_rcv, + "klips_debug:ipsec_rcv: " + "failled to allocate a rcv state object\n"); + goto error_alloc; + } + /* Don't unlink in the middle of a turnaround */ KLIPS_INC_USE; - memset(irs, 0, sizeof(*irs)); - /* XXX fudge it so that all nat-t stuff comes from ipsec0 */ /* eventually, the SA itself will determine which device * it comes from @@ -1685,6 +1704,7 @@ #ifdef CONFIG_IPSEC_NAT_TRAVERSAL #endif ipsec_rcv_decap(irs); KLIPS_DEC_USE; + ipsec_rcv_state_delete (irs); return 0; rcvleave: @@ -1692,10 +1712,86 @@ rcvleave: ipsec_kfree_skb(skb); } KLIPS_DEC_USE; + ipsec_rcv_state_delete (irs); +error_alloc: return 0; } #endif +// ------------------------------------------------------------------------ +// this handles creating and managing state for recv path + +static spinlock_t irs_cache_lock = SPIN_LOCK_UNLOCKED; +static kmem_cache_t *irs_cache_allocator = NULL; +static unsigned irs_cache_allocated_count = 0; + +int +ipsec_rcv_state_cache_init (void) +{ + if (irs_cache_allocator) + return -EBUSY; + + spin_lock_init(&irs_cache_lock); + + irs_cache_allocator = kmem_cache_create ("ipsec_irs", + sizeof (struct ipsec_rcv_state), 0, + 0, NULL, NULL); + if (! irs_cache_allocator) + return -ENOMEM; + + return 0; +} + +void +ipsec_rcv_state_cache_cleanup (void) +{ + if (unlikely (irs_cache_allocated_count)) + printk ("ipsec: deleting ipsec_irs kmem_cache while in use\n"); + + if (irs_cache_allocator) { + kmem_cache_destroy (irs_cache_allocator); + irs_cache_allocator = NULL; + } + irs_cache_allocated_count = 0; +} + +static struct ipsec_rcv_state * +ipsec_rcv_state_new (void) +{ + struct ipsec_rcv_state *irs; + + spin_lock_bh (&irs_cache_lock); + + irs = kmem_cache_alloc (irs_cache_allocator, GFP_ATOMIC); + + if (likely (irs != NULL)) + irs_cache_allocated_count++; + + spin_unlock_bh (&irs_cache_lock); + + if (unlikely (NULL == irs)) + goto bail; + + // initialize the object + memset((caddr_t)irs, 0, sizeof(*irs)); + +bail: + return irs; +} + +static void +ipsec_rcv_state_delete (struct ipsec_rcv_state *irs) +{ + if (unlikely (! irs)) + return; + + spin_lock_bh (&irs_cache_lock); + + irs_cache_allocated_count--; + kmem_cache_free (irs_cache_allocator, irs); + + spin_unlock_bh (&irs_cache_lock); +} /* * $Log: ipsec_rcv.c,v $ diff --git a/net/ipsec/ipsec_tunnel.c b/net/ipsec/ipsec_tunnel.c index e9ab6ee..7e9e9e2 100644 --- a/net/ipsec/ipsec_tunnel.c +++ b/net/ipsec/ipsec_tunnel.c @@ -689,6 +689,11 @@ #endif /* defined(HAS_NETIF_QUEUE) || de } } +/* management of buffers */ +static struct ipsec_xmit_state * ipsec_xmit_state_new (void); +static void ipsec_xmit_state_delete (struct ipsec_xmit_state *ixs); + + /* * This function assumes it is being called from dev_queue_xmit() * and that skb is filled properly by that function. @@ -696,20 +701,15 @@ #endif /* defined(HAS_NETIF_QUEUE) || de int ipsec_tunnel_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct ipsec_xmit_state ixs_mem; - struct ipsec_xmit_state *ixs = &ixs_mem; + struct ipsec_xmit_state *ixs = NULL; enum ipsec_xmit_value stat; -#ifdef CONFIG_IPSEC_NAT_TRAVERSAL - ixs->natt_type = 0, ixs->natt_head = 0; - ixs->natt_sport = 0, ixs->natt_dport = 0; -#endif + stat = IPSEC_XMIT_ERRMEMALLOC; + ixs = ipsec_xmit_state_new (); + if (! ixs) { + goto alloc_error; + } - memset((caddr_t)ixs, 0, sizeof(*ixs)); - ixs->oskb = NULL; - ixs->saved_header = NULL; /* saved copy of the hard header */ - ixs->route = NULL; - memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips)); ixs->dev = dev; ixs->skb = skb; @@ -790,6 +790,8 @@ #endif cleanup: ipsec_tunnel_cleanup(ixs); + ipsec_xmit_state_delete (ixs); +alloc_error: return 0; } @@ -1844,6 +1846,81 @@ #endif /* !NETDEV_23 */ return error; } +// ------------------------------------------------------------------------ +// this handles creating and managing state for xmit path + +static spinlock_t ixs_cache_lock = SPIN_LOCK_UNLOCKED; +static kmem_cache_t *ixs_cache_allocator = NULL; +static unsigned ixs_cache_allocated_count = 0; + +int +ipsec_xmit_state_cache_init (void) +{ + if (ixs_cache_allocator) + return -EBUSY; + + spin_lock_init(&ixs_cache_lock); + + ixs_cache_allocator = kmem_cache_create ("ipsec_ixs", + sizeof (struct ipsec_xmit_state), 0, + 0, NULL, NULL); + if (! ixs_cache_allocator) + return -ENOMEM; + + return 0; +} + +void +ipsec_xmit_state_cache_cleanup (void) +{ + if (unlikely (ixs_cache_allocated_count)) + printk ("ipsec: deleting ipsec_ixs kmem_cache while in use\n"); + + if (ixs_cache_allocator) { + kmem_cache_destroy (ixs_cache_allocator); + ixs_cache_allocator = NULL; + } + ixs_cache_allocated_count = 0; +} + +static struct ipsec_xmit_state * +ipsec_xmit_state_new (void) +{ + struct ipsec_xmit_state *ixs; + + spin_lock_bh (&ixs_cache_lock); + + ixs = kmem_cache_alloc (ixs_cache_allocator, GFP_ATOMIC); + + if (likely (ixs != NULL)) + ixs_cache_allocated_count++; + + spin_unlock_bh (&ixs_cache_lock); + + if (unlikely (NULL == ixs)) + goto bail; + + // initialize the object + memset((caddr_t)ixs, 0, sizeof(*ixs)); + +bail: + return ixs; +} + +static void +ipsec_xmit_state_delete (struct ipsec_xmit_state *ixs) +{ + if (unlikely (! ixs)) + return; + + spin_lock_bh (&ixs_cache_lock); + + ixs_cache_allocated_count--; + kmem_cache_free (ixs_cache_allocator, ixs); + + spin_unlock_bh (&ixs_cache_lock); +} + /* * $Log: ipsec_tunnel.c,v $ * Revision 1.232.2.4 2006/03/28 20:58:19 ken -- 1.4.1