modem: avoid spurious retransmit. limit ppp buffer size.

set actual modem receive speed to 48K
picotcp: avoid spurious retransmit after window full. set min RTO to 1
sec.
buffer incoming data until pico socket can accept it.
limit tcp payload to 512 bytes.

Issue #114
This commit is contained in:
Flyinghead 2020-11-30 12:52:46 +01:00
parent 8f77a5482a
commit 1d17dbd121
4 changed files with 157 additions and 105 deletions

View File

@ -11,7 +11,7 @@
#include "pico_protocol.h"
#include "pico_tree.h"
#ifdef __linux__
#if 0 // __linux__
#define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */
#else
#define PICO_DEFAULT_SOCKETQ (6 * 1024) /* seems like an acceptable default for small embedded systems */

View File

@ -24,7 +24,7 @@
#define TCP_TIME (pico_time)(PICO_TIME_MS())
#define PICO_TCP_RTO_MIN (70)
#define PICO_TCP_RTO_MIN (1000)
#define PICO_TCP_RTO_MAX (120000)
#define PICO_TCP_IW 2
#define PICO_TCP_SYN_TO 2000u
@ -1444,6 +1444,7 @@ static inline void tcp_fill_rst_header(struct pico_frame *fr, struct pico_tcp_hd
if(!(hdr1->flags & PICO_TCP_ACK))
hdr->ack = long_be(long_be(((struct pico_tcp_hdr *)(fr->transport_hdr))->seq) + fr->payload_len);
hdr->crc = 0;
hdr->crc = short_be(pico_tcp_checksum(f));
}
@ -1477,6 +1478,7 @@ int pico_tcp_reply_rst(struct pico_frame *fr)
if (0) {
#ifdef PICO_SUPPORT_IPV4
} else if (IS_IPV4(f)) {
f->local_ip.addr = fr->sock->local_addr.ip4.addr; // Masqueraded
tcp_dbg("Pushing IPv4 reset frame...\n");
pico_ipv4_frame_push(f, &(((struct pico_ipv4_hdr *)(f->net_hdr))->dst), PICO_PROTO_TCP);
#endif
@ -1866,7 +1868,7 @@ static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f)
t->snd_last_out = SEQN(cpy);
add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME);
tcp_dbg("TCP_CWND, %lu, %u, %u, %u\n", TCP_TIME, t->cwnd, t->ssthresh, t->in_flight);
tcp_dbg("Sending RTO!\n");
tcp_dbg("Sending RTO! port %d\n", short_be(t->sock.remote_port));
return 1;
} else {
tcp_dbg("RTO fail, retry!\n");
@ -1939,7 +1941,7 @@ static void tcp_retrans_timeout(pico_time val, void *sock)
return;
}
tcp_dbg("TIMEOUT! backoff = %d, rto: %d\n", t->backoff, t->rto);
tcp_dbg("TIMEOUT! backoff = %d, rto: %d, port: %d\n", t->backoff, t->rto, short_be(t->sock.remote_port));
t->retrans_tmr_due = 0ull;
if (tcp_is_allowed_to_send(t)) {
@ -1986,11 +1988,17 @@ static void add_retransmission_timer(struct pico_socket_tcp *t, pico_time next_t
val = next_ts + (t->rto << t->backoff);
}
}
if (next_ts == 0)
{
t->retrans_tmr_due = 0;
return;
}
tcp_dbg("add_retransmission_timer: timestamp %d, timeout %d, rto %d\n", (uint32_t)(next_ts - now), (uint32_t)(val - now), t->rto << t->backoff);
} else {
val = next_ts;
}
if ((val > 0) || (val > now)) {
if (val > now) {
t->retrans_tmr_due = val;
} else {
t->retrans_tmr_due = now + 1;
@ -2837,10 +2845,10 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
f->payload = (f->transport_hdr + ((hdr->len & 0xf0u) >> 2u));
f->payload_len = (uint16_t)(f->transport_len - ((hdr->len & 0xf0u) >> 2u));
tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len);
tcp_dbg("[sam] TCP> flags = 0x%02x\n", hdr->flags);
tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
tcp_dbg("[sam] TCP> [tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
// tcp_dbg("[sam] TCP> [tcp input] t_len: %u\n", f->transport_len);
// tcp_dbg("[sam] TCP> flags = 0x%02x\n", hdr->flags);
// tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
tcp_dbg("[tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
/* This copy of the frame has the current socket as owner */
f->sock = s;
@ -2960,11 +2968,11 @@ int pico_tcp_output(struct pico_socket *s, int loop_score)
if (t->x_mode != PICO_TCP_WINDOW_FULL) {
tcp_dbg("TCP> RIGHT SIZING (rwnd: %d, frame len: %d\n", t->recv_wnd << t->recv_wnd_scale, f->payload_len);
tcp_dbg("In window full...\n");
t->snd_nxt = SEQN(una);
t->snd_retry = SEQN(una);
t->snd_nxt = SEQN(f);
t->snd_retry = SEQN(f);
t->x_mode = PICO_TCP_WINDOW_FULL;
}
// TODO ? add_retransmission_timer(t, 0);
break;
}

View File

@ -284,7 +284,11 @@ static int modem_sched_func(int tag, int cycles, int jitter)
if (connected_time == 0)
connected_time = sh4_sched_now64();
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 238; // 238 us
// This value is critical. Setting it too low will cause some sockets to stall.
// Check Sonic Adventure 2 and Samba de Amigo (PAL) integrated browsers.
// 143 us/bytes corresponds to 56K but is too low for SA2 and SdA. They need >= 160.
// Using 166 for now (~ 48Kbps)
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 166;
modem_regs.reg1e.TDBE = 1;
if (!modem_regs.reg1e.RDBF)
@ -302,9 +306,6 @@ static int modem_sched_func(int tag, int cycles, int jitter)
if (modem_regs.reg04.FIFOEN)
SET_STATUS_BIT(0x0c, modem_regs.reg0c.RXFNE, 1);
SET_STATUS_BIT(0x01, modem_regs.reg01.RXHF, 1);
// Set small value to receive following data quickly.
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 62; // 62 us
}
}

