From 4617721c502b2ddaa4e324e86da4997edf738fa5 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 2 Sep 2026 09:55:01 -0400 Subject: ftrace: Synchronize the initialization of ftrace_ops 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 --- drivers/media/mmc/Kconfig | 3 + drivers/media/mmc/Makefile | 2 + drivers/media/mmc/siano/Kconfig | 16 ++ drivers/media/mmc/siano/Makefile | 4 + drivers/media/mmc/siano/smssdio.c | 351 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 376 insertions(+) create mode 100644 drivers/media/mmc/Kconfig create mode 100644 drivers/media/mmc/Makefile create mode 100644 drivers/media/mmc/siano/Kconfig create mode 100644 drivers/media/mmc/siano/Makefile create mode 100644 drivers/media/mmc/siano/smssdio.c (limited to 'drivers/media/mmc') diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig new file mode 100644 index 000000000..2f9877bc6 --- /dev/null +++ b/drivers/media/mmc/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +source "drivers/media/mmc/siano/Kconfig" diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile new file mode 100644 index 000000000..e847aa82a --- /dev/null +++ b/drivers/media/mmc/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-y += siano/ diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig new file mode 100644 index 000000000..570696019 --- /dev/null +++ b/drivers/media/mmc/siano/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Siano Mobile Silicon Digital TV device configuration +# +comment "MMC/SDIO DVB adapters" + depends on DVB_CORE && HAS_DMA && MMC + +config SMS_SDIO_DRV + tristate "Siano SMS1xxx based MDTV via SDIO interface" + depends on DVB_CORE && HAS_DMA + depends on MMC + depends on !RC_CORE || RC_CORE + select MEDIA_COMMON_OPTIONS + select SMS_SIANO_MDTV + help + Choose if you would like to have Siano's support for SDIO interface diff --git a/drivers/media/mmc/siano/Makefile b/drivers/media/mmc/siano/Makefile new file mode 100644 index 000000000..88cb8bec4 --- /dev/null +++ b/drivers/media/mmc/siano/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o + +ccflags-y += -I $(srctree)/drivers/media/common/siano diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c new file mode 100644 index 000000000..41dcab816 --- /dev/null +++ b/drivers/media/mmc/siano/smssdio.c @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * smssdio.c - Siano 1xxx SDIO interface driver + * + * Copyright 2008 Pierre Ossman + * + * Based on code by Siano Mobile Silicon, Inc., + * Copyright (C) 2006-2008, Uri Shkolnik + * + * This hardware is a bit odd in that all transfers should be done + * to/from the SMSSDIO_DATA register, yet the "increase address" bit + * always needs to be set. + * + * Also, buffers from the card are always aligned to 128 byte + * boundaries. + */ + +/* + * General cleanup notes: + * + * - only typedefs should be name *_t + * + * - use ERR_PTR and friends for smscore_register_device() + * + * - smscore_getbuffer should zero fields + * + * Fix stop command + */ + +#include "smscoreapi.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sms-cards.h" +#include "smsendian.h" + +/* Registers */ + +#define SMSSDIO_DATA 0x00 +#define SMSSDIO_INT 0x04 +#define SMSSDIO_BLOCK_SIZE 128 + +static const struct sdio_device_id smssdio_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), + .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), + .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), + .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), + .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), + .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_MING), + .driver_data = SMS1XXX_BOARD_SIANO_MING}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_PELE), + .driver_data = SMS1XXX_BOARD_SIANO_PELE}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_RIO), + .driver_data = SMS1XXX_BOARD_SIANO_RIO}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_2160), + .driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160}, + {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_1530), + .driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530}, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, smssdio_ids); + +struct smssdio_device { + struct sdio_func *func; + + struct smscore_device_t *coredev; + + struct smscore_buffer_t *split_cb; +}; + +/*******************************************************************/ +/* Siano core callbacks */ +/*******************************************************************/ + +static int smssdio_sendrequest(void *context, void *buffer, size_t size) +{ + int ret = 0; + struct smssdio_device *smsdev; + + smsdev = context; + + sdio_claim_host(smsdev->func); + + smsendian_handle_tx_message((struct sms_msg_data *) buffer); + while (size >= smsdev->func->cur_blksize) { + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, smsdev->func->cur_blksize); + if (ret) + goto out; + + buffer += smsdev->func->cur_blksize; + size -= smsdev->func->cur_blksize; + } + + if (size) { + ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, + buffer, size); + } + +out: + sdio_release_host(smsdev->func); + + return ret; +} + +/*******************************************************************/ +/* SDIO callbacks */ +/*******************************************************************/ + +static void smssdio_interrupt(struct sdio_func *func) +{ + int ret; + + struct smssdio_device *smsdev; + struct smscore_buffer_t *cb; + struct sms_msg_hdr *hdr; + size_t size; + + smsdev = sdio_get_drvdata(func); + + /* + * The interrupt register has no defined meaning. It is just + * a way of turning of the level triggered interrupt. + */ + (void)sdio_readb(func, SMSSDIO_INT, &ret); + if (ret) { + pr_err("Unable to read interrupt register!\n"); + return; + } + + if (smsdev->split_cb == NULL) { + cb = smscore_getbuffer(smsdev->coredev); + if (!cb) { + pr_err("Unable to allocate data buffer!\n"); + return; + } + + ret = sdio_memcpy_fromio(smsdev->func, + cb->p, + SMSSDIO_DATA, + SMSSDIO_BLOCK_SIZE); + if (ret) { + pr_err("Error %d reading initial block!\n", ret); + return; + } + + hdr = cb->p; + + if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) { + smsdev->split_cb = cb; + return; + } + + if (hdr->msg_length > smsdev->func->cur_blksize) + size = hdr->msg_length - smsdev->func->cur_blksize; + else + size = 0; + } else { + cb = smsdev->split_cb; + hdr = cb->p; + + size = hdr->msg_length - sizeof(struct sms_msg_hdr); + + smsdev->split_cb = NULL; + } + + if (size) { + void *buffer; + + buffer = cb->p + (hdr->msg_length - size); + size = ALIGN(size, SMSSDIO_BLOCK_SIZE); + + BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); + + /* + * First attempt to transfer all of it in one go... + */ + ret = sdio_memcpy_fromio(smsdev->func, + buffer, + SMSSDIO_DATA, + size); + if (ret && ret != -EINVAL) { + smscore_putbuffer(smsdev->coredev, cb); + pr_err("Error %d reading data from card!\n", ret); + return; + } + + /* + * ..then fall back to one block at a time if that is + * not possible... + * + * (we have to do this manually because of the + * problem with the "increase address" bit) + */ + if (ret == -EINVAL) { + while (size) { + ret = sdio_memcpy_fromio(smsdev->func, + buffer, SMSSDIO_DATA, + smsdev->func->cur_blksize); + if (ret) { + smscore_putbuffer(smsdev->coredev, cb); + pr_err("Error %d reading data from card!\n", + ret); + return; + } + + buffer += smsdev->func->cur_blksize; + if (size > smsdev->func->cur_blksize) + size -= smsdev->func->cur_blksize; + else + size = 0; + } + } + } + + cb->size = hdr->msg_length; + cb->offset = 0; + + smsendian_handle_rx_message((struct sms_msg_data *) cb->p); + smscore_onresponse(smsdev->coredev, cb); +} + +static int smssdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int ret; + + int board_id; + struct smssdio_device *smsdev; + struct smsdevice_params_t params; + + board_id = id->driver_data; + + smsdev = kzalloc_obj(struct smssdio_device); + if (!smsdev) + return -ENOMEM; + + smsdev->func = func; + + memset(¶ms, 0, sizeof(struct smsdevice_params_t)); + + params.device = &func->dev; + params.buffer_size = 0x5000; /* ?? */ + params.num_buffers = 22; /* ?? */ + params.context = smsdev; + + snprintf(params.devpath, sizeof(params.devpath), + "sdio\\%s", sdio_func_id(func)); + + params.sendrequest_handler = smssdio_sendrequest; + + params.device_type = sms_get_board(board_id)->type; + + if (params.device_type != SMS_STELLAR) + params.flags |= SMS_DEVICE_FAMILY2; + else { + /* + * FIXME: Stellar needs special handling... + */ + ret = -ENODEV; + goto free; + } + + ret = smscore_register_device(¶ms, &smsdev->coredev, GFP_DMA, NULL); + if (ret < 0) + goto free; + + smscore_set_board_id(smsdev->coredev, board_id); + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) + goto release; + + ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); + if (ret) + goto disable; + + ret = sdio_claim_irq(func, smssdio_interrupt); + if (ret) + goto disable; + + sdio_set_drvdata(func, smsdev); + + sdio_release_host(func); + + ret = smscore_start_device(smsdev->coredev); + if (ret < 0) + goto reclaim; + + return 0; + +reclaim: + sdio_claim_host(func); + sdio_release_irq(func); +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); + smscore_unregister_device(smsdev->coredev); +free: + kfree(smsdev); + + return ret; +} + +static void smssdio_remove(struct sdio_func *func) +{ + struct smssdio_device *smsdev; + + smsdev = sdio_get_drvdata(func); + + /* FIXME: racy! */ + if (smsdev->split_cb) + smscore_putbuffer(smsdev->coredev, smsdev->split_cb); + + smscore_unregister_device(smsdev->coredev); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + + kfree(smsdev); +} + +static struct sdio_driver smssdio_driver = { + .name = "smssdio", + .id_table = smssdio_ids, + .probe = smssdio_probe, + .remove = smssdio_remove, +}; +module_sdio_driver(smssdio_driver); + +MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); +MODULE_AUTHOR("Pierre Ossman"); +MODULE_LICENSE("GPL"); -- cgit v1.3.1