forked from Minki/linux
Driver core / kernfs changes for 6.0-rc1
Here is the set of driver core and kernfs changes for 6.0-rc1. "biggest" thing in here is some scalability improvements for kernfs for large systems. Other than that, included in here are: - arch topology and cache info changes that have been reviewed and discussed a lot. - potential error path cleanup fixes - deferred driver probe cleanups - firmware loader cleanups and tweaks - documentation updates - other small things All of these have been in the linux-next tree for a while with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCYuqCnw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ym/JgCcCnaycJY00ZPRQm3LQCyzfJ0HgqoAn2qxGV+K NKycLeXZSnuvIA87dycE =/4Jk -----END PGP SIGNATURE----- Merge tag 'driver-core-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core Pull driver core / kernfs updates from Greg KH: "Here is the set of driver core and kernfs changes for 6.0-rc1. The "biggest" thing in here is some scalability improvements for kernfs for large systems. Other than that, included in here are: - arch topology and cache info changes that have been reviewed and discussed a lot. - potential error path cleanup fixes - deferred driver probe cleanups - firmware loader cleanups and tweaks - documentation updates - other small things All of these have been in the linux-next tree for a while with no reported problems" * tag 'driver-core-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (63 commits) docs: embargoed-hardware-issues: fix invalid AMD contact email firmware_loader: Replace kmap() with kmap_local_page() sysfs docs: ABI: Fix typo in comment kobject: fix Kconfig.debug "its" grammar kernfs: Fix typo 'the the' in comment docs: driver-api: firmware: add driver firmware guidelines. (v3) arch_topology: Fix cache attributes detection in the CPU hotplug path ACPI: PPTT: Leave the table mapped for the runtime usage cacheinfo: Use atomic allocation for percpu cache attributes drivers/base: fix userspace break from using bin_attributes for cpumap and cpulist MAINTAINERS: Change mentions of mpm to olivia docs: ABI: sysfs-devices-soc: Update Lee Jones' email address docs: ABI: sysfs-class-pwm: Update Lee Jones' email address Documentation/process: Add embargoed HW contact for LLVM Revert "kernfs: Change kernfs_notify_list to llist." ACPI: Remove the unused find_acpi_cpu_cache_topology() arch_topology: Warn that topology for nested clusters is not supported arch_topology: Add support for parsing sockets in /cpu-map arch_topology: Set cluster identifier in each core/thread from /cpu-map arch_topology: Limit span of cpu_clustergroup_mask() ...
This commit is contained in:
commit
cfeafd9466
|
@ -38,7 +38,7 @@ What: /sys/module/<MODULENAME>/srcversion
|
||||||
Date: Jun 2005
|
Date: Jun 2005
|
||||||
Description:
|
Description:
|
||||||
If the module source has MODULE_VERSION, this file will contain
|
If the module source has MODULE_VERSION, this file will contain
|
||||||
the checksum of the the source code.
|
the checksum of the source code.
|
||||||
|
|
||||||
What: /sys/module/<MODULENAME>/version
|
What: /sys/module/<MODULENAME>/version
|
||||||
Date: Jun 2005
|
Date: Jun 2005
|
||||||
|
|
|
@ -81,7 +81,7 @@ Description:
|
||||||
What: /sys/class/pwm/pwmchip<N>/pwmX/capture
|
What: /sys/class/pwm/pwmchip<N>/pwmX/capture
|
||||||
Date: June 2016
|
Date: June 2016
|
||||||
KernelVersion: 4.8
|
KernelVersion: 4.8
|
||||||
Contact: Lee Jones <lee.jones@linaro.org>
|
Contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Capture information about a PWM signal. The output format is a
|
Capture information about a PWM signal. The output format is a
|
||||||
pair unsigned integers (period and duty cycle), separated by a
|
pair unsigned integers (period and duty cycle), separated by a
|
||||||
|
|
|
@ -78,7 +78,7 @@ What: /sys/class/rtrs-client/<session-name>/paths/<src@dst>/hca_name
|
||||||
Date: Feb 2020
|
Date: Feb 2020
|
||||||
KernelVersion: 5.7
|
KernelVersion: 5.7
|
||||||
Contact: Jack Wang <jinpu.wang@cloud.ionos.com> Danil Kipnis <danil.kipnis@cloud.ionos.com>
|
Contact: Jack Wang <jinpu.wang@cloud.ionos.com> Danil Kipnis <danil.kipnis@cloud.ionos.com>
|
||||||
Description: RO, Contains the the name of HCA the connection established on.
|
Description: RO, Contains the name of HCA the connection established on.
|
||||||
|
|
||||||
What: /sys/class/rtrs-client/<session-name>/paths/<src@dst>/hca_port
|
What: /sys/class/rtrs-client/<session-name>/paths/<src@dst>/hca_port
|
||||||
Date: Feb 2020
|
Date: Feb 2020
|
||||||
|
|
|
@ -24,7 +24,7 @@ What: /sys/class/rtrs-server/<session-name>/paths/<src@dst>/hca_name
|
||||||
Date: Feb 2020
|
Date: Feb 2020
|
||||||
KernelVersion: 5.7
|
KernelVersion: 5.7
|
||||||
Contact: Jack Wang <jinpu.wang@cloud.ionos.com> Danil Kipnis <danil.kipnis@cloud.ionos.com>
|
Contact: Jack Wang <jinpu.wang@cloud.ionos.com> Danil Kipnis <danil.kipnis@cloud.ionos.com>
|
||||||
Description: RO, Contains the the name of HCA the connection established on.
|
Description: RO, Contains the name of HCA the connection established on.
|
||||||
|
|
||||||
What: /sys/class/rtrs-server/<session-name>/paths/<src@dst>/hca_port
|
What: /sys/class/rtrs-server/<session-name>/paths/<src@dst>/hca_port
|
||||||
Date: Feb 2020
|
Date: Feb 2020
|
||||||
|
|
|
@ -74,7 +74,7 @@ Description:
|
||||||
|
|
||||||
Reads also cause the AC alarm timer status to be reset.
|
Reads also cause the AC alarm timer status to be reset.
|
||||||
|
|
||||||
Another way to reset the the status of the AC alarm timer is to
|
Another way to reset the status of the AC alarm timer is to
|
||||||
write (the number) 0 to this file.
|
write (the number) 0 to this file.
|
||||||
|
|
||||||
If the status return value indicates that the timer has expired,
|
If the status return value indicates that the timer has expired,
|
||||||
|
|
|
@ -303,5 +303,5 @@ Date: Apr 2010
|
||||||
Contact: Dominik Brodowski <linux@dominikbrodowski.net>
|
Contact: Dominik Brodowski <linux@dominikbrodowski.net>
|
||||||
Description:
|
Description:
|
||||||
Reports the runtime PM children usage count of a device, or
|
Reports the runtime PM children usage count of a device, or
|
||||||
0 if the the children will be ignored.
|
0 if the children will be ignored.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
What: /sys/devices/socX
|
What: /sys/devices/socX
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
The /sys/devices/ directory contains a sub-directory for each
|
The /sys/devices/ directory contains a sub-directory for each
|
||||||
System-on-Chip (SoC) device on a running platform. Information
|
System-on-Chip (SoC) device on a running platform. Information
|
||||||
|
@ -14,14 +14,14 @@ Description:
|
||||||
|
|
||||||
What: /sys/devices/socX/machine
|
What: /sys/devices/socX/machine
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Read-only attribute common to all SoCs. Contains the SoC machine
|
Read-only attribute common to all SoCs. Contains the SoC machine
|
||||||
name (e.g. Ux500).
|
name (e.g. Ux500).
|
||||||
|
|
||||||
What: /sys/devices/socX/family
|
What: /sys/devices/socX/family
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Read-only attribute common to all SoCs. Contains SoC family name
|
Read-only attribute common to all SoCs. Contains SoC family name
|
||||||
(e.g. DB8500).
|
(e.g. DB8500).
|
||||||
|
@ -59,7 +59,7 @@ Description:
|
||||||
|
|
||||||
What: /sys/devices/socX/soc_id
|
What: /sys/devices/socX/soc_id
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Read-only attribute supported by most SoCs. In the case of
|
Read-only attribute supported by most SoCs. In the case of
|
||||||
ST-Ericsson's chips this contains the SoC serial number.
|
ST-Ericsson's chips this contains the SoC serial number.
|
||||||
|
@ -72,21 +72,21 @@ Description:
|
||||||
|
|
||||||
What: /sys/devices/socX/revision
|
What: /sys/devices/socX/revision
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Read-only attribute supported by most SoCs. Contains the SoC's
|
Read-only attribute supported by most SoCs. Contains the SoC's
|
||||||
manufacturing revision number.
|
manufacturing revision number.
|
||||||
|
|
||||||
What: /sys/devices/socX/process
|
What: /sys/devices/socX/process
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
Read-only attribute supported ST-Ericsson's silicon. Contains the
|
Read-only attribute supported ST-Ericsson's silicon. Contains the
|
||||||
the process by which the silicon chip was manufactured.
|
the process by which the silicon chip was manufactured.
|
||||||
|
|
||||||
What: /sys/bus/soc
|
What: /sys/bus/soc
|
||||||
Date: January 2012
|
Date: January 2012
|
||||||
contact: Lee Jones <lee.jones@linaro.org>
|
contact: Lee Jones <lee@kernel.org>
|
||||||
Description:
|
Description:
|
||||||
The /sys/bus/soc/ directory contains the usual sub-folders
|
The /sys/bus/soc/ directory contains the usual sub-folders
|
||||||
expected under most buses. /sys/bus/soc/devices is of particular
|
expected under most buses. /sys/bus/soc/devices is of particular
|
||||||
|
|
|
@ -67,8 +67,7 @@ Description: Discover NUMA node a CPU belongs to
|
||||||
/sys/devices/system/cpu/cpu42/node2 -> ../../node/node2
|
/sys/devices/system/cpu/cpu42/node2 -> ../../node/node2
|
||||||
|
|
||||||
|
|
||||||
What: /sys/devices/system/cpu/cpuX/topology/core_id
|
What: /sys/devices/system/cpu/cpuX/topology/core_siblings
|
||||||
/sys/devices/system/cpu/cpuX/topology/core_siblings
|
|
||||||
/sys/devices/system/cpu/cpuX/topology/core_siblings_list
|
/sys/devices/system/cpu/cpuX/topology/core_siblings_list
|
||||||
/sys/devices/system/cpu/cpuX/topology/physical_package_id
|
/sys/devices/system/cpu/cpuX/topology/physical_package_id
|
||||||
/sys/devices/system/cpu/cpuX/topology/thread_siblings
|
/sys/devices/system/cpu/cpuX/topology/thread_siblings
|
||||||
|
@ -84,10 +83,6 @@ Description: CPU topology files that describe a logical CPU's relationship
|
||||||
|
|
||||||
Briefly, the files above are:
|
Briefly, the files above are:
|
||||||
|
|
||||||
core_id: the CPU core ID of cpuX. Typically it is the
|
|
||||||
hardware platform's identifier (rather than the kernel's).
|
|
||||||
The actual value is architecture and platform dependent.
|
|
||||||
|
|
||||||
core_siblings: internal kernel map of cpuX's hardware threads
|
core_siblings: internal kernel map of cpuX's hardware threads
|
||||||
within the same physical_package_id.
|
within the same physical_package_id.
|
||||||
|
|
||||||
|
|
|
@ -13,4 +13,5 @@ documents these features.
|
||||||
direct-fs-lookup
|
direct-fs-lookup
|
||||||
fallback-mechanisms
|
fallback-mechanisms
|
||||||
lookup-order
|
lookup-order
|
||||||
|
firmware-usage-guidelines
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
===================
|
||||||
|
Firmware Guidelines
|
||||||
|
===================
|
||||||
|
|
||||||
|
Users switching to a newer kernel should *not* have to install newer
|
||||||
|
firmware files to keep their hardware working. At the same time updated
|
||||||
|
firmware files must not cause any regressions for users of older kernel
|
||||||
|
releases.
|
||||||
|
|
||||||
|
Drivers that use firmware from linux-firmware should follow the rules in
|
||||||
|
this guide. (Where there is limited control of the firmware,
|
||||||
|
i.e. company doesn't support Linux, firmwares sourced from misc places,
|
||||||
|
then of course these rules will not apply strictly.)
|
||||||
|
|
||||||
|
* Firmware files shall be designed in a way that it allows checking for
|
||||||
|
firmware ABI version changes. It is recommended that firmware files be
|
||||||
|
versioned with at least a major/minor version. It is suggested that
|
||||||
|
the firmware files in linux-firmware be named with some device
|
||||||
|
specific name, and just the major version. The firmware version should
|
||||||
|
be stored in the firmware header, or as an exception, as part of the
|
||||||
|
firmware file name, in order to let the driver detact any non-ABI
|
||||||
|
fixes/changes. The firmware files in linux-firmware should be
|
||||||
|
overwritten with the newest compatible major version. Newer major
|
||||||
|
version firmware shall remain compatible with all kernels that load
|
||||||
|
that major number.
|
||||||
|
|
||||||
|
* If the kernel support for the hardware is normally inactive, or the
|
||||||
|
hardware isn't available for public consumption, this can
|
||||||
|
be ignored, until the first kernel release that enables that hardware.
|
||||||
|
This means no major version bumps without the kernel retaining
|
||||||
|
backwards compatibility for the older major versions. Minor version
|
||||||
|
bumps should not introduce new features that newer kernels depend on
|
||||||
|
non-optionally.
|
||||||
|
|
||||||
|
* If a security fix needs lockstep firmware and kernel fixes in order to
|
||||||
|
be successful, then all supported major versions in the linux-firmware
|
||||||
|
repo that are required by currently supported stable/LTS kernels,
|
||||||
|
should be updated with the security fix. The kernel patches should
|
||||||
|
detect if the firmware is new enough to declare if the security issue
|
||||||
|
is fixed. All communications around security fixes should point at
|
||||||
|
both the firmware and kernel fixes. If a security fix requires
|
||||||
|
deprecating old major versions, then this should only be done as a
|
||||||
|
last option, and be stated clearly in all communications.
|
||||||
|
|
|
@ -244,7 +244,7 @@ disclosure of a particular issue, unless requested by a response team or by
|
||||||
an involved disclosed party. The current ambassadors list:
|
an involved disclosed party. The current ambassadors list:
|
||||||
|
|
||||||
============= ========================================================
|
============= ========================================================
|
||||||
AMD Tom Lendacky <tom.lendacky@amd.com>
|
AMD Tom Lendacky <thomas.lendacky@amd.com>
|
||||||
Ampere Darren Hart <darren@os.amperecomputing.com>
|
Ampere Darren Hart <darren@os.amperecomputing.com>
|
||||||
ARM Catalin Marinas <catalin.marinas@arm.com>
|
ARM Catalin Marinas <catalin.marinas@arm.com>
|
||||||
IBM Power Anton Blanchard <anton@linux.ibm.com>
|
IBM Power Anton Blanchard <anton@linux.ibm.com>
|
||||||
|
@ -264,6 +264,9 @@ an involved disclosed party. The current ambassadors list:
|
||||||
|
|
||||||
Amazon
|
Amazon
|
||||||
Google Kees Cook <keescook@chromium.org>
|
Google Kees Cook <keescook@chromium.org>
|
||||||
|
|
||||||
|
GCC
|
||||||
|
LLVM Nick Desaulniers <ndesaulniers@google.com>
|
||||||
============= ========================================================
|
============= ========================================================
|
||||||
|
|
||||||
If you want your organization to be added to the ambassadors list, please
|
If you want your organization to be added to the ambassadors list, please
|
||||||
|
|
|
@ -174,7 +174,7 @@ CVE分配
|
||||||
|
|
||||||
============= ========================================================
|
============= ========================================================
|
||||||
ARM
|
ARM
|
||||||
AMD Tom Lendacky <tom.lendacky@amd.com>
|
AMD Tom Lendacky <thomas.lendacky@amd.com>
|
||||||
IBM
|
IBM
|
||||||
Intel Tony Luck <tony.luck@intel.com>
|
Intel Tony Luck <tony.luck@intel.com>
|
||||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||||
|
|
|
@ -177,7 +177,7 @@ CVE分配
|
||||||
|
|
||||||
============= ========================================================
|
============= ========================================================
|
||||||
ARM
|
ARM
|
||||||
AMD Tom Lendacky <tom.lendacky@amd.com>
|
AMD Tom Lendacky <thomas.lendacky@amd.com>
|
||||||
IBM
|
IBM
|
||||||
Intel Tony Luck <tony.luck@intel.com>
|
Intel Tony Luck <tony.luck@intel.com>
|
||||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||||
|
|
|
@ -7494,7 +7494,7 @@ F: Documentation/admin-guide/media/em28xx*
|
||||||
F: drivers/media/usb/em28xx/
|
F: drivers/media/usb/em28xx/
|
||||||
|
|
||||||
EMBEDDED LINUX
|
EMBEDDED LINUX
|
||||||
M: Matt Mackall <mpm@selenic.com>
|
M: Olivia Mackall <olivia@selenic.com>
|
||||||
M: David Woodhouse <dwmw2@infradead.org>
|
M: David Woodhouse <dwmw2@infradead.org>
|
||||||
L: linux-embedded@vger.kernel.org
|
L: linux-embedded@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
@ -8902,7 +8902,7 @@ F: include/trace/events/hwmon*.h
|
||||||
K: (devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
|
K: (devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
|
||||||
|
|
||||||
HARDWARE RANDOM NUMBER GENERATOR CORE
|
HARDWARE RANDOM NUMBER GENERATOR CORE
|
||||||
M: Matt Mackall <mpm@selenic.com>
|
M: Olivia Mackall <olivia@selenic.com>
|
||||||
M: Herbert Xu <herbert@gondor.apana.org.au>
|
M: Herbert Xu <herbert@gondor.apana.org.au>
|
||||||
L: linux-crypto@vger.kernel.org
|
L: linux-crypto@vger.kernel.org
|
||||||
S: Odd fixes
|
S: Odd fixes
|
||||||
|
|
|
@ -89,8 +89,6 @@ int __init parse_acpi_topology(void)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
int i, cache_id;
|
|
||||||
|
|
||||||
topology_id = find_acpi_cpu_topology(cpu, 0);
|
topology_id = find_acpi_cpu_topology(cpu, 0);
|
||||||
if (topology_id < 0)
|
if (topology_id < 0)
|
||||||
return topology_id;
|
return topology_id;
|
||||||
|
@ -107,18 +105,6 @@ int __init parse_acpi_topology(void)
|
||||||
cpu_topology[cpu].cluster_id = topology_id;
|
cpu_topology[cpu].cluster_id = topology_id;
|
||||||
topology_id = find_acpi_cpu_topology_package(cpu);
|
topology_id = find_acpi_cpu_topology_package(cpu);
|
||||||
cpu_topology[cpu].package_id = topology_id;
|
cpu_topology[cpu].package_id = topology_id;
|
||||||
|
|
||||||
i = acpi_find_last_cache_level(cpu);
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
/*
|
|
||||||
* this is the only part of cpu_topology that has
|
|
||||||
* a direct relationship with the cache topology
|
|
||||||
*/
|
|
||||||
cache_id = find_acpi_cpu_cache_topology(cpu, i);
|
|
||||||
if (cache_id > 0)
|
|
||||||
cpu_topology[cpu].llc_id = cache_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -437,7 +437,8 @@ static void cache_setup_acpi_cpu(struct acpi_table_header *table,
|
||||||
pr_debug("found = %p %p\n", found_cache, cpu_node);
|
pr_debug("found = %p %p\n", found_cache, cpu_node);
|
||||||
if (found_cache)
|
if (found_cache)
|
||||||
update_cache_properties(this_leaf, found_cache,
|
update_cache_properties(this_leaf, found_cache,
|
||||||
cpu_node, table->revision);
|
ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)),
|
||||||
|
table->revision);
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
@ -532,21 +533,37 @@ static int topology_get_acpi_cpu_tag(struct acpi_table_header *table,
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct acpi_table_header *acpi_get_pptt(void)
|
||||||
|
{
|
||||||
|
static struct acpi_table_header *pptt;
|
||||||
|
acpi_status status;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PPTT will be used at runtime on every CPU hotplug in path, so we
|
||||||
|
* don't need to call acpi_put_table() to release the table mapping.
|
||||||
|
*/
|
||||||
|
if (!pptt) {
|
||||||
|
status = acpi_get_table(ACPI_SIG_PPTT, 0, &pptt);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
acpi_pptt_warn_missing();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pptt;
|
||||||
|
}
|
||||||
|
|
||||||
static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
|
static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
|
||||||
{
|
{
|
||||||
struct acpi_table_header *table;
|
struct acpi_table_header *table;
|
||||||
acpi_status status;
|
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
table = acpi_get_pptt();
|
||||||
if (ACPI_FAILURE(status)) {
|
if (!table)
|
||||||
acpi_pptt_warn_missing();
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
|
||||||
retval = topology_get_acpi_cpu_tag(table, cpu, level, flag);
|
retval = topology_get_acpi_cpu_tag(table, cpu, level, flag);
|
||||||
pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n",
|
pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n",
|
||||||
cpu, level, retval);
|
cpu, level, retval);
|
||||||
acpi_put_table(table);
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -567,16 +584,13 @@ static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag)
|
||||||
static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
|
static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
|
||||||
{
|
{
|
||||||
struct acpi_table_header *table;
|
struct acpi_table_header *table;
|
||||||
acpi_status status;
|
|
||||||
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
||||||
struct acpi_pptt_processor *cpu_node = NULL;
|
struct acpi_pptt_processor *cpu_node = NULL;
|
||||||
int ret = -ENOENT;
|
int ret = -ENOENT;
|
||||||
|
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
table = acpi_get_pptt();
|
||||||
if (ACPI_FAILURE(status)) {
|
if (!table)
|
||||||
acpi_pptt_warn_missing();
|
return -ENOENT;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table->revision >= rev)
|
if (table->revision >= rev)
|
||||||
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
|
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
|
||||||
|
@ -584,8 +598,6 @@ static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag)
|
||||||
if (cpu_node)
|
if (cpu_node)
|
||||||
ret = (cpu_node->flags & flag) != 0;
|
ret = (cpu_node->flags & flag) != 0;
|
||||||
|
|
||||||
acpi_put_table(table);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,18 +616,15 @@ int acpi_find_last_cache_level(unsigned int cpu)
|
||||||
u32 acpi_cpu_id;
|
u32 acpi_cpu_id;
|
||||||
struct acpi_table_header *table;
|
struct acpi_table_header *table;
|
||||||
int number_of_levels = 0;
|
int number_of_levels = 0;
|
||||||
acpi_status status;
|
|
||||||
|
table = acpi_get_pptt();
|
||||||
|
if (!table)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
pr_debug("Cache Setup find last level CPU=%d\n", cpu);
|
pr_debug("Cache Setup find last level CPU=%d\n", cpu);
|
||||||
|
|
||||||
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id);
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
acpi_pptt_warn_missing();
|
|
||||||
} else {
|
|
||||||
number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id);
|
|
||||||
acpi_put_table(table);
|
|
||||||
}
|
|
||||||
pr_debug("Cache Setup find last level level=%d\n", number_of_levels);
|
pr_debug("Cache Setup find last level level=%d\n", number_of_levels);
|
||||||
|
|
||||||
return number_of_levels;
|
return number_of_levels;
|
||||||
|
@ -637,20 +646,16 @@ int acpi_find_last_cache_level(unsigned int cpu)
|
||||||
int cache_setup_acpi(unsigned int cpu)
|
int cache_setup_acpi(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct acpi_table_header *table;
|
struct acpi_table_header *table;
|
||||||
acpi_status status;
|
|
||||||
|
table = acpi_get_pptt();
|
||||||
|
if (!table)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
pr_debug("Cache Setup ACPI CPU %d\n", cpu);
|
pr_debug("Cache Setup ACPI CPU %d\n", cpu);
|
||||||
|
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
acpi_pptt_warn_missing();
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_setup_acpi_cpu(table, cpu);
|
cache_setup_acpi_cpu(table, cpu);
|
||||||
acpi_put_table(table);
|
|
||||||
|
|
||||||
return status;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -690,43 +695,6 @@ int find_acpi_cpu_topology(unsigned int cpu, int level)
|
||||||
return find_acpi_cpu_topology_tag(cpu, level, 0);
|
return find_acpi_cpu_topology_tag(cpu, level, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* find_acpi_cpu_cache_topology() - Determine a unique cache topology value
|
|
||||||
* @cpu: Kernel logical CPU number
|
|
||||||
* @level: The cache level for which we would like a unique ID
|
|
||||||
*
|
|
||||||
* Determine a unique ID for each unified cache in the system
|
|
||||||
*
|
|
||||||
* Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found.
|
|
||||||
* Otherwise returns a value which represents a unique topological feature.
|
|
||||||
*/
|
|
||||||
int find_acpi_cpu_cache_topology(unsigned int cpu, int level)
|
|
||||||
{
|
|
||||||
struct acpi_table_header *table;
|
|
||||||
struct acpi_pptt_cache *found_cache;
|
|
||||||
acpi_status status;
|
|
||||||
u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
|
||||||
struct acpi_pptt_processor *cpu_node = NULL;
|
|
||||||
int ret = -1;
|
|
||||||
|
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
acpi_pptt_warn_missing();
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
found_cache = acpi_find_cache_node(table, acpi_cpu_id,
|
|
||||||
CACHE_TYPE_UNIFIED,
|
|
||||||
level,
|
|
||||||
&cpu_node);
|
|
||||||
if (found_cache)
|
|
||||||
ret = ACPI_PTR_DIFF(cpu_node, table);
|
|
||||||
|
|
||||||
acpi_put_table(table);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find_acpi_cpu_topology_package() - Determine a unique CPU package value
|
* find_acpi_cpu_topology_package() - Determine a unique CPU package value
|
||||||
* @cpu: Kernel logical CPU number
|
* @cpu: Kernel logical CPU number
|
||||||
|
@ -766,50 +734,38 @@ int find_acpi_cpu_topology_package(unsigned int cpu)
|
||||||
int find_acpi_cpu_topology_cluster(unsigned int cpu)
|
int find_acpi_cpu_topology_cluster(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct acpi_table_header *table;
|
struct acpi_table_header *table;
|
||||||
acpi_status status;
|
|
||||||
struct acpi_pptt_processor *cpu_node, *cluster_node;
|
struct acpi_pptt_processor *cpu_node, *cluster_node;
|
||||||
u32 acpi_cpu_id;
|
u32 acpi_cpu_id;
|
||||||
int retval;
|
int retval;
|
||||||
int is_thread;
|
int is_thread;
|
||||||
|
|
||||||
status = acpi_get_table(ACPI_SIG_PPTT, 0, &table);
|
table = acpi_get_pptt();
|
||||||
if (ACPI_FAILURE(status)) {
|
if (!table)
|
||||||
acpi_pptt_warn_missing();
|
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
|
||||||
|
|
||||||
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
acpi_cpu_id = get_acpi_id_for_cpu(cpu);
|
||||||
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
|
cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
|
||||||
if (cpu_node == NULL || !cpu_node->parent) {
|
if (!cpu_node || !cpu_node->parent)
|
||||||
retval = -ENOENT;
|
return -ENOENT;
|
||||||
goto put_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_thread = cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD;
|
is_thread = cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD;
|
||||||
cluster_node = fetch_pptt_node(table, cpu_node->parent);
|
cluster_node = fetch_pptt_node(table, cpu_node->parent);
|
||||||
if (cluster_node == NULL) {
|
if (!cluster_node)
|
||||||
retval = -ENOENT;
|
return -ENOENT;
|
||||||
goto put_table;
|
|
||||||
}
|
|
||||||
if (is_thread) {
|
if (is_thread) {
|
||||||
if (!cluster_node->parent) {
|
if (!cluster_node->parent)
|
||||||
retval = -ENOENT;
|
return -ENOENT;
|
||||||
goto put_table;
|
|
||||||
}
|
|
||||||
cluster_node = fetch_pptt_node(table, cluster_node->parent);
|
cluster_node = fetch_pptt_node(table, cluster_node->parent);
|
||||||
if (cluster_node == NULL) {
|
if (!cluster_node)
|
||||||
retval = -ENOENT;
|
return -ENOENT;
|
||||||
goto put_table;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (cluster_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
|
if (cluster_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID)
|
||||||
retval = cluster_node->acpi_processor_id;
|
retval = cluster_node->acpi_processor_id;
|
||||||
else
|
else
|
||||||
retval = ACPI_PTR_DIFF(cluster_node, table);
|
retval = ACPI_PTR_DIFF(cluster_node, table);
|
||||||
|
|
||||||
put_table:
|
|
||||||
acpi_put_table(table);
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/cacheinfo.h>
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
@ -496,7 +497,7 @@ static int __init get_cpu_for_node(struct device_node *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init parse_core(struct device_node *core, int package_id,
|
static int __init parse_core(struct device_node *core, int package_id,
|
||||||
int core_id)
|
int cluster_id, int core_id)
|
||||||
{
|
{
|
||||||
char name[20];
|
char name[20];
|
||||||
bool leaf = true;
|
bool leaf = true;
|
||||||
|
@ -512,6 +513,7 @@ static int __init parse_core(struct device_node *core, int package_id,
|
||||||
cpu = get_cpu_for_node(t);
|
cpu = get_cpu_for_node(t);
|
||||||
if (cpu >= 0) {
|
if (cpu >= 0) {
|
||||||
cpu_topology[cpu].package_id = package_id;
|
cpu_topology[cpu].package_id = package_id;
|
||||||
|
cpu_topology[cpu].cluster_id = cluster_id;
|
||||||
cpu_topology[cpu].core_id = core_id;
|
cpu_topology[cpu].core_id = core_id;
|
||||||
cpu_topology[cpu].thread_id = i;
|
cpu_topology[cpu].thread_id = i;
|
||||||
} else if (cpu != -ENODEV) {
|
} else if (cpu != -ENODEV) {
|
||||||
|
@ -533,6 +535,7 @@ static int __init parse_core(struct device_node *core, int package_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_topology[cpu].package_id = package_id;
|
cpu_topology[cpu].package_id = package_id;
|
||||||
|
cpu_topology[cpu].cluster_id = cluster_id;
|
||||||
cpu_topology[cpu].core_id = core_id;
|
cpu_topology[cpu].core_id = core_id;
|
||||||
} else if (leaf && cpu != -ENODEV) {
|
} else if (leaf && cpu != -ENODEV) {
|
||||||
pr_err("%pOF: Can't get CPU for leaf core\n", core);
|
pr_err("%pOF: Can't get CPU for leaf core\n", core);
|
||||||
|
@ -542,13 +545,13 @@ static int __init parse_core(struct device_node *core, int package_id,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init parse_cluster(struct device_node *cluster, int depth)
|
static int __init parse_cluster(struct device_node *cluster, int package_id,
|
||||||
|
int cluster_id, int depth)
|
||||||
{
|
{
|
||||||
char name[20];
|
char name[20];
|
||||||
bool leaf = true;
|
bool leaf = true;
|
||||||
bool has_cores = false;
|
bool has_cores = false;
|
||||||
struct device_node *c;
|
struct device_node *c;
|
||||||
static int package_id __initdata;
|
|
||||||
int core_id = 0;
|
int core_id = 0;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
|
@ -563,7 +566,9 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
|
||||||
c = of_get_child_by_name(cluster, name);
|
c = of_get_child_by_name(cluster, name);
|
||||||
if (c) {
|
if (c) {
|
||||||
leaf = false;
|
leaf = false;
|
||||||
ret = parse_cluster(c, depth + 1);
|
ret = parse_cluster(c, package_id, i, depth + 1);
|
||||||
|
if (depth > 0)
|
||||||
|
pr_warn("Topology for clusters of clusters not yet supported\n");
|
||||||
of_node_put(c);
|
of_node_put(c);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -587,7 +592,8 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leaf) {
|
if (leaf) {
|
||||||
ret = parse_core(c, package_id, core_id++);
|
ret = parse_core(c, package_id, cluster_id,
|
||||||
|
core_id++);
|
||||||
} else {
|
} else {
|
||||||
pr_err("%pOF: Non-leaf cluster with core %s\n",
|
pr_err("%pOF: Non-leaf cluster with core %s\n",
|
||||||
cluster, name);
|
cluster, name);
|
||||||
|
@ -604,12 +610,35 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
|
||||||
if (leaf && !has_cores)
|
if (leaf && !has_cores)
|
||||||
pr_warn("%pOF: empty cluster\n", cluster);
|
pr_warn("%pOF: empty cluster\n", cluster);
|
||||||
|
|
||||||
if (leaf)
|
|
||||||
package_id++;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __init parse_socket(struct device_node *socket)
|
||||||
|
{
|
||||||
|
char name[20];
|
||||||
|
struct device_node *c;
|
||||||
|
bool has_socket = false;
|
||||||
|
int package_id = 0, ret;
|
||||||
|
|
||||||
|
do {
|
||||||
|
snprintf(name, sizeof(name), "socket%d", package_id);
|
||||||
|
c = of_get_child_by_name(socket, name);
|
||||||
|
if (c) {
|
||||||
|
has_socket = true;
|
||||||
|
ret = parse_cluster(c, package_id, -1, 0);
|
||||||
|
of_node_put(c);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
package_id++;
|
||||||
|
} while (c);
|
||||||
|
|
||||||
|
if (!has_socket)
|
||||||
|
ret = parse_cluster(socket, 0, -1, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init parse_dt_topology(void)
|
static int __init parse_dt_topology(void)
|
||||||
{
|
{
|
||||||
struct device_node *cn, *map;
|
struct device_node *cn, *map;
|
||||||
|
@ -630,7 +659,7 @@ static int __init parse_dt_topology(void)
|
||||||
if (!map)
|
if (!map)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = parse_cluster(map, 0);
|
ret = parse_socket(map);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto out_map;
|
goto out_map;
|
||||||
|
|
||||||
|
@ -641,8 +670,10 @@ static int __init parse_dt_topology(void)
|
||||||
* only mark cores described in the DT as possible.
|
* only mark cores described in the DT as possible.
|
||||||
*/
|
*/
|
||||||
for_each_possible_cpu(cpu)
|
for_each_possible_cpu(cpu)
|
||||||
if (cpu_topology[cpu].package_id == -1)
|
if (cpu_topology[cpu].package_id < 0) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
out_map:
|
out_map:
|
||||||
of_node_put(map);
|
of_node_put(map);
|
||||||
|
@ -667,7 +698,8 @@ const struct cpumask *cpu_coregroup_mask(int cpu)
|
||||||
/* not numa in package, lets use the package siblings */
|
/* not numa in package, lets use the package siblings */
|
||||||
core_mask = &cpu_topology[cpu].core_sibling;
|
core_mask = &cpu_topology[cpu].core_sibling;
|
||||||
}
|
}
|
||||||
if (cpu_topology[cpu].llc_id != -1) {
|
|
||||||
|
if (last_level_cache_is_valid(cpu)) {
|
||||||
if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
|
if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask))
|
||||||
core_mask = &cpu_topology[cpu].llc_sibling;
|
core_mask = &cpu_topology[cpu].llc_sibling;
|
||||||
}
|
}
|
||||||
|
@ -686,19 +718,31 @@ const struct cpumask *cpu_coregroup_mask(int cpu)
|
||||||
|
|
||||||
const struct cpumask *cpu_clustergroup_mask(int cpu)
|
const struct cpumask *cpu_clustergroup_mask(int cpu)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Forbid cpu_clustergroup_mask() to span more or the same CPUs as
|
||||||
|
* cpu_coregroup_mask().
|
||||||
|
*/
|
||||||
|
if (cpumask_subset(cpu_coregroup_mask(cpu),
|
||||||
|
&cpu_topology[cpu].cluster_sibling))
|
||||||
|
return get_cpu_mask(cpu);
|
||||||
|
|
||||||
return &cpu_topology[cpu].cluster_sibling;
|
return &cpu_topology[cpu].cluster_sibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_siblings_masks(unsigned int cpuid)
|
void update_siblings_masks(unsigned int cpuid)
|
||||||
{
|
{
|
||||||
struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
|
struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
|
||||||
int cpu;
|
int cpu, ret;
|
||||||
|
|
||||||
|
ret = detect_cache_attributes(cpuid);
|
||||||
|
if (ret)
|
||||||
|
pr_info("Early cacheinfo failed, ret = %d\n", ret);
|
||||||
|
|
||||||
/* update core and thread sibling masks */
|
/* update core and thread sibling masks */
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
cpu_topo = &cpu_topology[cpu];
|
cpu_topo = &cpu_topology[cpu];
|
||||||
|
|
||||||
if (cpu_topo->llc_id != -1 && cpuid_topo->llc_id == cpu_topo->llc_id) {
|
if (last_level_cache_is_shared(cpu, cpuid)) {
|
||||||
cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
|
cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling);
|
||||||
cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
|
cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling);
|
||||||
}
|
}
|
||||||
|
@ -706,15 +750,17 @@ void update_siblings_masks(unsigned int cpuid)
|
||||||
if (cpuid_topo->package_id != cpu_topo->package_id)
|
if (cpuid_topo->package_id != cpu_topo->package_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (cpuid_topo->cluster_id == cpu_topo->cluster_id &&
|
cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
|
||||||
cpuid_topo->cluster_id != -1) {
|
cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
|
||||||
|
|
||||||
|
if (cpuid_topo->cluster_id != cpu_topo->cluster_id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cpuid_topo->cluster_id >= 0) {
|
||||||
cpumask_set_cpu(cpu, &cpuid_topo->cluster_sibling);
|
cpumask_set_cpu(cpu, &cpuid_topo->cluster_sibling);
|
||||||
cpumask_set_cpu(cpuid, &cpu_topo->cluster_sibling);
|
cpumask_set_cpu(cpuid, &cpu_topo->cluster_sibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
|
|
||||||
cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
|
|
||||||
|
|
||||||
if (cpuid_topo->core_id != cpu_topo->core_id)
|
if (cpuid_topo->core_id != cpu_topo->core_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -750,7 +796,6 @@ void __init reset_cpu_topology(void)
|
||||||
cpu_topo->core_id = -1;
|
cpu_topo->core_id = -1;
|
||||||
cpu_topo->cluster_id = -1;
|
cpu_topo->cluster_id = -1;
|
||||||
cpu_topo->package_id = -1;
|
cpu_topo->package_id = -1;
|
||||||
cpu_topo->llc_id = -1;
|
|
||||||
|
|
||||||
clear_cpu_topology(cpu);
|
clear_cpu_topology(cpu);
|
||||||
}
|
}
|
||||||
|
@ -780,15 +825,20 @@ __weak int __init parse_acpi_topology(void)
|
||||||
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
|
#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV)
|
||||||
void __init init_cpu_topology(void)
|
void __init init_cpu_topology(void)
|
||||||
{
|
{
|
||||||
reset_cpu_topology();
|
int ret;
|
||||||
|
|
||||||
/*
|
reset_cpu_topology();
|
||||||
* Discard anything that was parsed if we hit an error so we
|
ret = parse_acpi_topology();
|
||||||
* don't use partial information.
|
if (!ret)
|
||||||
*/
|
ret = of_have_populated_dt() && parse_dt_topology();
|
||||||
if (parse_acpi_topology())
|
|
||||||
reset_cpu_topology();
|
if (ret) {
|
||||||
else if (of_have_populated_dt() && parse_dt_topology())
|
/*
|
||||||
|
* Discard anything that was parsed if we hit an error so we
|
||||||
|
* don't use partial information.
|
||||||
|
*/
|
||||||
reset_cpu_topology();
|
reset_cpu_topology();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -160,6 +160,7 @@ extern int devres_release_all(struct device *dev);
|
||||||
extern void device_block_probing(void);
|
extern void device_block_probing(void);
|
||||||
extern void device_unblock_probing(void);
|
extern void device_unblock_probing(void);
|
||||||
extern void deferred_probe_extend_timeout(void);
|
extern void deferred_probe_extend_timeout(void);
|
||||||
|
extern void driver_deferred_probe_trigger(void);
|
||||||
|
|
||||||
/* /sys/devices directory */
|
/* /sys/devices directory */
|
||||||
extern struct kset *devices_kset;
|
extern struct kset *devices_kset;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
|
@ -25,19 +25,60 @@ static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo);
|
||||||
#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu))
|
#define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu))
|
||||||
#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves)
|
#define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves)
|
||||||
#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list)
|
#define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list)
|
||||||
|
#define per_cpu_cacheinfo_idx(cpu, idx) \
|
||||||
|
(per_cpu_cacheinfo(cpu) + (idx))
|
||||||
|
|
||||||
struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu)
|
struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu)
|
||||||
{
|
{
|
||||||
return ci_cacheinfo(cpu);
|
return ci_cacheinfo(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
|
||||||
static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
|
static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
|
||||||
struct cacheinfo *sib_leaf)
|
struct cacheinfo *sib_leaf)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* For non DT/ACPI systems, assume unique level 1 caches,
|
||||||
|
* system-wide shared caches for all other levels. This will be used
|
||||||
|
* only if arch specific code has not populated shared_cpu_map
|
||||||
|
*/
|
||||||
|
if (!(IS_ENABLED(CONFIG_OF) || IS_ENABLED(CONFIG_ACPI)))
|
||||||
|
return !(this_leaf->level == 1);
|
||||||
|
|
||||||
|
if ((sib_leaf->attributes & CACHE_ID) &&
|
||||||
|
(this_leaf->attributes & CACHE_ID))
|
||||||
|
return sib_leaf->id == this_leaf->id;
|
||||||
|
|
||||||
return sib_leaf->fw_token == this_leaf->fw_token;
|
return sib_leaf->fw_token == this_leaf->fw_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool last_level_cache_is_valid(unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct cacheinfo *llc;
|
||||||
|
|
||||||
|
if (!cache_leaves(cpu))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1);
|
||||||
|
|
||||||
|
return (llc->attributes & CACHE_ID) || !!llc->fw_token;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y)
|
||||||
|
{
|
||||||
|
struct cacheinfo *llc_x, *llc_y;
|
||||||
|
|
||||||
|
if (!last_level_cache_is_valid(cpu_x) ||
|
||||||
|
!last_level_cache_is_valid(cpu_y))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
llc_x = per_cpu_cacheinfo_idx(cpu_x, cache_leaves(cpu_x) - 1);
|
||||||
|
llc_y = per_cpu_cacheinfo_idx(cpu_y, cache_leaves(cpu_y) - 1);
|
||||||
|
|
||||||
|
return cache_leaves_are_shared(llc_x, llc_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
/* OF properties to query for a given cache type */
|
/* OF properties to query for a given cache type */
|
||||||
struct cache_type_info {
|
struct cache_type_info {
|
||||||
const char *size_prop;
|
const char *size_prop;
|
||||||
|
@ -157,27 +198,16 @@ static int cache_setup_of_node(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
struct cacheinfo *this_leaf;
|
struct cacheinfo *this_leaf;
|
||||||
struct device *cpu_dev = get_cpu_device(cpu);
|
|
||||||
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
|
|
||||||
unsigned int index = 0;
|
unsigned int index = 0;
|
||||||
|
|
||||||
/* skip if fw_token is already populated */
|
np = of_cpu_device_node_get(cpu);
|
||||||
if (this_cpu_ci->info_list->fw_token) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cpu_dev) {
|
|
||||||
pr_err("No cpu device for CPU %d\n", cpu);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
np = cpu_dev->of_node;
|
|
||||||
if (!np) {
|
if (!np) {
|
||||||
pr_err("Failed to find cpu%d device node\n", cpu);
|
pr_err("Failed to find cpu%d device node\n", cpu);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (index < cache_leaves(cpu)) {
|
while (index < cache_leaves(cpu)) {
|
||||||
this_leaf = this_cpu_ci->info_list + index;
|
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
|
||||||
if (this_leaf->level != 1)
|
if (this_leaf->level != 1)
|
||||||
np = of_find_next_cache_node(np);
|
np = of_find_next_cache_node(np);
|
||||||
else
|
else
|
||||||
|
@ -196,16 +226,6 @@ static int cache_setup_of_node(unsigned int cpu)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline int cache_setup_of_node(unsigned int cpu) { return 0; }
|
static inline int cache_setup_of_node(unsigned int cpu) { return 0; }
|
||||||
static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
|
|
||||||
struct cacheinfo *sib_leaf)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* For non-DT/ACPI systems, assume unique level 1 caches, system-wide
|
|
||||||
* shared caches for all other levels. This will be used only if
|
|
||||||
* arch specific code has not populated shared_cpu_map
|
|
||||||
*/
|
|
||||||
return !(this_leaf->level == 1);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int __weak cache_setup_acpi(unsigned int cpu)
|
int __weak cache_setup_acpi(unsigned int cpu)
|
||||||
|
@ -215,6 +235,18 @@ int __weak cache_setup_acpi(unsigned int cpu)
|
||||||
|
|
||||||
unsigned int coherency_max_size;
|
unsigned int coherency_max_size;
|
||||||
|
|
||||||
|
static int cache_setup_properties(unsigned int cpu)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (of_have_populated_dt())
|
||||||
|
ret = cache_setup_of_node(cpu);
|
||||||
|
else if (!acpi_disabled)
|
||||||
|
ret = cache_setup_acpi(cpu);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int cache_shared_cpu_map_setup(unsigned int cpu)
|
static int cache_shared_cpu_map_setup(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
|
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
|
||||||
|
@ -225,21 +257,21 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
|
||||||
if (this_cpu_ci->cpu_map_populated)
|
if (this_cpu_ci->cpu_map_populated)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (of_have_populated_dt())
|
/*
|
||||||
ret = cache_setup_of_node(cpu);
|
* skip setting up cache properties if LLC is valid, just need
|
||||||
else if (!acpi_disabled)
|
* to update the shared cpu_map if the cache attributes were
|
||||||
ret = cache_setup_acpi(cpu);
|
* populated early before all the cpus are brought online
|
||||||
|
*/
|
||||||
if (ret)
|
if (!last_level_cache_is_valid(cpu)) {
|
||||||
return ret;
|
ret = cache_setup_properties(cpu);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
for (index = 0; index < cache_leaves(cpu); index++) {
|
for (index = 0; index < cache_leaves(cpu); index++) {
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
this_leaf = this_cpu_ci->info_list + index;
|
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
|
||||||
/* skip if shared_cpu_map is already populated */
|
|
||||||
if (!cpumask_empty(&this_leaf->shared_cpu_map))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
|
cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
|
||||||
for_each_online_cpu(i) {
|
for_each_online_cpu(i) {
|
||||||
|
@ -247,7 +279,8 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
|
||||||
|
|
||||||
if (i == cpu || !sib_cpu_ci->info_list)
|
if (i == cpu || !sib_cpu_ci->info_list)
|
||||||
continue;/* skip if itself or no cacheinfo */
|
continue;/* skip if itself or no cacheinfo */
|
||||||
sib_leaf = sib_cpu_ci->info_list + index;
|
|
||||||
|
sib_leaf = per_cpu_cacheinfo_idx(i, index);
|
||||||
if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
|
if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
|
||||||
cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
|
cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
|
||||||
cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
|
cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
|
||||||
|
@ -263,23 +296,19 @@ static int cache_shared_cpu_map_setup(unsigned int cpu)
|
||||||
|
|
||||||
static void cache_shared_cpu_map_remove(unsigned int cpu)
|
static void cache_shared_cpu_map_remove(unsigned int cpu)
|
||||||
{
|
{
|
||||||
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
|
|
||||||
struct cacheinfo *this_leaf, *sib_leaf;
|
struct cacheinfo *this_leaf, *sib_leaf;
|
||||||
unsigned int sibling, index;
|
unsigned int sibling, index;
|
||||||
|
|
||||||
for (index = 0; index < cache_leaves(cpu); index++) {
|
for (index = 0; index < cache_leaves(cpu); index++) {
|
||||||
this_leaf = this_cpu_ci->info_list + index;
|
this_leaf = per_cpu_cacheinfo_idx(cpu, index);
|
||||||
for_each_cpu(sibling, &this_leaf->shared_cpu_map) {
|
for_each_cpu(sibling, &this_leaf->shared_cpu_map) {
|
||||||
struct cpu_cacheinfo *sib_cpu_ci;
|
struct cpu_cacheinfo *sib_cpu_ci =
|
||||||
|
get_cpu_cacheinfo(sibling);
|
||||||
|
|
||||||
if (sibling == cpu) /* skip itself */
|
if (sibling == cpu || !sib_cpu_ci->info_list)
|
||||||
continue;
|
continue;/* skip if itself or no cacheinfo */
|
||||||
|
|
||||||
sib_cpu_ci = get_cpu_cacheinfo(sibling);
|
sib_leaf = per_cpu_cacheinfo_idx(sibling, index);
|
||||||
if (!sib_cpu_ci->info_list)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sib_leaf = sib_cpu_ci->info_list + index;
|
|
||||||
cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
|
cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
|
||||||
cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
|
cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
|
||||||
}
|
}
|
||||||
|
@ -310,17 +339,28 @@ int __weak populate_cache_leaves(unsigned int cpu)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int detect_cache_attributes(unsigned int cpu)
|
int detect_cache_attributes(unsigned int cpu)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Since early detection of the cacheinfo is allowed via this
|
||||||
|
* function and this also gets called as CPU hotplug callbacks via
|
||||||
|
* cacheinfo_cpu_online, the initialisation can be skipped and only
|
||||||
|
* CPU maps can be updated as the CPU online status would be update
|
||||||
|
* if called via cacheinfo_cpu_online path.
|
||||||
|
*/
|
||||||
|
if (per_cpu_cacheinfo(cpu))
|
||||||
|
goto update_cpu_map;
|
||||||
|
|
||||||
if (init_cache_level(cpu) || !cache_leaves(cpu))
|
if (init_cache_level(cpu) || !cache_leaves(cpu))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
|
per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
|
||||||
sizeof(struct cacheinfo), GFP_KERNEL);
|
sizeof(struct cacheinfo), GFP_ATOMIC);
|
||||||
if (per_cpu_cacheinfo(cpu) == NULL)
|
if (per_cpu_cacheinfo(cpu) == NULL) {
|
||||||
|
cache_leaves(cpu) = 0;
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* populate_cache_leaves() may completely setup the cache leaves and
|
* populate_cache_leaves() may completely setup the cache leaves and
|
||||||
|
@ -329,6 +369,8 @@ static int detect_cache_attributes(unsigned int cpu)
|
||||||
ret = populate_cache_leaves(cpu);
|
ret = populate_cache_leaves(cpu);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_ci;
|
goto free_ci;
|
||||||
|
|
||||||
|
update_cpu_map:
|
||||||
/*
|
/*
|
||||||
* For systems using DT for cache hierarchy, fw_token
|
* For systems using DT for cache hierarchy, fw_token
|
||||||
* and shared_cpu_map will be set up here only if they are
|
* and shared_cpu_map will be set up here only if they are
|
||||||
|
@ -614,7 +656,6 @@ static int cache_add_dev(unsigned int cpu)
|
||||||
int rc;
|
int rc;
|
||||||
struct device *ci_dev, *parent;
|
struct device *ci_dev, *parent;
|
||||||
struct cacheinfo *this_leaf;
|
struct cacheinfo *this_leaf;
|
||||||
struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
|
|
||||||
const struct attribute_group **cache_groups;
|
const struct attribute_group **cache_groups;
|
||||||
|
|
||||||
rc = cpu_cache_sysfs_init(cpu);
|
rc = cpu_cache_sysfs_init(cpu);
|
||||||
|
@ -623,7 +664,7 @@ static int cache_add_dev(unsigned int cpu)
|
||||||
|
|
||||||
parent = per_cpu_cache_dev(cpu);
|
parent = per_cpu_cache_dev(cpu);
|
||||||
for (i = 0; i < cache_leaves(cpu); i++) {
|
for (i = 0; i < cache_leaves(cpu); i++) {
|
||||||
this_leaf = this_cpu_ci->info_list + i;
|
this_leaf = per_cpu_cacheinfo_idx(cpu, i);
|
||||||
if (this_leaf->disable_sysfs)
|
if (this_leaf->disable_sysfs)
|
||||||
continue;
|
continue;
|
||||||
if (this_leaf->type == CACHE_TYPE_NOCACHE)
|
if (this_leaf->type == CACHE_TYPE_NOCACHE)
|
||||||
|
|
|
@ -54,6 +54,7 @@ static unsigned int defer_sync_state_count = 1;
|
||||||
static DEFINE_MUTEX(fwnode_link_lock);
|
static DEFINE_MUTEX(fwnode_link_lock);
|
||||||
static bool fw_devlink_is_permissive(void);
|
static bool fw_devlink_is_permissive(void);
|
||||||
static bool fw_devlink_drv_reg_done;
|
static bool fw_devlink_drv_reg_done;
|
||||||
|
static bool fw_devlink_best_effort;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fwnode_link_add - Create a link between two fwnode_handles.
|
* fwnode_link_add - Create a link between two fwnode_handles.
|
||||||
|
@ -976,6 +977,12 @@ static void device_links_missing_supplier(struct device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dev_is_best_effort(struct device *dev)
|
||||||
|
{
|
||||||
|
return (fw_devlink_best_effort && dev->can_match) ||
|
||||||
|
(dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_links_check_suppliers - Check presence of supplier drivers.
|
* device_links_check_suppliers - Check presence of supplier drivers.
|
||||||
* @dev: Consumer device.
|
* @dev: Consumer device.
|
||||||
|
@ -995,7 +1002,7 @@ static void device_links_missing_supplier(struct device *dev)
|
||||||
int device_links_check_suppliers(struct device *dev)
|
int device_links_check_suppliers(struct device *dev)
|
||||||
{
|
{
|
||||||
struct device_link *link;
|
struct device_link *link;
|
||||||
int ret = 0;
|
int ret = 0, fwnode_ret = 0;
|
||||||
struct fwnode_handle *sup_fw;
|
struct fwnode_handle *sup_fw;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1008,12 +1015,17 @@ int device_links_check_suppliers(struct device *dev)
|
||||||
sup_fw = list_first_entry(&dev->fwnode->suppliers,
|
sup_fw = list_first_entry(&dev->fwnode->suppliers,
|
||||||
struct fwnode_link,
|
struct fwnode_link,
|
||||||
c_hook)->supplier;
|
c_hook)->supplier;
|
||||||
dev_err_probe(dev, -EPROBE_DEFER, "wait for supplier %pfwP\n",
|
if (!dev_is_best_effort(dev)) {
|
||||||
sup_fw);
|
fwnode_ret = -EPROBE_DEFER;
|
||||||
mutex_unlock(&fwnode_link_lock);
|
dev_err_probe(dev, -EPROBE_DEFER,
|
||||||
return -EPROBE_DEFER;
|
"wait for supplier %pfwP\n", sup_fw);
|
||||||
|
} else {
|
||||||
|
fwnode_ret = -EAGAIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&fwnode_link_lock);
|
mutex_unlock(&fwnode_link_lock);
|
||||||
|
if (fwnode_ret == -EPROBE_DEFER)
|
||||||
|
return fwnode_ret;
|
||||||
|
|
||||||
device_links_write_lock();
|
device_links_write_lock();
|
||||||
|
|
||||||
|
@ -1023,6 +1035,14 @@ int device_links_check_suppliers(struct device *dev)
|
||||||
|
|
||||||
if (link->status != DL_STATE_AVAILABLE &&
|
if (link->status != DL_STATE_AVAILABLE &&
|
||||||
!(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
|
!(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
|
||||||
|
|
||||||
|
if (dev_is_best_effort(dev) &&
|
||||||
|
link->flags & DL_FLAG_INFERRED &&
|
||||||
|
!link->supplier->can_match) {
|
||||||
|
ret = -EAGAIN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
device_links_missing_supplier(dev);
|
device_links_missing_supplier(dev);
|
||||||
dev_err_probe(dev, -EPROBE_DEFER,
|
dev_err_probe(dev, -EPROBE_DEFER,
|
||||||
"supplier %s not ready\n",
|
"supplier %s not ready\n",
|
||||||
|
@ -1035,7 +1055,8 @@ int device_links_check_suppliers(struct device *dev)
|
||||||
dev->links.status = DL_DEV_PROBING;
|
dev->links.status = DL_DEV_PROBING;
|
||||||
|
|
||||||
device_links_write_unlock();
|
device_links_write_unlock();
|
||||||
return ret;
|
|
||||||
|
return ret ? ret : fwnode_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1300,6 +1321,18 @@ void device_links_driver_bound(struct device *dev)
|
||||||
* save to drop the managed link completely.
|
* save to drop the managed link completely.
|
||||||
*/
|
*/
|
||||||
device_link_drop_managed(link);
|
device_link_drop_managed(link);
|
||||||
|
} else if (dev_is_best_effort(dev) &&
|
||||||
|
link->flags & DL_FLAG_INFERRED &&
|
||||||
|
link->status != DL_STATE_CONSUMER_PROBE &&
|
||||||
|
!link->supplier->can_match) {
|
||||||
|
/*
|
||||||
|
* When dev_is_best_effort() is true, we ignore device
|
||||||
|
* links to suppliers that don't have a driver. If the
|
||||||
|
* consumer device still managed to probe, there's no
|
||||||
|
* point in maintaining a device link in a weird state
|
||||||
|
* (consumer probed before supplier). So delete it.
|
||||||
|
*/
|
||||||
|
device_link_drop_managed(link);
|
||||||
} else {
|
} else {
|
||||||
WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
|
WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
|
||||||
WRITE_ONCE(link->status, DL_STATE_ACTIVE);
|
WRITE_ONCE(link->status, DL_STATE_ACTIVE);
|
||||||
|
@ -1592,7 +1625,7 @@ static int __init fw_devlink_setup(char *arg)
|
||||||
}
|
}
|
||||||
early_param("fw_devlink", fw_devlink_setup);
|
early_param("fw_devlink", fw_devlink_setup);
|
||||||
|
|
||||||
static bool fw_devlink_strict;
|
static bool fw_devlink_strict = true;
|
||||||
static int __init fw_devlink_strict_setup(char *arg)
|
static int __init fw_devlink_strict_setup(char *arg)
|
||||||
{
|
{
|
||||||
return strtobool(arg, &fw_devlink_strict);
|
return strtobool(arg, &fw_devlink_strict);
|
||||||
|
@ -1666,6 +1699,62 @@ void fw_devlink_drivers_done(void)
|
||||||
device_links_write_unlock();
|
device_links_write_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wait_for_init_devices_probe - Try to probe any device needed for init
|
||||||
|
*
|
||||||
|
* Some devices might need to be probed and bound successfully before the kernel
|
||||||
|
* boot sequence can finish and move on to init/userspace. For example, a
|
||||||
|
* network interface might need to be bound to be able to mount a NFS rootfs.
|
||||||
|
*
|
||||||
|
* With fw_devlink=on by default, some of these devices might be blocked from
|
||||||
|
* probing because they are waiting on a optional supplier that doesn't have a
|
||||||
|
* driver. While fw_devlink will eventually identify such devices and unblock
|
||||||
|
* the probing automatically, it might be too late by the time it unblocks the
|
||||||
|
* probing of devices. For example, the IP4 autoconfig might timeout before
|
||||||
|
* fw_devlink unblocks probing of the network interface.
|
||||||
|
*
|
||||||
|
* This function is available to temporarily try and probe all devices that have
|
||||||
|
* a driver even if some of their suppliers haven't been added or don't have
|
||||||
|
* drivers.
|
||||||
|
*
|
||||||
|
* The drivers can then decide which of the suppliers are optional vs mandatory
|
||||||
|
* and probe the device if possible. By the time this function returns, all such
|
||||||
|
* "best effort" probes are guaranteed to be completed. If a device successfully
|
||||||
|
* probes in this mode, we delete all fw_devlink discovered dependencies of that
|
||||||
|
* device where the supplier hasn't yet probed successfully because they have to
|
||||||
|
* be optional dependencies.
|
||||||
|
*
|
||||||
|
* Any devices that didn't successfully probe go back to being treated as if
|
||||||
|
* this function was never called.
|
||||||
|
*
|
||||||
|
* This also means that some devices that aren't needed for init and could have
|
||||||
|
* waited for their optional supplier to probe (when the supplier's module is
|
||||||
|
* loaded later on) would end up probing prematurely with limited functionality.
|
||||||
|
* So call this function only when boot would fail without it.
|
||||||
|
*/
|
||||||
|
void __init wait_for_init_devices_probe(void)
|
||||||
|
{
|
||||||
|
if (!fw_devlink_flags || fw_devlink_is_permissive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for all ongoing probes to finish so that the "best effort" is
|
||||||
|
* only applied to devices that can't probe otherwise.
|
||||||
|
*/
|
||||||
|
wait_for_device_probe();
|
||||||
|
|
||||||
|
pr_info("Trying to probe devices needed for running init ...\n");
|
||||||
|
fw_devlink_best_effort = true;
|
||||||
|
driver_deferred_probe_trigger();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for all "best effort" probes to finish before going back to
|
||||||
|
* normal enforcement.
|
||||||
|
*/
|
||||||
|
wait_for_device_probe();
|
||||||
|
fw_devlink_best_effort = false;
|
||||||
|
}
|
||||||
|
|
||||||
static void fw_devlink_unblock_consumers(struct device *dev)
|
static void fw_devlink_unblock_consumers(struct device *dev)
|
||||||
{
|
{
|
||||||
struct device_link *link;
|
struct device_link *link;
|
||||||
|
@ -3843,6 +3932,26 @@ struct device *device_find_child_by_name(struct device *parent,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_find_child_by_name);
|
EXPORT_SYMBOL_GPL(device_find_child_by_name);
|
||||||
|
|
||||||
|
static int match_any(struct device *dev, void *unused)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* device_find_any_child - device iterator for locating a child device, if any.
|
||||||
|
* @parent: parent struct device
|
||||||
|
*
|
||||||
|
* This is similar to the device_find_child() function above, but it
|
||||||
|
* returns a reference to a child device, if any.
|
||||||
|
*
|
||||||
|
* NOTE: you will need to drop the reference with put_device() after use.
|
||||||
|
*/
|
||||||
|
struct device *device_find_any_child(struct device *parent)
|
||||||
|
{
|
||||||
|
return device_find_child(parent, NULL, match_any);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(device_find_any_child);
|
||||||
|
|
||||||
int __init devices_init(void)
|
int __init devices_init(void)
|
||||||
{
|
{
|
||||||
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
|
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
|
||||||
|
|
|
@ -172,7 +172,7 @@ static bool driver_deferred_probe_enable;
|
||||||
* changes in the midst of a probe, then deferred processing should be triggered
|
* changes in the midst of a probe, then deferred processing should be triggered
|
||||||
* again.
|
* again.
|
||||||
*/
|
*/
|
||||||
static void driver_deferred_probe_trigger(void)
|
void driver_deferred_probe_trigger(void)
|
||||||
{
|
{
|
||||||
if (!driver_deferred_probe_enable)
|
if (!driver_deferred_probe_enable)
|
||||||
return;
|
return;
|
||||||
|
@ -256,7 +256,12 @@ static int deferred_devs_show(struct seq_file *s, void *data)
|
||||||
}
|
}
|
||||||
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
|
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
|
||||||
|
|
||||||
|
#ifdef CONFIG_MODULES
|
||||||
|
int driver_deferred_probe_timeout = 10;
|
||||||
|
#else
|
||||||
int driver_deferred_probe_timeout;
|
int driver_deferred_probe_timeout;
|
||||||
|
#endif
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout);
|
EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout);
|
||||||
|
|
||||||
static int __init deferred_probe_timeout_setup(char *str)
|
static int __init deferred_probe_timeout_setup(char *str)
|
||||||
|
@ -269,42 +274,12 @@ static int __init deferred_probe_timeout_setup(char *str)
|
||||||
}
|
}
|
||||||
__setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
__setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
||||||
|
|
||||||
/**
|
|
||||||
* driver_deferred_probe_check_state() - Check deferred probe state
|
|
||||||
* @dev: device to check
|
|
||||||
*
|
|
||||||
* Return:
|
|
||||||
* * -ENODEV if initcalls have completed and modules are disabled.
|
|
||||||
* * -ETIMEDOUT if the deferred probe timeout was set and has expired
|
|
||||||
* and modules are enabled.
|
|
||||||
* * -EPROBE_DEFER in other cases.
|
|
||||||
*
|
|
||||||
* Drivers or subsystems can opt-in to calling this function instead of directly
|
|
||||||
* returning -EPROBE_DEFER.
|
|
||||||
*/
|
|
||||||
int driver_deferred_probe_check_state(struct device *dev)
|
|
||||||
{
|
|
||||||
if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) {
|
|
||||||
dev_warn(dev, "ignoring dependency for device, assuming no driver\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!driver_deferred_probe_timeout && initcalls_done) {
|
|
||||||
dev_warn(dev, "deferred probe timeout, ignoring dependency\n");
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(driver_deferred_probe_check_state);
|
|
||||||
|
|
||||||
static void deferred_probe_timeout_work_func(struct work_struct *work)
|
static void deferred_probe_timeout_work_func(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct device_private *p;
|
struct device_private *p;
|
||||||
|
|
||||||
fw_devlink_drivers_done();
|
fw_devlink_drivers_done();
|
||||||
|
|
||||||
driver_deferred_probe_timeout = 0;
|
|
||||||
driver_deferred_probe_trigger();
|
driver_deferred_probe_trigger();
|
||||||
flush_work(&deferred_probe_work);
|
flush_work(&deferred_probe_work);
|
||||||
|
|
||||||
|
@ -580,7 +555,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
|
||||||
{
|
{
|
||||||
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
|
bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) &&
|
||||||
!drv->suppress_bind_attrs;
|
!drv->suppress_bind_attrs;
|
||||||
int ret;
|
int ret, link_ret;
|
||||||
|
|
||||||
if (defer_all_probes) {
|
if (defer_all_probes) {
|
||||||
/*
|
/*
|
||||||
|
@ -592,9 +567,9 @@ static int really_probe(struct device *dev, struct device_driver *drv)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = device_links_check_suppliers(dev);
|
link_ret = device_links_check_suppliers(dev);
|
||||||
if (ret)
|
if (link_ret == -EPROBE_DEFER)
|
||||||
return ret;
|
return link_ret;
|
||||||
|
|
||||||
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
|
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
|
||||||
drv->bus->name, __func__, drv->name, dev_name(dev));
|
drv->bus->name, __func__, drv->name, dev_name(dev));
|
||||||
|
@ -633,6 +608,15 @@ re_probe:
|
||||||
|
|
||||||
ret = call_driver_probe(dev, drv);
|
ret = call_driver_probe(dev, drv);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
/*
|
||||||
|
* If fw_devlink_best_effort is active (denoted by -EAGAIN), the
|
||||||
|
* device might actually probe properly once some of its missing
|
||||||
|
* suppliers have probed. So, treat this as if the driver
|
||||||
|
* returned -EPROBE_DEFER.
|
||||||
|
*/
|
||||||
|
if (link_ret == -EAGAIN)
|
||||||
|
ret = -EPROBE_DEFER;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return probe errors as positive values so that the callers
|
* Return probe errors as positive values so that the callers
|
||||||
* can distinguish them from other errors.
|
* can distinguish them from other errors.
|
||||||
|
@ -1115,6 +1099,7 @@ static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
|
||||||
static int __driver_attach(struct device *dev, void *data)
|
static int __driver_attach(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct device_driver *drv = data;
|
struct device_driver *drv = data;
|
||||||
|
bool async = false;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1153,9 +1138,11 @@ static int __driver_attach(struct device *dev, void *data)
|
||||||
if (!dev->driver && !dev->p->async_driver) {
|
if (!dev->driver && !dev->p->async_driver) {
|
||||||
get_device(dev);
|
get_device(dev);
|
||||||
dev->p->async_driver = drv;
|
dev->p->async_driver = drv;
|
||||||
async_schedule_dev(__driver_attach_async_helper, dev);
|
async = true;
|
||||||
}
|
}
|
||||||
device_unlock(dev);
|
device_unlock(dev);
|
||||||
|
if (async)
|
||||||
|
async_schedule_dev(__driver_attach_async_helper, dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -482,6 +482,7 @@ int __init devtmpfs_init(void)
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
|
printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
|
||||||
unregister_filesystem(&dev_fs_type);
|
unregister_filesystem(&dev_fs_type);
|
||||||
|
thread = NULL;
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -435,11 +435,11 @@ static int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv,
|
||||||
|
|
||||||
/* decompress onto the new allocated page */
|
/* decompress onto the new allocated page */
|
||||||
page = fw_priv->pages[fw_priv->nr_pages - 1];
|
page = fw_priv->pages[fw_priv->nr_pages - 1];
|
||||||
xz_buf.out = kmap(page);
|
xz_buf.out = kmap_local_page(page);
|
||||||
xz_buf.out_pos = 0;
|
xz_buf.out_pos = 0;
|
||||||
xz_buf.out_size = PAGE_SIZE;
|
xz_buf.out_size = PAGE_SIZE;
|
||||||
xz_ret = xz_dec_run(xz_dec, &xz_buf);
|
xz_ret = xz_dec_run(xz_dec, &xz_buf);
|
||||||
kunmap(page);
|
kunmap_local(xz_buf.out);
|
||||||
fw_priv->size += xz_buf.out_pos;
|
fw_priv->size += xz_buf.out_pos;
|
||||||
/* partial decompression means either end or error */
|
/* partial decompression means either end or error */
|
||||||
if (xz_buf.out_pos != PAGE_SIZE)
|
if (xz_buf.out_pos != PAGE_SIZE)
|
||||||
|
|
|
@ -242,19 +242,17 @@ static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
|
||||||
loff_t offset, size_t count, bool read)
|
loff_t offset, size_t count, bool read)
|
||||||
{
|
{
|
||||||
while (count) {
|
while (count) {
|
||||||
void *page_data;
|
|
||||||
int page_nr = offset >> PAGE_SHIFT;
|
int page_nr = offset >> PAGE_SHIFT;
|
||||||
int page_ofs = offset & (PAGE_SIZE - 1);
|
int page_ofs = offset & (PAGE_SIZE - 1);
|
||||||
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
|
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
|
||||||
|
|
||||||
page_data = kmap(fw_priv->pages[page_nr]);
|
|
||||||
|
|
||||||
if (read)
|
if (read)
|
||||||
memcpy(buffer, page_data + page_ofs, page_cnt);
|
memcpy_from_page(buffer, fw_priv->pages[page_nr],
|
||||||
|
page_ofs, page_cnt);
|
||||||
else
|
else
|
||||||
memcpy(page_data + page_ofs, buffer, page_cnt);
|
memcpy_to_page(fw_priv->pages[page_nr], page_ofs,
|
||||||
|
buffer, page_cnt);
|
||||||
|
|
||||||
kunmap(fw_priv->pages[page_nr]);
|
|
||||||
buffer += page_cnt;
|
buffer += page_cnt;
|
||||||
offset += page_cnt;
|
offset += page_cnt;
|
||||||
count -= page_cnt;
|
count -= page_cnt;
|
||||||
|
|
|
@ -45,7 +45,7 @@ static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj,
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BIN_ATTR_RO(cpumap, 0);
|
static BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES);
|
||||||
|
|
||||||
static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
|
static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
|
||||||
struct bin_attribute *attr, char *buf,
|
struct bin_attribute *attr, char *buf,
|
||||||
|
@ -66,7 +66,7 @@ static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj,
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BIN_ATTR_RO(cpulist, 0);
|
static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct node_access_nodes - Access class device to hold user visible
|
* struct node_access_nodes - Access class device to hold user visible
|
||||||
|
|
|
@ -2733,7 +2733,7 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
|
||||||
mutex_unlock(&gpd_list_lock);
|
mutex_unlock(&gpd_list_lock);
|
||||||
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
|
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
|
||||||
__func__, PTR_ERR(pd));
|
__func__, PTR_ERR(pd));
|
||||||
return driver_deferred_probe_check_state(base_dev);
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
|
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
|
||||||
|
|
|
@ -62,47 +62,47 @@ define_id_show_func(ppin, "0x%llx");
|
||||||
static DEVICE_ATTR_ADMIN_RO(ppin);
|
static DEVICE_ATTR_ADMIN_RO(ppin);
|
||||||
|
|
||||||
define_siblings_read_func(thread_siblings, sibling_cpumask);
|
define_siblings_read_func(thread_siblings, sibling_cpumask);
|
||||||
static BIN_ATTR_RO(thread_siblings, 0);
|
static BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(thread_siblings_list, 0);
|
static BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES);
|
||||||
|
|
||||||
define_siblings_read_func(core_cpus, sibling_cpumask);
|
define_siblings_read_func(core_cpus, sibling_cpumask);
|
||||||
static BIN_ATTR_RO(core_cpus, 0);
|
static BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(core_cpus_list, 0);
|
static BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES);
|
||||||
|
|
||||||
define_siblings_read_func(core_siblings, core_cpumask);
|
define_siblings_read_func(core_siblings, core_cpumask);
|
||||||
static BIN_ATTR_RO(core_siblings, 0);
|
static BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(core_siblings_list, 0);
|
static BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES);
|
||||||
|
|
||||||
#ifdef TOPOLOGY_CLUSTER_SYSFS
|
#ifdef TOPOLOGY_CLUSTER_SYSFS
|
||||||
define_siblings_read_func(cluster_cpus, cluster_cpumask);
|
define_siblings_read_func(cluster_cpus, cluster_cpumask);
|
||||||
static BIN_ATTR_RO(cluster_cpus, 0);
|
static BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(cluster_cpus_list, 0);
|
static BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TOPOLOGY_DIE_SYSFS
|
#ifdef TOPOLOGY_DIE_SYSFS
|
||||||
define_siblings_read_func(die_cpus, die_cpumask);
|
define_siblings_read_func(die_cpus, die_cpumask);
|
||||||
static BIN_ATTR_RO(die_cpus, 0);
|
static BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(die_cpus_list, 0);
|
static BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
define_siblings_read_func(package_cpus, core_cpumask);
|
define_siblings_read_func(package_cpus, core_cpumask);
|
||||||
static BIN_ATTR_RO(package_cpus, 0);
|
static BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(package_cpus_list, 0);
|
static BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES);
|
||||||
|
|
||||||
#ifdef TOPOLOGY_BOOK_SYSFS
|
#ifdef TOPOLOGY_BOOK_SYSFS
|
||||||
define_id_show_func(book_id, "%d");
|
define_id_show_func(book_id, "%d");
|
||||||
static DEVICE_ATTR_RO(book_id);
|
static DEVICE_ATTR_RO(book_id);
|
||||||
define_siblings_read_func(book_siblings, book_cpumask);
|
define_siblings_read_func(book_siblings, book_cpumask);
|
||||||
static BIN_ATTR_RO(book_siblings, 0);
|
static BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(book_siblings_list, 0);
|
static BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TOPOLOGY_DRAWER_SYSFS
|
#ifdef TOPOLOGY_DRAWER_SYSFS
|
||||||
define_id_show_func(drawer_id, "%d");
|
define_id_show_func(drawer_id, "%d");
|
||||||
static DEVICE_ATTR_RO(drawer_id);
|
static DEVICE_ATTR_RO(drawer_id);
|
||||||
define_siblings_read_func(drawer_siblings, drawer_cpumask);
|
define_siblings_read_func(drawer_siblings, drawer_cpumask);
|
||||||
static BIN_ATTR_RO(drawer_siblings, 0);
|
static BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES);
|
||||||
static BIN_ATTR_RO(drawer_siblings_list, 0);
|
static BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct bin_attribute *bin_attrs[] = {
|
static struct bin_attribute *bin_attrs[] = {
|
||||||
|
|
|
@ -40,7 +40,7 @@ static int of_iommu_xlate(struct device *dev,
|
||||||
* a proper probe-ordering dependency mechanism in future.
|
* a proper probe-ordering dependency mechanism in future.
|
||||||
*/
|
*/
|
||||||
if (!ops)
|
if (!ops)
|
||||||
return driver_deferred_probe_check_state(dev);
|
return -ENODEV;
|
||||||
|
|
||||||
if (!try_module_get(ops->owner))
|
if (!try_module_get(ops->owner))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -47,9 +47,7 @@ int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
|
||||||
* just fall back to poll mode
|
* just fall back to poll mode
|
||||||
*/
|
*/
|
||||||
if (rc == -EPROBE_DEFER)
|
if (rc == -EPROBE_DEFER)
|
||||||
rc = driver_deferred_probe_check_state(&phy->mdio.dev);
|
rc = -ENODEV;
|
||||||
if (rc == -EPROBE_DEFER)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
phy->irq = rc;
|
phy->irq = rc;
|
||||||
|
|
|
@ -1919,6 +1919,8 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
|
||||||
of_property_read_string(of_aliases, "stdout", &name);
|
of_property_read_string(of_aliases, "stdout", &name);
|
||||||
if (name)
|
if (name)
|
||||||
of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
|
of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
|
||||||
|
if (of_stdout)
|
||||||
|
of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_aliases)
|
if (!of_aliases)
|
||||||
|
|
|
@ -129,7 +129,7 @@ static int dt_to_map_one_config(struct pinctrl *p,
|
||||||
np_pctldev = of_get_next_parent(np_pctldev);
|
np_pctldev = of_get_next_parent(np_pctldev);
|
||||||
if (!np_pctldev || of_node_is_root(np_pctldev)) {
|
if (!np_pctldev || of_node_is_root(np_pctldev)) {
|
||||||
of_node_put(np_pctldev);
|
of_node_put(np_pctldev);
|
||||||
ret = driver_deferred_probe_check_state(p->dev);
|
ret = -ENODEV;
|
||||||
/* keep deferring if modules are enabled */
|
/* keep deferring if modules are enabled */
|
||||||
if (IS_ENABLED(CONFIG_MODULES) && !allow_default && ret < 0)
|
if (IS_ENABLED(CONFIG_MODULES) && !allow_default && ret < 0)
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
|
|
|
@ -2687,11 +2687,6 @@ int spi_slave_abort(struct spi_device *spi)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spi_slave_abort);
|
EXPORT_SYMBOL_GPL(spi_slave_abort);
|
||||||
|
|
||||||
static int match_true(struct device *dev, void *data)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t slave_show(struct device *dev, struct device_attribute *attr,
|
static ssize_t slave_show(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
|
@ -2699,7 +2694,7 @@ static ssize_t slave_show(struct device *dev, struct device_attribute *attr,
|
||||||
dev);
|
dev);
|
||||||
struct device *child;
|
struct device *child;
|
||||||
|
|
||||||
child = device_find_child(&ctlr->dev, NULL, match_true);
|
child = device_find_any_child(&ctlr->dev);
|
||||||
return sprintf(buf, "%s\n",
|
return sprintf(buf, "%s\n",
|
||||||
child ? to_spi_device(child)->modalias : NULL);
|
child ? to_spi_device(child)->modalias : NULL);
|
||||||
}
|
}
|
||||||
|
@ -2718,7 +2713,7 @@ static ssize_t slave_store(struct device *dev, struct device_attribute *attr,
|
||||||
if (rc != 1 || !name[0])
|
if (rc != 1 || !name[0])
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
child = device_find_child(&ctlr->dev, NULL, match_true);
|
child = device_find_any_child(&ctlr->dev);
|
||||||
if (child) {
|
if (child) {
|
||||||
/* Remove registered slave */
|
/* Remove registered slave */
|
||||||
device_unregister(child);
|
device_unregister(child);
|
||||||
|
|
|
@ -1343,14 +1343,17 @@ static void __kernfs_remove(struct kernfs_node *kn)
|
||||||
{
|
{
|
||||||
struct kernfs_node *pos;
|
struct kernfs_node *pos;
|
||||||
|
|
||||||
|
/* Short-circuit if non-root @kn has already finished removal. */
|
||||||
|
if (!kn)
|
||||||
|
return;
|
||||||
|
|
||||||
lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem);
|
lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Short-circuit if non-root @kn has already finished removal.
|
|
||||||
* This is for kernfs_remove_self() which plays with active ref
|
* This is for kernfs_remove_self() which plays with active ref
|
||||||
* after removal.
|
* after removal.
|
||||||
*/
|
*/
|
||||||
if (!kn || (kn->parent && RB_EMPTY_NODE(&kn->rb)))
|
if (kn->parent && RB_EMPTY_NODE(&kn->rb))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pr_debug("kernfs %s: removing\n", kn->name);
|
pr_debug("kernfs %s: removing\n", kn->name);
|
||||||
|
|
205
fs/kernfs/file.c
205
fs/kernfs/file.c
|
@ -18,21 +18,8 @@
|
||||||
|
|
||||||
#include "kernfs-internal.h"
|
#include "kernfs-internal.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* There's one kernfs_open_file for each open file and one kernfs_open_node
|
|
||||||
* for each kernfs_node with one or more open files.
|
|
||||||
*
|
|
||||||
* kernfs_node->attr.open points to kernfs_open_node. attr.open is
|
|
||||||
* protected by kernfs_open_node_lock.
|
|
||||||
*
|
|
||||||
* filp->private_data points to seq_file whose ->private points to
|
|
||||||
* kernfs_open_file. kernfs_open_files are chained at
|
|
||||||
* kernfs_open_node->files, which is protected by kernfs_open_file_mutex.
|
|
||||||
*/
|
|
||||||
static DEFINE_SPINLOCK(kernfs_open_node_lock);
|
|
||||||
static DEFINE_MUTEX(kernfs_open_file_mutex);
|
|
||||||
|
|
||||||
struct kernfs_open_node {
|
struct kernfs_open_node {
|
||||||
|
struct rcu_head rcu_head;
|
||||||
atomic_t event;
|
atomic_t event;
|
||||||
wait_queue_head_t poll;
|
wait_queue_head_t poll;
|
||||||
struct list_head files; /* goes through kernfs_open_file.list */
|
struct list_head files; /* goes through kernfs_open_file.list */
|
||||||
|
@ -51,6 +38,70 @@ struct kernfs_open_node {
|
||||||
static DEFINE_SPINLOCK(kernfs_notify_lock);
|
static DEFINE_SPINLOCK(kernfs_notify_lock);
|
||||||
static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL;
|
static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL;
|
||||||
|
|
||||||
|
static inline struct mutex *kernfs_open_file_mutex_ptr(struct kernfs_node *kn)
|
||||||
|
{
|
||||||
|
int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS);
|
||||||
|
|
||||||
|
return &kernfs_locks->open_file_mutex[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct mutex *kernfs_open_file_mutex_lock(struct kernfs_node *kn)
|
||||||
|
{
|
||||||
|
struct mutex *lock;
|
||||||
|
|
||||||
|
lock = kernfs_open_file_mutex_ptr(kn);
|
||||||
|
|
||||||
|
mutex_lock(lock);
|
||||||
|
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kernfs_deref_open_node - Get kernfs_open_node corresponding to @kn.
|
||||||
|
*
|
||||||
|
* @of: associated kernfs_open_file instance.
|
||||||
|
* @kn: target kernfs_node.
|
||||||
|
*
|
||||||
|
* Fetch and return ->attr.open of @kn if @of->list is non empty.
|
||||||
|
* If @of->list is not empty we can safely assume that @of is on
|
||||||
|
* @kn->attr.open->files list and this guarantees that @kn->attr.open
|
||||||
|
* will not vanish i.e. dereferencing outside RCU read-side critical
|
||||||
|
* section is safe here.
|
||||||
|
*
|
||||||
|
* The caller needs to make sure that @of->list is not empty.
|
||||||
|
*/
|
||||||
|
static struct kernfs_open_node *
|
||||||
|
kernfs_deref_open_node(struct kernfs_open_file *of, struct kernfs_node *kn)
|
||||||
|
{
|
||||||
|
struct kernfs_open_node *on;
|
||||||
|
|
||||||
|
on = rcu_dereference_check(kn->attr.open, !list_empty(&of->list));
|
||||||
|
|
||||||
|
return on;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kernfs_deref_open_node_protected - Get kernfs_open_node corresponding to @kn
|
||||||
|
*
|
||||||
|
* @kn: target kernfs_node.
|
||||||
|
*
|
||||||
|
* Fetch and return ->attr.open of @kn when caller holds the
|
||||||
|
* kernfs_open_file_mutex_ptr(kn).
|
||||||
|
*
|
||||||
|
* Update of ->attr.open happens under kernfs_open_file_mutex_ptr(kn). So when
|
||||||
|
* the caller guarantees that this mutex is being held, other updaters can't
|
||||||
|
* change ->attr.open and this means that we can safely deref ->attr.open
|
||||||
|
* outside RCU read-side critical section.
|
||||||
|
*
|
||||||
|
* The caller needs to make sure that kernfs_open_file_mutex is held.
|
||||||
|
*/
|
||||||
|
static struct kernfs_open_node *
|
||||||
|
kernfs_deref_open_node_protected(struct kernfs_node *kn)
|
||||||
|
{
|
||||||
|
return rcu_dereference_protected(kn->attr.open,
|
||||||
|
lockdep_is_held(kernfs_open_file_mutex_ptr(kn)));
|
||||||
|
}
|
||||||
|
|
||||||
static struct kernfs_open_file *kernfs_of(struct file *file)
|
static struct kernfs_open_file *kernfs_of(struct file *file)
|
||||||
{
|
{
|
||||||
return ((struct seq_file *)file->private_data)->private;
|
return ((struct seq_file *)file->private_data)->private;
|
||||||
|
@ -156,8 +207,12 @@ static void kernfs_seq_stop(struct seq_file *sf, void *v)
|
||||||
static int kernfs_seq_show(struct seq_file *sf, void *v)
|
static int kernfs_seq_show(struct seq_file *sf, void *v)
|
||||||
{
|
{
|
||||||
struct kernfs_open_file *of = sf->private;
|
struct kernfs_open_file *of = sf->private;
|
||||||
|
struct kernfs_open_node *on = kernfs_deref_open_node(of, of->kn);
|
||||||
|
|
||||||
of->event = atomic_read(&of->kn->attr.open->event);
|
if (!on)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
of->event = atomic_read(&on->event);
|
||||||
|
|
||||||
return of->kn->attr.ops->seq_show(sf, v);
|
return of->kn->attr.ops->seq_show(sf, v);
|
||||||
}
|
}
|
||||||
|
@ -180,6 +235,7 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
struct kernfs_open_file *of = kernfs_of(iocb->ki_filp);
|
struct kernfs_open_file *of = kernfs_of(iocb->ki_filp);
|
||||||
ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE);
|
ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE);
|
||||||
const struct kernfs_ops *ops;
|
const struct kernfs_ops *ops;
|
||||||
|
struct kernfs_open_node *on;
|
||||||
char *buf;
|
char *buf;
|
||||||
|
|
||||||
buf = of->prealloc_buf;
|
buf = of->prealloc_buf;
|
||||||
|
@ -201,7 +257,15 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
of->event = atomic_read(&of->kn->attr.open->event);
|
on = kernfs_deref_open_node(of, of->kn);
|
||||||
|
if (!on) {
|
||||||
|
len = -EINVAL;
|
||||||
|
mutex_unlock(&of->mutex);
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
of->event = atomic_read(&on->event);
|
||||||
|
|
||||||
ops = kernfs_ops(of->kn);
|
ops = kernfs_ops(of->kn);
|
||||||
if (ops->read)
|
if (ops->read)
|
||||||
len = ops->read(of, buf, len, iocb->ki_pos);
|
len = ops->read(of, buf, len, iocb->ki_pos);
|
||||||
|
@ -243,7 +307,7 @@ static ssize_t kernfs_fop_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
* There is no easy way for us to know if userspace is only doing a partial
|
* There is no easy way for us to know if userspace is only doing a partial
|
||||||
* write, so we don't support them. We expect the entire buffer to come on
|
* write, so we don't support them. We expect the entire buffer to come on
|
||||||
* the first write. Hint: if you're writing a value, first read the file,
|
* the first write. Hint: if you're writing a value, first read the file,
|
||||||
* modify only the the value you're changing, then write entire buffer
|
* modify only the value you're changing, then write entire buffer
|
||||||
* back.
|
* back.
|
||||||
*/
|
*/
|
||||||
static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||||
|
@ -484,7 +548,6 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
|
||||||
* It is not possible to successfully wrap close.
|
* It is not possible to successfully wrap close.
|
||||||
* So error if someone is trying to use close.
|
* So error if someone is trying to use close.
|
||||||
*/
|
*/
|
||||||
rc = -EINVAL;
|
|
||||||
if (vma->vm_ops && vma->vm_ops->close)
|
if (vma->vm_ops && vma->vm_ops->close)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
|
@ -518,37 +581,31 @@ static int kernfs_get_open_node(struct kernfs_node *kn,
|
||||||
struct kernfs_open_file *of)
|
struct kernfs_open_file *of)
|
||||||
{
|
{
|
||||||
struct kernfs_open_node *on, *new_on = NULL;
|
struct kernfs_open_node *on, *new_on = NULL;
|
||||||
|
struct mutex *mutex = NULL;
|
||||||
|
|
||||||
retry:
|
mutex = kernfs_open_file_mutex_lock(kn);
|
||||||
mutex_lock(&kernfs_open_file_mutex);
|
on = kernfs_deref_open_node_protected(kn);
|
||||||
spin_lock_irq(&kernfs_open_node_lock);
|
|
||||||
|
|
||||||
if (!kn->attr.open && new_on) {
|
|
||||||
kn->attr.open = new_on;
|
|
||||||
new_on = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
on = kn->attr.open;
|
|
||||||
if (on)
|
|
||||||
list_add_tail(&of->list, &on->files);
|
|
||||||
|
|
||||||
spin_unlock_irq(&kernfs_open_node_lock);
|
|
||||||
mutex_unlock(&kernfs_open_file_mutex);
|
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
kfree(new_on);
|
list_add_tail(&of->list, &on->files);
|
||||||
|
mutex_unlock(mutex);
|
||||||
return 0;
|
return 0;
|
||||||
|
} else {
|
||||||
|
/* not there, initialize a new one */
|
||||||
|
new_on = kmalloc(sizeof(*new_on), GFP_KERNEL);
|
||||||
|
if (!new_on) {
|
||||||
|
mutex_unlock(mutex);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
atomic_set(&new_on->event, 1);
|
||||||
|
init_waitqueue_head(&new_on->poll);
|
||||||
|
INIT_LIST_HEAD(&new_on->files);
|
||||||
|
list_add_tail(&of->list, &new_on->files);
|
||||||
|
rcu_assign_pointer(kn->attr.open, new_on);
|
||||||
}
|
}
|
||||||
|
mutex_unlock(mutex);
|
||||||
|
|
||||||
/* not there, initialize a new one and retry */
|
return 0;
|
||||||
new_on = kmalloc(sizeof(*new_on), GFP_KERNEL);
|
|
||||||
if (!new_on)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
atomic_set(&new_on->event, 1);
|
|
||||||
init_waitqueue_head(&new_on->poll);
|
|
||||||
INIT_LIST_HEAD(&new_on->files);
|
|
||||||
goto retry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -567,24 +624,26 @@ static int kernfs_get_open_node(struct kernfs_node *kn,
|
||||||
static void kernfs_unlink_open_file(struct kernfs_node *kn,
|
static void kernfs_unlink_open_file(struct kernfs_node *kn,
|
||||||
struct kernfs_open_file *of)
|
struct kernfs_open_file *of)
|
||||||
{
|
{
|
||||||
struct kernfs_open_node *on = kn->attr.open;
|
struct kernfs_open_node *on;
|
||||||
unsigned long flags;
|
struct mutex *mutex = NULL;
|
||||||
|
|
||||||
mutex_lock(&kernfs_open_file_mutex);
|
mutex = kernfs_open_file_mutex_lock(kn);
|
||||||
spin_lock_irqsave(&kernfs_open_node_lock, flags);
|
|
||||||
|
on = kernfs_deref_open_node_protected(kn);
|
||||||
|
if (!on) {
|
||||||
|
mutex_unlock(mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (of)
|
if (of)
|
||||||
list_del(&of->list);
|
list_del(&of->list);
|
||||||
|
|
||||||
if (list_empty(&on->files))
|
if (list_empty(&on->files)) {
|
||||||
kn->attr.open = NULL;
|
rcu_assign_pointer(kn->attr.open, NULL);
|
||||||
else
|
kfree_rcu(on, rcu_head);
|
||||||
on = NULL;
|
}
|
||||||
|
|
||||||
spin_unlock_irqrestore(&kernfs_open_node_lock, flags);
|
mutex_unlock(mutex);
|
||||||
mutex_unlock(&kernfs_open_file_mutex);
|
|
||||||
|
|
||||||
kfree(on);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kernfs_fop_open(struct inode *inode, struct file *file)
|
static int kernfs_fop_open(struct inode *inode, struct file *file)
|
||||||
|
@ -722,11 +781,11 @@ static void kernfs_release_file(struct kernfs_node *kn,
|
||||||
/*
|
/*
|
||||||
* @of is guaranteed to have no other file operations in flight and
|
* @of is guaranteed to have no other file operations in flight and
|
||||||
* we just want to synchronize release and drain paths.
|
* we just want to synchronize release and drain paths.
|
||||||
* @kernfs_open_file_mutex is enough. @of->mutex can't be used
|
* @kernfs_open_file_mutex_ptr(kn) is enough. @of->mutex can't be used
|
||||||
* here because drain path may be called from places which can
|
* here because drain path may be called from places which can
|
||||||
* cause circular dependency.
|
* cause circular dependency.
|
||||||
*/
|
*/
|
||||||
lockdep_assert_held(&kernfs_open_file_mutex);
|
lockdep_assert_held(kernfs_open_file_mutex_ptr(kn));
|
||||||
|
|
||||||
if (!of->released) {
|
if (!of->released) {
|
||||||
/*
|
/*
|
||||||
|
@ -743,11 +802,12 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
struct kernfs_node *kn = inode->i_private;
|
struct kernfs_node *kn = inode->i_private;
|
||||||
struct kernfs_open_file *of = kernfs_of(filp);
|
struct kernfs_open_file *of = kernfs_of(filp);
|
||||||
|
struct mutex *mutex = NULL;
|
||||||
|
|
||||||
if (kn->flags & KERNFS_HAS_RELEASE) {
|
if (kn->flags & KERNFS_HAS_RELEASE) {
|
||||||
mutex_lock(&kernfs_open_file_mutex);
|
mutex = kernfs_open_file_mutex_lock(kn);
|
||||||
kernfs_release_file(kn, of);
|
kernfs_release_file(kn, of);
|
||||||
mutex_unlock(&kernfs_open_file_mutex);
|
mutex_unlock(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
kernfs_unlink_open_file(kn, of);
|
kernfs_unlink_open_file(kn, of);
|
||||||
|
@ -762,6 +822,7 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
|
||||||
{
|
{
|
||||||
struct kernfs_open_node *on;
|
struct kernfs_open_node *on;
|
||||||
struct kernfs_open_file *of;
|
struct kernfs_open_file *of;
|
||||||
|
struct mutex *mutex = NULL;
|
||||||
|
|
||||||
if (!(kn->flags & (KERNFS_HAS_MMAP | KERNFS_HAS_RELEASE)))
|
if (!(kn->flags & (KERNFS_HAS_MMAP | KERNFS_HAS_RELEASE)))
|
||||||
return;
|
return;
|
||||||
|
@ -771,20 +832,19 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
|
||||||
* ->attr.open at this point of time. This check allows early bail out
|
* ->attr.open at this point of time. This check allows early bail out
|
||||||
* if ->attr.open is already NULL. kernfs_unlink_open_file makes
|
* if ->attr.open is already NULL. kernfs_unlink_open_file makes
|
||||||
* ->attr.open NULL only while holding kernfs_open_file_mutex so below
|
* ->attr.open NULL only while holding kernfs_open_file_mutex so below
|
||||||
* check under kernfs_open_file_mutex will ensure bailing out if
|
* check under kernfs_open_file_mutex_ptr(kn) will ensure bailing out if
|
||||||
* ->attr.open became NULL while waiting for the mutex.
|
* ->attr.open became NULL while waiting for the mutex.
|
||||||
*/
|
*/
|
||||||
if (!kn->attr.open)
|
if (!rcu_access_pointer(kn->attr.open))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mutex_lock(&kernfs_open_file_mutex);
|
mutex = kernfs_open_file_mutex_lock(kn);
|
||||||
if (!kn->attr.open) {
|
on = kernfs_deref_open_node_protected(kn);
|
||||||
mutex_unlock(&kernfs_open_file_mutex);
|
if (!on) {
|
||||||
|
mutex_unlock(mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
on = kn->attr.open;
|
|
||||||
|
|
||||||
list_for_each_entry(of, &on->files, list) {
|
list_for_each_entry(of, &on->files, list) {
|
||||||
struct inode *inode = file_inode(of->file);
|
struct inode *inode = file_inode(of->file);
|
||||||
|
|
||||||
|
@ -795,7 +855,7 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
|
||||||
kernfs_release_file(kn, of);
|
kernfs_release_file(kn, of);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&kernfs_open_file_mutex);
|
mutex_unlock(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -815,7 +875,10 @@ void kernfs_drain_open_files(struct kernfs_node *kn)
|
||||||
__poll_t kernfs_generic_poll(struct kernfs_open_file *of, poll_table *wait)
|
__poll_t kernfs_generic_poll(struct kernfs_open_file *of, poll_table *wait)
|
||||||
{
|
{
|
||||||
struct kernfs_node *kn = kernfs_dentry_node(of->file->f_path.dentry);
|
struct kernfs_node *kn = kernfs_dentry_node(of->file->f_path.dentry);
|
||||||
struct kernfs_open_node *on = kn->attr.open;
|
struct kernfs_open_node *on = kernfs_deref_open_node(of, kn);
|
||||||
|
|
||||||
|
if (!on)
|
||||||
|
return EPOLLERR;
|
||||||
|
|
||||||
poll_wait(of->file, &on->poll, wait);
|
poll_wait(of->file, &on->poll, wait);
|
||||||
|
|
||||||
|
@ -922,13 +985,13 @@ void kernfs_notify(struct kernfs_node *kn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* kick poll immediately */
|
/* kick poll immediately */
|
||||||
spin_lock_irqsave(&kernfs_open_node_lock, flags);
|
rcu_read_lock();
|
||||||
on = kn->attr.open;
|
on = rcu_dereference(kn->attr.open);
|
||||||
if (on) {
|
if (on) {
|
||||||
atomic_inc(&on->event);
|
atomic_inc(&on->event);
|
||||||
wake_up_interruptible(&on->poll);
|
wake_up_interruptible(&on->poll);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&kernfs_open_node_lock, flags);
|
rcu_read_unlock();
|
||||||
|
|
||||||
/* schedule work to kick fsnotify */
|
/* schedule work to kick fsnotify */
|
||||||
spin_lock_irqsave(&kernfs_notify_lock, flags);
|
spin_lock_irqsave(&kernfs_notify_lock, flags);
|
||||||
|
|
|
@ -164,4 +164,8 @@ void kernfs_drain_open_files(struct kernfs_node *kn);
|
||||||
*/
|
*/
|
||||||
extern const struct inode_operations kernfs_symlink_iops;
|
extern const struct inode_operations kernfs_symlink_iops;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kernfs locks
|
||||||
|
*/
|
||||||
|
extern struct kernfs_global_locks *kernfs_locks;
|
||||||
#endif /* __KERNFS_INTERNAL_H */
|
#endif /* __KERNFS_INTERNAL_H */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "kernfs-internal.h"
|
#include "kernfs-internal.h"
|
||||||
|
|
||||||
struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;
|
struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache;
|
||||||
|
struct kernfs_global_locks *kernfs_locks;
|
||||||
|
|
||||||
static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry)
|
static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
|
@ -387,6 +388,22 @@ void kernfs_kill_sb(struct super_block *sb)
|
||||||
kfree(info);
|
kfree(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __init kernfs_mutex_init(void)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
|
||||||
|
for (count = 0; count < NR_KERNFS_LOCKS; count++)
|
||||||
|
mutex_init(&kernfs_locks->open_file_mutex[count]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __init kernfs_lock_init(void)
|
||||||
|
{
|
||||||
|
kernfs_locks = kmalloc(sizeof(struct kernfs_global_locks), GFP_KERNEL);
|
||||||
|
WARN_ON(!kernfs_locks);
|
||||||
|
|
||||||
|
kernfs_mutex_init();
|
||||||
|
}
|
||||||
|
|
||||||
void __init kernfs_init(void)
|
void __init kernfs_init(void)
|
||||||
{
|
{
|
||||||
kernfs_node_cache = kmem_cache_create("kernfs_node_cache",
|
kernfs_node_cache = kmem_cache_create("kernfs_node_cache",
|
||||||
|
@ -397,4 +414,6 @@ void __init kernfs_init(void)
|
||||||
kernfs_iattrs_cache = kmem_cache_create("kernfs_iattrs_cache",
|
kernfs_iattrs_cache = kmem_cache_create("kernfs_iattrs_cache",
|
||||||
sizeof(struct kernfs_iattrs),
|
sizeof(struct kernfs_iattrs),
|
||||||
0, SLAB_PANIC, NULL);
|
0, SLAB_PANIC, NULL);
|
||||||
|
|
||||||
|
kernfs_lock_init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1431,7 +1431,6 @@ int find_acpi_cpu_topology(unsigned int cpu, int level);
|
||||||
int find_acpi_cpu_topology_cluster(unsigned int cpu);
|
int find_acpi_cpu_topology_cluster(unsigned int cpu);
|
||||||
int find_acpi_cpu_topology_package(unsigned int cpu);
|
int find_acpi_cpu_topology_package(unsigned int cpu);
|
||||||
int find_acpi_cpu_topology_hetero_id(unsigned int cpu);
|
int find_acpi_cpu_topology_hetero_id(unsigned int cpu);
|
||||||
int find_acpi_cpu_cache_topology(unsigned int cpu, int level);
|
|
||||||
#else
|
#else
|
||||||
static inline int acpi_pptt_cpu_is_thread(unsigned int cpu)
|
static inline int acpi_pptt_cpu_is_thread(unsigned int cpu)
|
||||||
{
|
{
|
||||||
|
@ -1453,10 +1452,6 @@ static inline int find_acpi_cpu_topology_hetero_id(unsigned int cpu)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI_PCC
|
#ifdef CONFIG_ACPI_PCC
|
||||||
|
|
|
@ -68,7 +68,6 @@ struct cpu_topology {
|
||||||
int core_id;
|
int core_id;
|
||||||
int cluster_id;
|
int cluster_id;
|
||||||
int package_id;
|
int package_id;
|
||||||
int llc_id;
|
|
||||||
cpumask_t thread_sibling;
|
cpumask_t thread_sibling;
|
||||||
cpumask_t core_sibling;
|
cpumask_t core_sibling;
|
||||||
cpumask_t cluster_sibling;
|
cpumask_t cluster_sibling;
|
||||||
|
|
|
@ -82,6 +82,9 @@ struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu);
|
||||||
int init_cache_level(unsigned int cpu);
|
int init_cache_level(unsigned int cpu);
|
||||||
int populate_cache_leaves(unsigned int cpu);
|
int populate_cache_leaves(unsigned int cpu);
|
||||||
int cache_setup_acpi(unsigned int cpu);
|
int cache_setup_acpi(unsigned int cpu);
|
||||||
|
bool last_level_cache_is_valid(unsigned int cpu);
|
||||||
|
bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y);
|
||||||
|
int detect_cache_attributes(unsigned int cpu);
|
||||||
#ifndef CONFIG_ACPI_PPTT
|
#ifndef CONFIG_ACPI_PPTT
|
||||||
/*
|
/*
|
||||||
* acpi_find_last_cache_level is only called on ACPI enabled
|
* acpi_find_last_cache_level is only called on ACPI enabled
|
||||||
|
|
|
@ -1071,4 +1071,22 @@ cpumap_print_list_to_buf(char *buf, const struct cpumask *mask,
|
||||||
[0] = 1UL \
|
[0] = 1UL \
|
||||||
} }
|
} }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Provide a valid theoretical max size for cpumap and cpulist sysfs files
|
||||||
|
* to avoid breaking userspace which may allocate a buffer based on the size
|
||||||
|
* reported by e.g. fstat.
|
||||||
|
*
|
||||||
|
* for cpumap NR_CPUS * 9/32 - 1 should be an exact length.
|
||||||
|
*
|
||||||
|
* For cpulist 7 is (ceil(log10(NR_CPUS)) + 1) allowing for NR_CPUS to be up
|
||||||
|
* to 2 orders of magnitude larger than 8192. And then we divide by 2 to
|
||||||
|
* cover a worst-case of every other cpu being on one of two nodes for a
|
||||||
|
* very large NR_CPUS.
|
||||||
|
*
|
||||||
|
* Use PAGE_SIZE as a minimum for smaller configurations.
|
||||||
|
*/
|
||||||
|
#define CPUMAP_FILE_MAX_BYTES ((((NR_CPUS * 9)/32 - 1) > PAGE_SIZE) \
|
||||||
|
? (NR_CPUS * 9)/32 - 1 : PAGE_SIZE)
|
||||||
|
#define CPULIST_FILE_MAX_BYTES (((NR_CPUS * 7)/2 > PAGE_SIZE) ? (NR_CPUS * 7)/2 : PAGE_SIZE)
|
||||||
|
|
||||||
#endif /* __LINUX_CPUMASK_H */
|
#endif /* __LINUX_CPUMASK_H */
|
||||||
|
|
|
@ -905,6 +905,8 @@ struct device *device_find_child(struct device *dev, void *data,
|
||||||
int (*match)(struct device *dev, void *data));
|
int (*match)(struct device *dev, void *data));
|
||||||
struct device *device_find_child_by_name(struct device *parent,
|
struct device *device_find_child_by_name(struct device *parent,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
struct device *device_find_any_child(struct device *parent);
|
||||||
|
|
||||||
int device_rename(struct device *dev, const char *new_name);
|
int device_rename(struct device *dev, const char *new_name);
|
||||||
int device_move(struct device *dev, struct device *new_parent,
|
int device_move(struct device *dev, struct device *new_parent,
|
||||||
enum dpm_order dpm_order);
|
enum dpm_order dpm_order);
|
||||||
|
|
|
@ -129,6 +129,7 @@ extern struct device_driver *driver_find(const char *name,
|
||||||
struct bus_type *bus);
|
struct bus_type *bus);
|
||||||
extern int driver_probe_done(void);
|
extern int driver_probe_done(void);
|
||||||
extern void wait_for_device_probe(void);
|
extern void wait_for_device_probe(void);
|
||||||
|
void __init wait_for_init_devices_probe(void);
|
||||||
|
|
||||||
/* sysfs interface for exporting driver attributes */
|
/* sysfs interface for exporting driver attributes */
|
||||||
|
|
||||||
|
@ -241,7 +242,6 @@ driver_find_device_by_acpi_dev(struct device_driver *drv, const void *adev)
|
||||||
|
|
||||||
extern int driver_deferred_probe_timeout;
|
extern int driver_deferred_probe_timeout;
|
||||||
void driver_deferred_probe_add(struct device *dev);
|
void driver_deferred_probe_add(struct device *dev);
|
||||||
int driver_deferred_probe_check_state(struct device *dev);
|
|
||||||
void driver_init(void);
|
void driver_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,12 +71,16 @@ static inline void register_trusted_foundations(
|
||||||
|
|
||||||
static inline void of_register_trusted_foundations(void)
|
static inline void of_register_trusted_foundations(void)
|
||||||
{
|
{
|
||||||
|
struct device_node *np = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations");
|
||||||
|
|
||||||
|
if (!np)
|
||||||
|
return;
|
||||||
|
of_node_put(np);
|
||||||
/*
|
/*
|
||||||
* If we find the target should enable TF but does not support it,
|
* If we find the target should enable TF but does not support it,
|
||||||
* fail as the system won't be able to do much anyway
|
* fail as the system won't be able to do much anyway
|
||||||
*/
|
*/
|
||||||
if (of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"))
|
register_trusted_foundations(NULL);
|
||||||
register_trusted_foundations(NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool trusted_foundations_registered(void)
|
static inline bool trusted_foundations_registered(void)
|
||||||
|
|
|
@ -27,11 +27,15 @@ struct device;
|
||||||
* driver needs its child devices to be bound with
|
* driver needs its child devices to be bound with
|
||||||
* their respective drivers as soon as they are
|
* their respective drivers as soon as they are
|
||||||
* added.
|
* added.
|
||||||
|
* BEST_EFFORT: The fwnode/device needs to probe early and might be missing some
|
||||||
|
* suppliers. Only enforce ordering with suppliers that have
|
||||||
|
* drivers.
|
||||||
*/
|
*/
|
||||||
#define FWNODE_FLAG_LINKS_ADDED BIT(0)
|
#define FWNODE_FLAG_LINKS_ADDED BIT(0)
|
||||||
#define FWNODE_FLAG_NOT_DEVICE BIT(1)
|
#define FWNODE_FLAG_NOT_DEVICE BIT(1)
|
||||||
#define FWNODE_FLAG_INITIALIZED BIT(2)
|
#define FWNODE_FLAG_INITIALIZED BIT(2)
|
||||||
#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3)
|
#define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3)
|
||||||
|
#define FWNODE_FLAG_BEST_EFFORT BIT(4)
|
||||||
|
|
||||||
struct fwnode_handle {
|
struct fwnode_handle {
|
||||||
struct fwnode_handle *secondary;
|
struct fwnode_handle *secondary;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/uidgid.h>
|
#include <linux/uidgid.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
|
#include <linux/cache.h>
|
||||||
|
|
||||||
struct file;
|
struct file;
|
||||||
struct dentry;
|
struct dentry;
|
||||||
|
@ -34,6 +35,62 @@ struct kernfs_fs_context;
|
||||||
struct kernfs_open_node;
|
struct kernfs_open_node;
|
||||||
struct kernfs_iattrs;
|
struct kernfs_iattrs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NR_KERNFS_LOCK_BITS determines size (NR_KERNFS_LOCKS) of hash
|
||||||
|
* table of locks.
|
||||||
|
* Having a small hash table would impact scalability, since
|
||||||
|
* more and more kernfs_node objects will end up using same lock
|
||||||
|
* and having a very large hash table would waste memory.
|
||||||
|
*
|
||||||
|
* At the moment size of hash table of locks is being set based on
|
||||||
|
* the number of CPUs as follows:
|
||||||
|
*
|
||||||
|
* NR_CPU NR_KERNFS_LOCK_BITS NR_KERNFS_LOCKS
|
||||||
|
* 1 1 2
|
||||||
|
* 2-3 2 4
|
||||||
|
* 4-7 4 16
|
||||||
|
* 8-15 6 64
|
||||||
|
* 16-31 8 256
|
||||||
|
* 32 and more 10 1024
|
||||||
|
*
|
||||||
|
* The above relation between NR_CPU and number of locks is based
|
||||||
|
* on some internal experimentation which involved booting qemu
|
||||||
|
* with different values of smp, performing some sysfs operations
|
||||||
|
* on all CPUs and observing how increase in number of locks impacts
|
||||||
|
* completion time of these sysfs operations on each CPU.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
#define NR_KERNFS_LOCK_BITS (2 * (ilog2(NR_CPUS < 32 ? NR_CPUS : 32)))
|
||||||
|
#else
|
||||||
|
#define NR_KERNFS_LOCK_BITS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NR_KERNFS_LOCKS (1 << NR_KERNFS_LOCK_BITS)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There's one kernfs_open_file for each open file and one kernfs_open_node
|
||||||
|
* for each kernfs_node with one or more open files.
|
||||||
|
*
|
||||||
|
* filp->private_data points to seq_file whose ->private points to
|
||||||
|
* kernfs_open_file.
|
||||||
|
*
|
||||||
|
* kernfs_open_files are chained at kernfs_open_node->files, which is
|
||||||
|
* protected by kernfs_global_locks.open_file_mutex[i].
|
||||||
|
*
|
||||||
|
* To reduce possible contention in sysfs access, arising due to single
|
||||||
|
* locks, use an array of locks (e.g. open_file_mutex) and use kernfs_node
|
||||||
|
* object address as hash keys to get the index of these locks.
|
||||||
|
*
|
||||||
|
* Hashed mutexes are safe to use here because operations using these don't
|
||||||
|
* rely on global exclusion.
|
||||||
|
*
|
||||||
|
* In future we intend to replace other global locks with hashed ones as well.
|
||||||
|
* kernfs_global_locks acts as a holder for all such hash tables.
|
||||||
|
*/
|
||||||
|
struct kernfs_global_locks {
|
||||||
|
struct mutex open_file_mutex[NR_KERNFS_LOCKS];
|
||||||
|
};
|
||||||
|
|
||||||
enum kernfs_node_type {
|
enum kernfs_node_type {
|
||||||
KERNFS_DIR = 0x0001,
|
KERNFS_DIR = 0x0001,
|
||||||
KERNFS_FILE = 0x0002,
|
KERNFS_FILE = 0x0002,
|
||||||
|
@ -114,7 +171,7 @@ struct kernfs_elem_symlink {
|
||||||
|
|
||||||
struct kernfs_elem_attr {
|
struct kernfs_elem_attr {
|
||||||
const struct kernfs_ops *ops;
|
const struct kernfs_ops *ops;
|
||||||
struct kernfs_open_node *open;
|
struct kernfs_open_node __rcu *open;
|
||||||
loff_t size;
|
loff_t size;
|
||||||
struct kernfs_node *notify_next; /* for kernfs_notify() */
|
struct kernfs_node *notify_next; /* for kernfs_notify() */
|
||||||
};
|
};
|
||||||
|
|
|
@ -1560,7 +1560,7 @@ config DEBUG_KOBJECT_RELEASE
|
||||||
help
|
help
|
||||||
kobjects are reference counted objects. This means that their
|
kobjects are reference counted objects. This means that their
|
||||||
last reference count put is not predictable, and the kobject can
|
last reference count put is not predictable, and the kobject can
|
||||||
live on past the point at which a driver decides to drop it's
|
live on past the point at which a driver decides to drop its
|
||||||
initial reference to the kobject gained on allocation. An
|
initial reference to the kobject gained on allocation. An
|
||||||
example of this would be a struct device which has just been
|
example of this would be a struct device which has just been
|
||||||
unregistered.
|
unregistered.
|
||||||
|
|
|
@ -1434,6 +1434,7 @@ __be32 __init root_nfs_parse_addr(char *name)
|
||||||
static int __init wait_for_devices(void)
|
static int __init wait_for_devices(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
bool try_init_devs = true;
|
||||||
|
|
||||||
for (i = 0; i < DEVICE_WAIT_MAX; i++) {
|
for (i = 0; i < DEVICE_WAIT_MAX; i++) {
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
@ -1452,6 +1453,11 @@ static int __init wait_for_devices(void)
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
if (found)
|
if (found)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (try_init_devs &&
|
||||||
|
(ROOT_DEV == Root_NFS || ROOT_DEV == Root_CIFS)) {
|
||||||
|
try_init_devs = false;
|
||||||
|
wait_for_init_devices_probe();
|
||||||
|
}
|
||||||
ssleep(1);
|
ssleep(1);
|
||||||
}
|
}
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user