https://github.com/torvalds/linux/commit/5e0724d027f0548511a2165a209572d48fe7a4c8
TCP_NEW_SYN_RECV 漏了上面patch
把 syn_recv 状态的req也放进 sk 队列.
在收到第三个ack 的时候执行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| tcp_check_req() {
...
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL, req, &own_req, 0);
return inet_csk_complete_hashdance(sk, child, req, own_req);
}
tcp_v4_syn_recv_sock() {
...
// __inet_hash_nolisten(newsk, NULL); // 3.10.0
__inet_hash_nolisten(newsk, NULL, req_to_sk(req_unhash), own_req); // fix
}
__inet_hash_nolisten() {
...
spin_lock(lock);
if (osk) {
*own_req = sk_nulls_del_node_init_rcu(osk);
}
spin_unlock(lock);
...
}
inet_csk_complete_hashdance() {
if (own_req) {
inet_csk_reqsk_queue_drop(sk, req);
reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
if (inet_csk_reqsk_queue_add(sk, req, child))
return child;
}
/* Too bad, another child took ownership of the request, undo. */
bh_unlock_sock(child);
sock_put(child);
return NULL;
}
|
3.10.0 的代码 syn_recv_sock() -> __inet_hash_nolisten(newsk, NULL); 直接把newsk加到队列, 不考虑req。
就有可能出现BUG: 一个req创建了2个newsk, 在inet_csk_complete_hashdance的时候, 2个newsk都执行了inet_csk_reqsk_queue_drop. 导致req被多次释放
所以在splock内只让一个线程创建成功newsk, 用own_req标记