linuxlinux-kernellinux-device-driverembedded-linuxinterrupt

Is it correct to use Linux userspace interrupts with non-blocking reads?


As part as an embedded project in a Zynq SoC, I was following this guide to create a userspace interrupt in Linux: https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt.html

I was successful in getting everything up and running. In summary I was able to create a userspace device, generate interrupts from it, and read back the interrupts from my application program running on the target (written in C).

This is quite different from writing an ISR in a bare metal application. In the bare metal application I was able to create an interrupt handler and then connect that to the GIC and the interrupt ID. Then my program could do whatever, for example, it could be in an infinite while loop printing some strings and whenever that interrupt occurred, the interrupt handler code would get executed before going back to the while loop.

With the Linux application, as the guide showed, I am using a blocking read inside a while loop:

        nb = read(fd, &info, sizeof(info));
        if (nb == (ssize_t)sizeof(info)) {
            /* Do something in response to the interrupt. */
            printf("Interrupt #%u!\n", info);
        }

This seems like it defeats the purpose of using an interrupt, I need to constantly check if an interrupt has arrived and cannot do anything else in my program. To me, this seems like a flaw with userspace interrupt handling which I doubt was not thought about.

So my question is: what am I missing here? And if I am thinking about it in the wrong way, what is the correct way to make efficient use of userspace interrupts as a more traditional interrupts that do not require constant polling?

I am currently using the blocking read code inside a while loop, which gives me the functionality I am looking for regarding to this specific interrupt handling. However, this is not an ideal solution because I am stuck always polling for the interrupt. There are other functions available like select() and poll(), but they still do not solve my question.


Solution

  • I was successful in getting everything up and running. In summary I was able to create a userspace device, generate interrupts from it, and read back the interrupts from my application program running on the target (written in C).

    That sounds like what a UIO approach, such as the linked documentation describes, is intended to do. The point is to have most of a device driver running in userspace, instead of in the kernel. It is not intended for asynchronously signaling a userspace program that focuses on something else.

    According to the docs:

    If you use UIO for your card’s driver, here’s what you get:

    • only one small kernel module to write and maintain.

    • develop the main part of your driver in user space, with all the tools and libraries you’re used to.

    • bugs in your driver won’t crash the kernel.

    • updates of your driver can take place without recompiling the kernel.

    Note in particular that there is still a kernel module needed, and that this is what receives hardware interrupts. The docs you are referencing rely on an existing generic-IRQ driver. That is what receives hardware interrupts.

    This is quite different from writing an ISR in a bare metal application.

    Yes, it is. Userspace cannot talk directly to the hardware or receive hardware interrupts. That's not what UIO is about. There are other ways that the UIO interface could have been designed, but the chosen way is file-based, which is natural and fairly ubiquitous on Unix and Linux.

    what am I missing here? And if I am thinking about it in the wrong way, what is the correct way to make efficient use of userspace interrupts as a more traditional interrupts that do not require constant polling?

    You have altogether the wrong idea, starting with using the term "userspace interrupt". There actually is such a thing -- a pretty new userspace asynchronous IPC mechanism that bypasses the kernel -- but that's not suitable for interacting with hardware, and what your docs describe is not much like it. What you're looking at should be characterized as a GPIO driver that does most of its processing in userspace, relying on an existing generic driver in the kernel. The userspace portion that you write is downstream from the interrupts. This will be characteristic of any userspace software that responds to GPIO events, regardless of how that software is notified.

    The primary userspace mechanisms for asynchronous processing are

    There are system interfaces that allow a process to request notification via a signal when certain events occur. AIO and timers, for example. I don't think the system provides such a feature for GPIO, however.

    If you want a userspace program that responds to GPIO events in the context of some broader piece of work, then I suggest that you put the GPIO handling in a separate thread. You can use the approach you already have, blocking reads and all. Use mutex-protected shared variables to convey the effects to other threads in the program without them polling or blocking.