[RFC PATCH v2 3/5] driver: hwpf: Add support for Intel to hardware prefetch driver

Kohei Tarumizu tarumizu.kohei at fujitsu.com
Wed Nov 3 22:21:20 PDT 2021


This adds module init/exit code, and creates sysfs attribute files for
"enable". It works on only INTEL_FAM6_BROADWELL_X.

Signed-off-by: Kohei Tarumizu <tarumizu.kohei at fujitsu.com>
---
 drivers/hwpf/intel_hwpf.c | 219 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 219 insertions(+)
 create mode 100644 drivers/hwpf/intel_hwpf.c

diff --git a/drivers/hwpf/intel_hwpf.c b/drivers/hwpf/intel_hwpf.c
new file mode 100644
index 000000000..391a58200
--- /dev/null
+++ b/drivers/hwpf/intel_hwpf.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0.
+/*
+ * Copyright 2021 FUJITSU LIMITED
+ *
+ * Intel Hardware Prefetch support
+ */
+
+#include <linux/bitfield.h>
+#include <linux/hwpf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/intel-family.h>
+#include <asm/msr.h>
+
+enum cache_level {
+	CACHE_LEVEL_L1 = 0,
+	CACHE_LEVEL_L2,
+	CACHE_LEVEL_NOCACHE,
+};
+
+/*
+ * Classify the register specification of MSR_MISC_FEATURE_CONTROL depending on
+ * the target model.
+ *
+ * Note: This enum type is added for future extension. Currently, this driver
+ * supports only one TYPE. However, the register has several different
+ * specifications. For example, XEON_PHI series (06_57H, 06_85H) has different
+ * specification for each bits as follow:
+ * [0]    DCU Hardware Prefetcher Disable (R/W)
+ * [1]    L2 Hardware Prefetcher Disable (R/W)
+ * [63:2] Reserved
+ */
+enum register_type {
+	/*
+	 * The register specification for each bits of REGISTER_TYPE_BROADWELL
+	 * is as follow:
+	 * [0]    L2 Hardware Prefetcher Disable (R/W)
+	 * [1]    L2 Adjacent Cache Line Prefetcher Disable (R/W)
+	 * [2]    DCU Hardware Prefetcher Disable (R/W)
+	 * [3]    DCU IP Prefetcher Disable (R/W)
+	 * [63:4] Reserved
+	 */
+	REGISTER_TYPE_BROADWELL,
+	REGISTER_TYPE_NONE, /* Hardware prefetch is not supported */
+};
+
+#define TYPE_BROADWELL_L1_FIELD		GENMASK_ULL(3, 2)
+#define TYPE_BROADWELL_L2_FIELD		GENMASK_ULL(1, 0)
+
+static enum register_type hwpf_rtype;
+
+static unsigned int cache_leaves;
+
+static enum cache_level get_cache_level(unsigned int level)
+{
+	if (level >= cache_leaves)
+		return CACHE_LEVEL_NOCACHE;
+
+	return level;
+}
+
+/*
+ * Return: 0 if enabled, 1 if disabled, negative errno if an error occurs.
+ * If any bit in TYPE_XXX_FIELD is set to 1, it is considered disabled.
+ */
+static int get_enable(unsigned int cpu, unsigned int level)
+{
+	u64 reg;
+	int ret, val;
+	enum cache_level clevel;
+
+	clevel = get_cache_level(level);
+	if (clevel == CACHE_LEVEL_NOCACHE)
+		return -EINVAL;
+
+	ret = rdmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, &reg);
+	if (ret)
+		return ret;
+
+	switch (hwpf_rtype) {
+	case REGISTER_TYPE_BROADWELL:
+		switch (clevel) {
+		case CACHE_LEVEL_L1:
+			val = FIELD_GET(TYPE_BROADWELL_L1_FIELD, reg);
+			break;
+		case CACHE_LEVEL_L2:
+			val = FIELD_GET(TYPE_BROADWELL_L2_FIELD, reg);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (val > 0)
+		return 1;
+	else if (val == 0)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static int set_enable(unsigned int cpu, unsigned int level, int val)
+{
+	int ret;
+	u64 reg;
+	enum cache_level clevel;
+
+	clevel = get_cache_level(level);
+	if (clevel == CACHE_LEVEL_NOCACHE)
+		return -EINVAL;
+
+	ret = rdmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, &reg);
+	if (ret)
+		return ret;
+
+	switch (hwpf_rtype) {
+	case REGISTER_TYPE_BROADWELL:
+		if (val == 1)
+			val = 0x3;
+		else if (val == 0)
+			val = 0x0;
+		else
+			return -EINVAL;
+
+		switch (clevel) {
+		case CACHE_LEVEL_L1:
+			reg &= ~TYPE_BROADWELL_L1_FIELD;
+			reg |= FIELD_PREP(TYPE_BROADWELL_L1_FIELD, val);
+			break;
+		case CACHE_LEVEL_L2:
+			reg &= ~TYPE_BROADWELL_L2_FIELD;
+			reg |= FIELD_PREP(TYPE_BROADWELL_L2_FIELD, val);
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = wrmsrl_on_cpu(cpu, MSR_MISC_FEATURE_CONTROL, reg);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+
+struct hwpf_driver intel_hwpf_driver = {
+	.get_enable		= get_enable,
+	.set_enable		= set_enable,
+};
+
+enum register_type __init get_register_type(void)
+{
+	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+		return REGISTER_TYPE_NONE;
+
+	switch (boot_cpu_data.x86_model) {
+	/*
+	 * Note: In addition to BROADWELL, NEHALEM and others have same register
+	 * specifications as REGISTER_TYPE_BROADWELL. If you want to add support
+	 * for these processor, add the target model case here.
+	 */
+	case INTEL_FAM6_BROADWELL_X:
+		return REGISTER_TYPE_BROADWELL;
+	default:
+		return REGISTER_TYPE_NONE;
+	}
+}
+
+static int __init setup_hwpf_driver_params(void)
+{
+	hwpf_rtype = get_register_type();
+
+	switch (hwpf_rtype) {
+	case REGISTER_TYPE_BROADWELL:
+		cache_leaves = 2;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	intel_hwpf_driver.cache_leaves = cache_leaves;
+
+	return 0;
+}
+
+static int __init hwpf_init(void)
+{
+	int ret;
+
+	ret = setup_hwpf_driver_params();
+	if (ret < 0)
+		return ret;
+
+	ret = hwpf_register_driver(&intel_hwpf_driver);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void __exit hwpf_exit(void)
+{
+	hwpf_unregister_driver(&intel_hwpf_driver);
+}
+
+module_init(hwpf_init);
+module_exit(hwpf_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU LIMITED");
+MODULE_DESCRIPTION("Intel Hardware Prefetch Driver");
-- 
2.27.0




More information about the linux-arm-kernel mailing list