From: Mykyta Yatsenko Move the freader implementation from buildid.{c,h} into a dedicated compilation unit, freader.{c,h}. This allows reuse of freader outside buildid, e.g. for file dynptr support added later. Includes are updated and symbols are exported as needed. No functional change intended. Signed-off-by: Mykyta Yatsenko --- include/linux/freader.h | 32 +++++++++ lib/Makefile | 2 +- lib/buildid.c | 145 +--------------------------------------- lib/freader.c | 133 ++++++++++++++++++++++++++++++++++++ 4 files changed, 167 insertions(+), 145 deletions(-) create mode 100644 include/linux/freader.h create mode 100644 lib/freader.c diff --git a/include/linux/freader.h b/include/linux/freader.h new file mode 100644 index 000000000000..a08ea9f59945 --- /dev/null +++ b/include/linux/freader.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_FREADER_H +#define _LINUX_FREADER_H + +#include + +struct freader { + void *buf; + u32 buf_sz; + int err; + union { + struct { + struct file *file; + struct folio *folio; + void *addr; + loff_t folio_off; + bool may_fault; + }; + struct { + const char *data; + u64 data_sz; + }; + }; +}; + +void freader_init_from_file(struct freader *r, void *buf, u32 buf_sz, + struct file *file, bool may_fault); +void freader_init_from_mem(struct freader *r, const char *data, u64 data_sz); +const void *freader_fetch(struct freader *r, loff_t file_off, size_t sz); +void freader_cleanup(struct freader *r); +int freader_err(struct freader *r); +#endif diff --git a/lib/Makefile b/lib/Makefile index 392ff808c9b9..4761885228fa 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -40,7 +40,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ is_single_threaded.o plist.o decompress.o kobject_uevent.o \ earlycpio.o seq_buf.o siphash.o dec_and_lock.o \ nmi_backtrace.o win_minmax.o memcat_p.o \ - buildid.o objpool.o iomem_copy.o sys_info.o + buildid.o objpool.o iomem_copy.o sys_info.o freader.o lib-$(CONFIG_UNION_FIND) += union_find.o lib-$(CONFIG_PRINTK) += dump_stack.o diff --git a/lib/buildid.c b/lib/buildid.c index c4b0f376fb34..e770dc4b73d3 100644 --- a/lib/buildid.c +++ b/lib/buildid.c @@ -6,155 +6,12 @@ #include #include #include +#include #define BUILD_ID 3 #define MAX_PHDR_CNT 256 -struct freader { - void *buf; - u32 buf_sz; - int err; - union { - struct { - struct file *file; - struct folio *folio; - void *addr; - loff_t folio_off; - bool may_fault; - }; - struct { - const char *data; - u64 data_sz; - }; - }; -}; - -static void freader_init_from_file(struct freader *r, void *buf, u32 buf_sz, - struct file *file, bool may_fault) -{ - memset(r, 0, sizeof(*r)); - r->buf = buf; - r->buf_sz = buf_sz; - r->file = file; - r->may_fault = may_fault; -} - -static void freader_init_from_mem(struct freader *r, const char *data, u64 data_sz) -{ - memset(r, 0, sizeof(*r)); - r->data = data; - r->data_sz = data_sz; -} - -static void freader_put_folio(struct freader *r) -{ - if (!r->folio) - return; - kunmap_local(r->addr); - folio_put(r->folio); - r->folio = NULL; -} - -static int freader_get_folio(struct freader *r, loff_t file_off) -{ - /* check if we can just reuse current folio */ - if (r->folio && file_off >= r->folio_off && - file_off < r->folio_off + folio_size(r->folio)) - return 0; - - freader_put_folio(r); - - /* reject secretmem folios created with memfd_secret() */ - if (secretmem_mapping(r->file->f_mapping)) - return -EFAULT; - - r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT); - - /* if sleeping is allowed, wait for the page, if necessary */ - if (r->may_fault && (IS_ERR(r->folio) || !folio_test_uptodate(r->folio))) { - filemap_invalidate_lock_shared(r->file->f_mapping); - r->folio = read_cache_folio(r->file->f_mapping, file_off >> PAGE_SHIFT, - NULL, r->file); - filemap_invalidate_unlock_shared(r->file->f_mapping); - } - - if (IS_ERR(r->folio) || !folio_test_uptodate(r->folio)) { - if (!IS_ERR(r->folio)) - folio_put(r->folio); - r->folio = NULL; - return -EFAULT; - } - - r->folio_off = folio_pos(r->folio); - r->addr = kmap_local_folio(r->folio, 0); - - return 0; -} - -static const void *freader_fetch(struct freader *r, loff_t file_off, size_t sz) -{ - size_t folio_sz; - - /* provided internal temporary buffer should be sized correctly */ - if (WARN_ON(r->buf && sz > r->buf_sz)) { - r->err = -E2BIG; - return NULL; - } - - if (unlikely(file_off + sz < file_off)) { - r->err = -EOVERFLOW; - return NULL; - } - - /* working with memory buffer is much more straightforward */ - if (!r->buf) { - if (file_off + sz > r->data_sz) { - r->err = -ERANGE; - return NULL; - } - return r->data + file_off; - } - - /* fetch or reuse folio for given file offset */ - r->err = freader_get_folio(r, file_off); - if (r->err) - return NULL; - - /* if requested data is crossing folio boundaries, we have to copy - * everything into our local buffer to keep a simple linear memory - * access interface - */ - folio_sz = folio_size(r->folio); - if (file_off + sz > r->folio_off + folio_sz) { - int part_sz = r->folio_off + folio_sz - file_off; - - /* copy the part that resides in the current folio */ - memcpy(r->buf, r->addr + (file_off - r->folio_off), part_sz); - - /* fetch next folio */ - r->err = freader_get_folio(r, r->folio_off + folio_sz); - if (r->err) - return NULL; - - /* copy the rest of requested data */ - memcpy(r->buf + part_sz, r->addr, sz - part_sz); - - return r->buf; - } - - /* if data fits in a single folio, just return direct pointer */ - return r->addr + (file_off - r->folio_off); -} - -static void freader_cleanup(struct freader *r) -{ - if (!r->buf) - return; /* non-file-backed mode */ - - freader_put_folio(r); -} - /* * Parse build id from the note segment. This logic can be shared between * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are diff --git a/lib/freader.c b/lib/freader.c new file mode 100644 index 000000000000..32a17d137b32 --- /dev/null +++ b/lib/freader.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +void freader_init_from_file(struct freader *r, void *buf, u32 buf_sz, + struct file *file, bool may_fault) +{ + memset(r, 0, sizeof(*r)); + r->buf = buf; + r->buf_sz = buf_sz; + r->file = file; + r->may_fault = may_fault; +} + +void freader_init_from_mem(struct freader *r, const char *data, u64 data_sz) +{ + memset(r, 0, sizeof(*r)); + r->data = data; + r->data_sz = data_sz; +} + +static void freader_put_folio(struct freader *r) +{ + if (!r->folio) + return; + kunmap_local(r->addr); + folio_put(r->folio); + r->folio = NULL; +} + +static int freader_get_folio(struct freader *r, loff_t file_off) +{ + /* check if we can just reuse current folio */ + if (r->folio && file_off >= r->folio_off && + file_off < r->folio_off + folio_size(r->folio)) + return 0; + + freader_put_folio(r); + + /* reject secretmem folios created with memfd_secret() */ + if (secretmem_mapping(r->file->f_mapping)) + return -EFAULT; + + r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT); + + /* if sleeping is allowed, wait for the page, if necessary */ + if (r->may_fault && (IS_ERR(r->folio) || !folio_test_uptodate(r->folio))) { + filemap_invalidate_lock_shared(r->file->f_mapping); + r->folio = read_cache_folio(r->file->f_mapping, file_off >> PAGE_SHIFT, + NULL, r->file); + filemap_invalidate_unlock_shared(r->file->f_mapping); + } + + if (IS_ERR(r->folio) || !folio_test_uptodate(r->folio)) { + if (!IS_ERR(r->folio)) + folio_put(r->folio); + r->folio = NULL; + return -EFAULT; + } + + r->folio_off = folio_pos(r->folio); + r->addr = kmap_local_folio(r->folio, 0); + + return 0; +} + +const void *freader_fetch(struct freader *r, loff_t file_off, size_t sz) +{ + size_t folio_sz; + + /* provided internal temporary buffer should be sized correctly */ + if (WARN_ON(r->buf && sz > r->buf_sz)) { + r->err = -E2BIG; + return NULL; + } + + if (unlikely(file_off + sz < file_off)) { + r->err = -EOVERFLOW; + return NULL; + } + + /* working with memory buffer is much more straightforward */ + if (!r->buf) { + if (file_off + sz > r->data_sz) { + r->err = -ERANGE; + return NULL; + } + return r->data + file_off; + } + + /* fetch or reuse folio for given file offset */ + r->err = freader_get_folio(r, file_off); + if (r->err) + return NULL; + + /* if requested data is crossing folio boundaries, we have to copy + * everything into our local buffer to keep a simple linear memory + * access interface + */ + folio_sz = folio_size(r->folio); + if (file_off + sz > r->folio_off + folio_sz) { + int part_sz = r->folio_off + folio_sz - file_off; + + /* copy the part that resides in the current folio */ + memcpy(r->buf, r->addr + (file_off - r->folio_off), part_sz); + + /* fetch next folio */ + r->err = freader_get_folio(r, r->folio_off + folio_sz); + if (r->err) + return NULL; + + /* copy the rest of requested data */ + memcpy(r->buf + part_sz, r->addr, sz - part_sz); + + return r->buf; + } + + /* if data fits in a single folio, just return direct pointer */ + return r->addr + (file_off - r->folio_off); +} + +void freader_cleanup(struct freader *r) +{ + if (!r->buf) + return; /* non-file-backed mode */ + + freader_put_folio(r); +} -- 2.51.0