diff options
58 files changed, 1217 insertions, 755 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 69c65da16dff..61f5676a7429 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -666,3 +666,18 @@ Description: Preferred MTE tag checking mode ================ ============================================== See also: Documentation/arm64/memory-tagging-extension.rst + +What: /sys/devices/system/cpu/nohz_full +Date: Apr 2015 +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> +Description: + (RO) the list of CPUs that are in nohz_full mode. + These CPUs are set by boot parameter "nohz_full=". + +What: /sys/devices/system/cpu/isolated +Date: Apr 2015 +Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> +Description: + (RO) the list of CPUs that are isolated and don't + participate in load balancing. These CPUs are set by + boot parameter "isolcpus=". diff --git a/Documentation/admin-guide/cputopology.rst b/Documentation/admin-guide/cputopology.rst index 6b62e182baf4..d29cacc9b3c3 100644 --- a/Documentation/admin-guide/cputopology.rst +++ b/Documentation/admin-guide/cputopology.rst @@ -8,11 +8,9 @@ to /proc/cpuinfo output of some architectures. They reside in Documentation/ABI/stable/sysfs-devices-system-cpu. Architecture-neutral, drivers/base/topology.c, exports these attributes. -However, the book and drawer related sysfs files will only be created if -CONFIG_SCHED_BOOK and CONFIG_SCHED_DRAWER are selected, respectively. - -CONFIG_SCHED_BOOK and CONFIG_SCHED_DRAWER are currently only used on s390, -where they reflect the cpu and cache hierarchy. +However the die, cluster, book, and drawer hierarchy related sysfs files will +only be created if an architecture provides the related macros as described +below. For an architecture to support this feature, it must define some of these macros in include/asm-XXX/topology.h:: @@ -43,15 +41,14 @@ not defined by include/asm-XXX/topology.h: 2) topology_die_id: -1 3) topology_cluster_id: -1 4) topology_core_id: 0 -5) topology_sibling_cpumask: just the given CPU -6) topology_core_cpumask: just the given CPU -7) topology_cluster_cpumask: just the given CPU -8) topology_die_cpumask: just the given CPU - -For architectures that don't support books (CONFIG_SCHED_BOOK) there are no -default definitions for topology_book_id() and topology_book_cpumask(). -For architectures that don't support drawers (CONFIG_SCHED_DRAWER) there are -no default definitions for topology_drawer_id() and topology_drawer_cpumask(). +5) topology_book_id: -1 +6) topology_drawer_id: -1 +7) topology_sibling_cpumask: just the given CPU +8) topology_core_cpumask: just the given CPU +9) topology_cluster_cpumask: just the given CPU +10) topology_die_cpumask: just the given CPU +11) topology_book_cpumask: just the given CPU +12) topology_drawer_cpumask: just the given CPU Additionally, CPU topology information is provided under /sys/devices/system/cpu and includes these files. The internal diff --git a/Documentation/core-api/kobject.rst b/Documentation/core-api/kobject.rst index 2739f8b72575..7310247310a0 100644 --- a/Documentation/core-api/kobject.rst +++ b/Documentation/core-api/kobject.rst @@ -118,7 +118,7 @@ Initialization of kobjects Code which creates a kobject must, of course, initialize that object. Some of the internal fields are setup with a (mandatory) call to kobject_init():: - void kobject_init(struct kobject *kobj, struct kobj_type *ktype); + void kobject_init(struct kobject *kobj, const struct kobj_type *ktype); The ktype is required for a kobject to be created properly, as every kobject must have an associated kobj_type. After calling kobject_init(), to @@ -156,7 +156,7 @@ kobject_name():: There is a helper function to both initialize and add the kobject to the kernel at the same time, called surprisingly enough kobject_init_and_add():: - int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, + int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); The arguments are the same as the individual kobject_init() and @@ -299,7 +299,6 @@ kobj_type:: struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; - struct attribute **default_attrs; const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); @@ -313,10 +312,10 @@ call kobject_init() or kobject_init_and_add(). The release field in struct kobj_type is, of course, a pointer to the release() method for this type of kobject. The other two fields (sysfs_ops -and default_attrs) control how objects of this type are represented in +and default_groups) control how objects of this type are represented in sysfs; they are beyond the scope of this document. -The default_attrs pointer is a list of default attributes that will be +The default_groups pointer is a list of default attributes that will be automatically created for any kobject that is registered with this ktype. @@ -373,10 +372,9 @@ If a kset wishes to control the uevent operations of the kobjects associated with it, it can use the struct kset_uevent_ops to handle it:: struct kset_uevent_ops { - int (* const filter)(struct kset *kset, struct kobject *kobj); - const char *(* const name)(struct kset *kset, struct kobject *kobj); - int (* const uevent)(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env); + int (* const filter)(struct kobject *kobj); + const char *(* const name)(struct kobject *kobj); + int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); }; diff --git a/Documentation/driver-api/auxiliary_bus.rst b/Documentation/driver-api/auxiliary_bus.rst index ef902daf0d68..cec84908fbc0 100644 --- a/Documentation/driver-api/auxiliary_bus.rst +++ b/Documentation/driver-api/auxiliary_bus.rst @@ -6,231 +6,45 @@ Auxiliary Bus ============= -In some subsystems, the functionality of the core device (PCI/ACPI/other) is -too complex for a single device to be managed by a monolithic driver -(e.g. Sound Open Firmware), multiple devices might implement a common -intersection of functionality (e.g. NICs + RDMA), or a driver may want to -export an interface for another subsystem to drive (e.g. SIOV Physical Function -export Virtual Function management). A split of the functionality into child- -devices representing sub-domains of functionality makes it possible to -compartmentalize, layer, and distribute domain-specific concerns via a Linux -device-driver model. - -An example for this kind of requirement is the audio subsystem where a single -IP is handling multiple entities such as HDMI, Soundwire, local devices such as -mics/speakers etc. The split for the core's functionality can be arbitrary or -be defined by the DSP firmware topology and include hooks for test/debug. This -allows for the audio core device to be minimal and focused on hardware-specific -control and communication. - -Each auxiliary_device represents a part of its parent functionality. The -generic behavior can be extended and specialized as needed by encapsulating an -auxiliary_device within other domain-specific structures and the use of .ops -callbacks. Devices on the auxiliary bus do not share any structures and the use -of a communication channel with the parent is domain-specific. - -Note that ops are intended as a way to augment instance behavior within a class -of auxiliary devices, it is not the mechanism for exporting common -infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey -infrastructure from the parent module to the auxiliary module(s). - +.. kernel-doc:: drivers/base/auxiliary.c + :doc: PURPOSE When Should the Auxiliary Bus Be Used ===================================== -The auxiliary bus is to be used when a driver and one or more kernel modules, -who share a common header file with the driver, need a mechanism to connect and -provide access to a shared object allocated by the auxiliary_device's -registering driver. The registering driver for the auxiliary_device(s) and the -kernel module(s) registering auxiliary_drivers can be from the same subsystem, -or from multiple subsystems. - -The emphasis here is on a common generic interface that keeps subsystem -customization out of the bus infrastructure. - -One example is a PCI network device that is RDMA-capable and exports a child -device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI -driver allocates and registers an auxiliary_device for each physical -function on the NIC. The RDMA driver registers an auxiliary_driver that claims -each of these auxiliary_devices. This conveys data/ops published by the parent -PCI device/driver to the RDMA auxiliary_driver. - -Another use case is for the PCI device to be split out into multiple sub -functions. For each sub function an auxiliary_device is created. A PCI sub -function driver binds to such devices that creates its own one or more class -devices. A PCI sub function auxiliary device is likely to be contained in a -struct with additional attributes such as user defined sub function number and -optional attributes such as resources and a link to the parent device. These -attributes could be used by systemd/udev; and hence should be initialized -before a driver binds to an auxiliary_device. - -A key requirement for utilizing the auxiliary bus is that there is no -dependency on a physical bus, device, register accesses or regmap support. -These individual devices split from the core cannot live on the platform bus as -they are not physical devices that are controlled by DT/ACPI. The same -argument applies for not using MFD in this scenario as MFD relies on individual -function devices being physical devices. - -Auxiliary Device -================ - -An auxiliary_device represents a part of its parent device's functionality. It -is given a name that, combined with the registering drivers KBUILD_MODNAME, -creates a match_name that is used for driver binding, and an id that combined -with the match_name provide a unique name to register with the bus subsystem. - -Registering an auxiliary_device is a two-step process. First call -auxiliary_device_init(), which checks several aspects of the auxiliary_device -struct and performs a device_initialize(). After this step completes, any -error state must have a call to auxiliary_device_uninit() in its resolution path. -The second step in registering an auxiliary_device is to perform a call to -auxiliary_device_add(), which sets the name of the device and add the device to -the bus. - -Unregistering an auxiliary_device is also a two-step process to mirror the -register process. First call auxiliary_device_delete(), then call -auxiliary_device_uninit(). - -.. code-block:: c - - struct auxiliary_device { - struct device dev; - const char *name; - u32 id; - }; - -If two auxiliary_devices both with a match_name "mod.foo" are registered onto -the bus, they must have unique id values (e.g. "x" and "y") so that the -registered devices names are "mod.foo.x" and "mod.foo.y". If match_name + id -are not unique, then the device_add fails and generates an error message. - -The auxiliary_device.dev.type.release or auxiliary_device.dev.release must be -populated with a non-NULL pointer to successfully register the auxiliary_device. - -The auxiliary_device.dev.parent must also be populated. +.. kernel-doc:: drivers/base/auxiliary.c + :doc: USAGE + + +Auxiliary Device Creation +========================= + +.. kernel-doc:: include/linux/auxiliary_bus.h + :identifiers: auxiliary_device + +.. kernel-doc:: drivers/base/auxiliary.c + :identifiers: auxiliary_device_init __auxiliary_device_add + auxiliary_find_device Auxiliary Device Memory Model and Lifespan ------------------------------------------ -The registering driver is the entity that allocates memory for the -auxiliary_device and register it on the auxiliary bus. It is important to note -that, as opposed to the platform bus, the registering driver is wholly -responsible for the management for the memory used for the driver object. - -A parent object, defined in the shared header file, contains the -auxiliary_device. It also contains a pointer to the shared object(s), which -also is defined in the shared header. Both the parent object and the shared -object(s) are allocated by the registering driver. This layout allows the -auxiliary_driver's registering module to perform a container_of() call to go -from the pointer to the auxiliary_device, that is passed during the call to the -auxiliary_driver's probe function, up to the parent object, and then have -access to the shared object(s). - -The memory for the auxiliary_device is freed only in its release() callback -flow as defined by its registering driver. - -The memory for the shared object(s) must have a lifespan equal to, or greater -than, the lifespan of the memory for the auxiliary_device. The auxiliary_driver -should only consider that this shared object is valid as long as the -auxiliary_device is still registered on the auxiliary bus. It is up to the -registering driver to manage (e.g. free or keep available) the memory for the -shared object beyond the life of the auxiliary_device. - -The registering driver must unregister all auxiliary devices before its own -driver.remove() is completed. +.. kernel-doc:: include/linux/auxiliary_bus.h + :doc: DEVICE_LIFESPAN + Auxiliary Drivers ================= -Auxiliary drivers follow the standard driver model convention, where -discovery/enumeration is handled by the core, and drivers -provide probe() and remove() methods. They support power management -and shutdown notifications using the standard conventions. - -.. code-block:: c +.. kernel-doc:: include/linux/auxiliary_bus.h + :identifiers: auxiliary_driver module_auxiliary_driver - struct auxiliary_driver { - int (*probe)(struct auxiliary_device *, - const struct auxiliary_device_id *id); - void (*remove)(struct auxiliary_device *); - void (*shutdown)(struct auxiliary_device *); - int (*suspend)(struct auxiliary_device *, pm_message_t); - int (*resume)(struct auxiliary_device *); - struct device_driver driver; - const struct auxiliary_device_id *id_table; - }; - -Auxiliary drivers register themselves with the bus by calling -auxiliary_driver_register(). The id_table contains the match_names of auxiliary -devices that a driver can bind with. +.. kernel-doc:: drivers/base/auxiliary.c + :identifiers: __auxiliary_driver_register auxiliary_driver_unregister Example Usage ============= -Auxiliary devices are created and registered by a subsystem-level core device -that needs to break up its functionality into smaller fragments. One way to -extend the scope of an auxiliary_device is to encapsulate it within a domain- -pecific structure defined by the parent device. This structure contains the -auxiliary_device and any associated shared data/callbacks needed to establish -the connection with the parent. - -An example is: - -.. code-block:: c - - struct foo { - struct auxiliary_device auxdev; - void (*connect)(struct auxiliary_device *auxdev); - void (*disconnect)(struct auxiliary_device *auxdev); - void *data; - }; - -The parent device then registers the auxiliary_device by calling -auxiliary_device_init(), and then auxiliary_device_add(), with the pointer to -the auxdev member of the above structure. The parent provides a name for the -auxiliary_device that, combined with the parent's KBUILD_MODNAME, creates a -match_name that is be used for matching and binding with a driver. - -Whenever an auxiliary_driver is registered, based on the match_name, the -auxiliary_driver's probe() is invoked for the matching devices. The -auxiliary_driver can also be encapsulated inside custom drivers that make the -core device's functionality extensible by adding additional domain-specific ops -as follows: - -.. code-block:: c - - struct my_ops { - void (*send)(struct auxiliary_device *auxdev); - void (*receive)(struct auxiliary_device *auxdev); - }; - - - struct my_driver { - struct auxiliary_driver auxiliary_drv; - const struct my_ops ops; - }; - -An example of this type of usage is: - -.. code-block:: c - - const struct auxiliary_device_id my_auxiliary_id_table[] = { - { .name = "foo_mod.foo_dev" }, - { }, - }; - - const struct my_ops my_custom_ops = { - .send = my_tx, - .receive = my_rx, - }; - - const struct my_driver my_drv = { - .auxiliary_drv = { - .name = "myauxiliarydrv", - .id_table = my_auxiliary_id_table, - .probe = my_probe, - .remove = my_remove, - .shutdown = my_shutdown, - }, - .ops = my_custom_ops, - }; +.. kernel-doc:: drivers/base/auxiliary.c + :doc: EXAMPLE + diff --git a/Documentation/translations/zh_CN/core-api/kobject.rst b/Documentation/translations/zh_CN/core-api/kobject.rst index b7c37794cc7f..0747b472fdea 100644 --- a/Documentation/translations/zh_CN/core-api/kobject.rst +++ b/Documentation/translations/zh_CN/core-api/kobject.rst @@ -258,7 +258,6 @@ kobject_put()以避免错误的发生是一个很好的做法。 struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; - struct attribute **default_attrs; const struct attribute_group **default_groups; const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); @@ -271,10 +270,10 @@ kobject_init()或kobject_init_and_add()时必须指定一个指向该结构的 指针。 当然,kobj_type结构中的release字段是指向这种类型的kobject的release() -方法的一个指针。另外两个字段(sysfs_ops 和 default_attrs)控制这种 +方法的一个指针。另外两个字段(sysfs_ops 和 default_groups)控制这种 类型的对象如何在 sysfs 中被表示;它们超出了本文的范围。 -default_attrs 指针是一个默认属性的列表,它将为任何用这个 ktype 注册 +default_groups 指针是一个默认属性的列表,它将为任何用这个 ktype 注册 的 kobject 自动创建。 @@ -325,10 +324,9 @@ ksets 结构体kset_uevent_ops来处理它:: struct kset_uevent_ops { - int (* const filter)(struct kset *kset, struct kobject *kobj); - const char *(* const name)(struct kset *kset, struct kobject *kobj); - int (* const uevent)(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env); + int (* const filter)(struct kobject *kobj); + const char *(* const name)(struct kobject *kobj); + int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); }; diff --git a/MAINTAINERS b/MAINTAINERS index 246deaad6177..ededb9e667d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9819,10 +9819,9 @@ S: Maintained F: drivers/mfd/intel_soc_pmic* F: include/linux/mfd/intel_soc_pmic* -INTEL PMT DRIVER -M: "David E. Box" <david.e.box@linux.intel.com> -S: Maintained -F: drivers/mfd/intel_pmt.c +INTEL PMT DRIVERS +M: David E. Box <david.e.box@linux.intel.com> +S: Supported F: drivers/platform/x86/intel/pmt/ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT @@ -9889,6 +9888,11 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/intel/uncore-frequency.c +INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER +M: David E. Box <david.e.box@linux.intel.com> +S: Supported +F: drivers/platform/x86/intel/vsec.* + INTEL VIRTUAL BUTTON DRIVER M: AceLan Kao <acelan.kao@canonical.com> L: platform-driver-x86@vger.kernel.org diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index d432164b23b7..a76b94e41e91 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -324,6 +324,7 @@ static struct attribute *sq_sysfs_attrs[] = { &mapping_attr.attr, NULL, }; +ATTRIBUTE_GROUPS(sq_sysfs); static const struct sysfs_ops sq_sysfs_ops = { .show = sq_sysfs_show, @@ -332,7 +333,7 @@ static const struct sysfs_ops sq_sysfs_ops = { static struct kobj_type ktype_percpu_entry = { .sysfs_ops = &sq_sysfs_ops, - .default_attrs = sq_sysfs_attrs, + .default_groups = sq_sysfs_groups, }; static int sq_dev_add(struct device *dev, struct subsys_interface *sif) diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index ffcbe2bc460e..6f04b831a5c0 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -62,6 +62,17 @@ config DEVTMPFS_MOUNT rescue mode with init=/bin/sh, even when the /dev directory on the rootfs is completely empty. +config DEVTMPFS_SAFE + bool "Use nosuid,noexec mount options on devtmpfs" + depends on DEVTMPFS + help + This instructs the kernel to include the MS_NOEXEC and MS_NOSUID mount + flags when mounting devtmpfs. + + Notice: If enabled, things like /dev/mem cannot be mmapped + with the PROT_EXEC flag. This can break, for example, non-KMS + video drivers. + config STANDALONE bool "Select only drivers that don't need compile-time external firmware" default y diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index 9230c9472bb0..8c5e65930617 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -17,6 +17,147 @@ #include <linux/auxiliary_bus.h> #include "base.h" +/** + * DOC: PURPOSE + * + * In some subsystems, the functionality of the core device (PCI/ACPI/other) is + * too complex for a single device to be managed by a monolithic driver (e.g. + * Sound Open Firmware), multiple devices might implement a common intersection + * of functionality (e.g. NICs + RDMA), or a driver may want to export an + * interface for another subsystem to drive (e.g. SIOV Physical Function export + * Virtual Function management). A split of the functionality into child- + * devices representing sub-domains of functionality makes it possible to + * compartmentalize, layer, and distribute domain-specific concerns via a Linux + * device-driver model. + * + * An example for this kind of requirement is the audio subsystem where a + * single IP is handling multiple entities such as HDMI, Soundwire, local + * devices such as mics/speakers etc. The split for the core's functionality + * can be arbitrary or be defined by the DSP firmware topology and include + * hooks for test/debug. This allows for the audio core device to be minimal + * and focused on hardware-specific control and communication. + * + * Each auxiliary_device represents a part of its parent functionality. The + * generic behavior can be extended and specialized as needed by encapsulating + * an auxiliary_device within other domain-specific structures and the use of + * .ops callbacks. Devices on the auxiliary bus do not share any structures and + * the use of a communication channel with the parent is domain-specific. + * + * Note that ops are intended as a way to augment instance behavior within a + * class of auxiliary devices, it is not the mechanism for exporting common + * infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey + * infrastructure from the parent module to the auxiliary module(s). + */ + +/** + * DOC: USAGE + * + * The auxiliary bus is to be used when a driver and one or more kernel + * modules, who share a common header file with the driver, need a mechanism to + * connect and provide access to a shared object allocated by the + * auxiliary_device's registering driver. The registering driver for the + * auxiliary_device(s) and the kernel module(s) registering auxiliary_drivers + * can be from the same subsystem, or from multiple subsystems. + * + * The emphasis here is on a common generic interface that keeps subsystem + * customization out of the bus infrastructure. + * + * One example is a PCI network device that is RDMA-capable and exports a child + * device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI + * driver allocates and registers an auxiliary_device for each physical + * function on the NIC. The RDMA driver registers an auxiliary_driver that + * claims each of these auxiliary_devices. This conveys data/ops published by + * the parent PCI device/driver to the RDMA auxiliary_driver. + * + * Another use case is for the PCI device to be split out into multiple sub + * functions. For each sub function an auxiliary_device is created. A PCI sub + * function driver binds to such devices that creates its own one or more class + * devices. A PCI sub function auxiliary device is likely to be contained in a + * struct with additional attributes such as user defined sub function number + * and optional attributes such as resources and a link to the parent device. + * These attributes could be used by systemd/udev; and hence should be + * initialized before a driver binds to an auxiliary_device. + * + * A key requirement for utilizing the auxiliary bus is that there is no + * dependency on a physical bus, device, register accesses or regmap support. + * These individual devices split from the core cannot live on the platform bus + * as they are not physical devices that are controlled by DT/ACPI. The same + * argument applies for not using MFD in this scenario as MFD relies on + * individual function devices being physical devices. + */ + +/** + * DOC: EXAMPLE + * + * Auxiliary devices are created and registered by a subsystem-level core + * device that needs to break up its functionality into smaller fragments. One + * way to extend the scope of an auxiliary_device is to encapsulate it within a + * domain- pecific structure defined by the parent device. This structure + * contains the auxiliary_device and any associated shared data/callbacks + * needed to establish the connection with the parent. + * + * An example is: + * + * .. code-block:: c + * + * struct foo { + * struct auxiliary_device auxdev; + * void (*connect)(struct auxiliary_device *auxdev); + * void (*disconnect)(struct auxiliary_device *auxdev); + * void *data; + * }; + * + * The parent device then registers the auxiliary_device by calling + * auxiliary_device_init(), and then auxiliary_device_add(), with the pointer + * to the auxdev member of the above structure. The parent provides a name for + * the auxiliary_device that, combined with the parent's KBUILD_MODNAME, + * creates a match_name that is be used for matching and binding with a driver. + * + * Whenever an auxiliary_driver is registered, based on the match_name, the + * auxiliary_driver's probe() is invoked for the matching devices. The + * auxiliary_driver can also be encapsulated inside custom drivers that make + * the core device's functionality extensible by adding additional + * domain-specific ops as follows: + * + * .. code-block:: c + * + * struct my_ops { + * void (*send)(struct auxiliary_device *auxdev); + * void (*receive)(struct auxiliary_device *auxdev); + * }; + * + * + * struct my_driver { + * struct auxiliary_driver auxiliary_drv; + * const struct my_ops ops; + * }; + * + * An example of this type of usage is: + * + * .. code-block:: c + * + * const struct auxiliary_device_id my_auxiliary_id_table[] = { + * { .name = "foo_mod.foo_dev" }, + * { }, + * }; + * + * const struct my_ops my_custom_ops = { + * .send = my_tx, + * .receive = my_rx, + * }; + * + * const struct my_driver my_drv = { + * .auxiliary_drv = { + * .name = "myauxiliarydrv", + * .id_table = my_auxiliary_id_table, + * .probe = my_probe, + * .remove = my_remove, + * .shutdown = my_shutdown, + * }, + * .ops = my_custom_ops, + * }; + */ + static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id, const struct auxiliary_device *auxdev) { @@ -117,7 +258,7 @@ static struct bus_type auxiliary_bus_type = { * auxiliary_device_init - check auxiliary_device and initialize * @auxdev: auxiliary device struct * - * This is the first step in the two-step process to register an + * This is the second step in the three-step process to register an * auxiliary_device. * * When this function returns an error code, then the device_initialize will @@ -155,7 +296,7 @@ EXPORT_SYMBOL_GPL(auxiliary_device_init); * @auxdev: auxiliary bus device to add to the bus * @modname: name of the parent device's driver module * - * This is the second step in the two-step process to register an + * This is the third step in the three-step process to register an * auxiliary_device. * * This function must be called after a successful call to @@ -202,6 +343,8 @@ EXPORT_SYMBOL_GPL(__auxiliary_device_add); * This function returns a reference to a device that is 'found' * for later use, as determined by the @match callback. * + * The reference returned should be released with put_device(). + * * The callback should return 0 if the device doesn't match and non-zero * if it does. If the callback returns non-zero, this function will * return to the caller and not iterate over any more devices. @@ -225,6 +368,11 @@ EXPORT_SYMBOL_GPL(auxiliary_find_device); * @auxdrv: auxiliary_driver structure * @owner: owning module/driver * @modname: KBUILD_MODNAME for parent driver + * + * The expectation is that users will call the "auxiliary_driver_register" + * macro so that the caller's KBUILD_MODNAME is automatically inserted for the + * modname parameter. Only if a user requires a custom name would this version + * be called directly. */ int __auxiliary_driver_register(struct auxiliary_driver *auxdrv, struct module *owner, const char *modname) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index bdc98c5713d5..97936ec49bde 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -163,9 +163,9 @@ static struct kobj_type bus_ktype = { .release = bus_release, }; -static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) +static int bus_uevent_filter(struct kobject *kobj) { - struct kobj_type *ktype = get_ktype(kobj); + const struct kobj_type *ktype = get_ktype(kobj); if (ktype == &bus_ktype) return 1; diff --git a/drivers/base/core.c b/drivers/base/core.c index 63e769057487..8b17cb80420c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2260,9 +2260,9 @@ static struct kobj_type device_ktype = { }; -static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) +static int dev_uevent_filter(struct kobject *kobj) { - struct kobj_type *ktype = get_ktype(kobj); + const struct kobj_type *ktype = get_ktype(kobj); if (ktype == &device_ktype) { struct device *dev = kobj_to_dev(kobj); @@ -2274,7 +2274,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) return 0; } -static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) +static const char *dev_uevent_name(struct kobject *kobj) { struct device *dev = kobj_to_dev(kobj); @@ -2285,8 +2285,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj) return NULL; } -static int dev_uevent(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env) +static int dev_uevent(struct kobject *kobj, struct kobj_uevent_env *env) { struct device *dev = kobj_to_dev(kobj); int retval = 0; @@ -2381,7 +2380,7 @@ static ssize_t uevent_show(struct device *dev, struct device_attribute *attr, /* respect filter */ if (kset->uevent_ops && kset->uevent_ops->filter) - if (!kset->uevent_ops->filter(kset, &dev->kobj)) + if (!kset->uevent_ops->filter(&dev->kobj)) goto out; env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL); @@ -2389,7 +2388,7 @@ static ssize_t uevent_show(struct device *dev, struct device_attribute *attr, return -ENOMEM; /* let the kset specific function add its keys */ - retval = kset->uevent_ops->uevent(kset, &dev->kobj, env); + retval = kset->uevent_ops->uevent(&dev->kobj, env); if (retval) goto out; @@ -3028,6 +3027,23 @@ static inline struct kobject *get_glue_dir(struct device *dev) return dev->kobj.parent; } +/** + * kobject_has_children - Returns whether a kobject has children. + * @kobj: the object to test + * + * This will return whether a kobject has other kobjects as children. + * + * It does NOT account for the presence of attribute files, only sub + * directories. It also assumes there is no concurrent addition or + * removal of such children, and thus relies on external locking. + */ +static inline bool kobject_has_children(struct kobject *kobj) +{ + WARN_ON_ONCE(kref_read(&kobj->kref) == 0); + + return kobj->sd && kobj->sd->dir.subdirs; +} + /* * make sure cleaning up dir as the last step, we need to make * sure .release handler of kobject is run with holding the diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 68ea1f949daa..9eaaff2f556c 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -577,14 +577,14 @@ re_probe: if (dev->bus->dma_configure) { ret = dev->bus->dma_configure(dev); if (ret) - goto probe_failed; + goto pinctrl_bind_failed; } ret = driver_sysfs_add(dev); if (ret) { pr_err("%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); - goto probe_failed; + goto sysfs_failed; } if (dev->pm_domain && dev->pm_domain->activate) { @@ -657,6 +657,8 @@ dev_groups_failed: else if (drv->remove) drv->remove(dev); probe_failed: + driver_sysfs_remove(dev); +sysfs_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); @@ -666,7 +668,6 @@ pinctrl_bind_failed: arch_teardown_dma_ops(dev); kfree(dev->dma_range_map); dev->dma_range_map = NULL; - driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (dev->pm_domain && dev->pm_domain->dismiss) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 8be352ab4ddb..1e2c2d3882e2 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -29,6 +29,12 @@ #include <uapi/linux/mount.h> #include "base.h" +#ifdef CONFIG_DEVTMPFS_SAFE +#define DEVTMPFS_MFLAGS (MS_SILENT | MS_NOEXEC | MS_NOSUID) +#else +#define DEVTMPFS_MFLAGS (MS_SILENT) +#endif + static struct task_struct *thread; static int __initdata mount_dev = IS_ENABLED(CONFIG_DEVTMPFS_MOUNT); @@ -363,7 +369,7 @@ int __init devtmpfs_mount(void) if (!thread) return 0; - err = init_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "dev", "devtmpfs", DEVTMPFS_MFLAGS, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else @@ -412,7 +418,7 @@ static noinline int __init devtmpfs_setup(void *p) err = ksys_unshare(CLONE_NEWNS); if (err) goto out; - err = init_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "/", "devtmpfs", DEVTMPFS_MFLAGS, NULL); if (err) goto out; init_chdir("/.."); /* will traverse into overmounted root */ diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 598acf93a360..6cb04ac48bf0 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -258,8 +258,9 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) int ret; ret = platform_get_irq_optional(dev, num); - if (ret < 0 && ret != -EPROBE_DEFER) - dev_err(&dev->dev, "IRQ index %u not found\n", num); + if (ret < 0) + return dev_err_probe(&dev->dev, ret, + "IRQ index %u not found\n", num); return ret; } @@ -762,6 +763,10 @@ EXPORT_SYMBOL_GPL(platform_device_del); /** * platform_device_register - add a platform-level device * @pdev: platform device we're adding + * + * NOTE: _Never_ directly free @pdev after calling this function, even if it + * returned an error! Always use platform_device_put() to give up the + * reference initialised in this function instead. */ int platform_device_register(struct platform_device *pdev) { diff --git a/drivers/base/property.c b/drivers/base/property.c index 5379eae478b1..a74c21af97c1 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -478,8 +478,17 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode, unsigned int nargs, unsigned int index, struct fwnode_reference_args *args) { - return fwnode_call_int_op(fwnode, get_reference_args, prop, nargs_prop, - nargs, index, args); + int ret; + + ret = fwnode_call_int_op(fwnode, get_reference_args, prop, nargs_prop, + nargs, index, args); + + if (ret < 0 && !IS_ERR_OR_NULL(fwnode) && + !IS_ERR_OR_NULL(fwnode->secondary)) + ret = fwnode_call_int_op(fwnode->secondary, get_reference_args, + prop, nargs_prop, nargs, index, args); + + return ret; } EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); diff --git a/drivers/base/test/test_async_driver_probe.c b/drivers/base/test/test_async_driver_probe.c index 3bb7beb127a9..4d1976ca5072 100644 --- a/drivers/base/test/test_async_driver_probe.c +++ b/drivers/base/test/test_async_driver_probe.c @@ -104,7 +104,7 @@ static int __init test_async_probe_init(void) struct platform_device **pdev = NULL; int async_id = 0, sync_id = 0; unsigned long long duration; - ktime_t calltime, delta; + ktime_t calltime; int err, nid, cpu; pr_info("registering first set of asynchronous devices...\n"); @@ -133,8 +133,7 @@ static int __init test_async_probe_init(void) goto err_unregister_async_devs; } - delta = ktime_sub(ktime_get(), calltime); - duration = (unsigned long long) ktime_to_ms(delta); + duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime); pr_info("registration took %lld msecs\n", duration); if (duration > TEST_PROBE_THRESHOLD) { pr_err("test failed: probe took too long\n"); @@ -161,8 +160,7 @@ static int __init test_async_probe_init(void) async_id++; } - delta = ktime_sub(ktime_get(), calltime); - duration = (unsigned long long) ktime_to_ms(delta); + duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime); dev_info(&(*pdev)->dev, "registration took %lld msecs\n", duration); if (duration > TEST_PROBE_THRESHOLD) { @@ -197,8 +195,7 @@ static int __init test_async_probe_init(void) goto err_unregister_sync_devs; } - delta = ktime_sub(ktime_get(), calltime); - duration = (unsigned long long) ktime_to_ms(delta); + duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime); pr_info("registration took %lld msecs\n", duration); if (duration < TEST_PROBE_THRESHOLD) { dev_err(&(*pdev)->dev, @@ -223,8 +220,7 @@ static int __init test_async_probe_init(void) sync_id++; - delta = ktime_sub(ktime_get(), calltime); - duration = (unsigned long long) ktime_to_ms(delta); + duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime); dev_info(&(*pdev)->dev, "registration took %lld msecs\n", duration); if (duration < TEST_PROBE_THRESHOLD) { diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 8f2b641d0b8c..fc24e89f9592 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -45,11 +45,15 @@ static ssize_t name##_list_read(struct file *file, struct kobject *kobj, \ define_id_show_func(physical_package_id); static DEVICE_ATTR_RO(physical_package_id); +#ifdef TOPOLOGY_DIE_SYSFS define_id_show_func(die_id); static DEVICE_ATTR_RO(die_id); +#endif +#ifdef TOPOLOGY_CLUSTER_SYSFS define_id_show_func(cluster_id); static DEVICE_ATTR_RO(cluster_id); +#endif define_id_show_func(core_id); static DEVICE_ATTR_RO(core_id); @@ -66,19 +70,23 @@ define_siblings_read_func(core_siblings, core_cpumask); static BIN_ATTR_RO(core_siblings, 0); static BIN_ATTR_RO(core_siblings_list, 0); +#ifdef TOPOLOGY_CLUSTER_SYSFS define_siblings_read_func(cluster_cpus, cluster_cpumask); static BIN_ATTR_RO(cluster_cpus, 0); static BIN_ATTR_RO(cluster_cpus_list, 0); +#endif +#ifdef TOPOLOGY_DIE_SYSFS define_siblings_read_func(die_cpus, die_cpumask); static BIN_ATTR_RO(die_cpus, 0); static BIN_ATTR_RO(die_cpus_list, 0); +#endif define_siblings_read_func(package_cpus, core_cpumask); static BIN_ATTR_RO(package_cpus, 0); static BIN_ATTR_RO(package_cpus_list, 0); -#ifdef CONFIG_SCHED_BOOK +#ifdef TOPOLOGY_BOOK_SYSFS define_id_show_func(book_id); static DEVICE_ATTR_RO(book_id); define_siblings_read_func(book_siblings, book_cpumask); @@ -86,7 +94,7 @@ static BIN_ATTR_RO(book_siblings, 0); static BIN_ATTR_RO(book_siblings_list, 0); #endif -#ifdef CONFIG_SCHED_DRAWER +#ifdef TOPOLOGY_DRAWER_SYSFS define_id_show_func(drawer_id); static DEVICE_ATTR_RO(drawer_id); define_siblings_read_func(drawer_siblings, drawer_cpumask); @@ -101,17 +109,21 @@ static struct bin_attribute *bin_attrs[] = { &bin_attr_thread_siblings_list, &bin_attr_core_siblings, &bin_attr_core_siblings_list, +#ifdef TOPOLOGY_CLUSTER_SYSFS &bin_attr_cluster_cpus, &bin_attr_cluster_cpus_list, +#endif +#ifdef TOPOLOGY_DIE_SYSFS &bin_attr_die_cpus, &bin_attr_die_cpus_list, +#endif &bin_attr_package_cpus, &bin_attr_package_cpus_list, -#ifdef CONFIG_SCHED_BOOK +#ifdef TOPOLOGY_BOOK_SYSFS &bin_attr_book_siblings, &bin_attr_book_siblings_list, #endif -#ifdef CONFIG_SCHED_DRAWER +#ifdef TOPOLOGY_DRAWER_SYSFS &bin_attr_drawer_siblings, &bin_attr_drawer_siblings_list, #endif @@ -120,13 +132,17 @@ static struct bin_attribute *bin_attrs[] = { static struct attribute *default_attrs[] = { &dev_attr_physical_package_id.attr, +#ifdef TOPOLOGY_DIE_SYSFS &dev_attr_die_id.attr, +#endif +#ifdef TOPOLOGY_CLUSTER_SYSFS &dev_attr_cluster_id.attr, +#endif &dev_attr_core_id.attr, -#ifdef CONFIG_SCHED_BOOK +#ifdef TOPOLOGY_BOOK_SYSFS &dev_attr_book_id.attr, #endif -#ifdef CONFIG_SCHED_DRAWER +#ifdef TOPOLOGY_DRAWER_SYSFS &dev_attr_drawer_id.attr, #endif NULL diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c index 053baadcada9..2bba0babcb62 100644 --- a/drivers/dma-buf/dma-buf-sysfs-stats.c +++ b/drivers/dma-buf/dma-buf-sysfs-stats.c @@ -132,7 +132,7 @@ void dma_buf_stats_teardown(struct dma_buf *dmabuf) /* Statistics files do not need to send uevents. */ -static int dmabuf_sysfs_uevent_filter(struct kset *kset, struct kobject *kobj) +static int dmabuf_sysfs_uevent_filter(struct kobject *kobj) { return 0; } diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index 8b8127fa8955..3a353776bd34 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -302,12 +302,12 @@ static struct attribute *dmi_sysfs_sel_attrs[] = { &dmi_sysfs_attr_sel_per_log_type_descriptor_length.attr, NULL, }; - +ATTRIBUTE_GROUPS(dmi_sysfs_sel); static struct kobj_type dmi_system_event_log_ktype = { .release = dmi_entry_free, .sysfs_ops = &dmi_sysfs_specialize_attr_ops, - .default_attrs = dmi_sysfs_sel_attrs, + .default_groups = dmi_sysfs_sel_groups, }; typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, @@ -518,6 +518,7 @@ static struct attribute *dmi_sysfs_entry_attrs[] = { &dmi_sysfs_attr_entry_position.attr, NULL, }; +ATTRIBUTE_GROUPS(dmi_sysfs_entry); static ssize_t dmi_entry_raw_read_helper(struct dmi_sysfs_entry *entry, const struct dmi_header *dh, @@ -565,7 +566,7 @@ static void dmi_sysfs_entry_release(struct kobject *kobj) static struct kobj_type dmi_sysfs_entry_ktype = { .release = dmi_sysfs_entry_release, .sysfs_ops = &dmi_sysfs_attr_ops, - .default_attrs = dmi_sysfs_entry_attrs, + .default_groups = dmi_sysfs_entry_groups, }; static struct kset *dmi_kset; diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index 14d0970a7198..69353dd0ea22 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -574,14 +574,6 @@ static EDD_DEVICE_ATTR(interface, 0444, edd_show_interface, edd_has_edd30); static EDD_DEVICE_ATTR(host_bus, 0444, edd_show_host_bus, edd_has_edd30); static EDD_DEVICE_ATTR(mbr_signature, 0444, edd_show_mbr_signature, edd_has_mbr_signature); - -/* These are default attributes that are added for every edd - * device discovered. There are none. - */ -static struct attribute * def_attrs[] = { - NULL, -}; - /* These attributes are conditional and only added for some devices. */ static struct edd_attribute * edd_attrs[] = { &edd_attr_raw_data, @@ -619,7 +611,6 @@ static void edd_release(struct kobject * kobj) static struct kobj_type edd_ktype = { .release = edd_release, .sysfs_ops = &edd_attr_ops, - .default_attrs = def_attrs, }; static struct kset *edd_kset; diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index 24945e2da77b..8e59be3782cb 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -69,6 +69,7 @@ static struct attribute *def_attrs[] = { &memmap_type_attr.attr, NULL }; +ATTRIBUTE_GROUPS(def); static const struct sysfs_ops memmap_attr_ops = { .show = memmap_attr_show, @@ -118,7 +119,7 @@ static void __meminit release_firmware_map_entry(struct kobject *kobj) static struct kobj_type __refdata memmap_ktype = { .release = release_firmware_map_entry, .sysfs_ops = &memmap_attr_ops, - .default_attrs = def_attrs, + .default_groups = def_groups, }; /* diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 172c751a4f6c..c62f05420d32 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -395,7 +395,7 @@ static void fw_cfg_sysfs_cache_cleanup(void) } } -/* default_attrs: per-entry attributes and show methods */ +/* per-entry attributes and show methods */ #define FW_CFG_SYSFS_ATTR(_attr) \ struct fw_cfg_sysfs_attribute fw_cfg_sysfs_attr_##_attr = { \ @@ -428,6 +428,7 @@ static struct attribute *fw_cfg_sysfs_entry_attrs[] = { &fw_cfg_sysfs_attr_name.attr, NULL, }; +ATTRIBUTE_GROUPS(fw_cfg_sysfs_entry); /* sysfs_ops: find fw_cfg_[entry, attribute] and call appropriate show method */ static ssize_t fw_cfg_sysfs_attr_show(struct kobject *kobj, struct attribute *a, @@ -454,7 +455,7 @@ static void fw_cfg_sysfs_release_entry(struct kobject *kobj) /* kobj_type: ties together all properties required to register an entry */ static struct kobj_type fw_cfg_sysfs_entry_ktype = { - .default_attrs = fw_cfg_sysfs_entry_attrs, + .default_groups = fw_cfg_sysfs_entry_groups, .sysfs_ops = &fw_cfg_sysfs_attr_ops, .release = fw_cfg_sysfs_release_entry, }; diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c index b86761904949..303a491e520d 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -113,12 +113,16 @@ __init int sysfb_create_simplefb(const struct screen_info *si, sysfb_apply_efi_quirks(pd); ret = platform_device_add_resources(pd, &res, 1); - if (ret) + if (ret) { + platform_device_put(pd); return ret; + } ret = platform_device_add_data(pd, mode, sizeof(*mode)); - if (ret) + if (ret) { + platform_device_put(pd); return ret; + } return platform_device_add(pd); } diff --git a/drivers/infiniband/hw/irdma/main.c b/drivers/infiniband/hw/irdma/main.c index 3fda7b78a9af..9fab29039f1c 100644 --- a/drivers/infiniband/hw/irdma/main.c +++ b/drivers/infiniband/hw/irdma/main.c @@ -207,7 +207,7 @@ static void irdma_remove(struct auxiliary_device *aux_dev) struct iidc_auxiliary_dev, adev); struct ice_pf *pf = iidc_adev->pf; - struct irdma_device *iwdev = dev_get_drvdata(&aux_dev->dev); + struct irdma_device *iwdev = auxiliary_get_drvdata(aux_dev); irdma_ib_unregister_device(iwdev); ice_rdma_update_vsi_filter(pf, iwdev->vsi_num, false); @@ -295,7 +295,7 @@ static int irdma_probe(struct auxiliary_device *aux_dev, const struct auxiliary_ ice_rdma_update_vsi_filter(pf, iwdev->vsi_num, true); ibdev_dbg(&iwdev->ibdev, "INIT: Gen2 PF[%d] device probe success\n", PCI_FUNC(rf->pcidev->devfn)); - dev_set_drvdata(&aux_dev->dev, iwdev); + auxiliary_set_drvdata(aux_dev, iwdev); return 0; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 5ec8bd2f0b2f..85f526c861e9 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4422,7 +4422,7 @@ static int mlx5r_mp_probe(struct auxiliary_device *adev, } mutex_unlock(&mlx5_ib_multiport_mutex); - dev_set_drvdata(&adev->dev, mpi); + auxiliary_set_drvdata(adev, mpi); return 0; } @@ -4430,7 +4430,7 @@ static void mlx5r_mp_remove(struct auxiliary_device *adev) { struct mlx5_ib_multiport_info *mpi; - mpi = dev_get_drvdata(&adev->dev); + mpi = auxiliary_get_drvdata(adev); mutex_lock(&mlx5_ib_multiport_mutex); if (mpi->ibdev) mlx5_ib_unbind_slave_port(mpi->ibdev, mpi); @@ -4480,7 +4480,7 @@ static int mlx5r_probe(struct auxiliary_device *adev, return ret; } - dev_set_drvdata(&adev->dev, dev); + auxiliary_set_drvdata(adev, dev); return 0; } @@ -4488,7 +4488,7 @@ static void mlx5r_remove(struct auxiliary_device *adev) { struct mlx5_ib_dev *dev; - dev = dev_get_drvdata(&adev->dev); + dev = auxiliary_get_drvdata(adev); __mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX); } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index a21cbdf89477..ba0b3eb131f1 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -696,16 +696,6 @@ config MFD_INTEL_PMC_BXT Register and P-unit access. In addition this creates devices for iTCO watchdog and telemetry that are part of the PMC. -config MFD_INTEL_PMT - tristate "Intel Platform Monitoring Technology (PMT) support" - depends on X86 && PCI - select MFD_CORE - help - The Intel Platform Monitoring Technology (PMT) is an interface that - provides access to hardware monitor registers. This driver supports - Telemetry, Watcher, and Crashlog PMT capabilities/devices for - platforms starting from Tiger Lake. - config MFD_IPAQ_MICRO bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" depends on SA1100_H3100 || SA1100_H3600 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 4d53e951a92d..df1ecc4a4c95 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -211,7 +211,6 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o -obj-$(CONFIG_MFD_INTEL_PMT) += intel_pmt.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_NTXEC) += ntxec.o diff --git a/drivers/mfd/intel_pmt.c b/drivers/mfd/intel_pmt.c deleted file mode 100644 index dd7eb614c28e..000000000000 --- a/drivers/mfd/intel_pmt.c +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel Platform Monitoring Technology PMT driver - * - * Copyright (c) 2020, Intel Corporation. - * All Rights Reserved. - * - * Author: David E. Box <david.e.box@linux.intel.com> - */ - -#include <linux/bits.h> -#include <linux/kernel.h> -#include <linux/mfd/core.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/pm_runtime.h> -#include <linux/types.h> - -/* Intel DVSEC capability vendor space offsets */ -#define INTEL_DVSEC_ENTRIES 0xA -#define INTEL_DVSEC_SIZE 0xB -#define INTEL_DVSEC_TABLE 0xC -#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) -#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) -#define INTEL_DVSEC_ENTRY_SIZE 4 - -/* PMT capabilities */ -#define DVSEC_INTEL_ID_TELEMETRY 2 -#define DVSEC_INTEL_ID_WATCHER 3 -#define DVSEC_INTEL_ID_CRASHLOG 4 - -struct intel_dvsec_header { - u16 length; - u16 id; - u8 num_entries; - u8 entry_size; - u8 tbir; - u32 offset; -}; - -enum pmt_quirks { - /* Watcher capability not supported */ - PMT_QUIRK_NO_WATCHER = BIT(0), - - /* Crashlog capability not supported */ - PMT_QUIRK_NO_CRASHLOG = BIT(1), - - /* Use shift instead of mask to read discovery table offset */ - PMT_QUIRK_TABLE_SHIFT = BIT(2), - - /* DVSEC not present (provided in driver data) */ - PMT_QUIRK_NO_DVSEC = BIT(3), -}; - -struct pmt_platform_info { - unsigned long quirks; - struct intel_dvsec_header **capabilities; -}; - -static const struct pmt_platform_info tgl_info = { - .quirks = PMT_QUIRK_NO_WATCHER | PMT_QUIRK_NO_CRASHLOG | - PMT_QUIRK_TABLE_SHIFT, -}; - -/* DG1 Platform with DVSEC quirk*/ -static struct intel_dvsec_header dg1_telemetry = { - .length = 0x10, - .id = 2, - .num_entries = 1, - .entry_size = 3, - .tbir = 0, - .offset = 0x466000, -}; - -static struct intel_dvsec_header *dg1_capabilities[] = { - &dg1_telemetry, - NULL -}; - -static const struct pmt_platform_info dg1_info = { - .quirks = PMT_QUIRK_NO_DVSEC, - .capabilities = dg1_capabilities, -}; - -static int pmt_add_dev(struct pci_dev *pdev, struct intel_dvsec_header *header, - unsigned long quirks) -{ - struct device *dev = &pdev->dev; - struct resource *res, *tmp; - struct mfd_cell *cell; - const char *name; - int count = header->num_entries; - int size = header->entry_size; - int id = header->id; - int i; - - switch (id) { - case DVSEC_INTEL_ID_TELEMETRY: - name = "pmt_telemetry"; - break; - case DVSEC_INTEL_ID_WATCHER: - if (quirks & PMT_QUIRK_NO_WATCHER) { - dev_info(dev, "Watcher not supported\n"); - return -EINVAL; - } - name = "pmt_watcher"; - break; - case DVSEC_INTEL_ID_CRASHLOG: - if (quirks & PMT_QUIRK_NO_CRASHLOG) { - dev_info(dev, "Crashlog not supported\n"); - return -EINVAL; - } - name = "pmt_crashlog"; - break; - default: - return -EINVAL; - } - - if (!header->num_entries || !header->entry_size) { - dev_err(dev, "Invalid count or size for %s header\n", name); - return -EINVAL; - } - - cell = devm_kzalloc(dev, sizeof(*cell), GFP_KERNEL); - if (!cell) - return -ENOMEM; - - res = devm_kcalloc(dev, count, sizeof(*res), GFP_KERNEL); - if (!res) - return -ENOMEM; - - if (quirks & PMT_QUIRK_TABLE_SHIFT) - header->offset >>= 3; - - /* - * The PMT DVSEC contains the starting offset and count for a block of - * discovery tables, each providing access to monitoring facilities for - * a section of the device. Create a resource list of these tables to - * provide to the driver. - */ - for (i = 0, tmp = res; i < count; i++, tmp++) { - tmp->start = pdev->resource[header->tbir].start + - header->offset + i * (size << 2); - tmp->end = tmp->start + (size << 2) - 1; - tmp->flags = IORESOURCE_MEM; - } - - cell->resources = res; - cell->num_resources = count; - cell->name = name; - - return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, - NULL); -} - -static int pmt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct pmt_platform_info *info; - unsigned long quirks = 0; - bool found_devices = false; - int ret, pos = 0; - - ret = pcim_enable_device(pdev); - if (ret) - return ret; - - info = (struct pmt_platform_info *)id->driver_data; - - if (info) - quirks = info->quirks; - - if (info && (info->quirks & PMT_QUIRK_NO_DVSEC)) { - struct intel_dvsec_header **header; - - header = info->capabilities; - while (*header) { - ret = pmt_add_dev(pdev, *header, quirks); - if (ret) - dev_warn(&pdev->dev, - "Failed to add device for DVSEC id %d\n", - (*header)->id); - else - found_devices = true; - - ++header; - } - } else { - do { - struct intel_dvsec_header header; - u32 table; - u16 vid; - - pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); - if (!pos) - break; - - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER1, &vid); - if (vid != PCI_VENDOR_ID_INTEL) - continue; - - pci_read_config_word(pdev, pos + PCI_DVSEC_HEADER2, - &header.id); - pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, - &header.num_entries); - pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, - &header.entry_size); - pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, - &table); - - header.tbir = INTEL_DVSEC_TABLE_BAR(table); - header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - - ret = pmt_add_dev(pdev, &header, quirks); - if (ret) - continue; - - found_devices = true; - } while (true); - } - - if (!found_devices) - return -ENODEV; - - pm_runtime_put(&pdev->dev); - pm_runtime_allow(&pdev->dev); - - return 0; -} - -static void pmt_pci_remove(struct pci_dev *pdev) -{ - pm_runtime_forbid(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); -} - -#define PCI_DEVICE_ID_INTEL_PMT_ADL 0x467d -#define PCI_DEVICE_ID_INTEL_PMT_DG1 0x490e -#define PCI_DEVICE_ID_INTEL_PMT_OOBMSM 0x09a7 -#define PCI_DEVICE_ID_INTEL_PMT_TGL 0x9a0d -static const struct pci_device_id pmt_pci_ids[] = { - { PCI_DEVICE_DATA(INTEL, PMT_ADL, &tgl_info) }, - { PCI_DEVICE_DATA(INTEL, PMT_DG1, &dg1_info) }, - { PCI_DEVICE_DATA(INTEL, PMT_OOBMSM, NULL) }, - { PCI_DEVICE_DATA(INTEL, PMT_TGL, &tgl_info) }, - { } -}; -MODULE_DEVICE_TABLE(pci, pmt_pci_ids); - -static struct pci_driver pmt_pci_driver = { - .name = "intel-pmt", - .id_table = pmt_pci_ids, - .probe = pmt_pci_probe, - .remove = pmt_pci_remove, -}; -module_pci_driver(pmt_pci_driver); - -MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); -MODULE_DESCRIPTION("Intel Platform Monitoring Technology PMT driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ac69e0aa09bf..bf80fb612449 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -5534,7 +5534,7 @@ void mlx5e_destroy_netdev(struct mlx5e_priv *priv) static int mlx5e_resume(struct auxiliary_device *adev) { struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev); - struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev); + struct mlx5e_priv *priv = auxiliary_get_drvdata(adev); struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = edev->mdev; int err; @@ -5557,7 +5557,7 @@ static int mlx5e_resume(struct auxiliary_device *adev) static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state) { - struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev); + struct mlx5e_priv *priv = auxiliary_get_drvdata(adev); struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; @@ -5589,7 +5589,7 @@ static int mlx5e_probe(struct auxiliary_device *adev, mlx5e_build_nic_netdev(netdev); priv = netdev_priv(netdev); - dev_set_drvdata(&adev->dev, priv); + auxiliary_set_drvdata(adev, priv); priv->profile = profile; priv->ppriv = NULL; @@ -5637,7 +5637,7 @@ err_destroy_netdev: static void mlx5e_remove(struct auxiliary_device *adev) { - struct mlx5e_priv *priv = dev_get_drvdata(&adev->dev); + struct mlx5e_priv *priv = auxiliary_get_drvdata(adev); pm_message_t state = {}; mlx5e_dcbnl_delete_app(priv); diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 40096b25994a..8e65086bb6c8 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -170,3 +170,14 @@ config INTEL_UNCORE_FREQ_CONTROL To compile this driver as a module, choose M here: the module will be called intel-uncore-frequency. + +config INTEL_VSEC + tristate "Intel Vendor Specific Extended Capabilities Driver" + depends on PCI + select AUXILIARY_BUS + help + Adds support for feature drivers exposed using Intel PCIe VSEC and + DVSEC. + + To compile this driver as a module, choose M here: the module will + be called intel_vsec. diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 174a80a6c87c..35f2066578b2 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -26,6 +26,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o intel_oaktrail-y := oaktrail.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o +intel_vsec-y := vsec.o +obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o # Intel PMIC / PMC / P-Unit drivers intel_bxtwc_tmu-y := bxtwc_tmu.o diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index d630f883a717..e916fc966221 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -17,7 +17,7 @@ config INTEL_PMT_CLASS config INTEL_PMT_TELEMETRY tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitory Technology (PMT) Telemetry driver provides @@ -29,7 +29,7 @@ config INTEL_PMT_TELEMETRY config INTEL_PMT_CRASHLOG tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitoring Technology (PMT) crashlog driver provides diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 659b1073033c..1c9e3f3ea41c 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -13,6 +13,7 @@ #include <linux/mm.h> #include <linux/pci.h> +#include "../vsec.h" #include "class.h" #define PMT_XA_START 0 @@ -281,31 +282,29 @@ fail_dev_create: return ret; } -int intel_pmt_dev_create(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx) +int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, + struct intel_vsec_device *intel_vsec_dev, int idx) { + struct device *dev = &intel_vsec_dev->auxdev.dev; struct intel_pmt_header header; struct resource *disc_res; - int ret = -ENODEV; + int ret; - disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); - if (!disc_res) - return ret; + disc_res = &intel_vsec_dev->resource[idx]; - entry->disc_table = devm_platform_ioremap_resource(pdev, idx); + entry->disc_table = devm_ioremap_resource(dev, disc_res); if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); - ret = ns->pmt_header_decode(entry, &header, &pdev->dev); + ret = ns->pmt_header_decode(entry, &header, dev); if (ret) return ret; - ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); + ret = intel_pmt_populate_entry(entry, &header, dev, disc_res); if (ret) return ret; - return intel_pmt_dev_register(entry, ns, &pdev->dev); + return intel_pmt_dev_register(entry, ns, dev); } EXPORT_SYMBOL_GPL(intel_pmt_dev_create); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 1337019c2873..db11d58867ce 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -2,13 +2,14 @@ #ifndef _INTEL_PMT_CLASS_H #define _INTEL_PMT_CLASS_H -#include <linux/platform_device.h> #include <linux/xarray.h> #include <linux/types.h> #include <linux/bits.h> #include <linux/err.h> #include <linux/io.h> +#include "../vsec.h" + /* PMT access types */ #define ACCESS_BARID 2 #define ACCESS_LOCAL 3 @@ -47,7 +48,7 @@ struct intel_pmt_namespace { bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx); + struct intel_vsec_device *dev, int idx); void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns); #endif diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 1c1021f04d3c..34daf9df168b 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -8,6 +8,7 @@ * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define DRV_NAME "pmt_crashlog" - /* Crashlog discovery header types */ #define CRASH_TYPE_OOBMSM 1 @@ -257,34 +257,34 @@ static struct intel_pmt_namespace pmt_crashlog_ns = { /* * initialization */ -static int pmt_crashlog_remove(struct platform_device *pdev) +static void pmt_crashlog_remove(struct auxiliary_device *auxdev) { - struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); + struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); - - return 0; } -static int pmt_crashlog_probe(struct platform_device *pdev) +static int pmt_crashlog_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_crashlog_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i].entry; - ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -295,26 +295,30 @@ static int pmt_crashlog_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_crashlog_remove(pdev); + pmt_crashlog_remove(auxdev); return ret; } -static struct platform_driver pmt_crashlog_driver = { - .driver = { - .name = DRV_NAME, - }, - .remove = pmt_crashlog_remove, - .probe = pmt_crashlog_probe, +static const struct auxiliary_device_id pmt_crashlog_id_table[] = { + { .name = "intel_vsec.crashlog" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table); + +static struct auxiliary_driver pmt_crashlog_aux_driver = { + .id_table = pmt_crashlog_id_table, + .remove = pmt_crashlog_remove, + .probe = pmt_crashlog_probe, }; static int __init pmt_crashlog_init(void) { - return platform_driver_register(&pmt_crashlog_driver); + return auxiliary_driver_register(&pmt_crashlog_aux_driver); } static void __exit pmt_crashlog_exit(void) { - platform_driver_unregister(&pmt_crashlog_driver); + auxiliary_driver_unregister(&pmt_crashlog_aux_driver); xa_destroy(&crashlog_array); } @@ -323,5 +327,4 @@ module_exit(pmt_crashlog_exit); MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Crashlog driver"); -MODULE_ALIAS("platform:" DRV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 38d52651c572..6b6f3e2a617a 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -8,6 +8,7 @@ * Author: "David E. Box" <david.e.box@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define TELEM_DEV_NAME "pmt_telemetry" - #define TELEM_SIZE_OFFSET 0x0 #define TELEM_GUID_OFFSET 0x4 #define TELEM_BASE_OFFSET 0x8 @@ -79,34 +79,33 @@ static struct intel_pmt_namespace pmt_telem_ns = { .pmt_header_decode = pmt_telem_header_decode, }; -static int pmt_telem_remove(struct platform_device *pdev) +static void pmt_telem_remove(struct auxiliary_device *auxdev) { - struct pmt_telem_priv *priv = platform_get_drvdata(pdev); + struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); - - return 0; } -static int pmt_telem_probe(struct platform_device *pdev) +static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_telem_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i]; - ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -117,32 +116,35 @@ static int pmt_telem_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_telem_remove(pdev); + pmt_telem_remove(auxdev); return ret; } -static struct platform_driver pmt_telem_driver = { - .driver = { - .name = TELEM_DEV_NAME, - }, - .remove = pmt_telem_remove, - .probe = pmt_telem_probe, +static const struct auxiliary_device_id pmt_telem_id_table[] = { + { .name = "intel_vsec.telemetry" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table); + +static struct auxiliary_driver pmt_telem_aux_driver = { + .id_table = pmt_telem_id_table, + .remove = pmt_telem_remove, + .probe = pmt_telem_probe, }; static int __init pmt_telem_init(void) { - return platform_driver_register(&pmt_telem_driver); + return auxiliary_driver_register(&pmt_telem_aux_driver); } module_init(pmt_telem_init); static void __exit pmt_telem_exit(void) { - platform_driver_unregister(&pmt_telem_driver); + auxiliary_driver_unregister(&pmt_telem_aux_driver); xa_destroy(&telem_array); } module_exit(pmt_telem_exit); MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Telemetry driver"); -MODULE_ALIAS("platform:" TELEM_DEV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c new file mode 100644 index 000000000000..c3bdd75ed690 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Vendor Specific Extended Capabilities auxiliary bus driver + * + * Copyright (c) 2021, Intel Corporation. + * All Rights Reserved. + * + * Author: David E. Box <david.e.box@linux.intel.com> + * + * This driver discovers and creates auxiliary devices for Intel defined PCIe + * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, + * VSEC and DVSEC respectively. The driver supports features on specific PCIe + * endpoints that exist primarily to expose them. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "vsec.h" + +/* Intel DVSEC offsets */ +#define INTEL_DVSEC_ENTRIES 0xA +#define INTEL_DVSEC_SIZE 0xB +#define INTEL_DVSEC_TABLE 0xC +#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) +#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) +#define TABLE_OFFSET_SHIFT 3 + +static DEFINE_IDA(intel_vsec_ida); + +/** + * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. + * @rev: Revision ID of the VSEC/DVSEC register space + * @length: Length of the VSEC/DVSEC register space + * @id: ID of the feature + * @num_entries: Number of instances of the feature + * @entry_size: Size of the discovery table for each feature + * @tbir: BAR containing the discovery tables + * @offset: BAR offset of start of the first discovery table + */ +struct intel_vsec_header { + u8 rev; + u16 length; + u16 id; + u8 num_entries; + u8 entry_size; + u8 tbir; + u32 offset; +}; + +/* Platform specific data */ +struct intel_vsec_platform_info { + struct intel_vsec_header **capabilities; + unsigned long quirks; +}; + +enum intel_vsec_id { + VSEC_ID_TELEMETRY = 2, + VSEC_ID_WATCHER = 3, + VSEC_ID_CRASHLOG = 4, +}; + +static enum intel_vsec_id intel_vsec_allow_list[] = { + VSEC_ID_TELEMETRY, + VSEC_ID_WATCHER, + VSEC_ID_CRASHLOG, +}; + +static const char *intel_vsec_name(enum intel_vsec_id id) +{ + switch (id) { + case VSEC_ID_TELEMETRY: + return "telemetry"; + + case VSEC_ID_WATCHER: + return "watcher"; + + case VSEC_ID_CRASHLOG: + return "crashlog"; + + default: + return NULL; + } +} + +static bool intel_vsec_allowed(u16 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++) + if (intel_vsec_allow_list[i] == id) + return true; + + return false; +} + +static bool intel_vsec_disabled(u16 id, unsigned long quirks) +{ + switch (id) { + case VSEC_ID_WATCHER: + return !!(quirks & VSEC_QUIRK_NO_WATCHER); + + case VSEC_ID_CRASHLOG: + return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); + + default: + return false; + } +} + +static void intel_vsec_remove_aux(void *data) +{ + auxiliary_device_delete(data); + auxiliary_device_uninit(data); +} + +static void intel_vsec_dev_release(struct device *dev) +{ + struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); + + ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); +} + +static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *intel_vsec_dev, + const char *name) +{ + struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; + int ret; + + ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); + if (ret < 0) { + kfree(intel_vsec_dev); + return ret; + } + + auxdev->id = ret; + auxdev->name = name; + auxdev->dev.parent = &pdev->dev; + auxdev->dev.release = intel_vsec_dev_release; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + ida_free(intel_vsec_dev->ida, auxdev->id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); + return ret; + } + + ret = auxiliary_device_add(auxdev); + if (ret < 0) { + auxiliary_device_uninit(auxdev); + return ret; + } + + return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev); +} + +static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, + unsigned long quirks) +{ + struct intel_vsec_device *intel_vsec_dev; + struct resource *res, *tmp; + int i; + + if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) + return -EINVAL; + + if (!header->num_entries) { + dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); + return -EINVAL; + } + + if (!header->entry_size) { + dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); + return -EINVAL; + } + + intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); + if (!intel_vsec_dev) + return -ENOMEM; + + res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); + if (!res) { + kfree(intel_vsec_dev); + return -ENOMEM; + } + + if (quirks & VSEC_QUIRK_TABLE_SHIFT) + header->offset >>= TABLE_OFFSET_SHIFT; + + /* + * The DVSEC/VSEC contains the starting offset and count for a block of + * discovery tables. Create a resource array of these tables to the + * auxiliary device driver. + */ + for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { + tmp->start = pdev->resource[header->tbir].start + + header->offset + i * (header->entry_size * sizeof(u32)); + tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; + tmp->flags = IORESOURCE_MEM; + } + + intel_vsec_dev->pcidev = pdev; + intel_vsec_dev->resource = res; + intel_vsec_dev->num_resources = header->num_entries; + intel_vsec_dev->quirks = quirks; + intel_vsec_dev->ida = &intel_vsec_ida; + + return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id)); +} + +static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, + struct intel_vsec_header **header) +{ + bool have_devices = false; + int ret; + + for ( ; *header; header++) { + ret = intel_vsec_add_dev(pdev, *header, quirks); + if (ret) + dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", + (*header)->id); + else + have_devices = true; + } + + return have_devices; +} + +static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + u16 vid; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); + vid = PCI_DVSEC_HEADER1_VID(hdr); + if (vid != PCI_VENDOR_ID_INTEL) + continue; + + /* Support only revision 1 */ + header.rev = PCI_DVSEC_HEADER1_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); + continue; + } + + header.length = PCI_DVSEC_HEADER1_LEN(hdr); + + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); + header.id = PCI_DVSEC_HEADER2_ID(hdr); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); + + /* Support only revision 1 */ + header.rev = PCI_VNDR_HEADER_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); + continue; + } + + header.id = PCI_VNDR_HEADER_ID(hdr); + header.length = PCI_VNDR_HEADER_LEN(hdr); + + /* entry, size, and table offset are the same as DVSEC */ + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct intel_vsec_platform_info *info; + bool have_devices = false; + unsigned long quirks = 0; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = (struct intel_vsec_platform_info *)id->driver_data; + if (info) + quirks = info->quirks; + + if (intel_vsec_walk_dvsec(pdev, quirks)) + have_devices = true; + + if (intel_vsec_walk_vsec(pdev, quirks)) + have_devices = true; + + if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && + intel_vsec_walk_header(pdev, quirks, info->capabilities)) + have_devices = true; + + if (!have_devices) + return -ENODEV; + + return 0; +} + +/* TGL info */ +static const struct intel_vsec_platform_info tgl_info = { + .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT, +}; + +/* DG1 info */ +static struct intel_vsec_header dg1_telemetry = { + .length = 0x10, + .id = 2, + .num_entries = 1, + .entry_size = 3, + .tbir = 0, + .offset = 0x466000, +}; + +static struct intel_vsec_header *dg1_capabilities[] = { + &dg1_telemetry, + NULL +}; + +static const struct intel_vsec_platform_info dg1_info = { + .capabilities = dg1_capabilities, + .quirks = VSEC_QUIRK_NO_DVSEC, +}; + +#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d +#define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e +#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d +static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) }, + { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { } +}; +MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); + +static struct pci_driver intel_vsec_pci_driver = { + .name = "intel_vsec", + .id_table = intel_vsec_pci_ids, + .probe = intel_vsec_pci_probe, +}; +module_pci_driver(intel_vsec_pci_driver); + +MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h new file mode 100644 index 000000000000..4cc36678e8c5 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _VSEC_H +#define _VSEC_H + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> + +struct pci_dev; +struct resource; + +enum intel_vsec_quirks { + /* Watcher feature not supported */ + VSEC_QUIRK_NO_WATCHER = BIT(0), + + /* Crashlog feature not supported */ + VSEC_QUIRK_NO_CRASHLOG = BIT(1), + + /* Use shift instead of mask to read discovery table offset */ + VSEC_QUIRK_TABLE_SHIFT = BIT(2), + + /* DVSEC not present (provided in driver data) */ + VSEC_QUIRK_NO_DVSEC = BIT(3), +}; + +struct intel_vsec_device { + struct auxiliary_device auxdev; + struct pci_dev *pcidev; + struct resource *resource; + struct ida *ida; + unsigned long quirks; + int num_resources; +}; + +static inline struct intel_vsec_device *dev_to_ivdev(struct device *dev) +{ + return container_of(dev, struct intel_vsec_device, auxdev.dev); +} + +static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device *auxdev) +{ + return container_of(auxdev, struct intel_vsec_device, auxdev); +} +#endif diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 78037ffdb09b..d082d18e41a9 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -1293,7 +1293,7 @@ static int intel_link_probe(struct auxiliary_device *auxdev, bus->ops = &sdw_intel_ops; /* set driver data, accessed by snd_soc_dai_get_drvdata() */ - dev_set_drvdata(dev, cdns); + auxiliary_set_drvdata(auxdev, cdns); /* use generic bandwidth allocation algorithm */ sdw->cdns.bus.compute_params = sdw_compute_params; @@ -1321,7 +1321,7 @@ int intel_link_startup(struct auxiliary_device *auxdev) { struct sdw_cdns_stream_config config; struct device *dev = &auxdev->dev; - struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; int link_flags; @@ -1463,7 +1463,7 @@ err_init: static void intel_link_remove(struct auxiliary_device *auxdev) { struct device *dev = &auxdev->dev; - struct sdw_cdns *cdns = dev_get_drvdata(dev); + struct sdw_cdns *cdns = auxiliary_get_drvdata(auxdev); struct sdw_intel *sdw = cdns_to_intel(cdns); struct sdw_bus *bus = &cdns->bus; @@ -1488,7 +1488,7 @@ int intel_link_process_wakeen_event(struct auxiliary_device *auxdev) void __iomem *shim; u16 wake_sts; - sdw = dev_get_drvdata(dev); + sdw = auxiliary_get_drvdata(auxdev); bus = &sdw->cdns.bus; if (bus->prop.hw_disabled || !sdw->startup_done) { diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index e329022e1669..d99807765dfe 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -244,7 +244,7 @@ static struct sdw_intel_ctx goto err; link = &ldev->link_res; - link->cdns = dev_get_drvdata(&ldev->auxdev.dev); + link->cdns = auxiliary_get_drvdata(&ldev->auxdev); if (!link->cdns) { dev_err(&adev->dev, "failed to get link->cdns\n"); diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c index 63813fbb5f62..cf59f7e17c6d 100644 --- a/drivers/vdpa/mlx5/net/mlx5_vnet.c +++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c @@ -2683,7 +2683,7 @@ static int mlx5v_probe(struct auxiliary_device *adev, if (err) goto reg_err; - dev_set_drvdata(&adev->dev, mgtdev); + auxiliary_set_drvdata(adev, mgtdev); return 0; @@ -2696,7 +2696,7 @@ static void mlx5v_remove(struct auxiliary_device *adev) { struct mlx5_vdpa_mgmtdev *mgtdev; - mgtdev = dev_get_drvdata(&adev->dev); + mgtdev = auxiliary_get_drvdata(adev); vdpa_mgmtdev_unregister(&mgtdev->mgtdev); kfree(mgtdev); } diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 7d162b0efbf0..950c63fa4d0b 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -147,7 +147,7 @@ static int debugfs_locked_down(struct inode *inode, struct file *filp, const struct file_operations *real_fops) { - if ((inode->i_mode & 07777) == 0444 && + if ((inode->i_mode & 07777 & ~0444) == 0 && !(filp->f_mode & FMODE_WRITE) && !real_fops->unlocked_ioctl && !real_fops->compat_ioctl && diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c index 31384e7d6f90..0d3833a124a3 100644 --- a/fs/dlm/lockspace.c +++ b/fs/dlm/lockspace.c @@ -216,8 +216,7 @@ static int do_uevent(struct dlm_ls *ls, int in) return ls->ls_uevent_result; } -static int dlm_uevent(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env) +static int dlm_uevent(struct kobject *kobj, struct kobj_uevent_env *env) { struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index c0a34d9ddee4..a6002b2d146d 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -767,8 +767,7 @@ void gfs2_sys_fs_del(struct gfs2_sbd *sdp) wait_for_completion(&sdp->sd_kobj_unregister); } -static int gfs2_uevent(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env) +static int gfs2_uevent(struct kobject *kobj, struct kobj_uevent_env *env) { struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj); struct super_block *s = sdp->sd_vfs; diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 8e0a1378a4b1..e6d9772ddb4c 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -17,7 +17,6 @@ #include "kernfs-internal.h" -DECLARE_RWSEM(kernfs_rwsem); static DEFINE_SPINLOCK(kernfs_rename_lock); /* kn->parent and ->name */ static char kernfs_pr_cont_buf[PATH_MAX]; /* protected by rename_lock */ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */ @@ -26,7 +25,7 @@ static DEFINE_SPINLOCK(kernfs_idr_lock); /* root->ino_idr */ static bool kernfs_active(struct kernfs_node *kn) { - lockdep_assert_held(&kernfs_rwsem); + lockdep_assert_held(&kernfs_root(kn)->kernfs_rwsem); return atomic_read(&kn->active) >= 0; } @@ -457,14 +456,15 @@ void kernfs_put_active(struct kernfs_node *kn) * return after draining is complete. */ static void kernfs_drain(struct kernfs_node *kn) - __releases(&kernfs_rwsem) __acquires(&kernfs_rwsem) + __releases(&kernfs_root(kn)->kernfs_rwsem) + __acquires(&kernfs_root(kn)->kernfs_rwsem) { struct kernfs_root *root = kernfs_root(kn); - lockdep_assert_held_write(&kernfs_rwsem); + lockdep_assert_held_write(&root->kernfs_rwsem); WARN_ON_ONCE(kernfs_active(kn)); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); @@ -483,7 +483,7 @@ static void kernfs_drain(struct kernfs_node *kn) kernfs_drain_open_files(kn); - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); } /** @@ -718,11 +718,12 @@ err_unlock: int kernfs_add_one(struct kernfs_node *kn) { struct kernfs_node *parent = kn->parent; + struct kernfs_root *root = kernfs_root(parent); struct kernfs_iattrs *ps_iattr; bool has_ns; int ret; - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); ret = -EINVAL; has_ns = kernfs_ns_enabled(parent); @@ -753,7 +754,7 @@ int kernfs_add_one(struct kernfs_node *kn) ps_iattr->ia_mtime = ps_iattr->ia_ctime; } - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); /* * Activate the new node unless CREATE_DEACTIVATED is requested. @@ -767,7 +768,7 @@ int kernfs_add_one(struct kernfs_node *kn) return 0; out_unlock: - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); return ret; } @@ -788,7 +789,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, bool has_ns = kernfs_ns_enabled(parent); unsigned int hash; - lockdep_assert_held(&kernfs_rwsem); + lockdep_assert_held(&kernfs_root(parent)->kernfs_rwsem); if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", @@ -820,7 +821,7 @@ static struct kernfs_node *kernfs_walk_ns(struct kernfs_node *parent, size_t len; char *p, *name; - lockdep_assert_held_read(&kernfs_rwsem); + lockdep_assert_held_read(&kernfs_root(parent)->kernfs_rwsem); /* grab kernfs_rename_lock to piggy back on kernfs_pr_cont_buf */ spin_lock_irq(&kernfs_rename_lock); @@ -859,11 +860,12 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, const void *ns) { struct kernfs_node *kn; + struct kernfs_root *root = kernfs_root(parent); - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); kn = kernfs_find_ns(parent, name, ns); kernfs_get(kn); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return kn; } @@ -883,11 +885,12 @@ struct kernfs_node *kernfs_walk_and_get_ns(struct kernfs_node *parent, const char *path, const void *ns) { struct kernfs_node *kn; + struct kernfs_root *root = kernfs_root(parent); - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); kn = kernfs_walk_ns(parent, path, ns); kernfs_get(kn); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return kn; } @@ -912,6 +915,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops, return ERR_PTR(-ENOMEM); idr_init(&root->ino_idr); + init_rwsem(&root->kernfs_rwsem); INIT_LIST_HEAD(&root->supers); /* @@ -957,7 +961,13 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops, */ void kernfs_destroy_root(struct kernfs_root *root) { - kernfs_remove(root->kn); /* will also free @root */ + /* + * kernfs_remove holds kernfs_rwsem from the root so the root + * shouldn't be freed during the operation. + */ + kernfs_get(root->kn); + kernfs_remove(root->kn); + kernfs_put(root->kn); /* will also free @root */ } /** @@ -1035,6 +1045,7 @@ struct kernfs_node *kernfs_create_empty_dir(struct kernfs_node *parent, static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) { struct kernfs_node *kn; + struct kernfs_root *root; if (flags & LOOKUP_RCU) return -ECHILD; @@ -1046,18 +1057,19 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) /* If the kernfs parent node has changed discard and * proceed to ->lookup. */ - down_read(&kernfs_rwsem); spin_lock(&dentry->d_lock); parent = kernfs_dentry_node(dentry->d_parent); if (parent) { + spin_unlock(&dentry->d_lock); + root = kernfs_root(parent); + down_read(&root->kernfs_rwsem); if (kernfs_dir_changed(parent, dentry)) { - spin_unlock(&dentry->d_lock); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return 0; } - } - spin_unlock(&dentry->d_lock); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); + } else + spin_unlock(&dentry->d_lock); /* The kernfs parent node hasn't changed, leave the * dentry negative and return success. @@ -1066,7 +1078,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) } kn = kernfs_dentry_node(dentry); - down_read(&kernfs_rwsem); + root = kernfs_root(kn); + down_read(&root->kernfs_rwsem); /* The kernfs node has been deactivated */ if (!kernfs_active(kn)) @@ -1085,10 +1098,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) kernfs_info(dentry->d_sb)->ns != kn->ns) goto out_bad; - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return 1; out_bad: - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return 0; } @@ -1102,10 +1115,12 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, { struct kernfs_node *parent = dir->i_private; struct kernfs_node *kn; + struct kernfs_root *root; struct inode *inode = NULL; const void *ns = NULL; - down_read(&kernfs_rwsem); + root = kernfs_root(parent); + down_read(&root->kernfs_rwsem); if (kernfs_ns_enabled(parent)) ns = kernfs_info(dir->i_sb)->ns; @@ -1116,7 +1131,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, * create a negative. */ if (!kernfs_active(kn)) { - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return NULL; } inode = kernfs_get_inode(dir->i_sb, kn); @@ -1131,7 +1146,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, */ if (!IS_ERR(inode)) kernfs_set_rev(parent, dentry); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); /* instantiate and hash (possibly negative) dentry */ return d_splice_alias(inode, dentry); @@ -1254,7 +1269,7 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, { struct rb_node *rbn; - lockdep_assert_held_write(&kernfs_rwsem); + lockdep_assert_held_write(&kernfs_root(root)->kernfs_rwsem); /* if first iteration, visit leftmost descendant which may be root */ if (!pos) @@ -1289,8 +1304,9 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, void kernfs_activate(struct kernfs_node *kn) { struct kernfs_node *pos; + struct kernfs_root *root = kernfs_root(kn); - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); pos = NULL; while ((pos = kernfs_next_descendant_post(pos, kn))) { @@ -1304,14 +1320,14 @@ void kernfs_activate(struct kernfs_node *kn) pos->flags |= KERNFS_ACTIVATED; } - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); } static void __kernfs_remove(struct kernfs_node *kn) { struct kernfs_node *pos; - lockdep_assert_held_write(&kernfs_rwsem); + lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem); /* * Short-circuit if non-root @kn has already finished removal. @@ -1381,9 +1397,11 @@ static void __kernfs_remove(struct kernfs_node *kn) */ void kernfs_remove(struct kernfs_node *kn) { - down_write(&kernfs_rwsem); + struct kernfs_root *root = kernfs_root(kn); + + down_write(&root->kernfs_rwsem); __kernfs_remove(kn); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); } /** @@ -1469,8 +1487,9 @@ void kernfs_unbreak_active_protection(struct kernfs_node *kn) bool kernfs_remove_self(struct kernfs_node *kn) { bool ret; + struct kernfs_root *root = kernfs_root(kn); - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); kernfs_break_active_protection(kn); /* @@ -1498,9 +1517,9 @@ bool kernfs_remove_self(struct kernfs_node *kn) atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) break; - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); schedule(); - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); } finish_wait(waitq, &wait); WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); @@ -1513,7 +1532,7 @@ bool kernfs_remove_self(struct kernfs_node *kn) */ kernfs_unbreak_active_protection(kn); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); return ret; } @@ -1530,6 +1549,7 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns) { struct kernfs_node *kn; + struct kernfs_root *root; if (!parent) { WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory\n", @@ -1537,13 +1557,14 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, return -ENOENT; } - down_write(&kernfs_rwsem); + root = kernfs_root(parent); + down_write(&root->kernfs_rwsem); kn = kernfs_find_ns(parent, name, ns); if (kn) __kernfs_remove(kn); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); if (kn) return 0; @@ -1562,6 +1583,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, const char *new_name, const void *new_ns) { struct kernfs_node *old_parent; + struct kernfs_root *root; const char *old_name = NULL; int error; @@ -1569,7 +1591,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, if (!kn->parent) return -EINVAL; - down_write(&kernfs_rwsem); + root = kernfs_root(kn); + down_write(&root->kernfs_rwsem); error = -ENOENT; if (!kernfs_active(kn) || !kernfs_active(new_parent) || @@ -1623,7 +1646,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, error = 0; out: - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); return error; } @@ -1694,11 +1717,14 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) struct dentry *dentry = file->f_path.dentry; struct kernfs_node *parent = kernfs_dentry_node(dentry); struct kernfs_node *pos = file->private_data; + struct kernfs_root *root; const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; - down_read(&kernfs_rwsem); + + root = kernfs_root(parent); + down_read(&root->kernfs_rwsem); if (kernfs_ns_enabled(parent)) ns = kernfs_info(dentry->d_sb)->ns; @@ -1715,12 +1741,12 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) file->private_data = pos; kernfs_get(pos); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); if (!dir_emit(ctx, name, len, ino, type)) return 0; - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); } - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); file->private_data = NULL; ctx->pos = INT_MAX; return 0; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 60e2a86c535e..9414a7a60a9f 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -847,6 +847,7 @@ static void kernfs_notify_workfn(struct work_struct *work) { struct kernfs_node *kn; struct kernfs_super_info *info; + struct kernfs_root *root; repeat: /* pop one off the notify_list */ spin_lock_irq(&kernfs_notify_lock); @@ -859,8 +860,9 @@ repeat: kn->attr.notify_next = NULL; spin_unlock_irq(&kernfs_notify_lock); + root = kernfs_root(kn); /* kick fsnotify */ - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); list_for_each_entry(info, &kernfs_root(kn)->supers, node) { struct kernfs_node *parent; @@ -898,7 +900,7 @@ repeat: iput(inode); } - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); kernfs_put(kn); goto repeat; } diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index c0eae1725435..3d783d80f5da 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -99,10 +99,11 @@ int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { int ret; + struct kernfs_root *root = kernfs_root(kn); - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); ret = __kernfs_setattr(kn, iattr); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); return ret; } @@ -111,12 +112,14 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, { struct inode *inode = d_inode(dentry); struct kernfs_node *kn = inode->i_private; + struct kernfs_root *root; int error; if (!kn) return -EINVAL; - down_write(&kernfs_rwsem); + root = kernfs_root(kn); + down_write(&root->kernfs_rwsem); error = setattr_prepare(&init_user_ns, dentry, iattr); if (error) goto out; @@ -129,7 +132,7 @@ int kernfs_iop_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, setattr_copy(&init_user_ns, inode, iattr); out: - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); return error; } @@ -184,13 +187,14 @@ int kernfs_iop_getattr(struct user_namespace *mnt_userns, { struct inode *inode = d_inode(path->dentry); struct kernfs_node *kn = inode->i_private; + struct kernfs_root *root = kernfs_root(kn); - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); spin_lock(&inode->i_lock); kernfs_refresh_inode(kn, inode); generic_fillattr(&init_user_ns, inode, stat); spin_unlock(&inode->i_lock); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return 0; } @@ -274,19 +278,21 @@ int kernfs_iop_permission(struct user_namespace *mnt_userns, struct inode *inode, int mask) { struct kernfs_node *kn; + struct kernfs_root *root; int ret; if (mask & MAY_NOT_BLOCK) return -ECHILD; kn = inode->i_private; + root = kernfs_root(kn); - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); spin_lock(&inode->i_lock); kernfs_refresh_inode(kn, inode); ret = generic_permission(&init_user_ns, inode, mask); spin_unlock(&inode->i_lock); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return ret; } diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index f2f909d09f52..cfa79715fc1a 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -236,6 +236,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn, static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *kfc) { struct kernfs_super_info *info = kernfs_info(sb); + struct kernfs_root *kf_root = kfc->root; struct inode *inode; struct dentry *root; @@ -255,9 +256,9 @@ static int kernfs_fill_super(struct super_block *sb, struct kernfs_fs_context *k sb->s_shrink.seeks = 0; /* get root inode, initialize and unlock it */ - down_read(&kernfs_rwsem); + down_read(&kf_root->kernfs_rwsem); inode = kernfs_get_inode(sb, info->root->kn); - up_read(&kernfs_rwsem); + up_read(&kf_root->kernfs_rwsem); if (!inode) { pr_debug("kernfs: could not get root inode\n"); return -ENOMEM; @@ -334,6 +335,7 @@ int kernfs_get_tree(struct fs_context *fc) if (!sb->s_root) { struct kernfs_super_info *info = kernfs_info(sb); + struct kernfs_root *root = kfc->root; kfc->new_sb_created = true; @@ -344,9 +346,9 @@ int kernfs_get_tree(struct fs_context *fc) } sb->s_flags |= SB_ACTIVE; - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); list_add(&info->node, &info->root->supers); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); } fc->root = dget(sb->s_root); @@ -371,10 +373,11 @@ void kernfs_free_fs_context(struct fs_context *fc) void kernfs_kill_sb(struct super_block *sb) { struct kernfs_super_info *info = kernfs_info(sb); + struct kernfs_root *root = info->root; - down_write(&kernfs_rwsem); + down_write(&root->kernfs_rwsem); list_del(&info->node); - up_write(&kernfs_rwsem); + up_write(&root->kernfs_rwsem); /* * Remove the superblock from fs_supers/s_instances diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 19a6c71c6ff5..0ab13824822f 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -113,11 +113,12 @@ static int kernfs_getlink(struct inode *inode, char *path) struct kernfs_node *kn = inode->i_private; struct kernfs_node *parent = kn->parent; struct kernfs_node *target = kn->symlink.target_kn; + struct kernfs_root *root = kernfs_root(parent); int error; - down_read(&kernfs_rwsem); + down_read(&root->kernfs_rwsem); error = kernfs_get_target_path(parent, target, path); - up_read(&kernfs_rwsem); + up_read(&root->kernfs_rwsem); return error; } diff --git a/fs/nilfs2/sysfs.c b/fs/nilfs2/sysfs.c index 81f35c5b5a40..379d22e28ed6 100644 --- a/fs/nilfs2/sysfs.c +++ b/fs/nilfs2/sysfs.c @@ -57,7 +57,7 @@ static void nilfs_##name##_attr_release(struct kobject *kobj) \ complete(&subgroups->sg_##name##_kobj_unregister); \ } \ static struct kobj_type nilfs_##name##_ktype = { \ - .default_attrs = nilfs_##name##_attrs, \ + .default_groups = nilfs_##name##_groups, \ .sysfs_ops = &nilfs_##name##_attr_ops, \ .release = nilfs_##name##_attr_release, \ } @@ -129,6 +129,7 @@ static struct attribute *nilfs_snapshot_attrs[] = { NILFS_SNAPSHOT_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_snapshot); static ssize_t nilfs_snapshot_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) @@ -166,7 +167,7 @@ static const struct sysfs_ops nilfs_snapshot_attr_ops = { }; static struct kobj_type nilfs_snapshot_ktype = { - .default_attrs = nilfs_snapshot_attrs, + .default_groups = nilfs_snapshot_groups, .sysfs_ops = &nilfs_snapshot_attr_ops, .release = nilfs_snapshot_attr_release, }; @@ -226,6 +227,7 @@ static struct attribute *nilfs_mounted_snapshots_attrs[] = { NILFS_MOUNTED_SNAPSHOTS_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_mounted_snapshots); NILFS_DEV_INT_GROUP_OPS(mounted_snapshots, dev); NILFS_DEV_INT_GROUP_TYPE(mounted_snapshots, dev); @@ -339,6 +341,7 @@ static struct attribute *nilfs_checkpoints_attrs[] = { NILFS_CHECKPOINTS_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_checkpoints); NILFS_DEV_INT_GROUP_OPS(checkpoints, dev); NILFS_DEV_INT_GROUP_TYPE(checkpoints, dev); @@ -428,6 +431,7 @@ static struct attribute *nilfs_segments_attrs[] = { NILFS_SEGMENTS_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_segments); NILFS_DEV_INT_GROUP_OPS(segments, dev); NILFS_DEV_INT_GROUP_TYPE(segments, dev); @@ -689,6 +693,7 @@ static struct attribute *nilfs_segctor_attrs[] = { NILFS_SEGCTOR_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_segctor); NILFS_DEV_INT_GROUP_OPS(segctor, dev); NILFS_DEV_INT_GROUP_TYPE(segctor, dev); @@ -816,6 +821,7 @@ static struct attribute *nilfs_superblock_attrs[] = { NILFS_SUPERBLOCK_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_superblock); NILFS_DEV_INT_GROUP_OPS(superblock, dev); NILFS_DEV_INT_GROUP_TYPE(superblock, dev); @@ -924,6 +930,7 @@ static struct attribute *nilfs_dev_attrs[] = { NILFS_DEV_ATTR_LIST(README), NULL, }; +ATTRIBUTE_GROUPS(nilfs_dev); static ssize_t nilfs_dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) @@ -961,7 +968,7 @@ static const struct sysfs_ops nilfs_dev_attr_ops = { }; static struct kobj_type nilfs_dev_ktype = { - .default_attrs = nilfs_dev_attrs, + .default_groups = nilfs_dev_groups, .sysfs_ops = &nilfs_dev_attr_ops, .release = nilfs_dev_attr_release, }; diff --git a/include/linux/auxiliary_bus.h b/include/linux/auxiliary_bus.h index fc51d45f106b..de21d9d24a95 100644 --- a/include/linux/auxiliary_bus.h +++ b/include/linux/auxiliary_bus.h @@ -11,12 +11,172 @@ #include <linux/device.h> #include <linux/mod_devicetable.h> +/** + * DOC: DEVICE_LIFESPAN + * + * The registering driver is the entity that allocates memory for the + * auxiliary_device and registers it on the auxiliary bus. It is important to + * note that, as opposed to the platform bus, the registering driver is wholly + * responsible for the management of the memory used for the device object. + * + * To be clear the memory for the auxiliary_device is freed in the release() + * callback defined by the registering driver. The registering driver should + * only call auxiliary_device_delete() and then auxiliary_device_uninit() when + * it is done with the device. The release() function is then automatically + * called if and when other code releases their reference to the devices. + * + * A parent object, defined in the shared header file, contains the + * auxiliary_device. It also contains a pointer to the shared object(s), which + * also is defined in the shared header. Both the parent object and the shared + * object(s) are allocated by the registering driver. This layout allows the + * auxiliary_driver's registering module to perform a container_of() call to go + * from the pointer to the auxiliary_device, that is passed during the call to + * the auxiliary_driver's probe function, up to the parent object, and then + * have access to the shared object(s). + * + * The memory for the shared object(s) must have a lifespan equal to, or + * greater than, the lifespan of the memory for the auxiliary_device. The + * auxiliary_driver should only consider that the shared object is valid as + * long as the auxiliary_device is still registered on the auxiliary bus. It + * is up to the registering driver to manage (e.g. free or keep available) the + * memory for the shared object beyond the life of the auxiliary_device. + * + * The registering driver must unregister all auxiliary devices before its own + * driver.remove() is completed. An easy way to ensure this is to use the + * devm_add_action_or_reset() call to register a function against the parent + * device which unregisters the auxiliary device object(s). + * + * Finally, any operations which operate on the auxiliary devices must continue + * to function (if only to return an error) after the registering driver + * unregisters the auxiliary device. + */ + +/** + * struct auxiliary_device - auxiliary device object. + * @dev: Device, + * The release and parent fields of the device structure must be filled + * in + * @name: Match name found by the auxiliary device driver, + * @id: unique identitier if multiple devices of the same name are exported, + * + * An auxiliary_device represents a part of its parent device's functionality. + * It is given a name that, combined with the registering drivers + * KBUILD_MODNAME, creates a match_name that is used for driver binding, and an + * id that combined with the match_name provide a unique name to register with + * the bus subsystem. For example, a driver registering an auxiliary device is + * named 'foo_mod.ko' and the subdevice is named 'foo_dev'. The match name is + * therefore 'foo_mod.foo_dev'. + * + * Registering an auxiliary_device is a three-step process. + * + * First, a 'struct auxiliary_device' needs to be defined or allocated for each + * sub-device desired. The name, id, dev.release, and dev.parent fields of + * this structure must be filled in as follows. + * + * The 'name' field is to be given a name that is recognized by the auxiliary + * driver. If two auxiliary_devices with the same match_name, eg + * "foo_mod.foo_dev", are registered onto the bus, they must have unique id + * values (e.g. "x" and "y") so that the registered devices names are + * "foo_mod.foo_dev.x" and "foo_mod.foo_dev.y". If match_name + id are not + * unique, then the device_add fails and generates an error message. + * + * The auxiliary_device.dev.type.release or auxiliary_device.dev.release must + * be populated with a non-NULL pointer to successfully register the + * auxiliary_device. This release call is where resources associated with the + * auxiliary device must be free'ed. Because once the device is placed on the + * bus the parent driver can not tell what other code may have a reference to + * this data. + * + * The auxiliary_device.dev.parent should be set. Typically to the registering + * drivers device. + * + * Second, call auxiliary_device_init(), which checks several aspects of the + * auxiliary_device struct and performs a device_initialize(). After this step + * completes, any error state must have a call to auxiliary_device_uninit() in + * its resolution path. + * + * The third and final step in registering an auxiliary_device is to perform a + * call to auxiliary_device_add(), which sets the name of the device and adds + * the device to the bus. + * + * .. code-block:: c + * + * #define MY_DEVICE_NAME "foo_dev" + * + * ... + * + * struct auxiliary_device *my_aux_dev = my_aux_dev_alloc(xxx); + * + * // Step 1: + * my_aux_dev->name = MY_DEVICE_NAME; + * my_aux_dev->id = my_unique_id_alloc(xxx); + * my_aux_dev->dev.release = my_aux_dev_release; + * my_aux_dev->dev.parent = my_dev; + * + * // Step 2: + * if (auxiliary_device_init(my_aux_dev)) + * goto fail; + * + * // Step 3: + * if (auxiliary_device_add(my_aux_dev)) { + * auxiliary_device_uninit(my_aux_dev); + * goto fail; + * } + * + * ... + * + * + * Unregistering an auxiliary_device is a two-step process to mirror the + * register process. First call auxiliary_device_delete(), then call + * auxiliary_device_uninit(). + * + * .. code-block:: c + * + * auxiliary_device_delete(my_dev->my_aux_dev); + * auxiliary_device_uninit(my_dev->my_aux_dev); + */ struct auxiliary_device { struct device dev; const char *name; u32 id; }; +/** + * struct auxiliary_driver - Definition of an auxiliary bus driver + * @probe: Called when a matching device is added to the bus. + * @remove: Called when device is removed from the bus. + * @shutdown: Called at shut-down time to quiesce the device. + * @suspend: Called to put the device to sleep mode. Usually to a power state. + * @resume: Called to bring a device from sleep mode. + * @name: Driver name. + * @driver: Core driver structure. + * @id_table: Table of devices this driver should match on the bus. + * + * Auxiliary drivers follow the standard driver model convention, where + * discovery/enumeration is handled by the core, and drivers provide probe() + * and remove() methods. They support power management and shutdown + * notifications using the standard conventions. + * + * Auxiliary drivers register themselves with the bus by calling + * auxiliary_driver_register(). The id_table contains the match_names of + * auxiliary devices that a driver can bind with. + * + * .. code-block:: c + * + * static const struct auxiliary_device_id my_auxiliary_id_table[] = { + * { .name = "foo_mod.foo_dev" }, + * {}, + * }; + * + * MODULE_DEVICE_TABLE(auxiliary, my_auxiliary_id_table); + * + * struct auxiliary_driver my_drv = { + * .name = "myauxiliarydrv", + * .id_table = my_auxiliary_id_table, + * .probe = my_drv_probe, + * .remove = my_drv_remove + * }; + */ struct auxiliary_driver { int (*probe)(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id); void (*remove)(struct auxiliary_device *auxdev); @@ -28,6 +188,16 @@ struct auxiliary_driver { const struct auxiliary_device_id *id_table; }; +static inline void *auxiliary_get_drvdata(struct auxiliary_device *auxdev) +{ + return dev_get_drvdata(&auxdev->dev); +} + +static inline void auxiliary_set_drvdata(struct auxiliary_device *auxdev, void *data) +{ + dev_set_drvdata(&auxdev->dev, data); +} + static inline struct auxiliary_device *to_auxiliary_dev(struct device *dev) { return container_of(dev, struct auxiliary_device, dev); @@ -66,6 +236,10 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv); * Helper macro for auxiliary drivers which do not do anything special in * module init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() + * + * .. code-block:: c + * + * module_auxiliary_driver(my_drv); */ #define module_auxiliary_driver(__auxiliary_driver) \ module_driver(__auxiliary_driver, auxiliary_driver_register, auxiliary_driver_unregister) diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 3ccce6f24548..861c4f0f8a29 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -6,7 +6,6 @@ #ifndef __LINUX_KERNFS_H #define __LINUX_KERNFS_H -#include <linux/kernel.h> #include <linux/err.h> #include <linux/list.h> #include <linux/mutex.h> @@ -14,14 +13,18 @@ #include <linux/lockdep.h> #include <linux/rbtree.h> #include <linux/atomic.h> +#include <linux/bug.h> +#include <linux/types.h> #include <linux/uidgid.h> #include <linux/wait.h> +#include <linux/rwsem.h> struct file; struct dentry; struct iattr; struct seq_file; struct vm_area_struct; +struct vm_operations_struct; struct super_block; struct file_system_type; struct poll_table_struct; @@ -197,6 +200,7 @@ struct kernfs_root { struct list_head supers; wait_queue_head_t deactivate_waitq; + struct rw_semaphore kernfs_rwsem; }; struct kernfs_open_file { diff --git a/include/linux/kobject.h b/include/linux/kobject.h index efd56f990a46..c7b47399b36a 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -19,10 +19,10 @@ #include <linux/list.h> #include <linux/sysfs.h> #include <linux/compiler.h> +#include <linux/container_of.h> #include <linux/spinlock.h> #include <linux/kref.h> #include <linux/kobject_ns.h> -#include <linux/kernel.h> #include <linux/wait.h> #include <linux/atomic.h> #include <linux/workqueue.h> @@ -66,7 +66,7 @@ struct kobject { struct list_head entry; struct kobject *parent; struct kset *kset; - struct kobj_type *ktype; + const struct kobj_type *ktype; struct kernfs_node *sd; /* sysfs directory entry */ struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE @@ -90,13 +90,13 @@ static inline const char *kobject_name(const struct kobject *kobj) return kobj->name; } -extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype); +extern void kobject_init(struct kobject *kobj, const struct kobj_type *ktype); extern __printf(3, 4) __must_check int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...); extern __printf(4, 5) __must_check int kobject_init_and_add(struct kobject *kobj, - struct kobj_type *ktype, struct kobject *parent, + const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...); extern void kobject_del(struct kobject *kobj); @@ -117,23 +117,6 @@ extern void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid); extern char *kobject_get_path(struct kobject *kobj, gfp_t flag); -/** - * kobject_has_children - Returns whether a kobject has children. - * @kobj: the object to test - * - * This will return whether a kobject has other kobjects as children. - * - * It does NOT account for the presence of attribute files, only sub - * directories. It also assumes there is no concurrent addition or - * removal of such children, and thus relies on external locking. - */ -static inline bool kobject_has_children(struct kobject *kobj) -{ - WARN_ON_ONCE(kref_read(&kobj->kref) == 0); - - return kobj->sd && kobj->sd->dir.subdirs; -} - struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; @@ -153,10 +136,9 @@ struct kobj_uevent_env { }; struct kset_uevent_ops { - int (* const filter)(struct kset *kset, struct kobject *kobj); - const char *(* const name)(struct kset *kset, struct kobject *kobj); - int (* const uevent)(struct kset *kset, struct kobject *kobj, - struct kobj_uevent_env *env); + int (* const filter)(struct kobject *kobj); + const char *(* const name)(struct kobject *kobj); + int (* const uevent)(struct kobject *kobj, struct kobj_uevent_env *env); }; struct kobj_attribute { @@ -217,7 +199,7 @@ static inline void kset_put(struct kset *k) kobject_put(&k->kobj); } -static inline struct kobj_type *get_ktype(struct kobject *kobj) +static inline const struct kobj_type *get_ktype(struct kobject *kobj) { return kobj->ktype; } diff --git a/include/linux/topology.h b/include/linux/topology.h index 0b3704ad13c8..a6e201758ae9 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -180,6 +180,19 @@ static inline int cpu_to_mem(int cpu) #endif /* [!]CONFIG_HAVE_MEMORYLESS_NODES */ +#if defined(topology_die_id) && defined(topology_die_cpumask) +#define TOPOLOGY_DIE_SYSFS +#endif +#if defined(topology_cluster_id) && defined(topology_cluster_cpumask) +#define TOPOLOGY_CLUSTER_SYSFS +#endif +#if defined(topology_book_id) && defined(topology_book_cpumask) +#define TOPOLOGY_BOOK_SYSFS +#endif +#if defined(topology_drawer_id) && defined(topology_drawer_cpumask) +#define TOPOLOGY_DRAWER_SYSFS +#endif + #ifndef topology_physical_package_id #define topology_physical_package_id(cpu) ((void)(cpu), -1) #endif @@ -192,6 +205,12 @@ static inline int cpu_to_mem(int cpu) #ifndef topology_core_id #define topology_core_id(cpu) ((void)(cpu), 0) #endif +#ifndef topology_book_id +#define topology_book_id(cpu) ((void)(cpu), -1) +#endif +#ifndef topology_drawer_id +#define topology_drawer_id(cpu) ((void)(cpu), -1) +#endif #ifndef topology_sibling_cpumask #define topology_sibling_cpumask(cpu) cpumask_of(cpu) #endif @@ -204,6 +223,12 @@ static inline int cpu_to_mem(int cpu) #ifndef topology_die_cpumask #define topology_die_cpumask(cpu) cpumask_of(cpu) #endif +#ifndef topology_book_cpumask +#define topology_book_cpumask(cpu) cpumask_of(cpu) +#endif +#ifndef topology_drawer_cpumask +#define topology_drawer_cpumask(cpu) cpumask_of(cpu) +#endif #if defined(CONFIG_SCHED_SMT) && !defined(cpu_smt_mask) static inline const struct cpumask *cpu_smt_mask(int cpu) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index ff6ccbc6efe9..318f3f1f9e92 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -1086,7 +1086,11 @@ /* Designated Vendor-Specific (DVSEC, PCI_EXT_CAP_ID_DVSEC) */ #define PCI_DVSEC_HEADER1 0x4 /* Designated Vendor-Specific Header1 */ +#define PCI_DVSEC_HEADER1_VID(x) ((x) & 0xffff) +#define PCI_DVSEC_HEADER1_REV(x) (((x) >> 16) & 0xf) +#define PCI_DVSEC_HEADER1_LEN(x) (((x) >> 20) & 0xfff) #define PCI_DVSEC_HEADER2 0x8 /* Designated Vendor-Specific Header2 */ +#define PCI_DVSEC_HEADER2_ID(x) ((x) & 0xffff) /* Data Link Feature */ #define PCI_DLF_CAP 0x04 /* Capabilities Register */ diff --git a/kernel/params.c b/kernel/params.c index 8299bd764e42..5b92310425c5 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -926,9 +926,9 @@ static const struct sysfs_ops module_sysfs_ops = { .store = module_attr_store, }; -static int uevent_filter(struct kset *kset, struct kobject *kobj) +static int uevent_filter(struct kobject *kobj) { - struct kobj_type *ktype = get_ktype(kobj); + const struct kobj_type *ktype = get_ktype(kobj); if (ktype == &module_ktype) return 1; diff --git a/lib/kobject.c b/lib/kobject.c index 4a56f519139d..56fa037501b5 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -65,7 +65,7 @@ void kobject_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid) */ static int populate_dir(struct kobject *kobj) { - struct kobj_type *t = get_ktype(kobj); + const struct kobj_type *t = get_ktype(kobj); struct attribute *attr; int error = 0; int i; @@ -346,7 +346,7 @@ EXPORT_SYMBOL(kobject_set_name); * to kobject_put(), not by a call to kfree directly to ensure that all of * the memory is cleaned up properly. */ -void kobject_init(struct kobject *kobj, struct kobj_type *ktype) +void kobject_init(struct kobject *kobj, const struct kobj_type *ktype) { char *err_str; @@ -461,7 +461,7 @@ EXPORT_SYMBOL(kobject_add); * same type of error handling after a call to kobject_add() and kobject * lifetime rules are the same here. */ -int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, +int kobject_init_and_add(struct kobject *kobj, const struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) { va_list args; @@ -679,7 +679,7 @@ EXPORT_SYMBOL(kobject_get_unless_zero); static void kobject_cleanup(struct kobject *kobj) { struct kobject *parent = kobj->parent; - struct kobj_type *t = get_ktype(kobj); + const struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; pr_debug("kobject: '%s' (%p): %s, parent %p\n", diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index c87d5b6a8a55..7c44b7ae4c5c 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -501,7 +501,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, } /* skip the event, if the filter returns zero. */ if (uevent_ops && uevent_ops->filter) - if (!uevent_ops->filter(kset, kobj)) { + if (!uevent_ops->filter(kobj)) { pr_debug("kobject: '%s' (%p): %s: filter function " "caused the event to drop!\n", kobject_name(kobj), kobj, __func__); @@ -510,7 +510,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* originating subsystem */ if (uevent_ops && uevent_ops->name) - subsystem = uevent_ops->name(kset, kobj); + subsystem = uevent_ops->name(kobj); else subsystem = kobject_name(&kset->kobj); if (!subsystem) { @@ -554,7 +554,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* let the kset specific function add its stuff */ if (uevent_ops && uevent_ops->uevent) { - retval = uevent_ops->uevent(kset, kobj, env); + retval = uevent_ops->uevent(kobj, env); if (retval) { pr_debug("kobject: '%s' (%p): %s: uevent() returned " "%d\n", kobject_name(kobj), kobj, |