Skip to content
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

Merged
merged 13 commits into from
Jun 28, 2017

Conversation

pfalcon
Copy link
Contributor

@pfalcon pfalcon commented Jun 14, 2017

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.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 14, 2017

@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.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 15, 2017

@askawu: Please review too.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 15, 2017

Shippable fails (so far) due:

Commit 279ddf5839:
1: T5 Title contains the word 'wip' (case-insensitive)

That's "WIP: kernel: fifo: Add peek_head/peek_tail accessors." commit, about which I ask suggestions above.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 15, 2017

Looking that we have tests/net/lib/, maybe should create tests/net/sockets/ to group socket tests?

Copy link
Member

@jukkar jukkar left a 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
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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;
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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.

user_data);

/* if pkt == NULL, EOF */
if (pkt == NULL) {
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

if (pkt == NULL) {
struct net_pkt *last_pkt = _k_fifo_peek_tail(&ctx->recv_q);

if (last_pkt == NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed (other cases too).

*/
sock_set_eof(ctx);
k_fifo_cancel_wait(&ctx->recv_q);
NET_DBG("Marked socket %p as peer-closed\n", ctx);
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, was printk() initially...


ssize_t zsock_recv(int sock, void *buf, size_t max_len, int flags)
{
(void)flags;
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

CONFIG_NET_SLIP_TAP=y
CONFIG_TEST_RANDOM_GENERATOR=y


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra empty line

if (sock_type == SOCK_DGRAM) {
__ASSERT(0, "DGRAM is not yet handled");
} else if (sock_type == SOCK_STREAM) {
do {
Copy link
Collaborator

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.

Copy link
Contributor Author

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.)

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

@lpereira
Copy link
Collaborator

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 poll() or select(). Select is going to be tricky, considering that the fd is just a pointer in disguise, but poll() might be doable.

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.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 20, 2017

@lpereira :

For some future work, it would be nice to make something similar to poll() or select(). Select is going to be tricky, considering that the fd is just a pointer in disguise, but poll() might be doable.

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.

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.

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!

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 20, 2017

Some updates:

  • samples/net/socket_echo: Added Makefile.posix which allows to build the sample (very same source code) on Linux, etc.
  • tests/net/socket_udp: Converted to tests/net/socket/udp, in anticipation that there will be more socket tests.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 20, 2017

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).

So, previously this was tested on qemu_x86, now confirmed that on qemu_cortex_m3 it leads to the same behavior.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 20, 2017

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.

Ok, some stats:

code size of sockets.o:
qemu_x86: before: 1075, after: 1072, new zsock_recb_stream() is inlined
qemu_cortex_m3: before: 814, after: 826, new zsock_recb_stream() is inlined
qemu_xtensa: before: 781, after: 780, new zsock_recb_stream() is inlined
em_starterkit: before: 928, after: 928, new zsock_recb_stream() is inlined

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);
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few reasons:

  1. 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).
  2. These are Zephyr APIs, which may have divergence from true POSIX APIs (declared in socket.h).
  3. 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/.

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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)
Copy link
Collaborator

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>
Copy link
Collaborator

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/
Copy link
Collaborator

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/?

Copy link
Contributor Author

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.)

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 22, 2017

@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.

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 22, 2017

@dbkinder : Currently this fails with:

                    /home/buildslave/src/github.com/zephyrproject-rtos/zephyr/doc/reference/kconfig/CONFIG_NET_SOCKETS_POSIX_NAMES.rst:8: ERROR: Unknown target name: "zsock".

Context around CONFIG_NET_SOCKETS_POSIX_NAMES.rst:8 is:

.. option:: CONFIG_NET_SOCKETS_POSIX_NAMES:
.. _CONFIG_NET_SOCKETS_POSIX_NAMES:

By default, Sockets API function are prefixed with "zsock_" to avoid
namespacing issues. If this option is enabled, they will be provided
with standard POSIX names like socket(), recv(), and close(), to help

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.)

@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 22, 2017

@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.

@pfalcon pfalcon force-pushed the net-sockets1 branch 2 times, most recently from 4053653 to f1b0460 Compare June 23, 2017 18:21
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>
@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 26, 2017

@andyross , @andrewboie : Can you please check commit titled "WIP: kernel: fifo: Add peek_head/peek_tail accessor" (cc155f7 currently, though may be rebased later)...

Based on @Vudentz' suggestion at cc155f7#commitcomment-22763881 , this was refactored into kernel.h, please review: dbf743a

@jukkar jukkar merged commit b650fa8 into zephyrproject-rtos:master Jun 28, 2017
@pfalcon
Copy link
Contributor Author

pfalcon commented Jun 29, 2017

Thanks!

nagineni pushed a commit to nagineni/zephyr that referenced this pull request Nov 20, 2017
…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>
parthitce pushed a commit to linumiz/zephyr that referenced this pull request Jun 21, 2023
…t_double_serializer in 485f2a0 by adding a json_object_put call.
parthitce pushed a commit to linumiz/zephyr that referenced this pull request Jun 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants