Compare commits

..
Author SHA1 Message Date
Leency 4f181f1718 kernel/net: recheck the ARP entry after sleeping for a reply
Check kernel codestyle / Check kernel codestyle (pull_request) Successful in 23s
Test PR / Build (en_US) (pull_request) Successful in 2m47s
Test PR / Build (es_ES) (pull_request) Successful in 2m59s
Test PR / Build (ru_RU) (pull_request) Successful in 3m19s
arp_del_entry compacts the table, so the entry could move while an app
waited and esi then pointed at another IP's entry - a valid one sent the
packet to the wrong MAC. Look the IP up again when it no longer matches.
2026-09-30 10:14:27 +03:00
Leency 9c7ddc8221 kernel/net: never busy-wait for an ARP reply in a kernel thread
arp_ip_to_mac spun in delay_ms until the ARP request timed out. The
kernel's network threads (TCP input, TCP timers, ethernet input) and the
OS task run at MAX_PRIORITY, so while one of them spun here no
application was scheduled at all: on a tester's laptop the desktop and
zeroconf froze for the full 20 s ARP timeout every time a TCP segment
went out on an interface that had just lost its address.

A kernel thread now gives up at once (TCP retransmits the segment). An
application thread sleeps in 10 ms steps for at most ARP_WAIT_TICKS
(1 s) and then gives up as well; the reply arrives through the ethernet
input thread, which keeps running meanwhile.
2026-09-30 10:14:26 +03:00
6 changed files with 54 additions and 123 deletions

No files matched your search

+47 -7
View File
@@ -23,6 +23,7 @@ ARP_AWAITING_RESPONSE = 2
ARP_RESPONSE_TIMEOUT = 3
ARP_REQUEST_TTL = 31 ; 20 s
ARP_WAIT_TICKS = 100 ; 1 s
ARP_ENTRY_TTL = 937 ; 600 s
ARP_STATIC_ENTRY = -1
@@ -565,10 +566,10 @@ arp_ip_to_mac:
; Now send a request packet on the network
pop edi eax ; IP in eax, device number in ebx, for ARP_output_request
push esi edi
push eax esi edi ; arp_output_request trashes eax
mov ebx, [net_device_list + edi]
call arp_output_request
pop edi esi
pop edi esi eax
.found_it:
cmp [esi + ARP_entry.Status], ARP_VALID_MAPPING ; Does it have a MAC assigned?
je .valid
@@ -577,11 +578,50 @@ if ARP_BLOCK
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE ; Are we waiting for reply from remote end?
jne .give_up
push esi
mov esi, 10 ; wait 10 ms
call delay_ms
pop esi
jmp .found_it ; now check again
; Only applications may wait for the reply, kernel threads give up at once
push ecx edx
mov edx, [current_slot]
cmp [edx + APPDATA.priority], MAX_PRIORITY
je .wait_over
mov edx, [timer_ticks]
add edx, ARP_WAIT_TICKS
.wait:
push ebx
mov ebx, 1 ; sleep 10 ms
call delay_hs
pop ebx
; The entry may have moved (arp_del_entry), find it again
cmp [esi + ARP_entry.IP], eax
je .same_entry
mov ecx, [ARP_entries + edi]
test ecx, ecx
jz .wait_over
mov esi, edi
imul esi, (sizeof.ARP_entry * ARP_TABLE_SIZE)/4
add esi, ARP_table + ARP_entry.IP
.rescan:
cmp [esi], eax
je .same_entry
add esi, sizeof.ARP_entry
dec ecx
jnz .rescan
jmp .wait_over
.same_entry:
cmp [esi + ARP_entry.Status], ARP_VALID_MAPPING
je .wait_done
cmp [esi + ARP_entry.Status], ARP_AWAITING_RESPONSE
jne .wait_over
mov ecx, [timer_ticks]
sub ecx, edx
js .wait
.wait_over:
pop edx ecx
jmp .give_up
.wait_done:
pop edx ecx
else
+1 -1
View File
@@ -63,7 +63,7 @@ TCP_OPT_TIMESTAMP = 8
TCP_time_MSL = 47 ; max segment lifetime (30s)
TCP_time_re_min = 2 ; min retransmission (1,28s)
TCP_time_re_max = 100 ; max retransmission (64s)
TCP_time_pers_min = 2 ; min persist (1,28s)
TCP_time_pers_min = 8 ; min persist (5,12s)
TCP_time_pers_max = 94 ; max persist (60,16s)
TCP_time_keep_init = 118 ; connection establishment (75,52s)
TCP_time_keep_idle = 4608 ; idle time before 1st probe (2h)
-6
View File
@@ -525,11 +525,8 @@ endl
test [temp_bits], TCP_BIT_TIMESTAMP
jz .no_timestamp_rtt
cmp [ebx + TCP_SOCKET.ts_ecr], 0 ; nothing echoed
je .no_timestamp_rtt
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
shr eax, 6 ; 1/100 s -> 640 ms ticks
inc eax
call tcp_xmit_timer
jmp .rtt_done
@@ -1117,11 +1114,8 @@ endl
test [temp_bits], TCP_BIT_TIMESTAMP
jz .timestamp_not_present
cmp [ebx + TCP_SOCKET.ts_ecr], 0 ; nothing echoed
je .timestamp_not_present
mov eax, [timestamp]
sub eax, [ebx + TCP_SOCKET.ts_ecr]
shr eax, 6 ; 1/100 s -> 640 ms ticks
inc eax
call tcp_xmit_timer
jmp .rtt_done_
+1 -1
View File
@@ -626,7 +626,7 @@ endl
mov [eax + TCP_SOCKET.SND_MAX], edx ; [eax + TCP_SOCKET.SND_NXT] from before we updated it
cmp [eax + TCP_SOCKET.t_rtt], 0 ; are we currently timing anything?
jne @f
je @f
mov [eax + TCP_SOCKET.t_rtt], 1 ; nope, start transmission timer
mov [eax + TCP_SOCKET.t_rtseq], edi
inc [TCPS_segstimed]
+5 -15
View File
@@ -96,7 +96,7 @@ macro tcp_init_socket socket {
mov [socket + TCP_SOCKET.t_srtt], TCP_time_srtt_default
mov [socket + TCP_SOCKET.t_rttvar], TCP_time_rtt_default * 4
mov [socket + TCP_SOCKET.t_rttmin], TCP_time_re_min
mov [socket + TCP_SOCKET.t_rxtcur], TCP_time_rtt_default
;;; TODO: TCP_time_rangeset
mov [socket + TCP_SOCKET.SND_CWND], TCP_max_win shl TCP_max_winshift
mov [socket + TCP_SOCKET.SND_SSTHRESH], TCP_max_win shl TCP_max_winshift
@@ -518,7 +518,7 @@ tcp_xmit_timer:
inc [TCPS_rttupdated]
cmp [ebx + TCP_SOCKET.t_srtt], 0 ; first sample?
cmp [ebx + TCP_SOCKET.t_rtt], 0
je .no_rtt_yet
; srtt is stored as a fixed point with 3 bits after the binary point.
@@ -534,7 +534,7 @@ tcp_xmit_timer:
pop ecx
add [ebx + TCP_SOCKET.t_srtt], eax
jg @f ; signed: delta may be negative
ja @f
mov [ebx + TCP_SOCKET.t_srtt], 1
@@:
@@ -556,10 +556,10 @@ tcp_xmit_timer:
pop edx
add [ebx + TCP_SOCKET.t_rttvar], eax
jg @f
ja @f
mov [ebx + TCP_SOCKET.t_rttvar], 1
@@:
jmp .rto
ret
.no_rtt_yet:
@@ -572,16 +572,6 @@ tcp_xmit_timer:
mov [ebx + TCP_SOCKET.t_rttvar], eax
pop ecx
.rto:
; Retransmit timeout = srtt + 4*rttvar, reset the backoff
push ecx
mov ecx, [ebx + TCP_SOCKET.t_srtt]
shr ecx, TCP_RTT_SHIFT
add ecx, [ebx + TCP_SOCKET.t_rttvar]
tcpt_rangeset [ebx + TCP_SOCKET.t_rxtcur], ecx, TCP_time_re_min, TCP_time_re_max
pop ecx
mov [ebx + TCP_SOCKET.t_rxtshift], 0
mov [ebx + TCP_SOCKET.t_rtt], 0 ; the timed segment is done
ret
-93
View File
@@ -91,10 +91,6 @@ proc tcp_timer_640ms
jne .loop
inc [eax + TCP_SOCKET.t_idle]
cmp [eax + TCP_SOCKET.t_rtt], 0 ; timing a segment?
je @f
inc [eax + TCP_SOCKET.t_rtt]
@@:
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .check_more2
@@ -103,95 +99,6 @@ proc tcp_timer_640ms
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: Retransmission timer expired\n", eax
; Lock socket, an ACK may have stopped or restarted the timer meanwhile
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_lock
popa
test [eax + TCP_SOCKET.timer_flags], timer_flag_retransmission
jz .rexmt_cancelled
cmp [eax + TCP_SOCKET.timer_retransmission], 0
jne .rexmt_cancelled
; Too many retransmissions? Drop the connection
inc [eax + TCP_SOCKET.t_rxtshift]
cmp [eax + TCP_SOCKET.t_rxtshift], TCP_max_rxtshift
jbe .rexmt
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
DEBUGF DEBUG_NETWORK_VERBOSE, "socket %x: too many retransmissions, dropping\n", eax
push [eax + SOCKET.NextPtr]
mov ebx, ETIMEDOUT
call tcp_drop
pop eax
jmp .check_only
.rexmt_cancelled:
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
jmp .check_more2
.rexmt:
push ebx ecx edx
; Restart timer with backoff: t_rxtcur << min(t_rxtshift, 6)
mov ebx, [eax + TCP_SOCKET.t_rxtcur]
mov cl, [eax + TCP_SOCKET.t_rxtshift]
cmp cl, 6
jbe @f
mov cl, 6
@@:
shl ebx, cl
cmp ebx, TCP_time_re_min
jae @f
mov ebx, TCP_time_re_min
@@:
cmp ebx, TCP_time_re_max
jbe @f
mov ebx, TCP_time_re_max
@@:
mov [eax + TCP_SOCKET.timer_retransmission], ebx
; Resend from the last acknowledged byte, don't time it
push [eax + TCP_SOCKET.SND_UNA]
pop [eax + TCP_SOCKET.SND_NXT]
mov [eax + TCP_SOCKET.t_rtt], 0
mov [eax + TCP_SOCKET.t_dupacks], 0
; Slow start: ssthresh = max(2, min(wnd, cwnd) / 2 / mss) * mss, cwnd = mss
mov ecx, [eax + TCP_SOCKET.t_maxseg]
mov edx, [eax + TCP_SOCKET.SND_WND]
cmp edx, [eax + TCP_SOCKET.SND_CWND]
jbe @f
mov edx, [eax + TCP_SOCKET.SND_CWND]
@@:
mov ebx, eax ; socket ptr
mov eax, edx
shr eax, 1
xor edx, edx
div ecx
cmp eax, 2
jae @f
mov eax, 2
@@:
mul ecx
mov [ebx + TCP_SOCKET.SND_SSTHRESH], eax
mov [ebx + TCP_SOCKET.SND_CWND], ecx
mov eax, ebx
pop edx ecx ebx
pusha
lea ecx, [eax + SOCKET.mutex]
call mutex_unlock
popa
push eax
call tcp_output
pop eax