mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 19:13:39 -04:00
Merge tag 'v6.18-rc6' into drm-next
Linux 6.18-rc6 Backmerge in order to merge msm next Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
@@ -109,6 +109,15 @@ config KEXEC_HANDOVER
|
||||
to keep data or state alive across the kexec. For this to work,
|
||||
both source and target kernels need to have this option enabled.
|
||||
|
||||
config KEXEC_HANDOVER_DEBUG
|
||||
bool "Enable Kexec Handover debug checks"
|
||||
depends on KEXEC_HANDOVER
|
||||
help
|
||||
This option enables extra sanity checks for the Kexec Handover
|
||||
subsystem. Since, KHO performance is crucial in live update
|
||||
scenarios and the extra code might be adding overhead it is
|
||||
only optionally enabled.
|
||||
|
||||
config CRASH_DUMP
|
||||
bool "kernel crash dumps"
|
||||
default ARCH_DEFAULT_CRASH_DUMP
|
||||
|
||||
@@ -83,6 +83,7 @@ obj-$(CONFIG_KEXEC) += kexec.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
|
||||
obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o
|
||||
obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o
|
||||
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
|
||||
obj-$(CONFIG_COMPAT) += compat.o
|
||||
obj-$(CONFIG_CGROUPS) += cgroup/
|
||||
|
||||
@@ -4169,7 +4169,8 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
|
||||
}
|
||||
|
||||
/**
|
||||
* bpf_task_work_schedule_signal - Schedule BPF callback using task_work_add with TWA_SIGNAL mode
|
||||
* bpf_task_work_schedule_signal_impl - Schedule BPF callback using task_work_add with TWA_SIGNAL
|
||||
* mode
|
||||
* @task: Task struct for which callback should be scheduled
|
||||
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
|
||||
* @map__map: bpf_map that embeds struct bpf_task_work in the values
|
||||
@@ -4178,15 +4179,17 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
|
||||
*
|
||||
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
|
||||
*/
|
||||
__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
|
||||
void *map__map, bpf_task_work_callback_t callback,
|
||||
void *aux__prog)
|
||||
__bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
|
||||
struct bpf_task_work *tw, void *map__map,
|
||||
bpf_task_work_callback_t callback,
|
||||
void *aux__prog)
|
||||
{
|
||||
return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* bpf_task_work_schedule_resume - Schedule BPF callback using task_work_add with TWA_RESUME mode
|
||||
* bpf_task_work_schedule_resume_impl - Schedule BPF callback using task_work_add with TWA_RESUME
|
||||
* mode
|
||||
* @task: Task struct for which callback should be scheduled
|
||||
* @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
|
||||
* @map__map: bpf_map that embeds struct bpf_task_work in the values
|
||||
@@ -4195,9 +4198,10 @@ __bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct b
|
||||
*
|
||||
* Return: 0 if task work has been scheduled successfully, negative error code otherwise
|
||||
*/
|
||||
__bpf_kfunc int bpf_task_work_schedule_resume(struct task_struct *task, struct bpf_task_work *tw,
|
||||
void *map__map, bpf_task_work_callback_t callback,
|
||||
void *aux__prog)
|
||||
__bpf_kfunc int bpf_task_work_schedule_resume_impl(struct task_struct *task,
|
||||
struct bpf_task_work *tw, void *map__map,
|
||||
bpf_task_work_callback_t callback,
|
||||
void *aux__prog)
|
||||
{
|
||||
return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_RESUME);
|
||||
}
|
||||
@@ -4345,6 +4349,7 @@ BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLE
|
||||
BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_local_irq_save)
|
||||
BTF_ID_FLAGS(func, bpf_local_irq_restore)
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr)
|
||||
BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr)
|
||||
BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr)
|
||||
@@ -4353,6 +4358,7 @@ BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
|
||||
#endif
|
||||
#ifdef CONFIG_DMA_SHARED_BUFFER
|
||||
BTF_ID_FLAGS(func, bpf_iter_dmabuf_new, KF_ITER_NEW | KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
|
||||
@@ -4374,9 +4380,9 @@ BTF_ID_FLAGS(func, bpf_strnstr);
|
||||
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
|
||||
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
|
||||
#endif
|
||||
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_stream_vprintk_impl, KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_signal_impl, KF_TRUSTED_ARGS)
|
||||
BTF_ID_FLAGS(func, bpf_task_work_schedule_resume_impl, KF_TRUSTED_ARGS)
|
||||
BTF_KFUNCS_END(common_btf_ids)
|
||||
|
||||
static const struct btf_kfunc_id_set common_kfunc_set = {
|
||||
|
||||
@@ -216,6 +216,8 @@ static struct bpf_map *ringbuf_map_alloc(union bpf_attr *attr)
|
||||
|
||||
static void bpf_ringbuf_free(struct bpf_ringbuf *rb)
|
||||
{
|
||||
irq_work_sync(&rb->work);
|
||||
|
||||
/* copy pages pointer and nr_pages to local variable, as we are going
|
||||
* to unmap rb itself with vunmap() below
|
||||
*/
|
||||
|
||||
@@ -355,7 +355,8 @@ __bpf_kfunc_start_defs();
|
||||
* Avoid using enum bpf_stream_id so that kfunc users don't have to pull in the
|
||||
* enum in headers.
|
||||
*/
|
||||
__bpf_kfunc int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args, u32 len__sz, void *aux__prog)
|
||||
__bpf_kfunc int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, const void *args,
|
||||
u32 len__sz, void *aux__prog)
|
||||
{
|
||||
struct bpf_bprintf_data data = {
|
||||
.get_bin_args = true,
|
||||
|
||||
@@ -479,11 +479,6 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
||||
* BPF_TRAMP_F_SHARE_IPMODIFY is set, we can generate the
|
||||
* trampoline again, and retry register.
|
||||
*/
|
||||
/* reset fops->func and fops->trampoline for re-register */
|
||||
tr->fops->func = NULL;
|
||||
tr->fops->trampoline = 0;
|
||||
|
||||
/* free im memory and reallocate later */
|
||||
bpf_tramp_image_free(im);
|
||||
goto again;
|
||||
}
|
||||
|
||||
@@ -8866,7 +8866,7 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_state *cur)
|
||||
{
|
||||
struct bpf_func_state *fold, *fcur;
|
||||
int i, fr;
|
||||
int i, fr, num_slots;
|
||||
|
||||
reset_idmap_scratch(env);
|
||||
for (fr = old->curframe; fr >= 0; fr--) {
|
||||
@@ -8879,7 +8879,9 @@ static int widen_imprecise_scalars(struct bpf_verifier_env *env,
|
||||
&fcur->regs[i],
|
||||
&env->idmap_scratch);
|
||||
|
||||
for (i = 0; i < fold->allocated_stack / BPF_REG_SIZE; i++) {
|
||||
num_slots = min(fold->allocated_stack / BPF_REG_SIZE,
|
||||
fcur->allocated_stack / BPF_REG_SIZE);
|
||||
for (i = 0; i < num_slots; i++) {
|
||||
if (!is_spilled_reg(&fold->stack[i]) ||
|
||||
!is_spilled_reg(&fcur->stack[i]))
|
||||
continue;
|
||||
@@ -12259,8 +12261,8 @@ enum special_kfunc_type {
|
||||
KF_bpf_res_spin_lock_irqsave,
|
||||
KF_bpf_res_spin_unlock_irqrestore,
|
||||
KF___bpf_trap,
|
||||
KF_bpf_task_work_schedule_signal,
|
||||
KF_bpf_task_work_schedule_resume,
|
||||
KF_bpf_task_work_schedule_signal_impl,
|
||||
KF_bpf_task_work_schedule_resume_impl,
|
||||
};
|
||||
|
||||
BTF_ID_LIST(special_kfunc_list)
|
||||
@@ -12331,13 +12333,13 @@ BTF_ID(func, bpf_res_spin_unlock)
|
||||
BTF_ID(func, bpf_res_spin_lock_irqsave)
|
||||
BTF_ID(func, bpf_res_spin_unlock_irqrestore)
|
||||
BTF_ID(func, __bpf_trap)
|
||||
BTF_ID(func, bpf_task_work_schedule_signal)
|
||||
BTF_ID(func, bpf_task_work_schedule_resume)
|
||||
BTF_ID(func, bpf_task_work_schedule_signal_impl)
|
||||
BTF_ID(func, bpf_task_work_schedule_resume_impl)
|
||||
|
||||
static bool is_task_work_add_kfunc(u32 func_id)
|
||||
{
|
||||
return func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal] ||
|
||||
func_id == special_kfunc_list[KF_bpf_task_work_schedule_resume];
|
||||
return func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal_impl] ||
|
||||
func_id == special_kfunc_list[KF_bpf_task_work_schedule_resume_impl];
|
||||
}
|
||||
|
||||
static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta)
|
||||
|
||||
@@ -5892,7 +5892,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent, const char *name,
|
||||
* if the parent has to be frozen, the child has too.
|
||||
*/
|
||||
cgrp->freezer.e_freeze = parent->freezer.e_freeze;
|
||||
seqcount_init(&cgrp->freezer.freeze_seq);
|
||||
seqcount_spinlock_init(&cgrp->freezer.freeze_seq, &css_set_lock);
|
||||
if (cgrp->freezer.e_freeze) {
|
||||
/*
|
||||
* Set the CGRP_FREEZE flag, so when a process will be
|
||||
|
||||
@@ -373,7 +373,7 @@ static int __crash_shrink_memory(struct resource *old_res,
|
||||
old_res->start = 0;
|
||||
old_res->end = 0;
|
||||
} else {
|
||||
crashk_res.end = ram_res->start - 1;
|
||||
old_res->end = ram_res->start - 1;
|
||||
}
|
||||
|
||||
crash_free_reserved_phys_range(ram_res->start, ram_res->end);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/swiotlb.h>
|
||||
#include <asm/sections.h>
|
||||
#include "debug.h"
|
||||
|
||||
@@ -594,7 +595,9 @@ static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs)
|
||||
if (rc == -ENOMEM) {
|
||||
pr_err_once("cacheline tracking ENOMEM, dma-debug disabled\n");
|
||||
global_disable = true;
|
||||
} else if (rc == -EEXIST && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)) {
|
||||
} else if (rc == -EEXIST && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||
!(IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) &&
|
||||
is_swiotlb_active(entry->dev))) {
|
||||
err_printk(entry->dev, entry,
|
||||
"cacheline tracking EEXIST, overlapping mappings aren't supported\n");
|
||||
}
|
||||
|
||||
@@ -11773,7 +11773,8 @@ static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer)
|
||||
|
||||
event = container_of(hrtimer, struct perf_event, hw.hrtimer);
|
||||
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE ||
|
||||
event->hw.state & PERF_HES_STOPPED)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
event->pmu->read(event);
|
||||
@@ -11819,15 +11820,20 @@ static void perf_swevent_cancel_hrtimer(struct perf_event *event)
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
|
||||
/*
|
||||
* The throttle can be triggered in the hrtimer handler.
|
||||
* The HRTIMER_NORESTART should be used to stop the timer,
|
||||
* rather than hrtimer_cancel(). See perf_swevent_hrtimer()
|
||||
* Careful: this function can be triggered in the hrtimer handler,
|
||||
* for cpu-clock events, so hrtimer_cancel() would cause a
|
||||
* deadlock.
|
||||
*
|
||||
* So use hrtimer_try_to_cancel() to try to stop the hrtimer,
|
||||
* and the cpu-clock handler also sets the PERF_HES_STOPPED flag,
|
||||
* which guarantees that perf_swevent_hrtimer() will stop the
|
||||
* hrtimer once it sees the PERF_HES_STOPPED flag.
|
||||
*/
|
||||
if (is_sampling_event(event) && (hwc->interrupts != MAX_INTERRUPTS)) {
|
||||
ktime_t remaining = hrtimer_get_remaining(&hwc->hrtimer);
|
||||
local64_set(&hwc->period_left, ktime_to_ns(remaining));
|
||||
|
||||
hrtimer_cancel(&hwc->hrtimer);
|
||||
hrtimer_try_to_cancel(&hwc->hrtimer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11871,12 +11877,14 @@ static void cpu_clock_event_update(struct perf_event *event)
|
||||
|
||||
static void cpu_clock_event_start(struct perf_event *event, int flags)
|
||||
{
|
||||
event->hw.state = 0;
|
||||
local64_set(&event->hw.prev_count, local_clock());
|
||||
perf_swevent_start_hrtimer(event);
|
||||
}
|
||||
|
||||
static void cpu_clock_event_stop(struct perf_event *event, int flags)
|
||||
{
|
||||
event->hw.state = PERF_HES_STOPPED;
|
||||
perf_swevent_cancel_hrtimer(event);
|
||||
if (flags & PERF_EF_UPDATE)
|
||||
cpu_clock_event_update(event);
|
||||
@@ -11950,12 +11958,14 @@ static void task_clock_event_update(struct perf_event *event, u64 now)
|
||||
|
||||
static void task_clock_event_start(struct perf_event *event, int flags)
|
||||
{
|
||||
event->hw.state = 0;
|
||||
local64_set(&event->hw.prev_count, event->ctx->time);
|
||||
perf_swevent_start_hrtimer(event);
|
||||
}
|
||||
|
||||
static void task_clock_event_stop(struct perf_event *event, int flags)
|
||||
{
|
||||
event->hw.state = PERF_HES_STOPPED;
|
||||
perf_swevent_cancel_hrtimer(event);
|
||||
if (flags & PERF_EF_UPDATE)
|
||||
task_clock_event_update(event, event->ctx->time);
|
||||
|
||||
@@ -1680,10 +1680,10 @@ static bool futex_ref_get(struct futex_private_hash *fph)
|
||||
{
|
||||
struct mm_struct *mm = fph->mm;
|
||||
|
||||
guard(rcu)();
|
||||
guard(preempt)();
|
||||
|
||||
if (smp_load_acquire(&fph->state) == FR_PERCPU) {
|
||||
this_cpu_inc(*mm->futex_ref);
|
||||
if (READ_ONCE(fph->state) == FR_PERCPU) {
|
||||
__this_cpu_inc(*mm->futex_ref);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1694,10 +1694,10 @@ static bool futex_ref_put(struct futex_private_hash *fph)
|
||||
{
|
||||
struct mm_struct *mm = fph->mm;
|
||||
|
||||
guard(rcu)();
|
||||
guard(preempt)();
|
||||
|
||||
if (smp_load_acquire(&fph->state) == FR_PERCPU) {
|
||||
this_cpu_dec(*mm->futex_ref);
|
||||
if (READ_ONCE(fph->state) == FR_PERCPU) {
|
||||
__this_cpu_dec(*mm->futex_ref);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <linux/mm.h>
|
||||
#include "gcov.h"
|
||||
|
||||
#if (__GNUC__ >= 14)
|
||||
#if (__GNUC__ >= 15)
|
||||
#define GCOV_COUNTERS 10
|
||||
#elif (__GNUC__ >= 14)
|
||||
#define GCOV_COUNTERS 9
|
||||
#elif (__GNUC__ >= 10)
|
||||
#define GCOV_COUNTERS 8
|
||||
|
||||
@@ -1030,7 +1030,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
|
||||
void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
const char *name)
|
||||
{
|
||||
scoped_irqdesc_get_and_lock(irq, 0)
|
||||
scoped_irqdesc_get_and_buslock(irq, 0)
|
||||
__irq_do_set_handler(scoped_irqdesc, handle, is_chained, name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__irq_set_handler);
|
||||
|
||||
@@ -659,7 +659,7 @@ void __disable_irq(struct irq_desc *desc)
|
||||
|
||||
static int __disable_irq_nosync(unsigned int irq)
|
||||
{
|
||||
scoped_irqdesc_get_and_lock(irq, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
scoped_irqdesc_get_and_buslock(irq, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
__disable_irq(scoped_irqdesc);
|
||||
return 0;
|
||||
}
|
||||
@@ -789,7 +789,7 @@ void __enable_irq(struct irq_desc *desc)
|
||||
*/
|
||||
void enable_irq(unsigned int irq)
|
||||
{
|
||||
scoped_irqdesc_get_and_lock(irq, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
scoped_irqdesc_get_and_buslock(irq, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
struct irq_desc *desc = scoped_irqdesc;
|
||||
|
||||
if (WARN(!desc->irq_data.chip, "enable_irq before setup/request_irq: irq %u\n", irq))
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "KHO: " fmt
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/count_zeros.h>
|
||||
#include <linux/debugfs.h>
|
||||
@@ -22,6 +23,7 @@
|
||||
|
||||
#include <asm/early_ioremap.h>
|
||||
|
||||
#include "kexec_handover_internal.h"
|
||||
/*
|
||||
* KHO is tightly coupled with mm init and needs access to some of mm
|
||||
* internal APIs.
|
||||
@@ -67,10 +69,10 @@ early_param("kho", kho_parse_enable);
|
||||
* Keep track of memory that is to be preserved across KHO.
|
||||
*
|
||||
* The serializing side uses two levels of xarrays to manage chunks of per-order
|
||||
* 512 byte bitmaps. For instance if PAGE_SIZE = 4096, the entire 1G order of a
|
||||
* 1TB system would fit inside a single 512 byte bitmap. For order 0 allocations
|
||||
* each bitmap will cover 16M of address space. Thus, for 16G of memory at most
|
||||
* 512K of bitmap memory will be needed for order 0.
|
||||
* PAGE_SIZE byte bitmaps. For instance if PAGE_SIZE = 4096, the entire 1G order
|
||||
* of a 8TB system would fit inside a single 4096 byte bitmap. For order 0
|
||||
* allocations each bitmap will cover 128M of address space. Thus, for 16G of
|
||||
* memory at most 512K of bitmap memory will be needed for order 0.
|
||||
*
|
||||
* This approach is fully incremental, as the serialization progresses folios
|
||||
* can continue be aggregated to the tracker. The final step, immediately prior
|
||||
@@ -78,12 +80,14 @@ early_param("kho", kho_parse_enable);
|
||||
* successor kernel to parse.
|
||||
*/
|
||||
|
||||
#define PRESERVE_BITS (512 * 8)
|
||||
#define PRESERVE_BITS (PAGE_SIZE * 8)
|
||||
|
||||
struct kho_mem_phys_bits {
|
||||
DECLARE_BITMAP(preserve, PRESERVE_BITS);
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct kho_mem_phys_bits) == PAGE_SIZE);
|
||||
|
||||
struct kho_mem_phys {
|
||||
/*
|
||||
* Points to kho_mem_phys_bits, a sparse bitmap array. Each bit is sized
|
||||
@@ -131,28 +135,28 @@ static struct kho_out kho_out = {
|
||||
.finalized = false,
|
||||
};
|
||||
|
||||
static void *xa_load_or_alloc(struct xarray *xa, unsigned long index, size_t sz)
|
||||
static void *xa_load_or_alloc(struct xarray *xa, unsigned long index)
|
||||
{
|
||||
void *elm, *res;
|
||||
void *res = xa_load(xa, index);
|
||||
|
||||
elm = xa_load(xa, index);
|
||||
if (elm)
|
||||
return elm;
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
void *elm __free(free_page) = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
|
||||
elm = kzalloc(sz, GFP_KERNEL);
|
||||
if (!elm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(virt_to_phys(elm), PAGE_SIZE)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
res = xa_cmpxchg(xa, index, NULL, elm, GFP_KERNEL);
|
||||
if (xa_is_err(res))
|
||||
res = ERR_PTR(xa_err(res));
|
||||
|
||||
if (res) {
|
||||
kfree(elm);
|
||||
return ERR_PTR(xa_err(res));
|
||||
else if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return elm;
|
||||
return no_free_ptr(elm);
|
||||
}
|
||||
|
||||
static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
|
||||
@@ -167,12 +171,12 @@ static void __kho_unpreserve(struct kho_mem_track *track, unsigned long pfn,
|
||||
const unsigned long pfn_high = pfn >> order;
|
||||
|
||||
physxa = xa_load(&track->orders, order);
|
||||
if (!physxa)
|
||||
continue;
|
||||
if (WARN_ON_ONCE(!physxa))
|
||||
return;
|
||||
|
||||
bits = xa_load(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
|
||||
if (!bits)
|
||||
continue;
|
||||
if (WARN_ON_ONCE(!bits))
|
||||
return;
|
||||
|
||||
clear_bit(pfn_high % PRESERVE_BITS, bits->preserve);
|
||||
|
||||
@@ -216,8 +220,7 @@ static int __kho_preserve_order(struct kho_mem_track *track, unsigned long pfn,
|
||||
}
|
||||
}
|
||||
|
||||
bits = xa_load_or_alloc(&physxa->phys_bits, pfn_high / PRESERVE_BITS,
|
||||
sizeof(*bits));
|
||||
bits = xa_load_or_alloc(&physxa->phys_bits, pfn_high / PRESERVE_BITS);
|
||||
if (IS_ERR(bits))
|
||||
return PTR_ERR(bits);
|
||||
|
||||
@@ -345,15 +348,19 @@ static_assert(sizeof(struct khoser_mem_chunk) == PAGE_SIZE);
|
||||
static struct khoser_mem_chunk *new_chunk(struct khoser_mem_chunk *cur_chunk,
|
||||
unsigned long order)
|
||||
{
|
||||
struct khoser_mem_chunk *chunk;
|
||||
struct khoser_mem_chunk *chunk __free(free_page) = NULL;
|
||||
|
||||
chunk = kzalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
chunk = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(virt_to_phys(chunk), PAGE_SIZE)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chunk->hdr.order = order;
|
||||
if (cur_chunk)
|
||||
KHOSER_STORE_PTR(cur_chunk->hdr.next, chunk);
|
||||
return chunk;
|
||||
return no_free_ptr(chunk);
|
||||
}
|
||||
|
||||
static void kho_mem_ser_free(struct khoser_mem_chunk *first_chunk)
|
||||
@@ -374,14 +381,17 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
struct khoser_mem_chunk *chunk = NULL;
|
||||
struct kho_mem_phys *physxa;
|
||||
unsigned long order;
|
||||
int err = -ENOMEM;
|
||||
|
||||
xa_for_each(&ser->track.orders, order, physxa) {
|
||||
struct kho_mem_phys_bits *bits;
|
||||
unsigned long phys;
|
||||
|
||||
chunk = new_chunk(chunk, order);
|
||||
if (!chunk)
|
||||
if (IS_ERR(chunk)) {
|
||||
err = PTR_ERR(chunk);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!first_chunk)
|
||||
first_chunk = chunk;
|
||||
@@ -391,8 +401,10 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
|
||||
if (chunk->hdr.num_elms == ARRAY_SIZE(chunk->bitmaps)) {
|
||||
chunk = new_chunk(chunk, order);
|
||||
if (!chunk)
|
||||
if (IS_ERR(chunk)) {
|
||||
err = PTR_ERR(chunk);
|
||||
goto err_free;
|
||||
}
|
||||
}
|
||||
|
||||
elm = &chunk->bitmaps[chunk->hdr.num_elms];
|
||||
@@ -409,7 +421,7 @@ static int kho_mem_serialize(struct kho_serialization *ser)
|
||||
|
||||
err_free:
|
||||
kho_mem_ser_free(first_chunk);
|
||||
return -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __init deserialize_bitmap(unsigned int order,
|
||||
@@ -465,8 +477,8 @@ static void __init kho_mem_deserialize(const void *fdt)
|
||||
* area for early allocations that happen before page allocator is
|
||||
* initialized.
|
||||
*/
|
||||
static struct kho_scratch *kho_scratch;
|
||||
static unsigned int kho_scratch_cnt;
|
||||
struct kho_scratch *kho_scratch;
|
||||
unsigned int kho_scratch_cnt;
|
||||
|
||||
/*
|
||||
* The scratch areas are scaled by default as percent of memory allocated from
|
||||
@@ -752,6 +764,9 @@ int kho_preserve_folio(struct folio *folio)
|
||||
const unsigned int order = folio_order(folio);
|
||||
struct kho_mem_track *track = &kho_out.ser.track;
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(pfn << PAGE_SHIFT, PAGE_SIZE << order)))
|
||||
return -EINVAL;
|
||||
|
||||
return __kho_preserve_order(track, pfn, order);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kho_preserve_folio);
|
||||
@@ -775,6 +790,11 @@ int kho_preserve_pages(struct page *page, unsigned int nr_pages)
|
||||
unsigned long failed_pfn = 0;
|
||||
int err = 0;
|
||||
|
||||
if (WARN_ON(kho_scratch_overlap(start_pfn << PAGE_SHIFT,
|
||||
nr_pages << PAGE_SHIFT))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (pfn < end_pfn) {
|
||||
const unsigned int order =
|
||||
min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn));
|
||||
@@ -862,16 +882,17 @@ static struct kho_vmalloc_chunk *new_vmalloc_chunk(struct kho_vmalloc_chunk *cur
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk)
|
||||
static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk,
|
||||
unsigned short order)
|
||||
{
|
||||
struct kho_mem_track *track = &kho_out.ser.track;
|
||||
unsigned long pfn = PHYS_PFN(virt_to_phys(chunk));
|
||||
|
||||
__kho_unpreserve(track, pfn, pfn + 1);
|
||||
|
||||
for (int i = 0; chunk->phys[i]; i++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(chunk->phys) && chunk->phys[i]; i++) {
|
||||
pfn = PHYS_PFN(chunk->phys[i]);
|
||||
__kho_unpreserve(track, pfn, pfn + 1);
|
||||
__kho_unpreserve(track, pfn, pfn + (1 << order));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +903,7 @@ static void kho_vmalloc_free_chunks(struct kho_vmalloc *kho_vmalloc)
|
||||
while (chunk) {
|
||||
struct kho_vmalloc_chunk *tmp = chunk;
|
||||
|
||||
kho_vmalloc_unpreserve_chunk(chunk);
|
||||
kho_vmalloc_unpreserve_chunk(chunk, kho_vmalloc->order);
|
||||
|
||||
chunk = KHOSER_LOAD_PTR(chunk->hdr.next);
|
||||
free_page((unsigned long)tmp);
|
||||
@@ -992,7 +1013,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation)
|
||||
while (chunk) {
|
||||
struct page *page;
|
||||
|
||||
for (int i = 0; chunk->phys[i]; i++) {
|
||||
for (int i = 0; i < ARRAY_SIZE(chunk->phys) && chunk->phys[i]; i++) {
|
||||
phys_addr_t phys = chunk->phys[i];
|
||||
|
||||
if (idx + contig_pages > total_pages)
|
||||
|
||||
25
kernel/kexec_handover_debug.c
Normal file
25
kernel/kexec_handover_debug.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* kexec_handover_debug.c - kexec handover optional debug functionality
|
||||
* Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "KHO: " fmt
|
||||
|
||||
#include "kexec_handover_internal.h"
|
||||
|
||||
bool kho_scratch_overlap(phys_addr_t phys, size_t size)
|
||||
{
|
||||
phys_addr_t scratch_start, scratch_end;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < kho_scratch_cnt; i++) {
|
||||
scratch_start = kho_scratch[i].addr;
|
||||
scratch_end = kho_scratch[i].addr + kho_scratch[i].size;
|
||||
|
||||
if (phys < scratch_end && (phys + size) > scratch_start)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
20
kernel/kexec_handover_internal.h
Normal file
20
kernel/kexec_handover_internal.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef LINUX_KEXEC_HANDOVER_INTERNAL_H
|
||||
#define LINUX_KEXEC_HANDOVER_INTERNAL_H
|
||||
|
||||
#include <linux/kexec_handover.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
extern struct kho_scratch *kho_scratch;
|
||||
extern unsigned int kho_scratch_cnt;
|
||||
|
||||
#ifdef CONFIG_KEXEC_HANDOVER_DEBUG
|
||||
bool kho_scratch_overlap(phys_addr_t phys, size_t size);
|
||||
#else
|
||||
static inline bool kho_scratch_overlap(phys_addr_t phys, size_t size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_KEXEC_HANDOVER_DEBUG */
|
||||
|
||||
#endif /* LINUX_KEXEC_HANDOVER_INTERNAL_H */
|
||||
@@ -706,7 +706,6 @@ static void power_down(void)
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
if (hibernation_mode == HIBERNATION_SUSPEND) {
|
||||
pm_restore_gfp_mask();
|
||||
error = suspend_devices_and_enter(mem_sleep_current);
|
||||
if (!error)
|
||||
goto exit;
|
||||
@@ -746,9 +745,6 @@ static void power_down(void)
|
||||
cpu_relax();
|
||||
|
||||
exit:
|
||||
/* Match the pm_restore_gfp_mask() call in hibernate(). */
|
||||
pm_restrict_gfp_mask();
|
||||
|
||||
/* Restore swap signature. */
|
||||
error = swsusp_unmark();
|
||||
if (error)
|
||||
|
||||
@@ -31,23 +31,35 @@
|
||||
* held, unless the suspend/hibernate code is guaranteed not to run in parallel
|
||||
* with that modification).
|
||||
*/
|
||||
static unsigned int saved_gfp_count;
|
||||
static gfp_t saved_gfp_mask;
|
||||
|
||||
void pm_restore_gfp_mask(void)
|
||||
{
|
||||
WARN_ON(!mutex_is_locked(&system_transition_mutex));
|
||||
if (saved_gfp_mask) {
|
||||
gfp_allowed_mask = saved_gfp_mask;
|
||||
saved_gfp_mask = 0;
|
||||
}
|
||||
|
||||
if (WARN_ON(!saved_gfp_count) || --saved_gfp_count)
|
||||
return;
|
||||
|
||||
gfp_allowed_mask = saved_gfp_mask;
|
||||
saved_gfp_mask = 0;
|
||||
|
||||
pm_pr_dbg("GFP mask restored\n");
|
||||
}
|
||||
|
||||
void pm_restrict_gfp_mask(void)
|
||||
{
|
||||
WARN_ON(!mutex_is_locked(&system_transition_mutex));
|
||||
WARN_ON(saved_gfp_mask);
|
||||
|
||||
if (saved_gfp_count++) {
|
||||
WARN_ON((saved_gfp_mask & ~(__GFP_IO | __GFP_FS)) != gfp_allowed_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
saved_gfp_mask = gfp_allowed_mask;
|
||||
gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS);
|
||||
|
||||
pm_pr_dbg("GFP mask restricted\n");
|
||||
}
|
||||
|
||||
unsigned int lock_system_sleep(void)
|
||||
|
||||
@@ -132,6 +132,7 @@ int freeze_processes(void)
|
||||
if (!pm_freezing)
|
||||
static_branch_inc(&freezer_active);
|
||||
|
||||
pm_wakeup_clear(0);
|
||||
pm_freezing = true;
|
||||
error = try_to_freeze_tasks(true);
|
||||
if (!error)
|
||||
|
||||
@@ -595,7 +595,6 @@ static int enter_state(suspend_state_t state)
|
||||
}
|
||||
|
||||
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
|
||||
pm_wakeup_clear(0);
|
||||
pm_suspend_clear_flags();
|
||||
error = suspend_prepare(state);
|
||||
if (error)
|
||||
|
||||
@@ -635,7 +635,7 @@ struct cmp_data {
|
||||
};
|
||||
|
||||
/* Indicates the image size after compression */
|
||||
static atomic_t compressed_size = ATOMIC_INIT(0);
|
||||
static atomic64_t compressed_size = ATOMIC_INIT(0);
|
||||
|
||||
/*
|
||||
* Compression function that runs in its own thread.
|
||||
@@ -664,7 +664,7 @@ static int compress_threadfn(void *data)
|
||||
d->ret = crypto_acomp_compress(d->cr);
|
||||
d->cmp_len = d->cr->dlen;
|
||||
|
||||
atomic_set(&compressed_size, atomic_read(&compressed_size) + d->cmp_len);
|
||||
atomic64_add(d->cmp_len, &compressed_size);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
}
|
||||
@@ -689,14 +689,14 @@ static int save_compressed_image(struct swap_map_handle *handle,
|
||||
ktime_t start;
|
||||
ktime_t stop;
|
||||
size_t off;
|
||||
unsigned thr, run_threads, nr_threads;
|
||||
unsigned int thr, run_threads, nr_threads;
|
||||
unsigned char *page = NULL;
|
||||
struct cmp_data *data = NULL;
|
||||
struct crc_data *crc = NULL;
|
||||
|
||||
hib_init_batch(&hb);
|
||||
|
||||
atomic_set(&compressed_size, 0);
|
||||
atomic64_set(&compressed_size, 0);
|
||||
|
||||
/*
|
||||
* We'll limit the number of threads for compression to limit memory
|
||||
@@ -877,11 +877,14 @@ static int save_compressed_image(struct swap_map_handle *handle,
|
||||
stop = ktime_get();
|
||||
if (!ret)
|
||||
ret = err2;
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
swsusp_show_speed(start, stop, nr_to_write, "Wrote");
|
||||
pr_info("Image size after compression: %lld kbytes\n",
|
||||
(atomic64_read(&compressed_size) / 1024));
|
||||
pr_info("Image saving done\n");
|
||||
swsusp_show_speed(start, stop, nr_to_write, "Wrote");
|
||||
pr_info("Image size after compression: %d kbytes\n",
|
||||
(atomic_read(&compressed_size) / 1024));
|
||||
} else {
|
||||
pr_err("Image saving failed: %d\n", ret);
|
||||
}
|
||||
|
||||
out_clean:
|
||||
hib_finish_batch(&hb);
|
||||
@@ -899,7 +902,8 @@ static int save_compressed_image(struct swap_map_handle *handle,
|
||||
}
|
||||
vfree(data);
|
||||
}
|
||||
if (page) free_page((unsigned long)page);
|
||||
if (page)
|
||||
free_page((unsigned long)page);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -9606,7 +9606,7 @@ static int tg_set_cfs_bandwidth(struct task_group *tg,
|
||||
|
||||
guard(rq_lock_irq)(rq);
|
||||
cfs_rq->runtime_enabled = runtime_enabled;
|
||||
cfs_rq->runtime_remaining = 0;
|
||||
cfs_rq->runtime_remaining = 1;
|
||||
|
||||
if (cfs_rq->throttled)
|
||||
unthrottle_cfs_rq(cfs_rq);
|
||||
|
||||
@@ -67,8 +67,19 @@ static unsigned long scx_watchdog_timestamp = INITIAL_JIFFIES;
|
||||
|
||||
static struct delayed_work scx_watchdog_work;
|
||||
|
||||
/* for %SCX_KICK_WAIT */
|
||||
static unsigned long __percpu *scx_kick_cpus_pnt_seqs;
|
||||
/*
|
||||
* For %SCX_KICK_WAIT: Each CPU has a pointer to an array of pick_task sequence
|
||||
* numbers. The arrays are allocated with kvzalloc() as size can exceed percpu
|
||||
* allocator limits on large machines. O(nr_cpu_ids^2) allocation, allocated
|
||||
* lazily when enabling and freed when disabling to avoid waste when sched_ext
|
||||
* isn't active.
|
||||
*/
|
||||
struct scx_kick_pseqs {
|
||||
struct rcu_head rcu;
|
||||
unsigned long seqs[];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct scx_kick_pseqs __rcu *, scx_kick_pseqs);
|
||||
|
||||
/*
|
||||
* Direct dispatch marker.
|
||||
@@ -780,13 +791,23 @@ static void schedule_deferred(struct rq *rq)
|
||||
if (rq->scx.flags & SCX_RQ_IN_WAKEUP)
|
||||
return;
|
||||
|
||||
/* Don't do anything if there already is a deferred operation. */
|
||||
if (rq->scx.flags & SCX_RQ_BAL_CB_PENDING)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If in balance, the balance callbacks will be called before rq lock is
|
||||
* released. Schedule one.
|
||||
*
|
||||
*
|
||||
* We can't directly insert the callback into the
|
||||
* rq's list: The call can drop its lock and make the pending balance
|
||||
* callback visible to unrelated code paths that call rq_pin_lock().
|
||||
*
|
||||
* Just let balance_one() know that it must do it itself.
|
||||
*/
|
||||
if (rq->scx.flags & SCX_RQ_IN_BALANCE) {
|
||||
queue_balance_callback(rq, &rq->scx.deferred_bal_cb,
|
||||
deferred_bal_cb_workfn);
|
||||
rq->scx.flags |= SCX_RQ_BAL_CB_PENDING;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2003,6 +2024,19 @@ static void flush_dispatch_buf(struct scx_sched *sch, struct rq *rq)
|
||||
dspc->cursor = 0;
|
||||
}
|
||||
|
||||
static inline void maybe_queue_balance_callback(struct rq *rq)
|
||||
{
|
||||
lockdep_assert_rq_held(rq);
|
||||
|
||||
if (!(rq->scx.flags & SCX_RQ_BAL_CB_PENDING))
|
||||
return;
|
||||
|
||||
queue_balance_callback(rq, &rq->scx.deferred_bal_cb,
|
||||
deferred_bal_cb_workfn);
|
||||
|
||||
rq->scx.flags &= ~SCX_RQ_BAL_CB_PENDING;
|
||||
}
|
||||
|
||||
static int balance_one(struct rq *rq, struct task_struct *prev)
|
||||
{
|
||||
struct scx_sched *sch = scx_root;
|
||||
@@ -2150,6 +2184,8 @@ static int balance_scx(struct rq *rq, struct task_struct *prev,
|
||||
#endif
|
||||
rq_repin_lock(rq, rf);
|
||||
|
||||
maybe_queue_balance_callback(rq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3471,7 +3507,9 @@ static void scx_sched_free_rcu_work(struct work_struct *work)
|
||||
struct scx_dispatch_q *dsq;
|
||||
int node;
|
||||
|
||||
irq_work_sync(&sch->error_irq_work);
|
||||
kthread_stop(sch->helper->task);
|
||||
|
||||
free_percpu(sch->pcpu);
|
||||
|
||||
for_each_node_state(node, N_POSSIBLE)
|
||||
@@ -3850,6 +3888,27 @@ static const char *scx_exit_reason(enum scx_exit_kind kind)
|
||||
}
|
||||
}
|
||||
|
||||
static void free_kick_pseqs_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct scx_kick_pseqs *pseqs = container_of(rcu, struct scx_kick_pseqs, rcu);
|
||||
|
||||
kvfree(pseqs);
|
||||
}
|
||||
|
||||
static void free_kick_pseqs(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct scx_kick_pseqs **pseqs = per_cpu_ptr(&scx_kick_pseqs, cpu);
|
||||
struct scx_kick_pseqs *to_free;
|
||||
|
||||
to_free = rcu_replace_pointer(*pseqs, NULL, true);
|
||||
if (to_free)
|
||||
call_rcu(&to_free->rcu, free_kick_pseqs_rcu);
|
||||
}
|
||||
}
|
||||
|
||||
static void scx_disable_workfn(struct kthread_work *work)
|
||||
{
|
||||
struct scx_sched *sch = container_of(work, struct scx_sched, disable_work);
|
||||
@@ -3986,6 +4045,7 @@ static void scx_disable_workfn(struct kthread_work *work)
|
||||
free_percpu(scx_dsp_ctx);
|
||||
scx_dsp_ctx = NULL;
|
||||
scx_dsp_max_batch = 0;
|
||||
free_kick_pseqs();
|
||||
|
||||
mutex_unlock(&scx_enable_mutex);
|
||||
|
||||
@@ -4348,6 +4408,33 @@ static void scx_vexit(struct scx_sched *sch,
|
||||
irq_work_queue(&sch->error_irq_work);
|
||||
}
|
||||
|
||||
static int alloc_kick_pseqs(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* Allocate per-CPU arrays sized by nr_cpu_ids. Use kvzalloc as size
|
||||
* can exceed percpu allocator limits on large machines.
|
||||
*/
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct scx_kick_pseqs **pseqs = per_cpu_ptr(&scx_kick_pseqs, cpu);
|
||||
struct scx_kick_pseqs *new_pseqs;
|
||||
|
||||
WARN_ON_ONCE(rcu_access_pointer(*pseqs));
|
||||
|
||||
new_pseqs = kvzalloc_node(struct_size(new_pseqs, seqs, nr_cpu_ids),
|
||||
GFP_KERNEL, cpu_to_node(cpu));
|
||||
if (!new_pseqs) {
|
||||
free_kick_pseqs();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(*pseqs, new_pseqs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct scx_sched *scx_alloc_and_add_sched(struct sched_ext_ops *ops)
|
||||
{
|
||||
struct scx_sched *sch;
|
||||
@@ -4495,10 +4582,14 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
ret = alloc_kick_pseqs();
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
sch = scx_alloc_and_add_sched(ops);
|
||||
if (IS_ERR(sch)) {
|
||||
ret = PTR_ERR(sch);
|
||||
goto err_unlock;
|
||||
goto err_free_pseqs;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4701,6 +4792,8 @@ static int scx_enable(struct sched_ext_ops *ops, struct bpf_link *link)
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_pseqs:
|
||||
free_kick_pseqs();
|
||||
err_unlock:
|
||||
mutex_unlock(&scx_enable_mutex);
|
||||
return ret;
|
||||
@@ -5082,10 +5175,18 @@ static void kick_cpus_irq_workfn(struct irq_work *irq_work)
|
||||
{
|
||||
struct rq *this_rq = this_rq();
|
||||
struct scx_rq *this_scx = &this_rq->scx;
|
||||
unsigned long *pseqs = this_cpu_ptr(scx_kick_cpus_pnt_seqs);
|
||||
struct scx_kick_pseqs __rcu *pseqs_pcpu = __this_cpu_read(scx_kick_pseqs);
|
||||
bool should_wait = false;
|
||||
unsigned long *pseqs;
|
||||
s32 cpu;
|
||||
|
||||
if (unlikely(!pseqs_pcpu)) {
|
||||
pr_warn_once("kick_cpus_irq_workfn() called with NULL scx_kick_pseqs");
|
||||
return;
|
||||
}
|
||||
|
||||
pseqs = rcu_dereference_bh(pseqs_pcpu)->seqs;
|
||||
|
||||
for_each_cpu(cpu, this_scx->cpus_to_kick) {
|
||||
should_wait |= kick_one_cpu(cpu, this_rq, pseqs);
|
||||
cpumask_clear_cpu(cpu, this_scx->cpus_to_kick);
|
||||
@@ -5208,11 +5309,6 @@ void __init init_sched_ext_class(void)
|
||||
|
||||
scx_idle_init_masks();
|
||||
|
||||
scx_kick_cpus_pnt_seqs =
|
||||
__alloc_percpu(sizeof(scx_kick_cpus_pnt_seqs[0]) * nr_cpu_ids,
|
||||
__alignof__(scx_kick_cpus_pnt_seqs[0]));
|
||||
BUG_ON(!scx_kick_cpus_pnt_seqs);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct rq *rq = cpu_rq(cpu);
|
||||
int n = cpu_to_node(cpu);
|
||||
@@ -5688,8 +5784,8 @@ BTF_KFUNCS_START(scx_kfunc_ids_dispatch)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dispatch_nr_slots)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dispatch_cancel)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_to_local)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_slice)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_vtime)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_slice, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_vtime, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_vtime, KF_RCU)
|
||||
BTF_KFUNCS_END(scx_kfunc_ids_dispatch)
|
||||
@@ -5820,8 +5916,8 @@ __bpf_kfunc_end_defs();
|
||||
|
||||
BTF_KFUNCS_START(scx_kfunc_ids_unlocked)
|
||||
BTF_ID_FLAGS(func, scx_bpf_create_dsq, KF_SLEEPABLE)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_slice)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_vtime)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_slice, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_set_vtime, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move, KF_RCU)
|
||||
BTF_ID_FLAGS(func, scx_bpf_dsq_move_vtime, KF_RCU)
|
||||
BTF_KFUNCS_END(scx_kfunc_ids_unlocked)
|
||||
|
||||
@@ -6024,20 +6024,17 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
|
||||
struct sched_entity *se = cfs_rq->tg->se[cpu_of(rq)];
|
||||
|
||||
/*
|
||||
* It's possible we are called with !runtime_remaining due to things
|
||||
* like user changed quota setting(see tg_set_cfs_bandwidth()) or async
|
||||
* unthrottled us with a positive runtime_remaining but other still
|
||||
* running entities consumed those runtime before we reached here.
|
||||
* It's possible we are called with runtime_remaining < 0 due to things
|
||||
* like async unthrottled us with a positive runtime_remaining but other
|
||||
* still running entities consumed those runtime before we reached here.
|
||||
*
|
||||
* Anyway, we can't unthrottle this cfs_rq without any runtime remaining
|
||||
* because any enqueue in tg_unthrottle_up() will immediately trigger a
|
||||
* throttle, which is not supposed to happen on unthrottle path.
|
||||
* We can't unthrottle this cfs_rq without any runtime remaining because
|
||||
* any enqueue in tg_unthrottle_up() will immediately trigger a throttle,
|
||||
* which is not supposed to happen on unthrottle path.
|
||||
*/
|
||||
if (cfs_rq->runtime_enabled && cfs_rq->runtime_remaining <= 0)
|
||||
return;
|
||||
|
||||
se = cfs_rq->tg->se[cpu_of(rq)];
|
||||
|
||||
cfs_rq->throttled = 0;
|
||||
|
||||
update_rq_clock(rq);
|
||||
@@ -6437,6 +6434,16 @@ static void sync_throttle(struct task_group *tg, int cpu)
|
||||
|
||||
cfs_rq->throttle_count = pcfs_rq->throttle_count;
|
||||
cfs_rq->throttled_clock_pelt = rq_clock_pelt(cpu_rq(cpu));
|
||||
|
||||
/*
|
||||
* It is not enough to sync the "pelt_clock_throttled" indicator
|
||||
* with the parent cfs_rq when the hierarchy is not queued.
|
||||
* Always join a throttled hierarchy with PELT clock throttled
|
||||
* and leaf it to the first enqueue, or distribution to
|
||||
* unthrottle the PELT clock.
|
||||
*/
|
||||
if (cfs_rq->throttle_count)
|
||||
cfs_rq->pelt_clock_throttled = 1;
|
||||
}
|
||||
|
||||
/* conditionally throttle active cfs_rq's from put_prev_entity() */
|
||||
@@ -13187,6 +13194,8 @@ static void propagate_entity_cfs_rq(struct sched_entity *se)
|
||||
if (!cfs_rq_pelt_clock_throttled(cfs_rq))
|
||||
list_add_leaf_cfs_rq(cfs_rq);
|
||||
}
|
||||
|
||||
assert_list_leaf_cfs_rq(rq_of(cfs_rq));
|
||||
}
|
||||
#else /* !CONFIG_FAIR_GROUP_SCHED: */
|
||||
static void propagate_entity_cfs_rq(struct sched_entity *se) { }
|
||||
|
||||
@@ -784,6 +784,7 @@ enum scx_rq_flags {
|
||||
SCX_RQ_BAL_KEEP = 1 << 3, /* balance decided to keep current */
|
||||
SCX_RQ_BYPASSING = 1 << 4,
|
||||
SCX_RQ_CLK_VALID = 1 << 5, /* RQ clock is fresh and valid */
|
||||
SCX_RQ_BAL_CB_PENDING = 1 << 6, /* must queue a cb after dispatching */
|
||||
|
||||
SCX_RQ_IN_WAKEUP = 1 << 16,
|
||||
SCX_RQ_IN_BALANCE = 1 << 17,
|
||||
@@ -3740,11 +3741,9 @@ static inline int mm_cid_get(struct rq *rq, struct task_struct *t,
|
||||
struct mm_struct *mm)
|
||||
{
|
||||
struct mm_cid __percpu *pcpu_cid = mm->pcpu_cid;
|
||||
struct cpumask *cpumask;
|
||||
int cid;
|
||||
|
||||
lockdep_assert_rq_held(rq);
|
||||
cpumask = mm_cidmask(mm);
|
||||
cid = __this_cpu_read(pcpu_cid->cid);
|
||||
if (mm_cid_is_valid(cid)) {
|
||||
mm_cid_snapshot_time(rq, mm);
|
||||
|
||||
@@ -475,12 +475,6 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
|
||||
if (!kc->timer_create)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
new_timer = alloc_posix_timer();
|
||||
if (unlikely(!new_timer))
|
||||
return -EAGAIN;
|
||||
|
||||
spin_lock_init(&new_timer->it_lock);
|
||||
|
||||
/* Special case for CRIU to restore timers with a given timer ID. */
|
||||
if (unlikely(current->signal->timer_create_restore_ids)) {
|
||||
if (copy_from_user(&req_id, created_timer_id, sizeof(req_id)))
|
||||
@@ -490,6 +484,12 @@ static int do_timer_create(clockid_t which_clock, struct sigevent *event,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
new_timer = alloc_posix_timer();
|
||||
if (unlikely(!new_timer))
|
||||
return -EAGAIN;
|
||||
|
||||
spin_lock_init(&new_timer->it_lock);
|
||||
|
||||
/*
|
||||
* Add the timer to the hash table. The timer is not yet valid
|
||||
* after insertion, but has a unique ID allocated.
|
||||
|
||||
@@ -3070,7 +3070,7 @@ static int __init tk_aux_sysfs_init(void)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= MAX_AUX_CLOCKS; i++) {
|
||||
for (int i = 0; i < MAX_AUX_CLOCKS; i++) {
|
||||
char id[2] = { [0] = '0' + i, };
|
||||
struct kobject *clk = kobject_create_and_add(id, auxo);
|
||||
|
||||
|
||||
@@ -1971,7 +1971,8 @@ static void ftrace_hash_rec_enable_modify(struct ftrace_ops *ops)
|
||||
*/
|
||||
static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
|
||||
struct ftrace_hash *old_hash,
|
||||
struct ftrace_hash *new_hash)
|
||||
struct ftrace_hash *new_hash,
|
||||
bool update_target)
|
||||
{
|
||||
struct ftrace_page *pg;
|
||||
struct dyn_ftrace *rec, *end = NULL;
|
||||
@@ -2006,10 +2007,13 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
|
||||
if (rec->flags & FTRACE_FL_DISABLED)
|
||||
continue;
|
||||
|
||||
/* We need to update only differences of filter_hash */
|
||||
/*
|
||||
* Unless we are updating the target of a direct function,
|
||||
* we only need to update differences of filter_hash
|
||||
*/
|
||||
in_old = !!ftrace_lookup_ip(old_hash, rec->ip);
|
||||
in_new = !!ftrace_lookup_ip(new_hash, rec->ip);
|
||||
if (in_old == in_new)
|
||||
if (!update_target && (in_old == in_new))
|
||||
continue;
|
||||
|
||||
if (in_new) {
|
||||
@@ -2020,7 +2024,16 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
|
||||
if (is_ipmodify)
|
||||
goto rollback;
|
||||
|
||||
FTRACE_WARN_ON(rec->flags & FTRACE_FL_DIRECT);
|
||||
/*
|
||||
* If this is called by __modify_ftrace_direct()
|
||||
* then it is only changing where the direct
|
||||
* pointer is jumping to, and the record already
|
||||
* points to a direct trampoline. If it isn't,
|
||||
* then it is a bug to update ipmodify on a direct
|
||||
* caller.
|
||||
*/
|
||||
FTRACE_WARN_ON(!update_target &&
|
||||
(rec->flags & FTRACE_FL_DIRECT));
|
||||
|
||||
/*
|
||||
* Another ops with IPMODIFY is already
|
||||
@@ -2076,7 +2089,7 @@ static int ftrace_hash_ipmodify_enable(struct ftrace_ops *ops)
|
||||
if (ftrace_hash_empty(hash))
|
||||
hash = NULL;
|
||||
|
||||
return __ftrace_hash_update_ipmodify(ops, EMPTY_HASH, hash);
|
||||
return __ftrace_hash_update_ipmodify(ops, EMPTY_HASH, hash, false);
|
||||
}
|
||||
|
||||
/* Disabling always succeeds */
|
||||
@@ -2087,7 +2100,7 @@ static void ftrace_hash_ipmodify_disable(struct ftrace_ops *ops)
|
||||
if (ftrace_hash_empty(hash))
|
||||
hash = NULL;
|
||||
|
||||
__ftrace_hash_update_ipmodify(ops, hash, EMPTY_HASH);
|
||||
__ftrace_hash_update_ipmodify(ops, hash, EMPTY_HASH, false);
|
||||
}
|
||||
|
||||
static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
|
||||
@@ -2101,7 +2114,7 @@ static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
|
||||
if (ftrace_hash_empty(new_hash))
|
||||
new_hash = NULL;
|
||||
|
||||
return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash);
|
||||
return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash, false);
|
||||
}
|
||||
|
||||
static void print_ip_ins(const char *fmt, const unsigned char *p)
|
||||
@@ -5953,6 +5966,17 @@ static void register_ftrace_direct_cb(struct rcu_head *rhp)
|
||||
free_ftrace_hash(fhp);
|
||||
}
|
||||
|
||||
static void reset_direct(struct ftrace_ops *ops, unsigned long addr)
|
||||
{
|
||||
struct ftrace_hash *hash = ops->func_hash->filter_hash;
|
||||
|
||||
remove_direct_functions_hash(hash, addr);
|
||||
|
||||
/* cleanup for possible another register call */
|
||||
ops->func = NULL;
|
||||
ops->trampoline = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* register_ftrace_direct - Call a custom trampoline directly
|
||||
* for multiple functions registered in @ops
|
||||
@@ -6048,6 +6072,8 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
|
||||
ops->direct_call = addr;
|
||||
|
||||
err = register_ftrace_function_nolock(ops);
|
||||
if (err)
|
||||
reset_direct(ops, addr);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&direct_mutex);
|
||||
@@ -6080,7 +6106,6 @@ EXPORT_SYMBOL_GPL(register_ftrace_direct);
|
||||
int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
|
||||
bool free_filters)
|
||||
{
|
||||
struct ftrace_hash *hash = ops->func_hash->filter_hash;
|
||||
int err;
|
||||
|
||||
if (check_direct_multi(ops))
|
||||
@@ -6090,13 +6115,9 @@ int unregister_ftrace_direct(struct ftrace_ops *ops, unsigned long addr,
|
||||
|
||||
mutex_lock(&direct_mutex);
|
||||
err = unregister_ftrace_function(ops);
|
||||
remove_direct_functions_hash(hash, addr);
|
||||
reset_direct(ops, addr);
|
||||
mutex_unlock(&direct_mutex);
|
||||
|
||||
/* cleanup for possible another register call */
|
||||
ops->func = NULL;
|
||||
ops->trampoline = 0;
|
||||
|
||||
if (free_filters)
|
||||
ftrace_free_filter(ops);
|
||||
return err;
|
||||
@@ -6106,7 +6127,7 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_direct);
|
||||
static int
|
||||
__modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
|
||||
{
|
||||
struct ftrace_hash *hash;
|
||||
struct ftrace_hash *hash = ops->func_hash->filter_hash;
|
||||
struct ftrace_func_entry *entry, *iter;
|
||||
static struct ftrace_ops tmp_ops = {
|
||||
.func = ftrace_stub,
|
||||
@@ -6126,13 +6147,21 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Call __ftrace_hash_update_ipmodify() here, so that we can call
|
||||
* ops->ops_func for the ops. This is needed because the above
|
||||
* register_ftrace_function_nolock() worked on tmp_ops.
|
||||
*/
|
||||
err = __ftrace_hash_update_ipmodify(ops, hash, hash, true);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Now the ftrace_ops_list_func() is called to do the direct callers.
|
||||
* We can safely change the direct functions attached to each entry.
|
||||
*/
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
hash = ops->func_hash->filter_hash;
|
||||
size = 1 << hash->size_bits;
|
||||
for (i = 0; i < size; i++) {
|
||||
hlist_for_each_entry(iter, &hash->buckets[i], hlist) {
|
||||
@@ -6147,6 +6176,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
|
||||
|
||||
mutex_unlock(&ftrace_lock);
|
||||
|
||||
out:
|
||||
/* Removing the tmp_ops will add the updated direct callers to the functions */
|
||||
unregister_ftrace_function(&tmp_ops);
|
||||
|
||||
|
||||
@@ -7344,6 +7344,10 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Did the reader catch up with the writer? */
|
||||
if (cpu_buffer->reader_page == cpu_buffer->commit_page)
|
||||
goto out;
|
||||
|
||||
reader = rb_get_reader_page(cpu_buffer);
|
||||
if (WARN_ON(!reader))
|
||||
goto out;
|
||||
|
||||
@@ -5,6 +5,7 @@ config RV_MON_PAGEFAULT
|
||||
select RV_LTL_MONITOR
|
||||
depends on RV_MON_RTAPP
|
||||
depends on X86 || RISCV
|
||||
depends on MMU
|
||||
default y
|
||||
select LTL_MON_EVENTS_ID
|
||||
bool "pagefault monitor"
|
||||
|
||||
@@ -501,7 +501,7 @@ static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos)
|
||||
|
||||
list_for_each_entry_continue(mon, &rv_monitors_list, list) {
|
||||
if (mon->enabled)
|
||||
return mon;
|
||||
return &mon->list;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -509,7 +509,7 @@ static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos)
|
||||
|
||||
static void *enabled_monitors_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
struct rv_monitor *mon;
|
||||
struct list_head *head;
|
||||
loff_t l;
|
||||
|
||||
mutex_lock(&rv_interface_lock);
|
||||
@@ -517,15 +517,15 @@ static void *enabled_monitors_start(struct seq_file *m, loff_t *pos)
|
||||
if (list_empty(&rv_monitors_list))
|
||||
return NULL;
|
||||
|
||||
mon = list_entry(&rv_monitors_list, struct rv_monitor, list);
|
||||
head = &rv_monitors_list;
|
||||
|
||||
for (l = 0; l <= *pos; ) {
|
||||
mon = enabled_monitors_next(m, mon, &l);
|
||||
if (!mon)
|
||||
head = enabled_monitors_next(m, head, &l);
|
||||
if (!head)
|
||||
break;
|
||||
}
|
||||
|
||||
return mon;
|
||||
return head;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -3272,14 +3272,16 @@ static struct field_var *create_field_var(struct hist_trigger_data *hist_data,
|
||||
var = create_var(hist_data, file, field_name, val->size, val->type);
|
||||
if (IS_ERR(var)) {
|
||||
hist_err(tr, HIST_ERR_VAR_CREATE_FIND_FAIL, errpos(field_name));
|
||||
kfree(val);
|
||||
destroy_hist_field(val, 0);
|
||||
ret = PTR_ERR(var);
|
||||
goto err;
|
||||
}
|
||||
|
||||
field_var = kzalloc(sizeof(struct field_var), GFP_KERNEL);
|
||||
if (!field_var) {
|
||||
kfree(val);
|
||||
destroy_hist_field(val, 0);
|
||||
kfree_const(var->type);
|
||||
kfree(var->var.name);
|
||||
kfree(var);
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
||||
@@ -106,13 +106,14 @@ static struct tracepoint_user *__tracepoint_user_init(const char *name, struct t
|
||||
if (!tuser->name)
|
||||
return NULL;
|
||||
|
||||
/* Register tracepoint if it is loaded. */
|
||||
if (tpoint) {
|
||||
tuser->tpoint = tpoint;
|
||||
ret = tracepoint_user_register(tuser);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
tuser->tpoint = tpoint;
|
||||
tuser->refcount = 1;
|
||||
INIT_LIST_HEAD(&tuser->list);
|
||||
list_add(&tuser->list, &tracepoint_user_list);
|
||||
@@ -1513,6 +1514,10 @@ static int disable_trace_fprobe(struct trace_event_call *call,
|
||||
if (!trace_probe_is_enabled(tp)) {
|
||||
list_for_each_entry(tf, trace_probe_probe_list(tp), tp.list) {
|
||||
unregister_fprobe(&tf->fp);
|
||||
if (tf->tuser) {
|
||||
tracepoint_user_put(tf->tuser);
|
||||
tf->tuser = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user