diff --git a/0001-efivarfs-vars-usleep-before-reading-from-efivarfs-if.patch b/0001-efivarfs-vars-usleep-before-reading-from-efivarfs-if.patch new file mode 100644 index 0000000..9b2ae4d --- /dev/null +++ b/0001-efivarfs-vars-usleep-before-reading-from-efivarfs-if.patch @@ -0,0 +1,117 @@ +From 5ea8c3400693b30c2b65a887899dc2a8e36a9688 Mon Sep 17 00:00:00 2001 +From: Peter Jones +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 + diff --git a/efivar.spec b/efivar.spec index 69a73ef..c02022f 100644 --- a/efivar.spec +++ b/efivar.spec @@ -1,6 +1,6 @@ Name: efivar -Version: 33 -Release: 3%{?dist} +Version: 34 +Release: 1%{?dist} Summary: Tools to manage UEFI variables License: LGPLv2.1 URL: https://github.com/rhboot/efivar @@ -8,7 +8,10 @@ Requires: %{name}-libs = %{version}-%{release} ExclusiveArch: %{ix86} x86_64 aarch64 %{arm} BuildRequires: popt-devel git glibc-static libabigail -Source0: https://github.com/rhboot/efivar/archive/%{version}.tar.bz2 +# please don't fix this to reflect github's incomprehensible url that goes +# to a different tarball. +Source0: https://github.com/rhboot/efivar/archive/efivar-%{version}.tar.bz2 +Patch0001: 0001-efivarfs-vars-usleep-before-reading-from-efivarfs-if.patch %description efivar provides a simple command line interface to the UEFI variable facility. @@ -70,6 +73,9 @@ make libdir=%{_libdir} bindir=%{_bindir} CFLAGS="$RPM_OPT_FLAGS -flto" LDFLAGS=" %{_libdir}/*.so.* %changelog +* Tue Feb 27 2018 Peter Jones - 34-1 +- Update to efivar 34, and include a patch to avoid upstream rate limiting. + * Wed Feb 07 2018 Fedora Release Engineering - 33-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild diff --git a/sources b/sources index 21b6356..0efe79d 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (33.tar.bz2) = 76c053b38b6293e79eff2dcaaaed35c87e875fb7ab67163f91190085c2733c105b351d062b6f95786f95597396be5137284ab7537ba8b9968da4b648247862f3 +SHA512 (efivar-34.tar.bz2) = 6baa02e1ad919f84d129a032a4631126d6f43dfc247f63c247624cc2af697fb627fd76fe0ffed069b42b835d862582877644fb19843361ef9ced62aa7cd518c4