Using the Linux Sysfs_Notify Call

Using the Linux sysfs_notify call

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.

poll() from userspace on a sysfs file and sysfs_notify() from kernelspace won´t work tegether

I was wrong on placing the sysfs_notify() function.

The right place is in this case within ISR.

When an interrupt occures, the ISR is called and notifies userspace via sysfs_notify(), that new data are readable. poll() unblocks and the data are taken.

In my prior constellation the userspace poll() blocked until kobj_attribute show function is called. But this function is only called, if data are read from the file. This means userspace app was waiting for kernel module and visa verce.

Now its working fine.

Here my edited code:

// [...]

static struct kobject *gpio; // dir /sys/kernel/gpio

static ssize_t gpio17s_show(struct kobject *kobj,struct kobj_attribute
*attr,char *buf)
{
return sprintf(buf,"%li",sec1);
}

static ssize_t gpio17s_store(struct kobject *kobj,struct kobj_attribute
*attr,const char *buf,size_t count)
{
sscanf(buf,"%li",&sec1);
return count;
}

}

// [...] (two more files)

static ssize_t gpio26n_show(struct kobject *kobj,struct kobj_attribute
*attr,char *buf)
{
return sprintf(buf,"%li",nsec2);
}

static ssize_t gpio26n_store(struct kobject *kobj,struct kobj_attribute
*attr,const char *buf,size_t count)
{
sscanf(buf,"%li",&nsec2);
return count;
}

static int cleanup(int value,int ret) {

switch(value) {
case 5:
free_irq(speed_irq2,NULL);
case 4:
free_irq(speed_irq1,NULL);
case 3:
gpio_free(PIN2);
case 2:
gpio_free(PIN1);
case 1:
kobject_put(gpio);
}

return ret;
}

static irqreturn_t speed_isr(int irq, void *data) {

if(irq==speed_irq1) {
getrawmonotonic(&ts1);
sec1=ts1.tv_sec;
nsec1=ts1.tv_nsec;
}

if(irq==speed_irq2) {
getrawmonotonic(&ts2);
sec2=ts2.tv_sec;
nsec2=ts2.tv_nsec;

sysfs_notify(gpio,NULL,"gpio26n"); // !! HERE IS THE RIGHT PLACE !!
}

return IRQ_HANDLED;
}

static struct kobj_attribute gpio17s_attr = __ATTR(gpio17s,0644,
gpio17s_show,gpio17s_store);
static struct kobj_attribute gpio26n_attr = __ATTR(gpio26n,0644,
gpio26n_show,gpio26n_store);

static int __init d_init(void) {

int ret=0;

printk(KERN_INFO "Module successfully loaded...");

gpio=kobject_create_and_add("gpio",kernel_kobj);
if(!gpio) {
printk(KERN_ERR "Failed to create 'gpio'");
return -ENOMEM;
}

ret=sysfs_create_file(gpio,&gpio17s_attr.attr);
if(ret) {
printk(KERN_ERR "Failed to create file 'gpio17s'");
return cleanup(1,2);
}

// [...] (two more files)

ret=sysfs_create_file(gpio,&gpio26n_attr.attr);
if(ret) {
printk(KERN_ERR "Failed to create file 'gpio26n'");
return cleanup(1,5);
}

ret=gpio_request(PIN1,"gpio 17");
if(ret) {
printk(KERN_ERR "Failed to request 'gpio17'");
return cleanup(1,6);
}

ret=gpio_request(PIN2,"gpio 26");
if(ret) {
printk(KERN_ERR "Failed to request 'gpio26'");
return cleanup(2,7);
}

ret=gpio_to_irq(PIN1);
if(ret<0) {
printk(KERN_ERR "Unable to get irq for pin 17");
return cleanup(3,8);
}

speed_irq1=ret;

ret=gpio_to_irq(PIN2);
if(ret<0) {
printk(KERN_ERR "Unable to get irq for pin 26");
return cleanup(3,9);
}

speed_irq2=ret;

ret=request_irq(speed_irq1,speed_isr,IRQF_TRIGGER_RISING,
"Speed trigger 17",NULL);
if(ret) {
printk(KERN_ERR "Unable to request irq for pin 17");
return cleanup(3,10);
}

ret=request_irq(speed_irq2,speed_isr,IRQF_TRIGGER_RISING,
"Speed trigger 26",NULL);
if(ret) {
printk(KERN_ERR "Unable to request irq for pin 26");
return cleanup(4,10);
}

return 0;
}

