non-root access to GPIO timing problem during systemd startup

Hi all,

We have been working on a Python application that uses Adafruits GPIO library.
We are using Systemd to start our program, and Systemd is set to execute as a user which is a member of the GPIO group.
Now, the problem is when our program starts to early in the boot process, we get a permission denied
trying to setup the GPIO pin mode (input/output).
If we delay program startup long enough, some service or udev rule will eventually get arround to setting the permission, and our program is able to control GPIO’s just fine.

Looking into the /opt/scripts/boot/generic-startup.sh, reveals that some group permissions for GPIO are applied here:

echo “generic-board-startup: gpio”
if [ -d /sys/class/gpio/ ] ; then
/bin/chgrp -R gpio /sys/class/gpio/ || true
/bin/chmod -R g=u /sys/class/gpio/ || true

    /bin/chgrp -R gpio /dev/gpiochip* || true
    /bin/chmod -R g=u /dev/gpiochip* || true

fi

So we decide to start our programs after generic_startup.sh
This however was not enough to set a GPIO input modes ! so we had to add into generic-startup.sh

/bin/chgrp -R gpio /sys/devices/platform/ocp/ocp* || true
/bin/chmod -R g=u /sys/devices/platform/ocp/ocp* || true
/bin/chgrp -R gpio /sys/devices/platform/ocp/*.gpio || true
/bin/chmod -R g=u /sys/devices/platform/ocp/*.gpio || true

Now this works !
But my question is, these permission is somehow set by another service later on in the Systemd startup process, but we have not been able to pinpoint when or where ?

Can anybody here shed some light on the sequence in which permissions are being applied ?

Thank you.

Best regards

Brian

Hello,

I think most of the permission attributes are set in /etc/udev/rules.d/*.

Seth

P.S. You can search that dir. and all the files in it, specifically gpio, to figure out how things are situated on the filesystem in Linux for the beagleboard.org images. I am not sure about the sequence of what boots first in the image you are using.

Hi Seth,

Thanks, we have been looking at the rules.d scripts.
As the GPIO’s are “created” by the device tree, my guess is that the udev rules are not triggered since no new hardware has been detected, and that must be why setting permissions are done in the generic-startup script.
I might be wrong ?

Brian

We’ve mostly moved away from setting that in generic-startup as the udev rules seem to be pretty stable now…

Check on your version of:

debian@BeagleBone:~/$ dpkg -l | grep bb-customizations
ii  bb-customizations                          1.20220913.0-0~bullseye+20220913              armhf        beagleboard.org customizations

Regards,

Thanks Robert,

dpkg -l | grep bb-customizations
ii bb-customizations 1.20200306.0-0rcnee0~buster+20200306 all beagleboard.org customizations

It seems 80-gpio-noroot.rules and 82-gpio-config-pin.rules have been modified compared
to what we have.

Can you tell me when these rules are applied ? early in the boot process or late ?

sudo apt update ; sudo apt ugprade

When ever udev runs…

Edit, small correction… since udev is part of systemd, it becomes blurry…

Regards,

@BRJ ,

That is a good question. I have been thinking about it for some time. I have not found the answer yet.

I looked in the man pages and at some other sites for udev starting on systemd.

Seth

P.S. If you find it before me, you win!

https://www.freedesktop.org/wiki/Software/systemd/FrequentlyAskedQuestions/

This is what I found. There is a command further down the page on that link of freedesktop.org on their wiki page of FAQ.

Seth

P.S. Also: TipsAndTricks has some ideas. It seems it is not so easy to establish what is booted and when unless you can figure it out. Right. Right. Right.

$ systemd-cgls and man, not the man pages, it just keeps getting more difficult. I found a page that may interest you for starting .service files after boot is done: NetworkTarget .

I cannot find a way to figure out how to account for the boot (YET).

@silver2row ,

Robert is right, it becomes very blurry to determine when udev rules are applied.

Enabling udev_log=debug in /etc/udev/udev.conf, and analyzing journald or syslog,
does show a lot of information (udev is very busy…)

Early in the boot process systemd-udev-trigger.service is executed, it’s my understanding that this will make udev rules apply to already existing devices (such as GPIOs). I imagine it traverses the device tree, and this takes time !

In our case, from udev detecting/queuing the first GPIO entry until the last GPIO udev rule has been applied, takes arround 50s.

It also seems like udev has a maximum queue size, which also could influence processing time.
this line is seen throughout syslog, many times:
systemd-udevd[821]: Maximum number (10) of children reached.

I have not found a way, to determine when udev is done traversing all installed hardware.

Our objective is to make sure that our services are able to start correctly, when being started by systemd.
Currently we added force setting GPIO permissions manually, in a bash script (similar to generic-board.startup), and have our other services start hereafter.

Udev will get arround to setting the same permissions, but only a bit later …

Brian

1 Like

Hello @BRJ ,

Seth here. Um, he is usually right for whatever reason. Outside of that idea, are you building your own image or relying on the beagleboard.org images?

I ask b/c there are many ideas so far that help people enable a quicker boot time on the am335x boards like the BBB, BBG, and so on.

I cannot remember the actual post. It is too bad I did not doc. it.

Seth

P.S. There may even be an older couple of ideas on the blog. Let me link you to the blog after I review the content. It may help in a quick boot time if this can help you start your services quicker b/c of the lack of overhead and strong, heavy repos: Beagle Board Blog | Learn More about the lastes News and content will show some ideas for using u-boot to handle booting into Linux and the distro w/ the required .dtbo file or .dtb depending on the actual use case.

Also…I think the building of the Linux kernel, u-boot, and the required tools can be a form of building and booting into Linux in a faster way: https://bootlin.com/pub/conferences/2021/lee/maincent-devicetree-overlay-and-uboot-extension-board-management/maincent-devicetree-overlay-and-uboot-extension-board-management.pdf . Also, I could not find right away what it is that I am actually describing.

Anyway, they were saying scratch to 45 seconds to boot. Something like that idea. But, if found, one could switch out the toolchain, u-boot versioning, and kernel used. Just an idea.

I would poll the gpio files you need to access to check for ownership when your program runs. Then as soon as the ownership is set, you can continue running your software.

Probably add some sort of timeout to log an error and quit if the ownership doesn’t change within a given time.

In Python if you use pathlib you can get the owner and group of a file.

from pathlib import Path

path = Path("/path/to/your/file")
owner = path.owner()
group = path.group()

just check for group to equal ‘gpio’

I would not poll too quickly, every couple of seconds. Doing this allows for any random delays when booting.

1 Like

Hi @silver2row ,

At the moment we are using the official IOT image.
We did think about going the Yocto way, but time was critical, so we decided to focus on our application
rather than building everything from scratch at this point.

At the moment our boot time is app. 1min, which is I believe to be expected.

Thank you for your inputs.

Brian

1 Like

Hello @benedict.hewson,

Thanks we did think about doing what you describe, but felt it was better to force permissions to get a quicker startup time.

Brian

Im sure there are nuances to your case that I’m not aware of, but it would be pretty pythonic to simply handle the IO exception gracefully in the app, and retry accessing the gpio fd until the stat call is successful.
You could also consider a pre-start routine in your unit file to wait for permissions to change before starting your service. Something like
ExecStartPre=/bin/bash -c '(while ! sudo -u gpio_user test -w /gpio/dir/or/file; do echo "Waiting for GPIO permissions update..."; sleep 2; done); sleep 1'

SLR-