Re: [PATCH v2] misc: misc_minor_alloc to use ida for all dynamic/misc dynamic minors

From: Vimal Agrawal
Date: Tue Oct 15 2024 - 03:58:31 EST


Hi Greg,

Added commit that this fixed under "Fixes:" in commit log
As this was not causing any failure in any functions, I tested it by
looking at WARN in logs.
I wrote a very basic kunit for testing misc_register as I could not
find any tests for it.

I was hoping to fail misc_register if someone passes minor value in
the range of dynamic and misc dynamic minor but I do see at least one
case where PSMOUSE_MINOR(1)
is passed. Unless we decide to change the range of dynamic minors from
0 -127 to 1-127 but I am not sure if there are other such cases so to
be safer this patch is allowing
callers to pass minor in the range of dynamic /misc dynamic minors as
was the case earlier.

Thanks,
Vimal

On Tue, Oct 15, 2024 at 12:32 PM Vimal Agrawal <avimalin@xxxxxxxxx> wrote:
>
> misc_minor_alloc was allocating id using ida for minor only in case of
> MISC_DYNAMIC_MINOR but misc_minor_free was always freeing ids
> using ida_free causing a mismatch and following warn:
> > > WARNING: CPU: 0 PID: 159 at lib/idr.c:525 ida_free+0x3e0/0x41f
> > > ida_free called for id=127 which is not allocated.
> > > <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
> ...
> > > [<60941eb4>] ida_free+0x3e0/0x41f
> > > [<605ac993>] misc_minor_free+0x3e/0xbc
> > > [<605acb82>] misc_deregister+0x171/0x1b3
>
> misc_minor_alloc is changed to allocate id from ida for all minors
> falling in the range of dynamic/ misc dynamic minors
>
> Fixes: 0ad35fed618c ("char: misc: Increase the maximum number of dynamic misc devices to 1048448")
> Signed-off-by: Vimal Agrawal <vimal.agrawal@xxxxxxxxxx>
> ---
> drivers/char/misc.c | 35 +++++++++++++++++-----
> lib/Kconfig.debug | 11 +++++++
> lib/Makefile | 1 +
> lib/test_misc_minor.c | 67 +++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 107 insertions(+), 7 deletions(-)
> create mode 100644 lib/test_misc_minor.c
>
> diff --git a/drivers/char/misc.c b/drivers/char/misc.c
> index 541edc26ec89..9d0cd3459b4f 100644
> --- a/drivers/char/misc.c
> +++ b/drivers/char/misc.c
> @@ -63,16 +63,30 @@ static DEFINE_MUTEX(misc_mtx);
> #define DYNAMIC_MINORS 128 /* like dynamic majors */
> static DEFINE_IDA(misc_minors_ida);
>
> -static int misc_minor_alloc(void)
> +static int misc_minor_alloc(int minor)
> {
> int ret;
>
> - ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
> - if (ret >= 0) {
> - ret = DYNAMIC_MINORS - ret - 1;
> - } else {
> - ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
> + if (minor == MISC_DYNAMIC_MINOR) {
> + /* allocate free id */
> + ret = ida_alloc_max(&misc_minors_ida, DYNAMIC_MINORS - 1, GFP_KERNEL);
> + if (ret >= 0) {
> + ret = DYNAMIC_MINORS - ret - 1;
> + } else {
> + ret = ida_alloc_range(&misc_minors_ida, MISC_DYNAMIC_MINOR + 1,
> MINORMASK, GFP_KERNEL);
> + }
> + } else {
> + /* specific minor, check if it is in dynamic or misc dynamic range */
> + if (minor < DYNAMIC_MINORS) {
> + minor = DYNAMIC_MINORS - minor - 1;
> + ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
> + } else if (minor > MISC_DYNAMIC_MINOR) {
> + ret = ida_alloc_range(&misc_minors_ida, minor, minor, GFP_KERNEL);
> + } else {
> + /* case of non-dynamic minors, no need to allocate id */
> + ret = 0;
> + }
> }
> return ret;
> }
> @@ -219,7 +233,7 @@ int misc_register(struct miscdevice *misc)
> mutex_lock(&misc_mtx);
>
> if (is_dynamic) {
> - int i = misc_minor_alloc();
> + int i = misc_minor_alloc(misc->minor);
>
> if (i < 0) {
> err = -EBUSY;
> @@ -228,6 +242,7 @@ int misc_register(struct miscdevice *misc)
> misc->minor = i;
> } else {
> struct miscdevice *c;
> + int i;
>
> list_for_each_entry(c, &misc_list, list) {
> if (c->minor == misc->minor) {
> @@ -235,6 +250,12 @@ int misc_register(struct miscdevice *misc)
> goto out;
> }
> }
> +
> + i = misc_minor_alloc(misc->minor);
> + if (i < 0) {
> + err = -EBUSY;
> + goto out;
> + }
> }
>
> dev = MKDEV(MISC_MAJOR, misc->minor);
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 7315f643817a..5a5d27284e0a 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -2488,6 +2488,17 @@ config TEST_RHASHTABLE
> config TEST_IDA
> tristate "Perform selftest on IDA functions"
>
> +config TEST_MISC_MINOR
> + tristate "Basic misc minor Kunit test" if !KUNIT_ALL_TESTS
> + depends on KUNIT
> + default KUNIT_ALL_TESTS
> + help
> + Kunit test for the misc minor.
> + It tests misc minor functions for dynamic and misc dynamic minor.
> + This include misc_xxx functions
> +
> + If unsure, say N.
> +
> config TEST_PARMAN
> tristate "Perform selftest on priority array manager"
> depends on PARMAN
> diff --git a/lib/Makefile b/lib/Makefile
> index 773adf88af41..631d73f96f76 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o
> obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o
> obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
> obj-$(CONFIG_TEST_IDA) += test_ida.o
> +obj-$(CONFIG_TEST_MISC_MINOR) += test_misc_minor.o
> obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
> CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla)
> CFLAGS_test_ubsan.o += $(call cc-disable-warning, unused-but-set-variable)
> diff --git a/lib/test_misc_minor.c b/lib/test_misc_minor.c
> new file mode 100644
> index 000000000000..bcec3fb1c46a
> --- /dev/null
> +++ b/lib/test_misc_minor.c
> @@ -0,0 +1,67 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <kunit/test.h>
> +#include <kunit/test-bug.h>
> +#include <linux/module.h>
> +#include <linux/miscdevice.h>
> +
> +/* dynamic minor (2) */
> +static struct miscdevice dev_dynamic_minor = {
> + .minor = 2,
> + .name = "dev_dynamic_minor",
> +};
> +
> +/* static minor (LCD_MINOR) */
> +static struct miscdevice dev_static_minor = {
> + .minor = LCD_MINOR,
> + .name = "dev_static_minor",
> +};
> +
> +/* misc dynamic minor */
> +static struct miscdevice dev_misc_dynamic_minor = {
> + .minor = MISC_DYNAMIC_MINOR,
> + .name = "dev_misc_dynamic_minor",
> +};
> +
> +static void kunit_dynamic_minor(struct kunit *test)
> +{
> + int ret;
> +
> + ret=misc_register(&dev_dynamic_minor);
> + KUNIT_EXPECT_EQ(test, 0, ret);
> + KUNIT_EXPECT_EQ(test, 2, dev_dynamic_minor.minor);
> + misc_deregister(&dev_dynamic_minor);
> +}
> +
> +static void kunit_static_minor(struct kunit *test)
> +{
> + int ret;
> +
> + ret=misc_register(&dev_static_minor);
> + KUNIT_EXPECT_EQ(test, 0, ret);
> + KUNIT_EXPECT_EQ(test, LCD_MINOR, dev_static_minor.minor);
> + misc_deregister(&dev_static_minor);
> +}
> +
> +static void kunit_misc_dynamic_minor(struct kunit *test)
> +{
> + int ret;
> +
> + ret=misc_register(&dev_misc_dynamic_minor);
> + KUNIT_EXPECT_EQ(test, 0, ret);
> + misc_deregister(&dev_misc_dynamic_minor);
> +}
> +
> +static struct kunit_case test_cases[] = {
> + KUNIT_CASE(kunit_dynamic_minor),
> + KUNIT_CASE(kunit_static_minor),
> + KUNIT_CASE(kunit_misc_dynamic_minor),
> + {}
> +};
> +
> +static struct kunit_suite test_suite = {
> + .name = "misc_minor_test",
> + .test_cases = test_cases,
> +};
> +kunit_test_suite(test_suite);
> +
> +MODULE_LICENSE("GPL");
> --
> 2.17.1
>