summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Anderson <dianders@chromium.org>2022-10-18 10:37:11 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-10-21 23:02:41 +0000
commit9f3c4fb24e31d39c13a92f2e05ef80bac8e73206 (patch)
tree0021ffd66da6cad915dc8a127c127268ed923874
parent0ca7a9e4dad2e9780690524ced9273fa07052179 (diff)
downloadvboot-9f3c4fb24e31d39c13a92f2e05ef80bac8e73206.tar.gz
crossystem: arm: Retry if we fail to read a GPIO
It turns out that if two processes try to read the same GPIO at the same time that one of them will fail because the GPIO is "busy". This is really by design of the kernel's GPIO API. In order to read a value, each userspace process "requests" control of the GPIO and then queries it. There doesn't appear to be any way to "wait" for a GPIO that's been requested by someone else--we just need to wait a bit and try again later. ...so that's what we'll do. Without this patch, if you run the following script on a write-protected sc7180-trogdor class device in two shells at the same time: old_val="" while true; do val=$(crossystem wpsw_cur) if [[ "${val}" != "${old_val}" ]]; then echo "$(date): ${old_val} => ${val}" old_val="${val}" fi done Then you'll see stuff like this: GPIO_GET_LINEHANDLE_IOCTL: Device or resource busy Tue Oct 18 11:34:01 PDT 2022: 1 => 0 Tue Oct 18 11:34:01 PDT 2022: 0 => 1 GPIO_GET_LINEHANDLE_IOCTL: Device or resource busy Tue Oct 18 11:34:01 PDT 2022: 1 => 0 Tue Oct 18 11:34:01 PDT 2022: 0 => 1 The 0 actually comes from the fact that crossystem falls back to `GetVdatInt(VDAT_INT_HW_WPSW_BOOT)` if it fails to read the GPIO and that value isn't initted to anything on trogdor (VDAT_INT_HW_WPSW_BOOT is deprecated and not populated on trogdor). It is postulated that the above problem is causing some parts of the system to get confused about the write protect state of devices. BRANCH=none BUG=b:249498455 TEST=Run script in CL commit message and see no errors Change-Id: I307cdb4e290c27694690a19af60f4697ee0233e4 Signed-off-by: Douglas Anderson <dianders@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3963985 Reviewed-by: Brian Norris <briannorris@chromium.org> Reviewed-by: Julius Werner <jwerner@chromium.org> Commit-Queue: Julius Werner <jwerner@chromium.org> (cherry picked from commit 4ca43a34f5a936683ebcdc7b0edd059f313f61be) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3971702
-rw-r--r--host/arch/arm/lib/crossystem_arch.c23
1 files changed, 22 insertions, 1 deletions
diff --git a/host/arch/arm/lib/crossystem_arch.c b/host/arch/arm/lib/crossystem_arch.c
index 700622f6..79fb152e 100644
--- a/host/arch/arm/lib/crossystem_arch.c
+++ b/host/arch/arm/lib/crossystem_arch.c
@@ -18,6 +18,7 @@
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
@@ -199,13 +200,33 @@ static int gpioline_read_value(int chip_fd, int idx, bool active_low)
.lines = 1,
};
struct gpiohandle_data data;
+ int trynum;
int ret;
- ret = ioctl(chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &request);
+ /*
+ * If two callers try to read the same GPIO at the same time then
+ * one of the two will get back EBUSY. There's no great way to
+ * solve this, so we'll just retry a bunch with a small sleep in
+ * between.
+ */
+ for (trynum = 0; true; trynum++) {
+ ret = ioctl(chip_fd, GPIO_GET_LINEHANDLE_IOCTL, &request);
+
+ /*
+ * Not part of the loop condition so usleep doesn't clobber
+ * errno (implicitly used by perror).
+ */
+ if (ret >= 0 || errno != EBUSY || trynum >= 50)
+ break;
+
+ usleep(trynum * 1000);
+ }
+
if (ret < 0) {
perror("GPIO_GET_LINEHANDLE_IOCTL");
return -1;
}
+
if (request.fd < 0) {
fprintf(stderr, "bad LINEHANDLE fd %d\n", request.fd);
return -1;