Using the Linux sysfs_notify call
Asked Answered
T

1

12

I am trying to communicate asynchronously between a kernel driver and a user-space program (I know there are lots of questions here that ask for similar information, but I could find none that deal with sysfs_notify).

I am leaving Vilhelm's edit here, but adding the source to both a simple driver utilizing sysfs and a user-space program to poll it. The driver works fine (I got most of it from the net; it is missing the credits, but I couldn't find them when I went back to add them). Unfortunately, the polling program does not work. It always returns success immediately. Interestingly, if I don't perform the two reads prior to the poll, the revents members are set to POLLERR | POLLIN instead of just POLLIN as seen in the program output.

Program output:

root@ubuntu:/home/wmulcahy/demo# ./readhello
triggered
Attribute file value: 74 (t) [0]
revents[0]: 00000001
revents[1]: 00000001

Here is the driver: hello.c (you can see where I started out...)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>

struct my_attr {
    struct attribute attr;
    int value;
};

static struct my_attr notify = {
    .attr.name="notify",
    .attr.mode = 0644,
    .value = 0,
};

static struct my_attr trigger = {
    .attr.name="trigger",
    .attr.mode = 0644,
    .value = 0,
};

static struct attribute * myattr[] = {
    &notify.attr,
    &trigger.attr,
    NULL
};

static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct my_attr *a = container_of(attr, struct my_attr, attr);
    printk( "hello: show called (%s)\n", a->attr.name );
    return scnprintf(buf, PAGE_SIZE, "%s: %d\n", a->attr.name, a->value);
}
static struct kobject *mykobj;

static ssize_t store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
{
    struct my_attr *a = container_of(attr, struct my_attr, attr);

    sscanf(buf, "%d", &a->value);
    notify.value = a->value;
    printk("sysfs_notify store %s = %d\n", a->attr.name, a->value);
    sysfs_notify(mykobj, NULL, "notify");
    return sizeof(int);
}

static struct sysfs_ops myops = {
    .show = show,
    .store = store,
};

static struct kobj_type mytype = {
    .sysfs_ops = &myops,
    .default_attrs = myattr,
};

static struct kobject *mykobj;
static int __init hello_module_init(void)
{
    int err = -1;
    printk("Hello: init\n");
    mykobj = kzalloc(sizeof(*mykobj), GFP_KERNEL);
    if (mykobj) {
        kobject_init(mykobj, &mytype);
        if (kobject_add(mykobj, NULL, "%s", "hello")) {
             err = -1;
             printk("Hello: kobject_add() failed\n");
             kobject_put(mykobj);
             mykobj = NULL;
        }
        err = 0;
    }
    return err;
}

static void __exit hello_module_exit(void)
{
    if (mykobj) {
        kobject_put(mykobj);
        kfree(mykobj);
    }
    printk("Hello: exit\n");
}

module_init(hello_module_init);
module_exit(hello_module_exit);
MODULE_LICENSE("GPL");

And here is the poll program: readhello.c

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h> 
#include <sys/stat.h> 
#include <poll.h>

#define TEST_SYSFS_TRIGGER  "/sys/hello/trigger"
#define TEST_SYSFS_NOTIFY   "/sys/hello/notify"

int main(int argc, char **argv)
{
    int cnt, notifyFd, triggerFd, rv;
    char attrData[100];
    struct pollfd ufds[2];

    // Open a connection to the attribute file.
    if ((notifyFd = open(TEST_SYSFS_NOTIFY, O_RDWR)) < 0)
    {
        perror("Unable to open notify");
        exit(1);
    }
    // Open a connection to the attribute file.
    if ((triggerFd = open(TEST_SYSFS_TRIGGER, O_RDWR)) < 0)
    {
        perror("Unable to open trigger");
        exit(1);
    }

    ufds[0].fd = notifyFd;
    ufds[0].events = POLLIN;
    ufds[1].fd = triggerFd;
    ufds[1].events = POLLIN;

    // Someone suggested dummy reads before the poll() call
    cnt = read( notifyFd, attrData, 100 );
    cnt = read( triggerFd, attrData, 100 );
    ufds[0].revents = 0;
    ufds[1].revents = 0;
    if (( rv = poll( ufds, 2, 10000)) < 0 )
    {
        perror("poll error");
    }
    else if (rv == 0)
    {
        printf("Timeout occurred!\n");
    }
    else if (ufds[0].revents & POLLIN)
    {
        printf("triggered\n");
        cnt = read( notifyFd, attrData, 1 );
        printf( "Attribute file value: %02X (%c) [%d]\n", attrData[0], attrData[0], cnt );
    }
    printf( "revents[0]: %08X\n", ufds[0].revents );
    printf( "revents[1]: %08X\n", ufds[1].revents );

    close( triggerFd );
    close( notifyFd );
}

Some upcoming sysfs enhancements.

Internally, the patch adds a wait queue head to every kobject on the system; that queue is inserted into a poll table in response to a poll() call. The sysfs code has no way of knowing, however, when the value of any given sysfs attribute has changed, so the subsystem implementing a pollable attribute must make explicit calls to:

void sysfs_notify(struct kobject *kobj, char *dir, char *attr);

Thanks, Lee

Tessin answered 3/5, 2013 at 21:11 Comment(0)
A
14

The blocking poll is from the user side. User code can simply tell the kernel which attributes it's interested in, then block in a poll() until one of them has changed.

The sysfs_notify() is a kernel-side call that releases the user-space poll(). After you adjust your kernel attribute value, just call sysfs_notify() to allow any user-space applications to respond to their outstanding poll().

Think of the poll() as "subscribing" to notices of a change in an attribute of interest, and sysfs_notify() as "publishing" the change to any subscribers.

Avidity answered 6/5, 2013 at 17:54 Comment(6)
Isn't sysfs_poll automatically called for sysfs attributes?Lawlor
Yes, user-space poll() connects through the VFS and eventually to sysfs_poll(). That's the side that's waiting for changes. The side that produces the changes calls sysfs_notify() to wake up an active sysfs_poll().Avidity
Thank you both for your edit and answer. I had already seen that reference, Vilhelm, and understood what you are saying, Peter. I am going to edit the question to change its focus. I have some code examples both culled from the internet and written by myself that don't work. Maybe you can take a look.Tessin
The LWN document you link also says "With such attributes, user space need only open the attribute of interest pass it to poll() with the POLLERR and POLLPRI events selected." Try ufds[0].events = POLLPRI|POLLERR; instead of POLLINAvidity
As a side note: you might only need to pass POLLPRI since POLLERR is ignored in the events member according to the POSIX standard.Lawlor
Peter's suggestion was the answer, thanks! I checked the sysfs_poll() code and it returns POLLERR|POLLPRI when triggered. Seems odd, but it works. YAY! Vihelm is also correct - no reason to look at POLLERR at all unless the poll() function itself can add that flag to the return code.Tessin

© 2022 - 2024 — McMap. All rights reserved.