This still seems overly complex. I'll post a series that just uses request_symbol instead in a bit, everyone let me know what you think. On Wed, Aug 18, 2021 at 03:04:11AM +0530, Shreeya Patel wrote: > utf8data.h_shipped has a large database table which is an auto-generated > decodification trie for the unicode normalization functions. > We can avoid carrying this large table in the kernel unless it is required > by the filesystem during boot process. > > Hence, add utf8-data module which will be loaded only when UTF-8 encoding > support is needed by the filesystem, provided it is selected as M. > utf8-data will provide access to the data tables present in utf8data.h. > > Also, add support for enabling utf8-data as a built-in option so that > filesystems that require UTF-8 encoding during boot process can access > the data tables without any failure. > > Signed-off-by: Shreeya Patel <shreeya.patel@xxxxxxxxxxxxx> > --- > Changes in v2 > - Since there are no function pointer fields anymore, use utf8_data > as the name instead of utf8_ops > - Remove unnecessary variable utf8data_loaded > > fs/unicode/Kconfig | 23 +++++++++++-- > fs/unicode/Makefile | 3 +- > fs/unicode/utf8-core.c | 50 ++++++++++++++++++++++++++-- > fs/unicode/utf8-data.c | 42 +++++++++++++++++++++++ > fs/unicode/utf8-norm.c | 68 ++++++++++++++++++++++---------------- > fs/unicode/utf8-selftest.c | 25 ++++++-------- > fs/unicode/utf8n.h | 30 +++++++++++++++++ > 7 files changed, 193 insertions(+), 48 deletions(-) > create mode 100644 fs/unicode/utf8-data.c > > diff --git a/fs/unicode/Kconfig b/fs/unicode/Kconfig > index 2c27b9a5cd6c..80341fae5e63 100644 > --- a/fs/unicode/Kconfig > +++ b/fs/unicode/Kconfig > @@ -2,13 +2,30 @@ > # > # UTF-8 normalization > # > +# This config option will be automatically selected when UNICODE_UTF8_DATA > +# is enabled. UNICODE config will provide all the UTF-8 core and normalization > +# functions which will use UTF-8 data tables. > config UNICODE > bool "UTF-8 normalization and casefolding support" > + > +config UNICODE_UTF8_DATA > + tristate "UTF-8 support for native Case-Insensitive filesystems" > + select UNICODE > help > - Say Y here to enable UTF-8 NFD normalization and NFD+CF casefolding > - support. > + Say M here to enable UTF-8 NFD normalization and NFD+CF casefolding > + support as a loadable module or say Y for building it into the kernel. > + It is currently supported by EXT4 and F2FS filesystems. > + > + utf8data.h_shipped has a large database table which is an > + auto-generated decodification trie for the unicode normalization > + functions. Enabling UNICODE_UTF8_DATA as M will allow you to avoid > + carrying this large table into the kernel and module will only be > + loaded with the data tables whenever required by any filesystem. > + If your filesystem requires to have the utf8-data during boot time > + then you should have it built into the kernel by saying Y here to > + avoid any boot failure. > > config UNICODE_NORMALIZATION_SELFTEST > tristate "Test UTF-8 normalization support" > - depends on UNICODE > + depends on UNICODE_UTF8_DATA > default n > diff --git a/fs/unicode/Makefile b/fs/unicode/Makefile > index b88aecc86550..fc28a6e2c56f 100644 > --- a/fs/unicode/Makefile > +++ b/fs/unicode/Makefile > @@ -2,10 +2,11 @@ > > obj-$(CONFIG_UNICODE) += unicode.o > obj-$(CONFIG_UNICODE_NORMALIZATION_SELFTEST) += utf8-selftest.o > +obj-$(CONFIG_UNICODE_UTF8_DATA) += utf8-data.o > > unicode-y := utf8-norm.o utf8-core.o > > -$(obj)/utf8-norm.o: $(obj)/utf8data.h > +$(obj)/utf8-data.o: $(obj)/utf8data.h > > # In the normal build, the checked-in utf8data.h is just shipped. > # > diff --git a/fs/unicode/utf8-core.c b/fs/unicode/utf8-core.c > index dc25823bfed9..4eb08385e680 100644 > --- a/fs/unicode/utf8-core.c > +++ b/fs/unicode/utf8-core.c > @@ -192,7 +192,7 @@ static int utf8_parse_version(const char *version, unsigned int *maj, > return 0; > } > > -struct unicode_map *utf8_load(const char *version) > +static struct unicode_map *utf8_load_core(const char *version) > { > struct unicode_map *um = NULL; > int unicode_version; > @@ -225,11 +225,57 @@ struct unicode_map *utf8_load(const char *version) > > return um; > } > + > +static void utf8_unload_core(struct unicode_map *um) > +{ > + kfree(um); > +} > + > +static int utf8mod_get(void) > +{ > + int ret; > + > + spin_lock(&utf8_lock); > + ret = utf8_data && try_module_get(utf8_data->owner); > + spin_unlock(&utf8_lock); > + return ret; > +} > + > +struct unicode_map *utf8_load(const char *version) > +{ > + struct unicode_map *um; > + > + /* > + * try_then_request_module() is used here instead of using > + * request_module() because of the following problems that > + * could occur with the usage of request_module(). > + * 1) Multiple calls in parallel to utf8_load() would fail if > + * kmod_concurrent_max == 0 > + * 2) There would be unnecessary memory allocation and userspace > + * invocation in call_modprobe() that would always happen even if > + * the module is already loaded. > + * Hence, using try_then_request_module() would first check if the > + * module is already loaded, if not then it calls the request_module() > + * and finally would aquire the reference of the loaded module. > + */ > + if (!try_then_request_module(utf8mod_get(), "utf8-data")) { > + pr_err("Failed to load UTF-8 module\n"); > + return ERR_PTR(-ENODEV); > + } > + um = utf8_load_core(version); > + if (IS_ERR(um)) > + module_put(utf8_data->owner); > + > + return um; > +} > EXPORT_SYMBOL(utf8_load); > > void utf8_unload(struct unicode_map *um) > { > - kfree(um); > + if (um) { > + utf8_unload_core(um); > + module_put(utf8_data->owner); > + } > } > EXPORT_SYMBOL(utf8_unload); > > diff --git a/fs/unicode/utf8-data.c b/fs/unicode/utf8-data.c > new file mode 100644 > index 000000000000..1ae3c5dda6c7 > --- /dev/null > +++ b/fs/unicode/utf8-data.c > @@ -0,0 +1,42 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include "utf8n.h" > + > +#define __INCLUDED_FROM_UTF8NORM_C__ > +#include "utf8data.h" > +#undef __INCLUDED_FROM_UTF8NORM_C__ > + > +struct utf8data_table data = { > + .owner = THIS_MODULE, > + > + .utf8vers = utf8vers, > + > + .utf8agetab = utf8agetab, > + .utf8agetab_size = ARRAY_SIZE(utf8agetab), > + > + .utf8nfdicfdata = utf8nfdicfdata, > + .utf8nfdicfdata_size = ARRAY_SIZE(utf8nfdicfdata), > + > + .utf8nfdidata = utf8nfdidata, > + .utf8nfdidata_size = ARRAY_SIZE(utf8nfdidata), > + > + .utf8data = utf8data, > + .utf8data_size = ARRAY_SIZE(utf8data), > +}; > + > +static int __init utf8_init(void) > +{ > + unicode_register(&data); > + return 0; > +} > + > +static void __exit utf8_exit(void) > +{ > + unicode_unregister(); > +} > + > +module_init(utf8_init); > +module_exit(utf8_exit); > + > +MODULE_LICENSE("GPL v2"); > diff --git a/fs/unicode/utf8-norm.c b/fs/unicode/utf8-norm.c > index 1d2d2e5b906a..a6276f50a18f 100644 > --- a/fs/unicode/utf8-norm.c > +++ b/fs/unicode/utf8-norm.c > @@ -6,22 +6,18 @@ > > #include "utf8n.h" > > -struct utf8data { > - unsigned int maxage; > - unsigned int offset; > -}; > +/* Spinlock for protecting utf8_data */ > +DEFINE_SPINLOCK(utf8_lock); > > -#define __INCLUDED_FROM_UTF8NORM_C__ > -#include "utf8data.h" > -#undef __INCLUDED_FROM_UTF8NORM_C__ > +struct utf8data_table *utf8_data; > > int utf8version_is_supported(u8 maj, u8 min, u8 rev) > { > - int i = ARRAY_SIZE(utf8agetab) - 1; > + int i = utf8_data->utf8agetab_size - 1; > unsigned int sb_utf8version = UNICODE_AGE(maj, min, rev); > > - while (i >= 0 && utf8agetab[i] != 0) { > - if (sb_utf8version == utf8agetab[i]) > + while (i >= 0 && utf8_data->utf8agetab[i] != 0) { > + if (sb_utf8version == utf8_data->utf8agetab[i]) > return 1; > i--; > } > @@ -31,7 +27,7 @@ EXPORT_SYMBOL(utf8version_is_supported); > > int utf8version_latest(void) > { > - return utf8vers; > + return utf8_data->utf8vers; > } > EXPORT_SYMBOL(utf8version_latest); > > @@ -168,7 +164,7 @@ typedef const unsigned char utf8trie_t; > * underlying datatype: unsigned char. > * > * leaf[0]: The unicode version, stored as a generation number that is > - * an index into utf8agetab[]. With this we can filter code > + * an index into utf8_data->utf8agetab[]. With this we can filter code > * points based on the unicode version in which they were > * defined. The CCC of a non-defined code point is 0. > * leaf[1]: Canonical Combining Class. During normalization, we need > @@ -330,7 +326,7 @@ static utf8leaf_t *utf8nlookup(const struct utf8data *data, > if (len == 0) > return NULL; > > - trie = utf8data + data->offset; > + trie = utf8_data->utf8data + data->offset; > node = 1; > while (node) { > offlen = (*trie & OFFLEN) >> OFFLEN_SHIFT; > @@ -418,7 +414,7 @@ int utf8agemax(const struct utf8data *data, const char *s) > if (!leaf) > return -1; > > - leaf_age = utf8agetab[LEAF_GEN(leaf)]; > + leaf_age = utf8_data->utf8agetab[LEAF_GEN(leaf)]; > if (leaf_age <= data->maxage && leaf_age > age) > age = leaf_age; > s += utf8clen(s); > @@ -446,7 +442,7 @@ int utf8agemin(const struct utf8data *data, const char *s) > leaf = utf8lookup(data, hangul, s); > if (!leaf) > return -1; > - leaf_age = utf8agetab[LEAF_GEN(leaf)]; > + leaf_age = utf8_data->utf8agetab[LEAF_GEN(leaf)]; > if (leaf_age <= data->maxage && leaf_age < age) > age = leaf_age; > s += utf8clen(s); > @@ -473,7 +469,7 @@ int utf8nagemax(const struct utf8data *data, const char *s, size_t len) > leaf = utf8nlookup(data, hangul, s, len); > if (!leaf) > return -1; > - leaf_age = utf8agetab[LEAF_GEN(leaf)]; > + leaf_age = utf8_data->utf8agetab[LEAF_GEN(leaf)]; > if (leaf_age <= data->maxage && leaf_age > age) > age = leaf_age; > len -= utf8clen(s); > @@ -501,7 +497,7 @@ int utf8nagemin(const struct utf8data *data, const char *s, size_t len) > leaf = utf8nlookup(data, hangul, s, len); > if (!leaf) > return -1; > - leaf_age = utf8agetab[LEAF_GEN(leaf)]; > + leaf_age = utf8_data->utf8agetab[LEAF_GEN(leaf)]; > if (leaf_age <= data->maxage && leaf_age < age) > age = leaf_age; > len -= utf8clen(s); > @@ -529,7 +525,7 @@ ssize_t utf8len(const struct utf8data *data, const char *s) > leaf = utf8lookup(data, hangul, s); > if (!leaf) > return -1; > - if (utf8agetab[LEAF_GEN(leaf)] > data->maxage) > + if (utf8_data->utf8agetab[LEAF_GEN(leaf)] > data->maxage) > ret += utf8clen(s); > else if (LEAF_CCC(leaf) == DECOMPOSE) > ret += strlen(LEAF_STR(leaf)); > @@ -557,7 +553,7 @@ ssize_t utf8nlen(const struct utf8data *data, const char *s, size_t len) > leaf = utf8nlookup(data, hangul, s, len); > if (!leaf) > return -1; > - if (utf8agetab[LEAF_GEN(leaf)] > data->maxage) > + if (utf8_data->utf8agetab[LEAF_GEN(leaf)] > data->maxage) > ret += utf8clen(s); > else if (LEAF_CCC(leaf) == DECOMPOSE) > ret += strlen(LEAF_STR(leaf)); > @@ -690,7 +686,7 @@ int utf8byte(struct utf8cursor *u8c) > > ccc = LEAF_CCC(leaf); > /* Characters that are too new have CCC 0. */ > - if (utf8agetab[LEAF_GEN(leaf)] > u8c->data->maxage) { > + if (utf8_data->utf8agetab[LEAF_GEN(leaf)] > u8c->data->maxage) { > ccc = STOPPER; > } else if (ccc == DECOMPOSE) { > u8c->len -= utf8clen(u8c->s); > @@ -769,24 +765,40 @@ EXPORT_SYMBOL(utf8byte); > > const struct utf8data *utf8nfdi(unsigned int maxage) > { > - int i = ARRAY_SIZE(utf8nfdidata) - 1; > + int i = utf8_data->utf8nfdidata_size - 1; > > - while (maxage < utf8nfdidata[i].maxage) > + while (maxage < utf8_data->utf8nfdidata[i].maxage) > i--; > - if (maxage > utf8nfdidata[i].maxage) > + if (maxage > utf8_data->utf8nfdidata[i].maxage) > return NULL; > - return &utf8nfdidata[i]; > + return &utf8_data->utf8nfdidata[i]; > } > EXPORT_SYMBOL(utf8nfdi); > > const struct utf8data *utf8nfdicf(unsigned int maxage) > { > - int i = ARRAY_SIZE(utf8nfdicfdata) - 1; > + int i = utf8_data->utf8nfdicfdata_size - 1; > > - while (maxage < utf8nfdicfdata[i].maxage) > + while (maxage < utf8_data->utf8nfdicfdata[i].maxage) > i--; > - if (maxage > utf8nfdicfdata[i].maxage) > + if (maxage > utf8_data->utf8nfdicfdata[i].maxage) > return NULL; > - return &utf8nfdicfdata[i]; > + return &utf8_data->utf8nfdicfdata[i]; > } > EXPORT_SYMBOL(utf8nfdicf); > + > +void unicode_register(struct utf8data_table *data) > +{ > + spin_lock(&utf8_lock); > + utf8_data = data; > + spin_unlock(&utf8_lock); > +} > +EXPORT_SYMBOL(unicode_register); > + > +void unicode_unregister(void) > +{ > + spin_lock(&utf8_lock); > + utf8_data = NULL; > + spin_unlock(&utf8_lock); > +} > +EXPORT_SYMBOL(unicode_unregister); > diff --git a/fs/unicode/utf8-selftest.c b/fs/unicode/utf8-selftest.c > index 6fe8af7edccb..d8069f4ad452 100644 > --- a/fs/unicode/utf8-selftest.c > +++ b/fs/unicode/utf8-selftest.c > @@ -16,6 +16,7 @@ > > unsigned int failed_tests; > unsigned int total_tests; > +struct unicode_map *table; > > /* Tests will be based on this version. */ > #define latest_maj 12 > @@ -232,16 +233,9 @@ static void check_utf8_nfdicf(void) > } > } > > -static void check_utf8_comparisons(void) > +static void check_utf8_comparisons(struct unicode_map *table) > { > int i; > - struct unicode_map *table = utf8_load("12.1.0"); > - > - if (IS_ERR(table)) { > - pr_err("%s: Unable to load utf8 %d.%d.%d. Skipping.\n", > - __func__, latest_maj, latest_min, latest_rev); > - return; > - } > > for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) { > const struct qstr s1 = {.name = nfdi_test_data[i].str, > @@ -262,8 +256,6 @@ static void check_utf8_comparisons(void) > test_f(!utf8_strncasecmp(table, &s1, &s2), > "%s %s comparison mismatch\n", s1.name, s2.name); > } > - > - utf8_unload(table); > } > > static void check_supported_versions(void) > @@ -274,9 +266,6 @@ static void check_supported_versions(void) > /* Unicode 9.0.0 should be supported. */ > test(utf8version_is_supported(9, 0, 0)); > > - /* Unicode 1x.0.0 (the latest version) should be supported. */ > - test(utf8version_is_supported(latest_maj, latest_min, latest_rev)); > - > /* Next versions don't exist. */ > test(!utf8version_is_supported(13, 0, 0)); > test(!utf8version_is_supported(0, 0, 0)); > @@ -288,10 +277,17 @@ static int __init init_test_ucd(void) > failed_tests = 0; > total_tests = 0; > > + table = utf8_load("12.1.0"); > + if (IS_ERR(table)) { > + pr_err("%s: Unable to load utf8 %d.%d.%d. Could not run the tests\n", > + __func__, latest_maj, latest_min, latest_rev); > + return -EINVAL; > + } > + > check_supported_versions(); > check_utf8_nfdi(); > check_utf8_nfdicf(); > - check_utf8_comparisons(); > + check_utf8_comparisons(table); > > if (!failed_tests) > pr_info("All %u tests passed\n", total_tests); > @@ -303,6 +299,7 @@ static int __init init_test_ucd(void) > > static void __exit exit_test_ucd(void) > { > + utf8_unload(table); > } > > module_init(init_test_ucd); > diff --git a/fs/unicode/utf8n.h b/fs/unicode/utf8n.h > index 0acd530c2c79..eb73fee9efc4 100644 > --- a/fs/unicode/utf8n.h > +++ b/fs/unicode/utf8n.h > @@ -11,6 +11,7 @@ > #include <linux/export.h> > #include <linux/string.h> > #include <linux/module.h> > +#include <linux/spinlock.h> > > /* Encoding a unicode version number as a single unsigned int. */ > #define UNICODE_MAJ_SHIFT (16) > @@ -21,6 +22,9 @@ > ((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \ > ((unsigned int)(REV))) > > +extern spinlock_t utf8_lock; > +extern struct utf8data_table *utf8_data; > + > /* Highest unicode version supported by the data tables. */ > extern int utf8version_is_supported(u8 maj, u8 min, u8 rev); > extern int utf8version_latest(void); > @@ -105,4 +109,30 @@ extern int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data, > */ > extern int utf8byte(struct utf8cursor *u8c); > > +struct utf8data { > + unsigned int maxage; > + unsigned int offset; > +}; > + > +struct utf8data_table { > + struct module *owner; > + > + const unsigned int utf8vers; > + > + const unsigned int *utf8agetab; > + int utf8agetab_size; > + > + const struct utf8data *utf8nfdicfdata; > + int utf8nfdicfdata_size; > + > + const struct utf8data *utf8nfdidata; > + int utf8nfdidata_size; > + > + const unsigned char *utf8data; > + int utf8data_size; > +}; > + > +void unicode_register(struct utf8data_table *data); > +void unicode_unregister(void); > + > #endif /* UTF8NORM_H */ > -- > 2.30.2 > ---end quoted text---