aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHoria Geanta <horia.geanta@freescale.com>2014-02-12 09:20:06 -0500
committerSteffen Klassert <steffen.klassert@secunet.com>2014-02-13 01:40:30 -0500
commit0f24558e91563888d51e9be5b70981da920c37ac (patch)
tree31aba2926b9f641fe30e235acc5a87b1b4dab73b
parent5826bdd1816fa2baa122b62e14905c0ad8e7b96a (diff)
xfrm: avoid creating temporary SA when there are no listeners
In the case when KMs have no listeners, km_query() will fail and temporary SAs are garbage collected immediately after their allocation. This causes strain on memory allocation, leading even to OOM since temporary SA alloc/free cycle is performed for every packet and garbage collection does not keep up the pace. The sane thing to do is to make sure we have audience before temporary SA allocation. Signed-off-by: Horia Geanta <horia.geanta@freescale.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
-rw-r--r--include/net/xfrm.h15
-rw-r--r--net/key/af_key.c19
-rw-r--r--net/xfrm/xfrm_state.c31
-rw-r--r--net/xfrm/xfrm_user.c6
4 files changed, 71 insertions, 0 deletions
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index afa5730fb3bd..5313ccfdeedf 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -594,6 +594,7 @@ struct xfrm_mgr {
594 const struct xfrm_migrate *m, 594 const struct xfrm_migrate *m,
595 int num_bundles, 595 int num_bundles,
596 const struct xfrm_kmaddress *k); 596 const struct xfrm_kmaddress *k);
597 bool (*is_alive)(const struct km_event *c);
597}; 598};
598 599
599int xfrm_register_km(struct xfrm_mgr *km); 600int xfrm_register_km(struct xfrm_mgr *km);
@@ -1646,6 +1647,20 @@ static inline int xfrm_aevent_is_on(struct net *net)
1646 rcu_read_unlock(); 1647 rcu_read_unlock();
1647 return ret; 1648 return ret;
1648} 1649}
1650
1651static inline int xfrm_acquire_is_on(struct net *net)
1652{
1653 struct sock *nlsk;
1654 int ret = 0;
1655
1656 rcu_read_lock();
1657 nlsk = rcu_dereference(net->xfrm.nlsk);
1658 if (nlsk)
1659 ret = netlink_has_listeners(nlsk, XFRMNLGRP_ACQUIRE);
1660 rcu_read_unlock();
1661
1662 return ret;
1663}
1649#endif 1664#endif
1650 1665
1651static inline int xfrm_alg_len(const struct xfrm_algo *alg) 1666static inline int xfrm_alg_len(const struct xfrm_algo *alg)
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 1a04c1329362..e1c69d024197 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3059,6 +3059,24 @@ static u32 get_acqseq(void)
3059 return res; 3059 return res;
3060} 3060}
3061 3061
3062static bool pfkey_is_alive(const struct km_event *c)
3063{
3064 struct netns_pfkey *net_pfkey = net_generic(c->net, pfkey_net_id);
3065 struct sock *sk;
3066 bool is_alive = false;
3067
3068 rcu_read_lock();
3069 sk_for_each_rcu(sk, &net_pfkey->table) {
3070 if (pfkey_sk(sk)->registered) {
3071 is_alive = true;
3072 break;
3073 }
3074 }
3075 rcu_read_unlock();
3076
3077 return is_alive;
3078}
3079
3062static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp) 3080static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp)
3063{ 3081{
3064 struct sk_buff *skb; 3082 struct sk_buff *skb;
@@ -3784,6 +3802,7 @@ static struct xfrm_mgr pfkeyv2_mgr =
3784 .new_mapping = pfkey_send_new_mapping, 3802 .new_mapping = pfkey_send_new_mapping,
3785 .notify_policy = pfkey_send_policy_notify, 3803 .notify_policy = pfkey_send_policy_notify,
3786 .migrate = pfkey_send_migrate, 3804 .migrate = pfkey_send_migrate,
3805 .is_alive = pfkey_is_alive,
3787}; 3806};
3788 3807
3789static int __net_init pfkey_net_init(struct net *net) 3808static int __net_init pfkey_net_init(struct net *net)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index a26b7aa79475..0bf12f665b9b 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -161,6 +161,7 @@ static DEFINE_SPINLOCK(xfrm_state_gc_lock);
161int __xfrm_state_delete(struct xfrm_state *x); 161int __xfrm_state_delete(struct xfrm_state *x);
162 162
163int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); 163int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
164bool km_is_alive(const struct km_event *c);
164void km_state_expired(struct xfrm_state *x, int hard, u32 portid); 165void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
165 166
166static DEFINE_SPINLOCK(xfrm_type_lock); 167static DEFINE_SPINLOCK(xfrm_type_lock);
@@ -788,6 +789,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
788 struct xfrm_state *best = NULL; 789 struct xfrm_state *best = NULL;
789 u32 mark = pol->mark.v & pol->mark.m; 790 u32 mark = pol->mark.v & pol->mark.m;
790 unsigned short encap_family = tmpl->encap_family; 791 unsigned short encap_family = tmpl->encap_family;
792 struct km_event c;
791 793
792 to_put = NULL; 794 to_put = NULL;
793 795
@@ -832,6 +834,17 @@ found:
832 error = -EEXIST; 834 error = -EEXIST;
833 goto out; 835 goto out;
834 } 836 }
837
838 c.net = net;
839 /* If the KMs have no listeners (yet...), avoid allocating an SA
840 * for each and every packet - garbage collection might not
841 * handle the flood.
842 */
843 if (!km_is_alive(&c)) {
844 error = -ESRCH;
845 goto out;
846 }
847
835 x = xfrm_state_alloc(net); 848 x = xfrm_state_alloc(net);
836 if (x == NULL) { 849 if (x == NULL) {
837 error = -ENOMEM; 850 error = -ENOMEM;
@@ -1793,6 +1806,24 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address
1793} 1806}
1794EXPORT_SYMBOL(km_report); 1807EXPORT_SYMBOL(km_report);
1795 1808
1809bool km_is_alive(const struct km_event *c)
1810{
1811 struct xfrm_mgr *km;
1812 bool is_alive = false;
1813
1814 rcu_read_lock();
1815 list_for_each_entry_rcu(km, &xfrm_km_list, list) {
1816 if (km->is_alive && km->is_alive(c)) {
1817 is_alive = true;
1818 break;
1819 }
1820 }
1821 rcu_read_unlock();
1822
1823 return is_alive;
1824}
1825EXPORT_SYMBOL(km_is_alive);
1826
1796int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) 1827int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen)
1797{ 1828{
1798 int err; 1829 int err;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index ade9988f6e33..d7694f258294 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2982,6 +2982,11 @@ static int xfrm_send_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr,
2982 return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC); 2982 return nlmsg_multicast(net->xfrm.nlsk, skb, 0, XFRMNLGRP_MAPPING, GFP_ATOMIC);
2983} 2983}
2984 2984
2985static bool xfrm_is_alive(const struct km_event *c)
2986{
2987 return (bool)xfrm_acquire_is_on(c->net);
2988}
2989
2985static struct xfrm_mgr netlink_mgr = { 2990static struct xfrm_mgr netlink_mgr = {
2986 .id = "netlink", 2991 .id = "netlink",
2987 .notify = xfrm_send_state_notify, 2992 .notify = xfrm_send_state_notify,
@@ -2991,6 +2996,7 @@ static struct xfrm_mgr netlink_mgr = {
2991 .report = xfrm_send_report, 2996 .report = xfrm_send_report,
2992 .migrate = xfrm_send_migrate, 2997 .migrate = xfrm_send_migrate,
2993 .new_mapping = xfrm_send_mapping, 2998 .new_mapping = xfrm_send_mapping,
2999 .is_alive = xfrm_is_alive,
2994}; 3000};
2995 3001
2996static int __net_init xfrm_user_net_init(struct net *net) 3002static int __net_init xfrm_user_net_init(struct net *net)