static void __exit d_exit(void) {
// [...]

Greetings pfau

Why doesn't this call to `poll` block correctly on a sysfs device attribute file?

To quote some more from the comment you quoted:

Once poll/select indicates that the value has changed, you
need to close and re-open the file, or seek to 0 and read again.

But you do nothing with fds.fd.

Also, do a dummy read() before calling poll();
any newly opened file is considered changed.

How to attach file operations to sysfs attribute in platform driver?

It boils down to next:

  • reuse existing kobject from struct device (from your struct platform_device) for sysfs_create_group() (instead of creating your own kobject)
  • use DEVICE_ATTR() to declare struct device_attribute instead of regular __ATTR(), which creates struct kobj_attribute.

Here is how I created sysfs attributes for my platform driver.

  1. Create structure you'll be using as private data in show() / store() operations for your sysfs attribute (file). For example:

    struct mydrv {
    struct device *dev;
    long myparam;
    };
  2. Allocate this structure in your driver's probe():

    static int mydrv_probe(struct platform_device *pdev)
    {
    struct mydrv *mydrv;

    mydrv = devm_kzalloc(&pdev->dev, sizeof(*mydrv), GFP_KERNEL);
    mydrv->dev = &pdev->dev;
    platform_set_drvdata(pdev, mydrv);

    ...
    }
  3. Create show() / store() functions:

    static ssize_t mydrv_myparam_show(struct device *dev,
    struct device_attribute *attr, char *buf)
    {
    struct mydrv *mydrv = dev_get_drvdata(dev);
    int len;

    len = sprintf(buf, "%d\n", mydrv->myparam);
    if (len <= 0)
    dev_err(dev, "mydrv: Invalid sprintf len: %d\n", len);

    return len;
    }

    static ssize_t mydrv_myparam_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
    {
    struct mydrv *mydrv = dev_get_drvdata(dev);

    kstrtol(buf, 10, &mydrv->myparam);
    return count;
    }
  4. Create device attribute for those functions (right after those functions):

    static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
    mydrv_myparam_store);
  5. Declare attributes table (listing in fact sysfs files for you driver):

    static struct attribute *mydrv_attrs[] = {
    &dev_attr_myparam.attr,
    NULL
    };
  6. Declare attribute group (specifying in fact sysfs directory for your driver):

    static struct attribute_group mydrv_group = {
    .name = "mydrv",
    .attrs = mydrv_attrs,
    };

    static struct attribute_group *mydrv_groups[] = {
    &mydrv_group,
    NULL
    }

    which can be actually replaced with one line:

    ATTRIBUTE_GROUPS(mydrv);
  7. Create sysfs directory and files in your driver's probe() function:

    static int mydrv_probe(struct platform_device *pdev)
    {
    int ret;

    ...

    ret = sysfs_create_group(&pdev->dev.kobj, &mydrv_group);
    if (ret) {
    dev_err(&pdev->dev, "sysfs creation failed\n");
    return ret;
    }

    ...
    }
  8. Remove your sysfs files in your driver's remove() function:

    static int mydrv_remove(struct platform_device *pdev)
    {
    sysfs_remove_group(&pdev->dev.kobj, &mydrv_group);
    ...
    }

Race condition note

As @FranzForstmayr correctly pointed out, there may be race condition when adding sysfs files with sysfs_create_group() in mydrv_probe(). That's because user-space can be already notified that those files exist before mydrv_probe() called (where those files are actually being created by sysfs_create_group() function). This issue covered in details in "How to Create a sysfs File Correctly" article by Greg Kroah-Hartman.

So in our case of platform_device, instead of calling sysfs_create_group() (and its counterpart sysfs_remove_group()), you can use default attribute group. To do so, you need to assign corresponding .groups field of your struct device to your attribute groups variable:

static int mydrv_probe(struct platform_device *pdev)
{
...

pdev->dev.groups = mydrv_groups;

...
}

DISCLAIMER: I didn't test this code, though it should work, because of this code.

See [1,2,3] links for more insights on mentioned race condition.

For more examples, run next command in kernel source directory:

$ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/

Also you can search by "default attribute" in commit messages:

$ git log --no-merges --oneline --grep="default attribute" -- drivers/

Some commits I found this way: [4,5,6,7].

References

[1] My attributes are way too racy, what should I do?

[2] PATCH: sysfs: add devm_sysfs_create_group() and friends

[3] [GIT PATCH] Driver core patches for 3.11-rc2

[4] commit 1

[5] commit 2

[6] commit 3

[7] commit 4



Related Topics



Leave a reply



Submit