-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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
Introduce socket service API #66758
Merged
carlescufi
merged 6 commits into
zephyrproject-rtos:main
from
jukkar:devel/socket-service
Jan 16, 2024
Merged
Introduce socket service API #66758
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
be985da
net: sockets: Create a socket service API
jukkar 16fae69
tests: net: socket: service: Add tests for socket service API
jukkar 6b18136
samples: net: sockets: Add echo-service sample
jukkar 310ff08
net: shell: Add sockets services prints
jukkar 1b1db56
tests: net: socket: Add correct path to socket.h for POSIX_API
jukkar c6684c5
net: shell: Avoid gcc warning print with string catenation
jukkar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
/** | ||
* @file | ||
* @brief BSD Socket service API | ||
* | ||
* API can be used to install a k_work that is called | ||
* if there is data received to a socket. | ||
*/ | ||
|
||
/* | ||
* Copyright (c) 2023 Nordic Semiconductor ASA | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#ifndef ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_ | ||
#define ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_ | ||
|
||
/** | ||
* @brief BSD socket service API | ||
* @defgroup bsd_socket_service BSD socket service API | ||
* @ingroup networking | ||
* @{ | ||
*/ | ||
|
||
#include <sys/types.h> | ||
#include <zephyr/types.h> | ||
#include <zephyr/net/socket.h> | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
/** | ||
* This struct contains information which socket triggered | ||
* calls to the callback function. | ||
*/ | ||
struct net_socket_service_event { | ||
/** k_work that is done when there is desired activity in file descriptor. */ | ||
struct k_work work; | ||
/** Callback to be called for desired socket activity */ | ||
k_work_handler_t callback; | ||
/** Socket information that triggered this event. */ | ||
struct zsock_pollfd event; | ||
/** User data */ | ||
void *user_data; | ||
/** Service back pointer */ | ||
struct net_socket_service_desc *svc; | ||
}; | ||
|
||
/** | ||
* Main structure holding socket service configuration information. | ||
* The k_work item is created so that when there is data coming | ||
* to those fds, the k_work callback is then called. | ||
* The workqueue can be set NULL in which case system workqueue is used. | ||
* The service descriptor should be created at built time, and then used | ||
* as a parameter to register the sockets to be monitored. | ||
* User should create needed sockets and then setup the poll struct and | ||
* then register the sockets to be monitored at runtime. | ||
*/ | ||
struct net_socket_service_desc { | ||
jukkar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG | ||
/** | ||
* Owner name. This can be used in debugging to see who has | ||
* registered this service. | ||
*/ | ||
const char *owner; | ||
#endif | ||
/** Workqueue where the work is submitted. */ | ||
struct k_work_q *work_q; | ||
/** Pointer to the list of services that we are listening */ | ||
struct net_socket_service_event *pev; | ||
/** Length of the pollable socket array for this service. */ | ||
int pev_len; | ||
/** Where are my pollfd entries in the global list */ | ||
int *idx; | ||
}; | ||
|
||
#define __z_net_socket_svc_get_name(_svc_id) __z_net_socket_service_##_svc_id | ||
#define __z_net_socket_svc_get_idx(_svc_id) __z_net_socket_service_idx##_svc_id | ||
#define __z_net_socket_svc_get_owner __FILE__ ":" STRINGIFY(__LINE__) | ||
|
||
extern void net_socket_service_callback(struct k_work *work); | ||
|
||
#if CONFIG_NET_SOCKETS_LOG_LEVEL >= LOG_LEVEL_DBG | ||
#define NET_SOCKET_SERVICE_OWNER .owner = __z_net_socket_svc_get_owner, | ||
#else | ||
#define NET_SOCKET_SERVICE_OWNER | ||
#endif | ||
|
||
#define NET_SOCKET_SERVICE_CALLBACK_MODE(_flag) \ | ||
IF_ENABLED(_flag, \ | ||
(.work = Z_WORK_INITIALIZER(net_socket_service_callback),)) | ||
|
||
#define __z_net_socket_service_define(_name, _work_q, _cb, _count, _async, ...) \ | ||
static int __z_net_socket_svc_get_idx(_name); \ | ||
static struct net_socket_service_event \ | ||
__z_net_socket_svc_get_name(_name)[_count] = { \ | ||
[0 ... ((_count) - 1)] = { \ | ||
.event.fd = -1, /* Invalid socket */ \ | ||
NET_SOCKET_SERVICE_CALLBACK_MODE(_async) \ | ||
.callback = _cb, \ | ||
} \ | ||
}; \ | ||
COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (), __VA_ARGS__) \ | ||
const STRUCT_SECTION_ITERABLE(net_socket_service_desc, _name) = { \ | ||
NET_SOCKET_SERVICE_OWNER \ | ||
.work_q = (_work_q), \ | ||
.pev = __z_net_socket_svc_get_name(_name), \ | ||
.pev_len = (_count), \ | ||
.idx = &__z_net_socket_svc_get_idx(_name), \ | ||
} | ||
|
||
/** | ||
* @brief Statically define a network socket service. | ||
* The user callback is called asynchronously for this service meaning that | ||
* the service API will not wait until the user callback returns before continuing | ||
* with next socket service. | ||
* | ||
* The socket service can be accessed outside the module where it is defined using: | ||
* | ||
* @code extern struct net_socket_service_desc <name>; @endcode | ||
* | ||
* @note This macro cannot be used together with a static keyword. | ||
* If such a use-case is desired, use NET_SOCKET_SERVICE_ASYNC_DEFINE_STATIC | ||
* instead. | ||
* | ||
* @param name Name of the service. | ||
* @param work_q Pointer to workqueue where the work is done. Can be null in which case | ||
* system workqueue is used. | ||
* @param cb Callback function that is called for socket activity. | ||
* @param count How many pollable sockets is needed for this service. | ||
*/ | ||
#define NET_SOCKET_SERVICE_ASYNC_DEFINE(name, work_q, cb, count) \ | ||
__z_net_socket_service_define(name, work_q, cb, count, 1) | ||
|
||
/** | ||
* @brief Statically define a network socket service in a private (static) scope. | ||
* The user callback is called asynchronously for this service meaning that | ||
* the service API will not wait until the user callback returns before continuing | ||
* with next socket service. | ||
* | ||
* @param name Name of the service. | ||
* @param work_q Pointer to workqueue where the work is done. Can be null in which case | ||
* system workqueue is used. | ||
* @param cb Callback function that is called for socket activity. | ||
* @param count How many pollable sockets is needed for this service. | ||
*/ | ||
#define NET_SOCKET_SERVICE_ASYNC_DEFINE_STATIC(name, work_q, cb, count) \ | ||
__z_net_socket_service_define(name, work_q, cb, count, 1, static) | ||
|
||
/** | ||
* @brief Statically define a network socket service. | ||
* The user callback is called synchronously for this service meaning that | ||
* the service API will wait until the user callback returns before continuing | ||
* with next socket service. | ||
* | ||
* The socket service can be accessed outside the module where it is defined using: | ||
* | ||
* @code extern struct net_socket_service_desc <name>; @endcode | ||
* | ||
* @note This macro cannot be used together with a static keyword. | ||
* If such a use-case is desired, use NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC | ||
* instead. | ||
* | ||
* @param name Name of the service. | ||
* @param work_q Pointer to workqueue where the work is done. Can be null in which case | ||
* system workqueue is used. | ||
* @param cb Callback function that is called for socket activity. | ||
* @param count How many pollable sockets is needed for this service. | ||
*/ | ||
#define NET_SOCKET_SERVICE_SYNC_DEFINE(name, work_q, cb, count) \ | ||
__z_net_socket_service_define(name, work_q, cb, count, 0) | ||
|
||
/** | ||
* @brief Statically define a network socket service in a private (static) scope. | ||
* The user callback is called synchronously for this service meaning that | ||
* the service API will wait until the user callback returns before continuing | ||
* with next socket service. | ||
* | ||
* @param name Name of the service. | ||
* @param work_q Pointer to workqueue where the work is done. Can be null in which case | ||
* system workqueue is used. | ||
* @param cb Callback function that is called for socket activity. | ||
* @param count How many pollable sockets is needed for this service. | ||
*/ | ||
#define NET_SOCKET_SERVICE_SYNC_DEFINE_STATIC(name, work_q, cb, count) \ | ||
__z_net_socket_service_define(name, work_q, cb, count, 0, static) | ||
|
||
/** | ||
* @brief Register pollable sockets. | ||
* | ||
* @param service Pointer to a service description. | ||
* @param fds Socket array to poll. | ||
* @param len Length of the socket array. | ||
* @param user_data User specific data. | ||
* | ||
* @retval 0 No error | ||
* @retval -ENOENT Service is not found. | ||
* @retval -ENINVAL Invalid parameter. | ||
*/ | ||
__syscall int net_socket_service_register(const struct net_socket_service_desc *service, | ||
struct zsock_pollfd *fds, int len, void *user_data); | ||
|
||
/** | ||
* @brief Unregister pollable sockets. | ||
* | ||
* @param service Pointer to a service description. | ||
* | ||
* @retval 0 No error | ||
* @retval -ENOENT Service is not found. | ||
* @retval -ENINVAL Invalid parameter. | ||
*/ | ||
static inline int net_socket_service_unregister(const struct net_socket_service_desc *service) | ||
{ | ||
return net_socket_service_register(service, NULL, 0, NULL); | ||
} | ||
|
||
/** | ||
* @typedef net_socket_service_cb_t | ||
* @brief Callback used while iterating over socket services. | ||
* | ||
* @param svc Pointer to current socket service. | ||
* @param user_data A valid pointer to user data or NULL | ||
*/ | ||
typedef void (*net_socket_service_cb_t)(const struct net_socket_service_desc *svc, | ||
void *user_data); | ||
|
||
/** | ||
* @brief Go through all the socket services and call callback for each service. | ||
* | ||
* @param cb User-supplied callback function to call | ||
* @param user_data User specified data | ||
*/ | ||
void net_socket_service_foreach(net_socket_service_cb_t cb, void *user_data); | ||
jukkar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#include <syscalls/socket_service.h> | ||
|
||
/** | ||
* @} | ||
*/ | ||
|
||
#endif /* ZEPHYR_INCLUDE_NET_SOCKET_SERVICE_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(sockets_service_echo) | ||
|
||
FILE(GLOB app_sources src/*.c) | ||
target_sources(app PRIVATE ${app_sources}) | ||
|
||
include(${ZEPHYR_BASE}/samples/net/common/common.cmake) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
.. zephyr:code-sample:: sockets-service-echo | ||
:name: Echo server (service) | ||
:relevant-api: bsd_sockets | ||
|
||
Implements a simple IPv4/IPv6 TCP echo server using BSD sockets and socket service API. | ||
|
||
Overview | ||
******** | ||
|
||
The sockets/echo_service sample application for Zephyr implements a TCP echo | ||
server supporting both IPv4 and IPv6 and using a BSD Sockets compatible API. | ||
|
||
The purpose of this sample is to show how to use socket service API. | ||
The socket service is a concept where many blocking sockets can be listened by | ||
one thread, and which can then trigger a callback if there is activity in the set | ||
of sockets. This saves memory as only one thread needs to be created in the | ||
system. | ||
|
||
The application supports IPv4 and IPv6, and both UDP and TCP are also supported. | ||
The source code for this sample application can be found at: | ||
:zephyr_file:`samples/net/sockets/echo_service`. | ||
|
||
Requirements | ||
************ | ||
|
||
- :ref:`networking_with_host` | ||
- or, a board with hardware networking | ||
|
||
Building and Running | ||
******************** | ||
|
||
Build the Zephyr version of the sockets/echo_service application like this: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/net/sockets/echo_service | ||
:board: <board_to_use> | ||
:goals: build | ||
:compact: | ||
|
||
After the sample starts, it expects connections at 192.0.2.1, or 2001:db8::1 | ||
and port 4242. | ||
The easiest way to connect is: | ||
|
||
.. code-block:: console | ||
|
||
$ telnet 192.0.2.1 4242 | ||
|
||
After a connection is made, the application will echo back any line sent | ||
to it. The application implements a single-threaded server using blocking | ||
sockets, and currently is only implemented to serve only one client connection | ||
at time. After the current client disconnects, the next connection can proceed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Overlay for experimental TCP as qemu_x86 with E1000 | ||
|
||
CONFIG_PCIE=y | ||
|
||
CONFIG_NET_L2_ETHERNET=y | ||
CONFIG_NET_QEMU_ETHERNET=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# General config | ||
# The async method used in the sample needs more stack for the workqueue | ||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1500 | ||
CONFIG_POSIX_API=y | ||
|
||
# Networking config | ||
CONFIG_NETWORKING=y | ||
CONFIG_NET_IPV4=y | ||
CONFIG_NET_IPV6=y | ||
CONFIG_NET_TCP=y | ||
CONFIG_NET_SOCKETS=y | ||
CONFIG_NET_SOCKETS_POSIX_NAMES=y | ||
CONFIG_NET_IPV4_MAPPING_TO_IPV6=y | ||
CONFIG_POSIX_MAX_FDS=10 | ||
CONFIG_NET_MAX_CONN=5 | ||
CONFIG_NET_SOCKETS_SERVICE=y | ||
CONFIG_NET_SOCKETS_POLL_MAX=20 | ||
|
||
# Network driver config | ||
CONFIG_TEST_RANDOM_GENERATOR=y | ||
|
||
# Network address config | ||
CONFIG_NET_CONFIG_SETTINGS=y | ||
CONFIG_NET_CONFIG_NEED_IPV4=y | ||
CONFIG_NET_CONFIG_NEED_IPV6=y | ||
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" | ||
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2" | ||
pdgendt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" | ||
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2" | ||
|
||
# Network buffers | ||
CONFIG_NET_PKT_RX_COUNT=16 | ||
CONFIG_NET_PKT_TX_COUNT=16 | ||
CONFIG_NET_BUF_RX_COUNT=64 | ||
CONFIG_NET_BUF_TX_COUNT=64 | ||
CONFIG_NET_CONTEXT_NET_PKT_POOL=y | ||
|
||
CONFIG_NET_SHELL=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
sample: | ||
description: echo server using socket service API | ||
name: socket_service_echo | ||
common: | ||
harness: net | ||
depends_on: netif | ||
filter: CONFIG_FULL_LIBC_SUPPORTED and not CONFIG_NATIVE_LIBC | ||
# eventfd does not work properly with native_posix so exclude it here | ||
platform_exclude: | ||
- native_posix | ||
- native_posix_64 | ||
tests: | ||
sample.net.sockets.service.echo: | ||
tags: | ||
- net | ||
- socket |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 wonder if we really need this static configuration for socket service? It seems a bit problematic, at least for my use case - DHCP server. Here, ideally, I'd need to have a single socket for each interface that wants to support the DHCP server functionality. But I don't think we have a way to calculate the number of interfaces in the system at build time to supply the static configuration macros. Purely runtime registration would be less cumbersome in such case.
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 originally did some experimentation with runtime configuration, but as the API requires some housekeeping activities, allocating them at runtime requires malloc etc. There is a call to malloc at this PR too but hopefully we could get rid of it.
We could place
struct zsock_pollfd
into DHCP config in network interface, that way the information this API needs, would already be part of network interface.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.
Devicetree could help here, just sayin 🤷🏼♂️