-
Notifications
You must be signed in to change notification settings - Fork 7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net: BSD Sockets like API initial implementation #498
Conversation
@jukkar , @tbursztyka , @lpereira , @Likailiu : Please review. There're many commits, something definitely needs to be squashed, I'm open to suggestions how much. Other thing requiring decision is "WIP: kernel: fifo: Add peek_head/peek_tail accessors." - whether to turn these functions into true kernel fifo apis, or make private socket functions. |
@askawu: Please review too. |
Shippable fails (so far) due:
That's "WIP: kernel: fifo: Add peek_head/peek_tail accessors." commit, about which I ask suggestions above. |
Looking that we have tests/net/lib/, maybe should create tests/net/sockets/ to group socket tests? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor things needs tweaking, but looks very good as a whole.
CONFIG_NET_SLIP_TAP=y | ||
CONFIG_TEST_RANDOM_GENERATOR=y | ||
|
||
# Without CONFIG_NET_BUF_LOG printf() doesn't work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not really understand how net_buf logging could affect printf(), could you elaborate the issue more.
BTW, typically I have this setting in my prj.conf files
CONFIG_NET_LOG=y
CONFIG_SYS_LOG_NET_LEVEL=2
and then setting relevant net debug options to "y" and increasing CONFIG_SYS_LOG_NET_LEVEL to 4, will then start to print debug output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not really understand how net_buf logging could affect printf(), could you elaborate the issue more.
I don't understand either, and yet it's like that - w/o CONFIG_NET_BUF_LOG=y, printf() output is not shown (network echo works). I did 10-15 mins of "genetic algorithm" debugging, semi-randomly trying other logging and output options and only CONFIG_NET_BUF_LOG looked like a culprit. Didn't debug further, as you may imagine, that can be quite a timesink ;-)
CONFIG_TEST_RANDOM_GENERATOR=y | ||
|
||
# Without CONFIG_NET_BUF_LOG printf() doesn't work | ||
CONFIG_NET_BUF_LOG=y |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally one does not need to enable this unless one is debugging the actual net_buf implementation.
|
||
serv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
|
||
bind_addr.sin_family = AF_INET; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if this app would support also IPv6. This can be enabled later, if no time to do that now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So (also partially answering @lpereira's comment below), the primary purpose of this sample was to show that the same code (modulo includes of course) can be used both on POSIX and Zephyr. Turns out, I missed to include "Makefile.posix" to make that obvious, I'll add it. Then, Linux for example supports "dual stack" sockets which can be accessed via IPv4 or IPv6, but I don't think we have that feature in Zephyr. Otherwise, to support IPv4 and IPv6 at the same time, there would be needed 2 sockets, and poll()/select(), which are scheduled so "stage 3" of Sockets implementation (this patch being stage 1).
Perhaps you mean either IPv4 or IPv6? That can be done, though again, the idea was to show how sockets can be portable between POSIX and Zephyr, without extra #ifdef noise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant having dual IPv4 and IPv6 at the same time. But it requires two sockets at the moment and support for poll/select as you said, so we can postpone this for later stage.
subsys/net/lib/sockets/sockets.c
Outdated
user_data); | ||
|
||
/* if pkt == NULL, EOF */ | ||
if (pkt == NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be better to have "if (!pkt) {" here, so that the code has unified look with rest of the IP stack.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
subsys/net/lib/sockets/sockets.c
Outdated
if (pkt == NULL) { | ||
struct net_pkt *last_pkt = _k_fifo_peek_tail(&ctx->recv_q); | ||
|
||
if (last_pkt == NULL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed (other cases too).
subsys/net/lib/sockets/sockets.c
Outdated
*/ | ||
sock_set_eof(ctx); | ||
k_fifo_cancel_wait(&ctx->recv_q); | ||
NET_DBG("Marked socket %p as peer-closed\n", ctx); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to put \n at the end of the strings in NET_DBG(), as those strings will have \n automatically added. Please check also other calls to NET_DBG() in your patches for duplicate \n
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, was printk() initially...
subsys/net/lib/sockets/sockets.c
Outdated
|
||
ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags) | ||
{ | ||
(void)flags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use ARG_UNUSED(flags); here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
tests/net/socket_udp/prj.conf
Outdated
CONFIG_NET_SLIP_TAP=y | ||
CONFIG_TEST_RANDOM_GENERATOR=y | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extra empty line
subsys/net/lib/sockets/sockets.c
Outdated
if (sock_type == SOCK_DGRAM) { | ||
__ASSERT(0, "DGRAM is not yet handled"); | ||
} else if (sock_type == SOCK_STREAM) { | ||
do { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move this do { ... } while()
to a function and return zsock_recv_stream(ctx, buf, max_len, flags);
, reducing one indentation level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can. So, should I? (I don't expect source-level issues, like need to re-do some work in this function, but I'm not sure if a compiler will readily inline such a function, which in turn may lead to higher stack usage, etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GCC does tail-call optimization, so there's no need to worry about increased stack usage. Essentially, if a function does return function(...)
, instead of generating a CALL instruction (or their equivalent), it'll generate a JMP instruction (or their equivalent) after setting up the parameters. It's very likely that there won't be any special code for the parameters, as it might be able to reuse the ones in the current call frame.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GCC does tail-call optimization
GCC does tail-call optimization depending on the arch. For example, last time I looked re: that at gcc-xtensa, it didn't do it. Anyway, I assume it makes sense to try that refactor.
From a cursory glance, the patchset looks fine. I'll carve some time to review it more carefully. For some future work, it would be nice to make something similar to As far as samples-for-the-future goes, having a "forking" server would be interesting as well as having something like a thread pool with a shared FIFO to service clients. |
poll/select is planned, for "stage 3" of the implementation, GH-3664, stage 2 non-blocking support (GH-3663), because realistically, socket poll functionality becomes useful only with non-blocking mode. Indeed, select() will be tricky, so in my plan, I offload it for future "generic POSIX API" work, which also would include read()/write() support for socket objects, etc. poll() should be both good enough and more efficient, though even it would add enough bloat to this implementation.
I assume mentioning of "forking" server is a joke - realistically, we'll never have fork() or vfork() in Zephyr ;-). As I mentioned above, the idea of the current socket_echo sample is to show that it's possible to write apps portable between POSIX and Zephyr, so I didn't want to use Zephyr-specific threading. That again can be addressed by more generic POSIX API advent which would include Pthreads support. Anyway, more examples (or tests) of different kinds can be added later. Thanks for reviewing! |
Some updates:
|
So, previously this was tested on qemu_x86, now confirmed that on qemu_cortex_m3 it leads to the same behavior. |
Ok, some stats: code size of sockets.o: All in all, single use of static inline function is properly inlined in all cases (which can be "d'oh", but knowing gcc, seeing's believing), and in few cases even leads to surprising reduction in code size, though still noticeable increase for something, which is still relatively minor though. So, I'm rebasing patchset to use separate zsock_recv_stream() function. |
int zsock_listen(int sock, int backlog); | ||
int zsock_accept(int sock, struct sockaddr *addr, socklen_t *addrlen); | ||
ssize_t zsock_send(int sock, const void *buf, size_t len, int flags); | ||
ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be logical to place these definitions in a zsocket.h header, to be included by Zephyr applications wanting to use the native Zephyr socket symbols, and leave socket.h to define the POSIX only symbols, as below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, this turns one header into two, and reading literally, puts a networking-related header outside net/. Why would this be better? As it stands now, everything socket-related is in one header, but apps can configure subset of the functionality needed, like with the rest of subsystems in Zephyr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few reasons:
- It's often a standard module convention to have symbols declared in their own header. Makes it easy to find things. (I know, Zephyr doesn't always follow this convention).
- These are Zephyr APIs, which may have divergence from true POSIX APIs (declared in socket.h).
- It makes it easier to implement a new socket provider for the socket APIs for offloading. zsocket could just be one (the default) socket provider. Socket.h (or socket.c) would be the switch layer.
I assumed zsocket.h would also go into net/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's often a standard module convention to have symbols declared in their own header. Makes it easy to find things.
Well, that's actually mu biggest concern - if I'd see socket.h
, I would never think there's also zsocket.h
, or vice-versa. Zephyr doesn't split IPv4 and IPv6 headers. lwIP defines both native prefixed functions (lwip_recv(), etc.) and POSIXy macros in the same header, sockets.h. And in this case, it's also the single module, it just defines 2 API sets, one being alias of another (but it's the same API).
These are Zephyr APIs, which may have divergence from true POSIX APIs (declared in socket.h).
Well, there're no "true POSIX APIs", only "BSD Sockets like API" ("like" is important). True POSIX APIs live in <sys/socket.h>, Zephyr's BSD Sockets like API lives in <net/socket.h>.
It makes it easier to implement a new socket provider for the socket APIs for offloading. zsocket could just be one (the default) socket provider. Socket.h (or socket.c) would be the switch layer.
Ok, this finally explains why you suggest this change! But why do you want to do it this way? I imagined it would be done just the same way as offloading already implemented in net_context - that each individual zsock_*() will be the place of switching, and that offloading will be supported per netif.
You instead seem to propose that socket offloading will be global, not per netif. Rereading https://jira.zephyrproject.org/browse/ZEP-2271?focusedCommentId=19002&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-19002 , that seems to be what's written in the user story, but if you don't mind, I'd like to get an ack form @jukkar and @tbursztyka that it's ok to proceed with such design right away (vs further discuss it), as it leaves many questions open, e.g. why we have 2 inconsistently implemented offloading mechanisms. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Independent of where socket offloading occurs, it just seems it would be logical and future-proof to have zsocket.h defining native zsock functions, and socket.h declaring the socket functions, mapping to zsock. Anyways, just an opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Independent of where socket offloading occurs, it just seems it would be logical and future-proof to have zsocket.h defining native zsock functions, and socket.h declaring the socket functions
Gil, I appreciate the idea, but after additional checking, I don't see such pattern of splitting the same API across 2+ headers to be used in Zephyr. And as I mentioned above, these are indeed intended to be 2 facets of the same API, not 2 different APIs. I'll be happy to make such a change however is there will be 2nd vote for it. Otherwise, well, if it proves to be useful/need, we may need to do such a refactor in the future (e.g. when we'll have better defined POSIX API layer). Thanks for understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood.
ssize_t zsock_send(int sock, const void *buf, size_t len, int flags); | ||
ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags); | ||
|
||
#if defined(CONFIG_NET_SOCKETS_POSIX_NAMES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then could do here:
#include <zsocket.h>
|
||
#include <net/net_context.h> | ||
#include <net/net_pkt.h> | ||
#include <net/socket.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would also be
#include <net/zsocket.h>
as it's calling native Zephyr socket APIs, rather than POSIX socket, close, recv, etc.
@@ -1,3 +1,4 @@ | |||
obj-$(CONFIG_NET_SOCKETS) += sockets/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then, maybe the subdirectory should be zsocket/ or zsock/ instead of sockets/?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, why? There're dns, http on the same level, not zdns, zhttp. (There's zoap, yeah, but it's an exception.)
@andyross , @andrewboie : Can you please check commit titled "WIP: kernel: fifo: Add peek_head/peek_tail accessor" (cc155f7 currently, though may be rebased later), and tell whether you'd prefer these functions, k_fifo_peek_head() and k_fifo_peek_tail() to become part of the general FIFO API (by moving them to <kernel.h>), or if it's better for them to be used in adhoc way just for sockets code. In this regard it's similar to #26, but that was truly new functionality, so I submitted it ahead, to be sure I can rely on it at all. The functions above are just macro wrappers though, so if there're concerns with making them part of the API, everyone in need can just reinvent them, like done here. Thanks. |
@dbkinder : Currently this fails with:
Context around CONFIG_NET_SOCKETS_POSIX_NAMES.rst:8 is:
Any idea why it suddenly interpreters plain text as link mark up ans what to do to make it happy? (I already tried single quotes around zsock_, didn't help.) |
@dbkinder : Using backquotes seems to resolve the issue, but that's italics IIRC. Perhaps using double-backquotes (monospace IIRC) probably would make even more sense, it's just unclear why all this markup does in Kconfig files... Please suggest. |
4053653
to
f1b0460
Compare
This adds Kconfig and build infrastructure and implements zsock_socket() and zsock_close() functions. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Two changes are required so far: * There's unavoidable need to have a per-socket queue of packets (for data sockets) or pending connections (for listening sockets). These queues share the same space (as a C union). * There's a need to track "EOF" status of connection, synchronized with a queue of pending packets (i.e. EOF status should be processed only when all pending packets are processed). A natural place to store it per-packet then, and we had a "sent" bit which was used only for outgoing packets, recast it as "eof" for incoming socket packets. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
With CONFIG_NET_SOCKETS_POSIX_NAMES=y, "raw" POSIX names like socket(), recv(), close() will be exposed (using macro defines). The close() is the biggest culprit here, because in POSIX it applies to any file descriptor, but in this implementation - only to sockets. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
By moving user_data member at the beginning of structure. With refcount at the beginning, reliable passsing of contexts via FIFO was just impossible. (Queuing contexts to a FIFO is required for BSD Sockets API). Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
As explained in the docstrings, a usecase behind these operations is when other container objects are put in a fifo. The typical processing iteration make take just some data from a container at the head of fifo, with the container still being kept at the fifo, unless it becomes empty, and only then it's removed. Similarly with adding more data - first step may be to try to add more data to a container at the tail of fifo, and only if it's full, add another container to a fifo. The specific usecase these operations are added for is network subsystem processing, where net_buf's and net_pkt's are added to fifo. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
If a socket is closed without reading all data from peer or accepting all pending connection, they will be leaked. So, flush queues explicitly. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
The example source code is POSIX-compatible (modulo include files), i.e. can be built and behaves the same way for Zephyr and a POSIX system (e.g. Linux). Makefile.posix is available for the latter. Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
Based on @Vudentz' suggestion at cc155f7#commitcomment-22763881 , this was refactored into kernel.h, please review: dbf743a |
Thanks! |
…ject-rtos#498) Also, add a spot for a cleanup function to be registered with modules that will be called by a new zjs_modules_cleanup function. Currently, this is called only in ashell. Also, fix a bug where module instances weren't being released. Signed-off-by: Geoff Gustafson <geoff@linux.intel.com>
This implements BSD Sockets like API, blocking mode, functions: socket(), bind(), connect(), listen(), accept(), recv(), send(), per https://jira.zephyrproject.org/browse/ZEP-2226 .
A sample echo server is supplied (portable to (well, from) POSIX systems) and a simple test for UDP functionality.