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 yourstruct platform_device
) forsysfs_create_group()
(instead of creating your ownkobject
) - use
DEVICE_ATTR()
to declarestruct device_attribute
instead of regular__ATTR()
, which createsstruct kobj_attribute
.
Here is how I created sysfs attributes for my platform driver.
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;
};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);
...
}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;
}Create device attribute for those functions (right after those functions):
static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
mydrv_myparam_store);Declare attributes table (listing in fact sysfs files for you driver):
static struct attribute *mydrv_attrs[] = {
&dev_attr_myparam.attr,
NULL
};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);
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;
}
...
}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
Bash: Get List of Commands Starting with a Given String
Determine Tsc Frequency on Linux
Find and Delete Files with Non-Ascii Names
What's the Best Way to Find a String/Regex Match in Files Recursively? (Unix)
Finding Original MAC Address from Hardware Itself
Get a Nanosecond-Precise Atime, Mtime, Ctime Fields for File (Stat)
How to Print Message to Stderr in Go
Creating a Self-Extracting Zip Archive on a Linux Box
How Are Percpu Pointers Implemented in the Linux Kernel
How to Read a Sector Using a Bio Request in Linux Kernel
Hook into Linux Key Event Handling
What Is Kernel Section Mismatch
How to Force Node.Js Require to Be Case Sensitive