Skip to content

Conversation

benpicco
Copy link
Contributor

@benpicco benpicco commented Oct 11, 2019

Contribution description

async_read relies on select() to wait for file descriptors to become active.
The problem with this is, that select() does not work for POLLPRI events that are generated when watching GPIO fds.

In preparation for native GPIO support, this converts async_read from select() to poll().

Unfortunately, no SIGURG signal is being generated after calling fcntl(fd, F_SETOWN, getpid()), so the same fallback as on OS X is used for those: A child thread is spawned that blocks on poll() and manually generates the signal for the parent when poll() returns.

Testing procedure

Make sure netdev_tap still works by running the gnrc_networking example.
Also check if it still works on OS X.

Issues/PRs references

needed for #12451
taken from #7530
GPIO and SIGURG on StackOverflow

@benpicco benpicco added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation Platform: native Platform: This PR/issue effects the native platform labels Oct 11, 2019
@benpicco benpicco added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Oct 13, 2019
@benpicco benpicco requested review from jnohlgard and smlng January 29, 2020 10:32
@fjmolinas
Copy link
Contributor

GPIO and SIGURG on StackOverflow

No longer exists :(

@benpicco
Copy link
Contributor Author

benpicco commented Apr 21, 2020

I posted the question why a GPIO fd won't generate a signal to the process but there was no answer, I guess it got automatically deleted.

I had

this Code
#include <fcntl.h>
#include <linux/gpio.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define PIN_IN  (20)
#define PIN_OUT (26)

static int fd_in; /* to be read in the signal handler */

/* configure input pin */
static int setup_pin_int(int fd, int pin, int mode, int flank) {
    struct gpioevent_request req = {
        .lineoffset  = pin,
        .handleflags = mode,
        .eventflags  = flank
    };

    int res = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &req);

    if (res < 0) {
        return res;
    }

    return req.fd;
}

/* configure output pin */
static int setup_pin_out(int fd, int pin, int mode) {
    struct gpiohandle_request req = {
        .lineoffsets[0] = pin,
        .flags          = mode,
        .lines          = 1
    };

    int res = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);

    if (res < 0) {
        return res;
    }

    return req.fd;
}

static void pin_set(int fd, uint8_t val) {
    ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &val);
}

static void sigurg_handler(void) {
    struct gpioevent_data event;
    read(fd_in, &event, sizeof(event));

    if (event.id == GPIOEVENT_EVENT_RISING_EDGE) {
        puts("rising");
    }
    if (event.id == GPIOEVENT_EVENT_FALLING_EDGE) {
        puts("falling");
    }
}

static void signal_handler(int signal) {
    printf("got signal: %x\n", signal);

    if (signal == SIGURG) {
        sigurg_handler();
    }
}

static void register_signal(int signal, void (*handler)(int)) {
    struct sigaction sa = {
        .sa_handler = handler
    };

    sigaction(signal, &sa, NULL);
}

/* calling poll() on the fd works as expected */
static void _do_poll(int fd) {

    struct pollfd pfd = {
        .fd = fd,
        .events = POLLIN | POLLPRI
    };

    if (poll(&pfd, 1, 1000) > 0) {
        sigurg_handler();
    }
}

int main(void) {

    int pins[2];

    int fd = open("/dev/gpiochip0", O_RDWR);
    pins[0] = setup_pin_int(fd, PIN_IN, GPIOHANDLE_REQUEST_INPUT, GPIOEVENT_REQUEST_BOTH_EDGES);
    pins[1] = setup_pin_out(fd, PIN_OUT, GPIOHANDLE_REQUEST_OUTPUT);

    fd_in = pins[0];

    /* register signal handler */
    register_signal(SIGIO, signal_handler);
    register_signal(SIGURG, signal_handler);

    /* make the fd generate signals */
    fcntl(fd_in, F_SETOWN, getpid());
    fcntl(fd_in, F_SETSIG, SIGURG);
    fcntl(fd_in, F_SETFL, O_NONBLOCK | O_ASYNC);

    /* toggle the output pin each second */
    int state;
    while (1) {
        state = !state;
        sleep(1);

        printf("set %d\n", state);
        pin_set(pins[1], state);

         /* poll() is working on the fd */
//        _do_poll(fd_in);
    }

    close(fd);

    return 0;
}

running on the Raspi with a jumper bridging GPIO20 and GPIO26.

You will find that no signals are generated even though AFAIU those should be enabled by fcntl(fd_in, F_SETOWN, getpid()); (fcntl(fd_in, F_SETSIG, SIGURG); should not be necessary, but I got desperate)

