diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /drivers/acpi/acpi_mrrm.c | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/acpi/acpi_mrrm.c')
| -rw-r--r-- | drivers/acpi/acpi_mrrm.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/drivers/acpi/acpi_mrrm.c b/drivers/acpi/acpi_mrrm.c new file mode 100644 index 000000000..e99cbda83 --- /dev/null +++ b/drivers/acpi/acpi_mrrm.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Intel Corporation. + * + * Memory Range and Region Mapping (MRRM) structure + * + * Parse and report the platform's MRRM table in /sys. + */ + +#define pr_fmt(fmt) "acpi/mrrm: " fmt + +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/sysfs.h> + +/* Default assume one memory region covering all system memory, per the spec */ +static int max_mem_region = 1; + +/* Access for use by resctrl file system */ +int acpi_mrrm_max_mem_region(void) +{ + return max_mem_region; +} + +struct mrrm_mem_range_entry { + u64 base; + u64 length; + int node; + u8 local_region_id; + u8 remote_region_id; +}; + +static struct mrrm_mem_range_entry *mrrm_mem_range_entry; +static u32 mrrm_mem_entry_num; + +static int get_node_num(struct mrrm_mem_range_entry *e) +{ + unsigned int nid; + + for_each_online_node(nid) { + for (int z = 0; z < MAX_NR_ZONES; z++) { + struct zone *zone = NODE_DATA(nid)->node_zones + z; + + if (!populated_zone(zone)) + continue; + if (zone_intersects(zone, PHYS_PFN(e->base), PHYS_PFN(e->length))) + return zone_to_nid(zone); + } + } + + return -ENOENT; +} + +static __init int acpi_parse_mrrm(struct acpi_table_header *table) +{ + struct acpi_mrrm_mem_range_entry *mre_entry; + struct acpi_table_mrrm *mrrm; + void *mre, *mrrm_end; + int mre_count = 0; + + mrrm = (struct acpi_table_mrrm *)table; + if (!mrrm) + return -ENODEV; + + if (mrrm->header.revision != 1) + return -EINVAL; + + if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS) + return -EOPNOTSUPP; + + mrrm_end = (void *)mrrm + mrrm->header.length - 1; + mre = (void *)mrrm + sizeof(struct acpi_table_mrrm); + while (mre < mrrm_end) { + mre_entry = mre; + mre_count++; + mre += mre_entry->header.length; + } + if (!mre_count) { + pr_info(FW_BUG "No ranges listed in MRRM table\n"); + return -EINVAL; + } + + mrrm_mem_range_entry = kmalloc_objs(*mrrm_mem_range_entry, mre_count, + GFP_KERNEL | __GFP_ZERO); + if (!mrrm_mem_range_entry) + return -ENOMEM; + + mre = (void *)mrrm + sizeof(struct acpi_table_mrrm); + while (mre < mrrm_end) { + struct mrrm_mem_range_entry *e; + + mre_entry = mre; + e = mrrm_mem_range_entry + mrrm_mem_entry_num; + + e->base = mre_entry->addr_base; + e->length = mre_entry->addr_len; + e->node = get_node_num(e); + + if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_LOCAL) + e->local_region_id = mre_entry->local_region_id; + else + e->local_region_id = -1; + if (mre_entry->region_id_flags & ACPI_MRRM_VALID_REGION_ID_FLAGS_REMOTE) + e->remote_region_id = mre_entry->remote_region_id; + else + e->remote_region_id = -1; + + mrrm_mem_entry_num++; + mre += mre_entry->header.length; + } + + max_mem_region = mrrm->max_mem_region; + + return 0; +} + +#define RANGE_ATTR(name, fmt) \ +static ssize_t name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + struct mrrm_mem_range_entry *mre; \ + const char *kname = kobject_name(kobj); \ + int n, ret; \ + \ + ret = kstrtoint(kname + 5, 10, &n); \ + if (ret) \ + return ret; \ + \ + mre = mrrm_mem_range_entry + n; \ + \ + return sysfs_emit(buf, fmt, mre->name); \ +} \ +static struct kobj_attribute name##_attr = __ATTR_RO(name) + +RANGE_ATTR(base, "0x%llx\n"); +RANGE_ATTR(length, "0x%llx\n"); +RANGE_ATTR(node, "%d\n"); +RANGE_ATTR(local_region_id, "%d\n"); +RANGE_ATTR(remote_region_id, "%d\n"); + +static struct attribute *memory_range_attrs[] = { + &base_attr.attr, + &length_attr.attr, + &node_attr.attr, + &local_region_id_attr.attr, + &remote_region_id_attr.attr, + NULL +}; + +ATTRIBUTE_GROUPS(memory_range); + +static __init int add_boot_memory_ranges(void) +{ + struct kobject *pkobj, *kobj, **kobjs; + int ret = -EINVAL; + char name[16]; + int i; + + pkobj = kobject_create_and_add("memory_ranges", acpi_kobj); + if (!pkobj) + return -ENOMEM; + + kobjs = kzalloc_objs(*kobjs, mrrm_mem_entry_num); + if (!kobjs) { + kobject_put(pkobj); + return -ENOMEM; + } + + for (i = 0; i < mrrm_mem_entry_num; i++) { + scnprintf(name, sizeof(name), "range%d", i); + kobj = kobject_create_and_add(name, pkobj); + if (!kobj) { + ret = -ENOMEM; + goto cleanup; + } + + ret = sysfs_create_groups(kobj, memory_range_groups); + if (ret) { + kobject_put(kobj); + goto cleanup; + } + kobjs[i] = kobj; + } + + kfree(kobjs); + return 0; + +cleanup: + for (int j = 0; j < i; j++) { + if (kobjs[j]) { + sysfs_remove_groups(kobjs[j], memory_range_groups); + kobject_put(kobjs[j]); + } + } + kfree(kobjs); + kobject_put(pkobj); + return ret; +} + +static __init int mrrm_init(void) +{ + int ret; + + ret = acpi_table_parse(ACPI_SIG_MRRM, acpi_parse_mrrm); + if (ret < 0) + return ret; + + return add_boot_memory_ranges(); +} +device_initcall(mrrm_init); |
