diff options
37 files changed, 748 insertions, 297 deletions
diff --git a/Documentation/process/stable-kernel-rules.rst b/Documentation/process/stable-kernel-rules.rst index 51df1197d5ab..41f1e07abfdf 100644 --- a/Documentation/process/stable-kernel-rules.rst +++ b/Documentation/process/stable-kernel-rules.rst @@ -6,30 +6,29 @@ Everything you ever wanted to know about Linux -stable releases Rules on what kind of patches are accepted, and which ones are not, into the "-stable" tree: + - It or an equivalent fix must already exist in Linus' tree (upstream). - It must be obviously correct and tested. - It cannot be bigger than 100 lines, with context. - - It must fix only one thing. - - It must fix a real bug that bothers people (not a, "This could be a - problem..." type thing). - - It must fix a problem that causes a build error (but not for things - marked CONFIG_BROKEN), an oops, a hang, data corruption, a real - security issue, or some "oh, that's not good" issue. In short, something - critical. - - Serious issues as reported by a user of a distribution kernel may also - be considered if they fix a notable performance or interactivity issue. - As these fixes are not as obvious and have a higher risk of a subtle - regression they should only be submitted by a distribution kernel - maintainer and include an addendum linking to a bugzilla entry if it - exists and additional information on the user-visible impact. - - New device IDs and quirks are also accepted. - - No "theoretical race condition" issues, unless an explanation of how the - race can be exploited is also provided. - - It cannot contain any "trivial" fixes in it (spelling changes, - whitespace cleanups, etc). - It must follow the :ref:`Documentation/process/submitting-patches.rst <submittingpatches>` rules. - - It or an equivalent fix must already exist in Linus' tree (upstream). + - It must either fix a real bug that bothers people or just add a device ID. + To elaborate on the former: + + - It fixes a problem like an oops, a hang, data corruption, a real security + issue, a hardware quirk, a build error (but not for things marked + CONFIG_BROKEN), or some "oh, that's not good" issue. + - Serious issues as reported by a user of a distribution kernel may also + be considered if they fix a notable performance or interactivity issue. + As these fixes are not as obvious and have a higher risk of a subtle + regression they should only be submitted by a distribution kernel + maintainer and include an addendum linking to a bugzilla entry if it + exists and additional information on the user-visible impact. + - No "This could be a problem..." type of things like a "theoretical race + condition", unless an explanation of how the bug can be exploited is also + provided. + - No "trivial" fixes without benefit for users (spelling changes, whitespace + cleanups, etc). Procedure for submitting patches to the -stable tree @@ -41,111 +40,142 @@ Procedure for submitting patches to the -stable tree process but should follow the procedures in :ref:`Documentation/process/security-bugs.rst <securitybugs>`. -For all other submissions, choose one of the following procedures ------------------------------------------------------------------ +There are three options to submit a change to -stable trees: + + 1. Add a 'stable tag' to the description of a patch you then submit for + mainline inclusion. + 2. Ask the stable team to pick up a patch already mainlined. + 3. Submit a patch to the stable team that is equivalent to a change already + mainlined. + +The sections below describe each of the options in more detail. + +:ref:`option_1` is **strongly** preferred, it is the easiest and most common. +:ref:`option_2` is mainly meant for changes where backporting was not considered +at the time of submission. :ref:`option_3` is an alternative to the two earlier +options for cases where a mainlined patch needs adjustments to apply in older +series (for example due to API changes). + +When using option 2 or 3 you can ask for your change to be included in specific +stable series. When doing so, ensure the fix or an equivalent is applicable, +submitted, or already present in all newer stable trees still supported. This is +meant to prevent regressions that users might later encounter on updating, if +e.g. a fix merged for 5.19-rc1 would be backported to 5.10.y, but not to 5.15.y. .. _option_1: Option 1 ******** -To have the patch automatically included in the stable tree, add the tag +To have a patch you submit for mainline inclusion later automatically picked up +for stable trees, add the tag .. code-block:: none Cc: stable@vger.kernel.org -in the sign-off area. Once the patch is merged it will be applied to -the stable tree without anything else needing to be done by the author -or subsystem maintainer. +in the sign-off area. Once the patch is mainlined it will be applied to the +stable tree without anything else needing to be done by the author or +subsystem maintainer. -.. _option_2: +To sent additional instructions to the stable team, use a shell-style inline +comment: -Option 2 -******** + * To specify any additional patch prerequisites for cherry picking use the + following format in the sign-off area: -After the patch has been merged to Linus' tree, send an email to -stable@vger.kernel.org containing the subject of the patch, the commit ID, -why you think it should be applied, and what kernel version you wish it to -be applied to. + .. code-block:: none -.. _option_3: + Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle + Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle + Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic + Cc: <stable@vger.kernel.org> # 3.3.x + Signed-off-by: Ingo Molnar <mingo@elte.hu> -Option 3 -******** + The tag sequence has the meaning of: -Send the patch, after verifying that it follows the above rules, to -stable@vger.kernel.org. You must note the upstream commit ID in the -changelog of your submission, as well as the kernel version you wish -it to be applied to. + .. code-block:: none -:ref:`option_1` is **strongly** preferred, is the easiest and most common. -:ref:`option_2` and :ref:`option_3` are more useful if the patch isn't deemed -worthy at the time it is applied to a public git tree (for instance, because -it deserves more regression testing first). :ref:`option_3` is especially -useful if the original upstream patch needs to be backported (for example -the backport needs some special handling due to e.g. API changes). + git cherry-pick a1f84a3 + git cherry-pick 1b9508f + git cherry-pick fd21073 + git cherry-pick <this commit> -Note that for :ref:`option_3`, if the patch deviates from the original -upstream patch (for example because it had to be backported) this must be very -clearly documented and justified in the patch description. + * For patches that may have kernel version prerequisites specify them using + the following format in the sign-off area: -The upstream commit ID must be specified with a separate line above the commit -text, like this: + .. code-block:: none -.. code-block:: none + Cc: <stable@vger.kernel.org> # 3.3.x - commit <sha1> upstream. + The tag has the meaning of: -or alternatively: + .. code-block:: none -.. code-block:: none + git cherry-pick <this commit> - [ Upstream commit <sha1> ] + For each "-stable" tree starting with the specified version. -Additionally, some patches submitted via :ref:`option_1` may have additional -patch prerequisites which can be cherry-picked. This can be specified in the -following format in the sign-off area: + Note, such tagging is unnecessary if the stable team can derive the + appropriate versions from Fixes: tags. -.. code-block:: none + * To delay pick up of patches, use the following format: - Cc: <stable@vger.kernel.org> # 3.3.x: a1f84a3: sched: Check for idle - Cc: <stable@vger.kernel.org> # 3.3.x: 1b9508f: sched: Rate-limit newidle - Cc: <stable@vger.kernel.org> # 3.3.x: fd21073: sched: Fix affinity logic - Cc: <stable@vger.kernel.org> # 3.3.x - Signed-off-by: Ingo Molnar <mingo@elte.hu> + .. code-block:: none -The tag sequence has the meaning of: + Cc: <stable@vger.kernel.org> # after 4 weeks in mainline -.. code-block:: none + * For any other requests, just add a note to the stable tag. This for example + can be used to point out known problems: - git cherry-pick a1f84a3 - git cherry-pick 1b9508f - git cherry-pick fd21073 - git cherry-pick <this commit> + .. code-block:: none + + Cc: <stable@vger.kernel.org> # see patch description, needs adjustments for <= 6.3 + +.. _option_2: + +Option 2 +******** + +If the patch already has been merged to mainline, send an email to +stable@vger.kernel.org containing the subject of the patch, the commit ID, +why you think it should be applied, and what kernel versions you wish it to +be applied to. + +.. _option_3: -Also, some patches may have kernel version prerequisites. This can be -specified in the following format in the sign-off area: +Option 3 +******** + +Send the patch, after verifying that it follows the above rules, to +stable@vger.kernel.org and mention the kernel versions you wish it to be applied +to. When doing so, you must note the upstream commit ID in the changelog of your +submission with a separate line above the commit text, like this: .. code-block:: none - Cc: <stable@vger.kernel.org> # 3.3.x + commit <sha1> upstream. -The tag has the meaning of: +or alternatively: .. code-block:: none - git cherry-pick <this commit> + [ Upstream commit <sha1> ] + +If the submitted patch deviates from the original upstream patch (for example +because it had to be adjusted for the older API), this must be very clearly +documented and justified in the patch description. -For each "-stable" tree starting with the specified version. -Following the submission: +Following the submission +------------------------ - - The sender will receive an ACK when the patch has been accepted into the - queue, or a NAK if the patch is rejected. This response might take a few - days, according to the developer's schedules. - - If accepted, the patch will be added to the -stable queue, for review by - other developers and by the relevant subsystem maintainer. +The sender will receive an ACK when the patch has been accepted into the +queue, or a NAK if the patch is rejected. This response might take a few +days, according to the schedules of the stable team members. + +If accepted, the patch will be added to the -stable queue, for review by other +developers and by the relevant subsystem maintainer. Review cycle @@ -174,6 +204,7 @@ Review cycle security kernel team, and not go through the normal review cycle. Contact the kernel security team for more details on this procedure. + Trees ----- diff --git a/arch/x86/kernel/cpu/resctrl/pseudo_lock.c b/arch/x86/kernel/cpu/resctrl/pseudo_lock.c index 458cb7419502..8f559eeae08e 100644 --- a/arch/x86/kernel/cpu/resctrl/pseudo_lock.c +++ b/arch/x86/kernel/cpu/resctrl/pseudo_lock.c @@ -45,7 +45,21 @@ static u64 prefetch_disable_bits; */ static unsigned int pseudo_lock_major; static unsigned long pseudo_lock_minor_avail = GENMASK(MINORBITS, 0); -static struct class *pseudo_lock_class; + +static char *pseudo_lock_devnode(const struct device *dev, umode_t *mode) +{ + const struct rdtgroup *rdtgrp; + + rdtgrp = dev_get_drvdata(dev); + if (mode) + *mode = 0600; + return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdtgrp->kn->name); +} + +static const struct class pseudo_lock_class = { + .name = "pseudo_lock", + .devnode = pseudo_lock_devnode, +}; /** * get_prefetch_disable_bits - prefetch disable bits of supported platforms @@ -1353,7 +1367,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp) &pseudo_measure_fops); } - dev = device_create(pseudo_lock_class, NULL, + dev = device_create(&pseudo_lock_class, NULL, MKDEV(pseudo_lock_major, new_minor), rdtgrp, "%s", rdtgrp->kn->name); @@ -1383,7 +1397,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp) goto out; out_device: - device_destroy(pseudo_lock_class, MKDEV(pseudo_lock_major, new_minor)); + device_destroy(&pseudo_lock_class, MKDEV(pseudo_lock_major, new_minor)); out_debugfs: debugfs_remove_recursive(plr->debugfs_dir); pseudo_lock_minor_release(new_minor); @@ -1424,7 +1438,7 @@ void rdtgroup_pseudo_lock_remove(struct rdtgroup *rdtgrp) pseudo_lock_cstates_relax(plr); debugfs_remove_recursive(rdtgrp->plr->debugfs_dir); - device_destroy(pseudo_lock_class, MKDEV(pseudo_lock_major, plr->minor)); + device_destroy(&pseudo_lock_class, MKDEV(pseudo_lock_major, plr->minor)); pseudo_lock_minor_release(plr->minor); free: @@ -1560,16 +1574,6 @@ static const struct file_operations pseudo_lock_dev_fops = { .mmap = pseudo_lock_dev_mmap, }; -static char *pseudo_lock_devnode(const struct device *dev, umode_t *mode) -{ - const struct rdtgroup *rdtgrp; - - rdtgrp = dev_get_drvdata(dev); - if (mode) - *mode = 0600; - return kasprintf(GFP_KERNEL, "pseudo_lock/%s", rdtgrp->kn->name); -} - int rdt_pseudo_lock_init(void) { int ret; @@ -1580,21 +1584,18 @@ int rdt_pseudo_lock_init(void) pseudo_lock_major = ret; - pseudo_lock_class = class_create("pseudo_lock"); - if (IS_ERR(pseudo_lock_class)) { - ret = PTR_ERR(pseudo_lock_class); + ret = class_register(&pseudo_lock_class); + if (ret) { unregister_chrdev(pseudo_lock_major, "pseudo_lock"); return ret; } - pseudo_lock_class->devnode = pseudo_lock_devnode; return 0; } void rdt_pseudo_lock_release(void) { - class_destroy(pseudo_lock_class); - pseudo_lock_class = NULL; + class_unregister(&pseudo_lock_class); unregister_chrdev(pseudo_lock_major, "pseudo_lock"); pseudo_lock_major = 0; } diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index bdc0d5539b57..dae436253de4 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -40,7 +40,6 @@ #include <asm/processor.h> #include <asm/msr.h> -static struct class *cpuid_class; static enum cpuhp_state cpuhp_cpuid_state; struct cpuid_regs_done { @@ -124,26 +123,31 @@ static const struct file_operations cpuid_fops = { .open = cpuid_open, }; +static char *cpuid_devnode(const struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); +} + +static const struct class cpuid_class = { + .name = "cpuid", + .devnode = cpuid_devnode, +}; + static int cpuid_device_create(unsigned int cpu) { struct device *dev; - dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, + dev = device_create(&cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); return PTR_ERR_OR_ZERO(dev); } static int cpuid_device_destroy(unsigned int cpu) { - device_destroy(cpuid_class, MKDEV(CPUID_MAJOR, cpu)); + device_destroy(&cpuid_class, MKDEV(CPUID_MAJOR, cpu)); return 0; } -static char *cpuid_devnode(const struct device *dev, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); -} - static int __init cpuid_init(void) { int err; @@ -154,12 +158,9 @@ static int __init cpuid_init(void) CPUID_MAJOR); return -EBUSY; } - cpuid_class = class_create("cpuid"); - if (IS_ERR(cpuid_class)) { - err = PTR_ERR(cpuid_class); + err = class_register(&cpuid_class); + if (err) goto out_chrdev; - } - cpuid_class->devnode = cpuid_devnode; err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/cpuid:online", cpuid_device_create, cpuid_device_destroy); @@ -170,7 +171,7 @@ static int __init cpuid_init(void) return 0; out_class: - class_destroy(cpuid_class); + class_unregister(&cpuid_class); out_chrdev: __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid"); return err; @@ -180,7 +181,7 @@ module_init(cpuid_init); static void __exit cpuid_exit(void) { cpuhp_remove_state(cpuhp_cpuid_state); - class_destroy(cpuid_class); + class_unregister(&cpuid_class); __unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid"); } module_exit(cpuid_exit); diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index 7bb17d37db01..e17c16c54a37 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -39,7 +39,6 @@ #include <asm/cpufeature.h> #include <asm/msr.h> -static struct class *msr_class; static enum cpuhp_state cpuhp_msr_state; enum allow_write_msrs { @@ -235,26 +234,31 @@ static const struct file_operations msr_fops = { .compat_ioctl = msr_ioctl, }; +static char *msr_devnode(const struct device *dev, umode_t *mode) +{ + return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt)); +} + +static const struct class msr_class = { + .name = "msr", + .devnode = msr_devnode, +}; + static int msr_device_create(unsigned int cpu) { struct device *dev; - dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL, + dev = device_create(&msr_class, NULL, MKDEV(MSR_MAJOR, cpu), NULL, "msr%d", cpu); return PTR_ERR_OR_ZERO(dev); } static int msr_device_destroy(unsigned int cpu) { - device_destroy(msr_class, MKDEV(MSR_MAJOR, cpu)); + device_destroy(&msr_class, MKDEV(MSR_MAJOR, cpu)); return 0; } -static char *msr_devnode(const struct device *dev, umode_t *mode) -{ - return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt)); -} - static int __init msr_init(void) { int err; @@ -263,12 +267,9 @@ static int __init msr_init(void) pr_err("unable to get major %d for msr\n", MSR_MAJOR); return -EBUSY; } - msr_class = class_create("msr"); - if (IS_ERR(msr_class)) { - err = PTR_ERR(msr_class); + err = class_register(&msr_class); + if (err) goto out_chrdev; - } - msr_class->devnode = msr_devnode; err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/msr:online", msr_device_create, msr_device_destroy); @@ -278,7 +279,7 @@ static int __init msr_init(void) return 0; out_class: - class_destroy(msr_class); + class_unregister(&msr_class); out_chrdev: __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr"); return err; @@ -288,7 +289,7 @@ module_init(msr_init); static void __exit msr_exit(void) { cpuhp_remove_state(cpuhp_msr_state); - class_destroy(msr_class); + class_unregister(&msr_class); __unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr"); } module_exit(msr_exit) diff --git a/drivers/base/core.c b/drivers/base/core.c index 704ba73e1459..b7d7f410c256 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2306,12 +2306,12 @@ static void device_platform_notify(struct device *dev) static void device_platform_notify_remove(struct device *dev) { - acpi_device_notify_remove(dev); + if (platform_notify_remove) + platform_notify_remove(dev); software_node_notify_remove(dev); - if (platform_notify_remove) - platform_notify_remove(dev); + acpi_device_notify_remove(dev); } /** @@ -3528,18 +3528,17 @@ int device_add(struct device *dev) * the name, and force the use of dev_name() */ if (dev->init_name) { - dev_set_name(dev, "%s", dev->init_name); + error = dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } + if (dev_name(dev)) + error = 0; /* subsystems can specify simple device enumeration */ - if (!dev_name(dev) && dev->bus && dev->bus->dev_name) - dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); - - if (!dev_name(dev)) { - error = -EINVAL; + else if (dev->bus && dev->bus->dev_name) + error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); + if (error) goto name_error; - } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); @@ -3815,6 +3814,17 @@ void device_del(struct device *dev) device_platform_notify_remove(dev); device_links_purge(dev); + /* + * If a device does not have a driver attached, we need to clean + * up any managed resources. We do this in device_release(), but + * it's never called (and we leak the device) if a managed + * resource holds a reference to the device. So release all + * managed resources here, like we do in driver_detach(). We + * still need to do so again in device_release() in case someone + * adds a new resource after this point, though. + */ + devres_release_all(dev); + bus_notify(dev, BUS_NOTIFY_REMOVED_DEVICE); kobject_uevent(&dev->kobj, KOBJ_REMOVE); glue_dir = get_glue_dir(dev); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 43dab03958f1..9ea22e165acd 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -19,6 +19,7 @@ #include <linux/cpufeature.h> #include <linux/tick.h> #include <linux/pm_qos.h> +#include <linux/delay.h> #include <linux/sched/isolation.h> #include "base.h" @@ -50,12 +51,30 @@ static int cpu_subsys_online(struct device *dev) int cpuid = dev->id; int from_nid, to_nid; int ret; + int retries = 0; from_nid = cpu_to_node(cpuid); if (from_nid == NUMA_NO_NODE) return -ENODEV; +retry: ret = cpu_device_up(dev); + + /* + * If -EBUSY is returned, it is likely that hotplug is temporarily + * disabled when cpu_hotplug_disable() was called. This condition is + * transient. So we retry after waiting for an exponentially + * increasing delay up to a total of at least 620ms as some PCI + * device initialization can take quite a while. + */ + if (ret == -EBUSY) { + retries++; + if (retries > 5) + return ret; + msleep(10 * (1 << retries)); + goto retry; + } + /* * When hot adding memory to memoryless node and enabling a cpu * on the node, node number of the cpu may internally change. diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 878aa7646b37..a528cec24264 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -693,6 +693,8 @@ re_probe: device_remove(dev); driver_sysfs_remove(dev); + if (dev->bus && dev->bus->dma_cleanup) + dev->bus->dma_cleanup(dev); device_unbind_cleanup(dev); goto re_probe; diff --git a/drivers/base/node.c b/drivers/base/node.c index 8e871ba9162f..493d533f8375 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -20,7 +20,6 @@ #include <linux/pm_runtime.h> #include <linux/swap.h> #include <linux/slab.h> -#include <linux/hugetlb.h> static struct bus_type node_subsys = { .name = "node", diff --git a/drivers/base/test/.kunitconfig b/drivers/base/test/.kunitconfig new file mode 100644 index 000000000000..473923f0998b --- /dev/null +++ b/drivers/base/test/.kunitconfig @@ -0,0 +1,2 @@ +CONFIG_KUNIT=y +CONFIG_DM_KUNIT_TEST=y diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 610a1ba7a467..9d42051f8f8e 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -9,6 +9,10 @@ config TEST_ASYNC_DRIVER_PROBE If unsure say N. +config DM_KUNIT_TEST + tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS + depends on KUNIT + config DRIVER_PE_KUNIT_TEST bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT=y diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 7f76fee6f989..e321dfc7e922 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o +obj-$(CONFIG_DM_KUNIT_TEST) += root-device-test.o +obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o + obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/base/test/platform-device-test.c b/drivers/base/test/platform-device-test.c new file mode 100644 index 000000000000..ea05b8785743 --- /dev/null +++ b/drivers/base/test/platform-device-test.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <kunit/resource.h> + +#include <linux/device.h> +#include <linux/platform_device.h> + +#define DEVICE_NAME "test" + +struct test_priv { + bool probe_done; + bool release_done; + wait_queue_head_t probe_wq; + wait_queue_head_t release_wq; + struct device *dev; +}; + +static int platform_device_devm_init(struct kunit *test) +{ + struct test_priv *priv; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + init_waitqueue_head(&priv->probe_wq); + init_waitqueue_head(&priv->release_wq); + + test->priv = priv; + + return 0; +} + +static void devm_device_action(void *ptr) +{ + struct test_priv *priv = ptr; + + priv->release_done = true; + wake_up_interruptible(&priv->release_wq); +} + +static void devm_put_device_action(void *ptr) +{ + struct test_priv *priv = ptr; + + put_device(priv->dev); + priv->release_done = true; + wake_up_interruptible(&priv->release_wq); +} + +#define RELEASE_TIMEOUT_MS 100 + +/* + * Tests that a platform bus, non-probed device will run its + * device-managed actions when unregistered. + */ +static void platform_device_devm_register_unregister_test(struct kunit *test) +{ + struct platform_device *pdev; + struct test_priv *priv = test->priv; + int ret; + + pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + priv->dev = &pdev->dev; + + ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + platform_device_unregister(pdev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +/* + * Tests that a platform bus, non-probed device will run its + * device-managed actions when unregistered, even if someone still holds + * a reference to it. + */ +static void platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test) +{ + struct platform_device *pdev; + struct test_priv *priv = test->priv; + int ret; + + pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + priv->dev = &pdev->dev; + + get_device(priv->dev); + + ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + platform_device_unregister(pdev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +static int fake_probe(struct platform_device *pdev) +{ + struct test_priv *priv = platform_get_drvdata(pdev); + + priv->probe_done = true; + wake_up_interruptible(&priv->probe_wq); + + return 0; +} + +static struct platform_driver fake_driver = { + .probe = fake_probe, + .driver = { + .name = DEVICE_NAME, + }, +}; + +/* + * Tests that a platform bus, probed device will run its device-managed + * actions when unregistered. + */ +static void probed_platform_device_devm_register_unregister_test(struct kunit *test) +{ + struct platform_device *pdev; + struct test_priv *priv = test->priv; + int ret; + + ret = platform_driver_register(&fake_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_ASSERT_GT(test, ret, 0); + + ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + platform_device_unregister(pdev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); + + platform_driver_unregister(&fake_driver); +} + +/* + * Tests that a platform bus, probed device will run its device-managed + * actions when unregistered, even if someone still holds a reference to + * it. + */ +static void probed_platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test) +{ + struct platform_device *pdev; + struct test_priv *priv = test->priv; + int ret; + + ret = platform_driver_register(&fake_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_ASSERT_GT(test, ret, 0); + + get_device(priv->dev); + + ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + platform_device_unregister(pdev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); + + platform_driver_unregister(&fake_driver); +} + +static struct kunit_case platform_device_devm_tests[] = { + KUNIT_CASE(platform_device_devm_register_unregister_test), + KUNIT_CASE(platform_device_devm_register_get_unregister_with_devm_test), + KUNIT_CASE(probed_platform_device_devm_register_unregister_test), + KUNIT_CASE(probed_platform_device_devm_register_get_unregister_with_devm_test), + {} +}; + +static struct kunit_suite platform_device_devm_test_suite = { + .name = "platform-device-devm", + .init = platform_device_devm_init, + .test_cases = platform_device_devm_tests, +}; + +kunit_test_suite(platform_device_devm_test_suite); + +MODULE_DESCRIPTION("Test module for platform devices"); +MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/base/test/root-device-test.c b/drivers/base/test/root-device-test.c new file mode 100644 index 000000000000..9aea23c9123e --- /dev/null +++ b/drivers/base/test/root-device-test.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2023 Maxime Ripard <mripard@kernel.org> + +#include <kunit/resource.h> + +#include <linux/device.h> + +#define DEVICE_NAME "test" + +struct test_priv { + bool probe_done; + bool release_done; + wait_queue_head_t release_wq; + struct device *dev; +}; + +static int root_device_devm_init(struct kunit *test) +{ + struct test_priv *priv; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + init_waitqueue_head(&priv->release_wq); + + test->priv = priv; + + return 0; +} + +static void devm_device_action(void *ptr) +{ + struct test_priv *priv = ptr; + + priv->release_done = true; + wake_up_interruptible(&priv->release_wq); +} + +#define RELEASE_TIMEOUT_MS 100 + +/* + * Tests that a bus-less, non-probed device will run its device-managed + * actions when unregistered. + */ +static void root_device_devm_register_unregister_test(struct kunit *test) +{ + struct test_priv *priv = test->priv; + int ret; + + priv->dev = root_device_register(DEVICE_NAME); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + root_device_unregister(priv->dev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +static void devm_put_device_action(void *ptr) +{ + struct test_priv *priv = ptr; + + put_device(priv->dev); + priv->release_done = true; + wake_up_interruptible(&priv->release_wq); +} + +/* + * Tests that a bus-less, non-probed device will run its device-managed + * actions when unregistered, even if someone still holds a reference to + * it. + */ +static void root_device_devm_register_get_unregister_with_devm_test(struct kunit *test) +{ + struct test_priv *priv = test->priv; + int ret; + + priv->dev = root_device_register(DEVICE_NAME); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + get_device(priv->dev); + + ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv); + KUNIT_ASSERT_EQ(test, ret, 0); + + root_device_unregister(priv->dev); + + ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done, + msecs_to_jiffies(RELEASE_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +static struct kunit_case root_device_devm_tests[] = { + KUNIT_CASE(root_device_devm_register_unregister_test), + KUNIT_CASE(root_device_devm_register_get_unregister_with_devm_test), + {} +}; + +static struct kunit_suite root_device_devm_test_suite = { + .name = "root-device-devm", + .init = root_device_devm_init, + .test_cases = root_device_devm_tests, +}; + +kunit_test_suite(root_device_devm_test_suite); + +MODULE_DESCRIPTION("Test module for root devices"); +MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/base/test/test_async_driver_probe.c b/drivers/base/test/test_async_driver_probe.c index 929410d0dd6f..3465800baa6c 100644 --- a/drivers/base/test/test_async_driver_probe.c +++ b/drivers/base/test/test_async_driver_probe.c @@ -84,7 +84,7 @@ test_platform_device_register_node(char *name, int id, int nid) pdev = platform_device_alloc(name, id); if (!pdev) - return NULL; + return ERR_PTR(-ENOMEM); if (nid != NUMA_NO_NODE) set_dev_node(&pdev->dev, nid); diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index ea6b4013bc38..23f6f2eda84c 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -28,8 +28,13 @@ DEFINE_IDR(dev_nums_idr); static DEFINE_MUTEX(idr_lock); -struct class *tpm_class; -struct class *tpmrm_class; +const struct class tpm_class = { + .name = "tpm", + .shutdown_pre = tpm_class_shutdown, +}; +const struct class tpmrm_class = { + .name = "tmprm", +}; dev_t tpm_devt; static int tpm_request_locality(struct tpm_chip *chip) @@ -336,7 +341,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, device_initialize(&chip->dev); - chip->dev.class = tpm_class; + chip->dev.class = &tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = pdev; chip->dev.groups = chip->groups; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 586ca10b0d72..66b16d26eecc 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -476,18 +476,15 @@ static int __init tpm_init(void) { int rc; - tpm_class = class_create("tpm"); - if (IS_ERR(tpm_class)) { + rc = class_register(&tpm_class); + if (rc) { pr_err("couldn't create tpm class\n"); - return PTR_ERR(tpm_class); + return rc; } - tpm_class->shutdown_pre = tpm_class_shutdown; - - tpmrm_class = class_create("tpmrm"); - if (IS_ERR(tpmrm_class)) { + rc = class_register(&tpmrm_class); + if (rc) { pr_err("couldn't create tpmrm class\n"); - rc = PTR_ERR(tpmrm_class); goto out_destroy_tpm_class; } @@ -508,9 +505,9 @@ static int __init tpm_init(void) out_unreg_chrdev: unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES); out_destroy_tpmrm_class: - class_destroy(tpmrm_class); + class_unregister(&tpmrm_class); out_destroy_tpm_class: - class_destroy(tpm_class); + class_unregister(&tpm_class); return rc; } @@ -518,8 +515,8 @@ out_destroy_tpm_class: static void __exit tpm_exit(void) { idr_destroy(&dev_nums_idr); - class_destroy(tpm_class); - class_destroy(tpmrm_class); + class_unregister(&tpm_class); + class_unregister(&tpmrm_class); unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES); tpm_dev_common_exit(); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 460bb85dd142..61445f1dc46d 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -230,8 +230,8 @@ enum tpm2_pt_props { * compiler warnings about stack frame size. */ #define TPM_MAX_RNG_DATA 128 -extern struct class *tpm_class; -extern struct class *tpmrm_class; +extern const struct class tpm_class; +extern const struct class tpmrm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; extern const struct file_operations tpmrm_fops; diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index ffb35f0154c1..363afdd4d1d3 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -606,7 +606,7 @@ int tpm_devs_add(struct tpm_chip *chip) device_initialize(&chip->devs); chip->devs.parent = chip->dev.parent; - chip->devs.class = tpmrm_class; + chip->devs.class = &tpmrm_class; /* * Get extra reference on main device to hold on behalf of devs. diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index ea6b79b3aeeb..d55aaabab1ed 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -23,8 +23,6 @@ #include "hid-roccat-common.h" #include "hid-roccat-arvo.h" -static struct class *arvo_class; - static ssize_t arvo_sysfs_show_mode_key(struct device *dev, struct device_attribute *attr, char *buf) { @@ -268,6 +266,11 @@ static const struct attribute_group *arvo_groups[] = { NULL, }; +static const struct class arvo_class = { + .name = "arvo", + .dev_groups = arvo_groups, +}; + static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, struct arvo_device *arvo) { @@ -309,7 +312,7 @@ static int arvo_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(arvo_class, hdev, + retval = roccat_connect(&arvo_class, hdev, sizeof(struct arvo_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); @@ -433,21 +436,20 @@ static int __init arvo_init(void) { int retval; - arvo_class = class_create("arvo"); - if (IS_ERR(arvo_class)) - return PTR_ERR(arvo_class); - arvo_class->dev_groups = arvo_groups; + retval = class_register(&arvo_class); + if (retval) + return retval; retval = hid_register_driver(&arvo_driver); if (retval) - class_destroy(arvo_class); + class_unregister(&arvo_class); return retval; } static void __exit arvo_exit(void) { hid_unregister_driver(&arvo_driver); - class_destroy(arvo_class); + class_unregister(&arvo_class); } module_init(arvo_init); diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 3903a2cea00c..458060403397 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -23,8 +23,6 @@ #include "hid-roccat-common.h" #include "hid-roccat-isku.h" -static struct class *isku_class; - static void isku_profile_activated(struct isku_device *isku, uint new_profile) { isku->actual_profile = new_profile; @@ -248,6 +246,11 @@ static const struct attribute_group *isku_groups[] = { NULL, }; +static const struct class isku_class = { + .name = "isku", + .dev_groups = isku_groups, +}; + static int isku_init_isku_device_struct(struct usb_device *usb_dev, struct isku_device *isku) { @@ -289,7 +292,7 @@ static int isku_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(isku_class, hdev, + retval = roccat_connect(&isku_class, hdev, sizeof(struct isku_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); @@ -435,21 +438,21 @@ static struct hid_driver isku_driver = { static int __init isku_init(void) { int retval; - isku_class = class_create("isku"); - if (IS_ERR(isku_class)) - return PTR_ERR(isku_class); - isku_class->dev_groups = isku_groups; + + retval = class_register(&isku_class); + if (retval) + return retval; retval = hid_register_driver(&isku_driver); if (retval) - class_destroy(isku_class); + class_unregister(&isku_class); return retval; } static void __exit isku_exit(void) { hid_unregister_driver(&isku_driver); - class_destroy(isku_class); + class_unregister(&isku_class); } module_init(isku_init); diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 945ae236fb45..00a1abc7e839 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -89,9 +89,6 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command, return ((len < 0) ? len : ((len != size) ? -EIO : 0)); } -/* kone_class is used for creating sysfs attributes via roccat char device */ -static struct class *kone_class; - static void kone_set_settings_checksum(struct kone_settings *settings) { uint16_t checksum = 0; @@ -657,6 +654,12 @@ static const struct attribute_group *kone_groups[] = { NULL, }; +/* kone_class is used for creating sysfs attributes via roccat char device */ +static const struct class kone_class = { + .name = "kone", + .dev_groups = kone_groups, +}; + static int kone_init_kone_device_struct(struct usb_device *usb_dev, struct kone_device *kone) { @@ -712,8 +715,8 @@ static int kone_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kone_class, hdev, - sizeof(struct kone_roccat_report)); + retval = roccat_connect(&kone_class, hdev, + sizeof(struct kone_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); /* be tolerant about not getting chrdev */ @@ -890,21 +893,20 @@ static int __init kone_init(void) int retval; /* class name has to be same as driver name */ - kone_class = class_create("kone"); - if (IS_ERR(kone_class)) - return PTR_ERR(kone_class); - kone_class->dev_groups = kone_groups; + retval = class_register(&kone_class); + if (retval) + return retval; retval = hid_register_driver(&kone_driver); if (retval) - class_destroy(kone_class); + class_unregister(&kone_class); return retval; } static void __exit kone_exit(void) { hid_unregister_driver(&kone_driver); - class_destroy(kone_class); + class_unregister(&kone_class); } module_init(kone_init); diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 97b83b6f53dd..22b895436a7c 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -26,8 +26,6 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4}; -static struct class *koneplus_class; - static void koneplus_profile_activated(struct koneplus_device *koneplus, uint new_profile) { @@ -356,6 +354,11 @@ static const struct attribute_group *koneplus_groups[] = { NULL, }; +static const struct class koneplus_class = { + .name = "koneplus", + .dev_groups = koneplus_groups, +}; + static int koneplus_init_koneplus_device_struct(struct usb_device *usb_dev, struct koneplus_device *koneplus) { @@ -394,8 +397,8 @@ static int koneplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(koneplus_class, hdev, - sizeof(struct koneplus_roccat_report)); + retval = roccat_connect(&koneplus_class, hdev, + sizeof(struct koneplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -549,21 +552,20 @@ static int __init koneplus_init(void) int retval; /* class name has to be same as driver name */ - koneplus_class = class_create("koneplus"); - if (IS_ERR(koneplus_class)) - return PTR_ERR(koneplus_class); - koneplus_class->dev_groups = koneplus_groups; + retval = class_register(&koneplus_class); + if (retval) + return retval; retval = hid_register_driver(&koneplus_driver); if (retval) - class_destroy(koneplus_class); + class_unregister(&koneplus_class); return retval; } static void __exit koneplus_exit(void) { hid_unregister_driver(&koneplus_driver); - class_destroy(koneplus_class); + class_unregister(&koneplus_class); } module_init(koneplus_init); diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index a297756f2410..beca8aef8bbb 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -36,8 +36,6 @@ struct konepure_mouse_report_button { uint8_t unknown[2]; } __packed; -static struct class *konepure_class; - ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(actual_profile, 0x05, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_settings, 0x06, 0x1f); @@ -72,6 +70,11 @@ static const struct attribute_group *konepure_groups[] = { NULL, }; +static const struct class konepure_class = { + .name = "konepure", + .dev_groups = konepure_groups, +}; + static int konepure_init_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); @@ -98,8 +101,8 @@ static int konepure_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(konepure_class, hdev, - sizeof(struct konepure_mouse_report_button)); + retval = roccat_connect(&konepure_class, hdev, + sizeof(struct konepure_mouse_report_button)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -207,21 +210,20 @@ static int __init konepure_init(void) { int retval; - konepure_class = class_create("konepure"); - if (IS_ERR(konepure_class)) - return PTR_ERR(konepure_class); - konepure_class->dev_groups = konepure_groups; + retval = class_register(&konepure_class); + if (retval) + return retval; retval = hid_register_driver(&konepure_driver); if (retval) - class_destroy(konepure_class); + class_unregister(&konepure_class); return retval; } static void __exit konepure_exit(void) { hid_unregister_driver(&konepure_driver); - class_destroy(konepure_class); + class_unregister(&konepure_class); } module_init(konepure_init); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 1a1d96e11683..86af538c10d6 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -24,8 +24,6 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4}; -static struct class *kovaplus_class; - static uint kovaplus_convert_event_cpi(uint value) { return (value == 7 ? 4 : (value == 4 ? 3 : value)); @@ -409,6 +407,11 @@ static const struct attribute_group *kovaplus_groups[] = { NULL, }; +static const struct class kovaplus_class = { + .name = "kovaplus", + .dev_groups = kovaplus_groups, +}; + static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, struct kovaplus_device *kovaplus) { @@ -463,8 +466,8 @@ static int kovaplus_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(kovaplus_class, hdev, - sizeof(struct kovaplus_roccat_report)); + retval = roccat_connect(&kovaplus_class, hdev, + sizeof(struct kovaplus_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); } else { @@ -638,21 +641,20 @@ static int __init kovaplus_init(void) { int retval; - kovaplus_class = class_create("kovaplus"); - if (IS_ERR(kovaplus_class)) - return PTR_ERR(kovaplus_class); - kovaplus_class->dev_groups = kovaplus_groups; + retval = class_register(&kovaplus_class); + if (retval) + return retval; retval = hid_register_driver(&kovaplus_driver); if (retval) - class_destroy(kovaplus_class); + class_unregister(&kovaplus_class); return retval; } static void __exit kovaplus_exit(void) { hid_unregister_driver(&kovaplus_driver); - class_destroy(kovaplus_class); + class_unregister(&kovaplus_class); } module_init(kovaplus_init); diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 15528c3b013c..5663b9cd9c69 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -26,9 +26,6 @@ static uint profile_numbers[5] = {0, 1, 2, 3, 4}; -/* pyra_class is used for creating sysfs attributes via roccat char device */ -static struct class *pyra_class; - static void profile_activated(struct pyra_device *pyra, unsigned int new_profile) { @@ -366,6 +363,12 @@ static const struct attribute_group *pyra_groups[] = { NULL, }; +/* pyra_class is used for creating sysfs attributes via roccat char device */ +static const struct class pyra_class = { + .name = "pyra", + .dev_groups = pyra_groups, +}; + static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, struct pyra_device *pyra) { @@ -413,7 +416,7 @@ static int pyra_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(pyra_class, hdev, + retval = roccat_connect(&pyra_class, hdev, sizeof(struct pyra_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); @@ -585,21 +588,20 @@ static int __init pyra_init(void) int retval; /* class name has to be same as driver name */ - pyra_class = class_create("pyra"); - if (IS_ERR(pyra_class)) - return PTR_ERR(pyra_class); - pyra_class->dev_groups = pyra_groups; + retval = class_register(&pyra_class); + if (retval) + return retval; retval = hid_register_driver(&pyra_driver); if (retval) - class_destroy(pyra_class); + class_unregister(&pyra_class); return retval; } static void __exit pyra_exit(void) { hid_unregister_driver(&pyra_driver); - class_destroy(pyra_class); + class_unregister(&pyra_class); } module_init(pyra_init); diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c index 0eb17a3b925d..57714a4525e2 100644 --- a/drivers/hid/hid-roccat-ryos.c +++ b/drivers/hid/hid-roccat-ryos.c @@ -28,8 +28,6 @@ struct ryos_report_special { uint8_t data[4]; } __packed; -static struct class *ryos_class; - ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d); @@ -80,6 +78,11 @@ static const struct attribute_group *ryos_groups[] = { NULL, }; +static const struct class ryos_class = { + .name = "ryos", + .dev_groups = ryos_groups, +}; + static int ryos_init_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); @@ -106,7 +109,7 @@ static int ryos_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(ryos_class, hdev, + retval = roccat_connect(&ryos_class, hdev, sizeof(struct ryos_report_special)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); @@ -216,21 +219,20 @@ static int __init ryos_init(void) { int retval; - ryos_class = class_create("ryos"); - if (IS_ERR(ryos_class)) - return PTR_ERR(ryos_class); - ryos_class->dev_groups = ryos_groups; + retval = class_register(&ryos_class); + if (retval) + return retval; retval = hid_register_driver(&ryos_driver); if (retval) - class_destroy(ryos_class); + class_unregister(&ryos_class); return retval; } static void __exit ryos_exit(void) { hid_unregister_driver(&ryos_driver); - class_destroy(ryos_class); + class_unregister(&ryos_class); } module_init(ryos_init); diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 93be7acef673..2baa47a0efc5 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -22,8 +22,6 @@ #include "hid-roccat-common.h" #include "hid-roccat-savu.h" -static struct class *savu_class; - ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10); @@ -52,6 +50,11 @@ static const struct attribute_group *savu_groups[] = { NULL, }; +static const struct class savu_class = { + .name = "savu", + .dev_groups = savu_groups, +}; + static int savu_init_specials(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); @@ -78,7 +81,7 @@ static int savu_init_specials(struct hid_device *hdev) goto exit_free; } - retval = roccat_connect(savu_class, hdev, + retval = roccat_connect(&savu_class, hdev, sizeof(struct savu_roccat_report)); if (retval < 0) { hid_err(hdev, "couldn't init char dev\n"); @@ -204,21 +207,20 @@ static int __init savu_init(void) { int retval; - savu_class = class_create("savu"); - if (IS_ERR(savu_class)) - return PTR_ERR(savu_class); - savu_class->dev_groups = savu_groups; + retval = class_register(&savu_class); + if (retval) + return retval; retval = hid_register_driver(&savu_driver); if (retval) - class_destroy(savu_class); + class_unregister(&savu_class); return retval; } static void __exit savu_exit(void) { hid_unregister_driver(&savu_driver); - class_destroy(savu_class); + class_unregister(&savu_class); } module_init(savu_init); diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 6da80e442fdd..c7f7562e22e5 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -295,7 +295,7 @@ EXPORT_SYMBOL_GPL(roccat_report_event); * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on * success, a negative error code on failure. */ -int roccat_connect(struct class *klass, struct hid_device *hid, int report_size) +int roccat_connect(const struct class *klass, struct hid_device *hid, int report_size) { unsigned int minor; struct roccat_device *device; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index e63c56a0d57f..13c8dd8cd350 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -32,7 +32,9 @@ static int hidraw_major; static struct cdev hidraw_cdev; -static struct class *hidraw_class; +static const struct class hidraw_class = { + .name = "hidraw", +}; static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; static DECLARE_RWSEM(minors_rwsem); @@ -329,7 +331,7 @@ static void drop_ref(struct hidraw *hidraw, int exists_bit) hid_hw_close(hidraw->hid); wake_up_interruptible(&hidraw->wait); } - device_destroy(hidraw_class, + device_destroy(&hidraw_class, MKDEV(hidraw_major, hidraw->minor)); } else { --hidraw->open; @@ -569,7 +571,7 @@ int hidraw_connect(struct hid_device *hid) goto out; } - dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor), + dev->dev = device_create(&hidraw_class, &hid->dev, MKDEV(hidraw_major, minor), NULL, "%s%d", "hidraw", minor); if (IS_ERR(dev->dev)) { @@ -623,11 +625,9 @@ int __init hidraw_init(void) hidraw_major = MAJOR(dev_id); - hidraw_class = class_create("hidraw"); - if (IS_ERR(hidraw_class)) { - result = PTR_ERR(hidraw_class); + result = class_register(&hidraw_class); + if (result) goto error_cdev; - } cdev_init(&hidraw_cdev, &hidraw_ops); result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); @@ -639,7 +639,7 @@ out: return result; error_class: - class_destroy(hidraw_class); + class_unregister(&hidraw_class); error_cdev: unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); goto out; @@ -650,7 +650,7 @@ void hidraw_exit(void) dev_t dev_id = MKDEV(hidraw_major, 0); cdev_del(&hidraw_cdev); - class_destroy(hidraw_class); + class_unregister(&hidraw_class); unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); } diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 660995856a04..8b2bd65d70e7 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -383,9 +383,11 @@ static int kernfs_link_sibling(struct kernfs_node *kn) rb_insert_color(&kn->rb, &kn->parent->dir.children); /* successfully added, account subdir number */ + down_write(&kernfs_root(kn)->kernfs_iattr_rwsem); if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs++; kernfs_inc_rev(kn->parent); + up_write(&kernfs_root(kn)->kernfs_iattr_rwsem); return 0; } @@ -408,9 +410,11 @@ static bool kernfs_unlink_sibling(struct kernfs_node *kn) if (RB_EMPTY_NODE(&kn->rb)) return false; + down_write(&kernfs_root(kn)->kernfs_iattr_rwsem); if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs--; kernfs_inc_rev(kn->parent); + up_write(&kernfs_root(kn)->kernfs_iattr_rwsem); rb_erase(&kn->rb, &kn->parent->dir.children); RB_CLEAR_NODE(&kn->rb); diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index d49606accb07..c4bf26142eec 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -16,6 +16,8 @@ #include <linux/namei.h> #include <linux/seq_file.h> #include <linux/exportfs.h> +#include <linux/uuid.h> +#include <linux/statfs.h> #include "kernfs-internal.h" @@ -45,8 +47,15 @@ static int kernfs_sop_show_path(struct seq_file *sf, struct dentry *dentry) return 0; } +static int kernfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + simple_statfs(dentry, buf); + buf->f_fsid = uuid_to_fsid(dentry->d_sb->s_uuid.b); + return 0; +} + const struct super_operations kernfs_sops = { - .statfs = simple_statfs, + .statfs = kernfs_statfs, .drop_inode = generic_delete_inode, .evict_inode = kernfs_evict_inode, @@ -351,6 +360,8 @@ int kernfs_get_tree(struct fs_context *fc) } sb->s_flags |= SB_ACTIVE; + uuid_gen(&sb->s_uuid); + down_write(&root->kernfs_supers_rwsem); list_add(&info->node, &info->root->supers); up_write(&root->kernfs_supers_rwsem); diff --git a/include/linux/dev_printk.h b/include/linux/dev_printk.h index 8904063d4c9f..6bfe70decc9f 100644 --- a/include/linux/dev_printk.h +++ b/include/linux/dev_printk.h @@ -274,4 +274,6 @@ do { \ WARN_ONCE(condition, "%s %s: " format, \ dev_driver_string(dev), dev_name(dev), ## arg) +__printf(3, 4) int dev_err_probe(const struct device *dev, int err, const char *fmt, ...); + #endif /* _DEVICE_PRINTK_H_ */ diff --git a/include/linux/device.h b/include/linux/device.h index 941c0de6151a..56d93a1ffb7b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1249,8 +1249,6 @@ void device_link_remove(void *consumer, struct device *supplier); void device_links_supplier_sync_state_pause(void); void device_links_supplier_sync_state_resume(void); -__printf(3, 4) int dev_err_probe(const struct device *dev, int err, const char *fmt, ...); - /* Create alias, so I can be autoloaded. */ #define MODULE_ALIAS_CHARDEV(major,minor) \ MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor)) diff --git a/include/linux/hid-roccat.h b/include/linux/hid-roccat.h index 3214fb0815fc..753654fff07f 100644 --- a/include/linux/hid-roccat.h +++ b/include/linux/hid-roccat.h @@ -16,7 +16,7 @@ #ifdef __KERNEL__ -int roccat_connect(struct class *klass, struct hid_device *hid, +int roccat_connect(const struct class *klass, struct hid_device *hid, int report_size); void roccat_disconnect(int minor); int roccat_report_event(int minor, u8 const *data); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 73f5c120def8..2a36f3218b51 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -550,6 +550,10 @@ static inline int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { return -ENOSYS; } +static inline __poll_t kernfs_generic_poll(struct kernfs_open_file *of, + struct poll_table_struct *pt) +{ return -ENOSYS; } + static inline void kernfs_notify(struct kernfs_node *kn) { } static inline int kernfs_xattr_get(struct kernfs_node *kn, const char *name, diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c392c811d9ad..c30affcc43b4 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -69,14 +69,16 @@ struct kobject { const struct kobj_type *ktype; struct kernfs_node *sd; /* sysfs directory entry */ struct kref kref; -#ifdef CONFIG_DEBUG_KOBJECT_RELEASE - struct delayed_work release; -#endif + unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; + +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE + struct delayed_work release; +#endif }; __printf(2, 3) int kobject_set_name(struct kobject *kobj, const char *name, ...); diff --git a/lib/kobject.c b/lib/kobject.c index 16d530f9c174..59dbcbdb1c91 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -56,6 +56,14 @@ void kobject_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t *gid) kobj->ktype->get_ownership(kobj, uid, gid); } +static bool kobj_ns_type_is_valid(enum kobj_ns_type type) +{ + if ((type <= KOBJ_NS_TYPE_NONE) || (type >= KOBJ_NS_TYPES)) + return false; + + return true; +} + static int create_dir(struct kobject *kobj) { const struct kobj_type *ktype = get_ktype(kobj); @@ -66,12 +74,10 @@ static int create_dir(struct kobject *kobj) if (error) return error; - if (ktype) { - error = sysfs_create_groups(kobj, ktype->default_groups); - if (error) { - sysfs_remove_dir(kobj); - return error; - } + error = sysfs_create_groups(kobj, ktype->default_groups); + if (error) { + sysfs_remove_dir(kobj); + return error; } /* @@ -86,8 +92,7 @@ static int create_dir(struct kobject *kobj) */ ops = kobj_child_ns_ops(kobj); if (ops) { - BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE); - BUG_ON(ops->type >= KOBJ_NS_TYPES); + BUG_ON(!kobj_ns_type_is_valid(ops->type)); BUG_ON(!kobj_ns_type_registered(ops->type)); sysfs_enable_ns(kobj->sd); @@ -584,8 +589,7 @@ static void __kobject_del(struct kobject *kobj) sd = kobj->sd; ktype = get_ktype(kobj); - if (ktype) - sysfs_remove_groups(kobj, ktype->default_groups); + sysfs_remove_groups(kobj, ktype->default_groups); /* send "remove" if the caller did not do it but sent "add" */ if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) { @@ -662,10 +666,6 @@ static void kobject_cleanup(struct kobject *kobj) pr_debug("'%s' (%p): %s, parent %p\n", kobject_name(kobj), kobj, __func__, kobj->parent); - if (t && !t->release) - pr_debug("'%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n", - kobject_name(kobj), kobj); - /* remove from sysfs if the caller did not do it */ if (kobj->state_in_sysfs) { pr_debug("'%s' (%p): auto cleanup kobject_del\n", @@ -676,10 +676,13 @@ static void kobject_cleanup(struct kobject *kobj) parent = NULL; } - if (t && t->release) { + if (t->release) { pr_debug("'%s' (%p): calling ktype release\n", kobject_name(kobj), kobj); t->release(kobj); + } else { + pr_debug("'%s' (%p): does not have a release() function, it is broken and must be fixed. See Documentation/core-api/kobject.rst.\n", + kobject_name(kobj), kobj); } /* free name if we allocated it */ @@ -854,6 +857,11 @@ int kset_register(struct kset *k) if (!k) return -EINVAL; + if (!k->kobj.ktype) { + pr_err("must have a ktype to be initialized properly!\n"); + return -EINVAL; + } + kset_init(k); err = kobject_add_internal(&k->kobj); if (err) { @@ -1017,11 +1025,7 @@ int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) spin_lock(&kobj_ns_type_lock); error = -EINVAL; - if (type >= KOBJ_NS_TYPES) - goto out; - - error = -EINVAL; - if (type <= KOBJ_NS_TYPE_NONE) + if (!kobj_ns_type_is_valid(type)) goto out; error = -EBUSY; @@ -1041,7 +1045,7 @@ int kobj_ns_type_registered(enum kobj_ns_type type) int registered = 0; spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) + if (kobj_ns_type_is_valid(type)) registered = kobj_ns_ops_tbl[type] != NULL; spin_unlock(&kobj_ns_type_lock); @@ -1052,7 +1056,7 @@ const struct kobj_ns_type_operations *kobj_child_ns_ops(const struct kobject *pa { const struct kobj_ns_type_operations *ops = NULL; - if (parent && parent->ktype && parent->ktype->child_ns_type) + if (parent && parent->ktype->child_ns_type) ops = parent->ktype->child_ns_type(parent); return ops; @@ -1068,8 +1072,7 @@ bool kobj_ns_current_may_mount(enum kobj_ns_type type) bool may_mount = true; spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && - kobj_ns_ops_tbl[type]) + if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type]) may_mount = kobj_ns_ops_tbl[type]->current_may_mount(); spin_unlock(&kobj_ns_type_lock); @@ -1081,8 +1084,7 @@ void *kobj_ns_grab_current(enum kobj_ns_type type) void *ns = NULL; spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && - kobj_ns_ops_tbl[type]) + if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type]) ns = kobj_ns_ops_tbl[type]->grab_current_ns(); spin_unlock(&kobj_ns_type_lock); @@ -1095,8 +1097,7 @@ const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) const void *ns = NULL; spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && - kobj_ns_ops_tbl[type]) + if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type]) ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); spin_unlock(&kobj_ns_type_lock); @@ -1108,8 +1109,7 @@ const void *kobj_ns_initial(enum kobj_ns_type type) const void *ns = NULL; spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && - kobj_ns_ops_tbl[type]) + if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type]) ns = kobj_ns_ops_tbl[type]->initial_ns(); spin_unlock(&kobj_ns_type_lock); @@ -1119,7 +1119,7 @@ const void *kobj_ns_initial(enum kobj_ns_type type) void kobj_ns_drop(enum kobj_ns_type type, void *ns) { spin_lock(&kobj_ns_type_lock); - if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + if (kobj_ns_type_is_valid(type) && kobj_ns_ops_tbl[type] && kobj_ns_ops_tbl[type]->drop_ns) kobj_ns_ops_tbl[type]->drop_ns(ns); spin_unlock(&kobj_ns_type_lock); |