Merge tag 'driver-core-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core and debugfs updates from Greg KH: "Here is the "big" driver core and debugfs changes for 5.3-rc1 It's a lot of different patches, all across the tree due to some api changes and lots of debugfs cleanups. Other than the debugfs cleanups, in this set of changes we have: - bus iteration function cleanups - scripts/get_abi.pl tool to display and parse Documentation/ABI entries in a simple way - cleanups to Documenatation/ABI/ entries to make them parse easier due to typos and other minor things - default_attrs use for some ktype users - driver model documentation file conversions to .rst - compressed firmware file loading - deferred probe fixes All of these have been in linux-next for a while, with a bunch of merge issues that Stephen has been patient with me for" * tag 'driver-core-5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (102 commits) debugfs: make error message a bit more verbose orangefs: fix build warning from debugfs cleanup patch ubifs: fix build warning after debugfs cleanup patch driver: core: Allow subsystems to continue deferring probe drivers: base: cacheinfo: Ensure cpu hotplug work is done before Intel RDT arch_topology: Remove error messages on out-of-memory conditions lib: notifier-error-inject: no need to check return value of debugfs_create functions swiotlb: no need to check return value of debugfs_create functions ceph: no need to check return value of debugfs_create functions sunrpc: no need to check return value of debugfs_create functions ubifs: no need to check return value of debugfs_create functions orangefs: no need to check return value of debugfs_create functions nfsd: no need to check return value of debugfs_create functions lib: 842: no need to check return value of debugfs_create functions debugfs: provide pr_fmt() macro debugfs: log errors when something goes wrong drivers: s390/cio: Fix compilation warning about const qualifiers drivers: Add generic helper to match by of_node driver_find_device: Unify the match function with class_find_device() bus_find_device: Unify the match callback with class_find_device ...
This commit is contained in:
@@ -137,7 +137,6 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
|
||||
sizeof(*raw_capacity),
|
||||
GFP_KERNEL);
|
||||
if (!raw_capacity) {
|
||||
pr_err("cpu_capacity: failed to allocate memory for raw capacities\n");
|
||||
cap_parsing_failed = true;
|
||||
return false;
|
||||
}
|
||||
@@ -217,10 +216,8 @@ static int __init register_cpufreq_notifier(void)
|
||||
if (!acpi_disabled || !raw_capacity)
|
||||
return -EINVAL;
|
||||
|
||||
if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) {
|
||||
pr_err("cpu_capacity: failed to allocate memory for cpus_to_visit\n");
|
||||
if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cpumask_copy(cpus_to_visit, cpu_possible_mask);
|
||||
|
||||
|
@@ -323,8 +323,8 @@ EXPORT_SYMBOL_GPL(bus_for_each_dev);
|
||||
* return to the caller and not iterate over any more devices.
|
||||
*/
|
||||
struct device *bus_find_device(struct bus_type *bus,
|
||||
struct device *start, void *data,
|
||||
int (*match)(struct device *dev, void *data))
|
||||
struct device *start, const void *data,
|
||||
int (*match)(struct device *dev, const void *data))
|
||||
{
|
||||
struct klist_iter i;
|
||||
struct device *dev;
|
||||
@@ -342,7 +342,7 @@ struct device *bus_find_device(struct bus_type *bus,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bus_find_device);
|
||||
|
||||
static int match_name(struct device *dev, void *data)
|
||||
static int match_name(struct device *dev, const void *data)
|
||||
{
|
||||
const char *name = data;
|
||||
|
||||
|
@@ -660,7 +660,8 @@ static int cacheinfo_cpu_pre_down(unsigned int cpu)
|
||||
|
||||
static int __init cacheinfo_sysfs_init(void)
|
||||
{
|
||||
return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online",
|
||||
return cpuhp_setup_state(CPUHP_AP_BASE_CACHEINFO_ONLINE,
|
||||
"base/cacheinfo:online",
|
||||
cacheinfo_cpu_online, cacheinfo_cpu_pre_down);
|
||||
}
|
||||
device_initcall(cacheinfo_sysfs_init);
|
||||
|
@@ -3356,3 +3356,9 @@ void device_set_of_node_from_dev(struct device *dev, const struct device *dev2)
|
||||
dev->of_node_reused = true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_set_of_node_from_dev);
|
||||
|
||||
int device_match_of_node(struct device *dev, const void *np)
|
||||
{
|
||||
return dev->of_node == np;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_match_of_node);
|
||||
|
@@ -235,6 +235,19 @@ static int __init deferred_probe_timeout_setup(char *str)
|
||||
}
|
||||
__setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
||||
|
||||
static int __driver_deferred_probe_check_state(struct device *dev)
|
||||
{
|
||||
if (!initcalls_done)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!deferred_probe_timeout) {
|
||||
dev_WARN(dev, "deferred probe timeout, ignoring dependency");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* driver_deferred_probe_check_state() - Check deferred probe state
|
||||
* @dev: device to check
|
||||
@@ -248,14 +261,40 @@ __setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
||||
*/
|
||||
int driver_deferred_probe_check_state(struct device *dev)
|
||||
{
|
||||
if (initcalls_done) {
|
||||
if (!deferred_probe_timeout) {
|
||||
dev_WARN(dev, "deferred probe timeout, ignoring dependency");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
||||
return -ENODEV;
|
||||
}
|
||||
int ret;
|
||||
|
||||
ret = __driver_deferred_probe_check_state(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* driver_deferred_probe_check_state_continue() - check deferred probe state
|
||||
* @dev: device to check
|
||||
*
|
||||
* Returns -ETIMEDOUT if deferred probe debug timeout has expired, or
|
||||
* -EPROBE_DEFER otherwise.
|
||||
*
|
||||
* Drivers or subsystems can opt-in to calling this function instead of
|
||||
* directly returning -EPROBE_DEFER.
|
||||
*
|
||||
* This is similar to driver_deferred_probe_check_state(), but it allows the
|
||||
* subsystem to keep deferring probe after built-in drivers have had a chance
|
||||
* to probe. One scenario where that is useful is if built-in drivers rely on
|
||||
* resources that are provided by modular drivers.
|
||||
*/
|
||||
int driver_deferred_probe_check_state_continue(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __driver_deferred_probe_check_state(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
|
@@ -133,7 +133,7 @@ static struct bus_type *generic_match_buses[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int device_fwnode_match(struct device *dev, void *fwnode)
|
||||
static int device_fwnode_match(struct device *dev, const void *fwnode)
|
||||
{
|
||||
return dev_fwnode(dev) == fwnode;
|
||||
}
|
||||
|
@@ -73,8 +73,8 @@ EXPORT_SYMBOL_GPL(driver_for_each_device);
|
||||
* return to the caller and not iterate over any more devices.
|
||||
*/
|
||||
struct device *driver_find_device(struct device_driver *drv,
|
||||
struct device *start, void *data,
|
||||
int (*match)(struct device *dev, void *data))
|
||||
struct device *start, const void *data,
|
||||
int (*match)(struct device *dev, const void *data))
|
||||
{
|
||||
struct klist_iter i;
|
||||
struct device *dev;
|
||||
|
@@ -26,6 +26,9 @@ config FW_LOADER
|
||||
|
||||
if FW_LOADER
|
||||
|
||||
config FW_LOADER_PAGED_BUF
|
||||
bool
|
||||
|
||||
config EXTRA_FIRMWARE
|
||||
string "Build named firmware blobs into the kernel binary"
|
||||
help
|
||||
@@ -67,6 +70,7 @@ config EXTRA_FIRMWARE_DIR
|
||||
|
||||
config FW_LOADER_USER_HELPER
|
||||
bool "Enable the firmware sysfs fallback mechanism"
|
||||
select FW_LOADER_PAGED_BUF
|
||||
help
|
||||
This option enables a sysfs loading facility to enable firmware
|
||||
loading to the kernel through userspace as a fallback mechanism
|
||||
@@ -151,5 +155,19 @@ config FW_LOADER_USER_HELPER_FALLBACK
|
||||
|
||||
If you are unsure about this, say N here.
|
||||
|
||||
config FW_LOADER_COMPRESS
|
||||
bool "Enable compressed firmware support"
|
||||
select FW_LOADER_PAGED_BUF
|
||||
select XZ_DEC
|
||||
help
|
||||
This option enables the support for loading compressed firmware
|
||||
files. The caller of firmware API receives the decompressed file
|
||||
content. The compressed file is loaded as a fallback, only after
|
||||
loading the raw file failed at first.
|
||||
|
||||
Currently only XZ-compressed files are supported, and they have to
|
||||
be compressed with either none or crc32 integrity check type (pass
|
||||
"-C crc32" option to xz command).
|
||||
|
||||
endif # FW_LOADER
|
||||
endmenu
|
||||
|
@@ -219,20 +219,6 @@ static ssize_t firmware_loading_show(struct device *dev,
|
||||
return sprintf(buf, "%d\n", loading);
|
||||
}
|
||||
|
||||
/* one pages buffer should be mapped/unmapped only once */
|
||||
static int map_fw_priv_pages(struct fw_priv *fw_priv)
|
||||
{
|
||||
if (!fw_priv->is_paged_buf)
|
||||
return 0;
|
||||
|
||||
vunmap(fw_priv->data);
|
||||
fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
|
||||
PAGE_KERNEL_RO);
|
||||
if (!fw_priv->data)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* firmware_loading_store() - set value in the 'loading' control file
|
||||
* @dev: device pointer
|
||||
@@ -254,7 +240,6 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
struct fw_priv *fw_priv;
|
||||
ssize_t written = count;
|
||||
int loading = simple_strtol(buf, NULL, 10);
|
||||
int i;
|
||||
|
||||
mutex_lock(&fw_lock);
|
||||
fw_priv = fw_sysfs->fw_priv;
|
||||
@@ -265,12 +250,7 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
case 1:
|
||||
/* discarding any previous partial load */
|
||||
if (!fw_sysfs_done(fw_priv)) {
|
||||
for (i = 0; i < fw_priv->nr_pages; i++)
|
||||
__free_page(fw_priv->pages[i]);
|
||||
vfree(fw_priv->pages);
|
||||
fw_priv->pages = NULL;
|
||||
fw_priv->page_array_size = 0;
|
||||
fw_priv->nr_pages = 0;
|
||||
fw_free_paged_buf(fw_priv);
|
||||
fw_state_start(fw_priv);
|
||||
}
|
||||
break;
|
||||
@@ -284,7 +264,7 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||
* see the mapped 'buf->data' once the loading
|
||||
* is completed.
|
||||
* */
|
||||
rc = map_fw_priv_pages(fw_priv);
|
||||
rc = fw_map_paged_buf(fw_priv);
|
||||
if (rc)
|
||||
dev_err(dev, "%s: map pages failed\n",
|
||||
__func__);
|
||||
@@ -389,40 +369,13 @@ out:
|
||||
|
||||
static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
|
||||
{
|
||||
struct fw_priv *fw_priv= fw_sysfs->fw_priv;
|
||||
int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
|
||||
int err;
|
||||
|
||||
/* If the array of pages is too small, grow it... */
|
||||
if (fw_priv->page_array_size < pages_needed) {
|
||||
int new_array_size = max(pages_needed,
|
||||
fw_priv->page_array_size * 2);
|
||||
struct page **new_pages;
|
||||
|
||||
new_pages = vmalloc(array_size(new_array_size, sizeof(void *)));
|
||||
if (!new_pages) {
|
||||
fw_load_abort(fw_sysfs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(new_pages, fw_priv->pages,
|
||||
fw_priv->page_array_size * sizeof(void *));
|
||||
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
|
||||
(new_array_size - fw_priv->page_array_size));
|
||||
vfree(fw_priv->pages);
|
||||
fw_priv->pages = new_pages;
|
||||
fw_priv->page_array_size = new_array_size;
|
||||
}
|
||||
|
||||
while (fw_priv->nr_pages < pages_needed) {
|
||||
fw_priv->pages[fw_priv->nr_pages] =
|
||||
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
|
||||
|
||||
if (!fw_priv->pages[fw_priv->nr_pages]) {
|
||||
fw_load_abort(fw_sysfs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
fw_priv->nr_pages++;
|
||||
}
|
||||
return 0;
|
||||
err = fw_grow_paged_buf(fw_sysfs->fw_priv,
|
||||
PAGE_ALIGN(min_size) >> PAGE_SHIFT);
|
||||
if (err)
|
||||
fw_load_abort(fw_sysfs);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,7 +612,7 @@ static bool fw_run_sysfs_fallback(enum fw_opt opt_flags)
|
||||
/* Also permit LSMs and IMA to fail firmware sysfs fallback */
|
||||
ret = security_kernel_load_data(LOADING_FIRMWARE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return false;
|
||||
|
||||
return fw_force_sysfs_fallback(opt_flags);
|
||||
}
|
||||
|
@@ -64,12 +64,14 @@ struct fw_priv {
|
||||
void *data;
|
||||
size_t size;
|
||||
size_t allocated_size;
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
#ifdef CONFIG_FW_LOADER_PAGED_BUF
|
||||
bool is_paged_buf;
|
||||
bool need_uevent;
|
||||
struct page **pages;
|
||||
int nr_pages;
|
||||
int page_array_size;
|
||||
#endif
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
bool need_uevent;
|
||||
struct list_head pending_list;
|
||||
#endif
|
||||
const char *fw_name;
|
||||
@@ -133,4 +135,14 @@ static inline void fw_state_done(struct fw_priv *fw_priv)
|
||||
int assign_fw(struct firmware *fw, struct device *device,
|
||||
enum fw_opt opt_flags);
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_PAGED_BUF
|
||||
void fw_free_paged_buf(struct fw_priv *fw_priv);
|
||||
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
|
||||
int fw_map_paged_buf(struct fw_priv *fw_priv);
|
||||
#else
|
||||
static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
|
||||
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
|
||||
int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
|
||||
#endif
|
||||
|
||||
#endif /* __FIRMWARE_LOADER_H */
|
||||
|
@@ -33,6 +33,7 @@
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/xz.h>
|
||||
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
@@ -251,15 +252,7 @@ static void __free_fw_priv(struct kref *ref)
|
||||
list_del(&fw_priv->list);
|
||||
spin_unlock(&fwc->lock);
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_USER_HELPER
|
||||
if (fw_priv->is_paged_buf) {
|
||||
int i;
|
||||
vunmap(fw_priv->data);
|
||||
for (i = 0; i < fw_priv->nr_pages; i++)
|
||||
__free_page(fw_priv->pages[i]);
|
||||
vfree(fw_priv->pages);
|
||||
} else
|
||||
#endif
|
||||
fw_free_paged_buf(fw_priv); /* free leftover pages */
|
||||
if (!fw_priv->allocated_size)
|
||||
vfree(fw_priv->data);
|
||||
kfree_const(fw_priv->fw_name);
|
||||
@@ -274,6 +267,174 @@ static void free_fw_priv(struct fw_priv *fw_priv)
|
||||
spin_unlock(&fwc->lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FW_LOADER_PAGED_BUF
|
||||
void fw_free_paged_buf(struct fw_priv *fw_priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!fw_priv->pages)
|
||||
return;
|
||||
|
||||
for (i = 0; i < fw_priv->nr_pages; i++)
|
||||
__free_page(fw_priv->pages[i]);
|
||||
kvfree(fw_priv->pages);
|
||||
fw_priv->pages = NULL;
|
||||
fw_priv->page_array_size = 0;
|
||||
fw_priv->nr_pages = 0;
|
||||
}
|
||||
|
||||
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed)
|
||||
{
|
||||
/* If the array of pages is too small, grow it */
|
||||
if (fw_priv->page_array_size < pages_needed) {
|
||||
int new_array_size = max(pages_needed,
|
||||
fw_priv->page_array_size * 2);
|
||||
struct page **new_pages;
|
||||
|
||||
new_pages = kvmalloc_array(new_array_size, sizeof(void *),
|
||||
GFP_KERNEL);
|
||||
if (!new_pages)
|
||||
return -ENOMEM;
|
||||
memcpy(new_pages, fw_priv->pages,
|
||||
fw_priv->page_array_size * sizeof(void *));
|
||||
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
|
||||
(new_array_size - fw_priv->page_array_size));
|
||||
kvfree(fw_priv->pages);
|
||||
fw_priv->pages = new_pages;
|
||||
fw_priv->page_array_size = new_array_size;
|
||||
}
|
||||
|
||||
while (fw_priv->nr_pages < pages_needed) {
|
||||
fw_priv->pages[fw_priv->nr_pages] =
|
||||
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
|
||||
|
||||
if (!fw_priv->pages[fw_priv->nr_pages])
|
||||
return -ENOMEM;
|
||||
fw_priv->nr_pages++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fw_map_paged_buf(struct fw_priv *fw_priv)
|
||||
{
|
||||
/* one pages buffer should be mapped/unmapped only once */
|
||||
if (!fw_priv->pages)
|
||||
return 0;
|
||||
|
||||
vunmap(fw_priv->data);
|
||||
fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
|
||||
PAGE_KERNEL_RO);
|
||||
if (!fw_priv->data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* page table is no longer needed after mapping, let's free */
|
||||
kvfree(fw_priv->pages);
|
||||
fw_priv->pages = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XZ-compressed firmware support
|
||||
*/
|
||||
#ifdef CONFIG_FW_LOADER_COMPRESS
|
||||
/* show an error and return the standard error code */
|
||||
static int fw_decompress_xz_error(struct device *dev, enum xz_ret xz_ret)
|
||||
{
|
||||
if (xz_ret != XZ_STREAM_END) {
|
||||
dev_warn(dev, "xz decompression failed (xz_ret=%d)\n", xz_ret);
|
||||
return xz_ret == XZ_MEM_ERROR ? -ENOMEM : -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* single-shot decompression onto the pre-allocated buffer */
|
||||
static int fw_decompress_xz_single(struct device *dev, struct fw_priv *fw_priv,
|
||||
size_t in_size, const void *in_buffer)
|
||||
{
|
||||
struct xz_dec *xz_dec;
|
||||
struct xz_buf xz_buf;
|
||||
enum xz_ret xz_ret;
|
||||
|
||||
xz_dec = xz_dec_init(XZ_SINGLE, (u32)-1);
|
||||
if (!xz_dec)
|
||||
return -ENOMEM;
|
||||
|
||||
xz_buf.in_size = in_size;
|
||||
xz_buf.in = in_buffer;
|
||||
xz_buf.in_pos = 0;
|
||||
xz_buf.out_size = fw_priv->allocated_size;
|
||||
xz_buf.out = fw_priv->data;
|
||||
xz_buf.out_pos = 0;
|
||||
|
||||
xz_ret = xz_dec_run(xz_dec, &xz_buf);
|
||||
xz_dec_end(xz_dec);
|
||||
|
||||
fw_priv->size = xz_buf.out_pos;
|
||||
return fw_decompress_xz_error(dev, xz_ret);
|
||||
}
|
||||
|
||||
/* decompression on paged buffer and map it */
|
||||
static int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv,
|
||||
size_t in_size, const void *in_buffer)
|
||||
{
|
||||
struct xz_dec *xz_dec;
|
||||
struct xz_buf xz_buf;
|
||||
enum xz_ret xz_ret;
|
||||
struct page *page;
|
||||
int err = 0;
|
||||
|
||||
xz_dec = xz_dec_init(XZ_DYNALLOC, (u32)-1);
|
||||
if (!xz_dec)
|
||||
return -ENOMEM;
|
||||
|
||||
xz_buf.in_size = in_size;
|
||||
xz_buf.in = in_buffer;
|
||||
xz_buf.in_pos = 0;
|
||||
|
||||
fw_priv->is_paged_buf = true;
|
||||
fw_priv->size = 0;
|
||||
do {
|
||||
if (fw_grow_paged_buf(fw_priv, fw_priv->nr_pages + 1)) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* decompress onto the new allocated page */
|
||||
page = fw_priv->pages[fw_priv->nr_pages - 1];
|
||||
xz_buf.out = kmap(page);
|
||||
xz_buf.out_pos = 0;
|
||||
xz_buf.out_size = PAGE_SIZE;
|
||||
xz_ret = xz_dec_run(xz_dec, &xz_buf);
|
||||
kunmap(page);
|
||||
fw_priv->size += xz_buf.out_pos;
|
||||
/* partial decompression means either end or error */
|
||||
if (xz_buf.out_pos != PAGE_SIZE)
|
||||
break;
|
||||
} while (xz_ret == XZ_OK);
|
||||
|
||||
err = fw_decompress_xz_error(dev, xz_ret);
|
||||
if (!err)
|
||||
err = fw_map_paged_buf(fw_priv);
|
||||
|
||||
out:
|
||||
xz_dec_end(xz_dec);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fw_decompress_xz(struct device *dev, struct fw_priv *fw_priv,
|
||||
size_t in_size, const void *in_buffer)
|
||||
{
|
||||
/* if the buffer is pre-allocated, we can perform in single-shot mode */
|
||||
if (fw_priv->data)
|
||||
return fw_decompress_xz_single(dev, fw_priv, in_size, in_buffer);
|
||||
else
|
||||
return fw_decompress_xz_pages(dev, fw_priv, in_size, in_buffer);
|
||||
}
|
||||
#endif /* CONFIG_FW_LOADER_COMPRESS */
|
||||
|
||||
/* direct firmware loading support */
|
||||
static char fw_path_para[256];
|
||||
static const char * const fw_path[] = {
|
||||
@@ -293,7 +454,12 @@ module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
|
||||
MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
|
||||
|
||||
static int
|
||||
fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
|
||||
fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
|
||||
const char *suffix,
|
||||
int (*decompress)(struct device *dev,
|
||||
struct fw_priv *fw_priv,
|
||||
size_t in_size,
|
||||
const void *in_buffer))
|
||||
{
|
||||
loff_t size;
|
||||
int i, len;
|
||||
@@ -301,9 +467,11 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
|
||||
char *path;
|
||||
enum kernel_read_file_id id = READING_FIRMWARE;
|
||||
size_t msize = INT_MAX;
|
||||
void *buffer = NULL;
|
||||
|
||||
/* Already populated data member means we're loading into a buffer */
|
||||
if (fw_priv->data) {
|
||||
if (!decompress && fw_priv->data) {
|
||||
buffer = fw_priv->data;
|
||||
id = READING_FIRMWARE_PREALLOC_BUFFER;
|
||||
msize = fw_priv->allocated_size;
|
||||
}
|
||||
@@ -317,15 +485,15 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
|
||||
if (!fw_path[i][0])
|
||||
continue;
|
||||
|
||||
len = snprintf(path, PATH_MAX, "%s/%s",
|
||||
fw_path[i], fw_priv->fw_name);
|
||||
len = snprintf(path, PATH_MAX, "%s/%s%s",
|
||||
fw_path[i], fw_priv->fw_name, suffix);
|
||||
if (len >= PATH_MAX) {
|
||||
rc = -ENAMETOOLONG;
|
||||
break;
|
||||
}
|
||||
|
||||
fw_priv->size = 0;
|
||||
rc = kernel_read_file_from_path(path, &fw_priv->data, &size,
|
||||
rc = kernel_read_file_from_path(path, &buffer, &size,
|
||||
msize, id);
|
||||
if (rc) {
|
||||
if (rc != -ENOENT)
|
||||
@@ -336,8 +504,24 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
|
||||
path);
|
||||
continue;
|
||||
}
|
||||
dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name);
|
||||
fw_priv->size = size;
|
||||
if (decompress) {
|
||||
dev_dbg(device, "f/w decompressing %s\n",
|
||||
fw_priv->fw_name);
|
||||
rc = decompress(device, fw_priv, size, buffer);
|
||||
/* discard the superfluous original content */
|
||||
vfree(buffer);
|
||||
buffer = NULL;
|
||||
if (rc) {
|
||||
fw_free_paged_buf(fw_priv);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(device, "direct-loading %s\n",
|
||||
fw_priv->fw_name);
|
||||
if (!fw_priv->data)
|
||||
fw_priv->data = buffer;
|
||||
fw_priv->size = size;
|
||||
}
|
||||
fw_state_done(fw_priv);
|
||||
break;
|
||||
}
|
||||
@@ -584,7 +768,13 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
||||
if (ret <= 0) /* error or already assigned */
|
||||
goto out;
|
||||
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv);
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
|
||||
#ifdef CONFIG_FW_LOADER_COMPRESS
|
||||
if (ret == -ENOENT)
|
||||
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
|
||||
fw_decompress_xz);
|
||||
#endif
|
||||
|
||||
if (ret) {
|
||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||
dev_warn(device,
|
||||
|
@@ -66,6 +66,7 @@ static DEVICE_ATTR(cpulist, S_IRUGO, node_read_cpulist, NULL);
|
||||
* @dev: Device for this memory access class
|
||||
* @list_node: List element in the node's access list
|
||||
* @access: The access class rank
|
||||
* @hmem_attrs: Heterogeneous memory performance attributes
|
||||
*/
|
||||
struct node_access_nodes {
|
||||
struct device dev;
|
||||
@@ -673,8 +674,8 @@ int register_cpu_under_node(unsigned int cpu, unsigned int nid)
|
||||
/**
|
||||
* register_memory_node_under_compute_node - link memory node to its compute
|
||||
* node for a given access class.
|
||||
* @mem_node: Memory node number
|
||||
* @cpu_node: Cpu node number
|
||||
* @mem_nid: Memory node number
|
||||
* @cpu_nid: Cpu node number
|
||||
* @access: Access class to register
|
||||
*
|
||||
* Description:
|
||||
|
@@ -5,7 +5,7 @@
|
||||
* Copyright (c) 2002-3 Patrick Mochel
|
||||
* Copyright (c) 2002-3 Open Source Development Labs
|
||||
*
|
||||
* Please see Documentation/driver-model/platform.txt for more
|
||||
* Please see Documentation/driver-model/platform.rst for more
|
||||
* information.
|
||||
*/
|
||||
|
||||
|
Reference in New Issue
Block a user