[PATCH] efivarfs: Rate limit statfs() handler
Ard Biesheuvel <[email protected]>
| Newsgroups | org.kernel.vger.linux-efi,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
Ravi reports that statfs() may be called by unprivileged users on the efivarfs mount point, which may result in a flood of calls to the QueryVariableInfo() runtime service. These calls are disproportionately costly on x86 systems where the variable store is backed by SMM, as each SMM entry requires a rendez-vous of all the CPUs. So rate limit the calls to QueryVariableInfo() at twice per second, and return the most recently obtained values for calls that are elided. Cc: <[email protected]> Cc: Anisse Astier <[email protected]> Reported-by: Ravi Bangoria <[email protected]> Fixes: d86ff3333cb1 ("efivarfs: expose used and total size") Signed-off-by: Ard Biesheuvel <[email protected]> --- This supersedes [0], which had some issues, the main one being that a cache that requires explicit invalidation may go out of sync due to direct calls to SetVariable() made by other drivers. [0] https://lore.kernel.org/all/[email protected]/ fs/efivarfs/super.c | 30 ++++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 733c19571f1c..8d33f11db2a1 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -89,12 +89,30 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) /* Some UEFI firmware does not implement QueryVariableInfo() */ storage_space = remaining_space = 0; if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { - status = efivar_query_variable_info(attr, &storage_space, - &remaining_space, - &max_variable_size); - if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) - pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n", - status); + static DEFINE_RATELIMIT_STATE(_rs, 2 * HZ, 5); + static u64 storage, remaining; + static DEFINE_SPINLOCK(lock); + + if (!__ratelimit(&_rs)) { + ratelimit_set_flags(&_rs, RATELIMIT_MSG_ON_RELEASE); + + spin_lock(&lock); + storage_space = storage; + remaining_space = remaining; + spin_unlock(&lock); + } else { + status = efivar_query_variable_info(attr, &storage_space, + &remaining_space, + &max_variable_size); + if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) + pr_warn("query_variable_info() failed: 0x%lx\n", + status); + + spin_lock(&lock); + storage = storage_space; + remaining = remaining_space; + spin_unlock(&lock); + } } /* -- 2.55.0.699.gb54405d56f-goog