diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/core/filter.c | 23 |
1 files changed, 21 insertions, 2 deletions
diff --git a/net/core/filter.c b/net/core/filter.c index 54dddc92452d..1f0068eae501 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -387,6 +387,25 @@ int sk_chk_filter(struct sock_filter *filter, int flen) } /** + * sk_filter_rcu_release: Release a socket filter by rcu_head + * @rcu: rcu_head that contains the sk_filter to free + */ +static void sk_filter_rcu_release(struct rcu_head *rcu) +{ + struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu); + + sk_filter_release(fp); +} + +static void sk_filter_delayed_uncharge(struct sock *sk, struct sk_filter *fp) +{ + unsigned int size = sk_filter_len(fp); + + atomic_sub(size, &sk->sk_omem_alloc); + call_rcu_bh(&fp->rcu, sk_filter_rcu_release); +} + +/** * sk_attach_filter - attach a socket filter * @fprog: the filter program * @sk: the socket to use @@ -428,7 +447,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) rcu_assign_pointer(sk->sk_filter, fp); rcu_read_unlock_bh(); - sk_filter_uncharge(sk, old_fp); + sk_filter_delayed_uncharge(sk, old_fp); return 0; } @@ -441,7 +460,7 @@ int sk_detach_filter(struct sock *sk) filter = rcu_dereference(sk->sk_filter); if (filter) { rcu_assign_pointer(sk->sk_filter, NULL); - sk_filter_uncharge(sk, filter); + sk_filter_delayed_uncharge(sk, filter); ret = 0; } rcu_read_unlock_bh(); |