Merge branch 'linus' into sched/urgent, to pick up fixes and updates
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -488,61 +488,6 @@ get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_compat_sigset);
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
COMPAT_SYSCALL_DEFINE6(move_pages, pid_t, pid, compat_ulong_t, nr_pages,
|
||||
compat_uptr_t __user *, pages32,
|
||||
const int __user *, nodes,
|
||||
int __user *, status,
|
||||
int, flags)
|
||||
{
|
||||
const void __user * __user *pages;
|
||||
int i;
|
||||
|
||||
pages = compat_alloc_user_space(nr_pages * sizeof(void *));
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
compat_uptr_t p;
|
||||
|
||||
if (get_user(p, pages32 + i) ||
|
||||
put_user(compat_ptr(p), pages + i))
|
||||
return -EFAULT;
|
||||
}
|
||||
return sys_move_pages(pid, nr_pages, pages, nodes, status, flags);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(migrate_pages, compat_pid_t, pid,
|
||||
compat_ulong_t, maxnode,
|
||||
const compat_ulong_t __user *, old_nodes,
|
||||
const compat_ulong_t __user *, new_nodes)
|
||||
{
|
||||
unsigned long __user *old = NULL;
|
||||
unsigned long __user *new = NULL;
|
||||
nodemask_t tmp_mask;
|
||||
unsigned long nr_bits;
|
||||
unsigned long size;
|
||||
|
||||
nr_bits = min_t(unsigned long, maxnode - 1, MAX_NUMNODES);
|
||||
size = ALIGN(nr_bits, BITS_PER_LONG) / 8;
|
||||
if (old_nodes) {
|
||||
if (compat_get_bitmap(nodes_addr(tmp_mask), old_nodes, nr_bits))
|
||||
return -EFAULT;
|
||||
old = compat_alloc_user_space(new_nodes ? size * 2 : size);
|
||||
if (new_nodes)
|
||||
new = old + size / sizeof(unsigned long);
|
||||
if (copy_to_user(old, nodes_addr(tmp_mask), size))
|
||||
return -EFAULT;
|
||||
}
|
||||
if (new_nodes) {
|
||||
if (compat_get_bitmap(nodes_addr(tmp_mask), new_nodes, nr_bits))
|
||||
return -EFAULT;
|
||||
if (new == NULL)
|
||||
new = compat_alloc_user_space(size);
|
||||
if (copy_to_user(new, nodes_addr(tmp_mask), size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return sys_migrate_pages(pid, nr_bits + 1, old, new);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocate user-space memory for the duration of a single system call,
|
||||
* in order to marshall parameters inside a compat thunk.
|
||||
|
||||
+24
-36
@@ -124,24 +124,11 @@ struct cpuhp_step {
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(cpuhp_state_mutex);
|
||||
static struct cpuhp_step cpuhp_bp_states[];
|
||||
static struct cpuhp_step cpuhp_ap_states[];
|
||||
|
||||
static bool cpuhp_is_ap_state(enum cpuhp_state state)
|
||||
{
|
||||
/*
|
||||
* The extra check for CPUHP_TEARDOWN_CPU is only for documentation
|
||||
* purposes as that state is handled explicitly in cpu_down.
|
||||
*/
|
||||
return state > CPUHP_BRINGUP_CPU && state != CPUHP_TEARDOWN_CPU;
|
||||
}
|
||||
static struct cpuhp_step cpuhp_hp_states[];
|
||||
|
||||
static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state)
|
||||
{
|
||||
struct cpuhp_step *sp;
|
||||
|
||||
sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states;
|
||||
return sp + state;
|
||||
return cpuhp_hp_states + state;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,6 +226,15 @@ err:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static bool cpuhp_is_ap_state(enum cpuhp_state state)
|
||||
{
|
||||
/*
|
||||
* The extra check for CPUHP_TEARDOWN_CPU is only for documentation
|
||||
* purposes as that state is handled explicitly in cpu_down.
|
||||
*/
|
||||
return state > CPUHP_BRINGUP_CPU && state != CPUHP_TEARDOWN_CPU;
|
||||
}
|
||||
|
||||
static inline void wait_for_ap_thread(struct cpuhp_cpu_state *st, bool bringup)
|
||||
{
|
||||
struct completion *done = bringup ? &st->done_up : &st->done_down;
|
||||
@@ -1224,7 +1220,7 @@ int __boot_cpu_id;
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/* Boot processor state steps */
|
||||
static struct cpuhp_step cpuhp_bp_states[] = {
|
||||
static struct cpuhp_step cpuhp_hp_states[] = {
|
||||
[CPUHP_OFFLINE] = {
|
||||
.name = "offline",
|
||||
.startup.single = NULL,
|
||||
@@ -1289,24 +1285,6 @@ static struct cpuhp_step cpuhp_bp_states[] = {
|
||||
.teardown.single = NULL,
|
||||
.cant_stop = true,
|
||||
},
|
||||
/*
|
||||
* Handled on controll processor until the plugged processor manages
|
||||
* this itself.
|
||||
*/
|
||||
[CPUHP_TEARDOWN_CPU] = {
|
||||
.name = "cpu:teardown",
|
||||
.startup.single = NULL,
|
||||
.teardown.single = takedown_cpu,
|
||||
.cant_stop = true,
|
||||
},
|
||||
#else
|
||||
[CPUHP_BRINGUP_CPU] = { },
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Application processor state steps */
|
||||
static struct cpuhp_step cpuhp_ap_states[] = {
|
||||
#ifdef CONFIG_SMP
|
||||
/* Final state before CPU kills itself */
|
||||
[CPUHP_AP_IDLE_DEAD] = {
|
||||
.name = "idle:dead",
|
||||
@@ -1340,6 +1318,16 @@ static struct cpuhp_step cpuhp_ap_states[] = {
|
||||
[CPUHP_AP_ONLINE] = {
|
||||
.name = "ap:online",
|
||||
},
|
||||
/*
|
||||
* Handled on controll processor until the plugged processor manages
|
||||
* this itself.
|
||||
*/
|
||||
[CPUHP_TEARDOWN_CPU] = {
|
||||
.name = "cpu:teardown",
|
||||
.startup.single = NULL,
|
||||
.teardown.single = takedown_cpu,
|
||||
.cant_stop = true,
|
||||
},
|
||||
/* Handle smpboot threads park/unpark */
|
||||
[CPUHP_AP_SMPBOOT_THREADS] = {
|
||||
.name = "smpboot/threads:online",
|
||||
@@ -1408,11 +1396,11 @@ static int cpuhp_reserve_state(enum cpuhp_state state)
|
||||
|
||||
switch (state) {
|
||||
case CPUHP_AP_ONLINE_DYN:
|
||||
step = cpuhp_ap_states + CPUHP_AP_ONLINE_DYN;
|
||||
step = cpuhp_hp_states + CPUHP_AP_ONLINE_DYN;
|
||||
end = CPUHP_AP_ONLINE_DYN_END;
|
||||
break;
|
||||
case CPUHP_BP_PREPARE_DYN:
|
||||
step = cpuhp_bp_states + CPUHP_BP_PREPARE_DYN;
|
||||
step = cpuhp_hp_states + CPUHP_BP_PREPARE_DYN;
|
||||
end = CPUHP_BP_PREPARE_DYN_END;
|
||||
break;
|
||||
default:
|
||||
|
||||
+623
-158
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include <linux/hw_breakpoint.h>
|
||||
/*
|
||||
@@ -85,9 +86,9 @@ __weak int hw_breakpoint_weight(struct perf_event *bp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
|
||||
static inline enum bp_type_idx find_slot_idx(u64 bp_type)
|
||||
{
|
||||
if (bp->attr.bp_type & HW_BREAKPOINT_RW)
|
||||
if (bp_type & HW_BREAKPOINT_RW)
|
||||
return TYPE_DATA;
|
||||
|
||||
return TYPE_INST;
|
||||
@@ -122,7 +123,7 @@ static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type)
|
||||
|
||||
list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
|
||||
if (iter->hw.target == tsk &&
|
||||
find_slot_idx(iter) == type &&
|
||||
find_slot_idx(iter->attr.bp_type) == type &&
|
||||
(iter->cpu < 0 || cpu == iter->cpu))
|
||||
count += hw_breakpoint_weight(iter);
|
||||
}
|
||||
@@ -277,7 +278,7 @@ __weak void arch_unregister_hw_breakpoint(struct perf_event *bp)
|
||||
* ((per_cpu(info->flexible, *) > 1) + max(per_cpu(info->cpu_pinned, *))
|
||||
* + max(per_cpu(info->tsk_pinned, *))) < HBP_NUM
|
||||
*/
|
||||
static int __reserve_bp_slot(struct perf_event *bp)
|
||||
static int __reserve_bp_slot(struct perf_event *bp, u64 bp_type)
|
||||
{
|
||||
struct bp_busy_slots slots = {0};
|
||||
enum bp_type_idx type;
|
||||
@@ -288,11 +289,11 @@ static int __reserve_bp_slot(struct perf_event *bp)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Basic checks */
|
||||
if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
|
||||
bp->attr.bp_type == HW_BREAKPOINT_INVALID)
|
||||
if (bp_type == HW_BREAKPOINT_EMPTY ||
|
||||
bp_type == HW_BREAKPOINT_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
type = find_slot_idx(bp);
|
||||
type = find_slot_idx(bp_type);
|
||||
weight = hw_breakpoint_weight(bp);
|
||||
|
||||
fetch_bp_busy_slots(&slots, bp, type);
|
||||
@@ -317,19 +318,19 @@ int reserve_bp_slot(struct perf_event *bp)
|
||||
|
||||
mutex_lock(&nr_bp_mutex);
|
||||
|
||||
ret = __reserve_bp_slot(bp);
|
||||
ret = __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||
|
||||
mutex_unlock(&nr_bp_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __release_bp_slot(struct perf_event *bp)
|
||||
static void __release_bp_slot(struct perf_event *bp, u64 bp_type)
|
||||
{
|
||||
enum bp_type_idx type;
|
||||
int weight;
|
||||
|
||||
type = find_slot_idx(bp);
|
||||
type = find_slot_idx(bp_type);
|
||||
weight = hw_breakpoint_weight(bp);
|
||||
toggle_bp_slot(bp, false, type, weight);
|
||||
}
|
||||
@@ -339,11 +340,43 @@ void release_bp_slot(struct perf_event *bp)
|
||||
mutex_lock(&nr_bp_mutex);
|
||||
|
||||
arch_unregister_hw_breakpoint(bp);
|
||||
__release_bp_slot(bp);
|
||||
__release_bp_slot(bp, bp->attr.bp_type);
|
||||
|
||||
mutex_unlock(&nr_bp_mutex);
|
||||
}
|
||||
|
||||
static int __modify_bp_slot(struct perf_event *bp, u64 old_type)
|
||||
{
|
||||
int err;
|
||||
|
||||
__release_bp_slot(bp, old_type);
|
||||
|
||||
err = __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||
if (err) {
|
||||
/*
|
||||
* Reserve the old_type slot back in case
|
||||
* there's no space for the new type.
|
||||
*
|
||||
* This must succeed, because we just released
|
||||
* the old_type slot in the __release_bp_slot
|
||||
* call above. If not, something is broken.
|
||||
*/
|
||||
WARN_ON(__reserve_bp_slot(bp, old_type));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int modify_bp_slot(struct perf_event *bp, u64 old_type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&nr_bp_mutex);
|
||||
ret = __modify_bp_slot(bp, old_type);
|
||||
mutex_unlock(&nr_bp_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the kernel debugger to reserve breakpoint slots without
|
||||
* taking a lock using the dbg_* variant of for the reserve and
|
||||
@@ -354,7 +387,7 @@ int dbg_reserve_bp_slot(struct perf_event *bp)
|
||||
if (mutex_is_locked(&nr_bp_mutex))
|
||||
return -1;
|
||||
|
||||
return __reserve_bp_slot(bp);
|
||||
return __reserve_bp_slot(bp, bp->attr.bp_type);
|
||||
}
|
||||
|
||||
int dbg_release_bp_slot(struct perf_event *bp)
|
||||
@@ -362,7 +395,7 @@ int dbg_release_bp_slot(struct perf_event *bp)
|
||||
if (mutex_is_locked(&nr_bp_mutex))
|
||||
return -1;
|
||||
|
||||
__release_bp_slot(bp);
|
||||
__release_bp_slot(bp, bp->attr.bp_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -423,20 +456,45 @@ register_user_hw_breakpoint(struct perf_event_attr *attr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
|
||||
|
||||
int
|
||||
modify_user_hw_breakpoint_check(struct perf_event *bp, struct perf_event_attr *attr,
|
||||
bool check)
|
||||
{
|
||||
u64 old_addr = bp->attr.bp_addr;
|
||||
u64 old_len = bp->attr.bp_len;
|
||||
int old_type = bp->attr.bp_type;
|
||||
bool modify = attr->bp_type != old_type;
|
||||
int err = 0;
|
||||
|
||||
bp->attr.bp_addr = attr->bp_addr;
|
||||
bp->attr.bp_type = attr->bp_type;
|
||||
bp->attr.bp_len = attr->bp_len;
|
||||
|
||||
if (check && memcmp(&bp->attr, attr, sizeof(*attr)))
|
||||
return -EINVAL;
|
||||
|
||||
err = validate_hw_breakpoint(bp);
|
||||
if (!err && modify)
|
||||
err = modify_bp_slot(bp, old_type);
|
||||
|
||||
if (err) {
|
||||
bp->attr.bp_addr = old_addr;
|
||||
bp->attr.bp_type = old_type;
|
||||
bp->attr.bp_len = old_len;
|
||||
return err;
|
||||
}
|
||||
|
||||
bp->attr.disabled = attr->disabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* modify_user_hw_breakpoint - modify a user-space hardware breakpoint
|
||||
* @bp: the breakpoint structure to modify
|
||||
* @attr: new breakpoint attributes
|
||||
* @triggered: callback to trigger when we hit the breakpoint
|
||||
* @tsk: pointer to 'task_struct' of the process to which the address belongs
|
||||
*/
|
||||
int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
|
||||
{
|
||||
u64 old_addr = bp->attr.bp_addr;
|
||||
u64 old_len = bp->attr.bp_len;
|
||||
int old_type = bp->attr.bp_type;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
|
||||
* will not be possible to raise IPIs that invoke __perf_event_disable.
|
||||
@@ -448,30 +506,14 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
|
||||
else
|
||||
perf_event_disable(bp);
|
||||
|
||||
bp->attr.bp_addr = attr->bp_addr;
|
||||
bp->attr.bp_type = attr->bp_type;
|
||||
bp->attr.bp_len = attr->bp_len;
|
||||
if (!attr->disabled) {
|
||||
int err = modify_user_hw_breakpoint_check(bp, attr, false);
|
||||
|
||||
if (attr->disabled)
|
||||
goto end;
|
||||
|
||||
err = validate_hw_breakpoint(bp);
|
||||
if (!err)
|
||||
if (err)
|
||||
return err;
|
||||
perf_event_enable(bp);
|
||||
|
||||
if (err) {
|
||||
bp->attr.bp_addr = old_addr;
|
||||
bp->attr.bp_type = old_type;
|
||||
bp->attr.bp_len = old_len;
|
||||
if (!bp->attr.disabled)
|
||||
perf_event_enable(bp);
|
||||
|
||||
return err;
|
||||
bp->attr.disabled = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
bp->attr.disabled = attr->disabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
|
||||
|
||||
+1
-1
@@ -1691,7 +1691,7 @@ SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
|
||||
*/
|
||||
SYSCALL_DEFINE3(waitpid, pid_t, pid, int __user *, stat_addr, int, options)
|
||||
{
|
||||
return sys_wait4(pid, stat_addr, options, NULL);
|
||||
return kernel_wait4(pid, stat_addr, options, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+8
-3
@@ -1198,8 +1198,8 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
|
||||
* not set up a proper pointer then tough luck.
|
||||
*/
|
||||
put_user(0, tsk->clear_child_tid);
|
||||
sys_futex(tsk->clear_child_tid, FUTEX_WAKE,
|
||||
1, NULL, NULL, 0);
|
||||
do_futex(tsk->clear_child_tid, FUTEX_WAKE,
|
||||
1, NULL, NULL, 0, 0);
|
||||
}
|
||||
tsk->clear_child_tid = NULL;
|
||||
}
|
||||
@@ -2354,7 +2354,7 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp
|
||||
* constructed. Here we are modifying the current, active,
|
||||
* task_struct.
|
||||
*/
|
||||
SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
||||
int ksys_unshare(unsigned long unshare_flags)
|
||||
{
|
||||
struct fs_struct *fs, *new_fs = NULL;
|
||||
struct files_struct *fd, *new_fd = NULL;
|
||||
@@ -2470,6 +2470,11 @@ bad_unshare_out:
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
|
||||
{
|
||||
return ksys_unshare(unshare_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to unshare the files of the current task.
|
||||
* We don't want to expose copy_files internals to
|
||||
|
||||
+39
-13
@@ -192,11 +192,9 @@ out:
|
||||
* that to happen you need to do that yourself.
|
||||
*/
|
||||
|
||||
SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
|
||||
struct kexec_segment __user *, segments, unsigned long, flags)
|
||||
static inline int kexec_load_check(unsigned long nr_segments,
|
||||
unsigned long flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* We only trust the superuser with rebooting the system. */
|
||||
if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)
|
||||
return -EPERM;
|
||||
@@ -208,17 +206,29 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
|
||||
if ((flags & KEXEC_FLAGS) != (flags & ~KEXEC_ARCH_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
/* Verify we are on the appropriate architecture */
|
||||
if (((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH) &&
|
||||
((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH_DEFAULT))
|
||||
return -EINVAL;
|
||||
|
||||
/* Put an artificial cap on the number
|
||||
* of segments passed to kexec_load.
|
||||
*/
|
||||
if (nr_segments > KEXEC_SEGMENT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
|
||||
struct kexec_segment __user *, segments, unsigned long, flags)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = kexec_load_check(nr_segments, flags);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Verify we are on the appropriate architecture */
|
||||
if (((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH) &&
|
||||
((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH_DEFAULT))
|
||||
return -EINVAL;
|
||||
|
||||
/* Because we write directly to the reserved memory
|
||||
* region when loading crash kernels we need a mutex here to
|
||||
* prevent multiple crash kernels from attempting to load
|
||||
@@ -247,15 +257,16 @@ COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry,
|
||||
struct kexec_segment out, __user *ksegments;
|
||||
unsigned long i, result;
|
||||
|
||||
result = kexec_load_check(nr_segments, flags);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Don't allow clients that don't understand the native
|
||||
* architecture to do anything.
|
||||
*/
|
||||
if ((flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_DEFAULT)
|
||||
return -EINVAL;
|
||||
|
||||
if (nr_segments > KEXEC_SEGMENT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
ksegments = compat_alloc_user_space(nr_segments * sizeof(out));
|
||||
for (i = 0; i < nr_segments; i++) {
|
||||
result = copy_from_user(&in, &segments[i], sizeof(in));
|
||||
@@ -272,6 +283,21 @@ COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry,
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return sys_kexec_load(entry, nr_segments, ksegments, flags);
|
||||
/* Because we write directly to the reserved memory
|
||||
* region when loading crash kernels we need a mutex here to
|
||||
* prevent multiple crash kernels from attempting to load
|
||||
* simultaneously, and to prevent a crash kernel from loading
|
||||
* over the top of a in use crash kernel.
|
||||
*
|
||||
* KISS: always take the mutex.
|
||||
*/
|
||||
if (!mutex_trylock(&kexec_mutex))
|
||||
return -EBUSY;
|
||||
|
||||
result = do_kexec_load(entry, nr_segments, ksegments, flags);
|
||||
|
||||
mutex_unlock(&kexec_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
+13
-13
@@ -556,9 +556,9 @@ static void print_lock(struct held_lock *hlock)
|
||||
return;
|
||||
}
|
||||
|
||||
printk(KERN_CONT "%p", hlock->instance);
|
||||
print_lock_name(lock_classes + class_idx - 1);
|
||||
printk(KERN_CONT ", at: [<%p>] %pS\n",
|
||||
(void *)hlock->acquire_ip, (void *)hlock->acquire_ip);
|
||||
printk(KERN_CONT ", at: %pS\n", (void *)hlock->acquire_ip);
|
||||
}
|
||||
|
||||
static void lockdep_print_held_locks(struct task_struct *curr)
|
||||
@@ -808,7 +808,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
||||
if (verbose(class)) {
|
||||
graph_unlock();
|
||||
|
||||
printk("\nnew class %p: %s", class->key, class->name);
|
||||
printk("\nnew class %px: %s", class->key, class->name);
|
||||
if (class->name_version > 1)
|
||||
printk(KERN_CONT "#%d", class->name_version);
|
||||
printk(KERN_CONT "\n");
|
||||
@@ -1407,7 +1407,7 @@ static void print_lock_class_header(struct lock_class *class, int depth)
|
||||
}
|
||||
printk("%*s }\n", depth, "");
|
||||
|
||||
printk("%*s ... key at: [<%p>] %pS\n",
|
||||
printk("%*s ... key at: [<%px>] %pS\n",
|
||||
depth, "", class->key, class->key);
|
||||
}
|
||||
|
||||
@@ -2340,7 +2340,7 @@ cache_hit:
|
||||
|
||||
if (very_verbose(class)) {
|
||||
printk("\nhash chain already cached, key: "
|
||||
"%016Lx tail class: [%p] %s\n",
|
||||
"%016Lx tail class: [%px] %s\n",
|
||||
(unsigned long long)chain_key,
|
||||
class->key, class->name);
|
||||
}
|
||||
@@ -2349,7 +2349,7 @@ cache_hit:
|
||||
}
|
||||
|
||||
if (very_verbose(class)) {
|
||||
printk("\nnew hash chain, key: %016Lx tail class: [%p] %s\n",
|
||||
printk("\nnew hash chain, key: %016Lx tail class: [%px] %s\n",
|
||||
(unsigned long long)chain_key, class->key, class->name);
|
||||
}
|
||||
|
||||
@@ -2676,16 +2676,16 @@ check_usage_backwards(struct task_struct *curr, struct held_lock *this,
|
||||
void print_irqtrace_events(struct task_struct *curr)
|
||||
{
|
||||
printk("irq event stamp: %u\n", curr->irq_events);
|
||||
printk("hardirqs last enabled at (%u): [<%p>] %pS\n",
|
||||
printk("hardirqs last enabled at (%u): [<%px>] %pS\n",
|
||||
curr->hardirq_enable_event, (void *)curr->hardirq_enable_ip,
|
||||
(void *)curr->hardirq_enable_ip);
|
||||
printk("hardirqs last disabled at (%u): [<%p>] %pS\n",
|
||||
printk("hardirqs last disabled at (%u): [<%px>] %pS\n",
|
||||
curr->hardirq_disable_event, (void *)curr->hardirq_disable_ip,
|
||||
(void *)curr->hardirq_disable_ip);
|
||||
printk("softirqs last enabled at (%u): [<%p>] %pS\n",
|
||||
printk("softirqs last enabled at (%u): [<%px>] %pS\n",
|
||||
curr->softirq_enable_event, (void *)curr->softirq_enable_ip,
|
||||
(void *)curr->softirq_enable_ip);
|
||||
printk("softirqs last disabled at (%u): [<%p>] %pS\n",
|
||||
printk("softirqs last disabled at (%u): [<%px>] %pS\n",
|
||||
curr->softirq_disable_event, (void *)curr->softirq_disable_ip,
|
||||
(void *)curr->softirq_disable_ip);
|
||||
}
|
||||
@@ -3207,7 +3207,7 @@ static void __lockdep_init_map(struct lockdep_map *lock, const char *name,
|
||||
* Sanity check, the lock-class key must be persistent:
|
||||
*/
|
||||
if (!static_obj(key)) {
|
||||
printk("BUG: key %p not in .data!\n", key);
|
||||
printk("BUG: key %px not in .data!\n", key);
|
||||
/*
|
||||
* What it says above ^^^^^, I suggest you read it.
|
||||
*/
|
||||
@@ -3322,7 +3322,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
}
|
||||
atomic_inc((atomic_t *)&class->ops);
|
||||
if (very_verbose(class)) {
|
||||
printk("\nacquire class [%p] %s", class->key, class->name);
|
||||
printk("\nacquire class [%px] %s", class->key, class->name);
|
||||
if (class->name_version > 1)
|
||||
printk(KERN_CONT "#%d", class->name_version);
|
||||
printk(KERN_CONT "\n");
|
||||
@@ -4376,7 +4376,7 @@ print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
|
||||
pr_warn("WARNING: held lock freed!\n");
|
||||
print_kernel_ident();
|
||||
pr_warn("-------------------------\n");
|
||||
pr_warn("%s/%d is freeing memory %p-%p, with a lock still held there!\n",
|
||||
pr_warn("%s/%d is freeing memory %px-%px, with a lock still held there!\n",
|
||||
curr->comm, task_pid_nr(curr), mem_from, mem_to-1);
|
||||
print_lock(hlock);
|
||||
lockdep_print_held_locks(curr);
|
||||
|
||||
@@ -1268,8 +1268,7 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,
|
||||
|
||||
if (unlikely(ret)) {
|
||||
__set_current_state(TASK_RUNNING);
|
||||
if (rt_mutex_has_waiters(lock))
|
||||
remove_waiter(lock, &waiter);
|
||||
remove_waiter(lock, &waiter);
|
||||
rt_mutex_handle_deadlock(ret, chwalk, &waiter);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,12 +52,13 @@ static inline int rt_mutex_has_waiters(struct rt_mutex *lock)
|
||||
static inline struct rt_mutex_waiter *
|
||||
rt_mutex_top_waiter(struct rt_mutex *lock)
|
||||
{
|
||||
struct rt_mutex_waiter *w;
|
||||
|
||||
w = rb_entry(lock->waiters.rb_leftmost,
|
||||
struct rt_mutex_waiter, tree_entry);
|
||||
BUG_ON(w->lock != lock);
|
||||
struct rb_node *leftmost = rb_first_cached(&lock->waiters);
|
||||
struct rt_mutex_waiter *w = NULL;
|
||||
|
||||
if (leftmost) {
|
||||
w = rb_entry(leftmost, struct rt_mutex_waiter, tree_entry);
|
||||
BUG_ON(w->lock != lock);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ EXPORT_SYMBOL(down_write_trylock);
|
||||
void up_read(struct rw_semaphore *sem)
|
||||
{
|
||||
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
||||
DEBUG_RWSEMS_WARN_ON(sem->owner != RWSEM_READER_OWNED);
|
||||
|
||||
__up_read(sem);
|
||||
}
|
||||
@@ -129,6 +130,7 @@ EXPORT_SYMBOL(up_read);
|
||||
void up_write(struct rw_semaphore *sem)
|
||||
{
|
||||
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
||||
DEBUG_RWSEMS_WARN_ON(sem->owner != current);
|
||||
|
||||
rwsem_clear_owner(sem);
|
||||
__up_write(sem);
|
||||
@@ -142,6 +144,7 @@ EXPORT_SYMBOL(up_write);
|
||||
void downgrade_write(struct rw_semaphore *sem)
|
||||
{
|
||||
lock_downgrade(&sem->dep_map, _RET_IP_);
|
||||
DEBUG_RWSEMS_WARN_ON(sem->owner != current);
|
||||
|
||||
rwsem_set_reader_owned(sem);
|
||||
__downgrade_write(sem);
|
||||
@@ -211,6 +214,7 @@ EXPORT_SYMBOL(down_write_killable_nested);
|
||||
|
||||
void up_read_non_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
DEBUG_RWSEMS_WARN_ON(sem->owner != RWSEM_READER_OWNED);
|
||||
__up_read(sem);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
*/
|
||||
#define RWSEM_READER_OWNED ((struct task_struct *)1UL)
|
||||
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
# define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c)
|
||||
#else
|
||||
# define DEBUG_RWSEMS_WARN_ON(c)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
|
||||
/*
|
||||
* All writes to owner are protected by WRITE_ONCE() to make sure that
|
||||
@@ -41,7 +47,7 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
|
||||
* do a write to the rwsem cacheline when it is really necessary
|
||||
* to minimize cacheline contention.
|
||||
*/
|
||||
if (sem->owner != RWSEM_READER_OWNED)
|
||||
if (READ_ONCE(sem->owner) != RWSEM_READER_OWNED)
|
||||
WRITE_ONCE(sem->owner, RWSEM_READER_OWNED);
|
||||
}
|
||||
|
||||
|
||||
@@ -2181,10 +2181,6 @@ static void free_module(struct module *mod)
|
||||
/* Finally, free the core (containing the module structure) */
|
||||
disable_ro_nx(&mod->core_layout);
|
||||
module_memfree(mod->core_layout.base);
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
update_protections(current->mm);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *__symbol_get(const char *symbol)
|
||||
|
||||
+1
-1
@@ -289,7 +289,7 @@ void panic(const char *fmt, ...)
|
||||
disabled_wait(caller);
|
||||
}
|
||||
#endif
|
||||
pr_emerg("---[ end Kernel panic - not syncing: %s\n", buf);
|
||||
pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf);
|
||||
local_irq_enable();
|
||||
for (i = 0; ; i += PANIC_TIMER_STEP) {
|
||||
touch_softlockup_watchdog();
|
||||
|
||||
@@ -242,16 +242,16 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
|
||||
|
||||
/*
|
||||
* Reap the EXIT_ZOMBIE children we had before we ignored SIGCHLD.
|
||||
* sys_wait4() will also block until our children traced from the
|
||||
* kernel_wait4() will also block until our children traced from the
|
||||
* parent namespace are detached and become EXIT_DEAD.
|
||||
*/
|
||||
do {
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
rc = sys_wait4(-1, NULL, __WALL, NULL);
|
||||
rc = kernel_wait4(-1, NULL, __WALL, NULL);
|
||||
} while (rc != -ECHILD);
|
||||
|
||||
/*
|
||||
* sys_wait4() above can't reap the EXIT_DEAD children but we do not
|
||||
* kernel_wait4() above can't reap the EXIT_DEAD children but we do not
|
||||
* really care, we could reparent them to the global init. We could
|
||||
* exit and reap ->child_reaper even if it is not the last thread in
|
||||
* this pid_ns, free_pid(pid_allocated == 0) calls proc_cleanup_work(),
|
||||
|
||||
@@ -701,7 +701,7 @@ int hibernate(void)
|
||||
}
|
||||
|
||||
pr_info("Syncing filesystems ... \n");
|
||||
sys_sync();
|
||||
ksys_sync();
|
||||
pr_info("done.\n");
|
||||
|
||||
error = freeze_processes();
|
||||
|
||||
@@ -560,7 +560,7 @@ static int enter_state(suspend_state_t state)
|
||||
#ifndef CONFIG_SUSPEND_SKIP_SYNC
|
||||
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
|
||||
pr_info("Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
ksys_sync();
|
||||
pr_cont("done.\n");
|
||||
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
|
||||
break;
|
||||
|
||||
printk("Syncing filesystems ... ");
|
||||
sys_sync();
|
||||
ksys_sync();
|
||||
printk("done.\n");
|
||||
|
||||
error = freeze_processes();
|
||||
|
||||
+27
-11
@@ -77,12 +77,18 @@ static inline void rcu_seq_start(unsigned long *sp)
|
||||
WARN_ON_ONCE(rcu_seq_state(*sp) != 1);
|
||||
}
|
||||
|
||||
/* Compute the end-of-grace-period value for the specified sequence number. */
|
||||
static inline unsigned long rcu_seq_endval(unsigned long *sp)
|
||||
{
|
||||
return (*sp | RCU_SEQ_STATE_MASK) + 1;
|
||||
}
|
||||
|
||||
/* Adjust sequence number for end of update-side operation. */
|
||||
static inline void rcu_seq_end(unsigned long *sp)
|
||||
{
|
||||
smp_mb(); /* Ensure update-side operation before counter increment. */
|
||||
WARN_ON_ONCE(!rcu_seq_state(*sp));
|
||||
WRITE_ONCE(*sp, (*sp | RCU_SEQ_STATE_MASK) + 1);
|
||||
WRITE_ONCE(*sp, rcu_seq_endval(sp));
|
||||
}
|
||||
|
||||
/* Take a snapshot of the update side's sequence number. */
|
||||
@@ -295,9 +301,19 @@ static inline void rcu_init_levelspread(int *levelspread, const int *levelcnt)
|
||||
* Iterate over all possible CPUs in a leaf RCU node.
|
||||
*/
|
||||
#define for_each_leaf_node_possible_cpu(rnp, cpu) \
|
||||
for ((cpu) = cpumask_next(rnp->grplo - 1, cpu_possible_mask); \
|
||||
cpu <= rnp->grphi; \
|
||||
cpu = cpumask_next((cpu), cpu_possible_mask))
|
||||
for ((cpu) = cpumask_next((rnp)->grplo - 1, cpu_possible_mask); \
|
||||
(cpu) <= rnp->grphi; \
|
||||
(cpu) = cpumask_next((cpu), cpu_possible_mask))
|
||||
|
||||
/*
|
||||
* Iterate over all CPUs in a leaf RCU node's specified mask.
|
||||
*/
|
||||
#define rcu_find_next_bit(rnp, cpu, mask) \
|
||||
((rnp)->grplo + find_next_bit(&(mask), BITS_PER_LONG, (cpu)))
|
||||
#define for_each_leaf_node_cpu_mask(rnp, cpu, mask) \
|
||||
for ((cpu) = rcu_find_next_bit((rnp), 0, (mask)); \
|
||||
(cpu) <= rnp->grphi; \
|
||||
(cpu) = rcu_find_next_bit((rnp), (cpu) + 1 - (rnp->grplo), (mask)))
|
||||
|
||||
/*
|
||||
* Wrappers for the rcu_node::lock acquire and release.
|
||||
@@ -337,7 +353,7 @@ do { \
|
||||
} while (0)
|
||||
|
||||
#define raw_spin_unlock_irqrestore_rcu_node(p, flags) \
|
||||
raw_spin_unlock_irqrestore(&ACCESS_PRIVATE(p, lock), flags) \
|
||||
raw_spin_unlock_irqrestore(&ACCESS_PRIVATE(p, lock), flags)
|
||||
|
||||
#define raw_spin_trylock_rcu_node(p) \
|
||||
({ \
|
||||
@@ -348,6 +364,9 @@ do { \
|
||||
___locked; \
|
||||
})
|
||||
|
||||
#define raw_lockdep_assert_held_rcu_node(p) \
|
||||
lockdep_assert_held(&ACCESS_PRIVATE(p, lock))
|
||||
|
||||
#endif /* #if defined(SRCU) || !defined(TINY_RCU) */
|
||||
|
||||
#ifdef CONFIG_TINY_RCU
|
||||
@@ -356,24 +375,20 @@ static inline bool rcu_gp_is_normal(void) { return true; }
|
||||
static inline bool rcu_gp_is_expedited(void) { return false; }
|
||||
static inline void rcu_expedite_gp(void) { }
|
||||
static inline void rcu_unexpedite_gp(void) { }
|
||||
static inline void rcu_request_urgent_qs_task(struct task_struct *t) { }
|
||||
#else /* #ifdef CONFIG_TINY_RCU */
|
||||
bool rcu_gp_is_normal(void); /* Internal RCU use. */
|
||||
bool rcu_gp_is_expedited(void); /* Internal RCU use. */
|
||||
void rcu_expedite_gp(void);
|
||||
void rcu_unexpedite_gp(void);
|
||||
void rcupdate_announce_bootup_oddness(void);
|
||||
void rcu_request_urgent_qs_task(struct task_struct *t);
|
||||
#endif /* #else #ifdef CONFIG_TINY_RCU */
|
||||
|
||||
#define RCU_SCHEDULER_INACTIVE 0
|
||||
#define RCU_SCHEDULER_INIT 1
|
||||
#define RCU_SCHEDULER_RUNNING 2
|
||||
|
||||
#ifdef CONFIG_TINY_RCU
|
||||
static inline void rcu_request_urgent_qs_task(struct task_struct *t) { }
|
||||
#else /* #ifdef CONFIG_TINY_RCU */
|
||||
void rcu_request_urgent_qs_task(struct task_struct *t);
|
||||
#endif /* #else #ifdef CONFIG_TINY_RCU */
|
||||
|
||||
enum rcutorture_type {
|
||||
RCU_FLAVOR,
|
||||
RCU_BH_FLAVOR,
|
||||
@@ -470,6 +485,7 @@ void show_rcu_gp_kthreads(void);
|
||||
void rcu_force_quiescent_state(void);
|
||||
void rcu_bh_force_quiescent_state(void);
|
||||
void rcu_sched_force_quiescent_state(void);
|
||||
extern struct workqueue_struct *rcu_gp_wq;
|
||||
#endif /* #else #ifdef CONFIG_TINY_RCU */
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
|
||||
+20
-1
@@ -61,11 +61,30 @@ MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.vnet.ibm.com>");
|
||||
#define VERBOSE_PERFOUT_ERRSTRING(s) \
|
||||
do { if (verbose) pr_alert("%s" PERF_FLAG "!!! %s\n", perf_type, s); } while (0)
|
||||
|
||||
/*
|
||||
* The intended use cases for the nreaders and nwriters module parameters
|
||||
* are as follows:
|
||||
*
|
||||
* 1. Specify only the nr_cpus kernel boot parameter. This will
|
||||
* set both nreaders and nwriters to the value specified by
|
||||
* nr_cpus for a mixed reader/writer test.
|
||||
*
|
||||
* 2. Specify the nr_cpus kernel boot parameter, but set
|
||||
* rcuperf.nreaders to zero. This will set nwriters to the
|
||||
* value specified by nr_cpus for an update-only test.
|
||||
*
|
||||
* 3. Specify the nr_cpus kernel boot parameter, but set
|
||||
* rcuperf.nwriters to zero. This will set nreaders to the
|
||||
* value specified by nr_cpus for a read-only test.
|
||||
*
|
||||
* Various other use cases may of course be specified.
|
||||
*/
|
||||
|
||||
torture_param(bool, gp_async, false, "Use asynchronous GP wait primitives");
|
||||
torture_param(int, gp_async_max, 1000, "Max # outstanding waits per reader");
|
||||
torture_param(bool, gp_exp, false, "Use expedited GP wait primitives");
|
||||
torture_param(int, holdoff, 10, "Holdoff time before test start (s)");
|
||||
torture_param(int, nreaders, 0, "Number of RCU reader threads");
|
||||
torture_param(int, nreaders, -1, "Number of RCU reader threads");
|
||||
torture_param(int, nwriters, -1, "Number of RCU updater threads");
|
||||
torture_param(bool, shutdown, !IS_ENABLED(MODULE),
|
||||
"Shutdown at end of performance tests.");
|
||||
|
||||
+41
-31
@@ -909,34 +909,38 @@ rcu_torture_writer(void *arg)
|
||||
int nsynctypes = 0;
|
||||
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
|
||||
if (!can_expedite) {
|
||||
if (!can_expedite)
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
" GP expediting controlled from boot/sysfs for %s,\n",
|
||||
" GP expediting controlled from boot/sysfs for %s.\n",
|
||||
torture_type, cur_ops->name);
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
" Disabled dynamic grace-period expediting.\n",
|
||||
torture_type);
|
||||
}
|
||||
|
||||
/* Initialize synctype[] array. If none set, take default. */
|
||||
if (!gp_cond1 && !gp_exp1 && !gp_normal1 && !gp_sync1)
|
||||
gp_cond1 = gp_exp1 = gp_normal1 = gp_sync1 = true;
|
||||
if (gp_cond1 && cur_ops->get_state && cur_ops->cond_sync)
|
||||
if (gp_cond1 && cur_ops->get_state && cur_ops->cond_sync) {
|
||||
synctype[nsynctypes++] = RTWS_COND_GET;
|
||||
else if (gp_cond && (!cur_ops->get_state || !cur_ops->cond_sync))
|
||||
pr_alert("rcu_torture_writer: gp_cond without primitives.\n");
|
||||
if (gp_exp1 && cur_ops->exp_sync)
|
||||
pr_info("%s: Testing conditional GPs.\n", __func__);
|
||||
} else if (gp_cond && (!cur_ops->get_state || !cur_ops->cond_sync)) {
|
||||
pr_alert("%s: gp_cond without primitives.\n", __func__);
|
||||
}
|
||||
if (gp_exp1 && cur_ops->exp_sync) {
|
||||
synctype[nsynctypes++] = RTWS_EXP_SYNC;
|
||||
else if (gp_exp && !cur_ops->exp_sync)
|
||||
pr_alert("rcu_torture_writer: gp_exp without primitives.\n");
|
||||
if (gp_normal1 && cur_ops->deferred_free)
|
||||
pr_info("%s: Testing expedited GPs.\n", __func__);
|
||||
} else if (gp_exp && !cur_ops->exp_sync) {
|
||||
pr_alert("%s: gp_exp without primitives.\n", __func__);
|
||||
}
|
||||
if (gp_normal1 && cur_ops->deferred_free) {
|
||||
synctype[nsynctypes++] = RTWS_DEF_FREE;
|
||||
else if (gp_normal && !cur_ops->deferred_free)
|
||||
pr_alert("rcu_torture_writer: gp_normal without primitives.\n");
|
||||
if (gp_sync1 && cur_ops->sync)
|
||||
pr_info("%s: Testing asynchronous GPs.\n", __func__);
|
||||
} else if (gp_normal && !cur_ops->deferred_free) {
|
||||
pr_alert("%s: gp_normal without primitives.\n", __func__);
|
||||
}
|
||||
if (gp_sync1 && cur_ops->sync) {
|
||||
synctype[nsynctypes++] = RTWS_SYNC;
|
||||
else if (gp_sync && !cur_ops->sync)
|
||||
pr_alert("rcu_torture_writer: gp_sync without primitives.\n");
|
||||
pr_info("%s: Testing normal GPs.\n", __func__);
|
||||
} else if (gp_sync && !cur_ops->sync) {
|
||||
pr_alert("%s: gp_sync without primitives.\n", __func__);
|
||||
}
|
||||
if (WARN_ONCE(nsynctypes == 0,
|
||||
"rcu_torture_writer: No update-side primitives.\n")) {
|
||||
/*
|
||||
@@ -1011,6 +1015,9 @@ rcu_torture_writer(void *arg)
|
||||
rcu_unexpedite_gp();
|
||||
if (++expediting > 3)
|
||||
expediting = -expediting;
|
||||
} else if (!can_expedite) { /* Disabled during boot, recheck. */
|
||||
can_expedite = !rcu_gp_is_expedited() &&
|
||||
!rcu_gp_is_normal();
|
||||
}
|
||||
rcu_torture_writer_state = RTWS_STUTTER;
|
||||
stutter_wait("rcu_torture_writer");
|
||||
@@ -1021,6 +1028,10 @@ rcu_torture_writer(void *arg)
|
||||
while (can_expedite && expediting++ < 0)
|
||||
rcu_unexpedite_gp();
|
||||
WARN_ON_ONCE(can_expedite && rcu_gp_is_expedited());
|
||||
if (!can_expedite)
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
" Dynamic grace-period expediting was disabled.\n",
|
||||
torture_type);
|
||||
rcu_torture_writer_state = RTWS_STOPPING;
|
||||
torture_kthread_stopping("rcu_torture_writer");
|
||||
return 0;
|
||||
@@ -1045,13 +1056,13 @@ rcu_torture_fakewriter(void *arg)
|
||||
torture_random(&rand) % (nfakewriters * 8) == 0) {
|
||||
cur_ops->cb_barrier();
|
||||
} else if (gp_normal == gp_exp) {
|
||||
if (torture_random(&rand) & 0x80)
|
||||
if (cur_ops->sync && torture_random(&rand) & 0x80)
|
||||
cur_ops->sync();
|
||||
else
|
||||
else if (cur_ops->exp_sync)
|
||||
cur_ops->exp_sync();
|
||||
} else if (gp_normal) {
|
||||
} else if (gp_normal && cur_ops->sync) {
|
||||
cur_ops->sync();
|
||||
} else {
|
||||
} else if (cur_ops->exp_sync) {
|
||||
cur_ops->exp_sync();
|
||||
}
|
||||
stutter_wait("rcu_torture_fakewriter");
|
||||
@@ -1557,11 +1568,10 @@ static int rcu_torture_barrier_init(void)
|
||||
atomic_set(&barrier_cbs_count, 0);
|
||||
atomic_set(&barrier_cbs_invoked, 0);
|
||||
barrier_cbs_tasks =
|
||||
kzalloc(n_barrier_cbs * sizeof(barrier_cbs_tasks[0]),
|
||||
kcalloc(n_barrier_cbs, sizeof(barrier_cbs_tasks[0]),
|
||||
GFP_KERNEL);
|
||||
barrier_cbs_wq =
|
||||
kzalloc(n_barrier_cbs * sizeof(barrier_cbs_wq[0]),
|
||||
GFP_KERNEL);
|
||||
kcalloc(n_barrier_cbs, sizeof(barrier_cbs_wq[0]), GFP_KERNEL);
|
||||
if (barrier_cbs_tasks == NULL || !barrier_cbs_wq)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < n_barrier_cbs; i++) {
|
||||
@@ -1674,7 +1684,7 @@ static void rcu_torture_err_cb(struct rcu_head *rhp)
|
||||
* next grace period. Unlikely, but can happen. If it
|
||||
* does happen, the debug-objects subsystem won't have splatted.
|
||||
*/
|
||||
pr_alert("rcutorture: duplicated callback was invoked.\n");
|
||||
pr_alert("%s: duplicated callback was invoked.\n", KBUILD_MODNAME);
|
||||
}
|
||||
#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
|
||||
|
||||
@@ -1691,7 +1701,7 @@ static void rcu_test_debug_objects(void)
|
||||
|
||||
init_rcu_head_on_stack(&rh1);
|
||||
init_rcu_head_on_stack(&rh2);
|
||||
pr_alert("rcutorture: WARN: Duplicate call_rcu() test starting.\n");
|
||||
pr_alert("%s: WARN: Duplicate call_rcu() test starting.\n", KBUILD_MODNAME);
|
||||
|
||||
/* Try to queue the rh2 pair of callbacks for the same grace period. */
|
||||
preempt_disable(); /* Prevent preemption from interrupting test. */
|
||||
@@ -1706,11 +1716,11 @@ static void rcu_test_debug_objects(void)
|
||||
|
||||
/* Wait for them all to get done so we can safely return. */
|
||||
rcu_barrier();
|
||||
pr_alert("rcutorture: WARN: Duplicate call_rcu() test complete.\n");
|
||||
pr_alert("%s: WARN: Duplicate call_rcu() test complete.\n", KBUILD_MODNAME);
|
||||
destroy_rcu_head_on_stack(&rh1);
|
||||
destroy_rcu_head_on_stack(&rh2);
|
||||
#else /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
|
||||
pr_alert("rcutorture: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_rcu()\n");
|
||||
pr_alert("%s: !CONFIG_DEBUG_OBJECTS_RCU_HEAD, not testing duplicate call_rcu()\n", KBUILD_MODNAME);
|
||||
#endif /* #else #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
|
||||
}
|
||||
|
||||
@@ -1799,7 +1809,7 @@ rcu_torture_init(void)
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
if (nfakewriters > 0) {
|
||||
fakewriter_tasks = kzalloc(nfakewriters *
|
||||
fakewriter_tasks = kcalloc(nfakewriters,
|
||||
sizeof(fakewriter_tasks[0]),
|
||||
GFP_KERNEL);
|
||||
if (fakewriter_tasks == NULL) {
|
||||
@@ -1814,7 +1824,7 @@ rcu_torture_init(void)
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
}
|
||||
reader_tasks = kzalloc(nrealreaders * sizeof(reader_tasks[0]),
|
||||
reader_tasks = kcalloc(nrealreaders, sizeof(reader_tasks[0]),
|
||||
GFP_KERNEL);
|
||||
if (reader_tasks == NULL) {
|
||||
VERBOSE_TOROUT_ERRSTRING("out of memory");
|
||||
|
||||
+14
-15
@@ -386,7 +386,7 @@ void cleanup_srcu_struct(struct srcu_struct *sp)
|
||||
flush_delayed_work(&per_cpu_ptr(sp->sda, cpu)->work);
|
||||
if (WARN_ON(rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)) != SRCU_STATE_IDLE) ||
|
||||
WARN_ON(srcu_readers_active(sp))) {
|
||||
pr_info("cleanup_srcu_struct: Active srcu_struct %p state: %d\n", sp, rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)));
|
||||
pr_info("%s: Active srcu_struct %p state: %d\n", __func__, sp, rcu_seq_state(READ_ONCE(sp->srcu_gp_seq)));
|
||||
return; /* Caller forgot to stop doing call_srcu()? */
|
||||
}
|
||||
free_percpu(sp->sda);
|
||||
@@ -439,7 +439,7 @@ static void srcu_gp_start(struct srcu_struct *sp)
|
||||
struct srcu_data *sdp = this_cpu_ptr(sp->sda);
|
||||
int state;
|
||||
|
||||
lockdep_assert_held(&sp->lock);
|
||||
lockdep_assert_held(&ACCESS_PRIVATE(sp, lock));
|
||||
WARN_ON_ONCE(ULONG_CMP_GE(sp->srcu_gp_seq, sp->srcu_gp_seq_needed));
|
||||
rcu_segcblist_advance(&sdp->srcu_cblist,
|
||||
rcu_seq_current(&sp->srcu_gp_seq));
|
||||
@@ -492,8 +492,7 @@ static bool srcu_queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
|
||||
*/
|
||||
static void srcu_schedule_cbs_sdp(struct srcu_data *sdp, unsigned long delay)
|
||||
{
|
||||
srcu_queue_delayed_work_on(sdp->cpu, system_power_efficient_wq,
|
||||
&sdp->work, delay);
|
||||
srcu_queue_delayed_work_on(sdp->cpu, rcu_gp_wq, &sdp->work, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -527,11 +526,11 @@ static void srcu_gp_end(struct srcu_struct *sp)
|
||||
{
|
||||
unsigned long cbdelay;
|
||||
bool cbs;
|
||||
bool last_lvl;
|
||||
int cpu;
|
||||
unsigned long flags;
|
||||
unsigned long gpseq;
|
||||
int idx;
|
||||
int idxnext;
|
||||
unsigned long mask;
|
||||
struct srcu_data *sdp;
|
||||
struct srcu_node *snp;
|
||||
@@ -555,11 +554,11 @@ static void srcu_gp_end(struct srcu_struct *sp)
|
||||
|
||||
/* Initiate callback invocation as needed. */
|
||||
idx = rcu_seq_ctr(gpseq) % ARRAY_SIZE(snp->srcu_have_cbs);
|
||||
idxnext = (idx + 1) % ARRAY_SIZE(snp->srcu_have_cbs);
|
||||
rcu_for_each_node_breadth_first(sp, snp) {
|
||||
spin_lock_irq_rcu_node(snp);
|
||||
cbs = false;
|
||||
if (snp >= sp->level[rcu_num_lvls - 1])
|
||||
last_lvl = snp >= sp->level[rcu_num_lvls - 1];
|
||||
if (last_lvl)
|
||||
cbs = snp->srcu_have_cbs[idx] == gpseq;
|
||||
snp->srcu_have_cbs[idx] = gpseq;
|
||||
rcu_seq_set_state(&snp->srcu_have_cbs[idx], 1);
|
||||
@@ -572,13 +571,16 @@ static void srcu_gp_end(struct srcu_struct *sp)
|
||||
srcu_schedule_cbs_snp(sp, snp, mask, cbdelay);
|
||||
|
||||
/* Occasionally prevent srcu_data counter wrap. */
|
||||
if (!(gpseq & counter_wrap_check))
|
||||
if (!(gpseq & counter_wrap_check) && last_lvl)
|
||||
for (cpu = snp->grplo; cpu <= snp->grphi; cpu++) {
|
||||
sdp = per_cpu_ptr(sp->sda, cpu);
|
||||
spin_lock_irqsave_rcu_node(sdp, flags);
|
||||
if (ULONG_CMP_GE(gpseq,
|
||||
sdp->srcu_gp_seq_needed + 100))
|
||||
sdp->srcu_gp_seq_needed = gpseq;
|
||||
if (ULONG_CMP_GE(gpseq,
|
||||
sdp->srcu_gp_seq_needed_exp + 100))
|
||||
sdp->srcu_gp_seq_needed_exp = gpseq;
|
||||
spin_unlock_irqrestore_rcu_node(sdp, flags);
|
||||
}
|
||||
}
|
||||
@@ -593,9 +595,7 @@ static void srcu_gp_end(struct srcu_struct *sp)
|
||||
ULONG_CMP_LT(gpseq, sp->srcu_gp_seq_needed)) {
|
||||
srcu_gp_start(sp);
|
||||
spin_unlock_irq_rcu_node(sp);
|
||||
/* Throttle expedited grace periods: Should be rare! */
|
||||
srcu_reschedule(sp, rcu_seq_ctr(gpseq) & 0x3ff
|
||||
? 0 : SRCU_INTERVAL);
|
||||
srcu_reschedule(sp, 0);
|
||||
} else {
|
||||
spin_unlock_irq_rcu_node(sp);
|
||||
}
|
||||
@@ -626,7 +626,7 @@ static void srcu_funnel_exp_start(struct srcu_struct *sp, struct srcu_node *snp,
|
||||
spin_unlock_irqrestore_rcu_node(snp, flags);
|
||||
}
|
||||
spin_lock_irqsave_rcu_node(sp, flags);
|
||||
if (!ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, s))
|
||||
if (ULONG_CMP_LT(sp->srcu_gp_seq_needed_exp, s))
|
||||
sp->srcu_gp_seq_needed_exp = s;
|
||||
spin_unlock_irqrestore_rcu_node(sp, flags);
|
||||
}
|
||||
@@ -691,8 +691,7 @@ static void srcu_funnel_gp_start(struct srcu_struct *sp, struct srcu_data *sdp,
|
||||
rcu_seq_state(sp->srcu_gp_seq) == SRCU_STATE_IDLE) {
|
||||
WARN_ON_ONCE(ULONG_CMP_GE(sp->srcu_gp_seq, sp->srcu_gp_seq_needed));
|
||||
srcu_gp_start(sp);
|
||||
queue_delayed_work(system_power_efficient_wq, &sp->work,
|
||||
srcu_get_delay(sp));
|
||||
queue_delayed_work(rcu_gp_wq, &sp->work, srcu_get_delay(sp));
|
||||
}
|
||||
spin_unlock_irqrestore_rcu_node(sp, flags);
|
||||
}
|
||||
@@ -1225,7 +1224,7 @@ static void srcu_reschedule(struct srcu_struct *sp, unsigned long delay)
|
||||
spin_unlock_irq_rcu_node(sp);
|
||||
|
||||
if (pushgp)
|
||||
queue_delayed_work(system_power_efficient_wq, &sp->work, delay);
|
||||
queue_delayed_work(rcu_gp_wq, &sp->work, delay);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+26
-46
@@ -1161,7 +1161,7 @@ static int rcu_is_cpu_rrupt_from_idle(void)
|
||||
*/
|
||||
static void rcu_gpnum_ovf(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
{
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (ULONG_CMP_LT(READ_ONCE(rdp->gpnum) + ULONG_MAX / 4, rnp->gpnum))
|
||||
WRITE_ONCE(rdp->gpwrap, true);
|
||||
if (ULONG_CMP_LT(rdp->rcu_iw_gpnum + ULONG_MAX / 4, rnp->gpnum))
|
||||
@@ -1350,6 +1350,7 @@ static void rcu_check_gp_kthread_starvation(struct rcu_state *rsp)
|
||||
rsp->gp_kthread ? rsp->gp_kthread->state : ~0,
|
||||
rsp->gp_kthread ? task_cpu(rsp->gp_kthread) : -1);
|
||||
if (rsp->gp_kthread) {
|
||||
pr_err("RCU grace-period kthread stack dump:\n");
|
||||
sched_show_task(rsp->gp_kthread);
|
||||
wake_up_process(rsp->gp_kthread);
|
||||
}
|
||||
@@ -1628,7 +1629,7 @@ void rcu_cpu_stall_reset(void)
|
||||
static unsigned long rcu_cbs_completed(struct rcu_state *rsp,
|
||||
struct rcu_node *rnp)
|
||||
{
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/*
|
||||
* If RCU is idle, we just wait for the next grace period.
|
||||
@@ -1675,7 +1676,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,
|
||||
bool ret = false;
|
||||
struct rcu_node *rnp_root = rcu_get_root(rdp->rsp);
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/*
|
||||
* Pick up grace-period number for new callbacks. If this
|
||||
@@ -1803,7 +1804,7 @@ static bool rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* If no pending (not yet ready to invoke) callbacks, nothing to do. */
|
||||
if (!rcu_segcblist_pend_cbs(&rdp->cblist))
|
||||
@@ -1843,7 +1844,7 @@ static bool rcu_accelerate_cbs(struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
static bool rcu_advance_cbs(struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* If no pending (not yet ready to invoke) callbacks, nothing to do. */
|
||||
if (!rcu_segcblist_pend_cbs(&rdp->cblist))
|
||||
@@ -1871,7 +1872,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
bool ret;
|
||||
bool need_gp;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* Handle the ends of any preceding grace periods first. */
|
||||
if (rdp->completed == rnp->completed &&
|
||||
@@ -2296,7 +2297,7 @@ static bool
|
||||
rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (!rsp->gp_kthread || !cpu_needs_another_gp(rsp, rdp)) {
|
||||
/*
|
||||
* Either we have not yet spawned the grace-period
|
||||
@@ -2358,7 +2359,7 @@ static bool rcu_start_gp(struct rcu_state *rsp)
|
||||
static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
|
||||
__releases(rcu_get_root(rsp)->lock)
|
||||
{
|
||||
lockdep_assert_held(&rcu_get_root(rsp)->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rcu_get_root(rsp));
|
||||
WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
|
||||
WRITE_ONCE(rsp->gp_flags, READ_ONCE(rsp->gp_flags) | RCU_GP_FLAG_FQS);
|
||||
raw_spin_unlock_irqrestore_rcu_node(rcu_get_root(rsp), flags);
|
||||
@@ -2383,7 +2384,7 @@ rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp,
|
||||
unsigned long oldmask = 0;
|
||||
struct rcu_node *rnp_c;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* Walk up the rcu_node hierarchy. */
|
||||
for (;;) {
|
||||
@@ -2447,7 +2448,7 @@ static void rcu_report_unblock_qs_rnp(struct rcu_state *rsp,
|
||||
unsigned long mask;
|
||||
struct rcu_node *rnp_p;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (rcu_state_p == &rcu_sched_state || rsp != rcu_state_p ||
|
||||
rnp->qsmask != 0 || rcu_preempt_blocked_readers_cgp(rnp)) {
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
@@ -2592,7 +2593,7 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
|
||||
long mask;
|
||||
struct rcu_node *rnp = rnp_leaf;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) ||
|
||||
rnp->qsmaskinit || rcu_preempt_has_tasks(rnp))
|
||||
return;
|
||||
@@ -2691,7 +2692,6 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
/* Update counts and requeue any remaining callbacks. */
|
||||
rcu_segcblist_insert_done_cbs(&rdp->cblist, &rcl);
|
||||
smp_mb(); /* List handling before counting for rcu_barrier(). */
|
||||
rdp->n_cbs_invoked += count;
|
||||
rcu_segcblist_insert_count(&rdp->cblist, &rcl);
|
||||
|
||||
/* Reinstate batch limit if we have worked down the excess. */
|
||||
@@ -2845,10 +2845,8 @@ static void force_quiescent_state(struct rcu_state *rsp)
|
||||
!raw_spin_trylock(&rnp->fqslock);
|
||||
if (rnp_old != NULL)
|
||||
raw_spin_unlock(&rnp_old->fqslock);
|
||||
if (ret) {
|
||||
rsp->n_force_qs_lh++;
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
rnp_old = rnp;
|
||||
}
|
||||
/* rnp_old == rcu_get_root(rsp), rnp == NULL. */
|
||||
@@ -2857,7 +2855,6 @@ static void force_quiescent_state(struct rcu_state *rsp)
|
||||
raw_spin_lock_irqsave_rcu_node(rnp_old, flags);
|
||||
raw_spin_unlock(&rnp_old->fqslock);
|
||||
if (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
|
||||
rsp->n_force_qs_lh++;
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp_old, flags);
|
||||
return; /* Someone beat us to it. */
|
||||
}
|
||||
@@ -3355,8 +3352,6 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
{
|
||||
struct rcu_node *rnp = rdp->mynode;
|
||||
|
||||
rdp->n_rcu_pending++;
|
||||
|
||||
/* Check for CPU stalls, if enabled. */
|
||||
check_cpu_stall(rsp, rdp);
|
||||
|
||||
@@ -3365,48 +3360,31 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
|
||||
return 0;
|
||||
|
||||
/* Is the RCU core waiting for a quiescent state from this CPU? */
|
||||
if (rcu_scheduler_fully_active &&
|
||||
rdp->core_needs_qs && rdp->cpu_no_qs.b.norm &&
|
||||
rdp->rcu_qs_ctr_snap == __this_cpu_read(rcu_dynticks.rcu_qs_ctr)) {
|
||||
rdp->n_rp_core_needs_qs++;
|
||||
} else if (rdp->core_needs_qs && !rdp->cpu_no_qs.b.norm) {
|
||||
rdp->n_rp_report_qs++;
|
||||
if (rdp->core_needs_qs && !rdp->cpu_no_qs.b.norm)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Does this CPU have callbacks ready to invoke? */
|
||||
if (rcu_segcblist_ready_cbs(&rdp->cblist)) {
|
||||
rdp->n_rp_cb_ready++;
|
||||
if (rcu_segcblist_ready_cbs(&rdp->cblist))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Has RCU gone idle with this CPU needing another grace period? */
|
||||
if (cpu_needs_another_gp(rsp, rdp)) {
|
||||
rdp->n_rp_cpu_needs_gp++;
|
||||
if (cpu_needs_another_gp(rsp, rdp))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Has another RCU grace period completed? */
|
||||
if (READ_ONCE(rnp->completed) != rdp->completed) { /* outside lock */
|
||||
rdp->n_rp_gp_completed++;
|
||||
if (READ_ONCE(rnp->completed) != rdp->completed) /* outside lock */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Has a new RCU grace period started? */
|
||||
if (READ_ONCE(rnp->gpnum) != rdp->gpnum ||
|
||||
unlikely(READ_ONCE(rdp->gpwrap))) { /* outside lock */
|
||||
rdp->n_rp_gp_started++;
|
||||
unlikely(READ_ONCE(rdp->gpwrap))) /* outside lock */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Does this CPU need a deferred NOCB wakeup? */
|
||||
if (rcu_nocb_need_deferred_wakeup(rdp)) {
|
||||
rdp->n_rp_nocb_defer_wakeup++;
|
||||
if (rcu_nocb_need_deferred_wakeup(rdp))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* nothing to do */
|
||||
rdp->n_rp_need_nothing++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3618,7 +3596,7 @@ static void rcu_init_new_rnp(struct rcu_node *rnp_leaf)
|
||||
long mask;
|
||||
struct rcu_node *rnp = rnp_leaf;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
for (;;) {
|
||||
mask = rnp->grpmask;
|
||||
rnp = rnp->parent;
|
||||
@@ -3636,12 +3614,9 @@ static void rcu_init_new_rnp(struct rcu_node *rnp_leaf)
|
||||
static void __init
|
||||
rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
struct rcu_node *rnp = rcu_get_root(rsp);
|
||||
|
||||
/* Set up local state, ensuring consistent view of global state. */
|
||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||
rdp->grpmask = leaf_node_cpu_bit(rdp->mynode, cpu);
|
||||
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
|
||||
WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != 1);
|
||||
@@ -3649,7 +3624,6 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
rdp->cpu = cpu;
|
||||
rdp->rsp = rsp;
|
||||
rcu_boot_init_nocb_percpu_data(rdp);
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4193,6 +4167,8 @@ static void __init rcu_dump_rcu_node_tree(struct rcu_state *rsp)
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
struct workqueue_struct *rcu_gp_wq;
|
||||
|
||||
void __init rcu_init(void)
|
||||
{
|
||||
int cpu;
|
||||
@@ -4219,6 +4195,10 @@ void __init rcu_init(void)
|
||||
rcu_cpu_starting(cpu);
|
||||
rcutree_online_cpu(cpu);
|
||||
}
|
||||
|
||||
/* Create workqueue for expedited GPs and for Tree SRCU. */
|
||||
rcu_gp_wq = alloc_workqueue("rcu_gp", WQ_MEM_RECLAIM, 0);
|
||||
WARN_ON(!rcu_gp_wq);
|
||||
}
|
||||
|
||||
#include "tree_exp.h"
|
||||
|
||||
+3
-33
@@ -146,12 +146,6 @@ struct rcu_node {
|
||||
/* boosting for this rcu_node structure. */
|
||||
unsigned int boost_kthread_status;
|
||||
/* State of boost_kthread_task for tracing. */
|
||||
unsigned long n_tasks_boosted;
|
||||
/* Total number of tasks boosted. */
|
||||
unsigned long n_exp_boosts;
|
||||
/* Number of tasks boosted for expedited GP. */
|
||||
unsigned long n_normal_boosts;
|
||||
/* Number of tasks boosted for normal GP. */
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
struct swait_queue_head nocb_gp_wq[2];
|
||||
/* Place for rcu_nocb_kthread() to wait GP. */
|
||||
@@ -184,13 +178,6 @@ union rcu_noqs {
|
||||
u16 s; /* Set of bits, aggregate OR here. */
|
||||
};
|
||||
|
||||
/* Index values for nxttail array in struct rcu_data. */
|
||||
#define RCU_DONE_TAIL 0 /* Also RCU_WAIT head. */
|
||||
#define RCU_WAIT_TAIL 1 /* Also RCU_NEXT_READY head. */
|
||||
#define RCU_NEXT_READY_TAIL 2 /* Also RCU_NEXT head. */
|
||||
#define RCU_NEXT_TAIL 3
|
||||
#define RCU_NEXT_SIZE 4
|
||||
|
||||
/* Per-CPU data for read-copy update. */
|
||||
struct rcu_data {
|
||||
/* 1) quiescent-state and grace-period handling : */
|
||||
@@ -217,8 +204,6 @@ struct rcu_data {
|
||||
/* different grace periods. */
|
||||
long qlen_last_fqs_check;
|
||||
/* qlen at last check for QS forcing */
|
||||
unsigned long n_cbs_invoked; /* count of RCU cbs invoked. */
|
||||
unsigned long n_nocbs_invoked; /* count of no-CBs RCU cbs invoked. */
|
||||
unsigned long n_force_qs_snap;
|
||||
/* did other CPU force QS recently? */
|
||||
long blimit; /* Upper limit on a processed batch */
|
||||
@@ -234,18 +219,7 @@ struct rcu_data {
|
||||
/* Grace period that needs help */
|
||||
/* from cond_resched(). */
|
||||
|
||||
/* 5) __rcu_pending() statistics. */
|
||||
unsigned long n_rcu_pending; /* rcu_pending() calls since boot. */
|
||||
unsigned long n_rp_core_needs_qs;
|
||||
unsigned long n_rp_report_qs;
|
||||
unsigned long n_rp_cb_ready;
|
||||
unsigned long n_rp_cpu_needs_gp;
|
||||
unsigned long n_rp_gp_completed;
|
||||
unsigned long n_rp_gp_started;
|
||||
unsigned long n_rp_nocb_defer_wakeup;
|
||||
unsigned long n_rp_need_nothing;
|
||||
|
||||
/* 6) _rcu_barrier(), OOM callbacks, and expediting. */
|
||||
/* 5) _rcu_barrier(), OOM callbacks, and expediting. */
|
||||
struct rcu_head barrier_head;
|
||||
#ifdef CONFIG_RCU_FAST_NO_HZ
|
||||
struct rcu_head oom_head;
|
||||
@@ -256,7 +230,7 @@ struct rcu_data {
|
||||
atomic_long_t exp_workdone3; /* # done by others #3. */
|
||||
int exp_dynticks_snap; /* Double-check need for IPI. */
|
||||
|
||||
/* 7) Callback offloading. */
|
||||
/* 6) Callback offloading. */
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
struct rcu_head *nocb_head; /* CBs waiting for kthread. */
|
||||
struct rcu_head **nocb_tail;
|
||||
@@ -283,7 +257,7 @@ struct rcu_data {
|
||||
/* Leader CPU takes GP-end wakeups. */
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/* 8) RCU CPU stall data. */
|
||||
/* 7) RCU CPU stall data. */
|
||||
unsigned int softirq_snap; /* Snapshot of softirq activity. */
|
||||
/* ->rcu_iw* fields protected by leaf rcu_node ->lock. */
|
||||
struct irq_work rcu_iw; /* Check for non-irq activity. */
|
||||
@@ -374,10 +348,6 @@ struct rcu_state {
|
||||
/* kthreads, if configured. */
|
||||
unsigned long n_force_qs; /* Number of calls to */
|
||||
/* force_quiescent_state(). */
|
||||
unsigned long n_force_qs_lh; /* ~Number of calls leaving */
|
||||
/* due to lock unavailable. */
|
||||
unsigned long n_force_qs_ngp; /* Number of calls leaving */
|
||||
/* due to no GP active. */
|
||||
unsigned long gp_start; /* Time at which GP started, */
|
||||
/* but in jiffies. */
|
||||
unsigned long gp_activity; /* Time of last GP kthread */
|
||||
|
||||
+28
-8
@@ -28,6 +28,15 @@ static void rcu_exp_gp_seq_start(struct rcu_state *rsp)
|
||||
rcu_seq_start(&rsp->expedited_sequence);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return then value that expedited-grace-period counter will have
|
||||
* at the end of the current grace period.
|
||||
*/
|
||||
static __maybe_unused unsigned long rcu_exp_gp_seq_endval(struct rcu_state *rsp)
|
||||
{
|
||||
return rcu_seq_endval(&rsp->expedited_sequence);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record the end of an expedited grace period.
|
||||
*/
|
||||
@@ -366,21 +375,30 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
|
||||
int ret;
|
||||
struct rcu_node *rnp;
|
||||
|
||||
trace_rcu_exp_grace_period(rsp->name, rcu_exp_gp_seq_endval(rsp), TPS("reset"));
|
||||
sync_exp_reset_tree(rsp);
|
||||
trace_rcu_exp_grace_period(rsp->name, rcu_exp_gp_seq_endval(rsp), TPS("select"));
|
||||
rcu_for_each_leaf_node(rsp, rnp) {
|
||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||
|
||||
/* Each pass checks a CPU for identity, offline, and idle. */
|
||||
mask_ofl_test = 0;
|
||||
for_each_leaf_node_possible_cpu(rnp, cpu) {
|
||||
for_each_leaf_node_cpu_mask(rnp, cpu, rnp->expmask) {
|
||||
unsigned long mask = leaf_node_cpu_bit(rnp, cpu);
|
||||
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
struct rcu_dynticks *rdtp = per_cpu_ptr(&rcu_dynticks, cpu);
|
||||
int snap;
|
||||
|
||||
rdp->exp_dynticks_snap =
|
||||
rcu_dynticks_snap(rdp->dynticks);
|
||||
if (raw_smp_processor_id() == cpu ||
|
||||
rcu_dynticks_in_eqs(rdp->exp_dynticks_snap) ||
|
||||
!(rnp->qsmaskinitnext & rdp->grpmask))
|
||||
mask_ofl_test |= rdp->grpmask;
|
||||
!(rnp->qsmaskinitnext & mask)) {
|
||||
mask_ofl_test |= mask;
|
||||
} else {
|
||||
snap = rcu_dynticks_snap(rdtp);
|
||||
if (rcu_dynticks_in_eqs(snap))
|
||||
mask_ofl_test |= mask;
|
||||
else
|
||||
rdp->exp_dynticks_snap = snap;
|
||||
}
|
||||
}
|
||||
mask_ofl_ipi = rnp->expmask & ~mask_ofl_test;
|
||||
|
||||
@@ -394,7 +412,7 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
|
||||
/* IPI the remaining CPUs for expedited quiescent state. */
|
||||
for_each_leaf_node_possible_cpu(rnp, cpu) {
|
||||
for_each_leaf_node_cpu_mask(rnp, cpu, rnp->expmask) {
|
||||
unsigned long mask = leaf_node_cpu_bit(rnp, cpu);
|
||||
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
|
||||
|
||||
@@ -417,6 +435,7 @@ retry_ipi:
|
||||
(rnp->expmask & mask)) {
|
||||
/* Online, so delay for a bit and try again. */
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
trace_rcu_exp_grace_period(rsp->name, rcu_exp_gp_seq_endval(rsp), TPS("selectofl"));
|
||||
schedule_timeout_uninterruptible(1);
|
||||
goto retry_ipi;
|
||||
}
|
||||
@@ -443,6 +462,7 @@ static void synchronize_sched_expedited_wait(struct rcu_state *rsp)
|
||||
struct rcu_node *rnp_root = rcu_get_root(rsp);
|
||||
int ret;
|
||||
|
||||
trace_rcu_exp_grace_period(rsp->name, rcu_exp_gp_seq_endval(rsp), TPS("startwait"));
|
||||
jiffies_stall = rcu_jiffies_till_stall_check();
|
||||
jiffies_start = jiffies;
|
||||
|
||||
@@ -606,7 +626,7 @@ static void _synchronize_rcu_expedited(struct rcu_state *rsp,
|
||||
rew.rew_rsp = rsp;
|
||||
rew.rew_s = s;
|
||||
INIT_WORK_ONSTACK(&rew.rew_work, wait_rcu_exp_gp);
|
||||
schedule_work(&rew.rew_work);
|
||||
queue_work(rcu_gp_wq, &rew.rew_work);
|
||||
}
|
||||
|
||||
/* Wait for expedited grace period to complete. */
|
||||
|
||||
+22
-12
@@ -180,7 +180,7 @@ static void rcu_preempt_ctxt_queue(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
(rnp->expmask & rdp->grpmask ? RCU_EXP_BLKD : 0);
|
||||
struct task_struct *t = current;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
WARN_ON_ONCE(rdp->mynode != rnp);
|
||||
WARN_ON_ONCE(rnp->level != rcu_num_lvls - 1);
|
||||
|
||||
@@ -560,8 +560,14 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
|
||||
}
|
||||
t = list_entry(rnp->gp_tasks->prev,
|
||||
struct task_struct, rcu_node_entry);
|
||||
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry)
|
||||
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
|
||||
/*
|
||||
* We could be printing a lot while holding a spinlock.
|
||||
* Avoid triggering hard lockup.
|
||||
*/
|
||||
touch_nmi_watchdog();
|
||||
sched_show_task(t);
|
||||
}
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
}
|
||||
|
||||
@@ -957,14 +963,10 @@ static int rcu_boost(struct rcu_node *rnp)
|
||||
* expedited grace period must boost all blocked tasks, including
|
||||
* those blocking the pre-existing normal grace period.
|
||||
*/
|
||||
if (rnp->exp_tasks != NULL) {
|
||||
if (rnp->exp_tasks != NULL)
|
||||
tb = rnp->exp_tasks;
|
||||
rnp->n_exp_boosts++;
|
||||
} else {
|
||||
else
|
||||
tb = rnp->boost_tasks;
|
||||
rnp->n_normal_boosts++;
|
||||
}
|
||||
rnp->n_tasks_boosted++;
|
||||
|
||||
/*
|
||||
* We boost task t by manufacturing an rt_mutex that appears to
|
||||
@@ -1042,7 +1044,7 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
|
||||
{
|
||||
struct task_struct *t;
|
||||
|
||||
lockdep_assert_held(&rnp->lock);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (!rcu_preempt_blocked_readers_cgp(rnp) && rnp->exp_tasks == NULL) {
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
return;
|
||||
@@ -1677,6 +1679,12 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)
|
||||
char *ticks_title;
|
||||
unsigned long ticks_value;
|
||||
|
||||
/*
|
||||
* We could be printing a lot while holding a spinlock. Avoid
|
||||
* triggering hard lockup.
|
||||
*/
|
||||
touch_nmi_watchdog();
|
||||
|
||||
if (rsp->gpnum == rdp->gpnum) {
|
||||
ticks_title = "ticks this GP";
|
||||
ticks_value = rdp->ticks_this_gp;
|
||||
@@ -2235,7 +2243,6 @@ static int rcu_nocb_kthread(void *arg)
|
||||
smp_mb__before_atomic(); /* _add after CB invocation. */
|
||||
atomic_long_add(-c, &rdp->nocb_q_count);
|
||||
atomic_long_add(-cl, &rdp->nocb_q_count_lazy);
|
||||
rdp->n_nocbs_invoked += c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2312,8 +2319,11 @@ void __init rcu_init_nohz(void)
|
||||
cpumask_and(rcu_nocb_mask, cpu_possible_mask,
|
||||
rcu_nocb_mask);
|
||||
}
|
||||
pr_info("\tOffload RCU callbacks from CPUs: %*pbl.\n",
|
||||
cpumask_pr_args(rcu_nocb_mask));
|
||||
if (cpumask_empty(rcu_nocb_mask))
|
||||
pr_info("\tOffload RCU callbacks from CPUs: (none).\n");
|
||||
else
|
||||
pr_info("\tOffload RCU callbacks from CPUs: %*pbl.\n",
|
||||
cpumask_pr_args(rcu_nocb_mask));
|
||||
if (rcu_nocb_poll)
|
||||
pr_info("\tPoll for callbacks from no-CBs CPUs.\n");
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@ CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
obj-y += core.o loadavg.o clock.o cputime.o
|
||||
obj-y += idle_task.o fair.o rt.o deadline.o
|
||||
obj-y += wait.o wait_bit.o swait.o completion.o idle.o
|
||||
obj-y += idle.o fair.o rt.o deadline.o
|
||||
obj-y += wait.o wait_bit.o swait.o completion.o
|
||||
|
||||
obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o topology.o stop_task.o
|
||||
obj-$(CONFIG_SCHED_AUTOGROUP) += autogroup.o
|
||||
obj-$(CONFIG_SCHEDSTATS) += stats.o
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
/*
|
||||
* Auto-group scheduling implementation:
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
unsigned int __read_mostly sysctl_sched_autogroup_enabled = 1;
|
||||
@@ -168,18 +165,19 @@ autogroup_move_group(struct task_struct *p, struct autogroup *ag)
|
||||
autogroup_kref_put(prev);
|
||||
}
|
||||
|
||||
/* Allocates GFP_KERNEL, cannot be called under any spinlock */
|
||||
/* Allocates GFP_KERNEL, cannot be called under any spinlock: */
|
||||
void sched_autogroup_create_attach(struct task_struct *p)
|
||||
{
|
||||
struct autogroup *ag = autogroup_create();
|
||||
|
||||
autogroup_move_group(p, ag);
|
||||
/* drop extra reference added by autogroup_create() */
|
||||
|
||||
/* Drop extra reference added by autogroup_create(): */
|
||||
autogroup_kref_put(ag);
|
||||
}
|
||||
EXPORT_SYMBOL(sched_autogroup_create_attach);
|
||||
|
||||
/* Cannot be called under siglock. Currently has no users */
|
||||
/* Cannot be called under siglock. Currently has no users: */
|
||||
void sched_autogroup_detach(struct task_struct *p)
|
||||
{
|
||||
autogroup_move_group(p, &autogroup_default);
|
||||
@@ -202,7 +200,6 @@ static int __init setup_autogroup(char *str)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("noautogroup", setup_autogroup);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
@@ -224,7 +221,7 @@ int proc_sched_autogroup_set_nice(struct task_struct *p, int nice)
|
||||
if (nice < 0 && !can_nice(current, nice))
|
||||
return -EPERM;
|
||||
|
||||
/* this is a heavy operation taking global locks.. */
|
||||
/* This is a heavy operation, taking global locks.. */
|
||||
if (!capable(CAP_SYS_ADMIN) && time_before(jiffies, next))
|
||||
return -EAGAIN;
|
||||
|
||||
@@ -267,4 +264,4 @@ int autogroup_path(struct task_group *tg, char *buf, int buflen)
|
||||
|
||||
return snprintf(buf, buflen, "%s-%ld", "/autogroup", tg->autogroup->id);
|
||||
}
|
||||
#endif /* CONFIG_SCHED_DEBUG */
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifdef CONFIG_SCHED_AUTOGROUP
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/sched/autogroup.h>
|
||||
|
||||
struct autogroup {
|
||||
/*
|
||||
* reference doesn't mean how many thread attach to this
|
||||
* autogroup now. It just stands for the number of task
|
||||
* could use this autogroup.
|
||||
* Reference doesn't mean how many threads attach to this
|
||||
* autogroup now. It just stands for the number of tasks
|
||||
* which could use this autogroup.
|
||||
*/
|
||||
struct kref kref;
|
||||
struct task_group *tg;
|
||||
@@ -56,11 +52,9 @@ autogroup_task_group(struct task_struct *p, struct task_group *tg)
|
||||
return tg;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_DEBUG
|
||||
static inline int autogroup_path(struct task_group *tg, char *buf, int buflen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_SCHED_AUTOGROUP */
|
||||
|
||||
+12
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* sched_clock for unstable cpu clocks
|
||||
* sched_clock() for unstable CPU clocks
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat, Inc., Peter Zijlstra
|
||||
*
|
||||
@@ -11,7 +11,7 @@
|
||||
* Guillaume Chazarain <guichaz@gmail.com>
|
||||
*
|
||||
*
|
||||
* What:
|
||||
* What this file implements:
|
||||
*
|
||||
* cpu_clock(i) provides a fast (execution time) high resolution
|
||||
* clock with bounded drift between CPUs. The value of cpu_clock(i)
|
||||
@@ -26,11 +26,11 @@
|
||||
* at 0 on boot (but people really shouldn't rely on that).
|
||||
*
|
||||
* cpu_clock(i) -- can be used from any context, including NMI.
|
||||
* local_clock() -- is cpu_clock() on the current cpu.
|
||||
* local_clock() -- is cpu_clock() on the current CPU.
|
||||
*
|
||||
* sched_clock_cpu(i)
|
||||
*
|
||||
* How:
|
||||
* How it is implemented:
|
||||
*
|
||||
* The implementation either uses sched_clock() when
|
||||
* !CONFIG_HAVE_UNSTABLE_SCHED_CLOCK, which means in that case the
|
||||
@@ -52,19 +52,7 @@
|
||||
* that is otherwise invisible (TSC gets stopped).
|
||||
*
|
||||
*/
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/init.h>
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* Scheduler clock - returns current time in nanosec units.
|
||||
@@ -302,21 +290,21 @@ again:
|
||||
* cmpxchg64 below only protects one readout.
|
||||
*
|
||||
* We must reread via sched_clock_local() in the retry case on
|
||||
* 32bit as an NMI could use sched_clock_local() via the
|
||||
* 32-bit kernels as an NMI could use sched_clock_local() via the
|
||||
* tracer and hit between the readout of
|
||||
* the low32bit and the high 32bit portion.
|
||||
* the low 32-bit and the high 32-bit portion.
|
||||
*/
|
||||
this_clock = sched_clock_local(my_scd);
|
||||
/*
|
||||
* We must enforce atomic readout on 32bit, otherwise the
|
||||
* update on the remote cpu can hit inbetween the readout of
|
||||
* the low32bit and the high 32bit portion.
|
||||
* We must enforce atomic readout on 32-bit, otherwise the
|
||||
* update on the remote CPU can hit inbetween the readout of
|
||||
* the low 32-bit and the high 32-bit portion.
|
||||
*/
|
||||
remote_clock = cmpxchg64(&scd->clock, 0, 0);
|
||||
#else
|
||||
/*
|
||||
* On 64bit the read of [my]scd->clock is atomic versus the
|
||||
* update, so we can avoid the above 32bit dance.
|
||||
* On 64-bit kernels the read of [my]scd->clock is atomic versus the
|
||||
* update, so we can avoid the above 32-bit dance.
|
||||
*/
|
||||
sched_clock_local(my_scd);
|
||||
again:
|
||||
|
||||
@@ -11,10 +11,7 @@
|
||||
* typically be used for exclusion which gives rise to priority inversion.
|
||||
* Waiting for completion is a typically sync point, but not an exclusion point.
|
||||
*/
|
||||
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/completion.h>
|
||||
#include "sched.h"
|
||||
|
||||
/**
|
||||
* complete: - signals a single thread waiting on this completion
|
||||
@@ -283,7 +280,7 @@ EXPORT_SYMBOL(wait_for_completion_killable_timeout);
|
||||
bool try_wait_for_completion(struct completion *x)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 1;
|
||||
bool ret = true;
|
||||
|
||||
/*
|
||||
* Since x->done will need to be locked only
|
||||
@@ -292,11 +289,11 @@ bool try_wait_for_completion(struct completion *x)
|
||||
* return early in the blocking case.
|
||||
*/
|
||||
if (!READ_ONCE(x->done))
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
spin_lock_irqsave(&x->wait.lock, flags);
|
||||
if (!x->done)
|
||||
ret = 0;
|
||||
ret = false;
|
||||
else if (x->done != UINT_MAX)
|
||||
x->done--;
|
||||
spin_unlock_irqrestore(&x->wait.lock, flags);
|
||||
|
||||
+125
-69
@@ -5,37 +5,11 @@
|
||||
*
|
||||
* Copyright (C) 1991-2002 Linus Torvalds
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
#include <linux/sched/loadavg.h>
|
||||
#include <linux/sched/hotplug.h>
|
||||
#include <linux/wait_bit.h>
|
||||
#include <linux/cpuset.h>
|
||||
#include <linux/delayacct.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/context_tracking.h>
|
||||
#include <linux/rcupdate_wait.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/mmu_context.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/sched/isolation.h>
|
||||
#include "sched.h"
|
||||
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/tlb.h>
|
||||
#ifdef CONFIG_PARAVIRT
|
||||
#include <asm/paravirt.h>
|
||||
#endif
|
||||
|
||||
#include "sched.h"
|
||||
#include "../workqueue_internal.h"
|
||||
#include "../smpboot.h"
|
||||
|
||||
@@ -135,7 +109,7 @@ struct rq *task_rq_lock(struct task_struct *p, struct rq_flags *rf)
|
||||
* [L] ->on_rq
|
||||
* RELEASE (rq->lock)
|
||||
*
|
||||
* If we observe the old cpu in task_rq_lock, the acquire of
|
||||
* If we observe the old CPU in task_rq_lock, the acquire of
|
||||
* the old rq->lock will fully serialize against the stores.
|
||||
*
|
||||
* If we observe the new CPU in task_rq_lock, the acquire will
|
||||
@@ -333,7 +307,7 @@ void hrtick_start(struct rq *rq, u64 delay)
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
static void init_rq_hrtick(struct rq *rq)
|
||||
static void hrtick_rq_init(struct rq *rq)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
rq->hrtick_csd_pending = 0;
|
||||
@@ -351,7 +325,7 @@ static inline void hrtick_clear(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void init_rq_hrtick(struct rq *rq)
|
||||
static inline void hrtick_rq_init(struct rq *rq)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SCHED_HRTICK */
|
||||
@@ -609,7 +583,7 @@ static inline bool got_nohz_idle_kick(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
if (!test_bit(NOHZ_BALANCE_KICK, nohz_flags(cpu)))
|
||||
if (!(atomic_read(nohz_flags(cpu)) & NOHZ_KICK_MASK))
|
||||
return false;
|
||||
|
||||
if (idle_cpu(cpu) && !need_resched())
|
||||
@@ -619,7 +593,7 @@ static inline bool got_nohz_idle_kick(void)
|
||||
* We can't run Idle Load Balance on this CPU for this time so we
|
||||
* cancel it and clear NOHZ_BALANCE_KICK
|
||||
*/
|
||||
clear_bit(NOHZ_BALANCE_KICK, nohz_flags(cpu));
|
||||
atomic_andnot(NOHZ_KICK_MASK, nohz_flags(cpu));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1457,7 +1431,7 @@ EXPORT_SYMBOL_GPL(kick_process);
|
||||
*
|
||||
* - cpu_active must be a subset of cpu_online
|
||||
*
|
||||
* - on cpu-up we allow per-cpu kthreads on the online && !active cpu,
|
||||
* - on CPU-up we allow per-CPU kthreads on the online && !active CPU,
|
||||
* see __set_cpus_allowed_ptr(). At this point the newly online
|
||||
* CPU isn't yet part of the sched domains, and balancing will not
|
||||
* see it.
|
||||
@@ -2488,17 +2462,17 @@ void wake_up_new_task(struct task_struct *p)
|
||||
|
||||
#ifdef CONFIG_PREEMPT_NOTIFIERS
|
||||
|
||||
static struct static_key preempt_notifier_key = STATIC_KEY_INIT_FALSE;
|
||||
static DEFINE_STATIC_KEY_FALSE(preempt_notifier_key);
|
||||
|
||||
void preempt_notifier_inc(void)
|
||||
{
|
||||
static_key_slow_inc(&preempt_notifier_key);
|
||||
static_branch_inc(&preempt_notifier_key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(preempt_notifier_inc);
|
||||
|
||||
void preempt_notifier_dec(void)
|
||||
{
|
||||
static_key_slow_dec(&preempt_notifier_key);
|
||||
static_branch_dec(&preempt_notifier_key);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(preempt_notifier_dec);
|
||||
|
||||
@@ -2508,7 +2482,7 @@ EXPORT_SYMBOL_GPL(preempt_notifier_dec);
|
||||
*/
|
||||
void preempt_notifier_register(struct preempt_notifier *notifier)
|
||||
{
|
||||
if (!static_key_false(&preempt_notifier_key))
|
||||
if (!static_branch_unlikely(&preempt_notifier_key))
|
||||
WARN(1, "registering preempt_notifier while notifiers disabled\n");
|
||||
|
||||
hlist_add_head(¬ifier->link, ¤t->preempt_notifiers);
|
||||
@@ -2537,7 +2511,7 @@ static void __fire_sched_in_preempt_notifiers(struct task_struct *curr)
|
||||
|
||||
static __always_inline void fire_sched_in_preempt_notifiers(struct task_struct *curr)
|
||||
{
|
||||
if (static_key_false(&preempt_notifier_key))
|
||||
if (static_branch_unlikely(&preempt_notifier_key))
|
||||
__fire_sched_in_preempt_notifiers(curr);
|
||||
}
|
||||
|
||||
@@ -2555,7 +2529,7 @@ static __always_inline void
|
||||
fire_sched_out_preempt_notifiers(struct task_struct *curr,
|
||||
struct task_struct *next)
|
||||
{
|
||||
if (static_key_false(&preempt_notifier_key))
|
||||
if (static_branch_unlikely(&preempt_notifier_key))
|
||||
__fire_sched_out_preempt_notifiers(curr, next);
|
||||
}
|
||||
|
||||
@@ -2629,6 +2603,18 @@ static inline void finish_lock_switch(struct rq *rq)
|
||||
raw_spin_unlock_irq(&rq->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOP if the arch has not defined these:
|
||||
*/
|
||||
|
||||
#ifndef prepare_arch_switch
|
||||
# define prepare_arch_switch(next) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef finish_arch_post_lock_switch
|
||||
# define finish_arch_post_lock_switch() do { } while (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* prepare_task_switch - prepare to switch tasks
|
||||
* @rq: the runqueue preparing to switch
|
||||
@@ -3037,7 +3023,7 @@ unsigned long long task_sched_runtime(struct task_struct *p)
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
|
||||
/*
|
||||
* 64-bit doesn't need locks to atomically read a 64bit value.
|
||||
* 64-bit doesn't need locks to atomically read a 64-bit value.
|
||||
* So we have a optimization chance when the task's delta_exec is 0.
|
||||
* Reading ->on_cpu is racy, but this is ok.
|
||||
*
|
||||
@@ -3096,35 +3082,99 @@ void scheduler_tick(void)
|
||||
rq->idle_balance = idle_cpu(cpu);
|
||||
trigger_load_balance(rq);
|
||||
#endif
|
||||
rq_last_tick_reset(rq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
/**
|
||||
* scheduler_tick_max_deferment
|
||||
*
|
||||
* Keep at least one tick per second when a single
|
||||
* active task is running because the scheduler doesn't
|
||||
* yet completely support full dynticks environment.
|
||||
*
|
||||
* This makes sure that uptime, CFS vruntime, load
|
||||
* balancing, etc... continue to move forward, even
|
||||
* with a very low granularity.
|
||||
*
|
||||
* Return: Maximum deferment in nanoseconds.
|
||||
*/
|
||||
u64 scheduler_tick_max_deferment(void)
|
||||
|
||||
struct tick_work {
|
||||
int cpu;
|
||||
struct delayed_work work;
|
||||
};
|
||||
|
||||
static struct tick_work __percpu *tick_work_cpu;
|
||||
|
||||
static void sched_tick_remote(struct work_struct *work)
|
||||
{
|
||||
struct rq *rq = this_rq();
|
||||
unsigned long next, now = READ_ONCE(jiffies);
|
||||
struct delayed_work *dwork = to_delayed_work(work);
|
||||
struct tick_work *twork = container_of(dwork, struct tick_work, work);
|
||||
int cpu = twork->cpu;
|
||||
struct rq *rq = cpu_rq(cpu);
|
||||
struct rq_flags rf;
|
||||
|
||||
next = rq->last_sched_tick + HZ;
|
||||
/*
|
||||
* Handle the tick only if it appears the remote CPU is running in full
|
||||
* dynticks mode. The check is racy by nature, but missing a tick or
|
||||
* having one too much is no big deal because the scheduler tick updates
|
||||
* statistics and checks timeslices in a time-independent way, regardless
|
||||
* of when exactly it is running.
|
||||
*/
|
||||
if (!idle_cpu(cpu) && tick_nohz_tick_stopped_cpu(cpu)) {
|
||||
struct task_struct *curr;
|
||||
u64 delta;
|
||||
|
||||
if (time_before_eq(next, now))
|
||||
return 0;
|
||||
rq_lock_irq(rq, &rf);
|
||||
update_rq_clock(rq);
|
||||
curr = rq->curr;
|
||||
delta = rq_clock_task(rq) - curr->se.exec_start;
|
||||
|
||||
return jiffies_to_nsecs(next - now);
|
||||
/*
|
||||
* Make sure the next tick runs within a reasonable
|
||||
* amount of time.
|
||||
*/
|
||||
WARN_ON_ONCE(delta > (u64)NSEC_PER_SEC * 3);
|
||||
curr->sched_class->task_tick(rq, curr, 0);
|
||||
rq_unlock_irq(rq, &rf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the remote tick once per second (1Hz). This arbitrary
|
||||
* frequency is large enough to avoid overload but short enough
|
||||
* to keep scheduler internal stats reasonably up to date.
|
||||
*/
|
||||
queue_delayed_work(system_unbound_wq, dwork, HZ);
|
||||
}
|
||||
|
||||
static void sched_tick_start(int cpu)
|
||||
{
|
||||
struct tick_work *twork;
|
||||
|
||||
if (housekeeping_cpu(cpu, HK_FLAG_TICK))
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(!tick_work_cpu);
|
||||
|
||||
twork = per_cpu_ptr(tick_work_cpu, cpu);
|
||||
twork->cpu = cpu;
|
||||
INIT_DELAYED_WORK(&twork->work, sched_tick_remote);
|
||||
queue_delayed_work(system_unbound_wq, &twork->work, HZ);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
static void sched_tick_stop(int cpu)
|
||||
{
|
||||
struct tick_work *twork;
|
||||
|
||||
if (housekeeping_cpu(cpu, HK_FLAG_TICK))
|
||||
return;
|
||||
|
||||
WARN_ON_ONCE(!tick_work_cpu);
|
||||
|
||||
twork = per_cpu_ptr(tick_work_cpu, cpu);
|
||||
cancel_delayed_work_sync(&twork->work);
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
int __init sched_tick_offload_init(void)
|
||||
{
|
||||
tick_work_cpu = alloc_percpu(struct tick_work);
|
||||
BUG_ON(!tick_work_cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_NO_HZ_FULL */
|
||||
static inline void sched_tick_start(int cpu) { }
|
||||
static inline void sched_tick_stop(int cpu) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
|
||||
@@ -4892,7 +4942,7 @@ SYSCALL_DEFINE3(sched_getaffinity, pid_t, pid, unsigned int, len,
|
||||
*
|
||||
* Return: 0.
|
||||
*/
|
||||
SYSCALL_DEFINE0(sched_yield)
|
||||
static void do_sched_yield(void)
|
||||
{
|
||||
struct rq_flags rf;
|
||||
struct rq *rq;
|
||||
@@ -4913,7 +4963,11 @@ SYSCALL_DEFINE0(sched_yield)
|
||||
sched_preempt_enable_no_resched();
|
||||
|
||||
schedule();
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE0(sched_yield)
|
||||
{
|
||||
do_sched_yield();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4997,7 +5051,7 @@ EXPORT_SYMBOL(__cond_resched_softirq);
|
||||
void __sched yield(void)
|
||||
{
|
||||
set_current_state(TASK_RUNNING);
|
||||
sys_sched_yield();
|
||||
do_sched_yield();
|
||||
}
|
||||
EXPORT_SYMBOL(yield);
|
||||
|
||||
@@ -5786,6 +5840,7 @@ int sched_cpu_starting(unsigned int cpu)
|
||||
{
|
||||
set_cpu_rq_start_time(cpu);
|
||||
sched_rq_cpu_starting(cpu);
|
||||
sched_tick_start(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5797,6 +5852,7 @@ int sched_cpu_dying(unsigned int cpu)
|
||||
|
||||
/* Handle pending wakeups and then migrate everything off */
|
||||
sched_ttwu_pending();
|
||||
sched_tick_stop(cpu);
|
||||
|
||||
rq_lock_irqsave(rq, &rf);
|
||||
if (rq->rd) {
|
||||
@@ -5809,7 +5865,7 @@ int sched_cpu_dying(unsigned int cpu)
|
||||
|
||||
calc_load_migrate(rq);
|
||||
update_max_interval();
|
||||
nohz_balance_exit_idle(cpu);
|
||||
nohz_balance_exit_idle(rq);
|
||||
hrtick_clear(rq);
|
||||
return 0;
|
||||
}
|
||||
@@ -6022,13 +6078,11 @@ void __init sched_init(void)
|
||||
rq_attach_root(rq, &def_root_domain);
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
rq->last_load_update_tick = jiffies;
|
||||
rq->nohz_flags = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
rq->last_sched_tick = 0;
|
||||
rq->last_blocked_load_update_tick = jiffies;
|
||||
atomic_set(&rq->nohz_flags, 0);
|
||||
#endif
|
||||
#endif /* CONFIG_SMP */
|
||||
init_rq_hrtick(rq);
|
||||
hrtick_rq_init(rq);
|
||||
atomic_set(&rq->nr_iowait, 0);
|
||||
}
|
||||
|
||||
@@ -7027,3 +7081,5 @@ const u32 sched_prio_to_wmult[40] = {
|
||||
/* 10 */ 39045157, 49367440, 61356676, 76695844, 95443717,
|
||||
/* 15 */ 119304647, 148102320, 186737708, 238609294, 286331153,
|
||||
};
|
||||
|
||||
#undef CREATE_TRACE_POINTS
|
||||
|
||||
+11
-22
@@ -1,24 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/cgroup.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* CPU accounting code for task groups.
|
||||
*
|
||||
* Based on the work by Paul Menage (menage@google.com) and Balbir Singh
|
||||
* (balbir@in.ibm.com).
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
/* Time spent by the tasks of the cpu accounting group executing in ... */
|
||||
/* Time spent by the tasks of the CPU accounting group executing in ... */
|
||||
enum cpuacct_stat_index {
|
||||
CPUACCT_STAT_USER, /* ... user mode */
|
||||
CPUACCT_STAT_SYSTEM, /* ... kernel mode */
|
||||
@@ -35,12 +24,12 @@ struct cpuacct_usage {
|
||||
u64 usages[CPUACCT_STAT_NSTATS];
|
||||
};
|
||||
|
||||
/* track cpu usage of a group of tasks and its child groups */
|
||||
/* track CPU usage of a group of tasks and its child groups */
|
||||
struct cpuacct {
|
||||
struct cgroup_subsys_state css;
|
||||
/* cpuusage holds pointer to a u64-type object on every cpu */
|
||||
struct cpuacct_usage __percpu *cpuusage;
|
||||
struct kernel_cpustat __percpu *cpustat;
|
||||
struct cgroup_subsys_state css;
|
||||
/* cpuusage holds pointer to a u64-type object on every CPU */
|
||||
struct cpuacct_usage __percpu *cpuusage;
|
||||
struct kernel_cpustat __percpu *cpustat;
|
||||
};
|
||||
|
||||
static inline struct cpuacct *css_ca(struct cgroup_subsys_state *css)
|
||||
@@ -48,7 +37,7 @@ static inline struct cpuacct *css_ca(struct cgroup_subsys_state *css)
|
||||
return css ? container_of(css, struct cpuacct, css) : NULL;
|
||||
}
|
||||
|
||||
/* return cpu accounting group to which this task belongs */
|
||||
/* Return CPU accounting group to which this task belongs */
|
||||
static inline struct cpuacct *task_ca(struct task_struct *tsk)
|
||||
{
|
||||
return css_ca(task_css(tsk, cpuacct_cgrp_id));
|
||||
@@ -65,7 +54,7 @@ static struct cpuacct root_cpuacct = {
|
||||
.cpuusage = &root_cpuacct_cpuusage,
|
||||
};
|
||||
|
||||
/* create a new cpu accounting group */
|
||||
/* Create a new CPU accounting group */
|
||||
static struct cgroup_subsys_state *
|
||||
cpuacct_css_alloc(struct cgroup_subsys_state *parent_css)
|
||||
{
|
||||
@@ -96,7 +85,7 @@ out:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* destroy an existing cpu accounting group */
|
||||
/* Destroy an existing CPU accounting group */
|
||||
static void cpuacct_css_free(struct cgroup_subsys_state *css)
|
||||
{
|
||||
struct cpuacct *ca = css_ca(css);
|
||||
@@ -162,7 +151,7 @@ static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* return total cpu usage (in nanoseconds) of a group */
|
||||
/* Return total CPU usage (in nanoseconds) of a group */
|
||||
static u64 __cpuusage_read(struct cgroup_subsys_state *css,
|
||||
enum cpuacct_stat_index index)
|
||||
{
|
||||
|
||||
+11
-12
@@ -10,11 +10,7 @@
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include "cpudeadline.h"
|
||||
#include "sched.h"
|
||||
|
||||
static inline int parent(int i)
|
||||
{
|
||||
@@ -42,8 +38,9 @@ static void cpudl_heapify_down(struct cpudl *cp, int idx)
|
||||
return;
|
||||
|
||||
/* adapted from lib/prio_heap.c */
|
||||
while(1) {
|
||||
while (1) {
|
||||
u64 largest_dl;
|
||||
|
||||
l = left_child(idx);
|
||||
r = right_child(idx);
|
||||
largest = idx;
|
||||
@@ -131,6 +128,7 @@ int cpudl_find(struct cpudl *cp, struct task_struct *p,
|
||||
return 1;
|
||||
} else {
|
||||
int best_cpu = cpudl_maximum(cp);
|
||||
|
||||
WARN_ON(best_cpu != -1 && !cpu_present(best_cpu));
|
||||
|
||||
if (cpumask_test_cpu(best_cpu, &p->cpus_allowed) &&
|
||||
@@ -145,9 +143,9 @@ int cpudl_find(struct cpudl *cp, struct task_struct *p,
|
||||
}
|
||||
|
||||
/*
|
||||
* cpudl_clear - remove a cpu from the cpudl max-heap
|
||||
* cpudl_clear - remove a CPU from the cpudl max-heap
|
||||
* @cp: the cpudl max-heap context
|
||||
* @cpu: the target cpu
|
||||
* @cpu: the target CPU
|
||||
*
|
||||
* Notes: assumes cpu_rq(cpu)->lock is locked
|
||||
*
|
||||
@@ -186,8 +184,8 @@ void cpudl_clear(struct cpudl *cp, int cpu)
|
||||
/*
|
||||
* cpudl_set - update the cpudl max-heap
|
||||
* @cp: the cpudl max-heap context
|
||||
* @cpu: the target cpu
|
||||
* @dl: the new earliest deadline for this cpu
|
||||
* @cpu: the target CPU
|
||||
* @dl: the new earliest deadline for this CPU
|
||||
*
|
||||
* Notes: assumes cpu_rq(cpu)->lock is locked
|
||||
*
|
||||
@@ -205,6 +203,7 @@ void cpudl_set(struct cpudl *cp, int cpu, u64 dl)
|
||||
old_idx = cp->elements[cpu].idx;
|
||||
if (old_idx == IDX_INVALID) {
|
||||
int new_idx = cp->size++;
|
||||
|
||||
cp->elements[new_idx].dl = dl;
|
||||
cp->elements[new_idx].cpu = cpu;
|
||||
cp->elements[cpu].idx = new_idx;
|
||||
@@ -221,7 +220,7 @@ void cpudl_set(struct cpudl *cp, int cpu, u64 dl)
|
||||
/*
|
||||
* cpudl_set_freecpu - Set the cpudl.free_cpus
|
||||
* @cp: the cpudl max-heap context
|
||||
* @cpu: rd attached cpu
|
||||
* @cpu: rd attached CPU
|
||||
*/
|
||||
void cpudl_set_freecpu(struct cpudl *cp, int cpu)
|
||||
{
|
||||
@@ -231,7 +230,7 @@ void cpudl_set_freecpu(struct cpudl *cp, int cpu)
|
||||
/*
|
||||
* cpudl_clear_freecpu - Clear the cpudl.free_cpus
|
||||
* @cp: the cpudl max-heap context
|
||||
* @cpu: rd attached cpu
|
||||
* @cpu: rd attached CPU
|
||||
*/
|
||||
void cpudl_clear_freecpu(struct cpudl *cp, int cpu)
|
||||
{
|
||||
|
||||
+10
-19
@@ -1,35 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_CPUDL_H
|
||||
#define _LINUX_CPUDL_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/deadline.h>
|
||||
|
||||
#define IDX_INVALID -1
|
||||
#define IDX_INVALID -1
|
||||
|
||||
struct cpudl_item {
|
||||
u64 dl;
|
||||
int cpu;
|
||||
int idx;
|
||||
u64 dl;
|
||||
int cpu;
|
||||
int idx;
|
||||
};
|
||||
|
||||
struct cpudl {
|
||||
raw_spinlock_t lock;
|
||||
int size;
|
||||
cpumask_var_t free_cpus;
|
||||
struct cpudl_item *elements;
|
||||
raw_spinlock_t lock;
|
||||
int size;
|
||||
cpumask_var_t free_cpus;
|
||||
struct cpudl_item *elements;
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
int cpudl_find(struct cpudl *cp, struct task_struct *p,
|
||||
struct cpumask *later_mask);
|
||||
int cpudl_find(struct cpudl *cp, struct task_struct *p, struct cpumask *later_mask);
|
||||
void cpudl_set(struct cpudl *cp, int cpu, u64 dl);
|
||||
void cpudl_clear(struct cpudl *cp, int cpu);
|
||||
int cpudl_init(struct cpudl *cp);
|
||||
int cpudl_init(struct cpudl *cp);
|
||||
void cpudl_set_freecpu(struct cpudl *cp, int cpu);
|
||||
void cpudl_clear_freecpu(struct cpudl *cp, int cpu);
|
||||
void cpudl_cleanup(struct cpudl *cp);
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#endif /* _LINUX_CPUDL_H */
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
DEFINE_PER_CPU(struct update_util_data *, cpufreq_update_util_data);
|
||||
|
||||
+109
-104
@@ -11,61 +11,56 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
#include <trace/events/power.h>
|
||||
|
||||
struct sugov_tunables {
|
||||
struct gov_attr_set attr_set;
|
||||
unsigned int rate_limit_us;
|
||||
struct gov_attr_set attr_set;
|
||||
unsigned int rate_limit_us;
|
||||
};
|
||||
|
||||
struct sugov_policy {
|
||||
struct cpufreq_policy *policy;
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
struct sugov_tunables *tunables;
|
||||
struct list_head tunables_hook;
|
||||
struct sugov_tunables *tunables;
|
||||
struct list_head tunables_hook;
|
||||
|
||||
raw_spinlock_t update_lock; /* For shared policies */
|
||||
u64 last_freq_update_time;
|
||||
s64 freq_update_delay_ns;
|
||||
unsigned int next_freq;
|
||||
unsigned int cached_raw_freq;
|
||||
raw_spinlock_t update_lock; /* For shared policies */
|
||||
u64 last_freq_update_time;
|
||||
s64 freq_update_delay_ns;
|
||||
unsigned int next_freq;
|
||||
unsigned int cached_raw_freq;
|
||||
|
||||
/* The next fields are only needed if fast switch cannot be used. */
|
||||
struct irq_work irq_work;
|
||||
struct kthread_work work;
|
||||
struct mutex work_lock;
|
||||
struct kthread_worker worker;
|
||||
struct task_struct *thread;
|
||||
bool work_in_progress;
|
||||
/* The next fields are only needed if fast switch cannot be used: */
|
||||
struct irq_work irq_work;
|
||||
struct kthread_work work;
|
||||
struct mutex work_lock;
|
||||
struct kthread_worker worker;
|
||||
struct task_struct *thread;
|
||||
bool work_in_progress;
|
||||
|
||||
bool need_freq_update;
|
||||
bool need_freq_update;
|
||||
};
|
||||
|
||||
struct sugov_cpu {
|
||||
struct update_util_data update_util;
|
||||
struct sugov_policy *sg_policy;
|
||||
unsigned int cpu;
|
||||
struct update_util_data update_util;
|
||||
struct sugov_policy *sg_policy;
|
||||
unsigned int cpu;
|
||||
|
||||
bool iowait_boost_pending;
|
||||
unsigned int iowait_boost;
|
||||
unsigned int iowait_boost_max;
|
||||
bool iowait_boost_pending;
|
||||
unsigned int iowait_boost;
|
||||
unsigned int iowait_boost_max;
|
||||
u64 last_update;
|
||||
|
||||
/* The fields below are only needed when sharing a policy. */
|
||||
unsigned long util_cfs;
|
||||
unsigned long util_dl;
|
||||
unsigned long max;
|
||||
unsigned int flags;
|
||||
/* The fields below are only needed when sharing a policy: */
|
||||
unsigned long util_cfs;
|
||||
unsigned long util_dl;
|
||||
unsigned long max;
|
||||
|
||||
/* The field below is for single-CPU policies only. */
|
||||
/* The field below is for single-CPU policies only: */
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
unsigned long saved_idle_calls;
|
||||
unsigned long saved_idle_calls;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -79,9 +74,9 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
|
||||
|
||||
/*
|
||||
* Since cpufreq_update_util() is called with rq->lock held for
|
||||
* the @target_cpu, our per-cpu data is fully serialized.
|
||||
* the @target_cpu, our per-CPU data is fully serialized.
|
||||
*
|
||||
* However, drivers cannot in general deal with cross-cpu
|
||||
* However, drivers cannot in general deal with cross-CPU
|
||||
* requests, so while get_next_freq() will work, our
|
||||
* sugov_update_commit() call may not for the fast switching platforms.
|
||||
*
|
||||
@@ -111,6 +106,7 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time)
|
||||
}
|
||||
|
||||
delta_ns = time - sg_policy->last_freq_update_time;
|
||||
|
||||
return delta_ns >= sg_policy->freq_update_delay_ns;
|
||||
}
|
||||
|
||||
@@ -186,17 +182,28 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu)
|
||||
|
||||
static unsigned long sugov_aggregate_util(struct sugov_cpu *sg_cpu)
|
||||
{
|
||||
struct rq *rq = cpu_rq(sg_cpu->cpu);
|
||||
unsigned long util;
|
||||
|
||||
if (rq->rt.rt_nr_running) {
|
||||
util = sg_cpu->max;
|
||||
} else {
|
||||
util = sg_cpu->util_dl;
|
||||
if (rq->cfs.h_nr_running)
|
||||
util += sg_cpu->util_cfs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ideally we would like to set util_dl as min/guaranteed freq and
|
||||
* util_cfs + util_dl as requested freq. However, cpufreq is not yet
|
||||
* ready for such an interface. So, we only do the latter for now.
|
||||
*/
|
||||
return min(sg_cpu->util_cfs + sg_cpu->util_dl, sg_cpu->max);
|
||||
return min(util, sg_cpu->max);
|
||||
}
|
||||
|
||||
static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time)
|
||||
static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time, unsigned int flags)
|
||||
{
|
||||
if (sg_cpu->flags & SCHED_CPUFREQ_IOWAIT) {
|
||||
if (flags & SCHED_CPUFREQ_IOWAIT) {
|
||||
if (sg_cpu->iowait_boost_pending)
|
||||
return;
|
||||
|
||||
@@ -260,43 +267,51 @@ static bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu)
|
||||
static inline bool sugov_cpu_is_busy(struct sugov_cpu *sg_cpu) { return false; }
|
||||
#endif /* CONFIG_NO_HZ_COMMON */
|
||||
|
||||
/*
|
||||
* Make sugov_should_update_freq() ignore the rate limit when DL
|
||||
* has increased the utilization.
|
||||
*/
|
||||
static inline void ignore_dl_rate_limit(struct sugov_cpu *sg_cpu, struct sugov_policy *sg_policy)
|
||||
{
|
||||
if (cpu_util_dl(cpu_rq(sg_cpu->cpu)) > sg_cpu->util_dl)
|
||||
sg_policy->need_freq_update = true;
|
||||
}
|
||||
|
||||
static void sugov_update_single(struct update_util_data *hook, u64 time,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
|
||||
struct sugov_policy *sg_policy = sg_cpu->sg_policy;
|
||||
struct cpufreq_policy *policy = sg_policy->policy;
|
||||
unsigned long util, max;
|
||||
unsigned int next_f;
|
||||
bool busy;
|
||||
|
||||
sugov_set_iowait_boost(sg_cpu, time);
|
||||
sugov_set_iowait_boost(sg_cpu, time, flags);
|
||||
sg_cpu->last_update = time;
|
||||
|
||||
ignore_dl_rate_limit(sg_cpu, sg_policy);
|
||||
|
||||
if (!sugov_should_update_freq(sg_policy, time))
|
||||
return;
|
||||
|
||||
busy = sugov_cpu_is_busy(sg_cpu);
|
||||
|
||||
if (flags & SCHED_CPUFREQ_RT) {
|
||||
next_f = policy->cpuinfo.max_freq;
|
||||
} else {
|
||||
sugov_get_util(sg_cpu);
|
||||
max = sg_cpu->max;
|
||||
util = sugov_aggregate_util(sg_cpu);
|
||||
sugov_iowait_boost(sg_cpu, &util, &max);
|
||||
next_f = get_next_freq(sg_policy, util, max);
|
||||
/*
|
||||
* Do not reduce the frequency if the CPU has not been idle
|
||||
* recently, as the reduction is likely to be premature then.
|
||||
*/
|
||||
if (busy && next_f < sg_policy->next_freq) {
|
||||
next_f = sg_policy->next_freq;
|
||||
sugov_get_util(sg_cpu);
|
||||
max = sg_cpu->max;
|
||||
util = sugov_aggregate_util(sg_cpu);
|
||||
sugov_iowait_boost(sg_cpu, &util, &max);
|
||||
next_f = get_next_freq(sg_policy, util, max);
|
||||
/*
|
||||
* Do not reduce the frequency if the CPU has not been idle
|
||||
* recently, as the reduction is likely to be premature then.
|
||||
*/
|
||||
if (busy && next_f < sg_policy->next_freq) {
|
||||
next_f = sg_policy->next_freq;
|
||||
|
||||
/* Reset cached freq as next_freq has changed */
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
}
|
||||
/* Reset cached freq as next_freq has changed */
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
}
|
||||
|
||||
sugov_update_commit(sg_policy, time, next_f);
|
||||
}
|
||||
|
||||
@@ -312,6 +327,8 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, u64 time)
|
||||
unsigned long j_util, j_max;
|
||||
s64 delta_ns;
|
||||
|
||||
sugov_get_util(j_sg_cpu);
|
||||
|
||||
/*
|
||||
* If the CFS CPU utilization was last updated before the
|
||||
* previous frequency update and the time elapsed between the
|
||||
@@ -325,28 +342,22 @@ static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu, u64 time)
|
||||
if (delta_ns > TICK_NSEC) {
|
||||
j_sg_cpu->iowait_boost = 0;
|
||||
j_sg_cpu->iowait_boost_pending = false;
|
||||
j_sg_cpu->util_cfs = 0;
|
||||
if (j_sg_cpu->util_dl == 0)
|
||||
continue;
|
||||
}
|
||||
if (j_sg_cpu->flags & SCHED_CPUFREQ_RT)
|
||||
return policy->cpuinfo.max_freq;
|
||||
|
||||
j_max = j_sg_cpu->max;
|
||||
j_util = sugov_aggregate_util(j_sg_cpu);
|
||||
sugov_iowait_boost(j_sg_cpu, &j_util, &j_max);
|
||||
if (j_util * max > j_max * util) {
|
||||
util = j_util;
|
||||
max = j_max;
|
||||
}
|
||||
|
||||
sugov_iowait_boost(j_sg_cpu, &util, &max);
|
||||
}
|
||||
|
||||
return get_next_freq(sg_policy, util, max);
|
||||
}
|
||||
|
||||
static void sugov_update_shared(struct update_util_data *hook, u64 time,
|
||||
unsigned int flags)
|
||||
static void
|
||||
sugov_update_shared(struct update_util_data *hook, u64 time, unsigned int flags)
|
||||
{
|
||||
struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
|
||||
struct sugov_policy *sg_policy = sg_cpu->sg_policy;
|
||||
@@ -354,18 +365,13 @@ static void sugov_update_shared(struct update_util_data *hook, u64 time,
|
||||
|
||||
raw_spin_lock(&sg_policy->update_lock);
|
||||
|
||||
sugov_get_util(sg_cpu);
|
||||
sg_cpu->flags = flags;
|
||||
|
||||
sugov_set_iowait_boost(sg_cpu, time);
|
||||
sugov_set_iowait_boost(sg_cpu, time, flags);
|
||||
sg_cpu->last_update = time;
|
||||
|
||||
if (sugov_should_update_freq(sg_policy, time)) {
|
||||
if (flags & SCHED_CPUFREQ_RT)
|
||||
next_f = sg_policy->policy->cpuinfo.max_freq;
|
||||
else
|
||||
next_f = sugov_next_freq_shared(sg_cpu, time);
|
||||
ignore_dl_rate_limit(sg_cpu, sg_policy);
|
||||
|
||||
if (sugov_should_update_freq(sg_policy, time)) {
|
||||
next_f = sugov_next_freq_shared(sg_cpu, time);
|
||||
sugov_update_commit(sg_policy, time, next_f);
|
||||
}
|
||||
|
||||
@@ -423,8 +429,8 @@ static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf)
|
||||
return sprintf(buf, "%u\n", tunables->rate_limit_us);
|
||||
}
|
||||
|
||||
static ssize_t rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf,
|
||||
size_t count)
|
||||
static ssize_t
|
||||
rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count)
|
||||
{
|
||||
struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
|
||||
struct sugov_policy *sg_policy;
|
||||
@@ -479,11 +485,11 @@ static int sugov_kthread_create(struct sugov_policy *sg_policy)
|
||||
{
|
||||
struct task_struct *thread;
|
||||
struct sched_attr attr = {
|
||||
.size = sizeof(struct sched_attr),
|
||||
.sched_policy = SCHED_DEADLINE,
|
||||
.sched_flags = SCHED_FLAG_SUGOV,
|
||||
.sched_nice = 0,
|
||||
.sched_priority = 0,
|
||||
.size = sizeof(struct sched_attr),
|
||||
.sched_policy = SCHED_DEADLINE,
|
||||
.sched_flags = SCHED_FLAG_SUGOV,
|
||||
.sched_nice = 0,
|
||||
.sched_priority = 0,
|
||||
/*
|
||||
* Fake (unused) bandwidth; workaround to "fix"
|
||||
* priority inheritance.
|
||||
@@ -662,21 +668,20 @@ static int sugov_start(struct cpufreq_policy *policy)
|
||||
struct sugov_policy *sg_policy = policy->governor_data;
|
||||
unsigned int cpu;
|
||||
|
||||
sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
|
||||
sg_policy->last_freq_update_time = 0;
|
||||
sg_policy->next_freq = UINT_MAX;
|
||||
sg_policy->work_in_progress = false;
|
||||
sg_policy->need_freq_update = false;
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
|
||||
sg_policy->last_freq_update_time = 0;
|
||||
sg_policy->next_freq = UINT_MAX;
|
||||
sg_policy->work_in_progress = false;
|
||||
sg_policy->need_freq_update = false;
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
|
||||
for_each_cpu(cpu, policy->cpus) {
|
||||
struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
|
||||
|
||||
memset(sg_cpu, 0, sizeof(*sg_cpu));
|
||||
sg_cpu->cpu = cpu;
|
||||
sg_cpu->sg_policy = sg_policy;
|
||||
sg_cpu->flags = 0;
|
||||
sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
|
||||
sg_cpu->cpu = cpu;
|
||||
sg_cpu->sg_policy = sg_policy;
|
||||
sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
|
||||
}
|
||||
|
||||
for_each_cpu(cpu, policy->cpus) {
|
||||
@@ -720,14 +725,14 @@ static void sugov_limits(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
static struct cpufreq_governor schedutil_gov = {
|
||||
.name = "schedutil",
|
||||
.owner = THIS_MODULE,
|
||||
.dynamic_switching = true,
|
||||
.init = sugov_init,
|
||||
.exit = sugov_exit,
|
||||
.start = sugov_start,
|
||||
.stop = sugov_stop,
|
||||
.limits = sugov_limits,
|
||||
.name = "schedutil",
|
||||
.owner = THIS_MODULE,
|
||||
.dynamic_switching = true,
|
||||
.init = sugov_init,
|
||||
.exit = sugov_exit,
|
||||
.start = sugov_start,
|
||||
.stop = sugov_stop,
|
||||
.limits = sugov_limits,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL
|
||||
|
||||
+5
-10
@@ -14,7 +14,7 @@
|
||||
*
|
||||
* going from the lowest priority to the highest. CPUs in the INVALID state
|
||||
* are not eligible for routing. The system maintains this state with
|
||||
* a 2 dimensional bitmap (the first for priority class, the second for cpus
|
||||
* a 2 dimensional bitmap (the first for priority class, the second for CPUs
|
||||
* in that class). Therefore a typical application without affinity
|
||||
* restrictions can find a suitable CPU with O(1) complexity (e.g. two bit
|
||||
* searches). For tasks with affinity restrictions, the algorithm has a
|
||||
@@ -26,12 +26,7 @@
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/rt.h>
|
||||
#include <linux/slab.h>
|
||||
#include "cpupri.h"
|
||||
#include "sched.h"
|
||||
|
||||
/* Convert between a 140 based task->prio, and our 102 based cpupri */
|
||||
static int convert_prio(int prio)
|
||||
@@ -128,9 +123,9 @@ int cpupri_find(struct cpupri *cp, struct task_struct *p,
|
||||
}
|
||||
|
||||
/**
|
||||
* cpupri_set - update the cpu priority setting
|
||||
* cpupri_set - update the CPU priority setting
|
||||
* @cp: The cpupri context
|
||||
* @cpu: The target cpu
|
||||
* @cpu: The target CPU
|
||||
* @newpri: The priority (INVALID-RT99) to assign to this CPU
|
||||
*
|
||||
* Note: Assumes cpu_rq(cpu)->lock is locked
|
||||
@@ -151,7 +146,7 @@ void cpupri_set(struct cpupri *cp, int cpu, int newpri)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the cpu was currently mapped to a different value, we
|
||||
* If the CPU was currently mapped to a different value, we
|
||||
* need to map it to the new value then remove the old value.
|
||||
* Note, we must add the new value first, otherwise we risk the
|
||||
* cpu being missed by the priority loop in cpupri_find.
|
||||
|
||||
+9
-16
@@ -1,32 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_CPUPRI_H
|
||||
#define _LINUX_CPUPRI_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
||||
#define CPUPRI_NR_PRIORITIES (MAX_RT_PRIO + 2)
|
||||
|
||||
#define CPUPRI_INVALID -1
|
||||
#define CPUPRI_IDLE 0
|
||||
#define CPUPRI_NORMAL 1
|
||||
#define CPUPRI_INVALID -1
|
||||
#define CPUPRI_IDLE 0
|
||||
#define CPUPRI_NORMAL 1
|
||||
/* values 2-101 are RT priorities 0-99 */
|
||||
|
||||
struct cpupri_vec {
|
||||
atomic_t count;
|
||||
cpumask_var_t mask;
|
||||
atomic_t count;
|
||||
cpumask_var_t mask;
|
||||
};
|
||||
|
||||
struct cpupri {
|
||||
struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES];
|
||||
int *cpu_to_pri;
|
||||
struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES];
|
||||
int *cpu_to_pri;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
int cpupri_find(struct cpupri *cp,
|
||||
struct task_struct *p, struct cpumask *lowest_mask);
|
||||
int cpupri_find(struct cpupri *cp, struct task_struct *p, struct cpumask *lowest_mask);
|
||||
void cpupri_set(struct cpupri *cp, int cpu, int pri);
|
||||
int cpupri_init(struct cpupri *cp);
|
||||
int cpupri_init(struct cpupri *cp);
|
||||
void cpupri_cleanup(struct cpupri *cp);
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_CPUPRI_H */
|
||||
|
||||
+27
-31
@@ -1,10 +1,6 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tsacct_kern.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/context_tracking.h>
|
||||
#include <linux/sched/cputime.h>
|
||||
/*
|
||||
* Simple CPU accounting cgroup controller
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
|
||||
@@ -113,9 +109,9 @@ static inline void task_group_account_field(struct task_struct *p, int index,
|
||||
}
|
||||
|
||||
/*
|
||||
* Account user cpu time to a process.
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* @cputime: the cpu time spent in user space since the last update
|
||||
* Account user CPU time to a process.
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @cputime: the CPU time spent in user space since the last update
|
||||
*/
|
||||
void account_user_time(struct task_struct *p, u64 cputime)
|
||||
{
|
||||
@@ -135,9 +131,9 @@ void account_user_time(struct task_struct *p, u64 cputime)
|
||||
}
|
||||
|
||||
/*
|
||||
* Account guest cpu time to a process.
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* @cputime: the cpu time spent in virtual machine since the last update
|
||||
* Account guest CPU time to a process.
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @cputime: the CPU time spent in virtual machine since the last update
|
||||
*/
|
||||
void account_guest_time(struct task_struct *p, u64 cputime)
|
||||
{
|
||||
@@ -159,9 +155,9 @@ void account_guest_time(struct task_struct *p, u64 cputime)
|
||||
}
|
||||
|
||||
/*
|
||||
* Account system cpu time to a process and desired cpustat field
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* @cputime: the cpu time spent in kernel space since the last update
|
||||
* Account system CPU time to a process and desired cpustat field
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @cputime: the CPU time spent in kernel space since the last update
|
||||
* @index: pointer to cpustat field that has to be updated
|
||||
*/
|
||||
void account_system_index_time(struct task_struct *p,
|
||||
@@ -179,10 +175,10 @@ void account_system_index_time(struct task_struct *p,
|
||||
}
|
||||
|
||||
/*
|
||||
* Account system cpu time to a process.
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* Account system CPU time to a process.
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @hardirq_offset: the offset to subtract from hardirq_count()
|
||||
* @cputime: the cpu time spent in kernel space since the last update
|
||||
* @cputime: the CPU time spent in kernel space since the last update
|
||||
*/
|
||||
void account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime)
|
||||
{
|
||||
@@ -205,7 +201,7 @@ void account_system_time(struct task_struct *p, int hardirq_offset, u64 cputime)
|
||||
|
||||
/*
|
||||
* Account for involuntary wait time.
|
||||
* @cputime: the cpu time spent in involuntary wait
|
||||
* @cputime: the CPU time spent in involuntary wait
|
||||
*/
|
||||
void account_steal_time(u64 cputime)
|
||||
{
|
||||
@@ -216,7 +212,7 @@ void account_steal_time(u64 cputime)
|
||||
|
||||
/*
|
||||
* Account for idle time.
|
||||
* @cputime: the cpu time spent in idle wait
|
||||
* @cputime: the CPU time spent in idle wait
|
||||
*/
|
||||
void account_idle_time(u64 cputime)
|
||||
{
|
||||
@@ -338,7 +334,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times)
|
||||
#ifdef CONFIG_IRQ_TIME_ACCOUNTING
|
||||
/*
|
||||
* Account a tick to a process and cpustat
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @user_tick: is the tick from userspace
|
||||
* @rq: the pointer to rq
|
||||
*
|
||||
@@ -400,17 +396,16 @@ static void irqtime_account_idle_ticks(int ticks)
|
||||
irqtime_account_process_tick(current, 0, rq, ticks);
|
||||
}
|
||||
#else /* CONFIG_IRQ_TIME_ACCOUNTING */
|
||||
static inline void irqtime_account_idle_ticks(int ticks) {}
|
||||
static inline void irqtime_account_idle_ticks(int ticks) { }
|
||||
static inline void irqtime_account_process_tick(struct task_struct *p, int user_tick,
|
||||
struct rq *rq, int nr_ticks) {}
|
||||
struct rq *rq, int nr_ticks) { }
|
||||
#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
|
||||
|
||||
/*
|
||||
* Use precise platform statistics if available:
|
||||
*/
|
||||
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
|
||||
|
||||
#ifndef __ARCH_HAS_VTIME_TASK_SWITCH
|
||||
# ifndef __ARCH_HAS_VTIME_TASK_SWITCH
|
||||
void vtime_common_task_switch(struct task_struct *prev)
|
||||
{
|
||||
if (is_idle_task(prev))
|
||||
@@ -421,8 +416,7 @@ void vtime_common_task_switch(struct task_struct *prev)
|
||||
vtime_flush(prev);
|
||||
arch_vtime_task_switch(prev);
|
||||
}
|
||||
#endif
|
||||
|
||||
# endif
|
||||
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
|
||||
|
||||
|
||||
@@ -469,10 +463,12 @@ void thread_group_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st)
|
||||
*ut = cputime.utime;
|
||||
*st = cputime.stime;
|
||||
}
|
||||
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
|
||||
|
||||
#else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE: */
|
||||
|
||||
/*
|
||||
* Account a single tick of cpu time.
|
||||
* @p: the process that the cpu time gets accounted to
|
||||
* Account a single tick of CPU time.
|
||||
* @p: the process that the CPU time gets accounted to
|
||||
* @user_tick: indicates if the tick is a user or a system tick
|
||||
*/
|
||||
void account_process_tick(struct task_struct *p, int user_tick)
|
||||
|
||||
+45
-37
@@ -17,9 +17,6 @@
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
||||
struct dl_bandwidth def_dl_bandwidth;
|
||||
|
||||
static inline struct task_struct *dl_task_of(struct sched_dl_entity *dl_se)
|
||||
@@ -87,7 +84,7 @@ void __add_running_bw(u64 dl_bw, struct dl_rq *dl_rq)
|
||||
SCHED_WARN_ON(dl_rq->running_bw < old); /* overflow */
|
||||
SCHED_WARN_ON(dl_rq->running_bw > dl_rq->this_bw);
|
||||
/* kick cpufreq (see the comment in kernel/sched/sched.h). */
|
||||
cpufreq_update_util(rq_of_dl_rq(dl_rq), SCHED_CPUFREQ_DL);
|
||||
cpufreq_update_util(rq_of_dl_rq(dl_rq), 0);
|
||||
}
|
||||
|
||||
static inline
|
||||
@@ -101,7 +98,7 @@ void __sub_running_bw(u64 dl_bw, struct dl_rq *dl_rq)
|
||||
if (dl_rq->running_bw > old)
|
||||
dl_rq->running_bw = 0;
|
||||
/* kick cpufreq (see the comment in kernel/sched/sched.h). */
|
||||
cpufreq_update_util(rq_of_dl_rq(dl_rq), SCHED_CPUFREQ_DL);
|
||||
cpufreq_update_util(rq_of_dl_rq(dl_rq), 0);
|
||||
}
|
||||
|
||||
static inline
|
||||
@@ -514,7 +511,7 @@ static DEFINE_PER_CPU(struct callback_head, dl_pull_head);
|
||||
static void push_dl_tasks(struct rq *);
|
||||
static void pull_dl_task(struct rq *);
|
||||
|
||||
static inline void queue_push_tasks(struct rq *rq)
|
||||
static inline void deadline_queue_push_tasks(struct rq *rq)
|
||||
{
|
||||
if (!has_pushable_dl_tasks(rq))
|
||||
return;
|
||||
@@ -522,7 +519,7 @@ static inline void queue_push_tasks(struct rq *rq)
|
||||
queue_balance_callback(rq, &per_cpu(dl_push_head, rq->cpu), push_dl_tasks);
|
||||
}
|
||||
|
||||
static inline void queue_pull_task(struct rq *rq)
|
||||
static inline void deadline_queue_pull_task(struct rq *rq)
|
||||
{
|
||||
queue_balance_callback(rq, &per_cpu(dl_pull_head, rq->cpu), pull_dl_task);
|
||||
}
|
||||
@@ -539,12 +536,12 @@ static struct rq *dl_task_offline_migration(struct rq *rq, struct task_struct *p
|
||||
|
||||
/*
|
||||
* If we cannot preempt any rq, fall back to pick any
|
||||
* online cpu.
|
||||
* online CPU:
|
||||
*/
|
||||
cpu = cpumask_any_and(cpu_active_mask, &p->cpus_allowed);
|
||||
if (cpu >= nr_cpu_ids) {
|
||||
/*
|
||||
* Fail to find any suitable cpu.
|
||||
* Failed to find any suitable CPU.
|
||||
* The task will never come back!
|
||||
*/
|
||||
BUG_ON(dl_bandwidth_enabled());
|
||||
@@ -597,19 +594,18 @@ static inline void pull_dl_task(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void queue_push_tasks(struct rq *rq)
|
||||
static inline void deadline_queue_push_tasks(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void queue_pull_task(struct rq *rq)
|
||||
static inline void deadline_queue_pull_task(struct rq *rq)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
static void enqueue_task_dl(struct rq *rq, struct task_struct *p, int flags);
|
||||
static void __dequeue_task_dl(struct rq *rq, struct task_struct *p, int flags);
|
||||
static void check_preempt_curr_dl(struct rq *rq, struct task_struct *p,
|
||||
int flags);
|
||||
static void check_preempt_curr_dl(struct rq *rq, struct task_struct *p, int flags);
|
||||
|
||||
/*
|
||||
* We are being explicitly informed that a new instance is starting,
|
||||
@@ -1763,7 +1759,7 @@ pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
if (hrtick_enabled(rq))
|
||||
start_hrtick_dl(rq, p);
|
||||
|
||||
queue_push_tasks(rq);
|
||||
deadline_queue_push_tasks(rq);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -1776,6 +1772,14 @@ static void put_prev_task_dl(struct rq *rq, struct task_struct *p)
|
||||
enqueue_pushable_dl_task(rq, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* scheduler tick hitting a task of our scheduling class.
|
||||
*
|
||||
* NOTE: This function can be called remotely by the tick offload that
|
||||
* goes along full dynticks. Therefore no local assumption can be made
|
||||
* and everything must be accessed through the @rq and @curr passed in
|
||||
* parameters.
|
||||
*/
|
||||
static void task_tick_dl(struct rq *rq, struct task_struct *p, int queued)
|
||||
{
|
||||
update_curr_dl(rq);
|
||||
@@ -1865,7 +1869,7 @@ static int find_later_rq(struct task_struct *task)
|
||||
|
||||
/*
|
||||
* We have to consider system topology and task affinity
|
||||
* first, then we can look for a suitable cpu.
|
||||
* first, then we can look for a suitable CPU.
|
||||
*/
|
||||
if (!cpudl_find(&task_rq(task)->rd->cpudl, task, later_mask))
|
||||
return -1;
|
||||
@@ -1879,7 +1883,7 @@ static int find_later_rq(struct task_struct *task)
|
||||
* Now we check how well this matches with task's
|
||||
* affinity and system topology.
|
||||
*
|
||||
* The last cpu where the task run is our first
|
||||
* The last CPU where the task run is our first
|
||||
* guess, since it is most likely cache-hot there.
|
||||
*/
|
||||
if (cpumask_test_cpu(cpu, later_mask))
|
||||
@@ -1909,9 +1913,9 @@ static int find_later_rq(struct task_struct *task)
|
||||
best_cpu = cpumask_first_and(later_mask,
|
||||
sched_domain_span(sd));
|
||||
/*
|
||||
* Last chance: if a cpu being in both later_mask
|
||||
* Last chance: if a CPU being in both later_mask
|
||||
* and current sd span is valid, that becomes our
|
||||
* choice. Of course, the latest possible cpu is
|
||||
* choice. Of course, the latest possible CPU is
|
||||
* already under consideration through later_mask.
|
||||
*/
|
||||
if (best_cpu < nr_cpu_ids) {
|
||||
@@ -2067,7 +2071,7 @@ retry:
|
||||
if (task == next_task) {
|
||||
/*
|
||||
* The task is still there. We don't try
|
||||
* again, some other cpu will pull it when ready.
|
||||
* again, some other CPU will pull it when ready.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
@@ -2300,12 +2304,12 @@ static void switched_from_dl(struct rq *rq, struct task_struct *p)
|
||||
/*
|
||||
* Since this might be the only -deadline task on the rq,
|
||||
* this is the right place to try to pull some other one
|
||||
* from an overloaded cpu, if any.
|
||||
* from an overloaded CPU, if any.
|
||||
*/
|
||||
if (!task_on_rq_queued(p) || rq->dl.dl_nr_running)
|
||||
return;
|
||||
|
||||
queue_pull_task(rq);
|
||||
deadline_queue_pull_task(rq);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2327,7 +2331,7 @@ static void switched_to_dl(struct rq *rq, struct task_struct *p)
|
||||
if (rq->curr != p) {
|
||||
#ifdef CONFIG_SMP
|
||||
if (p->nr_cpus_allowed > 1 && rq->dl.overloaded)
|
||||
queue_push_tasks(rq);
|
||||
deadline_queue_push_tasks(rq);
|
||||
#endif
|
||||
if (dl_task(rq->curr))
|
||||
check_preempt_curr_dl(rq, p, 0);
|
||||
@@ -2352,7 +2356,7 @@ static void prio_changed_dl(struct rq *rq, struct task_struct *p,
|
||||
* or lowering its prio, so...
|
||||
*/
|
||||
if (!rq->dl.overloaded)
|
||||
queue_pull_task(rq);
|
||||
deadline_queue_pull_task(rq);
|
||||
|
||||
/*
|
||||
* If we now have a earlier deadline task than p,
|
||||
@@ -2626,17 +2630,17 @@ void __dl_clear_params(struct task_struct *p)
|
||||
{
|
||||
struct sched_dl_entity *dl_se = &p->dl;
|
||||
|
||||
dl_se->dl_runtime = 0;
|
||||
dl_se->dl_deadline = 0;
|
||||
dl_se->dl_period = 0;
|
||||
dl_se->flags = 0;
|
||||
dl_se->dl_bw = 0;
|
||||
dl_se->dl_density = 0;
|
||||
dl_se->dl_runtime = 0;
|
||||
dl_se->dl_deadline = 0;
|
||||
dl_se->dl_period = 0;
|
||||
dl_se->flags = 0;
|
||||
dl_se->dl_bw = 0;
|
||||
dl_se->dl_density = 0;
|
||||
|
||||
dl_se->dl_throttled = 0;
|
||||
dl_se->dl_yielded = 0;
|
||||
dl_se->dl_non_contending = 0;
|
||||
dl_se->dl_overrun = 0;
|
||||
dl_se->dl_throttled = 0;
|
||||
dl_se->dl_yielded = 0;
|
||||
dl_se->dl_non_contending = 0;
|
||||
dl_se->dl_overrun = 0;
|
||||
}
|
||||
|
||||
bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr)
|
||||
@@ -2655,21 +2659,22 @@ bool dl_param_changed(struct task_struct *p, const struct sched_attr *attr)
|
||||
#ifdef CONFIG_SMP
|
||||
int dl_task_can_attach(struct task_struct *p, const struct cpumask *cs_cpus_allowed)
|
||||
{
|
||||
unsigned int dest_cpu = cpumask_any_and(cpu_active_mask,
|
||||
cs_cpus_allowed);
|
||||
unsigned int dest_cpu;
|
||||
struct dl_bw *dl_b;
|
||||
bool overflow;
|
||||
int cpus, ret;
|
||||
unsigned long flags;
|
||||
|
||||
dest_cpu = cpumask_any_and(cpu_active_mask, cs_cpus_allowed);
|
||||
|
||||
rcu_read_lock_sched();
|
||||
dl_b = dl_bw_of(dest_cpu);
|
||||
raw_spin_lock_irqsave(&dl_b->lock, flags);
|
||||
cpus = dl_bw_cpus(dest_cpu);
|
||||
overflow = __dl_overflow(dl_b, cpus, 0, p->dl.dl_bw);
|
||||
if (overflow)
|
||||
if (overflow) {
|
||||
ret = -EBUSY;
|
||||
else {
|
||||
} else {
|
||||
/*
|
||||
* We reserve space for this task in the destination
|
||||
* root_domain, as we can't fail after this point.
|
||||
@@ -2681,6 +2686,7 @@ int dl_task_can_attach(struct task_struct *p, const struct cpumask *cs_cpus_allo
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&dl_b->lock, flags);
|
||||
rcu_read_unlock_sched();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2701,6 +2707,7 @@ int dl_cpuset_cpumask_can_shrink(const struct cpumask *cur,
|
||||
ret = 0;
|
||||
raw_spin_unlock_irqrestore(&cur_dl_b->lock, flags);
|
||||
rcu_read_unlock_sched();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2718,6 +2725,7 @@ bool dl_cpu_busy(unsigned int cpu)
|
||||
overflow = __dl_overflow(dl_b, cpus, 0, 0);
|
||||
raw_spin_unlock_irqrestore(&dl_b->lock, flags);
|
||||
rcu_read_unlock_sched();
|
||||
|
||||
return overflow;
|
||||
}
|
||||
#endif
|
||||
|
||||
+39
-64
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* kernel/sched/debug.c
|
||||
*
|
||||
* Print the CFS rbtree
|
||||
* Print the CFS rbtree and other debugging details
|
||||
*
|
||||
* Copyright(C) 2007, Red Hat, Inc., Ingo Molnar
|
||||
*
|
||||
@@ -9,16 +9,6 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/mempolicy.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
static DEFINE_SPINLOCK(sched_debug_lock);
|
||||
@@ -274,34 +264,19 @@ sd_alloc_ctl_domain_table(struct sched_domain *sd)
|
||||
if (table == NULL)
|
||||
return NULL;
|
||||
|
||||
set_table_entry(&table[0], "min_interval", &sd->min_interval,
|
||||
sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[1], "max_interval", &sd->max_interval,
|
||||
sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[2], "busy_idx", &sd->busy_idx,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, true);
|
||||
set_table_entry(&table[3], "idle_idx", &sd->idle_idx,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, true);
|
||||
set_table_entry(&table[4], "newidle_idx", &sd->newidle_idx,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, true);
|
||||
set_table_entry(&table[5], "wake_idx", &sd->wake_idx,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, true);
|
||||
set_table_entry(&table[6], "forkexec_idx", &sd->forkexec_idx,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, true);
|
||||
set_table_entry(&table[7], "busy_factor", &sd->busy_factor,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[8], "imbalance_pct", &sd->imbalance_pct,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[9], "cache_nice_tries",
|
||||
&sd->cache_nice_tries,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[10], "flags", &sd->flags,
|
||||
sizeof(int), 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[11], "max_newidle_lb_cost",
|
||||
&sd->max_newidle_lb_cost,
|
||||
sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[12], "name", sd->name,
|
||||
CORENAME_MAX_SIZE, 0444, proc_dostring, false);
|
||||
set_table_entry(&table[0] , "min_interval", &sd->min_interval, sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[1] , "max_interval", &sd->max_interval, sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[2] , "busy_idx", &sd->busy_idx, sizeof(int) , 0644, proc_dointvec_minmax, true );
|
||||
set_table_entry(&table[3] , "idle_idx", &sd->idle_idx, sizeof(int) , 0644, proc_dointvec_minmax, true );
|
||||
set_table_entry(&table[4] , "newidle_idx", &sd->newidle_idx, sizeof(int) , 0644, proc_dointvec_minmax, true );
|
||||
set_table_entry(&table[5] , "wake_idx", &sd->wake_idx, sizeof(int) , 0644, proc_dointvec_minmax, true );
|
||||
set_table_entry(&table[6] , "forkexec_idx", &sd->forkexec_idx, sizeof(int) , 0644, proc_dointvec_minmax, true );
|
||||
set_table_entry(&table[7] , "busy_factor", &sd->busy_factor, sizeof(int) , 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[8] , "imbalance_pct", &sd->imbalance_pct, sizeof(int) , 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[9] , "cache_nice_tries", &sd->cache_nice_tries, sizeof(int) , 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[10], "flags", &sd->flags, sizeof(int) , 0644, proc_dointvec_minmax, false);
|
||||
set_table_entry(&table[11], "max_newidle_lb_cost", &sd->max_newidle_lb_cost, sizeof(long), 0644, proc_doulongvec_minmax, false);
|
||||
set_table_entry(&table[12], "name", sd->name, CORENAME_MAX_SIZE, 0444, proc_dostring, false);
|
||||
/* &table[13] is terminator */
|
||||
|
||||
return table;
|
||||
@@ -332,8 +307,8 @@ static struct ctl_table *sd_alloc_ctl_cpu_table(int cpu)
|
||||
return table;
|
||||
}
|
||||
|
||||
static cpumask_var_t sd_sysctl_cpus;
|
||||
static struct ctl_table_header *sd_sysctl_header;
|
||||
static cpumask_var_t sd_sysctl_cpus;
|
||||
static struct ctl_table_header *sd_sysctl_header;
|
||||
|
||||
void register_sched_domain_sysctl(void)
|
||||
{
|
||||
@@ -413,14 +388,10 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group
|
||||
{
|
||||
struct sched_entity *se = tg->se[cpu];
|
||||
|
||||
#define P(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)F)
|
||||
#define P_SCHEDSTAT(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)schedstat_val(F))
|
||||
#define PN(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
#define PN_SCHEDSTAT(F) \
|
||||
SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)schedstat_val(F)))
|
||||
#define P(F) SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)F)
|
||||
#define P_SCHEDSTAT(F) SEQ_printf(m, " .%-30s: %lld\n", #F, (long long)schedstat_val(F))
|
||||
#define PN(F) SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
#define PN_SCHEDSTAT(F) SEQ_printf(m, " .%-30s: %lld.%06ld\n", #F, SPLIT_NS((long long)schedstat_val(F)))
|
||||
|
||||
if (!se)
|
||||
return;
|
||||
@@ -428,6 +399,7 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group
|
||||
PN(se->exec_start);
|
||||
PN(se->vruntime);
|
||||
PN(se->sum_exec_runtime);
|
||||
|
||||
if (schedstat_enabled()) {
|
||||
PN_SCHEDSTAT(se->statistics.wait_start);
|
||||
PN_SCHEDSTAT(se->statistics.sleep_start);
|
||||
@@ -440,6 +412,7 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group
|
||||
PN_SCHEDSTAT(se->statistics.wait_sum);
|
||||
P_SCHEDSTAT(se->statistics.wait_count);
|
||||
}
|
||||
|
||||
P(se->load.weight);
|
||||
P(se->runnable_weight);
|
||||
#ifdef CONFIG_SMP
|
||||
@@ -464,6 +437,7 @@ static char *task_group_path(struct task_group *tg)
|
||||
return group_path;
|
||||
|
||||
cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
|
||||
|
||||
return group_path;
|
||||
}
|
||||
#endif
|
||||
@@ -569,6 +543,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
|
||||
cfs_rq->avg.runnable_load_avg);
|
||||
SEQ_printf(m, " .%-30s: %lu\n", "util_avg",
|
||||
cfs_rq->avg.util_avg);
|
||||
SEQ_printf(m, " .%-30s: %u\n", "util_est_enqueued",
|
||||
cfs_rq->avg.util_est.enqueued);
|
||||
SEQ_printf(m, " .%-30s: %ld\n", "removed.load_avg",
|
||||
cfs_rq->removed.load_avg);
|
||||
SEQ_printf(m, " .%-30s: %ld\n", "removed.util_avg",
|
||||
@@ -804,9 +780,9 @@ void sysrq_sched_debug_show(void)
|
||||
/*
|
||||
* This itererator needs some explanation.
|
||||
* It returns 1 for the header position.
|
||||
* This means 2 is cpu 0.
|
||||
* In a hotplugged system some cpus, including cpu 0, may be missing so we have
|
||||
* to use cpumask_* to iterate over the cpus.
|
||||
* This means 2 is CPU 0.
|
||||
* In a hotplugged system some CPUs, including CPU 0, may be missing so we have
|
||||
* to use cpumask_* to iterate over the CPUs.
|
||||
*/
|
||||
static void *sched_debug_start(struct seq_file *file, loff_t *offset)
|
||||
{
|
||||
@@ -826,6 +802,7 @@ static void *sched_debug_start(struct seq_file *file, loff_t *offset)
|
||||
|
||||
if (n < nr_cpu_ids)
|
||||
return (void *)(unsigned long)(n + 2);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -840,10 +817,10 @@ static void sched_debug_stop(struct seq_file *file, void *data)
|
||||
}
|
||||
|
||||
static const struct seq_operations sched_debug_sops = {
|
||||
.start = sched_debug_start,
|
||||
.next = sched_debug_next,
|
||||
.stop = sched_debug_stop,
|
||||
.show = sched_debug_show,
|
||||
.start = sched_debug_start,
|
||||
.next = sched_debug_next,
|
||||
.stop = sched_debug_stop,
|
||||
.show = sched_debug_show,
|
||||
};
|
||||
|
||||
static int sched_debug_release(struct inode *inode, struct file *file)
|
||||
@@ -881,14 +858,10 @@ static int __init init_sched_debug_procfs(void)
|
||||
|
||||
__initcall(init_sched_debug_procfs);
|
||||
|
||||
#define __P(F) \
|
||||
SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)F)
|
||||
#define P(F) \
|
||||
SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)p->F)
|
||||
#define __PN(F) \
|
||||
SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
#define PN(F) \
|
||||
SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)p->F))
|
||||
#define __P(F) SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)F)
|
||||
#define P(F) SEQ_printf(m, "%-45s:%21Ld\n", #F, (long long)p->F)
|
||||
#define __PN(F) SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)F))
|
||||
#define PN(F) SEQ_printf(m, "%-45s:%14Ld.%06ld\n", #F, SPLIT_NS((long long)p->F))
|
||||
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
@@ -1023,6 +996,8 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns,
|
||||
P(se.avg.runnable_load_avg);
|
||||
P(se.avg.util_avg);
|
||||
P(se.avg.last_update_time);
|
||||
P(se.avg.util_est.ewma);
|
||||
P(se.avg.util_est.enqueued);
|
||||
#endif
|
||||
P(policy);
|
||||
P(prio);
|
||||
|
||||
+956
-459
File diff suppressed because it is too large
Load Diff
@@ -85,3 +85,8 @@ SCHED_FEAT(ATTACH_AGE_LOAD, true)
|
||||
SCHED_FEAT(WA_IDLE, true)
|
||||
SCHED_FEAT(WA_WEIGHT, true)
|
||||
SCHED_FEAT(WA_BIAS, true)
|
||||
|
||||
/*
|
||||
* UtilEstimation. Use estimated CPU utilization.
|
||||
*/
|
||||
SCHED_FEAT(UTIL_EST, true)
|
||||
|
||||
+125
-17
@@ -1,23 +1,14 @@
|
||||
/*
|
||||
* Generic entry point for the idle threads
|
||||
* Generic entry points for the idle threads and
|
||||
* implementation of the idle task scheduling class.
|
||||
*
|
||||
* (NOTE: these are not related to SCHED_IDLE batch scheduled
|
||||
* tasks which are handled in sched/fair.c )
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/idle.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/stackprotector.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
#include <asm/tlb.h>
|
||||
#include "sched.h"
|
||||
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
/* Linker adds these: start and end of __cpuidle functions */
|
||||
extern char __cpuidle_text_start[], __cpuidle_text_end[];
|
||||
|
||||
@@ -46,6 +37,7 @@ void cpu_idle_poll_ctrl(bool enable)
|
||||
static int __init cpu_idle_poll_setup(char *__unused)
|
||||
{
|
||||
cpu_idle_force_poll = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("nohlt", cpu_idle_poll_setup);
|
||||
@@ -53,6 +45,7 @@ __setup("nohlt", cpu_idle_poll_setup);
|
||||
static int __init cpu_idle_nopoll_setup(char *__unused)
|
||||
{
|
||||
cpu_idle_force_poll = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
__setup("hlt", cpu_idle_nopoll_setup);
|
||||
@@ -64,12 +57,14 @@ static noinline int __cpuidle cpu_idle_poll(void)
|
||||
trace_cpu_idle_rcuidle(0, smp_processor_id());
|
||||
local_irq_enable();
|
||||
stop_critical_timings();
|
||||
|
||||
while (!tif_need_resched() &&
|
||||
(cpu_idle_force_poll || tick_check_broadcast_expired()))
|
||||
cpu_relax();
|
||||
start_critical_timings();
|
||||
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id());
|
||||
rcu_idle_exit();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -332,8 +327,8 @@ void cpu_startup_entry(enum cpuhp_state state)
|
||||
{
|
||||
/*
|
||||
* This #ifdef needs to die, but it's too late in the cycle to
|
||||
* make this generic (arm and sh have never invoked the canary
|
||||
* init for the non boot cpus!). Will be fixed in 3.11
|
||||
* make this generic (ARM and SH have never invoked the canary
|
||||
* init for the non boot CPUs!). Will be fixed in 3.11
|
||||
*/
|
||||
#ifdef CONFIG_X86
|
||||
/*
|
||||
@@ -350,3 +345,116 @@ void cpu_startup_entry(enum cpuhp_state state)
|
||||
while (1)
|
||||
do_idle();
|
||||
}
|
||||
|
||||
/*
|
||||
* idle-task scheduling class.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int
|
||||
select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags)
|
||||
{
|
||||
return task_cpu(p); /* IDLE tasks as never migrated */
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Idle tasks are unconditionally rescheduled:
|
||||
*/
|
||||
static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
{
|
||||
resched_curr(rq);
|
||||
}
|
||||
|
||||
static struct task_struct *
|
||||
pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
put_prev_task(rq, prev);
|
||||
update_idle_core(rq);
|
||||
schedstat_inc(rq->sched_goidle);
|
||||
|
||||
return rq->idle;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not legal to sleep in the idle task - print a warning
|
||||
* message if some code attempts to do it:
|
||||
*/
|
||||
static void
|
||||
dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
{
|
||||
raw_spin_unlock_irq(&rq->lock);
|
||||
printk(KERN_ERR "bad: scheduling from the idle thread!\n");
|
||||
dump_stack();
|
||||
raw_spin_lock_irq(&rq->lock);
|
||||
}
|
||||
|
||||
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* scheduler tick hitting a task of our scheduling class.
|
||||
*
|
||||
* NOTE: This function can be called remotely by the tick offload that
|
||||
* goes along full dynticks. Therefore no local assumption can be made
|
||||
* and everything must be accessed through the @rq and @curr passed in
|
||||
* parameters.
|
||||
*/
|
||||
static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
|
||||
{
|
||||
}
|
||||
|
||||
static void set_curr_task_idle(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
static void switched_to_idle(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static void
|
||||
prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_curr_idle(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple, special scheduling class for the per-CPU idle tasks:
|
||||
*/
|
||||
const struct sched_class idle_sched_class = {
|
||||
/* .next is NULL */
|
||||
/* no enqueue/yield_task for idle tasks */
|
||||
|
||||
/* dequeue is not valid, we print a debug message there: */
|
||||
.dequeue_task = dequeue_task_idle,
|
||||
|
||||
.check_preempt_curr = check_preempt_curr_idle,
|
||||
|
||||
.pick_next_task = pick_next_task_idle,
|
||||
.put_prev_task = put_prev_task_idle,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_idle,
|
||||
.set_cpus_allowed = set_cpus_allowed_common,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_idle,
|
||||
.task_tick = task_tick_idle,
|
||||
|
||||
.get_rr_interval = get_rr_interval_idle,
|
||||
|
||||
.prio_changed = prio_changed_idle,
|
||||
.switched_to = switched_to_idle,
|
||||
.update_curr = update_curr_idle,
|
||||
};
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* idle-task scheduling class.
|
||||
*
|
||||
* (NOTE: these are not related to SCHED_IDLE tasks which are
|
||||
* handled in sched/fair.c)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int
|
||||
select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags)
|
||||
{
|
||||
return task_cpu(p); /* IDLE tasks as never migrated */
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/*
|
||||
* Idle tasks are unconditionally rescheduled:
|
||||
*/
|
||||
static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
{
|
||||
resched_curr(rq);
|
||||
}
|
||||
|
||||
static struct task_struct *
|
||||
pick_next_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
put_prev_task(rq, prev);
|
||||
update_idle_core(rq);
|
||||
schedstat_inc(rq->sched_goidle);
|
||||
return rq->idle;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not legal to sleep in the idle task - print a warning
|
||||
* message if some code attempts to do it:
|
||||
*/
|
||||
static void
|
||||
dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
{
|
||||
raw_spin_unlock_irq(&rq->lock);
|
||||
printk(KERN_ERR "bad: scheduling from the idle thread!\n");
|
||||
dump_stack();
|
||||
raw_spin_lock_irq(&rq->lock);
|
||||
}
|
||||
|
||||
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
|
||||
{
|
||||
rq_last_tick_reset(rq);
|
||||
}
|
||||
|
||||
static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
|
||||
{
|
||||
}
|
||||
|
||||
static void set_curr_task_idle(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
static void switched_to_idle(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static void
|
||||
prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
|
||||
{
|
||||
BUG();
|
||||
}
|
||||
|
||||
static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_curr_idle(struct rq *rq)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple, special scheduling class for the per-CPU idle tasks:
|
||||
*/
|
||||
const struct sched_class idle_sched_class = {
|
||||
/* .next is NULL */
|
||||
/* no enqueue/yield_task for idle tasks */
|
||||
|
||||
/* dequeue is not valid, we print a debug message there: */
|
||||
.dequeue_task = dequeue_task_idle,
|
||||
|
||||
.check_preempt_curr = check_preempt_curr_idle,
|
||||
|
||||
.pick_next_task = pick_next_task_idle,
|
||||
.put_prev_task = put_prev_task_idle,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_idle,
|
||||
.set_cpus_allowed = set_cpus_allowed_common,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_idle,
|
||||
.task_tick = task_tick_idle,
|
||||
|
||||
.get_rr_interval = get_rr_interval_idle,
|
||||
|
||||
.prio_changed = prio_changed_idle,
|
||||
.switched_to = switched_to_idle,
|
||||
.update_curr = update_curr_idle,
|
||||
};
|
||||
@@ -3,15 +3,10 @@
|
||||
* any CPU: unbound workqueues, timers, kthreads and any offloadable work.
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat, Inc., Frederic Weisbecker
|
||||
* Copyright (C) 2017-2018 SUSE, Frederic Weisbecker
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/sched/isolation.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/ctype.h>
|
||||
#include "sched.h"
|
||||
|
||||
DEFINE_STATIC_KEY_FALSE(housekeeping_overriden);
|
||||
EXPORT_SYMBOL_GPL(housekeeping_overriden);
|
||||
@@ -60,6 +55,9 @@ void __init housekeeping_init(void)
|
||||
|
||||
static_branch_enable(&housekeeping_overriden);
|
||||
|
||||
if (housekeeping_flags & HK_FLAG_TICK)
|
||||
sched_tick_offload_init();
|
||||
|
||||
/* We need at least one CPU to handle housekeeping work */
|
||||
WARN_ON_ONCE(cpumask_empty(housekeeping_mask));
|
||||
}
|
||||
@@ -119,7 +117,7 @@ static int __init housekeeping_nohz_full_setup(char *str)
|
||||
{
|
||||
unsigned int flags;
|
||||
|
||||
flags = HK_FLAG_TICK | HK_FLAG_TIMER | HK_FLAG_RCU | HK_FLAG_MISC;
|
||||
flags = HK_FLAG_TICK | HK_FLAG_WQ | HK_FLAG_TIMER | HK_FLAG_RCU | HK_FLAG_MISC;
|
||||
|
||||
return housekeeping_setup(str, flags);
|
||||
}
|
||||
|
||||
+15
-19
@@ -6,10 +6,6 @@
|
||||
* figure. Its a silly number but people think its important. We go through
|
||||
* great pains to make it work on big machines and tickless kernels.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched/loadavg.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
@@ -32,29 +28,29 @@
|
||||
* Due to a number of reasons the above turns in the mess below:
|
||||
*
|
||||
* - for_each_possible_cpu() is prohibitively expensive on machines with
|
||||
* serious number of cpus, therefore we need to take a distributed approach
|
||||
* serious number of CPUs, therefore we need to take a distributed approach
|
||||
* to calculating nr_active.
|
||||
*
|
||||
* \Sum_i x_i(t) = \Sum_i x_i(t) - x_i(t_0) | x_i(t_0) := 0
|
||||
* = \Sum_i { \Sum_j=1 x_i(t_j) - x_i(t_j-1) }
|
||||
*
|
||||
* So assuming nr_active := 0 when we start out -- true per definition, we
|
||||
* can simply take per-cpu deltas and fold those into a global accumulate
|
||||
* can simply take per-CPU deltas and fold those into a global accumulate
|
||||
* to obtain the same result. See calc_load_fold_active().
|
||||
*
|
||||
* Furthermore, in order to avoid synchronizing all per-cpu delta folding
|
||||
* Furthermore, in order to avoid synchronizing all per-CPU delta folding
|
||||
* across the machine, we assume 10 ticks is sufficient time for every
|
||||
* cpu to have completed this task.
|
||||
* CPU to have completed this task.
|
||||
*
|
||||
* This places an upper-bound on the IRQ-off latency of the machine. Then
|
||||
* again, being late doesn't loose the delta, just wrecks the sample.
|
||||
*
|
||||
* - cpu_rq()->nr_uninterruptible isn't accurately tracked per-cpu because
|
||||
* this would add another cross-cpu cacheline miss and atomic operation
|
||||
* to the wakeup path. Instead we increment on whatever cpu the task ran
|
||||
* when it went into uninterruptible state and decrement on whatever cpu
|
||||
* - cpu_rq()->nr_uninterruptible isn't accurately tracked per-CPU because
|
||||
* this would add another cross-CPU cacheline miss and atomic operation
|
||||
* to the wakeup path. Instead we increment on whatever CPU the task ran
|
||||
* when it went into uninterruptible state and decrement on whatever CPU
|
||||
* did the wakeup. This means that only the sum of nr_uninterruptible over
|
||||
* all cpus yields the correct result.
|
||||
* all CPUs yields the correct result.
|
||||
*
|
||||
* This covers the NO_HZ=n code, for extra head-aches, see the comment below.
|
||||
*/
|
||||
@@ -115,11 +111,11 @@ calc_load(unsigned long load, unsigned long exp, unsigned long active)
|
||||
* Handle NO_HZ for the global load-average.
|
||||
*
|
||||
* Since the above described distributed algorithm to compute the global
|
||||
* load-average relies on per-cpu sampling from the tick, it is affected by
|
||||
* load-average relies on per-CPU sampling from the tick, it is affected by
|
||||
* NO_HZ.
|
||||
*
|
||||
* The basic idea is to fold the nr_active delta into a global NO_HZ-delta upon
|
||||
* entering NO_HZ state such that we can include this as an 'extra' cpu delta
|
||||
* entering NO_HZ state such that we can include this as an 'extra' CPU delta
|
||||
* when we read the global state.
|
||||
*
|
||||
* Obviously reality has to ruin such a delightfully simple scheme:
|
||||
@@ -146,9 +142,9 @@ calc_load(unsigned long load, unsigned long exp, unsigned long active)
|
||||
* busy state.
|
||||
*
|
||||
* This is solved by pushing the window forward, and thus skipping the
|
||||
* sample, for this cpu (effectively using the NO_HZ-delta for this cpu which
|
||||
* sample, for this CPU (effectively using the NO_HZ-delta for this CPU which
|
||||
* was in effect at the time the window opened). This also solves the issue
|
||||
* of having to deal with a cpu having been in NO_HZ for multiple LOAD_FREQ
|
||||
* of having to deal with a CPU having been in NO_HZ for multiple LOAD_FREQ
|
||||
* intervals.
|
||||
*
|
||||
* When making the ILB scale, we should try to pull this in as well.
|
||||
@@ -299,7 +295,7 @@ calc_load_n(unsigned long load, unsigned long exp,
|
||||
}
|
||||
|
||||
/*
|
||||
* NO_HZ can leave us missing all per-cpu ticks calling
|
||||
* NO_HZ can leave us missing all per-CPU ticks calling
|
||||
* calc_load_fold_active(), but since a NO_HZ CPU folds its delta into
|
||||
* calc_load_nohz per calc_load_nohz_start(), all we need to do is fold
|
||||
* in the pending NO_HZ delta if our NO_HZ period crossed a load cycle boundary.
|
||||
@@ -363,7 +359,7 @@ void calc_global_load(unsigned long ticks)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Fold the 'old' NO_HZ-delta to include all NO_HZ cpus.
|
||||
* Fold the 'old' NO_HZ-delta to include all NO_HZ CPUs.
|
||||
*/
|
||||
delta = calc_load_nohz_fold();
|
||||
if (delta)
|
||||
|
||||
+12
-15
@@ -13,32 +13,25 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/membarrier.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "sched.h" /* for cpu_rq(). */
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* Bitmask made from a "or" of all commands within enum membarrier_cmd,
|
||||
* except MEMBARRIER_CMD_QUERY.
|
||||
*/
|
||||
#ifdef CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE
|
||||
#define MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK \
|
||||
(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE \
|
||||
#define MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK \
|
||||
(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE \
|
||||
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE)
|
||||
#else
|
||||
#define MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK 0
|
||||
#endif
|
||||
|
||||
#define MEMBARRIER_CMD_BITMASK \
|
||||
(MEMBARRIER_CMD_GLOBAL | MEMBARRIER_CMD_GLOBAL_EXPEDITED \
|
||||
| MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED \
|
||||
| MEMBARRIER_CMD_PRIVATE_EXPEDITED \
|
||||
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED \
|
||||
#define MEMBARRIER_CMD_BITMASK \
|
||||
(MEMBARRIER_CMD_GLOBAL | MEMBARRIER_CMD_GLOBAL_EXPEDITED \
|
||||
| MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED \
|
||||
| MEMBARRIER_CMD_PRIVATE_EXPEDITED \
|
||||
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED \
|
||||
| MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK)
|
||||
|
||||
static void ipi_mb(void *info)
|
||||
@@ -85,6 +78,7 @@ static int membarrier_global_expedited(void)
|
||||
*/
|
||||
if (cpu == raw_smp_processor_id())
|
||||
continue;
|
||||
|
||||
rcu_read_lock();
|
||||
p = task_rcu_dereference(&cpu_rq(cpu)->curr);
|
||||
if (p && p->mm && (atomic_read(&p->mm->membarrier_state) &
|
||||
@@ -188,6 +182,7 @@ static int membarrier_private_expedited(int flags)
|
||||
* rq->curr modification in scheduler.
|
||||
*/
|
||||
smp_mb(); /* exit from system call is not a mb */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -219,6 +214,7 @@ static int membarrier_register_global_expedited(void)
|
||||
}
|
||||
atomic_or(MEMBARRIER_STATE_GLOBAL_EXPEDITED_READY,
|
||||
&mm->membarrier_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -253,6 +249,7 @@ static int membarrier_register_private_expedited(int flags)
|
||||
synchronize_sched();
|
||||
}
|
||||
atomic_or(state, &mm->membarrier_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+35
-25
@@ -3,12 +3,8 @@
|
||||
* Real-Time Scheduling Class (mapped to the SCHED_FIFO and SCHED_RR
|
||||
* policies)
|
||||
*/
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq_work.h>
|
||||
|
||||
int sched_rr_timeslice = RR_TIMESLICE;
|
||||
int sysctl_sched_rr_timeslice = (MSEC_PER_SEC / HZ) * RR_TIMESLICE;
|
||||
|
||||
@@ -359,7 +355,7 @@ static DEFINE_PER_CPU(struct callback_head, rt_pull_head);
|
||||
static void push_rt_tasks(struct rq *);
|
||||
static void pull_rt_task(struct rq *);
|
||||
|
||||
static inline void queue_push_tasks(struct rq *rq)
|
||||
static inline void rt_queue_push_tasks(struct rq *rq)
|
||||
{
|
||||
if (!has_pushable_tasks(rq))
|
||||
return;
|
||||
@@ -367,7 +363,7 @@ static inline void queue_push_tasks(struct rq *rq)
|
||||
queue_balance_callback(rq, &per_cpu(rt_push_head, rq->cpu), push_rt_tasks);
|
||||
}
|
||||
|
||||
static inline void queue_pull_task(struct rq *rq)
|
||||
static inline void rt_queue_pull_task(struct rq *rq)
|
||||
{
|
||||
queue_balance_callback(rq, &per_cpu(rt_pull_head, rq->cpu), pull_rt_task);
|
||||
}
|
||||
@@ -425,7 +421,7 @@ static inline void pull_rt_task(struct rq *this_rq)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void queue_push_tasks(struct rq *rq)
|
||||
static inline void rt_queue_push_tasks(struct rq *rq)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SMP */
|
||||
@@ -961,9 +957,6 @@ static void update_curr_rt(struct rq *rq)
|
||||
if (unlikely((s64)delta_exec <= 0))
|
||||
return;
|
||||
|
||||
/* Kick cpufreq (see the comment in kernel/sched/sched.h). */
|
||||
cpufreq_update_util(rq, SCHED_CPUFREQ_RT);
|
||||
|
||||
schedstat_set(curr->se.statistics.exec_max,
|
||||
max(curr->se.statistics.exec_max, delta_exec));
|
||||
|
||||
@@ -1005,6 +998,9 @@ dequeue_top_rt_rq(struct rt_rq *rt_rq)
|
||||
|
||||
sub_nr_running(rq, rt_rq->rt_nr_running);
|
||||
rt_rq->rt_queued = 0;
|
||||
|
||||
/* Kick cpufreq (see the comment in kernel/sched/sched.h). */
|
||||
cpufreq_update_util(rq, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1021,6 +1017,9 @@ enqueue_top_rt_rq(struct rt_rq *rt_rq)
|
||||
|
||||
add_nr_running(rq, rt_rq->rt_nr_running);
|
||||
rt_rq->rt_queued = 1;
|
||||
|
||||
/* Kick cpufreq (see the comment in kernel/sched/sched.h). */
|
||||
cpufreq_update_util(rq, 0);
|
||||
}
|
||||
|
||||
#if defined CONFIG_SMP
|
||||
@@ -1453,9 +1452,9 @@ static void check_preempt_equal_prio(struct rq *rq, struct task_struct *p)
|
||||
return;
|
||||
|
||||
/*
|
||||
* There appears to be other cpus that can accept
|
||||
* current and none to run 'p', so lets reschedule
|
||||
* to try and push current away:
|
||||
* There appear to be other CPUs that can accept
|
||||
* the current task but none can run 'p', so lets reschedule
|
||||
* to try and push the current task away:
|
||||
*/
|
||||
requeue_task_rt(rq, p, 1);
|
||||
resched_curr(rq);
|
||||
@@ -1569,7 +1568,7 @@ pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
/* The running task is never eligible for pushing */
|
||||
dequeue_pushable_task(rq, p);
|
||||
|
||||
queue_push_tasks(rq);
|
||||
rt_queue_push_tasks(rq);
|
||||
|
||||
return p;
|
||||
}
|
||||
@@ -1596,12 +1595,13 @@ static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu)
|
||||
if (!task_running(rq, p) &&
|
||||
cpumask_test_cpu(cpu, &p->cpus_allowed))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the highest pushable rq's task, which is suitable to be executed
|
||||
* on the cpu, NULL otherwise
|
||||
* on the CPU, NULL otherwise
|
||||
*/
|
||||
static struct task_struct *pick_highest_pushable_task(struct rq *rq, int cpu)
|
||||
{
|
||||
@@ -1639,11 +1639,11 @@ static int find_lowest_rq(struct task_struct *task)
|
||||
return -1; /* No targets found */
|
||||
|
||||
/*
|
||||
* At this point we have built a mask of cpus representing the
|
||||
* At this point we have built a mask of CPUs representing the
|
||||
* lowest priority tasks in the system. Now we want to elect
|
||||
* the best one based on our affinity and topology.
|
||||
*
|
||||
* We prioritize the last cpu that the task executed on since
|
||||
* We prioritize the last CPU that the task executed on since
|
||||
* it is most likely cache-hot in that location.
|
||||
*/
|
||||
if (cpumask_test_cpu(cpu, lowest_mask))
|
||||
@@ -1651,7 +1651,7 @@ static int find_lowest_rq(struct task_struct *task)
|
||||
|
||||
/*
|
||||
* Otherwise, we consult the sched_domains span maps to figure
|
||||
* out which cpu is logically closest to our hot cache data.
|
||||
* out which CPU is logically closest to our hot cache data.
|
||||
*/
|
||||
if (!cpumask_test_cpu(this_cpu, lowest_mask))
|
||||
this_cpu = -1; /* Skip this_cpu opt if not among lowest */
|
||||
@@ -1692,6 +1692,7 @@ static int find_lowest_rq(struct task_struct *task)
|
||||
cpu = cpumask_any(lowest_mask);
|
||||
if (cpu < nr_cpu_ids)
|
||||
return cpu;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1827,7 +1828,7 @@ retry:
|
||||
* The task hasn't migrated, and is still the next
|
||||
* eligible task, but we failed to find a run-queue
|
||||
* to push it to. Do not retry in this case, since
|
||||
* other cpus will pull from us when ready.
|
||||
* other CPUs will pull from us when ready.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
@@ -1919,7 +1920,7 @@ static int rto_next_cpu(struct root_domain *rd)
|
||||
* rt_next_cpu() will simply return the first CPU found in
|
||||
* the rto_mask.
|
||||
*
|
||||
* If rto_next_cpu() is called with rto_cpu is a valid cpu, it
|
||||
* If rto_next_cpu() is called with rto_cpu is a valid CPU, it
|
||||
* will return the next CPU found in the rto_mask.
|
||||
*
|
||||
* If there are no more CPUs left in the rto_mask, then a check is made
|
||||
@@ -1980,7 +1981,7 @@ static void tell_cpu_to_push(struct rq *rq)
|
||||
raw_spin_lock(&rq->rd->rto_lock);
|
||||
|
||||
/*
|
||||
* The rto_cpu is updated under the lock, if it has a valid cpu
|
||||
* The rto_cpu is updated under the lock, if it has a valid CPU
|
||||
* then the IPI is still running and will continue due to the
|
||||
* update to loop_next, and nothing needs to be done here.
|
||||
* Otherwise it is finishing up and an ipi needs to be sent.
|
||||
@@ -2105,7 +2106,7 @@ static void pull_rt_task(struct rq *this_rq)
|
||||
|
||||
/*
|
||||
* There's a chance that p is higher in priority
|
||||
* than what's currently running on its cpu.
|
||||
* than what's currently running on its CPU.
|
||||
* This is just that p is wakeing up and hasn't
|
||||
* had a chance to schedule. We only pull
|
||||
* p if it is lower in priority than the
|
||||
@@ -2187,7 +2188,7 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p)
|
||||
if (!task_on_rq_queued(p) || rq->rt.rt_nr_running)
|
||||
return;
|
||||
|
||||
queue_pull_task(rq);
|
||||
rt_queue_pull_task(rq);
|
||||
}
|
||||
|
||||
void __init init_sched_rt_class(void)
|
||||
@@ -2218,7 +2219,7 @@ static void switched_to_rt(struct rq *rq, struct task_struct *p)
|
||||
if (task_on_rq_queued(p) && rq->curr != p) {
|
||||
#ifdef CONFIG_SMP
|
||||
if (p->nr_cpus_allowed > 1 && rq->rt.overloaded)
|
||||
queue_push_tasks(rq);
|
||||
rt_queue_push_tasks(rq);
|
||||
#endif /* CONFIG_SMP */
|
||||
if (p->prio < rq->curr->prio && cpu_online(cpu_of(rq)))
|
||||
resched_curr(rq);
|
||||
@@ -2242,7 +2243,7 @@ prio_changed_rt(struct rq *rq, struct task_struct *p, int oldprio)
|
||||
* may need to pull tasks to this runqueue.
|
||||
*/
|
||||
if (oldprio < p->prio)
|
||||
queue_pull_task(rq);
|
||||
rt_queue_pull_task(rq);
|
||||
|
||||
/*
|
||||
* If there's a higher priority task waiting to run
|
||||
@@ -2292,6 +2293,14 @@ static void watchdog(struct rq *rq, struct task_struct *p)
|
||||
static inline void watchdog(struct rq *rq, struct task_struct *p) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* scheduler tick hitting a task of our scheduling class.
|
||||
*
|
||||
* NOTE: This function can be called remotely by the tick offload that
|
||||
* goes along full dynticks. Therefore no local assumption can be made
|
||||
* and everything must be accessed through the @rq and @curr passed in
|
||||
* parameters.
|
||||
*/
|
||||
static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
|
||||
{
|
||||
struct sched_rt_entity *rt_se = &p->rt;
|
||||
@@ -2685,6 +2694,7 @@ int sched_rr_handler(struct ctl_table *table, int write,
|
||||
msecs_to_jiffies(sysctl_sched_rr_timeslice);
|
||||
}
|
||||
mutex_unlock(&mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+342
-302
File diff suppressed because it is too large
Load Diff
+11
-9
@@ -1,14 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
/*
|
||||
* /proc/schedstat implementation
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* bump this up when changing the output format or the meaning of an existing
|
||||
* Current schedstat API version.
|
||||
*
|
||||
* Bump this up when changing the output format or the meaning of an existing
|
||||
* format, so that tools can adapt (or abort)
|
||||
*/
|
||||
#define SCHEDSTAT_VERSION 15
|
||||
@@ -78,8 +77,8 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
||||
* This itererator needs some explanation.
|
||||
* It returns 1 for the header position.
|
||||
* This means 2 is cpu 0.
|
||||
* In a hotplugged system some cpus, including cpu 0, may be missing so we have
|
||||
* to use cpumask_* to iterate over the cpus.
|
||||
* In a hotplugged system some CPUs, including cpu 0, may be missing so we have
|
||||
* to use cpumask_* to iterate over the CPUs.
|
||||
*/
|
||||
static void *schedstat_start(struct seq_file *file, loff_t *offset)
|
||||
{
|
||||
@@ -99,12 +98,14 @@ static void *schedstat_start(struct seq_file *file, loff_t *offset)
|
||||
|
||||
if (n < nr_cpu_ids)
|
||||
return (void *)(unsigned long)(n + 2);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *schedstat_next(struct seq_file *file, void *data, loff_t *offset)
|
||||
{
|
||||
(*offset)++;
|
||||
|
||||
return schedstat_start(file, offset);
|
||||
}
|
||||
|
||||
@@ -134,6 +135,7 @@ static const struct file_operations proc_schedstat_operations = {
|
||||
static int __init proc_schedstat_init(void)
|
||||
{
|
||||
proc_create("schedstat", 0, NULL, &proc_schedstat_operations);
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(proc_schedstat_init);
|
||||
|
||||
+39
-45
@@ -30,35 +30,29 @@ rq_sched_info_dequeued(struct rq *rq, unsigned long long delta)
|
||||
if (rq)
|
||||
rq->rq_sched_info.run_delay += delta;
|
||||
}
|
||||
#define schedstat_enabled() static_branch_unlikely(&sched_schedstats)
|
||||
#define schedstat_enabled() static_branch_unlikely(&sched_schedstats)
|
||||
#define __schedstat_inc(var) do { var++; } while (0)
|
||||
#define schedstat_inc(var) do { if (schedstat_enabled()) { var++; } } while (0)
|
||||
#define schedstat_inc(var) do { if (schedstat_enabled()) { var++; } } while (0)
|
||||
#define __schedstat_add(var, amt) do { var += (amt); } while (0)
|
||||
#define schedstat_add(var, amt) do { if (schedstat_enabled()) { var += (amt); } } while (0)
|
||||
#define __schedstat_set(var, val) do { var = (val); } while (0)
|
||||
#define schedstat_set(var, val) do { if (schedstat_enabled()) { var = (val); } } while (0)
|
||||
#define schedstat_val(var) (var)
|
||||
#define schedstat_val_or_zero(var) ((schedstat_enabled()) ? (var) : 0)
|
||||
#define schedstat_add(var, amt) do { if (schedstat_enabled()) { var += (amt); } } while (0)
|
||||
#define __schedstat_set(var, val) do { var = (val); } while (0)
|
||||
#define schedstat_set(var, val) do { if (schedstat_enabled()) { var = (val); } } while (0)
|
||||
#define schedstat_val(var) (var)
|
||||
#define schedstat_val_or_zero(var) ((schedstat_enabled()) ? (var) : 0)
|
||||
|
||||
#else /* !CONFIG_SCHEDSTATS */
|
||||
static inline void
|
||||
rq_sched_info_arrive(struct rq *rq, unsigned long long delta)
|
||||
{}
|
||||
static inline void
|
||||
rq_sched_info_dequeued(struct rq *rq, unsigned long long delta)
|
||||
{}
|
||||
static inline void
|
||||
rq_sched_info_depart(struct rq *rq, unsigned long long delta)
|
||||
{}
|
||||
#define schedstat_enabled() 0
|
||||
#define __schedstat_inc(var) do { } while (0)
|
||||
#define schedstat_inc(var) do { } while (0)
|
||||
#define __schedstat_add(var, amt) do { } while (0)
|
||||
#define schedstat_add(var, amt) do { } while (0)
|
||||
#define __schedstat_set(var, val) do { } while (0)
|
||||
#define schedstat_set(var, val) do { } while (0)
|
||||
#define schedstat_val(var) 0
|
||||
#define schedstat_val_or_zero(var) 0
|
||||
#else /* !CONFIG_SCHEDSTATS: */
|
||||
static inline void rq_sched_info_arrive (struct rq *rq, unsigned long long delta) { }
|
||||
static inline void rq_sched_info_dequeued(struct rq *rq, unsigned long long delta) { }
|
||||
static inline void rq_sched_info_depart (struct rq *rq, unsigned long long delta) { }
|
||||
# define schedstat_enabled() 0
|
||||
# define __schedstat_inc(var) do { } while (0)
|
||||
# define schedstat_inc(var) do { } while (0)
|
||||
# define __schedstat_add(var, amt) do { } while (0)
|
||||
# define schedstat_add(var, amt) do { } while (0)
|
||||
# define __schedstat_set(var, val) do { } while (0)
|
||||
# define schedstat_set(var, val) do { } while (0)
|
||||
# define schedstat_val(var) 0
|
||||
# define schedstat_val_or_zero(var) 0
|
||||
#endif /* CONFIG_SCHEDSTATS */
|
||||
|
||||
#ifdef CONFIG_SCHED_INFO
|
||||
@@ -69,9 +63,9 @@ static inline void sched_info_reset_dequeued(struct task_struct *t)
|
||||
|
||||
/*
|
||||
* We are interested in knowing how long it was from the *first* time a
|
||||
* task was queued to the time that it finally hit a cpu, we call this routine
|
||||
* from dequeue_task() to account for possible rq->clock skew across cpus. The
|
||||
* delta taken on each cpu would annul the skew.
|
||||
* task was queued to the time that it finally hit a CPU, we call this routine
|
||||
* from dequeue_task() to account for possible rq->clock skew across CPUs. The
|
||||
* delta taken on each CPU would annul the skew.
|
||||
*/
|
||||
static inline void sched_info_dequeued(struct rq *rq, struct task_struct *t)
|
||||
{
|
||||
@@ -87,7 +81,7 @@ static inline void sched_info_dequeued(struct rq *rq, struct task_struct *t)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a task finally hits the cpu. We can now calculate how
|
||||
* Called when a task finally hits the CPU. We can now calculate how
|
||||
* long it was waiting to run. We also note when it began so that we
|
||||
* can keep stats on how long its timeslice is.
|
||||
*/
|
||||
@@ -112,9 +106,10 @@ static void sched_info_arrive(struct rq *rq, struct task_struct *t)
|
||||
*/
|
||||
static inline void sched_info_queued(struct rq *rq, struct task_struct *t)
|
||||
{
|
||||
if (unlikely(sched_info_on()))
|
||||
if (unlikely(sched_info_on())) {
|
||||
if (!t->sched_info.last_queued)
|
||||
t->sched_info.last_queued = rq_clock(rq);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,8 +122,7 @@ static inline void sched_info_queued(struct rq *rq, struct task_struct *t)
|
||||
*/
|
||||
static inline void sched_info_depart(struct rq *rq, struct task_struct *t)
|
||||
{
|
||||
unsigned long long delta = rq_clock(rq) -
|
||||
t->sched_info.last_arrival;
|
||||
unsigned long long delta = rq_clock(rq) - t->sched_info.last_arrival;
|
||||
|
||||
rq_sched_info_depart(rq, delta);
|
||||
|
||||
@@ -142,11 +136,10 @@ static inline void sched_info_depart(struct rq *rq, struct task_struct *t)
|
||||
* the idle task.) We are only called when prev != next.
|
||||
*/
|
||||
static inline void
|
||||
__sched_info_switch(struct rq *rq,
|
||||
struct task_struct *prev, struct task_struct *next)
|
||||
__sched_info_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
/*
|
||||
* prev now departs the cpu. It's not interesting to record
|
||||
* prev now departs the CPU. It's not interesting to record
|
||||
* stats about how efficient we were at scheduling the idle
|
||||
* process, however.
|
||||
*/
|
||||
@@ -156,18 +149,19 @@ __sched_info_switch(struct rq *rq,
|
||||
if (next != rq->idle)
|
||||
sched_info_arrive(rq, next);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sched_info_switch(struct rq *rq,
|
||||
struct task_struct *prev, struct task_struct *next)
|
||||
sched_info_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
if (unlikely(sched_info_on()))
|
||||
__sched_info_switch(rq, prev, next);
|
||||
}
|
||||
#else
|
||||
#define sched_info_queued(rq, t) do { } while (0)
|
||||
#define sched_info_reset_dequeued(t) do { } while (0)
|
||||
#define sched_info_dequeued(rq, t) do { } while (0)
|
||||
#define sched_info_depart(rq, t) do { } while (0)
|
||||
#define sched_info_arrive(rq, next) do { } while (0)
|
||||
#define sched_info_switch(rq, t, next) do { } while (0)
|
||||
|
||||
#else /* !CONFIG_SCHED_INFO: */
|
||||
# define sched_info_queued(rq, t) do { } while (0)
|
||||
# define sched_info_reset_dequeued(t) do { } while (0)
|
||||
# define sched_info_dequeued(rq, t) do { } while (0)
|
||||
# define sched_info_depart(rq, t) do { } while (0)
|
||||
# define sched_info_arrive(rq, next) do { } while (0)
|
||||
# define sched_info_switch(rq, t, next) do { } while (0)
|
||||
#endif /* CONFIG_SCHED_INFO */
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "sched.h"
|
||||
|
||||
/*
|
||||
* stop-task scheduling class.
|
||||
*
|
||||
@@ -9,6 +7,7 @@
|
||||
*
|
||||
* See kernel/stop_machine.c
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int
|
||||
@@ -75,6 +74,14 @@ static void put_prev_task_stop(struct rq *rq, struct task_struct *prev)
|
||||
cgroup_account_cputime(curr, delta_exec);
|
||||
}
|
||||
|
||||
/*
|
||||
* scheduler tick hitting a task of our scheduling class.
|
||||
*
|
||||
* NOTE: This function can be called remotely by the tick offload that
|
||||
* goes along full dynticks. Therefore no local assumption can be made
|
||||
* and everything must be accessed through the @rq and @curr passed in
|
||||
* parameters.
|
||||
*/
|
||||
static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/swait.h>
|
||||
/*
|
||||
* <linux/swait.h> (simple wait queues ) implementation:
|
||||
*/
|
||||
#include "sched.h"
|
||||
|
||||
void __init_swait_queue_head(struct swait_queue_head *q, const char *name,
|
||||
struct lock_class_key *key)
|
||||
|
||||
+21
-25
@@ -2,10 +2,6 @@
|
||||
/*
|
||||
* Scheduler topology setup/handling methods
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched/isolation.h>
|
||||
|
||||
#include "sched.h"
|
||||
|
||||
DEFINE_MUTEX(sched_domains_mutex);
|
||||
@@ -41,8 +37,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
|
||||
if (!(sd->flags & SD_LOAD_BALANCE)) {
|
||||
printk("does not load-balance\n");
|
||||
if (sd->parent)
|
||||
printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain"
|
||||
" has parent");
|
||||
printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain has parent");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -50,12 +45,10 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
|
||||
cpumask_pr_args(sched_domain_span(sd)), sd->name);
|
||||
|
||||
if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) {
|
||||
printk(KERN_ERR "ERROR: domain->span does not contain "
|
||||
"CPU%d\n", cpu);
|
||||
printk(KERN_ERR "ERROR: domain->span does not contain CPU%d\n", cpu);
|
||||
}
|
||||
if (!cpumask_test_cpu(cpu, sched_group_span(group))) {
|
||||
printk(KERN_ERR "ERROR: domain->groups does not contain"
|
||||
" CPU%d\n", cpu);
|
||||
printk(KERN_ERR "ERROR: domain->groups does not contain CPU%d\n", cpu);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%*s groups:", level + 1, "");
|
||||
@@ -115,8 +108,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
|
||||
|
||||
if (sd->parent &&
|
||||
!cpumask_subset(groupmask, sched_domain_span(sd->parent)))
|
||||
printk(KERN_ERR "ERROR: parent span is not a superset "
|
||||
"of domain->span\n");
|
||||
printk(KERN_ERR "ERROR: parent span is not a superset of domain->span\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -595,7 +587,7 @@ int group_balance_cpu(struct sched_group *sg)
|
||||
* are not.
|
||||
*
|
||||
* This leads to a few particularly weird cases where the sched_domain's are
|
||||
* not of the same number for each cpu. Consider:
|
||||
* not of the same number for each CPU. Consider:
|
||||
*
|
||||
* NUMA-2 0-3 0-3
|
||||
* groups: {0-2},{1-3} {1-3},{0-2}
|
||||
@@ -780,7 +772,7 @@ fail:
|
||||
* ^ ^ ^ ^
|
||||
* `-' `-'
|
||||
*
|
||||
* The sched_domains are per-cpu and have a two way link (parent & child) and
|
||||
* The sched_domains are per-CPU and have a two way link (parent & child) and
|
||||
* denote the ever growing mask of CPUs belonging to that level of topology.
|
||||
*
|
||||
* Each sched_domain has a circular (double) linked list of sched_group's, each
|
||||
@@ -1021,6 +1013,7 @@ __visit_domain_allocation_hell(struct s_data *d, const struct cpumask *cpu_map)
|
||||
d->rd = alloc_rootdomain();
|
||||
if (!d->rd)
|
||||
return sa_sd;
|
||||
|
||||
return sa_rootdomain;
|
||||
}
|
||||
|
||||
@@ -1047,12 +1040,14 @@ static void claim_allocations(int cpu, struct sched_domain *sd)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
static int sched_domains_numa_levels;
|
||||
enum numa_topology_type sched_numa_topology_type;
|
||||
static int *sched_domains_numa_distance;
|
||||
int sched_max_numa_distance;
|
||||
static struct cpumask ***sched_domains_numa_masks;
|
||||
static int sched_domains_curr_level;
|
||||
|
||||
static int sched_domains_numa_levels;
|
||||
static int sched_domains_curr_level;
|
||||
|
||||
int sched_max_numa_distance;
|
||||
static int *sched_domains_numa_distance;
|
||||
static struct cpumask ***sched_domains_numa_masks;
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -1074,11 +1069,11 @@ static int sched_domains_curr_level;
|
||||
* SD_ASYM_PACKING - describes SMT quirks
|
||||
*/
|
||||
#define TOPOLOGY_SD_FLAGS \
|
||||
(SD_SHARE_CPUCAPACITY | \
|
||||
(SD_SHARE_CPUCAPACITY | \
|
||||
SD_SHARE_PKG_RESOURCES | \
|
||||
SD_NUMA | \
|
||||
SD_ASYM_PACKING | \
|
||||
SD_ASYM_CPUCAPACITY | \
|
||||
SD_NUMA | \
|
||||
SD_ASYM_PACKING | \
|
||||
SD_ASYM_CPUCAPACITY | \
|
||||
SD_SHARE_POWERDOMAIN)
|
||||
|
||||
static struct sched_domain *
|
||||
@@ -1628,7 +1623,7 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve
|
||||
pr_err(" the %s domain not a subset of the %s domain\n",
|
||||
child->name, sd->name);
|
||||
#endif
|
||||
/* Fixup, ensure @sd has at least @child cpus. */
|
||||
/* Fixup, ensure @sd has at least @child CPUs. */
|
||||
cpumask_or(sched_domain_span(sd),
|
||||
sched_domain_span(sd),
|
||||
sched_domain_span(child));
|
||||
@@ -1720,6 +1715,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att
|
||||
ret = 0;
|
||||
error:
|
||||
__free_domain_allocs(&d, alloc_state, cpu_map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1824,6 +1820,7 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,
|
||||
return 1;
|
||||
|
||||
tmp = SD_ATTR_INIT;
|
||||
|
||||
return !memcmp(cur ? (cur + idx_cur) : &tmp,
|
||||
new ? (new + idx_new) : &tmp,
|
||||
sizeof(struct sched_domain_attr));
|
||||
@@ -1929,4 +1926,3 @@ match2:
|
||||
|
||||
mutex_unlock(&sched_domains_mutex);
|
||||
}
|
||||
|
||||
|
||||
+5
-8
@@ -3,14 +3,7 @@
|
||||
*
|
||||
* (C) 2004 Nadia Yvette Chambers, Oracle
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/kthread.h>
|
||||
#include "sched.h"
|
||||
|
||||
void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name, struct lock_class_key *key)
|
||||
{
|
||||
@@ -107,6 +100,7 @@ static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nr_exclusive;
|
||||
}
|
||||
|
||||
@@ -317,6 +311,7 @@ int do_wait_intr(wait_queue_head_t *wq, wait_queue_entry_t *wait)
|
||||
spin_unlock(&wq->lock);
|
||||
schedule();
|
||||
spin_lock(&wq->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(do_wait_intr);
|
||||
@@ -333,6 +328,7 @@ int do_wait_intr_irq(wait_queue_head_t *wq, wait_queue_entry_t *wait)
|
||||
spin_unlock_irq(&wq->lock);
|
||||
schedule();
|
||||
spin_lock_irq(&wq->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(do_wait_intr_irq);
|
||||
@@ -378,6 +374,7 @@ int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, i
|
||||
|
||||
if (ret)
|
||||
list_del_init(&wq_entry->entry);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(autoremove_wake_function);
|
||||
|
||||
+40
-87
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* The implementation of the wait_bit*() and related waiting APIs:
|
||||
*/
|
||||
#include <linux/wait_bit.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/hash.h>
|
||||
#include "sched.h"
|
||||
|
||||
#define WAIT_TABLE_BITS 8
|
||||
#define WAIT_TABLE_SIZE (1 << WAIT_TABLE_BITS)
|
||||
@@ -29,8 +26,8 @@ int wake_bit_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync
|
||||
wait_bit->key.bit_nr != key->bit_nr ||
|
||||
test_bit(key->bit_nr, key->flags))
|
||||
return 0;
|
||||
else
|
||||
return autoremove_wake_function(wq_entry, mode, sync, key);
|
||||
|
||||
return autoremove_wake_function(wq_entry, mode, sync, key);
|
||||
}
|
||||
EXPORT_SYMBOL(wake_bit_function);
|
||||
|
||||
@@ -50,7 +47,9 @@ __wait_on_bit(struct wait_queue_head *wq_head, struct wait_bit_queue_entry *wbq_
|
||||
if (test_bit(wbq_entry->key.bit_nr, wbq_entry->key.flags))
|
||||
ret = (*action)(&wbq_entry->key, mode);
|
||||
} while (test_bit(wbq_entry->key.bit_nr, wbq_entry->key.flags) && !ret);
|
||||
|
||||
finish_wait(wq_head, &wbq_entry->wq_entry);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(__wait_on_bit);
|
||||
@@ -73,6 +72,7 @@ int __sched out_of_line_wait_on_bit_timeout(
|
||||
DEFINE_WAIT_BIT(wq_entry, word, bit);
|
||||
|
||||
wq_entry.key.timeout = jiffies + timeout;
|
||||
|
||||
return __wait_on_bit(wq_head, &wq_entry, action, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(out_of_line_wait_on_bit_timeout);
|
||||
@@ -120,6 +120,7 @@ EXPORT_SYMBOL(out_of_line_wait_on_bit_lock);
|
||||
void __wake_up_bit(struct wait_queue_head *wq_head, void *word, int bit)
|
||||
{
|
||||
struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit);
|
||||
|
||||
if (waitqueue_active(wq_head))
|
||||
__wake_up(wq_head, TASK_NORMAL, 1, &key);
|
||||
}
|
||||
@@ -148,108 +149,55 @@ void wake_up_bit(void *word, int bit)
|
||||
}
|
||||
EXPORT_SYMBOL(wake_up_bit);
|
||||
|
||||
/*
|
||||
* Manipulate the atomic_t address to produce a better bit waitqueue table hash
|
||||
* index (we're keying off bit -1, but that would produce a horrible hash
|
||||
* value).
|
||||
*/
|
||||
static inline wait_queue_head_t *atomic_t_waitqueue(atomic_t *p)
|
||||
wait_queue_head_t *__var_waitqueue(void *p)
|
||||
{
|
||||
if (BITS_PER_LONG == 64) {
|
||||
unsigned long q = (unsigned long)p;
|
||||
return bit_waitqueue((void *)(q & ~1), q & 1);
|
||||
}
|
||||
return bit_waitqueue(p, 0);
|
||||
return bit_wait_table + hash_ptr(p, WAIT_TABLE_BITS);
|
||||
}
|
||||
EXPORT_SYMBOL(__var_waitqueue);
|
||||
|
||||
static int wake_atomic_t_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync,
|
||||
void *arg)
|
||||
static int
|
||||
var_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
|
||||
int sync, void *arg)
|
||||
{
|
||||
struct wait_bit_key *key = arg;
|
||||
struct wait_bit_queue_entry *wait_bit = container_of(wq_entry, struct wait_bit_queue_entry, wq_entry);
|
||||
atomic_t *val = key->flags;
|
||||
struct wait_bit_queue_entry *wbq_entry =
|
||||
container_of(wq_entry, struct wait_bit_queue_entry, wq_entry);
|
||||
|
||||
if (wait_bit->key.flags != key->flags ||
|
||||
wait_bit->key.bit_nr != key->bit_nr ||
|
||||
atomic_read(val) != 0)
|
||||
if (wbq_entry->key.flags != key->flags ||
|
||||
wbq_entry->key.bit_nr != key->bit_nr)
|
||||
return 0;
|
||||
|
||||
return autoremove_wake_function(wq_entry, mode, sync, key);
|
||||
}
|
||||
|
||||
/*
|
||||
* To allow interruptible waiting and asynchronous (i.e. nonblocking) waiting,
|
||||
* the actions of __wait_on_atomic_t() are permitted return codes. Nonzero
|
||||
* return codes halt waiting and return.
|
||||
*/
|
||||
static __sched
|
||||
int __wait_on_atomic_t(struct wait_queue_head *wq_head, struct wait_bit_queue_entry *wbq_entry,
|
||||
wait_atomic_t_action_f action, unsigned int mode)
|
||||
void init_wait_var_entry(struct wait_bit_queue_entry *wbq_entry, void *var, int flags)
|
||||
{
|
||||
atomic_t *val;
|
||||
int ret = 0;
|
||||
|
||||
do {
|
||||
prepare_to_wait(wq_head, &wbq_entry->wq_entry, mode);
|
||||
val = wbq_entry->key.flags;
|
||||
if (atomic_read(val) == 0)
|
||||
break;
|
||||
ret = (*action)(val, mode);
|
||||
} while (!ret && atomic_read(val) != 0);
|
||||
finish_wait(wq_head, &wbq_entry->wq_entry);
|
||||
return ret;
|
||||
*wbq_entry = (struct wait_bit_queue_entry){
|
||||
.key = {
|
||||
.flags = (var),
|
||||
.bit_nr = -1,
|
||||
},
|
||||
.wq_entry = {
|
||||
.private = current,
|
||||
.func = var_wake_function,
|
||||
.entry = LIST_HEAD_INIT(wbq_entry->wq_entry.entry),
|
||||
},
|
||||
};
|
||||
}
|
||||
EXPORT_SYMBOL(init_wait_var_entry);
|
||||
|
||||
#define DEFINE_WAIT_ATOMIC_T(name, p) \
|
||||
struct wait_bit_queue_entry name = { \
|
||||
.key = __WAIT_ATOMIC_T_KEY_INITIALIZER(p), \
|
||||
.wq_entry = { \
|
||||
.private = current, \
|
||||
.func = wake_atomic_t_function, \
|
||||
.entry = \
|
||||
LIST_HEAD_INIT((name).wq_entry.entry), \
|
||||
}, \
|
||||
}
|
||||
|
||||
__sched int out_of_line_wait_on_atomic_t(atomic_t *p,
|
||||
wait_atomic_t_action_f action,
|
||||
unsigned int mode)
|
||||
void wake_up_var(void *var)
|
||||
{
|
||||
struct wait_queue_head *wq_head = atomic_t_waitqueue(p);
|
||||
DEFINE_WAIT_ATOMIC_T(wq_entry, p);
|
||||
|
||||
return __wait_on_atomic_t(wq_head, &wq_entry, action, mode);
|
||||
__wake_up_bit(__var_waitqueue(var), var, -1);
|
||||
}
|
||||
EXPORT_SYMBOL(out_of_line_wait_on_atomic_t);
|
||||
|
||||
__sched int atomic_t_wait(atomic_t *counter, unsigned int mode)
|
||||
{
|
||||
schedule();
|
||||
if (signal_pending_state(mode, current))
|
||||
return -EINTR;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(atomic_t_wait);
|
||||
|
||||
/**
|
||||
* wake_up_atomic_t - Wake up a waiter on a atomic_t
|
||||
* @p: The atomic_t being waited on, a kernel virtual address
|
||||
*
|
||||
* Wake up anyone waiting for the atomic_t to go to zero.
|
||||
*
|
||||
* Abuse the bit-waker function and its waitqueue hash table set (the atomic_t
|
||||
* check is done by the waiter's wake function, not the by the waker itself).
|
||||
*/
|
||||
void wake_up_atomic_t(atomic_t *p)
|
||||
{
|
||||
__wake_up_bit(atomic_t_waitqueue(p), p, WAIT_ATOMIC_T_BIT_NR);
|
||||
}
|
||||
EXPORT_SYMBOL(wake_up_atomic_t);
|
||||
EXPORT_SYMBOL(wake_up_var);
|
||||
|
||||
__sched int bit_wait(struct wait_bit_key *word, int mode)
|
||||
{
|
||||
schedule();
|
||||
if (signal_pending_state(mode, current))
|
||||
return -EINTR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bit_wait);
|
||||
@@ -259,6 +207,7 @@ __sched int bit_wait_io(struct wait_bit_key *word, int mode)
|
||||
io_schedule();
|
||||
if (signal_pending_state(mode, current))
|
||||
return -EINTR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(bit_wait_io);
|
||||
@@ -266,11 +215,13 @@ EXPORT_SYMBOL(bit_wait_io);
|
||||
__sched int bit_wait_timeout(struct wait_bit_key *word, int mode)
|
||||
{
|
||||
unsigned long now = READ_ONCE(jiffies);
|
||||
|
||||
if (time_after_eq(now, word->timeout))
|
||||
return -EAGAIN;
|
||||
schedule_timeout(word->timeout - now);
|
||||
if (signal_pending_state(mode, current))
|
||||
return -EINTR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bit_wait_timeout);
|
||||
@@ -278,11 +229,13 @@ EXPORT_SYMBOL_GPL(bit_wait_timeout);
|
||||
__sched int bit_wait_io_timeout(struct wait_bit_key *word, int mode)
|
||||
{
|
||||
unsigned long now = READ_ONCE(jiffies);
|
||||
|
||||
if (time_after_eq(now, word->timeout))
|
||||
return -EAGAIN;
|
||||
io_schedule_timeout(word->timeout - now);
|
||||
if (signal_pending_state(mode, current))
|
||||
return -EINTR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bit_wait_io_timeout);
|
||||
|
||||
+22
-7
@@ -3573,9 +3573,8 @@ int __save_altstack(stack_t __user *uss, unsigned long sp)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
COMPAT_SYSCALL_DEFINE2(sigaltstack,
|
||||
const compat_stack_t __user *, uss_ptr,
|
||||
compat_stack_t __user *, uoss_ptr)
|
||||
static int do_compat_sigaltstack(const compat_stack_t __user *uss_ptr,
|
||||
compat_stack_t __user *uoss_ptr)
|
||||
{
|
||||
stack_t uss, uoss;
|
||||
int ret;
|
||||
@@ -3602,9 +3601,16 @@ COMPAT_SYSCALL_DEFINE2(sigaltstack,
|
||||
return ret;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE2(sigaltstack,
|
||||
const compat_stack_t __user *, uss_ptr,
|
||||
compat_stack_t __user *, uoss_ptr)
|
||||
{
|
||||
return do_compat_sigaltstack(uss_ptr, uoss_ptr);
|
||||
}
|
||||
|
||||
int compat_restore_altstack(const compat_stack_t __user *uss)
|
||||
{
|
||||
int err = compat_sys_sigaltstack(uss, NULL);
|
||||
int err = do_compat_sigaltstack(uss, NULL);
|
||||
/* squash all but -EFAULT for now */
|
||||
return err == -EFAULT ? err : 0;
|
||||
}
|
||||
@@ -3629,11 +3635,20 @@ int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp)
|
||||
|
||||
/**
|
||||
* sys_sigpending - examine pending signals
|
||||
* @set: where mask of pending signal is returned
|
||||
* @uset: where mask of pending signal is returned
|
||||
*/
|
||||
SYSCALL_DEFINE1(sigpending, old_sigset_t __user *, set)
|
||||
SYSCALL_DEFINE1(sigpending, old_sigset_t __user *, uset)
|
||||
{
|
||||
return sys_rt_sigpending((sigset_t __user *)set, sizeof(old_sigset_t));
|
||||
sigset_t set;
|
||||
int err;
|
||||
|
||||
if (sizeof(old_sigset_t) > sizeof(*uset))
|
||||
return -EINVAL;
|
||||
|
||||
err = do_sigpending(&set);
|
||||
if (!err && copy_to_user(uset, &set, sizeof(old_sigset_t)))
|
||||
err = -EFAULT;
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
+63
-11
@@ -69,6 +69,8 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#include "uid16.h"
|
||||
|
||||
#ifndef SET_UNALIGN_CTL
|
||||
# define SET_UNALIGN_CTL(a, b) (-EINVAL)
|
||||
#endif
|
||||
@@ -340,7 +342,7 @@ out_unlock:
|
||||
* operations (as far as semantic preservation is concerned).
|
||||
*/
|
||||
#ifdef CONFIG_MULTIUSER
|
||||
SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
|
||||
long __sys_setregid(gid_t rgid, gid_t egid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -392,12 +394,17 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
|
||||
{
|
||||
return __sys_setregid(rgid, egid);
|
||||
}
|
||||
|
||||
/*
|
||||
* setgid() is implemented like SysV w/ SAVED_IDS
|
||||
*
|
||||
* SMP: Same implicit races as above.
|
||||
*/
|
||||
SYSCALL_DEFINE1(setgid, gid_t, gid)
|
||||
long __sys_setgid(gid_t gid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -429,6 +436,11 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setgid, gid_t, gid)
|
||||
{
|
||||
return __sys_setgid(gid);
|
||||
}
|
||||
|
||||
/*
|
||||
* change the user struct in a credentials set to match the new UID
|
||||
*/
|
||||
@@ -473,7 +485,7 @@ static int set_user(struct cred *new)
|
||||
* 100% compatible with BSD. A program which uses just setuid() will be
|
||||
* 100% compatible with POSIX with saved IDs.
|
||||
*/
|
||||
SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
|
||||
long __sys_setreuid(uid_t ruid, uid_t euid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -533,6 +545,11 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
|
||||
{
|
||||
return __sys_setreuid(ruid, euid);
|
||||
}
|
||||
|
||||
/*
|
||||
* setuid() is implemented like SysV with SAVED_IDS
|
||||
*
|
||||
@@ -544,7 +561,7 @@ error:
|
||||
* will allow a root program to temporarily drop privileges and be able to
|
||||
* regain them by swapping the real and effective uid.
|
||||
*/
|
||||
SYSCALL_DEFINE1(setuid, uid_t, uid)
|
||||
long __sys_setuid(uid_t uid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -586,12 +603,17 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setuid, uid_t, uid)
|
||||
{
|
||||
return __sys_setuid(uid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function implements a generic ability to update ruid, euid,
|
||||
* and suid. This allows you to implement the 4.4 compatible seteuid().
|
||||
*/
|
||||
SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
|
||||
long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -656,6 +678,11 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
|
||||
{
|
||||
return __sys_setresuid(ruid, euid, suid);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t __user *, suidp)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
@@ -678,7 +705,7 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
|
||||
/*
|
||||
* Same as above, but for rgid, egid, sgid.
|
||||
*/
|
||||
SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
|
||||
long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
|
||||
{
|
||||
struct user_namespace *ns = current_user_ns();
|
||||
const struct cred *old;
|
||||
@@ -730,6 +757,11 @@ error:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
|
||||
{
|
||||
return __sys_setresgid(rgid, egid, sgid);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t __user *, sgidp)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
@@ -757,7 +789,7 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
|
||||
* whatever uid it wants to). It normally shadows "euid", except when
|
||||
* explicitly set by setfsuid() or for access..
|
||||
*/
|
||||
SYSCALL_DEFINE1(setfsuid, uid_t, uid)
|
||||
long __sys_setfsuid(uid_t uid)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
@@ -793,10 +825,15 @@ change_okay:
|
||||
return old_fsuid;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setfsuid, uid_t, uid)
|
||||
{
|
||||
return __sys_setfsuid(uid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Samma på svenska..
|
||||
*/
|
||||
SYSCALL_DEFINE1(setfsgid, gid_t, gid)
|
||||
long __sys_setfsgid(gid_t gid)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
@@ -830,6 +867,11 @@ change_okay:
|
||||
commit_creds(new);
|
||||
return old_fsgid;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setfsgid, gid_t, gid)
|
||||
{
|
||||
return __sys_setfsgid(gid);
|
||||
}
|
||||
#endif /* CONFIG_MULTIUSER */
|
||||
|
||||
/**
|
||||
@@ -1027,7 +1069,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(getpgid, pid_t, pid)
|
||||
static int do_getpgid(pid_t pid)
|
||||
{
|
||||
struct task_struct *p;
|
||||
struct pid *grp;
|
||||
@@ -1055,11 +1097,16 @@ out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(getpgid, pid_t, pid)
|
||||
{
|
||||
return do_getpgid(pid);
|
||||
}
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_GETPGRP
|
||||
|
||||
SYSCALL_DEFINE0(getpgrp)
|
||||
{
|
||||
return sys_getpgid(0);
|
||||
return do_getpgid(0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1103,7 +1150,7 @@ static void set_special_pids(struct pid *pid)
|
||||
change_pid(curr, PIDTYPE_PGID, pid);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE0(setsid)
|
||||
int ksys_setsid(void)
|
||||
{
|
||||
struct task_struct *group_leader = current->group_leader;
|
||||
struct pid *sid = task_pid(group_leader);
|
||||
@@ -1136,6 +1183,11 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE0(setsid)
|
||||
{
|
||||
return ksys_setsid();
|
||||
}
|
||||
|
||||
DECLARE_RWSEM(uts_sem);
|
||||
|
||||
#ifdef COMPAT_UTS_MACHINE
|
||||
|
||||
+383
-222
@@ -17,245 +17,406 @@ asmlinkage long sys_ni_syscall(void)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
cond_syscall(sys_quotactl);
|
||||
cond_syscall(sys32_quotactl);
|
||||
cond_syscall(sys_acct);
|
||||
cond_syscall(sys_lookup_dcookie);
|
||||
cond_syscall(compat_sys_lookup_dcookie);
|
||||
cond_syscall(sys_swapon);
|
||||
cond_syscall(sys_swapoff);
|
||||
cond_syscall(sys_kexec_load);
|
||||
cond_syscall(compat_sys_kexec_load);
|
||||
cond_syscall(sys_kexec_file_load);
|
||||
cond_syscall(sys_init_module);
|
||||
cond_syscall(sys_finit_module);
|
||||
cond_syscall(sys_delete_module);
|
||||
cond_syscall(sys_socketpair);
|
||||
cond_syscall(sys_bind);
|
||||
cond_syscall(sys_listen);
|
||||
cond_syscall(sys_accept);
|
||||
cond_syscall(sys_accept4);
|
||||
cond_syscall(sys_connect);
|
||||
cond_syscall(sys_getsockname);
|
||||
cond_syscall(sys_getpeername);
|
||||
cond_syscall(sys_sendto);
|
||||
cond_syscall(sys_send);
|
||||
cond_syscall(sys_recvfrom);
|
||||
cond_syscall(sys_recv);
|
||||
cond_syscall(sys_socket);
|
||||
cond_syscall(sys_setsockopt);
|
||||
cond_syscall(compat_sys_setsockopt);
|
||||
cond_syscall(sys_getsockopt);
|
||||
cond_syscall(compat_sys_getsockopt);
|
||||
cond_syscall(sys_shutdown);
|
||||
cond_syscall(sys_sendmsg);
|
||||
cond_syscall(sys_sendmmsg);
|
||||
cond_syscall(compat_sys_sendmsg);
|
||||
cond_syscall(compat_sys_sendmmsg);
|
||||
cond_syscall(sys_recvmsg);
|
||||
cond_syscall(sys_recvmmsg);
|
||||
cond_syscall(compat_sys_recvmsg);
|
||||
cond_syscall(compat_sys_recv);
|
||||
cond_syscall(compat_sys_recvfrom);
|
||||
cond_syscall(compat_sys_recvmmsg);
|
||||
cond_syscall(sys_socketcall);
|
||||
cond_syscall(sys_futex);
|
||||
cond_syscall(compat_sys_futex);
|
||||
cond_syscall(sys_set_robust_list);
|
||||
cond_syscall(compat_sys_set_robust_list);
|
||||
cond_syscall(sys_get_robust_list);
|
||||
cond_syscall(compat_sys_get_robust_list);
|
||||
cond_syscall(sys_epoll_create);
|
||||
cond_syscall(sys_epoll_create1);
|
||||
cond_syscall(sys_epoll_ctl);
|
||||
cond_syscall(sys_epoll_wait);
|
||||
cond_syscall(sys_epoll_pwait);
|
||||
cond_syscall(compat_sys_epoll_pwait);
|
||||
cond_syscall(sys_semget);
|
||||
cond_syscall(sys_semop);
|
||||
cond_syscall(sys_semtimedop);
|
||||
cond_syscall(compat_sys_semtimedop);
|
||||
cond_syscall(sys_semctl);
|
||||
cond_syscall(compat_sys_semctl);
|
||||
cond_syscall(sys_msgget);
|
||||
cond_syscall(sys_msgsnd);
|
||||
cond_syscall(compat_sys_msgsnd);
|
||||
cond_syscall(sys_msgrcv);
|
||||
cond_syscall(compat_sys_msgrcv);
|
||||
cond_syscall(sys_msgctl);
|
||||
cond_syscall(compat_sys_msgctl);
|
||||
cond_syscall(sys_shmget);
|
||||
cond_syscall(sys_shmat);
|
||||
cond_syscall(compat_sys_shmat);
|
||||
cond_syscall(sys_shmdt);
|
||||
cond_syscall(sys_shmctl);
|
||||
cond_syscall(compat_sys_shmctl);
|
||||
cond_syscall(sys_mq_open);
|
||||
cond_syscall(sys_mq_unlink);
|
||||
cond_syscall(sys_mq_timedsend);
|
||||
cond_syscall(sys_mq_timedreceive);
|
||||
cond_syscall(sys_mq_notify);
|
||||
cond_syscall(sys_mq_getsetattr);
|
||||
cond_syscall(compat_sys_mq_open);
|
||||
cond_syscall(compat_sys_mq_timedsend);
|
||||
cond_syscall(compat_sys_mq_timedreceive);
|
||||
cond_syscall(compat_sys_mq_notify);
|
||||
cond_syscall(compat_sys_mq_getsetattr);
|
||||
cond_syscall(sys_mbind);
|
||||
cond_syscall(sys_get_mempolicy);
|
||||
cond_syscall(sys_set_mempolicy);
|
||||
cond_syscall(compat_sys_mbind);
|
||||
cond_syscall(compat_sys_get_mempolicy);
|
||||
cond_syscall(compat_sys_set_mempolicy);
|
||||
cond_syscall(sys_add_key);
|
||||
cond_syscall(sys_request_key);
|
||||
cond_syscall(sys_keyctl);
|
||||
cond_syscall(compat_sys_keyctl);
|
||||
cond_syscall(compat_sys_socketcall);
|
||||
cond_syscall(sys_inotify_init);
|
||||
cond_syscall(sys_inotify_init1);
|
||||
cond_syscall(sys_inotify_add_watch);
|
||||
cond_syscall(sys_inotify_rm_watch);
|
||||
cond_syscall(sys_migrate_pages);
|
||||
cond_syscall(sys_move_pages);
|
||||
cond_syscall(sys_chown16);
|
||||
cond_syscall(sys_fchown16);
|
||||
cond_syscall(sys_getegid16);
|
||||
cond_syscall(sys_geteuid16);
|
||||
cond_syscall(sys_getgid16);
|
||||
cond_syscall(sys_getgroups16);
|
||||
cond_syscall(sys_getresgid16);
|
||||
cond_syscall(sys_getresuid16);
|
||||
cond_syscall(sys_getuid16);
|
||||
cond_syscall(sys_lchown16);
|
||||
cond_syscall(sys_setfsgid16);
|
||||
cond_syscall(sys_setfsuid16);
|
||||
cond_syscall(sys_setgid16);
|
||||
cond_syscall(sys_setgroups16);
|
||||
cond_syscall(sys_setregid16);
|
||||
cond_syscall(sys_setresgid16);
|
||||
cond_syscall(sys_setresuid16);
|
||||
cond_syscall(sys_setreuid16);
|
||||
cond_syscall(sys_setuid16);
|
||||
cond_syscall(sys_sgetmask);
|
||||
cond_syscall(sys_ssetmask);
|
||||
cond_syscall(sys_vm86old);
|
||||
cond_syscall(sys_vm86);
|
||||
cond_syscall(sys_modify_ldt);
|
||||
cond_syscall(sys_ipc);
|
||||
cond_syscall(compat_sys_ipc);
|
||||
cond_syscall(compat_sys_sysctl);
|
||||
cond_syscall(sys_flock);
|
||||
cond_syscall(sys_io_setup);
|
||||
cond_syscall(sys_io_destroy);
|
||||
cond_syscall(sys_io_submit);
|
||||
cond_syscall(sys_io_cancel);
|
||||
cond_syscall(sys_io_getevents);
|
||||
cond_syscall(compat_sys_io_setup);
|
||||
cond_syscall(compat_sys_io_submit);
|
||||
cond_syscall(compat_sys_io_getevents);
|
||||
cond_syscall(sys_sysfs);
|
||||
cond_syscall(sys_syslog);
|
||||
cond_syscall(sys_process_vm_readv);
|
||||
cond_syscall(sys_process_vm_writev);
|
||||
cond_syscall(compat_sys_process_vm_readv);
|
||||
cond_syscall(compat_sys_process_vm_writev);
|
||||
cond_syscall(sys_uselib);
|
||||
cond_syscall(sys_fadvise64);
|
||||
cond_syscall(sys_fadvise64_64);
|
||||
cond_syscall(sys_madvise);
|
||||
cond_syscall(sys_setuid);
|
||||
cond_syscall(sys_setregid);
|
||||
cond_syscall(sys_setgid);
|
||||
cond_syscall(sys_setreuid);
|
||||
cond_syscall(sys_setresuid);
|
||||
cond_syscall(sys_getresuid);
|
||||
cond_syscall(sys_setresgid);
|
||||
cond_syscall(sys_getresgid);
|
||||
cond_syscall(sys_setgroups);
|
||||
cond_syscall(sys_getgroups);
|
||||
cond_syscall(sys_setfsuid);
|
||||
cond_syscall(sys_setfsgid);
|
||||
cond_syscall(sys_capget);
|
||||
cond_syscall(sys_capset);
|
||||
cond_syscall(sys_copy_file_range);
|
||||
#define COND_SYSCALL(name) cond_syscall(sys_##name)
|
||||
#define COND_SYSCALL_COMPAT(name) cond_syscall(compat_sys_##name)
|
||||
|
||||
/* arch-specific weak syscall entries */
|
||||
cond_syscall(sys_pciconfig_read);
|
||||
cond_syscall(sys_pciconfig_write);
|
||||
cond_syscall(sys_pciconfig_iobase);
|
||||
cond_syscall(compat_sys_s390_ipc);
|
||||
cond_syscall(ppc_rtas);
|
||||
cond_syscall(sys_spu_run);
|
||||
cond_syscall(sys_spu_create);
|
||||
cond_syscall(sys_subpage_prot);
|
||||
cond_syscall(sys_s390_pci_mmio_read);
|
||||
cond_syscall(sys_s390_pci_mmio_write);
|
||||
/*
|
||||
* This list is kept in the same order as include/uapi/asm-generic/unistd.h.
|
||||
* Architecture specific entries go below, followed by deprecated or obsolete
|
||||
* system calls.
|
||||
*/
|
||||
|
||||
/* mmu depending weak syscall entries */
|
||||
cond_syscall(sys_mprotect);
|
||||
cond_syscall(sys_msync);
|
||||
cond_syscall(sys_mlock);
|
||||
cond_syscall(sys_munlock);
|
||||
cond_syscall(sys_mlockall);
|
||||
cond_syscall(sys_munlockall);
|
||||
cond_syscall(sys_mlock2);
|
||||
cond_syscall(sys_mincore);
|
||||
cond_syscall(sys_madvise);
|
||||
cond_syscall(sys_mremap);
|
||||
cond_syscall(sys_remap_file_pages);
|
||||
cond_syscall(compat_sys_move_pages);
|
||||
cond_syscall(compat_sys_migrate_pages);
|
||||
COND_SYSCALL(io_setup);
|
||||
COND_SYSCALL_COMPAT(io_setup);
|
||||
COND_SYSCALL(io_destroy);
|
||||
COND_SYSCALL(io_submit);
|
||||
COND_SYSCALL_COMPAT(io_submit);
|
||||
COND_SYSCALL(io_cancel);
|
||||
COND_SYSCALL(io_getevents);
|
||||
COND_SYSCALL_COMPAT(io_getevents);
|
||||
|
||||
/* block-layer dependent */
|
||||
cond_syscall(sys_bdflush);
|
||||
cond_syscall(sys_ioprio_set);
|
||||
cond_syscall(sys_ioprio_get);
|
||||
/* fs/xattr.c */
|
||||
|
||||
/* New file descriptors */
|
||||
cond_syscall(sys_signalfd);
|
||||
cond_syscall(sys_signalfd4);
|
||||
cond_syscall(compat_sys_signalfd);
|
||||
cond_syscall(compat_sys_signalfd4);
|
||||
cond_syscall(sys_timerfd_create);
|
||||
cond_syscall(sys_timerfd_settime);
|
||||
cond_syscall(sys_timerfd_gettime);
|
||||
cond_syscall(compat_sys_timerfd_settime);
|
||||
cond_syscall(compat_sys_timerfd_gettime);
|
||||
cond_syscall(sys_eventfd);
|
||||
cond_syscall(sys_eventfd2);
|
||||
cond_syscall(sys_memfd_create);
|
||||
cond_syscall(sys_userfaultfd);
|
||||
/* fs/dcache.c */
|
||||
|
||||
/* performance counters: */
|
||||
cond_syscall(sys_perf_event_open);
|
||||
/* fs/cookies.c */
|
||||
COND_SYSCALL(lookup_dcookie);
|
||||
COND_SYSCALL_COMPAT(lookup_dcookie);
|
||||
|
||||
/* fanotify! */
|
||||
cond_syscall(sys_fanotify_init);
|
||||
cond_syscall(sys_fanotify_mark);
|
||||
cond_syscall(compat_sys_fanotify_mark);
|
||||
/* fs/eventfd.c */
|
||||
COND_SYSCALL(eventfd2);
|
||||
|
||||
/* fs/eventfd.c */
|
||||
COND_SYSCALL(epoll_create1);
|
||||
COND_SYSCALL(epoll_ctl);
|
||||
COND_SYSCALL(epoll_pwait);
|
||||
COND_SYSCALL_COMPAT(epoll_pwait);
|
||||
|
||||
/* fs/fcntl.c */
|
||||
|
||||
/* fs/inotify_user.c */
|
||||
COND_SYSCALL(inotify_init1);
|
||||
COND_SYSCALL(inotify_add_watch);
|
||||
COND_SYSCALL(inotify_rm_watch);
|
||||
|
||||
/* fs/ioctl.c */
|
||||
|
||||
/* fs/ioprio.c */
|
||||
COND_SYSCALL(ioprio_set);
|
||||
COND_SYSCALL(ioprio_get);
|
||||
|
||||
/* fs/locks.c */
|
||||
COND_SYSCALL(flock);
|
||||
|
||||
/* fs/namei.c */
|
||||
|
||||
/* fs/namespace.c */
|
||||
|
||||
/* fs/nfsctl.c */
|
||||
|
||||
/* fs/open.c */
|
||||
|
||||
/* fs/pipe.c */
|
||||
|
||||
/* fs/quota.c */
|
||||
COND_SYSCALL(quotactl);
|
||||
|
||||
/* fs/readdir.c */
|
||||
|
||||
/* fs/read_write.c */
|
||||
|
||||
/* fs/sendfile.c */
|
||||
|
||||
/* fs/select.c */
|
||||
|
||||
/* fs/signalfd.c */
|
||||
COND_SYSCALL(signalfd4);
|
||||
COND_SYSCALL_COMPAT(signalfd4);
|
||||
|
||||
/* fs/splice.c */
|
||||
|
||||
/* fs/stat.c */
|
||||
|
||||
/* fs/sync.c */
|
||||
|
||||
/* fs/timerfd.c */
|
||||
COND_SYSCALL(timerfd_create);
|
||||
COND_SYSCALL(timerfd_settime);
|
||||
COND_SYSCALL_COMPAT(timerfd_settime);
|
||||
COND_SYSCALL(timerfd_gettime);
|
||||
COND_SYSCALL_COMPAT(timerfd_gettime);
|
||||
|
||||
/* fs/utimes.c */
|
||||
|
||||
/* kernel/acct.c */
|
||||
COND_SYSCALL(acct);
|
||||
|
||||
/* kernel/capability.c */
|
||||
COND_SYSCALL(capget);
|
||||
COND_SYSCALL(capset);
|
||||
|
||||
/* kernel/exec_domain.c */
|
||||
|
||||
/* kernel/exit.c */
|
||||
|
||||
/* kernel/fork.c */
|
||||
|
||||
/* kernel/futex.c */
|
||||
COND_SYSCALL(futex);
|
||||
COND_SYSCALL_COMPAT(futex);
|
||||
COND_SYSCALL(set_robust_list);
|
||||
COND_SYSCALL_COMPAT(set_robust_list);
|
||||
COND_SYSCALL(get_robust_list);
|
||||
COND_SYSCALL_COMPAT(get_robust_list);
|
||||
|
||||
/* kernel/hrtimer.c */
|
||||
|
||||
/* kernel/itimer.c */
|
||||
|
||||
/* kernel/kexec.c */
|
||||
COND_SYSCALL(kexec_load);
|
||||
COND_SYSCALL_COMPAT(kexec_load);
|
||||
|
||||
/* kernel/module.c */
|
||||
COND_SYSCALL(init_module);
|
||||
COND_SYSCALL(delete_module);
|
||||
|
||||
/* kernel/posix-timers.c */
|
||||
|
||||
/* kernel/printk.c */
|
||||
COND_SYSCALL(syslog);
|
||||
|
||||
/* kernel/ptrace.c */
|
||||
|
||||
/* kernel/sched/core.c */
|
||||
|
||||
/* kernel/signal.c */
|
||||
|
||||
/* kernel/sys.c */
|
||||
COND_SYSCALL(setregid);
|
||||
COND_SYSCALL(setgid);
|
||||
COND_SYSCALL(setreuid);
|
||||
COND_SYSCALL(setuid);
|
||||
COND_SYSCALL(setresuid);
|
||||
COND_SYSCALL(getresuid);
|
||||
COND_SYSCALL(setresgid);
|
||||
COND_SYSCALL(getresgid);
|
||||
COND_SYSCALL(setfsuid);
|
||||
COND_SYSCALL(setfsgid);
|
||||
COND_SYSCALL(setgroups);
|
||||
COND_SYSCALL(getgroups);
|
||||
|
||||
/* kernel/time.c */
|
||||
|
||||
/* kernel/timer.c */
|
||||
|
||||
/* ipc/mqueue.c */
|
||||
COND_SYSCALL(mq_open);
|
||||
COND_SYSCALL_COMPAT(mq_open);
|
||||
COND_SYSCALL(mq_unlink);
|
||||
COND_SYSCALL(mq_timedsend);
|
||||
COND_SYSCALL_COMPAT(mq_timedsend);
|
||||
COND_SYSCALL(mq_timedreceive);
|
||||
COND_SYSCALL_COMPAT(mq_timedreceive);
|
||||
COND_SYSCALL(mq_notify);
|
||||
COND_SYSCALL_COMPAT(mq_notify);
|
||||
COND_SYSCALL(mq_getsetattr);
|
||||
COND_SYSCALL_COMPAT(mq_getsetattr);
|
||||
|
||||
/* ipc/msg.c */
|
||||
COND_SYSCALL(msgget);
|
||||
COND_SYSCALL(msgctl);
|
||||
COND_SYSCALL_COMPAT(msgctl);
|
||||
COND_SYSCALL(msgrcv);
|
||||
COND_SYSCALL_COMPAT(msgrcv);
|
||||
COND_SYSCALL(msgsnd);
|
||||
COND_SYSCALL_COMPAT(msgsnd);
|
||||
|
||||
/* ipc/sem.c */
|
||||
COND_SYSCALL(semget);
|
||||
COND_SYSCALL(semctl);
|
||||
COND_SYSCALL_COMPAT(semctl);
|
||||
COND_SYSCALL(semtimedop);
|
||||
COND_SYSCALL_COMPAT(semtimedop);
|
||||
COND_SYSCALL(semop);
|
||||
|
||||
/* ipc/shm.c */
|
||||
COND_SYSCALL(shmget);
|
||||
COND_SYSCALL(shmctl);
|
||||
COND_SYSCALL_COMPAT(shmctl);
|
||||
COND_SYSCALL(shmat);
|
||||
COND_SYSCALL_COMPAT(shmat);
|
||||
COND_SYSCALL(shmdt);
|
||||
|
||||
/* net/socket.c */
|
||||
COND_SYSCALL(socket);
|
||||
COND_SYSCALL(socketpair);
|
||||
COND_SYSCALL(bind);
|
||||
COND_SYSCALL(listen);
|
||||
COND_SYSCALL(accept);
|
||||
COND_SYSCALL(connect);
|
||||
COND_SYSCALL(getsockname);
|
||||
COND_SYSCALL(getpeername);
|
||||
COND_SYSCALL(setsockopt);
|
||||
COND_SYSCALL_COMPAT(setsockopt);
|
||||
COND_SYSCALL(getsockopt);
|
||||
COND_SYSCALL_COMPAT(getsockopt);
|
||||
COND_SYSCALL(sendto);
|
||||
COND_SYSCALL(shutdown);
|
||||
COND_SYSCALL(recvfrom);
|
||||
COND_SYSCALL_COMPAT(recvfrom);
|
||||
COND_SYSCALL(sendmsg);
|
||||
COND_SYSCALL_COMPAT(sendmsg);
|
||||
COND_SYSCALL(recvmsg);
|
||||
COND_SYSCALL_COMPAT(recvmsg);
|
||||
|
||||
/* mm/filemap.c */
|
||||
|
||||
/* mm/nommu.c, also with MMU */
|
||||
COND_SYSCALL(mremap);
|
||||
|
||||
/* security/keys/keyctl.c */
|
||||
COND_SYSCALL(add_key);
|
||||
COND_SYSCALL(request_key);
|
||||
COND_SYSCALL(keyctl);
|
||||
COND_SYSCALL_COMPAT(keyctl);
|
||||
|
||||
/* arch/example/kernel/sys_example.c */
|
||||
|
||||
/* mm/fadvise.c */
|
||||
COND_SYSCALL(fadvise64_64);
|
||||
|
||||
/* mm/, CONFIG_MMU only */
|
||||
COND_SYSCALL(swapon);
|
||||
COND_SYSCALL(swapoff);
|
||||
COND_SYSCALL(mprotect);
|
||||
COND_SYSCALL(msync);
|
||||
COND_SYSCALL(mlock);
|
||||
COND_SYSCALL(munlock);
|
||||
COND_SYSCALL(mlockall);
|
||||
COND_SYSCALL(munlockall);
|
||||
COND_SYSCALL(mincore);
|
||||
COND_SYSCALL(madvise);
|
||||
COND_SYSCALL(remap_file_pages);
|
||||
COND_SYSCALL(mbind);
|
||||
COND_SYSCALL_COMPAT(mbind);
|
||||
COND_SYSCALL(get_mempolicy);
|
||||
COND_SYSCALL_COMPAT(get_mempolicy);
|
||||
COND_SYSCALL(set_mempolicy);
|
||||
COND_SYSCALL_COMPAT(set_mempolicy);
|
||||
COND_SYSCALL(migrate_pages);
|
||||
COND_SYSCALL_COMPAT(migrate_pages);
|
||||
COND_SYSCALL(move_pages);
|
||||
COND_SYSCALL_COMPAT(move_pages);
|
||||
|
||||
COND_SYSCALL(perf_event_open);
|
||||
COND_SYSCALL(accept4);
|
||||
COND_SYSCALL(recvmmsg);
|
||||
COND_SYSCALL_COMPAT(recvmmsg);
|
||||
|
||||
/*
|
||||
* Architecture specific syscalls: see further below
|
||||
*/
|
||||
|
||||
/* fanotify */
|
||||
COND_SYSCALL(fanotify_init);
|
||||
COND_SYSCALL(fanotify_mark);
|
||||
|
||||
/* open by handle */
|
||||
cond_syscall(sys_name_to_handle_at);
|
||||
cond_syscall(sys_open_by_handle_at);
|
||||
cond_syscall(compat_sys_open_by_handle_at);
|
||||
COND_SYSCALL(name_to_handle_at);
|
||||
COND_SYSCALL(open_by_handle_at);
|
||||
COND_SYSCALL_COMPAT(open_by_handle_at);
|
||||
|
||||
COND_SYSCALL(sendmmsg);
|
||||
COND_SYSCALL_COMPAT(sendmmsg);
|
||||
COND_SYSCALL(process_vm_readv);
|
||||
COND_SYSCALL_COMPAT(process_vm_readv);
|
||||
COND_SYSCALL(process_vm_writev);
|
||||
COND_SYSCALL_COMPAT(process_vm_writev);
|
||||
|
||||
/* compare kernel pointers */
|
||||
cond_syscall(sys_kcmp);
|
||||
COND_SYSCALL(kcmp);
|
||||
|
||||
COND_SYSCALL(finit_module);
|
||||
|
||||
/* operate on Secure Computing state */
|
||||
cond_syscall(sys_seccomp);
|
||||
COND_SYSCALL(seccomp);
|
||||
|
||||
COND_SYSCALL(memfd_create);
|
||||
|
||||
/* access BPF programs and maps */
|
||||
cond_syscall(sys_bpf);
|
||||
COND_SYSCALL(bpf);
|
||||
|
||||
/* execveat */
|
||||
cond_syscall(sys_execveat);
|
||||
COND_SYSCALL(execveat);
|
||||
|
||||
COND_SYSCALL(userfaultfd);
|
||||
|
||||
/* membarrier */
|
||||
cond_syscall(sys_membarrier);
|
||||
COND_SYSCALL(membarrier);
|
||||
|
||||
COND_SYSCALL(mlock2);
|
||||
|
||||
COND_SYSCALL(copy_file_range);
|
||||
|
||||
/* memory protection keys */
|
||||
cond_syscall(sys_pkey_mprotect);
|
||||
cond_syscall(sys_pkey_alloc);
|
||||
cond_syscall(sys_pkey_free);
|
||||
COND_SYSCALL(pkey_mprotect);
|
||||
COND_SYSCALL(pkey_alloc);
|
||||
COND_SYSCALL(pkey_free);
|
||||
|
||||
|
||||
/*
|
||||
* Architecture specific weak syscall entries.
|
||||
*/
|
||||
|
||||
/* pciconfig: alpha, arm, arm64, ia64, sparc */
|
||||
COND_SYSCALL(pciconfig_read);
|
||||
COND_SYSCALL(pciconfig_write);
|
||||
COND_SYSCALL(pciconfig_iobase);
|
||||
|
||||
/* sys_socketcall: arm, mips, x86, ... */
|
||||
COND_SYSCALL(socketcall);
|
||||
COND_SYSCALL_COMPAT(socketcall);
|
||||
|
||||
/* compat syscalls for arm64, x86, ... */
|
||||
COND_SYSCALL_COMPAT(sysctl);
|
||||
COND_SYSCALL_COMPAT(fanotify_mark);
|
||||
|
||||
/* x86 */
|
||||
COND_SYSCALL(vm86old);
|
||||
COND_SYSCALL(modify_ldt);
|
||||
COND_SYSCALL_COMPAT(quotactl32);
|
||||
COND_SYSCALL(vm86);
|
||||
COND_SYSCALL(kexec_file_load);
|
||||
|
||||
/* s390 */
|
||||
COND_SYSCALL(s390_pci_mmio_read);
|
||||
COND_SYSCALL(s390_pci_mmio_write);
|
||||
COND_SYSCALL_COMPAT(s390_ipc);
|
||||
|
||||
/* powerpc */
|
||||
cond_syscall(ppc_rtas);
|
||||
COND_SYSCALL(spu_run);
|
||||
COND_SYSCALL(spu_create);
|
||||
COND_SYSCALL(subpage_prot);
|
||||
|
||||
|
||||
/*
|
||||
* Deprecated system calls which are still defined in
|
||||
* include/uapi/asm-generic/unistd.h and wanted by >= 1 arch
|
||||
*/
|
||||
|
||||
/* __ARCH_WANT_SYSCALL_NO_FLAGS */
|
||||
COND_SYSCALL(epoll_create);
|
||||
COND_SYSCALL(inotify_init);
|
||||
COND_SYSCALL(eventfd);
|
||||
COND_SYSCALL(signalfd);
|
||||
COND_SYSCALL_COMPAT(signalfd);
|
||||
|
||||
/* __ARCH_WANT_SYSCALL_OFF_T */
|
||||
COND_SYSCALL(fadvise64);
|
||||
|
||||
/* __ARCH_WANT_SYSCALL_DEPRECATED */
|
||||
COND_SYSCALL(epoll_wait);
|
||||
COND_SYSCALL(recv);
|
||||
COND_SYSCALL_COMPAT(recv);
|
||||
COND_SYSCALL(send);
|
||||
COND_SYSCALL(bdflush);
|
||||
COND_SYSCALL(uselib);
|
||||
|
||||
|
||||
/*
|
||||
* The syscalls below are not found in include/uapi/asm-generic/unistd.h
|
||||
*/
|
||||
|
||||
/* obsolete: SGETMASK_SYSCALL */
|
||||
COND_SYSCALL(sgetmask);
|
||||
COND_SYSCALL(ssetmask);
|
||||
|
||||
/* obsolete: SYSFS_SYSCALL */
|
||||
COND_SYSCALL(sysfs);
|
||||
|
||||
/* obsolete: __ARCH_WANT_SYS_IPC */
|
||||
COND_SYSCALL(ipc);
|
||||
COND_SYSCALL_COMPAT(ipc);
|
||||
|
||||
/* obsolete: UID16 */
|
||||
COND_SYSCALL(chown16);
|
||||
COND_SYSCALL(fchown16);
|
||||
COND_SYSCALL(getegid16);
|
||||
COND_SYSCALL(geteuid16);
|
||||
COND_SYSCALL(getgid16);
|
||||
COND_SYSCALL(getgroups16);
|
||||
COND_SYSCALL(getresgid16);
|
||||
COND_SYSCALL(getresuid16);
|
||||
COND_SYSCALL(getuid16);
|
||||
COND_SYSCALL(lchown16);
|
||||
COND_SYSCALL(setfsgid16);
|
||||
COND_SYSCALL(setfsuid16);
|
||||
COND_SYSCALL(setgid16);
|
||||
COND_SYSCALL(setgroups16);
|
||||
COND_SYSCALL(setregid16);
|
||||
COND_SYSCALL(setresgid16);
|
||||
COND_SYSCALL(setresuid16);
|
||||
COND_SYSCALL(setreuid16);
|
||||
COND_SYSCALL(setuid16);
|
||||
|
||||
@@ -113,16 +113,6 @@ config NO_HZ_FULL
|
||||
|
||||
endchoice
|
||||
|
||||
config NO_HZ_FULL_ALL
|
||||
bool "Full dynticks system on all CPUs by default (except CPU 0)"
|
||||
depends on NO_HZ_FULL
|
||||
help
|
||||
If the user doesn't pass the nohz_full boot option to
|
||||
define the range of full dynticks CPUs, consider that all
|
||||
CPUs in the system are full dynticks by default.
|
||||
Note the boot CPU will still be kept outside the range to
|
||||
handle the timekeeping duty.
|
||||
|
||||
config NO_HZ
|
||||
bool "Old Idle dynticks config"
|
||||
depends on !ARCH_USES_GETTIMEOFFSET && GENERIC_CLOCKEVENTS
|
||||
|
||||
+10
-34
@@ -405,30 +405,12 @@ static int tick_nohz_cpu_down(unsigned int cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tick_nohz_init_all(void)
|
||||
{
|
||||
int err = -1;
|
||||
|
||||
#ifdef CONFIG_NO_HZ_FULL_ALL
|
||||
if (!alloc_cpumask_var(&tick_nohz_full_mask, GFP_KERNEL)) {
|
||||
WARN(1, "NO_HZ: Can't allocate full dynticks cpumask\n");
|
||||
return err;
|
||||
}
|
||||
err = 0;
|
||||
cpumask_setall(tick_nohz_full_mask);
|
||||
tick_nohz_full_running = true;
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
void __init tick_nohz_init(void)
|
||||
{
|
||||
int cpu, ret;
|
||||
|
||||
if (!tick_nohz_full_running) {
|
||||
if (tick_nohz_init_all() < 0)
|
||||
return;
|
||||
}
|
||||
if (!tick_nohz_full_running)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Full dynticks uses irq work to drive the tick rescheduling on safe
|
||||
@@ -481,11 +463,18 @@ static int __init setup_tick_nohz(char *str)
|
||||
|
||||
__setup("nohz=", setup_tick_nohz);
|
||||
|
||||
int tick_nohz_tick_stopped(void)
|
||||
bool tick_nohz_tick_stopped(void)
|
||||
{
|
||||
return __this_cpu_read(tick_cpu_sched.tick_stopped);
|
||||
}
|
||||
|
||||
bool tick_nohz_tick_stopped_cpu(int cpu)
|
||||
{
|
||||
struct tick_sched *ts = per_cpu_ptr(&tick_cpu_sched, cpu);
|
||||
|
||||
return ts->tick_stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* tick_nohz_update_jiffies - update jiffies when idle was interrupted
|
||||
*
|
||||
@@ -741,12 +730,6 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
|
||||
delta = KTIME_MAX;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NO_HZ_FULL
|
||||
/* Limit the tick delta to the maximum scheduler deferment */
|
||||
if (!ts->inidle)
|
||||
delta = min(delta, scheduler_tick_max_deferment());
|
||||
#endif
|
||||
|
||||
/* Calculate the next expiry time */
|
||||
if (delta < (KTIME_MAX - basemono))
|
||||
expires = basemono + delta;
|
||||
@@ -953,13 +936,6 @@ void tick_nohz_idle_enter(void)
|
||||
struct tick_sched *ts;
|
||||
|
||||
lockdep_assert_irqs_enabled();
|
||||
/*
|
||||
* Update the idle state in the scheduler domain hierarchy
|
||||
* when tick_nohz_stop_sched_tick() is called from the idle loop.
|
||||
* State will be updated to busy during the first busy tick after
|
||||
* exiting idle.
|
||||
*/
|
||||
set_cpu_sd_state_idle();
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include "trace.h"
|
||||
#include "trace_probe.h"
|
||||
|
||||
static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS];
|
||||
|
||||
@@ -237,6 +238,107 @@ void perf_trace_destroy(struct perf_event *p_event)
|
||||
mutex_unlock(&event_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBE_EVENTS
|
||||
int perf_kprobe_init(struct perf_event *p_event, bool is_retprobe)
|
||||
{
|
||||
int ret;
|
||||
char *func = NULL;
|
||||
struct trace_event_call *tp_event;
|
||||
|
||||
if (p_event->attr.kprobe_func) {
|
||||
func = kzalloc(KSYM_NAME_LEN, GFP_KERNEL);
|
||||
if (!func)
|
||||
return -ENOMEM;
|
||||
ret = strncpy_from_user(
|
||||
func, u64_to_user_ptr(p_event->attr.kprobe_func),
|
||||
KSYM_NAME_LEN);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (func[0] == '\0') {
|
||||
kfree(func);
|
||||
func = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
tp_event = create_local_trace_kprobe(
|
||||
func, (void *)(unsigned long)(p_event->attr.kprobe_addr),
|
||||
p_event->attr.probe_offset, is_retprobe);
|
||||
if (IS_ERR(tp_event)) {
|
||||
ret = PTR_ERR(tp_event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = perf_trace_event_init(tp_event, p_event);
|
||||
if (ret)
|
||||
destroy_local_trace_kprobe(tp_event);
|
||||
out:
|
||||
kfree(func);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void perf_kprobe_destroy(struct perf_event *p_event)
|
||||
{
|
||||
perf_trace_event_close(p_event);
|
||||
perf_trace_event_unreg(p_event);
|
||||
|
||||
destroy_local_trace_kprobe(p_event->tp_event);
|
||||
}
|
||||
#endif /* CONFIG_KPROBE_EVENTS */
|
||||
|
||||
#ifdef CONFIG_UPROBE_EVENTS
|
||||
int perf_uprobe_init(struct perf_event *p_event, bool is_retprobe)
|
||||
{
|
||||
int ret;
|
||||
char *path = NULL;
|
||||
struct trace_event_call *tp_event;
|
||||
|
||||
if (!p_event->attr.uprobe_path)
|
||||
return -EINVAL;
|
||||
path = kzalloc(PATH_MAX, GFP_KERNEL);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
ret = strncpy_from_user(
|
||||
path, u64_to_user_ptr(p_event->attr.uprobe_path), PATH_MAX);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (path[0] == '\0') {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tp_event = create_local_trace_uprobe(
|
||||
path, p_event->attr.probe_offset, is_retprobe);
|
||||
if (IS_ERR(tp_event)) {
|
||||
ret = PTR_ERR(tp_event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* local trace_uprobe need to hold event_mutex to call
|
||||
* uprobe_buffer_enable() and uprobe_buffer_disable().
|
||||
* event_mutex is not required for local trace_kprobes.
|
||||
*/
|
||||
mutex_lock(&event_mutex);
|
||||
ret = perf_trace_event_init(tp_event, p_event);
|
||||
if (ret)
|
||||
destroy_local_trace_uprobe(tp_event);
|
||||
mutex_unlock(&event_mutex);
|
||||
out:
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void perf_uprobe_destroy(struct perf_event *p_event)
|
||||
{
|
||||
mutex_lock(&event_mutex);
|
||||
perf_trace_event_close(p_event);
|
||||
perf_trace_event_unreg(p_event);
|
||||
mutex_unlock(&event_mutex);
|
||||
destroy_local_trace_uprobe(p_event->tp_event);
|
||||
}
|
||||
#endif /* CONFIG_UPROBE_EVENTS */
|
||||
|
||||
int perf_trace_add(struct perf_event *p_event, int flags)
|
||||
{
|
||||
struct trace_event_call *tp_event = p_event->tp_event;
|
||||
|
||||
@@ -462,6 +462,14 @@ disable_trace_kprobe(struct trace_kprobe *tk, struct trace_event_file *file)
|
||||
disable_kprobe(&tk->rp.kp);
|
||||
wait = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* if tk is not added to any list, it must be a local trace_kprobe
|
||||
* created with perf_event_open. We don't need to wait for these
|
||||
* trace_kprobes
|
||||
*/
|
||||
if (list_empty(&tk->list))
|
||||
wait = 0;
|
||||
out:
|
||||
if (wait) {
|
||||
/*
|
||||
@@ -1358,12 +1366,9 @@ static struct trace_event_functions kprobe_funcs = {
|
||||
.trace = print_kprobe_event
|
||||
};
|
||||
|
||||
static int register_kprobe_event(struct trace_kprobe *tk)
|
||||
static inline void init_trace_event_call(struct trace_kprobe *tk,
|
||||
struct trace_event_call *call)
|
||||
{
|
||||
struct trace_event_call *call = &tk->tp.call;
|
||||
int ret;
|
||||
|
||||
/* Initialize trace_event_call */
|
||||
INIT_LIST_HEAD(&call->class->fields);
|
||||
if (trace_kprobe_is_return(tk)) {
|
||||
call->event.funcs = &kretprobe_funcs;
|
||||
@@ -1372,6 +1377,19 @@ static int register_kprobe_event(struct trace_kprobe *tk)
|
||||
call->event.funcs = &kprobe_funcs;
|
||||
call->class->define_fields = kprobe_event_define_fields;
|
||||
}
|
||||
|
||||
call->flags = TRACE_EVENT_FL_KPROBE;
|
||||
call->class->reg = kprobe_register;
|
||||
call->data = tk;
|
||||
}
|
||||
|
||||
static int register_kprobe_event(struct trace_kprobe *tk)
|
||||
{
|
||||
struct trace_event_call *call = &tk->tp.call;
|
||||
int ret = 0;
|
||||
|
||||
init_trace_event_call(tk, call);
|
||||
|
||||
if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0)
|
||||
return -ENOMEM;
|
||||
ret = register_trace_event(&call->event);
|
||||
@@ -1379,9 +1397,6 @@ static int register_kprobe_event(struct trace_kprobe *tk)
|
||||
kfree(call->print_fmt);
|
||||
return -ENODEV;
|
||||
}
|
||||
call->flags = TRACE_EVENT_FL_KPROBE;
|
||||
call->class->reg = kprobe_register;
|
||||
call->data = tk;
|
||||
ret = trace_add_event_call(call);
|
||||
if (ret) {
|
||||
pr_info("Failed to register kprobe event: %s\n",
|
||||
@@ -1403,6 +1418,66 @@ static int unregister_kprobe_event(struct trace_kprobe *tk)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
/* create a trace_kprobe, but don't add it to global lists */
|
||||
struct trace_event_call *
|
||||
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
|
||||
bool is_return)
|
||||
{
|
||||
struct trace_kprobe *tk;
|
||||
int ret;
|
||||
char *event;
|
||||
|
||||
/*
|
||||
* local trace_kprobes are not added to probe_list, so they are never
|
||||
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
||||
* duplicated name here.
|
||||
*/
|
||||
event = func ? func : "DUMMY_EVENT";
|
||||
|
||||
tk = alloc_trace_kprobe(KPROBE_EVENT_SYSTEM, event, (void *)addr, func,
|
||||
offs, 0 /* maxactive */, 0 /* nargs */,
|
||||
is_return);
|
||||
|
||||
if (IS_ERR(tk)) {
|
||||
pr_info("Failed to allocate trace_probe.(%d)\n",
|
||||
(int)PTR_ERR(tk));
|
||||
return ERR_CAST(tk);
|
||||
}
|
||||
|
||||
init_trace_event_call(tk, &tk->tp.call);
|
||||
|
||||
if (set_print_fmt(&tk->tp, trace_kprobe_is_return(tk)) < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = __register_trace_kprobe(tk);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return &tk->tp.call;
|
||||
error:
|
||||
free_trace_kprobe(tk);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void destroy_local_trace_kprobe(struct trace_event_call *event_call)
|
||||
{
|
||||
struct trace_kprobe *tk;
|
||||
|
||||
tk = container_of(event_call, struct trace_kprobe, tp.call);
|
||||
|
||||
if (trace_probe_is_enabled(&tk->tp)) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
__unregister_trace_kprobe(tk);
|
||||
free_trace_kprobe(tk);
|
||||
}
|
||||
#endif /* CONFIG_PERF_EVENTS */
|
||||
|
||||
/* Make a tracefs interface for controlling probe points */
|
||||
static __init int init_kprobe_trace(void)
|
||||
{
|
||||
|
||||
@@ -416,3 +416,14 @@ store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs,
|
||||
}
|
||||
|
||||
extern int set_print_fmt(struct trace_probe *tp, bool is_return);
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
extern struct trace_event_call *
|
||||
create_local_trace_kprobe(char *func, void *addr, unsigned long offs,
|
||||
bool is_return);
|
||||
extern void destroy_local_trace_kprobe(struct trace_event_call *event_call);
|
||||
|
||||
extern struct trace_event_call *
|
||||
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return);
|
||||
extern void destroy_local_trace_uprobe(struct trace_event_call *event_call);
|
||||
#endif
|
||||
|
||||
@@ -1292,16 +1292,25 @@ static struct trace_event_functions uprobe_funcs = {
|
||||
.trace = print_uprobe_event
|
||||
};
|
||||
|
||||
static int register_uprobe_event(struct trace_uprobe *tu)
|
||||
static inline void init_trace_event_call(struct trace_uprobe *tu,
|
||||
struct trace_event_call *call)
|
||||
{
|
||||
struct trace_event_call *call = &tu->tp.call;
|
||||
int ret;
|
||||
|
||||
/* Initialize trace_event_call */
|
||||
INIT_LIST_HEAD(&call->class->fields);
|
||||
call->event.funcs = &uprobe_funcs;
|
||||
call->class->define_fields = uprobe_event_define_fields;
|
||||
|
||||
call->flags = TRACE_EVENT_FL_UPROBE;
|
||||
call->class->reg = trace_uprobe_register;
|
||||
call->data = tu;
|
||||
}
|
||||
|
||||
static int register_uprobe_event(struct trace_uprobe *tu)
|
||||
{
|
||||
struct trace_event_call *call = &tu->tp.call;
|
||||
int ret = 0;
|
||||
|
||||
init_trace_event_call(tu, call);
|
||||
|
||||
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1311,9 +1320,6 @@ static int register_uprobe_event(struct trace_uprobe *tu)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
call->flags = TRACE_EVENT_FL_UPROBE;
|
||||
call->class->reg = trace_uprobe_register;
|
||||
call->data = tu;
|
||||
ret = trace_add_event_call(call);
|
||||
|
||||
if (ret) {
|
||||
@@ -1339,6 +1345,70 @@ static int unregister_uprobe_event(struct trace_uprobe *tu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
struct trace_event_call *
|
||||
create_local_trace_uprobe(char *name, unsigned long offs, bool is_return)
|
||||
{
|
||||
struct trace_uprobe *tu;
|
||||
struct inode *inode;
|
||||
struct path path;
|
||||
int ret;
|
||||
|
||||
ret = kern_path(name, LOOKUP_FOLLOW, &path);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
inode = igrab(d_inode(path.dentry));
|
||||
path_put(&path);
|
||||
|
||||
if (!inode || !S_ISREG(inode->i_mode)) {
|
||||
iput(inode);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* local trace_kprobes are not added to probe_list, so they are never
|
||||
* searched in find_trace_kprobe(). Therefore, there is no concern of
|
||||
* duplicated name "DUMMY_EVENT" here.
|
||||
*/
|
||||
tu = alloc_trace_uprobe(UPROBE_EVENT_SYSTEM, "DUMMY_EVENT", 0,
|
||||
is_return);
|
||||
|
||||
if (IS_ERR(tu)) {
|
||||
pr_info("Failed to allocate trace_uprobe.(%d)\n",
|
||||
(int)PTR_ERR(tu));
|
||||
return ERR_CAST(tu);
|
||||
}
|
||||
|
||||
tu->offset = offs;
|
||||
tu->inode = inode;
|
||||
tu->filename = kstrdup(name, GFP_KERNEL);
|
||||
init_trace_event_call(tu, &tu->tp.call);
|
||||
|
||||
if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return &tu->tp.call;
|
||||
error:
|
||||
free_trace_uprobe(tu);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void destroy_local_trace_uprobe(struct trace_event_call *event_call)
|
||||
{
|
||||
struct trace_uprobe *tu;
|
||||
|
||||
tu = container_of(event_call, struct trace_uprobe, tp.call);
|
||||
|
||||
kfree(tu->tp.call.print_fmt);
|
||||
tu->tp.call.print_fmt = NULL;
|
||||
|
||||
free_trace_uprobe(tu);
|
||||
}
|
||||
#endif /* CONFIG_PERF_EVENTS */
|
||||
|
||||
/* Make a trace interface for controling probe points */
|
||||
static __init int init_uprobe_trace(void)
|
||||
{
|
||||
|
||||
+13
-12
@@ -18,44 +18,46 @@
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "uid16.h"
|
||||
|
||||
SYSCALL_DEFINE3(chown16, const char __user *, filename, old_uid_t, user, old_gid_t, group)
|
||||
{
|
||||
return sys_chown(filename, low2highuid(user), low2highgid(group));
|
||||
return ksys_chown(filename, low2highuid(user), low2highgid(group));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(lchown16, const char __user *, filename, old_uid_t, user, old_gid_t, group)
|
||||
{
|
||||
return sys_lchown(filename, low2highuid(user), low2highgid(group));
|
||||
return ksys_lchown(filename, low2highuid(user), low2highgid(group));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(fchown16, unsigned int, fd, old_uid_t, user, old_gid_t, group)
|
||||
{
|
||||
return sys_fchown(fd, low2highuid(user), low2highgid(group));
|
||||
return ksys_fchown(fd, low2highuid(user), low2highgid(group));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(setregid16, old_gid_t, rgid, old_gid_t, egid)
|
||||
{
|
||||
return sys_setregid(low2highgid(rgid), low2highgid(egid));
|
||||
return __sys_setregid(low2highgid(rgid), low2highgid(egid));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setgid16, old_gid_t, gid)
|
||||
{
|
||||
return sys_setgid(low2highgid(gid));
|
||||
return __sys_setgid(low2highgid(gid));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(setreuid16, old_uid_t, ruid, old_uid_t, euid)
|
||||
{
|
||||
return sys_setreuid(low2highuid(ruid), low2highuid(euid));
|
||||
return __sys_setreuid(low2highuid(ruid), low2highuid(euid));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setuid16, old_uid_t, uid)
|
||||
{
|
||||
return sys_setuid(low2highuid(uid));
|
||||
return __sys_setuid(low2highuid(uid));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid)
|
||||
{
|
||||
return sys_setresuid(low2highuid(ruid), low2highuid(euid),
|
||||
return __sys_setresuid(low2highuid(ruid), low2highuid(euid),
|
||||
low2highuid(suid));
|
||||
}
|
||||
|
||||
@@ -78,11 +80,10 @@ SYSCALL_DEFINE3(getresuid16, old_uid_t __user *, ruidp, old_uid_t __user *, euid
|
||||
|
||||
SYSCALL_DEFINE3(setresgid16, old_gid_t, rgid, old_gid_t, egid, old_gid_t, sgid)
|
||||
{
|
||||
return sys_setresgid(low2highgid(rgid), low2highgid(egid),
|
||||
return __sys_setresgid(low2highgid(rgid), low2highgid(egid),
|
||||
low2highgid(sgid));
|
||||
}
|
||||
|
||||
|
||||
SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egidp, old_gid_t __user *, sgidp)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
@@ -102,12 +103,12 @@ SYSCALL_DEFINE3(getresgid16, old_gid_t __user *, rgidp, old_gid_t __user *, egid
|
||||
|
||||
SYSCALL_DEFINE1(setfsuid16, old_uid_t, uid)
|
||||
{
|
||||
return sys_setfsuid(low2highuid(uid));
|
||||
return __sys_setfsuid(low2highuid(uid));
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE1(setfsgid16, old_gid_t, gid)
|
||||
{
|
||||
return sys_setfsgid(low2highgid(gid));
|
||||
return __sys_setfsgid(low2highgid(gid));
|
||||
}
|
||||
|
||||
static int groups16_to_user(old_gid_t __user *grouplist,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef LINUX_UID16_H
|
||||
#define LINUX_UID16_H
|
||||
|
||||
long __sys_setuid(uid_t uid);
|
||||
long __sys_setgid(gid_t gid);
|
||||
long __sys_setreuid(uid_t ruid, uid_t euid);
|
||||
long __sys_setregid(gid_t rgid, gid_t egid);
|
||||
long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid);
|
||||
long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid);
|
||||
long __sys_setfsuid(uid_t uid);
|
||||
long __sys_setfsgid(gid_t gid);
|
||||
|
||||
#endif /* LINUX_UID16_H */
|
||||
+2
-2
@@ -118,7 +118,7 @@ static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* If SIGCLD is ignored sys_wait4 won't populate the status. */
|
||||
/* If SIGCLD is ignored kernel_wait4 won't populate the status. */
|
||||
kernel_sigaction(SIGCHLD, SIG_DFL);
|
||||
pid = kernel_thread(call_usermodehelper_exec_async, sub_info, SIGCHLD);
|
||||
if (pid < 0) {
|
||||
@@ -135,7 +135,7 @@ static void call_usermodehelper_exec_sync(struct subprocess_info *sub_info)
|
||||
*
|
||||
* Thus the __user pointer cast is valid here.
|
||||
*/
|
||||
sys_wait4(pid, (int __user *)&ret, 0, NULL);
|
||||
kernel_wait4(pid, (int __user *)&ret, 0, NULL);
|
||||
|
||||
/*
|
||||
* If ret is 0, either call_usermodehelper_exec_async failed and
|
||||
|
||||
+2
-1
@@ -5573,12 +5573,13 @@ static void __init wq_numa_init(void)
|
||||
int __init workqueue_init_early(void)
|
||||
{
|
||||
int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
|
||||
int hk_flags = HK_FLAG_DOMAIN | HK_FLAG_WQ;
|
||||
int i, cpu;
|
||||
|
||||
WARN_ON(__alignof__(struct pool_workqueue) < __alignof__(long long));
|
||||
|
||||
BUG_ON(!alloc_cpumask_var(&wq_unbound_cpumask, GFP_KERNEL));
|
||||
cpumask_copy(wq_unbound_cpumask, housekeeping_cpumask(HK_FLAG_DOMAIN));
|
||||
cpumask_copy(wq_unbound_cpumask, housekeeping_cpumask(hk_flags));
|
||||
|
||||
pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user