Commit 38047d5c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'driver-core-4.17-rc1' of...

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

Pull driver core updates from Greg KH:
 "Here is the "big" set of driver core patches for 4.17-rc1.

  There's really not much here, just a bunch of firmware code
  refactoring from Luis as he attempts to wrangle that codebase into
  something that is managable, along with a bunch of userspace tests for
  it. Other than that, a handful of small bugfixes and reverts of things
  that didn't work out.

  Full details are in the shortlog, it's not all that much.

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

* tag 'driver-core-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (30 commits)
  drivers: base: remove check for callback in coredump_store()
  mt7601u: use firmware_request_cache() to address cache on reboot
  firmware: add firmware_request_cache() to help with cache on reboot
  firmware: fix typo on pr_info_once() when ignore_sysfs_fallback is used
  firmware: explicitly include vmalloc.h
  firmware: ensure the firmware cache is not used on incompatible calls
  test_firmware: modify custom fallback tests to use unique files
  firmware: add helper to check to see if fw cache is setup
  firmware: fix checking for return values for fw_add_devm_name()
  rename: _request_firmware_load() fw_load_sysfs_fallback()
  test_firmware: test three firmware kernel configs using a proc knob
  test_firmware: expand on library with shared helpers
  firmware: enable to force disable the fallback mechanism at run time
  firmware: enable run time change of forcing fallback loader
  firmware: move firmware loader into its own directory
  firmware: split firmware fallback functionality into its own file
  firmware: move loading timeout under struct firmware_fallback_config
  firmware: use helpers for setting up a temporary cache timeout
  firmware: simplify CONFIG_FW_LOADER_USER_HELPER_FALLBACK further
  drivers: base: add description for .coredump() callback
  ...