View File

@ -65,10 +65,93 @@ static struct pico_socket *pico_tcp_socket, *pico_udp_socket;
struct pico_ip4 public_ip;
struct pico_ip4 afo_ip;
// src socket -> socket fd
static std::map<struct pico_socket *, sock_t> tcp_sockets;
struct socket_pair
{
socket_pair() : pico_sock(nullptr), native_sock(INVALID_SOCKET) {}
socket_pair(pico_socket *pico_sock, sock_t native_sock) : pico_sock(pico_sock), native_sock(native_sock) {}
~socket_pair() {
if (pico_sock != nullptr)
pico_socket_close(pico_sock);
if (native_sock != INVALID_SOCKET)
closesocket(native_sock);
}
socket_pair(socket_pair &&) = default;
socket_pair(const socket_pair&) = delete;
socket_pair& operator=(const socket_pair&) = delete;
pico_socket *pico_sock;
sock_t native_sock;
std::vector<u8> in_buffer;
void receive_native()
{
size_t len;
const u8 *data;
u8 buf[512];
if (!in_buffer.empty())
{
len = in_buffer.size();
data = &in_buffer[0];
}
else
{
if (native_sock == INVALID_SOCKET)
return;
int r = recv(native_sock, buf, sizeof(buf), 0);
if (r == 0)
{
INFO_LOG(MODEM, "Socket[%d] recv(%zd) returned 0 -> EOF", short_be(pico_sock->remote_port), sizeof(buf));
pico_socket_shutdown(pico_sock, PICO_SHUT_RDWR);
closesocket(native_sock);
native_sock = INVALID_SOCKET;
return;
}
if (r < 0)
{
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
{
perror("recv tcp socket");
closesocket(native_sock);
native_sock = INVALID_SOCKET;
pico_socket_shutdown(pico_sock, PICO_SHUT_RDWR);
}
return;
}
len = r;
data = buf;
}
if (pico_sock->remote_port == short_be(5011) && len >= 5)
{
// Visual Concepts sport games
if (buf[0] == 1)
memcpy(&buf[1], &pico_sock->local_addr.ip4.addr, 4);
}
int r2 = pico_socket_send(pico_sock, data, len);
if (r2 < 0)
INFO_LOG(MODEM, "error TCP sending: %s", strerror(pico_err));
else if (r2 < (int)len)
{
if (r2 > 0 || in_buffer.empty())
{
len -= r2;
std::vector<u8> remain(len);
memcpy(&remain[0], &data[r2], len);
std::swap(in_buffer, remain);
}
}
else
{
in_buffer.clear();
}
}
};
// tcp sockets
static std::map<struct pico_socket *, socket_pair> tcp_sockets;
static std::map<struct pico_socket *, sock_t> tcp_connecting_sockets;
// src port -> socket fd
// udp sockets: src port -> socket fd
static std::map<uint16_t, sock_t> udp_sockets;
static const uint16_t games_udp_ports[] = {
@ -105,6 +188,9 @@ static const uint16_t games_tcp_ports[] = {
// listening port -> socket fd
static std::map<uint16_t, sock_t> tcp_listening_sockets;
static bool pico_stack_inited;
static bool pico_thread_running = false;
static void read_native_sockets();
void get_host_by_name(const char *name, struct pico_ip4 dnsaddr);
int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr);
@ -131,10 +217,17 @@ static int modem_write(struct pico_device *dev, const void *data, int len)
u8 *p = (u8 *)data;
in_buffer_lock.lock();
while (len > 0)
for (int i = 0; i < len; i++)
{
while (in_buffer.size() > 1024)
{
in_buffer_lock.unlock();
if (!pico_thread_running)
return 0;
usleep(5000);
in_buffer_lock.lock();
}
in_buffer.push(*p++);
len--;
}
in_buffer_lock.unlock();
@ -175,8 +268,6 @@ static void read_from_dc_socket(pico_socket *pico_sock, sock_t nat_sock)
if (send(nat_sock, buf, r, 0) < r)
{
perror("tcp_callback send");
closesocket(nat_sock);
pico_socket_close(pico_sock);
tcp_sockets.erase(pico_sock);
}
}
@ -194,7 +285,7 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
}
else
{
read_from_dc_socket(it->first, it->second);
read_from_dc_socket(it->first, it->second.native_sock);
}
}
@ -215,7 +306,7 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
else
{
pico_ipv4_to_string(peer, sock_a->local_addr.ip4.addr);
//printf("Connection established from %s:%d to %08x:%d\n", peer, short_be(port), sock_a->local_addr.ip4.addr, short_be(sock_a->local_port));
//printf("Connection established from port %d to %s:%d\n", short_be(port), peer, short_be(sock_a->local_port));
pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes);
/* Set keepalive options */
// uint32_t ka_val = 5;
@ -259,7 +350,9 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
{
set_tcp_nodelay(sockfd);
tcp_sockets[sock_a] = sockfd;
tcp_sockets.emplace(std::piecewise_construct,
std::forward_as_tuple(sock_a),
std::forward_as_tuple(sock_a, sockfd));
}
}
}
@ -269,7 +362,6 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
auto it = tcp_sockets.find(s);
if (it != tcp_sockets.end())
{
closesocket(it->second);
tcp_sockets.erase(it);
}
else
@ -289,14 +381,9 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
INFO_LOG(MODEM, "Socket error received: %s", strerror(pico_err));
auto it = tcp_sockets.find(s);
if (it == tcp_sockets.end())
{
INFO_LOG(MODEM, "PICO_SOCK_EV_ERR: Unknown socket: remote port %d", short_be(s->remote_port));
}
else
{
closesocket(it->second);
tcp_sockets.erase(it);
}
}
if (ev & PICO_SOCK_EV_CLOSE)
@ -308,7 +395,8 @@ static void tcp_callback(uint16_t ev, struct pico_socket *s)
}
else
{
shutdown(it->second, SHUT_WR);
if (it->second.native_sock != INVALID_SOCKET)
shutdown(it->second.native_sock, SHUT_WR);
pico_socket_shutdown(s, PICO_SHUT_RD);
}
}
@ -420,7 +508,11 @@ static void read_native_sockets()
}
set_non_blocking(sockfd);
set_tcp_nodelay(sockfd);
tcp_sockets[ps] = sockfd;
tcp_sockets.emplace(std::piecewise_construct,
std::forward_as_tuple(ps),
std::forward_as_tuple(ps, sockfd));
}
// Check connecting outbound TCP sockets
@ -461,7 +553,10 @@ static void read_native_sockets()
else
{
set_tcp_nodelay(it->second);
tcp_sockets[it->first] = it->second;
tcp_sockets.emplace(std::piecewise_construct,
std::forward_as_tuple(it->first),
std::forward_as_tuple(it->first, it->second));
read_from_dc_socket(it->first, it->second);
}
@ -469,16 +564,9 @@ static void read_native_sockets()
}
}
char buf[1500]; // FIXME MTU ?
char buf[1500];
struct pico_msginfo msginfo;
// If modem buffer is full, wait
in_buffer_lock.lock();
size_t in_buffer_size = in_buffer.size();
in_buffer_lock.unlock();
if (in_buffer_size >= 256)
return;
// Read UDP sockets
for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++)
{
@ -510,45 +598,11 @@ static void read_native_sockets()
// Read TCP sockets
for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); )
{
uint32_t space;
pico_tcp_get_bufspace_out(it->first, &space);
if (space < sizeof(buf))
{
// Wait for the out buffer to empty a bit
it++;
continue;
}
r = recv(it->second, buf, sizeof(buf), 0);
if (r > 0)
{
if (it->first->remote_port == short_be(5011) && r >= 5)
{
// Visual Concepts sport games
if (buf[0] == 1)
memcpy(&buf[1], &it->first->local_addr.ip4.addr, 4);
}
int r2 = pico_socket_send(it->first, buf, r);
if (r2 < 0)
INFO_LOG(MODEM, "error TCP sending: %s", strerror(pico_err));
else if (r2 < r)
// FIXME EAGAIN errors. Need to buffer data or wait for call back.
INFO_LOG(MODEM, "truncated send: %d -> %d", r, r2);
}
else if (r == 0)
{
pico_socket_shutdown(it->first, PICO_SHUT_WR);
shutdown(it->second, SHUT_RD);
}
else if (r < 0 && get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
{
perror("recv tcp socket");
closesocket(it->second);
pico_socket_close(it->first);
it->second.receive_native();
if (it->second.pico_sock == nullptr)
it = tcp_sockets.erase(it);
continue;
}
it++;
else
it++;
}
}
@ -557,11 +611,6 @@ void close_native_sockets()
for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++)
closesocket(it->second);
udp_sockets.clear();
for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++)
{
pico_socket_close(it->first);
closesocket(it->second);
}
tcp_sockets.clear();
for (auto it = tcp_connecting_sockets.begin(); it != tcp_connecting_sockets.end(); it++)
{
@ -576,21 +625,6 @@ static int modem_set_speed(struct pico_device *dev, uint32_t speed)
return 0;
}
#if 0 // _WIN32
static void usleep(unsigned int usec)
{
HANDLE timer;
LARGE_INTEGER ft;
ft.QuadPart = -(10 * (__int64)usec);
timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
CloseHandle(timer);
}
#endif
static void check_dns_entries()
{
static uint32_t dns_query_start = 0;
@ -669,9 +703,6 @@ static void check_dns_entries()
}
}
static bool pico_stack_inited;
static bool pico_thread_running = false;
static void *pico_thread_func(void *)
{
struct pico_ip4 ipaddr;
@ -769,6 +800,18 @@ static void *pico_thread_func(void *)
tcp_listening_sockets[port] = sockfd;
}
{
std::queue<u8> empty;
in_buffer_lock.lock();
std::swap(in_buffer, empty);
in_buffer_lock.unlock();
std::queue<u8> empty2;
out_buffer_lock.lock();
std::swap(out_buffer, empty2);
out_buffer_lock.unlock();
}
pico_ppp_set_serial_read(ppp, modem_read);
pico_ppp_set_serial_write(ppp, modem_write);
pico_ppp_set_serial_set_speed(ppp, modem_set_speed);
@ -780,7 +823,7 @@ static void *pico_thread_func(void *)
read_native_sockets();
pico_stack_tick();
check_dns_entries();
usleep(1000);
usleep(5000);
}
for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++)