summaryrefslogtreecommitdiffstats
path: root/sound/drivers/pcsp/pcsp.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /sound/drivers/pcsp/pcsp.c
downloadlinux-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.c216
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);