parents df34df48 1fe56e0c
...@@ -112,7 +112,7 @@ Since a device is created for the sysfs interface to help load firmware as a ...@@ -112,7 +112,7 @@ Since a device is created for the sysfs interface to help load firmware as a
fallback mechanism userspace can be informed of the addition of the device by fallback mechanism userspace can be informed of the addition of the device by
relying on kobject uevents. The addition of the device into the device relying on kobject uevents. The addition of the device into the device
hierarchy means the fallback mechanism for firmware loading has been initiated. hierarchy means the fallback mechanism for firmware loading has been initiated.
For details of implementation refer to _request_firmware_load(), in particular For details of implementation refer to fw_load_sysfs_fallback(), in particular
on the use of dev_set_uevent_suppress() and kobject_uevent(). on the use of dev_set_uevent_suppress() and kobject_uevent().
The kernel's kobject uevent mechanism is implemented in lib/kobject_uevent.c, The kernel's kobject uevent mechanism is implemented in lib/kobject_uevent.c,
......
...@@ -44,6 +44,20 @@ request_firmware_nowait ...@@ -44,6 +44,20 @@ request_firmware_nowait
.. kernel-doc:: drivers/base/firmware_class.c .. kernel-doc:: drivers/base/firmware_class.c
:functions: request_firmware_nowait :functions: request_firmware_nowait
Special optimizations on reboot
===============================
Some devices have an optimization in place to enable the firmware to be
retained during system reboot. When such optimizations are used the driver
author must ensure the firmware is still available on resume from suspend,
this can be done with firmware_request_cache() insted of requesting for the
firmare to be loaded.
firmware_request_cache()
-----------------------
.. kernel-doc:: drivers/base/firmware_class.c
:functions: firmware_request_cache
request firmware API expected driver use request firmware API expected driver use
======================================== ========================================
......
...@@ -5524,7 +5524,7 @@ M: Luis R. Rodriguez <mcgrof@kernel.org> ...@@ -5524,7 +5524,7 @@ M: Luis R. Rodriguez <mcgrof@kernel.org>
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
S: Maintained S: Maintained
F: Documentation/firmware_class/ F: Documentation/firmware_class/
F: drivers/base/firmware*.c F: drivers/base/firmware_loader/
F: include/linux/firmware.h F: include/linux/firmware.h
FLASH ADAPTER DRIVER (IBM Flash Adapter 900GB Full Height PCI Flash Card) FLASH ADAPTER DRIVER (IBM Flash Adapter 900GB Full Height PCI Flash Card)
......
...@@ -13,7 +13,7 @@ obj-y += power/ ...@@ -13,7 +13,7 @@ obj-y += power/
obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAS_DMA) += dma-mapping.o
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
obj-$(CONFIG_ISA_BUS_API) += isa.o obj-$(CONFIG_ISA_BUS_API) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-y += firmware_loader/
obj-$(CONFIG_NUMA) += node.o obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
ifeq ($(CONFIG_SYSFS),y) ifeq ($(CONFIG_SYSFS),y)
......
...@@ -169,11 +169,11 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu) ...@@ -169,11 +169,11 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
} }
#ifdef CONFIG_CPU_FREQ #ifdef CONFIG_CPU_FREQ
static cpumask_var_t cpus_to_visit __initdata; static cpumask_var_t cpus_to_visit;
static void __init parsing_done_workfn(struct work_struct *work); static void parsing_done_workfn(struct work_struct *work);
static __initdata DECLARE_WORK(parsing_done_work, parsing_done_workfn); static DECLARE_WORK(parsing_done_work, parsing_done_workfn);
static int __init static int
init_cpu_capacity_callback(struct notifier_block *nb, init_cpu_capacity_callback(struct notifier_block *nb,
unsigned long val, unsigned long val,
void *data) void *data)
...@@ -209,7 +209,7 @@ init_cpu_capacity_callback(struct notifier_block *nb, ...@@ -209,7 +209,7 @@ init_cpu_capacity_callback(struct notifier_block *nb,
return 0; return 0;
} }
static struct notifier_block init_cpu_capacity_notifier __initdata = { static struct notifier_block init_cpu_capacity_notifier = {
.notifier_call = init_cpu_capacity_callback, .notifier_call = init_cpu_capacity_callback,
}; };
...@@ -242,7 +242,7 @@ static int __init register_cpufreq_notifier(void) ...@@ -242,7 +242,7 @@ static int __init register_cpufreq_notifier(void)
} }
core_initcall(register_cpufreq_notifier); core_initcall(register_cpufreq_notifier);
static void __init parsing_done_workfn(struct work_struct *work) static void parsing_done_workfn(struct work_struct *work)
{ {
cpufreq_unregister_notifier(&init_cpu_capacity_notifier, cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
......
...@@ -382,8 +382,10 @@ int register_cpu(struct cpu *cpu, int num) ...@@ -382,8 +382,10 @@ int register_cpu(struct cpu *cpu, int num)
if (cpu->hotpluggable) if (cpu->hotpluggable)
cpu->dev.groups = hotplugable_cpu_attr_groups; cpu->dev.groups = hotplugable_cpu_attr_groups;
error = device_register(&cpu->dev); error = device_register(&cpu->dev);
if (error) if (error) {
put_device(&cpu->dev);
return error; return error;
}
per_cpu(cpu_sys_devices, num) = &cpu->dev; per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num)); register_cpu_under_node(num, cpu_to_node(num));
......
...@@ -292,8 +292,7 @@ static ssize_t coredump_store(struct device *dev, struct device_attribute *attr, ...@@ -292,8 +292,7 @@ static ssize_t coredump_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
device_lock(dev); device_lock(dev);
if (dev->driver->coredump) dev->driver->coredump(dev);
dev->driver->coredump(dev);
device_unlock(dev); device_unlock(dev);
return count; return count;
......
# SPDX-License-Identifier: GPL-2.0
# Makefile for the Linux firmware loader
obj-y := fallback_table.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
firmware_class-objs := main.o
firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __FIRMWARE_FALLBACK_H
#define __FIRMWARE_FALLBACK_H
#include <linux/firmware.h>
#include <linux/device.h>
/**
* struct firmware_fallback_config - firmware fallback configuratioon settings
*
* Helps describe and fine tune the fallback mechanism.
*
* @force_sysfs_fallback: force the sysfs fallback mechanism to be used
* as if one had enabled CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y.
* Useful to help debug a CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
* functionality on a kernel where that config entry has been disabled.
* @ignore_sysfs_fallback: force to disable the sysfs fallback mechanism.
* This emulates the behaviour as if we had set the kernel
* config CONFIG_FW_LOADER_USER_HELPER=n.
* @old_timeout: for internal use
* @loading_timeout: the timeout to wait for the fallback mechanism before
* giving up, in seconds.
*/
struct firmware_fallback_config {
unsigned int force_sysfs_fallback;
unsigned int ignore_sysfs_fallback;
int old_timeout;
int loading_timeout;
};
#ifdef CONFIG_FW_LOADER_USER_HELPER
int fw_sysfs_fallback(struct firmware *fw, const char *name,
struct device *device,
unsigned int opt_flags,
int ret);
void kill_pending_fw_fallback_reqs(bool only_kill_custom);
void fw_fallback_set_cache_timeout(void);
void fw_fallback_set_default_timeout(void);
int register_sysfs_loader(void);
void unregister_sysfs_loader(void);
#else /* CONFIG_FW_LOADER_USER_HELPER */
static inline int fw_sysfs_fallback(struct firmware *fw, const char *name,
struct device *device,
unsigned int opt_flags,
int ret)
{
/* Keep carrying over the same error */
return ret;
}
static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
static inline void fw_fallback_set_cache_timeout(void) { }
static inline void fw_fallback_set_default_timeout(void) { }
static inline int register_sysfs_loader(void)
{
return 0;
}
static inline void unregister_sysfs_loader(void)
{
}
#endif /* CONFIG_FW_LOADER_USER_HELPER */
#endif /* __FIRMWARE_FALLBACK_H */
// SPDX-License-Identifier: GPL-2.0
#include <linux/types.h>
#include <linux/kconfig.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/highmem.h>
#include <linux/umh.h>
#include <linux/sysctl.h>
#include "fallback.h"
#include "firmware.h"
/*
* firmware fallback configuration table
*/
/* Module or buit-in */
#ifdef CONFIG_FW_LOADER_USER_HELPER
static unsigned int zero;
static unsigned int one = 1;
struct firmware_fallback_config fw_fallback_config = {
.force_sysfs_fallback = IS_ENABLED(CONFIG_FW_LOADER_USER_HELPER_FALLBACK),
.loading_timeout = 60,
.old_timeout = 60,
};
EXPORT_SYMBOL_GPL(fw_fallback_config);
struct ctl_table firmware_config_table[] = {
{
.procname = "force_sysfs_fallback",
.data = &fw_fallback_config.force_sysfs_fallback,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_douintvec_minmax,
.extra1 = &zero,
.extra2 = &one,
},
{
.procname = "ignore_sysfs_fallback",
.data = &fw_fallback_config.ignore_sysfs_fallback,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_douintvec_minmax,
.extra1 = &zero,
.extra2 = &one,
},
{ }
};
EXPORT_SYMBOL_GPL(firmware_config_table);
#endif
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __FIRMWARE_LOADER_H
#define __FIRMWARE_LOADER_H
#include <linux/firmware.h>
#include <linux/types.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <generated/utsrelease.h>
/* firmware behavior options */
#define FW_OPT_UEVENT (1U << 0)
#define FW_OPT_NOWAIT (1U << 1)
#define FW_OPT_USERHELPER (1U << 2)
#define FW_OPT_NO_WARN (1U << 3)
#define FW_OPT_NOCACHE (1U << 4)
#define FW_OPT_NOFALLBACK (1U << 5)
enum fw_status {
FW_STATUS_UNKNOWN,
FW_STATUS_LOADING,
FW_STATUS_DONE,
FW_STATUS_ABORTED,
};
/*
* Concurrent request_firmware() for the same firmware need to be
* serialized. struct fw_state is simple state machine which hold the
* state of the firmware loading.
*/
struct fw_state {
struct completion completion;
enum fw_status status;
};
struct fw_priv {
struct kref ref;
struct list_head list;
struct firmware_cache *fwc;
struct fw_state fw_st;
void *data;
size_t size;
size_t allocated_size;
#ifdef CONFIG_FW_LOADER_USER_HELPER
bool is_paged_buf;
bool need_uevent;
struct page **pages;
int nr_pages;
int page_array_size;
struct list_head pending_list;
#endif
const char *fw_name;
};
extern struct mutex fw_lock;
static inline bool __fw_state_check(struct fw_priv *fw_priv,
enum fw_status status)
{
struct fw_state *fw_st = &fw_priv->fw_st;
return fw_st->status == status;
}
static inline int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
{
struct fw_state *fw_st = &fw_priv->fw_st;
long ret;
ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
if (ret != 0 && fw_st->status == FW_STATUS_ABORTED)
return -ENOENT;
if (!ret)
return -ETIMEDOUT;
return ret < 0 ? ret : 0;
}
static inline void __fw_state_set(struct fw_priv *fw_priv,
enum fw_status status)
{
struct fw_state *fw_st = &fw_priv->fw_st;
WRITE_ONCE(fw_st->status, status);
if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
complete_all(&fw_st->completion);
}
static inline void fw_state_aborted(struct fw_priv *fw_priv)
{
__fw_state_set(fw_priv, FW_STATUS_ABORTED);
}
static inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
{
return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
}
static inline void fw_state_start(struct fw_priv *fw_priv)
{
__fw_state_set(fw_priv, FW_STATUS_LOADING);
}
static inline void fw_state_done(struct fw_priv *fw_priv)
{
__fw_state_set(fw_priv, FW_STATUS_DONE);
}
int assign_fw(struct firmware *fw, struct device *device,
unsigned int opt_flags);
#endif /* __FIRMWARE_LOADER_H */
...@@ -315,7 +315,9 @@ static int register_node(struct node *node, int num) ...@@ -315,7 +315,9 @@ static int register_node(struct node *node, int num)
node->dev.groups = node_dev_groups; node->dev.groups = node_dev_groups;
error = device_register(&node->dev); error = device_register(&node->dev);
if (!error){ if (error)
put_device(&node->dev);
else {
hugetlb_register_node(node); hugetlb_register_node(node);
compaction_register_node(node); compaction_register_node(node);
......
...@@ -1153,8 +1153,10 @@ int __init platform_bus_init(void) ...@@ -1153,8 +1153,10 @@ int __init platform_bus_init(void)
early_platform_cleanup(); early_platform_cleanup();
error = device_register(&platform_bus); error = device_register(&platform_bus);
if (error) if (error) {
put_device(&platform_bus);
return error; return error;
}
error = bus_register(&platform_bus_type); error = bus_register(&platform_bus_type);
if (error) if (error)
device_unregister(&platform_bus); device_unregister(&platform_bus);
......
...@@ -150,6 +150,8 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr ...@@ -150,6 +150,8 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
out3: out3:
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
put_device(&soc_dev->dev);
soc_dev = NULL;
out2: out2:
kfree(soc_dev); kfree(soc_dev);
out1: out1:
......
...@@ -420,7 +420,7 @@ static int mt7601u_load_firmware(struct mt7601u_dev *dev) ...@@ -420,7 +420,7 @@ static int mt7601u_load_firmware(struct mt7601u_dev *dev)
MT_USB_DMA_CFG_TX_BULK_EN)); MT_USB_DMA_CFG_TX_BULK_EN));
if (firmware_running(dev)) if (firmware_running(dev))
return 0; return firmware_request_cache(dev->dev, MT7601U_FIRMWARE);
ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev); ret = request_firmware(&fw, MT7601U_FIRMWARE, dev->dev);
if (ret) if (ret)
......
...@@ -256,6 +256,7 @@ enum probe_type { ...@@ -256,6 +256,7 @@ enum probe_type {
* automatically. * automatically.
* @pm: Power management operations of the device which matched * @pm: Power management operations of the device which matched
* this driver. * this driver.
* @coredump: Called through sysfs to initiate a device coredump.
* @p: Driver core's private data, no one other than the driver * @p: Driver core's private data, no one other than the driver
* core can touch this. * core can touch this.
* *
......
...@@ -85,4 +85,7 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p, ...@@ -85,4 +85,7 @@ static inline int request_firmware_into_buf(const struct firmware **firmware_p,
} }
#endif #endif
int firmware_request_cache(struct device *device, const char *name);
#endif #endif
...@@ -253,6 +253,10 @@ extern struct ctl_table random_table[]; ...@@ -253,6 +253,10 @@ extern struct ctl_table random_table[];
extern struct ctl_table epoll_table[]; extern struct ctl_table epoll_table[];
#endif #endif
#ifdef CONFIG_FW_LOADER_USER_HELPER
extern struct ctl_table firmware_config_table[];
#endif
#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT #ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
int sysctl_legacy_va_layout; int sysctl_legacy_va_layout;
#endif #endif
...@@ -748,6 +752,13 @@ static struct ctl_table kern_table[] = { ...@@ -748,6 +752,13 @@ static struct ctl_table kern_table[] = {
.mode = 0555, .mode = 0555,
.child = usermodehelper_table, .child = usermodehelper_table,
}, },
#ifdef CONFIG_FW_LOADER_USER_HELPER
{
.procname = "firmware_config",
.mode = 0555,
.child = firmware_config_table,
},
#endif
{ {
.procname = "overflowuid", .procname = "overflowuid",
.data = &overflowuid, .data = &overflowuid,
......
...@@ -204,8 +204,9 @@ static int kobject_add_internal(struct kobject *kobj) ...@@ -204,8 +204,9 @@ static int kobject_add_internal(struct kobject *kobj)
return -ENOENT; return -ENOENT;
if (!kobj->name || !kobj->name[0]) { if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty " WARN(1,
"name!\n", kobj); "kobject: (%p): attempted to be registered with empty name!\n",
kobj);
return -EINVAL; return -EINVAL;
} }
...@@ -232,9 +233,8 @@ static int kobject_add_internal(struct kobject *kobj) ...@@ -232,9 +233,8 @@ static int kobject_add_internal(struct kobject *kobj)
/* be noisy on error issues */ /* be noisy on error issues */
if (error == -EEXIST) if (error == -EEXIST)
WARN(1, "%s failed for %s with " WARN(1,
"-EEXIST, don't try to register things with " "%s failed for %s with -EEXIST, don't try to register things with the same name in the same directory.\n",
"the same name in the same directory.\n",
__func__, kobject_name(kobj)); __func__, kobject_name(kobj));
else else
WARN(1, "%s failed for %s (error: %d parent: %s)\n", WARN(1, "%s failed for %s (error: %d parent: %s)\n",
...@@ -334,8 +334,8 @@ void kobject_init(struct kobject *kobj, struct kobj_type *ktype) ...@@ -334,8 +334,8 @@ void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
} }
if (kobj->state_initialized) { if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */ /* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized " pr_err("kobject (%p): tried to init an initialized object, something is seriously wrong.\n",
"object, something is seriously wrong.\n", kobj); kobj);
dump_stack(); dump_stack();
} }
...@@ -344,7 +344,7 @@ void kobject_init(struct kobject *kobj, struct kobj_type *ktype) ...@@ -344,7 +344,7 @@ void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
return; return;
error: error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); pr_err("kobject (%p): %s\n", kobj, err_str);
dump_stack(); dump_stack();
} }
EXPORT_SYMBOL(kobject_init); EXPORT_SYMBOL(kobject_init);
...@@ -357,7 +357,7 @@ static __printf(3, 0) int kobject_add_varg(struct kobject *kobj, ...@@ -357,7 +357,7 @@ static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
retval = kobject_set_name_vargs(kobj, fmt, vargs); retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) { if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n"); pr_err("kobject: can not set name properly!\n");
return retval; return retval;
} }
kobj->parent = parent; kobj->parent = parent;
...@@ -399,8 +399,7 @@ int kobject_add(struct kobject *kobj, struct kobject *parent, ...@@ -399,8 +399,7 @@ int kobject_add(struct kobject *kobj, struct kobject *parent,
return -EINVAL; return -EINVAL;
if (!kobj->state_initialized) { if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an " pr_err("kobject '%s' (%p): tried to add an uninitialized object, something is seriously wrong.\n",
"uninitialized object, something is seriously wrong.\n",
kobject_name(kobj), kobj); kobject_name(kobj), kobj);
dump_stack(); dump_stack();
return -EINVAL; return -EINVAL;
...@@ -590,9 +589,9 @@ struct kobject *kobject_get(struct kobject *kobj) ...@@ -590,9 +589,9 @@ struct kobject *kobject_get(struct kobject *kobj)
{ {
if (kobj) { if (kobj) {
if (!kobj->state_initialized) if (!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not " WARN(1, KERN_WARNING
"initialized, yet kobject_get() is being " "kobject: '%s' (%p): is not initialized, yet kobject_get() is being called.\n",
"called.\n", kobject_name(kobj), kobj); kobject_name(kobj), kobj);
kref_get(&kobj->kref); kref_get(&kobj->kref);
} }
return kobj; return kobj;
...@@ -622,8 +621,7 @@ static void kobject_cleanup(struct kobject *kobj) ...@@ -622,8 +621,7 @@ static void kobject_cleanup(struct kobject *kobj)
kobject_name(kobj), kobj, __func__, kobj->parent); kobject_name(kobj), kobj, __func__, kobj->parent);
if (t && !t->release) if (t && !t->release)
pr_debug("kobject: '%s' (%p): does not have a release() " pr_debug("kobject: '%s' (%p): does not have a release() function, it is broken and must be fixed.\n",
"function, it is broken and must be fixed.\n",
kobject_name(kobj), kobj); kobject_name(kobj), kobj);
/* send "remove" if the caller did not do it but sent "add" */ /* send "remove" if the caller did not do it but sent "add" */
...@@ -686,9 +684,9 @@ void kobject_put(struct kobject *kobj) ...@@ -686,9 +684,9 @@ void kobject_put(struct kobject *kobj)
{ {
if (kobj) { if (kobj) {
if (!kobj->state_initialized) if (!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not " WARN(1, KERN_WARNING
"initialized, yet kobject_put() is being " "kobject: '%s' (%p): is not initialized, yet kobject_put() is being called.\n",
"called.\n", kobject_name(kobj), kobj); kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release); kref_put(&kobj->kref, kobject_release);
} }
} }
...@@ -752,8 +750,7 @@ struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) ...@@ -752,8 +750,7 @@ struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
retval = kobject_add(kobj, parent, "%s", name); retval = kobject_add(kobj, parent, "%s", name);
if (retval) { if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d\n", pr_warn("%s: kobject_add error: %d\n", __func__, retval);
__func__, retval);
kobject_put(kobj); kobject_put(kobj);
kobj = NULL; kobj = NULL;
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" # No binaries, but make sure arg-less "make" doesn't trigger "run_tests"
all: all:
TEST_PROGS := fw_filesystem.sh fw_fallback.sh TEST_PROGS := fw_run_tests.sh
include ../lib.mk include ../lib.mk
......
CONFIG_TEST_FIRMWARE=y CONFIG_TEST_FIRMWARE=y
CONFIG_FW_LOADER=y
CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
#!/bin/sh #!/bin/bash
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# This validates that the kernel will fall back to using the fallback mechanism # This validates that the kernel will fall back to using the fallback mechanism
# to load firmware it can't find on disk itself. We must request a firmware # to load firmware it can't find on disk itself. We must request a firmware
...@@ -6,31 +6,17 @@ ...@@ -6,31 +6,17 @@
# won't find so that we can do the load ourself manually. # won't find so that we can do the load ourself manually.
set -e set -e
modprobe test_firmware TEST_REQS_FW_SYSFS_FALLBACK="yes"
TEST_REQS_FW_SET_CUSTOM_PATH="no"
TEST_DIR=$(dirname $0)
source $TEST_DIR/fw_lib.sh
DIR=/sys/devices/virtual/misc/test_firmware check_mods
check_setup
verify_reqs
setup_tmp_file
# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ trap "test_finish" EXIT
# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that
# as an indicator for CONFIG_FW_LOADER_USER_HELPER.
HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi)
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
else
echo "usermode helper disabled so ignoring test"
exit 0
fi
FWPATH=$(mktemp -d)
FW="$FWPATH/test-firmware.bin"
test_finish()
{
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
rm -f "$FW"
rmdir "$FWPATH"
}
load_fw() load_fw()
{ {
...@@ -169,12 +155,6 @@ load_fw_fallback_with_child() ...@@ -169,12 +155,6 @@ load_fw_fallback_with_child()
return $RET return $RET
} }
trap "test_finish" EXIT
# This is an unlikely real-world firmware content. :)
echo "ABCD0123" >"$FW"
NAME=$(basename "$FW")
test_syfs_timeout() test_syfs_timeout()
{ {
DEVPATH="$DIR"/"nope-$NAME"/loading DEVPATH="$DIR"/"nope-$NAME"/loading
...@@ -258,8 +238,10 @@ run_sysfs_main_tests() ...@@ -258,8 +238,10 @@ run_sysfs_main_tests()
run_sysfs_custom_load_tests() run_sysfs_custom_load_tests()
{ {
if load_fw_custom "$NAME" "$FW" ; then RANDOM_FILE_PATH=$(setup_random_file)
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
echo "$0: firmware was not loaded" >&2 echo "$0: firmware was not loaded" >&2
exit 1 exit 1
else else
...@@ -267,8 +249,10 @@ run_sysfs_custom_load_tests() ...@@ -267,8 +249,10 @@ run_sysfs_custom_load_tests()
fi fi
fi fi
if load_fw_custom "$NAME" "$FW" ; then RANDOM_FILE_PATH=$(setup_random_file)
if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
if load_fw_custom "$RANDOM_FILE" "$RANDOM_FILE_PATH" ; then
if ! diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
echo "$0: firmware was not loaded" >&2 echo "$0: firmware was not loaded" >&2
exit 1 exit 1
else else
...@@ -276,8 +260,12 @@ run_sysfs_custom_load_tests() ...@@ -276,8 +260,12 @@ run_sysfs_custom_load_tests()
fi fi
fi fi
if load_fw_custom_cancel "nope-$NAME" "$FW" ; then RANDOM_FILE_REAL="$RANDOM_FILE_PATH"
if diff -q "$FW" /dev/test_firmware >/dev/null ; then FAKE_RANDOM_FILE_PATH=$(setup_random_file_fake)
FAKE_RANDOM_FILE="$(basename $FAKE_RANDOM_FILE_PATH)"
if load_fw_custom_cancel "$FAKE_RANDOM_FILE" "$RANDOM_FILE_REAL" ; then
if diff -q "$RANDOM_FILE_PATH" /dev/test_firmware >/dev/null ; then
echo "$0: firmware was expected to be cancelled" >&2 echo "$0: firmware was expected to be cancelled" >&2
exit 1 exit 1
else else
...@@ -286,7 +274,10 @@ run_sysfs_custom_load_tests() ...@@ -286,7 +274,10 @@ run_sysfs_custom_load_tests()
fi fi
} }
run_sysfs_main_tests if [ "$HAS_FW_LOADER_USER_HELPER_FALLBACK" = "yes" ]; then
run_sysfs_main_tests
fi
run_sysfs_custom_load_tests run_sysfs_custom_load_tests
exit 0 exit 0
#!/bin/sh #!/bin/bash
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
# This validates that the kernel will load firmware out of its list of # This validates that the kernel will load firmware out of its list of
# firmware locations on disk. Since the user helper does similar work, # firmware locations on disk. Since the user helper does similar work,
...@@ -6,52 +6,15 @@ ...@@ -6,52 +6,15 @@
# know so we can be sure we're not accidentally testing the user helper. # know so we can be sure we're not accidentally testing the user helper.
set -e set -e
DIR=/sys/devices/virtual/misc/test_firmware TEST_REQS_FW_SYSFS_FALLBACK="no"
TEST_REQS_FW_SET_CUSTOM_PATH="yes"
TEST_DIR=$(dirname $0) TEST_DIR=$(dirname $0)
source $TEST_DIR/fw_lib.sh
test_modprobe() check_mods
{ check_setup
if [ ! -d $DIR ]; then verify_reqs
echo "$0: $DIR not present" setup_tmp_file
echo "You must have the following enabled in your kernel:"
cat $TEST_DIR/config
exit 1
fi
}
trap "test_modprobe" EXIT
if [ ! -d $DIR ]; then
modprobe test_firmware
fi
# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/
# These days most distros enable CONFIG_FW_LOADER_USER_HELPER but disable
# CONFIG_FW_LOADER_USER_HELPER_FALLBACK. We use /sys/class/firmware/ as an
# indicator for CONFIG_FW_LOADER_USER_HELPER.
HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi)
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
OLD_TIMEOUT=$(cat /sys/class/firmware/timeout)
fi
OLD_FWPATH=$(cat /sys/module/firmware_class/parameters/path)
FWPATH=$(mktemp -d)
FW="$FWPATH/test-firmware.bin"
test_finish()
{
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
fi
if [ "$OLD_FWPATH" = "" ]; then
OLD_FWPATH=" "
fi
echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
rm -f "$FW"
rmdir "$FWPATH"
}
trap "test_finish" EXIT trap "test_finish" EXIT
...@@ -60,14 +23,6 @@ if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then ...@@ -60,14 +23,6 @@ if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo 1 >/sys/class/firmware/timeout echo 1 >/sys/class/firmware/timeout
fi fi
# Set the kernel search path.
echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path
# This is an unlikely real-world firmware content. :)
echo "ABCD0123" >"$FW"
NAME=$(basename "$FW")
if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then
echo "$0: empty filename should not succeed" >&2 echo "$0: empty filename should not succeed" >&2
exit 1 exit 1
...@@ -275,10 +230,13 @@ test_wait_and_cancel_custom_load() ...@@ -275,10 +230,13 @@ test_wait_and_cancel_custom_load()
test_request_firmware_nowait_custom_nofile() test_request_firmware_nowait_custom_nofile()
{ {
echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: " echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: "
config_reset
config_unset_uevent config_unset_uevent
config_set_name nope-test-firmware.bin RANDOM_FILE_PATH=$(setup_random_file_fake)
RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
config_set_name $RANDOM_FILE
config_trigger_async & config_trigger_async &
test_wait_and_cancel_custom_load nope-test-firmware.bin test_wait_and_cancel_custom_load $RANDOM_FILE
wait wait
release_all_firmware release_all_firmware
echo "OK" echo "OK"
...@@ -316,7 +274,11 @@ test_request_firmware_nowait_uevent() ...@@ -316,7 +274,11 @@ test_request_firmware_nowait_uevent()
test_request_firmware_nowait_custom() test_request_firmware_nowait_custom()
{ {
echo -n "Batched request_firmware_nowait(uevent=false) try #$1: " echo -n "Batched request_firmware_nowait(uevent=false) try #$1: "
config_reset
config_unset_uevent config_unset_uevent
RANDOM_FILE_PATH=$(setup_random_file)
RANDOM_FILE="$(basename $RANDOM_FILE_PATH)"
config_set_name $RANDOM_FILE
config_trigger_async config_trigger_async
release_all_firmware release_all_firmware
echo "OK" echo "OK"
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Library of helpers for test scripts.
set -e
DIR=/sys/devices/virtual/misc/test_firmware
PROC_CONFIG="/proc/config.gz"
TEST_DIR=$(dirname $0)
print_reqs_exit()
{
echo "You must have the following enabled in your kernel:" >&2
cat $TEST_DIR/config >&2
exit 1
}
test_modprobe()
{
if [ ! -d $DIR ]; then
print_reqs_exit
fi
}
check_mods()
{
trap "test_modprobe" EXIT
if [ ! -d $DIR ]; then
modprobe test_firmware
fi
if [ ! -f $PROC_CONFIG ]; then
if modprobe configs 2>/dev/null; then
echo "Loaded configs module"
if [ ! -f $PROC_CONFIG ]; then
echo "You must have the following enabled in your kernel:" >&2
cat $TEST_DIR/config >&2
echo "Resorting to old heuristics" >&2
fi
else
echo "Failed to load configs module, using old heuristics" >&2
fi
fi
}
check_setup()
{
HAS_FW_LOADER_USER_HELPER="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER=y)"
HAS_FW_LOADER_USER_HELPER_FALLBACK="$(kconfig_has CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y)"
PROC_FW_IGNORE_SYSFS_FALLBACK="0"
PROC_FW_FORCE_SYSFS_FALLBACK="0"
if [ -z $PROC_SYS_DIR ]; then
PROC_SYS_DIR="/proc/sys/kernel"
fi
FW_PROC="${PROC_SYS_DIR}/firmware_config"
FW_FORCE_SYSFS_FALLBACK="$FW_PROC/force_sysfs_fallback"
FW_IGNORE_SYSFS_FALLBACK="$FW_PROC/ignore_sysfs_fallback"
if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
PROC_FW_FORCE_SYSFS_FALLBACK="$(cat $FW_FORCE_SYSFS_FALLBACK)"
fi
if [ -f $FW_IGNORE_SYSFS_FALLBACK ]; then
PROC_FW_IGNORE_SYSFS_FALLBACK="$(cat $FW_IGNORE_SYSFS_FALLBACK)"
fi
if [ "$PROC_FW_FORCE_SYSFS_FALLBACK" = "1" ]; then
HAS_FW_LOADER_USER_HELPER="yes"
HAS_FW_LOADER_USER_HELPER_FALLBACK="yes"
fi
if [ "$PROC_FW_IGNORE_SYSFS_FALLBACK" = "1" ]; then
HAS_FW_LOADER_USER_HELPER_FALLBACK="no"
HAS_FW_LOADER_USER_HELPER="no"
fi
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
OLD_TIMEOUT="$(cat /sys/class/firmware/timeout)"
fi
OLD_FWPATH="$(cat /sys/module/firmware_class/parameters/path)"
}
verify_reqs()
{
if [ "$TEST_REQS_FW_SYSFS_FALLBACK" = "yes" ]; then
if [ ! "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo "usermode helper disabled so ignoring test"
exit 0
fi
fi
}
setup_tmp_file()
{
FWPATH=$(mktemp -d)
FW="$FWPATH/test-firmware.bin"
echo "ABCD0123" >"$FW"
NAME=$(basename "$FW")
if [ "$TEST_REQS_FW_SET_CUSTOM_PATH" = "yes" ]; then
echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path
fi
}
__setup_random_file()
{
RANDOM_FILE_PATH="$(mktemp -p $FWPATH)"
# mktemp says dry-run -n is unsafe, so...
if [[ "$1" = "fake" ]]; then
rm -rf $RANDOM_FILE_PATH
sync
else
echo "ABCD0123" >"$RANDOM_FILE_PATH"
fi
echo $RANDOM_FILE_PATH
}
setup_random_file()
{
echo $(__setup_random_file)
}
setup_random_file_fake()
{
echo $(__setup_random_file fake)
}
proc_set_force_sysfs_fallback()
{
if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
echo -n $1 > $FW_FORCE_SYSFS_FALLBACK
check_setup
fi
}
proc_set_ignore_sysfs_fallback()
{
if [ -f $FW_IGNORE_SYSFS_FALLBACK ]; then
echo -n $1 > $FW_IGNORE_SYSFS_FALLBACK
check_setup
fi
}
proc_restore_defaults()
{
proc_set_force_sysfs_fallback 0
proc_set_ignore_sysfs_fallback 0
}
test_finish()
{
if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then
echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout
fi
if [ "$OLD_FWPATH" = "" ]; then
OLD_FWPATH=" "
fi
if [ "$TEST_REQS_FW_SET_CUSTOM_PATH" = "yes" ]; then
echo -n "$OLD_FWPATH" >/sys/module/firmware_class/parameters/path
fi
if [ -f $FW ]; then
rm -f "$FW"
fi
if [ -d $FWPATH ]; then
rm -rf "$FWPATH"
fi
proc_restore_defaults
}
kconfig_has()
{
if [ -f $PROC_CONFIG ]; then
if zgrep -q $1 $PROC_CONFIG 2>/dev/null; then
echo "yes"
else
echo "no"
fi
else
# We currently don't have easy heuristics to infer this
# so best we can do is just try to use the kernel assuming
# you had enabled it. This matches the old behaviour.
if [ "$1" = "CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y" ]; then
echo "yes"
elif [ "$1" = "CONFIG_FW_LOADER_USER_HELPER=y" ]; then
if [ -d /sys/class/firmware/ ]; then
echo yes
else
echo no
fi
fi
fi
}
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# This runs all known tests across all known possible configurations we could
# emulate in one run.
set -e
TEST_DIR=$(dirname $0)
source $TEST_DIR/fw_lib.sh
export HAS_FW_LOADER_USER_HELPER=""
export HAS_FW_LOADER_USER_HELPER_FALLBACK=""
run_tests()
{
proc_set_force_sysfs_fallback $1
proc_set_ignore_sysfs_fallback $2
$TEST_DIR/fw_filesystem.sh
proc_set_force_sysfs_fallback $1
proc_set_ignore_sysfs_fallback $2
$TEST_DIR/fw_fallback.sh
}
run_test_config_0001()
{
echo "-----------------------------------------------------"
echo "Running kernel configuration test 1 -- rare"
echo "Emulates:"
echo "CONFIG_FW_LOADER=y"
echo "CONFIG_FW_LOADER_USER_HELPER=n"
echo "CONFIG_FW_LOADER_USER_HELPER_FALLBACK=n"
run_tests 0 1
}
run_test_config_0002()
{
echo "-----------------------------------------------------"
echo "Running kernel configuration test 2 -- distro"
echo "Emulates:"
echo "CONFIG_FW_LOADER=y"
echo "CONFIG_FW_LOADER_USER_HELPER=y"
echo "CONFIG_FW_LOADER_USER_HELPER_FALLBACK=n"
proc_set_ignore_sysfs_fallback 0
run_tests 0 0
}
run_test_config_0003()
{
echo "-----------------------------------------------------"
echo "Running kernel configuration test 3 -- android"
echo "Emulates:"
echo "CONFIG_FW_LOADER=y"
echo "CONFIG_FW_LOADER_USER_HELPER=y"
echo "CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y"
run_tests 1 0
}
check_mods
check_setup
if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then
run_test_config_0001
run_test_config_0002
run_test_config_0003
else
echo "Running basic kernel configuration, working with your config"
run_test
fi
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment