diff options
| author | Paulo Alcantara <pc@manguebit.org> | 2026-09-26 19:20:30 -0300 |
|---|---|---|
| committer | Paulo Alcantara <pc@manguebit.org> | 2026-09-30 14:24:12 -0300 |
| commit | dd05add7b2b6004c5fa3a1f276f56d8668f86b65 (patch) | |
| tree | 23efdcaa0b8031f6ac6f57b6734d18d6c1adc6f2 /drivers/mtd/chips/chipreg.c | |
| download | linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.tar.gz linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.zip | |
netfs: zero the tail of a short DIO/unbuffered readgrafted
The buffered read collector zero-fills the tail of a short read that
stops below the inode's i_size (netfs_clear_unread()), so a read that
races an extending write still returns the expected number of bytes.
The non-buffered collector path does no such thing: it just records
how much was transferred.
Add the same zero-fill for the non-buffered case, gated on
NETFS_SREQ_CLEAR_TAIL: a subreq's source sets that flag when a short
result from it is known to be safe to treat as a hole, as opposed to
NETFS_SREQ_HIT_EOF, which means the read genuinely ran off the end of
the file and should be reported short as-is. Only CLEAR_TAIL should
zero-fill here; a real EOF must stay a real short read.
No source currently sets CLEAR_TAIL on an unbuffered/DIO subrequest,
so this is inert on its own -- a following change teaches cifs to set
it in the one case that needs it.
Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item")
Reviewed-by: David Howells <dhowells@redhat.com>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/mtd/chips/chipreg.c')
| -rw-r--r-- | drivers/mtd/chips/chipreg.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c new file mode 100644 index 000000000..a05e10368 --- /dev/null +++ b/drivers/mtd/chips/chipreg.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Registration for chip drivers + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kmod.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> + +static DEFINE_SPINLOCK(chip_drvs_lock); +static LIST_HEAD(chip_drvs_list); + +void register_mtd_chip_driver(struct mtd_chip_driver *drv) +{ + spin_lock(&chip_drvs_lock); + list_add(&drv->list, &chip_drvs_list); + spin_unlock(&chip_drvs_lock); +} + +void unregister_mtd_chip_driver(struct mtd_chip_driver *drv) +{ + spin_lock(&chip_drvs_lock); + list_del(&drv->list); + spin_unlock(&chip_drvs_lock); +} + +static struct mtd_chip_driver *get_mtd_chip_driver (const char *name) +{ + struct mtd_chip_driver *ret = NULL, *this; + + spin_lock(&chip_drvs_lock); + + list_for_each_entry(this, &chip_drvs_list, list) { + if (!strcmp(this->name, name)) { + ret = this; + break; + } + } + if (ret && !try_module_get(ret->module)) + ret = NULL; + + spin_unlock(&chip_drvs_lock); + + return ret; +} + + /* Hide all the horrid details, like some silly person taking + get_module_symbol() away from us, from the caller. */ + +struct mtd_info *do_map_probe(const char *name, struct map_info *map) +{ + struct mtd_chip_driver *drv; + struct mtd_info *ret; + + drv = get_mtd_chip_driver(name); + + if (!drv && !request_module("%s", name)) + drv = get_mtd_chip_driver(name); + + if (!drv) + return NULL; + + ret = drv->probe(map); + + /* We decrease the use count here. It may have been a + probe-only module, which is no longer required from this + point, having given us a handle on (and increased the use + count of) the actual driver code. + */ + module_put(drv->module); + + return ret; +} +/* + * Destroy an MTD device which was created for a map device. + * Make sure the MTD device is already unregistered before calling this + */ +void map_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + + if (map->fldrv->destroy) + map->fldrv->destroy(mtd); + + module_put(map->fldrv->module); + + kfree(mtd); +} + +EXPORT_SYMBOL(register_mtd_chip_driver); +EXPORT_SYMBOL(unregister_mtd_chip_driver); +EXPORT_SYMBOL(do_map_probe); +EXPORT_SYMBOL(map_destroy); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_DESCRIPTION("Core routines for registering and invoking MTD chip drivers"); |
