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-20 02:42:18 +0000
commit4ca43a34f5a936683ebcdc7b0edd059f313f61be (patch)
tree843eb853cae5b936b201b14188aa9dbb5334812e
parentf1a7efc0ed8bcfb88b4d241dfd516a6636672c81 (diff)
downloadvboot-4ca43a34f5a936683ebcdc7b0edd059f313f61be.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>
-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;