* [PATCH 0/1] tidbits: net-udp: solicit new client for server mode @ 2026-05-28 19:44 Hannes Diethelm 2026-05-28 19:44 ` [PATCH 1/1] " Hannes Diethelm 2026-05-29 5:29 ` [PATCH 0/1] " Philippe Gerum 0 siblings, 2 replies; 8+ messages in thread From: Hannes Diethelm @ 2026-05-28 19:44 UTC (permalink / raw) To: xenomai, rpm; +Cc: Hannes Diethelm As discussed in [PATCH] tidbits: net-udp: add server and client mode, evl_net_solicit() is also needed for each client in server mode. I went for option 2, it is not to lazy and I did not find any way to ask evl if an ARP is already done. While testing, I also discovered that EINPROGRESS can appear after some time. I think this is due the kernel garbage-collects the ARP entry. With this patch, this issue is also gone. Hannes Diethelm (1): tidbits: net-udp: solicit new client for server mode tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) -- 2.47.3 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/1] tidbits: net-udp: solicit new client for server mode 2026-05-28 19:44 [PATCH 0/1] tidbits: net-udp: solicit new client for server mode Hannes Diethelm @ 2026-05-28 19:44 ` Hannes Diethelm 2026-06-01 8:29 ` Philippe Gerum 2026-05-29 5:29 ` [PATCH 0/1] " Philippe Gerum 1 sibling, 1 reply; 8+ messages in thread From: Hannes Diethelm @ 2026-05-28 19:44 UTC (permalink / raw) To: xenomai, rpm; +Cc: Hannes Diethelm This fixes random EINPROGRESS at init or during runtime. Also correct whitespaces and make stdout more consistent. Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> --- tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c index 11b8459..7af834f 100644 --- a/tidbits/oob-net-udp.c +++ b/tidbits/oob-net-udp.c @@ -50,12 +50,12 @@ static void usage(void) } static void print_addr(char* text, struct sockaddr_in *addr){ - char ip_str[INET_ADDRSTRLEN+1]; - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); - evl_printf("%s--------\n", text); - evl_printf("IP-Address: %s\n", ip_str); - evl_printf("Port: %d\n", ntohs(addr->sin_port)); - evl_printf("Family: %d\n", addr->sin_family); + char ip_str[INET_ADDRSTRLEN+1]; + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); + evl_printf("== %s\n", text); + evl_printf(" ip-address: %s\n", ip_str); + evl_printf(" port: %d\n", ntohs(addr->sin_port)); + evl_printf(" family: %d\n", addr->sin_family); } static void sender(int s, const char *text, int mcount, @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, free(tbuf); } +#define SERVER_ADDR_LIST_SIZE 64 + static void server(int s, const char *text, int mcount, struct sockaddr_in *addr, int iter) { @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, ssize_t ret; char *tbuf; char rbuf[16384]; + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE]={}; + size_t addr_list_fill=0; + bool solicit_done; tlen = (strlen(text) + 1) * mcount; tbuf = malloc(tlen); @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, evl_printf(" (TRUNCATED)"); evl_printf(": %.*s\n", (int)ret, rbuf); + /* + * We need to call evl_net_solicit for each new + * client once before sending data. This will break + * realtime for the first response. + * If this is not done and the ARP address is not + * yet in cache or garbage-collected, oob_sendmsg + * will return EINPROGRESS on start or during runtime. + */ + solicit_done = false; + for(size_t i = 0; i < addr_list_fill && !solicit_done; i++){ + if(addr_list[i] == _addr.sin_addr.s_addr){ + solicit_done = true; + } + } + if(!solicit_done){ + if(verbosity){ + char ip_str[INET_ADDRSTRLEN+1]; + inet_ntop(AF_INET, &(_addr.sin_addr), ip_str, sizeof(ip_str)); + evl_printf("== client %s first seen: evl_net_solicit\n", ip_str); + } + ret = evl_net_solicit(s, (const struct sockaddr *)&_addr, + EVL_NEIGH_PERMANENT); + if (ret) + error(1, -ret, "evl_net_solicit()"); + + if(addr_list_fill < SERVER_ADDR_LIST_SIZE){ + addr_list[addr_list_fill] = _addr.sin_addr.s_addr; + addr_list_fill ++; + }else{ + error(1, EPERM, "address list full"); + } + } + iov.iov_base = tbuf; iov.iov_len = tlen; msghdr.msg_iov = &iov; @@ -433,7 +471,7 @@ int main(int argc, char *argv[]) receiver(s, &addr, iter); } else if (mode == CLIENT) { if (verbosity) - printf("== client mode (<= %s:%d)\n", ip, port); + printf("== client mode (<=> %s:%d)\n", ip, port); /* * Guarantee a mere oob path from the first packet @@ -449,8 +487,12 @@ int main(int argc, char *argv[]) client(s, text, mcount, &addr, iter, delay); } else if (mode == SERVER) { if (verbosity) - printf("== server mode (<= %s:%d)\n", ip, port); + printf("== server mode (<=> %s:%d)\n", ip, port); + /* + * The server calls evl_net_solicit() internally + * for each new ip address. + */ server(s, text, mcount, &addr, iter); } else { error(1, 0, "Mode not implemented"); -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 1/1] tidbits: net-udp: solicit new client for server mode 2026-05-28 19:44 ` [PATCH 1/1] " Hannes Diethelm @ 2026-06-01 8:29 ` Philippe Gerum 2026-06-01 9:10 ` Philippe Gerum 2026-06-01 19:33 ` [PATCH v2] " Hannes Diethelm 0 siblings, 2 replies; 8+ messages in thread From: Philippe Gerum @ 2026-06-01 8:29 UTC (permalink / raw) To: Hannes Diethelm; +Cc: xenomai Hannes Diethelm <hannes.diethelm@gmail.com> writes: > This fixes random EINPROGRESS at init or during runtime. > > Also correct whitespaces and make stdout more consistent. > > Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> > --- > tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ > 1 file changed, 50 insertions(+), 8 deletions(-) > > diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c > index 11b8459..7af834f 100644 > --- a/tidbits/oob-net-udp.c > +++ b/tidbits/oob-net-udp.c > @@ -50,12 +50,12 @@ static void usage(void) > } > > static void print_addr(char* text, struct sockaddr_in *addr){ > - char ip_str[INET_ADDRSTRLEN+1]; > - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); > - evl_printf("%s--------\n", text); > - evl_printf("IP-Address: %s\n", ip_str); > - evl_printf("Port: %d\n", ntohs(addr->sin_port)); > - evl_printf("Family: %d\n", addr->sin_family); > + char ip_str[INET_ADDRSTRLEN+1]; > + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); > + evl_printf("== %s\n", text); > + evl_printf(" ip-address: %s\n", ip_str); > + evl_printf(" port: %d\n", ntohs(addr->sin_port)); > + evl_printf(" family: %d\n", addr->sin_family); > } > > static void sender(int s, const char *text, int mcount, > @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, > free(tbuf); > } > > +#define SERVER_ADDR_LIST_SIZE 64 > + > static void server(int s, const char *text, int mcount, > struct sockaddr_in *addr, int iter) > { > @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, > ssize_t ret; > char *tbuf; > char rbuf[16384]; > + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE]={}; ^ missing whitespaces > + size_t addr_list_fill=0; > + bool solicit_done; > > tlen = (strlen(text) + 1) * mcount; > tbuf = malloc(tlen); > @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, > evl_printf(" (TRUNCATED)"); > evl_printf(": %.*s\n", (int)ret, rbuf); > > + /* > + * We need to call evl_net_solicit for each new > + * client once before sending data. This will break > + * realtime for the first response. > + * If this is not done and the ARP address is not > + * yet in cache or garbage-collected, oob_sendmsg > + * will return EINPROGRESS on start or during runtime. > + */ We can happily send redundant solicit requests to the core, no need to filter out cached addresses, evl_net_solicit() will do the right thing. > + solicit_done = false; > + for(size_t i = 0; i < addr_list_fill && !solicit_done; i++){ ^ missing whitespace > + if(addr_list[i] == _addr.sin_addr.s_addr){ ^ missing whitespace > + solicit_done = true; > + } > + } > + if(!solicit_done){ > + if(verbosity){ > + char ip_str[INET_ADDRSTRLEN+1]; > + inet_ntop(AF_INET, &(_addr.sin_addr), ip_str, sizeof(ip_str)); > + evl_printf("== client %s first seen: evl_net_solicit\n", ip_str); > + } > + ret = evl_net_solicit(s, (const struct sockaddr *)&_addr, > + EVL_NEIGH_PERMANENT); > + if (ret) > + error(1, -ret, "evl_net_solicit()"); > + > + if(addr_list_fill < SERVER_ADDR_LIST_SIZE){ > + addr_list[addr_list_fill] = _addr.sin_addr.s_addr; > + addr_list_fill ++; ^ extraneous whitespace > + }else{ > + error(1, EPERM, "address list full"); > + } > + } > + > iov.iov_base = tbuf; > iov.iov_len = tlen; > msghdr.msg_iov = &iov; > @@ -433,7 +471,7 @@ int main(int argc, char *argv[]) > receiver(s, &addr, iter); > } else if (mode == CLIENT) { > if (verbosity) > - printf("== client mode (<= %s:%d)\n", ip, port); > + printf("== client mode (<=> %s:%d)\n", ip, port); > > /* > * Guarantee a mere oob path from the first packet > @@ -449,8 +487,12 @@ int main(int argc, char *argv[]) > client(s, text, mcount, &addr, iter, delay); > } else if (mode == SERVER) { > if (verbosity) > - printf("== server mode (<= %s:%d)\n", ip, port); > + printf("== server mode (<=> %s:%d)\n", ip, port); > > + /* > + * The server calls evl_net_solicit() internally > + * for each new ip address. > + */ > server(s, text, mcount, &addr, iter); > } else { > error(1, 0, "Mode not implemented"); -- Philippe. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/1] tidbits: net-udp: solicit new client for server mode 2026-06-01 8:29 ` Philippe Gerum @ 2026-06-01 9:10 ` Philippe Gerum 2026-06-01 20:02 ` Hannes Diethelm 2026-06-01 19:33 ` [PATCH v2] " Hannes Diethelm 1 sibling, 1 reply; 8+ messages in thread From: Philippe Gerum @ 2026-06-01 9:10 UTC (permalink / raw) To: Hannes Diethelm; +Cc: xenomai Philippe Gerum <rpm@xenomai.org> writes: > Hannes Diethelm <hannes.diethelm@gmail.com> writes: > >> This fixes random EINPROGRESS at init or during runtime. >> >> Also correct whitespaces and make stdout more consistent. >> >> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> >> --- >> tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ >> 1 file changed, 50 insertions(+), 8 deletions(-) >> >> diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c >> index 11b8459..7af834f 100644 >> --- a/tidbits/oob-net-udp.c >> +++ b/tidbits/oob-net-udp.c >> @@ -50,12 +50,12 @@ static void usage(void) >> } >> >> static void print_addr(char* text, struct sockaddr_in *addr){ >> - char ip_str[INET_ADDRSTRLEN+1]; >> - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >> - evl_printf("%s--------\n", text); >> - evl_printf("IP-Address: %s\n", ip_str); >> - evl_printf("Port: %d\n", ntohs(addr->sin_port)); >> - evl_printf("Family: %d\n", addr->sin_family); >> + char ip_str[INET_ADDRSTRLEN+1]; >> + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >> + evl_printf("== %s\n", text); >> + evl_printf(" ip-address: %s\n", ip_str); >> + evl_printf(" port: %d\n", ntohs(addr->sin_port)); >> + evl_printf(" family: %d\n", addr->sin_family); >> } >> >> static void sender(int s, const char *text, int mcount, >> @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, >> free(tbuf); >> } >> >> +#define SERVER_ADDR_LIST_SIZE 64 >> + >> static void server(int s, const char *text, int mcount, >> struct sockaddr_in *addr, int iter) >> { >> @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, >> ssize_t ret; >> char *tbuf; >> char rbuf[16384]; >> + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE]={}; > ^ missing whitespaces >> + size_t addr_list_fill=0; >> + bool solicit_done; >> >> tlen = (strlen(text) + 1) * mcount; >> tbuf = malloc(tlen); >> @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, >> evl_printf(" (TRUNCATED)"); >> evl_printf(": %.*s\n", (int)ret, rbuf); >> >> + /* >> + * We need to call evl_net_solicit for each new >> + * client once before sending data. This will break >> + * realtime for the first response. >> + * If this is not done and the ARP address is not >> + * yet in cache or garbage-collected, oob_sendmsg >> + * will return EINPROGRESS on start or during runtime. >> + */ > > We can happily send redundant solicit requests to the core, no need to > filter out cached addresses, evl_net_solicit() will do the right thing. > Except that doing so would always demote the caller to the in-band stage, which may not be what you want. The fact that we'd need to maintain a cache in apps in order to figure out whether solicitation should be done shows a shortcoming in the core, users should not have to do this dance. We should have an oob call for probing the route+arp caches with ipv4. I'll look into this asap. -- Philippe. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/1] tidbits: net-udp: solicit new client for server mode 2026-06-01 9:10 ` Philippe Gerum @ 2026-06-01 20:02 ` Hannes Diethelm 2026-06-20 17:28 ` Philippe Gerum 0 siblings, 1 reply; 8+ messages in thread From: Hannes Diethelm @ 2026-06-01 20:02 UTC (permalink / raw) To: Philippe Gerum; +Cc: xenomai Am 01.06.26 um 11:10 schrieb Philippe Gerum: > Philippe Gerum <rpm@xenomai.org> writes: > >> Hannes Diethelm <hannes.diethelm@gmail.com> writes: >> >>> This fixes random EINPROGRESS at init or during runtime. >>> >>> Also correct whitespaces and make stdout more consistent. >>> >>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> >>> --- >>> tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ >>> 1 file changed, 50 insertions(+), 8 deletions(-) >>> >>> diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c >>> index 11b8459..7af834f 100644 >>> --- a/tidbits/oob-net-udp.c >>> +++ b/tidbits/oob-net-udp.c >>> @@ -50,12 +50,12 @@ static void usage(void) >>> } >>> >>> static void print_addr(char* text, struct sockaddr_in *addr){ >>> - char ip_str[INET_ADDRSTRLEN+1]; >>> - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >>> - evl_printf("%s--------\n", text); >>> - evl_printf("IP-Address: %s\n", ip_str); >>> - evl_printf("Port: %d\n", ntohs(addr->sin_port)); >>> - evl_printf("Family: %d\n", addr->sin_family); >>> + char ip_str[INET_ADDRSTRLEN+1]; >>> + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >>> + evl_printf("== %s\n", text); >>> + evl_printf(" ip-address: %s\n", ip_str); >>> + evl_printf(" port: %d\n", ntohs(addr->sin_port)); >>> + evl_printf(" family: %d\n", addr->sin_family); >>> } >>> >>> static void sender(int s, const char *text, int mcount, >>> @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, >>> free(tbuf); >>> } >>> >>> +#define SERVER_ADDR_LIST_SIZE 64 >>> + >>> static void server(int s, const char *text, int mcount, >>> struct sockaddr_in *addr, int iter) >>> { >>> @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, >>> ssize_t ret; >>> char *tbuf; >>> char rbuf[16384]; >>> + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE]={}; >> ^ missing whitespaces >>> + size_t addr_list_fill=0; >>> + bool solicit_done; >>> >>> tlen = (strlen(text) + 1) * mcount; >>> tbuf = malloc(tlen); >>> @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, >>> evl_printf(" (TRUNCATED)"); >>> evl_printf(": %.*s\n", (int)ret, rbuf); >>> >>> + /* >>> + * We need to call evl_net_solicit for each new >>> + * client once before sending data. This will break >>> + * realtime for the first response. >>> + * If this is not done and the ARP address is not >>> + * yet in cache or garbage-collected, oob_sendmsg >>> + * will return EINPROGRESS on start or during runtime. >>> + */ >> >> We can happily send redundant solicit requests to the core, no need to >> filter out cached addresses, evl_net_solicit() will do the right thing. >> > > Except that doing so would always demote the caller to the in-band > stage, which may not be what you want. The fact that we'd need to > maintain a cache in apps in order to figure out whether solicitation > should be done shows a shortcoming in the core, users should not have to > do this dance. > > We should have an oob call for probing the route+arp caches with > ipv4. I'll look into this asap. > Yes, this is the reason I do it this way and print out a message if this happens. I think server mode with multiple clients in real time is not something you can just easily do without careful considerations what happens when multiple clients send a message at the same time or TDMA behind. My intent in this example is mostly to show that it is possible. A real application would need one of these options: - A thread only for solicitation so the main thread doesn't get blocked - evl_net_solicit() with a flag like MSG_DONTWAIT and then poll in the main loop if the client is ready - Know the clients in advance - A warm up phase during clients can connect - ... However, a way for probing and reading the arp would shurely help. It would also be nice to have an "evl net" command to list the entry's. I tried the normal arp command but it doesn't behave nicely with evl. No hurry from my side, I have linuxcnc already running well with evl oob networking, I only need client mode and i know the IP in advance, so all good there. Sorry about the whitespace issues, switching between projects... I sent a patch v2. Feel free to merge this already and I can create a separate patch when probing ARP is here or wait and I create a patch v3. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/1] tidbits: net-udp: solicit new client for server mode 2026-06-01 20:02 ` Hannes Diethelm @ 2026-06-20 17:28 ` Philippe Gerum 0 siblings, 0 replies; 8+ messages in thread From: Philippe Gerum @ 2026-06-20 17:28 UTC (permalink / raw) To: Hannes Diethelm; +Cc: xenomai Hannes Diethelm <hannes.diethelm@gmail.com> writes: > Am 01.06.26 um 11:10 schrieb Philippe Gerum: >> Philippe Gerum <rpm@xenomai.org> writes: >> >>> Hannes Diethelm <hannes.diethelm@gmail.com> writes: >>> >>>> This fixes random EINPROGRESS at init or during runtime. >>>> >>>> Also correct whitespaces and make stdout more consistent. >>>> >>>> Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> >>>> --- >>>> tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ >>>> 1 file changed, 50 insertions(+), 8 deletions(-) >>>> >>>> diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c >>>> index 11b8459..7af834f 100644 >>>> --- a/tidbits/oob-net-udp.c >>>> +++ b/tidbits/oob-net-udp.c >>>> @@ -50,12 +50,12 @@ static void usage(void) >>>> } >>>> static void print_addr(char* text, struct sockaddr_in *addr){ >>>> - char ip_str[INET_ADDRSTRLEN+1]; >>>> - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >>>> - evl_printf("%s--------\n", text); >>>> - evl_printf("IP-Address: %s\n", ip_str); >>>> - evl_printf("Port: %d\n", ntohs(addr->sin_port)); >>>> - evl_printf("Family: %d\n", addr->sin_family); >>>> + char ip_str[INET_ADDRSTRLEN+1]; >>>> + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); >>>> + evl_printf("== %s\n", text); >>>> + evl_printf(" ip-address: %s\n", ip_str); >>>> + evl_printf(" port: %d\n", ntohs(addr->sin_port)); >>>> + evl_printf(" family: %d\n", addr->sin_family); >>>> } >>>> static void sender(int s, const char *text, int mcount, >>>> @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, >>>> free(tbuf); >>>> } >>>> +#define SERVER_ADDR_LIST_SIZE 64 >>>> + >>>> static void server(int s, const char *text, int mcount, >>>> struct sockaddr_in *addr, int iter) >>>> { >>>> @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, >>>> ssize_t ret; >>>> char *tbuf; >>>> char rbuf[16384]; >>>> + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE]={}; >>> ^ missing whitespaces >>>> + size_t addr_list_fill=0; >>>> + bool solicit_done; >>>> tlen = (strlen(text) + 1) * mcount; >>>> tbuf = malloc(tlen); >>>> @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, >>>> evl_printf(" (TRUNCATED)"); >>>> evl_printf(": %.*s\n", (int)ret, rbuf); >>>> + /* >>>> + * We need to call evl_net_solicit for each new >>>> + * client once before sending data. This will break >>>> + * realtime for the first response. >>>> + * If this is not done and the ARP address is not >>>> + * yet in cache or garbage-collected, oob_sendmsg >>>> + * will return EINPROGRESS on start or during runtime. >>>> + */ >>> >>> We can happily send redundant solicit requests to the core, no need to >>> filter out cached addresses, evl_net_solicit() will do the right thing. >>> >> Except that doing so would always demote the caller to the in-band >> stage, which may not be what you want. The fact that we'd need to >> maintain a cache in apps in order to figure out whether solicitation >> should be done shows a shortcoming in the core, users should not have to >> do this dance. >> We should have an oob call for probing the route+arp caches with >> ipv4. I'll look into this asap. >> > > Yes, this is the reason I do it this way and print out a message if > this happens. > > I think server mode with multiple clients in real time is not something you > can just easily do without careful considerations what happens when multiple > clients send a message at the same time or TDMA behind. My intent in this example > is mostly to show that it is possible. > > A real application would need one of these options: > - A thread only for solicitation so the main thread doesn't get blocked > - evl_net_solicit() with a flag like MSG_DONTWAIT and then poll in the main loop if > the client is ready > - Know the clients in advance > - A warm up phase during clients can connect > - ... > > However, a way for probing and reading the arp would shurely help. Here is a general proposal to deal with the issue of sending UDP packets to unplanned destinations, which is basically what the server mode has to do, when receiving unsolicited messages. We have a single invariant to cope with: the evl network stack wants to use the routing+arp information produced by the regular in-band stack, so that we can benefit from potentially complex routing logic without reinventing that wheel. Therefore, the route to any new/unsolicited destination must be resolved by the in-band stack once so that we can send UDP packets to that peer from the oob stage next. From the point above, and as you pointed out already, an oob sender must either plan for reaching a particular destination (e.g. soliciting it prior to entering time-critical code), or accept an initial delay for the route to be resolved if the destination is not yet known on first transmit (i.e. out of the oob caches). So, I would say that evl could meet the requirements you stated above by implementing the following: - Honor MSG_PROBE for oob_sendmsg(), so that only the general call sanity and route resolution to the destination host is performed when set in the request flags, without actually sending any data. On success of such call, we would know that the routing information is readily available from the oob caches, no offload to in-band would have happened if we had not given this flag. The absence of routing information to the destination from some oob cache would yield a specific error, so that the caller may decide what to do next. - Extend the effect of receiving MSG_DONTWAIT (and more generally O_NONBLOCK on fildes) to what we would do upon missing routing information: if present, return with a specific error code _without_ relaying the packet to the in-band stack. The caller may then decide to handle the case locally. Otherwise, proceed as usual (i.e. relay to the in-band stack, then notify the caller with -EINPROGRESS). - Provide a way to synchronize with the route resolution process in evl, i.e. a syscall that would block until a given destination is available from the oob cache. This call already exists, evl_net_solicit() can be used for that purpose (if a resolution request is already in flight, subsequent ones to the same destination won't cause any harm). Points 1 and 2 are implemented in [1]. Feedback greatly appreciated and important when you have time. Usability for real-world applications is key. > It would also be nice to have an "evl net" command to list the > entry's. I tried the normal arp command but it doesn't behave nicely > with evl. Could you please elaborate on this issue? Currently, updates to the in-band cache are propagated to the oob cache (by registering a hook into the relevant notifier chain in the kernel). So I would expect all destinations of interest to oob which are visible from the in-band arp cache to be available from the evl map as well. This said, I agree that we need a way to inspect the oob cache specifically. Working on it. [1] https://gitlab.com/Xenomai/xenomai4/linux-evl/-/tree/wip/net-solicit?ref_type=heads -- Philippe. ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2] tidbits: net-udp: solicit new client for server mode 2026-06-01 8:29 ` Philippe Gerum 2026-06-01 9:10 ` Philippe Gerum @ 2026-06-01 19:33 ` Hannes Diethelm 1 sibling, 0 replies; 8+ messages in thread From: Hannes Diethelm @ 2026-06-01 19:33 UTC (permalink / raw) To: rpm; +Cc: hannes.diethelm, xenomai This fixes random EINPROGRESS at init or during runtime. Also correct whitespaces and make stdout more consistent. Signed-off-by: Hannes Diethelm <hannes.diethelm@gmail.com> --- tidbits/oob-net-udp.c | 58 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/tidbits/oob-net-udp.c b/tidbits/oob-net-udp.c index 11b8459..658b463 100644 --- a/tidbits/oob-net-udp.c +++ b/tidbits/oob-net-udp.c @@ -50,12 +50,12 @@ static void usage(void) } static void print_addr(char* text, struct sockaddr_in *addr){ - char ip_str[INET_ADDRSTRLEN+1]; - inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); - evl_printf("%s--------\n", text); - evl_printf("IP-Address: %s\n", ip_str); - evl_printf("Port: %d\n", ntohs(addr->sin_port)); - evl_printf("Family: %d\n", addr->sin_family); + char ip_str[INET_ADDRSTRLEN+1]; + inet_ntop(AF_INET, &(addr->sin_addr), ip_str, sizeof(ip_str)); + evl_printf("== %s\n", text); + evl_printf(" ip-address: %s\n", ip_str); + evl_printf(" port: %d\n", ntohs(addr->sin_port)); + evl_printf(" family: %d\n", addr->sin_family); } static void sender(int s, const char *text, int mcount, @@ -226,6 +226,8 @@ static void client(int s, const char *text, int mcount, free(tbuf); } +#define SERVER_ADDR_LIST_SIZE 64 + static void server(int s, const char *text, int mcount, struct sockaddr_in *addr, int iter) { @@ -236,6 +238,9 @@ static void server(int s, const char *text, int mcount, ssize_t ret; char *tbuf; char rbuf[16384]; + in_addr_t addr_list[SERVER_ADDR_LIST_SIZE] = {}; + size_t addr_list_fill = 0; + bool solicit_done; tlen = (strlen(text) + 1) * mcount; tbuf = malloc(tlen); @@ -276,6 +281,39 @@ static void server(int s, const char *text, int mcount, evl_printf(" (TRUNCATED)"); evl_printf(": %.*s\n", (int)ret, rbuf); + /* + * We need to call evl_net_solicit for each new client + * once before sending data. This demotes the caller to the + * in-band stage for the first response. + * If this is not done and the ARP address is not + * yet in cache or garbage-collected, oob_sendmsg + * will return EINPROGRESS on start or during runtime. + */ + solicit_done = false; + for (size_t i = 0; i < addr_list_fill && !solicit_done; i++) { + if (addr_list[i] == _addr.sin_addr.s_addr) { + solicit_done = true; + } + } + if (!solicit_done) { + if (verbosity) { + char ip_str[INET_ADDRSTRLEN+1]; + inet_ntop(AF_INET, &(_addr.sin_addr), ip_str, sizeof(ip_str)); + evl_printf("== client %s first seen: evl_net_solicit\n", ip_str); + } + ret = evl_net_solicit(s, (const struct sockaddr *)&_addr, + EVL_NEIGH_PERMANENT); + if (ret) + error(1, -ret, "evl_net_solicit()"); + + if (addr_list_fill < SERVER_ADDR_LIST_SIZE) { + addr_list[addr_list_fill] = _addr.sin_addr.s_addr; + addr_list_fill++; + } else { + error(1, EPERM, "address list full"); + } + } + iov.iov_base = tbuf; iov.iov_len = tlen; msghdr.msg_iov = &iov; @@ -433,7 +471,7 @@ int main(int argc, char *argv[]) receiver(s, &addr, iter); } else if (mode == CLIENT) { if (verbosity) - printf("== client mode (<= %s:%d)\n", ip, port); + printf("== client mode (<=> %s:%d)\n", ip, port); /* * Guarantee a mere oob path from the first packet @@ -449,8 +487,12 @@ int main(int argc, char *argv[]) client(s, text, mcount, &addr, iter, delay); } else if (mode == SERVER) { if (verbosity) - printf("== server mode (<= %s:%d)\n", ip, port); + printf("== server mode (<=> %s:%d)\n", ip, port); + /* + * The server calls evl_net_solicit() internally + * for each new ip address. + */ server(s, text, mcount, &addr, iter); } else { error(1, 0, "Mode not implemented"); -- 2.47.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 0/1] tidbits: net-udp: solicit new client for server mode 2026-05-28 19:44 [PATCH 0/1] tidbits: net-udp: solicit new client for server mode Hannes Diethelm 2026-05-28 19:44 ` [PATCH 1/1] " Hannes Diethelm @ 2026-05-29 5:29 ` Philippe Gerum 1 sibling, 0 replies; 8+ messages in thread From: Philippe Gerum @ 2026-05-29 5:29 UTC (permalink / raw) To: Hannes Diethelm; +Cc: xenomai Hannes Diethelm <hannes.diethelm@gmail.com> writes: > As discussed in [PATCH] tidbits: net-udp: add server and client mode, > evl_net_solicit() is also needed for each client in server mode. > > I went for option 2, it is not to lazy and I did not find any way to > ask evl if an ARP is already done. > > While testing, I also discovered that EINPROGRESS can appear after some > time. I think this is due the kernel garbage-collects the ARP entry. Correct, there is a timeout on ARP entries. You can make an entry permanent and therefore not considered for garbage collection by passing the EVL_NEIGH_PERMANENT flag to evl_net_solicit() From the command line, the standard 'arp' command can be used to achieve what you would do with evl_net_solicit() as well, since in-band updates to the neighbour table are forwarded to evl. -- Philippe. ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-20 17:28 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-28 19:44 [PATCH 0/1] tidbits: net-udp: solicit new client for server mode Hannes Diethelm 2026-05-28 19:44 ` [PATCH 1/1] " Hannes Diethelm 2026-06-01 8:29 ` Philippe Gerum 2026-06-01 9:10 ` Philippe Gerum 2026-06-01 20:02 ` Hannes Diethelm 2026-06-20 17:28 ` Philippe Gerum 2026-06-01 19:33 ` [PATCH v2] " Hannes Diethelm 2026-05-29 5:29 ` [PATCH 0/1] " Philippe Gerum
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.