From nobody Mon Sep 28 05:45:28 2026 Received: from CH4PR04CU002.outbound.protection.outlook.com (mail-northcentralusazon11013026.outbound.protection.outlook.com [40.107.201.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53778347505; Tue, 25 Aug 2026 18:17:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.201.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681840; cv=fail; b=sAhWMWj+lYKac5F9pFJ84KlFSLOrWCiLt36HSsH1etaH29XAfbxHIbExfvRq7has5XuLAMr2q5RFWs/1NT1Pd485i3Ccj8FFeNZHlmI8mtjJrveVEpv8GdGWx7Bk6NNT+AyxLJi7BNTSIHksHS5bC/ZkkyJmiUMQe3dhB+UeuFE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681840; c=relaxed/simple; bh=K3Dw2+h+Bqzux+Q8IJmhk31/9DSirrHYsFN601M/s1o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=cvSc07l7E7ZFLPA22d++JQ3Wr2+5ne4OvVyAn7n8xRZ9MAFiOBt7S0uI9/tF7rcY3XLQdLVNNzEQ98CuR6fcS3NNBhZPjLO6umP5DJmkWv+2rtKZlUfx9DS/yGzBLrdIjg9pksDXYVz1UoUbVT8iDnX5k2xPTwkezWyrcJMjSaQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=F1VfB2Tz; arc=fail smtp.client-ip=40.107.201.26 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="F1VfB2Tz" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=QjU0/PjKLqvzPXWqvuHBMomx+MIU1cxmrocR6iMO8TMrtH6LyndrTKT9CmErHoHkXb8WE7zniwEzgIW7YaE44xU3w72uqdtxJlu+KcyrANObX2KPAXbLuB4zH1sVzXnfJeOdQb14gsJUMg/JVLm5U0t74q7/lsGMqBxfY467skKh+kQuNw+266OOLNlndCHtLfuEENydcgRAAWpliimU9pZBZkhDAfI9pckULg2Q6bRcWYebRKQpJlXsfiW4aExyOtBj+YH5vzdDlgbnNWg3vw8Ic+YiDE3k5v8FXbNXtfQEbErj+d0LHRD+Wrix5DtXu1rPDGc481Bmmw9W3Gjwzg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=D/3TQFD4h5Gz2cObvaCD/Z/WQqrA8cEtpo+oWXJLaMU=; b=adrzoiQF/NRtAPMpxVnvWPWH08Eyy0SabDh2jvrAFMRDuhhpevmxQ280Igf/LunM3asRMWcD8j1Tamie1QiOZ7t/+r/IzSCdp00UmwjAN6HxqpzDV2L7jtvYGgQhOE95aG13uMyhqO36bOzpmB5wRCgO2y+5tZVFRZcoFbOY/D/ixU/hFc/Lkt2L2fctcvzvOxun/pt595vQLEj2AeSa9DNCW4YHky29nX/HeJctp/xGiXLIxkMkDq4HwVe/k4b7Hr03+QHJYuY8Lfw9PzbvRhdThukzPBShxJi9aXjudBHcqbW2cpPaPixtm4KAR1C8SNQhSwsYbOThg4UeC+GvxA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=D/3TQFD4h5Gz2cObvaCD/Z/WQqrA8cEtpo+oWXJLaMU=; b=F1VfB2Tz7gl3RlVlC27fgGJSUf3wPERIFaA47/VH/3LCNHjZtQUT/ef20K8OyA4l4stM/y8mKzJTuXHrECGHViqDfe5kOqVetR1lDnSRBTemTmLfB2XKQ2J3+946MXJ/3hSlx6i30BqXFwBXUvnzDIdFbzv9lbMLS4tFelVs9TEgQ3PWgdNGKWnEdxVdzk1VL93mmygZrlvnxJVfkOOn4qafGlN92h6po679aVA64oUXdY1T0VjemDuvD4Unk+tSwnjhwLMaRG5YCbJkb7g0gjdfbIKML//YO9LIyeo/opexaAKcbldnxruD0qZxN3K1DFy2Wtd5+EnWd51qbQ2gtA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) by IA0PR12MB7675.namprd12.prod.outlook.com (2603:10b6:208:433::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.7; Tue, 25 Aug 2026 18:17:09 +0000 Received: from CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688]) by CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688%4]) with mapi id 15.21.0339.010; Tue, 25 Aug 2026 18:17:09 +0000 From: Koba Ko To: Catalin Marinas , Will Deacon , Rob Herring , Breno Leitao Cc: Jonathan Corbet , Shuah Khan , linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Fenghua Yu , Tushar Dave , Kai-Heng Feng , rupakr@nvidia.com, mohammedk@nvidia.com Subject: [RFC PATCH v2 1/3] arm64: add CPU prefetch and cache modulation sysfs interface Date: Wed, 26 Aug 2026 02:16:59 +0800 Message-ID: <20260825181701.3-2-kobak@nvidia.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260825181701.3-1-kobak@nvidia.com> References: <20260825181701.3-1-kobak@nvidia.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: TP0P295CA0038.TWNP295.PROD.OUTLOOK.COM (2603:1096:910:4::11) To CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CHXPR12MB999244:EE_|IA0PR12MB7675:EE_ X-MS-Office365-Filtering-Correlation-Id: d67e5779-99f2-4b3a-d251-08df02d513a5 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|7416014|376014|1800799024|23010399003|366016|10067099003|56012099006|5023799004|11063799006|6133799003|18002099003|22082099003; X-Microsoft-Antispam-Message-Info: jLUI+aYp4VDg/CborNIl9e9WkDvfh1WCS9vKVwgmx1E6p2lGtpQDx3aHRcZebobyYsAs7gz/kDn91MTWDn/N3+55fDWPfqasXUhnxSpqnHxsopwDkhi+nrndO0WUMGXXL3X7Lz2G8TT7VwNB+QOiQp42NCN/W3V9aWg42UoXbIUpf0fRXe77e7SiaZpcSqgLk/o9VGolRHOuFvMQQrr5T15Hf13z2ExwTDf3jjysM2SaqvGmTN1DDuXbHLrGMi/At7akNSkb2pBk/gzsh4hsPv3/KH1g8cIgz69jDciNtuJ+4y4hlOl/FFAFw2aDnkrSrW6bQuJTg91ZVDwN+9b4BFpy2QNV8bL5VR7A4zJ3YSQvod0hkvPocKxm14tfOEn/DMThj+afpmz0De9Gm24/GHoppYCiEPeqyYo49KSOWaFtz6IR5cJFXg59GcrpfreU4bilwkFoM9GxH8jMDqI+DScGMCyVN75Y7ftDV+czAE1td6rehnVnVoOk+QUNALzVAkcJz6mcKERd2NzeciCKNpiIVFQzCUGXt5C5K7UVH+znwxhWeuqCwSSitNdbm5sFMmoZbCP0nncC3nM9VpvBKh15Dqn9Mt5ljCmNhHI2zEGjf/5fIuRHZQsVYDpmuq0PO+4JOdg53qu9jEyYjASOYC2bsLtgKbkE+e1rltpAF7Q= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CHXPR12MB999244.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(7416014)(376014)(1800799024)(23010399003)(366016)(10067099003)(56012099006)(5023799004)(11063799006)(6133799003)(18002099003)(22082099003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?AoM0QQFJVncp3PhYehHBgW9B8YEcKkFq9G4SRkcjqiQZZfp4vFbJOhS+y/FH?= =?us-ascii?Q?0NDCNVZwsx4y4ybOCPCOCIVZDP+7VsWqgJOo076lnszScD1xKvfBTfDbfB7R?= =?us-ascii?Q?/+zVb5uptrLej/RdwPHeWpZxP1rjLvVEZFLR+ZUhtGSDQh/KZkxbZ6W0kwFp?= =?us-ascii?Q?sg1pEHIOOwnh9rwGUoxWAk/qwWpSNyuhc1wWWugjmBdmmrVhZDudLqifnqGG?= =?us-ascii?Q?WgRpskOeI6O2i5gnLj/hDgA9AuN/uP7nHhB+zVSwk3jhprz2m8RewyZY1XEg?= =?us-ascii?Q?aO4S8CnkyMQQO8TDMCjJPCg6x8hl4dMDE93ByaQUWQcZlq+5OGUZYeL96IBt?= =?us-ascii?Q?E9VTSM6UxC2tRxoz764uJvyRghi4Ezj1JOgtYTR26M2OpKZEi5D4lTyxa2GO?= =?us-ascii?Q?d1Ch87iko1UzBvA5lM5ufVMjzHnEgndKxENkKpQR6ab77FkPFhtFM34DaZ9i?= =?us-ascii?Q?OzcZ3BIcnyP6z5gwS4WkdWHY5CPRbCMoDLuLAnRtwl6uN18lz2elIJMO+S9Q?= =?us-ascii?Q?nfrsvKVYgUTfDp1v5pwtjPPMeWsbce8waYxNL6T8Wktv1GZ/Y2XylkrBqKoM?= =?us-ascii?Q?b2amuqse1Pd9gsIzuhueP3NZjLEUprFUilpyolIlWYuzl8tioS+LXuqgnnqV?= =?us-ascii?Q?XNZgHdnP4iTYvr4N0pUD2+KO+OcY5Mr4nbVdXGaRy5gFVx3UF5j3idIANb+q?= =?us-ascii?Q?7kor4aC1NJ273E1rtzbAa3U8qWLjopPXCbyGBrEBbU6eqgOtWdXZ+zehMV3D?= =?us-ascii?Q?23JYqQq2AsMVzBBr6UOvBLfvHJK1ltdAWi5OZSlL4hbcY44P9j8QoWIvfS6e?= =?us-ascii?Q?NZKM+ejOQeclbSCdgwrBYVPJxGHOcEidbUuKKiOViE14NBE7fLFMbs6j7jGf?= =?us-ascii?Q?Dv7TxhQw6UxLHmrwHiAxg/OOZ+koeMfQODTT1qhOWI+EkmQp2SUmmZYK3Evl?= =?us-ascii?Q?qAK8Yq/vm3dbpNNtl9jaXDG4tr/culisgiTW0WZa+gpx8tWl5fjRMbM55Lwk?= =?us-ascii?Q?bxXLuWLhMi/HTj01YV0uw8eX2wd+mDWkOSpLCyS2X1Aw9XMGd4xsRsrj7Tpz?= =?us-ascii?Q?uvAh/9D3hlXtRAFzT2T24ZdHvBxuM1NTY6HjcPpIDrUoz0pQnQaHuPtXMNPA?= =?us-ascii?Q?O6YL37CqePSRrWjagj7nu6Kk8CWF6cymgB8TWJYYlJpjcs7xLJskZ65EEx2j?= =?us-ascii?Q?iS5jIyxzh6b5jWxHzmkVw11t+oUvsGKBq0zBrqJC6q0jsAomreasQB7HMdMm?= =?us-ascii?Q?COe5BhdWlTAwY3zaz6b3K4+lk7KitQfwrieVpB4AmAWXuOxnhMf3ZBQrqQHz?= =?us-ascii?Q?8Hw1Sy8mcJcgycW12MYpxRzCGzT41TjrGaCUAzo1jT7Mi6/ELcnIwsaIHAzC?= =?us-ascii?Q?29VVmGMFCR/1fZF702MIdDPWX4RI3IGCXe2kJFsMznlOXzze7xyth34SNCDG?= =?us-ascii?Q?pHZF8f2JHCwWAMMgHmdCCv7h1LN+bxs9JTGs5R1vXMxozcy97NysvH0A2AV+?= =?us-ascii?Q?Vs+JLZNBuzhlhGB4EdyEWOD9gy//RFe1gKDAR8HxGF6c6wXJ8WBl9sk1cXLP?= =?us-ascii?Q?i6auWL8F52MYCtEGYFM4pgPU6vAjtaYqMdFaPY270rpb5WxGNJVLWNtJnLvG?= =?us-ascii?Q?Xm9BT+vEvYz3bPvBOb66T2nxkqdUzVDKpb/hh48aVUbfXBcwtif5boiIP9/A?= =?us-ascii?Q?LVp1HyaqzSQnAUSvZI5HIF0m5ukDcidtkAS5yE70/Wwf4KHWPNyjRGUN3YHB?= =?us-ascii?Q?M57gSv6MeQ=3D=3D?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: d67e5779-99f2-4b3a-d251-08df02d513a5 X-MS-Exchange-CrossTenant-AuthSource: CHXPR12MB999244.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Aug 2026 18:17:09.1765 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: xW1+DP4QyUkzRA8bUY99gLJpdYRORiO6fiQc+rn0VXb3hdME/SFa6BfhF+l4iQ5sC6aMeUiDJIv3qjJNJ8ZBhw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA0PR12MB7675 Content-Type: text/plain; charset="utf-8" Performance characterization on arm64 systems sometimes needs a way to inspect and bracket selected implementation-defined prefetch and cache controls without exposing raw register contents. Add an RFC-only arm64 cpumod interface that presents a small set of named, range-checked per-CPU sysfs attributes under each CPU device. Establish the Kconfig and Makefile plumbing, CPU profile detection for Grace and Vera from the target CPU's MIDR, register-field descriptors, per-CPU kobject and attribute helpers, locking, and target-CPU read/write callbacks. Common attributes are exposed for all supported profiles, while Grace- and Vera-specific fields are exposed only for the matching profile. Place the option at the end of the top-level Kernel Features menu because these implementation controls are not tied to an Arm architecture revision. Per-CPU cpumod sysfs state follows the CPU lifecycle. Module initialization populates subtrees for every online CPU while holding cpus_read_lock(), so initial enumeration and CPU hotplug state registration are serialized against concurrent topology changes. Offline CPUs are intentionally skipped during initial setup because target-CPU MIDR detection requires an IPI-capable online CPU; a later online callback creates their subtree. Profile detection reads MIDR through a synchronous callback on the CPU owning the sysfs subtree and returns nonzero only when that dispatch fails, so no IPI error can be hidden. After a successful read, retain CPUMOD_PROFILE_UNKNOWN as the unsupported-profile sentinel. Subtree creation skips that profile during both initial online enumeration and later CPUHP online callbacks without blocking module load or CPU hotplug. Object and sysfs setup failures remain fatal and are propagated. Keep the dynamically allocated kobject state in a private per-CPU pointer instead of the CPU device's generic driver-data slot. The online callback is idempotent when that pointer already records a subtree. The offline callback clears the private pointer before dropping the kobject reference, allowing the release callback to retain ownership of the final free. Register-backed controls are root-only read/write files. Reads synchronously execute on the target CPU and return the live register field value from that callback. Writes remain range-checked and continue to log the target-CPU readback after the register update. Register the dynamic CPUHP state with cpuhp_setup_state_nocalls_cpuslocked(). The nocalls form avoids replaying startup callbacks for CPUs already initialized by the explicit online enumeration. On init failure, release the CPU read lock, destroy all subtrees created so far, and return the error. Module exit removes the CPUHP state with the matching nocalls helper and tears down all remaining per-CPU state across possible CPUs. Reviewed-by: Fenghua Yu Reviewed-by: Tushar Dave Reviewed-by: Kai-Heng Feng Signed-off-by: Koba Ko --- arch/arm64/Kconfig | 17 + arch/arm64/kernel/Makefile | 1 + arch/arm64/kernel/arm_cpumod.c | 458 ++++++++++++++++++++++++ arch/arm64/kernel/arm_cpumod_internal.h | 93 +++++ 4 files changed, 569 insertions(+) create mode 100644 arch/arm64/kernel/arm_cpumod.c create mode 100644 arch/arm64/kernel/arm_cpumod_internal.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b3afe0688919b..5e4ada4884b89 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2417,6 +2417,23 @@ config ARM64_CONTPTE bit, for any mappings that meet the size and alignment requirements. This reduces TLB pressure and improves performance. =20 +config ARM64_CPUMOD + tristate "Arm CPU prefetch modulation sysfs controls" + help + Expose selected CPU implementation control register fields through + per-CPU sysfs attributes for performance characterization. + + The exposed controls allow hardware prefetch and cache-management + policy fields to be inspected and adjusted on CPUs where firmware + permits EL1 access to the relevant implementation control registers. + Systems where firmware traps or blocks these writes cannot use this + interface. + + This interface is intended for controlled performance evaluation and + should not be enabled by default on production systems. + + If unsure, say N. + endmenu # "Kernel Features" =20 menu "Boot options" diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index d2690c3ec5288..f180a52284e7d 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_VMCORE_INFO) +=3D vmcore_info.o obj-$(CONFIG_ARM_SDE_INTERFACE) +=3D sdei.o obj-$(CONFIG_ARM64_PTR_AUTH) +=3D pointer_auth.o obj-$(CONFIG_ARM64_MPAM) +=3D mpam.o +obj-$(CONFIG_ARM64_CPUMOD) +=3D arm_cpumod.o obj-$(CONFIG_ARM64_MTE) +=3D mte.o obj-y +=3D vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) +=3D vdso32-wrap.o diff --git a/arch/arm64/kernel/arm_cpumod.c b/arch/arm64/kernel/arm_cpumod.c new file mode 100644 index 0000000000000..bf21521594495 --- /dev/null +++ b/arch/arm64/kernel/arm_cpumod.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserv= ed. + */ + +#define pr_fmt(fmt) "cpumod: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arm_cpumod_internal.h" + +#define DRIVER_DESC "CPU Modulation Kernel Module" + +/* Disables hardware prefetching. + * 0b0 Enables hardware prefetching. This is the default value. + * 0b1 Disables hardware prefetching. + */ +#define CPUECTLR_PF_DIS (15) +#define CPUECTLR_PF_DIS_MSK BIT_ULL(15) + +#define CPUECTLR_CMC_WAYS (61) +#define CPUECTLR_CMC_WAYS_MSK GENMASK_ULL(63, 61) + +#define CPUECTLR2_PF_MODE (11) +#define CPUECTLR2_PF_MODE_MSK GENMASK_ULL(14, 11) + +#define CPUECTLR2_CBUSY_FILTER_WINDOW (9) +#define CPUECTLR2_CBUSY_FILTER_WINDOW_MSK GENMASK_ULL(10, 9) + +#define CPUECTLR2_CBUSY_FILTER_THRESHOLD (7) +#define CPUECTLR2_CBUSY_FILTER_THRESHOLD_MSK GENMASK_ULL(8, 7) + +static enum cpuhp_state cpumod_hp_state; +static DEFINE_PER_CPU(struct cpumod_subsys *, cpumod_subsys); + +struct cpumod_attr_call { + struct cpumod_subsys *subsys; + struct cpumod_attr *attr; + u64 value; +}; + +struct cpumod_midr_call { + u32 midr; +}; + +static const char *cpumod_profile_name(enum cpumod_profile profile) +{ + switch (profile) { + case CPUMOD_PROFILE_GRACE: + return "Grace"; + case CPUMOD_PROFILE_VERA: + return "Vera"; + default: + return "unknown"; + } +} + +/* IMP defined CPU Extended Control Register */ +#define IMP_CPUECTLR_EL1 sys_reg(3, 0, 15, 1, 4) +#define IMP_CPUECTLR2_EL1 sys_reg(3, 0, 15, 1, 5) + +static enum cpumod_profile cpumod_profile_from_midr(u32 midr) +{ + switch (midr & MIDR_CPU_MODEL_MASK) { + case MIDR_NEOVERSE_V2: + return CPUMOD_PROFILE_GRACE; + case MIDR_NVIDIA_OLYMPUS: + return CPUMOD_PROFILE_VERA; + default: + return CPUMOD_PROFILE_UNKNOWN; + } +} + +static void cpumod_read_midr_remote(void *info) +{ + struct cpumod_midr_call *call =3D info; + + call->midr =3D read_cpuid_id(); +} + +static int cpumod_detect_profile(struct cpumod_subsys *subsys) +{ + struct cpumod_midr_call call; + int ret; + + ret =3D smp_call_function_single(subsys->cpu, cpumod_read_midr_remote, + &call, 1); + if (ret) { + pr_err("Failed to read CPU%u MIDR: %d\n", subsys->cpu, ret); + return ret; + } + + subsys->profile =3D cpumod_profile_from_midr(call.midr); + if (subsys->profile =3D=3D CPUMOD_PROFILE_UNKNOWN) { + pr_debug("Unsupported CPU%u MIDR 0x%08x\n", subsys->cpu, + call.midr); + } + + return 0; +} + +static u64 cpumod_read_reg(enum cpumod_reg_id reg) +{ + switch (reg) { + case CPUMOD_REG_CPUECTLR: + return read_sysreg_s(IMP_CPUECTLR_EL1); + case CPUMOD_REG_CPUECTLR2: + return read_sysreg_s(IMP_CPUECTLR2_EL1); + default: + return 0; + } +} + +static void cpumod_write_reg(enum cpumod_reg_id reg, u64 value) +{ + switch (reg) { + case CPUMOD_REG_CPUECTLR: + write_sysreg_s(value, IMP_CPUECTLR_EL1); + /* The remote callback executes this barrier on the target CPU. */ + isb(); + break; + case CPUMOD_REG_CPUECTLR2: + write_sysreg_s(value, IMP_CPUECTLR2_EL1); + /* The remote callback executes this barrier on the target CPU. */ + isb(); + break; + default: + return; + } +} + +static u64 cpumod_attr_read_reg(const struct cpumod_attr *attr) +{ + return cpumod_attr_unpack_value(attr, cpumod_read_reg(attr->reg)); +} + +static void cpumod_attr_write_reg(const struct cpumod_attr *attr, u64 valu= e) +{ + u64 reg =3D cpumod_read_reg(attr->reg); + u64 field_value =3D cpumod_attr_pack_value(attr, value); + + cpumod_write_reg(attr->reg, (reg & ~attr->field_mask) | field_value); +} + +static void cpumod_attr_read_remote(void *info) +{ + struct cpumod_attr_call *call =3D info; + + call->value =3D cpumod_attr_read_reg(call->attr); +} + +static void cpumod_attr_write_remote(void *info) +{ + struct cpumod_attr_call *call =3D info; + struct cpumod_subsys *subsys =3D call->subsys; + struct cpumod_attr *attr =3D call->attr; + u64 readback; + + cpumod_attr_write_reg(attr, call->value); + readback =3D cpumod_attr_read_reg(attr); + pr_debug("cpu%u %s: %llx\n", + subsys->cpu, + attr->debug_name, + (unsigned long long)readback); +} + +static ssize_t cpumod_attr_show(struct kobject *kobj, + struct kobj_attribute *kattr, char *buf) +{ + struct cpumod_subsys *subsys =3D cpumod_subsys_from_kobj(kobj); + struct cpumod_attr *attr =3D cpumod_attr_from_kobj_attr(kattr); + struct cpumod_attr_call call =3D { + .subsys =3D subsys, + .attr =3D attr, + }; + int ret; + + guard(mutex)(&subsys->lock); + ret =3D smp_call_function_single(subsys->cpu, + cpumod_attr_read_remote, &call, 1); + if (ret) + return ret; + + ret =3D sysfs_emit(buf, "%llu\n", (unsigned long long)call.value); + + return ret; +} + +static ssize_t cpumod_attr_store(struct kobject *kobj, + struct kobj_attribute *kattr, + const char *buf, size_t count) +{ + struct cpumod_subsys *subsys =3D cpumod_subsys_from_kobj(kobj); + struct cpumod_attr *attr =3D cpumod_attr_from_kobj_attr(kattr); + struct cpumod_attr_call call =3D { + .subsys =3D subsys, + .attr =3D attr, + }; + unsigned int val; + int ret; + + ret =3D kstrtouint(buf, 10, &val); + if (ret) + return -EINVAL; + if (!cpumod_attr_value_valid(attr, val)) + return -EINVAL; + + call.value =3D cpumod_attr_mask_value(attr, val); + + guard(mutex)(&subsys->lock); + ret =3D smp_call_function_single(subsys->cpu, + cpumod_attr_write_remote, &call, 1); + if (ret) + return ret; + + return count; +} + +static ssize_t affected_cpus_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cpumod_subsys *subsys =3D cpumod_subsys_from_kobj(kobj); + + return sysfs_emit(buf, "%u\n", subsys->cpu); +} + +static struct kobj_attribute affected_cpus_attr =3D __ATTR_RO(affected_cpu= s); + +static umode_t cpumod_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct cpumod_subsys *subsys =3D cpumod_subsys_from_kobj(kobj); + struct cpumod_attr *cpumod_attr; + + if (attr =3D=3D &affected_cpus_attr.attr) + return attr->mode; + + cpumod_attr =3D container_of(attr, struct cpumod_attr, kattr.attr); + if (!cpumod_attr_visible_for_profile(cpumod_attr, subsys->profile)) + return 0; + + return attr->mode; +} + +#define CPUMOD_ATTR_RW(_name, _reg, _shift, _field_mask, \ + _max_value, _debug_name, \ + _visible_profiles) \ +static struct cpumod_attr _name##_attr =3D { \ + .kattr =3D __ATTR(_name, 0600, cpumod_attr_show, cpumod_attr_store), \ + .reg =3D _reg, \ + .shift =3D _shift, \ + .field_mask =3D _field_mask, \ + .max_value =3D _max_value, \ + .debug_name =3D _debug_name, \ + .visible_profiles =3D _visible_profiles, \ +} + +CPUMOD_ATTR_RW(pf_dis, CPUMOD_REG_CPUECTLR, CPUECTLR_PF_DIS, + CPUECTLR_PF_DIS_MSK, 1, "PF_DIS", + CPUMOD_PROFILE_VISIBLE_ALL); +CPUMOD_ATTR_RW(pf_mode, CPUMOD_REG_CPUECTLR2, CPUECTLR2_PF_MODE, + CPUECTLR2_PF_MODE_MSK, 9, "PF_MODE", + CPUMOD_PROFILE_VISIBLE_ALL); +CPUMOD_ATTR_RW(cbusy_filter_threshold, CPUMOD_REG_CPUECTLR2, + CPUECTLR2_CBUSY_FILTER_THRESHOLD, + CPUECTLR2_CBUSY_FILTER_THRESHOLD_MSK, 3, + "CBUSY_FILTER_THRESHOLD", + CPUMOD_PROFILE_VISIBLE_GRACE); +CPUMOD_ATTR_RW(cbusy_filter_window, CPUMOD_REG_CPUECTLR2, + CPUECTLR2_CBUSY_FILTER_WINDOW, + CPUECTLR2_CBUSY_FILTER_WINDOW_MSK, 3, + "CBUSY_FILTER_WINDOW", + CPUMOD_PROFILE_VISIBLE_GRACE); +CPUMOD_ATTR_RW(cmc_min_ways, CPUMOD_REG_CPUECTLR, + CPUECTLR_CMC_WAYS, CPUECTLR_CMC_WAYS_MSK, 7, + "CMC_MIN_WAYS", CPUMOD_PROFILE_VISIBLE_GRACE); +CPUMOD_ATTR_RW(l2spr_cmc_max_ways, CPUMOD_REG_CPUECTLR, + CPUECTLR_CMC_WAYS, CPUECTLR_CMC_WAYS_MSK, 7, + "L2SPR_CMC_MAX_WAYS", + CPUMOD_PROFILE_VISIBLE_VERA); + +#undef CPUMOD_ATTR_RW + +static struct attribute *cpumod_attrs[] =3D { + &affected_cpus_attr.attr, + &pf_dis_attr.kattr.attr, + &pf_mode_attr.kattr.attr, + &cbusy_filter_threshold_attr.kattr.attr, + &cbusy_filter_window_attr.kattr.attr, + &cmc_min_ways_attr.kattr.attr, + &l2spr_cmc_max_ways_attr.kattr.attr, + NULL, +}; + +static const struct attribute_group cpumod_group =3D { + .attrs =3D cpumod_attrs, + .is_visible =3D cpumod_attr_is_visible, +}; + +static const struct attribute_group *cpumod_groups[] =3D { + &cpumod_group, + NULL, +}; + +static void cpumod_release(struct kobject *kobj) +{ + kfree(cpumod_subsys_from_kobj(kobj)); +} + +static const struct kobj_type cpumod_ktype =3D { + .release =3D cpumod_release, + .sysfs_ops =3D &kobj_sysfs_ops, + .default_groups =3D cpumod_groups, +}; + +static int cpumod_create_subsys(struct device *cpu_dev, unsigned int cpu) +{ + struct cpumod_subsys *subsys; + int ret; + + if (per_cpu(cpumod_subsys, cpu)) + return 0; + + subsys =3D kzalloc_obj(*subsys, GFP_KERNEL); + if (!subsys) + return -ENOMEM; + + subsys->cpu =3D cpu; + mutex_init(&subsys->lock); + ret =3D cpumod_detect_profile(subsys); + if (ret) { + kfree(subsys); + return ret; + } + if (subsys->profile =3D=3D CPUMOD_PROFILE_UNKNOWN) { + kfree(subsys); + return 0; + } + + pr_debug("CPU%u detected %s profile\n", cpu, + cpumod_profile_name(subsys->profile)); + + ret =3D kobject_init_and_add(&subsys->kobj, &cpumod_ktype, + &cpu_dev->kobj, "cpumod"); + if (ret) { + pr_err("Failed to create cpumod sysfs subtree for CPU%u: %d\n", + cpu, ret); + kobject_put(&subsys->kobj); + return ret; + } + + per_cpu(cpumod_subsys, cpu) =3D subsys; + + return 0; +} + +static void cpumod_destroy_subsys(unsigned int cpu) +{ + struct cpumod_subsys *subsys =3D per_cpu(cpumod_subsys, cpu); + + if (!subsys) + return; + + per_cpu(cpumod_subsys, cpu) =3D NULL; + kobject_put(&subsys->kobj); +} + +static void cpumod_destroy_all_subsys(void) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + cpumod_destroy_subsys(cpu); +} + +static int cpumod_online_cpu(unsigned int cpu) +{ + struct device *cpu_dev =3D get_cpu_device(cpu); + + if (!cpu_dev) { + pr_err("Failed to get CPU%u device\n", cpu); + return -ENODEV; + } + + return cpumod_create_subsys(cpu_dev, cpu); +} + +static int cpumod_offline_cpu(unsigned int cpu) +{ + cpumod_destroy_subsys(cpu); + + return 0; +} + +static int __init cpumod_init(void) +{ + unsigned int cpu; + int ret; + + cpus_read_lock(); + for_each_online_cpu(cpu) { + struct device *cpu_dev =3D get_cpu_device(cpu); + + if (!cpu_dev) { + pr_err("Failed to get CPU%u device\n", cpu); + ret =3D -ENODEV; + goto err_unlock; + } + + ret =3D cpumod_create_subsys(cpu_dev, cpu); + if (ret) + goto err_unlock; + } + + ret =3D cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN, + "arm64/cpumod:online", + cpumod_online_cpu, + cpumod_offline_cpu); + cpus_read_unlock(); + if (ret < 0) + goto err_destroy; + + cpumod_hp_state =3D ret; + pr_info("module loaded\n"); + + return 0; + +err_unlock: + cpus_read_unlock(); +err_destroy: + cpumod_destroy_all_subsys(); + return ret; +} + +static void __exit cpumod_exit(void) +{ + cpuhp_remove_state_nocalls(cpumod_hp_state); + cpumod_destroy_all_subsys(); + pr_info("module unloaded\n"); +} + +module_init(cpumod_init); +module_exit(cpumod_exit); + +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_AUTHOR("kobak@nvidia.com"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/arch/arm64/kernel/arm_cpumod_internal.h b/arch/arm64/kernel/ar= m_cpumod_internal.h new file mode 100644 index 0000000000000..bc1e5b441ed79 --- /dev/null +++ b/arch/arm64/kernel/arm_cpumod_internal.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Internal arm_cpumod data structures and helpers. + */ + +#ifndef CPUMOD_INTERNAL_H +#define CPUMOD_INTERNAL_H + +#include +#include +#include +#include +#include +#include + +enum cpumod_profile { + CPUMOD_PROFILE_GRACE, + CPUMOD_PROFILE_VERA, + CPUMOD_PROFILE_UNKNOWN, +}; + +enum cpumod_reg_id { + CPUMOD_REG_CPUECTLR, + CPUMOD_REG_CPUECTLR2, +}; + +#define CPUMOD_PROFILE_VISIBLE_GRACE BIT(0) +#define CPUMOD_PROFILE_VISIBLE_VERA BIT(1) +#define CPUMOD_PROFILE_VISIBLE_ALL (CPUMOD_PROFILE_VISIBLE_GRACE | \ + CPUMOD_PROFILE_VISIBLE_VERA) + +struct cpumod_subsys { + /* Per-CPU instance lock: serializes sysfs read/write for this CPU. */ + struct mutex lock; + struct kobject kobj; + unsigned int cpu; + enum cpumod_profile profile; +}; + +struct cpumod_attr { + struct kobj_attribute kattr; + enum cpumod_reg_id reg; + u8 shift; + u64 field_mask; + u64 max_value; + const char *debug_name; + u8 visible_profiles; +}; + +static inline struct cpumod_subsys *cpumod_subsys_from_kobj(struct kobject= *kobj) +{ + return container_of(kobj, struct cpumod_subsys, kobj); +} + +static inline struct cpumod_attr *cpumod_attr_from_kobj_attr(struct kobj_a= ttribute *kattr) +{ + return container_of(kattr, struct cpumod_attr, kattr); +} + +static inline u64 cpumod_attr_mask_value(const struct cpumod_attr *attr, u= 64 value) +{ + return value & (attr->field_mask >> attr->shift); +} + +static inline bool cpumod_attr_value_valid(const struct cpumod_attr *attr,= u64 value) +{ + return value <=3D attr->max_value; +} + +static inline u64 cpumod_attr_unpack_value(const struct cpumod_attr *attr,= u64 reg) +{ + return (reg & attr->field_mask) >> attr->shift; +} + +static inline u64 cpumod_attr_pack_value(const struct cpumod_attr *attr, u= 64 value) +{ + return (value << attr->shift) & attr->field_mask; +} + +static inline bool cpumod_attr_visible_for_profile(const struct cpumod_att= r *attr, + enum cpumod_profile profile) +{ + switch (profile) { + case CPUMOD_PROFILE_GRACE: + return attr->visible_profiles & CPUMOD_PROFILE_VISIBLE_GRACE; + case CPUMOD_PROFILE_VERA: + return attr->visible_profiles & CPUMOD_PROFILE_VISIBLE_VERA; + default: + return false; + } +} + +#endif /* CPUMOD_INTERNAL_H */ --=20 2.43.0 From nobody Mon Sep 28 05:45:28 2026 Received: from CH4PR04CU002.outbound.protection.outlook.com (mail-northcentralusazon11013026.outbound.protection.outlook.com [40.107.201.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 510863128D7; Tue, 25 Aug 2026 18:17:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.201.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681838; cv=fail; b=kFmiJFAIeI26EQGgQGoE3pryVgKUbYv094YRP5yJNtEha5ynzDuQHK3kithFVYYH8jc29kGvM9Qra51m4cVQOWCYF9A5JOpcsxh7Dlr/kAC3/3b1LAA0WlneQkgXhx4jkolr2LpshKUMNemplmZljLv6Pwq4SB7vsa/X60es08c= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681838; c=relaxed/simple; bh=QCKaHucyq5gI/mYg1SzS624jhqqVx0yCNwsBOWUoxTE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=iWrS+9kBdsnN6garWgLlBtDTIZydQYsvpXzW8yQGwCyXvVdFnrLXDayNSYtpWYRAkmf4eXidOmBs5EIeyzD00I6ksz9cWye4zUDPCnBqpvF2P2tfQLkdcfPtLOv6dvJwmv+9fL7voRi7lQvl57SjFXYwJ+XvP7Gf/gZLTVwI6Tc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=q47PUbny; arc=fail smtp.client-ip=40.107.201.26 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="q47PUbny" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=aDD+FU0+IvuT1geeGoOH4ZTdL5VJSqBKWDnssY0CaJEIVNEkk/zMJG/IKMLBbx/bdPz44jiRjIIhg8KzzDWcdmOw28dMKTrOmf3iEIpp5KKXnQaSQ6tiFrRyCtz4ugfWjIKrU5O0HkDR4ldsZOSYhlZnu9fiOT2YzrHXy3QhltpJYsaV0E/CCwJkfbKe+8cIoE96WKNWp9BxjMj6p3Bn7wzdiRFVfxQY8D0JHe/ZCFcTMyliimM9U6iRboqh0/0RJDUUw0HRbZ3eRPaHOIZuLfC5Kng0vdveDWUXgPvnZm5MCqJ65hccMJxuRSNZjalTNY+dZ0jf+YCuVs7ONGiUsg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=BxQqbuYBKB/rQCG8tD7CF5/rsVfr9eXLg2YoDW/o0x0=; b=P7R+sCns9rwAaETlpuFXCBs/YM8gEZQb8UK7hnbfKUuLSKCiTiYpLffHeLZj3dqikiK0Fv6RiAkitB/DMh8AalS1dMD8LpfLTHQ2pxISR3M7TKzfixFTL3FvaLbd7k66RSW068rNYdVFciAfLFA9ew4bharOdsot56vb3KGvME+HyglU+aUt0Qfko6Yk650y3oxOgJV5PH/L4TPdHx7ztIx+bPFE7bP0cjUbfxHVlP1F4uPmjMzQpq3TF7J3A95GK0dKsWrdPAhGgYptlEbGABEm8PS9izpoZzXgPhcvIsUoN+Vf8iGJJ2XYeL7gBov8Na4Fqlt0E/ygyfLTuhbkdQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=BxQqbuYBKB/rQCG8tD7CF5/rsVfr9eXLg2YoDW/o0x0=; b=q47PUbnyMApnmctJitFYuiT4oWGjPDSOcgUrZX/AmIMw5rtnH02GZ3idVg+P251Ww92m/8Q+Ub9d9o0yrKQsBk3wOFfTV9swV53Y4jxiu3QznlVm3vEk+fQvOOV3ygW30pIndHYfyy1iWdniLHXBuLJ03+1dNK64veEq0+SlRl9i6ewcnPVckv3Xm1Ea/gk5m3qd3rOcJeFtEZRm08pg1rYQRjz5sYaD/yc7VrUwmr7R2X9yKE3EcNbIOnznCVvkPKJo9ao0r853518f8VGPwpt3QmRNzhze3PaCfU0ORmxuoSYhXF2qtZP7PmZcuHaJi8MZN+CY+oFDLTOdJjRa8g== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) by IA0PR12MB7675.namprd12.prod.outlook.com (2603:10b6:208:433::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.7; Tue, 25 Aug 2026 18:17:13 +0000 Received: from CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688]) by CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688%4]) with mapi id 15.21.0339.010; Tue, 25 Aug 2026 18:17:13 +0000 From: Koba Ko To: Catalin Marinas , Will Deacon , Rob Herring , Breno Leitao Cc: Jonathan Corbet , Shuah Khan , linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Fenghua Yu , Tushar Dave , Kai-Heng Feng , rupakr@nvidia.com, mohammedk@nvidia.com Subject: [RFC PATCH v2 2/3] Documentation/arch/arm64: document arm_cpumod Date: Wed, 26 Aug 2026 02:17:00 +0800 Message-ID: <20260825181701.3-3-kobak@nvidia.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260825181701.3-1-kobak@nvidia.com> References: <20260825181701.3-1-kobak@nvidia.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: TPYP295CA0035.TWNP295.PROD.OUTLOOK.COM (2603:1096:7d0:7::7) To CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CHXPR12MB999244:EE_|IA0PR12MB7675:EE_ X-MS-Office365-Filtering-Correlation-Id: 9432af1f-3e20-4d44-9551-08df02d515e1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|7416014|376014|1800799024|23010399003|366016|10067099003|56012099006|11063799006|6133799003|18002099003|22082099003; X-Microsoft-Antispam-Message-Info: Lpyd+LBG8wiBwtqslCkjtdszL6S5keRi8ohvHtJLz64nI37/Yg8VYHeayNV7/ieGbN3p0SWiK1evQrqCGPstgUk5xlgdR2tHvyO32hY9Gt/l60FR2VzK+Q3R+SxhhnrnQtXXBwUqdS2403ew0zmcINFCD8YOft43ZUvPjsKB6Tek+HLSYVl/5PnAK4Q8p1dxNdDKFkBE19QdB/lzdnIyloxJ4x1IdxCZMO7y93jGlLBqa26RzNhM2EcxsJ3929PL6CtiFiG/p49udeFTP1chjhOBrAXi+VaHcbwHSCpLttZ0/Y3pHAFHNQICqwgjE7uAobV+RPOJkkoByglAjTborKDrCF2b6CVPGqVG4Q+rZovfNxNWDTgRpdV6f4Xrs8uswkHudgh/70mvcNg85yk+agd+Rra90qJvCc5hpYSOGFcXCSDYJU3EpufvtFB9EPhQQMKEPYJ5wR0HDXQpblW+SlYzRqYH5elue9xjnpp1arlJnNYMsibFBTyxVldErcbcLJ5j1cSTifnxYsGdf0zENSOQk/bl4W/okT1qkfwUNuWGAw4o6hN2QfPrA/z0aEIrIlykojz+Wyiw7DhEvt4x3KHnD0MHU9rMiEQHsFe8w9ILcaiD5rkpCszSCgb4JCyOjGE7i7tQFwxLu5ZZHMFsZptEK+E6rDDKgjP67AGubM0= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CHXPR12MB999244.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(7416014)(376014)(1800799024)(23010399003)(366016)(10067099003)(56012099006)(11063799006)(6133799003)(18002099003)(22082099003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?wAXX84S49BOZ+k5lxS/09qTD9gJpHXTM3pATXarBgPmJ/SFJWnL0NNMvswo2?= =?us-ascii?Q?HyXJpsyb4xzLvwTxN8fvDgDdL6Hk/wDJFg2yJLZ4QOp+N8ktmctnhbVfKrbj?= =?us-ascii?Q?RkTpO1t4PR7MVppScWOEAzjxIJwQAIc9ZrPZd5CT5bq5xl9WqrU2G/n7VB58?= =?us-ascii?Q?KDDcnmVnljCd0jrRE4xiKgHvON9QHcGjZ65bp9LBHm4peLycevcjUjwvLy2W?= =?us-ascii?Q?UhRCvYkyW2RbNqyHIMyoWuwOeZ+5AKPGYnGdFq8RzeS+OnAzZdZXDHcfgO3R?= =?us-ascii?Q?jYqc4DSuR/PcInaWXnZnTuBNCQkRCbaFW72KB1exUZvctn9ur0DxlioUDyAU?= =?us-ascii?Q?tveW9fH946qxbr+QTH4EgRQGNYThLIKZA/HjFGbO8gkkmxDUOC3+/WZahyNt?= =?us-ascii?Q?snVegbY+cadIXqVv2XG0RF3z05e3QnHsaD4mH0X267vAyjEUsSY0//L9Djdf?= =?us-ascii?Q?Lt55Z6dCv36pwmwTR9IfEUh48VDgOJhi6Flfbu8l4eQ3DQLd+3i1ZXTXXZRQ?= =?us-ascii?Q?0f8r6THIbqgYW/Arsgxfu4UlmB08tTOY5C/pKdzKISuldwcPodEFoHAPUw8A?= =?us-ascii?Q?KMGqLkveGgIV/flZdPsVr1X6yysEOp0Yd5IJSEcy9b2R7FUd+/xfJvsfkPD7?= =?us-ascii?Q?1LYI3f4JfKcO6yjbK7Sa+Mo9MmLvkpLhsi+slOAew2sSYqt6bLQUlSp0BfGA?= =?us-ascii?Q?bLUW4RyxbAeyRBIOnxcZxfbQ1LEto/ho7plqFfdXy8buGE3K0kj+/DUUqHGx?= =?us-ascii?Q?uWa80dgmushR7ALLpW/BZr4Ily6IE4btLyvLV337RrpxLnW/VNKJc6HxpupE?= =?us-ascii?Q?HmlPXYONqge8q0yFDjNDwOYBE1Mo5vPY51vxFhG3stxQJjoaHJ3RvITh6Lp+?= =?us-ascii?Q?aZ+E9sSuwo39v/3jVoIptToB4RD9t4nr612un3Hkdc7mcbBN64sn6VeEqThD?= =?us-ascii?Q?Zw1iIV2UXlxzWKf9rEv3n5XBCTbKmHMOU77Y+L3ct0t9OadLBbfe1JFtPPxe?= =?us-ascii?Q?cem1XCXXPQrARLdDiT0FeFLovMwLP8qQGtyY3Nz8HnNXfQFxQXZ4BpQ9jsVF?= =?us-ascii?Q?wYindTVYf/OTL4Ne9HcpzGbqwqPO2VneSVFbO67M/jaZsKrrEkGrE1PhQfhD?= =?us-ascii?Q?Ra1U8k2lK7G48x1OjNHbB+sUj81Ep+f1YlICznzx9ij+4Z8sthwURea6DB40?= =?us-ascii?Q?AR2HX0nQXYI7BoeWga1YqNluMh58RXZV7fRrzGBv4R9PMgQ8ykrnNhROEzge?= =?us-ascii?Q?GsbN7bTmaIya+eAoxOJ3deHIRVbOg3p9Jtc9r/HlNCfeQ61zdMZfz9EwIOxa?= =?us-ascii?Q?wlzpFmLC1km65T5X1oMlhtnK55BxXJVMLj6wfW7SkdXTgg7BRtubRNTo3ERo?= =?us-ascii?Q?P8jVHDKPqZ9EOardBSAIGkLBGbCJdELEXsySAtFl/tkjw5g2+ZLZW8Aq4gI+?= =?us-ascii?Q?TVjgXVYgIssM/alrd5yvVMgLSpY/rpJPi4LAJqlyO87W6ROkGHek40u6h8st?= =?us-ascii?Q?hQfsu4OKGOQlTQ87kFqRT8ZH8nmSTtatGLid1iRrYKcCc3UUIHvIwP9JieUF?= =?us-ascii?Q?uqOatVhtPTEh1zZE2LYRlzKdS71VdVwkCt+GeScGB6f/sDFfZwoAFYoLbARr?= =?us-ascii?Q?Zy3PgQWDfMS1Fynwru/YIxzaoc7waCAMjgSVvXf+lP0bp8WRWVaMnjZvyFlP?= =?us-ascii?Q?Bk4Z4dRbSA0/p4l42tpXL45Jcxt3pNucS62pMvEHC3rtuYAdd/BWZQAIVIQ9?= =?us-ascii?Q?JNb0Gs6jTw=3D=3D?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 9432af1f-3e20-4d44-9551-08df02d515e1 X-MS-Exchange-CrossTenant-AuthSource: CHXPR12MB999244.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Aug 2026 18:17:12.9347 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: wMW3jiyB6a25wuiFvSKcOiO/GGbCh69p3cTLxTwBcgog6zmz7R+4270l0h5X5cGqmUm6fLG69J+0JrDohdnxJA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA0PR12MB7675 Content-Type: text/plain; charset="utf-8" Document the ABI intent and scope for the new arm64 cpumod interface. The sysfs directory is per-CPU and is created only for recognized Grace and Vera profiles: /sys/devices/system/cpu/cpuN/cpumod/ CPUs with unsupported MIDRs are skipped without blocking module load or CPU hotplug and do not receive a cpumod directory. Describe the common attributes, the Grace- and Vera-specific attributes, and the accepted range for each writable field. Document affected_cpus as world-readable read-only (0444), and the six register-backed controls as root-only read/write (0600). Out-of-range writes are documented as failing before target CPU register state is changed. Document the CPU hotplug lifecycle explicitly: the cpumod directory is present only while a supported CPU is online, is removed when that CPU goes offline, and is recreated when it returns online. Reads and writes therefore require an online target CPU. Also document the firmware prerequisite: EL1 must be allowed to access the relevant implementation control registers. The interface remains an RFC for controlled performance characterization, is not a general production tuning ABI, and should stay disabled by default on production systems. Reviewed-by: Fenghua Yu Reviewed-by: Tushar Dave Reviewed-by: Kai-Heng Feng Signed-off-by: Koba Ko --- .../ABI/testing/sysfs-devices-system-cpu | 77 +++++++++++++++++++ Documentation/arch/arm64/arm-cpumod.rst | 66 ++++++++++++++++ Documentation/arch/arm64/index.rst | 1 + 3 files changed, 144 insertions(+) create mode 100644 Documentation/arch/arm64/arm-cpumod.rst diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documenta= tion/ABI/testing/sysfs-devices-system-cpu index 82d10d556cc89..7275399e33435 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -806,3 +806,80 @@ Date: Nov 2022 Contact: Linux kernel mailing list Description: (RO) the list of CPUs that can be brought online. + +What: /sys/devices/system/cpu/cpuX/cpumod/affected_cpus +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RO) The decimal logical CPU identifier for this cpumod sysfs + instance. The file is present only while CPU X is online and its + MIDR matches a supported Grace or Vera profile. The file is + world-readable and read-only (0444). + +What: /sys/devices/system/cpu/cpuX/cpumod/pf_dis +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Hardware prefetch disable control for CPU X. The file is + present only while a supported Grace or Vera CPU is online. Reads + return a decimal value. Writes accept decimal values 0 and 1. + Invalid writes fail with -EINVAL without changing the target CPU + register. The file is root-only read/write (0600). + +What: /sys/devices/system/cpu/cpuX/cpumod/pf_mode +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Hardware prefetch aggressiveness mode for CPU X. The file is + present only while a supported Grace or Vera CPU is online. Reads + return a decimal value. Writes accept decimal values 0 through 9; + values 10 through 15 are reserved and rejected. Invalid writes fail + with -EINVAL without changing the target CPU register. The file is + root-only read/write (0600). + +What: /sys/devices/system/cpu/cpuX/cpumod/cbusy_filter_threshold +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Grace CPU cache-busy filter threshold. The file is present only + while a supported Grace CPU is online. Reads return a decimal value. + Writes accept decimal values 0 through 3. Invalid writes fail with + -EINVAL without changing the target CPU register. The file is + root-only read/write (0600). + +What: /sys/devices/system/cpu/cpuX/cpumod/cbusy_filter_window +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Grace CPU cache-busy filter window. The file is present only + while a supported Grace CPU is online. Reads return a decimal value. + Writes accept decimal values 0 through 3. Invalid writes fail with + -EINVAL without changing the target CPU register. The file is + root-only read/write (0600). + +What: /sys/devices/system/cpu/cpuX/cpumod/cmc_min_ways +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Grace CPU cache-management minimum-way control. The file is + present only while a supported Grace CPU is online. Reads return a + decimal value. Writes accept decimal values 0 through 7. Invalid + writes fail with -EINVAL without changing the target CPU register. + The file is root-only read/write (0600). + +What: /sys/devices/system/cpu/cpuX/cpumod/l2spr_cmc_max_ways +Date: August 2026 +Contact: Koba Ko +Contact: Linux ARM Kernel mailing list +Description: + (RW) Vera CPU cache-management maximum-way control. The file is + present only while a supported Vera CPU is online. Reads return a + decimal value. Writes accept decimal values 0 through 7. Invalid + writes fail with -EINVAL without changing the target CPU register. + The file is root-only read/write (0600). diff --git a/Documentation/arch/arm64/arm-cpumod.rst b/Documentation/arch/a= rm64/arm-cpumod.rst new file mode 100644 index 0000000000000..cf4ef9baf165f --- /dev/null +++ b/Documentation/arch/arm64/arm-cpumod.rst @@ -0,0 +1,66 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Arm CPU prefetch modulation controls +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``ARM64_CPUMOD`` exposes selected CPU implementation control register fiel= ds +through per-CPU sysfs attributes under:: + + /sys/devices/system/cpu/cpuN/cpumod/ + +The ``cpumod`` directory is created only when the CPU MIDR matches a recog= nized +Grace or Vera profile. CPUs with unsupported MIDRs are skipped without blo= cking +module load or CPU hotplug and do not have a ``cpumod`` directory. + +The interface is intended for controlled performance characterization and +evaluation. It is not intended as a general production tuning ABI, and the +configuration should remain disabled by default on production systems. + +Configuration and placement +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D + +The controls are built when ``CONFIG_ARM64_CPUMOD`` is enabled. The code l= ives +under ``arch/arm64/kernel/`` because the exposed state is CPU implementati= on +control state accessed by the arm64 kernel, similar in placement to other +architecture CPU-facing helpers. + +The ABI remains separate from the existing arm64 ``cpu*/regs`` sysfs files: +``cpumod`` exposes a small set of named, range-checked control fields rath= er +than a general raw register dump. + +CPU hotplug +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +A ``cpumod`` directory is created only for online CPUs with a supported +profile. The directory is removed when a CPU goes offline and recreated wh= en +the CPU comes back online, again only when its MIDR matches a supported +profile. Reads and writes require the target CPU to be online. + +Firmware requirement +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The controls require firmware to permit EL1 reads and writes to the releva= nt +CPU implementation control registers. On systems where firmware traps or +blocks those accesses, the interface cannot be used. + +Sysfs interface +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The ``cpumod`` sysfs attributes are documented in +``Documentation/ABI/testing/sysfs-devices-system-cpu``. +``affected_cpus`` is world-readable and read-only (0444). The register-bac= ked +controls are root-only read/write files (0600). + +Invalid writes +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Writes outside the documented range fail with ``-EINVAL`` before the remot= e CPU +register state is updated. + +Open register inventory +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The first RFC intentionally exposes only the currently validated subset. A +complete documented-field inventory, including L2CDP, should be reviewed b= efore +adding further ABI nodes. diff --git a/Documentation/arch/arm64/index.rst b/Documentation/arch/arm64/= index.rst index 98052b4ef4a1e..9e085ce55d9c4 100644 --- a/Documentation/arch/arm64/index.rst +++ b/Documentation/arch/arm64/index.rst @@ -8,6 +8,7 @@ ARM64 Architecture :maxdepth: 1 =20 acpi_object_usage + arm-cpumod amu arm-acpi arm-cca --=20 2.43.0 From nobody Mon Sep 28 05:45:28 2026 Received: from CH4PR04CU002.outbound.protection.outlook.com (mail-northcentralusazon11013026.outbound.protection.outlook.com [40.107.201.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA8A23314C3; Tue, 25 Aug 2026 18:17:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.201.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681843; cv=fail; b=GHAHmjs3T7iXy48YmDBtVwk1EoAym2CDVxwMIOsoeOmfRxrLmzO5I5PeVTGen+H8bX98ugv8MOws1UK9hF2N+YyY+Ktr1ZRxAKTn4FblnzH5fcS0/FlALp13jiT4Ax2C6lFQHETM3iq1jgd43k4rE8nV+jyB9isKuld8l/55Qks= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787681843; c=relaxed/simple; bh=pOavo+i65gZARQvRZFsVJTHP7GYZI+d/ajkYIYunhzw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=FM5xAefVaQr0OcGlk8VdSI+g2Ty7gjVKxrrFUD699H88xFB+FQxFwKzvWQ5wCVipaIjz3Dlof0Wi5zTCz8bkxSBBSRuHsJRQ5d5liyZmZMFrjTXQZap9Cr6du6clZhhe0oMOkoDwE2/i64omfVtA73kvBtQjiPUXiX6TYfSe1Mo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=sRLHNC78; arc=fail smtp.client-ip=40.107.201.26 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="sRLHNC78" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=YVoPWY+znckJ6ndTUwDukk5xIPBmptXLbYAB75epqNCMBrg78XTE6AIynQ2rbyfdHMrDLRMQ3tcZkLIabTeLtQrlLgz1cQF5A4v5ZQRHmGZOYHItRUl0/nXp6jmsljVZ3/jK4vZiVAB4QCDlIw1aWeQjTt+Mg6aG8/1fJR+rjF/Ujc5ctEtyLElUoYSs7cR3eVPTJLLSyItIsN4Z+R+3p+rA4+FBwOxy11UIlrq1kU9Evous1Nkh3RLxjycReLTBv00OAhKUEGT0zYRIE3Izp0mXrZKQsfVfVZt6opxMR54wcqhzgRJIcXHYCe61rwpZfR1iYFgOf45g0O/Zvk97Ew== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=kCRxdJOd6DMi9nCjxkxgjOEpdGeOFFnQxgkkKHVyZqw=; b=BDTD5QLuimVpj1pQlXISbmkDnqjAsbfKBqVKh3eR8e7LGNqv9Jz4u8PJqnsno1ZRCockARHHIMZsWCJsM33OloaTwbdrCcGzH92h2VS4jfhL2G40X+RA9WVyb1oSKC96Wt/ShdyFc1OFKY5U7TyyoCtTgEu+9QKMOE3gkSVf6/osNTX7aogE5SCH3o+i+ExvK9voEdFyPFM9mQzopUIpib6MEHk0FBFnIKfn27u9P6AVbCUr8J3mlCJgseBpMuaA7awqlRYuk4yopE1M1yAqCUH/UCF+SB7nRO7rWW9/IyqMnVX+olTlZxy138Q3H5cG2DUAWGbSi42dxxUjRfaq9g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=kCRxdJOd6DMi9nCjxkxgjOEpdGeOFFnQxgkkKHVyZqw=; b=sRLHNC78mycT+NHLdqqoDBtUGHt8QImExI7pzXB5TuOppReMtGrhgpb2ymEo7neQ0X50Es/omZslmQOocv2aRziVQhlb5sOmjzLmJ6IgRZrQ4dKEMvU0HnqX5vrphLKKaJZquDqS3DCl/+PDp2s+GmiAdco59aH9/EeyQLWXImMEXg8pCTQySXFa6OUu1Ik6Gid5tIH4vJC69q11zNss0P4PgAqp+Qkluqmi/GS6RL810MLWnUF1iwwObjD4EBjzFc4RvJU8CdwygH6L/BPSQynKzTKO3llOVWMdAC5v0mww58tc3Rfxz349NspgE6G9B3bJPmMtS7f/SSdy/fBUnw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) by IA0PR12MB7675.namprd12.prod.outlook.com (2603:10b6:208:433::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.360.7; Tue, 25 Aug 2026 18:17:17 +0000 Received: from CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688]) by CHXPR12MB999244.namprd12.prod.outlook.com ([fe80::168f:599c:f74d:7688%4]) with mapi id 15.21.0339.010; Tue, 25 Aug 2026 18:17:17 +0000 From: Koba Ko To: Catalin Marinas , Will Deacon , Rob Herring , Breno Leitao Cc: Jonathan Corbet , Shuah Khan , linux-arm-kernel@lists.infradead.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, Fenghua Yu , Tushar Dave , Kai-Heng Feng , rupakr@nvidia.com, mohammedk@nvidia.com Subject: [RFC PATCH v2 3/3] selftests: arm64: add arm_cpumod kselftest Date: Wed, 26 Aug 2026 02:17:01 +0800 Message-ID: <20260825181701.3-4-kobak@nvidia.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260825181701.3-1-kobak@nvidia.com> References: <20260825181701.3-1-kobak@nvidia.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: TPYP295CA0022.TWNP295.PROD.OUTLOOK.COM (2603:1096:7d0:a::18) To CHXPR12MB999244.namprd12.prod.outlook.com (2603:10b6:610:2fc::17) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CHXPR12MB999244:EE_|IA0PR12MB7675:EE_ X-MS-Office365-Filtering-Correlation-Id: 2612a2dc-ed63-4529-fdbf-08df02d51895 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|7416014|376014|1800799024|23010399003|366016|10067099003|56012099006|11063799006|6133799003|18002099003|22082099003; X-Microsoft-Antispam-Message-Info: uYjoHcCU/6AYL/cOK3KJR2+HyHb8OXZFgKKFXE6+xd30S20bGyIO9ux3/ElZbVJOWDdXituOTMspKuR2MwHtpi1elmi1PdY0LH5kejIyXSHHg++qkhVHoOPctGEiHeWda/0nEhqUu77njbjj69DLX0jWMM3pcIMRYhDOJRG/bBvdF4XcJn6Cy9jZYCJMcaNftJ/91CVZDac9AqAnDtREg4oQ+vl05f14bl2v6bc9AyU6ucmCAFw2cX11QqvWF83m3Xjvsh6eJwVdrOiOxSKBg8m31CoZxa0meeXfD3icx6kMs11cQo/Yxjzi0yR8D2SVDeSU0SF1zqmZ8hXYD6Y2yy7u+lngyOMN+ML2viAUrBnSrUbDy46idOIeHGXH44EZapS8jLaPz5hFkHEY6uKvUqdfx2EG2OuI+fxDKMFkaI8O3lbvUfgBtAD2l4+dgaTlUl5w6TegSrJVze8j3fis4TeERjHaG3pwagmrShfsP0ex2uFRq4/nWVCZ1QzfUtz+GKvTAW9oD+LM8Tmnx2/Jk+RWHOx7yheuUwsw09EiRcz/uninHQg9U/L0o1EPWW1nGAHrQ4+gHDuLIKMVjBkb7vPM2MI3yujzXzKuNFm6qHs739JXgt1U1d/ps9BJv6s3h5XuNKtKOCuxMjLTdfNN0ckMqybEbdMjWCnqMXC/f3o= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CHXPR12MB999244.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(7416014)(376014)(1800799024)(23010399003)(366016)(10067099003)(56012099006)(11063799006)(6133799003)(18002099003)(22082099003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?dVYhwYKK5TIDcc+3Rkjy9JEJ8pfRLFZnhF2Zhcbc891h/Nwc6J3+sTz99kd7?= =?us-ascii?Q?PkyteK/JiHg8dT/TVawgUOEUD4evHxaRW89+kh735vDc61pj4taJUn5Zy7rC?= =?us-ascii?Q?6z640dGFrtmTWnj9yzHeG+yeqaDrbTzW61wPiRMzKzsMuaSCBXAJyuPZ2n8w?= =?us-ascii?Q?IfborT6GMCxTePT2jtd3Laahfo4q3g3nTg54FxNNFycmWXZoFgoj2+8sXePY?= =?us-ascii?Q?JDfEPdK4uSYV34Rdp6SO3uaXuCrz2Gz0NM3a0wQqhS22TxF/sgIGF+yJbLSy?= =?us-ascii?Q?nmmuzk80u0cF2GpaIzaDb02f/RHZP8Kbe8vETF6+WeeCYh53kV5KanXL/Nuo?= =?us-ascii?Q?DbY8+yn0liMQmAfczwjIIrPNBU6rG9M00iJhWTHQu+sjRTx4YZX0ilB37NQa?= =?us-ascii?Q?tZxEM6TgKdFlLzQPxfjyVGg+8YzmP1s12FYufIjJIT1rU2tTGxW56d1VYH80?= =?us-ascii?Q?V3Cxy93Cpq5KCuuwsFE2ChJYmZlDSVDooeFmkO1pkgmIi50I9TWO3fzz6C5w?= =?us-ascii?Q?JLNkLSj0EOxgjsBtuVsI+7KfOHOAgfscO46HfxEDMQQbAzpsp/tV7+EHPCjK?= =?us-ascii?Q?f3lG3jWLqYL3EeO4MFkTVdao1PiOp64/LpquYCZ8jDzZx048YZf6B38BcN4D?= =?us-ascii?Q?J4/mpgW1rhKWBg74t5xxpyZYmYqKFgJxxoq2BHnlKWNwEqh7LWszdKSKlFqN?= =?us-ascii?Q?PCobtJ69ExrLcbtUBpZxbZIVopFoLNC/rG9xFncFUIbTSqu0o7uiGIpRpYHf?= =?us-ascii?Q?aTuuByH20DGkz0Zcoh3e3kxs15AgSxcOPwd0fXLizrheUy6RnyWUaSN47HsY?= =?us-ascii?Q?UZRvahPLeBOtdhwC/5n8nY+5FlmvWvWg/6hfWGgJPLGUzKRxk8cKRcqqfv7V?= =?us-ascii?Q?I5zqA26g3DYFrZhk8dg501ksVlKjPRBF7slC0S7RFHCZR8ntZsZOLZoRwtrW?= =?us-ascii?Q?FFpSRcURE+vSMdvfYME8wgktTbKuAMZrVbLRyeQgOt6CIYFZLr8Fuvas+E6/?= =?us-ascii?Q?empte7IuCAJQmORAN2Iy6zFJNprWSf2i8GgYXbNBGTuiLCPeQroNiYEQNcKC?= =?us-ascii?Q?P+/wdmljdSO7JHC/vWOFvsh95lToTddiBYjPPzRlcjRddsnwu4qXc1nOt7+u?= =?us-ascii?Q?PhWUkecQyxRkziLVjPLSxC5qEcU7QEGwwOTePy0Fq8OmhTfnYlYQxClnxsdk?= =?us-ascii?Q?IMwvezYU12dyapaUEGzmmFXvryhjIpzlNAyJ7vIylSQdmOQvJlRIav/oyFgV?= =?us-ascii?Q?GlC4lt0bP4wyD9TfZQZ5f2tyRoOR0a8j6cMi/RN0fp6pwUDou7camIYRVJ2i?= =?us-ascii?Q?gVT85lObb90+L4BGRHDnN5cho3UgLkfvrp9HD2sVX69juTIy4Nb0L+4eyu1J?= =?us-ascii?Q?6u1OhWE2xWE/hr9ufvj90n2g8JuUf6W9LF5LSY5jf8bhv3qCxkCNHnkQg1Ar?= =?us-ascii?Q?iePEJQMdwnSiSUagEgIzYalahTm4E3+LFheAjQr7SCtVSG4Pe5VNOzRxJTc1?= =?us-ascii?Q?FAkFfLjNWtWZ+90scHRLENtZi3vtE1dMmBC8J4nZC7wPVygxGHPWHX0pydPJ?= =?us-ascii?Q?I+YOwrT9zHMVCBaIa8R+h77N3KrNtjoDY2wHhs+aHAvwWqjSbiyIQ52eP85O?= =?us-ascii?Q?hcGvfSKCzfKA8HG6yKEyuyPAgO3iQ4boumKX4avPFAGJQkqqb8sNUCTjUMSz?= =?us-ascii?Q?njhdXrO0Xe6BmArNeotqD90MMZ9P+U4fYxcFCBuZzi99lIitkpS5kD47iYOi?= =?us-ascii?Q?F1JEJt1yCw=3D=3D?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2612a2dc-ed63-4529-fdbf-08df02d51895 X-MS-Exchange-CrossTenant-AuthSource: CHXPR12MB999244.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Aug 2026 18:17:17.4598 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: iY2WeoZljh9DZxvaK3HVkg08Ioq++dqSu2kYr9zcUav6hVwHTj1vVarnNEKIFugDwPyZQX49Rbq2gdjKtrsUKw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: IA0PR12MB7675 Content-Type: text/plain; charset="utf-8" Add a kselftest for the arm_cpumod sysfs ABI. The test loads or reuses the module and selects either one target CPU or a snapshot of all online CPUs. In single-CPU mode, skip an offline target before module validation. Observe the per-CPU online attribute first. If it is absent, immediately require the CPU directory to still exist before treating the CPU as non-hotpluggable and online. If the attribute exists, it must be readable and contain 1. After module load, recheck the online state before classifying a selected CPU without a cpumod directory as an unsupported profile. Fail if the CPU went offline or disappeared, log and skip CPUs that remain online without a directory, and require at least one selected CPU to expose a supported Grace or Vera profile. Return KSFT_SKIP if none do. For supported CPUs, recognize the exposed Grace or Vera profile-specific layout and validate the matching attributes. Only ARM_CPUMOD_EXPECT_PROFILE supplies an external profile expectation. Verify read paths by checking that attributes return decimal values and that affected_cpus identifies the current CPU as a decimal CPU number. Validate affected_cpus mode 444 and every visible register-backed control mode 600 with stat before functional write checks. Optional valid-write testing writes each current value back. Optional invalid-write testing checks selected out-of-range values and confirms that the prior value remains unchanged. Both write paths are opt-in. Valid writeback can alter implementation-defined CPU controls; invalid-write checks exercise rejection through the same firmware-sensitive interface, but are expected not to update state. Cover all-online-CPU mode through /sys/devices/system/cpu/online and, when the test loaded the module, verify cleanup by removing it and checking that the selected CPU subtrees disappear. The test does not offline or online CPUs. Reviewed-by: Fenghua Yu Reviewed-by: Tushar Dave Reviewed-by: Kai-Heng Feng Signed-off-by: Koba Ko --- tools/testing/selftests/arm64/Makefile | 2 +- .../selftests/arm64/arm_cpumod/Makefile | 5 + .../selftests/arm64/arm_cpumod/arm_cpumod.sh | 543 ++++++++++++++++++ 3 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/arm_cpumod/Makefile create mode 100755 tools/testing/selftests/arm64/arm_cpumod/arm_cpumod.sh diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftes= ts/arm64/Makefile index e456f3b62fa13..0873be5af0369 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -4,7 +4,7 @@ ARCH ?=3D $(shell uname -m 2>/dev/null || echo not) =20 ifneq (,$(filter $(ARCH),aarch64 arm64)) -ARM64_SUBTARGETS ?=3D tags signal pauth fp mte bti abi gcs + ARM64_SUBTARGETS ?=3D tags signal pauth fp mte bti abi gcs arm_cpumod else ARM64_SUBTARGETS :=3D endif diff --git a/tools/testing/selftests/arm64/arm_cpumod/Makefile b/tools/test= ing/selftests/arm64/arm_cpumod/Makefile new file mode 100644 index 0000000000000..db0341cdbc621 --- /dev/null +++ b/tools/testing/selftests/arm64/arm_cpumod/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +TEST_PROGS :=3D arm_cpumod.sh + +include ../../lib.mk diff --git a/tools/testing/selftests/arm64/arm_cpumod/arm_cpumod.sh b/tools= /testing/selftests/arm64/arm_cpumod/arm_cpumod.sh new file mode 100755 index 0000000000000..a161d2012df4a --- /dev/null +++ b/tools/testing/selftests/arm64/arm_cpumod/arm_cpumod.sh @@ -0,0 +1,543 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only + +readonly KSFT_SKIP=3D4 +readonly MODULE_NAME=3D"arm_cpumod" +readonly CPU_ID=3D"${ARM_CPUMOD_CPU:-0}" +readonly MODULE_PATH=3D"${ARM_CPUMOD_KO:-}" +readonly ENABLE_WRITES=3D"${ARM_CPUMOD_ENABLE_WRITES:-0}" +readonly ENABLE_INVALID_WRITES=3D"${ARM_CPUMOD_ENABLE_INVALID_WRITES:-0}" +readonly ALL_CPUS=3D"${ARM_CPUMOD_ALL_CPUS:-0}" +readonly EXPECT_PROFILE=3D"${ARM_CPUMOD_EXPECT_PROFILE:-auto}" +readonly CPU_ONLINE_PATH=3D"/sys/devices/system/cpu/online" +readonly COMMON_ATTRS=3D(affected_cpus pf_dis pf_mode) +readonly COMMON_RW_ATTRS=3D(pf_dis pf_mode) +readonly GRACE_ATTRS=3D(cbusy_filter_threshold cbusy_filter_window cmc_min= _ways) +readonly GRACE_RW_ATTRS=3D(cbusy_filter_threshold cbusy_filter_window cmc_= min_ways) +readonly VERA_ATTRS=3D(l2spr_cmc_max_ways) +readonly VERA_RW_ATTRS=3D(l2spr_cmc_max_ways) + +loaded_by_test=3D0 +reused_loaded_module=3D0 +detected_profile=3D"" +current_cpu=3D"${CPU_ID}" +verified_cpus=3D0 +CPU_LIST=3D() + +log() +{ + echo "${MODULE_NAME} selftest: $*" +} + +skip() +{ + log "$*" + exit "${KSFT_SKIP}" +} + +fail() +{ + log "$*" + exit 1 +} + +cpu_dir() +{ + local cpu=3D"$1" + + printf '/sys/devices/system/cpu/cpu%s' "${cpu}" +} + +cpu_is_online() +{ + local cpu=3D"$1" + local dir=3D"$(cpu_dir "${cpu}")" + local online=3D"${dir}/online" + local value + + if [ ! -e "${online}" ]; then + [ -d "${dir}" ] || return 1 + return 0 + fi + + [ -r "${online}" ] || return 1 + value=3D$(cat "${online}") || return 1 + [ "${value}" =3D "1" ] +} + +cpumod_dir() +{ + local cpu=3D"$1" + + printf '%s/cpumod' "$(cpu_dir "${cpu}")" +} + +current_cpumod_dir() +{ + cpumod_dir "${current_cpu}" +} + +cleanup() +{ + if [ "${loaded_by_test}" -eq 1 ]; then + rmmod "${MODULE_NAME}" >/dev/null 2>&1 || true + fi +} +trap cleanup EXIT + +require_cmd() +{ + command -v "$1" >/dev/null 2>&1 || skip "missing required command: $1" +} + +module_loaded() +{ + grep -q "^${MODULE_NAME} " /proc/modules +} + +wait_for_path_state() +{ + local expect_present=3D"$1" + local target=3D"$2" + local i + + for i in $(seq 1 50); do + if [ "${expect_present}" =3D "present" ]; then + [ -e "${target}" ] && return 0 + else + [ ! -e "${target}" ] && return 0 + fi + sleep 0.1 + done + + return 1 +} + +read_attr() +{ + local attr=3D"$1" + local file=3D"$(current_cpumod_dir)/${attr}" + local value + + value=3D$(cat "${file}") || fail "failed to read ${file}" + printf '%s' "${value}" +} + +attr_exists() +{ + local attr=3D"$1" + + [ -f "$(current_cpumod_dir)/${attr}" ] +} + +require_attr_present() +{ + local attr=3D"$1" + + attr_exists "${attr}" || fail "missing sysfs attribute $(current_cpumod_d= ir)/${attr}" +} + +require_attr_absent() +{ + local attr=3D"$1" + + attr_exists "${attr}" && fail "unexpected sysfs attribute $(current_cpumo= d_dir)/${attr}" +} + +check_decimal_attr() +{ + local attr=3D"$1" + local value + + value=3D$(read_attr "${attr}") + case "${value}" in + ''|*[!0-9]*) + fail "${attr} returned non-decimal value: ${value}" + ;; + esac +} + +check_attr_mode() +{ + local attr=3D"$1" + local expected=3D"$2" + local file=3D"$(current_cpumod_dir)/${attr}" + local mode + + mode=3D$(stat -c %a "${file}") || fail "failed to stat ${file}" + [ "${mode}" =3D "${expected}" ] || \ + fail "cpu${current_cpu}/${attr} mode expected ${expected}, got ${mode}" +} + +expand_online_cpus() +{ + local cpu_list=3D"$1" + local part + local start + local start_num + local end + local end_num + local cpu + local parts + + CPU_LIST=3D() + IFS=3D, read -ra parts <<< "${cpu_list}" + for part in "${parts[@]}"; do + part=3D"${part//[[:space:]]/}" + [ -n "${part}" ] || fail "empty CPU range in ${cpu_list}" + + if [[ "${part}" =3D=3D *-* ]]; then + start=3D"${part%-*}" + end=3D"${part#*-}" + [[ "${start}" =3D~ ^[0-9]+$ ]] || fail "invalid CPU range start: ${part= }" + [[ "${end}" =3D~ ^[0-9]+$ ]] || fail "invalid CPU range end: ${part}" + start_num=3D$((10#${start})) + end_num=3D$((10#${end})) + (( start_num <=3D end_num )) || fail "invalid descending CPU range: ${p= art}" + + for ((cpu =3D start_num; cpu <=3D end_num; cpu++)); do + CPU_LIST+=3D("${cpu}") + done + else + [[ "${part}" =3D~ ^[0-9]+$ ]] || fail "invalid CPU entry: ${part}" + CPU_LIST+=3D("$((10#${part}))") + fi + done + + [ "${#CPU_LIST[@]}" -gt 0 ] || fail "no online CPUs parsed from ${cpu_lis= t}" +} + +select_cpus() +{ + if [ "${ALL_CPUS}" =3D "1" ]; then + [ -r "${CPU_ONLINE_PATH}" ] || skip "missing ${CPU_ONLINE_PATH}" + expand_online_cpus "$(cat "${CPU_ONLINE_PATH}")" + else + cpu_is_online "${CPU_ID}" || \ + skip "cpu${CPU_ID} is offline or its online state is unreadable" + CPU_LIST=3D("${CPU_ID}") + fi +} + +detect_profile() +{ + local has_grace + local has_vera + local attr + local i + local dir=3D"$(current_cpumod_dir)" + + for i in $(seq 1 50); do + has_grace=3D0 + has_vera=3D0 + + for attr in "${GRACE_ATTRS[@]}"; do + if attr_exists "${attr}"; then + has_grace=3D1 + break + fi + done + + for attr in "${VERA_ATTRS[@]}"; do + if attr_exists "${attr}"; then + has_vera=3D1 + break + fi + done + + if [ "${has_grace}" -eq 1 ] && [ "${has_vera}" -eq 1 ]; then + fail "mixed Grace/Vera-specific attributes under ${dir}" + fi + + if [ "${has_grace}" -eq 1 ]; then + detected_profile=3D"grace" + break + fi + + if [ "${has_vera}" -eq 1 ]; then + detected_profile=3D"vera" + break + fi + + sleep 0.1 + done + + [ -n "${detected_profile}" ] || \ + fail "unable to detect Grace or Vera sysfs layout under ${dir}" + + if [ "${EXPECT_PROFILE}" !=3D "auto" ] && [ "${EXPECT_PROFILE}" !=3D "${d= etected_profile}" ]; then + fail "expected ${EXPECT_PROFILE} sysfs layout, detected ${detected_profi= le}" + fi +} + +check_requirements() +{ + case "${CPU_ID}" in + ''|*[!0-9]*) + fail "ARM_CPUMOD_CPU must be a decimal CPU index" + ;; + esac + + case "${EXPECT_PROFILE}" in + auto|grace|vera) + ;; + *) + fail "ARM_CPUMOD_EXPECT_PROFILE must be auto, grace, or vera" + ;; + esac + + case "${ENABLE_WRITES}" in + 0|1) + ;; + *) + fail "ARM_CPUMOD_ENABLE_WRITES must be 0 or 1" + ;; + esac + + case "${ENABLE_INVALID_WRITES}" in + 0|1) + ;; + *) + fail "ARM_CPUMOD_ENABLE_INVALID_WRITES must be 0 or 1" + ;; + esac + + case "${ALL_CPUS}" in + 0|1) + ;; + *) + fail "ARM_CPUMOD_ALL_CPUS must be 0 or 1" + ;; + esac + + [ "$(uname -m)" =3D "aarch64" ] || skip "requires an arm64 host" + [ "$(id -u)" -eq 0 ] || skip "must be run as root" + select_cpus + for current_cpu in "${CPU_LIST[@]}"; do + [ -d "$(cpu_dir "${current_cpu}")" ] || skip "missing CPU directory $(cp= u_dir "${current_cpu}")" + done + require_cmd rmmod + require_cmd stat + + if [ -n "${MODULE_PATH}" ]; then + [ -r "${MODULE_PATH}" ] || skip "ARM_CPUMOD_KO is not readable: ${MODULE= _PATH}" + require_cmd insmod + else + require_cmd modprobe + modprobe -n "${MODULE_NAME}" >/dev/null 2>&1 || \ + skip "set ARM_CPUMOD_KO or install ${MODULE_NAME} into /lib/modules" + fi +} + +load_module() +{ + if module_loaded; then + if [ -n "${MODULE_PATH}" ]; then + skip "${MODULE_NAME} is already loaded; unload it before using ARM_CPUM= OD_KO" + fi + + reused_loaded_module=3D1 + log "${MODULE_NAME} already loaded, reusing existing instance" + return 0 + fi + + if [ -n "${MODULE_PATH}" ]; then + insmod "${MODULE_PATH}" || fail "insmod failed for ${MODULE_PATH}" + module_loaded || fail "${MODULE_NAME} did not appear in /proc/modules af= ter insmod" + else + modprobe "${MODULE_NAME}" || fail "modprobe failed for ${MODULE_NAME}" + module_loaded || skip "${MODULE_NAME} did not appear in /proc/modules af= ter modprobe; built-in or non-unloadable setup is unsupported" + fi + + loaded_by_test=3D1 +} + +check_sysfs_layout() +{ + local attr + local expected_cpu + local value + local dir=3D"$(current_cpumod_dir)" + + wait_for_path_state present "${dir}" || \ + fail "cpumod sysfs directory did not appear at ${dir}" + + for attr in "${COMMON_ATTRS[@]}"; do + require_attr_present "${attr}" + done + + detect_profile + + case "${detected_profile}" in + grace) + for attr in "${GRACE_ATTRS[@]}"; do + require_attr_present "${attr}" + done + for attr in "${VERA_ATTRS[@]}"; do + require_attr_absent "${attr}" + done + ;; + vera) + for attr in "${GRACE_ATTRS[@]}"; do + require_attr_absent "${attr}" + done + for attr in "${VERA_ATTRS[@]}"; do + require_attr_present "${attr}" + done + ;; + esac + + expected_cpu=3D$((10#${current_cpu})) + value=3D$(read_attr affected_cpus) + [ "${value}" =3D "${expected_cpu}" ] || \ + fail "cpu${current_cpu} affected_cpus expected ${expected_cpu}, got ${va= lue}" + check_attr_mode affected_cpus 444 + + for attr in "${COMMON_RW_ATTRS[@]}"; do + check_attr_mode "${attr}" 600 + check_decimal_attr "${attr}" + done + + case "${detected_profile}" in + grace) + for attr in "${GRACE_RW_ATTRS[@]}"; do + check_attr_mode "${attr}" 600 + check_decimal_attr "${attr}" + done + ;; + vera) + for attr in "${VERA_RW_ATTRS[@]}"; do + check_attr_mode "${attr}" 600 + check_decimal_attr "${attr}" + done + ;; + esac +} + +writeback_same_value() +{ + local attr=3D"$1" + local before + local after + + before=3D$(read_attr "${attr}") + printf '%s\n' "${before}" > "$(current_cpumod_dir)/${attr}" || \ + fail "failed to write back cpu${current_cpu}/${attr}=3D${before}" + after=3D$(read_attr "${attr}") + [ "${after}" =3D "${before}" ] || \ + fail "cpu${current_cpu}/${attr} changed across writeback: before=3D${bef= ore} after=3D${after}" +} + +check_writeback_same_value() +{ + local attr + + [ "${ENABLE_WRITES}" =3D "1" ] || return 0 + + for attr in "${COMMON_RW_ATTRS[@]}"; do + writeback_same_value "${attr}" + done + + case "${detected_profile}" in + grace) + for attr in "${GRACE_RW_ATTRS[@]}"; do + writeback_same_value "${attr}" + done + ;; + vera) + for attr in "${VERA_RW_ATTRS[@]}"; do + writeback_same_value "${attr}" + done + ;; + esac +} + +check_invalid_write() +{ + local attr=3D"$1" + local value=3D"$2" + local before + local after + local file=3D"$(current_cpumod_dir)/${attr}" + + before=3D$(read_attr "${attr}") + if printf '%s\n' "${value}" > "${file}" 2>/dev/null; then + fail "cpu${current_cpu}/${attr} accepted invalid value ${value}" + fi + after=3D$(read_attr "${attr}") + [ "${after}" =3D "${before}" ] || \ + fail "cpu${current_cpu}/${attr} changed after invalid write: before=3D${= before} after=3D${after}" +} + +check_invalid_writes() +{ + [ "${ENABLE_INVALID_WRITES}" =3D "1" ] || return 0 + + check_invalid_write pf_dis 2 + check_invalid_write pf_mode 10 + check_invalid_write pf_mode 99 + + case "${detected_profile}" in + grace) + check_invalid_write cbusy_filter_threshold 4 + check_invalid_write cbusy_filter_window 4 + check_invalid_write cmc_min_ways 8 + ;; + vera) + check_invalid_write l2spr_cmc_max_ways 8 + ;; + esac +} + +check_cpu() +{ + current_cpu=3D"$1" + detected_profile=3D"" + check_sysfs_layout + log "cpu${current_cpu}: detected ${detected_profile} sysfs layout" + check_writeback_same_value + check_invalid_writes +} + +unload_and_verify_cleanup() +{ + local cpu + local dir + + if [ "${loaded_by_test}" -ne 1 ]; then + log "module was already loaded; leaving it in place and skipping unload = cleanup check" + return 0 + fi + + rmmod "${MODULE_NAME}" || fail "rmmod failed for ${MODULE_NAME}" + loaded_by_test=3D0 + for cpu in "${CPU_LIST[@]}"; do + dir=3D"$(cpumod_dir "${cpu}")" + wait_for_path_state absent "${dir}" || \ + fail "${dir} still present after unload" + done +} + +check_requirements +load_module +for current_cpu in "${CPU_LIST[@]}"; do + if [ ! -d "$(cpumod_dir "${current_cpu}")" ]; then + cpu_is_online "${current_cpu}" || \ + fail "cpu${current_cpu} went offline or its online state became unreada= ble" + log "cpu${current_cpu}: no cpumod directory; unsupported CPU profile, sk= ipping" + continue + fi + + check_cpu "${current_cpu}" + verified_cpus=3D$((verified_cpus + 1)) +done +unload_and_verify_cleanup +[ "${verified_cpus}" -gt 0 ] || \ + skip "no selected CPU exposes a supported cpumod profile" + +if [ "${reused_loaded_module}" -eq 1 ]; then + log "PASS verified_cpus=3D${verified_cpus} expect_profile=3D${EXPECT_PROF= ILE} writes=3D${ENABLE_WRITES} invalid_writes=3D${ENABLE_INVALID_WRITES} al= l_cpus=3D${ALL_CPUS} (reused pre-loaded module; unload cleanup check skippe= d)" +else + log "PASS verified_cpus=3D${verified_cpus} expect_profile=3D${EXPECT_PROF= ILE} writes=3D${ENABLE_WRITES} invalid_writes=3D${ENABLE_INVALID_WRITES} al= l_cpus=3D${ALL_CPUS}" +fi +exit 0 --=20 2.43.0