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 /sound/drivers/pcsp/pcsp.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 'sound/drivers/pcsp/pcsp.c')
| -rw-r--r-- | sound/drivers/pcsp/pcsp.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c new file mode 100644 index 000000000..ff6bb375c --- /dev/null +++ b/sound/drivers/pcsp/pcsp.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PC-Speaker driver for Linux + * + * Copyright (C) 1997-2001 David Woodhouse + * Copyright (C) 2001-2008 Stas Sergeev + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/mm.h> +#include "pcsp_input.h" +#include "pcsp.h" + +MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>"); +MODULE_DESCRIPTION("PC-Speaker driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcspkr"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ +static bool nopcm; /* Disable PCM capability of the driver */ + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for pcsp soundcard."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for pcsp soundcard."); +module_param(enable, bool, 0444); +MODULE_PARM_DESC(enable, "Enable PC-Speaker sound."); +module_param(nopcm, bool, 0444); +MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain."); + +struct snd_pcsp pcsp_chip; + +static int snd_pcsp_create(struct snd_card *card) +{ + unsigned int resolution = hrtimer_resolution; + int div, min_div, order; + + if (!nopcm) { + if (resolution > PCSP_MAX_PERIOD_NS) { + dev_err(card->dev, + "PCSP: Timer resolution is not sufficient (%unS)\n", + resolution); + dev_err(card->dev, + "PCSP: Make sure you have HPET and ACPI enabled.\n"); + dev_err(card->dev, "PCSP: Turned into nopcm mode.\n"); + nopcm = 1; + } + } + + if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS) + min_div = MIN_DIV; + else + min_div = MAX_DIV; +#if PCSP_DEBUG + dev_dbg(card->dev, "PCSP: lpj=%li, min_div=%i, res=%u\n", + loops_per_jiffy, min_div, resolution); +#endif + + div = MAX_DIV / min_div; + order = fls(div) - 1; + + pcsp_chip.max_treble = min(order, PCSP_MAX_TREBLE); + pcsp_chip.treble = min(pcsp_chip.max_treble, PCSP_DEFAULT_TREBLE); + pcsp_chip.playback_ptr = 0; + pcsp_chip.period_ptr = 0; + atomic_set(&pcsp_chip.timer_active, 0); + pcsp_chip.enable = 1; + pcsp_chip.pcspkr = 1; + + spin_lock_init(&pcsp_chip.substream_lock); + + pcsp_chip.card = card; + pcsp_chip.port = 0x61; + pcsp_chip.irq = -1; + pcsp_chip.dma = -1; + card->private_data = &pcsp_chip; + + return 0; +} + +static void pcsp_stop_beep(struct snd_pcsp *chip); + +static void alsa_card_pcsp_free(struct snd_card *card) +{ + pcsp_stop_beep(card->private_data); +} + +static int snd_card_pcsp_probe(int devnum, struct device *dev) +{ + struct snd_card *card; + int err; + + if (devnum != 0) + return -EINVAL; + + hrtimer_setup(&pcsp_chip.timer, pcsp_do_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + + err = snd_devm_card_new(dev, index, id, THIS_MODULE, 0, &card); + if (err < 0) + return err; + + err = snd_pcsp_create(card); + if (err < 0) + return err; + + if (!nopcm) { + err = snd_pcsp_new_pcm(&pcsp_chip); + if (err < 0) + return err; + } + err = snd_pcsp_new_mixer(&pcsp_chip, nopcm); + if (err < 0) + return err; + + strscpy(card->driver, "PC-Speaker"); + strscpy(card->shortname, "pcsp"); + sprintf(card->longname, "Internal PC-Speaker at port 0x%x", + pcsp_chip.port); + + err = snd_card_register(card); + if (err < 0) + return err; + card->private_free = alsa_card_pcsp_free; + + return 0; +} + +static int alsa_card_pcsp_init(struct device *dev) +{ + int err; + + err = snd_card_pcsp_probe(0, dev); + if (err) { + dev_err(dev, "PC-Speaker initialization failed.\n"); + return err; + } + + /* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */ + if (debug_pagealloc_enabled()) { + dev_warn(dev, + "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, which may make the sound noisy.\n"); + } + + return 0; +} + +static int pcsp_probe(struct platform_device *dev) +{ + int err; + + err = pcspkr_input_init(&pcsp_chip.input_dev, &dev->dev); + if (err < 0) + return err; + + err = alsa_card_pcsp_init(&dev->dev); + if (err < 0) + return err; + + platform_set_drvdata(dev, &pcsp_chip); + return 0; +} + +static void pcsp_stop_beep(struct snd_pcsp *chip) +{ + pcsp_sync_stop(chip); + pcspkr_stop_sound(); +} + +static int pcsp_suspend(struct device *dev) +{ + struct snd_pcsp *chip = dev_get_drvdata(dev); + pcsp_stop_beep(chip); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL); + +static void pcsp_shutdown(struct platform_device *dev) +{ + struct snd_pcsp *chip = platform_get_drvdata(dev); + pcsp_stop_beep(chip); +} + +static struct platform_driver pcsp_platform_driver = { + .driver = { + .name = "pcspkr", + .pm = &pcsp_pm, + }, + .probe = pcsp_probe, + .shutdown = pcsp_shutdown, +}; + +static int __init pcsp_init(void) +{ + if (!enable) + return -ENODEV; + return platform_driver_register(&pcsp_platform_driver); +} + +static void __exit pcsp_exit(void) +{ + platform_driver_unregister(&pcsp_platform_driver); +} + +module_init(pcsp_init); +module_exit(pcsp_exit); |
