diff options
-rw-r--r-- | include/linux/res_counter.h | 6 | ||||
-rw-r--r-- | include/net/sock.h | 10 | ||||
-rw-r--r-- | kernel/res_counter.c | 25 | ||||
-rw-r--r-- | net/core/sock.c | 4 |
4 files changed, 37 insertions, 8 deletions
diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h index d06d014afda6..da81af086eaf 100644 --- a/include/linux/res_counter.h +++ b/include/linux/res_counter.h | |||
@@ -109,12 +109,18 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent); | |||
109 | * | 109 | * |
110 | * returns 0 on success and <0 if the counter->usage will exceed the | 110 | * returns 0 on success and <0 if the counter->usage will exceed the |
111 | * counter->limit _locked call expects the counter->lock to be taken | 111 | * counter->limit _locked call expects the counter->lock to be taken |
112 | * | ||
113 | * charge_nofail works the same, except that it charges the resource | ||
114 | * counter unconditionally, and returns < 0 if the after the current | ||
115 | * charge we are over limit. | ||
112 | */ | 116 | */ |
113 | 117 | ||
114 | int __must_check res_counter_charge_locked(struct res_counter *counter, | 118 | int __must_check res_counter_charge_locked(struct res_counter *counter, |
115 | unsigned long val); | 119 | unsigned long val); |
116 | int __must_check res_counter_charge(struct res_counter *counter, | 120 | int __must_check res_counter_charge(struct res_counter *counter, |
117 | unsigned long val, struct res_counter **limit_fail_at); | 121 | unsigned long val, struct res_counter **limit_fail_at); |
122 | int __must_check res_counter_charge_nofail(struct res_counter *counter, | ||
123 | unsigned long val, struct res_counter **limit_fail_at); | ||
118 | 124 | ||
119 | /* | 125 | /* |
120 | * uncharge - tell that some portion of the resource is released | 126 | * uncharge - tell that some portion of the resource is released |
diff --git a/include/net/sock.h b/include/net/sock.h index 0e7a9b05f92b..4c69ac165e6b 100644 --- a/include/net/sock.h +++ b/include/net/sock.h | |||
@@ -1008,9 +1008,8 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot, | |||
1008 | struct res_counter *fail; | 1008 | struct res_counter *fail; |
1009 | int ret; | 1009 | int ret; |
1010 | 1010 | ||
1011 | ret = res_counter_charge(prot->memory_allocated, | 1011 | ret = res_counter_charge_nofail(prot->memory_allocated, |
1012 | amt << PAGE_SHIFT, &fail); | 1012 | amt << PAGE_SHIFT, &fail); |
1013 | |||
1014 | if (ret < 0) | 1013 | if (ret < 0) |
1015 | *parent_status = OVER_LIMIT; | 1014 | *parent_status = OVER_LIMIT; |
1016 | } | 1015 | } |
@@ -1054,12 +1053,11 @@ sk_memory_allocated_add(struct sock *sk, int amt, int *parent_status) | |||
1054 | } | 1053 | } |
1055 | 1054 | ||
1056 | static inline void | 1055 | static inline void |
1057 | sk_memory_allocated_sub(struct sock *sk, int amt, int parent_status) | 1056 | sk_memory_allocated_sub(struct sock *sk, int amt) |
1058 | { | 1057 | { |
1059 | struct proto *prot = sk->sk_prot; | 1058 | struct proto *prot = sk->sk_prot; |
1060 | 1059 | ||
1061 | if (mem_cgroup_sockets_enabled && sk->sk_cgrp && | 1060 | if (mem_cgroup_sockets_enabled && sk->sk_cgrp) |
1062 | parent_status != OVER_LIMIT) /* Otherwise was uncharged already */ | ||
1063 | memcg_memory_allocated_sub(sk->sk_cgrp, amt); | 1061 | memcg_memory_allocated_sub(sk->sk_cgrp, amt); |
1064 | 1062 | ||
1065 | atomic_long_sub(amt, prot->memory_allocated); | 1063 | atomic_long_sub(amt, prot->memory_allocated); |
diff --git a/kernel/res_counter.c b/kernel/res_counter.c index 6d269cce7aa1..d508363858b3 100644 --- a/kernel/res_counter.c +++ b/kernel/res_counter.c | |||
@@ -66,6 +66,31 @@ done: | |||
66 | return ret; | 66 | return ret; |
67 | } | 67 | } |
68 | 68 | ||
69 | int res_counter_charge_nofail(struct res_counter *counter, unsigned long val, | ||
70 | struct res_counter **limit_fail_at) | ||
71 | { | ||
72 | int ret, r; | ||
73 | unsigned long flags; | ||
74 | struct res_counter *c; | ||
75 | |||
76 | r = ret = 0; | ||
77 | *limit_fail_at = NULL; | ||
78 | local_irq_save(flags); | ||
79 | for (c = counter; c != NULL; c = c->parent) { | ||
80 | spin_lock(&c->lock); | ||
81 | r = res_counter_charge_locked(c, val); | ||
82 | if (r) | ||
83 | c->usage += val; | ||
84 | spin_unlock(&c->lock); | ||
85 | if (r < 0 && ret == 0) { | ||
86 | *limit_fail_at = c; | ||
87 | ret = r; | ||
88 | } | ||
89 | } | ||
90 | local_irq_restore(flags); | ||
91 | |||
92 | return ret; | ||
93 | } | ||
69 | void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) | 94 | void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) |
70 | { | 95 | { |
71 | if (WARN_ON(counter->usage < val)) | 96 | if (WARN_ON(counter->usage < val)) |
diff --git a/net/core/sock.c b/net/core/sock.c index 5c5af9988f94..3e81fd2e3c75 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -1827,7 +1827,7 @@ suppress_allocation: | |||
1827 | /* Alas. Undo changes. */ | 1827 | /* Alas. Undo changes. */ |
1828 | sk->sk_forward_alloc -= amt * SK_MEM_QUANTUM; | 1828 | sk->sk_forward_alloc -= amt * SK_MEM_QUANTUM; |
1829 | 1829 | ||
1830 | sk_memory_allocated_sub(sk, amt, parent_status); | 1830 | sk_memory_allocated_sub(sk, amt); |
1831 | 1831 | ||
1832 | return 0; | 1832 | return 0; |
1833 | } | 1833 | } |
@@ -1840,7 +1840,7 @@ EXPORT_SYMBOL(__sk_mem_schedule); | |||
1840 | void __sk_mem_reclaim(struct sock *sk) | 1840 | void __sk_mem_reclaim(struct sock *sk) |
1841 | { | 1841 | { |
1842 | sk_memory_allocated_sub(sk, | 1842 | sk_memory_allocated_sub(sk, |
1843 | sk->sk_forward_alloc >> SK_MEM_QUANTUM_SHIFT, 0); | 1843 | sk->sk_forward_alloc >> SK_MEM_QUANTUM_SHIFT); |
1844 | sk->sk_forward_alloc &= SK_MEM_QUANTUM - 1; | 1844 | sk->sk_forward_alloc &= SK_MEM_QUANTUM - 1; |
1845 | 1845 | ||
1846 | if (sk_under_memory_pressure(sk) && | 1846 | if (sk_under_memory_pressure(sk) && |