clinux-kernelsysfs

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


My kernel module for raspi, written to get a double light barrier, reads monotonic time if each of two input gpio´s edges are rising and an interrupt occours. The time value is passed via normal sysfs files to userspace(not dev files). This works fine.

My userspace implementation should read these values and calculate the resulting speed of an object passing the light barrier. This is currently solved by a periodic read on the sysfs files. If the value changes, the new values are picked up and used.

To save ressources, I want to poll() for a POLLPRI on the releated sysfs files. Inside the kernel module should a sysfs_notify() provide the changed state and value of releated sysfs file to userspace.

But my poll() in userspace blocks permanently. I tried also POLLIN as event. The result was a immediate return of the poll() function.

Here is my 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)
{
    sysfs_notify(gpio,NULL,"gpio26n"); // Is this the right place, the right invocation????
    // I saw this already in isr, but i believe this is not a good idea....
    // Has it perhaps to be in module_init function???
    // First arg('gpio') is kobject of the dir containing files(attributes)..
    // This can not be right in my mind, but how do I find the right kobj??
    // The third param 'gpio26n' if the file, on which I'm polling.

    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;
    }

    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) {
// [...]

And my userspace app

// [...]

int main() {

    int           f      ;
    char          buf[16];
    struct pollfd pfd    ;

    while(1) {
        memset(buf,0,sizeof(buf));

        if((f=open("/sys/kernel/gpio/gpio26n",O_RDONLY)) <0) {
            fprintf(stderr,"Failed to open sysfs file\n");
            exit(1);
        }

        if((lseek(f,0L,SEEK_SET)) <0) {
            fprintf(stderr,"Failed to set pointer\n");
            exit(2);
        }


        if((read(f,buf,1)) <0) {
            fprintf(stderr,"Failed to read from file\n");
            exit(3);
        }

        pfd.fd     = f      ;
        pfd.events = POLLPRI;

        poll(&pfd,1,-1); // This should block until value has changed....
        close(f);


        // fopen, read new value etc.
        // [...]

        // Do some stuff, calculate speed, etc
    }
}

Greetings pfau


Solution

  • 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