From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D3261225A5B; Fri, 22 Aug 2025 15:31:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876673; cv=none; b=goEUFPjq227wDToVh18g5yn6pIwruM0O85vsMiBf2rHrSmipSYdoYfB6cMuM6YkPALi2KQK/2E0b22d+wP0Ij9hf5+fdjQUoABtFz6gMultUV535FS930G/Yw1pXuUjNrRUjXaUGh9KVIIAwkfYzyiU9rlI3wYLrI43CED0rrfQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876673; c=relaxed/simple; bh=9QqXXJStw71ZU66vliOsj/UdHd+j+97HYjUzko96Bhw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HsTWau6uvV86CwvdThdw2i7Ta6XD7M3YFCNvevNGpozf0AzBm8dY7xBkNY+J4hBsu35isiyAWUlW1Xmy4GpL2sjSJMJQVMxgRB0F1Av4rAaa0Rm6AIHbDxo8nM4UI0W3kZZPWtFl43D6ynqN0gjinUWFixAkL0CmqUOgGLeOxdQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id A680827DC; Fri, 22 Aug 2025 08:31:02 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 970AB3F63F; Fri, 22 Aug 2025 08:31:05 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 01/33] cacheinfo: Expose the code to generate a cache-id from a device_node Date: Fri, 22 Aug 2025 15:29:42 +0000 Message-Id: <20250822153048.2287-2-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MPAM driver identifies caches by id for use with resctrl. It needs to know the cache-id when probe-ing, but the value isn't set in cacheinfo until device_initcall(). Expose the code that generates the cache-id. The parts of the MPAM driver that run early can use this to set up the resctrl structures before cacheinfo is ready in device_initcall(). Signed-off-by: James Morse --- Changes since v1: * Renamed cache_of_get_id() cache_of_calculate_id(). --- drivers/base/cacheinfo.c | 19 +++++++++++++------ include/linux/cacheinfo.h | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 613410705a47..f6289d142ba9 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -207,11 +207,10 @@ static bool match_cache_node(struct device_node *cpu, #define arch_compact_of_hwid(_x) (_x) #endif =20 -static void cache_of_set_id(struct cacheinfo *this_leaf, - struct device_node *cache_node) +unsigned long cache_of_calculate_id(struct device_node *cache_node) { struct device_node *cpu; - u32 min_id =3D ~0; + unsigned long min_id =3D ~0UL; =20 for_each_of_cpu_node(cpu) { u64 id =3D of_get_cpu_hwid(cpu, 0); @@ -219,15 +218,23 @@ static void cache_of_set_id(struct cacheinfo *this_le= af, id =3D arch_compact_of_hwid(id); if (FIELD_GET(GENMASK_ULL(63, 32), id)) { of_node_put(cpu); - return; + return ~0UL; } =20 if (match_cache_node(cpu, cache_node)) min_id =3D min(min_id, id); } =20 - if (min_id !=3D ~0) { - this_leaf->id =3D min_id; + return min_id; +} + +static void cache_of_set_id(struct cacheinfo *this_leaf, + struct device_node *cache_node) +{ + unsigned long id =3D cache_of_calculate_id(cache_node); + + if (id !=3D ~0UL) { + this_leaf->id =3D id; this_leaf->attributes |=3D CACHE_ID; } } diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index c8f4f0a0b874..2dcbb69139e9 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -112,6 +112,7 @@ int acpi_get_cache_info(unsigned int cpu, #endif =20 const struct attribute_group *cache_get_priv_group(struct cacheinfo *this_= leaf); +unsigned long cache_of_calculate_id(struct device_node *np); =20 /* * Get the cacheinfo structure for the cache associated with @cpu at --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A6BBF223DDA; Fri, 22 Aug 2025 15:34:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876866; cv=none; b=KcLRBtdVsUQOwNcxMRg/yrQeuxC2IEBdDWs3BIVRwyd6yXs6nRO1wTwBCnaZcDypPenDfp/Po+daNmmtUNQx4UF8b1ulW9UzgEGWu1VtL27I17xiVSPlZaNM2TDafr4Z6QC+BGUT2YBGUxjc4oJBPPJFQBW+njdvJNVKeEYau/8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876866; c=relaxed/simple; bh=9QqXXJStw71ZU66vliOsj/UdHd+j+97HYjUzko96Bhw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=o+ZTkI0Vl2+LHjHzQvkcGheVOoht/66u1dbjdS/tc6Q1kR4A6IHLkL6e0yB4b7uY7FGhx2cWV7J21h/OYLPYLDD+d4JwjFl5Mzi5QCMTKEYMM7IlXKBV/vSqDli+f4nug8QUY3Crs1xogdRt125XNhnnySdZC6wJGPxPk5PkJDY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B5CEA15A1; Fri, 22 Aug 2025 08:34:15 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 9F3ED3F63F; Fri, 22 Aug 2025 08:34:18 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 01/33] cacheinfo: Expose the code to generate a cache-id from a device_node Date: Fri, 22 Aug 2025 15:30:16 +0000 Message-Id: <20250822153048.2287-36-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MPAM driver identifies caches by id for use with resctrl. It needs to know the cache-id when probe-ing, but the value isn't set in cacheinfo until device_initcall(). Expose the code that generates the cache-id. The parts of the MPAM driver that run early can use this to set up the resctrl structures before cacheinfo is ready in device_initcall(). Signed-off-by: James Morse --- Changes since v1: * Renamed cache_of_get_id() cache_of_calculate_id(). --- drivers/base/cacheinfo.c | 19 +++++++++++++------ include/linux/cacheinfo.h | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index 613410705a47..f6289d142ba9 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -207,11 +207,10 @@ static bool match_cache_node(struct device_node *cpu, #define arch_compact_of_hwid(_x) (_x) #endif =20 -static void cache_of_set_id(struct cacheinfo *this_leaf, - struct device_node *cache_node) +unsigned long cache_of_calculate_id(struct device_node *cache_node) { struct device_node *cpu; - u32 min_id =3D ~0; + unsigned long min_id =3D ~0UL; =20 for_each_of_cpu_node(cpu) { u64 id =3D of_get_cpu_hwid(cpu, 0); @@ -219,15 +218,23 @@ static void cache_of_set_id(struct cacheinfo *this_le= af, id =3D arch_compact_of_hwid(id); if (FIELD_GET(GENMASK_ULL(63, 32), id)) { of_node_put(cpu); - return; + return ~0UL; } =20 if (match_cache_node(cpu, cache_node)) min_id =3D min(min_id, id); } =20 - if (min_id !=3D ~0) { - this_leaf->id =3D min_id; + return min_id; +} + +static void cache_of_set_id(struct cacheinfo *this_leaf, + struct device_node *cache_node) +{ + unsigned long id =3D cache_of_calculate_id(cache_node); + + if (id !=3D ~0UL) { + this_leaf->id =3D id; this_leaf->attributes |=3D CACHE_ID; } } diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index c8f4f0a0b874..2dcbb69139e9 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -112,6 +112,7 @@ int acpi_get_cache_info(unsigned int cpu, #endif =20 const struct attribute_group *cache_get_priv_group(struct cacheinfo *this_= leaf); +unsigned long cache_of_calculate_id(struct device_node *np); =20 /* * Get the cacheinfo structure for the cache associated with @cpu at --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 38A3126F2BF; Fri, 22 Aug 2025 15:34:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876871; cv=none; b=lzpXBac2mFNZzF3HQU5VxCKA49LC8sTVDUkN/qX9Cxhn3hreTeEkfb7jJXwgg6Mok/FOAHQ0RvsCr+Szgjb13YW7YBh4y4YasFrHkDQzekNKA9MGbuGypLqT848ANyBDbFu9kuXBItbZW5Cqvvn4Xv0gU3SEFyhRVLKyU3CL0r4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876871; c=relaxed/simple; bh=JNA8Ju3yLzQ5/rM7Bi6nt9PNOG5Nj2I/hDTMXTvlsIg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=B/MrhNlDqR6O0kju1Zrc2Kf+IXc17VYqSnXi2eijFBwNZf4qTsIZa5hkpI/5dgtEbgKfyY2bo+mx0+1Rm4RqUxbSD8crC0hbUKMPwIQu+EUs1J2GqUbWGEr6Rb7kPX/N/tYcZ8xr8eI/SxXrhte93snoyGTRWvWuvg1rj3SYznQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 5061C15A1; Fri, 22 Aug 2025 08:34:21 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 42F7A3F63F; Fri, 22 Aug 2025 08:34:24 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 02/33] drivers: base: cacheinfo: Add helper to find the cache size from cpu+level Date: Fri, 22 Aug 2025 15:30:17 +0000 Message-Id: <20250822153048.2287-37-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM needs to know the size of a cache associated with a particular CPU. The DT/ACPI agnostic way of doing this is to ask cacheinfo. Add a helper to do this. Signed-off-by: James Morse --- Changes since v1: * Converted to kdoc. * Simplified helper to use get_cpu_cacheinfo_level(). --- include/linux/cacheinfo.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index 2dcbb69139e9..e12d6f2c6a57 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -148,6 +148,21 @@ static inline int get_cpu_cacheinfo_id(int cpu, int le= vel) return ci ? ci->id : -1; } =20 +/** + * get_cpu_cacheinfo_size() - Get the size of the cache. + * @cpu: The cpu that is associated with the cache. + * @level: The level of the cache as seen by @cpu. + * + * Callers must hold the cpuhp lock. + * Returns the cache-size on success, or 0 for an error. + */ +static inline unsigned int get_cpu_cacheinfo_size(int cpu, int level) +{ + struct cacheinfo *ci =3D get_cpu_cacheinfo_level(cpu, level); + + return ci ? ci->size : 0; +} + #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) #define use_arch_cache_info() (true) #else --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 99683214A91; Fri, 22 Aug 2025 15:31:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876679; cv=none; b=o9MRDfEDgSzH3tmHugepRxDrt8may88a5eEG86xNBwT5i7DEVHo2O9Xvwcpf1VzQ6z9YLBehxxbdyF4ATBmpStzFcIfxJlNG7SBNCoO60DJKjw0UbbSUR3kBMhjIv6KAlnZtn2sv401M7EdXHrPAmJHPiKAQmutz7Do+Qip25t8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876679; c=relaxed/simple; bh=JNA8Ju3yLzQ5/rM7Bi6nt9PNOG5Nj2I/hDTMXTvlsIg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mUhQ5tlcyhVEKDsYTNKytYdAKokOym0T5WjjxLKMoCfEh4HXpnWj6CMXZltmsfCDc5na8/u/sMrY7aKZ0jQidOMuGLHw74lc1YFUB0YEUMIRZ+NVyisPNvdXMxv+3U55X/MApHMtJrh9gZyHplFpnNgEW+RcGv/96ebvA+4TRp8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D773F15A1; Fri, 22 Aug 2025 08:31:08 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id CAA2C3F63F; Fri, 22 Aug 2025 08:31:11 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 02/33] drivers: base: cacheinfo: Add helper to find the cache size from cpu+level Date: Fri, 22 Aug 2025 15:29:43 +0000 Message-Id: <20250822153048.2287-3-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM needs to know the size of a cache associated with a particular CPU. The DT/ACPI agnostic way of doing this is to ask cacheinfo. Add a helper to do this. Signed-off-by: James Morse --- Changes since v1: * Converted to kdoc. * Simplified helper to use get_cpu_cacheinfo_level(). --- include/linux/cacheinfo.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index 2dcbb69139e9..e12d6f2c6a57 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -148,6 +148,21 @@ static inline int get_cpu_cacheinfo_id(int cpu, int le= vel) return ci ? ci->id : -1; } =20 +/** + * get_cpu_cacheinfo_size() - Get the size of the cache. + * @cpu: The cpu that is associated with the cache. + * @level: The level of the cache as seen by @cpu. + * + * Callers must hold the cpuhp lock. + * Returns the cache-size on success, or 0 for an error. + */ +static inline unsigned int get_cpu_cacheinfo_size(int cpu, int level) +{ + struct cacheinfo *ci =3D get_cpu_cacheinfo_level(cpu, level); + + return ci ? ci->size : 0; +} + #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) #define use_arch_cache_info() (true) #else --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BE6402652B4; Fri, 22 Aug 2025 15:34:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876877; cv=none; b=Za3zQzFyoEZEgO0xN14yMSlXN85gDvfWSk0raXqbRIy9y6i38tseKMFPFmHkcf6hVT1otMwaKo/i1BjI3FS9oQi8rauTHuahRr06tCTcJkox4XNQgaRyFiWgmt3bY2p031xIT24RDGBHzPAp+j7rFsU9r0vViZXOKz2Olv26L3Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876877; c=relaxed/simple; bh=gEyQcZZsP23ow9G5sDRvS21MvQKVxhDWe31++cbp5L0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JaBwx32YtMaE3G2Qrw4AhB12FNg3Z0lsyza2qPwFyLse6JRZP01fjaYCW1CxqKmaF8pWJ1LWeqcZTi8bx8OAPPx3cz31fyip9dhrWvdqKJB3lb9rKTz9Rkio0ODHl547FWC1pmY1WnW/YtYny/smF1NyGdKE8C9wJ3Y6PdftGak= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E05DB27DC; Fri, 22 Aug 2025 08:34:26 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D25AC3F63F; Fri, 22 Aug 2025 08:34:29 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 03/33] ACPI / PPTT: Add a helper to fill a cpumask from a processor container Date: Fri, 22 Aug 2025 15:30:18 +0000 Message-Id: <20250822153048.2287-38-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The PPTT describes CPUs and caches, as well as processor containers. The ACPI table for MPAM describes the set of CPUs that can access an MSC with the UID of a processor container. Add a helper to find the processor container by its id, then walk the possible CPUs to fill a cpumask with the CPUs that have this processor container as a parent. CC: Dave Martin Reviewed-by: Sudeep Holla Signed-off-by: James Morse --- Changes since RFC: * Dropped has_leaf_flag dodging of acpi_pptt_leaf_node() * Added missing : in kernel-doc * Made helper return void as this never actually returns an error. --- drivers/acpi/pptt.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 3 ++ 2 files changed, 89 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 54676e3d82dd..4791ca2bdfac 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -298,6 +298,92 @@ static struct acpi_pptt_processor *acpi_find_processor= _node(struct acpi_table_he return NULL; } =20 +/** + * acpi_pptt_get_child_cpus() - Find all the CPUs below a PPTT processor n= ode + * @table_hdr: A reference to the PPTT table. + * @parent_node: A pointer to the processor node in the @table_hdr. + * @cpus: A cpumask to fill with the CPUs below @parent_node. + * + * Walks up the PPTT from every possible CPU to find if the provided + * @parent_node is a parent of this CPU. + */ +static void acpi_pptt_get_child_cpus(struct acpi_table_header *table_hdr, + struct acpi_pptt_processor *parent_node, + cpumask_t *cpus) +{ + struct acpi_pptt_processor *cpu_node; + u32 acpi_id; + int cpu; + + cpumask_clear(cpus); + + for_each_possible_cpu(cpu) { + acpi_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table_hdr, acpi_id); + + while (cpu_node) { + if (cpu_node =3D=3D parent_node) { + cpumask_set_cpu(cpu, cpus); + break; + } + cpu_node =3D fetch_pptt_node(table_hdr, cpu_node->parent); + } + } +} + +/** + * acpi_pptt_get_cpus_from_container() - Populate a cpumask with all CPUs = in a + * processor containers + * @acpi_cpu_id: The UID of the processor container. + * @cpus: The resulting CPU mask. + * + * Find the specified Processor Container, and fill @cpus with all the cpus + * below it. + * + * Not all 'Processor' entries in the PPTT are either a CPU or a Processor + * Container, they may exist purely to describe a Private resource. CPUs + * have to be leaves, so a Processor Container is a non-leaf that has the + * 'ACPI Processor ID valid' flag set. + * + * Return: 0 for a complete walk, or an error if the mask is incomplete. + */ +void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus) +{ + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table_hdr; + struct acpi_subtable_header *entry; + unsigned long table_end; + acpi_status status; + bool leaf_flag; + u32 proc_sz; + + cpumask_clear(cpus); + + status =3D acpi_get_table(ACPI_SIG_PPTT, 0, &table_hdr); + if (ACPI_FAILURE(status)) + return; + + table_end =3D (unsigned long)table_hdr + table_hdr->length; + entry =3D ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, + sizeof(struct acpi_table_pptt)); + proc_sz =3D sizeof(struct acpi_pptt_processor); + while ((unsigned long)entry + proc_sz <=3D table_end) { + cpu_node =3D (struct acpi_pptt_processor *)entry; + if (entry->type =3D=3D ACPI_PPTT_TYPE_PROCESSOR && + cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) { + leaf_flag =3D acpi_pptt_leaf_node(table_hdr, cpu_node); + if (!leaf_flag) { + if (cpu_node->acpi_processor_id =3D=3D acpi_cpu_id) + acpi_pptt_get_child_cpus(table_hdr, cpu_node, cpus); + } + } + entry =3D ACPI_ADD_PTR(struct acpi_subtable_header, entry, + entry->length); + } + + acpi_put_table(table_hdr); +} + static u8 acpi_cache_type(enum cache_type type) { switch (type) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 1c5bb1e887cd..f97a9ff678cc 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1541,6 +1541,7 @@ int find_acpi_cpu_topology(unsigned int cpu, int leve= l); int find_acpi_cpu_topology_cluster(unsigned int cpu); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); +void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1562,6 +1563,8 @@ static inline int find_acpi_cpu_topology_hetero_id(un= signed int cpu) { return -EINVAL; } +static inline void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, + cpumask_t *cpus) { } #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 048F9221714; Fri, 22 Aug 2025 15:31:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876685; cv=none; b=It0ftigCLqDWHxpR84dA9ViouHjce/qgKCRP2J3hp0Vr6DmHdcOz47iGfgjFx7+DmholAWwXGtzF5ixWn0YkXXsUc104931RI3IWX4NgJiWoEh0xDuHroYP+pW55bSLl6tcCAIwERmnJ8f90t8D5nhwJEGdgRJMsmjma+Iycths= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876685; c=relaxed/simple; bh=gEyQcZZsP23ow9G5sDRvS21MvQKVxhDWe31++cbp5L0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sP8oyJMfPQm6+n7shkHVrsMHnAHnRictJjImCglzBYzFQoBKiK0qdB/pHibzMgJpqcKcjBqpIZ9dXzJ6YmJMCKFttcznJeFS1VJxPp2CEJDtFKPjAqkrtcGaLvny10swd+DTKGZ7I13B5Gt+XPOyLyP7v+8onlbPUhooK0vmZ6E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0F6E327DC; Fri, 22 Aug 2025 08:31:15 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EDF8F3F63F; Fri, 22 Aug 2025 08:31:17 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 03/33] ACPI / PPTT: Add a helper to fill a cpumask from a processor container Date: Fri, 22 Aug 2025 15:29:44 +0000 Message-Id: <20250822153048.2287-4-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The PPTT describes CPUs and caches, as well as processor containers. The ACPI table for MPAM describes the set of CPUs that can access an MSC with the UID of a processor container. Add a helper to find the processor container by its id, then walk the possible CPUs to fill a cpumask with the CPUs that have this processor container as a parent. CC: Dave Martin Reviewed-by: Sudeep Holla Signed-off-by: James Morse --- Changes since RFC: * Dropped has_leaf_flag dodging of acpi_pptt_leaf_node() * Added missing : in kernel-doc * Made helper return void as this never actually returns an error. --- drivers/acpi/pptt.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 3 ++ 2 files changed, 89 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 54676e3d82dd..4791ca2bdfac 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -298,6 +298,92 @@ static struct acpi_pptt_processor *acpi_find_processor= _node(struct acpi_table_he return NULL; } =20 +/** + * acpi_pptt_get_child_cpus() - Find all the CPUs below a PPTT processor n= ode + * @table_hdr: A reference to the PPTT table. + * @parent_node: A pointer to the processor node in the @table_hdr. + * @cpus: A cpumask to fill with the CPUs below @parent_node. + * + * Walks up the PPTT from every possible CPU to find if the provided + * @parent_node is a parent of this CPU. + */ +static void acpi_pptt_get_child_cpus(struct acpi_table_header *table_hdr, + struct acpi_pptt_processor *parent_node, + cpumask_t *cpus) +{ + struct acpi_pptt_processor *cpu_node; + u32 acpi_id; + int cpu; + + cpumask_clear(cpus); + + for_each_possible_cpu(cpu) { + acpi_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table_hdr, acpi_id); + + while (cpu_node) { + if (cpu_node =3D=3D parent_node) { + cpumask_set_cpu(cpu, cpus); + break; + } + cpu_node =3D fetch_pptt_node(table_hdr, cpu_node->parent); + } + } +} + +/** + * acpi_pptt_get_cpus_from_container() - Populate a cpumask with all CPUs = in a + * processor containers + * @acpi_cpu_id: The UID of the processor container. + * @cpus: The resulting CPU mask. + * + * Find the specified Processor Container, and fill @cpus with all the cpus + * below it. + * + * Not all 'Processor' entries in the PPTT are either a CPU or a Processor + * Container, they may exist purely to describe a Private resource. CPUs + * have to be leaves, so a Processor Container is a non-leaf that has the + * 'ACPI Processor ID valid' flag set. + * + * Return: 0 for a complete walk, or an error if the mask is incomplete. + */ +void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus) +{ + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table_hdr; + struct acpi_subtable_header *entry; + unsigned long table_end; + acpi_status status; + bool leaf_flag; + u32 proc_sz; + + cpumask_clear(cpus); + + status =3D acpi_get_table(ACPI_SIG_PPTT, 0, &table_hdr); + if (ACPI_FAILURE(status)) + return; + + table_end =3D (unsigned long)table_hdr + table_hdr->length; + entry =3D ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr, + sizeof(struct acpi_table_pptt)); + proc_sz =3D sizeof(struct acpi_pptt_processor); + while ((unsigned long)entry + proc_sz <=3D table_end) { + cpu_node =3D (struct acpi_pptt_processor *)entry; + if (entry->type =3D=3D ACPI_PPTT_TYPE_PROCESSOR && + cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) { + leaf_flag =3D acpi_pptt_leaf_node(table_hdr, cpu_node); + if (!leaf_flag) { + if (cpu_node->acpi_processor_id =3D=3D acpi_cpu_id) + acpi_pptt_get_child_cpus(table_hdr, cpu_node, cpus); + } + } + entry =3D ACPI_ADD_PTR(struct acpi_subtable_header, entry, + entry->length); + } + + acpi_put_table(table_hdr); +} + static u8 acpi_cache_type(enum cache_type type) { switch (type) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 1c5bb1e887cd..f97a9ff678cc 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1541,6 +1541,7 @@ int find_acpi_cpu_topology(unsigned int cpu, int leve= l); int find_acpi_cpu_topology_cluster(unsigned int cpu); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); +void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1562,6 +1563,8 @@ static inline int find_acpi_cpu_topology_hetero_id(un= signed int cpu) { return -EINVAL; } +static inline void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, + cpumask_t *cpus) { } #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7536226B759; Fri, 22 Aug 2025 15:34:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876882; cv=none; b=ekC+D8jbTZv5yug8zcAYwQRqosbuRqQ93+dW8XkbqhJHdgIpgu0n2RjeBvm61vjMew2ZAS34b6/5mzAfMdspZst86acg+znsQvC388fUFScsj+Sa0Nus1E9pQKDFLNyIt9SBS9TbFHIZEosvRxwjePZCOtgieZLe2JMccilIpvo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876882; c=relaxed/simple; bh=1K3aJddFM7bxDXTS+/2zYCwPDK3GKzRZLqmbZHvYc/U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HWhmAcG0kjnGTa4eCsFIfO56C4w6xLf5Rz7BEPuoiNlr5tZXBdHaUn/ew5N7yfqobB/g9zU92rRyF8oRZrYY+lt1ZCYZLmyuu96jYSWS9bmwqanGGp9zOoU3z02rlL7P4RetWLksxbsEkWueOs3x9cTvcGwLcw+cQ70sTR6xUaM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 818A015A1; Fri, 22 Aug 2025 08:34:32 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 703143F63F; Fri, 22 Aug 2025 08:34:35 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 04/33] ACPI / PPTT: Stop acpi_count_levels() expecting callers to clear levels Date: Fri, 22 Aug 2025 15:30:19 +0000 Message-Id: <20250822153048.2287-39-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" acpi_count_levels() passes the number of levels back via a pointer argument. It also passes this to acpi_find_cache_level() as the starting_level, and preserves this value as it walks up the cpu_node tree counting the levels. This means the caller must initialise 'levels' due to acpi_count_levels() internals. The only caller acpi_get_cache_info() happens to have already initialised levels to zero, which acpi_count_levels() depends on to get the correct result. Two results are passed back from acpi_count_levels(), unlike split_levels, levels is not optional. Split these two results up. The mandatory 'levels' is always returned, which hides the internal details from the caller, and avoids having duplicated initialisation in all callers. split_levels remains an optional argument passed back. Suggested-by: Jonathan Cameron Signed-off-by: James Morse Reviewed-by: Lorenzo Pieralisi --- Changes since RFC: * Made acpi_count_levels() return the levels value. --- drivers/acpi/pptt.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 4791ca2bdfac..8f9b9508acba 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -181,10 +181,10 @@ acpi_find_cache_level(struct acpi_table_header *table= _hdr, * levels and split cache levels (data/instruction). * @table_hdr: Pointer to the head of the PPTT table * @cpu_node: processor node we wish to count caches for - * @levels: Number of levels if success. * @split_levels: Number of split cache levels (data/instruction) if - * success. Can by NULL. + * success. Can be NULL. * + * Returns number of levels. * Given a processor node containing a processing unit, walk into it and c= ount * how many levels exist solely for it, and then walk up each level until = we hit * the root node (ignore the package level because it may be possible to h= ave @@ -192,14 +192,18 @@ acpi_find_cache_level(struct acpi_table_header *table= _hdr, * split cache levels (data/instruction) that exist at each level on the w= ay * up. */ -static void acpi_count_levels(struct acpi_table_header *table_hdr, - struct acpi_pptt_processor *cpu_node, - unsigned int *levels, unsigned int *split_levels) +static int acpi_count_levels(struct acpi_table_header *table_hdr, + struct acpi_pptt_processor *cpu_node, + unsigned int *split_levels) { + int starting_level =3D 0; + do { - acpi_find_cache_level(table_hdr, cpu_node, levels, split_levels, 0, 0); + acpi_find_cache_level(table_hdr, cpu_node, &starting_level, split_levels= , 0, 0); cpu_node =3D fetch_pptt_node(table_hdr, cpu_node->parent); } while (cpu_node); + + return starting_level; } =20 /** @@ -731,7 +735,7 @@ int acpi_get_cache_info(unsigned int cpu, unsigned int = *levels, if (!cpu_node) return -ENOENT; =20 - acpi_count_levels(table, cpu_node, levels, split_levels); + *levels =3D acpi_count_levels(table, cpu_node, split_levels); =20 pr_debug("Cache Setup: last_level=3D%d split_levels=3D%d\n", *levels, split_levels ? *split_levels : -1); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A39AE221714; Fri, 22 Aug 2025 15:31:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876691; cv=none; b=h1Zb6z0pM1Ahnz6tfii6whwVTa/jTjqECzrrljh8k12ZU5IIAzvy1/dCvChlv08HLJ6xxbKSj9AETNwD91ozbwk49dW/hCd+Bvhl4b2CMr72YTL6GxHdzG6oQrSfu9Py4RcE8OeHdxoN/w/88COcsQXMpLmjhePWnX9i7DUHNPE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876691; c=relaxed/simple; bh=1K3aJddFM7bxDXTS+/2zYCwPDK3GKzRZLqmbZHvYc/U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=CspvCtDDXnqvYRmDw41WIFdujirC+CM0QqOvsQk57lHc9vJCXzymn4nv1d7BbBCkWecDeRZEfpTzK44JpCze36SYenBZppKpbaWN8EVaZhXaji6OwiW4MCQ/6nRrwC4a81kLLQXUbA6VsDz4cFEf0gR1DpBzWN6i+g8K1m5Ycf8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BE21027DD; Fri, 22 Aug 2025 08:31:20 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 951433F63F; Fri, 22 Aug 2025 08:31:23 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 04/33] ACPI / PPTT: Stop acpi_count_levels() expecting callers to clear levels Date: Fri, 22 Aug 2025 15:29:45 +0000 Message-Id: <20250822153048.2287-5-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" acpi_count_levels() passes the number of levels back via a pointer argument. It also passes this to acpi_find_cache_level() as the starting_level, and preserves this value as it walks up the cpu_node tree counting the levels. This means the caller must initialise 'levels' due to acpi_count_levels() internals. The only caller acpi_get_cache_info() happens to have already initialised levels to zero, which acpi_count_levels() depends on to get the correct result. Two results are passed back from acpi_count_levels(), unlike split_levels, levels is not optional. Split these two results up. The mandatory 'levels' is always returned, which hides the internal details from the caller, and avoids having duplicated initialisation in all callers. split_levels remains an optional argument passed back. Suggested-by: Jonathan Cameron Signed-off-by: James Morse --- Changes since RFC: * Made acpi_count_levels() return the levels value. --- drivers/acpi/pptt.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 4791ca2bdfac..8f9b9508acba 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -181,10 +181,10 @@ acpi_find_cache_level(struct acpi_table_header *table= _hdr, * levels and split cache levels (data/instruction). * @table_hdr: Pointer to the head of the PPTT table * @cpu_node: processor node we wish to count caches for - * @levels: Number of levels if success. * @split_levels: Number of split cache levels (data/instruction) if - * success. Can by NULL. + * success. Can be NULL. * + * Returns number of levels. * Given a processor node containing a processing unit, walk into it and c= ount * how many levels exist solely for it, and then walk up each level until = we hit * the root node (ignore the package level because it may be possible to h= ave @@ -192,14 +192,18 @@ acpi_find_cache_level(struct acpi_table_header *table= _hdr, * split cache levels (data/instruction) that exist at each level on the w= ay * up. */ -static void acpi_count_levels(struct acpi_table_header *table_hdr, - struct acpi_pptt_processor *cpu_node, - unsigned int *levels, unsigned int *split_levels) +static int acpi_count_levels(struct acpi_table_header *table_hdr, + struct acpi_pptt_processor *cpu_node, + unsigned int *split_levels) { + int starting_level =3D 0; + do { - acpi_find_cache_level(table_hdr, cpu_node, levels, split_levels, 0, 0); + acpi_find_cache_level(table_hdr, cpu_node, &starting_level, split_levels= , 0, 0); cpu_node =3D fetch_pptt_node(table_hdr, cpu_node->parent); } while (cpu_node); + + return starting_level; } =20 /** @@ -731,7 +735,7 @@ int acpi_get_cache_info(unsigned int cpu, unsigned int = *levels, if (!cpu_node) return -ENOENT; =20 - acpi_count_levels(table, cpu_node, levels, split_levels); + *levels =3D acpi_count_levels(table, cpu_node, split_levels); =20 pr_debug("Cache Setup: last_level=3D%d split_levels=3D%d\n", *levels, split_levels ? *split_levels : -1); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 45B79210F59; Fri, 22 Aug 2025 15:31:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876696; cv=none; b=nEKh3MbWjZyp18OJEKjRJmOkWJ8j9elrp9kN2Bu37Z3vAJuH97M0WP6SMtoVNS6+xrby1iOLzq3FPkh7aQvxwloW5nBy//y2G3OS3Yw+M6RZ6WjOXvrAsODGl+DFc+N2KJUM37gJG9tu2lIuDhA7gOpiUuVzmnC0s6hsddlJ3qw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876696; c=relaxed/simple; bh=S+jcvWCNj5jcUsaoRVJ88AiLLSmqJm96NQfZbZ9tiOU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UJcoKS7aTbv2w+snukFGuzTy30i0G4D7hNAVW/k4iSLJ91G7gsHvocnQAMpE6Go3GRl55o8txXwyNQNUAxIT/KzJqwzcM3Fcv3ItjQdPNo5LeY5bsXM04uYjjBeW0hk7ib8dWwZ5pUq/sf9R4faUVRj06zw3F1sXstHxcNS746Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 63A14288E; Fri, 22 Aug 2025 08:31:26 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4CD053F63F; Fri, 22 Aug 2025 08:31:29 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 05/33] ACPI / PPTT: Find cache level by cache-id Date: Fri, 22 Aug 2025 15:29:46 +0000 Message-Id: <20250822153048.2287-6-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MPAM table identifies caches by id. The MPAM driver also wants to know the cache level to determine if the platform is of the shape that can be managed via resctrl. Cacheinfo has this information, but only for CPUs that are online. Waiting for all CPUs to come online is a problem for platforms where CPUs are brought online late by user-space. Add a helper that walks every possible cache, until it finds the one identified by cache-id, then return the level. Add a cleanup based free-ing mechanism for acpi_get_table(). CC: Jonathan Cameron Signed-off-by: James Morse --- Changes since RFC: * acpi_count_levels() now returns a value. * Converted the table-get stuff to use Jonathan's cleanup helper. * Dropped Sudeep's Review tag due to the cleanup change. --- drivers/acpi/pptt.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 17 ++++++++++++ 2 files changed, 81 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 8f9b9508acba..660457644a5b 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -907,3 +907,67 @@ int find_acpi_cpu_topology_hetero_id(unsigned int cpu) return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE, ACPI_PPTT_ACPI_IDENTICAL); } + +/** + * find_acpi_cache_level_from_id() - Get the level of the specified cache + * @cache_id: The id field of the unified cache + * + * Determine the level relative to any CPU for the unified cache identifie= d by + * cache_id. This allows the property to be found even if the CPUs are off= line. + * + * The returned level can be used to group unified caches that are peers. + * + * The PPTT table must be rev 3 or later, + * + * If one CPUs L2 is shared with another as L3, this function will return + * an unpredictable value. + * + * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found. + * Otherwise returns a value which represents the level of the specified c= ache. + */ +int find_acpi_cache_level_from_id(u32 cache_id) +{ + u32 acpi_cpu_id; + int level, cpu, num_levels; + struct acpi_pptt_cache *cache; + struct acpi_pptt_cache_v1 *cache_v1; + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_table_ret= (ACPI_SIG_PPTT, 0); + + if (IS_ERR(table)) + return PTR_ERR(table); + + if (table->revision < 3) + return -ENOENT; + + /* + * If we found the cache first, we'd still need to walk from each CPU + * to find the level... + */ + for_each_possible_cpu(cpu) { + acpi_cpu_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table, acpi_cpu_id); + if (!cpu_node) + return -ENOENT; + num_levels =3D acpi_count_levels(table, cpu_node, NULL); + + /* Start at 1 for L1 */ + for (level =3D 1; level <=3D num_levels; level++) { + cache =3D acpi_find_cache_node(table, acpi_cpu_id, + ACPI_PPTT_CACHE_TYPE_UNIFIED, + level, &cpu_node); + if (!cache) + continue; + + cache_v1 =3D ACPI_ADD_PTR(struct acpi_pptt_cache_v1, + cache, + sizeof(struct acpi_pptt_cache)); + + if (cache->flags & ACPI_PPTT_CACHE_ID_VALID && + cache_v1->cache_id =3D=3D cache_id) + return level; + } + } + + return -ENOENT; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index f97a9ff678cc..30c10b1dcdb2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -8,6 +8,7 @@ #ifndef _LINUX_ACPI_H #define _LINUX_ACPI_H =20 +#include #include #include /* for struct resource */ #include @@ -221,6 +222,17 @@ void acpi_reserve_initial_tables (void); void acpi_table_init_complete (void); int acpi_table_init (void); =20 +static inline struct acpi_table_header *acpi_get_table_ret(char *signature= , u32 instance) +{ + struct acpi_table_header *table; + int status =3D acpi_get_table(signature, instance, &table); + + if (ACPI_FAILURE(status)) + return ERR_PTR(-ENOENT); + return table; +} +DEFINE_FREE(acpi_table, struct acpi_table_header *, if (!IS_ERR(_T)) acpi_= put_table(_T)) + int acpi_table_parse(char *id, acpi_tbl_table_handler handler); int __init_or_acpilib acpi_table_parse_entries(char *id, unsigned long table_size, int entry_id, @@ -1542,6 +1554,7 @@ int find_acpi_cpu_topology_cluster(unsigned int cpu); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); +int find_acpi_cache_level_from_id(u32 cache_id); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1565,6 +1578,10 @@ static inline int find_acpi_cpu_topology_hetero_id(u= nsigned int cpu) } static inline void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus) { } +static inline int find_acpi_cache_level_from_id(u32 cache_id) +{ + return -EINVAL; +} #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F237A274B56; Fri, 22 Aug 2025 15:34:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876888; cv=none; b=N2dC5xdwei45WNZfcSZ1W04vzNKNl1I3kgnrDPQFXJBo/xABPve/U21RBoamGA7FNLl/Qw1JPpNvWrYsIHsPShncQlV+qk8Qujqs9RKIZ5A7nsfoa1HahtPOBOH0Rm0Ym3P30J8s3HaRqIi3AEpM40sDN7zZNUYvUsQzgxpYfwM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876888; c=relaxed/simple; bh=S+jcvWCNj5jcUsaoRVJ88AiLLSmqJm96NQfZbZ9tiOU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lwyvlS+NbiWJktJr6rawRXE7lGBJCVyjjOz1+cNfBvhzyN5l0dUoORXG4tINIhv44uvVh08TXJouHJ9wHlzzkRRo/2Z0fy45bmkqQ/xQQdglOqCIuwPlTOu5uNNzjDX9mFa9sBd2k6KRIV31rrvm9VkoPYEUyzlq+mjKmS7yzB4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 22C742880; Fri, 22 Aug 2025 08:34:38 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 149D23F63F; Fri, 22 Aug 2025 08:34:40 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 05/33] ACPI / PPTT: Find cache level by cache-id Date: Fri, 22 Aug 2025 15:30:20 +0000 Message-Id: <20250822153048.2287-40-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MPAM table identifies caches by id. The MPAM driver also wants to know the cache level to determine if the platform is of the shape that can be managed via resctrl. Cacheinfo has this information, but only for CPUs that are online. Waiting for all CPUs to come online is a problem for platforms where CPUs are brought online late by user-space. Add a helper that walks every possible cache, until it finds the one identified by cache-id, then return the level. Add a cleanup based free-ing mechanism for acpi_get_table(). CC: Jonathan Cameron Signed-off-by: James Morse --- Changes since RFC: * acpi_count_levels() now returns a value. * Converted the table-get stuff to use Jonathan's cleanup helper. * Dropped Sudeep's Review tag due to the cleanup change. --- drivers/acpi/pptt.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 17 ++++++++++++ 2 files changed, 81 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 8f9b9508acba..660457644a5b 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -907,3 +907,67 @@ int find_acpi_cpu_topology_hetero_id(unsigned int cpu) return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE, ACPI_PPTT_ACPI_IDENTICAL); } + +/** + * find_acpi_cache_level_from_id() - Get the level of the specified cache + * @cache_id: The id field of the unified cache + * + * Determine the level relative to any CPU for the unified cache identifie= d by + * cache_id. This allows the property to be found even if the CPUs are off= line. + * + * The returned level can be used to group unified caches that are peers. + * + * The PPTT table must be rev 3 or later, + * + * If one CPUs L2 is shared with another as L3, this function will return + * an unpredictable value. + * + * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found. + * Otherwise returns a value which represents the level of the specified c= ache. + */ +int find_acpi_cache_level_from_id(u32 cache_id) +{ + u32 acpi_cpu_id; + int level, cpu, num_levels; + struct acpi_pptt_cache *cache; + struct acpi_pptt_cache_v1 *cache_v1; + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_table_ret= (ACPI_SIG_PPTT, 0); + + if (IS_ERR(table)) + return PTR_ERR(table); + + if (table->revision < 3) + return -ENOENT; + + /* + * If we found the cache first, we'd still need to walk from each CPU + * to find the level... + */ + for_each_possible_cpu(cpu) { + acpi_cpu_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table, acpi_cpu_id); + if (!cpu_node) + return -ENOENT; + num_levels =3D acpi_count_levels(table, cpu_node, NULL); + + /* Start at 1 for L1 */ + for (level =3D 1; level <=3D num_levels; level++) { + cache =3D acpi_find_cache_node(table, acpi_cpu_id, + ACPI_PPTT_CACHE_TYPE_UNIFIED, + level, &cpu_node); + if (!cache) + continue; + + cache_v1 =3D ACPI_ADD_PTR(struct acpi_pptt_cache_v1, + cache, + sizeof(struct acpi_pptt_cache)); + + if (cache->flags & ACPI_PPTT_CACHE_ID_VALID && + cache_v1->cache_id =3D=3D cache_id) + return level; + } + } + + return -ENOENT; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index f97a9ff678cc..30c10b1dcdb2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -8,6 +8,7 @@ #ifndef _LINUX_ACPI_H #define _LINUX_ACPI_H =20 +#include #include #include /* for struct resource */ #include @@ -221,6 +222,17 @@ void acpi_reserve_initial_tables (void); void acpi_table_init_complete (void); int acpi_table_init (void); =20 +static inline struct acpi_table_header *acpi_get_table_ret(char *signature= , u32 instance) +{ + struct acpi_table_header *table; + int status =3D acpi_get_table(signature, instance, &table); + + if (ACPI_FAILURE(status)) + return ERR_PTR(-ENOENT); + return table; +} +DEFINE_FREE(acpi_table, struct acpi_table_header *, if (!IS_ERR(_T)) acpi_= put_table(_T)) + int acpi_table_parse(char *id, acpi_tbl_table_handler handler); int __init_or_acpilib acpi_table_parse_entries(char *id, unsigned long table_size, int entry_id, @@ -1542,6 +1554,7 @@ int find_acpi_cpu_topology_cluster(unsigned int cpu); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); +int find_acpi_cache_level_from_id(u32 cache_id); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1565,6 +1578,10 @@ static inline int find_acpi_cpu_topology_hetero_id(u= nsigned int cpu) } static inline void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus) { } +static inline int find_acpi_cache_level_from_id(u32 cache_id) +{ + return -EINVAL; +} #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9B8B63128C7; Fri, 22 Aug 2025 15:34:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876894; cv=none; b=bQ3umHLQiVtLnxp9N9Hg+pq5o7XpzOaNZb7hFe1sMaNEFnIUxEPev5VlojEiRfgq6IsJ2IgHAtgKnZVUOGg+osP7ybnFTU7S4rPMhdF/1fIBHKizcumrlWbY1sjJ23gpb5uMYKsCcwDw0T9+qow1RdBOwJjr/mm17QPGXEL0S0M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876894; c=relaxed/simple; bh=DQbYWzAz7EinACFpXFh81z5VUFibq+peZuSz3p4d5N8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UybSMDtDX4lJZofwvh8D+rTxY1VnW9QBEP3EMPf5tWsDbCa1VaBk9n5gaagD11KXq/Wnd5pxcAT1PYQ0psm4knpCXnPfgr5kX9oD4+Ve+75Jhj6qg4SkvnSyfyaDSQyjvLEt36qbQrjtmthO4jMWisQKOybZFR+A1hDLngVAg3E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D2B8F15A1; Fri, 22 Aug 2025 08:34:43 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A3F6E3F63F; Fri, 22 Aug 2025 08:34:46 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew Subject: [PATCH 06/33] ACPI / PPTT: Add a helper to fill a cpumask from a cache_id Date: Fri, 22 Aug 2025 15:30:21 +0000 Message-Id: <20250822153048.2287-41-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM identifies CPUs by the cache_id in the PPTT cache structure. The driver needs to know which CPUs are associated with the cache, the CPUs may not all be online, so cacheinfo does not have the information. Add a helper to pull this information out of the PPTT. CC: Rohit Mathew Signed-off-by: James Morse Reviewed-by: Sudeep Holla Reviewed-by: Lorenzo Pieralisi --- Changes since RFC: * acpi_count_levels() now returns a value. * Converted the table-get stuff to use Jonathan's cleanup helper. * Dropped Sudeep's Review tag due to the cleanup change. --- drivers/acpi/pptt.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 6 +++++ 2 files changed, 68 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 660457644a5b..cb93a9a7f9b6 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -971,3 +971,65 @@ int find_acpi_cache_level_from_id(u32 cache_id) =20 return -ENOENT; } + +/** + * acpi_pptt_get_cpumask_from_cache_id() - Get the cpus associated with the + * specified cache + * @cache_id: The id field of the unified cache + * @cpus: Where to build the cpumask + * + * Determine which CPUs are below this cache in the PPTT. This allows the = property + * to be found even if the CPUs are offline. + * + * The PPTT table must be rev 3 or later, + * + * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found. + * Otherwise returns 0 and sets the cpus in the provided cpumask. + */ +int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus) +{ + u32 acpi_cpu_id; + int level, cpu, num_levels; + struct acpi_pptt_cache *cache; + struct acpi_pptt_cache_v1 *cache_v1; + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_table_ret= (ACPI_SIG_PPTT, 0); + + cpumask_clear(cpus); + + if (IS_ERR(table)) + return -ENOENT; + + if (table->revision < 3) + return -ENOENT; + + /* + * If we found the cache first, we'd still need to walk from each cpu. + */ + for_each_possible_cpu(cpu) { + acpi_cpu_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table, acpi_cpu_id); + if (!cpu_node) + return 0; + num_levels =3D acpi_count_levels(table, cpu_node, NULL); + + /* Start at 1 for L1 */ + for (level =3D 1; level <=3D num_levels; level++) { + cache =3D acpi_find_cache_node(table, acpi_cpu_id, + ACPI_PPTT_CACHE_TYPE_UNIFIED, + level, &cpu_node); + if (!cache) + continue; + + cache_v1 =3D ACPI_ADD_PTR(struct acpi_pptt_cache_v1, + cache, + sizeof(struct acpi_pptt_cache)); + + if (cache->flags & ACPI_PPTT_CACHE_ID_VALID && + cache_v1->cache_id =3D=3D cache_id) + cpumask_set_cpu(cpu, cpus); + } + } + + return 0; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 30c10b1dcdb2..4ad08f5f1d83 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1555,6 +1555,7 @@ int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); int find_acpi_cache_level_from_id(u32 cache_id); +int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1582,6 +1583,11 @@ static inline int find_acpi_cache_level_from_id(u32 = cache_id) { return -EINVAL; } +static inline int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, + cpumask_t *cpus) +{ + return -EINVAL; +} #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 099CC21D3CD; Fri, 22 Aug 2025 15:31:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876702; cv=none; b=l1XCmt4GE/P4CHuV6Msv20Jxu6U+WO+jEPkTyuwFMMPE4GcEwpaID8k2lr8vIkooyND4H8V9etitbYnB2C88Q2IzSdJ+tBHjrZjmwJFO6PNcXSwjAJibd+hBbczDETdIQ8Zn1R4Gsljxdj9RGmUGSnzF26kS5g3kvnqQd7kpX0Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876702; c=relaxed/simple; bh=DQbYWzAz7EinACFpXFh81z5VUFibq+peZuSz3p4d5N8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Ng+KA5oJXAht6lOtaeK52wrDmIq5HwM1xXirYhwYiNQRLm3yWPQKevoxp2kospemkPIDb4Mb5UELqoMPx8N0wT1JTF9r8J2TwCAV4mrgWXvdPMGV3QAQ0VcFVwJR/YBzwuPShEmpkl7XYHxpwlX0AsjMkVDmLGicykMjHcpjT9Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 20B3527DD; Fri, 22 Aug 2025 08:31:32 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id E7C623F63F; Fri, 22 Aug 2025 08:31:34 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew Subject: [PATCH 06/33] ACPI / PPTT: Add a helper to fill a cpumask from a cache_id Date: Fri, 22 Aug 2025 15:29:47 +0000 Message-Id: <20250822153048.2287-7-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM identifies CPUs by the cache_id in the PPTT cache structure. The driver needs to know which CPUs are associated with the cache, the CPUs may not all be online, so cacheinfo does not have the information. Add a helper to pull this information out of the PPTT. CC: Rohit Mathew Signed-off-by: James Morse Reviewed-by: Sudeep Holla --- Changes since RFC: * acpi_count_levels() now returns a value. * Converted the table-get stuff to use Jonathan's cleanup helper. * Dropped Sudeep's Review tag due to the cleanup change. --- drivers/acpi/pptt.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 6 +++++ 2 files changed, 68 insertions(+) diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 660457644a5b..cb93a9a7f9b6 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -971,3 +971,65 @@ int find_acpi_cache_level_from_id(u32 cache_id) =20 return -ENOENT; } + +/** + * acpi_pptt_get_cpumask_from_cache_id() - Get the cpus associated with the + * specified cache + * @cache_id: The id field of the unified cache + * @cpus: Where to build the cpumask + * + * Determine which CPUs are below this cache in the PPTT. This allows the = property + * to be found even if the CPUs are offline. + * + * The PPTT table must be rev 3 or later, + * + * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found. + * Otherwise returns 0 and sets the cpus in the provided cpumask. + */ +int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus) +{ + u32 acpi_cpu_id; + int level, cpu, num_levels; + struct acpi_pptt_cache *cache; + struct acpi_pptt_cache_v1 *cache_v1; + struct acpi_pptt_processor *cpu_node; + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_table_ret= (ACPI_SIG_PPTT, 0); + + cpumask_clear(cpus); + + if (IS_ERR(table)) + return -ENOENT; + + if (table->revision < 3) + return -ENOENT; + + /* + * If we found the cache first, we'd still need to walk from each cpu. + */ + for_each_possible_cpu(cpu) { + acpi_cpu_id =3D get_acpi_id_for_cpu(cpu); + cpu_node =3D acpi_find_processor_node(table, acpi_cpu_id); + if (!cpu_node) + return 0; + num_levels =3D acpi_count_levels(table, cpu_node, NULL); + + /* Start at 1 for L1 */ + for (level =3D 1; level <=3D num_levels; level++) { + cache =3D acpi_find_cache_node(table, acpi_cpu_id, + ACPI_PPTT_CACHE_TYPE_UNIFIED, + level, &cpu_node); + if (!cache) + continue; + + cache_v1 =3D ACPI_ADD_PTR(struct acpi_pptt_cache_v1, + cache, + sizeof(struct acpi_pptt_cache)); + + if (cache->flags & ACPI_PPTT_CACHE_ID_VALID && + cache_v1->cache_id =3D=3D cache_id) + cpumask_set_cpu(cpu, cpus); + } + } + + return 0; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 30c10b1dcdb2..4ad08f5f1d83 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1555,6 +1555,7 @@ int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus); int find_acpi_cache_level_from_id(u32 cache_id); +int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1582,6 +1583,11 @@ static inline int find_acpi_cache_level_from_id(u32 = cache_id) { return -EINVAL; } +static inline int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, + cpumask_t *cpus) +{ + return -EINVAL; +} #endif =20 void acpi_arch_init(void); --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A3EB421D3CD; Fri, 22 Aug 2025 15:31:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876708; cv=none; b=ZT0lfkrO57FXbgrDo8t+i47EbGxv0NiRG6IHEFWjW9hL4rAwMhfF/83szHIYSqJ3gj7q0oBTi9p6oLB2qij1VTFG2MfLzaz2tD7gykT+Cu3bqONgKvaJIed/Ou2YhFacjeW4o8yct9AmXMbw0Aaoj5XoYGZvl723Oy9E4oehy68= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876708; c=relaxed/simple; bh=RFQv8uiQ3LsjHQNy9ijjtx5mBRzc66JOkbgBBDf7fRw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ToyAnCunan5lVcCKQJja586SaUTJYIesSfw98MKLwqEurwDLsHVKMb00BYh8b/unTqdx0elHE3rXFqIiCcKt8A0IK0WuenhDgNzWritd613pPX6gKwAAbkfVJuv/mjzX/nwqEpkiy6TxRYG4fZ1Wu8PNl0PDK7Z7gC3IRsJrcOQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B83B8288E; Fri, 22 Aug 2025 08:31:37 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A36E53F63F; Fri, 22 Aug 2025 08:31:40 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 07/33] arm64: kconfig: Add Kconfig entry for MPAM Date: Fri, 22 Aug 2025 15:29:48 +0000 Message-Id: <20250822153048.2287-8-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The bulk of the MPAM driver lives outside the arch code because it largely manages MMIO devices that generate interrupts. The driver needs a Kconfig symbol to enable it, as MPAM is only found on arm64 platforms, that is where the Kconfig option makes the most sense. This Kconfig option will later be used by the arch code to enable or disable the MPAM context-switch code, and registering the CPUs properties with the MPAM driver. Signed-off-by: James Morse Reviewed-by: Jonathan Cameron --- arch/arm64/Kconfig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e9bbfacc35a6..658e47fc0c5a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2060,6 +2060,23 @@ config ARM64_TLB_RANGE ARMv8.4-TLBI provides TLBI invalidation instruction that apply to a range of input addresses. =20 +config ARM64_MPAM + bool "Enable support for MPAM" + help + Memory Partitioning and Monitoring is an optional extension + that allows the CPUs to mark load and store transactions with + labels for partition-id and performance-monitoring-group. + System components, such as the caches, can use the partition-id + to apply a performance policy. MPAM monitors can use the + partition-id and performance-monitoring-group to measure the + cache occupancy or data throughput. + + Use of this extension requires CPU support, support in the + memory system components (MSC), and a description from firmware + of where the MSC are in the address space. + + MPAM is exposed to user-space via the resctrl pseudo filesystem. + endmenu # "ARMv8.4 architectural features" =20 menu "ARMv8.5 architectural features" --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 12A6D277CB4; Fri, 22 Aug 2025 15:34:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876899; cv=none; b=hPmTgrj6E68xf5u2DTThPLyVzZaYDJEJWzayPKwL38zTHh7STkzPJE+HjG/RhZfbsdZ4DPM7g7CC2/aUoExCKsRtMmjWuaWsHvm72O9eOt9nGhsE0RT/H9b5mNSNbb/CLpadKSAj3crhnSvXppjIaeiA3ffDV5TuyZuJZ4642AM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876899; c=relaxed/simple; bh=RFQv8uiQ3LsjHQNy9ijjtx5mBRzc66JOkbgBBDf7fRw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NNoBfIHlLlag5GCsZZXjS1WlrfMsrG6yrFrNefnI/dd5DmlvVG6uytJtegHe7bdkz38bitbie+n1bB8tiREjlgjD5ilDrjlj9M8dURjuSH6Toq6rAZ3gXHRMbBKSXj4ZzdqV3Wy6cjEHtaVuuBfzMwuOFZ1obYepMjvZpcWrCPU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6A0A415A1; Fri, 22 Aug 2025 08:34:49 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5F7913F63F; Fri, 22 Aug 2025 08:34:52 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 07/33] arm64: kconfig: Add Kconfig entry for MPAM Date: Fri, 22 Aug 2025 15:30:22 +0000 Message-Id: <20250822153048.2287-42-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The bulk of the MPAM driver lives outside the arch code because it largely manages MMIO devices that generate interrupts. The driver needs a Kconfig symbol to enable it, as MPAM is only found on arm64 platforms, that is where the Kconfig option makes the most sense. This Kconfig option will later be used by the arch code to enable or disable the MPAM context-switch code, and registering the CPUs properties with the MPAM driver. Signed-off-by: James Morse Reviewed-by: Jonathan Cameron --- arch/arm64/Kconfig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e9bbfacc35a6..658e47fc0c5a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2060,6 +2060,23 @@ config ARM64_TLB_RANGE ARMv8.4-TLBI provides TLBI invalidation instruction that apply to a range of input addresses. =20 +config ARM64_MPAM + bool "Enable support for MPAM" + help + Memory Partitioning and Monitoring is an optional extension + that allows the CPUs to mark load and store transactions with + labels for partition-id and performance-monitoring-group. + System components, such as the caches, can use the partition-id + to apply a performance policy. MPAM monitors can use the + partition-id and performance-monitoring-group to measure the + cache occupancy or data throughput. + + Use of this extension requires CPU support, support in the + memory system components (MSC), and a description from firmware + of where the MSC are in the address space. + + MPAM is exposed to user-space via the resctrl pseudo filesystem. + endmenu # "ARMv8.4 architectural features" =20 menu "ARMv8.5 architectural features" --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 484F123BCE4; Fri, 22 Aug 2025 15:31:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876714; cv=none; b=VlFioa+VdebBWuVSkhX+dF8ODIkB772fAUiqjfeyWIYHpBvcIWty9AKKP/EYVMr5/d8Xynp6GjFEbcqYQgHRbN57nyo6e1onE/SnayaUBzuB8R4ewwXkaU3iMH/TGikaRSHMSGQlzk6vPiGJORIDuyLksGsMOHKRiw17k0O7cvc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876714; c=relaxed/simple; bh=Q8GeH70wKvlYBI01fp/Tx7l3lcdzEfWVAqf8aPhatFk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j1UAm8B6BctWW3eNzRKxsico4r5j+lLBrzAdt8HElPZgbexxRVXtFzBAQi9bIjRVCsoUhcRmpL+KeVQPKQYoYFG2lLoDovFvwD6rtq4ETx6za8nzf0XZzFE0YD8oUe/BxWxZ4R/qvQv53KQ2Lhiza6ouUJbKX0MwPthvJ2UITdE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 73C5C15A1; Fri, 22 Aug 2025 08:31:43 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 457F43F63F; Fri, 22 Aug 2025 08:31:46 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 08/33] ACPI / MPAM: Parse the MPAM table Date: Fri, 22 Aug 2025 15:29:49 +0000 Message-Id: <20250822153048.2287-9-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add code to parse the arm64 specific MPAM table, looking up the cache level from the PPTT and feeding the end result into the MPAM driver. CC: Carl Worth Link: https://developer.arm.com/documentation/den0065/3-0bet/?lang=3Den Signed-off-by: James Morse --- Changes since RFC: * Used DEFINE_RES_IRQ_NAMED() and friends macros. * Additional error handling. * Check for zero sized MSC. * Allow table revisions greater than 1. (no spec for revision 0!) * Use cleanup helpers to retrive ACPI tables, which allows some functions to be folded together. --- arch/arm64/Kconfig | 1 + drivers/acpi/arm64/Kconfig | 3 + drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/mpam.c | 331 ++++++++++++++++++++++++++++++++++++ drivers/acpi/tables.c | 2 +- include/linux/arm_mpam.h | 46 +++++ 6 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/arm64/mpam.c create mode 100644 include/linux/arm_mpam.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 658e47fc0c5a..e51ccf1da102 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2062,6 +2062,7 @@ config ARM64_TLB_RANGE =20 config ARM64_MPAM bool "Enable support for MPAM" + select ACPI_MPAM if ACPI help Memory Partitioning and Monitoring is an optional extension that allows the CPUs to mark load and store transactions with diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index b3ed6212244c..f2fd79f22e7d 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -21,3 +21,6 @@ config ACPI_AGDI =20 config ACPI_APMT bool + +config ACPI_MPAM + bool diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 05ecde9eaabe..9390b57cb564 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_ACPI_APMT) +=3D apmt.o obj-$(CONFIG_ACPI_FFH) +=3D ffh.o obj-$(CONFIG_ACPI_GTDT) +=3D gtdt.o obj-$(CONFIG_ACPI_IORT) +=3D iort.o +obj-$(CONFIG_ACPI_MPAM) +=3D mpam.o obj-$(CONFIG_ACPI_PROCESSOR_IDLE) +=3D cpuidle.o obj-$(CONFIG_ARM_AMBA) +=3D amba.o obj-y +=3D dma.o init.o diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c new file mode 100644 index 000000000000..e55fc2729ac5 --- /dev/null +++ b/drivers/acpi/arm64/mpam.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Arm Ltd. + +/* Parse the MPAM ACPI table feeding the discovered nodes into the driver = */ + +#define pr_fmt(fmt) "ACPI MPAM: " fmt + +#include +#include +#include +#include +#include +#include + +#include + +/* + * Flags for acpi_table_mpam_msc.*_interrupt_flags. + * See 2.1.1 Interrupt Flags, Table 5, of DEN0065B_MPAM_ACPI_3.0-bet. + */ +#define ACPI_MPAM_MSC_IRQ_MODE_MASK BIT(0) +#define ACPI_MPAM_MSC_IRQ_TYPE_MASK GENMASK(2, 1) +#define ACPI_MPAM_MSC_IRQ_TYPE_WIRED 0 +#define ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER BIT(3) +#define ACPI_MPAM_MSC_IRQ_AFFINITY_VALID BIT(4) + +static bool frob_irq(struct platform_device *pdev, int intid, u32 flags, + int *irq, u32 processor_container_uid) +{ + int sense; + + if (!intid) + return false; + + if (FIELD_GET(ACPI_MPAM_MSC_IRQ_TYPE_MASK, flags) !=3D + ACPI_MPAM_MSC_IRQ_TYPE_WIRED) + return false; + + sense =3D FIELD_GET(ACPI_MPAM_MSC_IRQ_MODE_MASK, flags); + + /* + * If the GSI is in the GIC's PPI range, try and create a partitioned + * percpu interrupt. + */ + if (16 <=3D intid && intid < 32 && processor_container_uid !=3D ~0) { + pr_err_once("Partitioned interrupts not supported\n"); + return false; + } + + *irq =3D acpi_register_gsi(&pdev->dev, intid, sense, ACPI_ACTIVE_HIGH); + if (*irq <=3D 0) { + pr_err_once("Failed to register interrupt 0x%x with ACPI\n", + intid); + return false; + } + + return true; +} + +static void acpi_mpam_parse_irqs(struct platform_device *pdev, + struct acpi_mpam_msc_node *tbl_msc, + struct resource *res, int *res_idx) +{ + u32 flags, aff; + int irq; + + flags =3D tbl_msc->overflow_interrupt_flags; + if (flags & ACPI_MPAM_MSC_IRQ_AFFINITY_VALID && + flags & ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER) + aff =3D tbl_msc->overflow_interrupt_affinity; + else + aff =3D ~0; + if (frob_irq(pdev, tbl_msc->overflow_interrupt, flags, &irq, aff)) + res[(*res_idx)++] =3D DEFINE_RES_IRQ_NAMED(irq, "overflow"); + + flags =3D tbl_msc->error_interrupt_flags; + if (flags & ACPI_MPAM_MSC_IRQ_AFFINITY_VALID && + flags & ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER) + aff =3D tbl_msc->error_interrupt_affinity; + else + aff =3D ~0; + if (frob_irq(pdev, tbl_msc->error_interrupt, flags, &irq, aff)) + res[(*res_idx)++] =3D DEFINE_RES_IRQ_NAMED(irq, "error"); +} + +static int acpi_mpam_parse_resource(struct mpam_msc *msc, + struct acpi_mpam_resource_node *res) +{ + int level, nid; + u32 cache_id; + + switch (res->locator_type) { + case ACPI_MPAM_LOCATION_TYPE_PROCESSOR_CACHE: + cache_id =3D res->locator.cache_locator.cache_reference; + level =3D find_acpi_cache_level_from_id(cache_id); + if (level <=3D 0) { + pr_err_once("Bad level (%u) for cache with id %u\n", level, cache_id); + return -EINVAL; + } + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE, + level, cache_id); + case ACPI_MPAM_LOCATION_TYPE_MEMORY: + nid =3D pxm_to_node(res->locator.memory_locator.proximity_domain); + if (nid =3D=3D NUMA_NO_NODE) + nid =3D 0; + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_MEMORY, + 255, nid); + default: + /* These get discovered later and treated as unknown */ + return 0; + } +} + +int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc) +{ + int i, err; + struct acpi_mpam_resource_node *resources; + + resources =3D (struct acpi_mpam_resource_node *)(tbl_msc + 1); + for (i =3D 0; i < tbl_msc->num_resource_nodes; i++) { + err =3D acpi_mpam_parse_resource(msc, &resources[i]); + if (err) + return err; + } + + return 0; +} + +static bool __init parse_msc_pm_link(struct acpi_mpam_msc_node *tbl_msc, + struct platform_device *pdev, + u32 *acpi_id) +{ + bool acpi_id_valid =3D false; + struct acpi_device *buddy; + char hid[16], uid[16]; + int err; + + memset(&hid, 0, sizeof(hid)); + memcpy(hid, &tbl_msc->hardware_id_linked_device, + sizeof(tbl_msc->hardware_id_linked_device)); + + if (!strcmp(hid, ACPI_PROCESSOR_CONTAINER_HID)) { + *acpi_id =3D tbl_msc->instance_id_linked_device; + acpi_id_valid =3D true; + } + + err =3D snprintf(uid, sizeof(uid), "%u", + tbl_msc->instance_id_linked_device); + if (err >=3D sizeof(uid)) + return acpi_id_valid; + + buddy =3D acpi_dev_get_first_match_dev(hid, uid, -1); + if (buddy) + device_link_add(&pdev->dev, &buddy->dev, DL_FLAG_STATELESS); + + return acpi_id_valid; +} + +static int decode_interface_type(struct acpi_mpam_msc_node *tbl_msc, + enum mpam_msc_iface *iface) +{ + switch (tbl_msc->interface_type) { + case 0: + *iface =3D MPAM_IFACE_MMIO; + return 0; + case 0xa: + *iface =3D MPAM_IFACE_PCC; + return 0; + default: + return -EINVAL; + } +} + +static int __init acpi_mpam_parse(void) +{ + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_ta= ble_ret(ACPI_SIG_MPAM, 0); + char *table_end, *table_offset =3D (char *)(table + 1); + struct property_entry props[4]; /* needs a sentinel */ + struct acpi_mpam_msc_node *tbl_msc; + int next_res, next_prop, err =3D 0; + struct acpi_device *companion; + struct platform_device *pdev; + enum mpam_msc_iface iface; + struct resource res[3]; + char uid[16]; + u32 acpi_id; + + if (acpi_disabled || !system_supports_mpam() || IS_ERR(table)) + return 0; + + if (IS_ERR(table)) + return 0; + + if (table->revision < 1) + return 0; + + table_end =3D (char *)table + table->length; + + while (table_offset < table_end) { + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + table_offset +=3D tbl_msc->length; + + /* + * If any of the reserved fields are set, make no attempt to + * parse the msc structure. This will prevent the driver from + * probing all the MSC, meaning it can't discover the system + * wide supported partid and pmg ranges. This avoids whatever + * this MSC is truncating the partids and creating a screaming + * error interrupt. + */ + if (tbl_msc->reserved || tbl_msc->reserved1 || tbl_msc->reserved2) + continue; + + if (!tbl_msc->mmio_size) + continue; + + if (decode_interface_type(tbl_msc, &iface)) + continue; + + next_res =3D 0; + next_prop =3D 0; + memset(res, 0, sizeof(res)); + memset(props, 0, sizeof(props)); + + pdev =3D platform_device_alloc("mpam_msc", tbl_msc->identifier); + if (!pdev) { + err =3D -ENOMEM; + break; + } + + if (tbl_msc->length < sizeof(*tbl_msc)) { + err =3D -EINVAL; + break; + } + + /* Some power management is described in the namespace: */ + err =3D snprintf(uid, sizeof(uid), "%u", tbl_msc->identifier); + if (err > 0 && err < sizeof(uid)) { + companion =3D acpi_dev_get_first_match_dev("ARMHAA5C", uid, -1); + if (companion) + ACPI_COMPANION_SET(&pdev->dev, companion); + } + + if (iface =3D=3D MPAM_IFACE_MMIO) { + res[next_res++] =3D DEFINE_RES_MEM_NAMED(tbl_msc->base_address, + tbl_msc->mmio_size, + "MPAM:MSC"); + } else if (iface =3D=3D MPAM_IFACE_PCC) { + props[next_prop++] =3D PROPERTY_ENTRY_U32("pcc-channel", + tbl_msc->base_address); + next_prop++; + } + + acpi_mpam_parse_irqs(pdev, tbl_msc, res, &next_res); + err =3D platform_device_add_resources(pdev, res, next_res); + if (err) + break; + + props[next_prop++] =3D PROPERTY_ENTRY_U32("arm,not-ready-us", + tbl_msc->max_nrdy_usec); + + /* + * The MSC's CPU affinity is described via its linked power + * management device, but only if it points at a Processor or + * Processor Container. + */ + if (parse_msc_pm_link(tbl_msc, pdev, &acpi_id)) { + props[next_prop++] =3D PROPERTY_ENTRY_U32("cpu_affinity", + acpi_id); + } + + err =3D device_create_managed_software_node(&pdev->dev, props, + NULL); + if (err) + break; + + /* Come back later if you want the RIS too */ + err =3D platform_device_add_data(pdev, tbl_msc, tbl_msc->length); + if (err) + break; + + err =3D platform_device_add(pdev); + if (err) + break; + } + + if (err) + platform_device_put(pdev); + + return err; +} + +int acpi_mpam_count_msc(void) +{ + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_ta= ble_ret(ACPI_SIG_MPAM, 0); + char *table_end, *table_offset =3D (char *)(table + 1); + struct acpi_mpam_msc_node *tbl_msc; + int count =3D 0; + + if (IS_ERR(table)) + return 0; + + if (table->revision < 1) + return 0; + + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + table_end =3D (char *)table + table->length; + + while (table_offset < table_end) { + if (!tbl_msc->mmio_size) + continue; + + if (tbl_msc->length < sizeof(*tbl_msc)) + return -EINVAL; + + count++; + + table_offset +=3D tbl_msc->length; + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + } + + return count; +} + +/* + * Call after ACPI devices have been created, which happens behind acpi_sc= an_init() + * called from subsys_initcall(). PCC requires the mailbox driver, which is + * initialised from postcore_initcall(). + */ +subsys_initcall_sync(acpi_mpam_parse); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index fa9bb8c8ce95..835e3795ede3 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -408,7 +408,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __non= string_array __initconst ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI, - ACPI_SIG_NBFT }; + ACPI_SIG_NBFT, ACPI_SIG_MPAM }; =20 #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) =20 diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h new file mode 100644 index 000000000000..0edefa6ba019 --- /dev/null +++ b/include/linux/arm_mpam.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Arm Ltd. */ + +#ifndef __LINUX_ARM_MPAM_H +#define __LINUX_ARM_MPAM_H + +#include +#include + +struct mpam_msc; + +enum mpam_msc_iface { + MPAM_IFACE_MMIO, /* a real MPAM MSC */ + MPAM_IFACE_PCC, /* a fake MPAM MSC */ +}; + +enum mpam_class_types { + MPAM_CLASS_CACHE, /* Well known caches, e.g. L2 */ + MPAM_CLASS_MEMORY, /* Main memory */ + MPAM_CLASS_UNKNOWN, /* Everything else, e.g. SMMU */ +}; + +#ifdef CONFIG_ACPI_MPAM +/* Parse the ACPI description of resources entries for this MSC. */ +int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc); + +int acpi_mpam_count_msc(void); +#else +static inline int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc) +{ + return -EINVAL; +} + +static inline int acpi_mpam_count_msc(void) { return -EINVAL; } +#endif + +static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, + int component_id) +{ + return -EINVAL; +} + +#endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DA749279DC3; Fri, 22 Aug 2025 15:35:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876905; cv=none; b=NMJf9Y8EX4k0ovmzUxA1KQcIei1wVGR8N1snCcUcELLWFZtqr+a/coYk2KleewNEyYC+a6l8hjMvrF/xouoNdFYkyQOa+nKzVk7yY6r7bYRn6rff0DWw6BR4YVM08uKYEWQXnblA9G718AF3Bd/sAXq+EHw+YBvwNyTdCj2Vsp8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876905; c=relaxed/simple; bh=Q8GeH70wKvlYBI01fp/Tx7l3lcdzEfWVAqf8aPhatFk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=h5uYhV/R27IqP7W9qyqQAaZ7GyI0IKu34MYE46iTqTN67TTAdEl/zmwxd8Fe5hksFy1K6Fbsr2yXEi488gNkNdh/oBrcn+asjApSFKi/CGYZsMm8onYXHQ7mEGB1UFF+Z8rWT5Bg7w1mR6DVETzMCWcsCrwn4EaW66T5o7eKZR8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0838E2880; Fri, 22 Aug 2025 08:34:55 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id EF5D93F63F; Fri, 22 Aug 2025 08:34:57 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 08/33] ACPI / MPAM: Parse the MPAM table Date: Fri, 22 Aug 2025 15:30:23 +0000 Message-Id: <20250822153048.2287-43-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add code to parse the arm64 specific MPAM table, looking up the cache level from the PPTT and feeding the end result into the MPAM driver. CC: Carl Worth Link: https://developer.arm.com/documentation/den0065/3-0bet/?lang=3Den Signed-off-by: James Morse --- Changes since RFC: * Used DEFINE_RES_IRQ_NAMED() and friends macros. * Additional error handling. * Check for zero sized MSC. * Allow table revisions greater than 1. (no spec for revision 0!) * Use cleanup helpers to retrive ACPI tables, which allows some functions to be folded together. --- arch/arm64/Kconfig | 1 + drivers/acpi/arm64/Kconfig | 3 + drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/mpam.c | 331 ++++++++++++++++++++++++++++++++++++ drivers/acpi/tables.c | 2 +- include/linux/arm_mpam.h | 46 +++++ 6 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/arm64/mpam.c create mode 100644 include/linux/arm_mpam.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 658e47fc0c5a..e51ccf1da102 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2062,6 +2062,7 @@ config ARM64_TLB_RANGE =20 config ARM64_MPAM bool "Enable support for MPAM" + select ACPI_MPAM if ACPI help Memory Partitioning and Monitoring is an optional extension that allows the CPUs to mark load and store transactions with diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index b3ed6212244c..f2fd79f22e7d 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -21,3 +21,6 @@ config ACPI_AGDI =20 config ACPI_APMT bool + +config ACPI_MPAM + bool diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 05ecde9eaabe..9390b57cb564 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_ACPI_APMT) +=3D apmt.o obj-$(CONFIG_ACPI_FFH) +=3D ffh.o obj-$(CONFIG_ACPI_GTDT) +=3D gtdt.o obj-$(CONFIG_ACPI_IORT) +=3D iort.o +obj-$(CONFIG_ACPI_MPAM) +=3D mpam.o obj-$(CONFIG_ACPI_PROCESSOR_IDLE) +=3D cpuidle.o obj-$(CONFIG_ARM_AMBA) +=3D amba.o obj-y +=3D dma.o init.o diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c new file mode 100644 index 000000000000..e55fc2729ac5 --- /dev/null +++ b/drivers/acpi/arm64/mpam.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Arm Ltd. + +/* Parse the MPAM ACPI table feeding the discovered nodes into the driver = */ + +#define pr_fmt(fmt) "ACPI MPAM: " fmt + +#include +#include +#include +#include +#include +#include + +#include + +/* + * Flags for acpi_table_mpam_msc.*_interrupt_flags. + * See 2.1.1 Interrupt Flags, Table 5, of DEN0065B_MPAM_ACPI_3.0-bet. + */ +#define ACPI_MPAM_MSC_IRQ_MODE_MASK BIT(0) +#define ACPI_MPAM_MSC_IRQ_TYPE_MASK GENMASK(2, 1) +#define ACPI_MPAM_MSC_IRQ_TYPE_WIRED 0 +#define ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER BIT(3) +#define ACPI_MPAM_MSC_IRQ_AFFINITY_VALID BIT(4) + +static bool frob_irq(struct platform_device *pdev, int intid, u32 flags, + int *irq, u32 processor_container_uid) +{ + int sense; + + if (!intid) + return false; + + if (FIELD_GET(ACPI_MPAM_MSC_IRQ_TYPE_MASK, flags) !=3D + ACPI_MPAM_MSC_IRQ_TYPE_WIRED) + return false; + + sense =3D FIELD_GET(ACPI_MPAM_MSC_IRQ_MODE_MASK, flags); + + /* + * If the GSI is in the GIC's PPI range, try and create a partitioned + * percpu interrupt. + */ + if (16 <=3D intid && intid < 32 && processor_container_uid !=3D ~0) { + pr_err_once("Partitioned interrupts not supported\n"); + return false; + } + + *irq =3D acpi_register_gsi(&pdev->dev, intid, sense, ACPI_ACTIVE_HIGH); + if (*irq <=3D 0) { + pr_err_once("Failed to register interrupt 0x%x with ACPI\n", + intid); + return false; + } + + return true; +} + +static void acpi_mpam_parse_irqs(struct platform_device *pdev, + struct acpi_mpam_msc_node *tbl_msc, + struct resource *res, int *res_idx) +{ + u32 flags, aff; + int irq; + + flags =3D tbl_msc->overflow_interrupt_flags; + if (flags & ACPI_MPAM_MSC_IRQ_AFFINITY_VALID && + flags & ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER) + aff =3D tbl_msc->overflow_interrupt_affinity; + else + aff =3D ~0; + if (frob_irq(pdev, tbl_msc->overflow_interrupt, flags, &irq, aff)) + res[(*res_idx)++] =3D DEFINE_RES_IRQ_NAMED(irq, "overflow"); + + flags =3D tbl_msc->error_interrupt_flags; + if (flags & ACPI_MPAM_MSC_IRQ_AFFINITY_VALID && + flags & ACPI_MPAM_MSC_IRQ_AFFINITY_PROCESSOR_CONTAINER) + aff =3D tbl_msc->error_interrupt_affinity; + else + aff =3D ~0; + if (frob_irq(pdev, tbl_msc->error_interrupt, flags, &irq, aff)) + res[(*res_idx)++] =3D DEFINE_RES_IRQ_NAMED(irq, "error"); +} + +static int acpi_mpam_parse_resource(struct mpam_msc *msc, + struct acpi_mpam_resource_node *res) +{ + int level, nid; + u32 cache_id; + + switch (res->locator_type) { + case ACPI_MPAM_LOCATION_TYPE_PROCESSOR_CACHE: + cache_id =3D res->locator.cache_locator.cache_reference; + level =3D find_acpi_cache_level_from_id(cache_id); + if (level <=3D 0) { + pr_err_once("Bad level (%u) for cache with id %u\n", level, cache_id); + return -EINVAL; + } + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE, + level, cache_id); + case ACPI_MPAM_LOCATION_TYPE_MEMORY: + nid =3D pxm_to_node(res->locator.memory_locator.proximity_domain); + if (nid =3D=3D NUMA_NO_NODE) + nid =3D 0; + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_MEMORY, + 255, nid); + default: + /* These get discovered later and treated as unknown */ + return 0; + } +} + +int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc) +{ + int i, err; + struct acpi_mpam_resource_node *resources; + + resources =3D (struct acpi_mpam_resource_node *)(tbl_msc + 1); + for (i =3D 0; i < tbl_msc->num_resource_nodes; i++) { + err =3D acpi_mpam_parse_resource(msc, &resources[i]); + if (err) + return err; + } + + return 0; +} + +static bool __init parse_msc_pm_link(struct acpi_mpam_msc_node *tbl_msc, + struct platform_device *pdev, + u32 *acpi_id) +{ + bool acpi_id_valid =3D false; + struct acpi_device *buddy; + char hid[16], uid[16]; + int err; + + memset(&hid, 0, sizeof(hid)); + memcpy(hid, &tbl_msc->hardware_id_linked_device, + sizeof(tbl_msc->hardware_id_linked_device)); + + if (!strcmp(hid, ACPI_PROCESSOR_CONTAINER_HID)) { + *acpi_id =3D tbl_msc->instance_id_linked_device; + acpi_id_valid =3D true; + } + + err =3D snprintf(uid, sizeof(uid), "%u", + tbl_msc->instance_id_linked_device); + if (err >=3D sizeof(uid)) + return acpi_id_valid; + + buddy =3D acpi_dev_get_first_match_dev(hid, uid, -1); + if (buddy) + device_link_add(&pdev->dev, &buddy->dev, DL_FLAG_STATELESS); + + return acpi_id_valid; +} + +static int decode_interface_type(struct acpi_mpam_msc_node *tbl_msc, + enum mpam_msc_iface *iface) +{ + switch (tbl_msc->interface_type) { + case 0: + *iface =3D MPAM_IFACE_MMIO; + return 0; + case 0xa: + *iface =3D MPAM_IFACE_PCC; + return 0; + default: + return -EINVAL; + } +} + +static int __init acpi_mpam_parse(void) +{ + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_ta= ble_ret(ACPI_SIG_MPAM, 0); + char *table_end, *table_offset =3D (char *)(table + 1); + struct property_entry props[4]; /* needs a sentinel */ + struct acpi_mpam_msc_node *tbl_msc; + int next_res, next_prop, err =3D 0; + struct acpi_device *companion; + struct platform_device *pdev; + enum mpam_msc_iface iface; + struct resource res[3]; + char uid[16]; + u32 acpi_id; + + if (acpi_disabled || !system_supports_mpam() || IS_ERR(table)) + return 0; + + if (IS_ERR(table)) + return 0; + + if (table->revision < 1) + return 0; + + table_end =3D (char *)table + table->length; + + while (table_offset < table_end) { + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + table_offset +=3D tbl_msc->length; + + /* + * If any of the reserved fields are set, make no attempt to + * parse the msc structure. This will prevent the driver from + * probing all the MSC, meaning it can't discover the system + * wide supported partid and pmg ranges. This avoids whatever + * this MSC is truncating the partids and creating a screaming + * error interrupt. + */ + if (tbl_msc->reserved || tbl_msc->reserved1 || tbl_msc->reserved2) + continue; + + if (!tbl_msc->mmio_size) + continue; + + if (decode_interface_type(tbl_msc, &iface)) + continue; + + next_res =3D 0; + next_prop =3D 0; + memset(res, 0, sizeof(res)); + memset(props, 0, sizeof(props)); + + pdev =3D platform_device_alloc("mpam_msc", tbl_msc->identifier); + if (!pdev) { + err =3D -ENOMEM; + break; + } + + if (tbl_msc->length < sizeof(*tbl_msc)) { + err =3D -EINVAL; + break; + } + + /* Some power management is described in the namespace: */ + err =3D snprintf(uid, sizeof(uid), "%u", tbl_msc->identifier); + if (err > 0 && err < sizeof(uid)) { + companion =3D acpi_dev_get_first_match_dev("ARMHAA5C", uid, -1); + if (companion) + ACPI_COMPANION_SET(&pdev->dev, companion); + } + + if (iface =3D=3D MPAM_IFACE_MMIO) { + res[next_res++] =3D DEFINE_RES_MEM_NAMED(tbl_msc->base_address, + tbl_msc->mmio_size, + "MPAM:MSC"); + } else if (iface =3D=3D MPAM_IFACE_PCC) { + props[next_prop++] =3D PROPERTY_ENTRY_U32("pcc-channel", + tbl_msc->base_address); + next_prop++; + } + + acpi_mpam_parse_irqs(pdev, tbl_msc, res, &next_res); + err =3D platform_device_add_resources(pdev, res, next_res); + if (err) + break; + + props[next_prop++] =3D PROPERTY_ENTRY_U32("arm,not-ready-us", + tbl_msc->max_nrdy_usec); + + /* + * The MSC's CPU affinity is described via its linked power + * management device, but only if it points at a Processor or + * Processor Container. + */ + if (parse_msc_pm_link(tbl_msc, pdev, &acpi_id)) { + props[next_prop++] =3D PROPERTY_ENTRY_U32("cpu_affinity", + acpi_id); + } + + err =3D device_create_managed_software_node(&pdev->dev, props, + NULL); + if (err) + break; + + /* Come back later if you want the RIS too */ + err =3D platform_device_add_data(pdev, tbl_msc, tbl_msc->length); + if (err) + break; + + err =3D platform_device_add(pdev); + if (err) + break; + } + + if (err) + platform_device_put(pdev); + + return err; +} + +int acpi_mpam_count_msc(void) +{ + struct acpi_table_header *table __free(acpi_table) =3D acpi_get_ta= ble_ret(ACPI_SIG_MPAM, 0); + char *table_end, *table_offset =3D (char *)(table + 1); + struct acpi_mpam_msc_node *tbl_msc; + int count =3D 0; + + if (IS_ERR(table)) + return 0; + + if (table->revision < 1) + return 0; + + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + table_end =3D (char *)table + table->length; + + while (table_offset < table_end) { + if (!tbl_msc->mmio_size) + continue; + + if (tbl_msc->length < sizeof(*tbl_msc)) + return -EINVAL; + + count++; + + table_offset +=3D tbl_msc->length; + tbl_msc =3D (struct acpi_mpam_msc_node *)table_offset; + } + + return count; +} + +/* + * Call after ACPI devices have been created, which happens behind acpi_sc= an_init() + * called from subsys_initcall(). PCC requires the mailbox driver, which is + * initialised from postcore_initcall(). + */ +subsys_initcall_sync(acpi_mpam_parse); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index fa9bb8c8ce95..835e3795ede3 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -408,7 +408,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __non= string_array __initconst ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI, - ACPI_SIG_NBFT }; + ACPI_SIG_NBFT, ACPI_SIG_MPAM }; =20 #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) =20 diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h new file mode 100644 index 000000000000..0edefa6ba019 --- /dev/null +++ b/include/linux/arm_mpam.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Arm Ltd. */ + +#ifndef __LINUX_ARM_MPAM_H +#define __LINUX_ARM_MPAM_H + +#include +#include + +struct mpam_msc; + +enum mpam_msc_iface { + MPAM_IFACE_MMIO, /* a real MPAM MSC */ + MPAM_IFACE_PCC, /* a fake MPAM MSC */ +}; + +enum mpam_class_types { + MPAM_CLASS_CACHE, /* Well known caches, e.g. L2 */ + MPAM_CLASS_MEMORY, /* Main memory */ + MPAM_CLASS_UNKNOWN, /* Everything else, e.g. SMMU */ +}; + +#ifdef CONFIG_ACPI_MPAM +/* Parse the ACPI description of resources entries for this MSC. */ +int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc); + +int acpi_mpam_count_msc(void); +#else +static inline int acpi_mpam_parse_resources(struct mpam_msc *msc, + struct acpi_mpam_msc_node *tbl_msc) +{ + return -EINVAL; +} + +static inline int acpi_mpam_count_msc(void) { return -EINVAL; } +#endif + +static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, + int component_id) +{ + return -EINVAL; +} + +#endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id AD10825A322; Fri, 22 Aug 2025 15:31:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876719; cv=none; b=HrLO846lgPUYeXmqu37vd24t+DNDw4Z9GzpvXAYA+Z5tAQ6EK8OdI5GWoRD1TiLYmqLJk85jI/Scm8OhU53o9PZu0xW5YTDWSd174LG/YixpGvQZjGetndA0IPm5bFgYkb2G2ZatBQMgtTxWXLocI+ck9U4B9Xe2IvbUjSGegpo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876719; c=relaxed/simple; bh=gOO5vOGv4Ephh2ylVQ4UyXsToGw9bnLCB7X8rKODstk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oCOYvv5MbAWKOCn7TrvJWvmWA/kn3vxATnQsBP8HW1rCyxTBcehOk6TiFHPri5oCTeC52nTXXQvawyJCONQUk96Boz7vWWISiddiVregokTbUwAHN3NwsT5evkacN9IwKNI8vu7D4SR9r1a6K6jiZcxK9mDuMEHGtrg31pGX85c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0E19115A1; Fri, 22 Aug 2025 08:31:49 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 00FD33F63F; Fri, 22 Aug 2025 08:31:51 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 09/33] dt-bindings: arm: Add MPAM MSC binding Date: Fri, 22 Aug 2025 15:29:50 +0000 Message-Id: <20250822153048.2287-10-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rob Herring The binding is designed around the assumption that an MSC will be a sub-block of something else such as a memory controller, cache controller, or IOMMU. However, it's certainly possible a design does not have that association or has a mixture of both, so the binding illustrates how we can support that with RIS child nodes. A key part of MPAM is we need to know about all of the MSCs in the system before it can be enabled. This drives the need for the genericish 'arm,mpam-msc' compatible. Though we can't assume an MSC is accessible until a h/w specific driver potentially enables the h/w. Cc: James Morse Signed-off-by: Rob Herring Signed-off-by: James Morse --- Changes since RFC: * Syntax(?) corrections supplied by Rob. * Culled some context in the example. --- .../devicetree/bindings/arm/arm,mpam-msc.yaml | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml diff --git a/Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml b/Docu= mentation/devicetree/bindings/arm/arm,mpam-msc.yaml new file mode 100644 index 000000000000..d984817b3385 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/arm,mpam-msc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm Memory System Resource Partitioning and Monitoring (MPAM) + +description: | + The Arm MPAM specification can be found here: + + https://developer.arm.com/documentation/ddi0598/latest + +maintainers: + - Rob Herring + +properties: + compatible: + items: + - const: arm,mpam-msc # Further details are discov= erable + - const: arm,mpam-memory-controller-msc + + reg: + maxItems: 1 + description: A memory region containing registers as defined in the MP= AM + specification. + + interrupts: + minItems: 1 + items: + - description: error (optional) + - description: overflow (optional, only for monitoring) + + interrupt-names: + oneOf: + - items: + - enum: [ error, overflow ] + - items: + - const: error + - const: overflow + + arm,not-ready-us: + description: The maximum time in microseconds for monitoring data to be + accurate after a settings change. For more information, see the + Not-Ready (NRDY) bit description in the MPAM specification. + + numa-node-id: true # see NUMA binding + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^ris@[0-9a-f]$': + type: object + additionalProperties: false + description: + RIS nodes for each RIS in an MSC. These nodes are required for each = RIS + implementing known MPAM controls + + properties: + compatible: + enum: + # Bulk storage for cache + - arm,mpam-cache + # Memory bandwidth + - arm,mpam-memory + + reg: + minimum: 0 + maximum: 0xf + + cpus: + description: + Phandle(s) to the CPU node(s) this RIS belongs to. By default, t= he parent + device's affinity is used. + + arm,mpam-device: + $ref: /schemas/types.yaml#/definitions/phandle + description: + By default, the MPAM enabled device associated with a RIS is the= MSC's + parent node. It is possible for each RIS to be associated with d= ifferent + devices in which case 'arm,mpam-device' should be used. + + required: + - compatible + - reg + +required: + - compatible + - reg + +dependencies: + interrupts: [ interrupt-names ] + +additionalProperties: false + +examples: + - | + L3: cache-controller@30000000 { + compatible =3D "arm,dsu-l3-cache", "cache"; + cache-level =3D <3>; + cache-unified; + + ranges =3D <0x0 0x30000000 0x800000>; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@10000 { + compatible =3D "arm,mpam-msc"; + + /* CPU affinity implied by parent cache node's */ + reg =3D <0x10000 0x2000>; + interrupts =3D <1>, <2>; + interrupt-names =3D "error", "overflow"; + arm,not-ready-us =3D <1>; + }; + }; + + mem: memory-controller@20000 { + compatible =3D "foo,a-memory-controller"; + reg =3D <0x20000 0x1000>; + + #address-cells =3D <1>; + #size-cells =3D <1>; + ranges; + + msc@21000 { + compatible =3D "arm,mpam-memory-controller-msc", "arm,mpam-msc= "; + reg =3D <0x21000 0x1000>; + interrupts =3D <3>; + interrupt-names =3D "error"; + arm,not-ready-us =3D <1>; + numa-node-id =3D <1>; + }; + }; + + iommu@40000 { + reg =3D <0x40000 0x1000>; + + ranges; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@41000 { + compatible =3D "arm,mpam-msc"; + reg =3D <0 0x1000>; + interrupts =3D <5>, <6>; + interrupt-names =3D "error", "overflow"; + arm,not-ready-us =3D <1>; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + ris@2 { + compatible =3D "arm,mpam-cache"; + reg =3D <0>; + // TODO: How to map to device(s)? + }; + }; + }; + + msc@80000 { + compatible =3D "foo,a-standalone-msc"; + reg =3D <0x80000 0x1000>; + + clocks =3D <&clks 123>; + + ranges; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@10000 { + compatible =3D "arm,mpam-msc"; + + reg =3D <0x10000 0x2000>; + interrupts =3D <7>; + interrupt-names =3D "overflow"; + arm,not-ready-us =3D <1>; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + ris@0 { + compatible =3D "arm,mpam-cache"; + reg =3D <0>; + arm,mpam-device =3D <&L2_0>; + }; + + ris@1 { + compatible =3D "arm,mpam-memory"; + reg =3D <1>; + arm,mpam-device =3D <&mem>; + }; + }; + }; + +... --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7E7AE231A32; Fri, 22 Aug 2025 15:35:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876911; cv=none; b=mtuwYKZvVnEvyHfrQYZOH/BpG1m1XGohXnGGhxY7NptmDEX5TlggBiUw4LFYjrO92jV1SQclsYdpjTN10MvIAJ/b4D7mjhf6i8ZAsqFqhqvfWOo260hHfvcoltqB8psco7RQaOo16fLPL+gUYOaI/gG6SyYRu12L0r4ag/bVujQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876911; c=relaxed/simple; bh=gOO5vOGv4Ephh2ylVQ4UyXsToGw9bnLCB7X8rKODstk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b6UAq1fZhXgW5GXyOvD0JHISBM4oVxWXIC8rFDjJrYer8SmSw5ZSrkY3oheiSyMOuL4gyFD+Wi7LOnlbR0g7j+Is4Tpg0Uv+OwgN1usmlgpl3lZ6iGq3ckL3NzV4s/rPTRxHLXLe+DIu9Vouc1pkiRUxWNnbmjmtlhmKBscTAbw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9C30F27B5; Fri, 22 Aug 2025 08:35:00 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 8CDFD3F63F; Fri, 22 Aug 2025 08:35:03 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 09/33] dt-bindings: arm: Add MPAM MSC binding Date: Fri, 22 Aug 2025 15:30:24 +0000 Message-Id: <20250822153048.2287-44-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rob Herring The binding is designed around the assumption that an MSC will be a sub-block of something else such as a memory controller, cache controller, or IOMMU. However, it's certainly possible a design does not have that association or has a mixture of both, so the binding illustrates how we can support that with RIS child nodes. A key part of MPAM is we need to know about all of the MSCs in the system before it can be enabled. This drives the need for the genericish 'arm,mpam-msc' compatible. Though we can't assume an MSC is accessible until a h/w specific driver potentially enables the h/w. Cc: James Morse Signed-off-by: Rob Herring Signed-off-by: James Morse --- Changes since RFC: * Syntax(?) corrections supplied by Rob. * Culled some context in the example. --- .../devicetree/bindings/arm/arm,mpam-msc.yaml | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml diff --git a/Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml b/Docu= mentation/devicetree/bindings/arm/arm,mpam-msc.yaml new file mode 100644 index 000000000000..d984817b3385 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,mpam-msc.yaml @@ -0,0 +1,200 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/arm,mpam-msc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm Memory System Resource Partitioning and Monitoring (MPAM) + +description: | + The Arm MPAM specification can be found here: + + https://developer.arm.com/documentation/ddi0598/latest + +maintainers: + - Rob Herring + +properties: + compatible: + items: + - const: arm,mpam-msc # Further details are discov= erable + - const: arm,mpam-memory-controller-msc + + reg: + maxItems: 1 + description: A memory region containing registers as defined in the MP= AM + specification. + + interrupts: + minItems: 1 + items: + - description: error (optional) + - description: overflow (optional, only for monitoring) + + interrupt-names: + oneOf: + - items: + - enum: [ error, overflow ] + - items: + - const: error + - const: overflow + + arm,not-ready-us: + description: The maximum time in microseconds for monitoring data to be + accurate after a settings change. For more information, see the + Not-Ready (NRDY) bit description in the MPAM specification. + + numa-node-id: true # see NUMA binding + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + '^ris@[0-9a-f]$': + type: object + additionalProperties: false + description: + RIS nodes for each RIS in an MSC. These nodes are required for each = RIS + implementing known MPAM controls + + properties: + compatible: + enum: + # Bulk storage for cache + - arm,mpam-cache + # Memory bandwidth + - arm,mpam-memory + + reg: + minimum: 0 + maximum: 0xf + + cpus: + description: + Phandle(s) to the CPU node(s) this RIS belongs to. By default, t= he parent + device's affinity is used. + + arm,mpam-device: + $ref: /schemas/types.yaml#/definitions/phandle + description: + By default, the MPAM enabled device associated with a RIS is the= MSC's + parent node. It is possible for each RIS to be associated with d= ifferent + devices in which case 'arm,mpam-device' should be used. + + required: + - compatible + - reg + +required: + - compatible + - reg + +dependencies: + interrupts: [ interrupt-names ] + +additionalProperties: false + +examples: + - | + L3: cache-controller@30000000 { + compatible =3D "arm,dsu-l3-cache", "cache"; + cache-level =3D <3>; + cache-unified; + + ranges =3D <0x0 0x30000000 0x800000>; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@10000 { + compatible =3D "arm,mpam-msc"; + + /* CPU affinity implied by parent cache node's */ + reg =3D <0x10000 0x2000>; + interrupts =3D <1>, <2>; + interrupt-names =3D "error", "overflow"; + arm,not-ready-us =3D <1>; + }; + }; + + mem: memory-controller@20000 { + compatible =3D "foo,a-memory-controller"; + reg =3D <0x20000 0x1000>; + + #address-cells =3D <1>; + #size-cells =3D <1>; + ranges; + + msc@21000 { + compatible =3D "arm,mpam-memory-controller-msc", "arm,mpam-msc= "; + reg =3D <0x21000 0x1000>; + interrupts =3D <3>; + interrupt-names =3D "error"; + arm,not-ready-us =3D <1>; + numa-node-id =3D <1>; + }; + }; + + iommu@40000 { + reg =3D <0x40000 0x1000>; + + ranges; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@41000 { + compatible =3D "arm,mpam-msc"; + reg =3D <0 0x1000>; + interrupts =3D <5>, <6>; + interrupt-names =3D "error", "overflow"; + arm,not-ready-us =3D <1>; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + ris@2 { + compatible =3D "arm,mpam-cache"; + reg =3D <0>; + // TODO: How to map to device(s)? + }; + }; + }; + + msc@80000 { + compatible =3D "foo,a-standalone-msc"; + reg =3D <0x80000 0x1000>; + + clocks =3D <&clks 123>; + + ranges; + #address-cells =3D <1>; + #size-cells =3D <1>; + + msc@10000 { + compatible =3D "arm,mpam-msc"; + + reg =3D <0x10000 0x2000>; + interrupts =3D <7>; + interrupt-names =3D "overflow"; + arm,not-ready-us =3D <1>; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + ris@0 { + compatible =3D "arm,mpam-cache"; + reg =3D <0>; + arm,mpam-device =3D <&L2_0>; + }; + + ris@1 { + compatible =3D "arm,mpam-memory"; + reg =3D <1>; + arm,mpam-device =3D <&mem>; + }; + }; + }; + +... --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 50A9E225A29; Fri, 22 Aug 2025 15:32:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876725; cv=none; b=Yyo1SxcBJMkKaqsXz5eEPGNhm906f54ntKzCSUzOcWVm0cGDHNMUTSsPOaQ/0dGIJ7qWVo86P5XUxZo3VSI+3Vqpl3iNGN8pkQQBT6KBjalv7shd0/hPbwV4jyIuHYYSVBi8ErzjQPlrutoVPEKNxDc2qxVoSdDTTLn+q4kxjHY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876725; c=relaxed/simple; bh=vIuGdNRCPU5J+19v/hxIlIGyES0PhAoT/cRI6hbqitk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HBuZjVr96vaHYT30Xdl+vlixz032p0ndEtNsNyqwe/irKGa1AZBRJdCVHzJc8Ix+t33DY6qOCbxUZ5auaEcNed8m8GxDdTVii3yP0WfUzbwidNWSeRqbLsrE1CPKiEZD84AUPFtQE4Xd37Gh7yrC/Bm5+oG4DLPhPlB9XHne/KE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9D08427DC; Fri, 22 Aug 2025 08:31:54 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 904113F63F; Fri, 22 Aug 2025 08:31:57 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 10/33] arm_mpam: Add probe/remove for mpam msc driver and kbuild boiler plate Date: Fri, 22 Aug 2025 15:29:51 +0000 Message-Id: <20250822153048.2287-11-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Probing MPAM is convoluted. MSCs that are integrated with a CPU may only be accessible from those CPUs, and they may not be online. Touching the hardware early is pointless as MPAM can't be used until the system-wide common values for num_partid and num_pmg have been discovered. Start with driver probe/remove and mapping the MSC. CC: Carl Worth Signed-off-by: James Morse --- Changes since RFC: * Check for status=3Dbroken DT devices. * Moved all the files around. * Made Kconfig symbols depend on EXPERT --- arch/arm64/Kconfig | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/resctrl/Kconfig | 11 ++ drivers/resctrl/Makefile | 4 + drivers/resctrl/mpam_devices.c | 336 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 62 ++++++ 7 files changed, 417 insertions(+) create mode 100644 drivers/resctrl/Kconfig create mode 100644 drivers/resctrl/Makefile create mode 100644 drivers/resctrl/mpam_devices.c create mode 100644 drivers/resctrl/mpam_internal.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e51ccf1da102..ea3c54e04275 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2062,6 +2062,7 @@ config ARM64_TLB_RANGE =20 config ARM64_MPAM bool "Enable support for MPAM" + select ARM64_MPAM_DRIVER select ACPI_MPAM if ACPI help Memory Partitioning and Monitoring is an optional extension diff --git a/drivers/Kconfig b/drivers/Kconfig index 4915a63866b0..3054b50a2f4c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -251,4 +251,6 @@ source "drivers/hte/Kconfig" =20 source "drivers/cdx/Kconfig" =20 +source "drivers/resctrl/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b5749cf67044..f41cf4eddeba 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -194,5 +194,6 @@ obj-$(CONFIG_HTE) +=3D hte/ obj-$(CONFIG_DRM_ACCEL) +=3D accel/ obj-$(CONFIG_CDX_BUS) +=3D cdx/ obj-$(CONFIG_DPLL) +=3D dpll/ +obj-y +=3D resctrl/ =20 obj-$(CONFIG_S390) +=3D s390/ diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig new file mode 100644 index 000000000000..dff7b87280ab --- /dev/null +++ b/drivers/resctrl/Kconfig @@ -0,0 +1,11 @@ +# Confusingly, this is everything but the CPU bits of MPAM. CPU here means +# CPU resources, not containers or cgroups etc. +config ARM64_MPAM_DRIVER + bool "MPAM driver for System IP, e,g. caches and memory controllers" + depends on ARM64_MPAM && EXPERT + +config ARM64_MPAM_DRIVER_DEBUG + bool "Enable debug messages from the MPAM driver." + depends on ARM64_MPAM_DRIVER + help + Say yes here to enable debug messages from the MPAM driver. diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile new file mode 100644 index 000000000000..92b48fa20108 --- /dev/null +++ b/drivers/resctrl/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_ARM64_MPAM_DRIVER) +=3D mpam.o +mpam-y +=3D mpam_devices.o + +cflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) +=3D -DDEBUG diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c new file mode 100644 index 000000000000..a0d9a699a6e7 --- /dev/null +++ b/drivers/resctrl/mpam_devices.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Arm Ltd. + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mpam_internal.h" + +/* + * mpam_list_lock protects the SRCU lists when writing. Once the + * mpam_enabled key is enabled these lists are read-only, + * unless the error interrupt disables the driver. + */ +static DEFINE_MUTEX(mpam_list_lock); +static LIST_HEAD(mpam_all_msc); + +static struct srcu_struct mpam_srcu; + +/* MPAM isn't available until all the MSC have been probed. */ +static u32 mpam_num_msc; + +static void mpam_discovery_complete(void) +{ + pr_err("Discovered all MSC\n"); +} + +static int mpam_dt_count_msc(void) +{ + int count =3D 0; + struct device_node *np; + + for_each_compatible_node(np, NULL, "arm,mpam-msc") { + if (of_device_is_available(np)) + count++; + } + + return count; +} + +static int mpam_dt_parse_resource(struct mpam_msc *msc, struct device_node= *np, + u32 ris_idx) +{ + int err =3D 0; + u32 level =3D 0; + unsigned long cache_id; + struct device_node *cache; + + do { + if (of_device_is_compatible(np, "arm,mpam-cache")) { + cache =3D of_parse_phandle(np, "arm,mpam-device", 0); + if (!cache) { + pr_err("Failed to read phandle\n"); + break; + } + } else if (of_device_is_compatible(np->parent, "cache")) { + cache =3D of_node_get(np->parent); + } else { + /* For now, only caches are supported */ + cache =3D NULL; + break; + } + + err =3D of_property_read_u32(cache, "cache-level", &level); + if (err) { + pr_err("Failed to read cache-level\n"); + break; + } + + cache_id =3D cache_of_calculate_id(cache); + if (cache_id =3D=3D ~0UL) { + err =3D -ENOENT; + break; + } + + err =3D mpam_ris_create(msc, ris_idx, MPAM_CLASS_CACHE, level, + cache_id); + } while (0); + of_node_put(cache); + + return err; +} + +static int mpam_dt_parse_resources(struct mpam_msc *msc, void *ignored) +{ + int err, num_ris =3D 0; + const u32 *ris_idx_p; + struct device_node *iter, *np; + + np =3D msc->pdev->dev.of_node; + for_each_child_of_node(np, iter) { + ris_idx_p =3D of_get_property(iter, "reg", NULL); + if (ris_idx_p) { + num_ris++; + err =3D mpam_dt_parse_resource(msc, iter, *ris_idx_p); + if (err) { + of_node_put(iter); + return err; + } + } + } + + if (!num_ris) + mpam_dt_parse_resource(msc, np, 0); + + return err; +} + +/* + * An MSC can control traffic from a set of CPUs, but may only be accessib= le + * from a (hopefully wider) set of CPUs. The common reason for this is pow= er + * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the + * the corresponding cache may also be powered off. By making accesses from + * one of those CPUs, we ensure this isn't the case. + */ +static int update_msc_accessibility(struct mpam_msc *msc) +{ + struct device_node *parent; + u32 affinity_id; + int err; + + if (!acpi_disabled) { + err =3D device_property_read_u32(&msc->pdev->dev, "cpu_affinity", + &affinity_id); + if (err) + cpumask_copy(&msc->accessibility, cpu_possible_mask); + else + acpi_pptt_get_cpus_from_container(affinity_id, + &msc->accessibility); + + return 0; + } + + /* This depends on the path to of_node */ + parent =3D of_get_parent(msc->pdev->dev.of_node); + if (parent =3D=3D of_root) { + cpumask_copy(&msc->accessibility, cpu_possible_mask); + err =3D 0; + } else { + err =3D -EINVAL; + pr_err("Cannot determine accessibility of MSC: %s\n", + dev_name(&msc->pdev->dev)); + } + of_node_put(parent); + + return err; +} + +static int fw_num_msc; + +static void mpam_pcc_rx_callback(struct mbox_client *cl, void *msg) +{ + /* TODO: wake up tasks blocked on this MSC's PCC channel */ +} + +static void mpam_msc_drv_remove(struct platform_device *pdev) +{ + struct mpam_msc *msc =3D platform_get_drvdata(pdev); + + if (!msc) + return; + + mutex_lock(&mpam_list_lock); + mpam_num_msc--; + platform_set_drvdata(pdev, NULL); + list_del_rcu(&msc->glbl_list); + synchronize_srcu(&mpam_srcu); + devm_kfree(&pdev->dev, msc); + mutex_unlock(&mpam_list_lock); +} + +static int mpam_msc_drv_probe(struct platform_device *pdev) +{ + int err; + struct mpam_msc *msc; + struct resource *msc_res; + void *plat_data =3D pdev->dev.platform_data; + + mutex_lock(&mpam_list_lock); + do { + msc =3D devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); + if (!msc) { + err =3D -ENOMEM; + break; + } + + mutex_init(&msc->probe_lock); + mutex_init(&msc->part_sel_lock); + mutex_init(&msc->outer_mon_sel_lock); + raw_spin_lock_init(&msc->inner_mon_sel_lock); + msc->id =3D mpam_num_msc++; + msc->pdev =3D pdev; + INIT_LIST_HEAD_RCU(&msc->glbl_list); + INIT_LIST_HEAD_RCU(&msc->ris); + + err =3D update_msc_accessibility(msc); + if (err) + break; + if (cpumask_empty(&msc->accessibility)) { + pr_err_once("msc:%u is not accessible from any CPU!", + msc->id); + err =3D -EINVAL; + break; + } + + if (device_property_read_u32(&pdev->dev, "pcc-channel", + &msc->pcc_subspace_id)) + msc->iface =3D MPAM_IFACE_MMIO; + else + msc->iface =3D MPAM_IFACE_PCC; + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) { + void __iomem *io; + + io =3D devm_platform_get_and_ioremap_resource(pdev, 0, + &msc_res); + if (IS_ERR(io)) { + pr_err("Failed to map MSC base address\n"); + err =3D PTR_ERR(io); + break; + } + msc->mapped_hwpage_sz =3D msc_res->end - msc_res->start; + msc->mapped_hwpage =3D io; + } else if (msc->iface =3D=3D MPAM_IFACE_PCC) { + msc->pcc_cl.dev =3D &pdev->dev; + msc->pcc_cl.rx_callback =3D mpam_pcc_rx_callback; + msc->pcc_cl.tx_block =3D false; + msc->pcc_cl.tx_tout =3D 1000; /* 1s */ + msc->pcc_cl.knows_txdone =3D false; + + msc->pcc_chan =3D pcc_mbox_request_channel(&msc->pcc_cl, + msc->pcc_subspace_id); + if (IS_ERR(msc->pcc_chan)) { + pr_err("Failed to request MSC PCC channel\n"); + err =3D PTR_ERR(msc->pcc_chan); + break; + } + } + + list_add_rcu(&msc->glbl_list, &mpam_all_msc); + platform_set_drvdata(pdev, msc); + } while (0); + mutex_unlock(&mpam_list_lock); + + if (!err) { + /* Create RIS entries described by firmware */ + if (!acpi_disabled) + err =3D acpi_mpam_parse_resources(msc, plat_data); + else + err =3D mpam_dt_parse_resources(msc, plat_data); + } + + if (!err && fw_num_msc =3D=3D mpam_num_msc) + mpam_discovery_complete(); + + if (err && msc) + mpam_msc_drv_remove(pdev); + + return err; +} + +static const struct of_device_id mpam_of_match[] =3D { + { .compatible =3D "arm,mpam-msc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpam_of_match); + +static struct platform_driver mpam_msc_driver =3D { + .driver =3D { + .name =3D "mpam_msc", + .of_match_table =3D of_match_ptr(mpam_of_match), + }, + .probe =3D mpam_msc_drv_probe, + .remove =3D mpam_msc_drv_remove, +}; + +/* + * MSC that are hidden under caches are not created as platform devices + * as there is no cache driver. Caches are also special-cased in + * update_msc_accessibility(). + */ +static void mpam_dt_create_foundling_msc(void) +{ + int err; + struct device_node *cache; + + for_each_compatible_node(cache, NULL, "cache") { + err =3D of_platform_populate(cache, mpam_of_match, NULL, NULL); + if (err) + pr_err("Failed to create MSC devices under caches\n"); + } +} + +static int __init mpam_msc_driver_init(void) +{ + if (!system_supports_mpam()) + return -EOPNOTSUPP; + + init_srcu_struct(&mpam_srcu); + + if (!acpi_disabled) + fw_num_msc =3D acpi_mpam_count_msc(); + else + fw_num_msc =3D mpam_dt_count_msc(); + + if (fw_num_msc <=3D 0) { + pr_err("No MSC devices found in firmware\n"); + return -EINVAL; + } + + if (acpi_disabled) + mpam_dt_create_foundling_msc(); + + return platform_driver_register(&mpam_msc_driver); +} +subsys_initcall(mpam_msc_driver_init); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h new file mode 100644 index 000000000000..07e0f240eaca --- /dev/null +++ b/drivers/resctrl/mpam_internal.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// Copyright (C) 2024 Arm Ltd. + +#ifndef MPAM_INTERNAL_H +#define MPAM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +struct mpam_msc { + /* member of mpam_all_msc */ + struct list_head glbl_list; + + int id; + struct platform_device *pdev; + + /* Not modified after mpam_is_enabled() becomes true */ + enum mpam_msc_iface iface; + u32 pcc_subspace_id; + struct mbox_client pcc_cl; + struct pcc_mbox_chan *pcc_chan; + u32 nrdy_usec; + cpumask_t accessibility; + + /* + * probe_lock is only take during discovery. After discovery these + * properties become read-only and the lists are protected by SRCU. + */ + struct mutex probe_lock; + unsigned long ris_idxs[128 / BITS_PER_LONG]; + u32 ris_max; + + /* mpam_msc_ris of this component */ + struct list_head ris; + + /* + * part_sel_lock protects access to the MSC hardware registers that are + * affected by MPAMCFG_PART_SEL. (including the ID registers that vary + * by RIS). + * If needed, take msc->lock first. + */ + struct mutex part_sel_lock; + + /* + * mon_sel_lock protects access to the MSC hardware registers that are + * affeted by MPAMCFG_MON_SEL. + * If needed, take msc->lock first. + */ + struct mutex outer_mon_sel_lock; + raw_spinlock_t inner_mon_sel_lock; + unsigned long inner_mon_sel_flags; + + void __iomem *mapped_hwpage; + size_t mapped_hwpage_sz; +}; + +#endif /* MPAM_INTERNAL_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1902727703A; Fri, 22 Aug 2025 15:35:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876916; cv=none; b=ZDZOi2qUYlNNwQqo9dtC9N/C2Jz4L0gByjJ9eOdhB2K7DFWf9gotQQcPMupfZf+iezO+8a2gObAhzKahE3Qx0E5i9hVuhJBQ9gqqJ2eJYcMcT8eEfzwQ/95S2qRXdssyn8QxOOg3048rbMTPh+K4btrzPRB3MPpNOtXW9cEllrE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876916; c=relaxed/simple; bh=vIuGdNRCPU5J+19v/hxIlIGyES0PhAoT/cRI6hbqitk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=riepn5UwTl7pDZzGJ7dRQYDOS5QnxTgpyz6lFJgMzWvb5NTzg5QHSatpPoKJWZLV3XKrs0dUv5249gRAlAFwj/lNZcAVcmCIDfzEaSXC3C0I6TxUZbqq6o1r2nWfx1Wj1DtigsNkbopCvVX0pfbw+9MpA6yfljVw4ZTAIN8ENiw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 39E102880; Fri, 22 Aug 2025 08:35:06 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2B5873F63F; Fri, 22 Aug 2025 08:35:09 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 10/33] arm_mpam: Add probe/remove for mpam msc driver and kbuild boiler plate Date: Fri, 22 Aug 2025 15:30:25 +0000 Message-Id: <20250822153048.2287-45-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Probing MPAM is convoluted. MSCs that are integrated with a CPU may only be accessible from those CPUs, and they may not be online. Touching the hardware early is pointless as MPAM can't be used until the system-wide common values for num_partid and num_pmg have been discovered. Start with driver probe/remove and mapping the MSC. CC: Carl Worth Signed-off-by: James Morse --- Changes since RFC: * Check for status=3Dbroken DT devices. * Moved all the files around. * Made Kconfig symbols depend on EXPERT --- arch/arm64/Kconfig | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/resctrl/Kconfig | 11 ++ drivers/resctrl/Makefile | 4 + drivers/resctrl/mpam_devices.c | 336 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 62 ++++++ 7 files changed, 417 insertions(+) create mode 100644 drivers/resctrl/Kconfig create mode 100644 drivers/resctrl/Makefile create mode 100644 drivers/resctrl/mpam_devices.c create mode 100644 drivers/resctrl/mpam_internal.h diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e51ccf1da102..ea3c54e04275 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2062,6 +2062,7 @@ config ARM64_TLB_RANGE =20 config ARM64_MPAM bool "Enable support for MPAM" + select ARM64_MPAM_DRIVER select ACPI_MPAM if ACPI help Memory Partitioning and Monitoring is an optional extension diff --git a/drivers/Kconfig b/drivers/Kconfig index 4915a63866b0..3054b50a2f4c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -251,4 +251,6 @@ source "drivers/hte/Kconfig" =20 source "drivers/cdx/Kconfig" =20 +source "drivers/resctrl/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b5749cf67044..f41cf4eddeba 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -194,5 +194,6 @@ obj-$(CONFIG_HTE) +=3D hte/ obj-$(CONFIG_DRM_ACCEL) +=3D accel/ obj-$(CONFIG_CDX_BUS) +=3D cdx/ obj-$(CONFIG_DPLL) +=3D dpll/ +obj-y +=3D resctrl/ =20 obj-$(CONFIG_S390) +=3D s390/ diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig new file mode 100644 index 000000000000..dff7b87280ab --- /dev/null +++ b/drivers/resctrl/Kconfig @@ -0,0 +1,11 @@ +# Confusingly, this is everything but the CPU bits of MPAM. CPU here means +# CPU resources, not containers or cgroups etc. +config ARM64_MPAM_DRIVER + bool "MPAM driver for System IP, e,g. caches and memory controllers" + depends on ARM64_MPAM && EXPERT + +config ARM64_MPAM_DRIVER_DEBUG + bool "Enable debug messages from the MPAM driver." + depends on ARM64_MPAM_DRIVER + help + Say yes here to enable debug messages from the MPAM driver. diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile new file mode 100644 index 000000000000..92b48fa20108 --- /dev/null +++ b/drivers/resctrl/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_ARM64_MPAM_DRIVER) +=3D mpam.o +mpam-y +=3D mpam_devices.o + +cflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) +=3D -DDEBUG diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c new file mode 100644 index 000000000000..a0d9a699a6e7 --- /dev/null +++ b/drivers/resctrl/mpam_devices.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2025 Arm Ltd. + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mpam_internal.h" + +/* + * mpam_list_lock protects the SRCU lists when writing. Once the + * mpam_enabled key is enabled these lists are read-only, + * unless the error interrupt disables the driver. + */ +static DEFINE_MUTEX(mpam_list_lock); +static LIST_HEAD(mpam_all_msc); + +static struct srcu_struct mpam_srcu; + +/* MPAM isn't available until all the MSC have been probed. */ +static u32 mpam_num_msc; + +static void mpam_discovery_complete(void) +{ + pr_err("Discovered all MSC\n"); +} + +static int mpam_dt_count_msc(void) +{ + int count =3D 0; + struct device_node *np; + + for_each_compatible_node(np, NULL, "arm,mpam-msc") { + if (of_device_is_available(np)) + count++; + } + + return count; +} + +static int mpam_dt_parse_resource(struct mpam_msc *msc, struct device_node= *np, + u32 ris_idx) +{ + int err =3D 0; + u32 level =3D 0; + unsigned long cache_id; + struct device_node *cache; + + do { + if (of_device_is_compatible(np, "arm,mpam-cache")) { + cache =3D of_parse_phandle(np, "arm,mpam-device", 0); + if (!cache) { + pr_err("Failed to read phandle\n"); + break; + } + } else if (of_device_is_compatible(np->parent, "cache")) { + cache =3D of_node_get(np->parent); + } else { + /* For now, only caches are supported */ + cache =3D NULL; + break; + } + + err =3D of_property_read_u32(cache, "cache-level", &level); + if (err) { + pr_err("Failed to read cache-level\n"); + break; + } + + cache_id =3D cache_of_calculate_id(cache); + if (cache_id =3D=3D ~0UL) { + err =3D -ENOENT; + break; + } + + err =3D mpam_ris_create(msc, ris_idx, MPAM_CLASS_CACHE, level, + cache_id); + } while (0); + of_node_put(cache); + + return err; +} + +static int mpam_dt_parse_resources(struct mpam_msc *msc, void *ignored) +{ + int err, num_ris =3D 0; + const u32 *ris_idx_p; + struct device_node *iter, *np; + + np =3D msc->pdev->dev.of_node; + for_each_child_of_node(np, iter) { + ris_idx_p =3D of_get_property(iter, "reg", NULL); + if (ris_idx_p) { + num_ris++; + err =3D mpam_dt_parse_resource(msc, iter, *ris_idx_p); + if (err) { + of_node_put(iter); + return err; + } + } + } + + if (!num_ris) + mpam_dt_parse_resource(msc, np, 0); + + return err; +} + +/* + * An MSC can control traffic from a set of CPUs, but may only be accessib= le + * from a (hopefully wider) set of CPUs. The common reason for this is pow= er + * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the + * the corresponding cache may also be powered off. By making accesses from + * one of those CPUs, we ensure this isn't the case. + */ +static int update_msc_accessibility(struct mpam_msc *msc) +{ + struct device_node *parent; + u32 affinity_id; + int err; + + if (!acpi_disabled) { + err =3D device_property_read_u32(&msc->pdev->dev, "cpu_affinity", + &affinity_id); + if (err) + cpumask_copy(&msc->accessibility, cpu_possible_mask); + else + acpi_pptt_get_cpus_from_container(affinity_id, + &msc->accessibility); + + return 0; + } + + /* This depends on the path to of_node */ + parent =3D of_get_parent(msc->pdev->dev.of_node); + if (parent =3D=3D of_root) { + cpumask_copy(&msc->accessibility, cpu_possible_mask); + err =3D 0; + } else { + err =3D -EINVAL; + pr_err("Cannot determine accessibility of MSC: %s\n", + dev_name(&msc->pdev->dev)); + } + of_node_put(parent); + + return err; +} + +static int fw_num_msc; + +static void mpam_pcc_rx_callback(struct mbox_client *cl, void *msg) +{ + /* TODO: wake up tasks blocked on this MSC's PCC channel */ +} + +static void mpam_msc_drv_remove(struct platform_device *pdev) +{ + struct mpam_msc *msc =3D platform_get_drvdata(pdev); + + if (!msc) + return; + + mutex_lock(&mpam_list_lock); + mpam_num_msc--; + platform_set_drvdata(pdev, NULL); + list_del_rcu(&msc->glbl_list); + synchronize_srcu(&mpam_srcu); + devm_kfree(&pdev->dev, msc); + mutex_unlock(&mpam_list_lock); +} + +static int mpam_msc_drv_probe(struct platform_device *pdev) +{ + int err; + struct mpam_msc *msc; + struct resource *msc_res; + void *plat_data =3D pdev->dev.platform_data; + + mutex_lock(&mpam_list_lock); + do { + msc =3D devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); + if (!msc) { + err =3D -ENOMEM; + break; + } + + mutex_init(&msc->probe_lock); + mutex_init(&msc->part_sel_lock); + mutex_init(&msc->outer_mon_sel_lock); + raw_spin_lock_init(&msc->inner_mon_sel_lock); + msc->id =3D mpam_num_msc++; + msc->pdev =3D pdev; + INIT_LIST_HEAD_RCU(&msc->glbl_list); + INIT_LIST_HEAD_RCU(&msc->ris); + + err =3D update_msc_accessibility(msc); + if (err) + break; + if (cpumask_empty(&msc->accessibility)) { + pr_err_once("msc:%u is not accessible from any CPU!", + msc->id); + err =3D -EINVAL; + break; + } + + if (device_property_read_u32(&pdev->dev, "pcc-channel", + &msc->pcc_subspace_id)) + msc->iface =3D MPAM_IFACE_MMIO; + else + msc->iface =3D MPAM_IFACE_PCC; + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) { + void __iomem *io; + + io =3D devm_platform_get_and_ioremap_resource(pdev, 0, + &msc_res); + if (IS_ERR(io)) { + pr_err("Failed to map MSC base address\n"); + err =3D PTR_ERR(io); + break; + } + msc->mapped_hwpage_sz =3D msc_res->end - msc_res->start; + msc->mapped_hwpage =3D io; + } else if (msc->iface =3D=3D MPAM_IFACE_PCC) { + msc->pcc_cl.dev =3D &pdev->dev; + msc->pcc_cl.rx_callback =3D mpam_pcc_rx_callback; + msc->pcc_cl.tx_block =3D false; + msc->pcc_cl.tx_tout =3D 1000; /* 1s */ + msc->pcc_cl.knows_txdone =3D false; + + msc->pcc_chan =3D pcc_mbox_request_channel(&msc->pcc_cl, + msc->pcc_subspace_id); + if (IS_ERR(msc->pcc_chan)) { + pr_err("Failed to request MSC PCC channel\n"); + err =3D PTR_ERR(msc->pcc_chan); + break; + } + } + + list_add_rcu(&msc->glbl_list, &mpam_all_msc); + platform_set_drvdata(pdev, msc); + } while (0); + mutex_unlock(&mpam_list_lock); + + if (!err) { + /* Create RIS entries described by firmware */ + if (!acpi_disabled) + err =3D acpi_mpam_parse_resources(msc, plat_data); + else + err =3D mpam_dt_parse_resources(msc, plat_data); + } + + if (!err && fw_num_msc =3D=3D mpam_num_msc) + mpam_discovery_complete(); + + if (err && msc) + mpam_msc_drv_remove(pdev); + + return err; +} + +static const struct of_device_id mpam_of_match[] =3D { + { .compatible =3D "arm,mpam-msc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpam_of_match); + +static struct platform_driver mpam_msc_driver =3D { + .driver =3D { + .name =3D "mpam_msc", + .of_match_table =3D of_match_ptr(mpam_of_match), + }, + .probe =3D mpam_msc_drv_probe, + .remove =3D mpam_msc_drv_remove, +}; + +/* + * MSC that are hidden under caches are not created as platform devices + * as there is no cache driver. Caches are also special-cased in + * update_msc_accessibility(). + */ +static void mpam_dt_create_foundling_msc(void) +{ + int err; + struct device_node *cache; + + for_each_compatible_node(cache, NULL, "cache") { + err =3D of_platform_populate(cache, mpam_of_match, NULL, NULL); + if (err) + pr_err("Failed to create MSC devices under caches\n"); + } +} + +static int __init mpam_msc_driver_init(void) +{ + if (!system_supports_mpam()) + return -EOPNOTSUPP; + + init_srcu_struct(&mpam_srcu); + + if (!acpi_disabled) + fw_num_msc =3D acpi_mpam_count_msc(); + else + fw_num_msc =3D mpam_dt_count_msc(); + + if (fw_num_msc <=3D 0) { + pr_err("No MSC devices found in firmware\n"); + return -EINVAL; + } + + if (acpi_disabled) + mpam_dt_create_foundling_msc(); + + return platform_driver_register(&mpam_msc_driver); +} +subsys_initcall(mpam_msc_driver_init); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h new file mode 100644 index 000000000000..07e0f240eaca --- /dev/null +++ b/drivers/resctrl/mpam_internal.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// Copyright (C) 2024 Arm Ltd. + +#ifndef MPAM_INTERNAL_H +#define MPAM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include + +struct mpam_msc { + /* member of mpam_all_msc */ + struct list_head glbl_list; + + int id; + struct platform_device *pdev; + + /* Not modified after mpam_is_enabled() becomes true */ + enum mpam_msc_iface iface; + u32 pcc_subspace_id; + struct mbox_client pcc_cl; + struct pcc_mbox_chan *pcc_chan; + u32 nrdy_usec; + cpumask_t accessibility; + + /* + * probe_lock is only take during discovery. After discovery these + * properties become read-only and the lists are protected by SRCU. + */ + struct mutex probe_lock; + unsigned long ris_idxs[128 / BITS_PER_LONG]; + u32 ris_max; + + /* mpam_msc_ris of this component */ + struct list_head ris; + + /* + * part_sel_lock protects access to the MSC hardware registers that are + * affected by MPAMCFG_PART_SEL. (including the ID registers that vary + * by RIS). + * If needed, take msc->lock first. + */ + struct mutex part_sel_lock; + + /* + * mon_sel_lock protects access to the MSC hardware registers that are + * affeted by MPAMCFG_MON_SEL. + * If needed, take msc->lock first. + */ + struct mutex outer_mon_sel_lock; + raw_spinlock_t inner_mon_sel_lock; + unsigned long inner_mon_sel_flags; + + void __iomem *mapped_hwpage; + size_t mapped_hwpage_sz; +}; + +#endif /* MPAM_INTERNAL_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A4AB828031C; Fri, 22 Aug 2025 15:35:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876922; cv=none; b=YahmP2Kbsgq0B3e33JKja41Tj9ETCHJMpgnH5Zh0XsABAkCINfxGOo+MX1hKBOEiBhNF8uVydySSDWExDlqxyvUL8BFRsLZVtOm5s7mdtPnFBXNDRaYFOYx280vwltBiNCn4sU/BtHoPHzJY27erLANV2WEhe82S5CZjFHcbEl8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876922; c=relaxed/simple; bh=s3Xj+lbMOvTmdXj7q5KRK49H5FJws7pRFoVYzoSjSUw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HtpD9mFSzHa/dg28uUqxXAlr1/MF3dT0lPaBw5tYKQLLUCSRDVn61fTlc/Xw0vIGvUsMO3XtBGEWs58Tr6wk/JAWUU2bnwQaHmI9LPSr4aLZUt1kVs+dpGd6EqpZkM5tfcKawrYxAzZnKg3hc5UL/kGU/KcYwdClN4vuG2ZRGsE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 02E582880; Fri, 22 Aug 2025 08:35:12 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C52F63F63F; Fri, 22 Aug 2025 08:35:14 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 11/33] arm_mpam: Add support for memory controller MSC on DT platforms Date: Fri, 22 Aug 2025 15:30:26 +0000 Message-Id: <20250822153048.2287-46-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Shanker Donthineni The device-tree binding has two examples for MSC associated with memory controllers. Add the support to discover the component_id from the device-tree and create 'memory' RIS. Signed-off-by: Shanker Donthineni [ morse: split out of a bigger patch, added affinity piece ] Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 67 ++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index a0d9a699a6e7..71a1fb1a9c75 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -62,41 +62,63 @@ static int mpam_dt_parse_resource(struct mpam_msc *msc,= struct device_node *np, u32 ris_idx) { int err =3D 0; - u32 level =3D 0; - unsigned long cache_id; - struct device_node *cache; + u32 class_id =3D 0, component_id =3D 0; + struct device_node *cache =3D NULL, *memory =3D NULL; + enum mpam_class_types type =3D MPAM_CLASS_UNKNOWN; =20 do { + /* What kind of MSC is this? */ if (of_device_is_compatible(np, "arm,mpam-cache")) { cache =3D of_parse_phandle(np, "arm,mpam-device", 0); if (!cache) { pr_err("Failed to read phandle\n"); break; } + type =3D MPAM_CLASS_CACHE; } else if (of_device_is_compatible(np->parent, "cache")) { cache =3D of_node_get(np->parent); + type =3D MPAM_CLASS_CACHE; + } else if (of_device_is_compatible(np, "arm,mpam-memory")) { + memory =3D of_parse_phandle(np, "arm,mpam-device", 0); + if (!memory) { + pr_err("Failed to read phandle\n"); + break; + } + type =3D MPAM_CLASS_MEMORY; + } else if (of_device_is_compatible(np, "arm,mpam-memory-controller-msc")= ) { + memory =3D of_node_get(np->parent); + type =3D MPAM_CLASS_MEMORY; } else { - /* For now, only caches are supported */ - cache =3D NULL; + /* + * For now, only caches and memory controllers are + * supported. + */ break; } =20 - err =3D of_property_read_u32(cache, "cache-level", &level); - if (err) { - pr_err("Failed to read cache-level\n"); - break; - } - - cache_id =3D cache_of_calculate_id(cache); - if (cache_id =3D=3D ~0UL) { - err =3D -ENOENT; - break; + /* Determine the class and component ids, based on type. */ + if (type =3D=3D MPAM_CLASS_CACHE) { + err =3D of_property_read_u32(cache, "cache-level", &class_id); + if (err) { + pr_err("Failed to read cache-level\n"); + break; + } + component_id =3D cache_of_calculate_id(cache); + if (component_id =3D=3D ~0UL) { + err =3D -ENOENT; + break; + } + } else if (type =3D=3D MPAM_CLASS_MEMORY) { + err =3D of_node_to_nid(np); + component_id =3D (err =3D=3D NUMA_NO_NODE) ? 0 : err; + class_id =3D 255; } =20 - err =3D mpam_ris_create(msc, ris_idx, MPAM_CLASS_CACHE, level, - cache_id); + err =3D mpam_ris_create(msc, ris_idx, type, class_id, + component_id); } while (0); of_node_put(cache); + of_node_put(memory); =20 return err; } @@ -157,9 +179,14 @@ static int update_msc_accessibility(struct mpam_msc *m= sc) cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { - err =3D -EINVAL; - pr_err("Cannot determine accessibility of MSC: %s\n", - dev_name(&msc->pdev->dev)); + if (of_device_is_compatible(parent, "memory")) { + cpumask_copy(&msc->accessibility, cpu_possible_mask); + err =3D 0; + } else { + err =3D -EINVAL; + pr_err("Cannot determine accessibility of MSC: %s\n", + dev_name(&msc->pdev->dev)); + } } of_node_put(parent); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D454123F26B; Fri, 22 Aug 2025 15:32:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876730; cv=none; b=X3sbimbeohwweUTbU0UbW19D7DgFlG6dPNVufIm5xQqxmQj5SpMzen8K49LHvD0ca93VcgrW9NUMYduKBt9HrNDlzNfm6RBKPzcV4fnIflKQUUgu7F+RloBTMRBvILjmjQaGrvz6Bels2f/QX/kggJ9C6pWANzQ6K6uyskn64iU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876730; c=relaxed/simple; bh=s3Xj+lbMOvTmdXj7q5KRK49H5FJws7pRFoVYzoSjSUw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nesp6Mn2tyE8t0u5bzRDKVzwNpQB9mpXQVqGwt4LxWrL/4xUB5R+He1gz49Gkb0H4jBlp28LWNQWOljJFIJqUAFLgtQHas6c0jvJXoKXsV0cfBWBRDQBYsIi3Y9TUIUxrNHe0Lyelt1YwuvQV9WMVUE3Ov+YTr+T9pdJ17rWtEs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 39C3E27B5; Fri, 22 Aug 2025 08:32:00 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2BD903F63F; Fri, 22 Aug 2025 08:32:03 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 11/33] arm_mpam: Add support for memory controller MSC on DT platforms Date: Fri, 22 Aug 2025 15:29:52 +0000 Message-Id: <20250822153048.2287-12-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Shanker Donthineni The device-tree binding has two examples for MSC associated with memory controllers. Add the support to discover the component_id from the device-tree and create 'memory' RIS. Signed-off-by: Shanker Donthineni [ morse: split out of a bigger patch, added affinity piece ] Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 67 ++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index a0d9a699a6e7..71a1fb1a9c75 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -62,41 +62,63 @@ static int mpam_dt_parse_resource(struct mpam_msc *msc,= struct device_node *np, u32 ris_idx) { int err =3D 0; - u32 level =3D 0; - unsigned long cache_id; - struct device_node *cache; + u32 class_id =3D 0, component_id =3D 0; + struct device_node *cache =3D NULL, *memory =3D NULL; + enum mpam_class_types type =3D MPAM_CLASS_UNKNOWN; =20 do { + /* What kind of MSC is this? */ if (of_device_is_compatible(np, "arm,mpam-cache")) { cache =3D of_parse_phandle(np, "arm,mpam-device", 0); if (!cache) { pr_err("Failed to read phandle\n"); break; } + type =3D MPAM_CLASS_CACHE; } else if (of_device_is_compatible(np->parent, "cache")) { cache =3D of_node_get(np->parent); + type =3D MPAM_CLASS_CACHE; + } else if (of_device_is_compatible(np, "arm,mpam-memory")) { + memory =3D of_parse_phandle(np, "arm,mpam-device", 0); + if (!memory) { + pr_err("Failed to read phandle\n"); + break; + } + type =3D MPAM_CLASS_MEMORY; + } else if (of_device_is_compatible(np, "arm,mpam-memory-controller-msc")= ) { + memory =3D of_node_get(np->parent); + type =3D MPAM_CLASS_MEMORY; } else { - /* For now, only caches are supported */ - cache =3D NULL; + /* + * For now, only caches and memory controllers are + * supported. + */ break; } =20 - err =3D of_property_read_u32(cache, "cache-level", &level); - if (err) { - pr_err("Failed to read cache-level\n"); - break; - } - - cache_id =3D cache_of_calculate_id(cache); - if (cache_id =3D=3D ~0UL) { - err =3D -ENOENT; - break; + /* Determine the class and component ids, based on type. */ + if (type =3D=3D MPAM_CLASS_CACHE) { + err =3D of_property_read_u32(cache, "cache-level", &class_id); + if (err) { + pr_err("Failed to read cache-level\n"); + break; + } + component_id =3D cache_of_calculate_id(cache); + if (component_id =3D=3D ~0UL) { + err =3D -ENOENT; + break; + } + } else if (type =3D=3D MPAM_CLASS_MEMORY) { + err =3D of_node_to_nid(np); + component_id =3D (err =3D=3D NUMA_NO_NODE) ? 0 : err; + class_id =3D 255; } =20 - err =3D mpam_ris_create(msc, ris_idx, MPAM_CLASS_CACHE, level, - cache_id); + err =3D mpam_ris_create(msc, ris_idx, type, class_id, + component_id); } while (0); of_node_put(cache); + of_node_put(memory); =20 return err; } @@ -157,9 +179,14 @@ static int update_msc_accessibility(struct mpam_msc *m= sc) cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { - err =3D -EINVAL; - pr_err("Cannot determine accessibility of MSC: %s\n", - dev_name(&msc->pdev->dev)); + if (of_device_is_compatible(parent, "memory")) { + cpumask_copy(&msc->accessibility, cpu_possible_mask); + err =3D 0; + } else { + err =3D -EINVAL; + pr_err("Cannot determine accessibility of MSC: %s\n", + dev_name(&msc->pdev->dev)); + } } of_node_put(parent); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 63446280A51; Fri, 22 Aug 2025 15:35:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876929; cv=none; b=NcNp9ZaC6pZUTD52Os3CrcFU4S2FM6QniwYhhqlPOuNtyJsLKhddtiI0nZI3Tsca0rOuZX/NV8mDKExfTHrk/wyG+waNVFxcSFokrMFYKTfuQGajpk4PCxrVW6OrMMyhsBkM5JPff7xS2jTxI5Vmej96DqV8kuX2mhn7pYU6YPU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876929; c=relaxed/simple; bh=rO1UJzDV98/vppIKoHqddqmx3hapljNfVW/hUFGiJY0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gGqvfHBkKitqY5W+YAyg2p/ZlEKPt658U1pwb1HHu3IgfL1XAoKRmjeDO6Zem+YMVcBt4xGwK4UfnLd7o+3NcgkJ7XlVkGKMvXesultIsk9z7aS3w4nm+4ADQohsNgEudBwpdiACG1YFzWMUzl+t8rQsmaAkbWdgLY2TqD+hGvs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B0B7715A1; Fri, 22 Aug 2025 08:35:17 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 861D53F63F; Fri, 22 Aug 2025 08:35:20 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Ben Horgan Subject: [PATCH 12/33] arm_mpam: Add the class and component structures for ris firmware described Date: Fri, 22 Aug 2025 15:30:27 +0000 Message-Id: <20250822153048.2287-47-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" An MSC is a container of resources, each identified by their RIS index. Some RIS are described by firmware to provide their position in the system. Others are discovered when the driver probes the hardware. To configure a resource it needs to be found by its class, e.g. 'L2'. There are two kinds of grouping, a class is a set of components, which are visible to user-space as there are likely to be multiple instances of the L2 cache. (e.g. one per cluster or package) struct mpam_components are a set of struct mpam_vmsc. A vMSC groups the RIS in an MSC that control the same logical piece of hardware. (e.g. L2). This is to allow hardware implementations where two controls are presented as different RIS. Re-combining these RIS allows their feature bits to be or-ed. This structure is not visible outside mpam_devices.c struct mpam_vmsc are then a set of struct mpam_msc_ris, which are not visible as each L2 cache may be composed of individual slices which need to be configured the same as the hardware is not able to distribute the configuration. Add support for creating and destroying these structures. A gfp is passed as the structures may need creating when a new RIS entry is discovered when probing the MSC. CC: Ben Horgan Signed-off-by: James Morse --- Changes since RFC: * removed a pr_err() debug message that crept in. --- drivers/resctrl/mpam_devices.c | 488 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 91 ++++++ include/linux/arm_mpam.h | 8 +- 3 files changed, 574 insertions(+), 13 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 71a1fb1a9c75..5baf2a8786fb 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -20,7 +20,6 @@ #include #include #include -#include #include =20 #include @@ -35,11 +34,483 @@ static DEFINE_MUTEX(mpam_list_lock); static LIST_HEAD(mpam_all_msc); =20 -static struct srcu_struct mpam_srcu; +struct srcu_struct mpam_srcu; =20 /* MPAM isn't available until all the MSC have been probed. */ static u32 mpam_num_msc; =20 +/* + * An MSC is a physical container for controls and monitors, each identifi= ed by + * their RIS index. These share a base-address, interrupts and some MMIO + * registers. A vMSC is a virtual container for RIS in an MSC that control= or + * monitor the same thing. Members of a vMSC are all RIS in the same MSC, = but + * not all RIS in an MSC share a vMSC. + * Components are a group of vMSC that control or monitor the same thing b= ut + * are from different MSC, so have different base-address, interrupts etc. + * Classes are the set components of the same type. + * + * The features of a vMSC is the union of the RIS it contains. + * The features of a Class and Component are the common subset of the vMSC + * they contain. + * + * e.g. The system cache may have bandwidth controls on multiple interface= s, + * for regulating traffic from devices independently of traffic from CPUs. + * If these are two RIS in one MSC, they will be treated as controlling + * different things, and will not share a vMSC/component/class. + * + * e.g. The L2 may have one MSC and two RIS, one for cache-controls another + * for bandwidth. These two RIS are members of the same vMSC. + * + * e.g. The set of RIS that make up the L2 are grouped as a component. The= se + * are sometimes termed slices. They should be configured the same, as if = there + * were only one. + * + * e.g. The SoC probably has more than one L2, each attached to a distinct= set + * of CPUs. All the L2 components are grouped as a class. + * + * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc = list, + * then linked via struct mpam_ris to a vmsc, component and class. + * The same MSC may exist under different class->component->vmsc paths, bu= t the + * RIS index will be unique. + */ +LIST_HEAD(mpam_classes); + +/* List of all objects that can be free()d after synchronise_srcu() */ +static LLIST_HEAD(mpam_garbage); + +#define init_garbage(x) init_llist_node(&(x)->garbage.llist) + +static struct mpam_vmsc * +mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc, gfp_t g= fp) +{ + struct mpam_vmsc *vmsc; + + lockdep_assert_held(&mpam_list_lock); + + vmsc =3D kzalloc(sizeof(*vmsc), gfp); + if (!comp) + return ERR_PTR(-ENOMEM); + init_garbage(vmsc); + + INIT_LIST_HEAD_RCU(&vmsc->ris); + INIT_LIST_HEAD_RCU(&vmsc->comp_list); + vmsc->comp =3D comp; + vmsc->msc =3D msc; + + list_add_rcu(&vmsc->comp_list, &comp->vmsc); + + return vmsc; +} + +static struct mpam_vmsc *mpam_vmsc_get(struct mpam_component *comp, + struct mpam_msc *msc, bool alloc, + gfp_t gfp) +{ + struct mpam_vmsc *vmsc; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + if (vmsc->msc->id =3D=3D msc->id) + return vmsc; + } + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_vmsc_alloc(comp, msc, gfp); +} + +static struct mpam_component * +mpam_component_alloc(struct mpam_class *class, int id, gfp_t gfp) +{ + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + comp =3D kzalloc(sizeof(*comp), gfp); + if (!comp) + return ERR_PTR(-ENOMEM); + init_garbage(comp); + + comp->comp_id =3D id; + INIT_LIST_HEAD_RCU(&comp->vmsc); + /* affinity is updated when ris are added */ + INIT_LIST_HEAD_RCU(&comp->class_list); + comp->class =3D class; + + list_add_rcu(&comp->class_list, &class->components); + + return comp; +} + +static struct mpam_component * +mpam_component_get(struct mpam_class *class, int id, bool alloc, gfp_t gfp) +{ + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(comp, &class->components, class_list) { + if (comp->comp_id =3D=3D id) + return comp; + } + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_component_alloc(class, id, gfp); +} + +static struct mpam_class * +mpam_class_alloc(u8 level_idx, enum mpam_class_types type, gfp_t gfp) +{ + struct mpam_class *class; + + lockdep_assert_held(&mpam_list_lock); + + class =3D kzalloc(sizeof(*class), gfp); + if (!class) + return ERR_PTR(-ENOMEM); + init_garbage(class); + + INIT_LIST_HEAD_RCU(&class->components); + /* affinity is updated when ris are added */ + class->level =3D level_idx; + class->type =3D type; + INIT_LIST_HEAD_RCU(&class->classes_list); + + list_add_rcu(&class->classes_list, &mpam_classes); + + return class; +} + +static struct mpam_class * +mpam_class_get(u8 level_idx, enum mpam_class_types type, bool alloc, gfp_t= gfp) +{ + bool found =3D false; + struct mpam_class *class; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, &mpam_classes, classes_list) { + if (class->type =3D=3D type && class->level =3D=3D level_idx) { + found =3D true; + break; + } + } + + if (found) + return class; + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_class_alloc(level_idx, type, gfp); +} + +#define add_to_garbage(x) \ +do { \ + __typeof__(x) _x =3D x; \ + (_x)->garbage.to_free =3D (_x); \ + llist_add(&(_x)->garbage.llist, &mpam_garbage); \ +} while (0) + +static void mpam_class_destroy(struct mpam_class *class) +{ + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&class->classes_list); + add_to_garbage(class); +} + +static void mpam_comp_destroy(struct mpam_component *comp) +{ + struct mpam_class *class =3D comp->class; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&comp->class_list); + add_to_garbage(comp); + + if (list_empty(&class->components)) + mpam_class_destroy(class); +} + +static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) +{ + struct mpam_component *comp =3D vmsc->comp; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&vmsc->comp_list); + add_to_garbage(vmsc); + + if (list_empty(&comp->vmsc)) + mpam_comp_destroy(comp); +} + +static void mpam_ris_destroy(struct mpam_msc_ris *ris) +{ + struct mpam_vmsc *vmsc =3D ris->vmsc; + struct mpam_msc *msc =3D vmsc->msc; + struct platform_device *pdev =3D msc->pdev; + struct mpam_component *comp =3D vmsc->comp; + struct mpam_class *class =3D comp->class; + + lockdep_assert_held(&mpam_list_lock); + + cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); + cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); + clear_bit(ris->ris_idx, msc->ris_idxs); + list_del_rcu(&ris->vmsc_list); + list_del_rcu(&ris->msc_list); + add_to_garbage(ris); + ris->garbage.pdev =3D pdev; + + if (list_empty(&vmsc->ris)) + mpam_vmsc_destroy(vmsc); +} + +/* + * There are two ways of reaching a struct mpam_msc_ris. Via the + * class->component->vmsc->ris, or via the msc. + * When destroying the msc, the other side needs unlinking and cleaning up= too. + */ +static void mpam_msc_destroy(struct mpam_msc *msc) +{ + struct platform_device *pdev =3D msc->pdev; + struct mpam_msc_ris *ris, *tmp; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&msc->glbl_list); + platform_set_drvdata(pdev, NULL); + + list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) + mpam_ris_destroy(ris); + + add_to_garbage(msc); + msc->garbage.pdev =3D pdev; +} + +static void mpam_free_garbage(void) +{ + struct mpam_garbage *iter, *tmp; + struct llist_node *to_free =3D llist_del_all(&mpam_garbage); + + if (!to_free) + return; + + synchronize_srcu(&mpam_srcu); + + llist_for_each_entry_safe(iter, tmp, to_free, llist) { + if (iter->pdev) + devm_kfree(&iter->pdev->dev, iter->to_free); + else + kfree(iter->to_free); + } +} + +/* Called recursively to walk the list of caches from a particular CPU */ +static void __mpam_get_cpumask_from_cache_id(int cpu, struct device_node *= cache_node, + unsigned long cache_id, + u32 cache_level, + cpumask_t *affinity) +{ + int err; + u32 iter_level; + unsigned long iter_cache_id; + struct device_node *iter_node __free(device_node) =3D of_find_next_cache_= node(cache_node); + + if (!iter_node) + return; + + err =3D of_property_read_u32(iter_node, "cache-level", &iter_level); + if (err) + return; + + /* + * get_cpu_cacheinfo_id() isn't ready until sometime + * during device_initcall(). Use cache_of_calculate_id(). + */ + iter_cache_id =3D cache_of_calculate_id(iter_node); + if (cache_id =3D=3D ~0UL) + return; + + if (iter_level =3D=3D cache_level && iter_cache_id =3D=3D cache_id) + cpumask_set_cpu(cpu, affinity); + + __mpam_get_cpumask_from_cache_id(cpu, iter_node, cache_id, cache_level, + affinity); +} + +/* + * The cacheinfo structures are only populated when CPUs are online. + * This helper walks the device tree to include offline CPUs too. + */ +int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, + cpumask_t *affinity) +{ + int cpu; + + if (!acpi_disabled) + return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); + + for_each_possible_cpu(cpu) { + struct device_node *cpu_node __free(device_node) =3D of_get_cpu_node(cpu= , NULL); + if (!cpu_node) { + pr_err("Failed to find cpu%d device node\n", cpu); + return -ENOENT; + } + + __mpam_get_cpumask_from_cache_id(cpu, cpu_node, cache_id, + cache_level, affinity); + continue; + } + + return 0; +} + +/* + * cpumask_of_node() only knows about online CPUs. This can't tell us whet= her + * a class is represented on all possible CPUs. + */ +static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (node_id =3D=3D cpu_to_node(cpu)) + cpumask_set_cpu(cpu, affinity); + } +} + +static int get_cpumask_from_cache(struct device_node *cache, + cpumask_t *affinity) +{ + int err; + u32 cache_level; + unsigned long cache_id; + + err =3D of_property_read_u32(cache, "cache-level", &cache_level); + if (err) { + pr_err("Failed to read cache-level from cache node\n"); + return -ENOENT; + } + + cache_id =3D cache_of_calculate_id(cache); + if (cache_id =3D=3D ~0UL) { + pr_err("Failed to calculate cache-id from cache node\n"); + return -ENOENT; + } + + return mpam_get_cpumask_from_cache_id(cache_id, cache_level, affinity); +} + +static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, + enum mpam_class_types type, + struct mpam_class *class, + struct mpam_component *comp) +{ + int err; + + switch (type) { + case MPAM_CLASS_CACHE: + err =3D mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, + affinity); + if (err) + return err; + + if (cpumask_empty(affinity)) + pr_warn_once("%s no CPUs associated with cache node", + dev_name(&msc->pdev->dev)); + + break; + case MPAM_CLASS_MEMORY: + get_cpumask_from_node_id(comp->comp_id, affinity); + /* affinity may be empty for CPU-less memory nodes */ + break; + case MPAM_CLASS_UNKNOWN: + return 0; + } + + cpumask_and(affinity, affinity, &msc->accessibility); + + return 0; +} + +static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, + int component_id, gfp_t gfp) +{ + int err; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + if (test_and_set_bit(ris_idx, msc->ris_idxs)) + return -EBUSY; + + ris =3D devm_kzalloc(&msc->pdev->dev, sizeof(*ris), gfp); + if (!ris) + return -ENOMEM; + init_garbage(ris); + + class =3D mpam_class_get(class_id, type, true, gfp); + if (IS_ERR(class)) + return PTR_ERR(class); + + comp =3D mpam_component_get(class, component_id, true, gfp); + if (IS_ERR(comp)) { + if (list_empty(&class->components)) + mpam_class_destroy(class); + return PTR_ERR(comp); + } + + vmsc =3D mpam_vmsc_get(comp, msc, true, gfp); + if (IS_ERR(vmsc)) { + if (list_empty(&comp->vmsc)) + mpam_comp_destroy(comp); + return PTR_ERR(vmsc); + } + + err =3D mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); + if (err) { + if (list_empty(&vmsc->ris)) + mpam_vmsc_destroy(vmsc); + return err; + } + + ris->ris_idx =3D ris_idx; + INIT_LIST_HEAD_RCU(&ris->vmsc_list); + ris->vmsc =3D vmsc; + + cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); + cpumask_or(&class->affinity, &class->affinity, &ris->affinity); + list_add_rcu(&ris->vmsc_list, &vmsc->ris); + + return 0; +} + +int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, int component_id) +{ + int err; + + mutex_lock(&mpam_list_lock); + err =3D mpam_ris_create_locked(msc, ris_idx, type, class_id, + component_id, GFP_KERNEL); + mutex_unlock(&mpam_list_lock); + if (err) + mpam_free_garbage(); + + return err; +} + static void mpam_discovery_complete(void) { pr_err("Discovered all MSC\n"); @@ -179,7 +650,10 @@ static int update_msc_accessibility(struct mpam_msc *m= sc) cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { - if (of_device_is_compatible(parent, "memory")) { + if (of_device_is_compatible(parent, "cache")) { + err =3D get_cpumask_from_cache(parent, + &msc->accessibility); + } else if (of_device_is_compatible(parent, "memory")) { cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { @@ -209,11 +683,10 @@ static void mpam_msc_drv_remove(struct platform_devic= e *pdev) =20 mutex_lock(&mpam_list_lock); mpam_num_msc--; - platform_set_drvdata(pdev, NULL); - list_del_rcu(&msc->glbl_list); - synchronize_srcu(&mpam_srcu); - devm_kfree(&pdev->dev, msc); + mpam_msc_destroy(msc); mutex_unlock(&mpam_list_lock); + + mpam_free_garbage(); } =20 static int mpam_msc_drv_probe(struct platform_device *pdev) @@ -230,6 +703,7 @@ static int mpam_msc_drv_probe(struct platform_device *p= dev) err =3D -ENOMEM; break; } + init_garbage(msc); =20 mutex_init(&msc->probe_lock); mutex_init(&msc->part_sel_lock); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 07e0f240eaca..d49bb884b433 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -7,10 +7,27 @@ #include #include #include +#include #include #include #include #include +#include + +/* + * Structures protected by SRCU may not be freed for a surprising amount of + * time (especially if perf is running). To ensure the MPAM error interrup= t can + * tear down all the structures, build a list of objects that can be gargb= age + * collected once synchronize_srcu() has returned. + * If pdev is non-NULL, use devm_kfree(). + */ +struct mpam_garbage { + /* member of mpam_garbage */ + struct llist_node llist; + + void *to_free; + struct platform_device *pdev; +}; =20 struct mpam_msc { /* member of mpam_all_msc */ @@ -57,6 +74,80 @@ struct mpam_msc { =20 void __iomem *mapped_hwpage; size_t mapped_hwpage_sz; + + struct mpam_garbage garbage; +}; + +struct mpam_class { + /* mpam_components in this class */ + struct list_head components; + + cpumask_t affinity; + + u8 level; + enum mpam_class_types type; + + /* member of mpam_classes */ + struct list_head classes_list; + + struct mpam_garbage garbage; +}; + +struct mpam_component { + u32 comp_id; + + /* mpam_vmsc in this component */ + struct list_head vmsc; + + cpumask_t affinity; + + /* member of mpam_class:components */ + struct list_head class_list; + + /* parent: */ + struct mpam_class *class; + + struct mpam_garbage garbage; }; =20 +struct mpam_vmsc { + /* member of mpam_component:vmsc_list */ + struct list_head comp_list; + + /* mpam_msc_ris in this vmsc */ + struct list_head ris; + + /* All RIS in this vMSC are members of this MSC */ + struct mpam_msc *msc; + + /* parent: */ + struct mpam_component *comp; + + struct mpam_garbage garbage; +}; + +struct mpam_msc_ris { + u8 ris_idx; + + cpumask_t affinity; + + /* member of mpam_vmsc:ris */ + struct list_head vmsc_list; + + /* member of mpam_msc:ris */ + struct list_head msc_list; + + /* parent: */ + struct mpam_vmsc *vmsc; + + struct mpam_garbage garbage; +}; + +/* List of all classes - protected by srcu*/ +extern struct srcu_struct mpam_srcu; +extern struct list_head mpam_classes; + +int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, + cpumask_t *affinity); + #endif /* MPAM_INTERNAL_H */ diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h index 0edefa6ba019..406a77be68cb 100644 --- a/include/linux/arm_mpam.h +++ b/include/linux/arm_mpam.h @@ -36,11 +36,7 @@ static inline int acpi_mpam_parse_resources(struct mpam_= msc *msc, static inline int acpi_mpam_count_msc(void) { return -EINVAL; } #endif =20 -static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, - enum mpam_class_types type, u8 class_id, - int component_id) -{ - return -EINVAL; -} +int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, int component_id); =20 #endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C614823F26B; Fri, 22 Aug 2025 15:32:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876736; cv=none; b=RCpX6s+vh7hSJZyxrPbc2yWU8us+isHE0d6CHeX5mccGPjLYw29xwArTcA9dDEELEIweg0+fxnHIwwlPDzRE2a1mCTc28D1lUx+9NrbyTZ3seh28HDdVlRpIAldY/Zj9cmzmKkQhT8UDGz1EDdf6D1S9rBPzsP8e8uWUpDV0Ue4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876736; c=relaxed/simple; bh=rO1UJzDV98/vppIKoHqddqmx3hapljNfVW/hUFGiJY0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Y/b/eeDja3LsLQ6vfR/67/clyR2XlB0+CnnYbZLWCfTODHxoXQEwwiG6yx8yv7+y6ztqvlgjKSn9SzzBol4wWDkh5tAk573FC4ZRM6MbO3dsvvoDY2jJ0A+CQVs5jY/9PF44VaLoZeo45ZnMkgFt8D+kiBSeCiNzrbDnhV2AoSo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id EBDEB15A1; Fri, 22 Aug 2025 08:32:05 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id BCA903F63F; Fri, 22 Aug 2025 08:32:08 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Ben Horgan Subject: [PATCH 12/33] arm_mpam: Add the class and component structures for ris firmware described Date: Fri, 22 Aug 2025 15:29:53 +0000 Message-Id: <20250822153048.2287-13-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" An MSC is a container of resources, each identified by their RIS index. Some RIS are described by firmware to provide their position in the system. Others are discovered when the driver probes the hardware. To configure a resource it needs to be found by its class, e.g. 'L2'. There are two kinds of grouping, a class is a set of components, which are visible to user-space as there are likely to be multiple instances of the L2 cache. (e.g. one per cluster or package) struct mpam_components are a set of struct mpam_vmsc. A vMSC groups the RIS in an MSC that control the same logical piece of hardware. (e.g. L2). This is to allow hardware implementations where two controls are presented as different RIS. Re-combining these RIS allows their feature bits to be or-ed. This structure is not visible outside mpam_devices.c struct mpam_vmsc are then a set of struct mpam_msc_ris, which are not visible as each L2 cache may be composed of individual slices which need to be configured the same as the hardware is not able to distribute the configuration. Add support for creating and destroying these structures. A gfp is passed as the structures may need creating when a new RIS entry is discovered when probing the MSC. CC: Ben Horgan Signed-off-by: James Morse --- Changes since RFC: * removed a pr_err() debug message that crept in. --- drivers/resctrl/mpam_devices.c | 488 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 91 ++++++ include/linux/arm_mpam.h | 8 +- 3 files changed, 574 insertions(+), 13 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 71a1fb1a9c75..5baf2a8786fb 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -20,7 +20,6 @@ #include #include #include -#include #include =20 #include @@ -35,11 +34,483 @@ static DEFINE_MUTEX(mpam_list_lock); static LIST_HEAD(mpam_all_msc); =20 -static struct srcu_struct mpam_srcu; +struct srcu_struct mpam_srcu; =20 /* MPAM isn't available until all the MSC have been probed. */ static u32 mpam_num_msc; =20 +/* + * An MSC is a physical container for controls and monitors, each identifi= ed by + * their RIS index. These share a base-address, interrupts and some MMIO + * registers. A vMSC is a virtual container for RIS in an MSC that control= or + * monitor the same thing. Members of a vMSC are all RIS in the same MSC, = but + * not all RIS in an MSC share a vMSC. + * Components are a group of vMSC that control or monitor the same thing b= ut + * are from different MSC, so have different base-address, interrupts etc. + * Classes are the set components of the same type. + * + * The features of a vMSC is the union of the RIS it contains. + * The features of a Class and Component are the common subset of the vMSC + * they contain. + * + * e.g. The system cache may have bandwidth controls on multiple interface= s, + * for regulating traffic from devices independently of traffic from CPUs. + * If these are two RIS in one MSC, they will be treated as controlling + * different things, and will not share a vMSC/component/class. + * + * e.g. The L2 may have one MSC and two RIS, one for cache-controls another + * for bandwidth. These two RIS are members of the same vMSC. + * + * e.g. The set of RIS that make up the L2 are grouped as a component. The= se + * are sometimes termed slices. They should be configured the same, as if = there + * were only one. + * + * e.g. The SoC probably has more than one L2, each attached to a distinct= set + * of CPUs. All the L2 components are grouped as a class. + * + * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc = list, + * then linked via struct mpam_ris to a vmsc, component and class. + * The same MSC may exist under different class->component->vmsc paths, bu= t the + * RIS index will be unique. + */ +LIST_HEAD(mpam_classes); + +/* List of all objects that can be free()d after synchronise_srcu() */ +static LLIST_HEAD(mpam_garbage); + +#define init_garbage(x) init_llist_node(&(x)->garbage.llist) + +static struct mpam_vmsc * +mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc, gfp_t g= fp) +{ + struct mpam_vmsc *vmsc; + + lockdep_assert_held(&mpam_list_lock); + + vmsc =3D kzalloc(sizeof(*vmsc), gfp); + if (!comp) + return ERR_PTR(-ENOMEM); + init_garbage(vmsc); + + INIT_LIST_HEAD_RCU(&vmsc->ris); + INIT_LIST_HEAD_RCU(&vmsc->comp_list); + vmsc->comp =3D comp; + vmsc->msc =3D msc; + + list_add_rcu(&vmsc->comp_list, &comp->vmsc); + + return vmsc; +} + +static struct mpam_vmsc *mpam_vmsc_get(struct mpam_component *comp, + struct mpam_msc *msc, bool alloc, + gfp_t gfp) +{ + struct mpam_vmsc *vmsc; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + if (vmsc->msc->id =3D=3D msc->id) + return vmsc; + } + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_vmsc_alloc(comp, msc, gfp); +} + +static struct mpam_component * +mpam_component_alloc(struct mpam_class *class, int id, gfp_t gfp) +{ + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + comp =3D kzalloc(sizeof(*comp), gfp); + if (!comp) + return ERR_PTR(-ENOMEM); + init_garbage(comp); + + comp->comp_id =3D id; + INIT_LIST_HEAD_RCU(&comp->vmsc); + /* affinity is updated when ris are added */ + INIT_LIST_HEAD_RCU(&comp->class_list); + comp->class =3D class; + + list_add_rcu(&comp->class_list, &class->components); + + return comp; +} + +static struct mpam_component * +mpam_component_get(struct mpam_class *class, int id, bool alloc, gfp_t gfp) +{ + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(comp, &class->components, class_list) { + if (comp->comp_id =3D=3D id) + return comp; + } + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_component_alloc(class, id, gfp); +} + +static struct mpam_class * +mpam_class_alloc(u8 level_idx, enum mpam_class_types type, gfp_t gfp) +{ + struct mpam_class *class; + + lockdep_assert_held(&mpam_list_lock); + + class =3D kzalloc(sizeof(*class), gfp); + if (!class) + return ERR_PTR(-ENOMEM); + init_garbage(class); + + INIT_LIST_HEAD_RCU(&class->components); + /* affinity is updated when ris are added */ + class->level =3D level_idx; + class->type =3D type; + INIT_LIST_HEAD_RCU(&class->classes_list); + + list_add_rcu(&class->classes_list, &mpam_classes); + + return class; +} + +static struct mpam_class * +mpam_class_get(u8 level_idx, enum mpam_class_types type, bool alloc, gfp_t= gfp) +{ + bool found =3D false; + struct mpam_class *class; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, &mpam_classes, classes_list) { + if (class->type =3D=3D type && class->level =3D=3D level_idx) { + found =3D true; + break; + } + } + + if (found) + return class; + + if (!alloc) + return ERR_PTR(-ENOENT); + + return mpam_class_alloc(level_idx, type, gfp); +} + +#define add_to_garbage(x) \ +do { \ + __typeof__(x) _x =3D x; \ + (_x)->garbage.to_free =3D (_x); \ + llist_add(&(_x)->garbage.llist, &mpam_garbage); \ +} while (0) + +static void mpam_class_destroy(struct mpam_class *class) +{ + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&class->classes_list); + add_to_garbage(class); +} + +static void mpam_comp_destroy(struct mpam_component *comp) +{ + struct mpam_class *class =3D comp->class; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&comp->class_list); + add_to_garbage(comp); + + if (list_empty(&class->components)) + mpam_class_destroy(class); +} + +static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) +{ + struct mpam_component *comp =3D vmsc->comp; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&vmsc->comp_list); + add_to_garbage(vmsc); + + if (list_empty(&comp->vmsc)) + mpam_comp_destroy(comp); +} + +static void mpam_ris_destroy(struct mpam_msc_ris *ris) +{ + struct mpam_vmsc *vmsc =3D ris->vmsc; + struct mpam_msc *msc =3D vmsc->msc; + struct platform_device *pdev =3D msc->pdev; + struct mpam_component *comp =3D vmsc->comp; + struct mpam_class *class =3D comp->class; + + lockdep_assert_held(&mpam_list_lock); + + cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); + cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); + clear_bit(ris->ris_idx, msc->ris_idxs); + list_del_rcu(&ris->vmsc_list); + list_del_rcu(&ris->msc_list); + add_to_garbage(ris); + ris->garbage.pdev =3D pdev; + + if (list_empty(&vmsc->ris)) + mpam_vmsc_destroy(vmsc); +} + +/* + * There are two ways of reaching a struct mpam_msc_ris. Via the + * class->component->vmsc->ris, or via the msc. + * When destroying the msc, the other side needs unlinking and cleaning up= too. + */ +static void mpam_msc_destroy(struct mpam_msc *msc) +{ + struct platform_device *pdev =3D msc->pdev; + struct mpam_msc_ris *ris, *tmp; + + lockdep_assert_held(&mpam_list_lock); + + list_del_rcu(&msc->glbl_list); + platform_set_drvdata(pdev, NULL); + + list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) + mpam_ris_destroy(ris); + + add_to_garbage(msc); + msc->garbage.pdev =3D pdev; +} + +static void mpam_free_garbage(void) +{ + struct mpam_garbage *iter, *tmp; + struct llist_node *to_free =3D llist_del_all(&mpam_garbage); + + if (!to_free) + return; + + synchronize_srcu(&mpam_srcu); + + llist_for_each_entry_safe(iter, tmp, to_free, llist) { + if (iter->pdev) + devm_kfree(&iter->pdev->dev, iter->to_free); + else + kfree(iter->to_free); + } +} + +/* Called recursively to walk the list of caches from a particular CPU */ +static void __mpam_get_cpumask_from_cache_id(int cpu, struct device_node *= cache_node, + unsigned long cache_id, + u32 cache_level, + cpumask_t *affinity) +{ + int err; + u32 iter_level; + unsigned long iter_cache_id; + struct device_node *iter_node __free(device_node) =3D of_find_next_cache_= node(cache_node); + + if (!iter_node) + return; + + err =3D of_property_read_u32(iter_node, "cache-level", &iter_level); + if (err) + return; + + /* + * get_cpu_cacheinfo_id() isn't ready until sometime + * during device_initcall(). Use cache_of_calculate_id(). + */ + iter_cache_id =3D cache_of_calculate_id(iter_node); + if (cache_id =3D=3D ~0UL) + return; + + if (iter_level =3D=3D cache_level && iter_cache_id =3D=3D cache_id) + cpumask_set_cpu(cpu, affinity); + + __mpam_get_cpumask_from_cache_id(cpu, iter_node, cache_id, cache_level, + affinity); +} + +/* + * The cacheinfo structures are only populated when CPUs are online. + * This helper walks the device tree to include offline CPUs too. + */ +int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, + cpumask_t *affinity) +{ + int cpu; + + if (!acpi_disabled) + return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); + + for_each_possible_cpu(cpu) { + struct device_node *cpu_node __free(device_node) =3D of_get_cpu_node(cpu= , NULL); + if (!cpu_node) { + pr_err("Failed to find cpu%d device node\n", cpu); + return -ENOENT; + } + + __mpam_get_cpumask_from_cache_id(cpu, cpu_node, cache_id, + cache_level, affinity); + continue; + } + + return 0; +} + +/* + * cpumask_of_node() only knows about online CPUs. This can't tell us whet= her + * a class is represented on all possible CPUs. + */ +static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (node_id =3D=3D cpu_to_node(cpu)) + cpumask_set_cpu(cpu, affinity); + } +} + +static int get_cpumask_from_cache(struct device_node *cache, + cpumask_t *affinity) +{ + int err; + u32 cache_level; + unsigned long cache_id; + + err =3D of_property_read_u32(cache, "cache-level", &cache_level); + if (err) { + pr_err("Failed to read cache-level from cache node\n"); + return -ENOENT; + } + + cache_id =3D cache_of_calculate_id(cache); + if (cache_id =3D=3D ~0UL) { + pr_err("Failed to calculate cache-id from cache node\n"); + return -ENOENT; + } + + return mpam_get_cpumask_from_cache_id(cache_id, cache_level, affinity); +} + +static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, + enum mpam_class_types type, + struct mpam_class *class, + struct mpam_component *comp) +{ + int err; + + switch (type) { + case MPAM_CLASS_CACHE: + err =3D mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, + affinity); + if (err) + return err; + + if (cpumask_empty(affinity)) + pr_warn_once("%s no CPUs associated with cache node", + dev_name(&msc->pdev->dev)); + + break; + case MPAM_CLASS_MEMORY: + get_cpumask_from_node_id(comp->comp_id, affinity); + /* affinity may be empty for CPU-less memory nodes */ + break; + case MPAM_CLASS_UNKNOWN: + return 0; + } + + cpumask_and(affinity, affinity, &msc->accessibility); + + return 0; +} + +static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, + int component_id, gfp_t gfp) +{ + int err; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + if (test_and_set_bit(ris_idx, msc->ris_idxs)) + return -EBUSY; + + ris =3D devm_kzalloc(&msc->pdev->dev, sizeof(*ris), gfp); + if (!ris) + return -ENOMEM; + init_garbage(ris); + + class =3D mpam_class_get(class_id, type, true, gfp); + if (IS_ERR(class)) + return PTR_ERR(class); + + comp =3D mpam_component_get(class, component_id, true, gfp); + if (IS_ERR(comp)) { + if (list_empty(&class->components)) + mpam_class_destroy(class); + return PTR_ERR(comp); + } + + vmsc =3D mpam_vmsc_get(comp, msc, true, gfp); + if (IS_ERR(vmsc)) { + if (list_empty(&comp->vmsc)) + mpam_comp_destroy(comp); + return PTR_ERR(vmsc); + } + + err =3D mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); + if (err) { + if (list_empty(&vmsc->ris)) + mpam_vmsc_destroy(vmsc); + return err; + } + + ris->ris_idx =3D ris_idx; + INIT_LIST_HEAD_RCU(&ris->vmsc_list); + ris->vmsc =3D vmsc; + + cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); + cpumask_or(&class->affinity, &class->affinity, &ris->affinity); + list_add_rcu(&ris->vmsc_list, &vmsc->ris); + + return 0; +} + +int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, int component_id) +{ + int err; + + mutex_lock(&mpam_list_lock); + err =3D mpam_ris_create_locked(msc, ris_idx, type, class_id, + component_id, GFP_KERNEL); + mutex_unlock(&mpam_list_lock); + if (err) + mpam_free_garbage(); + + return err; +} + static void mpam_discovery_complete(void) { pr_err("Discovered all MSC\n"); @@ -179,7 +650,10 @@ static int update_msc_accessibility(struct mpam_msc *m= sc) cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { - if (of_device_is_compatible(parent, "memory")) { + if (of_device_is_compatible(parent, "cache")) { + err =3D get_cpumask_from_cache(parent, + &msc->accessibility); + } else if (of_device_is_compatible(parent, "memory")) { cpumask_copy(&msc->accessibility, cpu_possible_mask); err =3D 0; } else { @@ -209,11 +683,10 @@ static void mpam_msc_drv_remove(struct platform_devic= e *pdev) =20 mutex_lock(&mpam_list_lock); mpam_num_msc--; - platform_set_drvdata(pdev, NULL); - list_del_rcu(&msc->glbl_list); - synchronize_srcu(&mpam_srcu); - devm_kfree(&pdev->dev, msc); + mpam_msc_destroy(msc); mutex_unlock(&mpam_list_lock); + + mpam_free_garbage(); } =20 static int mpam_msc_drv_probe(struct platform_device *pdev) @@ -230,6 +703,7 @@ static int mpam_msc_drv_probe(struct platform_device *p= dev) err =3D -ENOMEM; break; } + init_garbage(msc); =20 mutex_init(&msc->probe_lock); mutex_init(&msc->part_sel_lock); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 07e0f240eaca..d49bb884b433 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -7,10 +7,27 @@ #include #include #include +#include #include #include #include #include +#include + +/* + * Structures protected by SRCU may not be freed for a surprising amount of + * time (especially if perf is running). To ensure the MPAM error interrup= t can + * tear down all the structures, build a list of objects that can be gargb= age + * collected once synchronize_srcu() has returned. + * If pdev is non-NULL, use devm_kfree(). + */ +struct mpam_garbage { + /* member of mpam_garbage */ + struct llist_node llist; + + void *to_free; + struct platform_device *pdev; +}; =20 struct mpam_msc { /* member of mpam_all_msc */ @@ -57,6 +74,80 @@ struct mpam_msc { =20 void __iomem *mapped_hwpage; size_t mapped_hwpage_sz; + + struct mpam_garbage garbage; +}; + +struct mpam_class { + /* mpam_components in this class */ + struct list_head components; + + cpumask_t affinity; + + u8 level; + enum mpam_class_types type; + + /* member of mpam_classes */ + struct list_head classes_list; + + struct mpam_garbage garbage; +}; + +struct mpam_component { + u32 comp_id; + + /* mpam_vmsc in this component */ + struct list_head vmsc; + + cpumask_t affinity; + + /* member of mpam_class:components */ + struct list_head class_list; + + /* parent: */ + struct mpam_class *class; + + struct mpam_garbage garbage; }; =20 +struct mpam_vmsc { + /* member of mpam_component:vmsc_list */ + struct list_head comp_list; + + /* mpam_msc_ris in this vmsc */ + struct list_head ris; + + /* All RIS in this vMSC are members of this MSC */ + struct mpam_msc *msc; + + /* parent: */ + struct mpam_component *comp; + + struct mpam_garbage garbage; +}; + +struct mpam_msc_ris { + u8 ris_idx; + + cpumask_t affinity; + + /* member of mpam_vmsc:ris */ + struct list_head vmsc_list; + + /* member of mpam_msc:ris */ + struct list_head msc_list; + + /* parent: */ + struct mpam_vmsc *vmsc; + + struct mpam_garbage garbage; +}; + +/* List of all classes - protected by srcu*/ +extern struct srcu_struct mpam_srcu; +extern struct list_head mpam_classes; + +int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, + cpumask_t *affinity); + #endif /* MPAM_INTERNAL_H */ diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h index 0edefa6ba019..406a77be68cb 100644 --- a/include/linux/arm_mpam.h +++ b/include/linux/arm_mpam.h @@ -36,11 +36,7 @@ static inline int acpi_mpam_parse_resources(struct mpam_= msc *msc, static inline int acpi_mpam_count_msc(void) { return -EINVAL; } #endif =20 -static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, - enum mpam_class_types type, u8 class_id, - int component_id) -{ - return -EINVAL; -} +int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, + enum mpam_class_types type, u8 class_id, int component_id); =20 #endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F22BA28134C; Fri, 22 Aug 2025 15:35:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876933; cv=none; b=BaD/5aSgvO0/ZKALeT+7rMy/sQQg50ITu1aG9Sl5Vzg1lxp252RV9V89Se76OZp0Z3UBBLTophGVHAtiUbivu0K13BNw4Te5XuNon0HFt39dwVhUgbo8WSCgXYznYndp+Ig2RsWHfRh2Ti6Rfav4MorQ/qcFU+LyWHRlsCNXnzA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876933; c=relaxed/simple; bh=CnCRiR9Hhx0tPwGqihebUQ/GVFmFcbIVpe+sldLi6Ro=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZNsx85iLx94lqAnqD76QXMuT3QobFa3lz74JKS1Sf9gv0YLhWd+1FzkO5uEOSD8g8a8nIPtrpdwldFyXGCc3Pyg5Lr6A3TtcPWWzSzYfKjyHAbWvbkFZ5VynG7sm0kq1X9ud+r4Rxo2dvxBd97WHBPM91EvgMBfnQhzS8Pqr14Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 5A5A615A1; Fri, 22 Aug 2025 08:35:23 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 420033F63F; Fri, 22 Aug 2025 08:35:26 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 13/33] arm_mpam: Add MPAM MSC register layout definitions Date: Fri, 22 Aug 2025 15:30:28 +0000 Message-Id: <20250822153048.2287-48-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Memory Partitioning and Monitoring (MPAM) has memory mapped devices (MSCs) with an identity/configuration page. Add the definitions for these registers as offset within the page(s). Link: https://developer.arm.com/documentation/ihi0099/latest/ Signed-off-by: James Morse --- Changes since RFC: * Renamed MSMON_CFG_MBWU_CTL_TYPE_CSU as MSMON_CFG_CSU_CTL_TYPE_CSU * Whitepsace churn. * Cite a more recent document. * Removed some stale feature, fixed some names etc. --- drivers/resctrl/mpam_internal.h | 266 ++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index d49bb884b433..6e0982a1a9ac 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -150,4 +150,270 @@ extern struct list_head mpam_classes; int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 +/* + * MPAM MSCs have the following register layout. See: + * Arm Memory System Resource Partitioning and Monitoring (MPAM) System + * Component Specification. + * https://developer.arm.com/documentation/ihi0099/latest/ + */ +#define MPAM_ARCHITECTURE_V1 0x10 + +/* Memory mapped control pages: */ +/* ID Register offsets in the memory mapped page */ +#define MPAMF_IDR 0x0000 /* features id register */ +#define MPAMF_MSMON_IDR 0x0080 /* performance monitoring features */ +#define MPAMF_IMPL_IDR 0x0028 /* imp-def partitioning */ +#define MPAMF_CPOR_IDR 0x0030 /* cache-portion partitioning */ +#define MPAMF_CCAP_IDR 0x0038 /* cache-capacity partitioning */ +#define MPAMF_MBW_IDR 0x0040 /* mem-bw partitioning */ +#define MPAMF_PRI_IDR 0x0048 /* priority partitioning */ +#define MPAMF_CSUMON_IDR 0x0088 /* cache-usage monitor */ +#define MPAMF_MBWUMON_IDR 0x0090 /* mem-bw usage monitor */ +#define MPAMF_PARTID_NRW_IDR 0x0050 /* partid-narrowing */ +#define MPAMF_IIDR 0x0018 /* implementer id register */ +#define MPAMF_AIDR 0x0020 /* architectural id register */ + +/* Configuration and Status Register offsets in the memory mapped page */ +#define MPAMCFG_PART_SEL 0x0100 /* partid to configure: */ +#define MPAMCFG_CPBM 0x1000 /* cache-portion config */ +#define MPAMCFG_CMAX 0x0108 /* cache-capacity config */ +#define MPAMCFG_CMIN 0x0110 /* cache-capacity config */ +#define MPAMCFG_MBW_MIN 0x0200 /* min mem-bw config */ +#define MPAMCFG_MBW_MAX 0x0208 /* max mem-bw config */ +#define MPAMCFG_MBW_WINWD 0x0220 /* mem-bw accounting window config */ +#define MPAMCFG_MBW_PBM 0x2000 /* mem-bw portion bitmap config */ +#define MPAMCFG_PRI 0x0400 /* priority partitioning config */ +#define MPAMCFG_MBW_PROP 0x0500 /* mem-bw stride config */ +#define MPAMCFG_INTPARTID 0x0600 /* partid-narrowing config */ + +#define MSMON_CFG_MON_SEL 0x0800 /* monitor selector */ +#define MSMON_CFG_CSU_FLT 0x0810 /* cache-usage monitor filter */ +#define MSMON_CFG_CSU_CTL 0x0818 /* cache-usage monitor config */ +#define MSMON_CFG_MBWU_FLT 0x0820 /* mem-bw monitor filter */ +#define MSMON_CFG_MBWU_CTL 0x0828 /* mem-bw monitor config */ +#define MSMON_CSU 0x0840 /* current cache-usage */ +#define MSMON_CSU_CAPTURE 0x0848 /* last cache-usage value captured */ +#define MSMON_MBWU 0x0860 /* current mem-bw usage value */ +#define MSMON_MBWU_CAPTURE 0x0868 /* last mem-bw value captured */ +#define MSMON_MBWU_L 0x0880 /* current long mem-bw usage value */ +#define MSMON_MBWU_CAPTURE_L 0x0890 /* last long mem-bw value captured */ +#define MSMON_CAPT_EVNT 0x0808 /* signal a capture event */ +#define MPAMF_ESR 0x00F8 /* error status register */ +#define MPAMF_ECR 0x00F0 /* error control register */ + +/* MPAMF_IDR - MPAM features ID register */ +#define MPAMF_IDR_PARTID_MAX GENMASK(15, 0) +#define MPAMF_IDR_PMG_MAX GENMASK(23, 16) +#define MPAMF_IDR_HAS_CCAP_PART BIT(24) +#define MPAMF_IDR_HAS_CPOR_PART BIT(25) +#define MPAMF_IDR_HAS_MBW_PART BIT(26) +#define MPAMF_IDR_HAS_PRI_PART BIT(27) +#define MPAMF_IDR_EXT BIT(28) +#define MPAMF_IDR_HAS_IMPL_IDR BIT(29) +#define MPAMF_IDR_HAS_MSMON BIT(30) +#define MPAMF_IDR_HAS_PARTID_NRW BIT(31) +#define MPAMF_IDR_HAS_RIS BIT(32) +#define MPAMF_IDR_HAS_EXTD_ESR BIT(38) +#define MPAMF_IDR_HAS_ESR BIT(39) +#define MPAMF_IDR_RIS_MAX GENMASK(59, 56) + +/* MPAMF_MSMON_IDR - MPAM performance monitoring ID register */ +#define MPAMF_MSMON_IDR_MSMON_CSU BIT(16) +#define MPAMF_MSMON_IDR_MSMON_MBWU BIT(17) +#define MPAMF_MSMON_IDR_HAS_LOCAL_CAPT_EVNT BIT(31) + +/* MPAMF_CPOR_IDR - MPAM features cache portion partitioning ID register */ +#define MPAMF_CPOR_IDR_CPBM_WD GENMASK(15, 0) + +/* MPAMF_CCAP_IDR - MPAM features cache capacity partitioning ID register = */ +#define MPAMF_CCAP_IDR_CMAX_WD GENMASK(5, 0) +#define MPAMF_CCAP_IDR_CASSOC_WD GENMASK(12, 8) +#define MPAMF_CCAP_IDR_HAS_CASSOC BIT(28) +#define MPAMF_CCAP_IDR_HAS_CMIN BIT(29) +#define MPAMF_CCAP_IDR_NO_CMAX BIT(30) +#define MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM BIT(31) + +/* MPAMF_MBW_IDR - MPAM features memory bandwidth partitioning ID register= */ +#define MPAMF_MBW_IDR_BWA_WD GENMASK(5, 0) +#define MPAMF_MBW_IDR_HAS_MIN BIT(10) +#define MPAMF_MBW_IDR_HAS_MAX BIT(11) +#define MPAMF_MBW_IDR_HAS_PBM BIT(12) +#define MPAMF_MBW_IDR_HAS_PROP BIT(13) +#define MPAMF_MBW_IDR_WINDWR BIT(14) +#define MPAMF_MBW_IDR_BWPBM_WD GENMASK(28, 16) + +/* MPAMF_PRI_IDR - MPAM features priority partitioning ID register */ +#define MPAMF_PRI_IDR_HAS_INTPRI BIT(0) +#define MPAMF_PRI_IDR_INTPRI_0_IS_LOW BIT(1) +#define MPAMF_PRI_IDR_INTPRI_WD GENMASK(9, 4) +#define MPAMF_PRI_IDR_HAS_DSPRI BIT(16) +#define MPAMF_PRI_IDR_DSPRI_0_IS_LOW BIT(17) +#define MPAMF_PRI_IDR_DSPRI_WD GENMASK(25, 20) + +/* MPAMF_CSUMON_IDR - MPAM cache storage usage monitor ID register */ +#define MPAMF_CSUMON_IDR_NUM_MON GENMASK(15, 0) +#define MPAMF_CSUMON_IDR_HAS_OFLOW_CAPT BIT(24) +#define MPAMF_CSUMON_IDR_HAS_CEVNT_OFLW BIT(25) +#define MPAMF_CSUMON_IDR_HAS_OFSR BIT(26) +#define MPAMF_CSUMON_IDR_HAS_OFLOW_LNKG BIT(27) +#define MPAMF_CSUMON_IDR_HAS_XCL BIT(29) +#define MPAMF_CSUMON_IDR_CSU_RO BIT(30) +#define MPAMF_CSUMON_IDR_HAS_CAPTURE BIT(31) + +/* MPAMF_MBWUMON_IDR - MPAM memory bandwidth usage monitor ID register */ +#define MPAMF_MBWUMON_IDR_NUM_MON GENMASK(15, 0) +#define MPAMF_MBWUMON_IDR_HAS_RWBW BIT(28) +#define MPAMF_MBWUMON_IDR_LWD BIT(29) +#define MPAMF_MBWUMON_IDR_HAS_LONG BIT(30) +#define MPAMF_MBWUMON_IDR_HAS_CAPTURE BIT(31) + +/* MPAMF_PARTID_NRW_IDR - MPAM PARTID narrowing ID register */ +#define MPAMF_PARTID_NRW_IDR_INTPARTID_MAX GENMASK(15, 0) + +/* MPAMF_IIDR - MPAM implementation ID register */ +#define MPAMF_IIDR_PRODUCTID GENMASK(31, 20) +#define MPAMF_IIDR_PRODUCTID_SHIFT 20 +#define MPAMF_IIDR_VARIANT GENMASK(19, 16) +#define MPAMF_IIDR_VARIANT_SHIFT 16 +#define MPAMF_IIDR_REVISON GENMASK(15, 12) +#define MPAMF_IIDR_REVISON_SHIFT 12 +#define MPAMF_IIDR_IMPLEMENTER GENMASK(11, 0) +#define MPAMF_IIDR_IMPLEMENTER_SHIFT 0 + +/* MPAMF_AIDR - MPAM architecture ID register */ +#define MPAMF_AIDR_ARCH_MAJOR_REV GENMASK(7, 4) +#define MPAMF_AIDR_ARCH_MINOR_REV GENMASK(3, 0) + +/* MPAMCFG_PART_SEL - MPAM partition configuration selection register */ +#define MPAMCFG_PART_SEL_PARTID_SEL GENMASK(15, 0) +#define MPAMCFG_PART_SEL_INTERNAL BIT(16) +#define MPAMCFG_PART_SEL_RIS GENMASK(27, 24) + +/* MPAMCFG_CMAX - MPAM cache capacity configuration register */ +#define MPAMCFG_CMAX_SOFTLIM BIT(31) +#define MPAMCFG_CMAX_CMAX GENMASK(15, 0) + +/* MPAMCFG_CMIN - MPAM cache capacity configuration register */ +#define MPAMCFG_CMIN_CMIN GENMASK(15, 0) + +/* + * MPAMCFG_MBW_MIN - MPAM memory minimum bandwidth partitioning configurat= ion + * register + */ +#define MPAMCFG_MBW_MIN_MIN GENMASK(15, 0) + +/* + * MPAMCFG_MBW_MAX - MPAM memory maximum bandwidth partitioning configurat= ion + * register + */ +#define MPAMCFG_MBW_MAX_MAX GENMASK(15, 0) +#define MPAMCFG_MBW_MAX_HARDLIM BIT(31) + +/* + * MPAMCFG_MBW_WINWD - MPAM memory bandwidth partitioning window width + * register + */ +#define MPAMCFG_MBW_WINWD_US_FRAC GENMASK(7, 0) +#define MPAMCFG_MBW_WINWD_US_INT GENMASK(23, 8) + +/* MPAMCFG_PRI - MPAM priority partitioning configuration register */ +#define MPAMCFG_PRI_INTPRI GENMASK(15, 0) +#define MPAMCFG_PRI_DSPRI GENMASK(31, 16) + +/* + * MPAMCFG_MBW_PROP - Memory bandwidth proportional stride partitioning + * configuration register + */ +#define MPAMCFG_MBW_PROP_STRIDEM1 GENMASK(15, 0) +#define MPAMCFG_MBW_PROP_EN BIT(31) + +/* + * MPAMCFG_INTPARTID - MPAM internal partition narrowing configuration reg= ister + */ +#define MPAMCFG_INTPARTID_INTPARTID GENMASK(15, 0) +#define MPAMCFG_INTPARTID_INTERNAL BIT(16) + +/* MSMON_CFG_MON_SEL - Memory system performance monitor selection registe= r */ +#define MSMON_CFG_MON_SEL_MON_SEL GENMASK(15, 0) +#define MSMON_CFG_MON_SEL_RIS GENMASK(27, 24) + +/* MPAMF_ESR - MPAM Error Status Register */ +#define MPAMF_ESR_PARTID_MON GENMASK(15, 0) +#define MPAMF_ESR_PMG GENMASK(23, 16) +#define MPAMF_ESR_ERRCODE GENMASK(27, 24) +#define MPAMF_ESR_OVRWR BIT(31) +#define MPAMF_ESR_RIS GENMASK(35, 32) + +/* MPAMF_ECR - MPAM Error Control Register */ +#define MPAMF_ECR_INTEN BIT(0) + +/* Error conditions in accessing memory mapped registers */ +#define MPAM_ERRCODE_NONE 0 +#define MPAM_ERRCODE_PARTID_SEL_RANGE 1 +#define MPAM_ERRCODE_REQ_PARTID_RANGE 2 +#define MPAM_ERRCODE_MSMONCFG_ID_RANGE 3 +#define MPAM_ERRCODE_REQ_PMG_RANGE 4 +#define MPAM_ERRCODE_MONITOR_RANGE 5 +#define MPAM_ERRCODE_INTPARTID_RANGE 6 +#define MPAM_ERRCODE_UNEXPECTED_INTERNAL 7 + +/* + * MSMON_CFG_CSU_FLT - Memory system performance monitor configure cache s= torage + * usage monitor filter register + */ +#define MSMON_CFG_CSU_FLT_PARTID GENMASK(15, 0) +#define MSMON_CFG_CSU_FLT_PMG GENMASK(23, 16) + +/* + * MSMON_CFG_CSU_CTL - Memory system performance monitor configure cache s= torage + * usage monitor control register + * MSMON_CFG_MBWU_CTL - Memory system performance monitor configure memory + * bandwidth usage monitor control register + */ +#define MSMON_CFG_x_CTL_TYPE GENMASK(7, 0) +#define MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L BIT(15) +#define MSMON_CFG_x_CTL_MATCH_PARTID BIT(16) +#define MSMON_CFG_x_CTL_MATCH_PMG BIT(17) +#define MSMON_CFG_x_CTL_SCLEN BIT(19) +#define MSMON_CFG_x_CTL_SUBTYPE GENMASK(22, 20) +#define MSMON_CFG_x_CTL_OFLOW_FRZ BIT(24) +#define MSMON_CFG_x_CTL_OFLOW_INTR BIT(25) +#define MSMON_CFG_x_CTL_OFLOW_STATUS BIT(26) +#define MSMON_CFG_x_CTL_CAPT_RESET BIT(27) +#define MSMON_CFG_x_CTL_CAPT_EVNT GENMASK(30, 28) +#define MSMON_CFG_x_CTL_EN BIT(31) + +#define MSMON_CFG_MBWU_CTL_TYPE_MBWU 0x42 +#define MSMON_CFG_CSU_CTL_TYPE_CSU 0 + +/* + * MSMON_CFG_MBWU_FLT - Memory system performance monitor configure memory + * bandwidth usage monitor filter register + */ +#define MSMON_CFG_MBWU_FLT_PARTID GENMASK(15, 0) +#define MSMON_CFG_MBWU_FLT_PMG GENMASK(23, 16) +#define MSMON_CFG_MBWU_FLT_RWBW GENMASK(31, 30) + +/* + * MSMON_CSU - Memory system performance monitor cache storage usage monit= or + * register + * MSMON_CSU_CAPTURE - Memory system performance monitor cache storage us= age + * capture register + * MSMON_MBWU - Memory system performance monitor memory bandwidth usage + * monitor register + * MSMON_MBWU_CAPTURE - Memory system performance monitor memory bandwidth= usage + * capture register + */ +#define MSMON___VALUE GENMASK(30, 0) +#define MSMON___NRDY BIT(31) +#define MSMON___NRDY_L BIT(63) +#define MSMON___L_VALUE GENMASK(43, 0) +#define MSMON___LWD_VALUE GENMASK(62, 0) + +/* + * MSMON_CAPT_EVNT - Memory system performance monitoring capture event + * generation register + */ +#define MSMON_CAPT_EVNT_NOW BIT(0) + #endif /* MPAM_INTERNAL_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 30C892737F2; Fri, 22 Aug 2025 15:32:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876742; cv=none; b=rGSltRM2cQl2BhbMLay382CKO7+5SDNvc9v9coSHKmCrbjtGKLHZpHuJoQH6Do8SbHpbp6S8H1UVNQ/AddB83ZoqXlWqcrGy8GJ7cL2ZBrf4jktVU2dmXY8FwNmyTWYCZhinrf0XMuy7s3q2helqW8VI68HMAWJll+qd1GrJ1qo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876742; c=relaxed/simple; bh=CnCRiR9Hhx0tPwGqihebUQ/GVFmFcbIVpe+sldLi6Ro=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=B8TqaF+QUvNgpboPH3hjkcxy8k5nliLkBfNC9Lhe4czBtzejE6JbgxF4aBABgYnNGcMTL2kjk1BqXwOZfe9vqqcc2j+AyQfAcoZz/cvUqQRuhvWzOfFc0mLd1mq21T3mP42y53ep35dPHT33kLWe3YPrAng2sBUZ62o3qnUelmA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 862A827DD; Fri, 22 Aug 2025 08:32:11 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 78FE93F63F; Fri, 22 Aug 2025 08:32:14 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 13/33] arm_mpam: Add MPAM MSC register layout definitions Date: Fri, 22 Aug 2025 15:29:54 +0000 Message-Id: <20250822153048.2287-14-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Memory Partitioning and Monitoring (MPAM) has memory mapped devices (MSCs) with an identity/configuration page. Add the definitions for these registers as offset within the page(s). Link: https://developer.arm.com/documentation/ihi0099/latest/ Signed-off-by: James Morse Reviewed-by: Ben Horgan --- Changes since RFC: * Renamed MSMON_CFG_MBWU_CTL_TYPE_CSU as MSMON_CFG_CSU_CTL_TYPE_CSU * Whitepsace churn. * Cite a more recent document. * Removed some stale feature, fixed some names etc. --- drivers/resctrl/mpam_internal.h | 266 ++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index d49bb884b433..6e0982a1a9ac 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -150,4 +150,270 @@ extern struct list_head mpam_classes; int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 +/* + * MPAM MSCs have the following register layout. See: + * Arm Memory System Resource Partitioning and Monitoring (MPAM) System + * Component Specification. + * https://developer.arm.com/documentation/ihi0099/latest/ + */ +#define MPAM_ARCHITECTURE_V1 0x10 + +/* Memory mapped control pages: */ +/* ID Register offsets in the memory mapped page */ +#define MPAMF_IDR 0x0000 /* features id register */ +#define MPAMF_MSMON_IDR 0x0080 /* performance monitoring features */ +#define MPAMF_IMPL_IDR 0x0028 /* imp-def partitioning */ +#define MPAMF_CPOR_IDR 0x0030 /* cache-portion partitioning */ +#define MPAMF_CCAP_IDR 0x0038 /* cache-capacity partitioning */ +#define MPAMF_MBW_IDR 0x0040 /* mem-bw partitioning */ +#define MPAMF_PRI_IDR 0x0048 /* priority partitioning */ +#define MPAMF_CSUMON_IDR 0x0088 /* cache-usage monitor */ +#define MPAMF_MBWUMON_IDR 0x0090 /* mem-bw usage monitor */ +#define MPAMF_PARTID_NRW_IDR 0x0050 /* partid-narrowing */ +#define MPAMF_IIDR 0x0018 /* implementer id register */ +#define MPAMF_AIDR 0x0020 /* architectural id register */ + +/* Configuration and Status Register offsets in the memory mapped page */ +#define MPAMCFG_PART_SEL 0x0100 /* partid to configure: */ +#define MPAMCFG_CPBM 0x1000 /* cache-portion config */ +#define MPAMCFG_CMAX 0x0108 /* cache-capacity config */ +#define MPAMCFG_CMIN 0x0110 /* cache-capacity config */ +#define MPAMCFG_MBW_MIN 0x0200 /* min mem-bw config */ +#define MPAMCFG_MBW_MAX 0x0208 /* max mem-bw config */ +#define MPAMCFG_MBW_WINWD 0x0220 /* mem-bw accounting window config */ +#define MPAMCFG_MBW_PBM 0x2000 /* mem-bw portion bitmap config */ +#define MPAMCFG_PRI 0x0400 /* priority partitioning config */ +#define MPAMCFG_MBW_PROP 0x0500 /* mem-bw stride config */ +#define MPAMCFG_INTPARTID 0x0600 /* partid-narrowing config */ + +#define MSMON_CFG_MON_SEL 0x0800 /* monitor selector */ +#define MSMON_CFG_CSU_FLT 0x0810 /* cache-usage monitor filter */ +#define MSMON_CFG_CSU_CTL 0x0818 /* cache-usage monitor config */ +#define MSMON_CFG_MBWU_FLT 0x0820 /* mem-bw monitor filter */ +#define MSMON_CFG_MBWU_CTL 0x0828 /* mem-bw monitor config */ +#define MSMON_CSU 0x0840 /* current cache-usage */ +#define MSMON_CSU_CAPTURE 0x0848 /* last cache-usage value captured */ +#define MSMON_MBWU 0x0860 /* current mem-bw usage value */ +#define MSMON_MBWU_CAPTURE 0x0868 /* last mem-bw value captured */ +#define MSMON_MBWU_L 0x0880 /* current long mem-bw usage value */ +#define MSMON_MBWU_CAPTURE_L 0x0890 /* last long mem-bw value captured */ +#define MSMON_CAPT_EVNT 0x0808 /* signal a capture event */ +#define MPAMF_ESR 0x00F8 /* error status register */ +#define MPAMF_ECR 0x00F0 /* error control register */ + +/* MPAMF_IDR - MPAM features ID register */ +#define MPAMF_IDR_PARTID_MAX GENMASK(15, 0) +#define MPAMF_IDR_PMG_MAX GENMASK(23, 16) +#define MPAMF_IDR_HAS_CCAP_PART BIT(24) +#define MPAMF_IDR_HAS_CPOR_PART BIT(25) +#define MPAMF_IDR_HAS_MBW_PART BIT(26) +#define MPAMF_IDR_HAS_PRI_PART BIT(27) +#define MPAMF_IDR_EXT BIT(28) +#define MPAMF_IDR_HAS_IMPL_IDR BIT(29) +#define MPAMF_IDR_HAS_MSMON BIT(30) +#define MPAMF_IDR_HAS_PARTID_NRW BIT(31) +#define MPAMF_IDR_HAS_RIS BIT(32) +#define MPAMF_IDR_HAS_EXTD_ESR BIT(38) +#define MPAMF_IDR_HAS_ESR BIT(39) +#define MPAMF_IDR_RIS_MAX GENMASK(59, 56) + +/* MPAMF_MSMON_IDR - MPAM performance monitoring ID register */ +#define MPAMF_MSMON_IDR_MSMON_CSU BIT(16) +#define MPAMF_MSMON_IDR_MSMON_MBWU BIT(17) +#define MPAMF_MSMON_IDR_HAS_LOCAL_CAPT_EVNT BIT(31) + +/* MPAMF_CPOR_IDR - MPAM features cache portion partitioning ID register */ +#define MPAMF_CPOR_IDR_CPBM_WD GENMASK(15, 0) + +/* MPAMF_CCAP_IDR - MPAM features cache capacity partitioning ID register = */ +#define MPAMF_CCAP_IDR_CMAX_WD GENMASK(5, 0) +#define MPAMF_CCAP_IDR_CASSOC_WD GENMASK(12, 8) +#define MPAMF_CCAP_IDR_HAS_CASSOC BIT(28) +#define MPAMF_CCAP_IDR_HAS_CMIN BIT(29) +#define MPAMF_CCAP_IDR_NO_CMAX BIT(30) +#define MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM BIT(31) + +/* MPAMF_MBW_IDR - MPAM features memory bandwidth partitioning ID register= */ +#define MPAMF_MBW_IDR_BWA_WD GENMASK(5, 0) +#define MPAMF_MBW_IDR_HAS_MIN BIT(10) +#define MPAMF_MBW_IDR_HAS_MAX BIT(11) +#define MPAMF_MBW_IDR_HAS_PBM BIT(12) +#define MPAMF_MBW_IDR_HAS_PROP BIT(13) +#define MPAMF_MBW_IDR_WINDWR BIT(14) +#define MPAMF_MBW_IDR_BWPBM_WD GENMASK(28, 16) + +/* MPAMF_PRI_IDR - MPAM features priority partitioning ID register */ +#define MPAMF_PRI_IDR_HAS_INTPRI BIT(0) +#define MPAMF_PRI_IDR_INTPRI_0_IS_LOW BIT(1) +#define MPAMF_PRI_IDR_INTPRI_WD GENMASK(9, 4) +#define MPAMF_PRI_IDR_HAS_DSPRI BIT(16) +#define MPAMF_PRI_IDR_DSPRI_0_IS_LOW BIT(17) +#define MPAMF_PRI_IDR_DSPRI_WD GENMASK(25, 20) + +/* MPAMF_CSUMON_IDR - MPAM cache storage usage monitor ID register */ +#define MPAMF_CSUMON_IDR_NUM_MON GENMASK(15, 0) +#define MPAMF_CSUMON_IDR_HAS_OFLOW_CAPT BIT(24) +#define MPAMF_CSUMON_IDR_HAS_CEVNT_OFLW BIT(25) +#define MPAMF_CSUMON_IDR_HAS_OFSR BIT(26) +#define MPAMF_CSUMON_IDR_HAS_OFLOW_LNKG BIT(27) +#define MPAMF_CSUMON_IDR_HAS_XCL BIT(29) +#define MPAMF_CSUMON_IDR_CSU_RO BIT(30) +#define MPAMF_CSUMON_IDR_HAS_CAPTURE BIT(31) + +/* MPAMF_MBWUMON_IDR - MPAM memory bandwidth usage monitor ID register */ +#define MPAMF_MBWUMON_IDR_NUM_MON GENMASK(15, 0) +#define MPAMF_MBWUMON_IDR_HAS_RWBW BIT(28) +#define MPAMF_MBWUMON_IDR_LWD BIT(29) +#define MPAMF_MBWUMON_IDR_HAS_LONG BIT(30) +#define MPAMF_MBWUMON_IDR_HAS_CAPTURE BIT(31) + +/* MPAMF_PARTID_NRW_IDR - MPAM PARTID narrowing ID register */ +#define MPAMF_PARTID_NRW_IDR_INTPARTID_MAX GENMASK(15, 0) + +/* MPAMF_IIDR - MPAM implementation ID register */ +#define MPAMF_IIDR_PRODUCTID GENMASK(31, 20) +#define MPAMF_IIDR_PRODUCTID_SHIFT 20 +#define MPAMF_IIDR_VARIANT GENMASK(19, 16) +#define MPAMF_IIDR_VARIANT_SHIFT 16 +#define MPAMF_IIDR_REVISON GENMASK(15, 12) +#define MPAMF_IIDR_REVISON_SHIFT 12 +#define MPAMF_IIDR_IMPLEMENTER GENMASK(11, 0) +#define MPAMF_IIDR_IMPLEMENTER_SHIFT 0 + +/* MPAMF_AIDR - MPAM architecture ID register */ +#define MPAMF_AIDR_ARCH_MAJOR_REV GENMASK(7, 4) +#define MPAMF_AIDR_ARCH_MINOR_REV GENMASK(3, 0) + +/* MPAMCFG_PART_SEL - MPAM partition configuration selection register */ +#define MPAMCFG_PART_SEL_PARTID_SEL GENMASK(15, 0) +#define MPAMCFG_PART_SEL_INTERNAL BIT(16) +#define MPAMCFG_PART_SEL_RIS GENMASK(27, 24) + +/* MPAMCFG_CMAX - MPAM cache capacity configuration register */ +#define MPAMCFG_CMAX_SOFTLIM BIT(31) +#define MPAMCFG_CMAX_CMAX GENMASK(15, 0) + +/* MPAMCFG_CMIN - MPAM cache capacity configuration register */ +#define MPAMCFG_CMIN_CMIN GENMASK(15, 0) + +/* + * MPAMCFG_MBW_MIN - MPAM memory minimum bandwidth partitioning configurat= ion + * register + */ +#define MPAMCFG_MBW_MIN_MIN GENMASK(15, 0) + +/* + * MPAMCFG_MBW_MAX - MPAM memory maximum bandwidth partitioning configurat= ion + * register + */ +#define MPAMCFG_MBW_MAX_MAX GENMASK(15, 0) +#define MPAMCFG_MBW_MAX_HARDLIM BIT(31) + +/* + * MPAMCFG_MBW_WINWD - MPAM memory bandwidth partitioning window width + * register + */ +#define MPAMCFG_MBW_WINWD_US_FRAC GENMASK(7, 0) +#define MPAMCFG_MBW_WINWD_US_INT GENMASK(23, 8) + +/* MPAMCFG_PRI - MPAM priority partitioning configuration register */ +#define MPAMCFG_PRI_INTPRI GENMASK(15, 0) +#define MPAMCFG_PRI_DSPRI GENMASK(31, 16) + +/* + * MPAMCFG_MBW_PROP - Memory bandwidth proportional stride partitioning + * configuration register + */ +#define MPAMCFG_MBW_PROP_STRIDEM1 GENMASK(15, 0) +#define MPAMCFG_MBW_PROP_EN BIT(31) + +/* + * MPAMCFG_INTPARTID - MPAM internal partition narrowing configuration reg= ister + */ +#define MPAMCFG_INTPARTID_INTPARTID GENMASK(15, 0) +#define MPAMCFG_INTPARTID_INTERNAL BIT(16) + +/* MSMON_CFG_MON_SEL - Memory system performance monitor selection registe= r */ +#define MSMON_CFG_MON_SEL_MON_SEL GENMASK(15, 0) +#define MSMON_CFG_MON_SEL_RIS GENMASK(27, 24) + +/* MPAMF_ESR - MPAM Error Status Register */ +#define MPAMF_ESR_PARTID_MON GENMASK(15, 0) +#define MPAMF_ESR_PMG GENMASK(23, 16) +#define MPAMF_ESR_ERRCODE GENMASK(27, 24) +#define MPAMF_ESR_OVRWR BIT(31) +#define MPAMF_ESR_RIS GENMASK(35, 32) + +/* MPAMF_ECR - MPAM Error Control Register */ +#define MPAMF_ECR_INTEN BIT(0) + +/* Error conditions in accessing memory mapped registers */ +#define MPAM_ERRCODE_NONE 0 +#define MPAM_ERRCODE_PARTID_SEL_RANGE 1 +#define MPAM_ERRCODE_REQ_PARTID_RANGE 2 +#define MPAM_ERRCODE_MSMONCFG_ID_RANGE 3 +#define MPAM_ERRCODE_REQ_PMG_RANGE 4 +#define MPAM_ERRCODE_MONITOR_RANGE 5 +#define MPAM_ERRCODE_INTPARTID_RANGE 6 +#define MPAM_ERRCODE_UNEXPECTED_INTERNAL 7 + +/* + * MSMON_CFG_CSU_FLT - Memory system performance monitor configure cache s= torage + * usage monitor filter register + */ +#define MSMON_CFG_CSU_FLT_PARTID GENMASK(15, 0) +#define MSMON_CFG_CSU_FLT_PMG GENMASK(23, 16) + +/* + * MSMON_CFG_CSU_CTL - Memory system performance monitor configure cache s= torage + * usage monitor control register + * MSMON_CFG_MBWU_CTL - Memory system performance monitor configure memory + * bandwidth usage monitor control register + */ +#define MSMON_CFG_x_CTL_TYPE GENMASK(7, 0) +#define MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L BIT(15) +#define MSMON_CFG_x_CTL_MATCH_PARTID BIT(16) +#define MSMON_CFG_x_CTL_MATCH_PMG BIT(17) +#define MSMON_CFG_x_CTL_SCLEN BIT(19) +#define MSMON_CFG_x_CTL_SUBTYPE GENMASK(22, 20) +#define MSMON_CFG_x_CTL_OFLOW_FRZ BIT(24) +#define MSMON_CFG_x_CTL_OFLOW_INTR BIT(25) +#define MSMON_CFG_x_CTL_OFLOW_STATUS BIT(26) +#define MSMON_CFG_x_CTL_CAPT_RESET BIT(27) +#define MSMON_CFG_x_CTL_CAPT_EVNT GENMASK(30, 28) +#define MSMON_CFG_x_CTL_EN BIT(31) + +#define MSMON_CFG_MBWU_CTL_TYPE_MBWU 0x42 +#define MSMON_CFG_CSU_CTL_TYPE_CSU 0 + +/* + * MSMON_CFG_MBWU_FLT - Memory system performance monitor configure memory + * bandwidth usage monitor filter register + */ +#define MSMON_CFG_MBWU_FLT_PARTID GENMASK(15, 0) +#define MSMON_CFG_MBWU_FLT_PMG GENMASK(23, 16) +#define MSMON_CFG_MBWU_FLT_RWBW GENMASK(31, 30) + +/* + * MSMON_CSU - Memory system performance monitor cache storage usage monit= or + * register + * MSMON_CSU_CAPTURE - Memory system performance monitor cache storage us= age + * capture register + * MSMON_MBWU - Memory system performance monitor memory bandwidth usage + * monitor register + * MSMON_MBWU_CAPTURE - Memory system performance monitor memory bandwidth= usage + * capture register + */ +#define MSMON___VALUE GENMASK(30, 0) +#define MSMON___NRDY BIT(31) +#define MSMON___NRDY_L BIT(63) +#define MSMON___L_VALUE GENMASK(43, 0) +#define MSMON___LWD_VALUE GENMASK(62, 0) + +/* + * MSMON_CAPT_EVNT - Memory system performance monitoring capture event + * generation register + */ +#define MSMON_CAPT_EVNT_NOW BIT(0) + #endif /* MPAM_INTERNAL_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D95DC230BC5; Fri, 22 Aug 2025 15:32:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876747; cv=none; b=fBycG6n3KmqsZTBRDO0z30nU0FJu6p12zvz30Lgoj3pYV6Aa4uxU2mQdOqfGDW4a+Uj2ehilqCMsSuu+N4EB2ElUutP3wWNjePtBg+JTv/M5cQWO+RGCQe9LmMPizH8ALtqYt61YcUQg6VygojE2WNmJyY/c/XkyOZgq7TVTpTY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876747; c=relaxed/simple; bh=D8sn23uyVZKXjYi56qnnrhhb+TIoQOI3fZWQOUGpl+w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AI7vcnyt3bAgQko5hefrGXS3YZxNHlSLQnUyHnBxoOAAylSvecz2gKlL38a6XOz0hj/TilDj9u/tAn8uwScffDNJF9HvJEMJvew6OWFFgPAkCPG421Cqj5HKh4MB+vLIvYB7yJ9Etif/8KdguG9le/7SDqsE0KWC8hyenW2bfEM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 434D32880; Fri, 22 Aug 2025 08:32:17 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 139773F63F; Fri, 22 Aug 2025 08:32:19 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Lecopzer Chen Subject: [PATCH 14/33] arm_mpam: Add cpuhp callbacks to probe MSC hardware Date: Fri, 22 Aug 2025 15:29:55 +0000 Message-Id: <20250822153048.2287-15-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Because an MSC can only by accessed from the CPUs in its cpu-affinity set we need to be running on one of those CPUs to probe the MSC hardware. Do this work in the cpuhp callback. Probing the hardware will only happen before MPAM is enabled, walk all the MSCs and probe those we can reach that haven't already been probed. Later once MPAM is enabled, this cpuhp callback will be replaced by one that avoids the global list. Enabling a static key will also take the cpuhp lock, so can't be done from the cpuhp callback. Whenever a new MSC has been probed schedule work to test if all the MSCs have now been probed. CC: Lecopzer Chen Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 144 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 8 +- 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 5baf2a8786fb..9d6516f98acf 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -4,6 +4,7 @@ #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ =20 #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include =20 #include =20 @@ -39,6 +41,16 @@ struct srcu_struct mpam_srcu; /* MPAM isn't available until all the MSC have been probed. */ static u32 mpam_num_msc; =20 +static int mpam_cpuhp_state; +static DEFINE_MUTEX(mpam_cpuhp_state_lock); + +/* + * mpam is enabled once all devices have been probed from CPU online callb= acks, + * scheduled via this work_struct. If access to an MSC depends on a CPU th= at + * was not brought online at boot, this can happen surprisingly late. + */ +static DECLARE_WORK(mpam_enable_work, &mpam_enable); + /* * An MSC is a physical container for controls and monitors, each identifi= ed by * their RIS index. These share a base-address, interrupts and some MMIO @@ -78,6 +90,22 @@ LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */ static LLIST_HEAD(mpam_garbage); =20 +static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) +{ + WARN_ON_ONCE(reg > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + return readl_relaxed(msc->mapped_hwpage + reg); +} + +static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) +{ + lockdep_assert_held_once(&msc->part_sel_lock); + return __mpam_read_reg(msc, reg); +} + +#define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc,= MPAMF_##reg) + #define init_garbage(x) init_llist_node(&(x)->garbage.llist) =20 static struct mpam_vmsc * @@ -511,9 +539,84 @@ int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, return err; } =20 -static void mpam_discovery_complete(void) +static int mpam_msc_hw_probe(struct mpam_msc *msc) +{ + u64 idr; + int err; + + lockdep_assert_held(&msc->probe_lock); + + mutex_lock(&msc->part_sel_lock); + idr =3D mpam_read_partsel_reg(msc, AIDR); + if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) !=3D MPAM_ARCHITECTURE_V1) { + pr_err_once("%s does not match MPAM architecture v1.x\n", + dev_name(&msc->pdev->dev)); + err =3D -EIO; + } else { + msc->probed =3D true; + err =3D 0; + } + mutex_unlock(&msc->part_sel_lock); + + return err; +} + +static int mpam_cpu_online(unsigned int cpu) { - pr_err("Discovered all MSC\n"); + return 0; +} + +/* Before mpam is enabled, try to probe new MSC */ +static int mpam_discovery_cpu_online(unsigned int cpu) +{ + int err =3D 0; + struct mpam_msc *msc; + bool new_device_probed =3D false; + + mutex_lock(&mpam_list_lock); + list_for_each_entry(msc, &mpam_all_msc, glbl_list) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + mutex_lock(&msc->probe_lock); + if (!msc->probed) + err =3D mpam_msc_hw_probe(msc); + mutex_unlock(&msc->probe_lock); + + if (!err) + new_device_probed =3D true; + else + break; // mpam_broken + } + mutex_unlock(&mpam_list_lock); + + if (new_device_probed && !err) + schedule_work(&mpam_enable_work); + + return err; +} + +static int mpam_cpu_offline(unsigned int cpu) +{ + return 0; +} + +static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int onlin= e), + int (*offline)(unsigned int offline)) +{ + mutex_lock(&mpam_cpuhp_state_lock); + if (mpam_cpuhp_state) { + cpuhp_remove_state(mpam_cpuhp_state); + mpam_cpuhp_state =3D 0; + } + + mpam_cpuhp_state =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mpam:online", + online, offline); + if (mpam_cpuhp_state <=3D 0) { + pr_err("Failed to register cpuhp callbacks"); + mpam_cpuhp_state =3D 0; + } + mutex_unlock(&mpam_cpuhp_state_lock); } =20 static int mpam_dt_count_msc(void) @@ -772,7 +875,7 @@ static int mpam_msc_drv_probe(struct platform_device *p= dev) } =20 if (!err && fw_num_msc =3D=3D mpam_num_msc) - mpam_discovery_complete(); + mpam_register_cpuhp_callbacks(&mpam_discovery_cpu_online, NULL); =20 if (err && msc) mpam_msc_drv_remove(pdev); @@ -795,6 +898,41 @@ static struct platform_driver mpam_msc_driver =3D { .remove =3D mpam_msc_drv_remove, }; =20 +static void mpam_enable_once(void) +{ + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); + + pr_info("MPAM enabled\n"); +} + +/* + * Enable mpam once all devices have been probed. + * Scheduled by mpam_discovery_cpu_online() once all devices have been cre= ated. + * Also scheduled when new devices are probed when new CPUs come online. + */ +void mpam_enable(struct work_struct *work) +{ + static atomic_t once; + struct mpam_msc *msc; + bool all_devices_probed =3D true; + + /* Have we probed all the hw devices? */ + mutex_lock(&mpam_list_lock); + list_for_each_entry(msc, &mpam_all_msc, glbl_list) { + mutex_lock(&msc->probe_lock); + if (!msc->probed) + all_devices_probed =3D false; + mutex_unlock(&msc->probe_lock); + + if (!all_devices_probed) + break; + } + mutex_unlock(&mpam_list_lock); + + if (all_devices_probed && !atomic_fetch_inc(&once)) + mpam_enable_once(); +} + /* * MSC that are hidden under caches are not created as platform devices * as there is no cache driver. Caches are also special-cased in diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 6e0982a1a9ac..a98cca08a2ef 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -49,6 +49,7 @@ struct mpam_msc { * properties become read-only and the lists are protected by SRCU. */ struct mutex probe_lock; + bool probed; unsigned long ris_idxs[128 / BITS_PER_LONG]; u32 ris_max; =20 @@ -59,14 +60,14 @@ struct mpam_msc { * part_sel_lock protects access to the MSC hardware registers that are * affected by MPAMCFG_PART_SEL. (including the ID registers that vary * by RIS). - * If needed, take msc->lock first. + * If needed, take msc->probe_lock first. */ struct mutex part_sel_lock; =20 /* * mon_sel_lock protects access to the MSC hardware registers that are * affeted by MPAMCFG_MON_SEL. - * If needed, take msc->lock first. + * If needed, take msc->probe_lock first. */ struct mutex outer_mon_sel_lock; raw_spinlock_t inner_mon_sel_lock; @@ -147,6 +148,9 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +/* Scheduled work callback to enable mpam once all MSC have been probed */ +void mpam_enable(struct work_struct *work); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 02ED533A011; Fri, 22 Aug 2025 15:35:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876940; cv=none; b=ConxsZmBeZCgdwXNaMLx5vzkZZXak5FXelHixtnBJCTjXp559igUzz/xX8Kz3o3w6MR7UgGZhXxPtI05PqeFYqrOQR9FdKEk4zzrzPPdsyxYkK5I/UEyUQS+D9+B3TPGp9055G3Et+e5vhT0IQgUGIloU1LKPf5klkdrOviMNaY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876940; c=relaxed/simple; bh=D8sn23uyVZKXjYi56qnnrhhb+TIoQOI3fZWQOUGpl+w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FyUqq88RjbJpaUk7VKfUaQwgyh8IHKAcgPBSoGw+m7k3FPM1TqOO0cjxCze/3B7UXEPTYKE5n0CkwE49j8DNMY8lPwq0jpHrzXFJp2tm8nbSmuU4LPkRjAft68xohKezp90IHohKoKkgapMy4m7Ok7FFPghYxg8PYlXW0PjLV54= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 1978015A1; Fri, 22 Aug 2025 08:35:29 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id DD3033F63F; Fri, 22 Aug 2025 08:35:31 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Lecopzer Chen Subject: [PATCH 14/33] arm_mpam: Add cpuhp callbacks to probe MSC hardware Date: Fri, 22 Aug 2025 15:30:29 +0000 Message-Id: <20250822153048.2287-49-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Because an MSC can only by accessed from the CPUs in its cpu-affinity set we need to be running on one of those CPUs to probe the MSC hardware. Do this work in the cpuhp callback. Probing the hardware will only happen before MPAM is enabled, walk all the MSCs and probe those we can reach that haven't already been probed. Later once MPAM is enabled, this cpuhp callback will be replaced by one that avoids the global list. Enabling a static key will also take the cpuhp lock, so can't be done from the cpuhp callback. Whenever a new MSC has been probed schedule work to test if all the MSCs have now been probed. CC: Lecopzer Chen Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 144 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 8 +- 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 5baf2a8786fb..9d6516f98acf 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -4,6 +4,7 @@ #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ =20 #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include =20 #include =20 @@ -39,6 +41,16 @@ struct srcu_struct mpam_srcu; /* MPAM isn't available until all the MSC have been probed. */ static u32 mpam_num_msc; =20 +static int mpam_cpuhp_state; +static DEFINE_MUTEX(mpam_cpuhp_state_lock); + +/* + * mpam is enabled once all devices have been probed from CPU online callb= acks, + * scheduled via this work_struct. If access to an MSC depends on a CPU th= at + * was not brought online at boot, this can happen surprisingly late. + */ +static DECLARE_WORK(mpam_enable_work, &mpam_enable); + /* * An MSC is a physical container for controls and monitors, each identifi= ed by * their RIS index. These share a base-address, interrupts and some MMIO @@ -78,6 +90,22 @@ LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */ static LLIST_HEAD(mpam_garbage); =20 +static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) +{ + WARN_ON_ONCE(reg > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + return readl_relaxed(msc->mapped_hwpage + reg); +} + +static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) +{ + lockdep_assert_held_once(&msc->part_sel_lock); + return __mpam_read_reg(msc, reg); +} + +#define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc,= MPAMF_##reg) + #define init_garbage(x) init_llist_node(&(x)->garbage.llist) =20 static struct mpam_vmsc * @@ -511,9 +539,84 @@ int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, return err; } =20 -static void mpam_discovery_complete(void) +static int mpam_msc_hw_probe(struct mpam_msc *msc) +{ + u64 idr; + int err; + + lockdep_assert_held(&msc->probe_lock); + + mutex_lock(&msc->part_sel_lock); + idr =3D mpam_read_partsel_reg(msc, AIDR); + if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) !=3D MPAM_ARCHITECTURE_V1) { + pr_err_once("%s does not match MPAM architecture v1.x\n", + dev_name(&msc->pdev->dev)); + err =3D -EIO; + } else { + msc->probed =3D true; + err =3D 0; + } + mutex_unlock(&msc->part_sel_lock); + + return err; +} + +static int mpam_cpu_online(unsigned int cpu) { - pr_err("Discovered all MSC\n"); + return 0; +} + +/* Before mpam is enabled, try to probe new MSC */ +static int mpam_discovery_cpu_online(unsigned int cpu) +{ + int err =3D 0; + struct mpam_msc *msc; + bool new_device_probed =3D false; + + mutex_lock(&mpam_list_lock); + list_for_each_entry(msc, &mpam_all_msc, glbl_list) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + mutex_lock(&msc->probe_lock); + if (!msc->probed) + err =3D mpam_msc_hw_probe(msc); + mutex_unlock(&msc->probe_lock); + + if (!err) + new_device_probed =3D true; + else + break; // mpam_broken + } + mutex_unlock(&mpam_list_lock); + + if (new_device_probed && !err) + schedule_work(&mpam_enable_work); + + return err; +} + +static int mpam_cpu_offline(unsigned int cpu) +{ + return 0; +} + +static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int onlin= e), + int (*offline)(unsigned int offline)) +{ + mutex_lock(&mpam_cpuhp_state_lock); + if (mpam_cpuhp_state) { + cpuhp_remove_state(mpam_cpuhp_state); + mpam_cpuhp_state =3D 0; + } + + mpam_cpuhp_state =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mpam:online", + online, offline); + if (mpam_cpuhp_state <=3D 0) { + pr_err("Failed to register cpuhp callbacks"); + mpam_cpuhp_state =3D 0; + } + mutex_unlock(&mpam_cpuhp_state_lock); } =20 static int mpam_dt_count_msc(void) @@ -772,7 +875,7 @@ static int mpam_msc_drv_probe(struct platform_device *p= dev) } =20 if (!err && fw_num_msc =3D=3D mpam_num_msc) - mpam_discovery_complete(); + mpam_register_cpuhp_callbacks(&mpam_discovery_cpu_online, NULL); =20 if (err && msc) mpam_msc_drv_remove(pdev); @@ -795,6 +898,41 @@ static struct platform_driver mpam_msc_driver =3D { .remove =3D mpam_msc_drv_remove, }; =20 +static void mpam_enable_once(void) +{ + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); + + pr_info("MPAM enabled\n"); +} + +/* + * Enable mpam once all devices have been probed. + * Scheduled by mpam_discovery_cpu_online() once all devices have been cre= ated. + * Also scheduled when new devices are probed when new CPUs come online. + */ +void mpam_enable(struct work_struct *work) +{ + static atomic_t once; + struct mpam_msc *msc; + bool all_devices_probed =3D true; + + /* Have we probed all the hw devices? */ + mutex_lock(&mpam_list_lock); + list_for_each_entry(msc, &mpam_all_msc, glbl_list) { + mutex_lock(&msc->probe_lock); + if (!msc->probed) + all_devices_probed =3D false; + mutex_unlock(&msc->probe_lock); + + if (!all_devices_probed) + break; + } + mutex_unlock(&mpam_list_lock); + + if (all_devices_probed && !atomic_fetch_inc(&once)) + mpam_enable_once(); +} + /* * MSC that are hidden under caches are not created as platform devices * as there is no cache driver. Caches are also special-cased in diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 6e0982a1a9ac..a98cca08a2ef 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -49,6 +49,7 @@ struct mpam_msc { * properties become read-only and the lists are protected by SRCU. */ struct mutex probe_lock; + bool probed; unsigned long ris_idxs[128 / BITS_PER_LONG]; u32 ris_max; =20 @@ -59,14 +60,14 @@ struct mpam_msc { * part_sel_lock protects access to the MSC hardware registers that are * affected by MPAMCFG_PART_SEL. (including the ID registers that vary * by RIS). - * If needed, take msc->lock first. + * If needed, take msc->probe_lock first. */ struct mutex part_sel_lock; =20 /* * mon_sel_lock protects access to the MSC hardware registers that are * affeted by MPAMCFG_MON_SEL. - * If needed, take msc->lock first. + * If needed, take msc->probe_lock first. */ struct mutex outer_mon_sel_lock; raw_spinlock_t inner_mon_sel_lock; @@ -147,6 +148,9 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +/* Scheduled work callback to enable mpam once all MSC have been probed */ +void mpam_enable(struct work_struct *work); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9FB822367CE; Fri, 22 Aug 2025 15:32:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876753; cv=none; b=o3mHvLbbTIjGdINbK6Yx9ZLglt72OLiLX7ivzP/5BAsKzMWsGBpyBX7Ln3L1NvuZf/OKmVaW8Ffj9vbT1isdMQPeX9272hz0YIauVmCAIxfjVYZ2NeMvSPmrpVP9Za9o+6j6sWa7cIvPPOf2uxWou0ohNsKawKzKV3OOGU2Q0iw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876753; c=relaxed/simple; bh=UZqSjQBeTlP6OYVw3b2VX7s+Yc/tobpw5JtY5IDgs/w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FLUjyZJ3o+v6q+DD+kNYZrzhhpAKJxeibLx/30g+FGkkw+hzpA5FVk+ctBPLhWpjv2F2zrL5If79efRFxRc5+5+AZlXYXbocL5l3yZIPC5JEyihf2hxXyzrRhxJlKd+CBB9wQ5pNI6YnqGwUyQuTOq4YJ86unVaK2+BNtFmxzk4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D163B28AC; Fri, 22 Aug 2025 08:32:22 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C3A3A3F63F; Fri, 22 Aug 2025 08:32:25 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 15/33] arm_mpam: Probe MSCs to find the supported partid/pmg values Date: Fri, 22 Aug 2025 15:29:56 +0000 Message-Id: <20250822153048.2287-16-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" CPUs can generate traffic with a range of PARTID and PMG values, but each MSC may have its own maximum size for these fields. Before MPAM can be used, the driver needs to probe each RIS on each MSC, to find the system-wide smallest value that can be used. While doing this, RIS entries that firmware didn't describe are create under MPAM_CLASS_UNKNOWN. While we're here, implement the mpam_register_requestor() call for the arch code to register the CPU limits. Future callers of this will tell us about the SMMU and ITS. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 158 ++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 6 ++ include/linux/arm_mpam.h | 14 +++ 3 files changed, 171 insertions(+), 7 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 9d6516f98acf..012e09e80300 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,15 @@ static u32 mpam_num_msc; static int mpam_cpuhp_state; static DEFINE_MUTEX(mpam_cpuhp_state_lock); =20 +/* + * The smallest common values for any CPU or MSC in the system. + * Generating traffic outside this range will result in screaming interrup= ts. + */ +u16 mpam_partid_max; +u8 mpam_pmg_max; +static bool partid_max_init, partid_max_published; +static DEFINE_SPINLOCK(partid_max_lock); + /* * mpam is enabled once all devices have been probed from CPU online callb= acks, * scheduled via this work_struct. If access to an MSC depends on a CPU th= at @@ -106,6 +116,74 @@ static inline u32 _mpam_read_partsel_reg(struct mpam_m= sc *msc, u16 reg) =20 #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc,= MPAMF_##reg) =20 +static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) +{ + WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + writel_relaxed(val, msc->mapped_hwpage + reg); +} + +static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, = u32 val) +{ + lockdep_assert_held_once(&msc->part_sel_lock); + __mpam_write_reg(msc, reg, val); +} +#define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc= , MPAMCFG_##reg, val) + +static u64 mpam_msc_read_idr(struct mpam_msc *msc) +{ + u64 idr_high =3D 0, idr_low; + + lockdep_assert_held(&msc->part_sel_lock); + + idr_low =3D mpam_read_partsel_reg(msc, IDR); + if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) + idr_high =3D mpam_read_partsel_reg(msc, IDR + 4); + + return (idr_high << 32) | idr_low; +} + +static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) +{ + lockdep_assert_held(&msc->part_sel_lock); + + mpam_write_partsel_reg(msc, PART_SEL, partsel); +} + +static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) +{ + u32 partsel =3D FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | + FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); + + __mpam_part_sel_raw(partsel, msc); +} + +int mpam_register_requestor(u16 partid_max, u8 pmg_max) +{ + int err =3D 0; + + lockdep_assert_irqs_enabled(); + + spin_lock(&partid_max_lock); + if (!partid_max_init) { + mpam_partid_max =3D partid_max; + mpam_pmg_max =3D pmg_max; + partid_max_init =3D true; + } else if (!partid_max_published) { + mpam_partid_max =3D min(mpam_partid_max, partid_max); + mpam_pmg_max =3D min(mpam_pmg_max, pmg_max); + } else { + /* New requestors can't lower the values */ + if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) + err =3D -EBUSY; + } + spin_unlock(&partid_max_lock); + + return err; +} +EXPORT_SYMBOL(mpam_register_requestor); + #define init_garbage(x) init_llist_node(&(x)->garbage.llist) =20 static struct mpam_vmsc * @@ -520,6 +598,7 @@ static int mpam_ris_create_locked(struct mpam_msc *msc,= u8 ris_idx, cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); cpumask_or(&class->affinity, &class->affinity, &ris->affinity); list_add_rcu(&ris->vmsc_list, &vmsc->ris); + list_add_rcu(&ris->msc_list, &msc->ris); =20 return 0; } @@ -539,10 +618,37 @@ int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, return err; } =20 +static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc, + u8 ris_idx) +{ + int err; + struct mpam_msc_ris *ris, *found =3D ERR_PTR(-ENOENT); + + lockdep_assert_held(&mpam_list_lock); + + if (!test_bit(ris_idx, msc->ris_idxs)) { + err =3D mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN, + 0, 0, GFP_ATOMIC); + if (err) + return ERR_PTR(err); + } + + list_for_each_entry(ris, &msc->ris, msc_list) { + if (ris->ris_idx =3D=3D ris_idx) { + found =3D ris; + break; + } + } + + return found; +} + static int mpam_msc_hw_probe(struct mpam_msc *msc) { u64 idr; - int err; + u16 partid_max; + u8 ris_idx, pmg_max; + struct mpam_msc_ris *ris; =20 lockdep_assert_held(&msc->probe_lock); =20 @@ -551,14 +657,42 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) !=3D MPAM_ARCHITECTURE_V1) { pr_err_once("%s does not match MPAM architecture v1.x\n", dev_name(&msc->pdev->dev)); - err =3D -EIO; - } else { - msc->probed =3D true; - err =3D 0; + mutex_unlock(&msc->part_sel_lock); + return -EIO; } + + idr =3D mpam_msc_read_idr(msc); mutex_unlock(&msc->part_sel_lock); + msc->ris_max =3D FIELD_GET(MPAMF_IDR_RIS_MAX, idr); + + /* Use these values so partid/pmg always starts with a valid value */ + msc->partid_max =3D FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); + msc->pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); + + for (ris_idx =3D 0; ris_idx <=3D msc->ris_max; ris_idx++) { + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris_idx, 0, msc); + idr =3D mpam_msc_read_idr(msc); + mutex_unlock(&msc->part_sel_lock); + + partid_max =3D FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); + pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); + msc->partid_max =3D min(msc->partid_max, partid_max); + msc->pmg_max =3D min(msc->pmg_max, pmg_max); + + ris =3D mpam_get_or_create_ris(msc, ris_idx); + if (IS_ERR(ris)) + return PTR_ERR(ris); + } =20 - return err; + spin_lock(&partid_max_lock); + mpam_partid_max =3D min(mpam_partid_max, msc->partid_max); + mpam_pmg_max =3D min(mpam_pmg_max, msc->pmg_max); + spin_unlock(&partid_max_lock); + + msc->probed =3D true; + + return 0; } =20 static int mpam_cpu_online(unsigned int cpu) @@ -900,9 +1034,18 @@ static struct platform_driver mpam_msc_driver =3D { =20 static void mpam_enable_once(void) { + /* + * Once the cpuhp callbacks have been changed, mpam_partid_max can no + * longer change. + */ + spin_lock(&partid_max_lock); + partid_max_published =3D true; + spin_unlock(&partid_max_lock); + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 - pr_info("MPAM enabled\n"); + printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", + mpam_partid_max + 1, mpam_pmg_max + 1); } =20 /* @@ -972,4 +1115,5 @@ static int __init mpam_msc_driver_init(void) =20 return platform_driver_register(&mpam_msc_driver); } +/* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ subsys_initcall(mpam_msc_driver_init); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a98cca08a2ef..a623f405ddd8 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -50,6 +50,8 @@ struct mpam_msc { */ struct mutex probe_lock; bool probed; + u16 partid_max; + u8 pmg_max; unsigned long ris_idxs[128 / BITS_PER_LONG]; u32 ris_max; =20 @@ -148,6 +150,10 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +/* System wide partid/pmg values */ +extern u16 mpam_partid_max; +extern u8 mpam_pmg_max; + /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); =20 diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h index 406a77be68cb..8af93794c7a2 100644 --- a/include/linux/arm_mpam.h +++ b/include/linux/arm_mpam.h @@ -39,4 +39,18 @@ static inline int acpi_mpam_count_msc(void) { return -EI= NVAL; } int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, enum mpam_class_types type, u8 class_id, int component_id); =20 +/** + * mpam_register_requestor() - Register a requestor with the MPAM driver + * @partid_max: The maximum PARTID value the requestor can generate. + * @pmg_max: The maximum PMG value the requestor can generate. + * + * Registers a requestor with the MPAM driver to ensure the chosen system-= wide + * minimum PARTID and PMG values will allow the requestors features to be = used. + * + * Returns an error if the registration is too late, and a larger PARTID/P= MG + * value has been advertised to user-space. In this case the requestor sho= uld + * not use its MPAM features. Returns 0 on success. + */ +int mpam_register_requestor(u16 partid_max, u8 pmg_max); + #endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5355F34166E; Fri, 22 Aug 2025 15:35:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876945; cv=none; b=CVkwEECwRygBxNm8ttE1Ks45Ipsjdrhv983SMKHiXe/+8tb1b8gnQA0kkqovqXvufnbaNvaYbn3RcuSIzu5O4J1d/cCs8+soQQIz98RqsLSRmPSC9hNQ9INiFOIqWTUSKVkGx6IoPlj9N5bzItrWEiNvDqu0MSsySPk8PiVNb2Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876945; c=relaxed/simple; bh=UZqSjQBeTlP6OYVw3b2VX7s+Yc/tobpw5JtY5IDgs/w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b5gZzw6OUA610p/g36enE2rFxJoqjPvF1tiDtKJqmDTQmiH6j2O01dhEHX3u7e9Dn6ULqTHA6SwHNhDaFcjLl86JxIyCFJPCUdMXD7f9zbQ2DDCgHavS35OGOwxHhdNossJJ0TQYqw0AQkP/G9g3suu800TBnRNy7arZSGoicRU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id AA0D015A1; Fri, 22 Aug 2025 08:35:34 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 9BB9D3F63F; Fri, 22 Aug 2025 08:35:37 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 15/33] arm_mpam: Probe MSCs to find the supported partid/pmg values Date: Fri, 22 Aug 2025 15:30:30 +0000 Message-Id: <20250822153048.2287-50-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" CPUs can generate traffic with a range of PARTID and PMG values, but each MSC may have its own maximum size for these fields. Before MPAM can be used, the driver needs to probe each RIS on each MSC, to find the system-wide smallest value that can be used. While doing this, RIS entries that firmware didn't describe are create under MPAM_CLASS_UNKNOWN. While we're here, implement the mpam_register_requestor() call for the arch code to register the CPU limits. Future callers of this will tell us about the SMMU and ITS. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 158 ++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 6 ++ include/linux/arm_mpam.h | 14 +++ 3 files changed, 171 insertions(+), 7 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 9d6516f98acf..012e09e80300 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,15 @@ static u32 mpam_num_msc; static int mpam_cpuhp_state; static DEFINE_MUTEX(mpam_cpuhp_state_lock); =20 +/* + * The smallest common values for any CPU or MSC in the system. + * Generating traffic outside this range will result in screaming interrup= ts. + */ +u16 mpam_partid_max; +u8 mpam_pmg_max; +static bool partid_max_init, partid_max_published; +static DEFINE_SPINLOCK(partid_max_lock); + /* * mpam is enabled once all devices have been probed from CPU online callb= acks, * scheduled via this work_struct. If access to an MSC depends on a CPU th= at @@ -106,6 +116,74 @@ static inline u32 _mpam_read_partsel_reg(struct mpam_m= sc *msc, u16 reg) =20 #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc,= MPAMF_##reg) =20 +static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) +{ + WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + writel_relaxed(val, msc->mapped_hwpage + reg); +} + +static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, = u32 val) +{ + lockdep_assert_held_once(&msc->part_sel_lock); + __mpam_write_reg(msc, reg, val); +} +#define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc= , MPAMCFG_##reg, val) + +static u64 mpam_msc_read_idr(struct mpam_msc *msc) +{ + u64 idr_high =3D 0, idr_low; + + lockdep_assert_held(&msc->part_sel_lock); + + idr_low =3D mpam_read_partsel_reg(msc, IDR); + if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) + idr_high =3D mpam_read_partsel_reg(msc, IDR + 4); + + return (idr_high << 32) | idr_low; +} + +static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) +{ + lockdep_assert_held(&msc->part_sel_lock); + + mpam_write_partsel_reg(msc, PART_SEL, partsel); +} + +static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) +{ + u32 partsel =3D FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | + FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); + + __mpam_part_sel_raw(partsel, msc); +} + +int mpam_register_requestor(u16 partid_max, u8 pmg_max) +{ + int err =3D 0; + + lockdep_assert_irqs_enabled(); + + spin_lock(&partid_max_lock); + if (!partid_max_init) { + mpam_partid_max =3D partid_max; + mpam_pmg_max =3D pmg_max; + partid_max_init =3D true; + } else if (!partid_max_published) { + mpam_partid_max =3D min(mpam_partid_max, partid_max); + mpam_pmg_max =3D min(mpam_pmg_max, pmg_max); + } else { + /* New requestors can't lower the values */ + if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) + err =3D -EBUSY; + } + spin_unlock(&partid_max_lock); + + return err; +} +EXPORT_SYMBOL(mpam_register_requestor); + #define init_garbage(x) init_llist_node(&(x)->garbage.llist) =20 static struct mpam_vmsc * @@ -520,6 +598,7 @@ static int mpam_ris_create_locked(struct mpam_msc *msc,= u8 ris_idx, cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); cpumask_or(&class->affinity, &class->affinity, &ris->affinity); list_add_rcu(&ris->vmsc_list, &vmsc->ris); + list_add_rcu(&ris->msc_list, &msc->ris); =20 return 0; } @@ -539,10 +618,37 @@ int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, return err; } =20 +static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc, + u8 ris_idx) +{ + int err; + struct mpam_msc_ris *ris, *found =3D ERR_PTR(-ENOENT); + + lockdep_assert_held(&mpam_list_lock); + + if (!test_bit(ris_idx, msc->ris_idxs)) { + err =3D mpam_ris_create_locked(msc, ris_idx, MPAM_CLASS_UNKNOWN, + 0, 0, GFP_ATOMIC); + if (err) + return ERR_PTR(err); + } + + list_for_each_entry(ris, &msc->ris, msc_list) { + if (ris->ris_idx =3D=3D ris_idx) { + found =3D ris; + break; + } + } + + return found; +} + static int mpam_msc_hw_probe(struct mpam_msc *msc) { u64 idr; - int err; + u16 partid_max; + u8 ris_idx, pmg_max; + struct mpam_msc_ris *ris; =20 lockdep_assert_held(&msc->probe_lock); =20 @@ -551,14 +657,42 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) !=3D MPAM_ARCHITECTURE_V1) { pr_err_once("%s does not match MPAM architecture v1.x\n", dev_name(&msc->pdev->dev)); - err =3D -EIO; - } else { - msc->probed =3D true; - err =3D 0; + mutex_unlock(&msc->part_sel_lock); + return -EIO; } + + idr =3D mpam_msc_read_idr(msc); mutex_unlock(&msc->part_sel_lock); + msc->ris_max =3D FIELD_GET(MPAMF_IDR_RIS_MAX, idr); + + /* Use these values so partid/pmg always starts with a valid value */ + msc->partid_max =3D FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); + msc->pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); + + for (ris_idx =3D 0; ris_idx <=3D msc->ris_max; ris_idx++) { + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris_idx, 0, msc); + idr =3D mpam_msc_read_idr(msc); + mutex_unlock(&msc->part_sel_lock); + + partid_max =3D FIELD_GET(MPAMF_IDR_PARTID_MAX, idr); + pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); + msc->partid_max =3D min(msc->partid_max, partid_max); + msc->pmg_max =3D min(msc->pmg_max, pmg_max); + + ris =3D mpam_get_or_create_ris(msc, ris_idx); + if (IS_ERR(ris)) + return PTR_ERR(ris); + } =20 - return err; + spin_lock(&partid_max_lock); + mpam_partid_max =3D min(mpam_partid_max, msc->partid_max); + mpam_pmg_max =3D min(mpam_pmg_max, msc->pmg_max); + spin_unlock(&partid_max_lock); + + msc->probed =3D true; + + return 0; } =20 static int mpam_cpu_online(unsigned int cpu) @@ -900,9 +1034,18 @@ static struct platform_driver mpam_msc_driver =3D { =20 static void mpam_enable_once(void) { + /* + * Once the cpuhp callbacks have been changed, mpam_partid_max can no + * longer change. + */ + spin_lock(&partid_max_lock); + partid_max_published =3D true; + spin_unlock(&partid_max_lock); + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 - pr_info("MPAM enabled\n"); + printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", + mpam_partid_max + 1, mpam_pmg_max + 1); } =20 /* @@ -972,4 +1115,5 @@ static int __init mpam_msc_driver_init(void) =20 return platform_driver_register(&mpam_msc_driver); } +/* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ subsys_initcall(mpam_msc_driver_init); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a98cca08a2ef..a623f405ddd8 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -50,6 +50,8 @@ struct mpam_msc { */ struct mutex probe_lock; bool probed; + u16 partid_max; + u8 pmg_max; unsigned long ris_idxs[128 / BITS_PER_LONG]; u32 ris_max; =20 @@ -148,6 +150,10 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +/* System wide partid/pmg values */ +extern u16 mpam_partid_max; +extern u8 mpam_pmg_max; + /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); =20 diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h index 406a77be68cb..8af93794c7a2 100644 --- a/include/linux/arm_mpam.h +++ b/include/linux/arm_mpam.h @@ -39,4 +39,18 @@ static inline int acpi_mpam_count_msc(void) { return -EI= NVAL; } int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, enum mpam_class_types type, u8 class_id, int component_id); =20 +/** + * mpam_register_requestor() - Register a requestor with the MPAM driver + * @partid_max: The maximum PARTID value the requestor can generate. + * @pmg_max: The maximum PMG value the requestor can generate. + * + * Registers a requestor with the MPAM driver to ensure the chosen system-= wide + * minimum PARTID and PMG values will allow the requestors features to be = used. + * + * Returns an error if the registration is too late, and a larger PARTID/P= MG + * value has been advertised to user-space. In this case the requestor sho= uld + * not use its MPAM features. Returns 0 on success. + */ +int mpam_register_requestor(u16 partid_max, u8 pmg_max); + #endif /* __LINUX_ARM_MPAM_H */ --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id EBAC12472BF; Fri, 22 Aug 2025 15:35:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876951; cv=none; b=HTO5PZetN2GB2T1+zdjpAakId3avE4Mhb5tUhxd4R/Hlc/d34fIci7cihzx4iJwssfzBFh30aKcE9011j1y2lI3MuPwCmXahbg6HP8bWzRLta7Hujk9FG2vaGXk1JUVWFsbWgxrj+Mok7/7mi3ypd+0nhE1wH1KB4B2JovA1xw0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876951; c=relaxed/simple; bh=0Ze5L8S2Rtxz2WZnVOh9RQPMiP0i+scwI2LdJBUwSaY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Wj0f+xbIT5vx0smKnCHnj75Lky7Iy2OvSOQOauOrtTX2FRAoi9+u5Q8DVwQ4zW/1Cd+qWvgE7Q9rLs0YCQRax/kjR5QgFINNnKktgFBq535hddJOM3eCa3D8fEFVx69H/gYs9nKkDK0R53l52sirWZz78x5S3pK22aCPcElyRQ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 440B515A1; Fri, 22 Aug 2025 08:35:40 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 3759E3F63F; Fri, 22 Aug 2025 08:35:43 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 16/33] arm_mpam: Add helpers for managing the locking around the mon_sel registers Date: Fri, 22 Aug 2025 15:30:31 +0000 Message-Id: <20250822153048.2287-51-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MSC MON_SEL register needs to be accessed from hardirq context by the PMU drivers, making an irqsave spinlock the obvious lock to protect these registers. On systems with SCMI mailboxes it must be able to sleep, meaning a mutex must be used. Clearly these two can't exist at the same time. Add helpers for the MON_SEL locking. The outer lock must be taken in a pre-emptible context before the inner lock can be taken. On systems with SCMI mailboxes where the MON_SEL accesses must sleep - the inner lock will fail to be 'taken' if the caller is unable to sleep. This will allow the PMU driver to fail without having to check the interface type of each MSC. Signed-off-by: James Morse --- drivers/resctrl/mpam_internal.h | 57 ++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a623f405ddd8..c6f087f9fa7d 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -68,10 +68,19 @@ struct mpam_msc { =20 /* * mon_sel_lock protects access to the MSC hardware registers that are - * affeted by MPAMCFG_MON_SEL. + * affected by MPAMCFG_MON_SEL, and the mbwu_state. + * Both the 'inner' and 'outer' must be taken. + * For real MMIO MSC, the outer lock is unnecessary - but keeps the + * code common with: + * Firmware backed MSC need to sleep when accessing the MSC, which + * means some code-paths will always fail. For these MSC the outer + * lock is providing the protection, and the inner lock fails to + * be taken if the task is unable to sleep. + * * If needed, take msc->probe_lock first. */ struct mutex outer_mon_sel_lock; + bool outer_lock_held; raw_spinlock_t inner_mon_sel_lock; unsigned long inner_mon_sel_flags; =20 @@ -81,6 +90,52 @@ struct mpam_msc { struct mpam_garbage garbage; }; =20 +static inline bool __must_check mpam_mon_sel_inner_lock(struct mpam_msc *m= sc) +{ + /* + * The outer lock may be taken by a CPU that then issues an IPI to run + * a helper that takes the inner lock. lockdep can't help us here. + */ + WARN_ON_ONCE(!msc->outer_lock_held); + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) { + raw_spin_lock_irqsave(&msc->inner_mon_sel_lock, msc->inner_mon_sel_flags= ); + return true; + } + + /* Accesses must fail if we are not pre-emptible */ + return !!preemptible(); +} + +static inline void mpam_mon_sel_inner_unlock(struct mpam_msc *msc) +{ + WARN_ON_ONCE(!msc->outer_lock_held); + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) + raw_spin_unlock_irqrestore(&msc->inner_mon_sel_lock, msc->inner_mon_sel_= flags); +} + +static inline void mpam_mon_sel_outer_lock(struct mpam_msc *msc) +{ + mutex_lock(&msc->outer_mon_sel_lock); + msc->outer_lock_held =3D true; +} + +static inline void mpam_mon_sel_outer_unlock(struct mpam_msc *msc) +{ + msc->outer_lock_held =3D false; + mutex_unlock(&msc->outer_mon_sel_lock); +} + +static inline void mpam_mon_sel_lock_held(struct mpam_msc *msc) +{ + WARN_ON_ONCE(!msc->outer_lock_held); + if (msc->iface =3D=3D MPAM_IFACE_MMIO) + lockdep_assert_held_once(&msc->inner_mon_sel_lock); + else + lockdep_assert_preemption_enabled(); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; --=20 2.20.1 From nobody Fri Oct 3 23:02:09 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4832727AC41; Fri, 22 Aug 2025 15:32:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876758; cv=none; b=qHz3wCyO/zt6Nf5MZg/IwwB2lKkw77Z5EzVaNnODzrEii5PaSoR2ULSSi3DEN4kNdbLLyCvo87s5wAiyXoJxJnA0q0m0INEdo4xQ0hnXYkXGeC8a9lz5US37FGYKwYZSTXnDmsvWgzZfWWXqdWxvUE+MNVp74WujuzPqFUoas9E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876758; c=relaxed/simple; bh=0Ze5L8S2Rtxz2WZnVOh9RQPMiP0i+scwI2LdJBUwSaY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sP0OK539y/LYSpgWp6Lw68aqXmGZKp6+S+A3oMf3Lboy9GF6hJ3VRRa0yH6NPbIOq4MOftKYPY1o7NOEbJl4XGpV6I5EBafiir+mbGPml3rdb3G/MW96zpud2ZLxgNYFN84OcyF1vx2Zy51leTgBHnw8Y2afoAV4omqrAqsXLjk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6B7D528C7; Fri, 22 Aug 2025 08:32:28 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 5FD163F63F; Fri, 22 Aug 2025 08:32:31 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 16/33] arm_mpam: Add helpers for managing the locking around the mon_sel registers Date: Fri, 22 Aug 2025 15:29:57 +0000 Message-Id: <20250822153048.2287-17-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The MSC MON_SEL register needs to be accessed from hardirq context by the PMU drivers, making an irqsave spinlock the obvious lock to protect these registers. On systems with SCMI mailboxes it must be able to sleep, meaning a mutex must be used. Clearly these two can't exist at the same time. Add helpers for the MON_SEL locking. The outer lock must be taken in a pre-emptible context before the inner lock can be taken. On systems with SCMI mailboxes where the MON_SEL accesses must sleep - the inner lock will fail to be 'taken' if the caller is unable to sleep. This will allow the PMU driver to fail without having to check the interface type of each MSC. Signed-off-by: James Morse --- drivers/resctrl/mpam_internal.h | 57 ++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a623f405ddd8..c6f087f9fa7d 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -68,10 +68,19 @@ struct mpam_msc { =20 /* * mon_sel_lock protects access to the MSC hardware registers that are - * affeted by MPAMCFG_MON_SEL. + * affected by MPAMCFG_MON_SEL, and the mbwu_state. + * Both the 'inner' and 'outer' must be taken. + * For real MMIO MSC, the outer lock is unnecessary - but keeps the + * code common with: + * Firmware backed MSC need to sleep when accessing the MSC, which + * means some code-paths will always fail. For these MSC the outer + * lock is providing the protection, and the inner lock fails to + * be taken if the task is unable to sleep. + * * If needed, take msc->probe_lock first. */ struct mutex outer_mon_sel_lock; + bool outer_lock_held; raw_spinlock_t inner_mon_sel_lock; unsigned long inner_mon_sel_flags; =20 @@ -81,6 +90,52 @@ struct mpam_msc { struct mpam_garbage garbage; }; =20 +static inline bool __must_check mpam_mon_sel_inner_lock(struct mpam_msc *m= sc) +{ + /* + * The outer lock may be taken by a CPU that then issues an IPI to run + * a helper that takes the inner lock. lockdep can't help us here. + */ + WARN_ON_ONCE(!msc->outer_lock_held); + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) { + raw_spin_lock_irqsave(&msc->inner_mon_sel_lock, msc->inner_mon_sel_flags= ); + return true; + } + + /* Accesses must fail if we are not pre-emptible */ + return !!preemptible(); +} + +static inline void mpam_mon_sel_inner_unlock(struct mpam_msc *msc) +{ + WARN_ON_ONCE(!msc->outer_lock_held); + + if (msc->iface =3D=3D MPAM_IFACE_MMIO) + raw_spin_unlock_irqrestore(&msc->inner_mon_sel_lock, msc->inner_mon_sel_= flags); +} + +static inline void mpam_mon_sel_outer_lock(struct mpam_msc *msc) +{ + mutex_lock(&msc->outer_mon_sel_lock); + msc->outer_lock_held =3D true; +} + +static inline void mpam_mon_sel_outer_unlock(struct mpam_msc *msc) +{ + msc->outer_lock_held =3D false; + mutex_unlock(&msc->outer_mon_sel_lock); +} + +static inline void mpam_mon_sel_lock_held(struct mpam_msc *msc) +{ + WARN_ON_ONCE(!msc->outer_lock_held); + if (msc->iface =3D=3D MPAM_IFACE_MMIO) + lockdep_assert_held_once(&msc->inner_mon_sel_lock); + else + lockdep_assert_preemption_enabled(); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 97D47248F47; Fri, 22 Aug 2025 15:35:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876956; cv=none; b=c09PHEQK3Y3asKl6UTbNEI0P6YTZ/GH1dKU+V4NiEegvBwGCAjU7aXY4NNiSwkPQFIDxJehBWUj7I08KoCj4aFXJ6DkBkMdy49VZUb4nBQjbUEA1HgXI50z63Zy1uazIR8FVfpFsW1eXSHv4VtoHpV8GGxPn8eZ4BaKLpoO04vQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876956; c=relaxed/simple; bh=4vW69mMrJGj5IV1I3AfILIdlQx4eBEAkWU7a3LZSs0U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UJx/MDKTZfk4SJRh4BwfLHNSps3MiHZeodo34TqI89n7e21U6UAo6Ye8/QzbyWkUaP7DkSBdRCmnNGJNBjru1orBBd7ZFrbvXu4yWBMAsKtUWLOnepNAjxFatjZyBax3pYgTJ9O4rxRaKoy5KGVKOjOtkodwi513t2xttq/VnYU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 01BDA2880; Fri, 22 Aug 2025 08:35:46 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C77773F63F; Fri, 22 Aug 2025 08:35:48 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Dave Martin Subject: [PATCH 17/33] arm_mpam: Probe the hardware features resctrl supports Date: Fri, 22 Aug 2025 15:30:32 +0000 Message-Id: <20250822153048.2287-52-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expand the probing support with the control and monitor types we can use with resctrl. CC: Dave Martin Signed-off-by: James Morse --- Changes since RFC: * Made mpam_ris_hw_probe_hw_nrdy() more in C. * Added static assert on features bitmap size. --- drivers/resctrl/mpam_devices.c | 156 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 54 +++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 012e09e80300..290a04f8654f 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -102,7 +102,7 @@ static LLIST_HEAD(mpam_garbage); =20 static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) { - WARN_ON_ONCE(reg > msc->mapped_hwpage_sz); + WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); =20 return readl_relaxed(msc->mapped_hwpage + reg); @@ -131,6 +131,20 @@ static inline void _mpam_write_partsel_reg(struct mpam= _msc *msc, u16 reg, u32 va } #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc= , MPAMCFG_##reg, val) =20 +static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) +{ + mpam_mon_sel_lock_held(msc); + return __mpam_read_reg(msc, reg); +} +#define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##= reg) + +static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u= 32 val) +{ + mpam_mon_sel_lock_held(msc); + __mpam_write_reg(msc, reg, val); +} +#define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc,= MSMON_##reg, val) + static u64 mpam_msc_read_idr(struct mpam_msc *msc) { u64 idr_high =3D 0, idr_low; @@ -643,6 +657,139 @@ static struct mpam_msc_ris *mpam_get_or_create_ris(st= ruct mpam_msc *msc, return found; } =20 +/* + * IHI009A.a has this nugget: "If a monitor does not support automatic beh= aviour + * of NRDY, software can use this bit for any purpose" - so hardware might= not + * implement this - but it isn't RES0. + * + * Try and see what values stick in this bit. If we can write either value, + * its probably not implemented by hardware. + */ +static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris * ris, u32 mon_= reg) +{ + u32 now; + u64 mon_sel; + bool can_set, can_clear; + struct mpam_msc *msc =3D ris->vmsc->msc; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + return false; + + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + _mpam_write_monsel_reg(msc, mon_reg, mon_sel); + + _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); + now =3D _mpam_read_monsel_reg(msc, mon_reg); + can_set =3D now & MSMON___NRDY; + + _mpam_write_monsel_reg(msc, mon_reg, 0); + now =3D _mpam_read_monsel_reg(msc, mon_reg); + can_clear =3D !(now & MSMON___NRDY); + mpam_mon_sel_inner_unlock(msc); + + return (!can_set || !can_clear); +} + +#define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ + _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) + +static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) +{ + int err; + struct mpam_msc *msc =3D ris->vmsc->msc; + struct mpam_props *props =3D &ris->props; + + lockdep_assert_held(&msc->probe_lock); + lockdep_assert_held(&msc->part_sel_lock); + + /* Cache Portion partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { + u32 cpor_features =3D mpam_read_partsel_reg(msc, CPOR_IDR); + + props->cpbm_wd =3D FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); + if (props->cpbm_wd) + mpam_set_feature(mpam_feat_cpor_part, props); + } + + /* Memory bandwidth partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) { + u32 mbw_features =3D mpam_read_partsel_reg(msc, MBW_IDR); + + /* portion bitmap resolution */ + props->mbw_pbm_bits =3D FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features); + if (props->mbw_pbm_bits && + FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features)) + mpam_set_feature(mpam_feat_mbw_part, props); + + props->bwa_wd =3D FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) + mpam_set_feature(mpam_feat_mbw_max, props); + } + + /* Performance Monitoring */ + if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) { + u32 msmon_features =3D mpam_read_partsel_reg(msc, MSMON_IDR); + + /* + * If the firmware max-nrdy-us property is missing, the + * CSU counters can't be used. Should we wait forever? + */ + err =3D device_property_read_u32(&msc->pdev->dev, + "arm,not-ready-us", + &msc->nrdy_usec); + + if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) { + u32 csumonidr; + + csumonidr =3D mpam_read_partsel_reg(msc, CSUMON_IDR); + props->num_csu_mon =3D FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr); + if (props->num_csu_mon) { + bool hw_managed; + + mpam_set_feature(mpam_feat_msmon_csu, props); + + /* Is NRDY hardware managed? */ + mpam_mon_sel_outer_lock(msc); + hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, CSU); + mpam_mon_sel_outer_unlock(msc); + if (hw_managed) + mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); + } + + /* + * Accept the missing firmware property if NRDY appears + * un-implemented. + */ + if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) + pr_err_once("Counters are not usable because not-ready timeout was not= provided by firmware."); + } + if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { + bool hw_managed; + u32 mbwumonidr =3D mpam_read_partsel_reg(msc, MBWUMON_IDR); + + props->num_mbwu_mon =3D FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumonidr= ); + if (props->num_mbwu_mon) + mpam_set_feature(mpam_feat_msmon_mbwu, props); + + if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumonidr)) + mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props); + + /* Is NRDY hardware managed? */ + mpam_mon_sel_outer_lock(msc); + hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, MBWU); + mpam_mon_sel_outer_unlock(msc); + if (hw_managed) + mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); + + /* + * Don't warn about any missing firmware property for + * MBWU NRDY - it doesn't make any sense! + */ + } + } +} + static int mpam_msc_hw_probe(struct mpam_msc *msc) { u64 idr; @@ -663,6 +810,7 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) =20 idr =3D mpam_msc_read_idr(msc); mutex_unlock(&msc->part_sel_lock); + msc->ris_max =3D FIELD_GET(MPAMF_IDR_RIS_MAX, idr); =20 /* Use these values so partid/pmg always starts with a valid value */ @@ -683,6 +831,12 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) ris =3D mpam_get_or_create_ris(msc, ris_idx); if (IS_ERR(ris)) return PTR_ERR(ris); + ris->idr =3D idr; + + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris_idx, 0, msc); + mpam_ris_hw_probe(ris); + mutex_unlock(&msc->part_sel_lock); } =20 spin_lock(&partid_max_lock); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index c6f087f9fa7d..9f6cd4a68cce 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -136,6 +136,56 @@ static inline void mpam_mon_sel_lock_held(struct mpam_= msc *msc) lockdep_assert_preemption_enabled(); } =20 +/* + * When we compact the supported features, we don't care what they are. + * Storing them as a bitmap makes life easy. + */ +typedef u16 mpam_features_t; + +/* Bits for mpam_features_t */ +enum mpam_device_features { + mpam_feat_ccap_part =3D 0, + mpam_feat_cpor_part, + mpam_feat_mbw_part, + mpam_feat_mbw_min, + mpam_feat_mbw_max, + mpam_feat_mbw_prop, + mpam_feat_msmon, + mpam_feat_msmon_csu, + mpam_feat_msmon_csu_capture, + mpam_feat_msmon_csu_hw_nrdy, + mpam_feat_msmon_mbwu, + mpam_feat_msmon_mbwu_capture, + mpam_feat_msmon_mbwu_rwbw, + mpam_feat_msmon_mbwu_hw_nrdy, + mpam_feat_msmon_capt, + MPAM_FEATURE_LAST, +}; +static_assert(BITS_PER_TYPE(mpam_features_t) >=3D MPAM_FEATURE_LAST); +#define MPAM_ALL_FEATURES ((1 << MPAM_FEATURE_LAST) - 1) + +struct mpam_props { + mpam_features_t features; + + u16 cpbm_wd; + u16 mbw_pbm_bits; + u16 bwa_wd; + u16 num_csu_mon; + u16 num_mbwu_mon; +}; + +static inline bool mpam_has_feature(enum mpam_device_features feat, + struct mpam_props *props) +{ + return (1 << feat) & props->features; +} + +static inline void mpam_set_feature(enum mpam_device_features feat, + struct mpam_props *props) +{ + props->features |=3D (1 << feat); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; @@ -175,6 +225,8 @@ struct mpam_vmsc { /* mpam_msc_ris in this vmsc */ struct list_head ris; =20 + struct mpam_props props; + /* All RIS in this vMSC are members of this MSC */ struct mpam_msc *msc; =20 @@ -186,6 +238,8 @@ struct mpam_vmsc { =20 struct mpam_msc_ris { u8 ris_idx; + u64 idr; + struct mpam_props props; =20 cpumask_t affinity; =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0BCAC248F42; Fri, 22 Aug 2025 15:32:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876764; cv=none; b=cnkO3A8n9Jj20tR0ZvUgucJ+0kOGuWLYj1/P1dZr3yWvgLIQnXrZur5A1NkGrbun0Gbtmx3leFxLLrfYlay/E8qB1Z7Wtk3xYp07W3T4dv2vG3QWzZtW074VRS79k209bonox2+jJp6+LMe5dahOulXbKjT8l7plBnM7nOkXNJk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876764; c=relaxed/simple; bh=4vW69mMrJGj5IV1I3AfILIdlQx4eBEAkWU7a3LZSs0U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=c2L5mnNhqwZIaIKD+ssTTO5P2QZ9vbaNT/BlUmtNh/lu5dJ2+dzwPJuk4DzKcDxtz8NrUGMuBc05ucBZVs5+aawGQ/UgACoV5Y5yO5hpOmW4QqZrESKP8Am7HpYFs9aml/RtfEUqufOOCSVvC0JmiZF590/8YdGVFI2Fchbs3o8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2CCBB2C46; Fri, 22 Aug 2025 08:32:34 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id F13573F63F; Fri, 22 Aug 2025 08:32:36 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Dave Martin Subject: [PATCH 17/33] arm_mpam: Probe the hardware features resctrl supports Date: Fri, 22 Aug 2025 15:29:58 +0000 Message-Id: <20250822153048.2287-18-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expand the probing support with the control and monitor types we can use with resctrl. CC: Dave Martin Signed-off-by: James Morse --- Changes since RFC: * Made mpam_ris_hw_probe_hw_nrdy() more in C. * Added static assert on features bitmap size. --- drivers/resctrl/mpam_devices.c | 156 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 54 +++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 012e09e80300..290a04f8654f 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -102,7 +102,7 @@ static LLIST_HEAD(mpam_garbage); =20 static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) { - WARN_ON_ONCE(reg > msc->mapped_hwpage_sz); + WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); =20 return readl_relaxed(msc->mapped_hwpage + reg); @@ -131,6 +131,20 @@ static inline void _mpam_write_partsel_reg(struct mpam= _msc *msc, u16 reg, u32 va } #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc= , MPAMCFG_##reg, val) =20 +static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) +{ + mpam_mon_sel_lock_held(msc); + return __mpam_read_reg(msc, reg); +} +#define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##= reg) + +static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u= 32 val) +{ + mpam_mon_sel_lock_held(msc); + __mpam_write_reg(msc, reg, val); +} +#define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc,= MSMON_##reg, val) + static u64 mpam_msc_read_idr(struct mpam_msc *msc) { u64 idr_high =3D 0, idr_low; @@ -643,6 +657,139 @@ static struct mpam_msc_ris *mpam_get_or_create_ris(st= ruct mpam_msc *msc, return found; } =20 +/* + * IHI009A.a has this nugget: "If a monitor does not support automatic beh= aviour + * of NRDY, software can use this bit for any purpose" - so hardware might= not + * implement this - but it isn't RES0. + * + * Try and see what values stick in this bit. If we can write either value, + * its probably not implemented by hardware. + */ +static bool _mpam_ris_hw_probe_hw_nrdy(struct mpam_msc_ris * ris, u32 mon_= reg) +{ + u32 now; + u64 mon_sel; + bool can_set, can_clear; + struct mpam_msc *msc =3D ris->vmsc->msc; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + return false; + + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + _mpam_write_monsel_reg(msc, mon_reg, mon_sel); + + _mpam_write_monsel_reg(msc, mon_reg, MSMON___NRDY); + now =3D _mpam_read_monsel_reg(msc, mon_reg); + can_set =3D now & MSMON___NRDY; + + _mpam_write_monsel_reg(msc, mon_reg, 0); + now =3D _mpam_read_monsel_reg(msc, mon_reg); + can_clear =3D !(now & MSMON___NRDY); + mpam_mon_sel_inner_unlock(msc); + + return (!can_set || !can_clear); +} + +#define mpam_ris_hw_probe_hw_nrdy(_ris, _mon_reg) \ + _mpam_ris_hw_probe_hw_nrdy(_ris, MSMON_##_mon_reg) + +static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) +{ + int err; + struct mpam_msc *msc =3D ris->vmsc->msc; + struct mpam_props *props =3D &ris->props; + + lockdep_assert_held(&msc->probe_lock); + lockdep_assert_held(&msc->part_sel_lock); + + /* Cache Portion partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { + u32 cpor_features =3D mpam_read_partsel_reg(msc, CPOR_IDR); + + props->cpbm_wd =3D FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features); + if (props->cpbm_wd) + mpam_set_feature(mpam_feat_cpor_part, props); + } + + /* Memory bandwidth partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) { + u32 mbw_features =3D mpam_read_partsel_reg(msc, MBW_IDR); + + /* portion bitmap resolution */ + props->mbw_pbm_bits =3D FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features); + if (props->mbw_pbm_bits && + FIELD_GET(MPAMF_MBW_IDR_HAS_PBM, mbw_features)) + mpam_set_feature(mpam_feat_mbw_part, props); + + props->bwa_wd =3D FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) + mpam_set_feature(mpam_feat_mbw_max, props); + } + + /* Performance Monitoring */ + if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) { + u32 msmon_features =3D mpam_read_partsel_reg(msc, MSMON_IDR); + + /* + * If the firmware max-nrdy-us property is missing, the + * CSU counters can't be used. Should we wait forever? + */ + err =3D device_property_read_u32(&msc->pdev->dev, + "arm,not-ready-us", + &msc->nrdy_usec); + + if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) { + u32 csumonidr; + + csumonidr =3D mpam_read_partsel_reg(msc, CSUMON_IDR); + props->num_csu_mon =3D FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr); + if (props->num_csu_mon) { + bool hw_managed; + + mpam_set_feature(mpam_feat_msmon_csu, props); + + /* Is NRDY hardware managed? */ + mpam_mon_sel_outer_lock(msc); + hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, CSU); + mpam_mon_sel_outer_unlock(msc); + if (hw_managed) + mpam_set_feature(mpam_feat_msmon_csu_hw_nrdy, props); + } + + /* + * Accept the missing firmware property if NRDY appears + * un-implemented. + */ + if (err && mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, props)) + pr_err_once("Counters are not usable because not-ready timeout was not= provided by firmware."); + } + if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { + bool hw_managed; + u32 mbwumonidr =3D mpam_read_partsel_reg(msc, MBWUMON_IDR); + + props->num_mbwu_mon =3D FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumonidr= ); + if (props->num_mbwu_mon) + mpam_set_feature(mpam_feat_msmon_mbwu, props); + + if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumonidr)) + mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props); + + /* Is NRDY hardware managed? */ + mpam_mon_sel_outer_lock(msc); + hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, MBWU); + mpam_mon_sel_outer_unlock(msc); + if (hw_managed) + mpam_set_feature(mpam_feat_msmon_mbwu_hw_nrdy, props); + + /* + * Don't warn about any missing firmware property for + * MBWU NRDY - it doesn't make any sense! + */ + } + } +} + static int mpam_msc_hw_probe(struct mpam_msc *msc) { u64 idr; @@ -663,6 +810,7 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) =20 idr =3D mpam_msc_read_idr(msc); mutex_unlock(&msc->part_sel_lock); + msc->ris_max =3D FIELD_GET(MPAMF_IDR_RIS_MAX, idr); =20 /* Use these values so partid/pmg always starts with a valid value */ @@ -683,6 +831,12 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) ris =3D mpam_get_or_create_ris(msc, ris_idx); if (IS_ERR(ris)) return PTR_ERR(ris); + ris->idr =3D idr; + + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris_idx, 0, msc); + mpam_ris_hw_probe(ris); + mutex_unlock(&msc->part_sel_lock); } =20 spin_lock(&partid_max_lock); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index c6f087f9fa7d..9f6cd4a68cce 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -136,6 +136,56 @@ static inline void mpam_mon_sel_lock_held(struct mpam_= msc *msc) lockdep_assert_preemption_enabled(); } =20 +/* + * When we compact the supported features, we don't care what they are. + * Storing them as a bitmap makes life easy. + */ +typedef u16 mpam_features_t; + +/* Bits for mpam_features_t */ +enum mpam_device_features { + mpam_feat_ccap_part =3D 0, + mpam_feat_cpor_part, + mpam_feat_mbw_part, + mpam_feat_mbw_min, + mpam_feat_mbw_max, + mpam_feat_mbw_prop, + mpam_feat_msmon, + mpam_feat_msmon_csu, + mpam_feat_msmon_csu_capture, + mpam_feat_msmon_csu_hw_nrdy, + mpam_feat_msmon_mbwu, + mpam_feat_msmon_mbwu_capture, + mpam_feat_msmon_mbwu_rwbw, + mpam_feat_msmon_mbwu_hw_nrdy, + mpam_feat_msmon_capt, + MPAM_FEATURE_LAST, +}; +static_assert(BITS_PER_TYPE(mpam_features_t) >=3D MPAM_FEATURE_LAST); +#define MPAM_ALL_FEATURES ((1 << MPAM_FEATURE_LAST) - 1) + +struct mpam_props { + mpam_features_t features; + + u16 cpbm_wd; + u16 mbw_pbm_bits; + u16 bwa_wd; + u16 num_csu_mon; + u16 num_mbwu_mon; +}; + +static inline bool mpam_has_feature(enum mpam_device_features feat, + struct mpam_props *props) +{ + return (1 << feat) & props->features; +} + +static inline void mpam_set_feature(enum mpam_device_features feat, + struct mpam_props *props) +{ + props->features |=3D (1 << feat); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; @@ -175,6 +225,8 @@ struct mpam_vmsc { /* mpam_msc_ris in this vmsc */ struct list_head ris; =20 + struct mpam_props props; + /* All RIS in this vMSC are members of this MSC */ struct mpam_msc *msc; =20 @@ -186,6 +238,8 @@ struct mpam_vmsc { =20 struct mpam_msc_ris { u8 ris_idx; + u64 idr; + struct mpam_props props; =20 cpumask_t affinity; =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3B82A2857CF; Fri, 22 Aug 2025 15:36:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876961; cv=none; b=NZa9qe1nL6EVaq6cGC0ZgN5mnigcczQQOELjl3bCFG44rTZiXFzv/XGpsq3KgJBgaf8tAr++8k31DX7t8G5/AwF13C5YE5E8K+k3erCZDsb2hSR0idIyZ+j0NpTcz+8EYQ+P7eFqS+qXyAc2AYYAY5GbF2MNpYOweLCGFGJq7E8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876961; c=relaxed/simple; bh=qLpxKKQRoeowzKRU4A01FJgGIXdUmV1qXXFSySCrG7A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=odwhgBhYT5obE8dNrFcZv1YiCIiGy3Fx4SPwKZeC9H39Dh4JP+Upl087QrdU72rNpz/vtpeBKaxa4Jeh3+QjzGrb4AgbOLCApGofImEaVjYzLPzD7SEJwCWOS5AEjXssPbV94bP5Np3hhfBd+VkXKDr4pfWtWsPhXqxRjbCmQXk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 8FF6C15A1; Fri, 22 Aug 2025 08:35:51 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 83B103F63F; Fri, 22 Aug 2025 08:35:54 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 18/33] arm_mpam: Merge supported features during mpam_enable() into mpam_class Date: Fri, 22 Aug 2025 15:30:33 +0000 Message-Id: <20250822153048.2287-53-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" To make a decision about whether to expose an mpam class as a resctrl resource we need to know its overall supported features and properties. Once we've probed all the resources, we can walk the tree and produce overall values by merging the bitmaps. This eliminates features that are only supported by some MSC that make up a component or class. If bitmap properties are mismatched within a component we cannot support the mismatched feature. Care has to be taken as vMSC may hold mismatched RIS. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 215 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 8 ++ 2 files changed, 223 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 290a04f8654f..bb62de6d3847 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1186,8 +1186,223 @@ static struct platform_driver mpam_msc_driver =3D { .remove =3D mpam_msc_drv_remove, }; =20 +/* Any of these features mean the BWA_WD field is valid. */ +static bool mpam_has_bwa_wd_feature(struct mpam_props *props) +{ + if (mpam_has_feature(mpam_feat_mbw_min, props)) + return true; + if (mpam_has_feature(mpam_feat_mbw_max, props)) + return true; + if (mpam_has_feature(mpam_feat_mbw_prop, props)) + return true; + return false; +} + +#define MISMATCHED_HELPER(parent, child, helper, field, alias) \ + helper(parent) && \ + ((helper(child) && (parent)->field !=3D (child)->field) || \ + (!helper(child) && !(alias))) + +#define MISMATCHED_FEAT(parent, child, feat, field, alias) \ + mpam_has_feature((feat), (parent)) && \ + ((mpam_has_feature((feat), (child)) && (parent)->field !=3D (child)->fiel= d) || \ + (!mpam_has_feature((feat), (child)) && !(alias))) + +#define CAN_MERGE_FEAT(parent, child, feat, alias) \ + (alias) && !mpam_has_feature((feat), (parent)) && \ + mpam_has_feature((feat), (child)) + +/* + * Combine two props fields. + * If this is for controls that alias the same resource, it is safe to just + * copy the values over. If two aliasing controls implement the same scheme + * a safe value must be picked. + * For non-aliasing controls, these control different resources, and the + * resulting safe value must be compatible with both. When merging values = in + * the tree, all the aliasing resources must be handled first. + * On mismatch, parent is modified. + */ +static void __props_mismatch(struct mpam_props *parent, + struct mpam_props *child, bool alias) +{ + if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) { + parent->cpbm_wd =3D child->cpbm_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part, + cpbm_wd, alias)) { + pr_debug("%s cleared cpor_part\n", __func__); + mpam_clear_feature(mpam_feat_cpor_part, &parent->features); + parent->cpbm_wd =3D 0; + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) { + parent->mbw_pbm_bits =3D child->mbw_pbm_bits; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part, + mbw_pbm_bits, alias)) { + pr_debug("%s cleared mbw_part\n", __func__); + mpam_clear_feature(mpam_feat_mbw_part, &parent->features); + parent->mbw_pbm_bits =3D 0; + } + + /* bwa_wd is a count of bits, fewer bits means less precision */ + if (alias && !mpam_has_bwa_wd_feature(parent) && mpam_has_bwa_wd_feature(= child)) { + parent->bwa_wd =3D child->bwa_wd; + } else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature, + bwa_wd, alias)) { + pr_debug("%s took the min bwa_wd\n", __func__); + parent->bwa_wd =3D min(parent->bwa_wd, child->bwa_wd); + } + + /* For num properties, take the minimum */ + if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { + parent->num_csu_mon =3D child->num_csu_mon; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu, + num_csu_mon, alias)) { + pr_debug("%s took the min num_csu_mon\n", __func__); + parent->num_csu_mon =3D min(parent->num_csu_mon, child->num_csu_mon); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) { + parent->num_mbwu_mon =3D child->num_mbwu_mon; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu, + num_mbwu_mon, alias)) { + pr_debug("%s took the min num_mbwu_mon\n", __func__); + parent->num_mbwu_mon =3D min(parent->num_mbwu_mon, child->num_mbwu_mon); + } + + if (alias) { + /* Merge features for aliased resources */ + parent->features |=3D child->features; + } else { + /* Clear missing features for non aliasing */ + parent->features &=3D child->features; + } +} + +/* + * If a vmsc doesn't match class feature/configuration, do the right thing= (tm). + * For 'num' properties we can just take the minimum. + * For properties where the mismatched unused bits would make a difference= , we + * nobble the class feature, as we can't configure all the resources. + * e.g. The L3 cache is composed of two resources with 13 and 17 portion + * bitmaps respectively. + */ +static void +__class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc) +{ + struct mpam_props *cprops =3D &class->props; + struct mpam_props *vprops =3D &vmsc->props; + + lockdep_assert_held(&mpam_list_lock); /* we modify class */ + + pr_debug("%s: Merging features for class:0x%lx &=3D vmsc:0x%lx\n", + dev_name(&vmsc->msc->pdev->dev), + (long)cprops->features, (long)vprops->features); + + /* Take the safe value for any common features */ + __props_mismatch(cprops, vprops, false); +} + +static void +__vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris) +{ + struct mpam_props *rprops =3D &ris->props; + struct mpam_props *vprops =3D &vmsc->props; + + lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */ + + pr_debug("%s: Merging features for vmsc:0x%lx |=3D ris:0x%lx\n", + dev_name(&vmsc->msc->pdev->dev), + (long)vprops->features, (long)rprops->features); + + /* + * Merge mismatched features - Copy any features that aren't common, + * but take the safe value for any common features. + */ + __props_mismatch(vprops, rprops, true); +} + +/* + * Copy the first component's first vMSC's properties and features to the + * class. __class_props_mismatch() will remove conflicts. + * It is not possible to have a class with no components, or a component w= ith + * no resources. The vMSC properties have already been built. + */ +static void mpam_enable_init_class_features(struct mpam_class *class) +{ + struct mpam_vmsc *vmsc; + struct mpam_component *comp; + + comp =3D list_first_entry_or_null(&class->components, + struct mpam_component, class_list); + if (WARN_ON(!comp)) + return; + + vmsc =3D list_first_entry_or_null(&comp->vmsc, + struct mpam_vmsc, comp_list); + if (WARN_ON(!vmsc)) + return; + + class->props =3D vmsc->props; +} + +static void mpam_enable_merge_vmsc_features(struct mpam_component *comp) +{ + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct mpam_class *class =3D comp->class; + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + __vmsc_props_mismatch(vmsc, ris); + class->nrdy_usec =3D max(class->nrdy_usec, + vmsc->msc->nrdy_usec); + } + } +} + +static void mpam_enable_merge_class_features(struct mpam_component *comp) +{ + struct mpam_vmsc *vmsc; + struct mpam_class *class =3D comp->class; + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) + __class_props_mismatch(class, vmsc); +} + +/* + * Merge all the common resource features into class. + * vmsc features are bitwise-or'd together, this must be done first. + * Next the class features are the bitwise-and of all the vmsc features. + * Other features are the min/max as appropriate. + * + * To avoid walking the whole tree twice, the class->nrdy_usec property is + * updated when working with the vmsc as it is a max(), and doesn't need + * initialising first. + */ +static void mpam_enable_merge_features(struct list_head *all_classes_list) +{ + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, all_classes_list, classes_list) { + list_for_each_entry(comp, &class->components, class_list) + mpam_enable_merge_vmsc_features(comp); + + mpam_enable_init_class_features(class); + + list_for_each_entry(comp, &class->components, class_list) + mpam_enable_merge_class_features(comp); + } +} + static void mpam_enable_once(void) { + mutex_lock(&mpam_list_lock); + mpam_enable_merge_features(&mpam_classes); + mutex_unlock(&mpam_list_lock); + /* * Once the cpuhp callbacks have been changed, mpam_partid_max can no * longer change. diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9f6cd4a68cce..a2b0ff411138 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -186,12 +186,20 @@ static inline void mpam_set_feature(enum mpam_device_= features feat, props->features |=3D (1 << feat); } =20 +static inline void mpam_clear_feature(enum mpam_device_features feat, + mpam_features_t *supported) +{ + *supported &=3D ~(1 << feat); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; =20 cpumask_t affinity; =20 + struct mpam_props props; + u32 nrdy_usec; u8 level; enum mpam_class_types type; =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 979C0285C80; Fri, 22 Aug 2025 15:32:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876770; cv=none; b=pBoG1FyBA5xEQnF792yQlTqu6yNbHq32ZvkVZG+BryTEiRCCIt1EW/wgrWoqeI2pMCvMZJrIn4rLSBx5Dey6OaYFg80P4SSmyXWLLF5RgVzvAAOUnGJkjrRhyRlZ3XX3THVHc2zXfUQ2ZITL6lm/pYoHTgDzPx0I9M0GQ4vm+Qg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876770; c=relaxed/simple; bh=qLpxKKQRoeowzKRU4A01FJgGIXdUmV1qXXFSySCrG7A=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Ud6kCwBuSZrkVq3R3BolSspzIB+yop6EfHIxsfgcsv6nxFkJsQix6RfkBC91det12h+WtV+PgzJdq570Iue9gpDYpRCJjwXqaLZ+LPvIvL4R3SmhYElYmDNseYCU0lCeaCwGBI26nnyrhLzGd+QmhMI+2kTxHZ2RH9uV4C/d0wc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BE32D15A1; Fri, 22 Aug 2025 08:32:39 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id AF83C3F63F; Fri, 22 Aug 2025 08:32:42 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 18/33] arm_mpam: Merge supported features during mpam_enable() into mpam_class Date: Fri, 22 Aug 2025 15:29:59 +0000 Message-Id: <20250822153048.2287-19-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" To make a decision about whether to expose an mpam class as a resctrl resource we need to know its overall supported features and properties. Once we've probed all the resources, we can walk the tree and produce overall values by merging the bitmaps. This eliminates features that are only supported by some MSC that make up a component or class. If bitmap properties are mismatched within a component we cannot support the mismatched feature. Care has to be taken as vMSC may hold mismatched RIS. Signed-off-by: James Morse Reviewed-by: Ben Horgan --- drivers/resctrl/mpam_devices.c | 215 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 8 ++ 2 files changed, 223 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 290a04f8654f..bb62de6d3847 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1186,8 +1186,223 @@ static struct platform_driver mpam_msc_driver =3D { .remove =3D mpam_msc_drv_remove, }; =20 +/* Any of these features mean the BWA_WD field is valid. */ +static bool mpam_has_bwa_wd_feature(struct mpam_props *props) +{ + if (mpam_has_feature(mpam_feat_mbw_min, props)) + return true; + if (mpam_has_feature(mpam_feat_mbw_max, props)) + return true; + if (mpam_has_feature(mpam_feat_mbw_prop, props)) + return true; + return false; +} + +#define MISMATCHED_HELPER(parent, child, helper, field, alias) \ + helper(parent) && \ + ((helper(child) && (parent)->field !=3D (child)->field) || \ + (!helper(child) && !(alias))) + +#define MISMATCHED_FEAT(parent, child, feat, field, alias) \ + mpam_has_feature((feat), (parent)) && \ + ((mpam_has_feature((feat), (child)) && (parent)->field !=3D (child)->fiel= d) || \ + (!mpam_has_feature((feat), (child)) && !(alias))) + +#define CAN_MERGE_FEAT(parent, child, feat, alias) \ + (alias) && !mpam_has_feature((feat), (parent)) && \ + mpam_has_feature((feat), (child)) + +/* + * Combine two props fields. + * If this is for controls that alias the same resource, it is safe to just + * copy the values over. If two aliasing controls implement the same scheme + * a safe value must be picked. + * For non-aliasing controls, these control different resources, and the + * resulting safe value must be compatible with both. When merging values = in + * the tree, all the aliasing resources must be handled first. + * On mismatch, parent is modified. + */ +static void __props_mismatch(struct mpam_props *parent, + struct mpam_props *child, bool alias) +{ + if (CAN_MERGE_FEAT(parent, child, mpam_feat_cpor_part, alias)) { + parent->cpbm_wd =3D child->cpbm_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cpor_part, + cpbm_wd, alias)) { + pr_debug("%s cleared cpor_part\n", __func__); + mpam_clear_feature(mpam_feat_cpor_part, &parent->features); + parent->cpbm_wd =3D 0; + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_mbw_part, alias)) { + parent->mbw_pbm_bits =3D child->mbw_pbm_bits; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_mbw_part, + mbw_pbm_bits, alias)) { + pr_debug("%s cleared mbw_part\n", __func__); + mpam_clear_feature(mpam_feat_mbw_part, &parent->features); + parent->mbw_pbm_bits =3D 0; + } + + /* bwa_wd is a count of bits, fewer bits means less precision */ + if (alias && !mpam_has_bwa_wd_feature(parent) && mpam_has_bwa_wd_feature(= child)) { + parent->bwa_wd =3D child->bwa_wd; + } else if (MISMATCHED_HELPER(parent, child, mpam_has_bwa_wd_feature, + bwa_wd, alias)) { + pr_debug("%s took the min bwa_wd\n", __func__); + parent->bwa_wd =3D min(parent->bwa_wd, child->bwa_wd); + } + + /* For num properties, take the minimum */ + if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { + parent->num_csu_mon =3D child->num_csu_mon; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_csu, + num_csu_mon, alias)) { + pr_debug("%s took the min num_csu_mon\n", __func__); + parent->num_csu_mon =3D min(parent->num_csu_mon, child->num_csu_mon); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_mbwu, alias)) { + parent->num_mbwu_mon =3D child->num_mbwu_mon; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_msmon_mbwu, + num_mbwu_mon, alias)) { + pr_debug("%s took the min num_mbwu_mon\n", __func__); + parent->num_mbwu_mon =3D min(parent->num_mbwu_mon, child->num_mbwu_mon); + } + + if (alias) { + /* Merge features for aliased resources */ + parent->features |=3D child->features; + } else { + /* Clear missing features for non aliasing */ + parent->features &=3D child->features; + } +} + +/* + * If a vmsc doesn't match class feature/configuration, do the right thing= (tm). + * For 'num' properties we can just take the minimum. + * For properties where the mismatched unused bits would make a difference= , we + * nobble the class feature, as we can't configure all the resources. + * e.g. The L3 cache is composed of two resources with 13 and 17 portion + * bitmaps respectively. + */ +static void +__class_props_mismatch(struct mpam_class *class, struct mpam_vmsc *vmsc) +{ + struct mpam_props *cprops =3D &class->props; + struct mpam_props *vprops =3D &vmsc->props; + + lockdep_assert_held(&mpam_list_lock); /* we modify class */ + + pr_debug("%s: Merging features for class:0x%lx &=3D vmsc:0x%lx\n", + dev_name(&vmsc->msc->pdev->dev), + (long)cprops->features, (long)vprops->features); + + /* Take the safe value for any common features */ + __props_mismatch(cprops, vprops, false); +} + +static void +__vmsc_props_mismatch(struct mpam_vmsc *vmsc, struct mpam_msc_ris *ris) +{ + struct mpam_props *rprops =3D &ris->props; + struct mpam_props *vprops =3D &vmsc->props; + + lockdep_assert_held(&mpam_list_lock); /* we modify vmsc */ + + pr_debug("%s: Merging features for vmsc:0x%lx |=3D ris:0x%lx\n", + dev_name(&vmsc->msc->pdev->dev), + (long)vprops->features, (long)rprops->features); + + /* + * Merge mismatched features - Copy any features that aren't common, + * but take the safe value for any common features. + */ + __props_mismatch(vprops, rprops, true); +} + +/* + * Copy the first component's first vMSC's properties and features to the + * class. __class_props_mismatch() will remove conflicts. + * It is not possible to have a class with no components, or a component w= ith + * no resources. The vMSC properties have already been built. + */ +static void mpam_enable_init_class_features(struct mpam_class *class) +{ + struct mpam_vmsc *vmsc; + struct mpam_component *comp; + + comp =3D list_first_entry_or_null(&class->components, + struct mpam_component, class_list); + if (WARN_ON(!comp)) + return; + + vmsc =3D list_first_entry_or_null(&comp->vmsc, + struct mpam_vmsc, comp_list); + if (WARN_ON(!vmsc)) + return; + + class->props =3D vmsc->props; +} + +static void mpam_enable_merge_vmsc_features(struct mpam_component *comp) +{ + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct mpam_class *class =3D comp->class; + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + __vmsc_props_mismatch(vmsc, ris); + class->nrdy_usec =3D max(class->nrdy_usec, + vmsc->msc->nrdy_usec); + } + } +} + +static void mpam_enable_merge_class_features(struct mpam_component *comp) +{ + struct mpam_vmsc *vmsc; + struct mpam_class *class =3D comp->class; + + list_for_each_entry(vmsc, &comp->vmsc, comp_list) + __class_props_mismatch(class, vmsc); +} + +/* + * Merge all the common resource features into class. + * vmsc features are bitwise-or'd together, this must be done first. + * Next the class features are the bitwise-and of all the vmsc features. + * Other features are the min/max as appropriate. + * + * To avoid walking the whole tree twice, the class->nrdy_usec property is + * updated when working with the vmsc as it is a max(), and doesn't need + * initialising first. + */ +static void mpam_enable_merge_features(struct list_head *all_classes_list) +{ + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, all_classes_list, classes_list) { + list_for_each_entry(comp, &class->components, class_list) + mpam_enable_merge_vmsc_features(comp); + + mpam_enable_init_class_features(class); + + list_for_each_entry(comp, &class->components, class_list) + mpam_enable_merge_class_features(comp); + } +} + static void mpam_enable_once(void) { + mutex_lock(&mpam_list_lock); + mpam_enable_merge_features(&mpam_classes); + mutex_unlock(&mpam_list_lock); + /* * Once the cpuhp callbacks have been changed, mpam_partid_max can no * longer change. diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9f6cd4a68cce..a2b0ff411138 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -186,12 +186,20 @@ static inline void mpam_set_feature(enum mpam_device_= features feat, props->features |=3D (1 << feat); } =20 +static inline void mpam_clear_feature(enum mpam_device_features feat, + mpam_features_t *supported) +{ + *supported &=3D ~(1 << feat); +} + struct mpam_class { /* mpam_components in this class */ struct list_head components; =20 cpumask_t affinity; =20 + struct mpam_props props; + u32 nrdy_usec; u8 level; enum mpam_class_types type; =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2C91C233712; Fri, 22 Aug 2025 15:32:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876775; cv=none; b=Ba9MWCuMJhK3We/ndSES7vKiYZEv+o1BD5EI4M/Kca7KtwwE8t8QoGqxBdrCmqPAy1aE/BnlpGiGbwr9FGN4pot1ZoGfdFN7UOBHYdjlmAdMMBeScq1lsnSSUqxjwev2JglAfHM0fljQbeRvQUCnpYHgIJ6JB9MOnpcYslBIT7g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876775; c=relaxed/simple; bh=WMbYLx7wftmnKm+gAYMtfmtcXYgZ093690PaIbCtmeY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RLWMW2pLXuSIJ4qLNI3C0xNUyN4Vt6C/yy/+y6pEmbebICivIYu3zMOpQTdPbS3Hu2q6ueGIW9mW7Pc/hmr2WKk7a1CU3L3YDxTQLzo75hUsrGLKXa+XI3nXzsPWreAyr5372x3HbkByO2sTyp3VZAmcJaEcclINDoDhz8tMtb8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 8310F27DD; Fri, 22 Aug 2025 08:32:45 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4C9863F63F; Fri, 22 Aug 2025 08:32:48 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew Subject: [PATCH 19/33] arm_mpam: Reset MSC controls from cpu hp callbacks Date: Fri, 22 Aug 2025 15:30:00 +0000 Message-Id: <20250822153048.2287-20-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a CPU comes online, it may bring a newly accessible MSC with it. Only the default partid has its value reset by hardware, and even then the MSC might not have been reset since its config was previously dirtyied. e.g. Kexec. Any in-use partid must have its configuration restored, or reset. In-use partids may be held in caches and evicted later. MSC are also reset when CPUs are taken offline to cover cases where firmware doesn't reset the MSC over reboot using UEFI, or kexec where there is no firmware involvement. If the configuration for a RIS has not been touched since it was brought online, it does not need resetting again. To reset, write the maximum values for all discovered controls. CC: Rohit Mathew Signed-off-by: James Morse --- Changes since RFC: * Last bitmap write will always be non-zero. * Dropped READ_ONCE() - teh value can no longer change. --- drivers/resctrl/mpam_devices.c | 121 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 8 +++ 2 files changed, 129 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index bb62de6d3847..c1f01dd748ad 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -849,8 +850,115 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) return 0; } =20 +static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) +{ + u32 num_words, msb; + u32 bm =3D ~0; + int i; + + lockdep_assert_held(&msc->part_sel_lock); + + if (wd =3D=3D 0) + return; + + /* + * Write all ~0 to all but the last 32bit-word, which may + * have fewer bits... + */ + num_words =3D DIV_ROUND_UP(wd, 32); + for (i =3D 0; i < num_words - 1; i++, reg +=3D sizeof(bm)) + __mpam_write_reg(msc, reg, bm); + + /* + * ....and then the last (maybe) partial 32bit word. When wd is a + * multiple of 32, msb should be 31 to write a full 32bit word. + */ + msb =3D (wd - 1) % 32; + bm =3D GENMASK(msb, 0); + __mpam_write_reg(msc, reg, bm); +} + +static void mpam_reset_ris_partid(struct mpam_msc_ris *ris, u16 partid) +{ + u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; + struct mpam_msc *msc =3D ris->vmsc->msc; + struct mpam_props *rprops =3D &ris->props; + + mpam_assert_srcu_read_lock_held(); + + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris->ris_idx, partid, msc); + + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) + mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); + + if (mpam_has_feature(mpam_feat_mbw_part, rprops)) + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); + + if (mpam_has_feature(mpam_feat_mbw_min, rprops)) + mpam_write_partsel_reg(msc, MBW_MIN, 0); + + if (mpam_has_feature(mpam_feat_mbw_max, rprops)) + mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + + if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) + mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); + mutex_unlock(&msc->part_sel_lock); +} + +static void mpam_reset_ris(struct mpam_msc_ris *ris) +{ + u16 partid, partid_max; + + mpam_assert_srcu_read_lock_held(); + + if (ris->in_reset_state) + return; + + spin_lock(&partid_max_lock); + partid_max =3D mpam_partid_max; + spin_unlock(&partid_max_lock); + for (partid =3D 0; partid < partid_max; partid++) + mpam_reset_ris_partid(ris, partid); +} + +static void mpam_reset_msc(struct mpam_msc *msc, bool online) +{ + int idx; + struct mpam_msc_ris *ris; + + mpam_assert_srcu_read_lock_held(); + + mpam_mon_sel_outer_lock(msc); + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { + mpam_reset_ris(ris); + + /* + * Set in_reset_state when coming online. The reset state + * for non-zero partid may be lost while the CPUs are offline. + */ + ris->in_reset_state =3D online; + } + srcu_read_unlock(&mpam_srcu, idx); + mpam_mon_sel_outer_unlock(msc); +} + static int mpam_cpu_online(unsigned int cpu) { + int idx; + struct mpam_msc *msc; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) + mpam_reset_msc(msc, true); + } + srcu_read_unlock(&mpam_srcu, idx); + return 0; } =20 @@ -886,6 +994,19 @@ static int mpam_discovery_cpu_online(unsigned int cpu) =20 static int mpam_cpu_offline(unsigned int cpu) { + int idx; + struct mpam_msc *msc; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + if (atomic_dec_and_test(&msc->online_refs)) + mpam_reset_msc(msc, false); + } + srcu_read_unlock(&mpam_srcu, idx); + return 0; } =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a2b0ff411138..466d670a01eb 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -5,6 +5,7 @@ #define MPAM_INTERNAL_H =20 #include +#include #include #include #include @@ -43,6 +44,7 @@ struct mpam_msc { struct pcc_mbox_chan *pcc_chan; u32 nrdy_usec; cpumask_t accessibility; + atomic_t online_refs; =20 /* * probe_lock is only take during discovery. After discovery these @@ -248,6 +250,7 @@ struct mpam_msc_ris { u8 ris_idx; u64 idr; struct mpam_props props; + bool in_reset_state; =20 cpumask_t affinity; =20 @@ -267,6 +270,11 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +static inline void mpam_assert_srcu_read_lock_held(void) +{ + WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu))); +} + /* System wide partid/pmg values */ extern u16 mpam_partid_max; extern u8 mpam_pmg_max; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id DFF3924C076; Fri, 22 Aug 2025 15:36:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876967; cv=none; b=NQN+5N7AXd0OC+Tl231NpjGQhGDcgi2rTSkA6XTHYmKKyKvsb0Ml6hqLgE8SzCIZwEdIRmBC88w2vsFl2s3fKkY6t56BYE+V5Y8534GNhCkWXV66GWGLAbRMyQTqn1pbpVo8a2CAytKiA4EQlram3TqXP5UpsYxc5cq+Kokfpt8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876967; c=relaxed/simple; bh=WMbYLx7wftmnKm+gAYMtfmtcXYgZ093690PaIbCtmeY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HhVJr6Ux0Qdgatv+tv+CYzZBIr21Za8TPqwHYHMmN37Yw0pyTe+VMugijPYCZg5wGRTdXduuOfXoNo0eDplYRqZW3omApDkK/sm7Qv0ZonOGzvrPqAhqd7lY8dspRR/y7pVKK37zsjgmFB1SM7etilZL981Hrz8mjMJltzWfU3A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4E5382880; Fri, 22 Aug 2025 08:35:57 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 1EEBA3F63F; Fri, 22 Aug 2025 08:35:59 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew Subject: [PATCH 19/33] arm_mpam: Reset MSC controls from cpu hp callbacks Date: Fri, 22 Aug 2025 15:30:34 +0000 Message-Id: <20250822153048.2287-54-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When a CPU comes online, it may bring a newly accessible MSC with it. Only the default partid has its value reset by hardware, and even then the MSC might not have been reset since its config was previously dirtyied. e.g. Kexec. Any in-use partid must have its configuration restored, or reset. In-use partids may be held in caches and evicted later. MSC are also reset when CPUs are taken offline to cover cases where firmware doesn't reset the MSC over reboot using UEFI, or kexec where there is no firmware involvement. If the configuration for a RIS has not been touched since it was brought online, it does not need resetting again. To reset, write the maximum values for all discovered controls. CC: Rohit Mathew Signed-off-by: James Morse --- Changes since RFC: * Last bitmap write will always be non-zero. * Dropped READ_ONCE() - teh value can no longer change. --- drivers/resctrl/mpam_devices.c | 121 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 8 +++ 2 files changed, 129 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index bb62de6d3847..c1f01dd748ad 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -849,8 +850,115 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) return 0; } =20 +static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) +{ + u32 num_words, msb; + u32 bm =3D ~0; + int i; + + lockdep_assert_held(&msc->part_sel_lock); + + if (wd =3D=3D 0) + return; + + /* + * Write all ~0 to all but the last 32bit-word, which may + * have fewer bits... + */ + num_words =3D DIV_ROUND_UP(wd, 32); + for (i =3D 0; i < num_words - 1; i++, reg +=3D sizeof(bm)) + __mpam_write_reg(msc, reg, bm); + + /* + * ....and then the last (maybe) partial 32bit word. When wd is a + * multiple of 32, msb should be 31 to write a full 32bit word. + */ + msb =3D (wd - 1) % 32; + bm =3D GENMASK(msb, 0); + __mpam_write_reg(msc, reg, bm); +} + +static void mpam_reset_ris_partid(struct mpam_msc_ris *ris, u16 partid) +{ + u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; + struct mpam_msc *msc =3D ris->vmsc->msc; + struct mpam_props *rprops =3D &ris->props; + + mpam_assert_srcu_read_lock_held(); + + mutex_lock(&msc->part_sel_lock); + __mpam_part_sel(ris->ris_idx, partid, msc); + + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) + mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); + + if (mpam_has_feature(mpam_feat_mbw_part, rprops)) + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); + + if (mpam_has_feature(mpam_feat_mbw_min, rprops)) + mpam_write_partsel_reg(msc, MBW_MIN, 0); + + if (mpam_has_feature(mpam_feat_mbw_max, rprops)) + mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + + if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) + mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); + mutex_unlock(&msc->part_sel_lock); +} + +static void mpam_reset_ris(struct mpam_msc_ris *ris) +{ + u16 partid, partid_max; + + mpam_assert_srcu_read_lock_held(); + + if (ris->in_reset_state) + return; + + spin_lock(&partid_max_lock); + partid_max =3D mpam_partid_max; + spin_unlock(&partid_max_lock); + for (partid =3D 0; partid < partid_max; partid++) + mpam_reset_ris_partid(ris, partid); +} + +static void mpam_reset_msc(struct mpam_msc *msc, bool online) +{ + int idx; + struct mpam_msc_ris *ris; + + mpam_assert_srcu_read_lock_held(); + + mpam_mon_sel_outer_lock(msc); + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { + mpam_reset_ris(ris); + + /* + * Set in_reset_state when coming online. The reset state + * for non-zero partid may be lost while the CPUs are offline. + */ + ris->in_reset_state =3D online; + } + srcu_read_unlock(&mpam_srcu, idx); + mpam_mon_sel_outer_unlock(msc); +} + static int mpam_cpu_online(unsigned int cpu) { + int idx; + struct mpam_msc *msc; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) + mpam_reset_msc(msc, true); + } + srcu_read_unlock(&mpam_srcu, idx); + return 0; } =20 @@ -886,6 +994,19 @@ static int mpam_discovery_cpu_online(unsigned int cpu) =20 static int mpam_cpu_offline(unsigned int cpu) { + int idx; + struct mpam_msc *msc; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + if (!cpumask_test_cpu(cpu, &msc->accessibility)) + continue; + + if (atomic_dec_and_test(&msc->online_refs)) + mpam_reset_msc(msc, false); + } + srcu_read_unlock(&mpam_srcu, idx); + return 0; } =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index a2b0ff411138..466d670a01eb 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -5,6 +5,7 @@ #define MPAM_INTERNAL_H =20 #include +#include #include #include #include @@ -43,6 +44,7 @@ struct mpam_msc { struct pcc_mbox_chan *pcc_chan; u32 nrdy_usec; cpumask_t accessibility; + atomic_t online_refs; =20 /* * probe_lock is only take during discovery. After discovery these @@ -248,6 +250,7 @@ struct mpam_msc_ris { u8 ris_idx; u64 idr; struct mpam_props props; + bool in_reset_state; =20 cpumask_t affinity; =20 @@ -267,6 +270,11 @@ struct mpam_msc_ris { extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; =20 +static inline void mpam_assert_srcu_read_lock_held(void) +{ + WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu))); +} + /* System wide partid/pmg values */ extern u16 mpam_partid_max; extern u8 mpam_pmg_max; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C5F6624DD15; Fri, 22 Aug 2025 15:32:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876781; cv=none; b=nuhPVsJ8JO+SsAg4afHuYEfPiOfoSRo9gEjeaJjYnqlnDiLmO8x6OidUL/SaZH/DIQk3OYFAaDaEzEOXYeqqp2FIIVmYnhtUx0oiNkeSgxNGerKENWn7eQQ1z7HSORn1XJ6/uGuA9Hztl1vKV1wLIgrEBQxFZ4HY6NQVlxstgiU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876781; c=relaxed/simple; bh=482lR6qZEXMuwO0xSwqkMMzje9b+l5mSPWDxSUXfvvQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cSWsdbyl3ZbHp7Y7LINnjyv9IunXyR/hS3O+YmaCY63ZnKz8UvfsDop87kIB78x9GlxT5ueP1fIopFTlPfPmZyxVGk55o1+RhXLGVGS6oqvbofzO5W/rLNb4PQhs/t5c9U8qLT36jmTAgAOV+YFWj51/GPaRQWNOP4vUxRpwEnA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 25C3A2880; Fri, 22 Aug 2025 08:32:51 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 17D423F63F; Fri, 22 Aug 2025 08:32:53 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 20/33] arm_mpam: Add a helper to touch an MSC from any CPU Date: Fri, 22 Aug 2025 15:30:01 +0000 Message-Id: <20250822153048.2287-21-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Resetting RIS entries from the cpuhp callback is easy as the callback occurs on the correct CPU. This won't be true for any other caller that wants to reset or configure an MSC. Add a helper that schedules the provided function if necessary. Prevent the cpuhp callbacks from changing the MSC state by taking the cpuhp lock. Signed-off-by: James Morse Reviewed-by: Ben Horgan --- drivers/resctrl/mpam_devices.c | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index c1f01dd748ad..759244966736 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -906,20 +906,51 @@ static void mpam_reset_ris_partid(struct mpam_msc_ris= *ris, u16 partid) mutex_unlock(&msc->part_sel_lock); } =20 -static void mpam_reset_ris(struct mpam_msc_ris *ris) +/* + * Called via smp_call_on_cpu() to prevent migration, while still being + * pre-emptible. + */ +static int mpam_reset_ris(void *arg) { u16 partid, partid_max; + struct mpam_msc_ris *ris =3D arg; =20 mpam_assert_srcu_read_lock_held(); =20 if (ris->in_reset_state) - return; + return 0; =20 spin_lock(&partid_max_lock); partid_max =3D mpam_partid_max; spin_unlock(&partid_max_lock); for (partid =3D 0; partid < partid_max; partid++) mpam_reset_ris_partid(ris, partid); + + return 0; +} + +/* + * Get the preferred CPU for this MSC. If it is accessible from this CPU, + * this CPU is preferred. This can be preempted/migrated, it will only res= ult + * in more work. + */ +static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc) +{ + int cpu =3D raw_smp_processor_id(); + + if (cpumask_test_cpu(cpu, &msc->accessibility)) + return cpu; + + return cpumask_first_and(&msc->accessibility, cpu_online_mask); +} + +static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *= arg) +{ + lockdep_assert_irqs_enabled(); + lockdep_assert_cpus_held(); + mpam_assert_srcu_read_lock_held(); + + return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true); } =20 static void mpam_reset_msc(struct mpam_msc *msc, bool online) @@ -932,7 +963,7 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool o= nline) mpam_mon_sel_outer_lock(msc); idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { - mpam_reset_ris(ris); + mpam_touch_msc(msc, &mpam_reset_ris, ris); =20 /* * Set in_reset_state when coming online. The reset state --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 963F2273D6B; Fri, 22 Aug 2025 15:36:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876973; cv=none; b=jrcS+RHsi7USVVGrPVqTuejymiOCCQa0DNrqaDVxX9cqZg6ke3eQjzI2TbwXLtA8a/s2xU5B8iW38jCJ8d6CiytORUIF787aLDrMcARzzySxwef3Rwy6/9KjkhV7HuMlO+2+AzbMmougC7GzHk17+aWbrHKLGFzUUQilykYSMuk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876973; c=relaxed/simple; bh=482lR6qZEXMuwO0xSwqkMMzje9b+l5mSPWDxSUXfvvQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Bl7TN1y/ioQ6pgJdpkxDkro/Q70+pKxNsRTCjpZfeI5vpHXukFnvm80CUbiGLjVjLC3jNpmmNPdN81xktfLSZclqUQWYvcz9ZWfNooT/z0GkvqiqS4bE9ZyCOESrljtv8bJvlJKfgf3PLPyOagLiHS+OjUJuLCC6fUymLc0KSPA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id DCCD115A1; Fri, 22 Aug 2025 08:36:02 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id CF8E33F63F; Fri, 22 Aug 2025 08:36:05 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 20/33] arm_mpam: Add a helper to touch an MSC from any CPU Date: Fri, 22 Aug 2025 15:30:35 +0000 Message-Id: <20250822153048.2287-55-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Resetting RIS entries from the cpuhp callback is easy as the callback occurs on the correct CPU. This won't be true for any other caller that wants to reset or configure an MSC. Add a helper that schedules the provided function if necessary. Prevent the cpuhp callbacks from changing the MSC state by taking the cpuhp lock. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index c1f01dd748ad..759244966736 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -906,20 +906,51 @@ static void mpam_reset_ris_partid(struct mpam_msc_ris= *ris, u16 partid) mutex_unlock(&msc->part_sel_lock); } =20 -static void mpam_reset_ris(struct mpam_msc_ris *ris) +/* + * Called via smp_call_on_cpu() to prevent migration, while still being + * pre-emptible. + */ +static int mpam_reset_ris(void *arg) { u16 partid, partid_max; + struct mpam_msc_ris *ris =3D arg; =20 mpam_assert_srcu_read_lock_held(); =20 if (ris->in_reset_state) - return; + return 0; =20 spin_lock(&partid_max_lock); partid_max =3D mpam_partid_max; spin_unlock(&partid_max_lock); for (partid =3D 0; partid < partid_max; partid++) mpam_reset_ris_partid(ris, partid); + + return 0; +} + +/* + * Get the preferred CPU for this MSC. If it is accessible from this CPU, + * this CPU is preferred. This can be preempted/migrated, it will only res= ult + * in more work. + */ +static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc) +{ + int cpu =3D raw_smp_processor_id(); + + if (cpumask_test_cpu(cpu, &msc->accessibility)) + return cpu; + + return cpumask_first_and(&msc->accessibility, cpu_online_mask); +} + +static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *= arg) +{ + lockdep_assert_irqs_enabled(); + lockdep_assert_cpus_held(); + mpam_assert_srcu_read_lock_held(); + + return smp_call_on_cpu(mpam_get_msc_preferred_cpu(msc), fn, arg, true); } =20 static void mpam_reset_msc(struct mpam_msc *msc, bool online) @@ -932,7 +963,7 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool o= nline) mpam_mon_sel_outer_lock(msc); idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { - mpam_reset_ris(ris); + mpam_touch_msc(msc, &mpam_reset_ris, ris); =20 /* * Set in_reset_state when coming online. The reset state --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A4B89230BE1; Fri, 22 Aug 2025 15:33:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876787; cv=none; b=ICUls6It6e2WhVv5dJLmTwEz2zYexFDjPITdbfIiTxfuKeEAEOYIDolgxWSQ/qQLO4p5xPkH0vId1BqE4lTGTmsIZY37SnP0JiNQXO01l+M9AenK7iQjK0nKqrv6i4HlG1U40nLLjsU7o1paEo+WrGdcAD+fY9ael4xLpUA5SJk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876787; c=relaxed/simple; bh=TWVip1TkrPKgYZgIpSL+XlhROAFKrr+IQzPOiLtT210=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qrF9DSAldzwxBQ9brMv+6EUkTs0/A64KCD3ArBPE4F8KZN/820L6vMbLhRDvJ6Zfqwo4gAY8W0848iD5i/PNtbZEuQ460CARQkJNyrfgUWk1kJsSDfuZGrtMEBSCDrgJU+vovc0b6yAcloYXE1qy1XtZMvW8QEo3L53Avp9/RPo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B546E27DD; Fri, 22 Aug 2025 08:32:56 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A722B3F63F; Fri, 22 Aug 2025 08:32:59 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 21/33] arm_mpam: Extend reset logic to allow devices to be reset any time Date: Fri, 22 Aug 2025 15:30:02 +0000 Message-Id: <20250822153048.2287-22-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" cpuhp callbacks aren't the only time the MSC configuration may need to be reset. Resctrl has an API call to reset a class. If an MPAM error interrupt arrives it indicates the driver has misprogrammed an MSC. The safest thing to do is reset all the MSCs and disable MPAM. Add a helper to reset RIS via their class. Call this from mpam_disable(), which can be scheduled from the error interrupt handler. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 62 +++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 1 + 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 759244966736..3516cbe8623e 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -915,8 +915,6 @@ static int mpam_reset_ris(void *arg) u16 partid, partid_max; struct mpam_msc_ris *ris =3D arg; =20 - mpam_assert_srcu_read_lock_held(); - if (ris->in_reset_state) return 0; =20 @@ -1569,6 +1567,66 @@ static void mpam_enable_once(void) mpam_partid_max + 1, mpam_pmg_max + 1); } =20 +static void mpam_reset_component_locked(struct mpam_component *comp) +{ + int idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + might_sleep(); + lockdep_assert_cpus_held(); + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + if (!ris->in_reset_state) + mpam_touch_msc(msc, mpam_reset_ris, ris); + ris->in_reset_state =3D true; + } + } + srcu_read_unlock(&mpam_srcu, idx); +} + +static void mpam_reset_class_locked(struct mpam_class *class) +{ + int idx; + struct mpam_component *comp; + + lockdep_assert_cpus_held(); + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(comp, &class->components, class_list) + mpam_reset_component_locked(comp); + srcu_read_unlock(&mpam_srcu, idx); +} + +static void mpam_reset_class(struct mpam_class *class) +{ + cpus_read_lock(); + mpam_reset_class_locked(class); + cpus_read_unlock(); +} + +/* + * Called in response to an error IRQ. + * All of MPAMs errors indicate a software bug, restore any modified + * controls to their reset values. + */ +void mpam_disable(void) +{ + int idx; + struct mpam_class *class; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(class, &mpam_classes, classes_list, + srcu_read_lock_held(&mpam_srcu)) + mpam_reset_class(class); + srcu_read_unlock(&mpam_srcu, idx); +} + /* * Enable mpam once all devices have been probed. * Scheduled by mpam_discovery_cpu_online() once all devices have been cre= ated. diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 466d670a01eb..b30fee2b7674 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -281,6 +281,7 @@ extern u8 mpam_pmg_max; =20 /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); +void mpam_disable(void); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3186F286890; Fri, 22 Aug 2025 15:36:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876978; cv=none; b=dLaYinxSsdCVTFVsOTKQSGnht5ezCRm7jzCaBpAJYrdpAD4nunacpOAL6lVWXXihUWAqopI3WNWvY3F8duSyG6VXuplw1eKiT1VSzTkzJPWL4mNt3ZlIhbO01HYnqmX4aJba8IEdup4N2zdLUUglY6WM9VLKB/3R+sDnupIkCdw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876978; c=relaxed/simple; bh=TWVip1TkrPKgYZgIpSL+XlhROAFKrr+IQzPOiLtT210=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=owJWz//XAK+yS7cE783ZVCXNMis8vGb5OioQwa6FubTDj/koOho/d7v8SeKr5pyIcsoq3tRz15eM0Vh2sDjeP8XATosL3pYAenSqNrVdCAM+m978CGLKN9J9bYENE5NzXH+iOpJfqpO68+4O3IuORqVlZg31i2URXLNTSB47at0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 85B6C15A1; Fri, 22 Aug 2025 08:36:08 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 6E7C33F63F; Fri, 22 Aug 2025 08:36:11 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 21/33] arm_mpam: Extend reset logic to allow devices to be reset any time Date: Fri, 22 Aug 2025 15:30:36 +0000 Message-Id: <20250822153048.2287-56-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" cpuhp callbacks aren't the only time the MSC configuration may need to be reset. Resctrl has an API call to reset a class. If an MPAM error interrupt arrives it indicates the driver has misprogrammed an MSC. The safest thing to do is reset all the MSCs and disable MPAM. Add a helper to reset RIS via their class. Call this from mpam_disable(), which can be scheduled from the error interrupt handler. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 62 +++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 1 + 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 759244966736..3516cbe8623e 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -915,8 +915,6 @@ static int mpam_reset_ris(void *arg) u16 partid, partid_max; struct mpam_msc_ris *ris =3D arg; =20 - mpam_assert_srcu_read_lock_held(); - if (ris->in_reset_state) return 0; =20 @@ -1569,6 +1567,66 @@ static void mpam_enable_once(void) mpam_partid_max + 1, mpam_pmg_max + 1); } =20 +static void mpam_reset_component_locked(struct mpam_component *comp) +{ + int idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + might_sleep(); + lockdep_assert_cpus_held(); + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + if (!ris->in_reset_state) + mpam_touch_msc(msc, mpam_reset_ris, ris); + ris->in_reset_state =3D true; + } + } + srcu_read_unlock(&mpam_srcu, idx); +} + +static void mpam_reset_class_locked(struct mpam_class *class) +{ + int idx; + struct mpam_component *comp; + + lockdep_assert_cpus_held(); + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(comp, &class->components, class_list) + mpam_reset_component_locked(comp); + srcu_read_unlock(&mpam_srcu, idx); +} + +static void mpam_reset_class(struct mpam_class *class) +{ + cpus_read_lock(); + mpam_reset_class_locked(class); + cpus_read_unlock(); +} + +/* + * Called in response to an error IRQ. + * All of MPAMs errors indicate a software bug, restore any modified + * controls to their reset values. + */ +void mpam_disable(void) +{ + int idx; + struct mpam_class *class; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(class, &mpam_classes, classes_list, + srcu_read_lock_held(&mpam_srcu)) + mpam_reset_class(class); + srcu_read_unlock(&mpam_srcu, idx); +} + /* * Enable mpam once all devices have been probed. * Scheduled by mpam_discovery_cpu_online() once all devices have been cre= ated. diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 466d670a01eb..b30fee2b7674 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -281,6 +281,7 @@ extern u8 mpam_pmg_max; =20 /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); +void mpam_disable(void); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BE32E286890; Fri, 22 Aug 2025 15:36:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876984; cv=none; b=i2kIw89q11VZufPxa6YF6a5n1s8nh2Hv96LAO+bRFmatJ5uGM3RJXbOQCEeK0vzxOSEiVBhwMKRDlY2ABv75o38Mu92o+OYj+0Qs2WqiHAs+WGFtcMIP3C3HPx0OSIh+IDfGQficHzwscQMvPrvF7nvIjssAOHrP5ufgwKe51e0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876984; c=relaxed/simple; bh=97eqOvDRVNdpalL4cQZkLBZqWKPMfcJSnL2FMWLYgD4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=d5sck3bILBiicwZus8lyaEJ52bc+UmovQ3KP8FFK46OXYKVHh+Rs6pJoGqk6AtRlMKKrYQ8YJy8UidO7CQVLwxtIzZJaaMYfhIfBJUim4nkcRXC/zGdqZ9SMXXbPxLe+bsDhMUcmeGo6i1KG6V13axTcZCheg7qmvHESyOGTxvE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2BD0415A1; Fri, 22 Aug 2025 08:36:14 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 169B93F63F; Fri, 22 Aug 2025 08:36:16 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 22/33] arm_mpam: Register and enable IRQs Date: Fri, 22 Aug 2025 15:30:37 +0000 Message-Id: <20250822153048.2287-57-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Register and enable error IRQs. All the MPAM error interrupts indicate a software bug, e.g. out of range partid. If the error interrupt is ever signalled, attempt to disable MPAM. Only the irq handler accesses the ESR register, so no locking is needed. The work to disable MPAM after an error needs to happen at process context, use a threaded interrupt. There is no support for percpu threaded interrupts, for now schedule the work to be done from the irq handler. Enabling the IRQs in the MSC may involve cross calling to a CPU that can access the MSC. Once the IRQ is requested, the mpam_disable() path can be called asynchronously, which will walk structures sized by max_partid. Ensure this size is fixed before the interrupt is requested. CC: Rohit Mathew Tested-by: Rohit Mathew Signed-off-by: James Morse --- Changes since RFC: * Use guard marco when walking srcu list. * Use INTEN macro for enabling interrupts. * Move partid_max_published up earlier in mpam_enable_once(). --- drivers/resctrl/mpam_devices.c | 311 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 9 +- 2 files changed, 312 insertions(+), 8 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 3516cbe8623e..210d64fad0b1 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -62,6 +65,12 @@ static DEFINE_SPINLOCK(partid_max_lock); */ static DECLARE_WORK(mpam_enable_work, &mpam_enable); =20 +/* + * All mpam error interrupts indicate a software bug. On receipt, disable = the + * driver. + */ +static DECLARE_WORK(mpam_broken_work, &mpam_disable); + /* * An MSC is a physical container for controls and monitors, each identifi= ed by * their RIS index. These share a base-address, interrupts and some MMIO @@ -159,6 +168,24 @@ static u64 mpam_msc_read_idr(struct mpam_msc *msc) return (idr_high << 32) | idr_low; } =20 +static void mpam_msc_zero_esr(struct mpam_msc *msc) +{ + __mpam_write_reg(msc, MPAMF_ESR, 0); + if (msc->has_extd_esr) + __mpam_write_reg(msc, MPAMF_ESR + 4, 0); +} + +static u64 mpam_msc_read_esr(struct mpam_msc *msc) +{ + u64 esr_high =3D 0, esr_low; + + esr_low =3D __mpam_read_reg(msc, MPAMF_ESR); + if (msc->has_extd_esr) + esr_high =3D __mpam_read_reg(msc, MPAMF_ESR + 4); + + return (esr_high << 32) | esr_low; +} + static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) { lockdep_assert_held(&msc->part_sel_lock); @@ -405,12 +432,12 @@ static void mpam_msc_destroy(struct mpam_msc *msc) =20 lockdep_assert_held(&mpam_list_lock); =20 - list_del_rcu(&msc->glbl_list); - platform_set_drvdata(pdev, NULL); - list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) mpam_ris_destroy(ris); =20 + list_del_rcu(&msc->glbl_list); + platform_set_drvdata(pdev, NULL); + add_to_garbage(msc); msc->garbage.pdev =3D pdev; } @@ -828,6 +855,7 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); msc->partid_max =3D min(msc->partid_max, partid_max); msc->pmg_max =3D min(msc->pmg_max, pmg_max); + msc->has_extd_esr =3D FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr); =20 ris =3D mpam_get_or_create_ris(msc, ris_idx); if (IS_ERR(ris)) @@ -840,6 +868,9 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) mutex_unlock(&msc->part_sel_lock); } =20 + /* Clear any stale errors */ + mpam_msc_zero_esr(msc); + spin_lock(&partid_max_lock); mpam_partid_max =3D min(mpam_partid_max, msc->partid_max); mpam_pmg_max =3D min(mpam_pmg_max, msc->pmg_max); @@ -973,6 +1004,13 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool= online) mpam_mon_sel_outer_unlock(msc); } =20 +static void _enable_percpu_irq(void *_irq) +{ + int *irq =3D _irq; + + enable_percpu_irq(*irq, IRQ_TYPE_NONE); +} + static int mpam_cpu_online(unsigned int cpu) { int idx; @@ -983,6 +1021,9 @@ static int mpam_cpu_online(unsigned int cpu) if (!cpumask_test_cpu(cpu, &msc->accessibility)) continue; =20 + if (msc->reenable_error_ppi) + _enable_percpu_irq(&msc->reenable_error_ppi); + if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) mpam_reset_msc(msc, true); } @@ -1031,6 +1072,9 @@ static int mpam_cpu_offline(unsigned int cpu) if (!cpumask_test_cpu(cpu, &msc->accessibility)) continue; =20 + if (msc->reenable_error_ppi) + disable_percpu_irq(msc->reenable_error_ppi); + if (atomic_dec_and_test(&msc->online_refs)) mpam_reset_msc(msc, false); } @@ -1057,6 +1101,51 @@ static void mpam_register_cpuhp_callbacks(int (*onli= ne)(unsigned int online), mutex_unlock(&mpam_cpuhp_state_lock); } =20 +static int __setup_ppi(struct mpam_msc *msc) +{ + int cpu; + + msc->error_dev_id =3D alloc_percpu_gfp(struct mpam_msc *, GFP_KERNEL); + if (!msc->error_dev_id) + return -ENOMEM; + + for_each_cpu(cpu, &msc->accessibility) { + struct mpam_msc *empty =3D *per_cpu_ptr(msc->error_dev_id, cpu); + + if (empty) { + pr_err_once("%s shares PPI with %s!\n", + dev_name(&msc->pdev->dev), + dev_name(&empty->pdev->dev)); + return -EBUSY; + } + *per_cpu_ptr(msc->error_dev_id, cpu) =3D msc; + } + + return 0; +} + +static int mpam_msc_setup_error_irq(struct mpam_msc *msc) +{ + int irq; + + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + return 0; + + /* Allocate and initialise the percpu device pointer for PPI */ + if (irq_is_percpu(irq)) + return __setup_ppi(msc); + + /* sanity check: shared interrupts can be routed anywhere? */ + if (!cpumask_equal(&msc->accessibility, cpu_possible_mask)) { + pr_err_once("msc:%u is a private resource with a shared error interrupt", + msc->id); + return -EINVAL; + } + + return 0; +} + static int mpam_dt_count_msc(void) { int count =3D 0; @@ -1265,6 +1354,10 @@ static int mpam_msc_drv_probe(struct platform_device= *pdev) break; } =20 + err =3D mpam_msc_setup_error_irq(msc); + if (err) + break; + if (device_property_read_u32(&pdev->dev, "pcc-channel", &msc->pcc_subspace_id)) msc->iface =3D MPAM_IFACE_MMIO; @@ -1547,11 +1640,171 @@ static void mpam_enable_merge_features(struct list= _head *all_classes_list) } } =20 +static char *mpam_errcode_names[16] =3D { + [0] =3D "No error", + [1] =3D "PARTID_SEL_Range", + [2] =3D "Req_PARTID_Range", + [3] =3D "MSMONCFG_ID_RANGE", + [4] =3D "Req_PMG_Range", + [5] =3D "Monitor_Range", + [6] =3D "intPARTID_Range", + [7] =3D "Unexpected_INTERNAL", + [8] =3D "Undefined_RIS_PART_SEL", + [9] =3D "RIS_No_Control", + [10] =3D "Undefined_RIS_MON_SEL", + [11] =3D "RIS_No_Monitor", + [12 ... 15] =3D "Reserved" +}; + +static int mpam_enable_msc_ecr(void *_msc) +{ + struct mpam_msc *msc =3D _msc; + + __mpam_write_reg(msc, MPAMF_ECR, MPAMF_ECR_INTEN); + + return 0; +} + +static int mpam_disable_msc_ecr(void *_msc) +{ + struct mpam_msc *msc =3D _msc; + + __mpam_write_reg(msc, MPAMF_ECR, 0); + + return 0; +} + +static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) +{ + u64 reg; + u16 partid; + u8 errcode, pmg, ris; + + if (WARN_ON_ONCE(!msc) || + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), + &msc->accessibility))) + return IRQ_NONE; + + reg =3D mpam_msc_read_esr(msc); + + errcode =3D FIELD_GET(MPAMF_ESR_ERRCODE, reg); + if (!errcode) + return IRQ_NONE; + + /* Clear level triggered irq */ + mpam_msc_zero_esr(msc); + + partid =3D FIELD_GET(MPAMF_ESR_PARTID_MON, reg); + pmg =3D FIELD_GET(MPAMF_ESR_PMG, reg); + ris =3D FIELD_GET(MPAMF_ESR_RIS, reg); + + pr_err("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n", + msc->id, mpam_errcode_names[errcode], partid, pmg, ris); + + if (irq_is_percpu(irq)) { + mpam_disable_msc_ecr(msc); + schedule_work(&mpam_broken_work); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mpam_ppi_handler(int irq, void *dev_id) +{ + struct mpam_msc *msc =3D *(struct mpam_msc **)dev_id; + + return __mpam_irq_handler(irq, msc); +} + +static irqreturn_t mpam_spi_handler(int irq, void *dev_id) +{ + struct mpam_msc *msc =3D dev_id; + + return __mpam_irq_handler(irq, msc); +} + +static irqreturn_t mpam_disable_thread(int irq, void *dev_id); + +static int mpam_register_irqs(void) +{ + int err, irq; + struct mpam_msc *msc; + + lockdep_assert_cpus_held(); + + guard(srcu)(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + continue; + + /* The MPAM spec says the interrupt can be SPI, PPI or LPI */ + /* We anticipate sharing the interrupt with other MSCs */ + if (irq_is_percpu(irq)) { + err =3D request_percpu_irq(irq, &mpam_ppi_handler, + "mpam:msc:error", + msc->error_dev_id); + if (err) + return err; + + msc->reenable_error_ppi =3D irq; + smp_call_function_many(&msc->accessibility, + &_enable_percpu_irq, &irq, + true); + } else { + err =3D devm_request_threaded_irq(&msc->pdev->dev, irq, + &mpam_spi_handler, + &mpam_disable_thread, + IRQF_SHARED, + "mpam:msc:error", msc); + if (err) + return err; + } + + msc->error_irq_requested =3D true; + mpam_touch_msc(msc, mpam_enable_msc_ecr, msc); + msc->error_irq_hw_enabled =3D true; + } + + return 0; +} + +static void mpam_unregister_irqs(void) +{ + int irq, idx; + struct mpam_msc *msc; + + cpus_read_lock(); + /* take the lock as free_irq() can sleep */ + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + continue; + + if (msc->error_irq_hw_enabled) { + mpam_touch_msc(msc, mpam_disable_msc_ecr, msc); + msc->error_irq_hw_enabled =3D false; + } + + if (msc->error_irq_requested) { + if (irq_is_percpu(irq)) { + msc->reenable_error_ppi =3D 0; + free_percpu_irq(irq, msc->error_dev_id); + } else { + devm_free_irq(&msc->pdev->dev, irq, msc); + } + msc->error_irq_requested =3D false; + } + } + srcu_read_unlock(&mpam_srcu, idx); + cpus_read_unlock(); +} + static void mpam_enable_once(void) { - mutex_lock(&mpam_list_lock); - mpam_enable_merge_features(&mpam_classes); - mutex_unlock(&mpam_list_lock); + int err; =20 /* * Once the cpuhp callbacks have been changed, mpam_partid_max can no @@ -1561,6 +1814,27 @@ static void mpam_enable_once(void) partid_max_published =3D true; spin_unlock(&partid_max_lock); =20 + /* + * If all the MSC have been probed, enabling the IRQs happens next. + * That involves cross-calling to a CPU that can reach the MSC, and + * the locks must be taken in this order: + */ + cpus_read_lock(); + mutex_lock(&mpam_list_lock); + mpam_enable_merge_features(&mpam_classes); + + err =3D mpam_register_irqs(); + if (err) + pr_warn("Failed to register irqs: %d\n", err); + + mutex_unlock(&mpam_list_lock); + cpus_read_unlock(); + + if (err) { + schedule_work(&mpam_broken_work); + return; + } + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", @@ -1615,16 +1889,39 @@ static void mpam_reset_class(struct mpam_class *cla= ss) * All of MPAMs errors indicate a software bug, restore any modified * controls to their reset values. */ -void mpam_disable(void) +static irqreturn_t mpam_disable_thread(int irq, void *dev_id) { int idx; struct mpam_class *class; + struct mpam_msc *msc, *tmp; + + mutex_lock(&mpam_cpuhp_state_lock); + if (mpam_cpuhp_state) { + cpuhp_remove_state(mpam_cpuhp_state); + mpam_cpuhp_state =3D 0; + } + mutex_unlock(&mpam_cpuhp_state_lock); + + mpam_unregister_irqs(); =20 idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(class, &mpam_classes, classes_list, srcu_read_lock_held(&mpam_srcu)) mpam_reset_class(class); srcu_read_unlock(&mpam_srcu, idx); + + mutex_lock(&mpam_list_lock); + list_for_each_entry_safe(msc, tmp, &mpam_all_msc, glbl_list) + mpam_msc_destroy(msc); + mutex_unlock(&mpam_list_lock); + mpam_free_garbage(); + + return IRQ_HANDLED; +} + +void mpam_disable(struct work_struct *ignored) +{ + mpam_disable_thread(0, NULL); } =20 /* diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index b30fee2b7674..c9418c9cf9f2 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -44,6 +44,11 @@ struct mpam_msc { struct pcc_mbox_chan *pcc_chan; u32 nrdy_usec; cpumask_t accessibility; + bool has_extd_esr; + + int reenable_error_ppi; + struct mpam_msc * __percpu *error_dev_id; + atomic_t online_refs; =20 /* @@ -52,6 +57,8 @@ struct mpam_msc { */ struct mutex probe_lock; bool probed; + bool error_irq_requested; + bool error_irq_hw_enabled; u16 partid_max; u8 pmg_max; unsigned long ris_idxs[128 / BITS_PER_LONG]; @@ -281,7 +288,7 @@ extern u8 mpam_pmg_max; =20 /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); -void mpam_disable(void); +void mpam_disable(struct work_struct *work); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id EDE3F23BCFF; Fri, 22 Aug 2025 15:33:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876792; cv=none; b=A+1KteSzZqfY00PUohAAeO92MWv6y/8VdxoOR370wrV6wvLlmMSfDldMk6VYh51ZR4A/QF4pyULkUSj3I04kb1Ol8iKPyBYZUJ+PoAJDPaZQDWb0Pkz9w5hWju5+Yc7IMQ9boTZvgR1QJI96mVjRjIcX8wFyRsyEAoDRBVs1P8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876792; c=relaxed/simple; bh=97eqOvDRVNdpalL4cQZkLBZqWKPMfcJSnL2FMWLYgD4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cl068a2x95tAOV5AB3c4YG17ka93+ny6pw0TWvKAlqQpCRjoy/eTxtmNJmzyO25tW1uFdpejfPEdzj/EvfqitIP+3WAKYOlVw3+7k3wghAS8YeAdxoxKS/T4HPNUayDl0VSsxj/TIZA2Gfv3lF+yP8Fy5Ey8ziH+jd4CqhRyNig= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 513A227DC; Fri, 22 Aug 2025 08:33:02 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 453BC3F63F; Fri, 22 Aug 2025 08:33:05 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 22/33] arm_mpam: Register and enable IRQs Date: Fri, 22 Aug 2025 15:30:03 +0000 Message-Id: <20250822153048.2287-23-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Register and enable error IRQs. All the MPAM error interrupts indicate a software bug, e.g. out of range partid. If the error interrupt is ever signalled, attempt to disable MPAM. Only the irq handler accesses the ESR register, so no locking is needed. The work to disable MPAM after an error needs to happen at process context, use a threaded interrupt. There is no support for percpu threaded interrupts, for now schedule the work to be done from the irq handler. Enabling the IRQs in the MSC may involve cross calling to a CPU that can access the MSC. Once the IRQ is requested, the mpam_disable() path can be called asynchronously, which will walk structures sized by max_partid. Ensure this size is fixed before the interrupt is requested. CC: Rohit Mathew Tested-by: Rohit Mathew Signed-off-by: James Morse --- Changes since RFC: * Use guard marco when walking srcu list. * Use INTEN macro for enabling interrupts. * Move partid_max_published up earlier in mpam_enable_once(). --- drivers/resctrl/mpam_devices.c | 311 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 9 +- 2 files changed, 312 insertions(+), 8 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 3516cbe8623e..210d64fad0b1 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -62,6 +65,12 @@ static DEFINE_SPINLOCK(partid_max_lock); */ static DECLARE_WORK(mpam_enable_work, &mpam_enable); =20 +/* + * All mpam error interrupts indicate a software bug. On receipt, disable = the + * driver. + */ +static DECLARE_WORK(mpam_broken_work, &mpam_disable); + /* * An MSC is a physical container for controls and monitors, each identifi= ed by * their RIS index. These share a base-address, interrupts and some MMIO @@ -159,6 +168,24 @@ static u64 mpam_msc_read_idr(struct mpam_msc *msc) return (idr_high << 32) | idr_low; } =20 +static void mpam_msc_zero_esr(struct mpam_msc *msc) +{ + __mpam_write_reg(msc, MPAMF_ESR, 0); + if (msc->has_extd_esr) + __mpam_write_reg(msc, MPAMF_ESR + 4, 0); +} + +static u64 mpam_msc_read_esr(struct mpam_msc *msc) +{ + u64 esr_high =3D 0, esr_low; + + esr_low =3D __mpam_read_reg(msc, MPAMF_ESR); + if (msc->has_extd_esr) + esr_high =3D __mpam_read_reg(msc, MPAMF_ESR + 4); + + return (esr_high << 32) | esr_low; +} + static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) { lockdep_assert_held(&msc->part_sel_lock); @@ -405,12 +432,12 @@ static void mpam_msc_destroy(struct mpam_msc *msc) =20 lockdep_assert_held(&mpam_list_lock); =20 - list_del_rcu(&msc->glbl_list); - platform_set_drvdata(pdev, NULL); - list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) mpam_ris_destroy(ris); =20 + list_del_rcu(&msc->glbl_list); + platform_set_drvdata(pdev, NULL); + add_to_garbage(msc); msc->garbage.pdev =3D pdev; } @@ -828,6 +855,7 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) pmg_max =3D FIELD_GET(MPAMF_IDR_PMG_MAX, idr); msc->partid_max =3D min(msc->partid_max, partid_max); msc->pmg_max =3D min(msc->pmg_max, pmg_max); + msc->has_extd_esr =3D FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr); =20 ris =3D mpam_get_or_create_ris(msc, ris_idx); if (IS_ERR(ris)) @@ -840,6 +868,9 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) mutex_unlock(&msc->part_sel_lock); } =20 + /* Clear any stale errors */ + mpam_msc_zero_esr(msc); + spin_lock(&partid_max_lock); mpam_partid_max =3D min(mpam_partid_max, msc->partid_max); mpam_pmg_max =3D min(mpam_pmg_max, msc->pmg_max); @@ -973,6 +1004,13 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool= online) mpam_mon_sel_outer_unlock(msc); } =20 +static void _enable_percpu_irq(void *_irq) +{ + int *irq =3D _irq; + + enable_percpu_irq(*irq, IRQ_TYPE_NONE); +} + static int mpam_cpu_online(unsigned int cpu) { int idx; @@ -983,6 +1021,9 @@ static int mpam_cpu_online(unsigned int cpu) if (!cpumask_test_cpu(cpu, &msc->accessibility)) continue; =20 + if (msc->reenable_error_ppi) + _enable_percpu_irq(&msc->reenable_error_ppi); + if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) mpam_reset_msc(msc, true); } @@ -1031,6 +1072,9 @@ static int mpam_cpu_offline(unsigned int cpu) if (!cpumask_test_cpu(cpu, &msc->accessibility)) continue; =20 + if (msc->reenable_error_ppi) + disable_percpu_irq(msc->reenable_error_ppi); + if (atomic_dec_and_test(&msc->online_refs)) mpam_reset_msc(msc, false); } @@ -1057,6 +1101,51 @@ static void mpam_register_cpuhp_callbacks(int (*onli= ne)(unsigned int online), mutex_unlock(&mpam_cpuhp_state_lock); } =20 +static int __setup_ppi(struct mpam_msc *msc) +{ + int cpu; + + msc->error_dev_id =3D alloc_percpu_gfp(struct mpam_msc *, GFP_KERNEL); + if (!msc->error_dev_id) + return -ENOMEM; + + for_each_cpu(cpu, &msc->accessibility) { + struct mpam_msc *empty =3D *per_cpu_ptr(msc->error_dev_id, cpu); + + if (empty) { + pr_err_once("%s shares PPI with %s!\n", + dev_name(&msc->pdev->dev), + dev_name(&empty->pdev->dev)); + return -EBUSY; + } + *per_cpu_ptr(msc->error_dev_id, cpu) =3D msc; + } + + return 0; +} + +static int mpam_msc_setup_error_irq(struct mpam_msc *msc) +{ + int irq; + + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + return 0; + + /* Allocate and initialise the percpu device pointer for PPI */ + if (irq_is_percpu(irq)) + return __setup_ppi(msc); + + /* sanity check: shared interrupts can be routed anywhere? */ + if (!cpumask_equal(&msc->accessibility, cpu_possible_mask)) { + pr_err_once("msc:%u is a private resource with a shared error interrupt", + msc->id); + return -EINVAL; + } + + return 0; +} + static int mpam_dt_count_msc(void) { int count =3D 0; @@ -1265,6 +1354,10 @@ static int mpam_msc_drv_probe(struct platform_device= *pdev) break; } =20 + err =3D mpam_msc_setup_error_irq(msc); + if (err) + break; + if (device_property_read_u32(&pdev->dev, "pcc-channel", &msc->pcc_subspace_id)) msc->iface =3D MPAM_IFACE_MMIO; @@ -1547,11 +1640,171 @@ static void mpam_enable_merge_features(struct list= _head *all_classes_list) } } =20 +static char *mpam_errcode_names[16] =3D { + [0] =3D "No error", + [1] =3D "PARTID_SEL_Range", + [2] =3D "Req_PARTID_Range", + [3] =3D "MSMONCFG_ID_RANGE", + [4] =3D "Req_PMG_Range", + [5] =3D "Monitor_Range", + [6] =3D "intPARTID_Range", + [7] =3D "Unexpected_INTERNAL", + [8] =3D "Undefined_RIS_PART_SEL", + [9] =3D "RIS_No_Control", + [10] =3D "Undefined_RIS_MON_SEL", + [11] =3D "RIS_No_Monitor", + [12 ... 15] =3D "Reserved" +}; + +static int mpam_enable_msc_ecr(void *_msc) +{ + struct mpam_msc *msc =3D _msc; + + __mpam_write_reg(msc, MPAMF_ECR, MPAMF_ECR_INTEN); + + return 0; +} + +static int mpam_disable_msc_ecr(void *_msc) +{ + struct mpam_msc *msc =3D _msc; + + __mpam_write_reg(msc, MPAMF_ECR, 0); + + return 0; +} + +static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) +{ + u64 reg; + u16 partid; + u8 errcode, pmg, ris; + + if (WARN_ON_ONCE(!msc) || + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), + &msc->accessibility))) + return IRQ_NONE; + + reg =3D mpam_msc_read_esr(msc); + + errcode =3D FIELD_GET(MPAMF_ESR_ERRCODE, reg); + if (!errcode) + return IRQ_NONE; + + /* Clear level triggered irq */ + mpam_msc_zero_esr(msc); + + partid =3D FIELD_GET(MPAMF_ESR_PARTID_MON, reg); + pmg =3D FIELD_GET(MPAMF_ESR_PMG, reg); + ris =3D FIELD_GET(MPAMF_ESR_RIS, reg); + + pr_err("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n", + msc->id, mpam_errcode_names[errcode], partid, pmg, ris); + + if (irq_is_percpu(irq)) { + mpam_disable_msc_ecr(msc); + schedule_work(&mpam_broken_work); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mpam_ppi_handler(int irq, void *dev_id) +{ + struct mpam_msc *msc =3D *(struct mpam_msc **)dev_id; + + return __mpam_irq_handler(irq, msc); +} + +static irqreturn_t mpam_spi_handler(int irq, void *dev_id) +{ + struct mpam_msc *msc =3D dev_id; + + return __mpam_irq_handler(irq, msc); +} + +static irqreturn_t mpam_disable_thread(int irq, void *dev_id); + +static int mpam_register_irqs(void) +{ + int err, irq; + struct mpam_msc *msc; + + lockdep_assert_cpus_held(); + + guard(srcu)(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + continue; + + /* The MPAM spec says the interrupt can be SPI, PPI or LPI */ + /* We anticipate sharing the interrupt with other MSCs */ + if (irq_is_percpu(irq)) { + err =3D request_percpu_irq(irq, &mpam_ppi_handler, + "mpam:msc:error", + msc->error_dev_id); + if (err) + return err; + + msc->reenable_error_ppi =3D irq; + smp_call_function_many(&msc->accessibility, + &_enable_percpu_irq, &irq, + true); + } else { + err =3D devm_request_threaded_irq(&msc->pdev->dev, irq, + &mpam_spi_handler, + &mpam_disable_thread, + IRQF_SHARED, + "mpam:msc:error", msc); + if (err) + return err; + } + + msc->error_irq_requested =3D true; + mpam_touch_msc(msc, mpam_enable_msc_ecr, msc); + msc->error_irq_hw_enabled =3D true; + } + + return 0; +} + +static void mpam_unregister_irqs(void) +{ + int irq, idx; + struct mpam_msc *msc; + + cpus_read_lock(); + /* take the lock as free_irq() can sleep */ + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_srcu(msc, &mpam_all_msc, glbl_list, srcu_read_lock_he= ld(&mpam_srcu)) { + irq =3D platform_get_irq_byname_optional(msc->pdev, "error"); + if (irq <=3D 0) + continue; + + if (msc->error_irq_hw_enabled) { + mpam_touch_msc(msc, mpam_disable_msc_ecr, msc); + msc->error_irq_hw_enabled =3D false; + } + + if (msc->error_irq_requested) { + if (irq_is_percpu(irq)) { + msc->reenable_error_ppi =3D 0; + free_percpu_irq(irq, msc->error_dev_id); + } else { + devm_free_irq(&msc->pdev->dev, irq, msc); + } + msc->error_irq_requested =3D false; + } + } + srcu_read_unlock(&mpam_srcu, idx); + cpus_read_unlock(); +} + static void mpam_enable_once(void) { - mutex_lock(&mpam_list_lock); - mpam_enable_merge_features(&mpam_classes); - mutex_unlock(&mpam_list_lock); + int err; =20 /* * Once the cpuhp callbacks have been changed, mpam_partid_max can no @@ -1561,6 +1814,27 @@ static void mpam_enable_once(void) partid_max_published =3D true; spin_unlock(&partid_max_lock); =20 + /* + * If all the MSC have been probed, enabling the IRQs happens next. + * That involves cross-calling to a CPU that can reach the MSC, and + * the locks must be taken in this order: + */ + cpus_read_lock(); + mutex_lock(&mpam_list_lock); + mpam_enable_merge_features(&mpam_classes); + + err =3D mpam_register_irqs(); + if (err) + pr_warn("Failed to register irqs: %d\n", err); + + mutex_unlock(&mpam_list_lock); + cpus_read_unlock(); + + if (err) { + schedule_work(&mpam_broken_work); + return; + } + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", @@ -1615,16 +1889,39 @@ static void mpam_reset_class(struct mpam_class *cla= ss) * All of MPAMs errors indicate a software bug, restore any modified * controls to their reset values. */ -void mpam_disable(void) +static irqreturn_t mpam_disable_thread(int irq, void *dev_id) { int idx; struct mpam_class *class; + struct mpam_msc *msc, *tmp; + + mutex_lock(&mpam_cpuhp_state_lock); + if (mpam_cpuhp_state) { + cpuhp_remove_state(mpam_cpuhp_state); + mpam_cpuhp_state =3D 0; + } + mutex_unlock(&mpam_cpuhp_state_lock); + + mpam_unregister_irqs(); =20 idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(class, &mpam_classes, classes_list, srcu_read_lock_held(&mpam_srcu)) mpam_reset_class(class); srcu_read_unlock(&mpam_srcu, idx); + + mutex_lock(&mpam_list_lock); + list_for_each_entry_safe(msc, tmp, &mpam_all_msc, glbl_list) + mpam_msc_destroy(msc); + mutex_unlock(&mpam_list_lock); + mpam_free_garbage(); + + return IRQ_HANDLED; +} + +void mpam_disable(struct work_struct *ignored) +{ + mpam_disable_thread(0, NULL); } =20 /* diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index b30fee2b7674..c9418c9cf9f2 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -44,6 +44,11 @@ struct mpam_msc { struct pcc_mbox_chan *pcc_chan; u32 nrdy_usec; cpumask_t accessibility; + bool has_extd_esr; + + int reenable_error_ppi; + struct mpam_msc * __percpu *error_dev_id; + atomic_t online_refs; =20 /* @@ -52,6 +57,8 @@ struct mpam_msc { */ struct mutex probe_lock; bool probed; + bool error_irq_requested; + bool error_irq_hw_enabled; u16 partid_max; u8 pmg_max; unsigned long ris_idxs[128 / BITS_PER_LONG]; @@ -281,7 +288,7 @@ extern u8 mpam_pmg_max; =20 /* Scheduled work callback to enable mpam once all MSC have been probed */ void mpam_enable(struct work_struct *work); -void mpam_disable(void); +void mpam_disable(struct work_struct *work); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A915B23D297; Fri, 22 Aug 2025 15:33:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876798; cv=none; b=rQqjER8DMRC0idYFTzmW/Tlh1NTw4cnvOJY4gFN/g0FrgF9stJ2l+DMiSWoCOLzklAcF/vAaBESccSFx10DOOGJk9+UprjaTBu/ahdeusr62EUvSQm00kCpx4I+MeMXPDa/xT+bZWQ+E+yujOAA1m13ttApP21CPq3kjbxNlbss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876798; c=relaxed/simple; bh=TihBLORAFE4lrQMsSm4i/TF11ZiZA36p7IhI3+Wg0d4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=s78uapkgkxjo4s6+QNq7N+5ynHJmvTX+yt2g/1JTNwjvrP2bu2kgMO0S0pSPBQfSkx2CGjzrRVl+LqFmyG9O7+WBqxxuK7HAdh+AiAkUTmH1rUH4UNCRvkjswVcRgW7LelgK1cvI4HiTItOuGpWw12tIVJkYaMfOHPxYJHns3og= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E083427DC; Fri, 22 Aug 2025 08:33:07 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D58263F63F; Fri, 22 Aug 2025 08:33:10 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 23/33] arm_mpam: Use a static key to indicate when mpam is enabled Date: Fri, 22 Aug 2025 15:30:04 +0000 Message-Id: <20250822153048.2287-24-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Once all the MSC have been probed, the system wide usable number of PARTID is known and the configuration arrays can be allocated. After this point, checking all the MSC have been probed is pointless, and the cpuhp callbacks should restore the configuration, instead of just resetting the MSC. Add a static key to enable this behaviour. This will also allow MPAM to be disabled in repsonse to an error, and the architecture code to enable/disable the context switch of the MPAM system registers. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 8 ++++++++ drivers/resctrl/mpam_internal.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 210d64fad0b1..b424af666b1e 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -33,6 +33,8 @@ =20 #include "mpam_internal.h" =20 +DEFINE_STATIC_KEY_FALSE(mpam_enabled); /* TODO: move to arch code */ + /* * mpam_list_lock protects the SRCU lists when writing. Once the * mpam_enabled key is enabled these lists are read-only, @@ -1039,6 +1041,9 @@ static int mpam_discovery_cpu_online(unsigned int cpu) struct mpam_msc *msc; bool new_device_probed =3D false; =20 + if (mpam_is_enabled()) + return 0; + mutex_lock(&mpam_list_lock); list_for_each_entry(msc, &mpam_all_msc, glbl_list) { if (!cpumask_test_cpu(cpu, &msc->accessibility)) @@ -1835,6 +1840,7 @@ static void mpam_enable_once(void) return; } =20 + static_branch_enable(&mpam_enabled); mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", @@ -1902,6 +1908,8 @@ static irqreturn_t mpam_disable_thread(int irq, void = *dev_id) } mutex_unlock(&mpam_cpuhp_state_lock); =20 + static_branch_disable(&mpam_enabled); + mpam_unregister_irqs(); =20 idx =3D srcu_read_lock(&mpam_srcu); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index c9418c9cf9f2..3476ee97f8ac 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,13 @@ #include #include =20 +DECLARE_STATIC_KEY_FALSE(mpam_enabled); + +static inline bool mpam_is_enabled(void) +{ + return static_branch_likely(&mpam_enabled); +} + /* * Structures protected by SRCU may not be freed for a surprising amount of * time (especially if perf is running). To ensure the MPAM error interrup= t can --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7810B238C3A; Fri, 22 Aug 2025 15:36:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876989; cv=none; b=i12Vn9+Z357F0hCRKq+0cKfsFRiusZjvTNTEFZBF79+K8fRKk+oXxJ2tXeUwksth1tNtx9SV0n5elhISzuNbWdFo9gA+oyPz4VN1L4giuDcM2N+abbJtVAZye38cXPLXIYVCMr8vXHSg3i2AH+f+pcATETKzgNp/6NC5FWiNFNc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876989; c=relaxed/simple; bh=TihBLORAFE4lrQMsSm4i/TF11ZiZA36p7IhI3+Wg0d4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j6Y7Mg1GlgEbSDBAoBOBkJFJQmu6ZUPXuB9NbRo6v7pfT0GUXkHYFlG6tIx6SIJE6miV4Hdg7VZVrrexmiNJcTG0+qko1I7ifvFEFKTBEWxDqpQxqOVMzVbPQ2OY6Y+je4fp9Y9wit/YtEBPMDXhlws8CukVArSikk4TIWZA/+Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BCFA415A1; Fri, 22 Aug 2025 08:36:19 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id AEDE13F63F; Fri, 22 Aug 2025 08:36:22 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 23/33] arm_mpam: Use a static key to indicate when mpam is enabled Date: Fri, 22 Aug 2025 15:30:38 +0000 Message-Id: <20250822153048.2287-58-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Once all the MSC have been probed, the system wide usable number of PARTID is known and the configuration arrays can be allocated. After this point, checking all the MSC have been probed is pointless, and the cpuhp callbacks should restore the configuration, instead of just resetting the MSC. Add a static key to enable this behaviour. This will also allow MPAM to be disabled in repsonse to an error, and the architecture code to enable/disable the context switch of the MPAM system registers. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 8 ++++++++ drivers/resctrl/mpam_internal.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 210d64fad0b1..b424af666b1e 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -33,6 +33,8 @@ =20 #include "mpam_internal.h" =20 +DEFINE_STATIC_KEY_FALSE(mpam_enabled); /* TODO: move to arch code */ + /* * mpam_list_lock protects the SRCU lists when writing. Once the * mpam_enabled key is enabled these lists are read-only, @@ -1039,6 +1041,9 @@ static int mpam_discovery_cpu_online(unsigned int cpu) struct mpam_msc *msc; bool new_device_probed =3D false; =20 + if (mpam_is_enabled()) + return 0; + mutex_lock(&mpam_list_lock); list_for_each_entry(msc, &mpam_all_msc, glbl_list) { if (!cpumask_test_cpu(cpu, &msc->accessibility)) @@ -1835,6 +1840,7 @@ static void mpam_enable_once(void) return; } =20 + static_branch_enable(&mpam_enabled); mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline); =20 printk(KERN_INFO "MPAM enabled with %u partid and %u pmg\n", @@ -1902,6 +1908,8 @@ static irqreturn_t mpam_disable_thread(int irq, void = *dev_id) } mutex_unlock(&mpam_cpuhp_state_lock); =20 + static_branch_disable(&mpam_enabled); + mpam_unregister_irqs(); =20 idx =3D srcu_read_lock(&mpam_srcu); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index c9418c9cf9f2..3476ee97f8ac 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,13 @@ #include #include =20 +DECLARE_STATIC_KEY_FALSE(mpam_enabled); + +static inline bool mpam_is_enabled(void) +{ + return static_branch_likely(&mpam_enabled); +} + /* * Structures protected by SRCU may not be freed for a surprising amount of * time (especially if perf is running). To ensure the MPAM error interrup= t can --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 30CF32868BA; Fri, 22 Aug 2025 15:36:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876996; cv=none; b=PCxvbgo2/Hi0Wys6h52XAoO3kvcDCdftsZy2pVD39OgWoGqqRkBWfF8BabF9NMvYRa6ok6keEj0+XW//x7tK/tNsKYXgkesldnazu5IzEKlije/HVYCDkRoykndRTiLrxmMYGPxQQyRkXZAnl+RDZ89asBk453MErOQ3nyWucyM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876996; c=relaxed/simple; bh=CRhrC3ZbKIGMPYdRZ8EQWDJ/EHkyydISyEnpyH4g6Oc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LuPbWJvU+PyZKmSVbgg/0TVaw8H0rBoeejP0cJqet9OP2DF7H0+YFY4cdQ9GopcxedAS9lrCPBdS/mBnUsVX5AD1xMInGi/NsFBu76RDf32Uq2PfFHWde2v5R+WKKjJYqwB2HCFUgfjyRiE4x2pRe5TVv/m56flh0avFfxzE/F0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 853AF15A1; Fri, 22 Aug 2025 08:36:25 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 533983F63F; Fri, 22 Aug 2025 08:36:28 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Dave Martin Subject: [PATCH 24/33] arm_mpam: Allow configuration to be applied and restored during cpu online Date: Fri, 22 Aug 2025 15:30:39 +0000 Message-Id: <20250822153048.2287-59-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When CPUs come online the original configuration should be restored. Once the maximum partid is known, allocate an configuration array for each component, and reprogram each RIS configuration from this. The MPAM spec describes how multiple controls can interact. To prevent this happening by accident, always reset controls that don't have a valid configuration. This allows the same helper to be used for configuration and reset. CC: Dave Martin Signed-off-by: James Morse --- Changes since RFC: * Added a comment about the ordering around max_partid. * Allocate configurations after interrupts are registered to reduce churn. * Added mpam_assert_partid_sizes_fixed(); --- drivers/resctrl/mpam_devices.c | 253 +++++++++++++++++++++++++++++--- drivers/resctrl/mpam_internal.h | 26 +++- 2 files changed, 251 insertions(+), 28 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index b424af666b1e..8f6df2406c22 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -112,6 +112,16 @@ LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */ static LLIST_HEAD(mpam_garbage); =20 +/* + * Once mpam is enabled, new requestors cannot further reduce the available + * partid. Assert that the size is fixed, and new requestors will be turned + * away. + */ +static void mpam_assert_partid_sizes_fixed(void) +{ + WARN_ON_ONCE(!partid_max_published); +} + static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) { WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); @@ -374,12 +384,16 @@ static void mpam_class_destroy(struct mpam_class *cla= ss) add_to_garbage(class); } =20 +static void __destroy_component_cfg(struct mpam_component *comp); + static void mpam_comp_destroy(struct mpam_component *comp) { struct mpam_class *class =3D comp->class; =20 lockdep_assert_held(&mpam_list_lock); =20 + __destroy_component_cfg(comp); + list_del_rcu(&comp->class_list); add_to_garbage(comp); =20 @@ -911,51 +925,90 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *ms= c, u16 reg, u16 wd) __mpam_write_reg(msc, reg, bm); } =20 -static void mpam_reset_ris_partid(struct mpam_msc_ris *ris, u16 partid) +/* Called via IPI. Call while holding an SRCU reference */ +static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, + struct mpam_config *cfg) { u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *rprops =3D &ris->props; =20 - mpam_assert_srcu_read_lock_held(); - mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); =20 - if (mpam_has_feature(mpam_feat_cpor_part, rprops)) - mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) { + if (mpam_has_feature(mpam_feat_cpor_part, cfg)) + mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, + rprops->cpbm_wd); + } =20 - if (mpam_has_feature(mpam_feat_mbw_part, rprops)) - mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); + if (mpam_has_feature(mpam_feat_mbw_part, rprops)) { + if (mpam_has_feature(mpam_feat_mbw_part, cfg)) + mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, + rprops->mbw_pbm_bits); + } =20 if (mpam_has_feature(mpam_feat_mbw_min, rprops)) mpam_write_partsel_reg(msc, MBW_MIN, 0); =20 - if (mpam_has_feature(mpam_feat_mbw_max, rprops)) - mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + if (mpam_has_feature(mpam_feat_mbw_max, rprops)) { + if (mpam_has_feature(mpam_feat_mbw_max, cfg)) + mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max); + else + mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + } =20 if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); mutex_unlock(&msc->part_sel_lock); } =20 +struct reprogram_ris { + struct mpam_msc_ris *ris; + struct mpam_config *cfg; +}; + +/* Call with MSC lock held */ +static int mpam_reprogram_ris(void *_arg) +{ + u16 partid, partid_max; + struct reprogram_ris *arg =3D _arg; + struct mpam_msc_ris *ris =3D arg->ris; + struct mpam_config *cfg =3D arg->cfg; + + if (ris->in_reset_state) + return 0; + + spin_lock(&partid_max_lock); + partid_max =3D mpam_partid_max; + spin_unlock(&partid_max_lock); + for (partid =3D 0; partid <=3D partid_max; partid++) + mpam_reprogram_ris_partid(ris, partid, cfg); + + return 0; +} + /* * Called via smp_call_on_cpu() to prevent migration, while still being * pre-emptible. */ static int mpam_reset_ris(void *arg) { - u16 partid, partid_max; struct mpam_msc_ris *ris =3D arg; + struct reprogram_ris reprogram_arg; + struct mpam_config empty_cfg =3D { 0 }; =20 if (ris->in_reset_state) return 0; =20 - spin_lock(&partid_max_lock); - partid_max =3D mpam_partid_max; - spin_unlock(&partid_max_lock); - for (partid =3D 0; partid < partid_max; partid++) - mpam_reset_ris_partid(ris, partid); + reprogram_arg.ris =3D ris; + reprogram_arg.cfg =3D &empty_cfg; + + mpam_reprogram_ris(&reprogram_arg); =20 return 0; } @@ -986,13 +1039,11 @@ static int mpam_touch_msc(struct mpam_msc *msc, int = (*fn)(void *a), void *arg) =20 static void mpam_reset_msc(struct mpam_msc *msc, bool online) { - int idx; struct mpam_msc_ris *ris; =20 mpam_assert_srcu_read_lock_held(); =20 mpam_mon_sel_outer_lock(msc); - idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { mpam_touch_msc(msc, &mpam_reset_ris, ris); =20 @@ -1002,10 +1053,42 @@ static void mpam_reset_msc(struct mpam_msc *msc, bo= ol online) */ ris->in_reset_state =3D online; } - srcu_read_unlock(&mpam_srcu, idx); mpam_mon_sel_outer_unlock(msc); } =20 +static void mpam_reprogram_msc(struct mpam_msc *msc) +{ + u16 partid; + bool reset; + struct mpam_config *cfg; + struct mpam_msc_ris *ris; + + /* + * No lock for mpam_partid_max as partid_max_published has been + * set by mpam_enabled(), so the values can no longer change. + */ + mpam_assert_partid_sizes_fixed(); + + guard(srcu)(&mpam_srcu); + list_for_each_entry_rcu(ris, &msc->ris, msc_list) { + if (!mpam_is_enabled() && !ris->in_reset_state) { + mpam_touch_msc(msc, &mpam_reset_ris, ris); + ris->in_reset_state =3D true; + continue; + } + + reset =3D true; + for (partid =3D 0; partid <=3D mpam_partid_max; partid++) { + cfg =3D &ris->vmsc->comp->cfg[partid]; + if (cfg->features) + reset =3D false; + + mpam_reprogram_ris_partid(ris, partid, cfg); + } + ris->in_reset_state =3D reset; + } +} + static void _enable_percpu_irq(void *_irq) { int *irq =3D _irq; @@ -1027,7 +1110,7 @@ static int mpam_cpu_online(unsigned int cpu) _enable_percpu_irq(&msc->reenable_error_ppi); =20 if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) - mpam_reset_msc(msc, true); + mpam_reprogram_msc(msc); } srcu_read_unlock(&mpam_srcu, idx); =20 @@ -1807,6 +1890,45 @@ static void mpam_unregister_irqs(void) cpus_read_unlock(); } =20 +static void __destroy_component_cfg(struct mpam_component *comp) +{ + add_to_garbage(comp->cfg); +} + +static int __allocate_component_cfg(struct mpam_component *comp) +{ + mpam_assert_partid_sizes_fixed(); + + if (comp->cfg) + return 0; + + comp->cfg =3D kcalloc(mpam_partid_max + 1, sizeof(*comp->cfg), GFP_KERNEL= ); + if (!comp->cfg) + return -ENOMEM; + init_garbage(comp->cfg); + + return 0; +} + +static int mpam_allocate_config(void) +{ + int err =3D 0; + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, &mpam_classes, classes_list) { + list_for_each_entry(comp, &class->components, class_list) { + err =3D __allocate_component_cfg(comp); + if (err) + return err; + } + } + + return 0; +} + static void mpam_enable_once(void) { int err; @@ -1826,12 +1948,21 @@ static void mpam_enable_once(void) */ cpus_read_lock(); mutex_lock(&mpam_list_lock); - mpam_enable_merge_features(&mpam_classes); + do { + mpam_enable_merge_features(&mpam_classes); =20 - err =3D mpam_register_irqs(); - if (err) - pr_warn("Failed to register irqs: %d\n", err); + err =3D mpam_register_irqs(); + if (err) { + pr_warn("Failed to register irqs: %d\n", err); + break; + } =20 + err =3D mpam_allocate_config(); + if (err) { + pr_err("Failed to allocate configuration arrays.\n"); + break; + } + } while (0); mutex_unlock(&mpam_list_lock); cpus_read_unlock(); =20 @@ -1856,6 +1987,9 @@ static void mpam_reset_component_locked(struct mpam_c= omponent *comp) =20 might_sleep(); lockdep_assert_cpus_held(); + mpam_assert_partid_sizes_fixed(); + + memset(comp->cfg, 0, (mpam_partid_max * sizeof(*comp->cfg))); =20 idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { @@ -1960,6 +2094,79 @@ void mpam_enable(struct work_struct *work) mpam_enable_once(); } =20 +struct mpam_write_config_arg { + struct mpam_msc_ris *ris; + struct mpam_component *comp; + u16 partid; +}; + +static int __write_config(void *arg) +{ + struct mpam_write_config_arg *c =3D arg; + + mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]); + + return 0; +} + +#define maybe_update_config(cfg, feature, newcfg, member, changes) do { \ + if (mpam_has_feature(feature, newcfg) && \ + (newcfg)->member !=3D (cfg)->member) { \ + (cfg)->member =3D (newcfg)->member; \ + cfg->features |=3D (1 << feature); \ + \ + (changes) |=3D (1 << feature); \ + } \ +} while (0) + +static mpam_features_t mpam_update_config(struct mpam_config *cfg, + const struct mpam_config *newcfg) +{ + mpam_features_t changes =3D 0; + + maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, changes); + maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, changes); + maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, changes); + + return changes; +} + +/* TODO: split into write_config/sync_config */ +/* TODO: add config_dirty bitmap to drive sync_config */ +int mpam_apply_config(struct mpam_component *comp, u16 partid, + struct mpam_config *cfg) +{ + struct mpam_write_config_arg arg; + struct mpam_msc_ris *ris; + struct mpam_vmsc *vmsc; + struct mpam_msc *msc; + int idx; + + lockdep_assert_cpus_held(); + + /* Don't pass in the current config! */ + WARN_ON_ONCE(&comp->cfg[partid] =3D=3D cfg); + + if (!mpam_update_config(&comp->cfg[partid], cfg)) + return 0; + + arg.comp =3D comp; + arg.partid =3D partid; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + arg.ris =3D ris; + mpam_touch_msc(msc, __write_config, &arg); + } + } + srcu_read_unlock(&mpam_srcu, idx); + + return 0; +} + /* * MSC that are hidden under caches are not created as platform devices * as there is no cache driver. Caches are also special-cased in diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 3476ee97f8ac..70cba9f22746 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -191,11 +191,7 @@ struct mpam_props { u16 num_mbwu_mon; }; =20 -static inline bool mpam_has_feature(enum mpam_device_features feat, - struct mpam_props *props) -{ - return (1 << feat) & props->features; -} +#define mpam_has_feature(_feat, x) ((1 << (_feat)) & (x)->features) =20 static inline void mpam_set_feature(enum mpam_device_features feat, struct mpam_props *props) @@ -226,6 +222,17 @@ struct mpam_class { struct mpam_garbage garbage; }; =20 +struct mpam_config { + /* Which configuration values are valid. 0 is used for reset */ + mpam_features_t features; + + u32 cpbm; + u32 mbw_pbm; + u16 mbw_max; + + struct mpam_garbage garbage; +}; + struct mpam_component { u32 comp_id; =20 @@ -234,6 +241,12 @@ struct mpam_component { =20 cpumask_t affinity; =20 + /* + * Array of configuration values, indexed by partid. + * Read from cpuhp callbacks, hold the cpuhp lock when writing. + */ + struct mpam_config *cfg; + /* member of mpam_class:components */ struct list_head class_list; =20 @@ -298,6 +311,9 @@ extern u8 mpam_pmg_max; void mpam_enable(struct work_struct *work); void mpam_disable(struct work_struct *work); =20 +int mpam_apply_config(struct mpam_component *comp, u16 partid, + struct mpam_config *cfg); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8F98423D297; Fri, 22 Aug 2025 15:33:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876804; cv=none; b=AO7Pqj/OVAcgi1yxg2hKL5VRwMM1nJED4s4SjJ7ZyF7n9pkgZmi6KYaX0pLD9OceH4fzG+HomY8v6McllCMjPJw8lUlWAysKUGbuzR/RW/XDVp59eaaiCVAURWMGApVWCP4vXfwPqtN9c8mYGt1ofKnBCPJk3Td8V2nqf5UHlPc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876804; c=relaxed/simple; bh=CRhrC3ZbKIGMPYdRZ8EQWDJ/EHkyydISyEnpyH4g6Oc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ATS2vkopoFcEqyBtx+sNV6fRD4uIvcNd5o4vtSU33UdNgmHxk2puihrI4WVRYAfobNmVLtrbJqEBzqhJiI0VxTgfcJiZiio15g9LcLneZTM63LYEwUGfYw8GXQhtfSG24zA5uSwDaJuVZYWrpjL5WpHe+hzSulpUkoKxZrwbgFQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9F60F27B5; Fri, 22 Aug 2025 08:33:13 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 714993F63F; Fri, 22 Aug 2025 08:33:16 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Dave Martin Subject: [PATCH 24/33] arm_mpam: Allow configuration to be applied and restored during cpu online Date: Fri, 22 Aug 2025 15:30:05 +0000 Message-Id: <20250822153048.2287-25-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When CPUs come online the original configuration should be restored. Once the maximum partid is known, allocate an configuration array for each component, and reprogram each RIS configuration from this. The MPAM spec describes how multiple controls can interact. To prevent this happening by accident, always reset controls that don't have a valid configuration. This allows the same helper to be used for configuration and reset. CC: Dave Martin Signed-off-by: James Morse --- Changes since RFC: * Added a comment about the ordering around max_partid. * Allocate configurations after interrupts are registered to reduce churn. * Added mpam_assert_partid_sizes_fixed(); --- drivers/resctrl/mpam_devices.c | 253 +++++++++++++++++++++++++++++--- drivers/resctrl/mpam_internal.h | 26 +++- 2 files changed, 251 insertions(+), 28 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index b424af666b1e..8f6df2406c22 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -112,6 +112,16 @@ LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */ static LLIST_HEAD(mpam_garbage); =20 +/* + * Once mpam is enabled, new requestors cannot further reduce the available + * partid. Assert that the size is fixed, and new requestors will be turned + * away. + */ +static void mpam_assert_partid_sizes_fixed(void) +{ + WARN_ON_ONCE(!partid_max_published); +} + static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) { WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); @@ -374,12 +384,16 @@ static void mpam_class_destroy(struct mpam_class *cla= ss) add_to_garbage(class); } =20 +static void __destroy_component_cfg(struct mpam_component *comp); + static void mpam_comp_destroy(struct mpam_component *comp) { struct mpam_class *class =3D comp->class; =20 lockdep_assert_held(&mpam_list_lock); =20 + __destroy_component_cfg(comp); + list_del_rcu(&comp->class_list); add_to_garbage(comp); =20 @@ -911,51 +925,90 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *ms= c, u16 reg, u16 wd) __mpam_write_reg(msc, reg, bm); } =20 -static void mpam_reset_ris_partid(struct mpam_msc_ris *ris, u16 partid) +/* Called via IPI. Call while holding an SRCU reference */ +static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, + struct mpam_config *cfg) { u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *rprops =3D &ris->props; =20 - mpam_assert_srcu_read_lock_held(); - mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); =20 - if (mpam_has_feature(mpam_feat_cpor_part, rprops)) - mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd); + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) { + if (mpam_has_feature(mpam_feat_cpor_part, cfg)) + mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, + rprops->cpbm_wd); + } =20 - if (mpam_has_feature(mpam_feat_mbw_part, rprops)) - mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits); + if (mpam_has_feature(mpam_feat_mbw_part, rprops)) { + if (mpam_has_feature(mpam_feat_mbw_part, cfg)) + mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm); + else + mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, + rprops->mbw_pbm_bits); + } =20 if (mpam_has_feature(mpam_feat_mbw_min, rprops)) mpam_write_partsel_reg(msc, MBW_MIN, 0); =20 - if (mpam_has_feature(mpam_feat_mbw_max, rprops)) - mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + if (mpam_has_feature(mpam_feat_mbw_max, rprops)) { + if (mpam_has_feature(mpam_feat_mbw_max, cfg)) + mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max); + else + mpam_write_partsel_reg(msc, MBW_MAX, bwa_fract); + } =20 if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); mutex_unlock(&msc->part_sel_lock); } =20 +struct reprogram_ris { + struct mpam_msc_ris *ris; + struct mpam_config *cfg; +}; + +/* Call with MSC lock held */ +static int mpam_reprogram_ris(void *_arg) +{ + u16 partid, partid_max; + struct reprogram_ris *arg =3D _arg; + struct mpam_msc_ris *ris =3D arg->ris; + struct mpam_config *cfg =3D arg->cfg; + + if (ris->in_reset_state) + return 0; + + spin_lock(&partid_max_lock); + partid_max =3D mpam_partid_max; + spin_unlock(&partid_max_lock); + for (partid =3D 0; partid <=3D partid_max; partid++) + mpam_reprogram_ris_partid(ris, partid, cfg); + + return 0; +} + /* * Called via smp_call_on_cpu() to prevent migration, while still being * pre-emptible. */ static int mpam_reset_ris(void *arg) { - u16 partid, partid_max; struct mpam_msc_ris *ris =3D arg; + struct reprogram_ris reprogram_arg; + struct mpam_config empty_cfg =3D { 0 }; =20 if (ris->in_reset_state) return 0; =20 - spin_lock(&partid_max_lock); - partid_max =3D mpam_partid_max; - spin_unlock(&partid_max_lock); - for (partid =3D 0; partid < partid_max; partid++) - mpam_reset_ris_partid(ris, partid); + reprogram_arg.ris =3D ris; + reprogram_arg.cfg =3D &empty_cfg; + + mpam_reprogram_ris(&reprogram_arg); =20 return 0; } @@ -986,13 +1039,11 @@ static int mpam_touch_msc(struct mpam_msc *msc, int = (*fn)(void *a), void *arg) =20 static void mpam_reset_msc(struct mpam_msc *msc, bool online) { - int idx; struct mpam_msc_ris *ris; =20 mpam_assert_srcu_read_lock_held(); =20 mpam_mon_sel_outer_lock(msc); - idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_srcu(ris, &msc->ris, msc_list, srcu_read_lock_held(&m= pam_srcu)) { mpam_touch_msc(msc, &mpam_reset_ris, ris); =20 @@ -1002,10 +1053,42 @@ static void mpam_reset_msc(struct mpam_msc *msc, bo= ol online) */ ris->in_reset_state =3D online; } - srcu_read_unlock(&mpam_srcu, idx); mpam_mon_sel_outer_unlock(msc); } =20 +static void mpam_reprogram_msc(struct mpam_msc *msc) +{ + u16 partid; + bool reset; + struct mpam_config *cfg; + struct mpam_msc_ris *ris; + + /* + * No lock for mpam_partid_max as partid_max_published has been + * set by mpam_enabled(), so the values can no longer change. + */ + mpam_assert_partid_sizes_fixed(); + + guard(srcu)(&mpam_srcu); + list_for_each_entry_rcu(ris, &msc->ris, msc_list) { + if (!mpam_is_enabled() && !ris->in_reset_state) { + mpam_touch_msc(msc, &mpam_reset_ris, ris); + ris->in_reset_state =3D true; + continue; + } + + reset =3D true; + for (partid =3D 0; partid <=3D mpam_partid_max; partid++) { + cfg =3D &ris->vmsc->comp->cfg[partid]; + if (cfg->features) + reset =3D false; + + mpam_reprogram_ris_partid(ris, partid, cfg); + } + ris->in_reset_state =3D reset; + } +} + static void _enable_percpu_irq(void *_irq) { int *irq =3D _irq; @@ -1027,7 +1110,7 @@ static int mpam_cpu_online(unsigned int cpu) _enable_percpu_irq(&msc->reenable_error_ppi); =20 if (atomic_fetch_inc(&msc->online_refs) =3D=3D 0) - mpam_reset_msc(msc, true); + mpam_reprogram_msc(msc); } srcu_read_unlock(&mpam_srcu, idx); =20 @@ -1807,6 +1890,45 @@ static void mpam_unregister_irqs(void) cpus_read_unlock(); } =20 +static void __destroy_component_cfg(struct mpam_component *comp) +{ + add_to_garbage(comp->cfg); +} + +static int __allocate_component_cfg(struct mpam_component *comp) +{ + mpam_assert_partid_sizes_fixed(); + + if (comp->cfg) + return 0; + + comp->cfg =3D kcalloc(mpam_partid_max + 1, sizeof(*comp->cfg), GFP_KERNEL= ); + if (!comp->cfg) + return -ENOMEM; + init_garbage(comp->cfg); + + return 0; +} + +static int mpam_allocate_config(void) +{ + int err =3D 0; + struct mpam_class *class; + struct mpam_component *comp; + + lockdep_assert_held(&mpam_list_lock); + + list_for_each_entry(class, &mpam_classes, classes_list) { + list_for_each_entry(comp, &class->components, class_list) { + err =3D __allocate_component_cfg(comp); + if (err) + return err; + } + } + + return 0; +} + static void mpam_enable_once(void) { int err; @@ -1826,12 +1948,21 @@ static void mpam_enable_once(void) */ cpus_read_lock(); mutex_lock(&mpam_list_lock); - mpam_enable_merge_features(&mpam_classes); + do { + mpam_enable_merge_features(&mpam_classes); =20 - err =3D mpam_register_irqs(); - if (err) - pr_warn("Failed to register irqs: %d\n", err); + err =3D mpam_register_irqs(); + if (err) { + pr_warn("Failed to register irqs: %d\n", err); + break; + } =20 + err =3D mpam_allocate_config(); + if (err) { + pr_err("Failed to allocate configuration arrays.\n"); + break; + } + } while (0); mutex_unlock(&mpam_list_lock); cpus_read_unlock(); =20 @@ -1856,6 +1987,9 @@ static void mpam_reset_component_locked(struct mpam_c= omponent *comp) =20 might_sleep(); lockdep_assert_cpus_held(); + mpam_assert_partid_sizes_fixed(); + + memset(comp->cfg, 0, (mpam_partid_max * sizeof(*comp->cfg))); =20 idx =3D srcu_read_lock(&mpam_srcu); list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { @@ -1960,6 +2094,79 @@ void mpam_enable(struct work_struct *work) mpam_enable_once(); } =20 +struct mpam_write_config_arg { + struct mpam_msc_ris *ris; + struct mpam_component *comp; + u16 partid; +}; + +static int __write_config(void *arg) +{ + struct mpam_write_config_arg *c =3D arg; + + mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]); + + return 0; +} + +#define maybe_update_config(cfg, feature, newcfg, member, changes) do { \ + if (mpam_has_feature(feature, newcfg) && \ + (newcfg)->member !=3D (cfg)->member) { \ + (cfg)->member =3D (newcfg)->member; \ + cfg->features |=3D (1 << feature); \ + \ + (changes) |=3D (1 << feature); \ + } \ +} while (0) + +static mpam_features_t mpam_update_config(struct mpam_config *cfg, + const struct mpam_config *newcfg) +{ + mpam_features_t changes =3D 0; + + maybe_update_config(cfg, mpam_feat_cpor_part, newcfg, cpbm, changes); + maybe_update_config(cfg, mpam_feat_mbw_part, newcfg, mbw_pbm, changes); + maybe_update_config(cfg, mpam_feat_mbw_max, newcfg, mbw_max, changes); + + return changes; +} + +/* TODO: split into write_config/sync_config */ +/* TODO: add config_dirty bitmap to drive sync_config */ +int mpam_apply_config(struct mpam_component *comp, u16 partid, + struct mpam_config *cfg) +{ + struct mpam_write_config_arg arg; + struct mpam_msc_ris *ris; + struct mpam_vmsc *vmsc; + struct mpam_msc *msc; + int idx; + + lockdep_assert_cpus_held(); + + /* Don't pass in the current config! */ + WARN_ON_ONCE(&comp->cfg[partid] =3D=3D cfg); + + if (!mpam_update_config(&comp->cfg[partid], cfg)) + return 0; + + arg.comp =3D comp; + arg.partid =3D partid; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + arg.ris =3D ris; + mpam_touch_msc(msc, __write_config, &arg); + } + } + srcu_read_unlock(&mpam_srcu, idx); + + return 0; +} + /* * MSC that are hidden under caches are not created as platform devices * as there is no cache driver. Caches are also special-cased in diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 3476ee97f8ac..70cba9f22746 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -191,11 +191,7 @@ struct mpam_props { u16 num_mbwu_mon; }; =20 -static inline bool mpam_has_feature(enum mpam_device_features feat, - struct mpam_props *props) -{ - return (1 << feat) & props->features; -} +#define mpam_has_feature(_feat, x) ((1 << (_feat)) & (x)->features) =20 static inline void mpam_set_feature(enum mpam_device_features feat, struct mpam_props *props) @@ -226,6 +222,17 @@ struct mpam_class { struct mpam_garbage garbage; }; =20 +struct mpam_config { + /* Which configuration values are valid. 0 is used for reset */ + mpam_features_t features; + + u32 cpbm; + u32 mbw_pbm; + u16 mbw_max; + + struct mpam_garbage garbage; +}; + struct mpam_component { u32 comp_id; =20 @@ -234,6 +241,12 @@ struct mpam_component { =20 cpumask_t affinity; =20 + /* + * Array of configuration values, indexed by partid. + * Read from cpuhp callbacks, hold the cpuhp lock when writing. + */ + struct mpam_config *cfg; + /* member of mpam_class:components */ struct list_head class_list; =20 @@ -298,6 +311,9 @@ extern u8 mpam_pmg_max; void mpam_enable(struct work_struct *work); void mpam_disable(struct work_struct *work); =20 +int mpam_apply_config(struct mpam_component *comp, u16 partid, + struct mpam_config *cfg); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7B1EC258CD0; Fri, 22 Aug 2025 15:33:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876810; cv=none; b=i4ziNeVl0Md9vX1sfq2xfIYSAJslN1TJxkyHuDS2/rmChSb/BHCitCbHPIUB1aBVjKfTJC/ksSjQg1U6YUKuawMaD0+2avYR062u1IGiyH7LQEPF4lGqg8RsAxGpcekJZlkFsPXMcRgXlrWxul6N/2kUtE3ulE2NkOpzd51aqko= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876810; c=relaxed/simple; bh=mbb9q6KRsWmskHeD1qTbNfGyNp7R51nanlDTdw5ZBrU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ReqUpImRrc1y8DS0cFRW3+JG5DS8lYYlGCvo88WstrwUAp/4uUJTEVXsatuDJn1EmXSQJS7wwGtBVozZcxPAXkMRH66zoCC+ls4VZQgC9eRe6Aq7iUjmrU5pfMF6reDxLC6E0+ypArx9At2d+2XOLsZcJp9Rxg0MymFk53e9Rhw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9B9B627DD; Fri, 22 Aug 2025 08:33:19 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2CDA43F63F; Fri, 22 Aug 2025 08:33:22 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew , Zeng Heng , Dave Martin Subject: [PATCH 25/33] arm_mpam: Probe and reset the rest of the features Date: Fri, 22 Aug 2025 15:30:06 +0000 Message-Id: <20250822153048.2287-26-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM supports more features than are going to be exposed to resctrl. For partid other than 0, the reset values of these controls isn't known. Discover the rest of the features so they can be reset to avoid any side effects when resctrl is in use. PARTID narrowing allows MSC/RIS to support less configuration space than is usable. If this feature is found on a class of device we are likely to use, then reduce the partid_max to make it usable. This allows us to map a PARTID to itself. CC: Rohit Mathew CC: Zeng Heng CC: Dave Martin Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 175 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 16 ++- 2 files changed, 189 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 8f6df2406c22..aedd743d6827 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -213,6 +213,15 @@ static void __mpam_part_sel(u8 ris_idx, u16 partid, st= ruct mpam_msc *msc) __mpam_part_sel_raw(partsel, msc); } =20 +static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc = *msc) +{ + u32 partsel =3D FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | + FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) | + MPAMCFG_PART_SEL_INTERNAL; + + __mpam_part_sel_raw(partsel, msc); +} + int mpam_register_requestor(u16 partid_max, u8 pmg_max) { int err =3D 0; @@ -743,10 +752,35 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ri= s) int err; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *props =3D &ris->props; + struct mpam_class *class =3D ris->vmsc->comp->class; =20 lockdep_assert_held(&msc->probe_lock); lockdep_assert_held(&msc->part_sel_lock); =20 + /* Cache Capacity Partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) { + u32 ccap_features =3D mpam_read_partsel_reg(msc, CCAP_IDR); + + props->cmax_wd =3D FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features); + if (props->cmax_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM, ccap_features)) + mpam_set_feature(mpam_feat_cmax_softlim, props); + + if (props->cmax_wd && + !FIELD_GET(MPAMF_CCAP_IDR_NO_CMAX, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cmax, props); + + if (props->cmax_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CMIN, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cmin, props); + + props->cassoc_wd =3D FIELD_GET(MPAMF_CCAP_IDR_CASSOC_WD, ccap_features); + + if (props->cassoc_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CASSOC, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cassoc, props); + } + /* Cache Portion partitioning */ if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { u32 cpor_features =3D mpam_read_partsel_reg(msc, CPOR_IDR); @@ -769,6 +803,31 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) props->bwa_wd =3D FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) mpam_set_feature(mpam_feat_mbw_max, props); + + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features)) + mpam_set_feature(mpam_feat_mbw_min, props); + + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_PROP, mbw_features)) + mpam_set_feature(mpam_feat_mbw_prop, props); + } + + /* Priority partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) { + u32 pri_features =3D mpam_read_partsel_reg(msc, PRI_IDR); + + props->intpri_wd =3D FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features); + if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features= )) { + mpam_set_feature(mpam_feat_intpri_part, props); + if (FIELD_GET(MPAMF_PRI_IDR_INTPRI_0_IS_LOW, pri_features)) + mpam_set_feature(mpam_feat_intpri_part_0_low, props); + } + + props->dspri_wd =3D FIELD_GET(MPAMF_PRI_IDR_DSPRI_WD, pri_features); + if (props->dspri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_DSPRI, pri_features))= { + mpam_set_feature(mpam_feat_dspri_part, props); + if (FIELD_GET(MPAMF_PRI_IDR_DSPRI_0_IS_LOW, pri_features)) + mpam_set_feature(mpam_feat_dspri_part_0_low, props); + } } =20 /* Performance Monitoring */ @@ -832,6 +891,21 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) */ } } + + /* + * RIS with PARTID narrowing don't have enough storage for one + * configuration per PARTID. If these are in a class we could use, + * reduce the supported partid_max to match the number of intpartid. + * If the class is unknown, just ignore it. + */ + if (FIELD_GET(MPAMF_IDR_HAS_PARTID_NRW, ris->idr) && + class->type !=3D MPAM_CLASS_UNKNOWN) { + u32 nrwidr =3D mpam_read_partsel_reg(msc, PARTID_NRW_IDR); + u16 partid_max =3D FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr); + + mpam_set_feature(mpam_feat_partid_nrw, props); + msc->partid_max =3D min(msc->partid_max, partid_max); + } } =20 static int mpam_msc_hw_probe(struct mpam_msc *msc) @@ -929,13 +1003,29 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *m= sc, u16 reg, u16 wd) static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, struct mpam_config *cfg) { + u32 pri_val =3D 0; + u16 cmax =3D MPAMCFG_CMAX_CMAX; u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *rprops =3D &ris->props; + u16 dspri =3D GENMASK(rprops->dspri_wd, 0); + u16 intpri =3D GENMASK(rprops->intpri_wd, 0); =20 mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); =20 + if (mpam_has_feature(mpam_feat_partid_nrw, rprops)) { + /* Update the intpartid mapping */ + mpam_write_partsel_reg(msc, INTPARTID, + MPAMCFG_INTPARTID_INTERNAL | partid); + + /* + * Then switch to the 'internal' partid to update the + * configuration. + */ + __mpam_intpart_sel(ris->ris_idx, partid, msc); + } + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) { if (mpam_has_feature(mpam_feat_cpor_part, cfg)) mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); @@ -964,6 +1054,29 @@ static void mpam_reprogram_ris_partid(struct mpam_msc= _ris *ris, u16 partid, =20 if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); + + if (mpam_has_feature(mpam_feat_cmax_cmax, rprops)) + mpam_write_partsel_reg(msc, CMAX, cmax); + + if (mpam_has_feature(mpam_feat_cmax_cmin, rprops)) + mpam_write_partsel_reg(msc, CMIN, 0); + + if (mpam_has_feature(mpam_feat_intpri_part, rprops) || + mpam_has_feature(mpam_feat_dspri_part, rprops)) { + /* aces high? */ + if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) + intpri =3D 0; + if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) + dspri =3D 0; + + if (mpam_has_feature(mpam_feat_intpri_part, rprops)) + pri_val |=3D FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri); + if (mpam_has_feature(mpam_feat_dspri_part, rprops)) + pri_val |=3D FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri); + + mpam_write_partsel_reg(msc, PRI, pri_val); + } + mutex_unlock(&msc->part_sel_lock); } =20 @@ -1529,6 +1642,16 @@ static bool mpam_has_bwa_wd_feature(struct mpam_prop= s *props) return false; } =20 +/* Any of these features mean the CMAX_WD field is valid. */ +static bool mpam_has_cmax_wd_feature(struct mpam_props *props) +{ + if (mpam_has_feature(mpam_feat_cmax_cmax, props)) + return true; + if (mpam_has_feature(mpam_feat_cmax_cmin, props)) + return true; + return false; +} + #define MISMATCHED_HELPER(parent, child, helper, field, alias) \ helper(parent) && \ ((helper(child) && (parent)->field !=3D (child)->field) || \ @@ -1583,6 +1706,23 @@ static void __props_mismatch(struct mpam_props *pare= nt, parent->bwa_wd =3D min(parent->bwa_wd, child->bwa_wd); } =20 + if (alias && !mpam_has_cmax_wd_feature(parent) && mpam_has_cmax_wd_featur= e(child)) { + parent->cmax_wd =3D child->cmax_wd; + } else if (MISMATCHED_HELPER(parent, child, mpam_has_cmax_wd_feature, + cmax_wd, alias)) { + pr_debug("%s took the min cmax_wd\n", __func__); + parent->cmax_wd =3D min(parent->cmax_wd, child->cmax_wd); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_cmax_cassoc, alias)) { + parent->cassoc_wd =3D child->cassoc_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cmax_cassoc, + cassoc_wd, alias)) { + pr_debug("%s cleared cassoc_wd\n", __func__); + mpam_clear_feature(mpam_feat_cmax_cassoc, &parent->features); + parent->cassoc_wd =3D 0; + } + /* For num properties, take the minimum */ if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { parent->num_csu_mon =3D child->num_csu_mon; @@ -1600,6 +1740,41 @@ static void __props_mismatch(struct mpam_props *pare= nt, parent->num_mbwu_mon =3D min(parent->num_mbwu_mon, child->num_mbwu_mon); } =20 + if (CAN_MERGE_FEAT(parent, child, mpam_feat_intpri_part, alias)) { + parent->intpri_wd =3D child->intpri_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_intpri_part, + intpri_wd, alias)) { + pr_debug("%s took the min intpri_wd\n", __func__); + parent->intpri_wd =3D min(parent->intpri_wd, child->intpri_wd); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_dspri_part, alias)) { + parent->dspri_wd =3D child->dspri_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_dspri_part, + dspri_wd, alias)) { + pr_debug("%s took the min dspri_wd\n", __func__); + parent->dspri_wd =3D min(parent->dspri_wd, child->dspri_wd); + } + + /* TODO: alias support for these two */ + /* {int,ds}pri may not have differing 0-low behaviour */ + if (mpam_has_feature(mpam_feat_intpri_part, parent) && + (!mpam_has_feature(mpam_feat_intpri_part, child) || + mpam_has_feature(mpam_feat_intpri_part_0_low, parent) !=3D + mpam_has_feature(mpam_feat_intpri_part_0_low, child))) { + pr_debug("%s cleared intpri_part\n", __func__); + mpam_clear_feature(mpam_feat_intpri_part, &parent->features); + mpam_clear_feature(mpam_feat_intpri_part_0_low, &parent->features); + } + if (mpam_has_feature(mpam_feat_dspri_part, parent) && + (!mpam_has_feature(mpam_feat_dspri_part, child) || + mpam_has_feature(mpam_feat_dspri_part_0_low, parent) !=3D + mpam_has_feature(mpam_feat_dspri_part_0_low, child))) { + pr_debug("%s cleared dspri_part\n", __func__); + mpam_clear_feature(mpam_feat_dspri_part, &parent->features); + mpam_clear_feature(mpam_feat_dspri_part_0_low, &parent->features); + } + if (alias) { /* Merge features for aliased resources */ parent->features |=3D child->features; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 70cba9f22746..23445aedbabd 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -157,16 +157,23 @@ static inline void mpam_mon_sel_lock_held(struct mpam= _msc *msc) * When we compact the supported features, we don't care what they are. * Storing them as a bitmap makes life easy. */ -typedef u16 mpam_features_t; +typedef u32 mpam_features_t; =20 /* Bits for mpam_features_t */ enum mpam_device_features { - mpam_feat_ccap_part =3D 0, + mpam_feat_cmax_softlim, + mpam_feat_cmax_cmax, + mpam_feat_cmax_cmin, + mpam_feat_cmax_cassoc, mpam_feat_cpor_part, mpam_feat_mbw_part, mpam_feat_mbw_min, mpam_feat_mbw_max, mpam_feat_mbw_prop, + mpam_feat_intpri_part, + mpam_feat_intpri_part_0_low, + mpam_feat_dspri_part, + mpam_feat_dspri_part_0_low, mpam_feat_msmon, mpam_feat_msmon_csu, mpam_feat_msmon_csu_capture, @@ -176,6 +183,7 @@ enum mpam_device_features { mpam_feat_msmon_mbwu_rwbw, mpam_feat_msmon_mbwu_hw_nrdy, mpam_feat_msmon_capt, + mpam_feat_partid_nrw, MPAM_FEATURE_LAST, }; static_assert(BITS_PER_TYPE(mpam_features_t) >=3D MPAM_FEATURE_LAST); @@ -187,6 +195,10 @@ struct mpam_props { u16 cpbm_wd; u16 mbw_pbm_bits; u16 bwa_wd; + u16 cmax_wd; + u16 cassoc_wd; + u16 intpri_wd; + u16 dspri_wd; u16 num_csu_mon; u16 num_mbwu_mon; }; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3E51634F47E; Fri, 22 Aug 2025 15:36:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877002; cv=none; b=WNVKaFO9HvzBzJ9eZVHI1Yf2Ov/5gcULFLvF/u78SDy5XCkz05BAq6bRDGGYDiyA7YcMEtTWZmd7CyadXgsr9GbrXvZND+k9o6GsXj0l39/CWLn/c9NPMqfcfcwYIAYMxIQ/WwylutM8gVZ+GGn+ULZ8Wkzsiur9rVOIXmh/r0w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877002; c=relaxed/simple; bh=mbb9q6KRsWmskHeD1qTbNfGyNp7R51nanlDTdw5ZBrU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tPbHZ3wc4WEbiVY93fOTy3/65Uy3gZAsjtM8C3bYr3x/NPbyCpECtHrbQZ3RhuF/rRD6xEfP3C1Zxf9Nv98uUWUzHQ25cJy9Rb0vvavim/VIm/WB8D8as7XhJOij/9lFppGBfXnORacQplZjLmD/G4I4lEmsm09AW8PSE9rXhyE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 87F5C2880; Fri, 22 Aug 2025 08:36:31 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 140583F63F; Fri, 22 Aug 2025 08:36:33 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Rohit Mathew , Zeng Heng , Dave Martin Subject: [PATCH 25/33] arm_mpam: Probe and reset the rest of the features Date: Fri, 22 Aug 2025 15:30:40 +0000 Message-Id: <20250822153048.2287-60-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM supports more features than are going to be exposed to resctrl. For partid other than 0, the reset values of these controls isn't known. Discover the rest of the features so they can be reset to avoid any side effects when resctrl is in use. PARTID narrowing allows MSC/RIS to support less configuration space than is usable. If this feature is found on a class of device we are likely to use, then reduce the partid_max to make it usable. This allows us to map a PARTID to itself. CC: Rohit Mathew CC: Zeng Heng CC: Dave Martin Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 175 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 16 ++- 2 files changed, 189 insertions(+), 2 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 8f6df2406c22..aedd743d6827 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -213,6 +213,15 @@ static void __mpam_part_sel(u8 ris_idx, u16 partid, st= ruct mpam_msc *msc) __mpam_part_sel_raw(partsel, msc); } =20 +static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc = *msc) +{ + u32 partsel =3D FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | + FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) | + MPAMCFG_PART_SEL_INTERNAL; + + __mpam_part_sel_raw(partsel, msc); +} + int mpam_register_requestor(u16 partid_max, u8 pmg_max) { int err =3D 0; @@ -743,10 +752,35 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ri= s) int err; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *props =3D &ris->props; + struct mpam_class *class =3D ris->vmsc->comp->class; =20 lockdep_assert_held(&msc->probe_lock); lockdep_assert_held(&msc->part_sel_lock); =20 + /* Cache Capacity Partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) { + u32 ccap_features =3D mpam_read_partsel_reg(msc, CCAP_IDR); + + props->cmax_wd =3D FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features); + if (props->cmax_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM, ccap_features)) + mpam_set_feature(mpam_feat_cmax_softlim, props); + + if (props->cmax_wd && + !FIELD_GET(MPAMF_CCAP_IDR_NO_CMAX, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cmax, props); + + if (props->cmax_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CMIN, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cmin, props); + + props->cassoc_wd =3D FIELD_GET(MPAMF_CCAP_IDR_CASSOC_WD, ccap_features); + + if (props->cassoc_wd && + FIELD_GET(MPAMF_CCAP_IDR_HAS_CASSOC, ccap_features)) + mpam_set_feature(mpam_feat_cmax_cassoc, props); + } + /* Cache Portion partitioning */ if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) { u32 cpor_features =3D mpam_read_partsel_reg(msc, CPOR_IDR); @@ -769,6 +803,31 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) props->bwa_wd =3D FIELD_GET(MPAMF_MBW_IDR_BWA_WD, mbw_features); if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MAX, mbw_features)) mpam_set_feature(mpam_feat_mbw_max, props); + + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_MIN, mbw_features)) + mpam_set_feature(mpam_feat_mbw_min, props); + + if (props->bwa_wd && FIELD_GET(MPAMF_MBW_IDR_HAS_PROP, mbw_features)) + mpam_set_feature(mpam_feat_mbw_prop, props); + } + + /* Priority partitioning */ + if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) { + u32 pri_features =3D mpam_read_partsel_reg(msc, PRI_IDR); + + props->intpri_wd =3D FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features); + if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features= )) { + mpam_set_feature(mpam_feat_intpri_part, props); + if (FIELD_GET(MPAMF_PRI_IDR_INTPRI_0_IS_LOW, pri_features)) + mpam_set_feature(mpam_feat_intpri_part_0_low, props); + } + + props->dspri_wd =3D FIELD_GET(MPAMF_PRI_IDR_DSPRI_WD, pri_features); + if (props->dspri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_DSPRI, pri_features))= { + mpam_set_feature(mpam_feat_dspri_part, props); + if (FIELD_GET(MPAMF_PRI_IDR_DSPRI_0_IS_LOW, pri_features)) + mpam_set_feature(mpam_feat_dspri_part_0_low, props); + } } =20 /* Performance Monitoring */ @@ -832,6 +891,21 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) */ } } + + /* + * RIS with PARTID narrowing don't have enough storage for one + * configuration per PARTID. If these are in a class we could use, + * reduce the supported partid_max to match the number of intpartid. + * If the class is unknown, just ignore it. + */ + if (FIELD_GET(MPAMF_IDR_HAS_PARTID_NRW, ris->idr) && + class->type !=3D MPAM_CLASS_UNKNOWN) { + u32 nrwidr =3D mpam_read_partsel_reg(msc, PARTID_NRW_IDR); + u16 partid_max =3D FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr); + + mpam_set_feature(mpam_feat_partid_nrw, props); + msc->partid_max =3D min(msc->partid_max, partid_max); + } } =20 static int mpam_msc_hw_probe(struct mpam_msc *msc) @@ -929,13 +1003,29 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *m= sc, u16 reg, u16 wd) static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid, struct mpam_config *cfg) { + u32 pri_val =3D 0; + u16 cmax =3D MPAMCFG_CMAX_CMAX; u16 bwa_fract =3D MPAMCFG_MBW_MAX_MAX; struct mpam_msc *msc =3D ris->vmsc->msc; struct mpam_props *rprops =3D &ris->props; + u16 dspri =3D GENMASK(rprops->dspri_wd, 0); + u16 intpri =3D GENMASK(rprops->intpri_wd, 0); =20 mutex_lock(&msc->part_sel_lock); __mpam_part_sel(ris->ris_idx, partid, msc); =20 + if (mpam_has_feature(mpam_feat_partid_nrw, rprops)) { + /* Update the intpartid mapping */ + mpam_write_partsel_reg(msc, INTPARTID, + MPAMCFG_INTPARTID_INTERNAL | partid); + + /* + * Then switch to the 'internal' partid to update the + * configuration. + */ + __mpam_intpart_sel(ris->ris_idx, partid, msc); + } + if (mpam_has_feature(mpam_feat_cpor_part, rprops)) { if (mpam_has_feature(mpam_feat_cpor_part, cfg)) mpam_write_partsel_reg(msc, CPBM, cfg->cpbm); @@ -964,6 +1054,29 @@ static void mpam_reprogram_ris_partid(struct mpam_msc= _ris *ris, u16 partid, =20 if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) mpam_write_partsel_reg(msc, MBW_PROP, bwa_fract); + + if (mpam_has_feature(mpam_feat_cmax_cmax, rprops)) + mpam_write_partsel_reg(msc, CMAX, cmax); + + if (mpam_has_feature(mpam_feat_cmax_cmin, rprops)) + mpam_write_partsel_reg(msc, CMIN, 0); + + if (mpam_has_feature(mpam_feat_intpri_part, rprops) || + mpam_has_feature(mpam_feat_dspri_part, rprops)) { + /* aces high? */ + if (!mpam_has_feature(mpam_feat_intpri_part_0_low, rprops)) + intpri =3D 0; + if (!mpam_has_feature(mpam_feat_dspri_part_0_low, rprops)) + dspri =3D 0; + + if (mpam_has_feature(mpam_feat_intpri_part, rprops)) + pri_val |=3D FIELD_PREP(MPAMCFG_PRI_INTPRI, intpri); + if (mpam_has_feature(mpam_feat_dspri_part, rprops)) + pri_val |=3D FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri); + + mpam_write_partsel_reg(msc, PRI, pri_val); + } + mutex_unlock(&msc->part_sel_lock); } =20 @@ -1529,6 +1642,16 @@ static bool mpam_has_bwa_wd_feature(struct mpam_prop= s *props) return false; } =20 +/* Any of these features mean the CMAX_WD field is valid. */ +static bool mpam_has_cmax_wd_feature(struct mpam_props *props) +{ + if (mpam_has_feature(mpam_feat_cmax_cmax, props)) + return true; + if (mpam_has_feature(mpam_feat_cmax_cmin, props)) + return true; + return false; +} + #define MISMATCHED_HELPER(parent, child, helper, field, alias) \ helper(parent) && \ ((helper(child) && (parent)->field !=3D (child)->field) || \ @@ -1583,6 +1706,23 @@ static void __props_mismatch(struct mpam_props *pare= nt, parent->bwa_wd =3D min(parent->bwa_wd, child->bwa_wd); } =20 + if (alias && !mpam_has_cmax_wd_feature(parent) && mpam_has_cmax_wd_featur= e(child)) { + parent->cmax_wd =3D child->cmax_wd; + } else if (MISMATCHED_HELPER(parent, child, mpam_has_cmax_wd_feature, + cmax_wd, alias)) { + pr_debug("%s took the min cmax_wd\n", __func__); + parent->cmax_wd =3D min(parent->cmax_wd, child->cmax_wd); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_cmax_cassoc, alias)) { + parent->cassoc_wd =3D child->cassoc_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_cmax_cassoc, + cassoc_wd, alias)) { + pr_debug("%s cleared cassoc_wd\n", __func__); + mpam_clear_feature(mpam_feat_cmax_cassoc, &parent->features); + parent->cassoc_wd =3D 0; + } + /* For num properties, take the minimum */ if (CAN_MERGE_FEAT(parent, child, mpam_feat_msmon_csu, alias)) { parent->num_csu_mon =3D child->num_csu_mon; @@ -1600,6 +1740,41 @@ static void __props_mismatch(struct mpam_props *pare= nt, parent->num_mbwu_mon =3D min(parent->num_mbwu_mon, child->num_mbwu_mon); } =20 + if (CAN_MERGE_FEAT(parent, child, mpam_feat_intpri_part, alias)) { + parent->intpri_wd =3D child->intpri_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_intpri_part, + intpri_wd, alias)) { + pr_debug("%s took the min intpri_wd\n", __func__); + parent->intpri_wd =3D min(parent->intpri_wd, child->intpri_wd); + } + + if (CAN_MERGE_FEAT(parent, child, mpam_feat_dspri_part, alias)) { + parent->dspri_wd =3D child->dspri_wd; + } else if (MISMATCHED_FEAT(parent, child, mpam_feat_dspri_part, + dspri_wd, alias)) { + pr_debug("%s took the min dspri_wd\n", __func__); + parent->dspri_wd =3D min(parent->dspri_wd, child->dspri_wd); + } + + /* TODO: alias support for these two */ + /* {int,ds}pri may not have differing 0-low behaviour */ + if (mpam_has_feature(mpam_feat_intpri_part, parent) && + (!mpam_has_feature(mpam_feat_intpri_part, child) || + mpam_has_feature(mpam_feat_intpri_part_0_low, parent) !=3D + mpam_has_feature(mpam_feat_intpri_part_0_low, child))) { + pr_debug("%s cleared intpri_part\n", __func__); + mpam_clear_feature(mpam_feat_intpri_part, &parent->features); + mpam_clear_feature(mpam_feat_intpri_part_0_low, &parent->features); + } + if (mpam_has_feature(mpam_feat_dspri_part, parent) && + (!mpam_has_feature(mpam_feat_dspri_part, child) || + mpam_has_feature(mpam_feat_dspri_part_0_low, parent) !=3D + mpam_has_feature(mpam_feat_dspri_part_0_low, child))) { + pr_debug("%s cleared dspri_part\n", __func__); + mpam_clear_feature(mpam_feat_dspri_part, &parent->features); + mpam_clear_feature(mpam_feat_dspri_part_0_low, &parent->features); + } + if (alias) { /* Merge features for aliased resources */ parent->features |=3D child->features; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 70cba9f22746..23445aedbabd 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -157,16 +157,23 @@ static inline void mpam_mon_sel_lock_held(struct mpam= _msc *msc) * When we compact the supported features, we don't care what they are. * Storing them as a bitmap makes life easy. */ -typedef u16 mpam_features_t; +typedef u32 mpam_features_t; =20 /* Bits for mpam_features_t */ enum mpam_device_features { - mpam_feat_ccap_part =3D 0, + mpam_feat_cmax_softlim, + mpam_feat_cmax_cmax, + mpam_feat_cmax_cmin, + mpam_feat_cmax_cassoc, mpam_feat_cpor_part, mpam_feat_mbw_part, mpam_feat_mbw_min, mpam_feat_mbw_max, mpam_feat_mbw_prop, + mpam_feat_intpri_part, + mpam_feat_intpri_part_0_low, + mpam_feat_dspri_part, + mpam_feat_dspri_part_0_low, mpam_feat_msmon, mpam_feat_msmon_csu, mpam_feat_msmon_csu_capture, @@ -176,6 +183,7 @@ enum mpam_device_features { mpam_feat_msmon_mbwu_rwbw, mpam_feat_msmon_mbwu_hw_nrdy, mpam_feat_msmon_capt, + mpam_feat_partid_nrw, MPAM_FEATURE_LAST, }; static_assert(BITS_PER_TYPE(mpam_features_t) >=3D MPAM_FEATURE_LAST); @@ -187,6 +195,10 @@ struct mpam_props { u16 cpbm_wd; u16 mbw_pbm_bits; u16 bwa_wd; + u16 cmax_wd; + u16 cassoc_wd; + u16 intpri_wd; + u16 dspri_wd; u16 num_csu_mon; u16 num_mbwu_mon; }; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 212FE233712; Fri, 22 Aug 2025 15:33:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876815; cv=none; b=Fx30TJwxv4vrKyhIdGyLgPuq7F4FHDYXjHrfK5KpNoQpUypcNxpl585ZH6cYldef0cd/KL3koXawgmkMX2QthvW8W6/+XEdQJyf7CSxZBJq50ODoirW+msUCXv6yeiDP4JwGPQb3ByAhHQZ5jTxzqgPdgrVgdGJfR0Y1O+GPo6k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876815; c=relaxed/simple; bh=P0s70JGbxqI2NTlW01YUNWEsH9cS/kHpixRrL7dj8Qc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IgJ7aFB3IuR9WWyw4JXnSCMl6E3tg35RLc05s/jj1Vme8tj3QOleh7ERQJdobRXSm7knSvqgXTLYqiBiRt6JxPVeePHCY7TRiyPbFBsns3fSU2RFwLjXrVUfNfsOFJbtVID7R77UpOVT9EmstT85VgFvm0PG6M8fsJLCvPEkdlY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 375D415A1; Fri, 22 Aug 2025 08:33:25 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2981C3F63F; Fri, 22 Aug 2025 08:33:28 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 26/33] arm_mpam: Add helpers to allocate monitors Date: Fri, 22 Aug 2025 15:30:07 +0000 Message-Id: <20250822153048.2287-27-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM's MSC support a number of monitors, each of which supports bandwidth counters, or cache-storage-utilisation counters. To use a counter, a monitor needs to be configured. Add helpers to allocate and free CSU or MBWU monitors. Signed-off-by: James Morse Reviewed-by: Ben Horgan --- drivers/resctrl/mpam_devices.c | 2 ++ drivers/resctrl/mpam_internal.h | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index aedd743d6827..e7e00c632512 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -348,6 +348,8 @@ mpam_class_alloc(u8 level_idx, enum mpam_class_types ty= pe, gfp_t gfp) class->level =3D level_idx; class->type =3D type; INIT_LIST_HEAD_RCU(&class->classes_list); + ida_init(&class->ida_csu_mon); + ida_init(&class->ida_mbwu_mon); =20 list_add_rcu(&class->classes_list, &mpam_classes); =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 23445aedbabd..4981de120869 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -231,6 +231,9 @@ struct mpam_class { /* member of mpam_classes */ struct list_head classes_list; =20 + struct ida ida_csu_mon; + struct ida ida_mbwu_mon; + struct mpam_garbage garbage; }; =20 @@ -306,6 +309,38 @@ struct mpam_msc_ris { struct mpam_garbage garbage; }; =20 +static inline int mpam_alloc_csu_mon(struct mpam_class *class) +{ + struct mpam_props *cprops =3D &class->props; + + if (!mpam_has_feature(mpam_feat_msmon_csu, cprops)) + return -EOPNOTSUPP; + + return ida_alloc_range(&class->ida_csu_mon, 0, cprops->num_csu_mon - 1, + GFP_KERNEL); +} + +static inline void mpam_free_csu_mon(struct mpam_class *class, int csu_mon) +{ + ida_free(&class->ida_csu_mon, csu_mon); +} + +static inline int mpam_alloc_mbwu_mon(struct mpam_class *class) +{ + struct mpam_props *cprops =3D &class->props; + + if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops)) + return -EOPNOTSUPP; + + return ida_alloc_range(&class->ida_mbwu_mon, 0, + cprops->num_mbwu_mon - 1, GFP_KERNEL); +} + +static inline void mpam_free_mbwu_mon(struct mpam_class *class, int mbwu_m= on) +{ + ida_free(&class->ida_mbwu_mon, mbwu_mon); +} + /* List of all classes - protected by srcu*/ extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id ED0BF34F48B; Fri, 22 Aug 2025 15:36:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877007; cv=none; b=vDiqmyALUJkljvVUJygtlPdgHRnrMyLFj4npZrVHiXp3KJaCCIh3QlinGKuDSpDXwHb0w8wwPBIFROFTwDP02UtOLYxxKDSEs9ghWU0xWy3bm42lgBQ/B0irP4q6CW8emyXYwfsjnMifrPCpLM+QW/bhJbxQA9pUZzYlWrMjZeM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877007; c=relaxed/simple; bh=P0s70JGbxqI2NTlW01YUNWEsH9cS/kHpixRrL7dj8Qc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qvKvg4ZXDX+a8TMTATBrmCpd6kgrAn0Za3Rqvhib+DP1xcNHh7K12FR8c5JeaTQZG4IjYwyfhMRSWqkPe7fWxT8jQcMvmbcQx90TbF5+D3Yx7SUmlFWj4tdZ7o2T7+8ylFyv+RHrVCW8jAYDfx0TSDZ5SxRt5d7ROXWPIcNgbpY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 3D22015A1; Fri, 22 Aug 2025 08:36:37 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 163153F63F; Fri, 22 Aug 2025 08:36:39 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 26/33] arm_mpam: Add helpers to allocate monitors Date: Fri, 22 Aug 2025 15:30:41 +0000 Message-Id: <20250822153048.2287-61-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" MPAM's MSC support a number of monitors, each of which supports bandwidth counters, or cache-storage-utilisation counters. To use a counter, a monitor needs to be configured. Add helpers to allocate and free CSU or MBWU monitors. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 2 ++ drivers/resctrl/mpam_internal.h | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index aedd743d6827..e7e00c632512 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -348,6 +348,8 @@ mpam_class_alloc(u8 level_idx, enum mpam_class_types ty= pe, gfp_t gfp) class->level =3D level_idx; class->type =3D type; INIT_LIST_HEAD_RCU(&class->classes_list); + ida_init(&class->ida_csu_mon); + ida_init(&class->ida_mbwu_mon); =20 list_add_rcu(&class->classes_list, &mpam_classes); =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 23445aedbabd..4981de120869 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -231,6 +231,9 @@ struct mpam_class { /* member of mpam_classes */ struct list_head classes_list; =20 + struct ida ida_csu_mon; + struct ida ida_mbwu_mon; + struct mpam_garbage garbage; }; =20 @@ -306,6 +309,38 @@ struct mpam_msc_ris { struct mpam_garbage garbage; }; =20 +static inline int mpam_alloc_csu_mon(struct mpam_class *class) +{ + struct mpam_props *cprops =3D &class->props; + + if (!mpam_has_feature(mpam_feat_msmon_csu, cprops)) + return -EOPNOTSUPP; + + return ida_alloc_range(&class->ida_csu_mon, 0, cprops->num_csu_mon - 1, + GFP_KERNEL); +} + +static inline void mpam_free_csu_mon(struct mpam_class *class, int csu_mon) +{ + ida_free(&class->ida_csu_mon, csu_mon); +} + +static inline int mpam_alloc_mbwu_mon(struct mpam_class *class) +{ + struct mpam_props *cprops =3D &class->props; + + if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops)) + return -EOPNOTSUPP; + + return ida_alloc_range(&class->ida_mbwu_mon, 0, + cprops->num_mbwu_mon - 1, GFP_KERNEL); +} + +static inline void mpam_free_mbwu_mon(struct mpam_class *class, int mbwu_m= on) +{ + ida_free(&class->ida_mbwu_mon, mbwu_mon); +} + /* List of all classes - protected by srcu*/ extern struct srcu_struct mpam_srcu; extern struct list_head mpam_classes; --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C49361D9663; Fri, 22 Aug 2025 15:33:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876821; cv=none; b=YOADF3pfTxXgNg7DV2BmBwPuNuWo9DkPIpkCJ9MSLc4w8Hlq5BAJCLwHIyBfb9BdsgSYMYnQBR8Ebv/DHIy293lQ8bcsN85xMnsK0IweyVIB1AFYqqye5mdADTEv73x1B05gEMIrTgfrgf9yU3tBGFSNMB45iGO6OeaBXUnqpuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876821; c=relaxed/simple; bh=oXrYh29R4LWpJvmEOZSsBz66JvUIuR0f8iHNaITVlwA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Vdl1lyc+AQsJM75slsi8XpscY0RabQO77WITrVEzx3Hey2whHrFOfzeoaGsCDVDDrN6cVbeiw4ab+Ikf/Ose0ZV52MfxlzbRbZ/elnmfYRtAAbqT73qfBN8kIJSlKZsuXwXKg6VLfda22p6p0tFwW/Dri6m4Zz1npynunIxOsZE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C71BE27B5; Fri, 22 Aug 2025 08:33:30 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B8DC93F63F; Fri, 22 Aug 2025 08:33:33 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 27/33] arm_mpam: Add mpam_msmon_read() to read monitor value Date: Fri, 22 Aug 2025 15:30:08 +0000 Message-Id: <20250822153048.2287-28-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Reading a monitor involves configuring what you want to monitor, and reading the value. Components made up of multiple MSC may need values from each MSC. MSCs may take time to configure, returning 'not ready'. The maximum 'not ready' time should have been provided by firmware. Add mpam_msmon_read() to hide all this. If (one of) the MSC returns not ready, then wait the full timeout value before trying again. CC: Shanker Donthineni Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 222 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 18 +++ 2 files changed, 240 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index e7e00c632512..9ce771aaf671 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -973,6 +973,228 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) return 0; } =20 +struct mon_read { + struct mpam_msc_ris *ris; + struct mon_cfg *ctx; + enum mpam_device_features type; + u64 *val; + int err; +}; + +static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, + u32 *flt_val) +{ + struct mon_cfg *ctx =3D m->ctx; + + switch (m->type) { + case mpam_feat_msmon_csu: + *ctl_val =3D MSMON_CFG_CSU_CTL_TYPE_CSU; + break; + case mpam_feat_msmon_mbwu: + *ctl_val =3D MSMON_CFG_MBWU_CTL_TYPE_MBWU; + break; + default: + return; + } + + /* + * For CSU counters its implementation-defined what happens when not + * filtering by partid. + */ + *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PARTID; + + *flt_val =3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PARTID, ctx->partid); + if (m->ctx->match_pmg) { + *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PMG; + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PMG, ctx->pmg); + } + + if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props)) + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts); +} + +static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, + u32 *flt_val) +{ + struct mpam_msc *msc =3D m->ris->vmsc->msc; + + switch (m->type) { + case mpam_feat_msmon_csu: + *ctl_val =3D mpam_read_monsel_reg(msc, CFG_CSU_CTL); + *flt_val =3D mpam_read_monsel_reg(msc, CFG_CSU_FLT); + break; + case mpam_feat_msmon_mbwu: + *ctl_val =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); + *flt_val =3D mpam_read_monsel_reg(msc, CFG_MBWU_FLT); + break; + default: + return; + } +} + +/* Remove values set by the hardware to prevent apparant mismatches. */ +static void clean_msmon_ctl_val(u32 *cur_ctl) +{ + *cur_ctl &=3D ~MSMON_CFG_x_CTL_OFLOW_STATUS; +} + +static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, + u32 flt_val) +{ + struct mpam_msc *msc =3D m->ris->vmsc->msc; + + /* + * Write the ctl_val with the enable bit cleared, reset the counter, + * then enable counter. + */ + switch (m->type) { + case mpam_feat_msmon_csu: + mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val); + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val); + mpam_write_monsel_reg(msc, CSU, 0); + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + break; + case mpam_feat_msmon_mbwu: + mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); + mpam_write_monsel_reg(msc, MBWU, 0); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + break; + default: + return; + } +} + +/* Call with MSC lock held */ +static void __ris_msmon_read(void *arg) +{ + u64 now; + bool nrdy =3D false; + struct mon_read *m =3D arg; + struct mon_cfg *ctx =3D m->ctx; + struct mpam_msc_ris *ris =3D m->ris; + struct mpam_props *rprops =3D &ris->props; + struct mpam_msc *msc =3D m->ris->vmsc->msc; + u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; + + if (!mpam_mon_sel_inner_lock(msc)) { + m->err =3D -EIO; + return; + } + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); + + /* + * Read the existing configuration to avoid re-writing the same values. + * This saves waiting for 'nrdy' on subsequent reads. + */ + read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt); + clean_msmon_ctl_val(&cur_ctl); + gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val); + if (cur_flt !=3D flt_val || cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN)) + write_msmon_ctl_flt_vals(m, ctl_val, flt_val); + + switch (m->type) { + case mpam_feat_msmon_csu: + now =3D mpam_read_monsel_reg(msc, CSU); + if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + break; + case mpam_feat_msmon_mbwu: + now =3D mpam_read_monsel_reg(msc, MBWU); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + break; + default: + m->err =3D -EINVAL; + break; + } + mpam_mon_sel_inner_unlock(msc); + + if (nrdy) { + m->err =3D -EBUSY; + return; + } + + now =3D FIELD_GET(MSMON___VALUE, now); + *m->val +=3D now; +} + +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg) +{ + int err, idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + arg->ris =3D ris; + + err =3D smp_call_function_any(&msc->accessibility, + __ris_msmon_read, arg, + true); + if (!err && arg->err) + err =3D arg->err; + if (err) + break; + } + mpam_mon_sel_outer_unlock(msc); + if (err) + break; + } + srcu_read_unlock(&mpam_srcu, idx); + + return err; +} + +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, + enum mpam_device_features type, u64 *val) +{ + int err; + struct mon_read arg; + u64 wait_jiffies =3D 0; + struct mpam_props *cprops =3D &comp->class->props; + + might_sleep(); + + if (!mpam_is_enabled()) + return -EIO; + + if (!mpam_has_feature(type, cprops)) + return -EOPNOTSUPP; + + memset(&arg, 0, sizeof(arg)); + arg.ctx =3D ctx; + arg.type =3D type; + arg.val =3D val; + *val =3D 0; + + err =3D _msmon_read(comp, &arg); + if (err =3D=3D -EBUSY && comp->class->nrdy_usec) + wait_jiffies =3D usecs_to_jiffies(comp->class->nrdy_usec); + + while (wait_jiffies) + wait_jiffies =3D schedule_timeout_uninterruptible(wait_jiffies); + + if (err =3D=3D -EBUSY) { + memset(&arg, 0, sizeof(arg)); + arg.ctx =3D ctx; + arg.type =3D type; + arg.val =3D val; + *val =3D 0; + + err =3D _msmon_read(comp, &arg); + } + + return err; +} + static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) { u32 num_words, msb; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 4981de120869..76e406a2b0d1 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -309,6 +309,21 @@ struct mpam_msc_ris { struct mpam_garbage garbage; }; =20 +/* The values for MSMON_CFG_MBWU_FLT.RWBW */ +enum mon_filter_options { + COUNT_BOTH =3D 0, + COUNT_WRITE =3D 1, + COUNT_READ =3D 2, +}; + +struct mon_cfg { + u16 mon; + u8 pmg; + bool match_pmg; + u32 partid; + enum mon_filter_options opts; +}; + static inline int mpam_alloc_csu_mon(struct mpam_class *class) { struct mpam_props *cprops =3D &class->props; @@ -361,6 +376,9 @@ void mpam_disable(struct work_struct *work); int mpam_apply_config(struct mpam_component *comp, u16 partid, struct mpam_config *cfg); =20 +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, + enum mpam_device_features, u64 *val); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B4A4828750A; Fri, 22 Aug 2025 15:36:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877013; cv=none; b=eYtqPJBvBOT3K6bJ5vCwpDRLypftYK1qu3idowONWNuPexR9JKqbmjP49qjUIEtmcATrP38/nTG9NpPaNbXpMgYWGIpEfcNkEghA+cRseOZIc1IwZtkDm25XYOFmDZff0+/kPohxZRgbqR35ocy+eZjzFway9l2NtxymLOimB1s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877013; c=relaxed/simple; bh=oXrYh29R4LWpJvmEOZSsBz66JvUIuR0f8iHNaITVlwA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=in/+2fiNEbSwzTTxfp4Np/14nenl0f+t+9RLYAvMJISYQgBDElCScgRETTF0E/AxsdX008ba3r4zOA7SSDaNZIMk7Rfl/u0RDY6vFl6+sKTWTktsuZk+VrGlB/vTLnLzVCvj7nunn1m0tvMbQHQ7ND79fhN0ore8M0azDeMTAOU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E41E915A1; Fri, 22 Aug 2025 08:36:42 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C361A3F63F; Fri, 22 Aug 2025 08:36:45 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 27/33] arm_mpam: Add mpam_msmon_read() to read monitor value Date: Fri, 22 Aug 2025 15:30:42 +0000 Message-Id: <20250822153048.2287-62-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Reading a monitor involves configuring what you want to monitor, and reading the value. Components made up of multiple MSC may need values from each MSC. MSCs may take time to configure, returning 'not ready'. The maximum 'not ready' time should have been provided by firmware. Add mpam_msmon_read() to hide all this. If (one of) the MSC returns not ready, then wait the full timeout value before trying again. CC: Shanker Donthineni Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 222 ++++++++++++++++++++++++++++++++ drivers/resctrl/mpam_internal.h | 18 +++ 2 files changed, 240 insertions(+) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index e7e00c632512..9ce771aaf671 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -973,6 +973,228 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc) return 0; } =20 +struct mon_read { + struct mpam_msc_ris *ris; + struct mon_cfg *ctx; + enum mpam_device_features type; + u64 *val; + int err; +}; + +static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, + u32 *flt_val) +{ + struct mon_cfg *ctx =3D m->ctx; + + switch (m->type) { + case mpam_feat_msmon_csu: + *ctl_val =3D MSMON_CFG_CSU_CTL_TYPE_CSU; + break; + case mpam_feat_msmon_mbwu: + *ctl_val =3D MSMON_CFG_MBWU_CTL_TYPE_MBWU; + break; + default: + return; + } + + /* + * For CSU counters its implementation-defined what happens when not + * filtering by partid. + */ + *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PARTID; + + *flt_val =3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PARTID, ctx->partid); + if (m->ctx->match_pmg) { + *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PMG; + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PMG, ctx->pmg); + } + + if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props)) + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts); +} + +static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, + u32 *flt_val) +{ + struct mpam_msc *msc =3D m->ris->vmsc->msc; + + switch (m->type) { + case mpam_feat_msmon_csu: + *ctl_val =3D mpam_read_monsel_reg(msc, CFG_CSU_CTL); + *flt_val =3D mpam_read_monsel_reg(msc, CFG_CSU_FLT); + break; + case mpam_feat_msmon_mbwu: + *ctl_val =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); + *flt_val =3D mpam_read_monsel_reg(msc, CFG_MBWU_FLT); + break; + default: + return; + } +} + +/* Remove values set by the hardware to prevent apparant mismatches. */ +static void clean_msmon_ctl_val(u32 *cur_ctl) +{ + *cur_ctl &=3D ~MSMON_CFG_x_CTL_OFLOW_STATUS; +} + +static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, + u32 flt_val) +{ + struct mpam_msc *msc =3D m->ris->vmsc->msc; + + /* + * Write the ctl_val with the enable bit cleared, reset the counter, + * then enable counter. + */ + switch (m->type) { + case mpam_feat_msmon_csu: + mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val); + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val); + mpam_write_monsel_reg(msc, CSU, 0); + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + break; + case mpam_feat_msmon_mbwu: + mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); + mpam_write_monsel_reg(msc, MBWU, 0); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + break; + default: + return; + } +} + +/* Call with MSC lock held */ +static void __ris_msmon_read(void *arg) +{ + u64 now; + bool nrdy =3D false; + struct mon_read *m =3D arg; + struct mon_cfg *ctx =3D m->ctx; + struct mpam_msc_ris *ris =3D m->ris; + struct mpam_props *rprops =3D &ris->props; + struct mpam_msc *msc =3D m->ris->vmsc->msc; + u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; + + if (!mpam_mon_sel_inner_lock(msc)) { + m->err =3D -EIO; + return; + } + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); + + /* + * Read the existing configuration to avoid re-writing the same values. + * This saves waiting for 'nrdy' on subsequent reads. + */ + read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt); + clean_msmon_ctl_val(&cur_ctl); + gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val); + if (cur_flt !=3D flt_val || cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN)) + write_msmon_ctl_flt_vals(m, ctl_val, flt_val); + + switch (m->type) { + case mpam_feat_msmon_csu: + now =3D mpam_read_monsel_reg(msc, CSU); + if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + break; + case mpam_feat_msmon_mbwu: + now =3D mpam_read_monsel_reg(msc, MBWU); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + break; + default: + m->err =3D -EINVAL; + break; + } + mpam_mon_sel_inner_unlock(msc); + + if (nrdy) { + m->err =3D -EBUSY; + return; + } + + now =3D FIELD_GET(MSMON___VALUE, now); + *m->val +=3D now; +} + +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg) +{ + int err, idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + arg->ris =3D ris; + + err =3D smp_call_function_any(&msc->accessibility, + __ris_msmon_read, arg, + true); + if (!err && arg->err) + err =3D arg->err; + if (err) + break; + } + mpam_mon_sel_outer_unlock(msc); + if (err) + break; + } + srcu_read_unlock(&mpam_srcu, idx); + + return err; +} + +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, + enum mpam_device_features type, u64 *val) +{ + int err; + struct mon_read arg; + u64 wait_jiffies =3D 0; + struct mpam_props *cprops =3D &comp->class->props; + + might_sleep(); + + if (!mpam_is_enabled()) + return -EIO; + + if (!mpam_has_feature(type, cprops)) + return -EOPNOTSUPP; + + memset(&arg, 0, sizeof(arg)); + arg.ctx =3D ctx; + arg.type =3D type; + arg.val =3D val; + *val =3D 0; + + err =3D _msmon_read(comp, &arg); + if (err =3D=3D -EBUSY && comp->class->nrdy_usec) + wait_jiffies =3D usecs_to_jiffies(comp->class->nrdy_usec); + + while (wait_jiffies) + wait_jiffies =3D schedule_timeout_uninterruptible(wait_jiffies); + + if (err =3D=3D -EBUSY) { + memset(&arg, 0, sizeof(arg)); + arg.ctx =3D ctx; + arg.type =3D type; + arg.val =3D val; + *val =3D 0; + + err =3D _msmon_read(comp, &arg); + } + + return err; +} + static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) { u32 num_words, msb; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 4981de120869..76e406a2b0d1 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -309,6 +309,21 @@ struct mpam_msc_ris { struct mpam_garbage garbage; }; =20 +/* The values for MSMON_CFG_MBWU_FLT.RWBW */ +enum mon_filter_options { + COUNT_BOTH =3D 0, + COUNT_WRITE =3D 1, + COUNT_READ =3D 2, +}; + +struct mon_cfg { + u16 mon; + u8 pmg; + bool match_pmg; + u32 partid; + enum mon_filter_options opts; +}; + static inline int mpam_alloc_csu_mon(struct mpam_class *class) { struct mpam_props *cprops =3D &class->props; @@ -361,6 +376,9 @@ void mpam_disable(struct work_struct *work); int mpam_apply_config(struct mpam_component *comp, u16 partid, struct mpam_config *cfg); =20 +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, + enum mpam_device_features, u64 *val); + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 093411D9663; Fri, 22 Aug 2025 15:33:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876826; cv=none; b=dV+R5/xypNCJi9s3yTyRHmWy71u2QDYs/OPJu5UJsZTWr0U+jUQzcLZF/gcIjWN4Hkpv/DMOAn54ecRwFmk+VdgWava2ZLhoUjqnGn3rNkaObWvyaxyro/p6AfFDAHP7UUVhgGOdzQi+LbjJfTWYZwQds6UKgdH+lLdOM2j3N1Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876826; c=relaxed/simple; bh=e/v02KhRV9ZA3YNW1CPUZQu2Vj4wjqyoRUt+Zwhciqw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=GDJCV/qBppD1fSv25lMxT62wJTdIRekav+igGjbPJeYJXYspF4V8/yxo66SlzcKzo6uWllJ5tZzSJJS5kGaraAW3cl3boSorBIMbSyXezLgBtmhYh86K28iI9rkZgdvnpDYHJP2AO/qvCR5b0u4mVkumjbtsYfk6ICJb0M7oS/o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6AD3E2880; Fri, 22 Aug 2025 08:33:36 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 541EB3F63F; Fri, 22 Aug 2025 08:33:39 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 28/33] arm_mpam: Track bandwidth counter state for overflow and power management Date: Fri, 22 Aug 2025 15:30:09 +0000 Message-Id: <20250822153048.2287-29-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Bandwidth counters need to run continuously to correctly reflect the bandwidth. The value read may be lower than the previous value read in the case of overflow and when the hardware is reset due to CPU hotplug. Add struct mbwu_state to track the bandwidth counter to allow overflow and power management to be handled. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 163 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 54 ++++++++--- 2 files changed, 200 insertions(+), 17 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 9ce771aaf671..11be34b54643 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1004,6 +1004,7 @@ static void gen_msmon_ctl_flt_vals(struct mon_read *m= , u32 *ctl_val, *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PARTID; =20 *flt_val =3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PARTID, ctx->partid); + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts); if (m->ctx->match_pmg) { *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PMG; *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PMG, ctx->pmg); @@ -1041,6 +1042,7 @@ static void clean_msmon_ctl_val(u32 *cur_ctl) static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, u32 flt_val) { + struct msmon_mbwu_state *mbwu_state; struct mpam_msc *msc =3D m->ris->vmsc->msc; =20 /* @@ -1059,20 +1061,32 @@ static void write_msmon_ctl_flt_vals(struct mon_rea= d *m, u32 ctl_val, mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); mpam_write_monsel_reg(msc, MBWU, 0); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + + mbwu_state =3D &m->ris->mbwu_state[m->ctx->mon]; + if (mbwu_state) + mbwu_state->prev_val =3D 0; + break; default: return; } } =20 +static u64 mpam_msmon_overflow_val(struct mpam_msc_ris *ris) +{ + /* TODO: scaling, and long counters */ + return GENMASK_ULL(30, 0); +} + /* Call with MSC lock held */ static void __ris_msmon_read(void *arg) { - u64 now; bool nrdy =3D false; struct mon_read *m =3D arg; + u64 now, overflow_val =3D 0; struct mon_cfg *ctx =3D m->ctx; struct mpam_msc_ris *ris =3D m->ris; + struct msmon_mbwu_state *mbwu_state; struct mpam_props *rprops =3D &ris->props; struct mpam_msc *msc =3D m->ris->vmsc->msc; u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; @@ -1100,11 +1114,30 @@ static void __ris_msmon_read(void *arg) now =3D mpam_read_monsel_reg(msc, CSU); if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); break; case mpam_feat_msmon_mbwu: now =3D mpam_read_monsel_reg(msc, MBWU); if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); + + if (nrdy) + break; + + mbwu_state =3D &ris->mbwu_state[ctx->mon]; + if (!mbwu_state) + break; + + /* Add any pre-overflow value to the mbwu_state->val */ + if (mbwu_state->prev_val > now) + overflow_val =3D mpam_msmon_overflow_val(ris) - mbwu_state->prev_val; + + mbwu_state->prev_val =3D now; + mbwu_state->correction +=3D overflow_val; + + /* Include bandwidth consumed before the last hardware reset */ + now +=3D mbwu_state->correction; break; default: m->err =3D -EINVAL; @@ -1117,7 +1150,6 @@ static void __ris_msmon_read(void *arg) return; } =20 - now =3D FIELD_GET(MSMON___VALUE, now); *m->val +=3D now; } =20 @@ -1329,6 +1361,72 @@ static int mpam_reprogram_ris(void *_arg) return 0; } =20 +/* Call with MSC lock and outer mon_sel lock held */ +static int mpam_restore_mbwu_state(void *_ris) +{ + int i; + struct mon_read mwbu_arg; + struct mpam_msc_ris *ris =3D _ris; + struct mpam_msc *msc =3D ris->vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + + for (i =3D 0; i < ris->props.num_mbwu_mon; i++) { + if (ris->mbwu_state[i].enabled) { + mwbu_arg.ris =3D ris; + mwbu_arg.ctx =3D &ris->mbwu_state[i].cfg; + mwbu_arg.type =3D mpam_feat_msmon_mbwu; + + __ris_msmon_read(&mwbu_arg); + } + } + + mpam_mon_sel_outer_unlock(msc); + + return 0; +} + +/* Call with MSC lock and outer mon_sel lock held */ +static int mpam_save_mbwu_state(void *arg) +{ + int i; + u64 val; + struct mon_cfg *cfg; + u32 cur_flt, cur_ctl, mon_sel; + struct mpam_msc_ris *ris =3D arg; + struct msmon_mbwu_state *mbwu_state; + struct mpam_msc *msc =3D ris->vmsc->msc; + + for (i =3D 0; i < ris->props.num_mbwu_mon; i++) { + mbwu_state =3D &ris->mbwu_state[i]; + cfg =3D &mbwu_state->cfg; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + return -EIO; + + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); + + cur_flt =3D mpam_read_monsel_reg(msc, CFG_MBWU_FLT); + cur_ctl =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0); + + val =3D mpam_read_monsel_reg(msc, MBWU); + mpam_write_monsel_reg(msc, MBWU, 0); + + cfg->mon =3D i; + cfg->pmg =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PMG, cur_flt); + cfg->match_pmg =3D FIELD_GET(MSMON_CFG_x_CTL_MATCH_PMG, cur_ctl); + cfg->partid =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PARTID, cur_flt); + mbwu_state->correction +=3D val; + mbwu_state->enabled =3D FIELD_GET(MSMON_CFG_x_CTL_EN, cur_ctl); + mpam_mon_sel_inner_unlock(msc); + } + + return 0; +} + /* * Called via smp_call_on_cpu() to prevent migration, while still being * pre-emptible. @@ -1389,6 +1487,9 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool= online) * for non-zero partid may be lost while the CPUs are offline. */ ris->in_reset_state =3D online; + + if (mpam_is_enabled() && !online) + mpam_touch_msc(msc, &mpam_save_mbwu_state, ris); } mpam_mon_sel_outer_unlock(msc); } @@ -1423,6 +1524,9 @@ static void mpam_reprogram_msc(struct mpam_msc *msc) mpam_reprogram_ris_partid(ris, partid, cfg); } ris->in_reset_state =3D reset; + + if (mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props)) + mpam_touch_msc(msc, &mpam_restore_mbwu_state, ris); } } =20 @@ -2291,11 +2395,35 @@ static void mpam_unregister_irqs(void) =20 static void __destroy_component_cfg(struct mpam_component *comp) { + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + lockdep_assert_held(&mpam_list_lock); + add_to_garbage(comp->cfg); + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + if (mpam_mon_sel_inner_lock(msc)) { + list_for_each_entry(ris, &vmsc->ris, vmsc_list) + add_to_garbage(ris->mbwu_state); + mpam_mon_sel_inner_unlock(msc); + } + mpam_mon_sel_outer_lock(msc); + } } =20 static int __allocate_component_cfg(struct mpam_component *comp) { + int err =3D 0; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct msmon_mbwu_state *mbwu_state; + + lockdep_assert_held(&mpam_list_lock); mpam_assert_partid_sizes_fixed(); =20 if (comp->cfg) @@ -2306,6 +2434,37 @@ static int __allocate_component_cfg(struct mpam_comp= onent *comp) return -ENOMEM; init_garbage(comp->cfg); =20 + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + if (!vmsc->props.num_mbwu_mon) + continue; + + msc =3D vmsc->msc; + mpam_mon_sel_outer_lock(msc); + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + if (!ris->props.num_mbwu_mon) + continue; + + mbwu_state =3D kcalloc(ris->props.num_mbwu_mon, + sizeof(*ris->mbwu_state), + GFP_KERNEL); + if (!mbwu_state) { + __destroy_component_cfg(comp); + err =3D -ENOMEM; + break; + } + + if (mpam_mon_sel_inner_lock(msc)) { + init_garbage(mbwu_state); + ris->mbwu_state =3D mbwu_state; + mpam_mon_sel_inner_unlock(msc); + } + } + mpam_mon_sel_outer_unlock(msc); + + if (err) + break; + } + return 0; } =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 76e406a2b0d1..9a50a5432f4a 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -271,6 +271,42 @@ struct mpam_component { struct mpam_garbage garbage; }; =20 +/* The values for MSMON_CFG_MBWU_FLT.RWBW */ +enum mon_filter_options { + COUNT_BOTH =3D 0, + COUNT_WRITE =3D 1, + COUNT_READ =3D 2, +}; + +struct mon_cfg { + /* mon is wider than u16 to hold an out of range 'USE_RMID_IDX' */ + u32 mon; + u8 pmg; + bool match_pmg; + u32 partid; + enum mon_filter_options opts; +}; + +/* + * Changes to enabled and cfg are protected by the msc->lock. + * Changes to prev_val and correction are protected by the msc's mon_sel_l= ock. + */ +struct msmon_mbwu_state { + bool enabled; + struct mon_cfg cfg; + + /* The value last read from the hardware. Used to detect overflow. */ + u64 prev_val; + + /* + * The value to add to the new reading to account for power management, + * and shifts to trigger the overflow interrupt. + */ + u64 correction; + + struct mpam_garbage garbage; +}; + struct mpam_vmsc { /* member of mpam_component:vmsc_list */ struct list_head comp_list; @@ -306,22 +342,10 @@ struct mpam_msc_ris { /* parent: */ struct mpam_vmsc *vmsc; =20 - struct mpam_garbage garbage; -}; + /* msmon mbwu configuration is preserved over reset */ + struct msmon_mbwu_state *mbwu_state; =20 -/* The values for MSMON_CFG_MBWU_FLT.RWBW */ -enum mon_filter_options { - COUNT_BOTH =3D 0, - COUNT_WRITE =3D 1, - COUNT_READ =3D 2, -}; - -struct mon_cfg { - u16 mon; - u8 pmg; - bool match_pmg; - u32 partid; - enum mon_filter_options opts; + struct mpam_garbage garbage; }; =20 static inline int mpam_alloc_csu_mon(struct mpam_class *class) --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4516A285CAA; Fri, 22 Aug 2025 15:36:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877018; cv=none; b=H/J8rm2EaahH0Y+nrT+cqqiCIxmYGkMHloqiyN5Zsw8rtI5yZY0d+mAEacmIdiNL+H+5yit1CH1tO/r7i0R1iLANvQL5zxs+Z4XycFTAJBpuqNd6O/dUTQRGRoVAdh/LptQwkPhcONWJy5uVRfni4wi8ZIyOUcaht/9l4v5PCwE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877018; c=relaxed/simple; bh=e/v02KhRV9ZA3YNW1CPUZQu2Vj4wjqyoRUt+Zwhciqw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jYoIlN0fWdVS+klsMsjD9LsJKQ6JaeRdqSt8HGoq4sNEOW4Zp9/aUT3kficOQbhfWmjkkWjVKuMbWu3/7Shn8gMLELQ7pIYh7s/AMxXsTelGXB3uqfkbE0k0DjBb4gmr9ql1Pju+Y6wWHj19pccPtawMvnb+CdsZo6uCa4EwgO4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 807C72880; Fri, 22 Aug 2025 08:36:48 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 726133F63F; Fri, 22 Aug 2025 08:36:51 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 28/33] arm_mpam: Track bandwidth counter state for overflow and power management Date: Fri, 22 Aug 2025 15:30:43 +0000 Message-Id: <20250822153048.2287-63-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Bandwidth counters need to run continuously to correctly reflect the bandwidth. The value read may be lower than the previous value read in the case of overflow and when the hardware is reset due to CPU hotplug. Add struct mbwu_state to track the bandwidth counter to allow overflow and power management to be handled. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 163 +++++++++++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 54 ++++++++--- 2 files changed, 200 insertions(+), 17 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 9ce771aaf671..11be34b54643 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1004,6 +1004,7 @@ static void gen_msmon_ctl_flt_vals(struct mon_read *m= , u32 *ctl_val, *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PARTID; =20 *flt_val =3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PARTID, ctx->partid); + *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts); if (m->ctx->match_pmg) { *ctl_val |=3D MSMON_CFG_x_CTL_MATCH_PMG; *flt_val |=3D FIELD_PREP(MSMON_CFG_MBWU_FLT_PMG, ctx->pmg); @@ -1041,6 +1042,7 @@ static void clean_msmon_ctl_val(u32 *cur_ctl) static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, u32 flt_val) { + struct msmon_mbwu_state *mbwu_state; struct mpam_msc *msc =3D m->ris->vmsc->msc; =20 /* @@ -1059,20 +1061,32 @@ static void write_msmon_ctl_flt_vals(struct mon_rea= d *m, u32 ctl_val, mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); mpam_write_monsel_reg(msc, MBWU, 0); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); + + mbwu_state =3D &m->ris->mbwu_state[m->ctx->mon]; + if (mbwu_state) + mbwu_state->prev_val =3D 0; + break; default: return; } } =20 +static u64 mpam_msmon_overflow_val(struct mpam_msc_ris *ris) +{ + /* TODO: scaling, and long counters */ + return GENMASK_ULL(30, 0); +} + /* Call with MSC lock held */ static void __ris_msmon_read(void *arg) { - u64 now; bool nrdy =3D false; struct mon_read *m =3D arg; + u64 now, overflow_val =3D 0; struct mon_cfg *ctx =3D m->ctx; struct mpam_msc_ris *ris =3D m->ris; + struct msmon_mbwu_state *mbwu_state; struct mpam_props *rprops =3D &ris->props; struct mpam_msc *msc =3D m->ris->vmsc->msc; u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt; @@ -1100,11 +1114,30 @@ static void __ris_msmon_read(void *arg) now =3D mpam_read_monsel_reg(msc, CSU); if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops)) nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); break; case mpam_feat_msmon_mbwu: now =3D mpam_read_monsel_reg(msc, MBWU); if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); + + if (nrdy) + break; + + mbwu_state =3D &ris->mbwu_state[ctx->mon]; + if (!mbwu_state) + break; + + /* Add any pre-overflow value to the mbwu_state->val */ + if (mbwu_state->prev_val > now) + overflow_val =3D mpam_msmon_overflow_val(ris) - mbwu_state->prev_val; + + mbwu_state->prev_val =3D now; + mbwu_state->correction +=3D overflow_val; + + /* Include bandwidth consumed before the last hardware reset */ + now +=3D mbwu_state->correction; break; default: m->err =3D -EINVAL; @@ -1117,7 +1150,6 @@ static void __ris_msmon_read(void *arg) return; } =20 - now =3D FIELD_GET(MSMON___VALUE, now); *m->val +=3D now; } =20 @@ -1329,6 +1361,72 @@ static int mpam_reprogram_ris(void *_arg) return 0; } =20 +/* Call with MSC lock and outer mon_sel lock held */ +static int mpam_restore_mbwu_state(void *_ris) +{ + int i; + struct mon_read mwbu_arg; + struct mpam_msc_ris *ris =3D _ris; + struct mpam_msc *msc =3D ris->vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + + for (i =3D 0; i < ris->props.num_mbwu_mon; i++) { + if (ris->mbwu_state[i].enabled) { + mwbu_arg.ris =3D ris; + mwbu_arg.ctx =3D &ris->mbwu_state[i].cfg; + mwbu_arg.type =3D mpam_feat_msmon_mbwu; + + __ris_msmon_read(&mwbu_arg); + } + } + + mpam_mon_sel_outer_unlock(msc); + + return 0; +} + +/* Call with MSC lock and outer mon_sel lock held */ +static int mpam_save_mbwu_state(void *arg) +{ + int i; + u64 val; + struct mon_cfg *cfg; + u32 cur_flt, cur_ctl, mon_sel; + struct mpam_msc_ris *ris =3D arg; + struct msmon_mbwu_state *mbwu_state; + struct mpam_msc *msc =3D ris->vmsc->msc; + + for (i =3D 0; i < ris->props.num_mbwu_mon; i++) { + mbwu_state =3D &ris->mbwu_state[i]; + cfg =3D &mbwu_state->cfg; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + return -EIO; + + mon_sel =3D FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) | + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); + + cur_flt =3D mpam_read_monsel_reg(msc, CFG_MBWU_FLT); + cur_ctl =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0); + + val =3D mpam_read_monsel_reg(msc, MBWU); + mpam_write_monsel_reg(msc, MBWU, 0); + + cfg->mon =3D i; + cfg->pmg =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PMG, cur_flt); + cfg->match_pmg =3D FIELD_GET(MSMON_CFG_x_CTL_MATCH_PMG, cur_ctl); + cfg->partid =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PARTID, cur_flt); + mbwu_state->correction +=3D val; + mbwu_state->enabled =3D FIELD_GET(MSMON_CFG_x_CTL_EN, cur_ctl); + mpam_mon_sel_inner_unlock(msc); + } + + return 0; +} + /* * Called via smp_call_on_cpu() to prevent migration, while still being * pre-emptible. @@ -1389,6 +1487,9 @@ static void mpam_reset_msc(struct mpam_msc *msc, bool= online) * for non-zero partid may be lost while the CPUs are offline. */ ris->in_reset_state =3D online; + + if (mpam_is_enabled() && !online) + mpam_touch_msc(msc, &mpam_save_mbwu_state, ris); } mpam_mon_sel_outer_unlock(msc); } @@ -1423,6 +1524,9 @@ static void mpam_reprogram_msc(struct mpam_msc *msc) mpam_reprogram_ris_partid(ris, partid, cfg); } ris->in_reset_state =3D reset; + + if (mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props)) + mpam_touch_msc(msc, &mpam_restore_mbwu_state, ris); } } =20 @@ -2291,11 +2395,35 @@ static void mpam_unregister_irqs(void) =20 static void __destroy_component_cfg(struct mpam_component *comp) { + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + lockdep_assert_held(&mpam_list_lock); + add_to_garbage(comp->cfg); + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + msc =3D vmsc->msc; + + mpam_mon_sel_outer_lock(msc); + if (mpam_mon_sel_inner_lock(msc)) { + list_for_each_entry(ris, &vmsc->ris, vmsc_list) + add_to_garbage(ris->mbwu_state); + mpam_mon_sel_inner_unlock(msc); + } + mpam_mon_sel_outer_lock(msc); + } } =20 static int __allocate_component_cfg(struct mpam_component *comp) { + int err =3D 0; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + struct msmon_mbwu_state *mbwu_state; + + lockdep_assert_held(&mpam_list_lock); mpam_assert_partid_sizes_fixed(); =20 if (comp->cfg) @@ -2306,6 +2434,37 @@ static int __allocate_component_cfg(struct mpam_comp= onent *comp) return -ENOMEM; init_garbage(comp->cfg); =20 + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { + if (!vmsc->props.num_mbwu_mon) + continue; + + msc =3D vmsc->msc; + mpam_mon_sel_outer_lock(msc); + list_for_each_entry(ris, &vmsc->ris, vmsc_list) { + if (!ris->props.num_mbwu_mon) + continue; + + mbwu_state =3D kcalloc(ris->props.num_mbwu_mon, + sizeof(*ris->mbwu_state), + GFP_KERNEL); + if (!mbwu_state) { + __destroy_component_cfg(comp); + err =3D -ENOMEM; + break; + } + + if (mpam_mon_sel_inner_lock(msc)) { + init_garbage(mbwu_state); + ris->mbwu_state =3D mbwu_state; + mpam_mon_sel_inner_unlock(msc); + } + } + mpam_mon_sel_outer_unlock(msc); + + if (err) + break; + } + return 0; } =20 diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 76e406a2b0d1..9a50a5432f4a 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -271,6 +271,42 @@ struct mpam_component { struct mpam_garbage garbage; }; =20 +/* The values for MSMON_CFG_MBWU_FLT.RWBW */ +enum mon_filter_options { + COUNT_BOTH =3D 0, + COUNT_WRITE =3D 1, + COUNT_READ =3D 2, +}; + +struct mon_cfg { + /* mon is wider than u16 to hold an out of range 'USE_RMID_IDX' */ + u32 mon; + u8 pmg; + bool match_pmg; + u32 partid; + enum mon_filter_options opts; +}; + +/* + * Changes to enabled and cfg are protected by the msc->lock. + * Changes to prev_val and correction are protected by the msc's mon_sel_l= ock. + */ +struct msmon_mbwu_state { + bool enabled; + struct mon_cfg cfg; + + /* The value last read from the hardware. Used to detect overflow. */ + u64 prev_val; + + /* + * The value to add to the new reading to account for power management, + * and shifts to trigger the overflow interrupt. + */ + u64 correction; + + struct mpam_garbage garbage; +}; + struct mpam_vmsc { /* member of mpam_component:vmsc_list */ struct list_head comp_list; @@ -306,22 +342,10 @@ struct mpam_msc_ris { /* parent: */ struct mpam_vmsc *vmsc; =20 - struct mpam_garbage garbage; -}; + /* msmon mbwu configuration is preserved over reset */ + struct msmon_mbwu_state *mbwu_state; =20 -/* The values for MSMON_CFG_MBWU_FLT.RWBW */ -enum mon_filter_options { - COUNT_BOTH =3D 0, - COUNT_WRITE =3D 1, - COUNT_READ =3D 2, -}; - -struct mon_cfg { - u16 mon; - u8 pmg; - bool match_pmg; - u32 partid; - enum mon_filter_options opts; + struct mpam_garbage garbage; }; =20 static inline int mpam_alloc_csu_mon(struct mpam_class *class) --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E5ED02E54D0; Fri, 22 Aug 2025 15:33:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876832; cv=none; b=bEwiy1Hp21ye7waJPSJFlqTvt92lL3H3f5v6pxOrLGz6D99Wn5O9DPdqqh1G/qnO4nVkWYtTZEWQRmfEALwjAYT2tZUzQBj3g/xmqU5abUfZupVUrAtu87dOGZLfcSIzZdQlVVdiG9SLfZ8oM2ZSs3g6IcpxfmZSate9TvPP6A8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876832; c=relaxed/simple; bh=gsKt+RwCt60QW84QLf7WE0MX/pZHHt9xwPfXicEjrcQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JexG4PhgvU74ltxGcuWcW85XLwr9vBBTq8lDnnCJ5M1IdJ83kgz8hZ7BNLd2c8Zib5h8WXLTuTLNIl+abWNvDT/Gy6xSg9FRh+kjoegbJSVTM/GiApJSRMb13KlUFTQ09y+LmY4Wm9modZRsp+pwkG802eN+dxsEkEomVU2xjhs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0AFE62880; Fri, 22 Aug 2025 08:33:42 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id F12B93F63F; Fri, 22 Aug 2025 08:33:44 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 29/33] arm_mpam: Probe for long/lwd mbwu counters Date: Fri, 22 Aug 2025 15:30:10 +0000 Message-Id: <20250822153048.2287-30-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rohit Mathew mpam v0.1 and versions above v1.0 support optional long counter for memory bandwidth monitoring. The MPAMF_MBWUMON_IDR register have fields indicating support for long counters. As of now, a 44 bit counter represented by HAS_LONG field (bit 30) and a 63 bit counter represented by LWD (bit 29) can be optionally integrated. Probe for these counters and set corresponding feature bits if any of these counters are present. Signed-off-by: Rohit Mathew Signed-off-by: James Morse Reviewed-by: Ben Horgan --- drivers/resctrl/mpam_devices.c | 23 ++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 8 ++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 11be34b54643..2ab7f127baaa 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -870,7 +870,7 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) pr_err_once("Counters are not usable because not-ready timeout was not= provided by firmware."); } if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { - bool hw_managed; + bool has_long, hw_managed; u32 mbwumonidr =3D mpam_read_partsel_reg(msc, MBWUMON_IDR); =20 props->num_mbwu_mon =3D FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumonidr= ); @@ -880,6 +880,27 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumonidr)) mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props); =20 + /* + * Treat long counter and its extension, lwd as mutually + * exclusive feature bits. Though these are dependent + * fields at the implementation level, there would never + * be a need for mpam_feat_msmon_mbwu_44counter (long + * counter) and mpam_feat_msmon_mbwu_63counter (lwd) + * bits to be set together. + * + * mpam_feat_msmon_mbwu isn't treated as an exclusive + * bit as this feature bit would be used as the "front + * facing feature bit" for any checks related to mbwu + * monitors. + */ + has_long =3D FIELD_GET(MPAMF_MBWUMON_IDR_HAS_LONG, mbwumonidr); + if (props->num_mbwu_mon && has_long) { + if (FIELD_GET(MPAMF_MBWUMON_IDR_LWD, mbwumonidr)) + mpam_set_feature(mpam_feat_msmon_mbwu_63counter, props); + else + mpam_set_feature(mpam_feat_msmon_mbwu_44counter, props); + } + /* Is NRDY hardware managed? */ mpam_mon_sel_outer_lock(msc); hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, MBWU); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9a50a5432f4a..9f627b5f72a1 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -178,7 +178,15 @@ enum mpam_device_features { mpam_feat_msmon_csu, mpam_feat_msmon_csu_capture, mpam_feat_msmon_csu_hw_nrdy, + + /* + * Having mpam_feat_msmon_mbwu set doesn't mean the regular 31 bit MBWU + * counter would be used. The exact counter used is decided based on the + * status of mpam_feat_msmon_mbwu_l/mpam_feat_msmon_mbwu_lwd as well. + */ mpam_feat_msmon_mbwu, + mpam_feat_msmon_mbwu_44counter, + mpam_feat_msmon_mbwu_63counter, mpam_feat_msmon_mbwu_capture, mpam_feat_msmon_mbwu_rwbw, mpam_feat_msmon_mbwu_hw_nrdy, --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id E9B702877F5; Fri, 22 Aug 2025 15:37:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877024; cv=none; b=l0SLVFsE0WfKXuPCkUA6F0LjqycerlNguUqviTQrsPSYcizVoOyy2nsTS0XQr0KICBQ8tONUEU0GzbvXU/Laoq/tRKoOF2NtbRuwV9d1dfnNefLZMkpah5v+X6lYXnabWpUnjre9FINTXnzLnns/Om0Wt3LyayjYpHlu5ff78/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877024; c=relaxed/simple; bh=gsKt+RwCt60QW84QLf7WE0MX/pZHHt9xwPfXicEjrcQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qL3ewXDs1XZjA0GG5mtC3I+I9HPUKhjwUKe3U+/eDUqwD1gIPbNDatdGipChBMvpz53VxbBM5E7bQYnA2oFsYZzZGAb4jhMuKEEVZTUjosv/K3fYOF/On2tt4bkpdca42hQk58M3epj+sHfM7ZILcrUpNDG6HIzsZDYlJ46S+TQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 1E35715A1; Fri, 22 Aug 2025 08:36:54 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 10EB73F63F; Fri, 22 Aug 2025 08:36:56 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 29/33] arm_mpam: Probe for long/lwd mbwu counters Date: Fri, 22 Aug 2025 15:30:44 +0000 Message-Id: <20250822153048.2287-64-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rohit Mathew mpam v0.1 and versions above v1.0 support optional long counter for memory bandwidth monitoring. The MPAMF_MBWUMON_IDR register have fields indicating support for long counters. As of now, a 44 bit counter represented by HAS_LONG field (bit 30) and a 63 bit counter represented by LWD (bit 29) can be optionally integrated. Probe for these counters and set corresponding feature bits if any of these counters are present. Signed-off-by: Rohit Mathew Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 23 ++++++++++++++++++++++- drivers/resctrl/mpam_internal.h | 8 ++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 11be34b54643..2ab7f127baaa 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -870,7 +870,7 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) pr_err_once("Counters are not usable because not-ready timeout was not= provided by firmware."); } if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) { - bool hw_managed; + bool has_long, hw_managed; u32 mbwumonidr =3D mpam_read_partsel_reg(msc, MBWUMON_IDR); =20 props->num_mbwu_mon =3D FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumonidr= ); @@ -880,6 +880,27 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris) if (FIELD_GET(MPAMF_MBWUMON_IDR_HAS_RWBW, mbwumonidr)) mpam_set_feature(mpam_feat_msmon_mbwu_rwbw, props); =20 + /* + * Treat long counter and its extension, lwd as mutually + * exclusive feature bits. Though these are dependent + * fields at the implementation level, there would never + * be a need for mpam_feat_msmon_mbwu_44counter (long + * counter) and mpam_feat_msmon_mbwu_63counter (lwd) + * bits to be set together. + * + * mpam_feat_msmon_mbwu isn't treated as an exclusive + * bit as this feature bit would be used as the "front + * facing feature bit" for any checks related to mbwu + * monitors. + */ + has_long =3D FIELD_GET(MPAMF_MBWUMON_IDR_HAS_LONG, mbwumonidr); + if (props->num_mbwu_mon && has_long) { + if (FIELD_GET(MPAMF_MBWUMON_IDR_LWD, mbwumonidr)) + mpam_set_feature(mpam_feat_msmon_mbwu_63counter, props); + else + mpam_set_feature(mpam_feat_msmon_mbwu_44counter, props); + } + /* Is NRDY hardware managed? */ mpam_mon_sel_outer_lock(msc); hw_managed =3D mpam_ris_hw_probe_hw_nrdy(ris, MBWU); diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9a50a5432f4a..9f627b5f72a1 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -178,7 +178,15 @@ enum mpam_device_features { mpam_feat_msmon_csu, mpam_feat_msmon_csu_capture, mpam_feat_msmon_csu_hw_nrdy, + + /* + * Having mpam_feat_msmon_mbwu set doesn't mean the regular 31 bit MBWU + * counter would be used. The exact counter used is decided based on the + * status of mpam_feat_msmon_mbwu_l/mpam_feat_msmon_mbwu_lwd as well. + */ mpam_feat_msmon_mbwu, + mpam_feat_msmon_mbwu_44counter, + mpam_feat_msmon_mbwu_63counter, mpam_feat_msmon_mbwu_capture, mpam_feat_msmon_mbwu_rwbw, mpam_feat_msmon_mbwu_hw_nrdy, --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3ADC31D9663; Fri, 22 Aug 2025 15:33:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876837; cv=none; b=EeneNcED8OdNJ6pvEWp7Jed5uUlaQUAXMkjaYGqDgbVngsv9Xt3wqMpWMN/DOqouMb4uZq74+Hz2n4wVv12wov/RDByOSiPcTwc2ZuWLAGHaxXoCcz88uGhy9A1uK7VWViHgmjjgDcqeV9YDTuRWgZlk4uXR21GeAA0u5q3UwLc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876837; c=relaxed/simple; bh=s+tKsbJi/c/NTyvZ20R8uRUL3GNh8OK2lBvLuotvBO8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=d4ET8tOXHHZwkXpxIcaffFQDHUhG+pk9naGV8vBI/5Jgf7OogaCqpB/B1kfD9Uj+aC3zRYRkeTMHyjHfGiuKnUCGpILVtdgVBqCPOpa+CtrG3dZ9cJac3QpUx9Ak6uRnfver+qMfiR6qaaW0aCGOfhQj6wvF1bF1vB+UUgh1v6Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9846B27B5; Fri, 22 Aug 2025 08:33:47 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 8C4363F63F; Fri, 22 Aug 2025 08:33:50 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 30/33] arm_mpam: Use long MBWU counters if supported Date: Fri, 22 Aug 2025 15:30:11 +0000 Message-Id: <20250822153048.2287-31-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rohit Mathew If the 44 bit (long) or 63 bit (LWD) counters are detected on probing the RIS, use long/LWD counter instead of the regular 31 bit mbwu counter. Only 32bit accesses to the MSC are required to be supported by the spec, but these registers are 64bits. The lower half may overflow into the higher half between two 32bit reads. To avoid this, use a helper that reads the top half multiple times to check for overflow. Signed-off-by: Rohit Mathew [morse: merged multiple patches from Rohit] Signed-off-by: James Morse Reviewed-by: Ben Horgan --- Changes since RFC: * Commit message wrangling. * Refer to 31 bit counters as opposed to 32 bit (registers). --- drivers/resctrl/mpam_devices.c | 89 ++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 2ab7f127baaa..8fbcf6eb946a 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1002,6 +1002,48 @@ struct mon_read { int err; }; =20 +static bool mpam_ris_has_mbwu_long_counter(struct mpam_msc_ris *ris) +{ + return (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, &ris->props) || + mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props)); +} + +static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc) +{ + int retry =3D 3; + u32 mbwu_l_low; + u64 mbwu_l_high1, mbwu_l_high2; + + mpam_mon_sel_lock_held(msc); + + WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + mbwu_l_high2 =3D __mpam_read_reg(msc, MSMON_MBWU_L + 4); + do { + mbwu_l_high1 =3D mbwu_l_high2; + mbwu_l_low =3D __mpam_read_reg(msc, MSMON_MBWU_L); + mbwu_l_high2 =3D __mpam_read_reg(msc, MSMON_MBWU_L + 4); + + retry--; + } while (mbwu_l_high1 !=3D mbwu_l_high2 && retry > 0); + + if (mbwu_l_high1 =3D=3D mbwu_l_high2) + return (mbwu_l_high1 << 32) | mbwu_l_low; + return MSMON___NRDY_L; +} + +static void mpam_msc_zero_mbwu_l(struct mpam_msc *msc) +{ + mpam_mon_sel_lock_held(msc); + + WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + __mpam_write_reg(msc, MSMON_MBWU_L, 0); + __mpam_write_reg(msc, MSMON_MBWU_L + 4, 0); +} + static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, u32 *flt_val) { @@ -1058,6 +1100,7 @@ static void read_msmon_ctl_flt_vals(struct mon_read *= m, u32 *ctl_val, static void clean_msmon_ctl_val(u32 *cur_ctl) { *cur_ctl &=3D ~MSMON_CFG_x_CTL_OFLOW_STATUS; + *cur_ctl &=3D ~MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L; } =20 static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, @@ -1080,7 +1123,11 @@ static void write_msmon_ctl_flt_vals(struct mon_read= *m, u32 ctl_val, case mpam_feat_msmon_mbwu: mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); - mpam_write_monsel_reg(msc, MBWU, 0); + if (mpam_ris_has_mbwu_long_counter(m->ris)) + mpam_msc_zero_mbwu_l(m->ris->vmsc->msc); + else + mpam_write_monsel_reg(msc, MBWU, 0); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); =20 mbwu_state =3D &m->ris->mbwu_state[m->ctx->mon]; @@ -1095,8 +1142,13 @@ static void write_msmon_ctl_flt_vals(struct mon_read= *m, u32 ctl_val, =20 static u64 mpam_msmon_overflow_val(struct mpam_msc_ris *ris) { - /* TODO: scaling, and long counters */ - return GENMASK_ULL(30, 0); + /* TODO: implement scaling counters */ + if (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, &ris->props)) + return GENMASK_ULL(62, 0); + else if (mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props)) + return GENMASK_ULL(43, 0); + else + return GENMASK_ULL(30, 0); } =20 /* Call with MSC lock held */ @@ -1138,10 +1190,24 @@ static void __ris_msmon_read(void *arg) now =3D FIELD_GET(MSMON___VALUE, now); break; case mpam_feat_msmon_mbwu: - now =3D mpam_read_monsel_reg(msc, MBWU); - if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) - nrdy =3D now & MSMON___NRDY; - now =3D FIELD_GET(MSMON___VALUE, now); + /* + * If long or lwd counters are supported, use them, else revert + * to the 31 bit counter. + */ + if (mpam_ris_has_mbwu_long_counter(ris)) { + now =3D mpam_msc_read_mbwu_l(msc); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY_L; + if (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, rprops)) + now =3D FIELD_GET(MSMON___LWD_VALUE, now); + else + now =3D FIELD_GET(MSMON___L_VALUE, now); + } else { + now =3D mpam_read_monsel_reg(msc, MBWU); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); + } =20 if (nrdy) break; @@ -1433,8 +1499,13 @@ static int mpam_save_mbwu_state(void *arg) cur_ctl =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0); =20 - val =3D mpam_read_monsel_reg(msc, MBWU); - mpam_write_monsel_reg(msc, MBWU, 0); + if (mpam_ris_has_mbwu_long_counter(ris)) { + val =3D mpam_msc_read_mbwu_l(msc); + mpam_msc_zero_mbwu_l(msc); + } else { + val =3D mpam_read_monsel_reg(msc, MBWU); + mpam_write_monsel_reg(msc, MBWU, 0); + } =20 cfg->mon =3D i; cfg->pmg =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PMG, cur_flt); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 80DC82882B2; Fri, 22 Aug 2025 15:37:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877030; cv=none; b=USOSTHcEZhDIGBHpbVl2y488WO4L+MAwRRyRnyTiiVVr8kwyMSv3FKBvWL82bxLarMV+p6612eieNO4VPZO8f/NSWyFW9gFbMhqRb24nIJhxrYfyYv0iGHAXRQIv/vea9Yu+dXi6Lkf4azq1ZwO3BNSpP1BjO6y7Ai3Mjd/+kWU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877030; c=relaxed/simple; bh=s+tKsbJi/c/NTyvZ20R8uRUL3GNh8OK2lBvLuotvBO8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UCxuvY4MBZDFtoC4ULHXB+YGGwngVKZbBdFbmx7EN8uTnzcBhQLlkVFwUbTYcBcFSFc0mktGFQnigT9zeZN3cHKCBoHy0WpZ++wWADSjhhR0TKJE0d3wBnDSW1KCoD5u3z6dBY/NpUKp3R13k/s2mLUup66cHX3WZYciaIBkWcY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id B3F1D27B5; Fri, 22 Aug 2025 08:36:59 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A3ADE3F63F; Fri, 22 Aug 2025 08:37:02 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 30/33] arm_mpam: Use long MBWU counters if supported Date: Fri, 22 Aug 2025 15:30:45 +0000 Message-Id: <20250822153048.2287-65-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Rohit Mathew If the 44 bit (long) or 63 bit (LWD) counters are detected on probing the RIS, use long/LWD counter instead of the regular 31 bit mbwu counter. Only 32bit accesses to the MSC are required to be supported by the spec, but these registers are 64bits. The lower half may overflow into the higher half between two 32bit reads. To avoid this, use a helper that reads the top half multiple times to check for overflow. Signed-off-by: Rohit Mathew [morse: merged multiple patches from Rohit] Signed-off-by: James Morse --- Changes since RFC: * Commit message wrangling. * Refer to 31 bit counters as opposed to 32 bit (registers). --- drivers/resctrl/mpam_devices.c | 89 ++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 2ab7f127baaa..8fbcf6eb946a 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1002,6 +1002,48 @@ struct mon_read { int err; }; =20 +static bool mpam_ris_has_mbwu_long_counter(struct mpam_msc_ris *ris) +{ + return (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, &ris->props) || + mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props)); +} + +static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc) +{ + int retry =3D 3; + u32 mbwu_l_low; + u64 mbwu_l_high1, mbwu_l_high2; + + mpam_mon_sel_lock_held(msc); + + WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + mbwu_l_high2 =3D __mpam_read_reg(msc, MSMON_MBWU_L + 4); + do { + mbwu_l_high1 =3D mbwu_l_high2; + mbwu_l_low =3D __mpam_read_reg(msc, MSMON_MBWU_L); + mbwu_l_high2 =3D __mpam_read_reg(msc, MSMON_MBWU_L + 4); + + retry--; + } while (mbwu_l_high1 !=3D mbwu_l_high2 && retry > 0); + + if (mbwu_l_high1 =3D=3D mbwu_l_high2) + return (mbwu_l_high1 << 32) | mbwu_l_low; + return MSMON___NRDY_L; +} + +static void mpam_msc_zero_mbwu_l(struct mpam_msc *msc) +{ + mpam_mon_sel_lock_held(msc); + + WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz); + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); + + __mpam_write_reg(msc, MSMON_MBWU_L, 0); + __mpam_write_reg(msc, MSMON_MBWU_L + 4, 0); +} + static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val, u32 *flt_val) { @@ -1058,6 +1100,7 @@ static void read_msmon_ctl_flt_vals(struct mon_read *= m, u32 *ctl_val, static void clean_msmon_ctl_val(u32 *cur_ctl) { *cur_ctl &=3D ~MSMON_CFG_x_CTL_OFLOW_STATUS; + *cur_ctl &=3D ~MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L; } =20 static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val, @@ -1080,7 +1123,11 @@ static void write_msmon_ctl_flt_vals(struct mon_read= *m, u32 ctl_val, case mpam_feat_msmon_mbwu: mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val); - mpam_write_monsel_reg(msc, MBWU, 0); + if (mpam_ris_has_mbwu_long_counter(m->ris)) + mpam_msc_zero_mbwu_l(m->ris->vmsc->msc); + else + mpam_write_monsel_reg(msc, MBWU, 0); + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN); =20 mbwu_state =3D &m->ris->mbwu_state[m->ctx->mon]; @@ -1095,8 +1142,13 @@ static void write_msmon_ctl_flt_vals(struct mon_read= *m, u32 ctl_val, =20 static u64 mpam_msmon_overflow_val(struct mpam_msc_ris *ris) { - /* TODO: scaling, and long counters */ - return GENMASK_ULL(30, 0); + /* TODO: implement scaling counters */ + if (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, &ris->props)) + return GENMASK_ULL(62, 0); + else if (mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props)) + return GENMASK_ULL(43, 0); + else + return GENMASK_ULL(30, 0); } =20 /* Call with MSC lock held */ @@ -1138,10 +1190,24 @@ static void __ris_msmon_read(void *arg) now =3D FIELD_GET(MSMON___VALUE, now); break; case mpam_feat_msmon_mbwu: - now =3D mpam_read_monsel_reg(msc, MBWU); - if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) - nrdy =3D now & MSMON___NRDY; - now =3D FIELD_GET(MSMON___VALUE, now); + /* + * If long or lwd counters are supported, use them, else revert + * to the 31 bit counter. + */ + if (mpam_ris_has_mbwu_long_counter(ris)) { + now =3D mpam_msc_read_mbwu_l(msc); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY_L; + if (mpam_has_feature(mpam_feat_msmon_mbwu_63counter, rprops)) + now =3D FIELD_GET(MSMON___LWD_VALUE, now); + else + now =3D FIELD_GET(MSMON___L_VALUE, now); + } else { + now =3D mpam_read_monsel_reg(msc, MBWU); + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops)) + nrdy =3D now & MSMON___NRDY; + now =3D FIELD_GET(MSMON___VALUE, now); + } =20 if (nrdy) break; @@ -1433,8 +1499,13 @@ static int mpam_save_mbwu_state(void *arg) cur_ctl =3D mpam_read_monsel_reg(msc, CFG_MBWU_CTL); mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0); =20 - val =3D mpam_read_monsel_reg(msc, MBWU); - mpam_write_monsel_reg(msc, MBWU, 0); + if (mpam_ris_has_mbwu_long_counter(ris)) { + val =3D mpam_msc_read_mbwu_l(msc); + mpam_msc_zero_mbwu_l(msc); + } else { + val =3D mpam_read_monsel_reg(msc, MBWU); + mpam_write_monsel_reg(msc, MBWU, 0); + } =20 cfg->mon =3D i; cfg->pmg =3D FIELD_GET(MSMON_CFG_MBWU_FLT_PMG, cur_flt); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1184E2877F5; Fri, 22 Aug 2025 15:37:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877035; cv=none; b=npZZl2RML82A8SnFED8UBtWIY7y9ASE1hLLWTdxjG1yjUX8JugQUQYYKLMqNdpqe5HlHsgSlJI2Z6or+aGFeHsNtp9lsbhQeiniK1LGbBxvlkuP9Qdm+kCyTXtytSBKTVmhTak6Nx2ROU7FB27BmmasHFiMO74FgGUBpArohwEc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877035; c=relaxed/simple; bh=18sehYH0E8APdpVHinIWVpga+ncSNyEOb9yu9LBVPzI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PQo6S91Z2SuLH7vh0sM1osZUX5PxU5yQn0QSy0HwurMJpn97y70NmAqci0DlI7j6HRrWQYsf7vZaif78NAe6mEFMbWzhv6pGvahPAy0EhuRD3lOeDs5KfGziLK0mLQunk8wfqiyMz47Kt1rZqDlSnQ7BmWK0uuWiLMCRm9xVdK0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6A1CD15A1; Fri, 22 Aug 2025 08:37:05 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 4247A3F63F; Fri, 22 Aug 2025 08:37:08 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 31/33] arm_mpam: Add helper to reset saved mbwu state Date: Fri, 22 Aug 2025 15:30:46 +0000 Message-Id: <20250822153048.2287-66-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" resctrl expects to reset the bandwidth counters when the filesystem is mounted. To allow this, add a helper that clears the saved mbwu state. Instead of cross calling to each CPU that can access the component MSC to write to the counter, set a flag that causes it to be zero'd on the the next read. This is easily done by forcing a configuration update. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 49 +++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 5 +++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 8fbcf6eb946a..65c30ebfe001 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1155,9 +1155,11 @@ static u64 mpam_msmon_overflow_val(struct mpam_msc_r= is *ris) static void __ris_msmon_read(void *arg) { bool nrdy =3D false; + bool config_mismatch; struct mon_read *m =3D arg; u64 now, overflow_val =3D 0; struct mon_cfg *ctx =3D m->ctx; + bool reset_on_next_read =3D false; struct mpam_msc_ris *ris =3D m->ris; struct msmon_mbwu_state *mbwu_state; struct mpam_props *rprops =3D &ris->props; @@ -1172,6 +1174,14 @@ static void __ris_msmon_read(void *arg) FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); =20 + if (m->type =3D=3D mpam_feat_msmon_mbwu) { + mbwu_state =3D &ris->mbwu_state[ctx->mon]; + if (mbwu_state) { + reset_on_next_read =3D mbwu_state->reset_on_next_read; + mbwu_state->reset_on_next_read =3D false; + } + } + /* * Read the existing configuration to avoid re-writing the same values. * This saves waiting for 'nrdy' on subsequent reads. @@ -1179,7 +1189,10 @@ static void __ris_msmon_read(void *arg) read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt); clean_msmon_ctl_val(&cur_ctl); gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val); - if (cur_flt !=3D flt_val || cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN)) + config_mismatch =3D cur_flt !=3D flt_val || + cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN); + + if (config_mismatch || reset_on_next_read) write_msmon_ctl_flt_vals(m, ctl_val, flt_val); =20 switch (m->type) { @@ -1212,7 +1225,6 @@ static void __ris_msmon_read(void *arg) if (nrdy) break; =20 - mbwu_state =3D &ris->mbwu_state[ctx->mon]; if (!mbwu_state) break; =20 @@ -1314,6 +1326,39 @@ int mpam_msmon_read(struct mpam_component *comp, str= uct mon_cfg *ctx, return err; } =20 +void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ct= x) +{ + int idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + if (!mpam_is_enabled()) + return; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + if (!mpam_has_feature(mpam_feat_msmon_mbwu, &vmsc->props)) + continue; + + msc =3D vmsc->msc; + mpam_mon_sel_outer_lock(msc); + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + if (!mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props)) + continue; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + continue; + + ris->mbwu_state[ctx->mon].correction =3D 0; + ris->mbwu_state[ctx->mon].reset_on_next_read =3D true; + mpam_mon_sel_inner_unlock(msc); + } + mpam_mon_sel_outer_unlock(msc); + } + srcu_read_unlock(&mpam_srcu, idx); +} + static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) { u32 num_words, msb; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9f627b5f72a1..bbf0306abc82 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -297,10 +297,12 @@ struct mon_cfg { =20 /* * Changes to enabled and cfg are protected by the msc->lock. - * Changes to prev_val and correction are protected by the msc's mon_sel_l= ock. + * Changes to reset_on_next_read, prev_val and correction are protected by= the + * msc's mon_sel_lock. */ struct msmon_mbwu_state { bool enabled; + bool reset_on_next_read; struct mon_cfg cfg; =20 /* The value last read from the hardware. Used to detect overflow. */ @@ -410,6 +412,7 @@ int mpam_apply_config(struct mpam_component *comp, u16 = partid, =20 int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, enum mpam_device_features, u64 *val); +void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ct= x); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 29D84263899; Fri, 22 Aug 2025 15:34:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876843; cv=none; b=oa45m/Kv8C2pVxyn6EUzsk5i7zOw/XY707R5xblyC5bE20wh1kc2Om+3RvvuN5RVyXdcB9W9kWCkGPOe3d6RnSlof4E+mYOJKI8TNFP9eppiJ8yi+/ZoHqcU38k6PfyPVO/W5vWUGAisErgLPuUvsi/FXDnS1E9Ke90FRRmJEUg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876843; c=relaxed/simple; bh=18sehYH0E8APdpVHinIWVpga+ncSNyEOb9yu9LBVPzI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RrI5FltohFqt8dKeT6QZQ7ugMJLIUy4wVmKprSY1Hno9qjl6DQANZDHn+qWxProhpQrvlUuVDl1ToaGFPsEnG+6/Vko1KsT+ZgpKHT9ysHEoPalzE3rdc7vU4nlHM5K63/0Z37j+Gtyd6qVucDodpvSh36SvZ59l1NIZogoja5w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 32CCE27B5; Fri, 22 Aug 2025 08:33:53 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 26AD63F63F; Fri, 22 Aug 2025 08:33:56 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 31/33] arm_mpam: Add helper to reset saved mbwu state Date: Fri, 22 Aug 2025 15:30:12 +0000 Message-Id: <20250822153048.2287-32-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" resctrl expects to reset the bandwidth counters when the filesystem is mounted. To allow this, add a helper that clears the saved mbwu state. Instead of cross calling to each CPU that can access the component MSC to write to the counter, set a flag that causes it to be zero'd on the the next read. This is easily done by forcing a configuration update. Signed-off-by: James Morse --- drivers/resctrl/mpam_devices.c | 49 +++++++++++++++++++++++++++++++-- drivers/resctrl/mpam_internal.h | 5 +++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 8fbcf6eb946a..65c30ebfe001 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -1155,9 +1155,11 @@ static u64 mpam_msmon_overflow_val(struct mpam_msc_r= is *ris) static void __ris_msmon_read(void *arg) { bool nrdy =3D false; + bool config_mismatch; struct mon_read *m =3D arg; u64 now, overflow_val =3D 0; struct mon_cfg *ctx =3D m->ctx; + bool reset_on_next_read =3D false; struct mpam_msc_ris *ris =3D m->ris; struct msmon_mbwu_state *mbwu_state; struct mpam_props *rprops =3D &ris->props; @@ -1172,6 +1174,14 @@ static void __ris_msmon_read(void *arg) FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx); mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel); =20 + if (m->type =3D=3D mpam_feat_msmon_mbwu) { + mbwu_state =3D &ris->mbwu_state[ctx->mon]; + if (mbwu_state) { + reset_on_next_read =3D mbwu_state->reset_on_next_read; + mbwu_state->reset_on_next_read =3D false; + } + } + /* * Read the existing configuration to avoid re-writing the same values. * This saves waiting for 'nrdy' on subsequent reads. @@ -1179,7 +1189,10 @@ static void __ris_msmon_read(void *arg) read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt); clean_msmon_ctl_val(&cur_ctl); gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val); - if (cur_flt !=3D flt_val || cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN)) + config_mismatch =3D cur_flt !=3D flt_val || + cur_ctl !=3D (ctl_val | MSMON_CFG_x_CTL_EN); + + if (config_mismatch || reset_on_next_read) write_msmon_ctl_flt_vals(m, ctl_val, flt_val); =20 switch (m->type) { @@ -1212,7 +1225,6 @@ static void __ris_msmon_read(void *arg) if (nrdy) break; =20 - mbwu_state =3D &ris->mbwu_state[ctx->mon]; if (!mbwu_state) break; =20 @@ -1314,6 +1326,39 @@ int mpam_msmon_read(struct mpam_component *comp, str= uct mon_cfg *ctx, return err; } =20 +void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ct= x) +{ + int idx; + struct mpam_msc *msc; + struct mpam_vmsc *vmsc; + struct mpam_msc_ris *ris; + + if (!mpam_is_enabled()) + return; + + idx =3D srcu_read_lock(&mpam_srcu); + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) { + if (!mpam_has_feature(mpam_feat_msmon_mbwu, &vmsc->props)) + continue; + + msc =3D vmsc->msc; + mpam_mon_sel_outer_lock(msc); + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) { + if (!mpam_has_feature(mpam_feat_msmon_mbwu, &ris->props)) + continue; + + if (WARN_ON_ONCE(!mpam_mon_sel_inner_lock(msc))) + continue; + + ris->mbwu_state[ctx->mon].correction =3D 0; + ris->mbwu_state[ctx->mon].reset_on_next_read =3D true; + mpam_mon_sel_inner_unlock(msc); + } + mpam_mon_sel_outer_unlock(msc); + } + srcu_read_unlock(&mpam_srcu, idx); +} + static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd) { u32 num_words, msb; diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index 9f627b5f72a1..bbf0306abc82 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -297,10 +297,12 @@ struct mon_cfg { =20 /* * Changes to enabled and cfg are protected by the msc->lock. - * Changes to prev_val and correction are protected by the msc's mon_sel_l= ock. + * Changes to reset_on_next_read, prev_val and correction are protected by= the + * msc's mon_sel_lock. */ struct msmon_mbwu_state { bool enabled; + bool reset_on_next_read; struct mon_cfg cfg; =20 /* The value last read from the hardware. Used to detect overflow. */ @@ -410,6 +412,7 @@ int mpam_apply_config(struct mpam_component *comp, u16 = partid, =20 int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx, enum mpam_device_features, u64 *val); +void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ct= x); =20 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BB354263F4E; Fri, 22 Aug 2025 15:34:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876849; cv=none; b=X12DAZMMqmIHF6z7lbYFVZpFG0JmpdgCdDsS8rifKTlWC/w60PYJxJ/zOkcLIiZQfk4eeNB8aWYWhYUWTcENjs2vKQsuFIzi1jv0mT63wGiESwmTdv16m+/j3902GbJwc/HGAhc/JuVxJI33DyS0Vw4HETFQeZqKiRgJbL6Ssb0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876849; c=relaxed/simple; bh=6pZPHCBdZGWJ2iBr/oY/7x1I2KQSchwTuv6iBfvTpq4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iuAL/Rw4NB6PAsuW24x4kS0D7kiDt171Vje6qwNLdxPhHZDbJH4hqVDg+VBsm6IcqB6YZZPpQdW8NC40DnksueQBPBZ8C9udgjsjlYrLbcB0bV2P+sve1CuvU6EEoQFddx2KVHp9bv65au8IIOLtCL22C2u99kvtXNvqiTUQntc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E645227DC; Fri, 22 Aug 2025 08:33:58 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B90BD3F63F; Fri, 22 Aug 2025 08:34:01 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Jonathan Cameron Subject: [PATCH 32/33] arm_mpam: Add kunit test for bitmap reset Date: Fri, 22 Aug 2025 15:30:13 +0000 Message-Id: <20250822153048.2287-33-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The bitmap reset code has been a source of bugs. Add a unit test. This currently has to be built in, as the rest of the driver is builtin. Suggested-by: Jonathan Cameron Signed-off-by: James Morse --- drivers/resctrl/Kconfig | 13 ++++++ drivers/resctrl/mpam_devices.c | 4 ++ drivers/resctrl/test_mpam_devices.c | 68 +++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 drivers/resctrl/test_mpam_devices.c diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index dff7b87280ab..f5e0609975e4 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -4,8 +4,21 @@ config ARM64_MPAM_DRIVER bool "MPAM driver for System IP, e,g. caches and memory controllers" depends on ARM64_MPAM && EXPERT =20 +menu "ARM64 MPAM driver options" + config ARM64_MPAM_DRIVER_DEBUG bool "Enable debug messages from the MPAM driver." depends on ARM64_MPAM_DRIVER help Say yes here to enable debug messages from the MPAM driver. + +config MPAM_KUNIT_TEST + bool "KUnit tests for MPAM driver " if !KUNIT_ALL_TESTS + depends on KUNIT=3Dy + default KUNIT_ALL_TESTS + help + Enable this option to run tests in the MPAM driver. + + If unsure, say N. + +endmenu diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 65c30ebfe001..4cf5aae88c53 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -2903,3 +2903,7 @@ static int __init mpam_msc_driver_init(void) } /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ subsys_initcall(mpam_msc_driver_init); + +#ifdef CONFIG_MPAM_KUNIT_TEST +#include "test_mpam_devices.c" +#endif diff --git a/drivers/resctrl/test_mpam_devices.c b/drivers/resctrl/test_mpa= m_devices.c new file mode 100644 index 000000000000..8e9d6c88171c --- /dev/null +++ b/drivers/resctrl/test_mpam_devices.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2024 Arm Ltd. +/* This file is intended to be included into mpam_devices.c */ + +#include + +static void test_mpam_reset_msc_bitmap(struct kunit *test) +{ + char *buf =3D kunit_kzalloc(test, SZ_16K, GFP_KERNEL); + struct mpam_msc fake_msc; + u32 *test_result; + + if (!buf) + return; + + fake_msc.mapped_hwpage =3D buf; + fake_msc.mapped_hwpage_sz =3D SZ_16K; + cpumask_copy(&fake_msc.accessibility, cpu_possible_mask); + + mutex_init(&fake_msc.part_sel_lock); + mutex_lock(&fake_msc.part_sel_lock); + + test_result =3D (u32 *)(buf + MPAMCFG_CPBM); + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 0); + KUNIT_EXPECT_EQ(test, test_result[0], 0); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 1); + KUNIT_EXPECT_EQ(test, test_result[0], 1); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 16); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffff); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 32); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 33); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff); + KUNIT_EXPECT_EQ(test, test_result[1], 1); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mutex_unlock(&fake_msc.part_sel_lock); +} + +static struct kunit_case mpam_devices_test_cases[] =3D { + KUNIT_CASE(test_mpam_reset_msc_bitmap), + {} +}; + +static struct kunit_suite mpam_devices_test_suite =3D { + .name =3D "mpam_devices_test_suite", + .test_cases =3D mpam_devices_test_cases, +}; + +kunit_test_suites(&mpam_devices_test_suite); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0DD97288C27; Fri, 22 Aug 2025 15:37:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877041; cv=none; b=LmsMGIz86l45ApH5XvDkBDJh0AV+7MmlCue7qKh4rXfNY0PUKXzymCR2WFumR5PaRmZeKfNFYZfHYj5qn7eD9vcgFpCu0hQa8ghMydJENUtSu1W/2xY1OkcxJCOQzgWzBoBVBeoFpT2pHAm7/0xDPkeHALtNgiinody45Xby4GI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877041; c=relaxed/simple; bh=6pZPHCBdZGWJ2iBr/oY/7x1I2KQSchwTuv6iBfvTpq4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=sj/6EOhr/1RIadMbRURmXlAr/3N3dEB4K5CxjtN+hYSDq8BJsv5W2PvXAQrC26WcP+4bl5OeFyqAdc40ePnAX+SWreVOQ3PGPn6gkfDKsG1/pPfzBlRrd40akgbj1VXpwppJrJ/qY88TxD29xUKPXYOLpl4sybCxhI3LOvL/9hs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2B7EF15A1; Fri, 22 Aug 2025 08:37:11 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id ECDDE3F63F; Fri, 22 Aug 2025 08:37:13 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich , Jonathan Cameron Subject: [PATCH 32/33] arm_mpam: Add kunit test for bitmap reset Date: Fri, 22 Aug 2025 15:30:47 +0000 Message-Id: <20250822153048.2287-67-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The bitmap reset code has been a source of bugs. Add a unit test. This currently has to be built in, as the rest of the driver is builtin. Suggested-by: Jonathan Cameron Signed-off-by: James Morse --- drivers/resctrl/Kconfig | 13 ++++++ drivers/resctrl/mpam_devices.c | 4 ++ drivers/resctrl/test_mpam_devices.c | 68 +++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 drivers/resctrl/test_mpam_devices.c diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index dff7b87280ab..f5e0609975e4 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -4,8 +4,21 @@ config ARM64_MPAM_DRIVER bool "MPAM driver for System IP, e,g. caches and memory controllers" depends on ARM64_MPAM && EXPERT =20 +menu "ARM64 MPAM driver options" + config ARM64_MPAM_DRIVER_DEBUG bool "Enable debug messages from the MPAM driver." depends on ARM64_MPAM_DRIVER help Say yes here to enable debug messages from the MPAM driver. + +config MPAM_KUNIT_TEST + bool "KUnit tests for MPAM driver " if !KUNIT_ALL_TESTS + depends on KUNIT=3Dy + default KUNIT_ALL_TESTS + help + Enable this option to run tests in the MPAM driver. + + If unsure, say N. + +endmenu diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 65c30ebfe001..4cf5aae88c53 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -2903,3 +2903,7 @@ static int __init mpam_msc_driver_init(void) } /* Must occur after arm64_mpam_register_cpus() from arch_initcall() */ subsys_initcall(mpam_msc_driver_init); + +#ifdef CONFIG_MPAM_KUNIT_TEST +#include "test_mpam_devices.c" +#endif diff --git a/drivers/resctrl/test_mpam_devices.c b/drivers/resctrl/test_mpa= m_devices.c new file mode 100644 index 000000000000..8e9d6c88171c --- /dev/null +++ b/drivers/resctrl/test_mpam_devices.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2024 Arm Ltd. +/* This file is intended to be included into mpam_devices.c */ + +#include + +static void test_mpam_reset_msc_bitmap(struct kunit *test) +{ + char *buf =3D kunit_kzalloc(test, SZ_16K, GFP_KERNEL); + struct mpam_msc fake_msc; + u32 *test_result; + + if (!buf) + return; + + fake_msc.mapped_hwpage =3D buf; + fake_msc.mapped_hwpage_sz =3D SZ_16K; + cpumask_copy(&fake_msc.accessibility, cpu_possible_mask); + + mutex_init(&fake_msc.part_sel_lock); + mutex_lock(&fake_msc.part_sel_lock); + + test_result =3D (u32 *)(buf + MPAMCFG_CPBM); + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 0); + KUNIT_EXPECT_EQ(test, test_result[0], 0); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 1); + KUNIT_EXPECT_EQ(test, test_result[0], 1); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 16); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffff); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 32); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff); + KUNIT_EXPECT_EQ(test, test_result[1], 0); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 33); + KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff); + KUNIT_EXPECT_EQ(test, test_result[1], 1); + test_result[0] =3D 0; + test_result[1] =3D 0; + + mutex_unlock(&fake_msc.part_sel_lock); +} + +static struct kunit_case mpam_devices_test_cases[] =3D { + KUNIT_CASE(test_mpam_reset_msc_bitmap), + {} +}; + +static struct kunit_suite mpam_devices_test_suite =3D { + .name =3D "mpam_devices_test_suite", + .test_cases =3D mpam_devices_test_cases, +}; + +kunit_test_suites(&mpam_devices_test_suite); --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 2A235223DDA; Fri, 22 Aug 2025 15:34:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876855; cv=none; b=MRBsgGQT6C80R4Pyn8K1tKigD4KLWv7rTSfKXz3n4UTg+7cgnGT1oc0fYdEB/dk8BrAVULEXZIluIOLkjYbK7TdVUjl3E6jvPRlqlIxo64eH5ll6SqnLV23xrKP8A6b36Oe11MVZ9sSwWAop9FJskYERBiondTEc48eT7+IdtGg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755876855; c=relaxed/simple; bh=lragvBPTnKAmGnzARVDDLvZBMkp4gYril1NUMVAfObk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gDt5VJ3wiwfzPydMtPBs7X4YlK2Grr30AmgaF1u+SY9GcdWmLoejv7DNtI9cZSaf+DcuozGXEK1e+/D9WotYcD2Jjve0uWrb49Gh9qtAadGashx5wRP8m+iF1rn/nnNo1noDQmdjt1mXYHIUF2+EtqrJesnXK2xhVP1t6Rlw7fU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 83E962880; Fri, 22 Aug 2025 08:34:04 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 752093F63F; Fri, 22 Aug 2025 08:34:07 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 33/33] arm_mpam: Add kunit tests for props_mismatch() Date: Fri, 22 Aug 2025 15:30:14 +0000 Message-Id: <20250822153048.2287-34-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When features are mismatched between MSC the way features are combined to the class determines whether resctrl can support this SoC. Add some tests to illustrate the sort of thing that is expected to work, and those that must be removed. Signed-off-by: James Morse --- drivers/resctrl/mpam_internal.h | 8 +- drivers/resctrl/test_mpam_devices.c | 322 ++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index bbf0306abc82..6e973be095f8 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -18,6 +18,12 @@ =20 DECLARE_STATIC_KEY_FALSE(mpam_enabled); =20 +#ifdef CONFIG_MPAM_KUNIT_TEST +#define PACKED_FOR_KUNIT __packed +#else +#define PACKED_FOR_KUNIT +#endif + static inline bool mpam_is_enabled(void) { return static_branch_likely(&mpam_enabled); @@ -209,7 +215,7 @@ struct mpam_props { u16 dspri_wd; u16 num_csu_mon; u16 num_mbwu_mon; -}; +} PACKED_FOR_KUNIT; =20 #define mpam_has_feature(_feat, x) ((1 << (_feat)) & (x)->features) =20 diff --git a/drivers/resctrl/test_mpam_devices.c b/drivers/resctrl/test_mpa= m_devices.c index 8e9d6c88171c..ef39696e7ff8 100644 --- a/drivers/resctrl/test_mpam_devices.c +++ b/drivers/resctrl/test_mpam_devices.c @@ -4,6 +4,326 @@ =20 #include =20 +/* + * This test catches fields that aren't being sanitised - but can't tell y= ou + * which one... + */ +static void test__props_mismatch(struct kunit *test) +{ + struct mpam_props parent =3D { 0 }; + struct mpam_props child; + + memset(&child, 0xff, sizeof(child)); + __props_mismatch(&parent, &child, false); + + memset(&child, 0, sizeof(child)); + KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0); + + memset(&child, 0xff, sizeof(child)); + __props_mismatch(&parent, &child, true); + + KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0); +} + +static void test_mpam_enable_merge_features(struct kunit *test) +{ + /* o/` How deep is your stack? o/` */ + struct list_head fake_classes_list; + struct mpam_class fake_class =3D { 0 }; + struct mpam_component fake_comp1 =3D { 0 }; + struct mpam_component fake_comp2 =3D { 0 }; + struct mpam_vmsc fake_vmsc1 =3D { 0 }; + struct mpam_vmsc fake_vmsc2 =3D { 0 }; + struct mpam_msc fake_msc1 =3D { 0 }; + struct mpam_msc fake_msc2 =3D { 0 }; + struct mpam_msc_ris fake_ris1 =3D { 0 }; + struct mpam_msc_ris fake_ris2 =3D { 0 }; + struct platform_device fake_pdev =3D { 0 }; + +#define RESET_FAKE_HIEARCHY() do { \ + INIT_LIST_HEAD(&fake_classes_list); \ + \ + memset(&fake_class, 0, sizeof(fake_class)); \ + fake_class.level =3D 3; \ + fake_class.type =3D MPAM_CLASS_CACHE; \ + INIT_LIST_HEAD_RCU(&fake_class.components); \ + INIT_LIST_HEAD(&fake_class.classes_list); \ + \ + memset(&fake_comp1, 0, sizeof(fake_comp1)); \ + memset(&fake_comp2, 0, sizeof(fake_comp2)); \ + fake_comp1.comp_id =3D 1; \ + fake_comp2.comp_id =3D 2; \ + INIT_LIST_HEAD(&fake_comp1.vmsc); \ + INIT_LIST_HEAD(&fake_comp1.class_list); \ + INIT_LIST_HEAD(&fake_comp2.vmsc); \ + INIT_LIST_HEAD(&fake_comp2.class_list); \ + \ + memset(&fake_vmsc1, 0, sizeof(fake_vmsc1)); \ + memset(&fake_vmsc2, 0, sizeof(fake_vmsc2)); \ + INIT_LIST_HEAD(&fake_vmsc1.ris); \ + INIT_LIST_HEAD(&fake_vmsc1.comp_list); \ + fake_vmsc1.msc =3D &fake_msc1; \ + INIT_LIST_HEAD(&fake_vmsc2.ris); \ + INIT_LIST_HEAD(&fake_vmsc2.comp_list); \ + fake_vmsc2.msc =3D &fake_msc2; \ + \ + memset(&fake_ris1, 0, sizeof(fake_ris1)); \ + memset(&fake_ris2, 0, sizeof(fake_ris2)); \ + fake_ris1.ris_idx =3D 1; \ + INIT_LIST_HEAD(&fake_ris1.msc_list); \ + fake_ris2.ris_idx =3D 2; \ + INIT_LIST_HEAD(&fake_ris2.msc_list); \ + \ + fake_msc1.pdev =3D &fake_pdev; \ + fake_msc2.pdev =3D &fake_pdev; \ + \ + list_add(&fake_class.classes_list, &fake_classes_list); \ +} while (0) + + RESET_FAKE_HIEARCHY(); + + mutex_lock(&mpam_list_lock); + + /* One Class+Comp, two RIS in one vMSC with common features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D NULL; + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc1; + list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two RIS in one vMSC with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D NULL; + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc1; + list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* Multiple RIS within one MSC controlling the same resource can be misma= tched */ + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class= .props)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_vmsc1= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + KUNIT_EXPECT_EQ(test, fake_vmsc1.props.cmax_wd, 4); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_clas= s.props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with incompatible overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + mpam_set_feature(mpam_feat_mbw_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_mbw_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 5; + fake_ris2.props.cpbm_wd =3D 3; + fake_ris1.props.mbw_pbm_bits =3D 5; + fake_ris2.props.mbw_pbm_bits =3D 3; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_mbw_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.mbw_pbm_bits, 0); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with overlapping features that need tweaking */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_mbw_min, &fake_ris1.props); + mpam_set_feature(mpam_feat_mbw_min, &fake_ris2.props); + mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris2.props); + fake_ris1.props.bwa_wd =3D 5; + fake_ris2.props.bwa_wd =3D 3; + fake_ris1.props.cmax_wd =3D 5; + fake_ris2.props.cmax_wd =3D 3; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_mbw_min, &fake_class.p= rops)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmax, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.bwa_wd, 3); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 3); + + RESET_FAKE_HIEARCHY(); + + /* One Class Two Comp with overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D &fake_class; + list_add(&fake_comp2.class_list, &fake_class.components); + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp2; + list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class Two Comp with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D &fake_class; + list_add(&fake_comp2.class_list, &fake_class.components); + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp2; + list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple components can't control the same resource, mismatched featur= es can + * not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_clas= s.props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0); + + mutex_unlock(&mpam_list_lock); + +#undef RESET_FAKE_HIEARCHY +} + static void test_mpam_reset_msc_bitmap(struct kunit *test) { char *buf =3D kunit_kzalloc(test, SZ_16K, GFP_KERNEL); @@ -57,6 +377,8 @@ static void test_mpam_reset_msc_bitmap(struct kunit *tes= t) =20 static struct kunit_case mpam_devices_test_cases[] =3D { KUNIT_CASE(test_mpam_reset_msc_bitmap), + KUNIT_CASE(test_mpam_enable_merge_features), + KUNIT_CASE(test__props_mismatch), {} }; =20 --=20 2.20.1 From nobody Fri Oct 3 23:02:10 2025 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 96C4F288537; Fri, 22 Aug 2025 15:37:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877047; cv=none; b=qn8iIxu1dgsxMSHy8fpJzjMS68IxYxCF6nnKRNfDCVWfeXqmSabI2Sm73e3XoAHkSUbp0iPBvYU4+Z9KhFz2veS68mhEaLdT2VQBcKZRmQKSoUasvOTIIizsJntQ3bjewptTqAk+YM0zDpp9Z415NLWiYk0W6qcBb5Q/yhWAiCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755877047; c=relaxed/simple; bh=lragvBPTnKAmGnzARVDDLvZBMkp4gYril1NUMVAfObk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ispS1nBgtRGNJcVKPTo8o8sZadLIDB8rDGoLLt4yIePqGHTvZjtW6ZpkT860b/Epg03mbBoRzQYvPyV3KoZjQIW9yjlzmJ97oMnjV0TURYSRtGyVRKvPhjXxd6aG3unHRU/Iau7vMhPmcuquzOLB3D9JfnyUCLZlEA4jMPbakdY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BFF652880; Fri, 22 Aug 2025 08:37:16 -0700 (PDT) Received: from merodach.members.linode.com (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B04383F63F; Fri, 22 Aug 2025 08:37:19 -0700 (PDT) From: James Morse To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-acpi@vger.kernel.org, devicetree@vger.kernel.org Cc: James Morse , shameerali.kolothum.thodi@huawei.com, D Scott Phillips OS , carl@os.amperecomputing.com, lcherian@marvell.com, bobo.shaobowang@huawei.com, tan.shaopeng@fujitsu.com, baolin.wang@linux.alibaba.com, Jamie Iles , Xin Hao , peternewman@google.com, dfustini@baylibre.com, amitsinght@marvell.com, David Hildenbrand , Rex Nie , Dave Martin , Koba Ko , Shanker Donthineni , fenghuay@nvidia.com, baisheng.gao@unisoc.com, Jonathan Cameron , Rob Herring , Rohit Mathew , Rafael Wysocki , Len Brown , Lorenzo Pieralisi , Hanjun Guo , Sudeep Holla , Krzysztof Kozlowski , Conor Dooley , Catalin Marinas , Will Deacon , Greg Kroah-Hartman , Danilo Krummrich Subject: [PATCH 33/33] arm_mpam: Add kunit tests for props_mismatch() Date: Fri, 22 Aug 2025 15:30:48 +0000 Message-Id: <20250822153048.2287-68-james.morse@arm.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20250822153048.2287-1-james.morse@arm.com> References: <20250822153048.2287-1-james.morse@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When features are mismatched between MSC the way features are combined to the class determines whether resctrl can support this SoC. Add some tests to illustrate the sort of thing that is expected to work, and those that must be removed. Signed-off-by: James Morse --- drivers/resctrl/mpam_internal.h | 8 +- drivers/resctrl/test_mpam_devices.c | 322 ++++++++++++++++++++++++++++ 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_interna= l.h index bbf0306abc82..6e973be095f8 100644 --- a/drivers/resctrl/mpam_internal.h +++ b/drivers/resctrl/mpam_internal.h @@ -18,6 +18,12 @@ =20 DECLARE_STATIC_KEY_FALSE(mpam_enabled); =20 +#ifdef CONFIG_MPAM_KUNIT_TEST +#define PACKED_FOR_KUNIT __packed +#else +#define PACKED_FOR_KUNIT +#endif + static inline bool mpam_is_enabled(void) { return static_branch_likely(&mpam_enabled); @@ -209,7 +215,7 @@ struct mpam_props { u16 dspri_wd; u16 num_csu_mon; u16 num_mbwu_mon; -}; +} PACKED_FOR_KUNIT; =20 #define mpam_has_feature(_feat, x) ((1 << (_feat)) & (x)->features) =20 diff --git a/drivers/resctrl/test_mpam_devices.c b/drivers/resctrl/test_mpa= m_devices.c index 8e9d6c88171c..ef39696e7ff8 100644 --- a/drivers/resctrl/test_mpam_devices.c +++ b/drivers/resctrl/test_mpam_devices.c @@ -4,6 +4,326 @@ =20 #include =20 +/* + * This test catches fields that aren't being sanitised - but can't tell y= ou + * which one... + */ +static void test__props_mismatch(struct kunit *test) +{ + struct mpam_props parent =3D { 0 }; + struct mpam_props child; + + memset(&child, 0xff, sizeof(child)); + __props_mismatch(&parent, &child, false); + + memset(&child, 0, sizeof(child)); + KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0); + + memset(&child, 0xff, sizeof(child)); + __props_mismatch(&parent, &child, true); + + KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0); +} + +static void test_mpam_enable_merge_features(struct kunit *test) +{ + /* o/` How deep is your stack? o/` */ + struct list_head fake_classes_list; + struct mpam_class fake_class =3D { 0 }; + struct mpam_component fake_comp1 =3D { 0 }; + struct mpam_component fake_comp2 =3D { 0 }; + struct mpam_vmsc fake_vmsc1 =3D { 0 }; + struct mpam_vmsc fake_vmsc2 =3D { 0 }; + struct mpam_msc fake_msc1 =3D { 0 }; + struct mpam_msc fake_msc2 =3D { 0 }; + struct mpam_msc_ris fake_ris1 =3D { 0 }; + struct mpam_msc_ris fake_ris2 =3D { 0 }; + struct platform_device fake_pdev =3D { 0 }; + +#define RESET_FAKE_HIEARCHY() do { \ + INIT_LIST_HEAD(&fake_classes_list); \ + \ + memset(&fake_class, 0, sizeof(fake_class)); \ + fake_class.level =3D 3; \ + fake_class.type =3D MPAM_CLASS_CACHE; \ + INIT_LIST_HEAD_RCU(&fake_class.components); \ + INIT_LIST_HEAD(&fake_class.classes_list); \ + \ + memset(&fake_comp1, 0, sizeof(fake_comp1)); \ + memset(&fake_comp2, 0, sizeof(fake_comp2)); \ + fake_comp1.comp_id =3D 1; \ + fake_comp2.comp_id =3D 2; \ + INIT_LIST_HEAD(&fake_comp1.vmsc); \ + INIT_LIST_HEAD(&fake_comp1.class_list); \ + INIT_LIST_HEAD(&fake_comp2.vmsc); \ + INIT_LIST_HEAD(&fake_comp2.class_list); \ + \ + memset(&fake_vmsc1, 0, sizeof(fake_vmsc1)); \ + memset(&fake_vmsc2, 0, sizeof(fake_vmsc2)); \ + INIT_LIST_HEAD(&fake_vmsc1.ris); \ + INIT_LIST_HEAD(&fake_vmsc1.comp_list); \ + fake_vmsc1.msc =3D &fake_msc1; \ + INIT_LIST_HEAD(&fake_vmsc2.ris); \ + INIT_LIST_HEAD(&fake_vmsc2.comp_list); \ + fake_vmsc2.msc =3D &fake_msc2; \ + \ + memset(&fake_ris1, 0, sizeof(fake_ris1)); \ + memset(&fake_ris2, 0, sizeof(fake_ris2)); \ + fake_ris1.ris_idx =3D 1; \ + INIT_LIST_HEAD(&fake_ris1.msc_list); \ + fake_ris2.ris_idx =3D 2; \ + INIT_LIST_HEAD(&fake_ris2.msc_list); \ + \ + fake_msc1.pdev =3D &fake_pdev; \ + fake_msc2.pdev =3D &fake_pdev; \ + \ + list_add(&fake_class.classes_list, &fake_classes_list); \ +} while (0) + + RESET_FAKE_HIEARCHY(); + + mutex_lock(&mpam_list_lock); + + /* One Class+Comp, two RIS in one vMSC with common features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D NULL; + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc1; + list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two RIS in one vMSC with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D NULL; + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc1; + list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* Multiple RIS within one MSC controlling the same resource can be misma= tched */ + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class= .props)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_vmsc1= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + KUNIT_EXPECT_EQ(test, fake_vmsc1.props.cmax_wd, 4); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_clas= s.props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with incompatible overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + mpam_set_feature(mpam_feat_mbw_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_mbw_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 5; + fake_ris2.props.cpbm_wd =3D 3; + fake_ris1.props.mbw_pbm_bits =3D 5; + fake_ris2.props.mbw_pbm_bits =3D 3; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_mbw_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.mbw_pbm_bits, 0); + + RESET_FAKE_HIEARCHY(); + + /* One Class+Comp, two MSC with overlapping features that need tweaking */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D NULL; + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp1; + list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_mbw_min, &fake_ris1.props); + mpam_set_feature(mpam_feat_mbw_min, &fake_ris2.props); + mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris2.props); + fake_ris1.props.bwa_wd =3D 5; + fake_ris2.props.bwa_wd =3D 3; + fake_ris1.props.cmax_wd =3D 5; + fake_ris2.props.cmax_wd =3D 3; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple RIS in different MSC can't the same resource, mismatched + * features can not be supported. + */ + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_mbw_min, &fake_class.p= rops)); + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmax, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.bwa_wd, 3); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 3); + + RESET_FAKE_HIEARCHY(); + + /* One Class Two Comp with overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D &fake_class; + list_add(&fake_comp2.class_list, &fake_class.components); + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp2; + list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cpbm_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class= .props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4); + + RESET_FAKE_HIEARCHY(); + + /* One Class Two Comp with non-overlapping features */ + fake_comp1.class =3D &fake_class; + list_add(&fake_comp1.class_list, &fake_class.components); + fake_comp2.class =3D &fake_class; + list_add(&fake_comp2.class_list, &fake_class.components); + fake_vmsc1.comp =3D &fake_comp1; + list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc); + fake_vmsc2.comp =3D &fake_comp2; + list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc); + fake_ris1.vmsc =3D &fake_vmsc1; + list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris); + fake_ris2.vmsc =3D &fake_vmsc2; + list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris); + + mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props); + mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props); + fake_ris1.props.cpbm_wd =3D 4; + fake_ris2.props.cmax_wd =3D 4; + + mpam_enable_merge_features(&fake_classes_list); + + /* + * Multiple components can't control the same resource, mismatched featur= es can + * not be supported. + */ + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_clas= s.props)); + KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_clas= s.props)); + KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0); + KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0); + + mutex_unlock(&mpam_list_lock); + +#undef RESET_FAKE_HIEARCHY +} + static void test_mpam_reset_msc_bitmap(struct kunit *test) { char *buf =3D kunit_kzalloc(test, SZ_16K, GFP_KERNEL); @@ -57,6 +377,8 @@ static void test_mpam_reset_msc_bitmap(struct kunit *tes= t) =20 static struct kunit_case mpam_devices_test_cases[] =3D { KUNIT_CASE(test_mpam_reset_msc_bitmap), + KUNIT_CASE(test_mpam_enable_merge_features), + KUNIT_CASE(test__props_mismatch), {} }; =20 --=20 2.20.1