From patchwork Mon Feb 1 22:06:58 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matt Fleming X-Patchwork-Id: 60975 Delivered-To: patch@linaro.org Received: by 10.112.130.2 with SMTP id oa2csp351728lbb; Mon, 1 Feb 2016 14:10:38 -0800 (PST) X-Received: by 10.66.230.201 with SMTP id ta9mr42138460pac.52.1454364637817; Mon, 01 Feb 2016 14:10:37 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 76si42837984pfm.92.2016.02.01.14.10.37; Mon, 01 Feb 2016 14:10:37 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dkim=pass header.i=@codeblueprint-co-uk.20150623.gappssmtp.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932968AbcBAWKf (ORCPT + 30 others); Mon, 1 Feb 2016 17:10:35 -0500 Received: from mail-wm0-f44.google.com ([74.125.82.44]:37246 "EHLO mail-wm0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932664AbcBAWHU (ORCPT ); Mon, 1 Feb 2016 17:07:20 -0500 Received: by mail-wm0-f44.google.com with SMTP id l66so92386869wml.0 for ; Mon, 01 Feb 2016 14:07:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=codeblueprint-co-uk.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=bAF3eKZgPMi1hwzls/ptiC7c0DZDGa3ctcsjNHMIMKw=; b=DLuuH2iJb7MsJoJKrPpHtJaHRK8EOddS/jhEA3dRSSd7hD75QMIheB4MXXRQmYFs4O BP6hwzj40EcfSa9/b0DC6/t2hlViYye7vaeYcopY4JTslYbSrQfr2lnuoBql4aN9Vur3 9CBCLNpsaF4JqGa49gkKAMqaZAMIWw8Cn5Z8BjgsaezoVk/TnXIqB+7l4vSE81x5FFLC ugcsWyDUQWQOtMHe/YjcxOU0cmod0s+EbQ8iSQ1g76Mxu+RJQbogjmpq+S9Hoce2E2RP O7bEZmZMhpGaX8rv73Hck4Ii8vXn67eR3cdbkv6w7+6RZMEyvsjnTXp7pxpjqmMhQk6P 9uYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=bAF3eKZgPMi1hwzls/ptiC7c0DZDGa3ctcsjNHMIMKw=; b=NomwmPdflutrtvOJXR2UueU8yqlhFmET7xrtQybBPH0zavliX1C2tP6KOn3XR3zay8 jP6BO9jKASBQd6xPhe0HIsjSvFE0ZpE6ZrhCHLwkpnGk1zyGzt1Mu0w/PyjZMqq5uXNj Z6RTNryKeQDTl9Pdvc1qKkEvFslCEvvLZxsE7gg3fhsseYP6lM9TrXGXiKMtEViIsQ57 /fgwSjYNsL7rbul2np3T5AGSX2M9mYQD+PJ05HWTTqrSskQew0zxfhaL1gkhRzgMUOYm PNZ8pDqRlf3jgQGdjNje9lVAjEPGiactxzEFSdUCS36I/0qHkE2kwbcsWXrMxjzlIahK 3H9Q== X-Gm-Message-State: AG10YORxBwrzJSfoxMusj529DLEkFf9z3/X7AC0MuRgiG1v3QJVIV4bxB4lC92ze3Aro4g== X-Received: by 10.194.115.164 with SMTP id jp4mr25276841wjb.26.1454364439066; Mon, 01 Feb 2016 14:07:19 -0800 (PST) Received: from localhost (5ec16434.skybroadband.com. [94.193.100.52]) by smtp.gmail.com with ESMTPSA id pu8sm8866324wjc.17.2016.02.01.14.07.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 01 Feb 2016 14:07:18 -0800 (PST) From: Matt Fleming To: Ingo Molnar , "H . Peter Anvin" , Thomas Gleixner Cc: linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org, Ard Biesheuvel , Matt Fleming Subject: [PATCH 04/14] efi: Add nonblocking option to efi_query_variable_store() Date: Mon, 1 Feb 2016 22:06:58 +0000 Message-Id: <1454364428-494-5-git-send-email-matt@codeblueprint.co.uk> X-Mailer: git-send-email 2.6.2 In-Reply-To: <1454364428-494-1-git-send-email-matt@codeblueprint.co.uk> References: <1454364428-494-1-git-send-email-matt@codeblueprint.co.uk> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Ard Biesheuvel The function efi_query_variable_store() may be invoked by efivar_entry_set_nonblocking(), which itself takes care to only call a non-blocking version of the SetVariable() runtime wrapper. However, efi_query_variable_store() may call the SetVariable() wrapper directly, as well as the wrapper for QueryVariableInfo(), both of which could deadlock in the same way we are trying to prevent by calling efivar_entry_set_nonblocking() in the first place. So instead, modify efi_query_variable_store() to use the non-blocking variants of QueryVariableInfo() (and give up rather than free up space if the available space is below EFI_MIN_RESERVE) if invoked with the 'nonblocking' argument set to true. Signed-off-by: Ard Biesheuvel Signed-off-by: Matt Fleming --- arch/x86/platform/efi/quirks.c | 33 ++++++++++++++++++++++++++++++++- drivers/firmware/efi/vars.c | 16 ++++++++++++++-- include/linux/efi.h | 12 +++++++++--- 3 files changed, 55 insertions(+), 6 deletions(-) -- 2.6.2 diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 6452070f3025..dc4f7b51abf9 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -56,13 +56,41 @@ void efi_delete_dummy_variable(void) } /* + * In the nonblocking case we do not attempt to perform garbage + * collection if we do not have enough free space. Rather, we do the + * bare minimum check and give up immediately if the available space + * is below EFI_MIN_RESERVE. + * + * This function is intended to be small and simple because it is + * invoked from crash handler paths. + */ +static efi_status_t query_variable_store_nonblocking(u32 attributes, + unsigned long size) +{ + efi_status_t status; + u64 storage_size, remaining_size, max_size; + + status = efi.query_variable_info_nonblocking(attributes, &storage_size, + &remaining_size, + &max_size); + if (status != EFI_SUCCESS) + return status; + + if (remaining_size - size < EFI_MIN_RESERVE) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +/* * Some firmware implementations refuse to boot if there's insufficient space * in the variable store. Ensure that we never use more than a safe limit. * * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable * store. */ -efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) +efi_status_t efi_query_variable_store(u32 attributes, unsigned long size, + bool nonblocking) { efi_status_t status; u64 storage_size, remaining_size, max_size; @@ -70,6 +98,9 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) if (!(attributes & EFI_VARIABLE_NON_VOLATILE)) return 0; + if (nonblocking) + return query_variable_store_nonblocking(attributes, size); + status = efi.query_variable_info(attributes, &storage_size, &remaining_size, &max_size); if (status != EFI_SUCCESS) diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index 70a0fb10517f..d2a49626a335 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -234,7 +234,18 @@ check_var_size(u32 attributes, unsigned long size) if (!fops->query_variable_store) return EFI_UNSUPPORTED; - return fops->query_variable_store(attributes, size); + return fops->query_variable_store(attributes, size, false); +} + +static efi_status_t +check_var_size_nonblocking(u32 attributes, unsigned long size) +{ + const struct efivar_operations *fops = __efivars->ops; + + if (!fops->query_variable_store) + return EFI_UNSUPPORTED; + + return fops->query_variable_store(attributes, size, true); } static int efi_status_to_err(efi_status_t status) @@ -615,7 +626,8 @@ efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor, if (!spin_trylock_irqsave(&__efivars->lock, flags)) return -EBUSY; - status = check_var_size(attributes, size + ucs2_strsize(name, 1024)); + status = check_var_size_nonblocking(attributes, + size + ucs2_strsize(name, 1024)); if (status != EFI_SUCCESS) { spin_unlock_irqrestore(&__efivars->lock, flags); return -ENOSPC; diff --git a/include/linux/efi.h b/include/linux/efi.h index ad1e177ba48e..09f1559e7525 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -525,7 +525,9 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, unsigned long count, u64 *max_size, int *reset_type); -typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size); +typedef efi_status_t efi_query_variable_store_t(u32 attributes, + unsigned long size, + bool nonblocking); void efi_native_runtime_setup(void); @@ -881,13 +883,17 @@ extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if pos #ifdef CONFIG_X86 extern void efi_late_init(void); extern void efi_free_boot_services(void); -extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size); +extern efi_status_t efi_query_variable_store(u32 attributes, + unsigned long size, + bool nonblocking); extern void efi_find_mirror(void); #else static inline void efi_late_init(void) {} static inline void efi_free_boot_services(void) {} -static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) +static inline efi_status_t efi_query_variable_store(u32 attributes, + unsigned long size, + bool nonblocking) { return EFI_SUCCESS; }