So the conclusion would be that this is an omission by the Linux kernel, a conclusion that @bergzand had already reached before and thus implemented this solution where poll() is used in a separate thread and that thread then is used to generate the signal.

@aabadie
Copy link
Contributor

aabadie commented Jun 2, 2020

I tested on Linux and tests/gnrc_networking works: I'm able to ping between 2 instances. @bergzand , since you wrote the code this is inspired from, maybe you can have a look ? @fjmolinas IIRC, you have a Mac. Mind to try on it ?

@aabadie aabadie self-assigned this Jun 2, 2020
@fjmolinas
Copy link
Contributor

I tested on Linux and tests/gnrc_networking works: I'm able to ping between 2 instances. @bergzand , since you wrote the code this is inspired from, maybe you can have a look ? @fjmolinas IIRC, you have a Mac. Mind to try on it ?

I don't really have a working OSX setup. But does native even work on OSX, there is no 32bit support AFAIK? How would I test this? Build in docker?

@janschlicht
Copy link

I was send here from #12451 since I seem to be one of the few Mac users here.
I tried compiling but it fails right away with

make
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
/Users/schlicht/Desktop/riot/RIOT/makefiles/toolchain/llvm.inc.mk:25: objcopy not found. Hex file will not be created.
Building application "gnrc_networking" for "native" with MCU "native".
sed: 1: "1i /* Generated file do ...": command i expects \ followed by text
make: *** [/Users/schlicht/Desktop/riot/RIOT/examples/gnrc_networking/../../Makefile.include:821: /Users/schlicht/Desktop/riot/RIOT/examples/gnrc_networking/bin/native/riotbuild/riotbuild.h] Error 1

I had this problem before and it was fixed a few month ago.
I rebased on the most recent commit to bypass this error but it then fails due to the lack of 32bit support.

ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

So I don't think testing this is possible right now.

@miri64
Copy link
Member

miri64 commented Jun 28, 2020

So this only happens on linking, but there are no issues on compilation? This might hint, that poll() might actually work?

@miri64
Copy link
Member

miri64 commented Jun 28, 2020

We could try to run it on FreeBSD as a gauge. I think that was Ludwig's free replacement for OSX back when he introduced native.

@miri64
Copy link
Member

miri64 commented Jun 28, 2020

Thanks for testing @janschlicht by the way! ❤️

@fjmolinas
Copy link
Contributor

fjmolinas commented Jun 30, 2020

I finally got a setup working where I was able to compile, by luck I have OSXMojave, and when I downgraded xcode I was able to compile. No luck pinging between native instances though, neither on master or this PR, I then came across this #12836 (comment). So IMO not worth to keep investing time in this since native seems unusable in OSX. I would ACK and merge this unless @miri64 would rather not get this in during soft feature freeze.

@miri64
Copy link
Member

miri64 commented Jun 30, 2020

Let's wait with this until feature freeze.

Copy link
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

We are not in feature freeze anymore and since native is broken OSX because of 64bit, it's not possible to say that this PR is breaking something there. There's a consensus with @fjmolinas to merge this one (as it works on Linux anyway) and to see if it had an impact on OSX when we have 64 bit support for native.

Let's move forward, ACK!

@aabadie
Copy link
Contributor

aabadie commented Jul 21, 2020

@benpicco, also please rebase to trigger the new GH actions checks!

bergzand added 2 commits July 21, 2020 11:50
select() can not listen to POLLPRI events which are used by the
Kernel's GPIO API.

In preparation for that, rewrite async_read() to use poll() instead
of select().
fcntl(fd, F_SETOWN, getpid()); doesn't seem to work on Linux
to get generate a signal when an event on the GPIO fd occurs.

So fall back to the same method as on OS X and call poll() in
a child process.
@aabadie aabadie merged commit b7219d6 into RIOT-OS:master Jul 21, 2020
@benpicco benpicco deleted the native_poll branch July 21, 2020 12:02
@miri64
Copy link
Member

miri64 commented Jul 27, 2020

netdev_tap still works on FreeBSD with this merged.

@miri64
Copy link
Member

miri64 commented Jul 28, 2020

Something broke reboot here #14636

#ifdef __MACH__
kill(_sigio_child_pids[i], SIGKILL);
#endif
real_close(_fds[i]);
Copy link
Contributor Author

@benpicco benpicco Jul 28, 2020

Choose a reason for hiding this comment

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

That's the culprit.
We need to add this again with the new code - #14638

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: native Platform: This PR/issue effects the native platform Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants