This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF9160 offloaded poll() is incompatible with other Zephyr file descriptors (e.g. eventfd)

The big advantage of using poll() in application is the possibility to listen for multiple events at once such as:

- incoming traffic on BSD socket,
- incoming traffic on other socket type (e.g. pipe, socketpair),
- some event from other application thread/component using eventfd(),
- some timeouts using timerfd (not implemented in Zephyr yet, but it seems just a matter of time).

Current implementation of offloaded poll() for nRF91 modem sockets uses nrf_poll() underneath. This means that only modem sockets (file descriptors) are handled using such poll() call (and most mechanisms mentioned above do not). This is enough only for some simple usage, but is very limiting when application requires some complex behavior based on other application threads or traffic from other network interfaces.

We need to have for example some callback based mechanism for getting notifications from nRF sockets. That way those notifications can be easily integrated with Zephyr native poll() mechanism, which handles all possible socket types and allows implementation of much more advanced application code and possibly integration with higher number of existing POSIX-compatible libraries.

Parents
  • I would like to point out to this old thread that this issue has already been fixed since nRF Connect SDK 2.3.0

    Please see this PR:
    https://github.com/nrfconnect/sdk-nrf/pull/10242

    Now nRF91 sockets and native Zephyr sockets can be combined in `zsock_poll()`

  • Combining nrf91 and native socket might be ok, but can you combine eventfd with sockets in zsock_poll()???

  • Yes.
    Any file descriptor that Zephyr's poll() accept is fine.

    LwM2M engine uses socketpair and that works well with nRF91 sockets, so I assume eventFD works as well.

  • I had a look at LwM2M engine and I noticed that there is no check if poll returned with "0", indicating that the timeout occured.

    My setup is an offloaded nrf91 socket, an eventfd (to terminate my thread) and I need the timeout in order to execute something periodically (this all is similar to the mqtt_helper_poll_loop in the mqtt_helper_lib, but with the option to terminate the loop via the eventfd)

    What I see is that when I write to the eventfd filedescriptor zsock_poll returns 0 as if a timeout occured.

    I modified the posix/eventfd sample from zephyr and I see that the general approach should be fine. Reading via the eventfd works and the timeout is also working.

    This is the output:
    west build -b qemu_cortex_m3 -t run
    -- west build: running target run
    [7/8] Linking C executable zephyr/zephyr.elf
    Memory region         Used Size  Region Size  %age Used
               FLASH:       88512 B       256 KB     33.76%
                 RAM:       23240 B        64 KB     35.46%
            IDT_LIST:          0 GB         2 KB      0.00%
    [7/8] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
    qemu-system-arm: warning: nic stellaris_enet.0 has no peer
    Timer with period zero, disabling
    *** Booting nRF Connect SDK v2.5.0 ***
    Writing 2 to efd
    About to read
    Read 2 (0x2) from efd
    sleeping
    Writing 2 to efd
    About to read
    Read 2 (0x2) from efd
    sleeping
    Writing 3 to efd
    About to read
    Read 3 (0x3) from efd
    sleeping
    Writing 4 to efd
    About to read
    Read 4 (0x4) from efd
    read 4 -> exit
    exit reader thread
    Completed write loop
    Finished

    This is the modified sample:

    #include <sys/eventfd.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <zephyr/kernel.h>
    #include <zephyr/posix/poll.h>
    #include <zephyr/net/socket.h>

    #define fatal(msg) \
    do { \
    perror(msg); \
    exit(EXIT_FAILURE); \
    } while (0)

    K_THREAD_STACK_DEFINE(test_stack_area, 1024U);
    static struct k_thread reader_thread_data;
    static void reader(void *p_eventfd, void *p_wb, void *p_c);

    /* As Zephyr doesn't provide command-line args, emulate them. */
    char *input_argv[] = {"argv0", "2", "2", "3", "4"};

    int g_argc;
    char **g_argv;

    static void start_reader(int efd)
    {
    (void)k_thread_create(&reader_thread_data, test_stack_area,
    K_THREAD_STACK_SIZEOF(test_stack_area), reader, &efd, NULL, NULL,
    K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
    }

    static void writer(int efd)
    {
    int j;
    uint64_t u;
    ssize_t s;

    for (j = 1; j < g_argc; j++) {
    printf("Writing %s to efd\n", g_argv[j]);
    u = strtoull(g_argv[j], NULL, 0);
    s = eventfd_write(efd, u);
    sleep(1);
    if (s != 0) {
    fatal("write");
    }
    }
    printf("Completed write loop\n");
    }

    void reader(void *p_eventfd, void *p_wb, void *p_c)
    {
    uint64_t u;
    ssize_t s;
    struct zsock_pollfd fds[1];
    int event_fd = *(int *)p_eventfd;

    while (true) {
    fds[0].fd = event_fd;
    fds[0].events = ZSOCK_POLLIN;

    int err = zsock_poll(fds, 1, 500);

    if (err < 0) {
    fatal("poll");
    } else if (err == 0) {
    printf("sleeping\n");
    } else {
    if (fds[0].revents | ZSOCK_POLLIN) {
    printf("About to read\n");
    s = eventfd_read(fds[0].fd, &u);
    if (s != 0) {
    fatal("read");
    }
    printf("Read %llu (0x%llx) from efd\n", (unsigned long long)u,
    (unsigned long long)u);
    if (u == 4) {
    printf("read 4 -> exit\n");
    break;
    }
    }
    }
    }

    printf("exit reader thread\n");
    }

    int main(int argc, char *argv[])
    {
    int efd;

    argv = input_argv;
    argc = sizeof(input_argv) / sizeof(input_argv[0]);

    if (argc < 2) {
    fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
    exit(EXIT_FAILURE);
    }

    g_argc = argc;
    g_argv = argv;

    efd = eventfd(0, 0);
    if (efd == -1) {
    fatal("eventfd");
    }

    start_reader(efd);
    writer(efd);
    k_thread_join(&reader_thread_data, K_SECONDS(2U));
    printf("Finished\n");

    return 0;
    }
Reply
  • I had a look at LwM2M engine and I noticed that there is no check if poll returned with "0", indicating that the timeout occured.

    My setup is an offloaded nrf91 socket, an eventfd (to terminate my thread) and I need the timeout in order to execute something periodically (this all is similar to the mqtt_helper_poll_loop in the mqtt_helper_lib, but with the option to terminate the loop via the eventfd)

    What I see is that when I write to the eventfd filedescriptor zsock_poll returns 0 as if a timeout occured.

    I modified the posix/eventfd sample from zephyr and I see that the general approach should be fine. Reading via the eventfd works and the timeout is also working.

    This is the output:
    west build -b qemu_cortex_m3 -t run
    -- west build: running target run
    [7/8] Linking C executable zephyr/zephyr.elf
    Memory region         Used Size  Region Size  %age Used
               FLASH:       88512 B       256 KB     33.76%
                 RAM:       23240 B        64 KB     35.46%
            IDT_LIST:          0 GB         2 KB      0.00%
    [7/8] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
    qemu-system-arm: warning: nic stellaris_enet.0 has no peer
    Timer with period zero, disabling
    *** Booting nRF Connect SDK v2.5.0 ***
    Writing 2 to efd
    About to read
    Read 2 (0x2) from efd
    sleeping
    Writing 2 to efd
    About to read
    Read 2 (0x2) from efd
    sleeping
    Writing 3 to efd
    About to read
    Read 3 (0x3) from efd
    sleeping
    Writing 4 to efd
    About to read
    Read 4 (0x4) from efd
    read 4 -> exit
    exit reader thread
    Completed write loop
    Finished

    This is the modified sample:

    #include <sys/eventfd.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <zephyr/kernel.h>
    #include <zephyr/posix/poll.h>
    #include <zephyr/net/socket.h>

    #define fatal(msg) \
    do { \
    perror(msg); \
    exit(EXIT_FAILURE); \
    } while (0)

    K_THREAD_STACK_DEFINE(test_stack_area, 1024U);
    static struct k_thread reader_thread_data;
    static void reader(void *p_eventfd, void *p_wb, void *p_c);

    /* As Zephyr doesn't provide command-line args, emulate them. */
    char *input_argv[] = {"argv0", "2", "2", "3", "4"};

    int g_argc;
    char **g_argv;

    static void start_reader(int efd)
    {
    (void)k_thread_create(&reader_thread_data, test_stack_area,
    K_THREAD_STACK_SIZEOF(test_stack_area), reader, &efd, NULL, NULL,
    K_LOWEST_APPLICATION_THREAD_PRIO, 0, K_NO_WAIT);
    }

    static void writer(int efd)
    {
    int j;
    uint64_t u;
    ssize_t s;

    for (j = 1; j < g_argc; j++) {
    printf("Writing %s to efd\n", g_argv[j]);
    u = strtoull(g_argv[j], NULL, 0);
    s = eventfd_write(efd, u);
    sleep(1);
    if (s != 0) {
    fatal("write");
    }
    }
    printf("Completed write loop\n");
    }

    void reader(void *p_eventfd, void *p_wb, void *p_c)
    {
    uint64_t u;
    ssize_t s;
    struct zsock_pollfd fds[1];
    int event_fd = *(int *)p_eventfd;

    while (true) {
    fds[0].fd = event_fd;
    fds[0].events = ZSOCK_POLLIN;

    int err = zsock_poll(fds, 1, 500);

    if (err < 0) {
    fatal("poll");
    } else if (err == 0) {
    printf("sleeping\n");
    } else {
    if (fds[0].revents | ZSOCK_POLLIN) {
    printf("About to read\n");
    s = eventfd_read(fds[0].fd, &u);
    if (s != 0) {
    fatal("read");
    }
    printf("Read %llu (0x%llx) from efd\n", (unsigned long long)u,
    (unsigned long long)u);
    if (u == 4) {
    printf("read 4 -> exit\n");
    break;
    }
    }
    }
    }

    printf("exit reader thread\n");
    }

    int main(int argc, char *argv[])
    {
    int efd;

    argv = input_argv;
    argc = sizeof(input_argv) / sizeof(input_argv[0]);

    if (argc < 2) {
    fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
    exit(EXIT_FAILURE);
    }

    g_argc = argc;
    g_argv = argv;

    efd = eventfd(0, 0);
    if (efd == -1) {
    fatal("eventfd");
    }

    start_reader(efd);
    writer(efd);
    k_thread_join(&reader_thread_data, K_SECONDS(2U));
    printf("Finished\n");

    return 0;
    }
Children
Related