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_pcc.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_pcc.c')
| -rw-r--r-- | drivers/acpi/acpi_pcc.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/acpi/acpi_pcc.c b/drivers/acpi/acpi_pcc.c new file mode 100644 index 000000000..438c67189 --- /dev/null +++ b/drivers/acpi/acpi_pcc.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Author: Sudeep Holla <sudeep.holla@arm.com> + * Copyright 2021 Arm Limited + * + * The PCC Address Space also referred as PCC Operation Region pertains to the + * region of PCC subspace that succeeds the PCC signature. The PCC Operation + * Region works in conjunction with the PCC Table(Platform Communications + * Channel Table). PCC subspaces that are marked for use as PCC Operation + * Regions must not be used as PCC subspaces for the standard ACPI features + * such as CPPC, RASF, PDTT and MPST. These standard features must always use + * the PCC Table instead. + * + * This driver sets up the PCC Address Space and installs an handler to enable + * handling of PCC OpRegion in the firmware. + * + */ +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/completion.h> +#include <linux/idr.h> +#include <linux/io.h> + +#include <acpi/pcc.h> + +/* + * Arbitrary retries in case the remote processor is slow to respond + * to PCC commands + */ +#define PCC_CMD_WAIT_RETRIES_NUM 500ULL + +struct pcc_data { + struct pcc_mbox_chan *pcc_chan; + struct completion done; + struct mbox_client cl; + struct acpi_pcc_info ctx; +}; + +static struct acpi_pcc_info pcc_ctx; + +static void pcc_rx_callback(struct mbox_client *cl, void *m) +{ + struct pcc_data *data = container_of(cl, struct pcc_data, cl); + + complete(&data->done); +} + +static acpi_status +acpi_pcc_address_space_setup(acpi_handle region_handle, u32 function, + void *handler_context, void **region_context) +{ + struct pcc_data *data; + struct acpi_pcc_info *ctx = handler_context; + struct pcc_mbox_chan *pcc_chan; + acpi_status ret; + + data = kzalloc_obj(*data); + if (!data) + return AE_NO_MEMORY; + + data->cl.rx_callback = pcc_rx_callback; + data->cl.knows_txdone = true; + data->ctx.length = ctx->length; + data->ctx.subspace_id = ctx->subspace_id; + data->ctx.internal_buffer = ctx->internal_buffer; + + init_completion(&data->done); + data->pcc_chan = pcc_mbox_request_channel(&data->cl, ctx->subspace_id); + if (IS_ERR(data->pcc_chan)) { + pr_err("Failed to find PCC channel for subspace %d\n", + ctx->subspace_id); + ret = AE_NOT_FOUND; + goto err_free_data; + } + + pcc_chan = data->pcc_chan; + if (!pcc_chan->mchan->mbox->txdone_irq) { + pr_err("This channel-%d does not support interrupt.\n", + ctx->subspace_id); + ret = AE_SUPPORT; + goto err_free_channel; + } + + *region_context = data; + return AE_OK; + +err_free_channel: + pcc_mbox_free_channel(data->pcc_chan); +err_free_data: + kfree(data); + + return ret; +} + +static acpi_status +acpi_pcc_address_space_handler(u32 function, acpi_physical_address addr, + u32 bits, acpi_integer *value, + void *handler_context, void *region_context) +{ + int ret; + struct pcc_data *data = region_context; + u64 usecs_lat; + + reinit_completion(&data->done); + + /* Write to Shared Memory */ + memcpy_toio(data->pcc_chan->shmem, (void *)value, data->ctx.length); + + ret = mbox_send_message(data->pcc_chan->mchan, NULL); + if (ret < 0) + return AE_ERROR; + + /* + * pcc_chan->latency is just a Nominal value. In reality the remote + * processor could be much slower to reply. So add an arbitrary + * amount of wait on top of Nominal. + */ + usecs_lat = PCC_CMD_WAIT_RETRIES_NUM * data->pcc_chan->latency; + ret = wait_for_completion_timeout(&data->done, + usecs_to_jiffies(usecs_lat)); + if (ret == 0) { + pr_err("PCC command executed timeout!\n"); + return AE_TIME; + } + + mbox_chan_txdone(data->pcc_chan->mchan, ret); + + memcpy_fromio(value, data->pcc_chan->shmem, data->ctx.length); + + return AE_OK; +} + +void __init acpi_init_pcc(void) +{ + acpi_status status; + + status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_PLATFORM_COMM, + &acpi_pcc_address_space_handler, + &acpi_pcc_address_space_setup, + &pcc_ctx); + if (ACPI_FAILURE(status)) + pr_alert("OperationRegion handler could not be installed\n"); +} |
