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:
commit
383d991c41
77 files changed
+1246
-568
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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))) {
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1230,6 +1230,8 @@ err_master:
|
||||
err_driver:
|
||||
kfree(fake_bridge);
|
||||
err_struct:
|
||||
root_device_unregister(vme_root);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user