Received: by 2002:a05:6a10:5bc5:0:0:0:0 with SMTP id os5csp1109988pxb; Thu, 21 Oct 2021 16:07:50 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx8M3IGUwrpziKHLZ5UV5VpU6dUzYcmeXi6YzROyXXPoLncEpMVhewK2Tp+dntnSmBWjwmq X-Received: by 2002:a05:6a00:2353:b0:44d:e2fd:2293 with SMTP id j19-20020a056a00235300b0044de2fd2293mr8471374pfj.68.1634857670441; Thu, 21 Oct 2021 16:07:50 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634857670; cv=none; d=google.com; s=arc-20160816; b=zK1bMMLMB7FCiA3IMHhWUSfFgwR40+Ty0OmZcTjBRrjlGLeBKGy3+SwY/ItYtCZqZk 7CV++Zz4kTcvuthxrv4buIzAbA6Yy5XoF/4nnwpBVoOaqiBpznukzqAka0lGcJQUCK7o 1jvrT+n96DpNXz2/mYYW7OXFUZ6nAT3rg7UZ5X6q6vhJ36tdeTkGwxd0ucSeyV06WqtX AV8Y/IIg/yaNNsbzKwTKFtaWQLOxTCuhixfbeswCN0t/1aZp6gpQoL/0MHzisRrX39+E j8l2r4T4NSmbjJ+qOnR8TZI6Z9GcPnE+ZuWAYNCZd+xhpzmsGrFEnniMhYVdloIVf/Fx 55ag== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:references:in-reply-to:message-id:date:subject :cc:to:from; bh=XAHy+qoAZHdtIDELdLrs6FeSWSsdNQdwuDplgdeWUJU=; b=mDAEu7OQz4PgO8b2JbjWW3SasZvZ4ZnjeMTfArs/aKXyTjcxNfbZj/cdxUFVEyang3 00OZhNIGN53dIZvbmRpgG2Q63y6k94b6UDLOaPFIws2/L//5tkoe7HCmZUEXfAp389n3 Og70x2ASc8syT8FsNpsCxwZNDeXTA1xBMp3SsaMciQNGthEV/XM8S8XO3D73+neWwZFm 0Z7DF9bT1AfoXELyoQiOk1FS1RkmX6XFGGhMpKUqeR7f0j0DVOcpJTogVxWLBC3taXK0 6BZnhIzqx6iiVwV/9eNIZnnol3H45HI9iQvnlnehhscpk4GgWMkDA4trzAAwq7Pk1HK5 BLPA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id n2si11646383plf.362.2021.10.21.16.07.36; Thu, 21 Oct 2021 16:07:50 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232649AbhJUXIH (ORCPT + 99 others); Thu, 21 Oct 2021 19:08:07 -0400 Received: from mga07.intel.com ([134.134.136.100]:4523 "EHLO mga07.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232271AbhJUXHr (ORCPT ); Thu, 21 Oct 2021 19:07:47 -0400 X-IronPort-AV: E=McAfee;i="6200,9189,10144"; a="292634880" X-IronPort-AV: E=Sophos;i="5.87,170,1631602800"; d="scan'208";a="292634880" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 21 Oct 2021 16:02:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.87,170,1631602800"; d="scan'208";a="445033437" Received: from chang-linux-3.sc.intel.com ([172.25.66.175]) by orsmga006.jf.intel.com with ESMTP; 21 Oct 2021 16:02:27 -0700 From: "Chang S. Bae" To: linux-kernel@vger.kernel.org Cc: x86@kernel.org, tglx@linutronix.de, dave.hansen@linux.intel.com, arjan@linux.intel.com, ravi.v.shankar@intel.com, chang.seok.bae@intel.com Subject: [PATCH 18/23] x86/fpu/xstate: Add fpstate_realloc()/free() Date: Thu, 21 Oct 2021 15:55:22 -0700 Message-Id: <20211021225527.10184-19-chang.seok.bae@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20211021225527.10184-1-chang.seok.bae@intel.com> References: <20211021225527.10184-1-chang.seok.bae@intel.com> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The fpstate embedded in struct fpu is the default state for storing the FPU registers. It's sized so that the default supported features can be stored. For dynamically enabled features the register buffer is too small. The #NM handler detects first use of a feature which is disabled in the XFD MSR. After handling permission checks it recalculates the size for kernel space and user space state and invokes fpstate_realloc() which tries to reallocate fpstate and install it. Provide the allocator function which checks whether the current buffer size is sufficient and if not allocates one. If allocation is successful the new fpstate is initialized with the new features and sizes and the now enabled features is removed from the task's XFD mask. realloc_fpstate() uses vzalloc(). If use of this mechanism grows to re-allocate buffers larger than 64KB, a more sophisticated allocation scheme that includes purpose-built reclaim capability might be justified. Signed-off-by: Chang S. Bae Signed-off-by: Thomas Gleixner Signed-off-by: Chang S. Bae --- arch/x86/include/asm/fpu/api.h | 7 +++ arch/x86/kernel/fpu/xstate.c | 97 +++++++++++++++++++++++++++++++--- arch/x86/kernel/process.c | 10 ++++ 3 files changed, 106 insertions(+), 8 deletions(-) diff --git a/arch/x86/include/asm/fpu/api.h b/arch/x86/include/asm/fpu/api.h index 89762c28ad5a..c17d02decd65 100644 --- a/arch/x86/include/asm/fpu/api.h +++ b/arch/x86/include/asm/fpu/api.h @@ -130,6 +130,13 @@ static inline void fpstate_init_soft(struct swregs_state *soft) {} /* State tracking */ DECLARE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx); +/* Process cleanup */ +#ifdef CONFIG_X86_64 +extern void fpstate_free(struct fpu *fpu); +#else +static inline void fpstate_free(struct fpu *fpu) { } +#endif + /* fpstate-related functions which are exported to KVM */ extern void fpstate_clear_xstate_component(struct fpstate *fps, unsigned int xfeature); diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 1b2fad6e4964..3f65140b4e6f 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ #include #include +#include "context.h" #include "internal.h" #include "legacy.h" #include "xstate.h" @@ -1368,6 +1370,91 @@ void xfd_validate_state(struct fpstate *fpstate, u64 mask, bool rstor) } #endif /* CONFIG_X86_DEBUG_FPU */ +void fpstate_free(struct fpu *fpu) +{ + if (fpu->fpstate || fpu->fpstate != &fpu->__fpstate) + vfree(fpu->fpstate); +} + +/** + * fpu_install_fpstate - Update the active fpstate in the FPU + * + * @fpu: A struct fpu * pointer + * @newfps: A struct fpstate * pointer + * + * Returns: A null pointer if the last active fpstate is the embedded + * one or the new fpstate is already installed; + * otherwise, a pointer to the old fpstate which has to + * be freed by the caller. + */ +static struct fpstate *fpu_install_fpstate(struct fpu *fpu, + struct fpstate *newfps) +{ + struct fpstate *oldfps = fpu->fpstate; + + if (fpu->fpstate == newfps) + return NULL; + + fpu->fpstate = newfps; + return oldfps != &fpu->__fpstate ? oldfps : NULL; +} + +/** + * fpstate_realloc - Reallocate struct fpstate for the requested new features + * + * @xfeatures: A bitmap of xstate features which extend the enabled features + * of that task + * @ksize: The required size for the kernel buffer + * @usize: The required size for user space buffers + * + * Note vs. vmalloc(): If the task with a vzalloc()-allocated buffer + * terminates quickly, vfree()-induced IPIs may be a concern, but tasks + * with large states are likely to live longer. + * + * Returns: 0 on success, -ENOMEM on allocation error. + */ +static int fpstate_realloc(u64 xfeatures, unsigned int ksize, + unsigned int usize) +{ + struct fpu *fpu = ¤t->thread.fpu; + struct fpstate *curfps, *newfps = NULL; + unsigned int fpsize; + + curfps = fpu->fpstate; + fpsize = ksize + ALIGN(offsetof(struct fpstate, regs), 64); + + newfps = vzalloc(fpsize); + if (!newfps) + return -ENOMEM; + newfps->size = ksize; + newfps->user_size = usize; + newfps->is_valloc = true; + + fpregs_lock(); + /* + * Ensure that the current state is in the registers before + * swapping fpstate as that might invalidate it due to layout + * changes. + */ + if (test_thread_flag(TIF_NEED_FPU_LOAD)) + fpregs_restore_userregs(); + + newfps->xfeatures = curfps->xfeatures | xfeatures; + newfps->user_xfeatures = curfps->user_xfeatures | xfeatures; + newfps->xfd = curfps->xfd & ~xfeatures; + + curfps = fpu_install_fpstate(fpu, newfps); + + /* Do the final updates within the locked region */ + xstate_init_xcomp_bv(&newfps->regs.xsave, newfps->xfeatures); + xfd_update_state(newfps); + + fpregs_unlock(); + + vfree(curfps); + return 0; +} + static int validate_sigaltstack(unsigned int usize) { struct task_struct *thread, *leader = current->group_leader; @@ -1390,7 +1477,8 @@ static int __xstate_request_perm(u64 permitted, u64 requested) /* * This deliberately does not exclude !XSAVES as we still might * decide to optionally context switch XCR0 or talk the silicon - * vendors into extending XFD for the pre AMX states. + * vendors into extending XFD for the pre AMX states, especially + * AVX512. */ bool compacted = cpu_feature_enabled(X86_FEATURE_XSAVES); struct fpu *fpu = ¤t->group_leader->thread.fpu; @@ -1462,13 +1550,6 @@ static int xstate_request_perm(unsigned long idx) return ret; } -/* Place holder for now */ -static int fpstate_realloc(u64 xfeatures, unsigned int ksize, - unsigned int usize) -{ - return -ENOMEM; -} - int xfd_enable_feature(u64 xfd_err) { u64 xfd_event = xfd_err & XFEATURE_MASK_USER_DYNAMIC; diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 99025e32f105..f3f251787b99 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -90,9 +91,18 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) #endif /* Drop the copied pointer to current's fpstate */ dst->thread.fpu.fpstate = NULL; + return 0; } +#ifdef CONFIG_X86_64 +void arch_release_task_struct(struct task_struct *tsk) +{ + if (fpu_state_size_dynamic()) + fpstate_free(&tsk->thread.fpu); +} +#endif + /* * Free thread data structures etc.. */ -- 2.17.1