Skip to content

Commit 26e978e

Browse files
committed
extmod/modlwip: Implement a queue of incoming UDP/raw packets.
The bare-metal lwIP socket interface is currently quite limited when used for UDP streams, because it only allows one outstanding incoming UDP packet. If one UDP packet is waiting to be socket.recv'd and another one comes along, then the second one is simply dropped. This commit implements a queue for incoming UDP and raw packets. The queue depth is fixed at compile time, and is currently 4. This allows better use of UDP connections, eg more efficient. It also makes DTLS work better which sometimes has a queue of UDP packets (eg during the connection phase). Signed-off-by: Damien George <[email protected]>
1 parent 61eedbb commit 26e978e

File tree

1 file changed

+114
-53
lines changed

1 file changed

+114
-53
lines changed

extmod/modlwip.c

Lines changed: 114 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,15 @@ static const int error_lookup_table[] = {
286286
#define MOD_NETWORK_SOCK_DGRAM (2)
287287
#define MOD_NETWORK_SOCK_RAW (3)
288288

289+
// Total queue length for buffered UDP/raw incoming packets.
290+
#define LWIP_INCOMING_PACKET_QUEUE_LEN (4)
291+
292+
typedef struct _lwip_incoming_packet_t {
293+
struct pbuf *pbuf;
294+
ip_addr_t peer_addr;
295+
uint16_t peer_port;
296+
} lwip_incoming_packet_t;
297+
289298
typedef struct _lwip_socket_obj_t {
290299
mp_obj_base_t base;
291300

@@ -294,8 +303,11 @@ typedef struct _lwip_socket_obj_t {
294303
struct udp_pcb *udp;
295304
struct raw_pcb *raw;
296305
} pcb;
306+
307+
// Data structure that holds incoming pbuf's.
308+
// Each socket type has different state that it needs to keep track of.
297309
volatile union {
298-
struct pbuf *pbuf;
310+
// TCP listening sockets have a queue of incoming connections, implemented as a ringbuffer.
299311
struct {
300312
uint8_t alloc;
301313
uint8_t iget;
@@ -305,10 +317,23 @@ typedef struct _lwip_socket_obj_t {
305317
struct tcp_pcb **array; // if alloc != 0
306318
} tcp;
307319
} connection;
320+
321+
// Connected TCP sockets have a single incoming pbuf that new data is appended to.
322+
struct {
323+
struct pbuf *pbuf;
324+
} tcp;
325+
326+
// UDP and raw sockets have a queue of incoming pbuf's, implemented as a ringbuffer.
327+
struct {
328+
uint8_t iget; // ringbuffer read index
329+
uint8_t iput; // ringbuffer write index
330+
lwip_incoming_packet_t *array;
331+
} udp_raw;
308332
} incoming;
333+
309334
mp_obj_t callback;
310-
ip_addr_t peer;
311-
mp_uint_t peer_port;
335+
ip_addr_t tcp_peer_addr;
336+
mp_uint_t tcp_peer_port;
312337
mp_uint_t timeout;
313338
uint16_t recv_offset;
314339

@@ -347,9 +372,21 @@ static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) {
347372
&& socket->pcb.tcp->state == LISTEN;
348373

349374
if (!socket_is_listener) {
350-
if (socket->incoming.pbuf != NULL) {
351-
pbuf_free(socket->incoming.pbuf);
352-
socket->incoming.pbuf = NULL;
375+
if (socket->type == MOD_NETWORK_SOCK_STREAM) {
376+
if (socket->incoming.tcp.pbuf != NULL) {
377+
pbuf_free(socket->incoming.tcp.pbuf);
378+
socket->incoming.tcp.pbuf = NULL;
379+
}
380+
} else {
381+
for (size_t i = 0; i < LWIP_INCOMING_PACKET_QUEUE_LEN; ++i) {
382+
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[i];
383+
if (slot->pbuf != NULL) {
384+
pbuf_free(slot->pbuf);
385+
slot->pbuf = NULL;
386+
}
387+
}
388+
socket->incoming.udp_raw.iget = 0;
389+
socket->incoming.udp_raw.iput = 0;
353390
}
354391
} else {
355392
uint8_t alloc = socket->incoming.connection.alloc;
@@ -407,6 +444,19 @@ static inline void exec_user_callback(lwip_socket_obj_t *socket) {
407444
}
408445
}
409446

447+
static void udp_raw_incoming(lwip_socket_obj_t *socket, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
448+
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iput];
449+
if (slot->pbuf != NULL) {
450+
// No room in the inn, drop the packet.
451+
pbuf_free(p);
452+
} else {
453+
slot->pbuf = p;
454+
slot->peer_addr = *addr;
455+
slot->peer_port = port;
456+
socket->incoming.udp_raw.iput = (socket->incoming.udp_raw.iput + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
457+
}
458+
}
459+
410460
#if MICROPY_PY_LWIP_SOCK_RAW
411461
// Callback for incoming raw packets.
412462
#if LWIP_VERSION_MAJOR < 2
@@ -416,13 +466,7 @@ static u8_t _lwip_raw_incoming(void *arg, struct raw_pcb *pcb, struct pbuf *p, c
416466
#endif
417467
{
418468
lwip_socket_obj_t *socket = (lwip_socket_obj_t *)arg;
419-
420-
if (socket->incoming.pbuf != NULL) {
421-
pbuf_free(p);
422-
} else {
423-
socket->incoming.pbuf = p;
424-
memcpy(&socket->peer, addr, sizeof(socket->peer));
425-
}
469+
udp_raw_incoming(socket, p, addr, 0);
426470
return 1; // we ate the packet
427471
}
428472
#endif
@@ -436,15 +480,7 @@ static void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p,
436480
#endif
437481
{
438482
lwip_socket_obj_t *socket = (lwip_socket_obj_t *)arg;
439-
440-
if (socket->incoming.pbuf != NULL) {
441-
// That's why they call it "unreliable". No room in the inn, drop the packet.
442-
pbuf_free(p);
443-
} else {
444-
socket->incoming.pbuf = p;
445-
socket->peer_port = (mp_uint_t)port;
446-
memcpy(&socket->peer, addr, sizeof(socket->peer));
447-
}
483+
udp_raw_incoming(socket, p, addr, port);
448484
}
449485

450486
// Callback for general tcp errors.
@@ -562,13 +598,13 @@ static err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
562598
return ERR_OK;
563599
}
564600

565-
if (socket->incoming.pbuf == NULL) {
566-
socket->incoming.pbuf = p;
601+
if (socket->incoming.tcp.pbuf == NULL) {
602+
socket->incoming.tcp.pbuf = p;
567603
} else {
568604
#ifdef SOCKET_SINGLE_PBUF
569605
return ERR_BUF;
570606
#else
571-
pbuf_cat(socket->incoming.pbuf, p);
607+
pbuf_cat(socket->incoming.tcp.pbuf, p);
572608
#endif
573609
}
574610

@@ -639,7 +675,9 @@ static mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m
639675
// Helper function for recv/recvfrom to handle raw/UDP packets
640676
static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t *port, int *_errno) {
641677

642-
if (socket->incoming.pbuf == NULL) {
678+
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
679+
680+
if (slot->pbuf == NULL) {
643681
if (socket->timeout == 0) {
644682
// Non-blocking socket.
645683
*_errno = MP_EAGAIN;
@@ -648,7 +686,7 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u
648686

649687
// Wait for data to arrive on UDP socket.
650688
mp_uint_t start = mp_hal_ticks_ms();
651-
while (socket->incoming.pbuf == NULL) {
689+
while (slot->pbuf == NULL) {
652690
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
653691
*_errno = MP_ETIMEDOUT;
654692
return -1;
@@ -658,17 +696,18 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u
658696
}
659697

660698
if (ip != NULL) {
661-
memcpy(ip, &socket->peer, sizeof(socket->peer));
662-
*port = socket->peer_port;
699+
*ip = slot->peer_addr;
700+
*port = slot->peer_port;
663701
}
664702

665-
struct pbuf *p = socket->incoming.pbuf;
703+
struct pbuf *p = slot->pbuf;
666704

667705
MICROPY_PY_LWIP_ENTER
668706

669707
u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0);
670708
pbuf_free(p);
671-
socket->incoming.pbuf = NULL;
709+
slot->pbuf = NULL;
710+
socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
672711

673712
MICROPY_PY_LWIP_EXIT
674713

@@ -780,7 +819,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
780819
// Check for any pending errors
781820
STREAM_ERROR_CHECK(socket);
782821

783-
if (socket->incoming.pbuf == NULL) {
822+
if (socket->incoming.tcp.pbuf == NULL) {
784823

785824
// Non-blocking socket
786825
if (socket->timeout == 0) {
@@ -792,7 +831,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
792831
}
793832

794833
mp_uint_t start = mp_hal_ticks_ms();
795-
while (socket->state == STATE_CONNECTED && socket->incoming.pbuf == NULL) {
834+
while (socket->state == STATE_CONNECTED && socket->incoming.tcp.pbuf == NULL) {
796835
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
797836
*_errno = MP_ETIMEDOUT;
798837
return -1;
@@ -801,7 +840,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
801840
}
802841

803842
if (socket->state == STATE_PEER_CLOSED) {
804-
if (socket->incoming.pbuf == NULL) {
843+
if (socket->incoming.tcp.pbuf == NULL) {
805844
// socket closed and no data left in buffer
806845
return 0;
807846
}
@@ -819,7 +858,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
819858

820859
assert(socket->pcb.tcp != NULL);
821860

822-
struct pbuf *p = socket->incoming.pbuf;
861+
struct pbuf *p = socket->incoming.tcp.pbuf;
823862

824863
mp_uint_t remaining = p->len - socket->recv_offset;
825864
if (len > remaining) {
@@ -830,7 +869,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
830869

831870
remaining -= len;
832871
if (remaining == 0) {
833-
socket->incoming.pbuf = p->next;
872+
socket->incoming.tcp.pbuf = p->next;
834873
// If we don't ref here, free() will free the entire chain,
835874
// if we ref, it does what we need: frees 1st buf, and decrements
836875
// next buf's refcount back to 1.
@@ -854,8 +893,18 @@ static const mp_obj_type_t lwip_socket_type;
854893

855894
static void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
856895
lwip_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
857-
mp_printf(print, "<socket state=%d timeout=%d incoming=%p off=%d>", self->state, self->timeout,
858-
self->incoming.pbuf, self->recv_offset);
896+
mp_printf(print, "<socket state=%d timeout=%d incoming=", self->state, self->timeout);
897+
if (self->type == MOD_NETWORK_SOCK_STREAM) {
898+
mp_printf(print, "%p off=%d>", self->incoming.tcp.pbuf, self->recv_offset);
899+
} else {
900+
int num_in_queue = 0;
901+
for (size_t i = 0; i < LWIP_INCOMING_PACKET_QUEUE_LEN; ++i) {
902+
if (self->incoming.udp_raw.array[i].pbuf != NULL) {
903+
++num_in_queue;
904+
}
905+
}
906+
mp_printf(print, "%d>", num_in_queue);
907+
}
859908
}
860909

861910
// FIXME: Only supports two arguments at present
@@ -884,16 +933,22 @@ static mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, s
884933
socket->incoming.connection.tcp.item = NULL;
885934
break;
886935
case MOD_NETWORK_SOCK_DGRAM:
887-
socket->pcb.udp = udp_new();
888-
socket->incoming.pbuf = NULL;
889-
break;
890936
#if MICROPY_PY_LWIP_SOCK_RAW
891-
case MOD_NETWORK_SOCK_RAW: {
892-
mp_int_t proto = n_args <= 2 ? 0 : mp_obj_get_int(args[2]);
893-
socket->pcb.raw = raw_new(proto);
894-
break;
895-
}
937+
case MOD_NETWORK_SOCK_RAW:
896938
#endif
939+
if (socket->type == MOD_NETWORK_SOCK_DGRAM) {
940+
socket->pcb.udp = udp_new();
941+
}
942+
#if MICROPY_PY_LWIP_SOCK_RAW
943+
else {
944+
mp_int_t proto = n_args <= 2 ? 0 : mp_obj_get_int(args[2]);
945+
socket->pcb.raw = raw_new(proto);
946+
}
947+
#endif
948+
socket->incoming.udp_raw.iget = 0;
949+
socket->incoming.udp_raw.iput = 0;
950+
socket->incoming.udp_raw.array = m_new0(lwip_incoming_packet_t, LWIP_INCOMING_PACKET_QUEUE_LEN);
951+
break;
897952
default:
898953
mp_raise_OSError(MP_EINVAL);
899954
}
@@ -1075,7 +1130,7 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
10751130
// ...and set up the new socket for it.
10761131
socket2->domain = MOD_NETWORK_AF_INET;
10771132
socket2->type = MOD_NETWORK_SOCK_STREAM;
1078-
socket2->incoming.pbuf = NULL;
1133+
socket2->incoming.tcp.pbuf = NULL;
10791134
socket2->timeout = socket->timeout;
10801135
socket2->state = STATE_CONNECTED;
10811136
socket2->recv_offset = 0;
@@ -1130,8 +1185,8 @@ static mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
11301185
socket->state = STATE_NEW;
11311186
mp_raise_OSError(error_lookup_table[-err]);
11321187
}
1133-
socket->peer_port = (mp_uint_t)port;
1134-
memcpy(&socket->peer, &dest, sizeof(socket->peer));
1188+
socket->tcp_peer_addr = dest;
1189+
socket->tcp_peer_port = (mp_uint_t)port;
11351190
MICROPY_PY_LWIP_EXIT
11361191

11371192
// And now we wait...
@@ -1299,8 +1354,8 @@ static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
12991354
mp_uint_t ret = 0;
13001355
switch (socket->type) {
13011356
case MOD_NETWORK_SOCK_STREAM: {
1302-
memcpy(&ip, &socket->peer, sizeof(socket->peer));
1303-
port = (mp_uint_t)socket->peer_port;
1357+
ip = socket->tcp_peer_addr;
1358+
port = (mp_uint_t)socket->tcp_peer_port;
13041359
ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno);
13051360
break;
13061361
}
@@ -1537,9 +1592,15 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_
15371592
if (lwip_socket_incoming_array(socket)[socket->incoming.connection.iget] != NULL) {
15381593
ret |= MP_STREAM_POLL_RD;
15391594
}
1595+
} else if (socket->type == MOD_NETWORK_SOCK_STREAM) {
1596+
// For TCP sockets there is just one slot for incoming data
1597+
if (socket->incoming.tcp.pbuf != NULL) {
1598+
ret |= MP_STREAM_POLL_RD;
1599+
}
15401600
} else {
1541-
// Otherwise there is just one slot for incoming data
1542-
if (socket->incoming.pbuf != NULL) {
1601+
// Otherwise for UDP/raw there is a queue of incoming data
1602+
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
1603+
if (slot->pbuf != NULL) {
15431604
ret |= MP_STREAM_POLL_RD;
15441605
}
15451606
}

0 commit comments

Comments
 (0)