Skip to content

Commit a302afe

Browse files
committed
Merge branch 'robust_listener'
Eric Dumazet says: ==================== tcp/dccp: make our listener code more robust This patch series addresses request sockets leaks and listener dismantle phase. This survives a stress test with listeners being added/removed quite randomly. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 47ea032 + ebb516a commit a302afe

File tree

7 files changed

+67
-57
lines changed

7 files changed

+67
-57
lines changed

include/net/inet_connection_sock.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,8 @@ struct dst_entry *inet_csk_route_child_sock(const struct sock *sk,
268268
struct sock *newsk,
269269
const struct request_sock *req);
270270

271-
static inline void inet_csk_reqsk_queue_add(struct sock *sk,
272-
struct request_sock *req,
273-
struct sock *child)
274-
{
275-
reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);
276-
}
277-
271+
void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req,
272+
struct sock *child);
278273
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
279274
unsigned long timeout);
280275

@@ -299,6 +294,7 @@ static inline int inet_csk_reqsk_queue_is_full(const struct sock *sk)
299294
}
300295

301296
void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req);
297+
void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req);
302298

303299
void inet_csk_destroy_sock(struct sock *sk);
304300
void inet_csk_prepare_forced_close(struct sock *sk);

include/net/request_sock.h

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -186,25 +186,6 @@ static inline bool reqsk_queue_empty(const struct request_sock_queue *queue)
186186
return queue->rskq_accept_head == NULL;
187187
}
188188

189-
static inline void reqsk_queue_add(struct request_sock_queue *queue,
190-
struct request_sock *req,
191-
struct sock *parent,
192-
struct sock *child)
193-
{
194-
spin_lock(&queue->rskq_lock);
195-
req->sk = child;
196-
sk_acceptq_added(parent);
197-
198-
if (queue->rskq_accept_head == NULL)
199-
queue->rskq_accept_head = req;
200-
else
201-
queue->rskq_accept_tail->dl_next = req;
202-
203-
queue->rskq_accept_tail = req;
204-
req->dl_next = NULL;
205-
spin_unlock(&queue->rskq_lock);
206-
}
207-
208189
static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue,
209190
struct sock *parent)
210191
{

net/dccp/ipv4.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ void dccp_req_err(struct sock *sk, u64 seq)
208208

209209
if (!between48(seq, dccp_rsk(req)->dreq_iss, dccp_rsk(req)->dreq_gss)) {
210210
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
211-
reqsk_put(req);
212211
} else {
213212
/*
214213
* Still in RESPOND, just remove it silently.
@@ -218,6 +217,7 @@ void dccp_req_err(struct sock *sk, u64 seq)
218217
*/
219218
inet_csk_reqsk_queue_drop(req->rsk_listener, req);
220219
}
220+
reqsk_put(req);
221221
}
222222
EXPORT_SYMBOL(dccp_req_err);
223223

@@ -828,7 +828,7 @@ static int dccp_v4_rcv(struct sk_buff *skb)
828828
if (likely(sk->sk_state == DCCP_LISTEN)) {
829829
nsk = dccp_check_req(sk, skb, req);
830830
} else {
831-
inet_csk_reqsk_queue_drop(sk, req);
831+
inet_csk_reqsk_queue_drop_and_put(sk, req);
832832
goto lookup;
833833
}
834834
if (!nsk) {

net/dccp/ipv6.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ static int dccp_v6_rcv(struct sk_buff *skb)
686686
if (likely(sk->sk_state == DCCP_LISTEN)) {
687687
nsk = dccp_check_req(sk, skb, req);
688688
} else {
689-
inet_csk_reqsk_queue_drop(sk, req);
689+
inet_csk_reqsk_queue_drop_and_put(sk, req);
690690
goto lookup;
691691
}
692692
if (!nsk) {

net/ipv4/inet_connection_sock.c

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,13 @@ void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req)
546546
}
547547
EXPORT_SYMBOL(inet_csk_reqsk_queue_drop);
548548

549+
void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req)
550+
{
551+
inet_csk_reqsk_queue_drop(sk, req);
552+
reqsk_put(req);
553+
}
554+
EXPORT_SYMBOL(inet_csk_reqsk_queue_drop_and_put);
555+
549556
static void reqsk_timer_handler(unsigned long data)
550557
{
551558
struct request_sock *req = (struct request_sock *)data;
@@ -608,8 +615,7 @@ static void reqsk_timer_handler(unsigned long data)
608615
return;
609616
}
610617
drop:
611-
inet_csk_reqsk_queue_drop(sk_listener, req);
612-
reqsk_put(req);
618+
inet_csk_reqsk_queue_drop_and_put(sk_listener, req);
613619
}
614620

615621
static void reqsk_queue_hash_req(struct request_sock *req,
@@ -758,6 +764,53 @@ int inet_csk_listen_start(struct sock *sk, int backlog)
758764
}
759765
EXPORT_SYMBOL_GPL(inet_csk_listen_start);
760766

