Merge e92b2872d0 ("Merge tag 'rust-fixes-7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ojeda/linux") into android-mainline

Steps on the way to v7.1-rc3

Change-Id: I2d17138de692e4ae859912534b1318bed5735d09
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas authored and android-test-infra-autosubmit@system.gserviceaccount.com committed 2026-06-01 09:13:09 -07:00
commit 383d991c41
77 files changed
+1246 -568

No files matched your search

+1
View File
@@ -682,6 +682,7 @@ Peter A Jonsson <pj@ludd.ltu.se>
Peter Hilber <peter.hilber@oss.qualcomm.com> <quic_philber@quicinc.com>
Peter Oruba <peter.oruba@amd.com>
Peter Oruba <peter@oruba.de>
Peter Rosin <peda@lysator.liu.se> <peda@axentia.se>
Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> <pierre-louis.bossart@linux.intel.com>
Pratyush Anand <pratyush.anand@gmail.com> <pratyush.anand@st.com>
Pratyush Yadav <pratyush@kernel.org> <ptyadav@amazon.de>
@@ -16,10 +16,15 @@ allOf:
properties:
compatible:
enum:
- amlogic,meson6-i2c # Meson6, Meson8 and compatible SoCs
- amlogic,meson-gxbb-i2c # GXBB and compatible SoCs
- amlogic,meson-axg-i2c # AXG and compatible SoCs
oneOf:
- items:
- enum:
- amlogic,t7-i2c
- const: amlogic,meson-axg-i2c
- enum:
- amlogic,meson6-i2c # Meson6, Meson8 and compatible SoCs
- amlogic,meson-gxbb-i2c # GXBB and compatible SoCs
- amlogic,meson-axg-i2c # AXG and compatible SoCs
reg:
maxItems: 1
@@ -22,7 +22,9 @@ properties:
compatible:
oneOf:
- items:
- const: apple,t6020-i2c
- enum:
- apple,t6020-i2c
- apple,t8122-i2c
- const: apple,t8103-i2c
- items:
- enum:
+1 -1
View File
@@ -135,4 +135,4 @@ References
4. **Lenovo IdeaPad Laptop Driver:** Reference for DMI-based hardware
feature gating in Lenovo laptops.
https://github.com/torvalds/linux/blob/master/drivers/platform/x86/ideapad-laptop.c
https://github.com/torvalds/linux/blob/master/drivers/platform/x86/lenovo/ideapad-laptop.c
+93 -1
View File
@@ -24,6 +24,97 @@ Quick access to CPU number, node ID
Allows to implement per CPU data efficiently. Documentation is in code and
selftests. :(
Optimized RSEQ V2
-----------------
On architectures which utilize the generic entry code and generic TIF bits
the kernel supports runtime optimizations for RSEQ, which also enable
enhanced features like scheduler time slice extensions.
To enable them a task has to register the RSEQ region with at least the
length advertised by getauxval(AT_RSEQ_FEATURE_SIZE).
If existing binaries register with RSEQ_ORIG_SIZE (32 bytes), the kernel
keeps the legacy low performance mode enabled to fulfil the expectations
of existing users regarding the original RSEQ implementation behaviour.
The following table documents the ABI and behavioral guarantees of the
legacy and the optimized V2 mode.
.. list-table:: RSEQ modes
:header-rows: 1
* - Nr
- What
- Legacy
- Optimized V2
* - 1
- The cpu_id_start, cpu_id, node_id and mm_cid fields (User mode read
only)
.. Legacy
- Updated by the kernel unconditionally after each context switch and
before signal delivery
.. Optimized V2
- Updated by the kernel if and only if they change, i.e. if the task
is migrated or mm_cid changes
* - 2
- The rseq_cs critical section field
.. Legacy
- Evaluated and handled unconditionally after each context switch and
before signal delivery
.. Optimized V2
- Evaluated and handled conditionally only when user space was
interrupted and was scheduled out or before delivering a signal in
the interrupted context.
* - 3
- Read only fields
.. Legacy
- No strict enforcement except in debug mode
.. Optimized V2
- Strict enforcement
* - 4
- membarrier(...RSEQ)
.. Legacy
- All running threads of the process are interrupted and the ID fields
are rewritten and eventually active critical sections are aborted
before they return to user space. All threads which are scheduled
out whether voluntary or not are covered by #1/#2 above.
.. Optimized V2
- All running threads of the process are interrupted and eventually
active critical sections are aborted before these threads return to
user space. The ID fields are only updated if changed as a
consequence of the interrupt. All threads which are scheduled out
whether voluntary or not are covered by #1/#2 above.
* - 5
- Time slice extensions
.. Legacy
- Not supported
.. Optimized V2
- Supported
The legacy mode is obviously less performant as it does unconditional
updates and critical section checks even if not strictly required by the
ABI contract. That can't be changed anymore as some users depend on that
observed behavior, which in turn enables them to violate the ABI and
overwrite the cpu_id_start field for their own purposes. This is obviously
discouraged as it renders RSEQ incompatible with the intended usage and
breaks the expectation of other libraries in the same application.
The ABI compliant optimized v2 mode, which respects the read only fields,
does not require unconditional updates and therefore is way more
performant. The kernel validates the read only fields for compliance. If
user space modifies them, the process is killed. Compliant usage allows
multiple libraries in the same application to benefit from the RSEQ
functionality without disturbing each other. The ABI compliant optimized v2
mode also enables extended RSEQ features like time slice extensions.
Scheduler time slice extensions
-------------------------------
@@ -37,7 +128,8 @@ The prerequisites for this functionality are:
* Enabled at boot time (default is enabled)
* A rseq userspace pointer has been registered for the thread
* A rseq userspace pointer has been registered for the thread in
optimized V2 mode
The thread has to enable the functionality via prctl(2)::
+11 -13
View File
@@ -4299,18 +4299,16 @@ F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
F: drivers/video/backlight/aw99706.c
AXENTIA ARM DEVICES
M: Peter Rosin <peda@axentia.se>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
S: Orphan
F: arch/arm/boot/dts/microchip/at91-linea.dtsi
F: arch/arm/boot/dts/microchip/at91-natte.dtsi
F: arch/arm/boot/dts/microchip/at91-nattis-2-natte-2.dts
F: arch/arm/boot/dts/microchip/at91-tse850-3.dts
AXENTIA ASOC DRIVERS
M: Peter Rosin <peda@axentia.se>
L: linux-sound@vger.kernel.org
S: Maintained
S: Orphan
F: Documentation/devicetree/bindings/sound/axentia,*
F: sound/soc/atmel/tse850-pcm5142.c
@@ -12051,7 +12049,7 @@ F: Documentation/i2c/busses/i2c-nvidia-gpu.rst
F: drivers/i2c/busses/i2c-nvidia-gpu.c
I2C MUXES
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-i2c@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/i2c/i2c-arb*
@@ -12452,7 +12450,7 @@ F: drivers/iio/industrialio-backend.c
F: include/linux/iio/backend.h
IIO DIGITAL POTENTIOMETER DAC
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
@@ -12460,7 +12458,7 @@ F: Documentation/devicetree/bindings/iio/dac/dpot-dac.yaml
F: drivers/iio/dac/dpot-dac.c
IIO ENVELOPE DETECTOR
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
@@ -12476,7 +12474,7 @@ F: include/linux/iio/iio-gts-helper.h
F: drivers/iio/test/iio-test-gts.c
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.yaml
@@ -12507,7 +12505,7 @@ F: include/linux/iio/
F: tools/iio/
IIO UNIT CONVERTER
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/afe/current-sense-amplifier.yaml
@@ -15730,7 +15728,7 @@ F: Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml
F: drivers/media/i2c/max96717.c
MAX9860 MONO AUDIO VOICE CODEC DRIVER
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/max9860.txt
@@ -15945,7 +15943,7 @@ F: Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
F: drivers/net/can/spi/mcp251xfd/
MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
@@ -18250,7 +18248,7 @@ F: include/linux/mmc/
F: include/uapi/linux/mmc/
MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
S: Odd Fixes
F: Documentation/ABI/testing/sysfs-class-mux*
F: Documentation/devicetree/bindings/mux/
@@ -19359,7 +19357,7 @@ F: include/dt-bindings/display/tda998x.h
K: "nxp,tda998x"
NXP TFA9879 DRIVER
M: Peter Rosin <peda@axentia.se>
M: Peter Rosin <peda@lysator.liu.se>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
+2
View File
@@ -504,6 +504,8 @@ export rust_common_flags := --edition=2021 \
-Wclippy::as_ptr_cast_mut \
-Wclippy::as_underscore \
-Wclippy::cast_lossless \
-Aclippy::collapsible_if \
-Aclippy::collapsible_match \
-Wclippy::ignored_unit_patterns \
-Aclippy::incompatible_msrv \
-Wclippy::mut_mut \
-1
View File
@@ -76,7 +76,6 @@ CONFIG_SERIAL_8250_CONSOLE=y
# CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_TILEBLITTING=y
CONFIG_FB_RADEON=y
CONFIG_FB_3DFX=y
-1
View File
@@ -76,7 +76,6 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_NVRAM=y
# CONFIG_HWMON is not set
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_OF=y
CONFIG_FB_MATROX=y
CONFIG_FB_MATROX_MILLENIUM=y
-1
View File
@@ -121,7 +121,6 @@ CONFIG_I2C_CHARDEV=y
CONFIG_AGP=m
CONFIG_AGP_UNINORTH=m
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_TILEBLITTING=y
CONFIG_FB_OF=y
CONFIG_FB_NVIDIA=y
-1
View File
@@ -98,7 +98,6 @@ CONFIG_SENSORS_LM85=y
CONFIG_SENSORS_LM90=y
CONFIG_DRM=y
CONFIG_DRM_RADEON=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_TILEBLITTING=y
CONFIG_FB_VGA16=y
CONFIG_FB_NVIDIA=y
-1
View File
@@ -196,7 +196,6 @@ CONFIG_I2C_CHARDEV=y
# CONFIG_PTP_1588_CLOCK is not set
CONFIG_DRM=y
CONFIG_DRM_AST=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_OF=y
CONFIG_FB_MATROX=m
CONFIG_FB_MATROX_MILLENIUM=y
-1
View File
@@ -249,7 +249,6 @@ CONFIG_I2C_CHARDEV=y
CONFIG_I2C_AMD8111=y
CONFIG_I2C_PASEMI=y
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_OF=y
CONFIG_FB_MATROX=y
CONFIG_FB_MATROX_MILLENIUM=y
-1
View File
@@ -118,7 +118,6 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_AMD8111=y
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_OF=y
CONFIG_FB_MATROX=y
CONFIG_FB_MATROX_MILLENIUM=y
-1
View File
@@ -214,7 +214,6 @@ CONFIG_SENSORS_IBMPOWERNV=m
CONFIG_DRM=m
CONFIG_DRM_AST=m
CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y
+6
View File
@@ -62,6 +62,12 @@ CC32FLAGSREMOVE += -fno-stack-clash-protection
# 32-bit one. clang validates the values passed to these arguments during
# parsing, even when -fno-stack-protector is passed afterwards.
CC32FLAGSREMOVE += -mstack-protector-guard%
# ftrace is disabled for the vdso but arch/powerpc/Makefile adds this define to
# KBUILD_CPPFLAGS, which enables use of the 'patchable_function_entry'
# attribute in the 'inline' define via 'notrace'. This attribute is not
# supported for the powerpcle target, resulting in many instances of
# -Wunknown-attributes.
CC32FLAGSREMOVE += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
endif
LD32FLAGS := -Wl,-soname=linux-vdso32.so.1
AS32FLAGS := -D__VDSO32__
+1 -1
View File
@@ -16,4 +16,4 @@ GCOV_PROFILE_core_$(BITS).o := n
KCOV_INSTRUMENT_core_$(BITS).o := n
UBSAN_SANITIZE_core_$(BITS).o := n
KASAN_SANITIZE_core.o := n
KASAN_SANITIZE_core_$(BITS) := n
KASAN_SANITIZE_core_$(BITS).o := n
+8 -1
View File
@@ -52,7 +52,14 @@ int exit_vmx_usercopy(void)
}
EXPORT_SYMBOL(exit_vmx_usercopy);
int enter_vmx_ops(void)
/*
* Can be called from kexec copy_page() path with MMU off. The kexec
* code sets preempt_count to HARDIRQ_OFFSET so we return early here.
* Since in_interrupt() is always inline, __no_sanitize_address on this
* function is sufficient to avoid KASAN shadow memory accesses in real
* mode.
*/
int __no_sanitize_address enter_vmx_ops(void)
{
if (in_interrupt())
return 0;
+3 -2
View File
@@ -2242,6 +2242,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
const u64 last_period = event->hw.last_period;
s64 prev, delta, left;
int record = 0;
int mark_event = regs->dsisr & MMCRA_SAMPLE_ENABLE;
if (event->hw.state & PERF_HES_STOPPED) {
write_pmc(event->hw.idx, 0);
@@ -2304,9 +2305,9 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
* In ISA v3.0 and before values "0" and "7" are considered reserved.
* In ISA v3.1, value "7" has been used to indicate "larx/stcx".
* Drop the sample if "type" has reserved values for this field with a
* ISA version check.
* ISA version check for marked events.
*/
if (event->attr.sample_type & PERF_SAMPLE_DATA_SRC &&
if (mark_event && event->attr.sample_type & PERF_SAMPLE_DATA_SRC &&
ppmu->get_mem_data_src) {
val = (regs->dar & SIER_TYPE_MASK) >> SIER_TYPE_SHIFT;
if (val == 0 || (val == 7 && !cpu_has_feature(CPU_FTR_ARCH_31))) {
+2 -2
View File
@@ -477,7 +477,7 @@ int cpm1_gpiochip_add16(struct device *dev)
struct device_node *np = dev->of_node;
struct cpm1_gpio16_chip *cpm1_gc;
struct gpio_chip *gc;
u16 mask;
u32 mask;
cpm1_gc = devm_kzalloc(dev, sizeof(*cpm1_gc), GFP_KERNEL);
if (!cpm1_gc)
@@ -485,7 +485,7 @@ int cpm1_gpiochip_add16(struct device *dev)
spin_lock_init(&cpm1_gc->lock);
if (!of_property_read_u16(np, "fsl,cpm1-gpio-irq-mask", &mask)) {
if (!of_property_read_u32(np, "fsl,cpm1-gpio-irq-mask", &mask)) {
int i, j;
for (i = 0, j = 0; i < 16; i++)
+1 -2
View File
@@ -272,13 +272,12 @@ void __init pas_pci_init(void)
{
struct device_node *root = of_find_node_by_path("/");
struct device_node *np;
int res;
pci_set_flags(PCI_SCAN_ALL_PCIE_DEVS);
np = of_find_compatible_node(root, NULL, "pasemi,rootbus");
if (np) {
res = pas_add_bridge(np);
pas_add_bridge(np);
of_node_put(np);
}
of_node_put(root);
+1 -3
View File
@@ -950,8 +950,6 @@ static int __init ps3_start_probe_thread(enum ps3_bus_type bus_type)
static int __init ps3_register_devices(void)
{
int result;
if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
return -ENODEV;
@@ -959,7 +957,7 @@ static int __init ps3_register_devices(void)
/* ps3_repository_dump_bus_info(); */
result = ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE);
ps3_start_probe_thread(PS3_BUS_TYPE_STORAGE);
ps3_register_vuart_devices();
+116 -17
View File
@@ -16,6 +16,7 @@ static void *htm_buf;
static void *htm_status_buf;
static void *htm_info_buf;
static void *htm_caps_buf;
static void *htm_mem_buf;
static u32 nodeindex;
static u32 nodalchipindex;
static u32 coreindexonchip;
@@ -86,7 +87,7 @@ static ssize_t htm_return_check(long rc)
static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
void *htm_buf = filp->private_data;
void *htm_buf_data = filp->private_data;
unsigned long page, read_size, available;
loff_t offset;
long rc, ret;
@@ -100,7 +101,7 @@ static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
* - last three values are address, size and offset
*/
rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf),
htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf_data),
PAGE_SIZE, page);
ret = htm_return_check(rc);
@@ -112,7 +113,61 @@ static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
available = PAGE_SIZE;
read_size = min(count, available);
*ppos += read_size;
return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available);
return simple_read_from_buffer(ubuf, count, &offset, htm_buf_data, available);
}
static ssize_t htmsystem_mem_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
void *htm_mem_data = filp->private_data;
long rc, ret;
u64 *num_entries;
u64 to_copy = 0;
loff_t offset = 0;
u64 mem_offset = 0;
/*
* Invoke H_HTM call with:
* - operation as htm status (H_HTM_OP_STATUS)
* - last three values as addr, size and offset. "offset"
* is value from output buffer header that points to next
* entry to dump. 0 is the first entry to dump. next entry
* is read from the output bufferbyte offset 0x8.
*
* When first time hcall is invoked, mem_offset should be
* zero because zero is the first entry.
* In the next hcall, offset of next entry to read from is
* picked from output buffer header itself. So don't fill
* mem_offset for first read.
*
* If there is no further data to read in next iteration,
* offset value from output buffer header will point to -1.
*/
if (*ppos) {
mem_offset = *(u64 *)(htm_mem_data + 0x8);
if (mem_offset == -1)
return 0;
}
rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
htmtype, H_HTM_OP_DUMP_SYSMEM_CONF, virt_to_phys(htm_mem_data),
PAGE_SIZE, be64_to_cpu(mem_offset));
ret = htm_return_check(rc);
if (ret <= 0) {
pr_debug("H_HTM hcall returned for op: H_HTM_OP_DUMP_SYSMEM_CONF with hcall returning %ld\n", ret);
return ret;
}
/*
* HTM system mem buffer, start of buffer + 0x10 gives the
* number of HTM entries in the buffer.
* So total count to copy is:
* 32 bytes (for first 5 fields) + (number of HTM entries * entry size)
*/
num_entries = htm_mem_data + 0x10;
to_copy = 32 + (be64_to_cpu(*num_entries) * 32);
*ppos += to_copy;
return simple_read_from_buffer(ubuf, count, &offset, htm_mem_data, to_copy);
}
static const struct file_operations htmdump_fops = {
@@ -121,6 +176,12 @@ static const struct file_operations htmdump_fops = {
.open = simple_open,
};
static const struct file_operations htmsystem_mem_fops = {
.llseek = NULL,
.read = htmsystem_mem_read,
.open = simple_open,
};
static int htmconfigure_set(void *data, u64 val)
{
long rc, ret;
@@ -226,20 +287,31 @@ static int htmstart_get(void *data, u64 *val)
static ssize_t htmstatus_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
void *htm_status_buf = filp->private_data;
void *htm_status_data = filp->private_data;
long rc, ret;
u64 *num_entries;
u64 to_copy;
int htmstatus_flag;
loff_t offset = 0;
u64 status_offset = 0;
/*
* Invoke H_HTM call with:
* - operation as htm status (H_HTM_OP_STATUS)
* - last three values as addr, size and offset
* - last three values as addr, size and offset.
* "offset" is value from output buffer header
* that points to next entry to dump. 0 is the first
* entry to dump. next entry is read from the output
* bufferbyte offset 0x8.
*/
if (*ppos) {
status_offset = *(u64 *)(htm_status_data + 0x8);
if (status_offset == -1)
return 0;
}
rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_buf),
PAGE_SIZE, 0);
htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_data),
PAGE_SIZE, be64_to_cpu(status_offset));
ret = htm_return_check(rc);
if (ret <= 0) {
@@ -255,13 +327,15 @@ static ssize_t htmstatus_read(struct file *filp, char __user *ubuf,
* So total count to copy is:
* 32 bytes (for first 7 fields) + (number of HTM entries * entry size)
*/
num_entries = htm_status_buf + 0x10;
num_entries = htm_status_data + 0x10;
if (htmtype == 0x2)
htmstatus_flag = 0x8;
else
htmstatus_flag = 0x6;
to_copy = 32 + (be64_to_cpu(*num_entries) * htmstatus_flag);
return simple_read_from_buffer(ubuf, count, ppos, htm_status_buf, to_copy);
*ppos += to_copy;
return simple_read_from_buffer(ubuf, count, &offset, htm_status_data, to_copy);
}
static const struct file_operations htmstatus_fops = {
@@ -273,19 +347,30 @@ static const struct file_operations htmstatus_fops = {
static ssize_t htminfo_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
void *htm_info_buf = filp->private_data;
void *htm_info_data = filp->private_data;
long rc, ret;
u64 *num_entries;
u64 to_copy;
loff_t offset = 0;
u64 info_offset = 0;
/*
* Invoke H_HTM call with:
* - operation as htm status (H_HTM_OP_STATUS)
* - last three values as addr, size and offset
* "offset" is value from output buffer header
* that points to next entry to dump. 0 is the first
* entry to dump. next entry is read from the output
* bufferbyte offset 0x8.
*/
if (*ppos) {
info_offset = *(u64 *)(htm_info_data + 0x8);
if (info_offset == -1)
return 0;
}
rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_buf),
PAGE_SIZE, 0);
htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_data),
PAGE_SIZE, be64_to_cpu(info_offset));
ret = htm_return_check(rc);
if (ret <= 0) {
@@ -301,15 +386,17 @@ static ssize_t htminfo_read(struct file *filp, char __user *ubuf,
* So total count to copy is:
* 32 bytes (for first 5 fields) + (number of HTM entries * entry size)
*/
num_entries = htm_info_buf + 0x10;
num_entries = htm_info_data + 0x10;
to_copy = 32 + (be64_to_cpu(*num_entries) * 16);
return simple_read_from_buffer(ubuf, count, ppos, htm_info_buf, to_copy);
*ppos += to_copy;
return simple_read_from_buffer(ubuf, count, &offset, htm_info_data, to_copy);
}
static ssize_t htmcaps_read(struct file *filp, char __user *ubuf,
size_t count, loff_t *ppos)
{
void *htm_caps_buf = filp->private_data;
void *htm_caps_data = filp->private_data;
long rc, ret;
/*
@@ -319,7 +406,7 @@ static ssize_t htmcaps_read(struct file *filp, char __user *ubuf,
* and zero
*/
rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_buf),
htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_data),
0x80, 0);
ret = htm_return_check(rc);
@@ -328,7 +415,7 @@ static ssize_t htmcaps_read(struct file *filp, char __user *ubuf,
return ret;
}
return simple_read_from_buffer(ubuf, count, ppos, htm_caps_buf, 0x80);
return simple_read_from_buffer(ubuf, count, ppos, htm_caps_data, 0x80);
}
static const struct file_operations htminfo_fops = {
@@ -457,9 +544,17 @@ static int htmdump_init_debugfs(void)
return -ENOMEM;
}
/* Memory to present HTM system memory configuration */
htm_mem_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!htm_mem_buf) {
pr_err("Failed to allocate htm mem buf\n");
return -ENOMEM;
}
debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops);
debugfs_create_file("htminfo", 0400, htmdump_debugfs_dir, htm_info_buf, &htminfo_fops);
debugfs_create_file("htmcaps", 0400, htmdump_debugfs_dir, htm_caps_buf, &htmcaps_fops);
debugfs_create_file("htmsystem_mem", 0400, htmdump_debugfs_dir, htm_mem_buf, &htmsystem_mem_fops);
return 0;
}
@@ -482,6 +577,10 @@ static void __exit htmdump_exit(void)
{
debugfs_remove_recursive(htmdump_debugfs_dir);
kfree(htm_buf);
kfree(htm_status_buf);
kfree(htm_info_buf);
kfree(htm_caps_buf);
kfree(htm_mem_buf);
}
module_init(htmdump_init);
+97 -84
View File
@@ -190,33 +190,34 @@ static int hvpipe_rtas_recv_msg(char __user *buf, int size)
return -ENOMEM;
}
ret = rtas_ibm_receive_hvpipe_msg(work_area, &srcID,
&bytes_written);
if (!ret) {
/*
* Recv HVPIPE RTAS is successful.
* When releasing FD or no one is waiting on the
* specific source, issue recv HVPIPE RTAS call
* so that pipe is not blocked - this func is called
* with NULL buf.
*/
if (buf) {
if (size < bytes_written) {
pr_err("Received the payload size = %d, but the buffer size = %d\n",
bytes_written, size);
bytes_written = size;
}
ret = copy_to_user(buf,
rtas_work_area_raw_buf(work_area),
bytes_written);
if (!ret)
ret = bytes_written;
}
} else {
pr_err("ibm,receive-hvpipe-msg failed with %d\n",
ret);
/*
* Recv HVPIPE RTAS is successful.
* When releasing FD or no one is waiting on the
* specific source, issue recv HVPIPE RTAS call
* so that pipe is not blocked - this func is called
* with NULL buf.
*/
ret = rtas_ibm_receive_hvpipe_msg(work_area, &srcID, &bytes_written);
if (ret) {
pr_err("ibm,receive-hvpipe-msg failed with %d\n", ret);
goto out;
}
if (!buf)
goto out;
if (size < bytes_written) {
pr_err("Received the payload size = %d, but the buffer size = %d\n",
bytes_written, size);
bytes_written = size;
}
if (copy_to_user(buf, rtas_work_area_raw_buf(work_area), bytes_written))
ret = -EFAULT;
else
ret = bytes_written;
out:
rtas_work_area_free(work_area);
return ret;
}
@@ -327,8 +328,8 @@ static ssize_t papr_hvpipe_handle_read(struct file *file,
{
struct hvpipe_source_info *src_info = file->private_data;
struct papr_hvpipe_hdr hdr;
long ret;
struct papr_hvpipe_hdr hdr = {};
ssize_t ret = 0;
/*
* Return -ENXIO during migration
@@ -376,7 +377,7 @@ static ssize_t papr_hvpipe_handle_read(struct file *file,
ret = copy_to_user(buf, &hdr, HVPIPE_HDR_LEN);
if (ret)
return ret;
return -EFAULT;
/*
* Message event has payload, so get the payload with
@@ -385,19 +386,23 @@ static ssize_t papr_hvpipe_handle_read(struct file *file,
if (hdr.flags & HVPIPE_MSG_AVAILABLE) {
ret = hvpipe_rtas_recv_msg(buf + HVPIPE_HDR_LEN,
size - HVPIPE_HDR_LEN);
if (ret > 0) {
/*
* Always clear MSG_AVAILABLE once the RTAS call has drained
* the message, regardless of whether copy_to_user succeeded.
*/
if (ret >= 0 || ret == -EFAULT)
src_info->hvpipe_status &= ~HVPIPE_MSG_AVAILABLE;
ret += HVPIPE_HDR_LEN;
}
} else if (hdr.flags & HVPIPE_LOST_CONNECTION) {
/*
* Hypervisor is closing the pipe for the specific
* source. So notify user space.
*/
src_info->hvpipe_status &= ~HVPIPE_LOST_CONNECTION;
ret = HVPIPE_HDR_LEN;
}
if (ret >= 0)
ret += HVPIPE_HDR_LEN;
return ret;
}
@@ -444,16 +449,18 @@ static int papr_hvpipe_handle_release(struct inode *inode,
struct file *file)
{
struct hvpipe_source_info *src_info;
unsigned long flags;
/*
* Hold the lock, remove source from src_list, reset the
* hvpipe status and release the lock to prevent any race
* with message event IRQ.
*/
spin_lock(&hvpipe_src_list_lock);
spin_lock_irqsave(&hvpipe_src_list_lock, flags);
src_info = file->private_data;
list_del(&src_info->list);
file->private_data = NULL;
spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
/*
* If the pipe for this specific source has any pending
* payload, issue recv HVPIPE RTAS so that pipe will not
@@ -461,10 +468,8 @@ static int papr_hvpipe_handle_release(struct inode *inode,
*/
if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE) {
src_info->hvpipe_status = 0;
spin_unlock(&hvpipe_src_list_lock);
hvpipe_rtas_recv_msg(NULL, 0);
} else
spin_unlock(&hvpipe_src_list_lock);
}
kfree(src_info);
return 0;
@@ -479,50 +484,53 @@ static const struct file_operations papr_hvpipe_handle_ops = {
static int papr_hvpipe_dev_create_handle(u32 srcID)
{
struct hvpipe_source_info *src_info __free(kfree) = NULL;
spin_lock(&hvpipe_src_list_lock);
/*
* Do not allow more than one process communicates with
* each source.
*/
src_info = hvpipe_find_source(srcID);
if (src_info) {
spin_unlock(&hvpipe_src_list_lock);
pr_err("pid(%d) is already using the source(%d)\n",
src_info->tsk->pid, srcID);
return -EALREADY;
}
spin_unlock(&hvpipe_src_list_lock);
struct hvpipe_source_info *src_info;
int fd;
unsigned long flags;
src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
if (!src_info)
return -ENOMEM;
src_info->srcID = srcID;
src_info->tsk = current;
init_waitqueue_head(&src_info->recv_wqh);
FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
(void *)src_info, O_RDWR));
if (fdf.err)
return fdf.err;
retain_and_null_ptr(src_info);
spin_lock(&hvpipe_src_list_lock);
/*
* If two processes are executing ioctl() for the same
* source ID concurrently, prevent the second process to
* acquire FD.
* Do not allow more than one process communicates with
* each source.
*/
spin_lock_irqsave(&hvpipe_src_list_lock, flags);
if (hvpipe_find_source(srcID)) {
spin_unlock(&hvpipe_src_list_lock);
spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
pr_err("pid(%s:%d) could not get the source(%d)\n",
current->comm, task_pid_nr(current), srcID);
kfree(src_info);
return -EALREADY;
}
list_add(&src_info->list, &hvpipe_src_list);
spin_unlock(&hvpipe_src_list_lock);
return fd_publish(fdf);
spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
fd = FD_ADD(O_RDONLY | O_CLOEXEC,
anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
(void *)src_info, O_RDWR));
if (fd < 0) {
spin_lock_irqsave(&hvpipe_src_list_lock, flags);
list_del(&src_info->list);
spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
/*
* if we fail to add FD, that means no userspace program is
* polling. In that case if there is a msg pending because the
* interrupt was fired after the src_info was added to the
* global list, then let's consume it here, to unblock the
* hvpipe
*/
if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
hvpipe_rtas_recv_msg(NULL, 0);
kfree(src_info);
return fd;
}
return fd;
}
/*
@@ -685,20 +693,19 @@ static int __init enable_hvpipe_IRQ(void)
struct device_node *np;
hvpipe_check_exception_token = rtas_function_token(RTAS_FN_CHECK_EXCEPTION);
if (hvpipe_check_exception_token == RTAS_UNKNOWN_SERVICE)
if (hvpipe_check_exception_token == RTAS_UNKNOWN_SERVICE)
return -ENODEV;
/* hvpipe events */
np = of_find_node_by_path("/event-sources/ibm,hvpipe-msg-events");
if (np != NULL) {
request_event_sources_irqs(np, hvpipe_event_interrupt,
"HPIPE_EVENT");
of_node_put(np);
} else {
pr_err("Can not enable hvpipe event IRQ\n");
if (!np) {
pr_err("No device node found, could not enable hvpipe event IRQ\n");
return -ENODEV;
}
request_event_sources_irqs(np, hvpipe_event_interrupt, "HPIPE_EVENT");
of_node_put(np);
return 0;
}
@@ -775,23 +782,29 @@ static int __init papr_hvpipe_init(void)
}
ret = enable_hvpipe_IRQ();
if (!ret) {
ret = set_hvpipe_sys_param(1);
if (!ret)
ret = misc_register(&papr_hvpipe_dev);
}
if (ret)
goto out_wq;
if (!ret) {
pr_info("hvpipe feature is enabled\n");
hvpipe_feature = true;
return 0;
}
ret = misc_register(&papr_hvpipe_dev);
if (ret)
goto out_wq;
pr_err("hvpipe feature is not enabled %d\n", ret);
ret = set_hvpipe_sys_param(1);
if (ret)
goto out_misc;
pr_info("hvpipe feature is enabled\n");
hvpipe_feature = true;
return 0;
out_misc:
misc_deregister(&papr_hvpipe_dev);
out_wq:
destroy_workqueue(papr_hvpipe_wq);
out:
kfree(papr_hvpipe_work);
papr_hvpipe_work = NULL;
pr_err("hvpipe feature is not enabled %d\n", ret);
return ret;
}
machine_device_initcall(pseries, papr_hvpipe_init);
@@ -21,7 +21,6 @@ struct hvpipe_source_info {
u32 srcID;
u32 hvpipe_status;
wait_queue_head_t recv_wqh; /* wake up poll() waitq */
struct task_struct *tsk;
};
/*
+8 -5
View File
@@ -1294,13 +1294,16 @@ int x86_perf_rdpmc_index(struct perf_event *event)
return event->hw.event_base_rdpmc;
}
static inline int match_prev_assignment(struct hw_perf_event *hwc,
static inline int match_prev_assignment(struct perf_event *event,
struct cpu_hw_events *cpuc,
int i)
{
struct hw_perf_event *hwc = &event->hw;
return hwc->idx == cpuc->assign[i] &&
hwc->last_cpu == smp_processor_id() &&
hwc->last_tag == cpuc->tags[i];
hwc->last_cpu == smp_processor_id() &&
hwc->last_tag == cpuc->tags[i] &&
!is_acr_event_group(event);
}
static void x86_pmu_start(struct perf_event *event, int flags);
@@ -1346,7 +1349,7 @@ static void x86_pmu_enable(struct pmu *pmu)
* - no other event has used the counter since
*/
if (hwc->idx == -1 ||
match_prev_assignment(hwc, cpuc, i))
match_prev_assignment(event, cpuc, i))
continue;
/*
@@ -1367,7 +1370,7 @@ static void x86_pmu_enable(struct pmu *pmu)
event = cpuc->event_list[i];
hwc = &event->hw;
if (!match_prev_assignment(hwc, cpuc, i))
if (!match_prev_assignment(event, cpuc, i))
x86_assign_hw_event(event, cpuc, i);
else if (i < n_running)
continue;
+39 -11
View File
@@ -3118,11 +3118,11 @@ static void intel_pmu_enable_fixed(struct perf_event *event)
intel_set_masks(event, idx);
/*
* Enable IRQ generation (0x8), if not PEBS,
* and enable ring-3 counting (0x2) and ring-0 counting (0x1)
* if requested:
* Enable IRQ generation (0x8), if not PEBS or self-reloaded
* ACR event, and enable ring-3 counting (0x2) and ring-0
* counting (0x1) if requested:
*/
if (!event->attr.precise_ip)
if (!event->attr.precise_ip && !is_acr_self_reload_event(event))
bits |= INTEL_FIXED_0_ENABLE_PMI;
if (hwc->config & ARCH_PERFMON_EVENTSEL_USR)
bits |= INTEL_FIXED_0_USER;
@@ -3306,6 +3306,15 @@ static void intel_pmu_enable_event(struct perf_event *event)
intel_set_masks(event, idx);
static_call_cond(intel_pmu_enable_acr_event)(event);
static_call_cond(intel_pmu_enable_event_ext)(event);
/*
* For self-reloaded ACR event, don't enable PMI since
* HW won't set overflow bit in GLOBAL_STATUS. Otherwise,
* the PMI would be recognized as a suspicious NMI.
*/
if (is_acr_self_reload_event(event))
hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT;
else if (!event->attr.precise_ip)
hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
__x86_pmu_enable_event(hwc, enable_mask);
break;
case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1:
@@ -3332,23 +3341,41 @@ static void intel_pmu_enable_event(struct perf_event *event)
static void intel_pmu_acr_late_setup(struct cpu_hw_events *cpuc)
{
struct perf_event *event, *leader;
int i, j, idx;
int i, j, k, bit, idx;
/*
* FIXME: ACR mask parsing relies on cpuc->event_list[] (active events only).
* Disabling an ACR event causes bit-shifting errors in the acr_mask of
* remaining group members. As ACR sampling requires all events to be active,
* this limitation is acceptable for now. Revisit if independent event toggling
* is required.
*/
for (i = 0; i < cpuc->n_events; i++) {
leader = cpuc->event_list[i];
if (!is_acr_event_group(leader))
continue;
/* The ACR events must be contiguous. */
/* Find the last event of the ACR group. */
for (j = i; j < cpuc->n_events; j++) {
event = cpuc->event_list[j];
if (event->group_leader != leader->group_leader)
break;
for_each_set_bit(idx, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) {
if (i + idx >= cpuc->n_events ||
!is_acr_event_group(cpuc->event_list[i + idx]))
return;
__set_bit(cpuc->assign[i + idx], (unsigned long *)&event->hw.config1);
}
/*
* Translate the user-space ACR mask (attr.config2) into the physical
* counter bitmask (hw.config1) for each ACR event in the group.
* NOTE: ACR event contiguity is guaranteed by intel_pmu_hw_config().
*/
for (k = i; k < j; k++) {
event = cpuc->event_list[k];
event->hw.config1 = 0;
for_each_set_bit(bit, (unsigned long *)&event->attr.config2, X86_PMC_IDX_MAX) {
idx = i + bit;
/* Event index of ACR group must locate in [i, j). */
if (idx >= j || !is_acr_event_group(cpuc->event_list[idx]))
continue;
__set_bit(cpuc->assign[idx], (unsigned long *)&event->hw.config1);
}
}
i = j - 1;
@@ -7504,6 +7531,7 @@ static __always_inline void intel_pmu_init_pnc(struct pmu *pmu)
hybrid(pmu, event_constraints) = intel_pnc_event_constraints;
hybrid(pmu, pebs_constraints) = intel_pnc_pebs_event_constraints;
hybrid(pmu, extra_regs) = intel_pnc_extra_regs;
static_call_update(intel_pmu_enable_acr_event, intel_pmu_enable_acr);
}
static __always_inline void intel_pmu_init_skt(struct pmu *pmu)
+10
View File
@@ -137,6 +137,16 @@ static inline bool is_acr_event_group(struct perf_event *event)
return check_leader_group(event->group_leader, PERF_X86_EVENT_ACR);
}
static inline bool is_acr_self_reload_event(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
if (hwc->idx < 0)
return false;
return test_bit(hwc->idx, (unsigned long *)&hwc->config1);
}
struct amd_nb {
int nb_id; /* NorthBridge id */
int refcnt; /* reference count */
+5 -1
View File
@@ -450,6 +450,10 @@ __init static int append_e820_table(struct boot_e820_entry *entries, u32 nr_entr
{
struct boot_e820_entry *entry = entries;
/* If there aren't any entries, we'll want to fall back to another source: */
if (!nr_entries)
return -ENOENT;
while (nr_entries) {
u64 start = entry->addr;
u64 size = entry->size;
@@ -458,7 +462,7 @@ __init static int append_e820_table(struct boot_e820_entry *entries, u32 nr_entr
/* Ignore the remaining entries on 64-bit overflow: */
if (start > end && likely(size))
return -1;
return -EINVAL;
e820__range_add(start, size, type);
+9 -4
View File
@@ -695,17 +695,22 @@ static void __init xen_e820_resolve_conflicts(phys_addr_t start,
return;
end = start + size;
entry = xen_e820_table.entries;
mapcnt = 0;
for (mapcnt = 0; mapcnt < xen_e820_table.nr_entries; mapcnt++) {
while (mapcnt < xen_e820_table.nr_entries) {
entry = xen_e820_table.entries + mapcnt;
if (entry->addr >= end)
return;
if (entry->addr + entry->size > start &&
entry->type == E820_TYPE_NVS)
entry->type == E820_TYPE_NVS) {
xen_e820_swap_entry_with_ram(entry);
/* E820 map has been changed, restart loop! */
mapcnt = 0;
continue;
}
entry++;
mapcnt++;
}
}
@@ -204,7 +204,6 @@ impl<T> ArrayRangeAllocator<T> {
// caller will mark them as unused, which means that they can be freed if the system comes
// under memory pressure.
let mut freed_range = FreedRange::interior_pages(offset, size);
#[expect(clippy::collapsible_if)] // reads better like this
if offset % PAGE_SIZE != 0 {
if i == 0 || self.ranges[i - 1].endpoint() <= (offset & PAGE_MASK) {
freed_range.start_page_idx -= 1;
-1
View File
@@ -201,7 +201,6 @@ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o
obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o
obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
+5 -1
View File
@@ -77,9 +77,13 @@ static int ads7871_read_reg8(struct spi_device *spi, int reg)
static int ads7871_read_reg16(struct spi_device *spi, int reg)
{
int ret;
reg = reg | INST_READ_BM | INST_16BIT_BM;
ret = spi_w8r16(spi, reg);
return ret;
if (ret < 0)
return ret;
return le16_to_cpu((__force __le16)ret);
}
static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
+2 -2
View File
@@ -796,13 +796,13 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id
ret = corsairpsu_init(priv);
if (ret < 0) {
dev_err(&hdev->dev, "unable to initialize device (%d)\n", ret);
goto fail_and_stop;
goto fail_and_close;
}
ret = corsairpsu_fwinfo(priv);
if (ret < 0) {
dev_err(&hdev->dev, "unable to query firmware (%d)\n", ret);
goto fail_and_stop;
goto fail_and_close;
}
corsairpsu_get_criticals(priv);
+30 -9
View File
@@ -333,7 +333,13 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index]));
int fan;
mutex_lock(&data->update_lock);
fan = FAN_FROM_REG(data->fan[attr->index]);
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", fan);
}
static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
@@ -366,12 +372,14 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
int nr = attr->index;
int pwm;
mutex_lock(&data->update_lock);
if (data->pwm_highres)
pwm = data->pwm1[nr];
else
pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
(2 * data->pwm1_freq);
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", pwm);
}
@@ -529,6 +537,7 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
int nr = attr->index;
int temp;
mutex_lock(&data->update_lock);
if (!nr) {
/*
* Use unsigned temperature unless its value is zero.
@@ -544,7 +553,10 @@ static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
else
temp = TEMP11_FROM_REG(data->temp11[nr]);
}
return sprintf(buf, "%d\n", temp + data->temp2_offset);
temp += data->temp2_offset;
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", temp);
}
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
@@ -592,9 +604,14 @@ static ssize_t temp2_crit_hyst_show(struct device *dev,
struct device_attribute *dummy, char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", temp8_from_reg(data, 2)
+ data->temp2_offset
- TEMP8_FROM_REG(data->temp2_crit_hyst));
int temp;
mutex_lock(&data->update_lock);
temp = temp8_from_reg(data, 2) + data->temp2_offset
- TEMP8_FROM_REG(data->temp2_crit_hyst);
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", temp);
}
static ssize_t show_lut_temp_hyst(struct device *dev,
@@ -602,10 +619,14 @@ static ssize_t show_lut_temp_hyst(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm63_data *data = lm63_update_device(dev);
int temp;
return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index)
+ data->temp2_offset
- TEMP8_FROM_REG(data->lut_temp_hyst));
mutex_lock(&data->update_lock);
temp = lut_temp_from_reg(data, attr->index) + data->temp2_offset
- TEMP8_FROM_REG(data->lut_temp_hyst);
mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", temp);
}
/*
@@ -616,7 +637,7 @@ static ssize_t temp2_crit_hyst_store(struct device *dev,
struct device_attribute *dummy,
const char *buf, size_t count)
{
struct lm63_data *data = dev_get_drvdata(dev);
struct lm63_data *data = lm63_update_device(dev);
struct i2c_client *client = data->client;
long val;
int err;
+5 -5
View File
@@ -137,7 +137,7 @@ static const struct lm75_params device_params[] = {
},
[as6200] = {
.config_reg_16bits = true,
.set_mask = 0x94C0, /* 8 sample/s, 4 CF, positive polarity */
.set_mask = 0xC010, /* 8 sample/s, 4 CF */
.default_resolution = 12,
.default_sample_time = 125,
.num_sample_times = 4,
@@ -286,8 +286,8 @@ static const struct lm75_params device_params[] = {
},
[tmp112] = {
.config_reg_16bits = true,
.set_mask = 0x60C0, /* 12-bit mode, 8 samples / second */
.clr_mask = 1 << 15, /* no one-shot mode*/
.set_mask = 0xC060, /* 12-bit mode, 8 samples / second */
.clr_mask = 1 << 7, /* no one-shot mode*/
.default_resolution = 12,
.default_sample_time = 125,
.num_sample_times = 4,
@@ -353,7 +353,7 @@ static inline int lm75_write_config(struct lm75_data *data, u16 set_mask,
u16 clr_mask)
{
return regmap_update_bits(data->regmap, LM75_REG_CONF,
clr_mask | LM75_SHUTDOWN, set_mask);
clr_mask | set_mask | LM75_SHUTDOWN, set_mask);
}
static irqreturn_t lm75_alarm_handler(int irq, void *private)
@@ -416,7 +416,7 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
switch (data->kind) {
case as6200:
case tmp112:
*val = (regval >> 13) & 0x1;
*val = !!(regval & BIT(13)) == !!(regval & BIT(2));
break;
default:
return -EINVAL;
+32 -9
View File
@@ -431,10 +431,16 @@ static int ltc2992_get_voltage(struct ltc2992_state *st, u32 reg, u32 scale, lon
static int ltc2992_set_voltage(struct ltc2992_state *st, u32 reg, u32 scale, long val)
{
val = DIV_ROUND_CLOSEST(val * 1000, scale);
val = val << 4;
u32 reg_val;
long vmax;
return ltc2992_write_reg(st, reg, 2, val);
vmax = DIV_ROUND_CLOSEST_ULL(0xFFFULL * scale, 1000);
val = max(val, 0L);
val = min(val, vmax);
reg_val = min(DIV_ROUND_CLOSEST_ULL((u64)val * 1000, scale),
0xFFFULL) << 4;
return ltc2992_write_reg(st, reg, 2, reg_val);
}
static int ltc2992_read_gpio_alarm(struct ltc2992_state *st, int nr_gpio, u32 attr, long *val)
@@ -559,9 +565,15 @@ static int ltc2992_get_current(struct ltc2992_state *st, u32 reg, u32 channel, l
static int ltc2992_set_current(struct ltc2992_state *st, u32 reg, u32 channel, long val)
{
u32 reg_val;
long cmax;
reg_val = DIV_ROUND_CLOSEST(val * st->r_sense_uohm[channel], LTC2992_IADC_NANOV_LSB);
reg_val = reg_val << 4;
cmax = DIV_ROUND_CLOSEST_ULL(0xFFFULL * LTC2992_IADC_NANOV_LSB,
st->r_sense_uohm[channel]);
val = max(val, 0L);
val = min(val, cmax);
reg_val = min(DIV_ROUND_CLOSEST_ULL((u64)val * st->r_sense_uohm[channel],
LTC2992_IADC_NANOV_LSB),
0xFFFULL) << 4;
return ltc2992_write_reg(st, reg, 2, reg_val);
}
@@ -625,8 +637,10 @@ static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, lon
if (reg_val < 0)
return reg_val;
*val = mul_u64_u32_div(reg_val, LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB,
st->r_sense_uohm[channel] * 1000);
*val = mul_u64_u32_div(reg_val,
LTC2992_VADC_UV_LSB / 1000 *
LTC2992_IADC_NANOV_LSB,
st->r_sense_uohm[channel]);
return 0;
}
@@ -634,9 +648,18 @@ static int ltc2992_get_power(struct ltc2992_state *st, u32 reg, u32 channel, lon
static int ltc2992_set_power(struct ltc2992_state *st, u32 reg, u32 channel, long val)
{
u32 reg_val;
u64 pmax, uval;
reg_val = mul_u64_u32_div(val, st->r_sense_uohm[channel] * 1000,
LTC2992_VADC_UV_LSB * LTC2992_IADC_NANOV_LSB);
uval = max(val, 0L);
pmax = mul_u64_u32_div(0xFFFFFFULL,
LTC2992_VADC_UV_LSB / 1000 *
LTC2992_IADC_NANOV_LSB,
st->r_sense_uohm[channel]);
uval = min(uval, pmax);
reg_val = min(mul_u64_u32_div(uval, st->r_sense_uohm[channel],
LTC2992_VADC_UV_LSB / 1000 *
LTC2992_IADC_NANOV_LSB),
0xFFFFFFULL);
return ltc2992_write_reg(st, reg, 3, reg_val);
}
+3 -3
View File
@@ -895,8 +895,6 @@ static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
f7_msg->result = 0;
f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1);
reinit_completion(&i2c_dev->complete);
cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
cr2 = readl_relaxed(base + STM32F7_I2C_CR2);
@@ -1728,6 +1726,8 @@ static int stm32f7_i2c_xfer_core(struct i2c_adapter *i2c_adap,
if (ret)
goto pm_free;
reinit_completion(&i2c_dev->complete);
stm32f7_i2c_xfer_msg(i2c_dev, msgs);
if (!i2c_dev->atomic)
@@ -2253,7 +2253,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)",
&res->start);
adap->owner = THIS_MODULE;
adap->timeout = 2 * HZ;
adap->timeout = 8 * HZ;
adap->retries = 3;
adap->algo = &stm32f7_i2c_algo;
adap->dev.parent = &pdev->dev;
+1
View File
@@ -371,6 +371,7 @@ static const struct acpi_device_id i2c_acpi_force_100khz_device_ids[] = {
* a 400KHz frequency. The root cause of the issue is not known.
*/
{ "DLL0945", 0 },
{ "ELAN0678", 0 },
{ "ELAN06FA", 0 },
{}
};
+1 -2
View File
@@ -445,8 +445,7 @@ static int i2c_init_recovery(struct i2c_adapter *adap)
bri->set_scl = set_scl_gpio_value;
if (bri->sda_gpiod) {
bri->get_sda = get_sda_gpio_value;
/* FIXME: add proper flag instead of '0' once available */
if (gpiod_get_direction(bri->sda_gpiod) == 0)
if (gpiod_get_direction(bri->sda_gpiod) == GPIO_LINE_DIRECTION_OUT)
bri->set_sda = set_sda_gpio_value;
}
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
+12
View File
@@ -566,6 +566,18 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
if (res)
return res;
/* Reject invalid caller-supplied block lengths before any
* tracepoint or native smbus_xfer callback runs.
*/
if (data &&
(protocol == I2C_SMBUS_I2C_BLOCK_DATA ||
protocol == I2C_SMBUS_BLOCK_PROC_CALL ||
(protocol == I2C_SMBUS_BLOCK_DATA &&
read_write == I2C_SMBUS_WRITE)) &&
(data->block[0] == 0 ||
data->block[0] > I2C_SMBUS_BLOCK_MAX))
return -EINVAL;
/* If enabled, the following two tracepoints are conditional on
* read_write and protocol.
*/
+5 -4
View File
@@ -487,12 +487,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
client->adapter->retries = arg;
break;
case I2C_TIMEOUT:
if (arg > INT_MAX)
/*
* For historical reasons, user-space sets the timeout value in
* units of 10 ms.
*/
if (arg > INT_MAX / 10)
return -EINVAL;
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
+2 -2
View File
@@ -15,7 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
#include <linux/workqueue.h>
#define TU_VERSION_MAX_LENGTH 128
@@ -124,7 +124,7 @@ static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
case I2C_SLAVE_STOP:
if (tu->reg_idx == TU_NUM_REGS) {
set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
queue_delayed_work(system_long_wq, &tu->worker,
queue_delayed_work(system_dfl_long_wq, &tu->worker,
msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
}
+5
View File
@@ -214,6 +214,11 @@ static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
* We ignore banks here, because banked chips don't use I2C
* block transfers
*/
if (data->block[0] == 0 ||
data->block[0] > I2C_SMBUS_BLOCK_MAX) {
ret = -EINVAL;
break;
}
if (data->block[0] > 256 - command) /* Avoid overrun */
data->block[0] = 256 - command;
len = data->block[0];
@@ -194,7 +194,8 @@ struct rtw_cbuf *rtw_cbuf_alloc(u32 size)
struct rtw_cbuf *cbuf;
cbuf = kzalloc_flex(*cbuf, bufs, size);
cbuf->size = size;
if (cbuf)
cbuf->size = size;
return cbuf;
}
+2
View File
@@ -1230,6 +1230,8 @@ err_master:
err_driver:
kfree(fake_bridge);
err_struct:
root_device_unregister(vme_root);
return retval;
}
+2 -1
View File
@@ -1178,7 +1178,7 @@ static int usblp_probe(struct usb_interface *intf,
}
/* Allocate buffer for printer status */
usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL);
usblp->statusbuf = kzalloc(STATUS_BUF_SIZE, GFP_KERNEL);
if (!usblp->statusbuf) {
retval = -ENOMEM;
goto abort;
@@ -1377,6 +1377,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp)
{
int err, length;
memset(usblp->device_id_string, 0, USBLP_DEVICE_ID_SIZE);
err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1);
if (err < 0) {
dev_dbg(&usblp->intf->dev,
+4 -1
View File
@@ -286,12 +286,15 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
ret = ulpi_of_register(ulpi);
if (ret)
if (ret) {
kfree(ulpi);
return ret;
}
ret = ulpi_read_id(ulpi);
if (ret) {
of_node_put(ulpi->dev.of_node);
kfree(ulpi);
return ret;
}
+6 -6
View File
@@ -1359,12 +1359,6 @@ int dwc3_core_init(struct dwc3 *dwc)
hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
*/
dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE);
ret = dwc3_phy_setup(dwc);
if (ret)
return ret;
@@ -1398,6 +1392,12 @@ int dwc3_core_init(struct dwc3 *dwc)
if (ret)
goto err_exit_phy;
/*
* Write Linux Version Code to our GUID register so it's easy to figure
* out which kernel version a bug was found.
*/
dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE);
dwc3_core_setup_global_control(dwc);
dwc3_core_num_eps(dwc);
-4
View File
@@ -733,8 +733,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
if (status == 0) {
omap_writew(reg, UDC_TXDMA_CFG);
/* EMIFF or SDRC */
omap_set_dma_src_burst_mode(ep->lch,
OMAP_DMA_DATA_BURST_4);
omap_set_dma_src_data_pack(ep->lch, 1);
/* TIPB */
omap_set_dma_dest_params(ep->lch,
@@ -756,8 +754,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
UDC_DATA_DMA,
0, 0);
/* EMIFF or SDRC */
omap_set_dma_dest_burst_mode(ep->lch,
OMAP_DMA_DATA_BURST_4);
omap_set_dma_dest_data_pack(ep->lch, 1);
}
}
+4
View File
@@ -1513,7 +1513,11 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */
.driver_info = NCTRL(2) | RSVD(3) },
{ USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1251, 0xff) }, /* Telit LE910Cx (RNDIS) */
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1253, 0xff) }, /* Telit LE910Cx (ECM) */
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1254, 0xff) }, /* Telit LE910Cx */
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1255, 0xff) }, /* Telit LE910Cx */
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1260),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
{ USB_DEVICE(TELIT_VENDOR_ID, 0x1261),
+18 -9
View File
@@ -732,9 +732,14 @@ static const char * const pd_rev[] = {
(tcpm_cc_is_source((port)->cc2) && \
!tcpm_cc_is_source((port)->cc1)))
#define tcpm_port_is_debug_source(port) \
(tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2))
#define tcpm_port_is_debug_sink(port) \
(tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2))
#define tcpm_port_is_debug(port) \
((tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) || \
(tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2)))
(tcpm_port_is_debug_source(port) || tcpm_port_is_debug_sink(port))
#define tcpm_port_is_audio(port) \
(tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2))
@@ -5176,7 +5181,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK);
break;
case SRC_ATTACH_WAIT:
if (tcpm_port_is_debug(port))
if (tcpm_port_is_debug_source(port))
tcpm_set_state(port, DEBUG_ACC_ATTACHED,
port->timings.cc_debounce_time);
else if (tcpm_port_is_audio(port))
@@ -5434,7 +5439,7 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC);
break;
case SNK_ATTACH_WAIT:
if (tcpm_port_is_debug(port))
if (tcpm_port_is_debug_sink(port))
tcpm_set_state(port, DEBUG_ACC_ATTACHED,
PD_T_CC_DEBOUNCE);
else if (tcpm_port_is_audio(port))
@@ -5454,7 +5459,7 @@ static void run_state_machine(struct tcpm_port *port)
if (tcpm_port_is_disconnected(port))
tcpm_set_state(port, SNK_UNATTACHED,
PD_T_PD_DEBOUNCE);
else if (tcpm_port_is_debug(port))
else if (tcpm_port_is_debug_sink(port))
tcpm_set_state(port, DEBUG_ACC_ATTACHED,
PD_T_CC_DEBOUNCE);
else if (tcpm_port_is_audio(port))
@@ -5935,6 +5940,8 @@ static void run_state_machine(struct tcpm_port *port)
/* remove existing capabilities */
tcpm_partner_source_caps_reset(port);
tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP);
port->vdm_sm_running = false;
port->explicit_contract = false;
tcpm_ams_finish(port);
if (port->pwr_role == TYPEC_SOURCE) {
port->upcoming_state = SRC_SEND_CAPABILITIES;
@@ -6360,10 +6367,10 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
switch (port->state) {
case TOGGLING:
if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_source(port))
tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
else if (tcpm_port_is_sink(port))
else if (tcpm_port_is_debug_sink(port) || tcpm_port_is_sink(port))
tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
case CHECK_CONTAMINANT:
@@ -6371,9 +6378,11 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
break;
case SRC_UNATTACHED:
case ACC_UNATTACHED:
if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_source(port))
tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
else if (tcpm_port_is_debug_sink(port))
tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
case SRC_ATTACH_WAIT:
if (tcpm_port_is_disconnected(port) ||
@@ -6395,7 +6404,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
}
break;
case SNK_UNATTACHED:
if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
if (tcpm_port_is_debug_sink(port) || tcpm_port_is_audio(port) ||
tcpm_port_is_sink(port))
tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
break;
+26 -11
View File
@@ -9,6 +9,11 @@
void __rseq_handle_slowpath(struct pt_regs *regs);
static __always_inline bool rseq_v2(struct task_struct *t)
{
return IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY) && likely(t->rseq.event.has_rseq > 1);
}
/* Invoked from resume_user_mode_work() */
static inline void rseq_handle_slowpath(struct pt_regs *regs)
{
@@ -16,8 +21,7 @@ static inline void rseq_handle_slowpath(struct pt_regs *regs)
if (current->rseq.event.slowpath)
__rseq_handle_slowpath(regs);
} else {
/* '&' is intentional to spare one conditional branch */
if (current->rseq.event.sched_switch & current->rseq.event.has_rseq)
if (current->rseq.event.sched_switch && current->rseq.event.has_rseq)
__rseq_handle_slowpath(regs);
}
}
@@ -30,9 +34,9 @@ void __rseq_signal_deliver(int sig, struct pt_regs *regs);
*/
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs)
{
if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) {
/* '&' is intentional to spare one conditional branch */
if (current->rseq.event.has_rseq & current->rseq.event.user_irq)
if (rseq_v2(current)) {
/* has_rseq is implied in rseq_v2() */
if (current->rseq.event.user_irq)
__rseq_signal_deliver(ksig->sig, regs);
} else {
if (current->rseq.event.has_rseq)
@@ -50,15 +54,22 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t)
{
struct rseq_event *ev = &t->rseq.event;
if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY)) {
/*
* Only apply the user_irq optimization for RSEQ ABI V2 registrations.
* Legacy users like TCMalloc rely on the original ABI V1 behaviour
* which updates IDs on every context swtich.
*/
if (rseq_v2(t)) {
/*
* Avoid a boat load of conditionals by using simple logic
* to determine whether NOTIFY_RESUME needs to be raised.
* Avoid a boat load of conditionals by using simple logic to
* determine whether TIF_NOTIFY_RESUME or TIF_RSEQ needs to be
* raised.
*
* It's required when the CPU or MM CID has changed or
* the entry was from user space.
* It's required when the CPU or MM CID has changed or the entry
* was via interrupt from user space. ev->has_rseq does not have
* to be evaluated here because rseq_v2() implies has_rseq.
*/
bool raise = (ev->user_irq | ev->ids_changed) & ev->has_rseq;
bool raise = ev->user_irq | ev->ids_changed;
if (raise) {
ev->sched_switch = true;
@@ -66,6 +77,7 @@ static __always_inline void rseq_sched_switch_event(struct task_struct *t)
}
} else {
if (ev->has_rseq) {
t->rseq.event.ids_changed = true;
t->rseq.event.sched_switch = true;
rseq_raise_notify_resume(t);
}
@@ -119,6 +131,8 @@ static inline void rseq_virt_userspace_exit(void)
static inline void rseq_reset(struct task_struct *t)
{
/* Protect against preemption and membarrier IPI */
guard(irqsave)();
memset(&t->rseq, 0, sizeof(t->rseq));
t->rseq.ids.cpu_id = RSEQ_CPU_ID_UNINITIALIZED;
}
@@ -159,6 +173,7 @@ static inline unsigned int rseq_alloc_align(void)
}
#else /* CONFIG_RSEQ */
static inline bool rseq_v2(struct task_struct *t) { return false; }
static inline void rseq_handle_slowpath(struct pt_regs *regs) { }
static inline void rseq_signal_deliver(struct ksignal *ksig, struct pt_regs *regs) { }
static inline void rseq_sched_switch_event(struct task_struct *t) { }
+59 -63
View File
@@ -111,6 +111,20 @@ static __always_inline void rseq_slice_clear_grant(struct task_struct *t)
t->rseq.slice.state.granted = false;
}
/*
* Open coded, so it can be invoked within a user access region.
*
* This clears the user space state of the time slice extensions field only when
* the task has registered the optimized RSEQ_ABI V2. Some legacy registrations,
* e.g. TCMalloc, have conflicting non-ABI fields in struct RSEQ, which would be
* overwritten by an unconditional write.
*/
#define rseq_slice_clear_user(rseq, efault) \
do { \
if (rseq_slice_extension_enabled()) \
unsafe_put_user(0U, &rseq->slice_ctrl.all, efault); \
} while (0)
static __always_inline bool __rseq_grant_slice_extension(bool work_pending)
{
struct task_struct *curr = current;
@@ -230,10 +244,10 @@ static __always_inline bool rseq_slice_extension_enabled(void) { return false; }
static __always_inline bool rseq_arm_slice_extension_timer(void) { return false; }
static __always_inline void rseq_slice_clear_grant(struct task_struct *t) { }
static __always_inline bool rseq_grant_slice_extension(unsigned long ti_work, unsigned long mask) { return false; }
#define rseq_slice_clear_user(rseq, efault) do { } while (0)
#endif /* !CONFIG_RSEQ_SLICE_EXTENSION */
bool rseq_debug_update_user_cs(struct task_struct *t, struct pt_regs *regs, unsigned long csaddr);
bool rseq_debug_validate_ids(struct task_struct *t);
static __always_inline void rseq_note_user_irq_entry(void)
{
@@ -353,43 +367,6 @@ efault:
return false;
}
/*
* On debug kernels validate that user space did not mess with it if the
* debug branch is enabled.
*/
bool rseq_debug_validate_ids(struct task_struct *t)
{
struct rseq __user *rseq = t->rseq.usrptr;
u32 cpu_id, uval, node_id;
/*
* On the first exit after registering the rseq region CPU ID is
* RSEQ_CPU_ID_UNINITIALIZED and node_id in user space is 0!
*/
node_id = t->rseq.ids.cpu_id != RSEQ_CPU_ID_UNINITIALIZED ?
cpu_to_node(t->rseq.ids.cpu_id) : 0;
scoped_user_read_access(rseq, efault) {
unsafe_get_user(cpu_id, &rseq->cpu_id_start, efault);
if (cpu_id != t->rseq.ids.cpu_id)
goto die;
unsafe_get_user(uval, &rseq->cpu_id, efault);
if (uval != cpu_id)
goto die;
unsafe_get_user(uval, &rseq->node_id, efault);
if (uval != node_id)
goto die;
unsafe_get_user(uval, &rseq->mm_cid, efault);
if (uval != t->rseq.ids.mm_cid)
goto die;
}
return true;
die:
t->rseq.event.fatal = true;
efault:
return false;
}
#endif /* RSEQ_BUILD_SLOW_PATH */
/*
@@ -499,37 +476,50 @@ efault:
* faults in task context are fatal too.
*/
static rseq_inline
bool rseq_set_ids_get_csaddr(struct task_struct *t, struct rseq_ids *ids,
u32 node_id, u64 *csaddr)
bool rseq_set_ids_get_csaddr(struct task_struct *t, struct rseq_ids *ids, u64 *csaddr)
{
struct rseq __user *rseq = t->rseq.usrptr;
if (static_branch_unlikely(&rseq_debug_enabled)) {
if (!rseq_debug_validate_ids(t))
return false;
}
scoped_user_rw_access(rseq, efault) {
/* Validate the R/O fields for debug and optimized mode */
if (static_branch_unlikely(&rseq_debug_enabled) || rseq_v2(t)) {
u32 cpu_id, uval;
unsafe_get_user(cpu_id, &rseq->cpu_id_start, efault);
if (cpu_id != t->rseq.ids.cpu_id)
goto die;
unsafe_get_user(uval, &rseq->cpu_id, efault);
if (uval != cpu_id)
goto die;
unsafe_get_user(uval, &rseq->node_id, efault);
if (uval != t->rseq.ids.node_id)
goto die;
unsafe_get_user(uval, &rseq->mm_cid, efault);
if (uval != t->rseq.ids.mm_cid)
goto die;
}
unsafe_put_user(ids->cpu_id, &rseq->cpu_id_start, efault);
unsafe_put_user(ids->cpu_id, &rseq->cpu_id, efault);
unsafe_put_user(node_id, &rseq->node_id, efault);
unsafe_put_user(ids->node_id, &rseq->node_id, efault);
unsafe_put_user(ids->mm_cid, &rseq->mm_cid, efault);
if (csaddr)
unsafe_get_user(*csaddr, &rseq->rseq_cs, efault);
/* Open coded, so it's in the same user access region */
if (rseq_slice_extension_enabled()) {
/* Unconditionally clear it, no point in conditionals */
unsafe_put_user(0U, &rseq->slice_ctrl.all, efault);
}
/* RSEQ ABI V2 only operations */
if (rseq_v2(t))
rseq_slice_clear_user(rseq, efault);
}
rseq_slice_clear_grant(t);
/* Cache the new values */
t->rseq.ids.cpu_cid = ids->cpu_cid;
t->rseq.ids = *ids;
rseq_stat_inc(rseq_stats.ids);
rseq_trace_update(t, ids);
return true;
die:
t->rseq.event.fatal = true;
efault:
return false;
}
@@ -539,11 +529,11 @@ efault:
* is in a critical section.
*/
static rseq_inline bool rseq_update_usr(struct task_struct *t, struct pt_regs *regs,
struct rseq_ids *ids, u32 node_id)
struct rseq_ids *ids)
{
u64 csaddr;
if (!rseq_set_ids_get_csaddr(t, ids, node_id, &csaddr))
if (!rseq_set_ids_get_csaddr(t, ids, &csaddr))
return false;
/*
@@ -612,6 +602,14 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t
* interrupts disabled
*/
guard(pagefault)();
/*
* This optimization is only valid when the task registered for the
* optimized RSEQ_ABI_V2 variant. Some legacy users rely on the original
* RSEQ implementation behaviour which unconditionally updated the IDs.
* rseq_sched_switch_event() ensures that legacy registrations always
* have both sched_switch and ids_changed set, which is compatible with
* the historical TIF_NOTIFY_RESUME behaviour.
*/
if (likely(!t->rseq.event.ids_changed)) {
struct rseq __user *rseq = t->rseq.usrptr;
/*
@@ -623,11 +621,9 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t
scoped_user_rw_access(rseq, efault) {
unsafe_get_user(csaddr, &rseq->rseq_cs, efault);
/* Open coded, so it's in the same user access region */
if (rseq_slice_extension_enabled()) {
/* Unconditionally clear it, no point in conditionals */
unsafe_put_user(0U, &rseq->slice_ctrl.all, efault);
}
/* RSEQ ABI V2 only operations */
if (rseq_v2(t))
rseq_slice_clear_user(rseq, efault);
}
rseq_slice_clear_grant(t);
@@ -640,12 +636,12 @@ static __always_inline bool rseq_exit_user_update(struct pt_regs *regs, struct t
}
struct rseq_ids ids = {
.cpu_id = task_cpu(t),
.mm_cid = task_mm_cid(t),
.cpu_id = task_cpu(t),
.mm_cid = task_mm_cid(t),
.node_id = cpu_to_node(ids.cpu_id),
};
u32 node_id = cpu_to_node(ids.cpu_id);
return rseq_update_usr(t, regs, &ids, node_id);
return rseq_update_usr(t, regs, &ids);
efault:
return false;
}
+11 -2
View File
@@ -9,6 +9,12 @@
#ifdef CONFIG_RSEQ
struct rseq;
/*
* rseq_event::has_rseq contains the ABI version number so preserving it
* in AND operations requires a mask.
*/
#define RSEQ_HAS_RSEQ_VERSION_MASK 0xff
/**
* struct rseq_event - Storage for rseq related event management
* @all: Compound to initialize and clear the data efficiently
@@ -17,7 +23,8 @@ struct rseq;
* exit to user
* @ids_changed: Indicator that IDs need to be updated
* @user_irq: True on interrupt entry from user mode
* @has_rseq: True if the task has a rseq pointer installed
* @has_rseq: Greater than 0 if the task has a rseq pointer installed.
* Contains the RSEQ version number
* @error: Compound error code for the slow path to analyze
* @fatal: User space data corrupted or invalid
* @slowpath: Indicator that slow path processing via TIF_NOTIFY_RESUME
@@ -59,8 +66,9 @@ struct rseq_event {
* compiler emit a single compare on 64-bit
* @cpu_id: The CPU ID which was written last to user space
* @mm_cid: The MM CID which was written last to user space
* @node_id: The node ID which was written last to user space
*
* @cpu_id and @mm_cid are updated when the data is written to user space.
* @cpu_id, @mm_cid and @node_id are updated when the data is written to user space.
*/
struct rseq_ids {
union {
@@ -70,6 +78,7 @@ struct rseq_ids {
u32 mm_cid;
};
};
u32 node_id;
};
/**
+4 -1
View File
@@ -28,7 +28,7 @@ enum rseq_cs_flags_bit {
RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0,
RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1,
RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2,
/* (3) Intentional gap to put new bits into a separate byte */
/* (3) Intentional gap to keep new bits separate */
/* User read only feature flags */
RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE_BIT = 4,
@@ -161,6 +161,9 @@ struct rseq {
* - RSEQ_CS_FLAG_NO_RESTART_ON_PREEMPT
* - RSEQ_CS_FLAG_NO_RESTART_ON_SIGNAL
* - RSEQ_CS_FLAG_NO_RESTART_ON_MIGRATE
*
* It is now used for feature status advertisement by the kernel.
* See: enum rseq_cs_flags_bit for further information.
*/
__u32 flags;
+55 -15
View File
@@ -7007,6 +7007,7 @@ static void perf_mmap_open(struct vm_area_struct *vma)
}
static void perf_pmu_output_stop(struct perf_event *event);
static void perf_mmap_unaccount(struct vm_area_struct *vma, struct perf_buffer *rb);
/*
* A buffer can be mmap()ed multiple times; either directly through the same
@@ -7022,8 +7023,6 @@ static void perf_mmap_close(struct vm_area_struct *vma)
mapped_f unmapped = get_mapped(event, event_unmapped);
struct perf_buffer *rb = ring_buffer_get(event);
struct user_struct *mmap_user = rb->mmap_user;
int mmap_locked = rb->mmap_locked;
unsigned long size = perf_data_size(rb);
bool detach_rest = false;
/* FIXIES vs perf_pmu_unregister() */
@@ -7118,11 +7117,7 @@ again:
* Aside from that, this buffer is 'fully' detached and unmapped,
* undo the VM accounting.
*/
atomic_long_sub((size >> PAGE_SHIFT) + 1 - mmap_locked,
&mmap_user->locked_vm);
atomic64_sub(mmap_locked, &vma->vm_mm->pinned_vm);
free_uid(mmap_user);
perf_mmap_unaccount(vma, rb);
out_put:
ring_buffer_put(rb); /* could be last */
@@ -7262,6 +7257,15 @@ static void perf_mmap_account(struct vm_area_struct *vma, long user_extra, long
atomic64_add(extra, &vma->vm_mm->pinned_vm);
}
static void perf_mmap_unaccount(struct vm_area_struct *vma, struct perf_buffer *rb)
{
struct user_struct *user = rb->mmap_user;
atomic_long_sub((perf_data_size(rb) >> PAGE_SHIFT) + 1 - rb->mmap_locked,
&user->locked_vm);
atomic64_sub(rb->mmap_locked, &vma->vm_mm->pinned_vm);
}
static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event,
unsigned long nr_pages)
{
@@ -7324,8 +7328,6 @@ static int perf_mmap_rb(struct vm_area_struct *vma, struct perf_event *event,
if (!rb)
return -ENOMEM;
refcount_set(&rb->mmap_count, 1);
rb->mmap_user = get_current_user();
rb->mmap_locked = extra;
ring_buffer_attach(event, rb);
@@ -7475,16 +7477,54 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
mapped(event, vma->vm_mm);
/*
* Try to map it into the page table. On fail, invoke
* perf_mmap_close() to undo the above, as the callsite expects
* full cleanup in this case and therefore does not invoke
* vmops::close().
* Try to map it into the page table. On fail undo the above,
* as the callsite expects full cleanup in this case and
* therefore does not invoke vmops::close().
*/
ret = map_range(event->rb, vma);
if (ret)
perf_mmap_close(vma);
if (likely(!ret))
return 0;
/* Error path */
/*
* If this is the first mmap(), then event->mmap_count should
* be stable at 1. It is only modified by:
* perf_mmap_{open,close}() and perf_mmap().
*
* The former are not possible because this mmap() hasn't been
* successful yet, and the latter is serialized by
* event->mmap_mutex which we still hold (note that mmap_lock
* is not strictly sufficient here, because the event fd can
* be passed to another process through trivial means like
* fork(), leading to concurrent mmap() from different mm).
*
* Make sure to remove event->rb before releasing
* event->mmap_mutex, such that any concurrent mmap() will not
* attempt use this failed buffer.
*/
if (refcount_read(&event->mmap_count) == 1) {
/*
* Minimal perf_mmap_close(); there can't be AUX or
* other events on account of this being the first.
*/
mapped = get_mapped(event, event_unmapped);
if (mapped)
mapped(event, vma->vm_mm);
perf_mmap_unaccount(vma, event->rb);
ring_buffer_attach(event, NULL); /* drops last rb->refcount */
refcount_set(&event->mmap_count, 0);
return ret;
}
/*
* Otherwise this is an already existing buffer, and there is
* no race vs first exposure, so fall-through and call
* perf_mmap_close().
*/
}
perf_mmap_close(vma);
return ret;
}
+1
View File
@@ -67,6 +67,7 @@ static inline void rb_free_rcu(struct rcu_head *rcu_head)
struct perf_buffer *rb;
rb = container_of(rcu_head, struct perf_buffer, rcu_head);
free_uid(rb->mmap_user);
rb_free(rb);
}
+2
View File
@@ -340,6 +340,8 @@ ring_buffer_init(struct perf_buffer *rb, long watermark, int flags)
rb->paused = 1;
mutex_init(&rb->aux_mutex);
rb->mmap_user = get_current_user();
refcount_set(&rb->mmap_count, 1);
}
void perf_aux_output_flag(struct perf_output_handle *handle, u64 flags)
+132 -82
View File
@@ -236,11 +236,6 @@ static int __init rseq_debugfs_init(void)
}
__initcall(rseq_debugfs_init);
static bool rseq_set_ids(struct task_struct *t, struct rseq_ids *ids, u32 node_id)
{
return rseq_set_ids_get_csaddr(t, ids, node_id, NULL);
}
static bool rseq_handle_cs(struct task_struct *t, struct pt_regs *regs)
{
struct rseq __user *urseq = t->rseq.usrptr;
@@ -258,14 +253,16 @@ efault:
static void rseq_slowpath_update_usr(struct pt_regs *regs)
{
/*
* Preserve rseq state and user_irq state. The generic entry code
* clears user_irq on the way out, the non-generic entry
* architectures are not having user_irq.
* Preserve has_rseq and user_irq state. The generic entry code clears
* user_irq on the way out, the non-generic entry architectures are not
* setting user_irq.
*/
const struct rseq_event evt_mask = { .has_rseq = true, .user_irq = true, };
const struct rseq_event evt_mask = {
.has_rseq = RSEQ_HAS_RSEQ_VERSION_MASK,
.user_irq = true,
};
struct task_struct *t = current;
struct rseq_ids ids;
u32 node_id;
bool event;
if (unlikely(t->flags & PF_EXITING))
@@ -301,9 +298,9 @@ static void rseq_slowpath_update_usr(struct pt_regs *regs)
if (!event)
return;
node_id = cpu_to_node(ids.cpu_id);
ids.node_id = cpu_to_node(ids.cpu_id);
if (unlikely(!rseq_update_usr(t, regs, &ids, node_id))) {
if (unlikely(!rseq_update_usr(t, regs, &ids))) {
/*
* Clear the errors just in case this might survive magically, but
* leave the rest intact.
@@ -335,8 +332,9 @@ void __rseq_handle_slowpath(struct pt_regs *regs)
void __rseq_signal_deliver(int sig, struct pt_regs *regs)
{
rseq_stat_inc(rseq_stats.signal);
/*
* Don't update IDs, they are handled on exit to user if
* Don't update IDs yet, they are handled on exit to user if
* necessary. The important thing is to abort a critical section of
* the interrupted context as after this point the instruction
* pointer in @regs points to the signal handler.
@@ -349,6 +347,13 @@ void __rseq_signal_deliver(int sig, struct pt_regs *regs)
current->rseq.event.error = 0;
force_sigsegv(sig);
}
/*
* In legacy mode, force the update of IDs before returning to user
* space to stay compatible.
*/
if (!rseq_v2(current))
rseq_force_update();
}
/*
@@ -384,19 +389,22 @@ void rseq_syscall(struct pt_regs *regs)
static bool rseq_reset_ids(void)
{
struct rseq_ids ids = {
.cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
.mm_cid = 0,
};
struct rseq __user *rseq = current->rseq.usrptr;
/*
* If this fails, terminate it because this leaves the kernel in
* stupid state as exit to user space will try to fixup the ids
* again.
*/
if (rseq_set_ids(current, &ids, 0))
return true;
scoped_user_rw_access(rseq, efault) {
unsafe_put_user(0, &rseq->cpu_id_start, efault);
unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id, efault);
unsafe_put_user(0, &rseq->node_id, efault);
unsafe_put_user(0, &rseq->mm_cid, efault);
}
return true;
efault:
force_sig(SIGSEGV);
return false;
}
@@ -404,70 +412,29 @@ static bool rseq_reset_ids(void)
/* The original rseq structure size (including padding) is 32 bytes. */
#define ORIG_RSEQ_SIZE 32
/*
* sys_rseq - setup restartable sequences for caller thread.
*/
SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32, sig)
static long rseq_register(struct rseq __user * rseq, u32 rseq_len, int flags, u32 sig)
{
u32 rseqfl = 0;
u8 version = 1;
if (flags & RSEQ_FLAG_UNREGISTER) {
if (flags & ~RSEQ_FLAG_UNREGISTER)
return -EINVAL;
/* Unregister rseq for current thread. */
if (current->rseq.usrptr != rseq || !current->rseq.usrptr)
return -EINVAL;
if (rseq_len != current->rseq.len)
return -EINVAL;
if (current->rseq.sig != sig)
return -EPERM;
if (!rseq_reset_ids())
return -EFAULT;
rseq_reset(current);
return 0;
}
if (unlikely(flags & ~(RSEQ_FLAG_SLICE_EXT_DEFAULT_ON)))
return -EINVAL;
if (current->rseq.usrptr) {
/*
* If rseq is already registered, check whether
* the provided address differs from the prior
* one.
*/
if (current->rseq.usrptr != rseq || rseq_len != current->rseq.len)
return -EINVAL;
if (current->rseq.sig != sig)
return -EPERM;
/* Already registered. */
return -EBUSY;
}
/*
* If there was no rseq previously registered, ensure the provided rseq
* is properly aligned, as communcated to user-space through the ELF
* auxiliary vector AT_RSEQ_ALIGN. If rseq_len is the original rseq
* size, the required alignment is the original struct rseq alignment.
*
* The rseq_len is required to be greater or equal to the original rseq
* size. In order to be valid, rseq_len is either the original rseq size,
* or large enough to contain all supported fields, as communicated to
* user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE.
*/
if (rseq_len < ORIG_RSEQ_SIZE ||
(rseq_len == ORIG_RSEQ_SIZE && !IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE)) ||
(rseq_len != ORIG_RSEQ_SIZE && (!IS_ALIGNED((unsigned long)rseq, rseq_alloc_align()) ||
rseq_len < offsetof(struct rseq, end))))
return -EINVAL;
if (!access_ok(rseq, rseq_len))
return -EFAULT;
if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION)) {
rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE;
if (rseq_slice_extension_enabled() &&
(flags & RSEQ_FLAG_SLICE_EXT_DEFAULT_ON))
rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED;
/*
* Architectures, which use the generic IRQ entry code (at least) enable
* registrations with a size greater than the original v1 fixed sized
* @rseq_len, which has been validated already to utilize the optimized
* v2 ABI mode which also enables extended RSEQ features beyond MMCID.
*/
if (IS_ENABLED(CONFIG_GENERIC_IRQ_ENTRY) && rseq_len > ORIG_RSEQ_SIZE)
version = 2;
if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION) && version > 1) {
if (rseq_slice_extension_enabled()) {
rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_AVAILABLE;
if (flags & RSEQ_FLAG_SLICE_EXT_DEFAULT_ON)
rseqfl |= RSEQ_CS_FLAG_SLICE_EXT_ENABLED;
}
}
scoped_user_write_access(rseq, efault) {
@@ -485,7 +452,15 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32
unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id, efault);
unsafe_put_user(0U, &rseq->node_id, efault);
unsafe_put_user(0U, &rseq->mm_cid, efault);
unsafe_put_user(0U, &rseq->slice_ctrl.all, efault);
/*
* All fields past mm_cid are only valid for non-legacy v2
* registrations.
*/
if (version > 1) {
if (IS_ENABLED(CONFIG_RSEQ_SLICE_EXTENSION))
unsafe_put_user(0U, &rseq->slice_ctrl.all, efault);
}
}
/*
@@ -501,11 +476,10 @@ SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32
#endif
/*
* If rseq was previously inactive, and has just been
* registered, ensure the cpu_id_start and cpu_id fields
* are updated before returning to user-space.
* Ensure the cpu_id_start and cpu_id fields are updated before
* returning to user-space.
*/
current->rseq.event.has_rseq = true;
current->rseq.event.has_rseq = version;
rseq_force_update();
return 0;
@@ -513,6 +487,80 @@ efault:
return -EFAULT;
}
static long rseq_unregister(struct rseq __user * rseq, u32 rseq_len, int flags, u32 sig)
{
if (flags & ~RSEQ_FLAG_UNREGISTER)
return -EINVAL;
if (current->rseq.usrptr != rseq || !current->rseq.usrptr)
return -EINVAL;
if (rseq_len != current->rseq.len)
return -EINVAL;
if (current->rseq.sig != sig)
return -EPERM;
if (!rseq_reset_ids())
return -EFAULT;
rseq_reset(current);
return 0;
}
static long rseq_reregister(struct rseq __user * rseq, u32 rseq_len, u32 sig)
{
/*
* If rseq is already registered, check whether the provided address
* differs from the prior one.
*/
if (current->rseq.usrptr != rseq || rseq_len != current->rseq.len)
return -EINVAL;
if (current->rseq.sig != sig)
return -EPERM;
/* Already registered. */
return -EBUSY;
}
static bool rseq_length_valid(struct rseq __user *rseq, unsigned int rseq_len)
{
/*
* Ensure the provided rseq is properly aligned, as communicated to
* user-space through the ELF auxiliary vector AT_RSEQ_ALIGN. If
* rseq_len is the original rseq size, the required alignment is the
* original struct rseq alignment.
*
* In order to be valid, rseq_len is either the original rseq size, or
* large enough to contain all supported fields, as communicated to
* user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE.
*/
if (rseq_len < ORIG_RSEQ_SIZE)
return false;
if (rseq_len == ORIG_RSEQ_SIZE)
return IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE);
return IS_ALIGNED((unsigned long)rseq, rseq_alloc_align()) &&
rseq_len >= offsetof(struct rseq, end);
}
#define RSEQ_FLAGS_SUPPORTED (RSEQ_FLAG_SLICE_EXT_DEFAULT_ON)
/*
* sys_rseq - Register or unregister restartable sequences for the caller thread.
*/
SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32, sig)
{
if (flags & RSEQ_FLAG_UNREGISTER)
return rseq_unregister(rseq, rseq_len, flags, sig);
if (unlikely(flags & ~RSEQ_FLAGS_SUPPORTED))
return -EINVAL;
if (current->rseq.usrptr)
return rseq_reregister(rseq, rseq_len, sig);
if (!rseq_length_valid(rseq, rseq_len))
return -EINVAL;
return rseq_register(rseq, rseq_len, flags, sig);
}
#ifdef CONFIG_RSEQ_SLICE_EXTENSION
struct slice_timer {
struct hrtimer timer;
@@ -713,6 +761,8 @@ int rseq_slice_extension_prctl(unsigned long arg2, unsigned long arg3)
return -ENOTSUPP;
if (!current->rseq.usrptr)
return -ENXIO;
if (!rseq_v2(current))
return -ENOTSUPP;
/* No change? */
if (enable == !!current->rseq.slice.state.enabled)
+37 -7
View File
@@ -913,11 +913,11 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
*
* lag_i >= 0 -> V >= v_i
*
* \Sum (v_i - v)*w_i
* V = ------------------ + v
* \Sum (v_i - v0)*w_i
* V = ------------------- + v0
* \Sum w_i
*
* lag_i >= 0 -> \Sum (v_i - v)*w_i >= (v_i - v)*(\Sum w_i)
* lag_i >= 0 -> \Sum (v_i - v0)*w_i >= (v_i - v0)*(\Sum w_i)
*
* Note: using 'avg_vruntime() > se->vruntime' is inaccurate due
* to the loss in precision caused by the division.
@@ -925,7 +925,7 @@ bool update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se)
static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime)
{
struct sched_entity *curr = cfs_rq->curr;
s64 avg = cfs_rq->sum_w_vruntime;
s64 key, avg = cfs_rq->sum_w_vruntime;
long load = cfs_rq->sum_weight;
if (curr && curr->on_rq) {
@@ -935,7 +935,36 @@ static int vruntime_eligible(struct cfs_rq *cfs_rq, u64 vruntime)
load += weight;
}
return avg >= vruntime_op(vruntime, "-", cfs_rq->zero_vruntime) * load;
key = vruntime_op(vruntime, "-", cfs_rq->zero_vruntime);
/*
* The worst case term for @key includes 'NSEC_TICK * NICE_0_LOAD'
* and @load obviously includes NICE_0_LOAD. NSEC_TICK is around 24
* bits, while NICE_0_LOAD is 20 on 64bit and 10 otherwise.
*
* This gives that on 64bit the product will be at least 64bit which
* overflows s64, while on 32bit it will only be 44bits and should fit
* comfortably.
*/
#ifdef CONFIG_64BIT
#ifdef CONFIG_ARCH_SUPPORTS_INT128
/* This often results in simpler code than __builtin_mul_overflow(). */
return avg >= (__int128)key * load;
#else
s64 rhs;
/*
* On overflow, the sign of key tells us the correct answer: a large
* positive key means vruntime >> V, so not eligible; a large negative
* key means vruntime << V, so eligible.
*/
if (check_mul_overflow(key, load, &rhs))
return key <= 0;
return avg >= rhs;
#endif
#else /* 32bit */
return avg >= key * load;
#endif
}
int entity_eligible(struct cfs_rq *cfs_rq, struct sched_entity *se)
@@ -9221,9 +9250,10 @@ pick:
/*
* Because p is enqueued, nse being null can only mean that we
* dequeued a delayed task.
* dequeued a delayed task. If there are still entities queued in
* cfs, check if the next one will be p.
*/
if (!nse)
if (!nse && cfs_rq->nr_queued)
goto pick;
if (sched_feat(RUN_TO_PARITY))
+10 -1
View File
@@ -199,7 +199,16 @@ static void ipi_rseq(void *info)
* is negligible.
*/
smp_mb();
rseq_sched_switch_event(current);
/*
* Legacy mode requires that IDs are written and the critical section is
* evaluated. V2 optimized mode handles the critical section and IDs are
* only updated if they change as a consequence of preemption after
* return from this IPI.
*/
if (rseq_v2(current))
rseq_sched_switch_event(current);
else
rseq_force_update();
}
static void ipi_sync_rq_state(void *info)
+29 -11
View File
@@ -1860,19 +1860,37 @@ static int tmigr_setup_groups(unsigned int cpu, unsigned int node,
* child to the new parents. So tmigr_active_up() activates the
* new parents while walking up from the old root to the new.
*
* * It is ensured that @start is active, as this setup path is
* executed in hotplug prepare callback. This is executed by an
* already connected and !idle CPU. Even if all other CPUs go idle,
* the CPU executing the setup will be responsible up to current top
* level group. And the next time it goes inactive, it will release
* the new childmask and parent to subsequent walkers through this
* @child. Therefore propagate active state unconditionally.
* * It is ensured that @start is active, (or on the way to be activated
* by another CPU that woke up before the current one) as this setup path
* is executed in hotplug prepare callback. This is executed by an already
* connected and !idle CPU in the hierarchy.
*
* * The below RmW atomic operation ensures that:
*
* 1) If the old root has been completely activated, the latest state is
* acquired (the below implicit acquire pairs with the implicit release
* from cmpxchg() in tmigr_active_up()).
*
* 2) If the old root is still on the way to be activated, the lagging behind
* CPU performing the activation will acquire the links up to the new root.
* (The below implicit release pairs with the implicit acquire from cmpxchg()
* in tmigr_active_up()).
*
* 3) Every subsequent CPU below the old root will acquire the new links while
* walking through the old root (The below implicit release pairs with the
* implicit acquire from cmpxchg() in either tmigr_active_up()) or
* tmigr_inactive_up().
*/
state.state = atomic_read(&start->migr_state);
WARN_ON_ONCE(!state.active);
state.state = atomic_fetch_or(0, &start->migr_state);
WARN_ON_ONCE(!start->parent);
data.childmask = start->groupmask;
__walk_groups_from(tmigr_active_up, &data, start, start->parent);
/*
* If the state of the old root is inactive, another CPU is on its way to activate
* it and propagate to the new root.
*/
if (state.active) {
data.childmask = start->groupmask;
__walk_groups_from(tmigr_active_up, &data, start, start->parent);
}
}
/* Root update */
+2
View File
@@ -403,6 +403,8 @@ BINDGEN_TARGET_x86 := x86_64-linux-gnu
BINDGEN_TARGET_arm64 := aarch64-linux-gnu
BINDGEN_TARGET_arm := arm-linux-gnueabi
BINDGEN_TARGET_loongarch := loongarch64-linux-gnusf
# This is only for i386 UM builds, which need the 32-bit target not -m32
BINDGEN_TARGET_i386 := i386-linux-gnu
BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH))
BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH))
+84 -100
View File
@@ -249,22 +249,6 @@ fn init_fields(
});
// Again span for better diagnostics
let write = quote_spanned!(ident.span()=> ::core::ptr::write);
// NOTE: the field accessor ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
let accessor = if pinned {
let project_ident = format_ident!("__project_{ident}");
quote! {
// SAFETY: TODO
unsafe { #data.#project_ident(&mut (*#slot).#ident) }
}
} else {
quote! {
// SAFETY: TODO
unsafe { &mut (*#slot).#ident }
}
};
quote! {
#(#attrs)*
{
@@ -272,51 +256,31 @@ fn init_fields(
// SAFETY: TODO
unsafe { #write(&raw mut (*#slot).#ident, #value_ident) };
}
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
}
}
InitializerKind::Init { ident, value, .. } => {
// Again span for better diagnostics
let init = format_ident!("init", span = value.span());
// NOTE: the field accessor ensures that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` below has the same requirement.
let (value_init, accessor) = if pinned {
let project_ident = format_ident!("__project_{ident}");
(
quote! {
// SAFETY:
// - `slot` is valid, because we are inside of an initializer closure, we
// return when an error/panic occurs.
// - We also use `#data` to require the correct trait (`Init` or `PinInit`)
// for `#ident`.
unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? };
},
quote! {
// SAFETY: TODO
unsafe { #data.#project_ident(&mut (*#slot).#ident) }
},
)
let value_init = if pinned {
quote! {
// SAFETY:
// - `slot` is valid, because we are inside of an initializer closure, we
// return when an error/panic occurs.
// - We also use `#data` to require the correct trait (`Init` or `PinInit`)
// for `#ident`.
unsafe { #data.#ident(&raw mut (*#slot).#ident, #init)? };
}
} else {
(
quote! {
// SAFETY: `slot` is valid, because we are inside of an initializer
// closure, we return when an error/panic occurs.
unsafe {
::pin_init::Init::__init(
#init,
&raw mut (*#slot).#ident,
)?
};
},
quote! {
// SAFETY: TODO
unsafe { &mut (*#slot).#ident }
},
)
quote! {
// SAFETY: `slot` is valid, because we are inside of an initializer
// closure, we return when an error/panic occurs.
unsafe {
::pin_init::Init::__init(
#init,
&raw mut (*#slot).#ident,
)?
};
}
};
quote! {
#(#attrs)*
@@ -324,9 +288,6 @@ fn init_fields(
let #init = #value;
#value_init
}
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
}
}
InitializerKind::Code { block: value, .. } => quote! {
@@ -339,18 +300,41 @@ fn init_fields(
if let Some(ident) = kind.ident() {
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
// NOTE: The reference is derived from the guard so that it only lives as long as the
// guard does and cannot escape the scope. If it's created via `&mut (*#slot).#ident`
// like the unaligned field guard, it will become effectively `'static`.
let accessor = if pinned {
let project_ident = format_ident!("__project_{ident}");
quote! {
// SAFETY: the initialization is pinned.
unsafe { #data.#project_ident(#guard.let_binding()) }
}
} else {
quote! {
#guard.let_binding()
}
};
res.extend(quote! {
#(#cfgs)*
// Create the drop guard:
// Create the drop guard.
//
// We rely on macro hygiene to make it impossible for users to access this local
// variable.
// SAFETY: We forget the guard later when initialization has succeeded.
let #guard = unsafe {
// SAFETY:
// - `&raw mut (*slot).#ident` is valid.
// - `make_field_check` checks that `&raw mut (*slot).#ident` is properly aligned.
// - `(*slot).#ident` has been initialized above.
// - We only need the ownership to the pointee back when initialization has
// succeeded, where we `forget` the guard.
let mut #guard = unsafe {
::pin_init::__internal::DropGuard::new(
&raw mut (*slot).#ident
)
};
#(#cfgs)*
#[allow(unused_variables)]
let #ident = #accessor;
});
guards.push(guard);
guard_attrs.push(cfgs);
@@ -367,49 +351,49 @@ fn init_fields(
}
}
/// Generate the check for ensuring that every field has been initialized.
/// Generate the check for ensuring that every field has been initialized and aligned.
fn make_field_check(
fields: &Punctuated<InitializerField, Token![,]>,
init_kind: InitKind,
path: &Path,
) -> TokenStream {
let field_attrs = fields
let field_attrs: Vec<_> = fields
.iter()
.filter_map(|f| f.kind.ident().map(|_| &f.attrs));
let field_name = fields.iter().filter_map(|f| f.kind.ident());
match init_kind {
InitKind::Normal => quote! {
// We use unreachable code to ensure that all fields have been mentioned exactly once,
// this struct initializer will still be type-checked and complain with a very natural
// error message if a field is forgotten/mentioned more than once.
#[allow(unreachable_code, clippy::diverging_sub_expression)]
// SAFETY: this code is never executed.
let _ = || unsafe {
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
)*
})
};
},
InitKind::Zeroing => quote! {
// We use unreachable code to ensure that all fields have been mentioned at most once.
// Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
// be zeroed. This struct initializer will still be type-checked and complain with a
// very natural error message if a field is mentioned more than once, or doesn't exist.
#[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
// SAFETY: this code is never executed.
let _ = || unsafe {
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
)*
..::core::mem::zeroed()
})
};
},
.filter_map(|f| f.kind.ident().map(|_| &f.attrs))
.collect();
let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect();
let zeroing_trailer = match init_kind {
InitKind::Normal => None,
InitKind::Zeroing => Some(quote! {
..::core::mem::zeroed()
}),
};
quote! {
#[allow(unreachable_code, clippy::diverging_sub_expression)]
// We use unreachable code to perform field checks. They're still checked by the compiler.
// SAFETY: this code is never executed.
let _ = || unsafe {
// Create references to ensure that the initialized field is properly aligned.
// Unaligned fields will cause the compiler to emit E0793. We do not support
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
// `ptr::write` for value-initialization case has the same requirement.
#(
#(#field_attrs)*
let _ = &(*slot).#field_name;
)*
// If the zeroing trailer is not present, this checks that all fields have been
// mentioned exactly once. If the zeroing trailer is present, all missing fields will be
// zeroed, so this checks that all fields have been mentioned at most once. The use of
// struct initializer will still generate very natural error messages for any misuse.
::core::ptr::write(slot, #path {
#(
#(#field_attrs)*
#field_name: ::core::panic!(),
)*
#zeroing_trailer
})
};
}
}
+19 -9
View File
@@ -238,32 +238,42 @@ fn stack_init_reuse() {
/// When a value of this type is dropped, it drops a `T`.
///
/// Can be forgotten to prevent the drop.
///
/// # Invariants
///
/// - `ptr` is valid and properly aligned.
/// - `*ptr` is initialized and owned by this guard.
pub struct DropGuard<T: ?Sized> {
ptr: *mut T,
}
impl<T: ?Sized> DropGuard<T> {
/// Creates a new [`DropGuard<T>`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped.
/// Creates a drop guard and transfer the ownership of the pointer content.
///
/// The ownership is only relinguished if the guard is forgotten via [`core::mem::forget`].
///
/// # Safety
///
/// `ptr` must be a valid pointer.
///
/// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`:
/// - has not been dropped,
/// - is not accessible by any other means,
/// - will not be dropped by any other means.
/// - `ptr` is valid and properly aligned.
/// - `*ptr` is initialized, and the ownership is transferred to this guard.
#[inline]
pub unsafe fn new(ptr: *mut T) -> Self {
// INVARIANT: By safety requirement.
Self { ptr }
}
/// Create a let binding for accessor use.
#[inline]
pub fn let_binding(&mut self) -> &mut T {
// SAFETY: Per type invariant.
unsafe { &mut *self.ptr }
}
}
impl<T: ?Sized> Drop for DropGuard<T> {
#[inline]
fn drop(&mut self) {
// SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
// ensuring that this operation is safe.
// SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.
unsafe { ptr::drop_in_place(self.ptr) }
}
}
+15 -6
View File
@@ -14,14 +14,20 @@ LDLIBS += -lpthread -ldl
# still track changes to header files and depend on shared object.
OVERRIDE_TARGETS = 1
TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test param_test \
param_test_benchmark param_test_compare_twice param_test_mm_cid \
param_test_mm_cid_benchmark param_test_mm_cid_compare_twice \
syscall_errors_test slice_test
TEST_GEN_PROGS = basic_test basic_percpu_ops_test basic_percpu_ops_mm_cid_test \
param_test_benchmark param_test_mm_cid_benchmark
TEST_GEN_PROGS_EXTENDED = librseq.so
TEST_GEN_PROGS_EXTENDED = librseq.so \
param_test \
param_test_compare_twice \
param_test_mm_cid \
param_test_mm_cid_compare_twice \
syscall_errors_test \
legacy_check \
slice_test \
check_optimized
TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh
TEST_PROGS = run_param_test.sh run_syscall_errors_test.sh run_legacy_check.sh run_timeslice_test.sh
TEST_FILES := settings
@@ -62,3 +68,6 @@ $(OUTPUT)/syscall_errors_test: syscall_errors_test.c $(TEST_GEN_PROGS_EXTENDED)
$(OUTPUT)/slice_test: slice_test.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h
$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@
$(OUTPUT)/check_optimized: check_optimized.c $(TEST_GEN_PROGS_EXTENDED) rseq.h rseq-*.h
$(CC) $(CFLAGS) $< $(LDLIBS) -lrseq -o $@
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: LGPL-2.1
#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include "rseq.h"
int main(int argc, char **argv)
{
if (__rseq_register_current_thread(true, false))
return -1;
return 0;
}
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <signal.h>
#include <stdint.h>
#include <unistd.h>
#include "rseq.h"
#include "../kselftest_harness.h"
FIXTURE(legacy)
{
};
static int cpu_id_in_sigfn = -1;
static void sigfn(int sig)
{
struct rseq_abi *rs = rseq_get_abi();
cpu_id_in_sigfn = rs->cpu_id_start;
}
FIXTURE_SETUP(legacy)
{
int res = __rseq_register_current_thread(true, true);
switch (res) {
case -ENOSYS:
SKIP(return, "RSEQ not enabled\n");
case -EBUSY:
SKIP(return, "GLIBC owns RSEQ. Disable GLIBC RSEQ registration\n");
default:
ASSERT_EQ(res, 0);
}
ASSERT_NE(signal(SIGUSR1, sigfn), SIG_ERR);
}
FIXTURE_TEARDOWN(legacy)
{
}
TEST_F(legacy, legacy_test)
{
struct rseq_abi *rs = rseq_get_abi();
ASSERT_NE(rs, NULL);
/* Overwrite rs::cpu_id_start */
rs->cpu_id_start = -1;
sleep(1);
ASSERT_NE(rs->cpu_id_start, -1);
rs->cpu_id_start = -1;
ASSERT_EQ(raise(SIGUSR1), 0);
ASSERT_NE(rs->cpu_id_start, -1);
ASSERT_NE(cpu_id_in_sigfn, -1);
}
TEST_HARNESS_MAIN
+16 -9
View File
@@ -38,7 +38,7 @@ static int opt_modulo, verbose;
static int opt_yield, opt_signal, opt_sleep,
opt_disable_rseq, opt_threads = 200,
opt_disable_mod = 0, opt_test = 's';
static bool opt_rseq_legacy;
static long long opt_reps = 5000;
static __thread __attribute__((tls_model("initial-exec")))
@@ -281,9 +281,12 @@ unsigned int yield_mod_cnt, nr_abort;
} \
}
#define rseq_no_glibc true
#else
#define printf_verbose(fmt, ...)
#define rseq_no_glibc false
#endif /* BENCHMARK */
@@ -481,7 +484,7 @@ void *test_percpu_spinlock_thread(void *arg)
long long i, reps;
if (!opt_disable_rseq && thread_data->reg &&
rseq_register_current_thread())
__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
abort();
reps = thread_data->reps;
for (i = 0; i < reps; i++) {
@@ -558,7 +561,7 @@ void *test_percpu_inc_thread(void *arg)
long long i, reps;
if (!opt_disable_rseq && thread_data->reg &&
rseq_register_current_thread())
__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
abort();
reps = thread_data->reps;
for (i = 0; i < reps; i++) {
@@ -712,7 +715,7 @@ void *test_percpu_list_thread(void *arg)
long long i, reps;
struct percpu_list *list = (struct percpu_list *)arg;
if (!opt_disable_rseq && rseq_register_current_thread())
if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
abort();
reps = opt_reps;
@@ -895,7 +898,7 @@ void *test_percpu_buffer_thread(void *arg)
long long i, reps;
struct percpu_buffer *buffer = (struct percpu_buffer *)arg;
if (!opt_disable_rseq && rseq_register_current_thread())
if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
abort();
reps = opt_reps;
@@ -1105,7 +1108,7 @@ void *test_percpu_memcpy_buffer_thread(void *arg)
long long i, reps;
struct percpu_memcpy_buffer *buffer = (struct percpu_memcpy_buffer *)arg;
if (!opt_disable_rseq && rseq_register_current_thread())
if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
abort();
reps = opt_reps;
@@ -1258,7 +1261,7 @@ void *test_membarrier_worker_thread(void *arg)
const int iters = opt_reps;
int i;
if (rseq_register_current_thread()) {
if (__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) {
fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
errno, strerror(errno));
abort();
@@ -1323,7 +1326,7 @@ void *test_membarrier_manager_thread(void *arg)
intptr_t expect_a = 0, expect_b = 0;
int cpu_a = 0, cpu_b = 0;
if (rseq_register_current_thread()) {
if (__rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy)) {
fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n",
errno, strerror(errno));
abort();
@@ -1475,6 +1478,7 @@ static void show_usage(int argc, char **argv)
printf(" [-D M] Disable rseq for each M threads\n");
printf(" [-T test] Choose test: (s)pinlock, (l)ist, (b)uffer, (m)emcpy, (i)ncrement, membarrie(r)\n");
printf(" [-M] Push into buffer and memcpy buffer with memory barriers.\n");
printf(" [-O] Test with optimized RSEQ\n");
printf(" [-v] Verbose output.\n");
printf(" [-h] Show this help.\n");
printf("\n");
@@ -1602,6 +1606,9 @@ int main(int argc, char **argv)
case 'M':
opt_mo = RSEQ_MO_RELEASE;
break;
case 'L':
opt_rseq_legacy = true;
break;
default:
show_usage(argc, argv);
goto error;
@@ -1618,7 +1625,7 @@ int main(int argc, char **argv)
if (set_signal_handler())
goto error;
if (!opt_disable_rseq && rseq_register_current_thread())
if (!opt_disable_rseq && __rseq_register_current_thread(rseq_no_glibc, opt_rseq_legacy))
goto error;
if (!opt_disable_rseq && !rseq_validate_cpu_id()) {
fprintf(stderr, "Error: cpu id getter unavailable\n");
+6 -1
View File
@@ -191,10 +191,15 @@ struct rseq_abi {
*/
struct rseq_abi_slice_ctrl slice_ctrl;
/*
* Place holder to push the size above 32 bytes.
*/
__u8 __reserved;
/*
* Flexible array member at end of structure, after last feature field.
*/
char end[];
} __attribute__((aligned(4 * sizeof(__u64))));
} __attribute__((aligned(256)));
#endif /* _RSEQ_ABI_H */
+18 -21
View File
@@ -56,6 +56,7 @@ ptrdiff_t rseq_offset;
* unsuccessful.
*/
unsigned int rseq_size = -1U;
static unsigned int rseq_alloc_size;
/* Flags used during rseq registration. */
unsigned int rseq_flags;
@@ -115,29 +116,17 @@ bool rseq_available(void)
}
}
/* The rseq areas need to be at least 32 bytes. */
static
unsigned int get_rseq_min_alloc_size(void)
{
unsigned int alloc_size = rseq_size;
if (alloc_size < ORIG_RSEQ_ALLOC_SIZE)
alloc_size = ORIG_RSEQ_ALLOC_SIZE;
return alloc_size;
}
/*
* Return the feature size supported by the kernel.
*
* Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE):
*
* 0: Return ORIG_RSEQ_FEATURE_SIZE (20)
* 0: Return ORIG_RSEQ_FEATURE_SIZE (20)
* > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE).
*
* It should never return a value below ORIG_RSEQ_FEATURE_SIZE.
*/
static
unsigned int get_rseq_kernel_feature_size(void)
static unsigned int get_rseq_kernel_feature_size(void)
{
unsigned long auxv_rseq_feature_size, auxv_rseq_align;
@@ -152,15 +141,24 @@ unsigned int get_rseq_kernel_feature_size(void)
return ORIG_RSEQ_FEATURE_SIZE;
}
int rseq_register_current_thread(void)
int __rseq_register_current_thread(bool nolibc, bool legacy)
{
unsigned int size;
int rc;
if (!rseq_ownership) {
/* Treat libc's ownership as a successful registration. */
return 0;
return nolibc ? -EBUSY : 0;
}
rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG);
/* The minimal allocation size is 32, which is the legacy allocation size */
size = get_rseq_kernel_feature_size();
if (legacy || size < ORIG_RSEQ_ALLOC_SIZE)
rseq_alloc_size = ORIG_RSEQ_ALLOC_SIZE;
else
rseq_alloc_size = size;
rc = sys_rseq(&__rseq.abi, rseq_alloc_size, 0, RSEQ_SIG);
if (rc) {
/*
* After at least one thread has registered successfully
@@ -179,9 +177,8 @@ int rseq_register_current_thread(void)
* The first thread to register sets the rseq_size to mimic the libc
* behavior.
*/
if (RSEQ_READ_ONCE(rseq_size) == 0) {
RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size());
}
if (RSEQ_READ_ONCE(rseq_size) == 0)
RSEQ_WRITE_ONCE(rseq_size, size);
return 0;
}
@@ -194,7 +191,7 @@ int rseq_unregister_current_thread(void)
/* Treat libc's ownership as a successful unregistration. */
return 0;
}
rc = sys_rseq(&__rseq.abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
rc = sys_rseq(&__rseq.abi, rseq_alloc_size, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
if (rc)
return -1;
return 0;
+7 -1
View File
@@ -8,6 +8,7 @@
#ifndef RSEQ_H
#define RSEQ_H
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
@@ -142,7 +143,12 @@ static inline struct rseq_abi *rseq_get_abi(void)
* succeed. A restartable sequence executed from a non-registered
* thread will always fail.
*/
int rseq_register_current_thread(void);
int __rseq_register_current_thread(bool nolibc, bool legacy);
static inline int rseq_register_current_thread(void)
{
return __rseq_register_current_thread(false, false);
}
/*
* Unregister rseq for current thread.
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" ./legacy_check
@@ -34,6 +34,11 @@ REPS=1000
SLOW_REPS=100
NR_THREADS=$((6*${NR_CPUS}))
# Prevent GLIBC from registering RSEQ so the selftest can run in legacy and
# performance optimized mode.
GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0"
export GLIBC_TUNABLES
function do_tests()
{
local i=0
@@ -103,6 +108,40 @@ function inject_blocking()
NR_LOOPS=
}
echo "Testing in legacy RSEQ mode"
echo "Yield injection (25%)"
inject_blocking -m 4 -y -L
echo "Yield injection (50%)"
inject_blocking -m 2 -y -L
echo "Yield injection (100%)"
inject_blocking -m 1 -y -L
echo "Kill injection (25%)"
inject_blocking -m 4 -k -L
echo "Kill injection (50%)"
inject_blocking -m 2 -k -L
echo "Kill injection (100%)"
inject_blocking -m 1 -k -L
echo "Sleep injection (1ms, 25%)"
inject_blocking -m 4 -s 1 -L
echo "Sleep injection (1ms, 50%)"
inject_blocking -m 2 -s 1 -L
echo "Sleep injection (1ms, 100%)"
inject_blocking -m 1 -s 1 -L
./check_optimized || {
echo "Skipping optimized RSEQ mode test. Not supported";
exit 0
}
echo "Testing in optimized RSEQ mode"
echo "Yield injection (25%)"
inject_blocking -m 4 -y
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0+
# Prevent GLIBC from registering RSEQ so the selftest can run in legacy
# and performance optimized mode.
GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0"
export GLIBC_TUNABLES
./check_optimized || {
echo "Skipping optimized RSEQ mode test. Not supported";
exit 0
}
./slice_test
+7 -5
View File
@@ -124,6 +124,13 @@ FIXTURE_SETUP(slice_ext)
{
cpu_set_t affinity;
if (__rseq_register_current_thread(true, false))
SKIP(return, "RSEQ not supported\n");
if (prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
PR_RSEQ_SLICE_EXT_ENABLE, 0, 0))
SKIP(return, "Time slice extension not supported\n");
ASSERT_EQ(sched_getaffinity(0, sizeof(affinity), &affinity), 0);
/* Pin it on a single CPU. Avoid CPU 0 */
@@ -137,11 +144,6 @@ FIXTURE_SETUP(slice_ext)
break;
}
ASSERT_EQ(rseq_register_current_thread(), 0);
ASSERT_EQ(prctl(PR_RSEQ_SLICE_EXTENSION, PR_RSEQ_SLICE_EXTENSION_SET,
PR_RSEQ_SLICE_EXT_ENABLE, 0, 0), 0);
self->noise_params.noise_nsecs = variant->noise_nsecs;
self->noise_params.sleep_nsecs = variant->sleep_nsecs;
self->noise_params.run = 1;