Merge tag 'irqchip-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core
Pull irqchip updates from Marc Zyngier - The huge (and terrifying) TI INTR/INTA set of drivers - Rewrite of the stm32mp1-exti driver as a platform driver - Update the IOMMU MSI mapping API to be RT friendly - A number of cleanups and other low impact fixes
This commit is contained in:
+10
-3
@@ -162,10 +162,14 @@ static void cpu_map_kthread_stop(struct work_struct *work)
|
||||
static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
|
||||
struct xdp_frame *xdpf)
|
||||
{
|
||||
unsigned int hard_start_headroom;
|
||||
unsigned int frame_size;
|
||||
void *pkt_data_start;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Part of headroom was reserved to xdpf */
|
||||
hard_start_headroom = sizeof(struct xdp_frame) + xdpf->headroom;
|
||||
|
||||
/* build_skb need to place skb_shared_info after SKB end, and
|
||||
* also want to know the memory "truesize". Thus, need to
|
||||
* know the memory frame size backing xdp_buff.
|
||||
@@ -183,15 +187,15 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
|
||||
* is not at a fixed memory location, with mixed length
|
||||
* packets, which is bad for cache-line hotness.
|
||||
*/
|
||||
frame_size = SKB_DATA_ALIGN(xdpf->len + xdpf->headroom) +
|
||||
frame_size = SKB_DATA_ALIGN(xdpf->len + hard_start_headroom) +
|
||||
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
|
||||
|
||||
pkt_data_start = xdpf->data - xdpf->headroom;
|
||||
pkt_data_start = xdpf->data - hard_start_headroom;
|
||||
skb = build_skb(pkt_data_start, frame_size);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
skb_reserve(skb, xdpf->headroom);
|
||||
skb_reserve(skb, hard_start_headroom);
|
||||
__skb_put(skb, xdpf->len);
|
||||
if (xdpf->metasize)
|
||||
skb_metadata_set(skb, xdpf->metasize);
|
||||
@@ -205,6 +209,9 @@ static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
|
||||
* - RX ring dev queue index (skb_record_rx_queue)
|
||||
*/
|
||||
|
||||
/* Allow SKB to reuse area used by xdp_frame */
|
||||
xdp_scrub_frame(xdpf);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
||||
+18
-14
@@ -554,19 +554,6 @@ struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type typ
|
||||
}
|
||||
EXPORT_SYMBOL(bpf_prog_get_type_path);
|
||||
|
||||
static void bpf_evict_inode(struct inode *inode)
|
||||
{
|
||||
enum bpf_type type;
|
||||
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
clear_inode(inode);
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(inode->i_link);
|
||||
if (!bpf_inode_type(inode, &type))
|
||||
bpf_any_put(inode->i_private, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display the mount options in /proc/mounts.
|
||||
*/
|
||||
@@ -579,11 +566,28 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_destroy_inode_deferred(struct rcu_head *head)
|
||||
{
|
||||
struct inode *inode = container_of(head, struct inode, i_rcu);
|
||||
enum bpf_type type;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(inode->i_link);
|
||||
if (!bpf_inode_type(inode, &type))
|
||||
bpf_any_put(inode->i_private, type);
|
||||
free_inode_nonrcu(inode);
|
||||
}
|
||||
|
||||
static void bpf_destroy_inode(struct inode *inode)
|
||||
{
|
||||
call_rcu(&inode->i_rcu, bpf_destroy_inode_deferred);
|
||||
}
|
||||
|
||||
static const struct super_operations bpf_super_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.show_options = bpf_show_options,
|
||||
.evict_inode = bpf_evict_inode,
|
||||
.destroy_inode = bpf_destroy_inode,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
+15
-7
@@ -136,21 +136,29 @@ static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)
|
||||
|
||||
void *bpf_map_area_alloc(size_t size, int numa_node)
|
||||
{
|
||||
/* We definitely need __GFP_NORETRY, so OOM killer doesn't
|
||||
* trigger under memory pressure as we really just want to
|
||||
* fail instead.
|
||||
/* We really just want to fail instead of triggering OOM killer
|
||||
* under memory pressure, therefore we set __GFP_NORETRY to kmalloc,
|
||||
* which is used for lower order allocation requests.
|
||||
*
|
||||
* It has been observed that higher order allocation requests done by
|
||||
* vmalloc with __GFP_NORETRY being set might fail due to not trying
|
||||
* to reclaim memory from the page cache, thus we set
|
||||
* __GFP_RETRY_MAYFAIL to avoid such situations.
|
||||
*/
|
||||
const gfp_t flags = __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO;
|
||||
|
||||
const gfp_t flags = __GFP_NOWARN | __GFP_ZERO;
|
||||
void *area;
|
||||
|
||||
if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) {
|
||||
area = kmalloc_node(size, GFP_USER | flags, numa_node);
|
||||
area = kmalloc_node(size, GFP_USER | __GFP_NORETRY | flags,
|
||||
numa_node);
|
||||
if (area != NULL)
|
||||
return area;
|
||||
}
|
||||
|
||||
return __vmalloc_node_flags_caller(size, numa_node, GFP_KERNEL | flags,
|
||||
__builtin_return_address(0));
|
||||
return __vmalloc_node_flags_caller(size, numa_node,
|
||||
GFP_KERNEL | __GFP_RETRY_MAYFAIL |
|
||||
flags, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
void bpf_map_area_free(void *area)
|
||||
|
||||
+95
-66
@@ -212,7 +212,7 @@ struct bpf_call_arg_meta {
|
||||
int access_size;
|
||||
s64 msize_smax_value;
|
||||
u64 msize_umax_value;
|
||||
int ptr_id;
|
||||
int ref_obj_id;
|
||||
int func_id;
|
||||
};
|
||||
|
||||
@@ -346,35 +346,23 @@ static bool reg_type_may_be_null(enum bpf_reg_type type)
|
||||
type == PTR_TO_TCP_SOCK_OR_NULL;
|
||||
}
|
||||
|
||||
static bool type_is_refcounted(enum bpf_reg_type type)
|
||||
{
|
||||
return type == PTR_TO_SOCKET;
|
||||
}
|
||||
|
||||
static bool type_is_refcounted_or_null(enum bpf_reg_type type)
|
||||
{
|
||||
return type == PTR_TO_SOCKET || type == PTR_TO_SOCKET_OR_NULL;
|
||||
}
|
||||
|
||||
static bool reg_is_refcounted(const struct bpf_reg_state *reg)
|
||||
{
|
||||
return type_is_refcounted(reg->type);
|
||||
}
|
||||
|
||||
static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg)
|
||||
{
|
||||
return reg->type == PTR_TO_MAP_VALUE &&
|
||||
map_value_has_spin_lock(reg->map_ptr);
|
||||
}
|
||||
|
||||
static bool reg_is_refcounted_or_null(const struct bpf_reg_state *reg)
|
||||
static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
|
||||
{
|
||||
return type_is_refcounted_or_null(reg->type);
|
||||
return type == PTR_TO_SOCKET ||
|
||||
type == PTR_TO_SOCKET_OR_NULL ||
|
||||
type == PTR_TO_TCP_SOCK ||
|
||||
type == PTR_TO_TCP_SOCK_OR_NULL;
|
||||
}
|
||||
|
||||
static bool arg_type_is_refcounted(enum bpf_arg_type type)
|
||||
static bool arg_type_may_be_refcounted(enum bpf_arg_type type)
|
||||
{
|
||||
return type == ARG_PTR_TO_SOCKET;
|
||||
return type == ARG_PTR_TO_SOCK_COMMON;
|
||||
}
|
||||
|
||||
/* Determine whether the function releases some resources allocated by another
|
||||
@@ -392,6 +380,12 @@ static bool is_acquire_function(enum bpf_func_id func_id)
|
||||
func_id == BPF_FUNC_sk_lookup_udp;
|
||||
}
|
||||
|
||||
static bool is_ptr_cast_function(enum bpf_func_id func_id)
|
||||
{
|
||||
return func_id == BPF_FUNC_tcp_sock ||
|
||||
func_id == BPF_FUNC_sk_fullsock;
|
||||
}
|
||||
|
||||
/* string representation of 'enum bpf_reg_type' */
|
||||
static const char * const reg_type_str[] = {
|
||||
[NOT_INIT] = "?",
|
||||
@@ -466,6 +460,8 @@ static void print_verifier_state(struct bpf_verifier_env *env,
|
||||
verbose(env, ",call_%d", func(env, reg)->callsite);
|
||||
} else {
|
||||
verbose(env, "(id=%d", reg->id);
|
||||
if (reg_type_may_be_refcounted_or_null(t))
|
||||
verbose(env, ",ref_obj_id=%d", reg->ref_obj_id);
|
||||
if (t != SCALAR_VALUE)
|
||||
verbose(env, ",off=%d", reg->off);
|
||||
if (type_is_pkt_pointer(t))
|
||||
@@ -1901,8 +1897,9 @@ continue_func:
|
||||
}
|
||||
frame++;
|
||||
if (frame >= MAX_CALL_FRAMES) {
|
||||
WARN_ONCE(1, "verifier bug. Call stack is too deep\n");
|
||||
return -EFAULT;
|
||||
verbose(env, "the call stack of %d frames is too deep !\n",
|
||||
frame);
|
||||
return -E2BIG;
|
||||
}
|
||||
goto process_func;
|
||||
}
|
||||
@@ -2414,16 +2411,15 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
|
||||
/* Any sk pointer can be ARG_PTR_TO_SOCK_COMMON */
|
||||
if (!type_is_sk_pointer(type))
|
||||
goto err_type;
|
||||
} else if (arg_type == ARG_PTR_TO_SOCKET) {
|
||||
expected_type = PTR_TO_SOCKET;
|
||||
if (type != expected_type)
|
||||
goto err_type;
|
||||
if (meta->ptr_id || !reg->id) {
|
||||
verbose(env, "verifier internal error: mismatched references meta=%d, reg=%d\n",
|
||||
meta->ptr_id, reg->id);
|
||||
return -EFAULT;
|
||||
if (reg->ref_obj_id) {
|
||||
if (meta->ref_obj_id) {
|
||||
verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
|
||||
regno, reg->ref_obj_id,
|
||||
meta->ref_obj_id);
|
||||
return -EFAULT;
|
||||
}
|
||||
meta->ref_obj_id = reg->ref_obj_id;
|
||||
}
|
||||
meta->ptr_id = reg->id;
|
||||
} else if (arg_type == ARG_PTR_TO_SPIN_LOCK) {
|
||||
if (meta->func_id == BPF_FUNC_spin_lock) {
|
||||
if (process_spin_lock(env, regno, true))
|
||||
@@ -2740,32 +2736,38 @@ static bool check_arg_pair_ok(const struct bpf_func_proto *fn)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_refcount_ok(const struct bpf_func_proto *fn)
|
||||
static bool check_refcount_ok(const struct bpf_func_proto *fn, int func_id)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (arg_type_is_refcounted(fn->arg1_type))
|
||||
if (arg_type_may_be_refcounted(fn->arg1_type))
|
||||
count++;
|
||||
if (arg_type_is_refcounted(fn->arg2_type))
|
||||
if (arg_type_may_be_refcounted(fn->arg2_type))
|
||||
count++;
|
||||
if (arg_type_is_refcounted(fn->arg3_type))
|
||||
if (arg_type_may_be_refcounted(fn->arg3_type))
|
||||
count++;
|
||||
if (arg_type_is_refcounted(fn->arg4_type))
|
||||
if (arg_type_may_be_refcounted(fn->arg4_type))
|
||||
count++;
|
||||
if (arg_type_is_refcounted(fn->arg5_type))
|
||||
if (arg_type_may_be_refcounted(fn->arg5_type))
|
||||
count++;
|
||||
|
||||
/* A reference acquiring function cannot acquire
|
||||
* another refcounted ptr.
|
||||
*/
|
||||
if (is_acquire_function(func_id) && count)
|
||||
return false;
|
||||
|
||||
/* We only support one arg being unreferenced at the moment,
|
||||
* which is sufficient for the helper functions we have right now.
|
||||
*/
|
||||
return count <= 1;
|
||||
}
|
||||
|
||||
static int check_func_proto(const struct bpf_func_proto *fn)
|
||||
static int check_func_proto(const struct bpf_func_proto *fn, int func_id)
|
||||
{
|
||||
return check_raw_mode_ok(fn) &&
|
||||
check_arg_pair_ok(fn) &&
|
||||
check_refcount_ok(fn) ? 0 : -EINVAL;
|
||||
check_refcount_ok(fn, func_id) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/* Packet data might have moved, any old PTR_TO_PACKET[_META,_END]
|
||||
@@ -2799,19 +2801,20 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
|
||||
}
|
||||
|
||||
static void release_reg_references(struct bpf_verifier_env *env,
|
||||
struct bpf_func_state *state, int id)
|
||||
struct bpf_func_state *state,
|
||||
int ref_obj_id)
|
||||
{
|
||||
struct bpf_reg_state *regs = state->regs, *reg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_BPF_REG; i++)
|
||||
if (regs[i].id == id)
|
||||
if (regs[i].ref_obj_id == ref_obj_id)
|
||||
mark_reg_unknown(env, regs, i);
|
||||
|
||||
bpf_for_each_spilled_reg(i, state, reg) {
|
||||
if (!reg)
|
||||
continue;
|
||||
if (reg_is_refcounted(reg) && reg->id == id)
|
||||
if (reg->ref_obj_id == ref_obj_id)
|
||||
__mark_reg_unknown(reg);
|
||||
}
|
||||
}
|
||||
@@ -2820,15 +2823,20 @@ static void release_reg_references(struct bpf_verifier_env *env,
|
||||
* resources. Identify all copies of the same pointer and clear the reference.
|
||||
*/
|
||||
static int release_reference(struct bpf_verifier_env *env,
|
||||
struct bpf_call_arg_meta *meta)
|
||||
int ref_obj_id)
|
||||
{
|
||||
struct bpf_verifier_state *vstate = env->cur_state;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= vstate->curframe; i++)
|
||||
release_reg_references(env, vstate->frame[i], meta->ptr_id);
|
||||
err = release_reference_state(cur_func(env), ref_obj_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return release_reference_state(cur_func(env), meta->ptr_id);
|
||||
for (i = 0; i <= vstate->curframe; i++)
|
||||
release_reg_references(env, vstate->frame[i], ref_obj_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
@@ -3047,7 +3055,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
memset(&meta, 0, sizeof(meta));
|
||||
meta.pkt_access = fn->pkt_access;
|
||||
|
||||
err = check_func_proto(fn);
|
||||
err = check_func_proto(fn, func_id);
|
||||
if (err) {
|
||||
verbose(env, "kernel subsystem misconfigured func %s#%d\n",
|
||||
func_id_name(func_id), func_id);
|
||||
@@ -3093,7 +3101,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
return err;
|
||||
}
|
||||
} else if (is_release_function(func_id)) {
|
||||
err = release_reference(env, &meta);
|
||||
err = release_reference(env, meta.ref_obj_id);
|
||||
if (err) {
|
||||
verbose(env, "func %s#%d reference has not been acquired before\n",
|
||||
func_id_name(func_id), func_id);
|
||||
@@ -3154,8 +3162,10 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
|
||||
if (id < 0)
|
||||
return id;
|
||||
/* For release_reference() */
|
||||
/* For mark_ptr_or_null_reg() */
|
||||
regs[BPF_REG_0].id = id;
|
||||
/* For release_reference() */
|
||||
regs[BPF_REG_0].ref_obj_id = id;
|
||||
} else {
|
||||
/* For mark_ptr_or_null_reg() */
|
||||
regs[BPF_REG_0].id = ++env->id_gen;
|
||||
@@ -3170,6 +3180,10 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_ptr_cast_function(func_id))
|
||||
/* For release_reference() */
|
||||
regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
|
||||
|
||||
do_refine_retval_range(regs, fn->ret_type, func_id, &meta);
|
||||
|
||||
err = check_map_func_compatibility(env, meta.map_ptr, func_id);
|
||||
@@ -3368,7 +3382,7 @@ do_sim:
|
||||
*dst_reg = *ptr_reg;
|
||||
}
|
||||
ret = push_stack(env, env->insn_idx + 1, env->insn_idx, true);
|
||||
if (!ptr_is_dst_reg)
|
||||
if (!ptr_is_dst_reg && ret)
|
||||
*dst_reg = tmp;
|
||||
return !ret ? -EFAULT : 0;
|
||||
}
|
||||
@@ -4665,11 +4679,19 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
|
||||
} else if (reg->type == PTR_TO_TCP_SOCK_OR_NULL) {
|
||||
reg->type = PTR_TO_TCP_SOCK;
|
||||
}
|
||||
if (is_null || !(reg_is_refcounted(reg) ||
|
||||
reg_may_point_to_spin_lock(reg))) {
|
||||
/* We don't need id from this point onwards anymore,
|
||||
* thus we should better reset it, so that state
|
||||
* pruning has chances to take effect.
|
||||
if (is_null) {
|
||||
/* We don't need id and ref_obj_id from this point
|
||||
* onwards anymore, thus we should better reset it,
|
||||
* so that state pruning has chances to take effect.
|
||||
*/
|
||||
reg->id = 0;
|
||||
reg->ref_obj_id = 0;
|
||||
} else if (!reg_may_point_to_spin_lock(reg)) {
|
||||
/* For not-NULL ptr, reg->ref_obj_id will be reset
|
||||
* in release_reg_references().
|
||||
*
|
||||
* reg->id is still used by spin_lock ptr. Other
|
||||
* than spin_lock ptr type, reg->id can be reset.
|
||||
*/
|
||||
reg->id = 0;
|
||||
}
|
||||
@@ -4684,11 +4706,16 @@ static void mark_ptr_or_null_regs(struct bpf_verifier_state *vstate, u32 regno,
|
||||
{
|
||||
struct bpf_func_state *state = vstate->frame[vstate->curframe];
|
||||
struct bpf_reg_state *reg, *regs = state->regs;
|
||||
u32 ref_obj_id = regs[regno].ref_obj_id;
|
||||
u32 id = regs[regno].id;
|
||||
int i, j;
|
||||
|
||||
if (reg_is_refcounted_or_null(®s[regno]) && is_null)
|
||||
release_reference_state(state, id);
|
||||
if (ref_obj_id && ref_obj_id == id && is_null)
|
||||
/* regs[regno] is in the " == NULL" branch.
|
||||
* No one could have freed the reference state before
|
||||
* doing the NULL check.
|
||||
*/
|
||||
WARN_ON_ONCE(release_reference_state(state, id));
|
||||
|
||||
for (i = 0; i < MAX_BPF_REG; i++)
|
||||
mark_ptr_or_null_reg(state, ®s[i], id, is_null);
|
||||
@@ -6052,15 +6079,17 @@ static int propagate_liveness(struct bpf_verifier_env *env,
|
||||
}
|
||||
/* Propagate read liveness of registers... */
|
||||
BUILD_BUG_ON(BPF_REG_FP + 1 != MAX_BPF_REG);
|
||||
/* We don't need to worry about FP liveness because it's read-only */
|
||||
for (i = 0; i < BPF_REG_FP; i++) {
|
||||
if (vparent->frame[vparent->curframe]->regs[i].live & REG_LIVE_READ)
|
||||
continue;
|
||||
if (vstate->frame[vstate->curframe]->regs[i].live & REG_LIVE_READ) {
|
||||
err = mark_reg_read(env, &vstate->frame[vstate->curframe]->regs[i],
|
||||
&vparent->frame[vstate->curframe]->regs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
for (frame = 0; frame <= vstate->curframe; frame++) {
|
||||
/* We don't need to worry about FP liveness, it's read-only */
|
||||
for (i = frame < vstate->curframe ? BPF_REG_6 : 0; i < BPF_REG_FP; i++) {
|
||||
if (vparent->frame[frame]->regs[i].live & REG_LIVE_READ)
|
||||
continue;
|
||||
if (vstate->frame[frame]->regs[i].live & REG_LIVE_READ) {
|
||||
err = mark_reg_read(env, &vstate->frame[frame]->regs[i],
|
||||
&vparent->frame[frame]->regs[i]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-2
@@ -564,6 +564,20 @@ static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st)
|
||||
cpuhp_invoke_callback(cpu, st->state, false, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline bool can_rollback_cpu(struct cpuhp_cpu_state *st)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_HOTPLUG_CPU))
|
||||
return true;
|
||||
/*
|
||||
* When CPU hotplug is disabled, then taking the CPU down is not
|
||||
* possible because takedown_cpu() and the architecture and
|
||||
* subsystem specific mechanisms are not available. So the CPU
|
||||
* which would be completely unplugged again needs to stay around
|
||||
* in the current state.
|
||||
*/
|
||||
return st->state <= CPUHP_BRINGUP_CPU;
|
||||
}
|
||||
|
||||
static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
|
||||
enum cpuhp_state target)
|
||||
{
|
||||
@@ -574,8 +588,10 @@ static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,
|
||||
st->state++;
|
||||
ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
||||
if (ret) {
|
||||
st->target = prev_state;
|
||||
undo_cpu_up(cpu, st);
|
||||
if (can_rollback_cpu(st)) {
|
||||
st->target = prev_state;
|
||||
undo_cpu_up(cpu, st);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -706,7 +706,7 @@ static struct dma_debug_entry *dma_entry_alloc(void)
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
entry->stacktrace.max_entries = DMA_DEBUG_STACKTRACE_ENTRIES;
|
||||
entry->stacktrace.entries = entry->st_entries;
|
||||
entry->stacktrace.skip = 2;
|
||||
entry->stacktrace.skip = 1;
|
||||
save_stack_trace(&entry->stacktrace);
|
||||
#endif
|
||||
|
||||
|
||||
+43
-9
@@ -2009,8 +2009,8 @@ event_sched_out(struct perf_event *event,
|
||||
event->pmu->del(event, 0);
|
||||
event->oncpu = -1;
|
||||
|
||||
if (event->pending_disable) {
|
||||
event->pending_disable = 0;
|
||||
if (READ_ONCE(event->pending_disable) >= 0) {
|
||||
WRITE_ONCE(event->pending_disable, -1);
|
||||
state = PERF_EVENT_STATE_OFF;
|
||||
}
|
||||
perf_event_set_state(event, state);
|
||||
@@ -2198,7 +2198,8 @@ EXPORT_SYMBOL_GPL(perf_event_disable);
|
||||
|
||||
void perf_event_disable_inatomic(struct perf_event *event)
|
||||
{
|
||||
event->pending_disable = 1;
|
||||
WRITE_ONCE(event->pending_disable, smp_processor_id());
|
||||
/* can fail, see perf_pending_event_disable() */
|
||||
irq_work_queue(&event->pending);
|
||||
}
|
||||
|
||||
@@ -5810,10 +5811,45 @@ void perf_event_wakeup(struct perf_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_pending_event_disable(struct perf_event *event)
|
||||
{
|
||||
int cpu = READ_ONCE(event->pending_disable);
|
||||
|
||||
if (cpu < 0)
|
||||
return;
|
||||
|
||||
if (cpu == smp_processor_id()) {
|
||||
WRITE_ONCE(event->pending_disable, -1);
|
||||
perf_event_disable_local(event);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU-A CPU-B
|
||||
*
|
||||
* perf_event_disable_inatomic()
|
||||
* @pending_disable = CPU-A;
|
||||
* irq_work_queue();
|
||||
*
|
||||
* sched-out
|
||||
* @pending_disable = -1;
|
||||
*
|
||||
* sched-in
|
||||
* perf_event_disable_inatomic()
|
||||
* @pending_disable = CPU-B;
|
||||
* irq_work_queue(); // FAILS
|
||||
*
|
||||
* irq_work_run()
|
||||
* perf_pending_event()
|
||||
*
|
||||
* But the event runs on CPU-B and wants disabling there.
|
||||
*/
|
||||
irq_work_queue_on(&event->pending, cpu);
|
||||
}
|
||||
|
||||
static void perf_pending_event(struct irq_work *entry)
|
||||
{
|
||||
struct perf_event *event = container_of(entry,
|
||||
struct perf_event, pending);
|
||||
struct perf_event *event = container_of(entry, struct perf_event, pending);
|
||||
int rctx;
|
||||
|
||||
rctx = perf_swevent_get_recursion_context();
|
||||
@@ -5822,10 +5858,7 @@ static void perf_pending_event(struct irq_work *entry)
|
||||
* and we won't recurse 'further'.
|
||||
*/
|
||||
|
||||
if (event->pending_disable) {
|
||||
event->pending_disable = 0;
|
||||
perf_event_disable_local(event);
|
||||
}
|
||||
perf_pending_event_disable(event);
|
||||
|
||||
if (event->pending_wakeup) {
|
||||
event->pending_wakeup = 0;
|
||||
@@ -10236,6 +10269,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
||||
|
||||
|
||||
init_waitqueue_head(&event->waitq);
|
||||
event->pending_disable = -1;
|
||||
init_irq_work(&event->pending, perf_pending_event);
|
||||
|
||||
mutex_init(&event->mmap_mutex);
|
||||
|
||||
@@ -392,7 +392,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
|
||||
* store that will be enabled on successful return
|
||||
*/
|
||||
if (!handle->size) { /* A, matches D */
|
||||
event->pending_disable = 1;
|
||||
event->pending_disable = smp_processor_id();
|
||||
perf_output_wakeup(handle);
|
||||
local_set(&rb->aux_nest, 0);
|
||||
goto err_put;
|
||||
@@ -480,7 +480,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size)
|
||||
|
||||
if (wakeup) {
|
||||
if (handle->aux_flags & PERF_AUX_FLAG_TRUNCATED)
|
||||
handle->event->pending_disable = 1;
|
||||
handle->event->pending_disable = smp_processor_id();
|
||||
perf_output_wakeup(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,9 @@ config GENERIC_MSI_IRQ_DOMAIN
|
||||
select IRQ_DOMAIN_HIERARCHY
|
||||
select GENERIC_MSI_IRQ
|
||||
|
||||
config IRQ_MSI_IOMMU
|
||||
bool
|
||||
|
||||
config HANDLE_DOMAIN_IRQ
|
||||
bool
|
||||
|
||||
|
||||
@@ -1449,12 +1449,43 @@ int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)
|
||||
int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on)
|
||||
{
|
||||
data = data->parent_data;
|
||||
|
||||
if (data->chip->flags & IRQCHIP_SKIP_SET_WAKE)
|
||||
return 0;
|
||||
|
||||
if (data->chip->irq_set_wake)
|
||||
return data->chip->irq_set_wake(data, on);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent);
|
||||
|
||||
/**
|
||||
* irq_chip_request_resources_parent - Request resources on the parent interrupt
|
||||
* @data: Pointer to interrupt specific data
|
||||
*/
|
||||
int irq_chip_request_resources_parent(struct irq_data *data)
|
||||
{
|
||||
data = data->parent_data;
|
||||
|
||||
if (data->chip->irq_request_resources)
|
||||
return data->chip->irq_request_resources(data);
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent);
|
||||
|
||||
/**
|
||||
* irq_chip_release_resources_parent - Release resources on the parent interrupt
|
||||
* @data: Pointer to interrupt specific data
|
||||
*/
|
||||
void irq_chip_release_resources_parent(struct irq_data *data)
|
||||
{
|
||||
data = data->parent_data;
|
||||
if (data->chip->irq_release_resources)
|
||||
data->chip->irq_release_resources(data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -558,6 +558,7 @@ int __init early_irq_init(void)
|
||||
alloc_masks(&desc[i], node);
|
||||
raw_spin_lock_init(&desc[i].lock);
|
||||
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
|
||||
mutex_init(&desc[i].request_mutex);
|
||||
desc_set_defaults(i, &desc[i], node, NULL, NULL);
|
||||
}
|
||||
return arch_early_irq_init();
|
||||
|
||||
@@ -1297,7 +1297,7 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
|
||||
/**
|
||||
* __irq_domain_alloc_irqs - Allocate IRQs from domain
|
||||
* @domain: domain to allocate from
|
||||
* @irq_base: allocate specified IRQ nubmer if irq_base >= 0
|
||||
* @irq_base: allocate specified IRQ number if irq_base >= 0
|
||||
* @nr_irqs: number of IRQs to allocate
|
||||
* @node: NUMA node id for memory allocation
|
||||
* @arg: domain specific argument
|
||||
|
||||
+12
-17
@@ -4689,8 +4689,8 @@ static void free_zapped_rcu(struct rcu_head *ch)
|
||||
return;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
if (!graph_lock())
|
||||
goto out_irq;
|
||||
arch_spin_lock(&lockdep_lock);
|
||||
current->lockdep_recursion = 1;
|
||||
|
||||
/* closed head */
|
||||
pf = delayed_free.pf + (delayed_free.index ^ 1);
|
||||
@@ -4702,8 +4702,8 @@ static void free_zapped_rcu(struct rcu_head *ch)
|
||||
*/
|
||||
call_rcu_zapped(delayed_free.pf + delayed_free.index);
|
||||
|
||||
graph_unlock();
|
||||
out_irq:
|
||||
current->lockdep_recursion = 0;
|
||||
arch_spin_unlock(&lockdep_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
@@ -4744,21 +4744,17 @@ static void lockdep_free_key_range_reg(void *start, unsigned long size)
|
||||
{
|
||||
struct pending_free *pf;
|
||||
unsigned long flags;
|
||||
int locked;
|
||||
|
||||
init_data_structures_once();
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
locked = graph_lock();
|
||||
if (!locked)
|
||||
goto out_irq;
|
||||
|
||||
arch_spin_lock(&lockdep_lock);
|
||||
current->lockdep_recursion = 1;
|
||||
pf = get_pending_free();
|
||||
__lockdep_free_key_range(pf, start, size);
|
||||
call_rcu_zapped(pf);
|
||||
|
||||
graph_unlock();
|
||||
out_irq:
|
||||
current->lockdep_recursion = 0;
|
||||
arch_spin_unlock(&lockdep_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
@@ -4911,9 +4907,8 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
return;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
if (!graph_lock())
|
||||
goto out_irq;
|
||||
|
||||
arch_spin_lock(&lockdep_lock);
|
||||
current->lockdep_recursion = 1;
|
||||
pf = get_pending_free();
|
||||
hlist_for_each_entry_rcu(k, hash_head, hash_entry) {
|
||||
if (k == key) {
|
||||
@@ -4925,8 +4920,8 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
WARN_ON_ONCE(!found);
|
||||
__lockdep_free_key_range(pf, key, 1);
|
||||
call_rcu_zapped(pf);
|
||||
graph_unlock();
|
||||
out_irq:
|
||||
current->lockdep_recursion = 0;
|
||||
arch_spin_unlock(&lockdep_lock);
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
/* Wait until is_dynamic_key() has finished accessing k->hash_entry. */
|
||||
|
||||
+13
-2
@@ -29,6 +29,7 @@
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
||||
/*
|
||||
* Access another process' address space via ptrace.
|
||||
@@ -924,18 +925,26 @@ int ptrace_request(struct task_struct *child, long request,
|
||||
ret = ptrace_setsiginfo(child, &siginfo);
|
||||
break;
|
||||
|
||||
case PTRACE_GETSIGMASK:
|
||||
case PTRACE_GETSIGMASK: {
|
||||
sigset_t *mask;
|
||||
|
||||
if (addr != sizeof(sigset_t)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy_to_user(datavp, &child->blocked, sizeof(sigset_t)))
|
||||
if (test_tsk_restore_sigmask(child))
|
||||
mask = &child->saved_sigmask;
|
||||
else
|
||||
mask = &child->blocked;
|
||||
|
||||
if (copy_to_user(datavp, mask, sizeof(sigset_t)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PTRACE_SETSIGMASK: {
|
||||
sigset_t new_set;
|
||||
@@ -961,6 +970,8 @@ int ptrace_request(struct task_struct *child, long request,
|
||||
child->blocked = new_set;
|
||||
spin_unlock_irq(&child->sighand->siglock);
|
||||
|
||||
clear_tsk_restore_sigmask(child);
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-3
@@ -7784,10 +7784,10 @@ static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
|
||||
if (cfs_rq->last_h_load_update == now)
|
||||
return;
|
||||
|
||||
cfs_rq->h_load_next = NULL;
|
||||
WRITE_ONCE(cfs_rq->h_load_next, NULL);
|
||||
for_each_sched_entity(se) {
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
cfs_rq->h_load_next = se;
|
||||
WRITE_ONCE(cfs_rq->h_load_next, se);
|
||||
if (cfs_rq->last_h_load_update == now)
|
||||
break;
|
||||
}
|
||||
@@ -7797,7 +7797,7 @@ static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
|
||||
cfs_rq->last_h_load_update = now;
|
||||
}
|
||||
|
||||
while ((se = cfs_rq->h_load_next) != NULL) {
|
||||
while ((se = READ_ONCE(cfs_rq->h_load_next)) != NULL) {
|
||||
load = cfs_rq->h_load;
|
||||
load = div64_ul(load * se->avg.load_avg,
|
||||
cfs_rq_load_avg(cfs_rq) + 1);
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ static void populate_seccomp_data(struct seccomp_data *sd)
|
||||
|
||||
sd->nr = syscall_get_nr(task, regs);
|
||||
sd->arch = syscall_get_arch();
|
||||
syscall_get_arguments(task, regs, 0, 6, args);
|
||||
syscall_get_arguments(task, regs, args);
|
||||
sd->args[0] = args[0];
|
||||
sd->args[1] = args[1];
|
||||
sd->args[2] = args[2];
|
||||
|
||||
+4
-9
@@ -3605,16 +3605,11 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
|
||||
if (unlikely(sig != kinfo.si_signo))
|
||||
goto err;
|
||||
|
||||
/* Only allow sending arbitrary signals to yourself. */
|
||||
ret = -EPERM;
|
||||
if ((task_pid(current) != pid) &&
|
||||
(kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL)) {
|
||||
/* Only allow sending arbitrary signals to yourself. */
|
||||
ret = -EPERM;
|
||||
if (kinfo.si_code != SI_USER)
|
||||
goto err;
|
||||
|
||||
/* Turn this into a regular kill signal. */
|
||||
prepare_kill_siginfo(sig, &kinfo);
|
||||
}
|
||||
(kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL))
|
||||
goto err;
|
||||
} else {
|
||||
prepare_kill_siginfo(sig, &kinfo);
|
||||
}
|
||||
|
||||
+2
-1
@@ -128,6 +128,7 @@ static int zero;
|
||||
static int __maybe_unused one = 1;
|
||||
static int __maybe_unused two = 2;
|
||||
static int __maybe_unused four = 4;
|
||||
static unsigned long zero_ul;
|
||||
static unsigned long one_ul = 1;
|
||||
static unsigned long long_max = LONG_MAX;
|
||||
static int one_hundred = 100;
|
||||
@@ -1750,7 +1751,7 @@ static struct ctl_table fs_table[] = {
|
||||
.maxlen = sizeof(files_stat.max_files),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_doulongvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra1 = &zero_ul,
|
||||
.extra2 = &long_max,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -594,7 +594,7 @@ static ktime_t alarm_timer_remaining(struct k_itimer *timr, ktime_t now)
|
||||
{
|
||||
struct alarm *alarm = &timr->it.alarm.alarmtimer;
|
||||
|
||||
return ktime_sub(now, alarm->node.expires);
|
||||
return ktime_sub(alarm->node.expires, now);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1992,7 +1992,7 @@ static void print_bug_type(void)
|
||||
* modifying the code. @failed should be one of either:
|
||||
* EFAULT - if the problem happens on reading the @ip address
|
||||
* EINVAL - if what is read at @ip is not what was expected
|
||||
* EPERM - if the problem happens on writting to the @ip address
|
||||
* EPERM - if the problem happens on writing to the @ip address
|
||||
*/
|
||||
void ftrace_bug(int failed, struct dyn_ftrace *rec)
|
||||
{
|
||||
@@ -2391,7 +2391,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
|
||||
return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr);
|
||||
}
|
||||
|
||||
return -1; /* unknow ftrace bug */
|
||||
return -1; /* unknown ftrace bug */
|
||||
}
|
||||
|
||||
void __weak ftrace_replace_code(int mod_flags)
|
||||
@@ -3004,7 +3004,7 @@ ftrace_allocate_pages(unsigned long num_to_init)
|
||||
int cnt;
|
||||
|
||||
if (!num_to_init)
|
||||
return 0;
|
||||
return NULL;
|
||||
|
||||
start_pg = pg = kzalloc(sizeof(*pg), GFP_KERNEL);
|
||||
if (!pg)
|
||||
@@ -4755,7 +4755,7 @@ static int
|
||||
ftrace_set_addr(struct ftrace_ops *ops, unsigned long ip, int remove,
|
||||
int reset, int enable)
|
||||
{
|
||||
return ftrace_set_hash(ops, 0, 0, ip, remove, reset, enable);
|
||||
return ftrace_set_hash(ops, NULL, 0, ip, remove, reset, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5463,7 +5463,7 @@ void ftrace_create_filter_files(struct ftrace_ops *ops,
|
||||
|
||||
/*
|
||||
* The name "destroy_filter_files" is really a misnomer. Although
|
||||
* in the future, it may actualy delete the files, but this is
|
||||
* in the future, it may actually delete the files, but this is
|
||||
* really intended to make sure the ops passed in are disabled
|
||||
* and that when this function returns, the caller is free to
|
||||
* free the ops.
|
||||
@@ -5786,7 +5786,7 @@ void ftrace_module_enable(struct module *mod)
|
||||
/*
|
||||
* If the tracing is enabled, go ahead and enable the record.
|
||||
*
|
||||
* The reason not to enable the record immediatelly is the
|
||||
* The reason not to enable the record immediately is the
|
||||
* inherent check of ftrace_make_nop/ftrace_make_call for
|
||||
* correct previous instructions. Making first the NOP
|
||||
* conversion puts the module to the correct state, thus
|
||||
|
||||
@@ -7041,12 +7041,16 @@ static void buffer_pipe_buf_release(struct pipe_inode_info *pipe,
|
||||
buf->private = 0;
|
||||
}
|
||||
|
||||
static void buffer_pipe_buf_get(struct pipe_inode_info *pipe,
|
||||
static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf)
|
||||
{
|
||||
struct buffer_ref *ref = (struct buffer_ref *)buf->private;
|
||||
|
||||
if (ref->ref > INT_MAX/2)
|
||||
return false;
|
||||
|
||||
ref->ref++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Pipe buffer operations for a buffer. */
|
||||
|
||||
@@ -74,7 +74,7 @@ int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type)
|
||||
static int create_dyn_event(int argc, char **argv)
|
||||
{
|
||||
struct dyn_event_operations *ops;
|
||||
int ret;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (argv[0][0] == '-' || argv[0][0] == '!')
|
||||
return dyn_event_release(argc, argv, NULL);
|
||||
|
||||
@@ -3713,7 +3713,6 @@ static void track_data_destroy(struct hist_trigger_data *hist_data,
|
||||
struct trace_event_file *file = hist_data->event_file;
|
||||
|
||||
destroy_hist_field(data->track_data.track_var, 0);
|
||||
destroy_hist_field(data->track_data.var_ref, 0);
|
||||
|
||||
if (data->action == ACTION_SNAPSHOT) {
|
||||
struct track_data *track_data;
|
||||
|
||||
@@ -314,6 +314,7 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
|
||||
struct ring_buffer_event *event;
|
||||
struct ring_buffer *buffer;
|
||||
unsigned long irq_flags;
|
||||
unsigned long args[6];
|
||||
int pc;
|
||||
int syscall_nr;
|
||||
int size;
|
||||
@@ -347,7 +348,8 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->nr = syscall_nr;
|
||||
syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
|
||||
syscall_get_arguments(current, regs, args);
|
||||
memcpy(entry->args, args, sizeof(unsigned long) * sys_data->nb_args);
|
||||
|
||||
event_trigger_unlock_commit(trace_file, buffer, event, entry,
|
||||
irq_flags, pc);
|
||||
@@ -583,6 +585,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
|
||||
struct syscall_metadata *sys_data;
|
||||
struct syscall_trace_enter *rec;
|
||||
struct hlist_head *head;
|
||||
unsigned long args[6];
|
||||
bool valid_prog_array;
|
||||
int syscall_nr;
|
||||
int rctx;
|
||||
@@ -613,8 +616,8 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
|
||||
return;
|
||||
|
||||
rec->nr = syscall_nr;
|
||||
syscall_get_arguments(current, regs, 0, sys_data->nb_args,
|
||||
(unsigned long *)&rec->args);
|
||||
syscall_get_arguments(current, regs, args);
|
||||
memcpy(&rec->args, args, sizeof(unsigned long) * sys_data->nb_args);
|
||||
|
||||
if ((valid_prog_array &&
|
||||
!perf_call_bpf_enter(sys_data->enter_event, regs, sys_data, rec)) ||
|
||||
|
||||
+4
-2
@@ -554,13 +554,15 @@ static void softlockup_start_all(void)
|
||||
|
||||
int lockup_detector_online_cpu(unsigned int cpu)
|
||||
{
|
||||
watchdog_enable(cpu);
|
||||
if (cpumask_test_cpu(cpu, &watchdog_allowed_mask))
|
||||
watchdog_enable(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lockup_detector_offline_cpu(unsigned int cpu)
|
||||
{
|
||||
watchdog_disable(cpu);
|
||||
if (cpumask_test_cpu(cpu, &watchdog_allowed_mask))
|
||||
watchdog_disable(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user