767+
static void inet_child_forget(struct sock *sk, struct request_sock *req,
768+
struct sock *child)
769+
{
770+
sk->sk_prot->disconnect(child, O_NONBLOCK);
771+
772+
sock_orphan(child);
773+
774+
percpu_counter_inc(sk->sk_prot->orphan_count);
775+
776+
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
777+
BUG_ON(tcp_sk(child)->fastopen_rsk != req);
778+
BUG_ON(sk != req->rsk_listener);
779+
780+
/* Paranoid, to prevent race condition if
781+
* an inbound pkt destined for child is
782+
* blocked by sock lock in tcp_v4_rcv().
783+
* Also to satisfy an assertion in
784+
* tcp_v4_destroy_sock().
785+
*/
786+
tcp_sk(child)->fastopen_rsk = NULL;
787+
}
788+
inet_csk_destroy_sock(child);
789+
reqsk_put(req);
790+
}
791+
792+
void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req,
793+
struct sock *child)
794+
{
795+
struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
796+
797+
spin_lock(&queue->rskq_lock);
798+
if (unlikely(sk->sk_state != TCP_LISTEN)) {
799+
inet_child_forget(sk, req, child);
800+
} else {
801+
req->sk = child;
802+
req->dl_next = NULL;
803+
if (queue->rskq_accept_head == NULL)
804+
queue->rskq_accept_head = req;
805+
else
806+
queue->rskq_accept_tail->dl_next = req;
807+
queue->rskq_accept_tail = req;
808+
sk_acceptq_added(sk);
809+
}
810+
spin_unlock(&queue->rskq_lock);
811+
}
812+
EXPORT_SYMBOL(inet_csk_reqsk_queue_add);
813+
761814
/*
762815
* This routine closes sockets which have been at least partially
763816
* opened, but not yet accepted.
@@ -784,31 +837,11 @@ void inet_csk_listen_stop(struct sock *sk)
784837
WARN_ON(sock_owned_by_user(child));
785838
sock_hold(child);
786839

787-
sk->sk_prot->disconnect(child, O_NONBLOCK);
788-
789-
sock_orphan(child);
790-
791-
percpu_counter_inc(sk->sk_prot->orphan_count);
792-
793-
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
794-
BUG_ON(tcp_sk(child)->fastopen_rsk != req);
795-
BUG_ON(sk != req->rsk_listener);
796-
797-
/* Paranoid, to prevent race condition if
798-
* an inbound pkt destined for child is
799-
* blocked by sock lock in tcp_v4_rcv().
800-
* Also to satisfy an assertion in
801-
* tcp_v4_destroy_sock().
802-
*/
803-
tcp_sk(child)->fastopen_rsk = NULL;
804-
}
805-
inet_csk_destroy_sock(child);
806-
840+
inet_child_forget(sk, req, child);
807841
bh_unlock_sock(child);
808842
local_bh_enable();
809843
sock_put(child);
810844

811-
reqsk_put(req);
812845
cond_resched();
813846
}
814847
if (queue->fastopenq.rskq_rst_head) {
@@ -823,7 +856,7 @@ void inet_csk_listen_stop(struct sock *sk)
823856
req = next;
824857
}
825858
}
826-
WARN_ON(sk->sk_ack_backlog);
859+
WARN_ON_ONCE(sk->sk_ack_backlog);
827860
}
828861
EXPORT_SYMBOL_GPL(inet_csk_listen_stop);
829862

net/ipv4/tcp_ipv4.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,17 +324,17 @@ void tcp_req_err(struct sock *sk, u32 seq)
324324

325325
if (seq != tcp_rsk(req)->snt_isn) {
326326
NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
327-
reqsk_put(req);
328327
} else {
329328
/*
330329
* Still in SYN_RECV, just remove it silently.
331330
* There is no good way to pass the error to the newly
332331
* created socket, and POSIX does not want network
333332
* errors returned from accept().
334333
*/
335-
NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS);
336334
inet_csk_reqsk_queue_drop(req->rsk_listener, req);
335+
NET_INC_STATS_BH(net, LINUX_MIB_LISTENDROPS);
337336
}
337+
reqsk_put(req);
338338
}
339339
EXPORT_SYMBOL(tcp_req_err);
340340

@@ -1591,7 +1591,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
15911591
if (likely(sk->sk_state == TCP_LISTEN)) {
15921592
nsk = tcp_check_req(sk, skb, req, false);
15931593
} else {
1594-
inet_csk_reqsk_queue_drop(sk, req);
1594+
inet_csk_reqsk_queue_drop_and_put(sk, req);
15951595
goto lookup;
15961596
}
15971597
if (!nsk) {

net/ipv6/tcp_ipv6.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,7 +1386,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
13861386
if (likely(sk->sk_state == TCP_LISTEN)) {
13871387
nsk = tcp_check_req(sk, skb, req, false);
13881388
} else {
1389-
inet_csk_reqsk_queue_drop(sk, req);
1389+
inet_csk_reqsk_queue_drop_and_put(sk, req);
13901390
goto lookup;
13911391
}
13921392
if (!nsk) {

0 commit comments

Comments
 (0)