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 /fs/ntfs/bdev-io.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 'fs/ntfs/bdev-io.c')
| -rw-r--r-- | fs/ntfs/bdev-io.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c new file mode 100644 index 000000000..86db4d929 --- /dev/null +++ b/fs/ntfs/bdev-io.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NTFS block device I/O. + * + * Copyright (c) 2026 LG Electronics Co., Ltd. + */ + +#include <linux/blkdev.h> + +#include "ntfs.h" + +/* + * ntfs_bdev_read - Read data directly from block device using bio + * @bdev: block device to read from + * @data: destination buffer + * @start: starting byte offset on the block device + * @size: number of bytes to read + * + * Reads @size bytes starting from byte offset @start directly from the block + * device using one or more BIOs. This function bypasses the page cache + * completely and performs synchronous I/O with REQ_META | REQ_SYNC flags set. + * + * The @start offset must be sector-aligned (512 bytes). If it is not aligned, + * the function will return -EINVAL. + * + * If the destination buffer @data is not a vmalloc address, it falls back + * to the more efficient bdev_rw_virt() helper. + * + * Return: 0 on success, negative error code on failure. + */ +int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t size) +{ + unsigned int done = 0, added; + int error; + struct bio *bio; + blk_opf_t op; + sector_t sector = start >> SECTOR_SHIFT; + + if (start & (SECTOR_SIZE - 1)) + return -EINVAL; + + op = REQ_OP_READ | REQ_META | REQ_SYNC; + if (!is_vmalloc_addr(data)) + return bdev_rw_virt(bdev, sector, data, size, op); + + bio = bio_alloc(bdev, + bio_max_segs(DIV_ROUND_UP(size, PAGE_SIZE)), + op, GFP_KERNEL); + bio->bi_iter.bi_sector = sector; + + do { + added = bio_add_vmalloc_chunk(bio, data + done, size - done); + if (!added) { + struct bio *prev = bio; + + bio = bio_alloc(prev->bi_bdev, + bio_max_segs(DIV_ROUND_UP(size - done, PAGE_SIZE)), + prev->bi_opf, GFP_KERNEL); + bio->bi_iter.bi_sector = bio_end_sector(prev); + bio_chain(prev, bio); + submit_bio(prev); + } + done += added; + } while (done < size); + + error = submit_bio_wait(bio); + bio_put(bio); + + if ((op & REQ_OP_MASK) == REQ_OP_READ) + invalidate_kernel_vmap_range(data, size); + return error; +} + +/* + * ntfs_bdev_write - Update block device contents via page cache + * @sb: super block of the mounted NTFS filesystem + * @buf: source buffer containing data to write + * @start: starting byte offset on the block device + * @size: number of bytes to write + * + * Writes @size bytes from @buf to the block device (sb->s_bdev) starting + * at byte offset @start. The write is performed entirely through the page + * cache of the block device's address space. + */ +int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size) +{ + pgoff_t idx, idx_end; + loff_t offset, end = start + size; + u32 from, to, buf_off = 0; + struct folio *folio; + + idx = start >> PAGE_SHIFT; + idx_end = end >> PAGE_SHIFT; + from = start & ~PAGE_MASK; + + if (idx == idx_end) + idx_end++; + + for (; idx < idx_end; idx++, from = 0) { + u32 len; + + folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL); + if (IS_ERR(folio)) { + ntfs_error(sb, "Unable to read %ld page", idx); + return PTR_ERR(folio); + } + + offset = (loff_t)idx << PAGE_SHIFT; + to = min_t(u32, end - offset, PAGE_SIZE); + len = to - from; + + memcpy_to_folio(folio, from, buf + buf_off, len); + buf_off += len; + folio_mark_uptodate(folio); + folio_mark_dirty(folio); + folio_put(folio); + } + + return 0; +} |
