Driver core changes for 6.6-rc1

Here is a small set of driver core updates and additions for 6.6-rc1.
 
 Included in here are:
   - stable kernel documentation updates
   - class structure const work from Ivan on various subsystems
   - kernfs tweaks
   - driver core tests!
   - kobject sanity cleanups
   - kobject structure reordering to save space
   - driver core error code handling fixups
   - other minor driver core cleanups
 
 All of these have been in linux-next for a while with no reported
 problems.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZPH77Q8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylZMACePk8SitfaJc6FfFf5I7YK7Nq0V8MAn0nUjgsR
 i8NcNpu/Yv4HGrDgTdh/
 =PJbk
 -----END PGP SIGNATURE-----

Merge tag 'driver-core-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core updates from Greg KH:
 "Here is a small set of driver core updates and additions for 6.6-rc1.

  Included in here are:

   - stable kernel documentation updates

   - class structure const work from Ivan on various subsystems

   - kernfs tweaks

   - driver core tests!

   - kobject sanity cleanups

   - kobject structure reordering to save space

   - driver core error code handling fixups

   - other minor driver core cleanups

  All of these have been in linux-next for a while with no reported
  problems"

* tag 'driver-core-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (32 commits)
  driver core: Call in reversed order in device_platform_notify_remove()
  driver core: Return proper error code when dev_set_name() fails
  kobject: Remove redundant checks for whether ktype is NULL
  kobject: Add sanity check for kset->kobj.ktype in kset_register()
  drivers: base: test: Add missing MODULE_* macros to root device tests
  drivers: base: test: Add missing MODULE_* macros for platform devices tests
  drivers: base: Free devm resources when unregistering a device
  drivers: base: Add basic devm tests for platform devices
  drivers: base: Add basic devm tests for root devices
  kernfs: fix missing kernfs_iattr_rwsem locking
  docs: stable-kernel-rules: mention that regressions must be prevented
  docs: stable-kernel-rules: fine-tune various details
  docs: stable-kernel-rules: make the examples for option 1 a proper list
  docs: stable-kernel-rules: move text around to improve flow
  docs: stable-kernel-rules: improve structure by changing headlines
  base/node: Remove duplicated include
  kernfs: attach uuid for every kernfs and report it in fsid
  kernfs: add stub helper for kernfs_generic_poll()
  x86/resctrl: make pseudo_lock_class a static const structure
  x86/MSR: make msr_class a static const structure
  ...
This commit is contained in:
Linus Torvalds 2023-09-01 09:43:18 -07:00
commit 28a4f91f5f
37 changed files with 749 additions and 298 deletions

View File

@ -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,32 +40,106 @@ 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.
To sent additional instructions to the stable team, use a shell-style inline
comment:
* To specify any additional patch prerequisites for cherry picking use the
following format in the sign-off area:
.. code-block:: none
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>
The tag sequence has the meaning of:
.. code-block:: none
git cherry-pick a1f84a3
git cherry-pick 1b9508f
git cherry-pick fd21073
git cherry-pick <this commit>
* For patches that may have kernel version prerequisites specify them using
the following format in the sign-off area:
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x
The tag has the meaning of:
.. code-block:: none
git cherry-pick <this commit>
For each "-stable" tree starting with the specified version.
Note, such tagging is unnecessary if the stable team can derive the
appropriate versions from Fixes: tags.
* To delay pick up of patches, use the following format:
.. code-block:: none
Cc: <stable@vger.kernel.org> # after 4 weeks in mainline
* For any other requests, just add a note to the stable tag. This for example
can be used to point out known problems:
.. code-block:: none
Cc: <stable@vger.kernel.org> # see patch description, needs adjustments for <= 6.3
.. _option_2:
Option 2
********
After the patch has been merged to Linus' tree, send an email to
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 version you wish it to
why you think it should be applied, and what kernel versions you wish it to
be applied to.
.. _option_3:
@ -75,23 +148,9 @@ Option 3
********
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.
: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).
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.
The upstream commit ID must be specified with a separate line above the commit
text, like this:
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
@ -103,49 +162,20 @@ or alternatively:
[ Upstream commit <sha1> ]
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:
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.
.. code-block:: none
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>
Following the submission
------------------------
The tag sequence has the meaning of:
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.
.. code-block:: none
git cherry-pick a1f84a3
git cherry-pick 1b9508f
git cherry-pick fd21073
git cherry-pick <this commit>
Also, some patches may have kernel version prerequisites. This can be
specified in the following format in the sign-off area:
.. code-block:: none
Cc: <stable@vger.kernel.org> # 3.3.x
The tag has the meaning of:
.. code-block:: none
git cherry-pick <this commit>
For each "-stable" tree starting with the specified version.
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.
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
-----

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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",

View File

@ -0,0 +1,2 @@
CONFIG_KUNIT=y
CONFIG_DM_KUNIT_TEST=y

View File

@ -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

View File

@ -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)

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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_ */

View File

@ -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))

View File

@ -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);

View File

@ -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,

View File

@ -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, ...);

View File

@ -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);