diff options
Diffstat (limited to 'net/ipv4/tcp.c')
-rw-r--r-- | net/ipv4/tcp.c | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 92b3e61b847d..2c0e340518d2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c | |||
@@ -3080,6 +3080,38 @@ void tcp_done(struct sock *sk) | |||
3080 | } | 3080 | } |
3081 | EXPORT_SYMBOL_GPL(tcp_done); | 3081 | EXPORT_SYMBOL_GPL(tcp_done); |
3082 | 3082 | ||
3083 | int tcp_abort(struct sock *sk, int err) | ||
3084 | { | ||
3085 | if (!sk_fullsock(sk)) { | ||
3086 | sock_gen_put(sk); | ||
3087 | return -EOPNOTSUPP; | ||
3088 | } | ||
3089 | |||
3090 | /* Don't race with userspace socket closes such as tcp_close. */ | ||
3091 | lock_sock(sk); | ||
3092 | |||
3093 | /* Don't race with BH socket closes such as inet_csk_listen_stop. */ | ||
3094 | local_bh_disable(); | ||
3095 | bh_lock_sock(sk); | ||
3096 | |||
3097 | if (!sock_flag(sk, SOCK_DEAD)) { | ||
3098 | sk->sk_err = err; | ||
3099 | /* This barrier is coupled with smp_rmb() in tcp_poll() */ | ||
3100 | smp_wmb(); | ||
3101 | sk->sk_error_report(sk); | ||
3102 | if (tcp_need_reset(sk->sk_state)) | ||
3103 | tcp_send_active_reset(sk, GFP_ATOMIC); | ||
3104 | tcp_done(sk); | ||
3105 | } | ||
3106 | |||
3107 | bh_unlock_sock(sk); | ||
3108 | local_bh_enable(); | ||
3109 | release_sock(sk); | ||
3110 | sock_put(sk); | ||
3111 | return 0; | ||
3112 | } | ||
3113 | EXPORT_SYMBOL_GPL(tcp_abort); | ||
3114 | |||
3083 | extern struct tcp_congestion_ops tcp_reno; | 3115 | extern struct tcp_congestion_ops tcp_reno; |
3084 | 3116 | ||
3085 | static __initdata unsigned long thash_entries; | 3117 | static __initdata unsigned long thash_entries; |