118 lines
3.5 KiB
Diff
118 lines
3.5 KiB
Diff
From 5ea8c3400693b30c2b65a887899dc2a8e36a9688 Mon Sep 17 00:00:00 2001
|
|
From: Peter Jones <pjones@redhat.com>
|
|
Date: Fri, 23 Feb 2018 15:49:02 -0500
|
|
Subject: [PATCH] efivarfs / vars: usleep() before reading from efivarfs if
|
|
euid != 0
|
|
|
|
There's a kernel rate limiter on efi variable reads now for
|
|
non-root users, and we'd rather just not hit it than have to dig out
|
|
from having hit it. So this adds a 10ms sleep before each read call.
|
|
|
|
If you do have 50 variables, efibootmgr will do 100 reads, which would
|
|
trigger the rate limit. In that case, this patch adds 1 second (plus
|
|
lossage due to calling, etc.), so it should stay just below the
|
|
triggering threshold. That will definitely be /smoother/ than hitting
|
|
it, and almost certainly faster as well, because the extra calls will
|
|
re-enforce the limit.
|
|
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
---
|
|
src/efivarfs.c | 12 ++++++++++++
|
|
src/vars.c | 11 +++++++++++
|
|
src/util.h | 7 +++++++
|
|
3 files changed, 30 insertions(+)
|
|
|
|
diff --git a/src/efivarfs.c b/src/efivarfs.c
|
|
index d1458a24d1e..38e4074e977 100644
|
|
--- a/src/efivarfs.c
|
|
+++ b/src/efivarfs.c
|
|
@@ -220,6 +220,16 @@ efivarfs_get_variable(efi_guid_t guid, const char *name, uint8_t **data,
|
|
int fd = -1;
|
|
char *path = NULL;
|
|
int rc;
|
|
+ int ratelimit;
|
|
+
|
|
+ /*
|
|
+ * The kernel rate limiter hits us if we go faster than 100 efi
|
|
+ * variable reads per second as non-root. So if we're not root, just
|
|
+ * delay this long after each read. The user is not going to notice.
|
|
+ *
|
|
+ * 1s / 100 = 10000us.
|
|
+ */
|
|
+ ratelimit = geteuid() == 0 ? 0 : 10000;
|
|
|
|
rc = make_efivarfs_path(&path, guid, name);
|
|
if (rc < 0) {
|
|
@@ -233,12 +243,14 @@ efivarfs_get_variable(efi_guid_t guid, const char *name, uint8_t **data,
|
|
goto err;
|
|
}
|
|
|
|
+ usleep(ratelimit);
|
|
rc = read(fd, &ret_attributes, sizeof (ret_attributes));
|
|
if (rc < 0) {
|
|
efi_error("read failed");
|
|
goto err;
|
|
}
|
|
|
|
+ usleep(ratelimit);
|
|
rc = read_file(fd, &ret_data, &size);
|
|
if (rc < 0) {
|
|
efi_error("read_file failed");
|
|
diff --git a/src/vars.c b/src/vars.c
|
|
index a7b5e2387f9..8522725a51f 100644
|
|
--- a/src/vars.c
|
|
+++ b/src/vars.c
|
|
@@ -305,6 +305,16 @@ vars_get_variable(efi_guid_t guid, const char *name, uint8_t **data,
|
|
char *path = NULL;
|
|
int rc;
|
|
int fd = -1;
|
|
+ int ratelimit;
|
|
+
|
|
+ /*
|
|
+ * The kernel rate limiter hits us if we go faster than 100 efi
|
|
+ * variable reads per second as non-root. So if we're not root, just
|
|
+ * delay this long after each read. The user is not going to notice.
|
|
+ *
|
|
+ * 1s / 100 = 10000us.
|
|
+ */
|
|
+ ratelimit = geteuid() == 0 ? 0 : 10000;
|
|
|
|
rc = asprintf(&path, "%s%s-" GUID_FORMAT "/raw_var",
|
|
get_vars_path(),
|
|
@@ -322,6 +332,7 @@ vars_get_variable(efi_guid_t guid, const char *name, uint8_t **data,
|
|
goto err;
|
|
}
|
|
|
|
+ usleep(ratelimit);
|
|
rc = read_file(fd, &buf, &bufsize);
|
|
if (rc < 0) {
|
|
efi_error("read_file(%s) failed", path);
|
|
diff --git a/src/util.h b/src/util.h
|
|
index deef7e71bc4..69042d3cf9a 100644
|
|
--- a/src/util.h
|
|
+++ b/src/util.h
|
|
@@ -25,6 +25,7 @@
|
|
#include <endian.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
+#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
@@ -137,6 +138,12 @@ read_file(int fd, uint8_t **buf, size_t *bufsize)
|
|
* before doing so. */
|
|
s = read(fd, p, size - filesize);
|
|
if (s < 0 && errno == EAGAIN) {
|
|
+ /*
|
|
+ * if we got EAGAIN, there's a good chance we've hit
|
|
+ * the kernel rate limiter. Doing more reads is just
|
|
+ * going to make it worse, so instead, give it a rest.
|
|
+ */
|
|
+ sched_yield();
|
|
continue;
|
|
} else if (s < 0) {
|
|
int saved_errno = errno;
|
|
--
|
|
2.15.0
|
|
|